Add material library
This commit is contained in:
		
							parent
							
								
									2d1ba5b90a
								
							
						
					
					
						commit
						2869533db9
					
				
					 73 changed files with 8571 additions and 1 deletions
				
			
		
							
								
								
									
										12
									
								
								Makefile
									
										
									
									
									
								
							
							
						
						
									
										12
									
								
								Makefile
									
										
									
									
									
								
							|  | @ -18,7 +18,7 @@ LIB_DIR = lib | ||||||
| 
 | 
 | ||||||
| STATIC_INCLUDE_DIR = include | STATIC_INCLUDE_DIR = include | ||||||
| 
 | 
 | ||||||
| CPPFLAGS = -Isrc/imgui -g -D ASSET_DIR=\"$(ASSET_PREFIX)\" | CPPFLAGS = -Isrc/imgui -Isrc/material-colors -g -D ASSET_DIR=\"$(ASSET_PREFIX)\" | ||||||
| CFLAGS = -g -D ASSET_DIR=\"$(ASSET_PREFIX)\" | CFLAGS = -g -D ASSET_DIR=\"$(ASSET_PREFIX)\" | ||||||
| LDFLAGS = -g | LDFLAGS = -g | ||||||
| #global LDLIBS
 | #global LDLIBS
 | ||||||
|  | @ -51,6 +51,16 @@ C_SRC += $(wildcard $(SRC_DIR)/*.c) #GET LIST OF ALL C FILES | ||||||
| CPP_SRC += $(wildcard $(SRC_DIR)/*.cpp) #GET LIST OF ALL CPP FILES | CPP_SRC += $(wildcard $(SRC_DIR)/*.cpp) #GET LIST OF ALL CPP FILES | ||||||
| CPP_SRC += $(wildcard $(SRC_DIR)/rlImGui/*.cpp) #GET LIST OF ALL CPP FILES | CPP_SRC += $(wildcard $(SRC_DIR)/rlImGui/*.cpp) #GET LIST OF ALL CPP FILES | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
|  | # BUILD MATERIAL_COLORS SYSETM
 | ||||||
|  | CPP_SRC += $(wildcard $(SRC_DIR)/material-colors/cpp/quantize/celebi.cpp) | ||||||
|  | CPP_SRC += $(wildcard $(SRC_DIR)/material-colors/cpp/quantize/wsmeans.cpp) | ||||||
|  | CPP_SRC += $(wildcard $(SRC_DIR)/material-colors/cpp/quantize/wu.cpp) | ||||||
|  | CPP_SRC += $(wildcard $(SRC_DIR)/material-colors/cpp/quantize/lab.cpp) | ||||||
|  | CPP_SRC += $(wildcard $(SRC_DIR)/material-colors/cpp/utils/utils.cpp) | ||||||
|  | EXTRA_DIRS += $(OBJ_DIR)/material-colors/cpp/quantize | ||||||
|  | EXTRA_DIRS += $(OBJ_DIR)/material-colors/cpp/utils | ||||||
|  | 
 | ||||||
| ifeq ($(IMGUI_MODE), BUILD) | ifeq ($(IMGUI_MODE), BUILD) | ||||||
| CPP_SRC += $(wildcard $(SRC_DIR)/imgui/*.cpp) #GET LIST OF ALL CPP FILES | CPP_SRC += $(wildcard $(SRC_DIR)/imgui/*.cpp) #GET LIST OF ALL CPP FILES | ||||||
| EXTRA_DIRS += $(OBJ_DIR)/imgui | EXTRA_DIRS += $(OBJ_DIR)/imgui | ||||||
|  |  | ||||||
							
								
								
									
										71
									
								
								src/material-colors/cpp/blend/blend.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										71
									
								
								src/material-colors/cpp/blend/blend.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,71 @@ | ||||||
|  | /*
 | ||||||
|  |  * Copyright 2022 Google LLC | ||||||
|  |  * | ||||||
|  |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * | ||||||
|  |  *      http://www.apache.org/licenses/LICENSE-2.0
 | ||||||
|  |  * | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | #include "cpp/blend/blend.h" | ||||||
|  | 
 | ||||||
|  | #include <algorithm> | ||||||
|  | #include <cstdint> | ||||||
|  | 
 | ||||||
|  | #include "cpp/cam/cam.h" | ||||||
|  | #include "cpp/cam/hct.h" | ||||||
|  | #include "cpp/cam/viewing_conditions.h" | ||||||
|  | #include "cpp/utils/utils.h" | ||||||
|  | 
 | ||||||
|  | namespace material_color_utilities { | ||||||
|  | 
 | ||||||
|  | Argb BlendHarmonize(const Argb design_color, const Argb key_color) { | ||||||
|  |   Hct from_hct(design_color); | ||||||
|  |   Hct to_hct(key_color); | ||||||
|  |   double difference_degrees = DiffDegrees(from_hct.get_hue(), to_hct.get_hue()); | ||||||
|  |   double rotation_degrees = std::min(difference_degrees * 0.5, 15.0); | ||||||
|  |   double output_hue = SanitizeDegreesDouble( | ||||||
|  |       from_hct.get_hue() + | ||||||
|  |       rotation_degrees * | ||||||
|  |           RotationDirection(from_hct.get_hue(), to_hct.get_hue())); | ||||||
|  |   from_hct.set_hue(output_hue); | ||||||
|  |   return from_hct.ToInt(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | Argb BlendHctHue(const Argb from, const Argb to, const double amount) { | ||||||
|  |   int ucs = BlendCam16Ucs(from, to, amount); | ||||||
|  |   Hct ucs_hct(ucs); | ||||||
|  |   Hct from_hct(from); | ||||||
|  |   from_hct.set_hue(ucs_hct.get_hue()); | ||||||
|  |   return from_hct.ToInt(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | Argb BlendCam16Ucs(const Argb from, const Argb to, const double amount) { | ||||||
|  |   Cam from_cam = CamFromInt(from); | ||||||
|  |   Cam to_cam = CamFromInt(to); | ||||||
|  | 
 | ||||||
|  |   const double a_j = from_cam.jstar; | ||||||
|  |   const double a_a = from_cam.astar; | ||||||
|  |   const double a_b = from_cam.bstar; | ||||||
|  | 
 | ||||||
|  |   const double b_j = to_cam.jstar; | ||||||
|  |   const double b_a = to_cam.astar; | ||||||
|  |   const double b_b = to_cam.bstar; | ||||||
|  | 
 | ||||||
|  |   const double jstar = a_j + (b_j - a_j) * amount; | ||||||
|  |   const double astar = a_a + (b_a - a_a) * amount; | ||||||
|  |   const double bstar = a_b + (b_b - a_b) * amount; | ||||||
|  | 
 | ||||||
|  |   const Cam blended = CamFromUcsAndViewingConditions(jstar, astar, bstar, | ||||||
|  |                                                      kDefaultViewingConditions); | ||||||
|  |   return IntFromCam(blended); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | }  // namespace material_color_utilities
 | ||||||
							
								
								
									
										31
									
								
								src/material-colors/cpp/blend/blend.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								src/material-colors/cpp/blend/blend.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,31 @@ | ||||||
|  | /*
 | ||||||
|  |  * Copyright 2022 Google LLC | ||||||
|  |  * | ||||||
|  |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * | ||||||
|  |  *      http://www.apache.org/licenses/LICENSE-2.0
 | ||||||
|  |  * | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | #ifndef CPP_BLEND_BLEND_H_ | ||||||
|  | #define CPP_BLEND_BLEND_H_ | ||||||
|  | 
 | ||||||
|  | #include <cstdint> | ||||||
|  | 
 | ||||||
|  | #include "cpp/utils/utils.h" | ||||||
|  | 
 | ||||||
|  | namespace material_color_utilities { | ||||||
|  | 
 | ||||||
|  | Argb BlendHarmonize(const Argb design_color, const Argb key_color); | ||||||
|  | Argb BlendHctHue(const Argb from, const Argb to, const double amount); | ||||||
|  | Argb BlendCam16Ucs(const Argb from, const Argb to, const double amount); | ||||||
|  | 
 | ||||||
|  | }  // namespace material_color_utilities
 | ||||||
|  | #endif  // CPP_BLEND_BLEND_H_
 | ||||||
							
								
								
									
										31
									
								
								src/material-colors/cpp/blend/blend_test.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								src/material-colors/cpp/blend/blend_test.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,31 @@ | ||||||
|  | /*
 | ||||||
|  |  * Copyright 2022 Google LLC | ||||||
|  |  * | ||||||
|  |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * | ||||||
|  |  *      http://www.apache.org/licenses/LICENSE-2.0
 | ||||||
|  |  * | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | #include "cpp/blend/blend.h" | ||||||
|  | 
 | ||||||
|  | #include "testing/base/public/gunit.h" | ||||||
|  | #include "cpp/utils/utils.h" | ||||||
|  | 
 | ||||||
|  | namespace material_color_utilities { | ||||||
|  | 
 | ||||||
|  | namespace { | ||||||
|  | TEST(BlendTest, RedToBlue) { | ||||||
|  |   int blended = BlendHctHue(0xffff0000, 0xff0000ff, 0.8); | ||||||
|  |   EXPECT_EQ(HexFromArgb(blended), "ff905eff"); | ||||||
|  | } | ||||||
|  | }  // namespace
 | ||||||
|  | 
 | ||||||
|  | }  // namespace material_color_utilities
 | ||||||
							
								
								
									
										263
									
								
								src/material-colors/cpp/cam/cam.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										263
									
								
								src/material-colors/cpp/cam/cam.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,263 @@ | ||||||
|  | /*
 | ||||||
|  |  * Copyright 2022 Google LLC | ||||||
|  |  * | ||||||
|  |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * | ||||||
|  |  *      http://www.apache.org/licenses/LICENSE-2.0
 | ||||||
|  |  * | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | #include "cpp/cam/cam.h" | ||||||
|  | 
 | ||||||
|  | #include <assert.h> | ||||||
|  | #include <math.h> | ||||||
|  | 
 | ||||||
|  | #include "cpp/cam/hct_solver.h" | ||||||
|  | #include "cpp/cam/viewing_conditions.h" | ||||||
|  | #include "cpp/utils/utils.h" | ||||||
|  | 
 | ||||||
|  | namespace material_color_utilities { | ||||||
|  | 
 | ||||||
|  | Cam CamFromJchAndViewingConditions(double j, double c, double h, | ||||||
|  |                                    ViewingConditions viewing_conditions); | ||||||
|  | 
 | ||||||
|  | Cam CamFromUcsAndViewingConditions( | ||||||
|  |     double jstar, double astar, double bstar, | ||||||
|  |     const ViewingConditions &viewing_conditions) { | ||||||
|  |   const double a = astar; | ||||||
|  |   const double b = bstar; | ||||||
|  |   const double m = sqrt(a * a + b * b); | ||||||
|  |   const double m_2 = (exp(m * 0.0228) - 1.0) / 0.0228; | ||||||
|  |   const double c = m_2 / viewing_conditions.fl_root; | ||||||
|  |   double h = atan2(b, a) * (180.0 / kPi); | ||||||
|  |   if (h < 0.0) { | ||||||
|  |     h += 360.0; | ||||||
|  |   } | ||||||
|  |   const double j = jstar / (1 - (jstar - 100) * 0.007); | ||||||
|  |   return CamFromJchAndViewingConditions(j, c, h, viewing_conditions); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | Cam CamFromIntAndViewingConditions( | ||||||
|  |     Argb argb, const ViewingConditions &viewing_conditions) { | ||||||
|  |   // XYZ from ARGB, inlined.
 | ||||||
|  |   int red = (argb & 0x00ff0000) >> 16; | ||||||
|  |   int green = (argb & 0x0000ff00) >> 8; | ||||||
|  |   int blue = (argb & 0x000000ff); | ||||||
|  |   double red_l = Linearized(red); | ||||||
|  |   double green_l = Linearized(green); | ||||||
|  |   double blue_l = Linearized(blue); | ||||||
|  |   double x = 0.41233895 * red_l + 0.35762064 * green_l + 0.18051042 * blue_l; | ||||||
|  |   double y = 0.2126 * red_l + 0.7152 * green_l + 0.0722 * blue_l; | ||||||
|  |   double z = 0.01932141 * red_l + 0.11916382 * green_l + 0.95034478 * blue_l; | ||||||
|  | 
 | ||||||
|  |   // Convert XYZ to 'cone'/'rgb' responses
 | ||||||
|  |   double r_c = 0.401288 * x + 0.650173 * y - 0.051461 * z; | ||||||
|  |   double g_c = -0.250268 * x + 1.204414 * y + 0.045854 * z; | ||||||
|  |   double b_c = -0.002079 * x + 0.048952 * y + 0.953127 * z; | ||||||
|  | 
 | ||||||
|  |   // Discount illuminant.
 | ||||||
|  |   double r_d = viewing_conditions.rgb_d[0] * r_c; | ||||||
|  |   double g_d = viewing_conditions.rgb_d[1] * g_c; | ||||||
|  |   double b_d = viewing_conditions.rgb_d[2] * b_c; | ||||||
|  | 
 | ||||||
|  |   // Chromatic adaptation.
 | ||||||
|  |   double r_af = pow(viewing_conditions.fl * fabs(r_d) / 100.0, 0.42); | ||||||
|  |   double g_af = pow(viewing_conditions.fl * fabs(g_d) / 100.0, 0.42); | ||||||
|  |   double b_af = pow(viewing_conditions.fl * fabs(b_d) / 100.0, 0.42); | ||||||
|  |   double r_a = Signum(r_d) * 400.0 * r_af / (r_af + 27.13); | ||||||
|  |   double g_a = Signum(g_d) * 400.0 * g_af / (g_af + 27.13); | ||||||
|  |   double b_a = Signum(b_d) * 400.0 * b_af / (b_af + 27.13); | ||||||
|  | 
 | ||||||
|  |   // Redness-greenness
 | ||||||
|  |   double a = (11.0 * r_a + -12.0 * g_a + b_a) / 11.0; | ||||||
|  |   double b = (r_a + g_a - 2.0 * b_a) / 9.0; | ||||||
|  |   double u = (20.0 * r_a + 20.0 * g_a + 21.0 * b_a) / 20.0; | ||||||
|  |   double p2 = (40.0 * r_a + 20.0 * g_a + b_a) / 20.0; | ||||||
|  | 
 | ||||||
|  |   double radians = atan2(b, a); | ||||||
|  |   double degrees = radians * 180.0 / kPi; | ||||||
|  |   double hue = SanitizeDegreesDouble(degrees); | ||||||
|  |   double hue_radians = hue * kPi / 180.0; | ||||||
|  |   double ac = p2 * viewing_conditions.nbb; | ||||||
|  | 
 | ||||||
|  |   double j = 100.0 * pow(ac / viewing_conditions.aw, | ||||||
|  |                          viewing_conditions.c * viewing_conditions.z); | ||||||
|  |   double q = (4.0 / viewing_conditions.c) * sqrt(j / 100.0) * | ||||||
|  |              (viewing_conditions.aw + 4.0) * viewing_conditions.fl_root; | ||||||
|  |   double hue_prime = hue < 20.14 ? hue + 360 : hue; | ||||||
|  |   double e_hue = 0.25 * (cos(hue_prime * kPi / 180.0 + 2.0) + 3.8); | ||||||
|  |   double p1 = | ||||||
|  |       50000.0 / 13.0 * e_hue * viewing_conditions.n_c * viewing_conditions.ncb; | ||||||
|  |   double t = p1 * sqrt(a * a + b * b) / (u + 0.305); | ||||||
|  |   double alpha = | ||||||
|  |       pow(t, 0.9) * | ||||||
|  |       pow(1.64 - pow(0.29, viewing_conditions.background_y_to_white_point_y), | ||||||
|  |           0.73); | ||||||
|  |   double c = alpha * sqrt(j / 100.0); | ||||||
|  |   double m = c * viewing_conditions.fl_root; | ||||||
|  |   double s = 50.0 * sqrt((alpha * viewing_conditions.c) / | ||||||
|  |                          (viewing_conditions.aw + 4.0)); | ||||||
|  |   double jstar = (1.0 + 100.0 * 0.007) * j / (1.0 + 0.007 * j); | ||||||
|  |   double mstar = 1.0 / 0.0228 * log(1.0 + 0.0228 * m); | ||||||
|  |   double astar = mstar * cos(hue_radians); | ||||||
|  |   double bstar = mstar * sin(hue_radians); | ||||||
|  |   return {hue, c, j, q, m, s, jstar, astar, bstar}; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | Cam CamFromInt(Argb argb) { | ||||||
|  |   return CamFromIntAndViewingConditions(argb, kDefaultViewingConditions); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | Argb IntFromCamAndViewingConditions(Cam cam, | ||||||
|  |                                     ViewingConditions viewing_conditions) { | ||||||
|  |   double alpha = (cam.chroma == 0.0 || cam.j == 0.0) | ||||||
|  |                      ? 0.0 | ||||||
|  |                      : cam.chroma / sqrt(cam.j / 100.0); | ||||||
|  |   double t = pow( | ||||||
|  |       alpha / pow(1.64 - pow(0.29, | ||||||
|  |                              viewing_conditions.background_y_to_white_point_y), | ||||||
|  |                   0.73), | ||||||
|  |       1.0 / 0.9); | ||||||
|  |   double h_rad = cam.hue * kPi / 180.0; | ||||||
|  |   double e_hue = 0.25 * (cos(h_rad + 2.0) + 3.8); | ||||||
|  |   double ac = | ||||||
|  |       viewing_conditions.aw * | ||||||
|  |       pow(cam.j / 100.0, 1.0 / viewing_conditions.c / viewing_conditions.z); | ||||||
|  |   double p1 = e_hue * (50000.0 / 13.0) * viewing_conditions.n_c * | ||||||
|  |               viewing_conditions.ncb; | ||||||
|  |   double p2 = ac / viewing_conditions.nbb; | ||||||
|  |   double h_sin = sin(h_rad); | ||||||
|  |   double h_cos = cos(h_rad); | ||||||
|  |   double gamma = 23.0 * (p2 + 0.305) * t / | ||||||
|  |                  (23.0 * p1 + 11.0 * t * h_cos + 108.0 * t * h_sin); | ||||||
|  |   double a = gamma * h_cos; | ||||||
|  |   double b = gamma * h_sin; | ||||||
|  |   double r_a = (460.0 * p2 + 451.0 * a + 288.0 * b) / 1403.0; | ||||||
|  |   double g_a = (460.0 * p2 - 891.0 * a - 261.0 * b) / 1403.0; | ||||||
|  |   double b_a = (460.0 * p2 - 220.0 * a - 6300.0 * b) / 1403.0; | ||||||
|  | 
 | ||||||
|  |   double r_c_base = fmax(0, (27.13 * fabs(r_a)) / (400.0 - fabs(r_a))); | ||||||
|  |   double r_c = | ||||||
|  |       Signum(r_a) * (100.0 / viewing_conditions.fl) * pow(r_c_base, 1.0 / 0.42); | ||||||
|  |   double g_c_base = fmax(0, (27.13 * fabs(g_a)) / (400.0 - fabs(g_a))); | ||||||
|  |   double g_c = | ||||||
|  |       Signum(g_a) * (100.0 / viewing_conditions.fl) * pow(g_c_base, 1.0 / 0.42); | ||||||
|  |   double b_c_base = fmax(0, (27.13 * fabs(b_a)) / (400.0 - fabs(b_a))); | ||||||
|  |   double b_c = | ||||||
|  |       Signum(b_a) * (100.0 / viewing_conditions.fl) * pow(b_c_base, 1.0 / 0.42); | ||||||
|  |   double r_x = r_c / viewing_conditions.rgb_d[0]; | ||||||
|  |   double g_x = g_c / viewing_conditions.rgb_d[1]; | ||||||
|  |   double b_x = b_c / viewing_conditions.rgb_d[2]; | ||||||
|  |   double x = 1.86206786 * r_x - 1.01125463 * g_x + 0.14918677 * b_x; | ||||||
|  |   double y = 0.38752654 * r_x + 0.62144744 * g_x - 0.00897398 * b_x; | ||||||
|  |   double z = -0.01584150 * r_x - 0.03412294 * g_x + 1.04996444 * b_x; | ||||||
|  | 
 | ||||||
|  |   // intFromXyz
 | ||||||
|  |   double r_l = 3.2406 * x - 1.5372 * y - 0.4986 * z; | ||||||
|  |   double g_l = -0.9689 * x + 1.8758 * y + 0.0415 * z; | ||||||
|  |   double b_l = 0.0557 * x - 0.2040 * y + 1.0570 * z; | ||||||
|  | 
 | ||||||
|  |   int red = Delinearized(r_l); | ||||||
|  |   int green = Delinearized(g_l); | ||||||
|  |   int blue = Delinearized(b_l); | ||||||
|  | 
 | ||||||
|  |   return ArgbFromRgb(red, green, blue); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | Argb IntFromCam(Cam cam) { | ||||||
|  |   return IntFromCamAndViewingConditions(cam, kDefaultViewingConditions); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | Cam CamFromJchAndViewingConditions(double j, double c, double h, | ||||||
|  |                                    ViewingConditions viewing_conditions) { | ||||||
|  |   double q = (4.0 / viewing_conditions.c) * sqrt(j / 100.0) * | ||||||
|  |              (viewing_conditions.aw + 4.0) * (viewing_conditions.fl_root); | ||||||
|  |   double m = c * viewing_conditions.fl_root; | ||||||
|  |   double alpha = c / sqrt(j / 100.0); | ||||||
|  |   double s = 50.0 * sqrt((alpha * viewing_conditions.c) / | ||||||
|  |                          (viewing_conditions.aw + 4.0)); | ||||||
|  |   double hue_radians = h * kPi / 180.0; | ||||||
|  |   double jstar = (1.0 + 100.0 * 0.007) * j / (1.0 + 0.007 * j); | ||||||
|  |   double mstar = 1.0 / 0.0228 * log(1.0 + 0.0228 * m); | ||||||
|  |   double astar = mstar * cos(hue_radians); | ||||||
|  |   double bstar = mstar * sin(hue_radians); | ||||||
|  |   return {h, c, j, q, m, s, jstar, astar, bstar}; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | double CamDistance(Cam a, Cam b) { | ||||||
|  |   double d_j = a.jstar - b.jstar; | ||||||
|  |   double d_a = a.astar - b.astar; | ||||||
|  |   double d_b = a.bstar - b.bstar; | ||||||
|  |   double d_e_prime = sqrt(d_j * d_j + d_a * d_a + d_b * d_b); | ||||||
|  |   double d_e = 1.41 * pow(d_e_prime, 0.63); | ||||||
|  |   return d_e; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | Argb IntFromHcl(double hue, double chroma, double lstar) { | ||||||
|  |   return SolveToInt(hue, chroma, lstar); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | Cam CamFromXyzAndViewingConditions( | ||||||
|  |     double x, double y, double z, const ViewingConditions &viewing_conditions) { | ||||||
|  |   // Convert XYZ to 'cone'/'rgb' responses
 | ||||||
|  |   double r_c = 0.401288 * x + 0.650173 * y - 0.051461 * z; | ||||||
|  |   double g_c = -0.250268 * x + 1.204414 * y + 0.045854 * z; | ||||||
|  |   double b_c = -0.002079 * x + 0.048952 * y + 0.953127 * z; | ||||||
|  | 
 | ||||||
|  |   // Discount illuminant.
 | ||||||
|  |   double r_d = viewing_conditions.rgb_d[0] * r_c; | ||||||
|  |   double g_d = viewing_conditions.rgb_d[1] * g_c; | ||||||
|  |   double b_d = viewing_conditions.rgb_d[2] * b_c; | ||||||
|  | 
 | ||||||
|  |   // Chromatic adaptation.
 | ||||||
|  |   double r_af = pow(viewing_conditions.fl * fabs(r_d) / 100.0, 0.42); | ||||||
|  |   double g_af = pow(viewing_conditions.fl * fabs(g_d) / 100.0, 0.42); | ||||||
|  |   double b_af = pow(viewing_conditions.fl * fabs(b_d) / 100.0, 0.42); | ||||||
|  |   double r_a = Signum(r_d) * 400.0 * r_af / (r_af + 27.13); | ||||||
|  |   double g_a = Signum(g_d) * 400.0 * g_af / (g_af + 27.13); | ||||||
|  |   double b_a = Signum(b_d) * 400.0 * b_af / (b_af + 27.13); | ||||||
|  | 
 | ||||||
|  |   // Redness-greenness
 | ||||||
|  |   double a = (11.0 * r_a + -12.0 * g_a + b_a) / 11.0; | ||||||
|  |   double b = (r_a + g_a - 2.0 * b_a) / 9.0; | ||||||
|  |   double u = (20.0 * r_a + 20.0 * g_a + 21.0 * b_a) / 20.0; | ||||||
|  |   double p2 = (40.0 * r_a + 20.0 * g_a + b_a) / 20.0; | ||||||
|  | 
 | ||||||
|  |   double radians = atan2(b, a); | ||||||
|  |   double degrees = radians * 180.0 / kPi; | ||||||
|  |   double hue = SanitizeDegreesDouble(degrees); | ||||||
|  |   double hue_radians = hue * kPi / 180.0; | ||||||
|  |   double ac = p2 * viewing_conditions.nbb; | ||||||
|  | 
 | ||||||
|  |   double j = 100.0 * pow(ac / viewing_conditions.aw, | ||||||
|  |                          viewing_conditions.c * viewing_conditions.z); | ||||||
|  |   double q = (4.0 / viewing_conditions.c) * sqrt(j / 100.0) * | ||||||
|  |              (viewing_conditions.aw + 4.0) * viewing_conditions.fl_root; | ||||||
|  |   double hue_prime = hue < 20.14 ? hue + 360 : hue; | ||||||
|  |   double e_hue = 0.25 * (cos(hue_prime * kPi / 180.0 + 2.0) + 3.8); | ||||||
|  |   double p1 = | ||||||
|  |       50000.0 / 13.0 * e_hue * viewing_conditions.n_c * viewing_conditions.ncb; | ||||||
|  |   double t = p1 * sqrt(a * a + b * b) / (u + 0.305); | ||||||
|  |   double alpha = | ||||||
|  |       pow(t, 0.9) * | ||||||
|  |       pow(1.64 - pow(0.29, viewing_conditions.background_y_to_white_point_y), | ||||||
|  |           0.73); | ||||||
|  |   double c = alpha * sqrt(j / 100.0); | ||||||
|  |   double m = c * viewing_conditions.fl_root; | ||||||
|  |   double s = 50.0 * sqrt((alpha * viewing_conditions.c) / | ||||||
|  |                          (viewing_conditions.aw + 4.0)); | ||||||
|  |   double jstar = (1.0 + 100.0 * 0.007) * j / (1.0 + 0.007 * j); | ||||||
|  |   double mstar = 1.0 / 0.0228 * log(1.0 + 0.0228 * m); | ||||||
|  |   double astar = mstar * cos(hue_radians); | ||||||
|  |   double bstar = mstar * sin(hue_radians); | ||||||
|  |   return {hue, c, j, q, m, s, jstar, astar, bstar}; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | }  // namespace material_color_utilities
 | ||||||
							
								
								
									
										53
									
								
								src/material-colors/cpp/cam/cam.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										53
									
								
								src/material-colors/cpp/cam/cam.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,53 @@ | ||||||
|  | /*
 | ||||||
|  |  * Copyright 2022 Google LLC | ||||||
|  |  * | ||||||
|  |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * | ||||||
|  |  *      http://www.apache.org/licenses/LICENSE-2.0
 | ||||||
|  |  * | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | #ifndef CPP_CAM_CAM_H_ | ||||||
|  | #define CPP_CAM_CAM_H_ | ||||||
|  | 
 | ||||||
|  | #include "cpp/cam/viewing_conditions.h" | ||||||
|  | #include "cpp/utils/utils.h" | ||||||
|  | 
 | ||||||
|  | namespace material_color_utilities { | ||||||
|  | 
 | ||||||
|  | struct Cam { | ||||||
|  |   double hue = 0.0; | ||||||
|  |   double chroma = 0.0; | ||||||
|  |   double j = 0.0; | ||||||
|  |   double q = 0.0; | ||||||
|  |   double m = 0.0; | ||||||
|  |   double s = 0.0; | ||||||
|  | 
 | ||||||
|  |   double jstar = 0.0; | ||||||
|  |   double astar = 0.0; | ||||||
|  |   double bstar = 0.0; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | Cam CamFromInt(Argb argb); | ||||||
|  | Cam CamFromIntAndViewingConditions(Argb argb, | ||||||
|  |                                    const ViewingConditions &viewing_conditions); | ||||||
|  | Argb IntFromHcl(double hue, double chroma, double lstar); | ||||||
|  | Argb IntFromCam(Cam cam); | ||||||
|  | Cam CamFromUcsAndViewingConditions(double jstar, double astar, double bstar, | ||||||
|  |                                    const ViewingConditions &viewing_conditions); | ||||||
|  | /**
 | ||||||
|  |  * Given color expressed in the XYZ color space and viewed | ||||||
|  |  * in [viewingConditions], converts the color to CAM16. | ||||||
|  |  */ | ||||||
|  | Cam CamFromXyzAndViewingConditions(double x, double y, double z, | ||||||
|  |                                    const ViewingConditions &viewing_conditions); | ||||||
|  | 
 | ||||||
|  | }  // namespace material_color_utilities
 | ||||||
|  | #endif  // CPP_CAM_CAM_H_
 | ||||||
							
								
								
									
										109
									
								
								src/material-colors/cpp/cam/cam_test.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										109
									
								
								src/material-colors/cpp/cam/cam_test.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,109 @@ | ||||||
|  | /*
 | ||||||
|  |  * Copyright 2022 Google LLC | ||||||
|  |  * | ||||||
|  |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * | ||||||
|  |  *      http://www.apache.org/licenses/LICENSE-2.0
 | ||||||
|  |  * | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | #include "cpp/cam/cam.h" | ||||||
|  | 
 | ||||||
|  | #include "testing/base/public/gmock.h" | ||||||
|  | #include "testing/base/public/gunit.h" | ||||||
|  | 
 | ||||||
|  | namespace material_color_utilities { | ||||||
|  | 
 | ||||||
|  | namespace { | ||||||
|  | using testing::DoubleNear; | ||||||
|  | 
 | ||||||
|  | using testing::Eq; | ||||||
|  | 
 | ||||||
|  | Argb RED = 0xffff0000; | ||||||
|  | Argb GREEN = 0xff00ff00; | ||||||
|  | Argb BLUE = 0xff0000ff; | ||||||
|  | Argb WHITE = 0xffffffff; | ||||||
|  | Argb BLACK = 0xff000000; | ||||||
|  | 
 | ||||||
|  | TEST(CamTest, Red) { | ||||||
|  |   Cam cam = CamFromInt(RED); | ||||||
|  | 
 | ||||||
|  |   EXPECT_THAT(cam.hue, DoubleNear(27.408, 0.001)); | ||||||
|  |   EXPECT_THAT(cam.chroma, DoubleNear(113.357, 0.001)); | ||||||
|  |   EXPECT_THAT(cam.j, DoubleNear(46.445, 0.001)); | ||||||
|  |   EXPECT_THAT(cam.m, DoubleNear(89.494, 0.001)); | ||||||
|  |   EXPECT_THAT(cam.s, DoubleNear(91.889, 0.001)); | ||||||
|  |   EXPECT_THAT(cam.q, DoubleNear(105.988, 0.001)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TEST(CamTest, Green) { | ||||||
|  |   Cam cam = CamFromInt(GREEN); | ||||||
|  | 
 | ||||||
|  |   EXPECT_THAT(cam.hue, DoubleNear(142.139, 0.001)); | ||||||
|  |   EXPECT_THAT(cam.chroma, DoubleNear(108.410, 0.001)); | ||||||
|  |   EXPECT_THAT(cam.j, DoubleNear(79.331, 0.001)); | ||||||
|  |   EXPECT_THAT(cam.m, DoubleNear(85.587, 0.001)); | ||||||
|  |   EXPECT_THAT(cam.s, DoubleNear(78.604, 0.001)); | ||||||
|  |   EXPECT_THAT(cam.q, DoubleNear(138.520, 0.001)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TEST(CamTest, Blue) { | ||||||
|  |   Cam cam = CamFromInt(BLUE); | ||||||
|  | 
 | ||||||
|  |   EXPECT_THAT(cam.hue, DoubleNear(282.788, 0.001)); | ||||||
|  |   EXPECT_THAT(cam.chroma, DoubleNear(87.230, 0.001)); | ||||||
|  |   EXPECT_THAT(cam.j, DoubleNear(25.465, 0.001)); | ||||||
|  |   EXPECT_THAT(cam.m, DoubleNear(68.867, 0.001)); | ||||||
|  |   EXPECT_THAT(cam.s, DoubleNear(93.674, 0.001)); | ||||||
|  |   EXPECT_THAT(cam.q, DoubleNear(78.481, 0.001)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TEST(CamTest, White) { | ||||||
|  |   Cam cam = CamFromInt(WHITE); | ||||||
|  | 
 | ||||||
|  |   EXPECT_THAT(cam.hue, DoubleNear(209.492, 0.001)); | ||||||
|  |   EXPECT_THAT(cam.chroma, DoubleNear(2.869, 0.001)); | ||||||
|  |   EXPECT_THAT(cam.j, DoubleNear(100.0, 0.001)); | ||||||
|  |   EXPECT_THAT(cam.m, DoubleNear(2.265, 0.001)); | ||||||
|  |   EXPECT_THAT(cam.s, DoubleNear(12.068, 0.001)); | ||||||
|  |   EXPECT_THAT(cam.q, DoubleNear(155.521, 0.001)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TEST(CamTest, Black) { | ||||||
|  |   Cam cam = CamFromInt(BLACK); | ||||||
|  | 
 | ||||||
|  |   EXPECT_THAT(cam.hue, DoubleNear(0.0, 0.001)); | ||||||
|  |   EXPECT_THAT(cam.chroma, DoubleNear(0.0, 0.001)); | ||||||
|  |   EXPECT_THAT(cam.j, DoubleNear(0.0, 0.001)); | ||||||
|  |   EXPECT_THAT(cam.m, DoubleNear(0.0, 0.001)); | ||||||
|  |   EXPECT_THAT(cam.s, DoubleNear(0.0, 0.001)); | ||||||
|  |   EXPECT_THAT(cam.q, DoubleNear(0.0, 0.001)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TEST(CamTest, RedRoundTrip) { | ||||||
|  |   Cam cam = CamFromInt(RED); | ||||||
|  |   Argb argb = IntFromCam(cam); | ||||||
|  |   EXPECT_THAT(argb, Eq(RED)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TEST(CamTest, GreenRoundTrip) { | ||||||
|  |   Cam cam = CamFromInt(GREEN); | ||||||
|  |   Argb argb = IntFromCam(cam); | ||||||
|  |   EXPECT_THAT(argb, Eq(GREEN)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TEST(CamTest, BlueRoundTrip) { | ||||||
|  |   Cam cam = CamFromInt(BLUE); | ||||||
|  |   Argb argb = IntFromCam(cam); | ||||||
|  |   EXPECT_THAT(argb, Eq(BLUE)); | ||||||
|  | } | ||||||
|  | }  // namespace
 | ||||||
|  | 
 | ||||||
|  | }  // namespace material_color_utilities
 | ||||||
							
								
								
									
										57
									
								
								src/material-colors/cpp/cam/hct.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										57
									
								
								src/material-colors/cpp/cam/hct.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,57 @@ | ||||||
|  | /*
 | ||||||
|  |  * Copyright 2022 Google LLC | ||||||
|  |  * | ||||||
|  |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * | ||||||
|  |  *      http://www.apache.org/licenses/LICENSE-2.0
 | ||||||
|  |  * | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | #include "cpp/cam/hct.h" | ||||||
|  | 
 | ||||||
|  | #include "cpp/cam/hct_solver.h" | ||||||
|  | #include "cpp/utils/utils.h" | ||||||
|  | 
 | ||||||
|  | namespace material_color_utilities { | ||||||
|  | Hct::Hct(double hue, double chroma, double tone) { | ||||||
|  |   SetInternalState(SolveToInt(hue, chroma, tone)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | Hct::Hct(Argb argb) { SetInternalState(argb); } | ||||||
|  | 
 | ||||||
|  | double Hct::get_hue() const { return hue_; } | ||||||
|  | 
 | ||||||
|  | double Hct::get_chroma() const { return chroma_; } | ||||||
|  | 
 | ||||||
|  | double Hct::get_tone() const { return tone_; } | ||||||
|  | 
 | ||||||
|  | Argb Hct::ToInt() const { return argb_; } | ||||||
|  | 
 | ||||||
|  | void Hct::set_hue(double new_hue) { | ||||||
|  |   SetInternalState(SolveToInt(new_hue, chroma_, tone_)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void Hct::set_chroma(double new_chroma) { | ||||||
|  |   SetInternalState(SolveToInt(hue_, new_chroma, tone_)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void Hct::set_tone(double new_tone) { | ||||||
|  |   SetInternalState(SolveToInt(hue_, chroma_, new_tone)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void Hct::SetInternalState(Argb argb) { | ||||||
|  |   argb_ = argb; | ||||||
|  |   Cam cam = CamFromInt(argb); | ||||||
|  |   hue_ = cam.hue; | ||||||
|  |   chroma_ = cam.chroma; | ||||||
|  |   tone_ = LstarFromArgb(argb); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | }  // namespace material_color_utilities
 | ||||||
							
								
								
									
										137
									
								
								src/material-colors/cpp/cam/hct.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										137
									
								
								src/material-colors/cpp/cam/hct.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,137 @@ | ||||||
|  | /*
 | ||||||
|  |  * Copyright 2022 Google LLC | ||||||
|  |  * | ||||||
|  |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * | ||||||
|  |  *      http://www.apache.org/licenses/LICENSE-2.0
 | ||||||
|  |  * | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | #ifndef CPP_CAM_HCT_H_ | ||||||
|  | #define CPP_CAM_HCT_H_ | ||||||
|  | 
 | ||||||
|  | #include "cpp/utils/utils.h" | ||||||
|  | 
 | ||||||
|  | namespace material_color_utilities { | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * HCT: hue, chroma, and tone. | ||||||
|  |  * | ||||||
|  |  * A color system built using CAM16 hue and chroma, and L* (lightness) from | ||||||
|  |  * the L*a*b* color space, providing a perceptually accurate | ||||||
|  |  * color measurement system that can also accurately render what colors | ||||||
|  |  * will appear as in different lighting environments. | ||||||
|  |  * | ||||||
|  |  * Using L* creates a link between the color system, contrast, and thus | ||||||
|  |  * accessibility. Contrast ratio depends on relative luminance, or Y in the XYZ | ||||||
|  |  * color space. L*, or perceptual luminance can be calculated from Y. | ||||||
|  |  * | ||||||
|  |  * Unlike Y, L* is linear to human perception, allowing trivial creation of | ||||||
|  |  * accurate color tones. | ||||||
|  |  * | ||||||
|  |  * Unlike contrast ratio, measuring contrast in L* is linear, and simple to | ||||||
|  |  * calculate. A difference of 40 in HCT tone guarantees a contrast ratio >= 3.0, | ||||||
|  |  * and a difference of 50 guarantees a contrast ratio >= 4.5. | ||||||
|  |  */ | ||||||
|  | class Hct { | ||||||
|  |  public: | ||||||
|  |   /**
 | ||||||
|  |    * Creates an HCT color from hue, chroma, and tone. | ||||||
|  |    * | ||||||
|  |    * @param hue 0 <= hue < 360; invalid values are corrected. | ||||||
|  |    * @param chroma >= 0; the maximum value of chroma depends on the hue | ||||||
|  |    * and tone. May be lower than the requested chroma. | ||||||
|  |    * @param tone 0 <= tone <= 100; invalid values are corrected. | ||||||
|  |    * @return HCT representation of a color in default viewing conditions. | ||||||
|  |    */ | ||||||
|  |   Hct(double hue, double chroma, double tone); | ||||||
|  | 
 | ||||||
|  |   /**
 | ||||||
|  |    * Creates an HCT color from a color. | ||||||
|  |    * | ||||||
|  |    * @param argb ARGB representation of a color. | ||||||
|  |    * @return HCT representation of a color in default viewing conditions | ||||||
|  |    */ | ||||||
|  |   explicit Hct(Argb argb); | ||||||
|  | 
 | ||||||
|  |   /**
 | ||||||
|  |    * Returns the hue of the color. | ||||||
|  |    * | ||||||
|  |    * @return hue of the color, in degrees. | ||||||
|  |    */ | ||||||
|  |   double get_hue() const; | ||||||
|  | 
 | ||||||
|  |   /**
 | ||||||
|  |    * Returns the chroma of the color. | ||||||
|  |    * | ||||||
|  |    * @return chroma of the color. | ||||||
|  |    */ | ||||||
|  |   double get_chroma() const; | ||||||
|  | 
 | ||||||
|  |   /**
 | ||||||
|  |    * Returns the tone of the color. | ||||||
|  |    * | ||||||
|  |    * @return tone of the color, satisfying 0 <= tone <= 100. | ||||||
|  |    */ | ||||||
|  |   double get_tone() const; | ||||||
|  | 
 | ||||||
|  |   /**
 | ||||||
|  |    * Returns the color in ARGB format. | ||||||
|  |    * | ||||||
|  |    * @return an integer, representing the color in ARGB format. | ||||||
|  |    */ | ||||||
|  |   Argb ToInt() const; | ||||||
|  | 
 | ||||||
|  |   /**
 | ||||||
|  |    * Sets the hue of this color. Chroma may decrease because chroma has a | ||||||
|  |    * different maximum for any given hue and tone. | ||||||
|  |    * | ||||||
|  |    * @param new_hue 0 <= new_hue < 360; invalid values are corrected. | ||||||
|  |    */ | ||||||
|  |   void set_hue(double new_hue); | ||||||
|  | 
 | ||||||
|  |   /**
 | ||||||
|  |    * Sets the chroma of this color. Chroma may decrease because chroma has a | ||||||
|  |    * different maximum for any given hue and tone. | ||||||
|  |    * | ||||||
|  |    * @param new_chroma 0 <= new_chroma < ? | ||||||
|  |    */ | ||||||
|  |   void set_chroma(double new_chroma); | ||||||
|  | 
 | ||||||
|  |   /**
 | ||||||
|  |    * Sets the tone of this color. Chroma may decrease because chroma has a | ||||||
|  |    * different maximum for any given hue and tone. | ||||||
|  |    * | ||||||
|  |    * @param new_tone 0 <= new_tone <= 100; invalid valids are corrected. | ||||||
|  |    */ | ||||||
|  |   void set_tone(double new_tone); | ||||||
|  | 
 | ||||||
|  |   /**
 | ||||||
|  |    * For using HCT as a key in a ordered map. | ||||||
|  |    */ | ||||||
|  |   bool operator<(const Hct& a) const { return hue_ < a.hue_; } | ||||||
|  | 
 | ||||||
|  |  private: | ||||||
|  |   /**
 | ||||||
|  |    * Sets the Hct object to represent an sRGB color. | ||||||
|  |    * | ||||||
|  |    * @param argb the new color as an integer in ARGB format. | ||||||
|  |    */ | ||||||
|  |   void SetInternalState(Argb argb); | ||||||
|  | 
 | ||||||
|  |   double hue_ = 0.0; | ||||||
|  |   double chroma_ = 0.0; | ||||||
|  |   double tone_ = 0.0; | ||||||
|  |   Argb argb_ = 0; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | }  // namespace material_color_utilities
 | ||||||
|  | 
 | ||||||
|  | #endif  // CPP_CAM_HCT_H_
 | ||||||
							
								
								
									
										526
									
								
								src/material-colors/cpp/cam/hct_solver.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										526
									
								
								src/material-colors/cpp/cam/hct_solver.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,526 @@ | ||||||
|  | /*
 | ||||||
|  |  * Copyright 2022 Google LLC | ||||||
|  |  * | ||||||
|  |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * | ||||||
|  |  *      http://www.apache.org/licenses/LICENSE-2.0
 | ||||||
|  |  * | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | #include "cpp/cam/hct_solver.h" | ||||||
|  | 
 | ||||||
|  | #include <math.h> | ||||||
|  | 
 | ||||||
|  | #include "cpp/cam/viewing_conditions.h" | ||||||
|  | #include "cpp/utils/utils.h" | ||||||
|  | 
 | ||||||
|  | namespace material_color_utilities { | ||||||
|  | 
 | ||||||
|  | constexpr double kScaledDiscountFromLinrgb[3][3] = { | ||||||
|  |     { | ||||||
|  |         0.001200833568784504, | ||||||
|  |         0.002389694492170889, | ||||||
|  |         0.0002795742885861124, | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |         0.0005891086651375999, | ||||||
|  |         0.0029785502573438758, | ||||||
|  |         0.0003270666104008398, | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |         0.00010146692491640572, | ||||||
|  |         0.0005364214359186694, | ||||||
|  |         0.0032979401770712076, | ||||||
|  |     }, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | constexpr double kLinrgbFromScaledDiscount[3][3] = { | ||||||
|  |     { | ||||||
|  |         1373.2198709594231, | ||||||
|  |         -1100.4251190754821, | ||||||
|  |         -7.278681089101213, | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |         -271.815969077903, | ||||||
|  |         559.6580465940733, | ||||||
|  |         -32.46047482791194, | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |         1.9622899599665666, | ||||||
|  |         -57.173814538844006, | ||||||
|  |         308.7233197812385, | ||||||
|  |     }, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | constexpr double kYFromLinrgb[3] = {0.2126, 0.7152, 0.0722}; | ||||||
|  | 
 | ||||||
|  | constexpr double kCriticalPlanes[255] = { | ||||||
|  |     0.015176349177441876, 0.045529047532325624, 0.07588174588720938, | ||||||
|  |     0.10623444424209313,  0.13658714259697685,  0.16693984095186062, | ||||||
|  |     0.19729253930674434,  0.2276452376616281,   0.2579979360165119, | ||||||
|  |     0.28835063437139563,  0.3188300904430532,   0.350925934958123, | ||||||
|  |     0.3848314933096426,   0.42057480301049466,  0.458183274052838, | ||||||
|  |     0.4976837250274023,   0.5391024159806381,   0.5824650784040898, | ||||||
|  |     0.6277969426914107,   0.6751227633498623,   0.7244668422128921, | ||||||
|  |     0.775853049866786,    0.829304845476233,    0.8848452951698498, | ||||||
|  |     0.942497089126609,    1.0022825574869039,   1.0642236851973577, | ||||||
|  |     1.1283421258858297,   1.1946592148522128,   1.2631959812511864, | ||||||
|  |     1.3339731595349034,   1.407011200216447,    1.4823302800086415, | ||||||
|  |     1.5599503113873272,   1.6398909516233677,   1.7221716113234105, | ||||||
|  |     1.8068114625156377,   1.8938294463134073,   1.9832442801866852, | ||||||
|  |     2.075074464868551,    2.1693382909216234,   2.2660538449872063, | ||||||
|  |     2.36523901573795,     2.4669114995532007,   2.5710888059345764, | ||||||
|  |     2.6777882626779785,   2.7870270208169257,   2.898822059350997, | ||||||
|  |     3.0131901897720907,   3.1301480604002863,   3.2497121605402226, | ||||||
|  |     3.3718988244681087,   3.4967242352587946,   3.624204428461639, | ||||||
|  |     3.754355295633311,    3.887192587735158,    4.022731918402185, | ||||||
|  |     4.160988767090289,    4.301978482107941,    4.445716283538092, | ||||||
|  |     4.592217266055746,    4.741496401646282,    4.893568542229298, | ||||||
|  |     5.048448422192488,    5.20615066083972,     5.3666897647573375, | ||||||
|  |     5.5300801301023865,   5.696336044816294,    5.865471690767354, | ||||||
|  |     6.037501145825082,    6.212438385869475,    6.390297286737924, | ||||||
|  |     6.571091626112461,    6.7548350853498045,   6.941541251256611, | ||||||
|  |     7.131223617812143,    7.323895587840543,    7.5195704746346665, | ||||||
|  |     7.7182615035334345,   7.919981813454504,    8.124744458384042, | ||||||
|  |     8.332562408825165,    8.543448553206703,    8.757415699253682, | ||||||
|  |     8.974476575321063,    9.194643831691977,    9.417930041841839, | ||||||
|  |     9.644347703669503,    9.873909240696694,    10.106627003236781, | ||||||
|  |     10.342513269534024,   10.58158024687427,    10.8238400726681, | ||||||
|  |     11.069304815507364,   11.317986476196008,   11.569896988756009, | ||||||
|  |     11.825048221409341,   12.083451977536606,   12.345119996613247, | ||||||
|  |     12.610063955123938,   12.878295467455942,   13.149826086772048, | ||||||
|  |     13.42466730586372,    13.702830557985108,   13.984327217668513, | ||||||
|  |     14.269168601521828,   14.55736596900856,    14.848930523210871, | ||||||
|  |     15.143873411576273,   15.44220572664832,    15.743938506781891, | ||||||
|  |     16.04908273684337,    16.35764934889634,    16.66964922287304, | ||||||
|  |     16.985093187232053,   17.30399201960269,    17.62635644741625, | ||||||
|  |     17.95219714852476,    18.281524751807332,   18.614349837764564, | ||||||
|  |     18.95068293910138,    19.290534541298456,   19.633915083172692, | ||||||
|  |     19.98083495742689,    20.331304511189067,   20.685334046541502, | ||||||
|  |     21.042933821039977,   21.404114048223256,   21.76888489811322, | ||||||
|  |     22.137256497705877,   22.50923893145328,    22.884842241736916, | ||||||
|  |     23.264076429332462,   23.6469514538663,     24.033477234264016, | ||||||
|  |     24.42366364919083,    24.817520537484558,   25.21505769858089, | ||||||
|  |     25.61628489293138,    26.021211842414342,   26.429848230738664, | ||||||
|  |     26.842203703840827,   27.258287870275353,   27.678110301598522, | ||||||
|  |     28.10168053274597,    28.529008062403893,   28.96010235337422, | ||||||
|  |     29.39497283293396,    29.83362889318845,    30.276079891419332, | ||||||
|  |     30.722335150426627,   31.172403958865512,   31.62629557157785, | ||||||
|  |     32.08401920991837,    32.54558406207592,    33.010999283389665, | ||||||
|  |     33.4802739966603,     33.953417292456834,   34.430438229418264, | ||||||
|  |     34.911345834551085,   35.39614910352207,    35.88485700094671, | ||||||
|  |     36.37747846067349,    36.87402238606382,    37.37449765026789, | ||||||
|  |     37.87891309649659,    38.38727753828926,    38.89959975977785, | ||||||
|  |     39.41588851594697,    39.93615253289054,    40.460400508064545, | ||||||
|  |     40.98864111053629,    41.520882981230194,   42.05713473317016, | ||||||
|  |     42.597404951718396,   43.141702194811224,   43.6900349931913, | ||||||
|  |     44.24241185063697,    44.798841244188324,   45.35933162437017, | ||||||
|  |     45.92389141541209,    46.49252901546552,    47.065252796817916, | ||||||
|  |     47.64207110610409,    48.22299226451468,    48.808024568002054, | ||||||
|  |     49.3971762874833,     49.9904556690408,     50.587870934119984, | ||||||
|  |     51.189430279724725,   51.79514187861014,    52.40501387947288, | ||||||
|  |     53.0190544071392,     53.637271562750364,   54.259673423945976, | ||||||
|  |     54.88626804504493,    55.517063457223934,   56.15206766869424, | ||||||
|  |     56.79128866487574,    57.43473440856916,    58.08241284012621, | ||||||
|  |     58.734331877617365,   59.39049941699807,    60.05092333227251, | ||||||
|  |     60.715611475655585,   61.38457167773311,    62.057811747619894, | ||||||
|  |     62.7353394731159,     63.417162620860914,   64.10328893648692, | ||||||
|  |     64.79372614476921,    65.48848194977529,    66.18756403501224, | ||||||
|  |     66.89098006357258,    67.59873767827808,    68.31084450182222, | ||||||
|  |     69.02730813691093,    69.74813616640164,    70.47333615344107, | ||||||
|  |     71.20291564160104,    71.93688215501312,    72.67524319850172, | ||||||
|  |     73.41800625771542,    74.16517879925733,    74.9167682708136, | ||||||
|  |     75.67278210128072,    76.43322770089146,    77.1981124613393, | ||||||
|  |     77.96744375590167,    78.74122893956174,    79.51947534912904, | ||||||
|  |     80.30219030335869,    81.08938110306934,    81.88105503125999, | ||||||
|  |     82.67721935322541,    83.4778813166706,     84.28304815182372, | ||||||
|  |     85.09272707154808,    85.90692527145302,    86.72564993000343, | ||||||
|  |     87.54890820862819,    88.3767072518277,     89.2090541872801, | ||||||
|  |     90.04595612594655,    90.88742016217518,    91.73345337380438, | ||||||
|  |     92.58406282226491,    93.43925555268066,    94.29903859396902, | ||||||
|  |     95.16341895893969,    96.03240364439274,    96.9059996312159, | ||||||
|  |     97.78421388448044,    98.6670533535366,     99.55452497210776, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Sanitizes a small enough angle in radians. | ||||||
|  |  * | ||||||
|  |  * @param angle An angle in radians; must not deviate too much from 0. | ||||||
|  |  * @return A coterminal angle between 0 and 2pi. | ||||||
|  |  */ | ||||||
|  | double SanitizeRadians(double angle) { return fmod(angle + kPi * 8, kPi * 2); } | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Delinearizes an RGB component, returning a floating-point number. | ||||||
|  |  * | ||||||
|  |  * @param rgb_component 0.0 <= rgb_component <= 100.0, represents linear R/G/B | ||||||
|  |  * channel | ||||||
|  |  * @return 0.0 <= output <= 255.0, color channel converted to regular RGB space | ||||||
|  |  */ | ||||||
|  | double TrueDelinearized(double rgb_component) { | ||||||
|  |   double normalized = rgb_component / 100.0; | ||||||
|  |   double delinearized = 0.0; | ||||||
|  |   if (normalized <= 0.0031308) { | ||||||
|  |     delinearized = normalized * 12.92; | ||||||
|  |   } else { | ||||||
|  |     delinearized = 1.055 * pow(normalized, 1.0 / 2.4) - 0.055; | ||||||
|  |   } | ||||||
|  |   return delinearized * 255.0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | double ChromaticAdaptation(double component) { | ||||||
|  |   double af = pow(abs(component), 0.42); | ||||||
|  |   return Signum(component) * 400.0 * af / (af + 27.13); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Returns the hue of a linear RGB color in CAM16. | ||||||
|  |  * | ||||||
|  |  * @param linrgb The linear RGB coordinates of a color. | ||||||
|  |  * @return The hue of the color in CAM16, in radians. | ||||||
|  |  */ | ||||||
|  | double HueOf(Vec3 linrgb) { | ||||||
|  |   Vec3 scaledDiscount = MatrixMultiply(linrgb, kScaledDiscountFromLinrgb); | ||||||
|  |   double r_a = ChromaticAdaptation(scaledDiscount.a); | ||||||
|  |   double g_a = ChromaticAdaptation(scaledDiscount.b); | ||||||
|  |   double b_a = ChromaticAdaptation(scaledDiscount.c); | ||||||
|  |   // redness-greenness
 | ||||||
|  |   double a = (11.0 * r_a + -12.0 * g_a + b_a) / 11.0; | ||||||
|  |   // yellowness-blueness
 | ||||||
|  |   double b = (r_a + g_a - 2.0 * b_a) / 9.0; | ||||||
|  |   return atan2(b, a); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool AreInCyclicOrder(double a, double b, double c) { | ||||||
|  |   double delta_a_b = SanitizeRadians(b - a); | ||||||
|  |   double delta_a_c = SanitizeRadians(c - a); | ||||||
|  |   return delta_a_b < delta_a_c; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Solves the lerp equation. | ||||||
|  |  * | ||||||
|  |  * @param source The starting number. | ||||||
|  |  * @param mid The number in the middle. | ||||||
|  |  * @param target The ending number. | ||||||
|  |  * @return A number t such that lerp(source, target, t) = mid. | ||||||
|  |  */ | ||||||
|  | double Intercept(double source, double mid, double target) { | ||||||
|  |   return (mid - source) / (target - source); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | Vec3 LerpPoint(Vec3 source, double t, Vec3 target) { | ||||||
|  |   return (Vec3){ | ||||||
|  |       source.a + (target.a - source.a) * t, | ||||||
|  |       source.b + (target.b - source.b) * t, | ||||||
|  |       source.c + (target.c - source.c) * t, | ||||||
|  |   }; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | double GetAxis(Vec3 vector, int axis) { | ||||||
|  |   switch (axis) { | ||||||
|  |     case 0: | ||||||
|  |       return vector.a; | ||||||
|  |     case 1: | ||||||
|  |       return vector.b; | ||||||
|  |     case 2: | ||||||
|  |       return vector.c; | ||||||
|  |     default: | ||||||
|  |       return -1.0; | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Intersects a segment with a plane. | ||||||
|  |  * | ||||||
|  |  * @param source The coordinates of point A. | ||||||
|  |  * @param coordinate The R-, G-, or B-coordinate of the plane. | ||||||
|  |  * @param target The coordinates of point B. | ||||||
|  |  * @param axis The axis the plane is perpendicular with. (0: R, 1: G, 2: B) | ||||||
|  |  * @return The intersection point of the segment AB with the plane R=coordinate, | ||||||
|  |  * G=coordinate, or B=coordinate | ||||||
|  |  */ | ||||||
|  | Vec3 SetCoordinate(Vec3 source, double coordinate, Vec3 target, int axis) { | ||||||
|  |   double t = | ||||||
|  |       Intercept(GetAxis(source, axis), coordinate, GetAxis(target, axis)); | ||||||
|  |   return LerpPoint(source, t, target); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool IsBounded(double x) { return 0.0 <= x && x <= 100.0; } | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Returns the nth possible vertex of the polygonal intersection. | ||||||
|  |  * | ||||||
|  |  * @param y The Y value of the plane. | ||||||
|  |  * @param n The zero-based index of the point. 0 <= n <= 11. | ||||||
|  |  * @return The nth possible vertex of the polygonal intersection of the y plane | ||||||
|  |  * and the RGB cube, in linear RGB coordinates, if it exists. If this possible | ||||||
|  |  * vertex lies outside of the cube, | ||||||
|  |  *     [-1.0, -1.0, -1.0] is returned. | ||||||
|  |  */ | ||||||
|  | Vec3 NthVertex(double y, int n) { | ||||||
|  |   double k_r = kYFromLinrgb[0]; | ||||||
|  |   double k_g = kYFromLinrgb[1]; | ||||||
|  |   double k_b = kYFromLinrgb[2]; | ||||||
|  |   double coord_a = n % 4 <= 1 ? 0.0 : 100.0; | ||||||
|  |   double coord_b = n % 2 == 0 ? 0.0 : 100.0; | ||||||
|  |   if (n < 4) { | ||||||
|  |     double g = coord_a; | ||||||
|  |     double b = coord_b; | ||||||
|  |     double r = (y - g * k_g - b * k_b) / k_r; | ||||||
|  |     if (IsBounded(r)) { | ||||||
|  |       return (Vec3){r, g, b}; | ||||||
|  |     } else { | ||||||
|  |       return (Vec3){-1.0, -1.0, -1.0}; | ||||||
|  |     } | ||||||
|  |   } else if (n < 8) { | ||||||
|  |     double b = coord_a; | ||||||
|  |     double r = coord_b; | ||||||
|  |     double g = (y - r * k_r - b * k_b) / k_g; | ||||||
|  |     if (IsBounded(g)) { | ||||||
|  |       return (Vec3){r, g, b}; | ||||||
|  |     } else { | ||||||
|  |       return (Vec3){-1.0, -1.0, -1.0}; | ||||||
|  |     } | ||||||
|  |   } else { | ||||||
|  |     double r = coord_a; | ||||||
|  |     double g = coord_b; | ||||||
|  |     double b = (y - r * k_r - g * k_g) / k_b; | ||||||
|  |     if (IsBounded(b)) { | ||||||
|  |       return (Vec3){r, g, b}; | ||||||
|  |     } else { | ||||||
|  |       return (Vec3){-1.0, -1.0, -1.0}; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Finds the segment containing the desired color. | ||||||
|  |  * | ||||||
|  |  * @param y The Y value of the color. | ||||||
|  |  * @param target_hue The hue of the color. | ||||||
|  |  * @return A list of two sets of linear RGB coordinates, each corresponding to | ||||||
|  |  * an endpoint of the segment containing the desired color. | ||||||
|  |  */ | ||||||
|  | void BisectToSegment(double y, double target_hue, Vec3 out[2]) { | ||||||
|  |   Vec3 left = (Vec3){-1.0, -1.0, -1.0}; | ||||||
|  |   Vec3 right = left; | ||||||
|  |   double left_hue = 0.0; | ||||||
|  |   double right_hue = 0.0; | ||||||
|  |   bool initialized = false; | ||||||
|  |   bool uncut = true; | ||||||
|  |   for (int n = 0; n < 12; n++) { | ||||||
|  |     Vec3 mid = NthVertex(y, n); | ||||||
|  |     if (mid.a < 0) { | ||||||
|  |       continue; | ||||||
|  |     } | ||||||
|  |     double mid_hue = HueOf(mid); | ||||||
|  |     if (!initialized) { | ||||||
|  |       left = mid; | ||||||
|  |       right = mid; | ||||||
|  |       left_hue = mid_hue; | ||||||
|  |       right_hue = mid_hue; | ||||||
|  |       initialized = true; | ||||||
|  |       continue; | ||||||
|  |     } | ||||||
|  |     if (uncut || AreInCyclicOrder(left_hue, mid_hue, right_hue)) { | ||||||
|  |       uncut = false; | ||||||
|  |       if (AreInCyclicOrder(left_hue, target_hue, mid_hue)) { | ||||||
|  |         right = mid; | ||||||
|  |         right_hue = mid_hue; | ||||||
|  |       } else { | ||||||
|  |         left = mid; | ||||||
|  |         left_hue = mid_hue; | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |   out[0] = left; | ||||||
|  |   out[1] = right; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | Vec3 Midpoint(Vec3 a, Vec3 b) { | ||||||
|  |   return (Vec3){ | ||||||
|  |       (a.a + b.a) / 2, | ||||||
|  |       (a.b + b.b) / 2, | ||||||
|  |       (a.c + b.c) / 2, | ||||||
|  |   }; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | int CriticalPlaneBelow(double x) { return (int)floor(x - 0.5); } | ||||||
|  | 
 | ||||||
|  | int CriticalPlaneAbove(double x) { return (int)ceil(x - 0.5); } | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Finds a color with the given Y and hue on the boundary of the cube. | ||||||
|  |  * | ||||||
|  |  * @param y The Y value of the color. | ||||||
|  |  * @param target_hue The hue of the color. | ||||||
|  |  * @return The desired color, in linear RGB coordinates. | ||||||
|  |  */ | ||||||
|  | Vec3 BisectToLimit(double y, double target_hue) { | ||||||
|  |   Vec3 segment[2]; | ||||||
|  |   BisectToSegment(y, target_hue, segment); | ||||||
|  |   Vec3 left = segment[0]; | ||||||
|  |   double left_hue = HueOf(left); | ||||||
|  |   Vec3 right = segment[1]; | ||||||
|  |   for (int axis = 0; axis < 3; axis++) { | ||||||
|  |     if (GetAxis(left, axis) != GetAxis(right, axis)) { | ||||||
|  |       int l_plane = -1; | ||||||
|  |       int r_plane = 255; | ||||||
|  |       if (GetAxis(left, axis) < GetAxis(right, axis)) { | ||||||
|  |         l_plane = CriticalPlaneBelow(TrueDelinearized(GetAxis(left, axis))); | ||||||
|  |         r_plane = CriticalPlaneAbove(TrueDelinearized(GetAxis(right, axis))); | ||||||
|  |       } else { | ||||||
|  |         l_plane = CriticalPlaneAbove(TrueDelinearized(GetAxis(left, axis))); | ||||||
|  |         r_plane = CriticalPlaneBelow(TrueDelinearized(GetAxis(right, axis))); | ||||||
|  |       } | ||||||
|  |       for (int i = 0; i < 8; i++) { | ||||||
|  |         if (abs(r_plane - l_plane) <= 1) { | ||||||
|  |           break; | ||||||
|  |         } else { | ||||||
|  |           int m_plane = (int)floor((l_plane + r_plane) / 2.0); | ||||||
|  |           double mid_plane_coordinate = kCriticalPlanes[m_plane]; | ||||||
|  |           Vec3 mid = SetCoordinate(left, mid_plane_coordinate, right, axis); | ||||||
|  |           double mid_hue = HueOf(mid); | ||||||
|  |           if (AreInCyclicOrder(left_hue, target_hue, mid_hue)) { | ||||||
|  |             right = mid; | ||||||
|  |             r_plane = m_plane; | ||||||
|  |           } else { | ||||||
|  |             left = mid; | ||||||
|  |             left_hue = mid_hue; | ||||||
|  |             l_plane = m_plane; | ||||||
|  |           } | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |   return Midpoint(left, right); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | double InverseChromaticAdaptation(double adapted) { | ||||||
|  |   double adapted_abs = abs(adapted); | ||||||
|  |   double base = fmax(0, 27.13 * adapted_abs / (400.0 - adapted_abs)); | ||||||
|  |   return Signum(adapted) * pow(base, 1.0 / 0.42); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Finds a color with the given hue, chroma, and Y. | ||||||
|  |  * | ||||||
|  |  * @param hue_radians The desired hue in radians. | ||||||
|  |  * @param chroma The desired chroma. | ||||||
|  |  * @param y The desired Y. | ||||||
|  |  * @return The desired color as a hexadecimal integer, if found; 0 otherwise. | ||||||
|  |  */ | ||||||
|  | Argb FindResultByJ(double hue_radians, double chroma, double y) { | ||||||
|  |   // Initial estimate of j.
 | ||||||
|  |   double j = sqrt(y) * 11.0; | ||||||
|  |   // ===========================================================
 | ||||||
|  |   // Operations inlined from Cam16 to avoid repeated calculation
 | ||||||
|  |   // ===========================================================
 | ||||||
|  |   ViewingConditions viewing_conditions = kDefaultViewingConditions; | ||||||
|  |   double t_inner_coeff = | ||||||
|  |       1 / | ||||||
|  |       pow(1.64 - pow(0.29, viewing_conditions.background_y_to_white_point_y), | ||||||
|  |           0.73); | ||||||
|  |   double e_hue = 0.25 * (cos(hue_radians + 2.0) + 3.8); | ||||||
|  |   double p1 = e_hue * (50000.0 / 13.0) * viewing_conditions.n_c * | ||||||
|  |               viewing_conditions.ncb; | ||||||
|  |   double h_sin = sin(hue_radians); | ||||||
|  |   double h_cos = cos(hue_radians); | ||||||
|  |   for (int iteration_round = 0; iteration_round < 5; iteration_round++) { | ||||||
|  |     // ===========================================================
 | ||||||
|  |     // Operations inlined from Cam16 to avoid repeated calculation
 | ||||||
|  |     // ===========================================================
 | ||||||
|  |     double j_normalized = j / 100.0; | ||||||
|  |     double alpha = | ||||||
|  |         chroma == 0.0 || j == 0.0 ? 0.0 : chroma / sqrt(j_normalized); | ||||||
|  |     double t = pow(alpha * t_inner_coeff, 1.0 / 0.9); | ||||||
|  |     double ac = | ||||||
|  |         viewing_conditions.aw * | ||||||
|  |         pow(j_normalized, 1.0 / viewing_conditions.c / viewing_conditions.z); | ||||||
|  |     double p2 = ac / viewing_conditions.nbb; | ||||||
|  |     double gamma = 23.0 * (p2 + 0.305) * t / | ||||||
|  |                    (23.0 * p1 + 11 * t * h_cos + 108.0 * t * h_sin); | ||||||
|  |     double a = gamma * h_cos; | ||||||
|  |     double b = gamma * h_sin; | ||||||
|  |     double r_a = (460.0 * p2 + 451.0 * a + 288.0 * b) / 1403.0; | ||||||
|  |     double g_a = (460.0 * p2 - 891.0 * a - 261.0 * b) / 1403.0; | ||||||
|  |     double b_a = (460.0 * p2 - 220.0 * a - 6300.0 * b) / 1403.0; | ||||||
|  |     double r_c_scaled = InverseChromaticAdaptation(r_a); | ||||||
|  |     double g_c_scaled = InverseChromaticAdaptation(g_a); | ||||||
|  |     double b_c_scaled = InverseChromaticAdaptation(b_a); | ||||||
|  |     Vec3 scaled = (Vec3){r_c_scaled, g_c_scaled, b_c_scaled}; | ||||||
|  |     Vec3 linrgb = MatrixMultiply(scaled, kLinrgbFromScaledDiscount); | ||||||
|  |     // ===========================================================
 | ||||||
|  |     // Operations inlined from Cam16 to avoid repeated calculation
 | ||||||
|  |     // ===========================================================
 | ||||||
|  |     if (linrgb.a < 0 || linrgb.b < 0 || linrgb.c < 0) { | ||||||
|  |       return 0; | ||||||
|  |     } | ||||||
|  |     double k_r = kYFromLinrgb[0]; | ||||||
|  |     double k_g = kYFromLinrgb[1]; | ||||||
|  |     double k_b = kYFromLinrgb[2]; | ||||||
|  |     double fnj = k_r * linrgb.a + k_g * linrgb.b + k_b * linrgb.c; | ||||||
|  |     if (fnj <= 0) { | ||||||
|  |       return 0; | ||||||
|  |     } | ||||||
|  |     if (iteration_round == 4 || abs(fnj - y) < 0.002) { | ||||||
|  |       if (linrgb.a > 100.01 || linrgb.b > 100.01 || linrgb.c > 100.01) { | ||||||
|  |         return 0; | ||||||
|  |       } | ||||||
|  |       return ArgbFromLinrgb(linrgb); | ||||||
|  |     } | ||||||
|  |     // Iterates with Newton method,
 | ||||||
|  |     // Using 2 * fn(j) / j as the approximation of fn'(j)
 | ||||||
|  |     j = j - (fnj - y) * j / (2 * fnj); | ||||||
|  |   } | ||||||
|  |   return 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Finds an sRGB color with the given hue, chroma, and L*, if possible. | ||||||
|  |  * | ||||||
|  |  * @param hue_degrees The desired hue, in degrees. | ||||||
|  |  * @param chroma The desired chroma. | ||||||
|  |  * @param lstar The desired L*. | ||||||
|  |  * @return A hexadecimal representing the sRGB color. The color has sufficiently | ||||||
|  |  * close hue, chroma, and L* to the desired values, if possible; otherwise, the | ||||||
|  |  * hue and L* will be sufficiently close, and chroma will be maximized. | ||||||
|  |  */ | ||||||
|  | Argb SolveToInt(double hue_degrees, double chroma, double lstar) { | ||||||
|  |   if (chroma < 0.0001 || lstar < 0.0001 || lstar > 99.9999) { | ||||||
|  |     return IntFromLstar(lstar); | ||||||
|  |   } | ||||||
|  |   hue_degrees = SanitizeDegreesDouble(hue_degrees); | ||||||
|  |   double hue_radians = hue_degrees / 180 * kPi; | ||||||
|  |   double y = YFromLstar(lstar); | ||||||
|  |   Argb exact_answer = FindResultByJ(hue_radians, chroma, y); | ||||||
|  |   if (exact_answer != 0) { | ||||||
|  |     return exact_answer; | ||||||
|  |   } | ||||||
|  |   Vec3 linrgb = BisectToLimit(y, hue_radians); | ||||||
|  |   return ArgbFromLinrgb(linrgb); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Finds an sRGB color with the given hue, chroma, and L*, if possible. | ||||||
|  |  * | ||||||
|  |  * @param hue_degrees The desired hue, in degrees. | ||||||
|  |  * @param chroma The desired chroma. | ||||||
|  |  * @param lstar The desired L*. | ||||||
|  |  * @return An CAM16 object representing the sRGB color. The color has | ||||||
|  |  * sufficiently close hue, chroma, and L* to the desired values, if possible; | ||||||
|  |  * otherwise, the hue and L* will be sufficiently close, and chroma will be | ||||||
|  |  * maximized. | ||||||
|  |  */ | ||||||
|  | Cam SolveToCam(double hue_degrees, double chroma, double lstar) { | ||||||
|  |   return CamFromInt(SolveToInt(hue_degrees, chroma, lstar)); | ||||||
|  | } | ||||||
|  | }  // namespace material_color_utilities
 | ||||||
							
								
								
									
										28
									
								
								src/material-colors/cpp/cam/hct_solver.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								src/material-colors/cpp/cam/hct_solver.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,28 @@ | ||||||
|  | /*
 | ||||||
|  |  * Copyright 2022 Google LLC | ||||||
|  |  * | ||||||
|  |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * | ||||||
|  |  *      http://www.apache.org/licenses/LICENSE-2.0
 | ||||||
|  |  * | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | #ifndef CPP_CAM_HCT_SOLVER_H_ | ||||||
|  | #define CPP_CAM_HCT_SOLVER_H_ | ||||||
|  | 
 | ||||||
|  | #include "cpp/cam/cam.h" | ||||||
|  | 
 | ||||||
|  | namespace material_color_utilities { | ||||||
|  | 
 | ||||||
|  | Argb SolveToInt(double hue_degrees, double chroma, double lstar); | ||||||
|  | Cam SolveToCam(double hue_degrees, double chroma, double lstar); | ||||||
|  | 
 | ||||||
|  | }  // namespace material_color_utilities
 | ||||||
|  | #endif  // CPP_CAM_HCT_SOLVER_H_
 | ||||||
							
								
								
									
										74
									
								
								src/material-colors/cpp/cam/hct_solver_test.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										74
									
								
								src/material-colors/cpp/cam/hct_solver_test.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,74 @@ | ||||||
|  | /*
 | ||||||
|  |  * Copyright 2022 Google LLC | ||||||
|  |  * | ||||||
|  |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * | ||||||
|  |  *      http://www.apache.org/licenses/LICENSE-2.0
 | ||||||
|  |  * | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | #include "cpp/cam/hct_solver.h" | ||||||
|  | 
 | ||||||
|  | #include "testing/base/public/gmock.h" | ||||||
|  | #include "testing/base/public/gunit.h" | ||||||
|  | #include "cpp/cam/cam.h" | ||||||
|  | #include "cpp/utils/utils.h" | ||||||
|  | 
 | ||||||
|  | namespace material_color_utilities { | ||||||
|  | 
 | ||||||
|  | namespace { | ||||||
|  | using testing::Eq; | ||||||
|  | 
 | ||||||
|  | TEST(HctSolverTest, Red) { | ||||||
|  |   // Compute HCT
 | ||||||
|  |   Argb color = 0xFFFE0315; | ||||||
|  |   Cam cam = CamFromInt(color); | ||||||
|  |   double tone = LstarFromArgb(color); | ||||||
|  | 
 | ||||||
|  |   // Compute input
 | ||||||
|  |   Argb recovered = SolveToInt(cam.hue, cam.chroma, tone); | ||||||
|  |   EXPECT_THAT(recovered, Eq(color)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TEST(HctSolverTest, Green) { | ||||||
|  |   // Compute HCT
 | ||||||
|  |   Argb color = 0xFF15FE03; | ||||||
|  |   Cam cam = CamFromInt(color); | ||||||
|  |   double tone = LstarFromArgb(color); | ||||||
|  | 
 | ||||||
|  |   // Compute input
 | ||||||
|  |   Argb recovered = SolveToInt(cam.hue, cam.chroma, tone); | ||||||
|  |   EXPECT_THAT(recovered, Eq(color)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TEST(HctSolverTest, Blue) { | ||||||
|  |   // Compute HCT
 | ||||||
|  |   Argb color = 0xFF0315FE; | ||||||
|  |   Cam cam = CamFromInt(color); | ||||||
|  |   double tone = LstarFromArgb(color); | ||||||
|  | 
 | ||||||
|  |   // Compute input
 | ||||||
|  |   Argb recovered = SolveToInt(cam.hue, cam.chroma, tone); | ||||||
|  |   EXPECT_THAT(recovered, Eq(color)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TEST(HctSolverTest, Exhaustive) { | ||||||
|  |   for (int colorIndex = 0; colorIndex <= 0xFFFFFF; colorIndex++) { | ||||||
|  |     Argb color = 0xFF000000 | colorIndex; | ||||||
|  |     Cam cam = CamFromInt(color); | ||||||
|  |     double tone = LstarFromArgb(color); | ||||||
|  | 
 | ||||||
|  |     // Compute input
 | ||||||
|  |     Argb recovered = SolveToInt(cam.hue, cam.chroma, tone); | ||||||
|  |     EXPECT_THAT(recovered, Eq(color)); | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | }  // namespace
 | ||||||
|  | }  // namespace material_color_utilities
 | ||||||
							
								
								
									
										100
									
								
								src/material-colors/cpp/cam/hct_test.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										100
									
								
								src/material-colors/cpp/cam/hct_test.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,100 @@ | ||||||
|  | /*
 | ||||||
|  |  * Copyright 2022 Google LLC | ||||||
|  |  * | ||||||
|  |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * | ||||||
|  |  *      http://www.apache.org/licenses/LICENSE-2.0
 | ||||||
|  |  * | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | #include "cpp/cam/hct.h" | ||||||
|  | 
 | ||||||
|  | #include <tuple> | ||||||
|  | 
 | ||||||
|  | #include "testing/base/public/gmock.h" | ||||||
|  | #include "testing/base/public/gunit.h" | ||||||
|  | #include "cpp/cam/cam.h" | ||||||
|  | #include "cpp/utils/utils.h" | ||||||
|  | 
 | ||||||
|  | namespace material_color_utilities { | ||||||
|  | 
 | ||||||
|  | namespace { | ||||||
|  | using ::testing::Combine; | ||||||
|  | using ::testing::DoubleNear; | ||||||
|  | using ::testing::Eq; | ||||||
|  | using ::testing::Lt; | ||||||
|  | using ::testing::TestWithParam; | ||||||
|  | using ::testing::Values; | ||||||
|  | 
 | ||||||
|  | TEST(HctTest, LimitedToSRGB) { | ||||||
|  |   // Ensures that the HCT class can only represent sRGB colors.
 | ||||||
|  |   // An impossibly high chroma is used.
 | ||||||
|  |   Hct hct(/*hue=*/120.0, /*chroma=*/200.0, /*tone=*/50.0); | ||||||
|  |   Argb argb = hct.ToInt(); | ||||||
|  | 
 | ||||||
|  |   // The hue, chroma, and tone members of hct should actually
 | ||||||
|  |   // represent the sRGB color.
 | ||||||
|  |   EXPECT_THAT(CamFromInt(argb).hue, Eq(hct.get_hue())); | ||||||
|  |   EXPECT_THAT(CamFromInt(argb).chroma, Eq(hct.get_chroma())); | ||||||
|  |   EXPECT_THAT(LstarFromArgb(argb), Eq(hct.get_tone())); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TEST(HctTest, TruncatesColors) { | ||||||
|  |   // Ensures that HCT truncates colors.
 | ||||||
|  |   Hct hct(/*hue=*/120.0, /*chroma=*/60.0, /*tone=*/50.0); | ||||||
|  |   double chroma = hct.get_chroma(); | ||||||
|  |   EXPECT_THAT(chroma, Lt(60.0)); | ||||||
|  | 
 | ||||||
|  |   // The new chroma should be lower than the original.
 | ||||||
|  |   hct.set_tone(180.0); | ||||||
|  |   EXPECT_THAT(hct.get_chroma(), Lt(chroma)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool IsOnBoundary(int rgb_component) { | ||||||
|  |   return rgb_component == 0 || rgb_component == 255; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool ColorIsOnBoundary(int argb) { | ||||||
|  |   return IsOnBoundary(RedFromInt(argb)) || IsOnBoundary(GreenFromInt(argb)) || | ||||||
|  |          IsOnBoundary(BlueFromInt(argb)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | using HctTest = TestWithParam<std::tuple<int, int, int>>; | ||||||
|  | 
 | ||||||
|  | TEST_P(HctTest, Correctness) { | ||||||
|  |   std::tuple<int, int, int> hctTuple = GetParam(); | ||||||
|  |   int hue = std::get<0>(hctTuple); | ||||||
|  |   int chroma = std::get<1>(hctTuple); | ||||||
|  |   int tone = std::get<2>(hctTuple); | ||||||
|  | 
 | ||||||
|  |   Hct color(hue, chroma, tone); | ||||||
|  | 
 | ||||||
|  |   if (chroma > 0) { | ||||||
|  |     EXPECT_THAT(color.get_hue(), DoubleNear(hue, 4.0)); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   EXPECT_THAT(color.get_chroma(), Lt(chroma + 2.5)); | ||||||
|  | 
 | ||||||
|  |   if (color.get_chroma() < chroma - 2.5) { | ||||||
|  |     EXPECT_TRUE(ColorIsOnBoundary(color.ToInt())); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   EXPECT_THAT(color.get_tone(), DoubleNear(tone, 0.5)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | INSTANTIATE_TEST_SUITE_P( | ||||||
|  |     HctTests, HctTest, | ||||||
|  |     Combine(/*hues*/ Values(15, 45, 75, 105, 135, 165, 195, 225, 255, 285, 315, | ||||||
|  |                             345), | ||||||
|  |             /*chromas*/ Values(0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100), | ||||||
|  |             /*tones*/ Values(20, 30, 40, 50, 60, 70, 80))); | ||||||
|  | 
 | ||||||
|  | }  // namespace
 | ||||||
|  | }  // namespace material_color_utilities
 | ||||||
							
								
								
									
										118
									
								
								src/material-colors/cpp/cam/viewing_conditions.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										118
									
								
								src/material-colors/cpp/cam/viewing_conditions.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,118 @@ | ||||||
|  | /*
 | ||||||
|  |  * Copyright 2022 Google LLC | ||||||
|  |  * | ||||||
|  |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * | ||||||
|  |  *      http://www.apache.org/licenses/LICENSE-2.0
 | ||||||
|  |  * | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | #include "cpp/cam/viewing_conditions.h" | ||||||
|  | 
 | ||||||
|  | #include <math.h> | ||||||
|  | #include <stdio.h> | ||||||
|  | 
 | ||||||
|  | #include "cpp/utils/utils.h" | ||||||
|  | 
 | ||||||
|  | namespace material_color_utilities { | ||||||
|  | 
 | ||||||
|  | static double lerp(double start, double stop, double amount) { | ||||||
|  |   return (1.0 - amount) * start + amount * stop; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | ViewingConditions CreateViewingConditions(const double white_point[3], | ||||||
|  |                                           const double adapting_luminance, | ||||||
|  |                                           const double background_lstar, | ||||||
|  |                                           const double surround, | ||||||
|  |                                           const bool discounting_illuminant) { | ||||||
|  |   double background_lstar_corrected = | ||||||
|  |       (background_lstar < 30.0) ? 30.0 : background_lstar; | ||||||
|  |   double rgb_w[3] = { | ||||||
|  |       0.401288 * white_point[0] + 0.650173 * white_point[1] - | ||||||
|  |           0.051461 * white_point[2], | ||||||
|  |       -0.250268 * white_point[0] + 1.204414 * white_point[1] + | ||||||
|  |           0.045854 * white_point[2], | ||||||
|  |       -0.002079 * white_point[0] + 0.048952 * white_point[1] + | ||||||
|  |           0.953127 * white_point[2], | ||||||
|  |   }; | ||||||
|  |   double f = 0.8 + (surround / 10.0); | ||||||
|  |   double c = f >= 0.9 ? lerp(0.59, 0.69, (f - 0.9) * 10.0) | ||||||
|  |                       : lerp(0.525, 0.59, (f - 0.8) * 10.0); | ||||||
|  |   double d = discounting_illuminant | ||||||
|  |                  ? 1.0 | ||||||
|  |                  : f * (1.0 - ((1.0 / 3.6) * | ||||||
|  |                                exp((-adapting_luminance - 42.0) / 92.0))); | ||||||
|  |   d = d > 1.0 ? 1.0 : d < 0.0 ? 0.0 : d; | ||||||
|  |   double nc = f; | ||||||
|  |   double rgb_d[3] = {(d * (100.0 / rgb_w[0]) + 1.0 - d), | ||||||
|  |                      (d * (100.0 / rgb_w[1]) + 1.0 - d), | ||||||
|  |                      (d * (100.0 / rgb_w[2]) + 1.0 - d)}; | ||||||
|  | 
 | ||||||
|  |   double k = 1.0 / (5.0 * adapting_luminance + 1.0); | ||||||
|  |   double k4 = k * k * k * k; | ||||||
|  |   double k4f = 1.0 - k4; | ||||||
|  |   double fl = (k4 * adapting_luminance) + | ||||||
|  |               (0.1 * k4f * k4f * pow(5.0 * adapting_luminance, 1.0 / 3.0)); | ||||||
|  |   double fl_root = pow(fl, 0.25); | ||||||
|  |   double n = YFromLstar(background_lstar_corrected) / white_point[1]; | ||||||
|  |   double z = 1.48 + sqrt(n); | ||||||
|  |   double nbb = 0.725 / pow(n, 0.2); | ||||||
|  |   double ncb = nbb; | ||||||
|  |   double rgb_a_factors[3] = {pow(fl * rgb_d[0] * rgb_w[0] / 100.0, 0.42), | ||||||
|  |                              pow(fl * rgb_d[1] * rgb_w[1] / 100.0, 0.42), | ||||||
|  |                              pow(fl * rgb_d[2] * rgb_w[2] / 100.0, 0.42)}; | ||||||
|  |   double rgb_a[3] = { | ||||||
|  |       400.0 * rgb_a_factors[0] / (rgb_a_factors[0] + 27.13), | ||||||
|  |       400.0 * rgb_a_factors[1] / (rgb_a_factors[1] + 27.13), | ||||||
|  |       400.0 * rgb_a_factors[2] / (rgb_a_factors[2] + 27.13), | ||||||
|  |   }; | ||||||
|  |   double aw = (40.0 * rgb_a[0] + 20.0 * rgb_a[1] + rgb_a[2]) / 20.0 * nbb; | ||||||
|  |   ViewingConditions viewingConditions = { | ||||||
|  |       adapting_luminance, | ||||||
|  |       background_lstar_corrected, | ||||||
|  |       surround, | ||||||
|  |       discounting_illuminant, | ||||||
|  |       n, | ||||||
|  |       aw, | ||||||
|  |       nbb, | ||||||
|  |       ncb, | ||||||
|  |       c, | ||||||
|  |       nc, | ||||||
|  |       fl, | ||||||
|  |       fl_root, | ||||||
|  |       z, | ||||||
|  |       {white_point[0], white_point[1], white_point[2]}, | ||||||
|  |       {rgb_d[0], rgb_d[1], rgb_d[2]}, | ||||||
|  |   }; | ||||||
|  |   return viewingConditions; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | ViewingConditions DefaultWithBackgroundLstar(const double background_lstar) { | ||||||
|  |   return CreateViewingConditions(kWhitePointD65, | ||||||
|  |                                  (200.0 / kPi * YFromLstar(50.0) / 100.0), | ||||||
|  |                                  background_lstar, 2.0, 0); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void PrintDefaultFrame() { | ||||||
|  |   ViewingConditions frame = CreateViewingConditions( | ||||||
|  |       kWhitePointD65, (200.0 / kPi * YFromLstar(50.0) / 100.0), 50.0, 2.0, 0); | ||||||
|  |   printf( | ||||||
|  |       "(Frame){%0.9lf,\n %0.9lf,\n %0.9lf,\n %s\n, %0.9lf,\n " | ||||||
|  |       "%0.9lf,\n%0.9lf,\n%0.9lf,\n%0.9lf,\n%0.9lf,\n" | ||||||
|  |       "%0.9lf,\n%0.9lf,\n%0.9lf,\n%0.9lf,\n" | ||||||
|  |       "%0.9lf,\n%0.9lf\n};", | ||||||
|  |       frame.adapting_luminance, frame.background_lstar, frame.surround, | ||||||
|  |       frame.discounting_illuminant ? "true" : "false", | ||||||
|  |       frame.background_y_to_white_point_y, frame.aw, frame.nbb, frame.ncb, | ||||||
|  |       frame.c, frame.n_c, frame.fl, frame.fl_root, frame.z, frame.rgb_d[0], | ||||||
|  |       frame.rgb_d[1], frame.rgb_d[2]); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | }  // namespace material_color_utilities
 | ||||||
							
								
								
									
										68
									
								
								src/material-colors/cpp/cam/viewing_conditions.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										68
									
								
								src/material-colors/cpp/cam/viewing_conditions.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,68 @@ | ||||||
|  | /*
 | ||||||
|  |  * Copyright 2022 Google LLC | ||||||
|  |  * | ||||||
|  |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * | ||||||
|  |  *      http://www.apache.org/licenses/LICENSE-2.0
 | ||||||
|  |  * | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | #ifndef CPP_CAM_VIEWING_CONDITIONS_H_ | ||||||
|  | #define CPP_CAM_VIEWING_CONDITIONS_H_ | ||||||
|  | 
 | ||||||
|  | namespace material_color_utilities { | ||||||
|  | 
 | ||||||
|  | struct ViewingConditions { | ||||||
|  |   double adapting_luminance = 0.0; | ||||||
|  |   double background_lstar = 0.0; | ||||||
|  |   double surround = 0.0; | ||||||
|  |   bool discounting_illuminant = false; | ||||||
|  |   double background_y_to_white_point_y = 0.0; | ||||||
|  |   double aw = 0.0; | ||||||
|  |   double nbb = 0.0; | ||||||
|  |   double ncb = 0.0; | ||||||
|  |   double c = 0.0; | ||||||
|  |   double n_c = 0.0; | ||||||
|  |   double fl = 0.0; | ||||||
|  |   double fl_root = 0.0; | ||||||
|  |   double z = 0.0; | ||||||
|  | 
 | ||||||
|  |   double white_point[3] = {0.0, 0.0, 0.0}; | ||||||
|  |   double rgb_d[3] = {0.0, 0.0, 0.0}; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | ViewingConditions CreateViewingConditions(const double white_point[3], | ||||||
|  |                                           const double adapting_luminance, | ||||||
|  |                                           const double background_lstar, | ||||||
|  |                                           const double surround, | ||||||
|  |                                           const bool discounting_illuminant); | ||||||
|  | 
 | ||||||
|  | ViewingConditions DefaultWithBackgroundLstar(const double background_lstar); | ||||||
|  | 
 | ||||||
|  | static const ViewingConditions kDefaultViewingConditions = (ViewingConditions){ | ||||||
|  |     11.725676537, | ||||||
|  |     50.000000000, | ||||||
|  |     2.000000000, | ||||||
|  |     false, | ||||||
|  |     0.184186503, | ||||||
|  |     29.981000900, | ||||||
|  |     1.016919255, | ||||||
|  |     1.016919255, | ||||||
|  |     0.689999998, | ||||||
|  |     1.000000000, | ||||||
|  |     0.388481468, | ||||||
|  |     0.789482653, | ||||||
|  |     1.909169555, | ||||||
|  |     {95.047, 100.0, 108.883}, | ||||||
|  |     {1.021177769, 0.986307740, 0.933960497}, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | }  // namespace material_color_utilities
 | ||||||
|  | #endif  // CPP_CAM_VIEWING_CONDITIONS_H_
 | ||||||
							
								
								
									
										138
									
								
								src/material-colors/cpp/contrast/contrast.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										138
									
								
								src/material-colors/cpp/contrast/contrast.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,138 @@ | ||||||
|  | /*
 | ||||||
|  |  * Copyright 2023 Google LLC | ||||||
|  |  * | ||||||
|  |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * | ||||||
|  |  *      http://www.apache.org/licenses/LICENSE-2.0
 | ||||||
|  |  * | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | #include "cpp/contrast/contrast.h" | ||||||
|  | 
 | ||||||
|  | #include <algorithm> | ||||||
|  | #include <cmath> | ||||||
|  | 
 | ||||||
|  | #include "cpp/utils/utils.h" | ||||||
|  | 
 | ||||||
|  | namespace material_color_utilities { | ||||||
|  | // Given a color and a contrast ratio to reach, the luminance of a color that
 | ||||||
|  | // reaches that ratio with the color can be calculated. However, that luminance
 | ||||||
|  | // may not contrast as desired, i.e. the contrast ratio of the input color
 | ||||||
|  | // and the returned luminance may not reach the contrast ratio  asked for.
 | ||||||
|  | //
 | ||||||
|  | // When the desired contrast ratio and the result contrast ratio differ by
 | ||||||
|  | // more than this amount,  an error value should be returned, or the method
 | ||||||
|  | // should be documented as 'unsafe', meaning, it will return a valid luminance
 | ||||||
|  | // but that luminance may not meet the requested contrast ratio.
 | ||||||
|  | //
 | ||||||
|  | // 0.04 selected because it ensures the resulting ratio rounds to the
 | ||||||
|  | // same tenth.
 | ||||||
|  | constexpr double CONTRAST_RATIO_EPSILON = 0.04; | ||||||
|  | 
 | ||||||
|  | // Color spaces that measure luminance, such as Y in XYZ, L* in L*a*b*,
 | ||||||
|  | // or T in HCT, are known as  perceptual accurate color spaces.
 | ||||||
|  | //
 | ||||||
|  | // To be displayed, they must gamut map to a "display space", one that has
 | ||||||
|  | // a defined limit on the number of colors. Display spaces include sRGB,
 | ||||||
|  | // more commonly understood as RGB/HSL/HSV/HSB.
 | ||||||
|  | //
 | ||||||
|  | // Gamut mapping is undefined and not defined by the color space. Any
 | ||||||
|  | // gamut mapping algorithm must choose how to sacrifice accuracy in hue,
 | ||||||
|  | // saturation, and/or lightness.
 | ||||||
|  | //
 | ||||||
|  | // A principled solution is to maintain lightness, thus maintaining
 | ||||||
|  | // contrast/a11y, maintain hue, thus maintaining aesthetic intent, and reduce
 | ||||||
|  | // chroma until the color is in gamut.
 | ||||||
|  | //
 | ||||||
|  | // HCT chooses this solution, but, that doesn't mean it will _exactly_ matched
 | ||||||
|  | // desired lightness, if only because RGB is quantized: RGB is expressed as
 | ||||||
|  | // a set of integers: there may be an RGB color with, for example,
 | ||||||
|  | // 47.892 lightness, but not 47.891.
 | ||||||
|  | //
 | ||||||
|  | // To allow for this inherent incompatibility between perceptually accurate
 | ||||||
|  | // color spaces and display color spaces, methods that take a contrast ratio
 | ||||||
|  | // and luminance, and return a luminance that reaches that contrast ratio for
 | ||||||
|  | // the input luminance, purposefully darken/lighten their result such that
 | ||||||
|  | // the desired contrast ratio will be reached even if inaccuracy is introduced.
 | ||||||
|  | //
 | ||||||
|  | // 0.4 is generous, ex. HCT requires much less delta. It was chosen because
 | ||||||
|  | // it provides a rough guarantee that as long as a percetual color space
 | ||||||
|  | // gamut maps lightness such that the resulting lightness rounds to the same
 | ||||||
|  | // as the requested, the desired contrast ratio will be reached.
 | ||||||
|  | constexpr double LUMINANCE_GAMUT_MAP_TOLERANCE = 0.4; | ||||||
|  | 
 | ||||||
|  | double RatioOfYs(double y1, double y2) { | ||||||
|  |   double lighter = y1 > y2 ? y1 : y2; | ||||||
|  |   double darker = (lighter == y2) ? y1 : y2; | ||||||
|  |   return (lighter + 5.0) / (darker + 5.0); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | double RatioOfTones(double tone_a, double tone_b) { | ||||||
|  |   tone_a = std::clamp(tone_a, 0.0, 100.0); | ||||||
|  |   tone_b = std::clamp(tone_b, 0.0, 100.0); | ||||||
|  |   return RatioOfYs(YFromLstar(tone_a), YFromLstar(tone_b)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | double Lighter(double tone, double ratio) { | ||||||
|  |   if (tone < 0.0 || tone > 100.0) { | ||||||
|  |     return -1.0; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   double dark_y = YFromLstar(tone); | ||||||
|  |   double light_y = ratio * (dark_y + 5.0) - 5.0; | ||||||
|  |   double real_contrast = RatioOfYs(light_y, dark_y); | ||||||
|  |   double delta = abs(real_contrast - ratio); | ||||||
|  |   if (real_contrast < ratio && delta > CONTRAST_RATIO_EPSILON) { | ||||||
|  |     return -1; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   // ensure gamut mapping, which requires a 'range' on tone, will still result
 | ||||||
|  |   // the correct ratio by darkening slightly.
 | ||||||
|  |   double value = LstarFromY(light_y) + LUMINANCE_GAMUT_MAP_TOLERANCE; | ||||||
|  |   if (value < 0 || value > 100) { | ||||||
|  |     return -1; | ||||||
|  |   } | ||||||
|  |   return value; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | double Darker(double tone, double ratio) { | ||||||
|  |   if (tone < 0.0 || tone > 100.0) { | ||||||
|  |     return -1.0; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   double light_y = YFromLstar(tone); | ||||||
|  |   double dark_y = ((light_y + 5.0) / ratio) - 5.0; | ||||||
|  |   double real_contrast = RatioOfYs(light_y, dark_y); | ||||||
|  | 
 | ||||||
|  |   double delta = abs(real_contrast - ratio); | ||||||
|  |   if (real_contrast < ratio && delta > CONTRAST_RATIO_EPSILON) { | ||||||
|  |     return -1; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   // ensure gamut mapping, which requires a 'range' on tone, will still result
 | ||||||
|  |   // the correct ratio by darkening slightly.
 | ||||||
|  |   double value = LstarFromY(dark_y) - LUMINANCE_GAMUT_MAP_TOLERANCE; | ||||||
|  |   if (value < 0 || value > 100) { | ||||||
|  |     return -1; | ||||||
|  |   } | ||||||
|  |   return value; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | double LighterUnsafe(double tone, double ratio) { | ||||||
|  |   double lighter_safe = Lighter(tone, ratio); | ||||||
|  |   return (lighter_safe < 0.0) ? 100.0 : lighter_safe; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | double DarkerUnsafe(double tone, double ratio) { | ||||||
|  |   double darker_safe = Darker(tone, ratio); | ||||||
|  |   return (darker_safe < 0.0) ? 0.0 : darker_safe; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | }  // namespace material_color_utilities
 | ||||||
							
								
								
									
										97
									
								
								src/material-colors/cpp/contrast/contrast.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										97
									
								
								src/material-colors/cpp/contrast/contrast.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,97 @@ | ||||||
|  | /*
 | ||||||
|  |  * Copyright 2023 Google LLC | ||||||
|  |  * | ||||||
|  |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * | ||||||
|  |  *      http://www.apache.org/licenses/LICENSE-2.0
 | ||||||
|  |  * | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | #ifndef CPP_CONTRAST_CONTRAST_H_ | ||||||
|  | #define CPP_CONTRAST_CONTRAST_H_ | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Utility methods for calculating contrast given two colors, or calculating a | ||||||
|  |  * color given one color and a contrast ratio. | ||||||
|  |  * | ||||||
|  |  * Contrast ratio is calculated using XYZ's Y. When linearized to match human | ||||||
|  |  * perception, Y becomes HCT's tone and L*a*b*'s' L*. Informally, this is the | ||||||
|  |  * lightness of a color. | ||||||
|  |  * | ||||||
|  |  * Methods refer to tone, T in the the HCT color space. | ||||||
|  |  * Tone is equivalent to L* in the L*a*b* color space, or L in the LCH color | ||||||
|  |  * space. | ||||||
|  |  */ | ||||||
|  | namespace material_color_utilities { | ||||||
|  | /**
 | ||||||
|  |  * @return a contrast ratio, which ranges from 1 to 21. | ||||||
|  |  * @param tone_a Tone between 0 and 100. Values outside will be clamped. | ||||||
|  |  * @param tone_b Tone between 0 and 100. Values outside will be clamped. | ||||||
|  |  */ | ||||||
|  | double RatioOfTones(double tone_a, double tone_b); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * @return a tone >= [tone] that ensures [ratio]. | ||||||
|  |  * Return value is between 0 and 100. | ||||||
|  |  * Returns -1 if [ratio] cannot be achieved with [tone]. | ||||||
|  |  * | ||||||
|  |  * @param tone Tone return value must contrast with. | ||||||
|  |  * Range is 0 to 100. Invalid values will result in -1 being returned. | ||||||
|  |  * @param ratio Contrast ratio of return value and [tone]. | ||||||
|  |  * Range is 1 to 21, invalid values have undefined behavior. | ||||||
|  |  */ | ||||||
|  | double Lighter(double tone, double ratio); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * @return a tone <= [tone] that ensures [ratio]. | ||||||
|  |  * Return value is between 0 and 100. | ||||||
|  |  * Returns -1 if [ratio] cannot be achieved with [tone]. | ||||||
|  |  * | ||||||
|  |  * @param tone Tone return value must contrast with. | ||||||
|  |  * Range is 0 to 100. Invalid values will result in -1 being returned. | ||||||
|  |  * @param ratio Contrast ratio of return value and [tone]. | ||||||
|  |  * Range is 1 to 21, invalid values have undefined behavior. | ||||||
|  |  */ | ||||||
|  | double Darker(double tone, double ratio); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * @return a tone >= [tone] that ensures [ratio]. | ||||||
|  |  * Return value is between 0 and 100. | ||||||
|  |  * Returns 100 if [ratio] cannot be achieved with [tone]. | ||||||
|  |  * | ||||||
|  |  * This method is unsafe because the returned value is guaranteed to be in | ||||||
|  |  * bounds for tone, i.e. between 0 and 100. However, that value may not reach | ||||||
|  |  * the [ratio] with [tone]. For example, there is no color lighter than T100. | ||||||
|  |  * | ||||||
|  |  * @param tone Tone return value must contrast with. | ||||||
|  |  * Range is 0 to 100. Invalid values will result in 100 being returned. | ||||||
|  |  * @param ratio Desired contrast ratio of return value and tone parameter. | ||||||
|  |  * Range is 1 to 21, invalid values have undefined behavior. | ||||||
|  |  */ | ||||||
|  | double LighterUnsafe(double tone, double ratio); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * @return a tone <= [tone] that ensures [ratio]. | ||||||
|  |  * Return value is between 0 and 100. | ||||||
|  |  * Returns 0 if [ratio] cannot be achieved with [tone]. | ||||||
|  |  * | ||||||
|  |  * This method is unsafe because the returned value is guaranteed to be in | ||||||
|  |  * bounds for tone, i.e. between 0 and 100. However, that value may not reach | ||||||
|  |  * the [ratio] with [tone]. For example, there is no color darker than T0. | ||||||
|  |  * | ||||||
|  |  * @param tone Tone return value must contrast with. | ||||||
|  |  * Range is 0 to 100. Invalid values will result in 0 being returned. | ||||||
|  |  * @param ratio Desired contrast ratio of return value and tone parameter. | ||||||
|  |  * Range is 1 to 21, invalid values have undefined behavior. | ||||||
|  |  */ | ||||||
|  | double DarkerUnsafe(double tone, double ratio); | ||||||
|  | }  // namespace material_color_utilities
 | ||||||
|  | 
 | ||||||
|  | #endif  // CPP_CONTRAST_CONTRAST_H_
 | ||||||
							
								
								
									
										61
									
								
								src/material-colors/cpp/contrast/contrast_test.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										61
									
								
								src/material-colors/cpp/contrast/contrast_test.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,61 @@ | ||||||
|  | /*
 | ||||||
|  |  * Copyright 2023 Google LLC | ||||||
|  |  * | ||||||
|  |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * | ||||||
|  |  *      http://www.apache.org/licenses/LICENSE-2.0
 | ||||||
|  |  * | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | #include "cpp/contrast/contrast.h" | ||||||
|  | 
 | ||||||
|  | #include "testing/base/public/gunit.h" | ||||||
|  | 
 | ||||||
|  | namespace material_color_utilities { | ||||||
|  | 
 | ||||||
|  | namespace { | ||||||
|  | TEST(ContrastTest, RatioOfTonesOutOfBoundsInput) { | ||||||
|  |   EXPECT_NEAR(RatioOfTones(-10.0, 110.0), 21.0, 0.001); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TEST(ContrastTest, LighterImpossibleRatioErrors) { | ||||||
|  |   EXPECT_NEAR(Lighter(90.0, 10.0), -1.0, 0.001); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TEST(ContrastTest, LighterOutOfBoundsInputAboveErrors) { | ||||||
|  |   EXPECT_NEAR(Lighter(110.0, 2.0), -1.0, 0.001); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TEST(ContrastTest, LighterOutOfBoundsInputBelowErrors) { | ||||||
|  |   EXPECT_NEAR(Lighter(-10.0, 2.0), -1.0, 0.001); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TEST(ContrastTest, LighterUnsafeReturnsMaxTone) { | ||||||
|  |   EXPECT_NEAR(LighterUnsafe(100.0, 2.0), 100, 0.001); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TEST(ContrastTest, DarkerImpossibleRatioErrors) { | ||||||
|  |   EXPECT_NEAR(Darker(10.0, 20.0), -1.0, 0.001); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TEST(ContrastTest, DarkerOutOfBoundsInputAboveErrors) { | ||||||
|  |   EXPECT_NEAR(Darker(110.0, 2.0), -1.0, 0.001); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TEST(ContrastTest, DarkerOutOfBoundsInputBelowErrors) { | ||||||
|  |   EXPECT_NEAR(Darker(-10.0, 2.0), -1.0, 0.001); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TEST(ContrastTest, DarkerUnsafeReturnsMinTone) { | ||||||
|  |   EXPECT_NEAR(DarkerUnsafe(0.0, 2.0), 0.0, 0.001); | ||||||
|  | } | ||||||
|  | }  // namespace
 | ||||||
|  | 
 | ||||||
|  | }  // namespace material_color_utilities
 | ||||||
							
								
								
									
										42
									
								
								src/material-colors/cpp/dislike/dislike.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								src/material-colors/cpp/dislike/dislike.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,42 @@ | ||||||
|  | /*
 | ||||||
|  |  * Copyright 2023 Google LLC | ||||||
|  |  * | ||||||
|  |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * | ||||||
|  |  *      http://www.apache.org/licenses/LICENSE-2.0
 | ||||||
|  |  * | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | #include "cpp/dislike/dislike.h" | ||||||
|  | 
 | ||||||
|  | #include <cmath> | ||||||
|  | 
 | ||||||
|  | #include "cpp/cam/hct.h" | ||||||
|  | 
 | ||||||
|  | namespace material_color_utilities { | ||||||
|  | 
 | ||||||
|  | bool IsDisliked(Hct hct) { | ||||||
|  |   double roundedHue = std::round(hct.get_hue()); | ||||||
|  | 
 | ||||||
|  |   bool hue_passes = roundedHue >= 90.0 && roundedHue <= 111.0; | ||||||
|  |   bool chroma_passes = std::round(hct.get_chroma()) > 16.0; | ||||||
|  |   bool tone_passes = std::round(hct.get_tone()) < 65.0; | ||||||
|  | 
 | ||||||
|  |   return hue_passes && chroma_passes && tone_passes; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | Hct FixIfDisliked(Hct hct) { | ||||||
|  |   if (IsDisliked(hct)) { | ||||||
|  |     return Hct(hct.get_hue(), hct.get_chroma(), 70.0); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   return hct; | ||||||
|  | } | ||||||
|  | }  // namespace material_color_utilities
 | ||||||
							
								
								
									
										55
									
								
								src/material-colors/cpp/dislike/dislike.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										55
									
								
								src/material-colors/cpp/dislike/dislike.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,55 @@ | ||||||
|  | /*
 | ||||||
|  |  * Copyright 2023 Google LLC | ||||||
|  |  * | ||||||
|  |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * | ||||||
|  |  *      http://www.apache.org/licenses/LICENSE-2.0
 | ||||||
|  |  * | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | #ifndef CPP_DISLIKE_DISLIKE_H_ | ||||||
|  | #define CPP_DISLIKE_DISLIKE_H_ | ||||||
|  | 
 | ||||||
|  | #include "cpp/cam/hct.h" | ||||||
|  | 
 | ||||||
|  | namespace material_color_utilities { | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Checks and/or fixes universally disliked colors. | ||||||
|  |  * | ||||||
|  |  * Color science studies of color preference indicate universal distaste for | ||||||
|  |  * dark yellow-greens, and also show this is correlated to distate for | ||||||
|  |  * biological waste and rotting food. | ||||||
|  |  * | ||||||
|  |  * See Palmer and Schloss, 2010 or Schloss and Palmer's Chapter 21 in Handbook | ||||||
|  |  * of Color Psychology (2015). | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * @return whether the color is disliked. | ||||||
|  |  * | ||||||
|  |  * Disliked is defined as a dark yellow-green that is not neutral. | ||||||
|  |  * @param hct The color to be tested. | ||||||
|  |  */ | ||||||
|  | bool IsDisliked(Hct hct); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * If a color is disliked, lightens it to make it likable. | ||||||
|  |  * | ||||||
|  |  * The original color is not modified. | ||||||
|  |  * | ||||||
|  |  * @param hct The color to be tested (and fixed, if needed). | ||||||
|  |  * @return The original color if it is not disliked; otherwise, the fixed | ||||||
|  |  *     color. | ||||||
|  |  */ | ||||||
|  | Hct FixIfDisliked(Hct hct); | ||||||
|  | }  // namespace material_color_utilities
 | ||||||
|  | 
 | ||||||
|  | #endif  // CPP_DISLIKE_DISLIKE_H_
 | ||||||
							
								
								
									
										77
									
								
								src/material-colors/cpp/dislike/dislike_test.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										77
									
								
								src/material-colors/cpp/dislike/dislike_test.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,77 @@ | ||||||
|  | /*
 | ||||||
|  |  * Copyright 2023 Google LLC | ||||||
|  |  * | ||||||
|  |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * | ||||||
|  |  *      http://www.apache.org/licenses/LICENSE-2.0
 | ||||||
|  |  * | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | #include "cpp/dislike/dislike.h" | ||||||
|  | 
 | ||||||
|  | #include "testing/base/public/gunit.h" | ||||||
|  | #include "cpp/cam/hct.h" | ||||||
|  | 
 | ||||||
|  | namespace material_color_utilities { | ||||||
|  | 
 | ||||||
|  | namespace { | ||||||
|  | 
 | ||||||
|  | using testing::TestWithParam; | ||||||
|  | using testing::Values; | ||||||
|  | using SkinToneTest = TestWithParam<int>; | ||||||
|  | 
 | ||||||
|  | TEST_P(SkinToneTest, MonkSkinToneScaleColorsLiked) { | ||||||
|  |   int argb = GetParam(); | ||||||
|  | 
 | ||||||
|  |   EXPECT_FALSE(IsDisliked(Hct(argb))); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | INSTANTIATE_TEST_SUITE_P(DislikeTest, SkinToneTest, | ||||||
|  |                          Values(0xfff6ede4, 0xfff3e7db, 0xfff7ead0, 0xffeadaba, | ||||||
|  |                                 0xffd7bd96, 0xffa07e56, 0xff825c43, 0xff604134, | ||||||
|  |                                 0xff3a312a, 0xff292420)); | ||||||
|  | 
 | ||||||
|  | using BileTest = TestWithParam<int>; | ||||||
|  | 
 | ||||||
|  | TEST_P(BileTest, BileColorsDisliked) { | ||||||
|  |   int argb = GetParam(); | ||||||
|  | 
 | ||||||
|  |   EXPECT_TRUE(IsDisliked(Hct(argb))); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | INSTANTIATE_TEST_SUITE_P(DislikeTest, BileTest, | ||||||
|  |                          Values(0xff95884B, 0xff716B40, 0xffB08E00, 0xff4C4308, | ||||||
|  |                                 0xff464521)); | ||||||
|  | 
 | ||||||
|  | using BileFixingTest = TestWithParam<int>; | ||||||
|  | 
 | ||||||
|  | TEST_P(BileFixingTest, BileColorsFixed) { | ||||||
|  |   int argb = GetParam(); | ||||||
|  | 
 | ||||||
|  |   Hct bile_color = Hct(argb); | ||||||
|  |   EXPECT_TRUE(IsDisliked(bile_color)); | ||||||
|  |   Hct fixed_bile_color = FixIfDisliked(bile_color); | ||||||
|  |   EXPECT_FALSE(IsDisliked(fixed_bile_color)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | INSTANTIATE_TEST_SUITE_P(DislikeTest, BileFixingTest, | ||||||
|  |                          Values(0xff95884B, 0xff716B40, 0xffB08E00, 0xff4C4308, | ||||||
|  |                                 0xff464521)); | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | TEST(DislikeTest, Tone67Liked) { | ||||||
|  |   Hct color = Hct(100.0, 50.0, 67.0); | ||||||
|  |   EXPECT_FALSE(IsDisliked(color)); | ||||||
|  |   EXPECT_EQ(FixIfDisliked(color).ToInt(), color.ToInt()); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | }  // namespace
 | ||||||
|  | 
 | ||||||
|  | }  // namespace material_color_utilities
 | ||||||
							
								
								
									
										72
									
								
								src/material-colors/cpp/dynamiccolor/contrast_curve.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										72
									
								
								src/material-colors/cpp/dynamiccolor/contrast_curve.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,72 @@ | ||||||
|  | /*
 | ||||||
|  |  * Copyright 2023 Google LLC | ||||||
|  |  * | ||||||
|  |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * | ||||||
|  |  *      http://www.apache.org/licenses/LICENSE-2.0
 | ||||||
|  |  * | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | #ifndef CPP_DYNAMICCOLOR_CONTRAST_CURVE_H_ | ||||||
|  | #define CPP_DYNAMICCOLOR_CONTRAST_CURVE_H_ | ||||||
|  | 
 | ||||||
|  | #include "cpp/utils/utils.h" | ||||||
|  | 
 | ||||||
|  | namespace material_color_utilities { | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * A class containing a value that changes with the contrast level. | ||||||
|  |  * | ||||||
|  |  * Usually represents the contrast requirements for a dynamic color on its | ||||||
|  |  * background. The four values correspond to values for contrast levels -1.0, | ||||||
|  |  * 0.0, 0.5, and 1.0, respectively. | ||||||
|  |  */ | ||||||
|  | struct ContrastCurve { | ||||||
|  |   double low; | ||||||
|  |   double normal; | ||||||
|  |   double medium; | ||||||
|  |   double high; | ||||||
|  | 
 | ||||||
|  |   /**
 | ||||||
|  |    * Creates a `ContrastCurve` object. | ||||||
|  |    * | ||||||
|  |    * @param low Value for contrast level -1.0 | ||||||
|  |    * @param normal Value for contrast level 0.0 | ||||||
|  |    * @param medium Value for contrast level 0.5 | ||||||
|  |    * @param high Value for contrast level 1.0 | ||||||
|  |    */ | ||||||
|  |   ContrastCurve(double low, double normal, double medium, double high) | ||||||
|  |       : low(low), normal(normal), medium(medium), high(high) {} | ||||||
|  | 
 | ||||||
|  |   /**
 | ||||||
|  |    * Returns the value at a given contrast level. | ||||||
|  |    * | ||||||
|  |    * @param contrastLevel The contrast level. 0.0 is the default (normal); -1.0 | ||||||
|  |    *     is the lowest; 1.0 is the highest. | ||||||
|  |    * @return The value. For contrast ratios, a number between 1.0 and 21.0. | ||||||
|  |    */ | ||||||
|  |   double get(double contrastLevel) { | ||||||
|  |     if (contrastLevel <= -1.0) { | ||||||
|  |       return low; | ||||||
|  |     } else if (contrastLevel < 0.0) { | ||||||
|  |       return Lerp(low, normal, (contrastLevel - (-1)) / 1); | ||||||
|  |     } else if (contrastLevel < 0.5) { | ||||||
|  |       return Lerp(normal, medium, (contrastLevel - 0) / 0.5); | ||||||
|  |     } else if (contrastLevel < 1.0) { | ||||||
|  |       return Lerp(medium, high, (contrastLevel - 0.5) / 0.5); | ||||||
|  |     } else { | ||||||
|  |       return high; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | }  // namespace material_color_utilities
 | ||||||
|  | 
 | ||||||
|  | #endif  // CPP_DYNAMICCOLOR_CONTRAST_CURVE_H_
 | ||||||
							
								
								
									
										306
									
								
								src/material-colors/cpp/dynamiccolor/dynamic_color.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										306
									
								
								src/material-colors/cpp/dynamiccolor/dynamic_color.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,306 @@ | ||||||
|  | /*
 | ||||||
|  |  * Copyright 2023 Google LLC | ||||||
|  |  * | ||||||
|  |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * | ||||||
|  |  *      http://www.apache.org/licenses/LICENSE-2.0
 | ||||||
|  |  * | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | #include "cpp/dynamiccolor/dynamic_color.h" | ||||||
|  | 
 | ||||||
|  | #include <algorithm> | ||||||
|  | #include <cmath> | ||||||
|  | #include <functional> | ||||||
|  | #include <optional> | ||||||
|  | #include <string> | ||||||
|  | #include <vector> | ||||||
|  | 
 | ||||||
|  | #include "cpp/cam/hct.h" | ||||||
|  | #include "cpp/contrast/contrast.h" | ||||||
|  | #include "cpp/dynamiccolor/dynamic_scheme.h" | ||||||
|  | #include "cpp/dynamiccolor/tone_delta_pair.h" | ||||||
|  | #include "cpp/palettes/tones.h" | ||||||
|  | 
 | ||||||
|  | namespace material_color_utilities { | ||||||
|  | 
 | ||||||
|  | using std::function; | ||||||
|  | using std::nullopt; | ||||||
|  | using std::optional; | ||||||
|  | 
 | ||||||
|  | using DoubleFunction = function<double(const DynamicScheme&)>; | ||||||
|  | 
 | ||||||
|  | template <class T, class U> | ||||||
|  | optional<U> SafeCall(optional<function<optional<U>(const T&)>> f, const T& x) { | ||||||
|  |   if (f == nullopt) { | ||||||
|  |     return nullopt; | ||||||
|  |   } else { | ||||||
|  |     return f.value()(x); | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | template <class T, class U> | ||||||
|  | optional<U> SafeCallCleanResult(optional<function<U(T)>> f, T x) { | ||||||
|  |   if (f == nullopt) { | ||||||
|  |     return nullopt; | ||||||
|  |   } else { | ||||||
|  |     return f.value()(x); | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | double ForegroundTone(double bg_tone, double ratio) { | ||||||
|  |   double lighter_tone = LighterUnsafe(/*tone*/ bg_tone, /*ratio*/ ratio); | ||||||
|  |   double darker_tone = DarkerUnsafe(/*tone*/ bg_tone, /*ratio*/ ratio); | ||||||
|  |   double lighter_ratio = RatioOfTones(lighter_tone, bg_tone); | ||||||
|  |   double darker_ratio = RatioOfTones(darker_tone, bg_tone); | ||||||
|  |   double prefer_lighter = TonePrefersLightForeground(bg_tone); | ||||||
|  | 
 | ||||||
|  |   if (prefer_lighter) { | ||||||
|  |     double negligible_difference = | ||||||
|  |         (abs(lighter_ratio - darker_ratio) < 0.1 && lighter_ratio < ratio && | ||||||
|  |          darker_ratio < ratio); | ||||||
|  |     return lighter_ratio >= ratio || lighter_ratio >= darker_ratio || | ||||||
|  |                    negligible_difference | ||||||
|  |                ? lighter_tone | ||||||
|  |                : darker_tone; | ||||||
|  |   } else { | ||||||
|  |     return darker_ratio >= ratio || darker_ratio >= lighter_ratio | ||||||
|  |                ? darker_tone | ||||||
|  |                : lighter_tone; | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | double EnableLightForeground(double tone) { | ||||||
|  |   if (TonePrefersLightForeground(tone) && !ToneAllowsLightForeground(tone)) { | ||||||
|  |     return 49.0; | ||||||
|  |   } | ||||||
|  |   return tone; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool TonePrefersLightForeground(double tone) { return round(tone) < 60; } | ||||||
|  | 
 | ||||||
|  | bool ToneAllowsLightForeground(double tone) { return round(tone) <= 49; } | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Default constructor. | ||||||
|  |  */ | ||||||
|  | DynamicColor::DynamicColor( | ||||||
|  |     std::string name, std::function<TonalPalette(const DynamicScheme&)> palette, | ||||||
|  |     std::function<double(const DynamicScheme&)> tone, bool is_background, | ||||||
|  | 
 | ||||||
|  |     std::optional<std::function<DynamicColor(const DynamicScheme&)>> background, | ||||||
|  |     std::optional<std::function<DynamicColor(const DynamicScheme&)>> | ||||||
|  |         second_background, | ||||||
|  |     std::optional<ContrastCurve> contrast_curve, | ||||||
|  |     std::optional<std::function<ToneDeltaPair(const DynamicScheme&)>> | ||||||
|  |         tone_delta_pair) | ||||||
|  |     : name_(name), | ||||||
|  |       palette_(palette), | ||||||
|  |       tone_(tone), | ||||||
|  |       is_background_(is_background), | ||||||
|  |       background_(background), | ||||||
|  |       second_background_(second_background), | ||||||
|  |       contrast_curve_(contrast_curve), | ||||||
|  |       tone_delta_pair_(tone_delta_pair) {} | ||||||
|  | 
 | ||||||
|  | DynamicColor DynamicColor::FromPalette( | ||||||
|  |     std::string name, std::function<TonalPalette(const DynamicScheme&)> palette, | ||||||
|  |     std::function<double(const DynamicScheme&)> tone) { | ||||||
|  |   return DynamicColor(name, palette, tone, | ||||||
|  |                       /*is_background=*/false, | ||||||
|  |                       /*background=*/nullopt, | ||||||
|  |                       /*second_background=*/nullopt, | ||||||
|  |                       /*contrast_curve=*/nullopt, | ||||||
|  |                       /*tone_delta_pair=*/nullopt); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | Argb DynamicColor::GetArgb(const DynamicScheme& scheme) { | ||||||
|  |   return palette_(scheme).get(GetTone(scheme)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | Hct DynamicColor::GetHct(const DynamicScheme& scheme) { | ||||||
|  |   return Hct(GetArgb(scheme)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | double DynamicColor::GetTone(const DynamicScheme& scheme) { | ||||||
|  |   bool decreasingContrast = scheme.contrast_level < 0; | ||||||
|  | 
 | ||||||
|  |   // Case 1: dual foreground, pair of colors with delta constraint.
 | ||||||
|  |   if (tone_delta_pair_ != std::nullopt) { | ||||||
|  |     ToneDeltaPair tone_delta_pair = tone_delta_pair_.value()(scheme); | ||||||
|  |     DynamicColor role_a = tone_delta_pair.role_a_; | ||||||
|  |     DynamicColor role_b = tone_delta_pair.role_b_; | ||||||
|  |     double delta = tone_delta_pair.delta_; | ||||||
|  |     TonePolarity polarity = tone_delta_pair.polarity_; | ||||||
|  |     bool stay_together = tone_delta_pair.stay_together_; | ||||||
|  | 
 | ||||||
|  |     DynamicColor bg = background_.value()(scheme); | ||||||
|  |     double bg_tone = bg.GetTone(scheme); | ||||||
|  | 
 | ||||||
|  |     bool a_is_nearer = | ||||||
|  |         (polarity == TonePolarity::kNearer || | ||||||
|  |          (polarity == TonePolarity::kLighter && !scheme.is_dark) || | ||||||
|  |          (polarity == TonePolarity::kDarker && scheme.is_dark)); | ||||||
|  |     DynamicColor nearer = a_is_nearer ? role_a : role_b; | ||||||
|  |     DynamicColor farther = a_is_nearer ? role_b : role_a; | ||||||
|  |     bool am_nearer = this->name_ == nearer.name_; | ||||||
|  |     double expansion_dir = scheme.is_dark ? 1 : -1; | ||||||
|  | 
 | ||||||
|  |     // 1st round: solve to min, each
 | ||||||
|  |     double n_contrast = | ||||||
|  |         nearer.contrast_curve_.value().get(scheme.contrast_level); | ||||||
|  |     double f_contrast = | ||||||
|  |         farther.contrast_curve_.value().get(scheme.contrast_level); | ||||||
|  | 
 | ||||||
|  |     // If a color is good enough, it is not adjusted.
 | ||||||
|  |     // Initial and adjusted tones for `nearer`
 | ||||||
|  |     double n_initial_tone = nearer.tone_(scheme); | ||||||
|  |     double n_tone = RatioOfTones(bg_tone, n_initial_tone) >= n_contrast | ||||||
|  |                         ? n_initial_tone | ||||||
|  |                         : ForegroundTone(bg_tone, n_contrast); | ||||||
|  |     // Initial and adjusted tones for `farther`
 | ||||||
|  |     double f_initial_tone = farther.tone_(scheme); | ||||||
|  |     double f_tone = RatioOfTones(bg_tone, f_initial_tone) >= f_contrast | ||||||
|  |                         ? f_initial_tone | ||||||
|  |                         : ForegroundTone(bg_tone, f_contrast); | ||||||
|  | 
 | ||||||
|  |     if (decreasingContrast) { | ||||||
|  |       // If decreasing contrast, adjust color to the "bare minimum"
 | ||||||
|  |       // that satisfies contrast.
 | ||||||
|  |       n_tone = ForegroundTone(bg_tone, n_contrast); | ||||||
|  |       f_tone = ForegroundTone(bg_tone, f_contrast); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if ((f_tone - n_tone) * expansion_dir >= delta) { | ||||||
|  |       // Good! Tones satisfy the constraint; no change needed.
 | ||||||
|  |     } else { | ||||||
|  |       // 2nd round: expand farther to match delta.
 | ||||||
|  |       f_tone = std::clamp(n_tone + delta * expansion_dir, 0.0, 100.0); | ||||||
|  |       if ((f_tone - n_tone) * expansion_dir >= delta) { | ||||||
|  |         // Good! Tones now satisfy the constraint; no change needed.
 | ||||||
|  |       } else { | ||||||
|  |         // 3rd round: contract nearer to match delta.
 | ||||||
|  |         n_tone = std::clamp(f_tone - delta * expansion_dir, 0.0, 100.0); | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // Avoids the 50-59 awkward zone.
 | ||||||
|  |     if (50 <= n_tone && n_tone < 60) { | ||||||
|  |       // If `nearer` is in the awkward zone, move it away, together with
 | ||||||
|  |       // `farther`.
 | ||||||
|  |       if (expansion_dir > 0) { | ||||||
|  |         n_tone = 60; | ||||||
|  |         f_tone = std::max(f_tone, n_tone + delta * expansion_dir); | ||||||
|  |       } else { | ||||||
|  |         n_tone = 49; | ||||||
|  |         f_tone = std::min(f_tone, n_tone + delta * expansion_dir); | ||||||
|  |       } | ||||||
|  |     } else if (50 <= f_tone && f_tone < 60) { | ||||||
|  |       if (stay_together) { | ||||||
|  |         // Fixes both, to avoid two colors on opposite sides of the "awkward
 | ||||||
|  |         // zone".
 | ||||||
|  |         if (expansion_dir > 0) { | ||||||
|  |           n_tone = 60; | ||||||
|  |           f_tone = std::max(f_tone, n_tone + delta * expansion_dir); | ||||||
|  |         } else { | ||||||
|  |           n_tone = 49; | ||||||
|  |           f_tone = std::min(f_tone, n_tone + delta * expansion_dir); | ||||||
|  |         } | ||||||
|  |       } else { | ||||||
|  |         // Not required to stay together; fixes just one.
 | ||||||
|  |         if (expansion_dir > 0) { | ||||||
|  |           f_tone = 60; | ||||||
|  |         } else { | ||||||
|  |           f_tone = 49; | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // Returns `n_tone` if this color is `nearer`, otherwise `f_tone`.
 | ||||||
|  |     return am_nearer ? n_tone : f_tone; | ||||||
|  |   } else { | ||||||
|  |     // Case 2: No contrast pair; just solve for itself.
 | ||||||
|  |     double answer = tone_(scheme); | ||||||
|  | 
 | ||||||
|  |     if (background_ == std::nullopt) { | ||||||
|  |       return answer;  // No adjustment for colors with no background.
 | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     double bg_tone = background_.value()(scheme).GetTone(scheme); | ||||||
|  | 
 | ||||||
|  |     double desired_ratio = contrast_curve_.value().get(scheme.contrast_level); | ||||||
|  | 
 | ||||||
|  |     if (RatioOfTones(bg_tone, answer) >= desired_ratio) { | ||||||
|  |       // Don't "improve" what's good enough.
 | ||||||
|  |     } else { | ||||||
|  |       // Rough improvement.
 | ||||||
|  |       answer = ForegroundTone(bg_tone, desired_ratio); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if (decreasingContrast) { | ||||||
|  |       answer = ForegroundTone(bg_tone, desired_ratio); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if (is_background_ && 50 <= answer && answer < 60) { | ||||||
|  |       // Must adjust
 | ||||||
|  |       if (RatioOfTones(49, bg_tone) >= desired_ratio) { | ||||||
|  |         answer = 49; | ||||||
|  |       } else { | ||||||
|  |         answer = 60; | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if (second_background_ != std::nullopt) { | ||||||
|  |       // Case 3: Adjust for dual backgrounds.
 | ||||||
|  | 
 | ||||||
|  |       double bg_tone_1 = background_.value()(scheme).GetTone(scheme); | ||||||
|  |       double bg_tone_2 = second_background_.value()(scheme).GetTone(scheme); | ||||||
|  | 
 | ||||||
|  |       double upper = std::max(bg_tone_1, bg_tone_2); | ||||||
|  |       double lower = std::min(bg_tone_1, bg_tone_2); | ||||||
|  | 
 | ||||||
|  |       if (RatioOfTones(upper, answer) >= desired_ratio && | ||||||
|  |           RatioOfTones(lower, answer) >= desired_ratio) { | ||||||
|  |         return answer; | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  |       // The darkest light tone that satisfies the desired ratio,
 | ||||||
|  |       // or -1 if such ratio cannot be reached.
 | ||||||
|  |       double lightOption = Lighter(upper, desired_ratio); | ||||||
|  | 
 | ||||||
|  |       // The lightest dark tone that satisfies the desired ratio,
 | ||||||
|  |       // or -1 if such ratio cannot be reached.
 | ||||||
|  |       double darkOption = Darker(lower, desired_ratio); | ||||||
|  | 
 | ||||||
|  |       // Tones suitable for the foreground.
 | ||||||
|  |       std::vector<double> availables; | ||||||
|  |       if (lightOption != -1) { | ||||||
|  |         availables.push_back(lightOption); | ||||||
|  |       } | ||||||
|  |       if (darkOption != -1) { | ||||||
|  |         availables.push_back(darkOption); | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  |       bool prefersLight = TonePrefersLightForeground(bg_tone_1) || | ||||||
|  |                           TonePrefersLightForeground(bg_tone_2); | ||||||
|  |       if (prefersLight) { | ||||||
|  |         return (lightOption < 0) ? 100 : lightOption; | ||||||
|  |       } | ||||||
|  |       if (availables.size() == 1) { | ||||||
|  |         return availables[0]; | ||||||
|  |       } | ||||||
|  |       return (darkOption < 0) ? 0 : darkOption; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return answer; | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | }  // namespace material_color_utilities
 | ||||||
							
								
								
									
										131
									
								
								src/material-colors/cpp/dynamiccolor/dynamic_color.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										131
									
								
								src/material-colors/cpp/dynamiccolor/dynamic_color.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,131 @@ | ||||||
|  | /*
 | ||||||
|  |  * Copyright 2023 Google LLC | ||||||
|  |  * | ||||||
|  |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * | ||||||
|  |  *      http://www.apache.org/licenses/LICENSE-2.0
 | ||||||
|  |  * | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | #ifndef CPP_DYNAMICCOLOR_DYNAMIC_COLOR_H_ | ||||||
|  | #define CPP_DYNAMICCOLOR_DYNAMIC_COLOR_H_ | ||||||
|  | 
 | ||||||
|  | #include <functional> | ||||||
|  | #include <optional> | ||||||
|  | #include <string> | ||||||
|  | 
 | ||||||
|  | #include "cpp/cam/hct.h" | ||||||
|  | #include "cpp/dynamiccolor/contrast_curve.h" | ||||||
|  | #include "cpp/dynamiccolor/dynamic_scheme.h" | ||||||
|  | #include "cpp/utils/utils.h" | ||||||
|  | 
 | ||||||
|  | namespace material_color_utilities { | ||||||
|  | 
 | ||||||
|  | struct ToneDeltaPair; | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Given a background tone, find a foreground tone, while ensuring they reach | ||||||
|  |  * a contrast ratio that is as close to [ratio] as possible. | ||||||
|  |  * | ||||||
|  |  * [bgTone] Tone in HCT. Range is 0 to 100, undefined behavior when it falls | ||||||
|  |  * outside that range. | ||||||
|  |  * [ratio] The contrast ratio desired between [bgTone] and the return value. | ||||||
|  |  */ | ||||||
|  | double ForegroundTone(double bg_tone, double ratio); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Adjust a tone such that white has 4.5 contrast, if the tone is | ||||||
|  |  * reasonably close to supporting it. | ||||||
|  |  */ | ||||||
|  | double EnableLightForeground(double tone); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Returns whether [tone] prefers a light foreground. | ||||||
|  |  * | ||||||
|  |  * People prefer white foregrounds on ~T60-70. Observed over time, and also | ||||||
|  |  * by Andrew Somers during research for APCA. | ||||||
|  |  * | ||||||
|  |  * T60 used as to create the smallest discontinuity possible when skipping | ||||||
|  |  * down to T49 in order to ensure light foregrounds. | ||||||
|  |  * | ||||||
|  |  * Since `tertiaryContainer` in dark monochrome scheme requires a tone of | ||||||
|  |  * 60, it should not be adjusted. Therefore, 60 is excluded here. | ||||||
|  |  */ | ||||||
|  | bool TonePrefersLightForeground(double tone); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Returns whether [tone] can reach a contrast ratio of 4.5 with a lighter | ||||||
|  |  * color. | ||||||
|  |  */ | ||||||
|  | bool ToneAllowsLightForeground(double tone); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * @param name_ The name of the dynamic color. | ||||||
|  |  * @param palette_ Function that provides a TonalPalette given | ||||||
|  |  * DynamicScheme. A TonalPalette is defined by a hue and chroma, so this | ||||||
|  |  * replaces the need to specify hue/chroma. By providing a tonal palette, when | ||||||
|  |  * contrast adjustments are made, intended chroma can be preserved. | ||||||
|  |  * @param tone_ Function that provides a tone given DynamicScheme. | ||||||
|  |  * @param is_background_ Whether this dynamic color is a background, with | ||||||
|  |  * some other color as the foreground. | ||||||
|  |  * @param background_ The background of the dynamic color (as a function of a | ||||||
|  |  *     `DynamicScheme`), if it exists. | ||||||
|  |  * @param second_background_ A second background of the dynamic color (as a | ||||||
|  |  *     function of a `DynamicScheme`), if it | ||||||
|  |  * exists. | ||||||
|  |  * @param contrast_curve_ A `ContrastCurve` object specifying how its contrast | ||||||
|  |  * against its background should behave in various contrast levels options. | ||||||
|  |  * @param tone_delta_pair_ A `ToneDeltaPair` object specifying a tone delta | ||||||
|  |  * constraint between two colors. One of them must be the color being | ||||||
|  |  * constructed. | ||||||
|  |  */ | ||||||
|  | struct DynamicColor { | ||||||
|  |   std::string name_; | ||||||
|  |   std::function<TonalPalette(const DynamicScheme&)> palette_; | ||||||
|  |   std::function<double(const DynamicScheme&)> tone_; | ||||||
|  |   bool is_background_; | ||||||
|  | 
 | ||||||
|  |   std::optional<std::function<DynamicColor(const DynamicScheme&)>> background_; | ||||||
|  |   std::optional<std::function<DynamicColor(const DynamicScheme&)>> | ||||||
|  |       second_background_; | ||||||
|  |   std::optional<ContrastCurve> contrast_curve_; | ||||||
|  |   std::optional<std::function<ToneDeltaPair(const DynamicScheme&)>> | ||||||
|  |       tone_delta_pair_; | ||||||
|  | 
 | ||||||
|  |   /** A convenience constructor, only requiring name, palette, and tone. */ | ||||||
|  |   static DynamicColor FromPalette( | ||||||
|  |       std::string name, | ||||||
|  |       std::function<TonalPalette(const DynamicScheme&)> palette, | ||||||
|  |       std::function<double(const DynamicScheme&)> tone); | ||||||
|  | 
 | ||||||
|  |   Argb GetArgb(const DynamicScheme& scheme); | ||||||
|  | 
 | ||||||
|  |   Hct GetHct(const DynamicScheme& scheme); | ||||||
|  | 
 | ||||||
|  |   double GetTone(const DynamicScheme& scheme); | ||||||
|  | 
 | ||||||
|  |   /** The default constructor. */ | ||||||
|  |   DynamicColor(std::string name, | ||||||
|  |                std::function<TonalPalette(const DynamicScheme&)> palette, | ||||||
|  |                std::function<double(const DynamicScheme&)> tone, | ||||||
|  |                bool is_background, | ||||||
|  | 
 | ||||||
|  |                std::optional<std::function<DynamicColor(const DynamicScheme&)>> | ||||||
|  |                    background, | ||||||
|  |                std::optional<std::function<DynamicColor(const DynamicScheme&)>> | ||||||
|  |                    second_background, | ||||||
|  |                std::optional<ContrastCurve> contrast_curve, | ||||||
|  |                std::optional<std::function<ToneDeltaPair(const DynamicScheme&)>> | ||||||
|  |                    tone_delta_pair); | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | }  // namespace material_color_utilities
 | ||||||
|  | 
 | ||||||
|  | #endif  // CPP_DYNAMICCOLOR_DYNAMIC_COLOR_H_
 | ||||||
							
								
								
									
										286
									
								
								src/material-colors/cpp/dynamiccolor/dynamic_scheme.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										286
									
								
								src/material-colors/cpp/dynamiccolor/dynamic_scheme.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,286 @@ | ||||||
|  | /*
 | ||||||
|  |  * Copyright 2023 Google LLC | ||||||
|  |  * | ||||||
|  |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * | ||||||
|  |  *      http://www.apache.org/licenses/LICENSE-2.0
 | ||||||
|  |  * | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | #include "cpp/dynamiccolor/dynamic_scheme.h" | ||||||
|  | 
 | ||||||
|  | #include <optional> | ||||||
|  | #include <vector> | ||||||
|  | 
 | ||||||
|  | #include "cpp/cam/hct.h" | ||||||
|  | #include "cpp/dynamiccolor/material_dynamic_colors.h" | ||||||
|  | #include "cpp/dynamiccolor/variant.h" | ||||||
|  | #include "cpp/palettes/tones.h" | ||||||
|  | #include "cpp/utils/utils.h" | ||||||
|  | 
 | ||||||
|  | namespace material_color_utilities { | ||||||
|  | 
 | ||||||
|  | DynamicScheme::DynamicScheme(Hct source_color_hct, Variant variant, | ||||||
|  |                              double contrast_level, bool is_dark, | ||||||
|  |                              TonalPalette primary_palette, | ||||||
|  |                              TonalPalette secondary_palette, | ||||||
|  |                              TonalPalette tertiary_palette, | ||||||
|  |                              TonalPalette neutral_palette, | ||||||
|  |                              TonalPalette neutral_variant_palette, | ||||||
|  |                              std::optional<TonalPalette> error_palette) | ||||||
|  |     : source_color_hct(source_color_hct), | ||||||
|  |       variant(variant), | ||||||
|  |       is_dark(is_dark), | ||||||
|  |       contrast_level(contrast_level), | ||||||
|  |       primary_palette(primary_palette), | ||||||
|  |       secondary_palette(secondary_palette), | ||||||
|  |       tertiary_palette(tertiary_palette), | ||||||
|  |       neutral_palette(neutral_palette), | ||||||
|  |       neutral_variant_palette(neutral_variant_palette), | ||||||
|  |       error_palette(error_palette.value_or(TonalPalette(25.0, 84.0))) {} | ||||||
|  | 
 | ||||||
|  | double DynamicScheme::GetRotatedHue(Hct source_color, std::vector<double> hues, | ||||||
|  |                                     std::vector<double> rotations) { | ||||||
|  |   double source_hue = source_color.get_hue(); | ||||||
|  | 
 | ||||||
|  |   if (rotations.size() == 1) { | ||||||
|  |     return SanitizeDegreesDouble(source_color.get_hue() + rotations[0]); | ||||||
|  |   } | ||||||
|  |   int size = hues.size(); | ||||||
|  |   for (int i = 0; i <= (size - 2); ++i) { | ||||||
|  |     double this_hue = hues[i]; | ||||||
|  |     double next_hue = hues[i + 1]; | ||||||
|  |     if (this_hue < source_hue && source_hue < next_hue) { | ||||||
|  |       return SanitizeDegreesDouble(source_hue + rotations[i]); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   return source_hue; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | Argb DynamicScheme::SourceColorArgb() const { return source_color_hct.ToInt(); } | ||||||
|  | 
 | ||||||
|  | Argb DynamicScheme::GetPrimaryPaletteKeyColor() const { | ||||||
|  |   return MaterialDynamicColors::PrimaryPaletteKeyColor().GetArgb(*this); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | Argb DynamicScheme::GetSecondaryPaletteKeyColor() const { | ||||||
|  |   return MaterialDynamicColors::SecondaryPaletteKeyColor().GetArgb(*this); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | Argb DynamicScheme::GetTertiaryPaletteKeyColor() const { | ||||||
|  |   return MaterialDynamicColors::TertiaryPaletteKeyColor().GetArgb(*this); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | Argb DynamicScheme::GetNeutralPaletteKeyColor() const { | ||||||
|  |   return MaterialDynamicColors::NeutralPaletteKeyColor().GetArgb(*this); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | Argb DynamicScheme::GetNeutralVariantPaletteKeyColor() const { | ||||||
|  |   return MaterialDynamicColors::NeutralVariantPaletteKeyColor().GetArgb(*this); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | Argb DynamicScheme::GetBackground() const { | ||||||
|  |   return MaterialDynamicColors::Background().GetArgb(*this); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | Argb DynamicScheme::GetOnBackground() const { | ||||||
|  |   return MaterialDynamicColors::OnBackground().GetArgb(*this); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | Argb DynamicScheme::GetSurface() const { | ||||||
|  |   return MaterialDynamicColors::Surface().GetArgb(*this); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | Argb DynamicScheme::GetSurfaceDim() const { | ||||||
|  |   return MaterialDynamicColors::SurfaceDim().GetArgb(*this); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | Argb DynamicScheme::GetSurfaceBright() const { | ||||||
|  |   return MaterialDynamicColors::SurfaceBright().GetArgb(*this); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | Argb DynamicScheme::GetSurfaceContainerLowest() const { | ||||||
|  |   return MaterialDynamicColors::SurfaceContainerLowest().GetArgb(*this); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | Argb DynamicScheme::GetSurfaceContainerLow() const { | ||||||
|  |   return MaterialDynamicColors::SurfaceContainerLow().GetArgb(*this); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | Argb DynamicScheme::GetSurfaceContainer() const { | ||||||
|  |   return MaterialDynamicColors::SurfaceContainer().GetArgb(*this); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | Argb DynamicScheme::GetSurfaceContainerHigh() const { | ||||||
|  |   return MaterialDynamicColors::SurfaceContainerHigh().GetArgb(*this); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | Argb DynamicScheme::GetSurfaceContainerHighest() const { | ||||||
|  |   return MaterialDynamicColors::SurfaceContainerHighest().GetArgb(*this); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | Argb DynamicScheme::GetOnSurface() const { | ||||||
|  |   return MaterialDynamicColors::OnSurface().GetArgb(*this); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | Argb DynamicScheme::GetSurfaceVariant() const { | ||||||
|  |   return MaterialDynamicColors::SurfaceVariant().GetArgb(*this); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | Argb DynamicScheme::GetOnSurfaceVariant() const { | ||||||
|  |   return MaterialDynamicColors::OnSurfaceVariant().GetArgb(*this); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | Argb DynamicScheme::GetInverseSurface() const { | ||||||
|  |   return MaterialDynamicColors::InverseSurface().GetArgb(*this); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | Argb DynamicScheme::GetInverseOnSurface() const { | ||||||
|  |   return MaterialDynamicColors::InverseOnSurface().GetArgb(*this); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | Argb DynamicScheme::GetOutline() const { | ||||||
|  |   return MaterialDynamicColors::Outline().GetArgb(*this); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | Argb DynamicScheme::GetOutlineVariant() const { | ||||||
|  |   return MaterialDynamicColors::OutlineVariant().GetArgb(*this); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | Argb DynamicScheme::GetShadow() const { | ||||||
|  |   return MaterialDynamicColors::Shadow().GetArgb(*this); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | Argb DynamicScheme::GetScrim() const { | ||||||
|  |   return MaterialDynamicColors::Scrim().GetArgb(*this); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | Argb DynamicScheme::GetSurfaceTint() const { | ||||||
|  |   return MaterialDynamicColors::SurfaceTint().GetArgb(*this); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | Argb DynamicScheme::GetPrimary() const { | ||||||
|  |   return MaterialDynamicColors::Primary().GetArgb(*this); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | Argb DynamicScheme::GetOnPrimary() const { | ||||||
|  |   return MaterialDynamicColors::OnPrimary().GetArgb(*this); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | Argb DynamicScheme::GetPrimaryContainer() const { | ||||||
|  |   return MaterialDynamicColors::PrimaryContainer().GetArgb(*this); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | Argb DynamicScheme::GetOnPrimaryContainer() const { | ||||||
|  |   return MaterialDynamicColors::OnPrimaryContainer().GetArgb(*this); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | Argb DynamicScheme::GetInversePrimary() const { | ||||||
|  |   return MaterialDynamicColors::InversePrimary().GetArgb(*this); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | Argb DynamicScheme::GetSecondary() const { | ||||||
|  |   return MaterialDynamicColors::Secondary().GetArgb(*this); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | Argb DynamicScheme::GetOnSecondary() const { | ||||||
|  |   return MaterialDynamicColors::OnSecondary().GetArgb(*this); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | Argb DynamicScheme::GetSecondaryContainer() const { | ||||||
|  |   return MaterialDynamicColors::SecondaryContainer().GetArgb(*this); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | Argb DynamicScheme::GetOnSecondaryContainer() const { | ||||||
|  |   return MaterialDynamicColors::OnSecondaryContainer().GetArgb(*this); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | Argb DynamicScheme::GetTertiary() const { | ||||||
|  |   return MaterialDynamicColors::Tertiary().GetArgb(*this); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | Argb DynamicScheme::GetOnTertiary() const { | ||||||
|  |   return MaterialDynamicColors::OnTertiary().GetArgb(*this); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | Argb DynamicScheme::GetTertiaryContainer() const { | ||||||
|  |   return MaterialDynamicColors::TertiaryContainer().GetArgb(*this); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | Argb DynamicScheme::GetOnTertiaryContainer() const { | ||||||
|  |   return MaterialDynamicColors::OnTertiaryContainer().GetArgb(*this); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | Argb DynamicScheme::GetError() const { | ||||||
|  |   return MaterialDynamicColors::Error().GetArgb(*this); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | Argb DynamicScheme::GetOnError() const { | ||||||
|  |   return MaterialDynamicColors::OnError().GetArgb(*this); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | Argb DynamicScheme::GetErrorContainer() const { | ||||||
|  |   return MaterialDynamicColors::ErrorContainer().GetArgb(*this); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | Argb DynamicScheme::GetOnErrorContainer() const { | ||||||
|  |   return MaterialDynamicColors::OnErrorContainer().GetArgb(*this); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | Argb DynamicScheme::GetPrimaryFixed() const { | ||||||
|  |   return MaterialDynamicColors::PrimaryFixed().GetArgb(*this); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | Argb DynamicScheme::GetPrimaryFixedDim() const { | ||||||
|  |   return MaterialDynamicColors::PrimaryFixedDim().GetArgb(*this); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | Argb DynamicScheme::GetOnPrimaryFixed() const { | ||||||
|  |   return MaterialDynamicColors::OnPrimaryFixed().GetArgb(*this); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | Argb DynamicScheme::GetOnPrimaryFixedVariant() const { | ||||||
|  |   return MaterialDynamicColors::OnPrimaryFixedVariant().GetArgb(*this); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | Argb DynamicScheme::GetSecondaryFixed() const { | ||||||
|  |   return MaterialDynamicColors::SecondaryFixed().GetArgb(*this); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | Argb DynamicScheme::GetSecondaryFixedDim() const { | ||||||
|  |   return MaterialDynamicColors::SecondaryFixedDim().GetArgb(*this); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | Argb DynamicScheme::GetOnSecondaryFixed() const { | ||||||
|  |   return MaterialDynamicColors::OnSecondaryFixed().GetArgb(*this); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | Argb DynamicScheme::GetOnSecondaryFixedVariant() const { | ||||||
|  |   return MaterialDynamicColors::OnSecondaryFixedVariant().GetArgb(*this); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | Argb DynamicScheme::GetTertiaryFixed() const { | ||||||
|  |   return MaterialDynamicColors::TertiaryFixed().GetArgb(*this); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | Argb DynamicScheme::GetTertiaryFixedDim() const { | ||||||
|  |   return MaterialDynamicColors::TertiaryFixedDim().GetArgb(*this); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | Argb DynamicScheme::GetOnTertiaryFixed() const { | ||||||
|  |   return MaterialDynamicColors::OnTertiaryFixed().GetArgb(*this); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | Argb DynamicScheme::GetOnTertiaryFixedVariant() const { | ||||||
|  |   return MaterialDynamicColors::OnTertiaryFixedVariant().GetArgb(*this); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | }  // namespace material_color_utilities
 | ||||||
							
								
								
									
										113
									
								
								src/material-colors/cpp/dynamiccolor/dynamic_scheme.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										113
									
								
								src/material-colors/cpp/dynamiccolor/dynamic_scheme.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,113 @@ | ||||||
|  | /*
 | ||||||
|  |  * Copyright 2023 Google LLC | ||||||
|  |  * | ||||||
|  |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * | ||||||
|  |  *      http://www.apache.org/licenses/LICENSE-2.0
 | ||||||
|  |  * | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | #ifndef CPP_DYNAMICCOLOR_DYNAMIC_SCHEME_H_ | ||||||
|  | #define CPP_DYNAMICCOLOR_DYNAMIC_SCHEME_H_ | ||||||
|  | 
 | ||||||
|  | #include <optional> | ||||||
|  | #include <vector> | ||||||
|  | 
 | ||||||
|  | #include "cpp/cam/hct.h" | ||||||
|  | #include "cpp/dynamiccolor/variant.h" | ||||||
|  | #include "cpp/palettes/tones.h" | ||||||
|  | #include "cpp/utils/utils.h" | ||||||
|  | 
 | ||||||
|  | namespace material_color_utilities { | ||||||
|  | 
 | ||||||
|  | struct DynamicScheme { | ||||||
|  |   Hct source_color_hct; | ||||||
|  |   Variant variant; | ||||||
|  |   bool is_dark; | ||||||
|  |   double contrast_level; | ||||||
|  | 
 | ||||||
|  |   TonalPalette primary_palette; | ||||||
|  |   TonalPalette secondary_palette; | ||||||
|  |   TonalPalette tertiary_palette; | ||||||
|  |   TonalPalette neutral_palette; | ||||||
|  |   TonalPalette neutral_variant_palette; | ||||||
|  |   TonalPalette error_palette; | ||||||
|  | 
 | ||||||
|  |   DynamicScheme(Hct source_color_hct, Variant variant, double contrast_level, | ||||||
|  |                 bool is_dark, TonalPalette primary_palette, | ||||||
|  |                 TonalPalette secondary_palette, TonalPalette tertiary_palette, | ||||||
|  |                 TonalPalette neutral_palette, | ||||||
|  |                 TonalPalette neutral_variant_palette, | ||||||
|  |                 std::optional<TonalPalette> error_palette = std::nullopt); | ||||||
|  | 
 | ||||||
|  |   static double GetRotatedHue(Hct source_color, std::vector<double> hues, | ||||||
|  |                               std::vector<double> rotations); | ||||||
|  | 
 | ||||||
|  |   Argb SourceColorArgb() const; | ||||||
|  | 
 | ||||||
|  |   Argb GetPrimaryPaletteKeyColor() const; | ||||||
|  |   Argb GetSecondaryPaletteKeyColor() const; | ||||||
|  |   Argb GetTertiaryPaletteKeyColor() const; | ||||||
|  |   Argb GetNeutralPaletteKeyColor() const; | ||||||
|  |   Argb GetNeutralVariantPaletteKeyColor() const; | ||||||
|  |   Argb GetBackground() const; | ||||||
|  |   Argb GetOnBackground() const; | ||||||
|  |   Argb GetSurface() const; | ||||||
|  |   Argb GetSurfaceDim() const; | ||||||
|  |   Argb GetSurfaceBright() const; | ||||||
|  |   Argb GetSurfaceContainerLowest() const; | ||||||
|  |   Argb GetSurfaceContainerLow() const; | ||||||
|  |   Argb GetSurfaceContainer() const; | ||||||
|  |   Argb GetSurfaceContainerHigh() const; | ||||||
|  |   Argb GetSurfaceContainerHighest() const; | ||||||
|  |   Argb GetOnSurface() const; | ||||||
|  |   Argb GetSurfaceVariant() const; | ||||||
|  |   Argb GetOnSurfaceVariant() const; | ||||||
|  |   Argb GetInverseSurface() const; | ||||||
|  |   Argb GetInverseOnSurface() const; | ||||||
|  |   Argb GetOutline() const; | ||||||
|  |   Argb GetOutlineVariant() const; | ||||||
|  |   Argb GetShadow() const; | ||||||
|  |   Argb GetScrim() const; | ||||||
|  |   Argb GetSurfaceTint() const; | ||||||
|  |   Argb GetPrimary() const; | ||||||
|  |   Argb GetOnPrimary() const; | ||||||
|  |   Argb GetPrimaryContainer() const; | ||||||
|  |   Argb GetOnPrimaryContainer() const; | ||||||
|  |   Argb GetInversePrimary() const; | ||||||
|  |   Argb GetSecondary() const; | ||||||
|  |   Argb GetOnSecondary() const; | ||||||
|  |   Argb GetSecondaryContainer() const; | ||||||
|  |   Argb GetOnSecondaryContainer() const; | ||||||
|  |   Argb GetTertiary() const; | ||||||
|  |   Argb GetOnTertiary() const; | ||||||
|  |   Argb GetTertiaryContainer() const; | ||||||
|  |   Argb GetOnTertiaryContainer() const; | ||||||
|  |   Argb GetError() const; | ||||||
|  |   Argb GetOnError() const; | ||||||
|  |   Argb GetErrorContainer() const; | ||||||
|  |   Argb GetOnErrorContainer() const; | ||||||
|  |   Argb GetPrimaryFixed() const; | ||||||
|  |   Argb GetPrimaryFixedDim() const; | ||||||
|  |   Argb GetOnPrimaryFixed() const; | ||||||
|  |   Argb GetOnPrimaryFixedVariant() const; | ||||||
|  |   Argb GetSecondaryFixed() const; | ||||||
|  |   Argb GetSecondaryFixedDim() const; | ||||||
|  |   Argb GetOnSecondaryFixed() const; | ||||||
|  |   Argb GetOnSecondaryFixedVariant() const; | ||||||
|  |   Argb GetTertiaryFixed() const; | ||||||
|  |   Argb GetTertiaryFixedDim() const; | ||||||
|  |   Argb GetOnTertiaryFixed() const; | ||||||
|  |   Argb GetOnTertiaryFixedVariant() const; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | }  // namespace material_color_utilities
 | ||||||
|  | 
 | ||||||
|  | #endif  // CPP_DYNAMICCOLOR_DYNAMIC_SCHEME_H_
 | ||||||
							
								
								
									
										1153
									
								
								src/material-colors/cpp/dynamiccolor/material_dynamic_colors.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										1153
									
								
								src/material-colors/cpp/dynamiccolor/material_dynamic_colors.cpp
									
										
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							|  | @ -0,0 +1,84 @@ | ||||||
|  | /*
 | ||||||
|  |  * Copyright 2023 Google LLC | ||||||
|  |  * | ||||||
|  |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * | ||||||
|  |  *      http://www.apache.org/licenses/LICENSE-2.0
 | ||||||
|  |  * | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | #ifndef CPP_DYNAMICCOLOR_MATERIAL_DYNAMIC_COLORS_H_ | ||||||
|  | #define CPP_DYNAMICCOLOR_MATERIAL_DYNAMIC_COLORS_H_ | ||||||
|  | 
 | ||||||
|  | #include "cpp/dynamiccolor/dynamic_color.h" | ||||||
|  | 
 | ||||||
|  | namespace material_color_utilities { | ||||||
|  | 
 | ||||||
|  | class MaterialDynamicColors { | ||||||
|  |  public: | ||||||
|  |   static DynamicColor PrimaryPaletteKeyColor(); | ||||||
|  |   static DynamicColor SecondaryPaletteKeyColor(); | ||||||
|  |   static DynamicColor TertiaryPaletteKeyColor(); | ||||||
|  |   static DynamicColor NeutralPaletteKeyColor(); | ||||||
|  |   static DynamicColor NeutralVariantPaletteKeyColor(); | ||||||
|  |   static DynamicColor Background(); | ||||||
|  |   static DynamicColor OnBackground(); | ||||||
|  |   static DynamicColor Surface(); | ||||||
|  |   static DynamicColor SurfaceDim(); | ||||||
|  |   static DynamicColor SurfaceBright(); | ||||||
|  |   static DynamicColor SurfaceContainerLowest(); | ||||||
|  |   static DynamicColor SurfaceContainerLow(); | ||||||
|  |   static DynamicColor SurfaceContainer(); | ||||||
|  |   static DynamicColor SurfaceContainerHigh(); | ||||||
|  |   static DynamicColor SurfaceContainerHighest(); | ||||||
|  |   static DynamicColor OnSurface(); | ||||||
|  |   static DynamicColor SurfaceVariant(); | ||||||
|  |   static DynamicColor OnSurfaceVariant(); | ||||||
|  |   static DynamicColor InverseSurface(); | ||||||
|  |   static DynamicColor InverseOnSurface(); | ||||||
|  |   static DynamicColor Outline(); | ||||||
|  |   static DynamicColor OutlineVariant(); | ||||||
|  |   static DynamicColor Shadow(); | ||||||
|  |   static DynamicColor Scrim(); | ||||||
|  |   static DynamicColor SurfaceTint(); | ||||||
|  |   static DynamicColor Primary(); | ||||||
|  |   static DynamicColor OnPrimary(); | ||||||
|  |   static DynamicColor PrimaryContainer(); | ||||||
|  |   static DynamicColor OnPrimaryContainer(); | ||||||
|  |   static DynamicColor InversePrimary(); | ||||||
|  |   static DynamicColor Secondary(); | ||||||
|  |   static DynamicColor OnSecondary(); | ||||||
|  |   static DynamicColor SecondaryContainer(); | ||||||
|  |   static DynamicColor OnSecondaryContainer(); | ||||||
|  |   static DynamicColor Tertiary(); | ||||||
|  |   static DynamicColor OnTertiary(); | ||||||
|  |   static DynamicColor TertiaryContainer(); | ||||||
|  |   static DynamicColor OnTertiaryContainer(); | ||||||
|  |   static DynamicColor Error(); | ||||||
|  |   static DynamicColor OnError(); | ||||||
|  |   static DynamicColor ErrorContainer(); | ||||||
|  |   static DynamicColor OnErrorContainer(); | ||||||
|  |   static DynamicColor PrimaryFixed(); | ||||||
|  |   static DynamicColor PrimaryFixedDim(); | ||||||
|  |   static DynamicColor OnPrimaryFixed(); | ||||||
|  |   static DynamicColor OnPrimaryFixedVariant(); | ||||||
|  |   static DynamicColor SecondaryFixed(); | ||||||
|  |   static DynamicColor SecondaryFixedDim(); | ||||||
|  |   static DynamicColor OnSecondaryFixed(); | ||||||
|  |   static DynamicColor OnSecondaryFixedVariant(); | ||||||
|  |   static DynamicColor TertiaryFixed(); | ||||||
|  |   static DynamicColor TertiaryFixedDim(); | ||||||
|  |   static DynamicColor OnTertiaryFixed(); | ||||||
|  |   static DynamicColor OnTertiaryFixedVariant(); | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | }  // namespace material_color_utilities
 | ||||||
|  | 
 | ||||||
|  | #endif  // CPP_DYNAMICCOLOR_MATERIAL_DYNAMIC_COLORS_H_
 | ||||||
							
								
								
									
										78
									
								
								src/material-colors/cpp/dynamiccolor/tone_delta_pair.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										78
									
								
								src/material-colors/cpp/dynamiccolor/tone_delta_pair.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,78 @@ | ||||||
|  | /*
 | ||||||
|  |  * Copyright 2023 Google LLC | ||||||
|  |  * | ||||||
|  |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * | ||||||
|  |  *      http://www.apache.org/licenses/LICENSE-2.0
 | ||||||
|  |  * | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | #ifndef CPP_DYNAMICCOLOR_TONE_DELTA_PAIR_H_ | ||||||
|  | #define CPP_DYNAMICCOLOR_TONE_DELTA_PAIR_H_ | ||||||
|  | 
 | ||||||
|  | #include "cpp/dynamiccolor/dynamic_color.h" | ||||||
|  | 
 | ||||||
|  | namespace material_color_utilities { | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Describes the different in tone between colors. | ||||||
|  |  */ | ||||||
|  | enum class TonePolarity { kDarker, kLighter, kNearer, kFarther }; | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Documents a constraint between two DynamicColors, in which their tones must | ||||||
|  |  * have a certain distance from each other. | ||||||
|  |  * | ||||||
|  |  * Prefer a DynamicColor with a background, this is for special cases when | ||||||
|  |  * designers want tonal distance, literally contrast, between two colors that | ||||||
|  |  * don't have a background / foreground relationship or a contrast guarantee. | ||||||
|  |  */ | ||||||
|  | struct ToneDeltaPair { | ||||||
|  |   DynamicColor role_a_; | ||||||
|  |   DynamicColor role_b_; | ||||||
|  |   double delta_; | ||||||
|  |   TonePolarity polarity_; | ||||||
|  |   bool stay_together_; | ||||||
|  | 
 | ||||||
|  |   /**
 | ||||||
|  |    * Documents a constraint in tone distance between two DynamicColors. | ||||||
|  |    * | ||||||
|  |    * The polarity is an adjective that describes "A", compared to "B". | ||||||
|  |    * | ||||||
|  |    * For instance, ToneDeltaPair(A, B, 15, 'darker', stayTogether) states that | ||||||
|  |    * A's tone should be at least 15 darker than B's. | ||||||
|  |    * | ||||||
|  |    * 'nearer' and 'farther' describes closeness to the surface roles. For | ||||||
|  |    * instance, ToneDeltaPair(A, B, 10, 'nearer', stayTogether) states that A | ||||||
|  |    * should be 10 lighter than B in light mode, and 10 darker than B in dark | ||||||
|  |    * mode. | ||||||
|  |    * | ||||||
|  |    * @param roleA The first role in a pair. | ||||||
|  |    * @param roleB The second role in a pair. | ||||||
|  |    * @param delta Required difference between tones. Absolute value, negative | ||||||
|  |    * values have undefined behavior. | ||||||
|  |    * @param polarity The relative relation between tones of roleA and roleB, | ||||||
|  |    * as described above. | ||||||
|  |    * @param stayTogether Whether these two roles should stay on the same side of | ||||||
|  |    * the "awkward zone" (T50-59). This is necessary for certain cases where | ||||||
|  |    * one role has two backgrounds. | ||||||
|  |    */ | ||||||
|  |   ToneDeltaPair(DynamicColor role_a, DynamicColor role_b, double delta, | ||||||
|  |                 TonePolarity polarity, bool stay_together) | ||||||
|  |       : role_a_(role_a), | ||||||
|  |         role_b_(role_b), | ||||||
|  |         delta_(delta), | ||||||
|  |         polarity_(polarity), | ||||||
|  |         stay_together_(stay_together) {} | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | }  // namespace material_color_utilities
 | ||||||
|  | 
 | ||||||
|  | #endif  // CPP_DYNAMICCOLOR_TONE_DELTA_PAIR_H_
 | ||||||
							
								
								
									
										36
									
								
								src/material-colors/cpp/dynamiccolor/variant.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										36
									
								
								src/material-colors/cpp/dynamiccolor/variant.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,36 @@ | ||||||
|  | /*
 | ||||||
|  |  * Copyright 2023 Google LLC | ||||||
|  |  * | ||||||
|  |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * | ||||||
|  |  *      http://www.apache.org/licenses/LICENSE-2.0
 | ||||||
|  |  * | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | #ifndef CPP_DYNAMICCOLOR_VARIANT_H_ | ||||||
|  | #define CPP_DYNAMICCOLOR_VARIANT_H_ | ||||||
|  | 
 | ||||||
|  | namespace material_color_utilities { | ||||||
|  | 
 | ||||||
|  | enum class Variant { | ||||||
|  |   kMonochrome, | ||||||
|  |   kNeutral, | ||||||
|  |   kTonalSpot, | ||||||
|  |   kVibrant, | ||||||
|  |   kExpressive, | ||||||
|  |   kFidelity, | ||||||
|  |   kContent, | ||||||
|  |   kRainbow, | ||||||
|  |   kFruitSalad, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | }  // namespace material_color_utilities
 | ||||||
|  | 
 | ||||||
|  | #endif  // CPP_DYNAMICCOLOR_VARIANT_H_
 | ||||||
							
								
								
									
										39
									
								
								src/material-colors/cpp/palettes/core.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								src/material-colors/cpp/palettes/core.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,39 @@ | ||||||
|  | /*
 | ||||||
|  |  * Copyright 2022 Google LLC | ||||||
|  |  * | ||||||
|  |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * | ||||||
|  |  *      http://www.apache.org/licenses/LICENSE-2.0
 | ||||||
|  |  * | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | #ifndef CPP_PALETTES_CORE_H_ | ||||||
|  | #define CPP_PALETTES_CORE_H_ | ||||||
|  | 
 | ||||||
|  | #include "cpp/palettes/tones.h" | ||||||
|  | 
 | ||||||
|  | namespace material_color_utilities { | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Comprises foundational palettes to build a color scheme. Generated from a | ||||||
|  |  * source color, these palettes will then be part of a [DynamicScheme] together | ||||||
|  |  * with appearance preferences. | ||||||
|  | */ | ||||||
|  | typedef struct { | ||||||
|  |     TonalPalette primary; | ||||||
|  |     TonalPalette secondary; | ||||||
|  |     TonalPalette tertiary; | ||||||
|  |     TonalPalette neutral; | ||||||
|  |     TonalPalette neutral_variant; | ||||||
|  | } CorePalettes; | ||||||
|  | 
 | ||||||
|  | }  // namespace material_color_utilities
 | ||||||
|  | 
 | ||||||
|  | #endif  // CPP_PALETTES_CORE_H_
 | ||||||
							
								
								
									
										116
									
								
								src/material-colors/cpp/palettes/tones.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										116
									
								
								src/material-colors/cpp/palettes/tones.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,116 @@ | ||||||
|  | /*
 | ||||||
|  |  * Copyright 2022 Google LLC | ||||||
|  |  * | ||||||
|  |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * | ||||||
|  |  *      http://www.apache.org/licenses/LICENSE-2.0
 | ||||||
|  |  * | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | #include "cpp/palettes/tones.h" | ||||||
|  | 
 | ||||||
|  | #include <cmath> | ||||||
|  | 
 | ||||||
|  | #include "cpp/cam/cam.h" | ||||||
|  | #include "cpp/cam/hct.h" | ||||||
|  | 
 | ||||||
|  | namespace material_color_utilities { | ||||||
|  | 
 | ||||||
|  | TonalPalette::TonalPalette(Argb argb) : key_color_(0.0, 0.0, 0.0) { | ||||||
|  |   Cam cam = CamFromInt(argb); | ||||||
|  |   hue_ = cam.hue; | ||||||
|  |   chroma_ = cam.chroma; | ||||||
|  |   key_color_ = KeyColor(cam.hue, cam.chroma).create(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TonalPalette::TonalPalette(Hct hct) | ||||||
|  |     : key_color_(hct.get_hue(), hct.get_chroma(), hct.get_tone()) { | ||||||
|  |   hue_ = hct.get_hue(); | ||||||
|  |   chroma_ = hct.get_chroma(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TonalPalette::TonalPalette(double hue, double chroma) | ||||||
|  |     : key_color_(hue, chroma, 0.0) { | ||||||
|  |   hue_ = hue; | ||||||
|  |   chroma_ = chroma; | ||||||
|  |   key_color_ = KeyColor(hue, chroma).create(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TonalPalette::TonalPalette(double hue, double chroma, Hct key_color) | ||||||
|  |     : key_color_(key_color.get_hue(), key_color.get_chroma(), | ||||||
|  |                  key_color.get_tone()) { | ||||||
|  |   hue_ = hue; | ||||||
|  |   chroma_ = chroma; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | Argb TonalPalette::get(double tone) const { | ||||||
|  |   return IntFromHcl(hue_, chroma_, tone); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | KeyColor::KeyColor(double hue, double requested_chroma) | ||||||
|  |     : hue_(hue), requested_chroma_(requested_chroma) {} | ||||||
|  | 
 | ||||||
|  | Hct KeyColor::create() { | ||||||
|  |   // Pivot around T50 because T50 has the most chroma available, on
 | ||||||
|  |   // average. Thus it is most likely to have a direct answer.
 | ||||||
|  |   const int pivot_tone = 50; | ||||||
|  |   const int tone_step_size = 1; | ||||||
|  |   // Epsilon to accept values slightly higher than the requested chroma.
 | ||||||
|  |   const double epsilon = 0.01; | ||||||
|  | 
 | ||||||
|  |   // Binary search to find the tone that can provide a chroma that is closest
 | ||||||
|  |   // to the requested chroma.
 | ||||||
|  |   int lower_tone = 0; | ||||||
|  |   int upper_tone = 100; | ||||||
|  |   while (lower_tone < upper_tone) { | ||||||
|  |     const int mid_tone = (lower_tone + upper_tone) / 2; | ||||||
|  |     bool is_ascending = | ||||||
|  |         max_chroma(mid_tone) < max_chroma(mid_tone + tone_step_size); | ||||||
|  |     bool sufficient_chroma = | ||||||
|  |         max_chroma(mid_tone) >= requested_chroma_ - epsilon; | ||||||
|  | 
 | ||||||
|  |     if (sufficient_chroma) { | ||||||
|  |       // Either range [lower_tone, mid_tone] or [mid_tone, upper_tone] has
 | ||||||
|  |       // the answer, so search in the range that is closer the pivot tone.
 | ||||||
|  |       if (abs(lower_tone - pivot_tone) < abs(upper_tone - pivot_tone)) { | ||||||
|  |         upper_tone = mid_tone; | ||||||
|  |       } else { | ||||||
|  |         if (lower_tone == mid_tone) { | ||||||
|  |           return Hct(hue_, requested_chroma_, lower_tone); | ||||||
|  |         } | ||||||
|  |         lower_tone = mid_tone; | ||||||
|  |       } | ||||||
|  |     } else { | ||||||
|  |       // As there's no sufficient chroma in the mid_tone, follow the direction
 | ||||||
|  |       // to the chroma peak.
 | ||||||
|  |       if (is_ascending) { | ||||||
|  |         lower_tone = mid_tone + tone_step_size; | ||||||
|  |       } else { | ||||||
|  |         // Keep mid_tone for potential chroma peak.
 | ||||||
|  |         upper_tone = mid_tone; | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   return Hct(hue_, requested_chroma_, lower_tone); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | double KeyColor::max_chroma(double tone) { | ||||||
|  |   auto it = chroma_cache_.find(tone); | ||||||
|  |   if (it != chroma_cache_.end()) { | ||||||
|  |     return it->second; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   double chroma = Hct(hue_, max_chroma_value_, tone).get_chroma(); | ||||||
|  |   chroma_cache_[tone] = chroma; | ||||||
|  |   return chroma; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | }  // namespace material_color_utilities
 | ||||||
							
								
								
									
										78
									
								
								src/material-colors/cpp/palettes/tones.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										78
									
								
								src/material-colors/cpp/palettes/tones.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,78 @@ | ||||||
|  | /*
 | ||||||
|  |  * Copyright 2022 Google LLC | ||||||
|  |  * | ||||||
|  |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * | ||||||
|  |  *      http://www.apache.org/licenses/LICENSE-2.0
 | ||||||
|  |  * | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | #ifndef CPP_PALETTES_TONES_H_ | ||||||
|  | #define CPP_PALETTES_TONES_H_ | ||||||
|  | 
 | ||||||
|  | #include <unordered_map> | ||||||
|  | 
 | ||||||
|  | #include "cpp/cam/hct.h" | ||||||
|  | #include "cpp/utils/utils.h" | ||||||
|  | 
 | ||||||
|  | namespace material_color_utilities { | ||||||
|  | 
 | ||||||
|  | class TonalPalette { | ||||||
|  |  public: | ||||||
|  |   explicit TonalPalette(Argb argb); | ||||||
|  |   TonalPalette(Hct hct); | ||||||
|  |   TonalPalette(double hue, double chroma); | ||||||
|  |   TonalPalette(double hue, double chroma, Hct key_color); | ||||||
|  | 
 | ||||||
|  |   /**
 | ||||||
|  |    * Returns the color for a given tone in this palette. | ||||||
|  |    * | ||||||
|  |    * @param tone 0.0 <= tone <= 100.0 | ||||||
|  |    * @return a color as an integer, in ARGB format. | ||||||
|  |    */ | ||||||
|  |   Argb get(double tone) const; | ||||||
|  | 
 | ||||||
|  |   double get_hue() const { return hue_; } | ||||||
|  |   double get_chroma() const { return chroma_; } | ||||||
|  |   Hct get_key_color() const { return key_color_; } | ||||||
|  | 
 | ||||||
|  |  private: | ||||||
|  |   double hue_; | ||||||
|  |   double chroma_; | ||||||
|  |   Hct key_color_; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Key color is a color that represents the hue and chroma of a tonal palette | ||||||
|  |  */ | ||||||
|  | class KeyColor { | ||||||
|  |  public: | ||||||
|  |   KeyColor(double hue, double requested_chroma); | ||||||
|  |   /**
 | ||||||
|  |    * Creates a key color from a [hue] and a [chroma]. | ||||||
|  |    * The key color is the first tone, starting from T50, matching the given hue | ||||||
|  |    * and chroma. | ||||||
|  |    * | ||||||
|  |    * @return Key color in Hct. | ||||||
|  |    */ | ||||||
|  |   Hct create(); | ||||||
|  | 
 | ||||||
|  |  private: | ||||||
|  |   const double max_chroma_value_ = 200.0; | ||||||
|  |   double hue_; | ||||||
|  |   double requested_chroma_; | ||||||
|  |   // Cache that maps tone to max chroma to avoid duplicated HCT calculation.
 | ||||||
|  |   std::unordered_map<double, double> chroma_cache_; | ||||||
|  | 
 | ||||||
|  |   double max_chroma(double tone); | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | }  // namespace material_color_utilities
 | ||||||
|  | #endif  // CPP_PALETTES_TONES_H_
 | ||||||
							
								
								
									
										85
									
								
								src/material-colors/cpp/palettes/tones_test.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										85
									
								
								src/material-colors/cpp/palettes/tones_test.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,85 @@ | ||||||
|  | /*
 | ||||||
|  |  * Copyright 2022 Google LLC | ||||||
|  |  * | ||||||
|  |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * | ||||||
|  |  *      http://www.apache.org/licenses/LICENSE-2.0
 | ||||||
|  |  * | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | #include "cpp/palettes/tones.h" | ||||||
|  | 
 | ||||||
|  | #include "testing/base/public/gunit.h" | ||||||
|  | #include "cpp/cam/hct.h" | ||||||
|  | #include "cpp/utils/utils.h" | ||||||
|  | 
 | ||||||
|  | namespace material_color_utilities { | ||||||
|  | 
 | ||||||
|  | namespace { | ||||||
|  | 
 | ||||||
|  | TEST(TonesTest, Blue) { | ||||||
|  |   Argb color = 0xff0000ff; | ||||||
|  |   TonalPalette tonal_palette = TonalPalette(color); | ||||||
|  |   EXPECT_EQ(HexFromArgb(tonal_palette.get(100)), "ffffffff"); | ||||||
|  |   EXPECT_EQ(HexFromArgb(tonal_palette.get(95)), "fff1efff"); | ||||||
|  |   EXPECT_EQ(HexFromArgb(tonal_palette.get(90)), "ffe0e0ff"); | ||||||
|  |   EXPECT_EQ(HexFromArgb(tonal_palette.get(80)), "ffbec2ff"); | ||||||
|  |   EXPECT_EQ(HexFromArgb(tonal_palette.get(70)), "ff9da3ff"); | ||||||
|  |   EXPECT_EQ(HexFromArgb(tonal_palette.get(60)), "ff7c84ff"); | ||||||
|  |   EXPECT_EQ(HexFromArgb(tonal_palette.get(50)), "ff5a64ff"); | ||||||
|  |   EXPECT_EQ(HexFromArgb(tonal_palette.get(40)), "ff343dff"); | ||||||
|  |   EXPECT_EQ(HexFromArgb(tonal_palette.get(30)), "ff0000ef"); | ||||||
|  |   EXPECT_EQ(HexFromArgb(tonal_palette.get(20)), "ff0001ac"); | ||||||
|  |   EXPECT_EQ(HexFromArgb(tonal_palette.get(10)), "ff00006e"); | ||||||
|  |   EXPECT_EQ(HexFromArgb(tonal_palette.get(0)), "ff000000"); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TEST(KeyColorTests, ExactChromaAvailable) { | ||||||
|  |   // Requested chroma is exactly achievable at a certain tone.
 | ||||||
|  |   TonalPalette palette = TonalPalette(50.0, 60.0); | ||||||
|  |   Hct result = palette.get_key_color(); | ||||||
|  | 
 | ||||||
|  |   EXPECT_NEAR(result.get_hue(), 50.0, 10.0); | ||||||
|  |   EXPECT_NEAR(result.get_chroma(), 60.0, 0.5); | ||||||
|  |   // Tone might vary, but should be within the range from 0 to 100.
 | ||||||
|  |   EXPECT_GT(result.get_tone(), 0); | ||||||
|  |   EXPECT_LT(result.get_tone(), 100); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TEST(KeyColorTests, UnusuallyHighChroma) { | ||||||
|  |   // Requested chroma is above what is achievable. For Hue 149, chroma peak
 | ||||||
|  |   // is 89.6 at Tone 87.9. The result key color's chroma should be close to the
 | ||||||
|  |   // chroma peak.
 | ||||||
|  |   TonalPalette palette = TonalPalette(149.0, 200.0); | ||||||
|  |   Hct result = palette.get_key_color(); | ||||||
|  | 
 | ||||||
|  |   EXPECT_NEAR(result.get_hue(), 149.0, 10.0); | ||||||
|  |   EXPECT_GT(result.get_chroma(), 89.0); | ||||||
|  |   // Tone might vary, but should be within the range from 0 to 100.
 | ||||||
|  |   EXPECT_GT(result.get_tone(), 0); | ||||||
|  |   EXPECT_LT(result.get_tone(), 100); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TEST(KeyColorTests, UnusuallyLowChroma) { | ||||||
|  |   // By definition, the key color should be the first tone, starting from Tone
 | ||||||
|  |   // 50, matching the given hue and chroma. When requesting a very low chroma,
 | ||||||
|  |   // the result should be close to Tone 50, since most tones can produce a low
 | ||||||
|  |   // chroma.
 | ||||||
|  |   TonalPalette palette = TonalPalette(50.0, 3.0); | ||||||
|  |   Hct result = palette.get_key_color(); | ||||||
|  | 
 | ||||||
|  |   // Higher error tolerance for hue when the requested chroma is unusually low.
 | ||||||
|  |   EXPECT_NEAR(result.get_hue(), 50.0, 10.0); | ||||||
|  |   EXPECT_NEAR(result.get_chroma(), 3.0, 0.5); | ||||||
|  |   EXPECT_NEAR(result.get_tone(), 50.0, 0.5); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | }  // namespace
 | ||||||
|  | }  // namespace material_color_utilities
 | ||||||
							
								
								
									
										60
									
								
								src/material-colors/cpp/quantize/celebi.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										60
									
								
								src/material-colors/cpp/quantize/celebi.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,60 @@ | ||||||
|  | /*
 | ||||||
|  |  * Copyright 2022 Google LLC | ||||||
|  |  * | ||||||
|  |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * | ||||||
|  |  *      http://www.apache.org/licenses/LICENSE-2.0
 | ||||||
|  |  * | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | #include "cpp/quantize/celebi.h" | ||||||
|  | 
 | ||||||
|  | #include <cstddef> | ||||||
|  | #include <cstdio> | ||||||
|  | #include <cstdlib> | ||||||
|  | #include <vector> | ||||||
|  | 
 | ||||||
|  | #include "cpp/quantize/wsmeans.h" | ||||||
|  | #include "cpp/quantize/wu.h" | ||||||
|  | #include "cpp/utils/utils.h" | ||||||
|  | 
 | ||||||
|  | namespace material_color_utilities { | ||||||
|  | 
 | ||||||
|  | QuantizerResult QuantizeCelebi(const std::vector<Argb>& pixels, | ||||||
|  |                                uint16_t max_colors) { | ||||||
|  |   if (max_colors == 0 || pixels.empty()) { | ||||||
|  |     return QuantizerResult(); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   if (max_colors > 256) { | ||||||
|  |     max_colors = 256; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   int pixel_count = pixels.size(); | ||||||
|  | 
 | ||||||
|  |   std::vector<Argb> opaque_pixels; | ||||||
|  |   opaque_pixels.reserve(pixel_count); | ||||||
|  |   for (int i = 0; i < pixel_count; i++) { | ||||||
|  |     int pixel = pixels[i]; | ||||||
|  |     if (!IsOpaque(pixel)) { | ||||||
|  |       continue; | ||||||
|  |     } | ||||||
|  |     opaque_pixels.push_back(pixel); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   std::vector<Argb> wu_result = QuantizeWu(opaque_pixels, max_colors); | ||||||
|  | 
 | ||||||
|  |   QuantizerResult result = | ||||||
|  |       QuantizeWsmeans(opaque_pixels, wu_result, max_colors); | ||||||
|  | 
 | ||||||
|  |   return result; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | }  // namespace material_color_utilities
 | ||||||
							
								
								
									
										35
									
								
								src/material-colors/cpp/quantize/celebi.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								src/material-colors/cpp/quantize/celebi.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,35 @@ | ||||||
|  | /*
 | ||||||
|  |  * Copyright 2022 Google LLC | ||||||
|  |  * | ||||||
|  |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * | ||||||
|  |  *      http://www.apache.org/licenses/LICENSE-2.0
 | ||||||
|  |  * | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | #ifndef CPP_QUANTIZE_CELEBI_H_ | ||||||
|  | #define CPP_QUANTIZE_CELEBI_H_ | ||||||
|  | 
 | ||||||
|  | #include <stdint.h> | ||||||
|  | #include <stdlib.h> | ||||||
|  | 
 | ||||||
|  | #include <vector> | ||||||
|  | 
 | ||||||
|  | #include "cpp/quantize/wsmeans.h" | ||||||
|  | #include "cpp/utils/utils.h" | ||||||
|  | 
 | ||||||
|  | namespace material_color_utilities { | ||||||
|  | 
 | ||||||
|  | QuantizerResult QuantizeCelebi(const std::vector<Argb>& pixels, | ||||||
|  |                                uint16_t max_colors); | ||||||
|  | 
 | ||||||
|  | }  // namespace material_color_utilities
 | ||||||
|  | 
 | ||||||
|  | #endif  // CPP_QUANTIZE_CELEBI_H_
 | ||||||
							
								
								
									
										131
									
								
								src/material-colors/cpp/quantize/celebi_test.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										131
									
								
								src/material-colors/cpp/quantize/celebi_test.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,131 @@ | ||||||
|  | /*
 | ||||||
|  |  * Copyright 2022 Google LLC | ||||||
|  |  * | ||||||
|  |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * | ||||||
|  |  *      http://www.apache.org/licenses/LICENSE-2.0
 | ||||||
|  |  * | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | #include "cpp/quantize/celebi.h" | ||||||
|  | 
 | ||||||
|  | #include <cstdint> | ||||||
|  | #include <vector> | ||||||
|  | 
 | ||||||
|  | #include "testing/base/public/gunit.h" | ||||||
|  | 
 | ||||||
|  | namespace material_color_utilities { | ||||||
|  | 
 | ||||||
|  | namespace { | ||||||
|  | 
 | ||||||
|  | TEST(CelebiTest, FullImage) { | ||||||
|  |   std::vector<Argb> pixels(12544); | ||||||
|  |   for (size_t i = 0; i < pixels.size(); i++) { | ||||||
|  |     // Creates 128 distinct colors
 | ||||||
|  |     pixels[i] = i % 8000; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   int iterations = 1; | ||||||
|  |   uint16_t max_colors = 128; | ||||||
|  |   double sum = 0; | ||||||
|  | 
 | ||||||
|  |   for (int i = 0; i < iterations; i++) { | ||||||
|  |     clock_t begin = clock(); | ||||||
|  |     QuantizeCelebi(pixels, max_colors); | ||||||
|  |     clock_t end = clock(); | ||||||
|  |     double time_spent = static_cast<double>(end - begin) / CLOCKS_PER_SEC; | ||||||
|  |     sum += time_spent; | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TEST(CelebiTest, OneRed) { | ||||||
|  |   std::vector<Argb> pixels; | ||||||
|  |   pixels.push_back(0xffff0000); | ||||||
|  |   QuantizerResult result = QuantizeCelebi(pixels, 256); | ||||||
|  |   EXPECT_EQ(result.color_to_count.size(), 1u); | ||||||
|  |   EXPECT_EQ(result.color_to_count[0xffff0000], 1u); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TEST(CelebiTest, OneGreen) { | ||||||
|  |   std::vector<Argb> pixels; | ||||||
|  |   pixels.push_back(0xff00ff00); | ||||||
|  |   QuantizerResult result = QuantizeCelebi(pixels, 256); | ||||||
|  |   EXPECT_EQ(result.color_to_count.size(), 1u); | ||||||
|  |   EXPECT_EQ(result.color_to_count[0xff00ff00], 1u); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TEST(CelebiTest, OneBlue) { | ||||||
|  |   std::vector<Argb> pixels; | ||||||
|  |   pixels.push_back(0xff0000ff); | ||||||
|  |   QuantizerResult result = QuantizeCelebi(pixels, 256); | ||||||
|  |   EXPECT_EQ(result.color_to_count.size(), 1u); | ||||||
|  |   EXPECT_EQ(result.color_to_count[0xff0000ff], 1u); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TEST(CelebiTest, FiveBlue) { | ||||||
|  |   std::vector<Argb> pixels; | ||||||
|  |   for (int i = 0; i < 5; i++) { | ||||||
|  |     pixels.push_back(0xff0000ff); | ||||||
|  |   } | ||||||
|  |   QuantizerResult result = QuantizeCelebi(pixels, 256); | ||||||
|  |   EXPECT_EQ(result.color_to_count.size(), 1u); | ||||||
|  |   EXPECT_EQ(result.color_to_count[0xff0000ff], 5u); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TEST(CelebiTest, OneRedOneGreenOneBlue) { | ||||||
|  |   std::vector<Argb> pixels; | ||||||
|  |   pixels.push_back(0xffff0000); | ||||||
|  |   pixels.push_back(0xff00ff00); | ||||||
|  |   pixels.push_back(0xff0000ff); | ||||||
|  |   QuantizerResult result = QuantizeCelebi(pixels, 256); | ||||||
|  |   EXPECT_EQ(result.color_to_count.size(), 3u); | ||||||
|  |   EXPECT_EQ(result.color_to_count[0xffff0000], 1u); | ||||||
|  |   EXPECT_EQ(result.color_to_count[0xff00ff00], 1u); | ||||||
|  |   EXPECT_EQ(result.color_to_count[0xff0000ff], 1u); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TEST(CelebiTest, TwoRedThreeGreen) { | ||||||
|  |   std::vector<Argb> pixels; | ||||||
|  |   pixels.push_back(0xffff0000); | ||||||
|  |   pixels.push_back(0xffff0000); | ||||||
|  |   pixels.push_back(0xff00ff00); | ||||||
|  |   pixels.push_back(0xff00ff00); | ||||||
|  |   pixels.push_back(0xff00ff00); | ||||||
|  |   QuantizerResult result = QuantizeCelebi(pixels, 256); | ||||||
|  |   EXPECT_EQ(result.color_to_count.size(), 2u); | ||||||
|  |   EXPECT_EQ(result.color_to_count[0xffff0000], 2u); | ||||||
|  |   EXPECT_EQ(result.color_to_count[0xff00ff00], 3u); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TEST(CelebiTest, NoColors) { | ||||||
|  |   std::vector<Argb> pixels; | ||||||
|  |   pixels.push_back(0xFFFFFFFF); | ||||||
|  |   QuantizerResult result = QuantizeCelebi(pixels, 0); | ||||||
|  |   EXPECT_TRUE(result.color_to_count.empty()); | ||||||
|  |   EXPECT_TRUE(result.input_pixel_to_cluster_pixel.empty()); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TEST(CelebiTest, SingleTransparent) { | ||||||
|  |   std::vector<Argb> pixels; | ||||||
|  |   pixels.push_back(0x20F93013); | ||||||
|  |   QuantizerResult result = QuantizeCelebi(pixels, 1); | ||||||
|  |   EXPECT_TRUE(result.color_to_count.empty()); | ||||||
|  |   EXPECT_TRUE(result.input_pixel_to_cluster_pixel.empty()); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TEST(CelebiTest, TooManyColors) { | ||||||
|  |   std::vector<Argb> pixels; | ||||||
|  |   QuantizerResult result = QuantizeCelebi(pixels, 32767); | ||||||
|  |   EXPECT_TRUE(result.color_to_count.empty()); | ||||||
|  |   EXPECT_TRUE(result.input_pixel_to_cluster_pixel.empty()); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | }  // namespace
 | ||||||
|  | }  // namespace material_color_utilities
 | ||||||
							
								
								
									
										96
									
								
								src/material-colors/cpp/quantize/lab.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										96
									
								
								src/material-colors/cpp/quantize/lab.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,96 @@ | ||||||
|  | /*
 | ||||||
|  |  * Copyright 2022 Google LLC | ||||||
|  |  * | ||||||
|  |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * | ||||||
|  |  *      http://www.apache.org/licenses/LICENSE-2.0
 | ||||||
|  |  * | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | #include "cpp/quantize/lab.h" | ||||||
|  | 
 | ||||||
|  | #include <math.h> | ||||||
|  | 
 | ||||||
|  | #include "cpp/utils/utils.h" | ||||||
|  | 
 | ||||||
|  | namespace material_color_utilities { | ||||||
|  | 
 | ||||||
|  | Argb IntFromLab(const Lab lab) { | ||||||
|  |   double e = 216.0 / 24389.0; | ||||||
|  |   double kappa = 24389.0 / 27.0; | ||||||
|  |   double ke = 8.0; | ||||||
|  | 
 | ||||||
|  |   double fy = (lab.l + 16.0) / 116.0; | ||||||
|  |   double fx = (lab.a / 500.0) + fy; | ||||||
|  |   double fz = fy - (lab.b / 200.0); | ||||||
|  |   double fx3 = fx * fx * fx; | ||||||
|  |   double x_normalized = (fx3 > e) ? fx3 : (116.0 * fx - 16.0) / kappa; | ||||||
|  |   double y_normalized = (lab.l > ke) ? fy * fy * fy : (lab.l / kappa); | ||||||
|  |   double fz3 = fz * fz * fz; | ||||||
|  |   double z_normalized = (fz3 > e) ? fz3 : (116.0 * fz - 16.0) / kappa; | ||||||
|  |   double x = x_normalized * kWhitePointD65[0]; | ||||||
|  |   double y = y_normalized * kWhitePointD65[1]; | ||||||
|  |   double z = z_normalized * kWhitePointD65[2]; | ||||||
|  | 
 | ||||||
|  |   // intFromXyz
 | ||||||
|  |   double rL = 3.2406 * x - 1.5372 * y - 0.4986 * z; | ||||||
|  |   double gL = -0.9689 * x + 1.8758 * y + 0.0415 * z; | ||||||
|  |   double bL = 0.0557 * x - 0.2040 * y + 1.0570 * z; | ||||||
|  | 
 | ||||||
|  |   int red = Delinearized(rL); | ||||||
|  |   int green = Delinearized(gL); | ||||||
|  |   int blue = Delinearized(bL); | ||||||
|  | 
 | ||||||
|  |   return ArgbFromRgb(red, green, blue); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | Lab LabFromInt(const Argb argb) { | ||||||
|  |   int red = (argb & 0x00ff0000) >> 16; | ||||||
|  |   int green = (argb & 0x0000ff00) >> 8; | ||||||
|  |   int blue = (argb & 0x000000ff); | ||||||
|  |   double red_l = Linearized(red); | ||||||
|  |   double green_l = Linearized(green); | ||||||
|  |   double blue_l = Linearized(blue); | ||||||
|  |   double x = 0.41233895 * red_l + 0.35762064 * green_l + 0.18051042 * blue_l; | ||||||
|  |   double y = 0.2126 * red_l + 0.7152 * green_l + 0.0722 * blue_l; | ||||||
|  |   double z = 0.01932141 * red_l + 0.11916382 * green_l + 0.95034478 * blue_l; | ||||||
|  |   double y_normalized = y / kWhitePointD65[1]; | ||||||
|  |   double e = 216.0 / 24389.0; | ||||||
|  |   double kappa = 24389.0 / 27.0; | ||||||
|  |   double fy; | ||||||
|  |   if (y_normalized > e) { | ||||||
|  |     fy = pow(y_normalized, 1.0 / 3.0); | ||||||
|  |   } else { | ||||||
|  |     fy = (kappa * y_normalized + 16) / 116; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   double x_normalized = x / kWhitePointD65[0]; | ||||||
|  |   double fx; | ||||||
|  |   if (x_normalized > e) { | ||||||
|  |     fx = pow(x_normalized, 1.0 / 3.0); | ||||||
|  |   } else { | ||||||
|  |     fx = (kappa * x_normalized + 16) / 116; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   double z_normalized = z / kWhitePointD65[2]; | ||||||
|  |   double fz; | ||||||
|  |   if (z_normalized > e) { | ||||||
|  |     fz = pow(z_normalized, 1.0 / 3.0); | ||||||
|  |   } else { | ||||||
|  |     fz = (kappa * z_normalized + 16) / 116; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   double l = 116.0 * fy - 16; | ||||||
|  |   double a = 500.0 * (fx - fy); | ||||||
|  |   double b = 200.0 * (fy - fz); | ||||||
|  |   return {l, a, b}; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | }  // namespace material_color_utilities
 | ||||||
							
								
								
									
										57
									
								
								src/material-colors/cpp/quantize/lab.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										57
									
								
								src/material-colors/cpp/quantize/lab.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,57 @@ | ||||||
|  | /*
 | ||||||
|  |  * Copyright 2022 Google LLC | ||||||
|  |  * | ||||||
|  |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * | ||||||
|  |  *      http://www.apache.org/licenses/LICENSE-2.0
 | ||||||
|  |  * | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | #ifndef CPP_QUANTIZE_LAB_H_ | ||||||
|  | #define CPP_QUANTIZE_LAB_H_ | ||||||
|  | 
 | ||||||
|  | #include <algorithm> | ||||||
|  | #include <cmath> | ||||||
|  | #include <cstdint> | ||||||
|  | #include <cstdlib> | ||||||
|  | #include <map> | ||||||
|  | #include <set> | ||||||
|  | #include <string> | ||||||
|  | #include <unordered_map> | ||||||
|  | #include <unordered_set> | ||||||
|  | #include <vector> | ||||||
|  | 
 | ||||||
|  | #include "cpp/utils/utils.h" | ||||||
|  | 
 | ||||||
|  | namespace material_color_utilities { | ||||||
|  | 
 | ||||||
|  | struct Lab { | ||||||
|  |   double l = 0.0; | ||||||
|  |   double a = 0.0; | ||||||
|  |   double b = 0.0; | ||||||
|  | 
 | ||||||
|  |   double DeltaE(const Lab& lab) { | ||||||
|  |     double d_l = l - lab.l; | ||||||
|  |     double d_a = a - lab.a; | ||||||
|  |     double d_b = b - lab.b; | ||||||
|  |     return (d_l * d_l) + (d_a * d_a) + (d_b * d_b); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   std::string ToString() { | ||||||
|  |     return "Lab: L* " + std::to_string(l) + " a* " + std::to_string(a) + | ||||||
|  |            " b* " + std::to_string(b); | ||||||
|  |   } | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | Argb IntFromLab(const Lab lab); | ||||||
|  | Lab LabFromInt(const Argb argb); | ||||||
|  | 
 | ||||||
|  | }  // namespace material_color_utilities
 | ||||||
|  | #endif  // CPP_QUANTIZE_LAB_H_
 | ||||||
							
								
								
									
										267
									
								
								src/material-colors/cpp/quantize/wsmeans.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										267
									
								
								src/material-colors/cpp/quantize/wsmeans.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,267 @@ | ||||||
|  | /*
 | ||||||
|  |  * Copyright 2022 Google LLC | ||||||
|  |  * | ||||||
|  |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * | ||||||
|  |  *      http://www.apache.org/licenses/LICENSE-2.0
 | ||||||
|  |  * | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | #include "cpp/quantize/wsmeans.h" | ||||||
|  | 
 | ||||||
|  | #include <algorithm> | ||||||
|  | #include <cmath> | ||||||
|  | #include <cstdint> | ||||||
|  | #include <cstdlib> | ||||||
|  | #include <map> | ||||||
|  | #include <set> | ||||||
|  | #include <string> | ||||||
|  | #include <unordered_map> | ||||||
|  | #include <unordered_set> | ||||||
|  | #include <vector> | ||||||
|  | 
 | ||||||
|  | #include "cpp/quantize/lab.h" | ||||||
|  | 
 | ||||||
|  | constexpr int kMaxIterations = 100; | ||||||
|  | constexpr double kMinDeltaE = 3.0; | ||||||
|  | 
 | ||||||
|  | namespace material_color_utilities { | ||||||
|  | 
 | ||||||
|  | struct Swatch { | ||||||
|  |   Argb argb = 0; | ||||||
|  |   int population = 0; | ||||||
|  | 
 | ||||||
|  |   bool operator<(const Swatch& b) const { return population > b.population; } | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | struct DistanceToIndex { | ||||||
|  |   double distance = 0.0; | ||||||
|  |   int index = 0; | ||||||
|  | 
 | ||||||
|  |   bool operator<(const DistanceToIndex& a) const { | ||||||
|  |     return distance < a.distance; | ||||||
|  |   } | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | QuantizerResult QuantizeWsmeans(const std::vector<Argb>& input_pixels, | ||||||
|  |                                 const std::vector<Argb>& starting_clusters, | ||||||
|  |                                 uint16_t max_colors) { | ||||||
|  |   if (max_colors == 0 || input_pixels.empty()) { | ||||||
|  |     return QuantizerResult(); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   if (max_colors > 256) { | ||||||
|  |     // If colors is outside the range, just set it the max.
 | ||||||
|  |     max_colors = 256; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   uint32_t pixel_count = input_pixels.size(); | ||||||
|  |   //absl::flat_hash_map<Argb, int> pixel_to_count;
 | ||||||
|  |   std::unordered_map<Argb, int> pixel_to_count; | ||||||
|  | 
 | ||||||
|  |   std::vector<uint32_t> pixels; | ||||||
|  |   pixels.reserve(pixel_count); | ||||||
|  |   std::vector<Lab> points; | ||||||
|  |   points.reserve(pixel_count); | ||||||
|  |   for (Argb pixel : input_pixels) { | ||||||
|  |     // tested over 1000 runs with 128 colors, 12544 (112 x 112)
 | ||||||
|  |     // std::map 10.9 ms
 | ||||||
|  |     // std::unordered_map 10.2 ms
 | ||||||
|  |     // absl::btree_map 9.0 ms
 | ||||||
|  |     // absl::flat_hash_map 8.0 ms
 | ||||||
|  |     auto it = pixel_to_count.find(pixel); | ||||||
|  |     if (it != pixel_to_count.end()) { | ||||||
|  |       it->second++; | ||||||
|  | 
 | ||||||
|  |     } else { | ||||||
|  |       pixels.push_back(pixel); | ||||||
|  |       points.push_back(LabFromInt(pixel)); | ||||||
|  |       pixel_to_count[pixel] = 1; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   int cluster_count = std::min((int)max_colors, (int)points.size()); | ||||||
|  | 
 | ||||||
|  |   if (!starting_clusters.empty()) { | ||||||
|  |     cluster_count = std::min(cluster_count, (int)starting_clusters.size()); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   int pixel_count_sums[256] = {}; | ||||||
|  |   std::vector<Lab> clusters; | ||||||
|  |   clusters.reserve(starting_clusters.size()); | ||||||
|  |   for (int argb : starting_clusters) { | ||||||
|  |     clusters.push_back(LabFromInt(argb)); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   srand(42688); | ||||||
|  |   int additional_clusters_needed = cluster_count - clusters.size(); | ||||||
|  |   if (starting_clusters.empty() && additional_clusters_needed > 0) { | ||||||
|  |     for (int i = 0; i < additional_clusters_needed; i++) { | ||||||
|  |       // Adds a random Lab color to clusters.
 | ||||||
|  |       double l = rand() / (static_cast<double>(RAND_MAX)) * (100.0) + 0.0; | ||||||
|  |       double a = | ||||||
|  |           rand() / (static_cast<double>(RAND_MAX)) * (100.0 - -100.0) - 100.0; | ||||||
|  |       double b = | ||||||
|  |           rand() / (static_cast<double>(RAND_MAX)) * (100.0 - -100.0) - 100.0; | ||||||
|  |       clusters.push_back({l, a, b}); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   std::vector<int> cluster_indices; | ||||||
|  |   cluster_indices.reserve(points.size()); | ||||||
|  | 
 | ||||||
|  |   srand(42688); | ||||||
|  |   for (size_t i = 0; i < points.size(); i++) { | ||||||
|  |     cluster_indices.push_back(rand() % cluster_count); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   std::vector<std::vector<int>> index_matrix( | ||||||
|  |       cluster_count, std::vector<int>(cluster_count, 0)); | ||||||
|  | 
 | ||||||
|  |   std::vector<std::vector<DistanceToIndex>> distance_to_index_matrix( | ||||||
|  |       cluster_count, std::vector<DistanceToIndex>(cluster_count)); | ||||||
|  | 
 | ||||||
|  |   for (int iteration = 0; iteration < kMaxIterations; iteration++) { | ||||||
|  |     // Calculate cluster distances
 | ||||||
|  |     for (int i = 0; i < cluster_count; i++) { | ||||||
|  |       distance_to_index_matrix[i][i].distance = 0; | ||||||
|  |       distance_to_index_matrix[i][i].index = i; | ||||||
|  |       for (int j = i + 1; j < cluster_count; j++) { | ||||||
|  |         double distance = clusters[i].DeltaE(clusters[j]); | ||||||
|  | 
 | ||||||
|  |         distance_to_index_matrix[j][i].distance = distance; | ||||||
|  |         distance_to_index_matrix[j][i].index = i; | ||||||
|  |         distance_to_index_matrix[i][j].distance = distance; | ||||||
|  |         distance_to_index_matrix[i][j].index = j; | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  |       std::vector<DistanceToIndex> row = distance_to_index_matrix[i]; | ||||||
|  |       std::sort(row.begin(), row.end()); | ||||||
|  | 
 | ||||||
|  |       for (int j = 0; j < cluster_count; j++) { | ||||||
|  |         index_matrix[i][j] = row[j].index; | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // Reassign points
 | ||||||
|  |     bool color_moved = false; | ||||||
|  |     for (size_t i = 0; i < points.size(); i++) { | ||||||
|  |       Lab point = points[i]; | ||||||
|  | 
 | ||||||
|  |       int previous_cluster_index = cluster_indices[i]; | ||||||
|  |       Lab previous_cluster = clusters[previous_cluster_index]; | ||||||
|  |       double previous_distance = point.DeltaE(previous_cluster); | ||||||
|  |       double minimum_distance = previous_distance; | ||||||
|  |       int new_cluster_index = -1; | ||||||
|  | 
 | ||||||
|  |       for (int j = 0; j < cluster_count; j++) { | ||||||
|  |         if (distance_to_index_matrix[previous_cluster_index][j].distance >= | ||||||
|  |             4 * previous_distance) { | ||||||
|  |           continue; | ||||||
|  |         } | ||||||
|  |         double distance = point.DeltaE(clusters[j]); | ||||||
|  |         if (distance < minimum_distance) { | ||||||
|  |           minimum_distance = distance; | ||||||
|  |           new_cluster_index = j; | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |       if (new_cluster_index != -1) { | ||||||
|  |         double distanceChange = | ||||||
|  |             abs(sqrt(minimum_distance) - sqrt(previous_distance)); | ||||||
|  |         if (distanceChange > kMinDeltaE) { | ||||||
|  |           color_moved = true; | ||||||
|  |           cluster_indices[i] = new_cluster_index; | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if (!color_moved && (iteration != 0)) { | ||||||
|  |       break; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // Recalculate cluster centers
 | ||||||
|  |     double component_a_sums[256] = {}; | ||||||
|  |     double component_b_sums[256] = {}; | ||||||
|  |     double component_c_sums[256] = {}; | ||||||
|  |     for (int i = 0; i < cluster_count; i++) { | ||||||
|  |       pixel_count_sums[i] = 0; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     for (size_t i = 0; i < points.size(); i++) { | ||||||
|  |       int clusterIndex = cluster_indices[i]; | ||||||
|  |       Lab point = points[i]; | ||||||
|  |       int count = pixel_to_count[pixels[i]]; | ||||||
|  | 
 | ||||||
|  |       pixel_count_sums[clusterIndex] += count; | ||||||
|  |       component_a_sums[clusterIndex] += (point.l * count); | ||||||
|  |       component_b_sums[clusterIndex] += (point.a * count); | ||||||
|  |       component_c_sums[clusterIndex] += (point.b * count); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     for (int i = 0; i < cluster_count; i++) { | ||||||
|  |       int count = pixel_count_sums[i]; | ||||||
|  |       if (count == 0) { | ||||||
|  |         clusters[i] = {0, 0, 0}; | ||||||
|  |         continue; | ||||||
|  |       } | ||||||
|  |       double a = component_a_sums[i] / count; | ||||||
|  |       double b = component_b_sums[i] / count; | ||||||
|  |       double c = component_c_sums[i] / count; | ||||||
|  |       clusters[i] = {a, b, c}; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   std::vector<Swatch> swatches; | ||||||
|  |   std::vector<Argb> cluster_argbs; | ||||||
|  |   std::vector<Argb> all_cluster_argbs; | ||||||
|  |   for (int i = 0; i < cluster_count; i++) { | ||||||
|  |     Argb possible_new_cluster = IntFromLab(clusters[i]); | ||||||
|  |     all_cluster_argbs.push_back(possible_new_cluster); | ||||||
|  | 
 | ||||||
|  |     int count = pixel_count_sums[i]; | ||||||
|  |     if (count == 0) { | ||||||
|  |       continue; | ||||||
|  |     } | ||||||
|  |     int use_new_cluster = 1; | ||||||
|  |     for (size_t j = 0; j < swatches.size(); j++) { | ||||||
|  |       if (swatches[j].argb == possible_new_cluster) { | ||||||
|  |         swatches[j].population += count; | ||||||
|  |         use_new_cluster = 0; | ||||||
|  |         break; | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if (use_new_cluster == 0) { | ||||||
|  |       continue; | ||||||
|  |     } | ||||||
|  |     cluster_argbs.push_back(possible_new_cluster); | ||||||
|  |     swatches.push_back({possible_new_cluster, count}); | ||||||
|  |   } | ||||||
|  |   std::sort(swatches.begin(), swatches.end()); | ||||||
|  | 
 | ||||||
|  |   // Constructs the quantizer result to return.
 | ||||||
|  | 
 | ||||||
|  |   std::map<Argb, uint32_t> color_to_count; | ||||||
|  |   for (size_t i = 0; i < swatches.size(); i++) { | ||||||
|  |     color_to_count[swatches[i].argb] = swatches[i].population; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   std::map<Argb, Argb> input_pixel_to_cluster_pixel; | ||||||
|  |   for (size_t i = 0; i < points.size(); i++) { | ||||||
|  |     int pixel = pixels[i]; | ||||||
|  |     int cluster_index = cluster_indices[i]; | ||||||
|  |     int cluster_argb = all_cluster_argbs[cluster_index]; | ||||||
|  |     input_pixel_to_cluster_pixel[pixel] = cluster_argb; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   return {color_to_count, input_pixel_to_cluster_pixel}; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | }  // namespace material_color_utilities
 | ||||||
							
								
								
									
										38
									
								
								src/material-colors/cpp/quantize/wsmeans.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								src/material-colors/cpp/quantize/wsmeans.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,38 @@ | ||||||
|  | /*
 | ||||||
|  |  * Copyright 2022 Google LLC | ||||||
|  |  * | ||||||
|  |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * | ||||||
|  |  *      http://www.apache.org/licenses/LICENSE-2.0
 | ||||||
|  |  * | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | #ifndef CPP_QUANTIZE_WSMEANS_H_ | ||||||
|  | #define CPP_QUANTIZE_WSMEANS_H_ | ||||||
|  | #include <stdint.h> | ||||||
|  | 
 | ||||||
|  | #include <map> | ||||||
|  | #include <vector> | ||||||
|  | 
 | ||||||
|  | #include "cpp/utils/utils.h" | ||||||
|  | 
 | ||||||
|  | namespace material_color_utilities { | ||||||
|  | 
 | ||||||
|  | struct QuantizerResult { | ||||||
|  |   std::map<Argb, uint32_t> color_to_count; | ||||||
|  |   std::map<Argb, Argb> input_pixel_to_cluster_pixel; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | QuantizerResult QuantizeWsmeans(const std::vector<Argb>& input_pixels, | ||||||
|  |                                 const std::vector<Argb>& starting_clusters, | ||||||
|  |                                 uint16_t max_colors); | ||||||
|  | }  // namespace material_color_utilities
 | ||||||
|  | 
 | ||||||
|  | #endif  // CPP_QUANTIZE_WSMEANS_H_
 | ||||||
							
								
								
									
										96
									
								
								src/material-colors/cpp/quantize/wsmeans_test.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										96
									
								
								src/material-colors/cpp/quantize/wsmeans_test.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,96 @@ | ||||||
|  | /*
 | ||||||
|  |  * Copyright 2022 Google LLC | ||||||
|  |  * | ||||||
|  |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * | ||||||
|  |  *      http://www.apache.org/licenses/LICENSE-2.0
 | ||||||
|  |  * | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | #include "cpp/quantize/wsmeans.h" | ||||||
|  | 
 | ||||||
|  | #include <vector> | ||||||
|  | 
 | ||||||
|  | #include "testing/base/public/gunit.h" | ||||||
|  | 
 | ||||||
|  | namespace material_color_utilities { | ||||||
|  | 
 | ||||||
|  | namespace { | ||||||
|  | TEST(WsmeansTest, FullImage) { | ||||||
|  |   std::vector<Argb> pixels(12544); | ||||||
|  |   for (size_t i = 0; i < pixels.size(); i++) { | ||||||
|  |     // Creates 128 distinct colors
 | ||||||
|  |     pixels[i] = i % 8000; | ||||||
|  |   } | ||||||
|  |   std::vector<Argb> starting_clusters; | ||||||
|  | 
 | ||||||
|  |   int iterations = 1; | ||||||
|  |   int max_colors = 128; | ||||||
|  | 
 | ||||||
|  |   double sum = 0; | ||||||
|  | 
 | ||||||
|  |   for (int i = 0; i < iterations; i++) { | ||||||
|  |     clock_t begin = clock(); | ||||||
|  |     QuantizeWsmeans(pixels, starting_clusters, max_colors); | ||||||
|  |     clock_t end = clock(); | ||||||
|  |     double time_spent = static_cast<double>(end - begin) / CLOCKS_PER_SEC; | ||||||
|  |     sum += time_spent; | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TEST(WsmeansTest, OneRedAndO) { | ||||||
|  |   std::vector<Argb> pixels; | ||||||
|  |   pixels.push_back(0xff141216); | ||||||
|  |   std::vector<Argb> starting_clusters; | ||||||
|  |   QuantizerResult result = QuantizeWsmeans(pixels, starting_clusters, 256); | ||||||
|  |   EXPECT_EQ(result.color_to_count.size(), 1u); | ||||||
|  |   EXPECT_EQ(result.color_to_count[0xff141216], 1u); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TEST(WsmeansTest, OneRed) { | ||||||
|  |   std::vector<Argb> pixels; | ||||||
|  |   pixels.push_back(0xffff0000); | ||||||
|  |   std::vector<Argb> starting_clusters; | ||||||
|  |   QuantizerResult result = QuantizeWsmeans(pixels, starting_clusters, 256); | ||||||
|  |   EXPECT_EQ(result.color_to_count.size(), 1u); | ||||||
|  |   EXPECT_EQ(result.color_to_count[0xffff0000], 1u); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TEST(WsmeansTest, OneGreen) { | ||||||
|  |   std::vector<Argb> pixels; | ||||||
|  |   pixels.push_back(0xff00ff00); | ||||||
|  |   std::vector<Argb> starting_clusters; | ||||||
|  |   QuantizerResult result = QuantizeWsmeans(pixels, starting_clusters, 256); | ||||||
|  |   EXPECT_EQ(result.color_to_count.size(), 1u); | ||||||
|  |   EXPECT_EQ(result.color_to_count[0xff00ff00], 1u); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TEST(WsmeansTest, OneBlue) { | ||||||
|  |   std::vector<Argb> pixels; | ||||||
|  |   pixels.push_back(0xff0000ff); | ||||||
|  |   std::vector<Argb> starting_clusters; | ||||||
|  |   QuantizerResult result = QuantizeWsmeans(pixels, starting_clusters, 256); | ||||||
|  |   EXPECT_EQ(result.color_to_count.size(), 1u); | ||||||
|  |   EXPECT_EQ(result.color_to_count[0xff0000ff], 1u); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TEST(WsmeansTest, FiveBlue) { | ||||||
|  |   std::vector<Argb> pixels; | ||||||
|  |   for (int i = 0; i < 5; i++) { | ||||||
|  |     pixels.push_back(0xff0000ff); | ||||||
|  |   } | ||||||
|  |   std::vector<Argb> starting_clusters; | ||||||
|  |   QuantizerResult result = QuantizeWsmeans(pixels, starting_clusters, 256); | ||||||
|  |   EXPECT_EQ(result.color_to_count.size(), 1u); | ||||||
|  |   EXPECT_EQ(result.color_to_count[0xff0000ff], 5u); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | }  // namespace
 | ||||||
|  | }  // namespace material_color_utilities
 | ||||||
							
								
								
									
										357
									
								
								src/material-colors/cpp/quantize/wu.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										357
									
								
								src/material-colors/cpp/quantize/wu.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,357 @@ | ||||||
|  | /*
 | ||||||
|  |  * Copyright 2022 Google LLC | ||||||
|  |  * | ||||||
|  |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * | ||||||
|  |  *      http://www.apache.org/licenses/LICENSE-2.0
 | ||||||
|  |  * | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | #include "cpp/quantize/wu.h" | ||||||
|  | 
 | ||||||
|  | #include <stdlib.h> | ||||||
|  | 
 | ||||||
|  | #include <cassert> | ||||||
|  | #include <cstdint> | ||||||
|  | #include <cstdio> | ||||||
|  | #include <vector> | ||||||
|  | 
 | ||||||
|  | #include "cpp/utils/utils.h" | ||||||
|  | 
 | ||||||
|  | namespace material_color_utilities { | ||||||
|  | 
 | ||||||
|  | struct Box { | ||||||
|  |   int r0 = 0; | ||||||
|  |   int r1 = 0; | ||||||
|  |   int g0 = 0; | ||||||
|  |   int g1 = 0; | ||||||
|  |   int b0 = 0; | ||||||
|  |   int b1 = 0; | ||||||
|  |   int vol = 0; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | enum class Direction { | ||||||
|  |   kRed, | ||||||
|  |   kGreen, | ||||||
|  |   kBlue, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | constexpr int kIndexBits = 5; | ||||||
|  | constexpr int kIndexCount = ((1 << kIndexBits) + 1); | ||||||
|  | constexpr int kTotalSize = (kIndexCount * kIndexCount * kIndexCount); | ||||||
|  | constexpr int kMaxColors = 256; | ||||||
|  | 
 | ||||||
|  | using IntArray = std::vector<int64_t>; | ||||||
|  | using DoubleArray = std::vector<double>; | ||||||
|  | 
 | ||||||
|  | int GetIndex(int r, int g, int b) { | ||||||
|  |   return (r << (kIndexBits * 2)) + (r << (kIndexBits + 1)) + (g << kIndexBits) + | ||||||
|  |          r + g + b; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void ConstructHistogram(const std::vector<Argb>& pixels, IntArray& weights, | ||||||
|  |                         IntArray& m_r, IntArray& m_g, IntArray& m_b, | ||||||
|  |                         DoubleArray& moments) { | ||||||
|  |   for (size_t i = 0; i < pixels.size(); i++) { | ||||||
|  |     Argb pixel = pixels[i]; | ||||||
|  |     int red = RedFromInt(pixel); | ||||||
|  |     int green = GreenFromInt(pixel); | ||||||
|  |     int blue = BlueFromInt(pixel); | ||||||
|  | 
 | ||||||
|  |     int bits_to_remove = 8 - kIndexBits; | ||||||
|  |     int index_r = (red >> bits_to_remove) + 1; | ||||||
|  |     int index_g = (green >> bits_to_remove) + 1; | ||||||
|  |     int index_b = (blue >> bits_to_remove) + 1; | ||||||
|  |     int index = GetIndex(index_r, index_g, index_b); | ||||||
|  | 
 | ||||||
|  |     weights[index]++; | ||||||
|  |     m_r[index] += red; | ||||||
|  |     m_g[index] += green; | ||||||
|  |     m_b[index] += blue; | ||||||
|  |     moments[index] += (red * red) + (green * green) + (blue * blue); | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void ComputeMoments(IntArray& weights, IntArray& m_r, IntArray& m_g, | ||||||
|  |                     IntArray& m_b, DoubleArray& moments) { | ||||||
|  |   for (int r = 1; r < kIndexCount; r++) { | ||||||
|  |     int64_t area[kIndexCount] = {}; | ||||||
|  |     int64_t area_r[kIndexCount] = {}; | ||||||
|  |     int64_t area_g[kIndexCount] = {}; | ||||||
|  |     int64_t area_b[kIndexCount] = {}; | ||||||
|  |     double area_2[kIndexCount] = {}; | ||||||
|  |     for (int g = 1; g < kIndexCount; g++) { | ||||||
|  |       int64_t line = 0; | ||||||
|  |       int64_t line_r = 0; | ||||||
|  |       int64_t line_g = 0; | ||||||
|  |       int64_t line_b = 0; | ||||||
|  |       double line_2 = 0.0; | ||||||
|  |       for (int b = 1; b < kIndexCount; b++) { | ||||||
|  |         int index = GetIndex(r, g, b); | ||||||
|  |         line += weights[index]; | ||||||
|  |         line_r += m_r[index]; | ||||||
|  |         line_g += m_g[index]; | ||||||
|  |         line_b += m_b[index]; | ||||||
|  |         line_2 += moments[index]; | ||||||
|  | 
 | ||||||
|  |         area[b] += line; | ||||||
|  |         area_r[b] += line_r; | ||||||
|  |         area_g[b] += line_g; | ||||||
|  |         area_b[b] += line_b; | ||||||
|  |         area_2[b] += line_2; | ||||||
|  | 
 | ||||||
|  |         int previous_index = GetIndex(r - 1, g, b); | ||||||
|  |         weights[index] = weights[previous_index] + area[b]; | ||||||
|  |         m_r[index] = m_r[previous_index] + area_r[b]; | ||||||
|  |         m_g[index] = m_g[previous_index] + area_g[b]; | ||||||
|  |         m_b[index] = m_b[previous_index] + area_b[b]; | ||||||
|  |         moments[index] = moments[previous_index] + area_2[b]; | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | int64_t Top(const Box& cube, const Direction direction, const int position, | ||||||
|  |             const IntArray& moment) { | ||||||
|  |   if (direction == Direction::kRed) { | ||||||
|  |     return (moment[GetIndex(position, cube.g1, cube.b1)] - | ||||||
|  |             moment[GetIndex(position, cube.g1, cube.b0)] - | ||||||
|  |             moment[GetIndex(position, cube.g0, cube.b1)] + | ||||||
|  |             moment[GetIndex(position, cube.g0, cube.b0)]); | ||||||
|  |   } else if (direction == Direction::kGreen) { | ||||||
|  |     return (moment[GetIndex(cube.r1, position, cube.b1)] - | ||||||
|  |             moment[GetIndex(cube.r1, position, cube.b0)] - | ||||||
|  |             moment[GetIndex(cube.r0, position, cube.b1)] + | ||||||
|  |             moment[GetIndex(cube.r0, position, cube.b0)]); | ||||||
|  |   } else { | ||||||
|  |     return (moment[GetIndex(cube.r1, cube.g1, position)] - | ||||||
|  |             moment[GetIndex(cube.r1, cube.g0, position)] - | ||||||
|  |             moment[GetIndex(cube.r0, cube.g1, position)] + | ||||||
|  |             moment[GetIndex(cube.r0, cube.g0, position)]); | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | int64_t Bottom(const Box& cube, const Direction direction, | ||||||
|  |                const IntArray& moment) { | ||||||
|  |   if (direction == Direction::kRed) { | ||||||
|  |     return (-moment[GetIndex(cube.r0, cube.g1, cube.b1)] + | ||||||
|  |             moment[GetIndex(cube.r0, cube.g1, cube.b0)] + | ||||||
|  |             moment[GetIndex(cube.r0, cube.g0, cube.b1)] - | ||||||
|  |             moment[GetIndex(cube.r0, cube.g0, cube.b0)]); | ||||||
|  |   } else if (direction == Direction::kGreen) { | ||||||
|  |     return (-moment[GetIndex(cube.r1, cube.g0, cube.b1)] + | ||||||
|  |             moment[GetIndex(cube.r1, cube.g0, cube.b0)] + | ||||||
|  |             moment[GetIndex(cube.r0, cube.g0, cube.b1)] - | ||||||
|  |             moment[GetIndex(cube.r0, cube.g0, cube.b0)]); | ||||||
|  |   } else { | ||||||
|  |     return (-moment[GetIndex(cube.r1, cube.g1, cube.b0)] + | ||||||
|  |             moment[GetIndex(cube.r1, cube.g0, cube.b0)] + | ||||||
|  |             moment[GetIndex(cube.r0, cube.g1, cube.b0)] - | ||||||
|  |             moment[GetIndex(cube.r0, cube.g0, cube.b0)]); | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | int64_t Vol(const Box& cube, const IntArray& moment) { | ||||||
|  |   return (moment[GetIndex(cube.r1, cube.g1, cube.b1)] - | ||||||
|  |           moment[GetIndex(cube.r1, cube.g1, cube.b0)] - | ||||||
|  |           moment[GetIndex(cube.r1, cube.g0, cube.b1)] + | ||||||
|  |           moment[GetIndex(cube.r1, cube.g0, cube.b0)] - | ||||||
|  |           moment[GetIndex(cube.r0, cube.g1, cube.b1)] + | ||||||
|  |           moment[GetIndex(cube.r0, cube.g1, cube.b0)] + | ||||||
|  |           moment[GetIndex(cube.r0, cube.g0, cube.b1)] - | ||||||
|  |           moment[GetIndex(cube.r0, cube.g0, cube.b0)]); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | double Variance(const Box& cube, const IntArray& weights, const IntArray& m_r, | ||||||
|  |                 const IntArray& m_g, const IntArray& m_b, | ||||||
|  |                 const DoubleArray& moments) { | ||||||
|  |   double dr = Vol(cube, m_r); | ||||||
|  |   double dg = Vol(cube, m_g); | ||||||
|  |   double db = Vol(cube, m_b); | ||||||
|  |   double xx = moments[GetIndex(cube.r1, cube.g1, cube.b1)] - | ||||||
|  |               moments[GetIndex(cube.r1, cube.g1, cube.b0)] - | ||||||
|  |               moments[GetIndex(cube.r1, cube.g0, cube.b1)] + | ||||||
|  |               moments[GetIndex(cube.r1, cube.g0, cube.b0)] - | ||||||
|  |               moments[GetIndex(cube.r0, cube.g1, cube.b1)] + | ||||||
|  |               moments[GetIndex(cube.r0, cube.g1, cube.b0)] + | ||||||
|  |               moments[GetIndex(cube.r0, cube.g0, cube.b1)] - | ||||||
|  |               moments[GetIndex(cube.r0, cube.g0, cube.b0)]; | ||||||
|  |   double hypotenuse = dr * dr + dg * dg + db * db; | ||||||
|  |   double volume = Vol(cube, weights); | ||||||
|  |   return xx - hypotenuse / volume; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | double Maximize(const Box& cube, const Direction direction, const int first, | ||||||
|  |                 const int last, int* cut, const int64_t whole_w, | ||||||
|  |                 const int64_t whole_r, const int64_t whole_g, | ||||||
|  |                 const int64_t whole_b, const IntArray& weights, | ||||||
|  |                 const IntArray& m_r, const IntArray& m_g, const IntArray& m_b) { | ||||||
|  |   int64_t bottom_r = Bottom(cube, direction, m_r); | ||||||
|  |   int64_t bottom_g = Bottom(cube, direction, m_g); | ||||||
|  |   int64_t bottom_b = Bottom(cube, direction, m_b); | ||||||
|  |   int64_t bottom_w = Bottom(cube, direction, weights); | ||||||
|  | 
 | ||||||
|  |   double max = 0.0; | ||||||
|  |   *cut = -1; | ||||||
|  | 
 | ||||||
|  |   int64_t half_r, half_g, half_b, half_w; | ||||||
|  |   for (int i = first; i < last; i++) { | ||||||
|  |     half_r = bottom_r + Top(cube, direction, i, m_r); | ||||||
|  |     half_g = bottom_g + Top(cube, direction, i, m_g); | ||||||
|  |     half_b = bottom_b + Top(cube, direction, i, m_b); | ||||||
|  |     half_w = bottom_w + Top(cube, direction, i, weights); | ||||||
|  |     if (half_w == 0) { | ||||||
|  |       continue; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     double temp = (static_cast<double>(half_r) * half_r + | ||||||
|  |                    static_cast<double>(half_g) * half_g + | ||||||
|  |                    static_cast<double>(half_b) * half_b) / | ||||||
|  |                   static_cast<double>(half_w); | ||||||
|  | 
 | ||||||
|  |     half_r = whole_r - half_r; | ||||||
|  |     half_g = whole_g - half_g; | ||||||
|  |     half_b = whole_b - half_b; | ||||||
|  |     half_w = whole_w - half_w; | ||||||
|  |     if (half_w == 0) { | ||||||
|  |       continue; | ||||||
|  |     } | ||||||
|  |     temp += (static_cast<double>(half_r) * half_r + | ||||||
|  |              static_cast<double>(half_g) * half_g + | ||||||
|  |              static_cast<double>(half_b) * half_b) / | ||||||
|  |             static_cast<double>(half_w); | ||||||
|  | 
 | ||||||
|  |     if (temp > max) { | ||||||
|  |       max = temp; | ||||||
|  |       *cut = i; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |   return max; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool Cut(Box& box1, Box& box2, const IntArray& weights, const IntArray& m_r, | ||||||
|  |          const IntArray& m_g, const IntArray& m_b) { | ||||||
|  |   int64_t whole_r = Vol(box1, m_r); | ||||||
|  |   int64_t whole_g = Vol(box1, m_g); | ||||||
|  |   int64_t whole_b = Vol(box1, m_b); | ||||||
|  |   int64_t whole_w = Vol(box1, weights); | ||||||
|  | 
 | ||||||
|  |   int cut_r, cut_g, cut_b; | ||||||
|  |   double max_r = | ||||||
|  |       Maximize(box1, Direction::kRed, box1.r0 + 1, box1.r1, &cut_r, whole_w, | ||||||
|  |                whole_r, whole_g, whole_b, weights, m_r, m_g, m_b); | ||||||
|  |   double max_g = | ||||||
|  |       Maximize(box1, Direction::kGreen, box1.g0 + 1, box1.g1, &cut_g, whole_w, | ||||||
|  |                whole_r, whole_g, whole_b, weights, m_r, m_g, m_b); | ||||||
|  |   double max_b = | ||||||
|  |       Maximize(box1, Direction::kBlue, box1.b0 + 1, box1.b1, &cut_b, whole_w, | ||||||
|  |                whole_r, whole_g, whole_b, weights, m_r, m_g, m_b); | ||||||
|  | 
 | ||||||
|  |   Direction direction; | ||||||
|  |   if (max_r >= max_g && max_r >= max_b) { | ||||||
|  |     direction = Direction::kRed; | ||||||
|  |     if (cut_r < 0) { | ||||||
|  |       return false; | ||||||
|  |     } | ||||||
|  |   } else if (max_g >= max_r && max_g >= max_b) { | ||||||
|  |     direction = Direction::kGreen; | ||||||
|  |   } else { | ||||||
|  |     direction = Direction::kBlue; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   box2.r1 = box1.r1; | ||||||
|  |   box2.g1 = box1.g1; | ||||||
|  |   box2.b1 = box1.b1; | ||||||
|  | 
 | ||||||
|  |   if (direction == Direction::kRed) { | ||||||
|  |     box2.r0 = box1.r1 = cut_r; | ||||||
|  |     box2.g0 = box1.g0; | ||||||
|  |     box2.b0 = box1.b0; | ||||||
|  |   } else if (direction == Direction::kGreen) { | ||||||
|  |     box2.r0 = box1.r0; | ||||||
|  |     box2.g0 = box1.g1 = cut_g; | ||||||
|  |     box2.b0 = box1.b0; | ||||||
|  |   } else { | ||||||
|  |     box2.r0 = box1.r0; | ||||||
|  |     box2.g0 = box1.g0; | ||||||
|  |     box2.b0 = box1.b1 = cut_b; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   box1.vol = (box1.r1 - box1.r0) * (box1.g1 - box1.g0) * (box1.b1 - box1.b0); | ||||||
|  |   box2.vol = (box2.r1 - box2.r0) * (box2.g1 - box2.g0) * (box2.b1 - box2.b0); | ||||||
|  |   return true; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | std::vector<Argb> QuantizeWu(const std::vector<Argb>& pixels, | ||||||
|  |                              uint16_t max_colors) { | ||||||
|  |   if (max_colors <= 0 || max_colors > 256 || pixels.empty()) { | ||||||
|  |     return std::vector<Argb>(); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   IntArray weights(kTotalSize, 0); | ||||||
|  |   IntArray moments_red(kTotalSize, 0); | ||||||
|  |   IntArray moments_green(kTotalSize, 0); | ||||||
|  |   IntArray moments_blue(kTotalSize, 0); | ||||||
|  |   DoubleArray moments(kTotalSize, 0.0); | ||||||
|  |   ConstructHistogram(pixels, weights, moments_red, moments_green, moments_blue, | ||||||
|  |                      moments); | ||||||
|  |   ComputeMoments(weights, moments_red, moments_green, moments_blue, moments); | ||||||
|  | 
 | ||||||
|  |   std::vector<Box> cubes(kMaxColors); | ||||||
|  |   cubes[0].r0 = cubes[0].g0 = cubes[0].b0 = 0; | ||||||
|  |   cubes[0].r1 = cubes[0].g1 = cubes[0].b1 = kIndexCount - 1; | ||||||
|  | 
 | ||||||
|  |   std::vector<double> volume_variance(kMaxColors); | ||||||
|  |   int next = 0; | ||||||
|  |   for (int i = 1; i < max_colors; ++i) { | ||||||
|  |     if (Cut(cubes[next], cubes[i], weights, moments_red, moments_green, | ||||||
|  |             moments_blue)) { | ||||||
|  |       volume_variance[next] = | ||||||
|  |           cubes[next].vol > 1 ? Variance(cubes[next], weights, moments_red, | ||||||
|  |                                          moments_green, moments_blue, moments) | ||||||
|  |                               : 0.0; | ||||||
|  |       volume_variance[i] = cubes[i].vol > 1 | ||||||
|  |                                ? Variance(cubes[i], weights, moments_red, | ||||||
|  |                                           moments_green, moments_blue, moments) | ||||||
|  |                                : 0.0; | ||||||
|  |     } else { | ||||||
|  |       volume_variance[next] = 0.0; | ||||||
|  |       i--; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     next = 0; | ||||||
|  |     double temp = volume_variance[0]; | ||||||
|  |     for (int j = 1; j <= i; j++) { | ||||||
|  |       if (volume_variance[j] > temp) { | ||||||
|  |         temp = volume_variance[j]; | ||||||
|  |         next = j; | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |     if (temp <= 0.0) { | ||||||
|  |       max_colors = i + 1; | ||||||
|  |       break; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   std::vector<Argb> out_colors; | ||||||
|  |   for (int i = 0; i < max_colors; ++i) { | ||||||
|  |     int64_t weight = Vol(cubes[i], weights); | ||||||
|  |     if (weight > 0) { | ||||||
|  |       int32_t red = Vol(cubes[i], moments_red) / weight; | ||||||
|  |       int32_t green = Vol(cubes[i], moments_green) / weight; | ||||||
|  |       int32_t blue = Vol(cubes[i], moments_blue) / weight; | ||||||
|  |       uint32_t argb = ArgbFromRgb(red, green, blue); | ||||||
|  |       out_colors.push_back(argb); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   return out_colors; | ||||||
|  | } | ||||||
|  | }  // namespace material_color_utilities
 | ||||||
							
								
								
									
										31
									
								
								src/material-colors/cpp/quantize/wu.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								src/material-colors/cpp/quantize/wu.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,31 @@ | ||||||
|  | /*
 | ||||||
|  |  * Copyright 2022 Google LLC | ||||||
|  |  * | ||||||
|  |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * | ||||||
|  |  *      http://www.apache.org/licenses/LICENSE-2.0
 | ||||||
|  |  * | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | #ifndef CPP_QUANTIZE_WU_H_ | ||||||
|  | #define CPP_QUANTIZE_WU_H_ | ||||||
|  | 
 | ||||||
|  | #include <stdint.h> | ||||||
|  | 
 | ||||||
|  | #include <vector> | ||||||
|  | 
 | ||||||
|  | #include "cpp/utils/utils.h" | ||||||
|  | 
 | ||||||
|  | namespace material_color_utilities { | ||||||
|  | 
 | ||||||
|  | std::vector<Argb> QuantizeWu(const std::vector<Argb>& pixels, | ||||||
|  |                              uint16_t max_colors); | ||||||
|  | } | ||||||
|  | #endif  // CPP_QUANTIZE_WU_H_
 | ||||||
							
								
								
									
										116
									
								
								src/material-colors/cpp/quantize/wu_test.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										116
									
								
								src/material-colors/cpp/quantize/wu_test.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,116 @@ | ||||||
|  | /*
 | ||||||
|  |  * Copyright 2022 Google LLC | ||||||
|  |  * | ||||||
|  |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * | ||||||
|  |  *      http://www.apache.org/licenses/LICENSE-2.0
 | ||||||
|  |  * | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | #include "cpp/quantize/wu.h" | ||||||
|  | 
 | ||||||
|  | #include <cstdint> | ||||||
|  | #include <vector> | ||||||
|  | 
 | ||||||
|  | #include "testing/base/public/gunit.h" | ||||||
|  | 
 | ||||||
|  | namespace material_color_utilities { | ||||||
|  | 
 | ||||||
|  | namespace { | ||||||
|  | 
 | ||||||
|  | TEST(WuTest, FullImage) { | ||||||
|  |   std::vector<Argb> pixels(12544); | ||||||
|  |   for (size_t i = 0; i < pixels.size(); i++) { | ||||||
|  |     // Creates 128 distinct colors
 | ||||||
|  |     pixels[i] = i % 8000; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   uint16_t max_colors = 128; | ||||||
|  | 
 | ||||||
|  |   QuantizeWu(pixels, max_colors); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TEST(WuTest, TwoRedThreeGreen) { | ||||||
|  |   std::vector<Argb> pixels; | ||||||
|  |   pixels.push_back(0xffff0000); | ||||||
|  |   pixels.push_back(0xffff0000); | ||||||
|  |   pixels.push_back(0xffff0000); | ||||||
|  |   pixels.push_back(0xff00ff00); | ||||||
|  |   pixels.push_back(0xff00ff00); | ||||||
|  |   std::vector<Argb> result = QuantizeWu(pixels, 256); | ||||||
|  |   EXPECT_EQ(result.size(), 2u); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TEST(WuTest, OneRed) { | ||||||
|  |   std::vector<Argb> pixels; | ||||||
|  |   pixels.push_back(0xffff0000); | ||||||
|  |   std::vector<Argb> result = QuantizeWu(pixels, 256); | ||||||
|  |   EXPECT_EQ(result.size(), 1u); | ||||||
|  |   EXPECT_EQ(result[0], 0xffff0000); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TEST(WuTest, OneGreen) { | ||||||
|  |   std::vector<Argb> pixels; | ||||||
|  |   pixels.push_back(0xff00ff00); | ||||||
|  |   std::vector<Argb> result = QuantizeWu(pixels, 256); | ||||||
|  |   EXPECT_EQ(result.size(), 1u); | ||||||
|  |   EXPECT_EQ(result[0], 0xff00ff00); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TEST(WuTest, OneBlue) { | ||||||
|  |   std::vector<Argb> pixels; | ||||||
|  |   pixels.push_back(0xff0000ff); | ||||||
|  |   std::vector<Argb> result = QuantizeWu(pixels, 256); | ||||||
|  |   EXPECT_EQ(result.size(), 1u); | ||||||
|  |   EXPECT_EQ(result[0], 0xff0000ff); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TEST(WuTest, FiveBlue) { | ||||||
|  |   std::vector<Argb> pixels; | ||||||
|  |   for (int i = 0; i < 5; i++) { | ||||||
|  |     pixels.push_back(0xff0000ff); | ||||||
|  |   } | ||||||
|  |   std::vector<Argb> result = QuantizeWu(pixels, 256); | ||||||
|  |   EXPECT_EQ(result.size(), 1u); | ||||||
|  |   EXPECT_EQ(result[0], 0xff0000ff); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TEST(WuTest, OneRedAndO) { | ||||||
|  |   std::vector<Argb> pixels; | ||||||
|  |   pixels.push_back(0xff141216); | ||||||
|  |   std::vector<Argb> result = QuantizeWu(pixels, 256); | ||||||
|  |   EXPECT_EQ(result.size(), 1u); | ||||||
|  |   EXPECT_EQ(result[0], 0xff141216); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TEST(WuTest, RedGreenBlue) { | ||||||
|  |   std::vector<Argb> pixels; | ||||||
|  |   pixels.push_back(0xffff0000); | ||||||
|  |   pixels.push_back(0xff00ff00); | ||||||
|  |   pixels.push_back(0xff0000ff); | ||||||
|  |   std::vector<Argb> result = QuantizeWu(pixels, 256); | ||||||
|  |   EXPECT_EQ(result.size(), 3u); | ||||||
|  |   EXPECT_EQ(result[0], 0xff0000ff); | ||||||
|  |   EXPECT_EQ(result[1], 0xffff0000); | ||||||
|  |   EXPECT_EQ(result[2], 0xff00ff00); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TEST(WuTest, Testonly) { | ||||||
|  |   std::vector<Argb> pixels; | ||||||
|  |   pixels.push_back(0xff010203); | ||||||
|  |   pixels.push_back(0xff665544); | ||||||
|  |   pixels.push_back(0xff708090); | ||||||
|  |   pixels.push_back(0xffc0ffee); | ||||||
|  |   pixels.push_back(0xfffedcba); | ||||||
|  |   std::vector<Argb> result = QuantizeWu(pixels, 256); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | }  // namespace
 | ||||||
|  | }  // namespace material_color_utilities
 | ||||||
							
								
								
									
										58
									
								
								src/material-colors/cpp/scheme/scheme_content.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										58
									
								
								src/material-colors/cpp/scheme/scheme_content.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,58 @@ | ||||||
|  | /*
 | ||||||
|  |  * Copyright 2023 Google LLC | ||||||
|  |  * | ||||||
|  |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * | ||||||
|  |  *      http://www.apache.org/licenses/LICENSE-2.0
 | ||||||
|  |  * | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | #include "cpp/scheme/scheme_content.h" | ||||||
|  | 
 | ||||||
|  | #include <cmath> | ||||||
|  | 
 | ||||||
|  | #include "cpp/cam/hct.h" | ||||||
|  | #include "cpp/dislike/dislike.h" | ||||||
|  | #include "cpp/dynamiccolor/dynamic_scheme.h" | ||||||
|  | #include "cpp/dynamiccolor/variant.h" | ||||||
|  | #include "cpp/palettes/tones.h" | ||||||
|  | #include "cpp/temperature/temperature_cache.h" | ||||||
|  | 
 | ||||||
|  | namespace material_color_utilities { | ||||||
|  | 
 | ||||||
|  | SchemeContent::SchemeContent(Hct set_source_color_hct, bool set_is_dark, | ||||||
|  |                              double set_contrast_level) | ||||||
|  |     : DynamicScheme( | ||||||
|  |           /*set_source_color_hct:*/ set_source_color_hct, | ||||||
|  |           /*variant:*/ Variant::kContent, | ||||||
|  |           /*contrast_level:*/ set_contrast_level, | ||||||
|  |           /*is_dark:*/ set_is_dark, | ||||||
|  |           /*primary_palette:*/ | ||||||
|  |           TonalPalette(set_source_color_hct.get_hue(), | ||||||
|  |                        set_source_color_hct.get_chroma()), | ||||||
|  |           /*secondary_palette:*/ | ||||||
|  |           TonalPalette(set_source_color_hct.get_hue(), | ||||||
|  |                        fmax(set_source_color_hct.get_chroma() - 32.0, | ||||||
|  |                             set_source_color_hct.get_chroma() * 0.5)), | ||||||
|  |           /*tertiary_palette:*/ | ||||||
|  |           TonalPalette(FixIfDisliked(TemperatureCache(set_source_color_hct) | ||||||
|  |                                          .GetAnalogousColors(3, 6) | ||||||
|  |                                          .at(2))), | ||||||
|  |           /*neutral_palette:*/ | ||||||
|  |           TonalPalette(set_source_color_hct.get_hue(), | ||||||
|  |                        set_source_color_hct.get_chroma() / 8.0), | ||||||
|  |           /*neutral_variant_palette:*/ | ||||||
|  |           TonalPalette(set_source_color_hct.get_hue(), | ||||||
|  |                        set_source_color_hct.get_chroma() / 8.0 + 4.0)) {} | ||||||
|  | 
 | ||||||
|  | SchemeContent::SchemeContent(Hct set_source_color_hct, bool set_is_dark) | ||||||
|  |     : SchemeContent::SchemeContent(set_source_color_hct, set_is_dark, 0.0) {} | ||||||
|  | 
 | ||||||
|  | }  // namespace material_color_utilities
 | ||||||
							
								
								
									
										33
									
								
								src/material-colors/cpp/scheme/scheme_content.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								src/material-colors/cpp/scheme/scheme_content.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,33 @@ | ||||||
|  | /*
 | ||||||
|  |  * Copyright 2023 Google LLC | ||||||
|  |  * | ||||||
|  |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * | ||||||
|  |  *      http://www.apache.org/licenses/LICENSE-2.0
 | ||||||
|  |  * | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | #ifndef CPP_SCHEME_SCHEME_CONTENT_H_ | ||||||
|  | #define CPP_SCHEME_SCHEME_CONTENT_H_ | ||||||
|  | 
 | ||||||
|  | #include "cpp/cam/hct.h" | ||||||
|  | #include "cpp/dynamiccolor/dynamic_scheme.h" | ||||||
|  | 
 | ||||||
|  | namespace material_color_utilities { | ||||||
|  | 
 | ||||||
|  | struct SchemeContent : public DynamicScheme { | ||||||
|  |   SchemeContent(Hct set_source_color_hct, bool set_is_dark, | ||||||
|  |                 double set_contrast_level); | ||||||
|  |   SchemeContent(Hct set_source_color_hct, bool set_is_dark); | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | }  // namespace material_color_utilities
 | ||||||
|  | 
 | ||||||
|  | #endif  // CPP_SCHEME_SCHEME_CONTENT_H_
 | ||||||
							
								
								
									
										62
									
								
								src/material-colors/cpp/scheme/scheme_expressive.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										62
									
								
								src/material-colors/cpp/scheme/scheme_expressive.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,62 @@ | ||||||
|  | /*
 | ||||||
|  |  * Copyright 2023 Google LLC | ||||||
|  |  * | ||||||
|  |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * | ||||||
|  |  *      http://www.apache.org/licenses/LICENSE-2.0
 | ||||||
|  |  * | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | #include "cpp/scheme/scheme_expressive.h" | ||||||
|  | 
 | ||||||
|  | #include <vector> | ||||||
|  | 
 | ||||||
|  | #include "cpp/cam/hct.h" | ||||||
|  | #include "cpp/dynamiccolor/dynamic_scheme.h" | ||||||
|  | #include "cpp/dynamiccolor/variant.h" | ||||||
|  | #include "cpp/palettes/tones.h" | ||||||
|  | 
 | ||||||
|  | namespace material_color_utilities { | ||||||
|  | 
 | ||||||
|  | const std::vector<double> kHues = {0, 21, 51, 121, 151, 191, 271, 321, 360}; | ||||||
|  | 
 | ||||||
|  | const std::vector<double> kSecondaryRotations = {45, 95, 45, 20, 45, | ||||||
|  |                                                  90, 45, 45, 45}; | ||||||
|  | 
 | ||||||
|  | const std::vector<double> kTertiaryRotations = {120, 120, 20,  45, 20, | ||||||
|  |                                                 15,  20,  120, 120}; | ||||||
|  | 
 | ||||||
|  | SchemeExpressive::SchemeExpressive(Hct set_source_color_hct, bool set_is_dark, | ||||||
|  |                                    double set_contrast_level) | ||||||
|  |     : DynamicScheme( | ||||||
|  |           /*set_source_color_hct:*/ set_source_color_hct, | ||||||
|  |           /*variant:*/ Variant::kExpressive, | ||||||
|  |           /*contrast_level:*/ set_contrast_level, | ||||||
|  |           /*is_dark:*/ set_is_dark, | ||||||
|  |           /*primary_palette:*/ | ||||||
|  |           TonalPalette(set_source_color_hct.get_hue() + 240.0, 40.0), | ||||||
|  |           /*secondary_palette:*/ | ||||||
|  |           TonalPalette(DynamicScheme::GetRotatedHue(set_source_color_hct, kHues, | ||||||
|  |                                                     kSecondaryRotations), | ||||||
|  |                        24.0), | ||||||
|  |           /*tertiary_palette:*/ | ||||||
|  |           TonalPalette(DynamicScheme::GetRotatedHue(set_source_color_hct, kHues, | ||||||
|  |                                                     kTertiaryRotations), | ||||||
|  |                        32.0), | ||||||
|  |           /*neutral_palette:*/ | ||||||
|  |           TonalPalette(set_source_color_hct.get_hue() + 15.0, 8.0), | ||||||
|  |           /*neutral_variant_palette:*/ | ||||||
|  |           TonalPalette(set_source_color_hct.get_hue() + 15, 12.0)) {} | ||||||
|  | 
 | ||||||
|  | SchemeExpressive::SchemeExpressive(Hct set_source_color_hct, bool set_is_dark) | ||||||
|  |     : SchemeExpressive::SchemeExpressive(set_source_color_hct, set_is_dark, | ||||||
|  |                                          0.0) {} | ||||||
|  | 
 | ||||||
|  | }  // namespace material_color_utilities
 | ||||||
							
								
								
									
										32
									
								
								src/material-colors/cpp/scheme/scheme_expressive.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								src/material-colors/cpp/scheme/scheme_expressive.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,32 @@ | ||||||
|  | /*
 | ||||||
|  |  * Copyright 2023 Google LLC | ||||||
|  |  * | ||||||
|  |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * | ||||||
|  |  *      http://www.apache.org/licenses/LICENSE-2.0
 | ||||||
|  |  * | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | #ifndef CPP_SCHEME_SCHEME_EXPRESSIVE_H_ | ||||||
|  | #define CPP_SCHEME_SCHEME_EXPRESSIVE_H_ | ||||||
|  | 
 | ||||||
|  | #include "cpp/cam/hct.h" | ||||||
|  | #include "cpp/dynamiccolor/dynamic_scheme.h" | ||||||
|  | 
 | ||||||
|  | namespace material_color_utilities { | ||||||
|  | 
 | ||||||
|  | struct SchemeExpressive : public DynamicScheme { | ||||||
|  |   SchemeExpressive(Hct source_color_hct, bool is_dark, double contrast_level); | ||||||
|  |   SchemeExpressive(Hct source_color_hct, bool is_dark); | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | }  // namespace material_color_utilities
 | ||||||
|  | 
 | ||||||
|  | #endif  // CPP_SCHEME_SCHEME_EXPRESSIVE_H_
 | ||||||
							
								
								
									
										57
									
								
								src/material-colors/cpp/scheme/scheme_fidelity.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										57
									
								
								src/material-colors/cpp/scheme/scheme_fidelity.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,57 @@ | ||||||
|  | /*
 | ||||||
|  |  * Copyright 2023 Google LLC | ||||||
|  |  * | ||||||
|  |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * | ||||||
|  |  *      http://www.apache.org/licenses/LICENSE-2.0
 | ||||||
|  |  * | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | #include "cpp/scheme/scheme_fidelity.h" | ||||||
|  | 
 | ||||||
|  | #include <cmath> | ||||||
|  | 
 | ||||||
|  | #include "cpp/cam/hct.h" | ||||||
|  | #include "cpp/dislike/dislike.h" | ||||||
|  | #include "cpp/dynamiccolor/dynamic_scheme.h" | ||||||
|  | #include "cpp/dynamiccolor/variant.h" | ||||||
|  | #include "cpp/palettes/tones.h" | ||||||
|  | #include "cpp/temperature/temperature_cache.h" | ||||||
|  | 
 | ||||||
|  | namespace material_color_utilities { | ||||||
|  | 
 | ||||||
|  | SchemeFidelity::SchemeFidelity(Hct set_source_color_hct, bool set_is_dark, | ||||||
|  |                                double set_contrast_level) | ||||||
|  |     : DynamicScheme( | ||||||
|  |           /*set_source_color_hct:*/ set_source_color_hct, | ||||||
|  |           /*variant:*/ Variant::kFidelity, | ||||||
|  |           /*contrast_level:*/ set_contrast_level, | ||||||
|  |           /*is_dark:*/ set_is_dark, | ||||||
|  |           /*primary_palette:*/ | ||||||
|  |           TonalPalette(set_source_color_hct.get_hue(), | ||||||
|  |                        set_source_color_hct.get_chroma()), | ||||||
|  |           /*secondary_palette:*/ | ||||||
|  |           TonalPalette(set_source_color_hct.get_hue(), | ||||||
|  |                        fmax(set_source_color_hct.get_chroma() - 32.0, | ||||||
|  |                             set_source_color_hct.get_chroma() * 0.5)), | ||||||
|  |           /*tertiary_palette:*/ | ||||||
|  |           TonalPalette(FixIfDisliked( | ||||||
|  |               TemperatureCache(set_source_color_hct).GetComplement())), | ||||||
|  |           /*neutral_palette:*/ | ||||||
|  |           TonalPalette(set_source_color_hct.get_hue(), | ||||||
|  |                        set_source_color_hct.get_chroma() / 8.0), | ||||||
|  |           /*neutral_variant_palette:*/ | ||||||
|  |           TonalPalette(set_source_color_hct.get_hue(), | ||||||
|  |                        set_source_color_hct.get_chroma() / 8.0 + 4.0)) {} | ||||||
|  | 
 | ||||||
|  | SchemeFidelity::SchemeFidelity(Hct set_source_color_hct, bool set_is_dark) | ||||||
|  |     : SchemeFidelity::SchemeFidelity(set_source_color_hct, set_is_dark, 0.0) {} | ||||||
|  | 
 | ||||||
|  | }  // namespace material_color_utilities
 | ||||||
							
								
								
									
										33
									
								
								src/material-colors/cpp/scheme/scheme_fidelity.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								src/material-colors/cpp/scheme/scheme_fidelity.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,33 @@ | ||||||
|  | /*
 | ||||||
|  |  * Copyright 2023 Google LLC | ||||||
|  |  * | ||||||
|  |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * | ||||||
|  |  *      http://www.apache.org/licenses/LICENSE-2.0
 | ||||||
|  |  * | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | #ifndef CPP_SCHEME_SCHEME_FIDELITY_H_ | ||||||
|  | #define CPP_SCHEME_SCHEME_FIDELITY_H_ | ||||||
|  | 
 | ||||||
|  | #include "cpp/cam/hct.h" | ||||||
|  | #include "cpp/dynamiccolor/dynamic_scheme.h" | ||||||
|  | 
 | ||||||
|  | namespace material_color_utilities { | ||||||
|  | 
 | ||||||
|  | struct SchemeFidelity : public DynamicScheme { | ||||||
|  |   SchemeFidelity(Hct set_source_color_hct, bool set_is_dark, | ||||||
|  |                  double set_contrast_level); | ||||||
|  |   SchemeFidelity(Hct set_source_color_hct, bool set_is_dark); | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | }  // namespace material_color_utilities
 | ||||||
|  | 
 | ||||||
|  | #endif  // CPP_SCHEME_SCHEME_FIDELITY_H_
 | ||||||
							
								
								
									
										52
									
								
								src/material-colors/cpp/scheme/scheme_fruit_salad.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										52
									
								
								src/material-colors/cpp/scheme/scheme_fruit_salad.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,52 @@ | ||||||
|  | /*
 | ||||||
|  |  * Copyright 2023 Google LLC | ||||||
|  |  * | ||||||
|  |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * | ||||||
|  |  *      http://www.apache.org/licenses/LICENSE-2.0
 | ||||||
|  |  * | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | #include "cpp/scheme/scheme_fruit_salad.h" | ||||||
|  | 
 | ||||||
|  | #include "cpp/cam/hct.h" | ||||||
|  | #include "cpp/dynamiccolor/dynamic_scheme.h" | ||||||
|  | #include "cpp/dynamiccolor/variant.h" | ||||||
|  | #include "cpp/palettes/tones.h" | ||||||
|  | 
 | ||||||
|  | namespace material_color_utilities { | ||||||
|  | 
 | ||||||
|  | SchemeFruitSalad::SchemeFruitSalad(Hct set_source_color_hct, bool set_is_dark, | ||||||
|  |                                    double set_contrast_level) | ||||||
|  |     : DynamicScheme( | ||||||
|  |           /*set_source_color_hct:*/ set_source_color_hct, | ||||||
|  |           /*variant:*/ Variant::kFruitSalad, | ||||||
|  |           /*contrast_level:*/ set_contrast_level, | ||||||
|  |           /*is_dark:*/ set_is_dark, | ||||||
|  |           /*primary_palette:*/ | ||||||
|  |           TonalPalette( | ||||||
|  |               SanitizeDegreesDouble(set_source_color_hct.get_hue() - 50.0), | ||||||
|  |               48.0), | ||||||
|  |           /*secondary_palette:*/ | ||||||
|  |           TonalPalette( | ||||||
|  |               SanitizeDegreesDouble(set_source_color_hct.get_hue() - 50.0), | ||||||
|  |               36.0), | ||||||
|  |           /*tertiary_palette:*/ | ||||||
|  |           TonalPalette(set_source_color_hct.get_hue(), 36.0), | ||||||
|  |           /*neutral_palette:*/ | ||||||
|  |           TonalPalette(set_source_color_hct.get_hue(), 10.0), | ||||||
|  |           /*neutral_variant_palette:*/ | ||||||
|  |           TonalPalette(set_source_color_hct.get_hue(), 16.0)) {} | ||||||
|  | 
 | ||||||
|  | SchemeFruitSalad::SchemeFruitSalad(Hct set_source_color_hct, bool set_is_dark) | ||||||
|  |     : SchemeFruitSalad::SchemeFruitSalad(set_source_color_hct, set_is_dark, | ||||||
|  |                                          0.0) {} | ||||||
|  | 
 | ||||||
|  | }  // namespace material_color_utilities
 | ||||||
							
								
								
									
										32
									
								
								src/material-colors/cpp/scheme/scheme_fruit_salad.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								src/material-colors/cpp/scheme/scheme_fruit_salad.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,32 @@ | ||||||
|  | /*
 | ||||||
|  |  * Copyright 2023 Google LLC | ||||||
|  |  * | ||||||
|  |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * | ||||||
|  |  *      http://www.apache.org/licenses/LICENSE-2.0
 | ||||||
|  |  * | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | #ifndef CPP_SCHEME_SCHEME_FRUIT_SALAD_H_ | ||||||
|  | #define CPP_SCHEME_SCHEME_FRUIT_SALAD_H_ | ||||||
|  | 
 | ||||||
|  | #include "cpp/cam/hct.h" | ||||||
|  | #include "cpp/dynamiccolor/dynamic_scheme.h" | ||||||
|  | 
 | ||||||
|  | namespace material_color_utilities { | ||||||
|  | 
 | ||||||
|  | struct SchemeFruitSalad : public DynamicScheme { | ||||||
|  |   SchemeFruitSalad(Hct source_color_hct, bool is_dark, double contrast_level); | ||||||
|  |   SchemeFruitSalad(Hct source_color_hct, bool is_dark); | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | }  // namespace material_color_utilities
 | ||||||
|  | 
 | ||||||
|  | #endif  // CPP_SCHEME_SCHEME_FRUIT_SALAD_H_
 | ||||||
							
								
								
									
										48
									
								
								src/material-colors/cpp/scheme/scheme_monochrome.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										48
									
								
								src/material-colors/cpp/scheme/scheme_monochrome.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,48 @@ | ||||||
|  | /*
 | ||||||
|  |  * Copyright 2023 Google LLC | ||||||
|  |  * | ||||||
|  |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * | ||||||
|  |  *      http://www.apache.org/licenses/LICENSE-2.0
 | ||||||
|  |  * | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | #include "cpp/scheme/scheme_monochrome.h" | ||||||
|  | 
 | ||||||
|  | #include "cpp/cam/hct.h" | ||||||
|  | #include "cpp/dynamiccolor/dynamic_scheme.h" | ||||||
|  | #include "cpp/dynamiccolor/variant.h" | ||||||
|  | #include "cpp/palettes/tones.h" | ||||||
|  | 
 | ||||||
|  | namespace material_color_utilities { | ||||||
|  | 
 | ||||||
|  | SchemeMonochrome::SchemeMonochrome(Hct set_source_color_hct, bool set_is_dark, | ||||||
|  |                                    double set_contrast_level) | ||||||
|  |     : DynamicScheme( | ||||||
|  |           /*set_source_color_hct:*/ set_source_color_hct, | ||||||
|  |           /*variant:*/ Variant::kMonochrome, | ||||||
|  |           /*contrast_level:*/ set_contrast_level, | ||||||
|  |           /*is_dark:*/ set_is_dark, | ||||||
|  |           /*primary_palette:*/ | ||||||
|  |           TonalPalette(set_source_color_hct.get_hue(), 0.0), | ||||||
|  |           /*secondary_palette:*/ | ||||||
|  |           TonalPalette(set_source_color_hct.get_hue(), 0.0), | ||||||
|  |           /*tertiary_palette:*/ | ||||||
|  |           TonalPalette(set_source_color_hct.get_hue(), 0.0), | ||||||
|  |           /*neutral_palette:*/ | ||||||
|  |           TonalPalette(set_source_color_hct.get_hue(), 0.0), | ||||||
|  |           /*neutral_variant_palette:*/ | ||||||
|  |           TonalPalette(set_source_color_hct.get_hue(), 0.0)) {} | ||||||
|  | 
 | ||||||
|  | SchemeMonochrome::SchemeMonochrome(Hct set_source_color_hct, bool set_is_dark) | ||||||
|  |     : SchemeMonochrome::SchemeMonochrome(set_source_color_hct, set_is_dark, | ||||||
|  |                                          0.0) {} | ||||||
|  | 
 | ||||||
|  | }  // namespace material_color_utilities
 | ||||||
							
								
								
									
										32
									
								
								src/material-colors/cpp/scheme/scheme_monochrome.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								src/material-colors/cpp/scheme/scheme_monochrome.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,32 @@ | ||||||
|  | /*
 | ||||||
|  |  * Copyright 2023 Google LLC | ||||||
|  |  * | ||||||
|  |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * | ||||||
|  |  *      http://www.apache.org/licenses/LICENSE-2.0
 | ||||||
|  |  * | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | #ifndef CPP_SCHEME_SCHEME_MONOCHROME_H_ | ||||||
|  | #define CPP_SCHEME_SCHEME_MONOCHROME_H_ | ||||||
|  | 
 | ||||||
|  | #include "cpp/cam/hct.h" | ||||||
|  | #include "cpp/dynamiccolor/dynamic_scheme.h" | ||||||
|  | 
 | ||||||
|  | namespace material_color_utilities { | ||||||
|  | 
 | ||||||
|  | struct SchemeMonochrome : public DynamicScheme { | ||||||
|  |   SchemeMonochrome(Hct source_color_hct, bool is_dark, double contrast_level); | ||||||
|  |   SchemeMonochrome(Hct source_color_hct, bool is_dark); | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | }  // namespace material_color_utilities
 | ||||||
|  | 
 | ||||||
|  | #endif  // CPP_SCHEME_SCHEME_MONOCHROME_H_
 | ||||||
							
								
								
									
										95
									
								
								src/material-colors/cpp/scheme/scheme_monochrome_test.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										95
									
								
								src/material-colors/cpp/scheme/scheme_monochrome_test.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,95 @@ | ||||||
|  | /*
 | ||||||
|  |  * Copyright 2022 Google LLC | ||||||
|  |  * | ||||||
|  |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * | ||||||
|  |  *      http://www.apache.org/licenses/LICENSE-2.0
 | ||||||
|  |  * | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | #include "cpp/scheme/scheme_monochrome.h" | ||||||
|  | 
 | ||||||
|  | #include "testing/base/public/gunit.h" | ||||||
|  | #include "cpp/cam/hct.h" | ||||||
|  | #include "cpp/dynamiccolor/material_dynamic_colors.h" | ||||||
|  | 
 | ||||||
|  | namespace material_color_utilities { | ||||||
|  | 
 | ||||||
|  | namespace { | ||||||
|  | TEST(SchemeMonochromeTest, darkTheme_monochromeSpec) { | ||||||
|  |   SchemeMonochrome scheme = SchemeMonochrome(Hct(0xff0000ff), true, 0.0); | ||||||
|  |   EXPECT_NEAR(MaterialDynamicColors::Primary().GetHct(scheme).get_tone(), 100.0, | ||||||
|  |               1.0); | ||||||
|  |   EXPECT_NEAR(MaterialDynamicColors::OnPrimary().GetHct(scheme).get_tone(), | ||||||
|  |               10.0, 1.0); | ||||||
|  |   EXPECT_NEAR( | ||||||
|  |       MaterialDynamicColors::PrimaryContainer().GetHct(scheme).get_tone(), 85.0, | ||||||
|  |       1.0); | ||||||
|  |   EXPECT_NEAR( | ||||||
|  |       MaterialDynamicColors::OnPrimaryContainer().GetHct(scheme).get_tone(), | ||||||
|  |       0.0, 1.0); | ||||||
|  |   EXPECT_NEAR(MaterialDynamicColors::Secondary().GetHct(scheme).get_tone(), | ||||||
|  |               80.0, 1.0); | ||||||
|  |   EXPECT_NEAR(MaterialDynamicColors::OnSecondary().GetHct(scheme).get_tone(), | ||||||
|  |               10.0, 1.0); | ||||||
|  |   EXPECT_NEAR( | ||||||
|  |       MaterialDynamicColors::SecondaryContainer().GetHct(scheme).get_tone(), | ||||||
|  |       30.0, 1.0); | ||||||
|  |   EXPECT_NEAR( | ||||||
|  |       MaterialDynamicColors::OnSecondaryContainer().GetHct(scheme).get_tone(), | ||||||
|  |       90.0, 1.0); | ||||||
|  |   EXPECT_NEAR(MaterialDynamicColors::Tertiary().GetHct(scheme).get_tone(), 90.0, | ||||||
|  |               1.0); | ||||||
|  |   EXPECT_NEAR(MaterialDynamicColors::OnTertiary().GetHct(scheme).get_tone(), | ||||||
|  |               10.0, 1.0); | ||||||
|  |   EXPECT_NEAR( | ||||||
|  |       MaterialDynamicColors::TertiaryContainer().GetHct(scheme).get_tone(), | ||||||
|  |       60.0, 1.0); | ||||||
|  |   EXPECT_NEAR( | ||||||
|  |       MaterialDynamicColors::OnTertiaryContainer().GetHct(scheme).get_tone(), | ||||||
|  |       0.0, 1.0); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TEST(SchemeMonochromeTest, lightTheme_monochromeSpec) { | ||||||
|  |   SchemeMonochrome scheme = SchemeMonochrome(Hct(0xff0000ff), false, 0.0); | ||||||
|  |   EXPECT_NEAR(MaterialDynamicColors::Primary().GetHct(scheme).get_tone(), 0.0, | ||||||
|  |               1.0); | ||||||
|  |   EXPECT_NEAR(MaterialDynamicColors::OnPrimary().GetHct(scheme).get_tone(), | ||||||
|  |               90.0, 1.0); | ||||||
|  |   EXPECT_NEAR( | ||||||
|  |       MaterialDynamicColors::PrimaryContainer().GetHct(scheme).get_tone(), 25.0, | ||||||
|  |       1.0); | ||||||
|  |   EXPECT_NEAR( | ||||||
|  |       MaterialDynamicColors::OnPrimaryContainer().GetHct(scheme).get_tone(), | ||||||
|  |       100.0, 1.0); | ||||||
|  |   EXPECT_NEAR(MaterialDynamicColors::Secondary().GetHct(scheme).get_tone(), | ||||||
|  |               40.0, 1.0); | ||||||
|  |   EXPECT_NEAR(MaterialDynamicColors::OnSecondary().GetHct(scheme).get_tone(), | ||||||
|  |               100.0, 1.0); | ||||||
|  |   EXPECT_NEAR( | ||||||
|  |       MaterialDynamicColors::SecondaryContainer().GetHct(scheme).get_tone(), | ||||||
|  |       85.0, 1.0); | ||||||
|  |   EXPECT_NEAR( | ||||||
|  |       MaterialDynamicColors::OnSecondaryContainer().GetHct(scheme).get_tone(), | ||||||
|  |       10.0, 1.0); | ||||||
|  |   EXPECT_NEAR(MaterialDynamicColors::Tertiary().GetHct(scheme).get_tone(), 25.0, | ||||||
|  |               1.0); | ||||||
|  |   EXPECT_NEAR(MaterialDynamicColors::OnTertiary().GetHct(scheme).get_tone(), | ||||||
|  |               90.0, 1.0); | ||||||
|  |   EXPECT_NEAR( | ||||||
|  |       MaterialDynamicColors::TertiaryContainer().GetHct(scheme).get_tone(), | ||||||
|  |       49.0, 1.0); | ||||||
|  |   EXPECT_NEAR( | ||||||
|  |       MaterialDynamicColors::OnTertiaryContainer().GetHct(scheme).get_tone(), | ||||||
|  |       100.0, 1.0); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | }  // namespace
 | ||||||
|  | }  // namespace material_color_utilities
 | ||||||
							
								
								
									
										47
									
								
								src/material-colors/cpp/scheme/scheme_neutral.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										47
									
								
								src/material-colors/cpp/scheme/scheme_neutral.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,47 @@ | ||||||
|  | /*
 | ||||||
|  |  * Copyright 2023 Google LLC | ||||||
|  |  * | ||||||
|  |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * | ||||||
|  |  *      http://www.apache.org/licenses/LICENSE-2.0
 | ||||||
|  |  * | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | #include "cpp/scheme/scheme_neutral.h" | ||||||
|  | 
 | ||||||
|  | #include "cpp/cam/hct.h" | ||||||
|  | #include "cpp/dynamiccolor/dynamic_scheme.h" | ||||||
|  | #include "cpp/dynamiccolor/variant.h" | ||||||
|  | #include "cpp/palettes/tones.h" | ||||||
|  | 
 | ||||||
|  | namespace material_color_utilities { | ||||||
|  | 
 | ||||||
|  | SchemeNeutral::SchemeNeutral(Hct set_source_color_hct, bool set_is_dark, | ||||||
|  |                              double set_contrast_level) | ||||||
|  |     : DynamicScheme( | ||||||
|  |           /*set_source_color_hct:*/ set_source_color_hct, | ||||||
|  |           /*variant:*/ Variant::kNeutral, | ||||||
|  |           /*contrast_level:*/ set_contrast_level, | ||||||
|  |           /*is_dark:*/ set_is_dark, | ||||||
|  |           /*primary_palette:*/ | ||||||
|  |           TonalPalette(set_source_color_hct.get_hue(), 12.0), | ||||||
|  |           /*secondary_palette:*/ | ||||||
|  |           TonalPalette(set_source_color_hct.get_hue(), 8.0), | ||||||
|  |           /*tertiary_palette:*/ | ||||||
|  |           TonalPalette(set_source_color_hct.get_hue(), 16.0), | ||||||
|  |           /*neutral_palette:*/ | ||||||
|  |           TonalPalette(set_source_color_hct.get_hue(), 2.0), | ||||||
|  |           /*neutral_variant_palette:*/ | ||||||
|  |           TonalPalette(set_source_color_hct.get_hue(), 2.0)) {} | ||||||
|  | 
 | ||||||
|  | SchemeNeutral::SchemeNeutral(Hct set_source_color_hct, bool set_is_dark) | ||||||
|  |     : SchemeNeutral::SchemeNeutral(set_source_color_hct, set_is_dark, 0.0) {} | ||||||
|  | 
 | ||||||
|  | }  // namespace material_color_utilities
 | ||||||
							
								
								
									
										32
									
								
								src/material-colors/cpp/scheme/scheme_neutral.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								src/material-colors/cpp/scheme/scheme_neutral.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,32 @@ | ||||||
|  | /*
 | ||||||
|  |  * Copyright 2023 Google LLC | ||||||
|  |  * | ||||||
|  |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * | ||||||
|  |  *      http://www.apache.org/licenses/LICENSE-2.0
 | ||||||
|  |  * | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | #ifndef CPP_SCHEME_SCHEME_NEUTRAL_H_ | ||||||
|  | #define CPP_SCHEME_SCHEME_NEUTRAL_H_ | ||||||
|  | 
 | ||||||
|  | #include "cpp/cam/hct.h" | ||||||
|  | #include "cpp/dynamiccolor/dynamic_scheme.h" | ||||||
|  | 
 | ||||||
|  | namespace material_color_utilities { | ||||||
|  | 
 | ||||||
|  | struct SchemeNeutral : public DynamicScheme { | ||||||
|  |   SchemeNeutral(Hct source_color_hct, bool is_dark, double contrast_level); | ||||||
|  |   SchemeNeutral(Hct source_color_hct, bool is_dark); | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | }  // namespace material_color_utilities
 | ||||||
|  | 
 | ||||||
|  | #endif  // CPP_SCHEME_SCHEME_NEUTRAL_H_
 | ||||||
							
								
								
									
										49
									
								
								src/material-colors/cpp/scheme/scheme_rainbow.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										49
									
								
								src/material-colors/cpp/scheme/scheme_rainbow.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,49 @@ | ||||||
|  | /*
 | ||||||
|  |  * Copyright 2023 Google LLC | ||||||
|  |  * | ||||||
|  |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * | ||||||
|  |  *      http://www.apache.org/licenses/LICENSE-2.0
 | ||||||
|  |  * | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | #include "cpp/scheme/scheme_rainbow.h" | ||||||
|  | 
 | ||||||
|  | #include "cpp/cam/hct.h" | ||||||
|  | #include "cpp/dynamiccolor/dynamic_scheme.h" | ||||||
|  | #include "cpp/dynamiccolor/variant.h" | ||||||
|  | #include "cpp/palettes/tones.h" | ||||||
|  | 
 | ||||||
|  | namespace material_color_utilities { | ||||||
|  | 
 | ||||||
|  | SchemeRainbow::SchemeRainbow(Hct set_source_color_hct, bool set_is_dark, | ||||||
|  |                              double set_contrast_level) | ||||||
|  |     : DynamicScheme( | ||||||
|  |           /*set_source_color_hct:*/ set_source_color_hct, | ||||||
|  |           /*variant:*/ Variant::kRainbow, | ||||||
|  |           /*contrast_level:*/ set_contrast_level, | ||||||
|  |           /*is_dark:*/ set_is_dark, | ||||||
|  |           /*primary_palette:*/ | ||||||
|  |           TonalPalette(set_source_color_hct.get_hue(), 48.0), | ||||||
|  |           /*secondary_palette:*/ | ||||||
|  |           TonalPalette(set_source_color_hct.get_hue(), 16.0), | ||||||
|  |           /*tertiary_palette:*/ | ||||||
|  |           TonalPalette( | ||||||
|  |               SanitizeDegreesDouble(set_source_color_hct.get_hue() + 60.0), | ||||||
|  |               24.0), | ||||||
|  |           /*neutral_palette:*/ | ||||||
|  |           TonalPalette(set_source_color_hct.get_hue(), 0.0), | ||||||
|  |           /*neutral_variant_palette:*/ | ||||||
|  |           TonalPalette(set_source_color_hct.get_hue(), 0.0)) {} | ||||||
|  | 
 | ||||||
|  | SchemeRainbow::SchemeRainbow(Hct set_source_color_hct, bool set_is_dark) | ||||||
|  |     : SchemeRainbow::SchemeRainbow(set_source_color_hct, set_is_dark, 0.0) {} | ||||||
|  | 
 | ||||||
|  | }  // namespace material_color_utilities
 | ||||||
							
								
								
									
										32
									
								
								src/material-colors/cpp/scheme/scheme_rainbow.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								src/material-colors/cpp/scheme/scheme_rainbow.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,32 @@ | ||||||
|  | /*
 | ||||||
|  |  * Copyright 2023 Google LLC | ||||||
|  |  * | ||||||
|  |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * | ||||||
|  |  *      http://www.apache.org/licenses/LICENSE-2.0
 | ||||||
|  |  * | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | #ifndef CPP_SCHEME_SCHEME_RAINBOW_H_ | ||||||
|  | #define CPP_SCHEME_SCHEME_RAINBOW_H_ | ||||||
|  | 
 | ||||||
|  | #include "cpp/cam/hct.h" | ||||||
|  | #include "cpp/dynamiccolor/dynamic_scheme.h" | ||||||
|  | 
 | ||||||
|  | namespace material_color_utilities { | ||||||
|  | 
 | ||||||
|  | struct SchemeRainbow : public DynamicScheme { | ||||||
|  |   SchemeRainbow(Hct source_color_hct, bool is_dark, double contrast_level); | ||||||
|  |   SchemeRainbow(Hct source_color_hct, bool is_dark); | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | }  // namespace material_color_utilities
 | ||||||
|  | 
 | ||||||
|  | #endif  // CPP_SCHEME_SCHEME_RAINBOW_H_
 | ||||||
							
								
								
									
										49
									
								
								src/material-colors/cpp/scheme/scheme_tonal_spot.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										49
									
								
								src/material-colors/cpp/scheme/scheme_tonal_spot.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,49 @@ | ||||||
|  | /*
 | ||||||
|  |  * Copyright 2023 Google LLC | ||||||
|  |  * | ||||||
|  |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * | ||||||
|  |  *      http://www.apache.org/licenses/LICENSE-2.0
 | ||||||
|  |  * | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | #include "cpp/scheme/scheme_tonal_spot.h" | ||||||
|  | 
 | ||||||
|  | #include "cpp/cam/hct.h" | ||||||
|  | #include "cpp/dynamiccolor/dynamic_scheme.h" | ||||||
|  | #include "cpp/dynamiccolor/variant.h" | ||||||
|  | #include "cpp/palettes/tones.h" | ||||||
|  | 
 | ||||||
|  | namespace material_color_utilities { | ||||||
|  | 
 | ||||||
|  | SchemeTonalSpot::SchemeTonalSpot(Hct set_source_color_hct, bool set_is_dark, | ||||||
|  |                                  double set_contrast_level) | ||||||
|  |     : DynamicScheme( | ||||||
|  |           /*set_source_color_hct:*/ set_source_color_hct, | ||||||
|  |           /*variant:*/ Variant::kTonalSpot, | ||||||
|  |           /*contrast_level:*/ set_contrast_level, | ||||||
|  |           /*is_dark:*/ set_is_dark, | ||||||
|  |           /*primary_palette:*/ | ||||||
|  |           TonalPalette(set_source_color_hct.get_hue(), 36.0), | ||||||
|  |           /*secondary_palette:*/ | ||||||
|  |           TonalPalette(set_source_color_hct.get_hue(), 16.0), | ||||||
|  |           /*tertiary_palette:*/ | ||||||
|  |           TonalPalette( | ||||||
|  |               SanitizeDegreesDouble(set_source_color_hct.get_hue() + 60), 24.0), | ||||||
|  |           /*neutral_palette:*/ | ||||||
|  |           TonalPalette(set_source_color_hct.get_hue(), 6.0), | ||||||
|  |           /*neutral_variant_palette:*/ | ||||||
|  |           TonalPalette(set_source_color_hct.get_hue(), 8.0)) {} | ||||||
|  | 
 | ||||||
|  | SchemeTonalSpot::SchemeTonalSpot(Hct set_source_color_hct, bool set_is_dark) | ||||||
|  |     : SchemeTonalSpot::SchemeTonalSpot(set_source_color_hct, set_is_dark, 0.0) { | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | }  // namespace material_color_utilities
 | ||||||
							
								
								
									
										32
									
								
								src/material-colors/cpp/scheme/scheme_tonal_spot.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								src/material-colors/cpp/scheme/scheme_tonal_spot.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,32 @@ | ||||||
|  | /*
 | ||||||
|  |  * Copyright 2023 Google LLC | ||||||
|  |  * | ||||||
|  |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * | ||||||
|  |  *      http://www.apache.org/licenses/LICENSE-2.0
 | ||||||
|  |  * | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | #ifndef CPP_SCHEME_SCHEME_TONAL_SPOT_H_ | ||||||
|  | #define CPP_SCHEME_SCHEME_TONAL_SPOT_H_ | ||||||
|  | 
 | ||||||
|  | #include "cpp/cam/hct.h" | ||||||
|  | #include "cpp/dynamiccolor/dynamic_scheme.h" | ||||||
|  | 
 | ||||||
|  | namespace material_color_utilities { | ||||||
|  | 
 | ||||||
|  | struct SchemeTonalSpot : public DynamicScheme { | ||||||
|  |   SchemeTonalSpot(Hct source_color_hct, bool is_dark, double contrast_level); | ||||||
|  |   SchemeTonalSpot(Hct source_color_hct, bool is_dark); | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | }  // namespace material_color_utilities
 | ||||||
|  | 
 | ||||||
|  | #endif  // CPP_SCHEME_SCHEME_TONAL_SPOT_H_
 | ||||||
							
								
								
									
										61
									
								
								src/material-colors/cpp/scheme/scheme_vibrant.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										61
									
								
								src/material-colors/cpp/scheme/scheme_vibrant.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,61 @@ | ||||||
|  | /*
 | ||||||
|  |  * Copyright 2023 Google LLC | ||||||
|  |  * | ||||||
|  |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * | ||||||
|  |  *      http://www.apache.org/licenses/LICENSE-2.0
 | ||||||
|  |  * | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | #include "cpp/scheme/scheme_vibrant.h" | ||||||
|  | 
 | ||||||
|  | #include <vector> | ||||||
|  | 
 | ||||||
|  | #include "cpp/cam/hct.h" | ||||||
|  | #include "cpp/dynamiccolor/dynamic_scheme.h" | ||||||
|  | #include "cpp/dynamiccolor/variant.h" | ||||||
|  | #include "cpp/palettes/tones.h" | ||||||
|  | 
 | ||||||
|  | namespace material_color_utilities { | ||||||
|  | 
 | ||||||
|  | const std::vector<double> kHues = {0, 41, 61, 101, 131, 181, 251, 301, 360}; | ||||||
|  | 
 | ||||||
|  | const std::vector<double> kSecondaryRotations = {18, 15, 10, 12, 15, | ||||||
|  |                                                  18, 15, 12, 12}; | ||||||
|  | 
 | ||||||
|  | const std::vector<double> kTertiaryRotations = {35, 30, 20, 25, 30, | ||||||
|  |                                                 35, 30, 25, 25}; | ||||||
|  | 
 | ||||||
|  | SchemeVibrant::SchemeVibrant(Hct set_source_color_hct, bool set_is_dark, | ||||||
|  |                              double set_contrast_level) | ||||||
|  |     : DynamicScheme( | ||||||
|  |           /*set_source_color_hct:*/ set_source_color_hct, | ||||||
|  |           /*variant:*/ Variant::kVibrant, | ||||||
|  |           /*contrast_level:*/ set_contrast_level, | ||||||
|  |           /*is_dark:*/ set_is_dark, | ||||||
|  |           /*primary_palette:*/ | ||||||
|  |           TonalPalette(set_source_color_hct.get_hue(), 200.0), | ||||||
|  |           /*secondary_palette:*/ | ||||||
|  |           TonalPalette(DynamicScheme::GetRotatedHue(set_source_color_hct, kHues, | ||||||
|  |                                                     kSecondaryRotations), | ||||||
|  |                        24.0), | ||||||
|  |           /*tertiary_palette:*/ | ||||||
|  |           TonalPalette(DynamicScheme::GetRotatedHue(set_source_color_hct, kHues, | ||||||
|  |                                                     kTertiaryRotations), | ||||||
|  |                        32.0), | ||||||
|  |           /*neutral_palette:*/ | ||||||
|  |           TonalPalette(set_source_color_hct.get_hue(), 10.0), | ||||||
|  |           /*neutral_variant_palette:*/ | ||||||
|  |           TonalPalette(set_source_color_hct.get_hue(), 12.0)) {} | ||||||
|  | 
 | ||||||
|  | SchemeVibrant::SchemeVibrant(Hct set_source_color_hct, bool set_is_dark) | ||||||
|  |     : SchemeVibrant::SchemeVibrant(set_source_color_hct, set_is_dark, 0.0) {} | ||||||
|  | 
 | ||||||
|  | }  // namespace material_color_utilities
 | ||||||
							
								
								
									
										32
									
								
								src/material-colors/cpp/scheme/scheme_vibrant.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								src/material-colors/cpp/scheme/scheme_vibrant.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,32 @@ | ||||||
|  | /*
 | ||||||
|  |  * Copyright 2023 Google LLC | ||||||
|  |  * | ||||||
|  |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * | ||||||
|  |  *      http://www.apache.org/licenses/LICENSE-2.0
 | ||||||
|  |  * | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | #ifndef CPP_SCHEME_SCHEME_VARIANT_H_ | ||||||
|  | #define CPP_SCHEME_SCHEME_VARIANT_H_ | ||||||
|  | 
 | ||||||
|  | #include "cpp/cam/hct.h" | ||||||
|  | #include "cpp/dynamiccolor/dynamic_scheme.h" | ||||||
|  | 
 | ||||||
|  | namespace material_color_utilities { | ||||||
|  | 
 | ||||||
|  | struct SchemeVibrant : public DynamicScheme { | ||||||
|  |   SchemeVibrant(Hct source_color_hct, bool is_dark, double contrast_level); | ||||||
|  |   SchemeVibrant(Hct source_color_hct, bool is_dark); | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | }  // namespace material_color_utilities
 | ||||||
|  | 
 | ||||||
|  | #endif  // CPP_SCHEME_SCHEME_VARIANT_H_
 | ||||||
							
								
								
									
										124
									
								
								src/material-colors/cpp/score/score.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										124
									
								
								src/material-colors/cpp/score/score.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,124 @@ | ||||||
|  | /*
 | ||||||
|  |  * Copyright 2022 Google LLC | ||||||
|  |  * | ||||||
|  |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * | ||||||
|  |  *      http://www.apache.org/licenses/LICENSE-2.0
 | ||||||
|  |  * | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | #include "cpp/score/score.h" | ||||||
|  | 
 | ||||||
|  | #include <algorithm> | ||||||
|  | #include <cmath> | ||||||
|  | #include <cstdint> | ||||||
|  | #include <map> | ||||||
|  | #include <utility> | ||||||
|  | #include <vector> | ||||||
|  | 
 | ||||||
|  | #include "cpp/cam/hct.h" | ||||||
|  | #include "cpp/utils/utils.h" | ||||||
|  | 
 | ||||||
|  | namespace material_color_utilities { | ||||||
|  | 
 | ||||||
|  | constexpr double kTargetChroma = 48.0;  // A1 Chroma
 | ||||||
|  | constexpr double kWeightProportion = 0.7; | ||||||
|  | constexpr double kWeightChromaAbove = 0.3; | ||||||
|  | constexpr double kWeightChromaBelow = 0.1; | ||||||
|  | constexpr double kCutoffChroma = 5.0; | ||||||
|  | constexpr double kCutoffExcitedProportion = 0.01; | ||||||
|  | 
 | ||||||
|  | bool CompareScoredHCT(const std::pair<Hct, double>& a, | ||||||
|  |                       const std::pair<Hct, double>& b) { | ||||||
|  |   return a.second > b.second; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | std::vector<Argb> RankedSuggestions( | ||||||
|  |     const std::map<Argb, uint32_t>& argb_to_population, | ||||||
|  |     const ScoreOptions& options) { | ||||||
|  |   // Get the HCT color for each Argb value, while finding the per hue count and
 | ||||||
|  |   // total count.
 | ||||||
|  |   std::vector<Hct> colors_hct; | ||||||
|  |   std::vector<uint32_t> hue_population(360, 0); | ||||||
|  |   double population_sum = 0; | ||||||
|  |   for (const auto& [argb, population] : argb_to_population) { | ||||||
|  |     Hct hct(argb); | ||||||
|  |     colors_hct.push_back(hct); | ||||||
|  |     int hue = floor(hct.get_hue()); | ||||||
|  |     hue_population[hue] += population; | ||||||
|  |     population_sum += population; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   // Hues with more usage in neighboring 30 degree slice get a larger number.
 | ||||||
|  |   std::vector<double> hue_excited_proportions(360, 0.0); | ||||||
|  |   for (int hue = 0; hue < 360; hue++) { | ||||||
|  |     double proportion = hue_population[hue] / population_sum; | ||||||
|  |     for (int i = hue - 14; i < hue + 16; i++) { | ||||||
|  |       int neighbor_hue = SanitizeDegreesInt(i); | ||||||
|  |       hue_excited_proportions[neighbor_hue] += proportion; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   // Scores each HCT color based on usage and chroma, while optionally
 | ||||||
|  |   // filtering out values that do not have enough chroma or usage.
 | ||||||
|  |   std::vector<std::pair<Hct, double>> scored_hcts; | ||||||
|  |   for (Hct hct : colors_hct) { | ||||||
|  |     int hue = SanitizeDegreesInt(round(hct.get_hue())); | ||||||
|  |     double proportion = hue_excited_proportions[hue]; | ||||||
|  |     if (options.filter && (hct.get_chroma() < kCutoffChroma || | ||||||
|  |                            proportion <= kCutoffExcitedProportion)) { | ||||||
|  |       continue; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     double proportion_score = proportion * 100.0 * kWeightProportion; | ||||||
|  |     double chroma_weight = hct.get_chroma() < kTargetChroma | ||||||
|  |                                ? kWeightChromaBelow | ||||||
|  |                                : kWeightChromaAbove; | ||||||
|  |     double chroma_score = (hct.get_chroma() - kTargetChroma) * chroma_weight; | ||||||
|  |     double score = proportion_score + chroma_score; | ||||||
|  |     scored_hcts.push_back({hct, score}); | ||||||
|  |   } | ||||||
|  |   // Sorted so that colors with higher scores come first.
 | ||||||
|  |   sort(scored_hcts.begin(), scored_hcts.end(), CompareScoredHCT); | ||||||
|  | 
 | ||||||
|  |   // Iterates through potential hue differences in degrees in order to select
 | ||||||
|  |   // the colors with the largest distribution of hues possible. Starting at
 | ||||||
|  |   // 90 degrees(maximum difference for 4 colors) then decreasing down to a
 | ||||||
|  |   // 15 degree minimum.
 | ||||||
|  |   std::vector<Hct> chosen_colors; | ||||||
|  |   for (int difference_degrees = 90; difference_degrees >= 15; | ||||||
|  |        difference_degrees--) { | ||||||
|  |     chosen_colors.clear(); | ||||||
|  |     for (auto entry : scored_hcts) { | ||||||
|  |       Hct hct = entry.first; | ||||||
|  |       auto duplicate_hue = std::find_if( | ||||||
|  |           chosen_colors.begin(), chosen_colors.end(), | ||||||
|  |           [&hct, difference_degrees](Hct chosen_hct) { | ||||||
|  |             return DiffDegrees(hct.get_hue(), chosen_hct.get_hue()) < | ||||||
|  |                    difference_degrees; | ||||||
|  |           }); | ||||||
|  |       if (duplicate_hue == chosen_colors.end()) { | ||||||
|  |         chosen_colors.push_back(hct); | ||||||
|  |         if (chosen_colors.size() >= options.desired) break; | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |     if (chosen_colors.size() >= options.desired) break; | ||||||
|  |   } | ||||||
|  |   std::vector<Argb> colors; | ||||||
|  |   if (chosen_colors.empty()) { | ||||||
|  |     colors.push_back(options.fallback_color_argb); | ||||||
|  |   } | ||||||
|  |   for (auto chosen_hct : chosen_colors) { | ||||||
|  |     colors.push_back(chosen_hct.ToInt()); | ||||||
|  |   } | ||||||
|  |   return colors; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | }  // namespace material_color_utilities
 | ||||||
							
								
								
									
										59
									
								
								src/material-colors/cpp/score/score.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										59
									
								
								src/material-colors/cpp/score/score.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,59 @@ | ||||||
|  | /*
 | ||||||
|  |  * Copyright 2022 Google LLC | ||||||
|  |  * | ||||||
|  |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * | ||||||
|  |  *      http://www.apache.org/licenses/LICENSE-2.0
 | ||||||
|  |  * | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | #ifndef CPP_SCORE_SCORE_H_ | ||||||
|  | #define CPP_SCORE_SCORE_H_ | ||||||
|  | 
 | ||||||
|  | #include <cstdlib> | ||||||
|  | #include <map> | ||||||
|  | #include <vector> | ||||||
|  | 
 | ||||||
|  | #include "cpp/utils/utils.h" | ||||||
|  | 
 | ||||||
|  | namespace material_color_utilities { | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Default options for ranking colors based on usage counts. | ||||||
|  |  * `desired`: is the max count of the colors returned. | ||||||
|  |  * `fallback_color_argb`: Is the default color that should be used if no | ||||||
|  |  *                        other colors are suitable. | ||||||
|  |  * `filter`: controls if the resulting colors should be filtered to not include | ||||||
|  |  *         hues that are not used often enough, and colors that are effectively | ||||||
|  |  *         grayscale. | ||||||
|  |  */ | ||||||
|  | struct ScoreOptions { | ||||||
|  |   size_t desired = 4;  // 4 colors matches the Android wallpaper picker.
 | ||||||
|  |   int fallback_color_argb = 0xff4285f4;  // Google Blue.
 | ||||||
|  |   bool filter = true;                    // Avoid unsuitable colors.
 | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Given a map with keys of colors and values of how often the color appears, | ||||||
|  |  * rank the colors based on suitability for being used for a UI theme. | ||||||
|  |  * | ||||||
|  |  * The list returned is of length <= [desired]. The recommended color is the | ||||||
|  |  * first item, the least suitable is the last. There will always be at least | ||||||
|  |  * one color returned. If all the input colors were not suitable for a theme, | ||||||
|  |  * a default fallback color will be provided, Google Blue, or supplied fallback | ||||||
|  |  * color. The default number of colors returned is 4, simply because that's the | ||||||
|  |  * # of colors display in Android 12's wallpaper picker. | ||||||
|  |  */ | ||||||
|  | std::vector<Argb> RankedSuggestions( | ||||||
|  |     const std::map<Argb, uint32_t>& argb_to_population, | ||||||
|  |     const ScoreOptions& options = {}); | ||||||
|  | }  // namespace material_color_utilities
 | ||||||
|  | 
 | ||||||
|  | #endif  // CPP_SCORE_SCORE_H_
 | ||||||
							
								
								
									
										257
									
								
								src/material-colors/cpp/score/score_test.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										257
									
								
								src/material-colors/cpp/score/score_test.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,257 @@ | ||||||
|  | /*
 | ||||||
|  |  * Copyright 2022 Google LLC | ||||||
|  |  * | ||||||
|  |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * | ||||||
|  |  *      http://www.apache.org/licenses/LICENSE-2.0
 | ||||||
|  |  * | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | #include "cpp/score/score.h" | ||||||
|  | 
 | ||||||
|  | #include <cstdint> | ||||||
|  | #include <map> | ||||||
|  | #include <vector> | ||||||
|  | 
 | ||||||
|  | #include "testing/base/public/gunit.h" | ||||||
|  | #include "cpp/utils/utils.h" | ||||||
|  | 
 | ||||||
|  | namespace material_color_utilities { | ||||||
|  | 
 | ||||||
|  | namespace { | ||||||
|  | 
 | ||||||
|  | TEST(ScoreTest, PrioritizesChroma) { | ||||||
|  |   std::map<Argb, uint32_t> argb_to_population = { | ||||||
|  |       {0xff000000, 1}, {0xffffffff, 1}, {0xff0000ff, 1}}; | ||||||
|  | 
 | ||||||
|  |   std::vector<Argb> ranked = | ||||||
|  |       RankedSuggestions(argb_to_population, {.desired = 4}); | ||||||
|  | 
 | ||||||
|  |   EXPECT_EQ(ranked.size(), 1u); | ||||||
|  |   EXPECT_EQ(ranked[0], 0xff0000ff); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TEST(ScoreTest, PrioritizesChromaWhenProportionsEqual) { | ||||||
|  |   std::map<Argb, uint32_t> argb_to_population = { | ||||||
|  |       {0xffff0000, 1}, {0xff00ff00, 1}, {0xff0000ff, 1}}; | ||||||
|  | 
 | ||||||
|  |   std::vector<Argb> ranked = | ||||||
|  |       RankedSuggestions(argb_to_population, {.desired = 4}); | ||||||
|  | 
 | ||||||
|  |   EXPECT_EQ(ranked.size(), 3u); | ||||||
|  |   EXPECT_EQ(ranked[0], 0xffff0000); | ||||||
|  |   EXPECT_EQ(ranked[1], 0xff00ff00); | ||||||
|  |   EXPECT_EQ(ranked[2], 0xff0000ff); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TEST(ScoreTest, GeneratesGblueWhenNoColorsAvailable) { | ||||||
|  |   std::map<Argb, uint32_t> argb_to_population = {{0xff000000, 1}}; | ||||||
|  | 
 | ||||||
|  |   std::vector<Argb> ranked = | ||||||
|  |       RankedSuggestions(argb_to_population, {.desired = 4}); | ||||||
|  | 
 | ||||||
|  |   EXPECT_EQ(ranked.size(), 1u); | ||||||
|  |   EXPECT_EQ(ranked[0], 0xff4285f4); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TEST(ScoreTest, DedupesNearbyHues) { | ||||||
|  |   std::map<Argb, uint32_t> argb_to_population = { | ||||||
|  |       {0xff008772, 1},  // H 180 C 42 T 50
 | ||||||
|  |       {0xff318477, 1}   // H 184 C 35 T 50
 | ||||||
|  |   }; | ||||||
|  | 
 | ||||||
|  |   std::vector<Argb> ranked = | ||||||
|  |       RankedSuggestions(argb_to_population, {.desired = 4}); | ||||||
|  | 
 | ||||||
|  |   EXPECT_EQ(ranked.size(), 1u); | ||||||
|  |   EXPECT_EQ(ranked[0], 0xff008772); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TEST(ScoreTest, MaximizesHueDistance) { | ||||||
|  |   std::map<Argb, uint32_t> argb_to_population = { | ||||||
|  |       {0xff008772, 1},  // H 180 C 42 T 50
 | ||||||
|  |       {0xff008587, 1},  // H 198 C 50 T 50
 | ||||||
|  |       {0xff007ebc, 1}   // H 245 C 50 T 50
 | ||||||
|  |   }; | ||||||
|  | 
 | ||||||
|  |   std::vector<Argb> ranked = | ||||||
|  |       RankedSuggestions(argb_to_population, {.desired = 2}); | ||||||
|  | 
 | ||||||
|  |   EXPECT_EQ(ranked.size(), 2u); | ||||||
|  |   EXPECT_EQ(ranked[0], 0xff007ebc); | ||||||
|  |   EXPECT_EQ(ranked[1], 0xff008772); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TEST(ScoreTest, GeneratedScenarioOne) { | ||||||
|  |   std::map<Argb, uint32_t> argb_to_population = { | ||||||
|  |       {0xff7ea16d, 67}, | ||||||
|  |       {0xffd8ccae, 67}, | ||||||
|  |       {0xff835c0d, 49}, | ||||||
|  |   }; | ||||||
|  | 
 | ||||||
|  |   std::vector<Argb> ranked = RankedSuggestions( | ||||||
|  |       argb_to_population, | ||||||
|  |       {.desired = 3, .fallback_color_argb = (int)0xff8d3819, .filter = false}); | ||||||
|  | 
 | ||||||
|  |   EXPECT_EQ(ranked.size(), 3u); | ||||||
|  |   EXPECT_EQ(ranked[0], 0xff7ea16d); | ||||||
|  |   EXPECT_EQ(ranked[1], 0xffd8ccae); | ||||||
|  |   EXPECT_EQ(ranked[2], 0xff835c0d); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TEST(ScoreTest, GeneratedScenarioTwo) { | ||||||
|  |   std::map<Argb, uint32_t> argb_to_population = { | ||||||
|  |       {0xffd33881, 14}, | ||||||
|  |       {0xff3205cc, 77}, | ||||||
|  |       {0xff0b48cf, 36}, | ||||||
|  |       {0xffa08f5d, 81}, | ||||||
|  |   }; | ||||||
|  | 
 | ||||||
|  |   std::vector<Argb> ranked = RankedSuggestions( | ||||||
|  |       argb_to_population, | ||||||
|  |       {.desired = 4, .fallback_color_argb = (int)0xff7d772b, .filter = true}); | ||||||
|  | 
 | ||||||
|  |   EXPECT_EQ(ranked.size(), 3u); | ||||||
|  |   EXPECT_EQ(ranked[0], 0xff3205cc); | ||||||
|  |   EXPECT_EQ(ranked[1], 0xffa08f5d); | ||||||
|  |   EXPECT_EQ(ranked[2], 0xffd33881); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TEST(ScoreTest, GeneratedScenarioThree) { | ||||||
|  |   std::map<Argb, uint32_t> argb_to_population = { | ||||||
|  |       {0xffbe94a6, 23}, | ||||||
|  |       {0xffc33fd7, 42}, | ||||||
|  |       {0xff899f36, 90}, | ||||||
|  |       {0xff94c574, 82}, | ||||||
|  |   }; | ||||||
|  | 
 | ||||||
|  |   std::vector<Argb> ranked = RankedSuggestions( | ||||||
|  |       argb_to_population, | ||||||
|  |       {.desired = 3, .fallback_color_argb = (int)0xffaa79a4, .filter = true}); | ||||||
|  | 
 | ||||||
|  |   EXPECT_EQ(ranked.size(), 3u); | ||||||
|  |   EXPECT_EQ(ranked[0], 0xff94c574); | ||||||
|  |   EXPECT_EQ(ranked[1], 0xffc33fd7); | ||||||
|  |   EXPECT_EQ(ranked[2], 0xffbe94a6); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TEST(ScoreTest, GeneratedScenarioFour) { | ||||||
|  |   std::map<Argb, uint32_t> argb_to_population = { | ||||||
|  |       {0xffdf241c, 85}, {0xff685859, 44}, {0xffd06d5f, 34}, | ||||||
|  |       {0xff561c54, 27}, {0xff713090, 88}, | ||||||
|  |   }; | ||||||
|  | 
 | ||||||
|  |   std::vector<Argb> ranked = RankedSuggestions( | ||||||
|  |       argb_to_population, | ||||||
|  |       {.desired = 5, .fallback_color_argb = (int)0xff58c19c, .filter = false}); | ||||||
|  | 
 | ||||||
|  |   EXPECT_EQ(ranked.size(), 2u); | ||||||
|  |   EXPECT_EQ(ranked[0], 0xffdf241c); | ||||||
|  |   EXPECT_EQ(ranked[1], 0xff561c54); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TEST(ScoreTest, GeneratedScenarioFive) { | ||||||
|  |   std::map<Argb, uint32_t> argb_to_population = { | ||||||
|  |       {0xffbe66f8, 41}, {0xff4bbda9, 88}, {0xff80f6f9, 44}, | ||||||
|  |       {0xffab8017, 43}, {0xffe89307, 65}, | ||||||
|  |   }; | ||||||
|  | 
 | ||||||
|  |   std::vector<Argb> ranked = RankedSuggestions( | ||||||
|  |       argb_to_population, | ||||||
|  |       {.desired = 3, .fallback_color_argb = (int)0xff916691, .filter = false}); | ||||||
|  | 
 | ||||||
|  |   EXPECT_EQ(ranked.size(), 3u); | ||||||
|  |   EXPECT_EQ(ranked[0], 0xffab8017); | ||||||
|  |   EXPECT_EQ(ranked[1], 0xff4bbda9); | ||||||
|  |   EXPECT_EQ(ranked[2], 0xffbe66f8); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TEST(ScoreTest, GeneratedScenarioSix) { | ||||||
|  |   std::map<Argb, uint32_t> argb_to_population = { | ||||||
|  |       {0xff18ea8f, 93}, {0xff327593, 18}, {0xff066a18, 53}, | ||||||
|  |       {0xfffa8a23, 74}, {0xff04ca1f, 62}, | ||||||
|  |   }; | ||||||
|  | 
 | ||||||
|  |   std::vector<Argb> ranked = RankedSuggestions( | ||||||
|  |       argb_to_population, | ||||||
|  |       {.desired = 2, .fallback_color_argb = (int)0xff4c377a, .filter = false}); | ||||||
|  | 
 | ||||||
|  |   EXPECT_EQ(ranked.size(), 2u); | ||||||
|  |   EXPECT_EQ(ranked[0], 0xff18ea8f); | ||||||
|  |   EXPECT_EQ(ranked[1], 0xfffa8a23); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TEST(ScoreTest, GeneratedScenarioSeven) { | ||||||
|  |   std::map<Argb, uint32_t> argb_to_population = { | ||||||
|  |       {0xff2e05ed, 23}, {0xff153e55, 90}, {0xff9ab220, 23}, | ||||||
|  |       {0xff153379, 66}, {0xff68bcc3, 81}, | ||||||
|  |   }; | ||||||
|  | 
 | ||||||
|  |   std::vector<Argb> ranked = RankedSuggestions( | ||||||
|  |       argb_to_population, | ||||||
|  |       {.desired = 2, .fallback_color_argb = (int)0xfff588dc, .filter = true}); | ||||||
|  | 
 | ||||||
|  |   EXPECT_EQ(ranked.size(), 2u); | ||||||
|  |   EXPECT_EQ(ranked[0], 0xff2e05ed); | ||||||
|  |   EXPECT_EQ(ranked[1], 0xff9ab220); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TEST(ScoreTest, GeneratedScenarioEight) { | ||||||
|  |   std::map<Argb, uint32_t> argb_to_population = { | ||||||
|  |       {0xff816ec5, 24}, | ||||||
|  |       {0xff6dcb94, 19}, | ||||||
|  |       {0xff3cae91, 98}, | ||||||
|  |       {0xff5b542f, 25}, | ||||||
|  |   }; | ||||||
|  | 
 | ||||||
|  |   std::vector<Argb> ranked = RankedSuggestions( | ||||||
|  |       argb_to_population, | ||||||
|  |       {.desired = 1, .fallback_color_argb = (int)0xff84b0fd, .filter = false}); | ||||||
|  | 
 | ||||||
|  |   EXPECT_EQ(ranked.size(), 1u); | ||||||
|  |   EXPECT_EQ(ranked[0], 0xff3cae91); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TEST(ScoreTest, GeneratedScenarioNine) { | ||||||
|  |   std::map<Argb, uint32_t> argb_to_population = { | ||||||
|  |       {0xff206f86, 52}, {0xff4a620d, 96}, {0xfff51401, 85}, | ||||||
|  |       {0xff2b8ebf, 3},  {0xff277766, 59}, | ||||||
|  |   }; | ||||||
|  | 
 | ||||||
|  |   std::vector<Argb> ranked = RankedSuggestions( | ||||||
|  |       argb_to_population, | ||||||
|  |       {.desired = 3, .fallback_color_argb = (int)0xff02b415, .filter = true}); | ||||||
|  | 
 | ||||||
|  |   EXPECT_EQ(ranked.size(), 3u); | ||||||
|  |   EXPECT_EQ(ranked[0], 0xfff51401); | ||||||
|  |   EXPECT_EQ(ranked[1], 0xff4a620d); | ||||||
|  |   EXPECT_EQ(ranked[2], 0xff2b8ebf); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TEST(ScoreTest, GeneratedScenarioTen) { | ||||||
|  |   std::map<Argb, uint32_t> argb_to_population = { | ||||||
|  |       {0xff8b1d99, 54}, | ||||||
|  |       {0xff27effe, 43}, | ||||||
|  |       {0xff6f558d, 2}, | ||||||
|  |       {0xff77fdf2, 78}, | ||||||
|  |   }; | ||||||
|  | 
 | ||||||
|  |   std::vector<Argb> ranked = RankedSuggestions( | ||||||
|  |       argb_to_population, | ||||||
|  |       {.desired = 4, .fallback_color_argb = (int)0xff5e7a10, .filter = true}); | ||||||
|  | 
 | ||||||
|  |   EXPECT_EQ(ranked.size(), 3u); | ||||||
|  |   EXPECT_EQ(ranked[0], 0xff27effe); | ||||||
|  |   EXPECT_EQ(ranked[1], 0xff8b1d99); | ||||||
|  |   EXPECT_EQ(ranked[2], 0xff6f558d); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | }  // namespace
 | ||||||
|  | }  // namespace material_color_utilities
 | ||||||
							
								
								
									
										249
									
								
								src/material-colors/cpp/temperature/temperature_cache.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										249
									
								
								src/material-colors/cpp/temperature/temperature_cache.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,249 @@ | ||||||
|  | /*
 | ||||||
|  |  * Copyright 2022 Google LLC | ||||||
|  |  * | ||||||
|  |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * | ||||||
|  |  *      http://www.apache.org/licenses/LICENSE-2.0
 | ||||||
|  |  * | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | #include "cpp/temperature/temperature_cache.h" | ||||||
|  | 
 | ||||||
|  | #include <algorithm> | ||||||
|  | #include <map> | ||||||
|  | #include <vector> | ||||||
|  | 
 | ||||||
|  | #include "cpp/cam/hct.h" | ||||||
|  | #include "cpp/quantize/lab.h" | ||||||
|  | #include "cpp/utils/utils.h" | ||||||
|  | 
 | ||||||
|  | namespace material_color_utilities { | ||||||
|  | 
 | ||||||
|  | TemperatureCache::TemperatureCache(Hct input) : input_(input) {} | ||||||
|  | 
 | ||||||
|  | Hct TemperatureCache::GetComplement() { | ||||||
|  |   if (precomputed_complement_.has_value()) { | ||||||
|  |     return precomputed_complement_.value(); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   double coldest_hue = GetColdest().get_hue(); | ||||||
|  |   double coldest_temp = GetTempsByHct().at(GetColdest()); | ||||||
|  | 
 | ||||||
|  |   double warmest_hue = GetWarmest().get_hue(); | ||||||
|  |   double warmest_temp = GetTempsByHct().at(GetWarmest()); | ||||||
|  |   double range = warmest_temp - coldest_temp; | ||||||
|  |   bool start_hue_is_coldest_to_warmest = | ||||||
|  |       IsBetween(input_.get_hue(), coldest_hue, warmest_hue); | ||||||
|  |   double start_hue = | ||||||
|  |       start_hue_is_coldest_to_warmest ? warmest_hue : coldest_hue; | ||||||
|  |   double end_hue = start_hue_is_coldest_to_warmest ? coldest_hue : warmest_hue; | ||||||
|  |   double direction_of_rotation = 1.0; | ||||||
|  |   double smallest_error = 1000.0; | ||||||
|  |   Hct answer = GetHctsByHue().at((int)round(input_.get_hue())); | ||||||
|  | 
 | ||||||
|  |   double complement_relative_temp = (1.0 - GetRelativeTemperature(input_)); | ||||||
|  |   // Find the color in the other section, closest to the inverse percentile
 | ||||||
|  |   // of the input color. This is the complement.
 | ||||||
|  |   for (double hue_addend = 0.0; hue_addend <= 360.0; hue_addend += 1.0) { | ||||||
|  |     double hue = | ||||||
|  |         SanitizeDegreesDouble(start_hue + direction_of_rotation * hue_addend); | ||||||
|  |     if (!IsBetween(hue, start_hue, end_hue)) { | ||||||
|  |       continue; | ||||||
|  |     } | ||||||
|  |     Hct possible_answer = GetHctsByHue().at((int)round(hue)); | ||||||
|  |     double relative_temp = | ||||||
|  |         (GetTempsByHct().at(possible_answer) - coldest_temp) / range; | ||||||
|  |     double error = abs(complement_relative_temp - relative_temp); | ||||||
|  |     if (error < smallest_error) { | ||||||
|  |       smallest_error = error; | ||||||
|  |       answer = possible_answer; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |   precomputed_complement_ = answer; | ||||||
|  |   return precomputed_complement_.value(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | std::vector<Hct> TemperatureCache::GetAnalogousColors() { | ||||||
|  |   return GetAnalogousColors(5, 12); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | std::vector<Hct> TemperatureCache::GetAnalogousColors(int count, | ||||||
|  |                                                       int divisions) { | ||||||
|  |   // The starting hue is the hue of the input color.
 | ||||||
|  |   int start_hue = (int)round(input_.get_hue()); | ||||||
|  |   Hct start_hct = GetHctsByHue().at(start_hue); | ||||||
|  |   double last_temp = GetRelativeTemperature(start_hct); | ||||||
|  | 
 | ||||||
|  |   std::vector<Hct> all_colors; | ||||||
|  |   all_colors.push_back(start_hct); | ||||||
|  | 
 | ||||||
|  |   double absolute_total_temp_delta = 0.0; | ||||||
|  |   for (int i = 0; i < 360; i++) { | ||||||
|  |     int hue = SanitizeDegreesInt(start_hue + i); | ||||||
|  |     Hct hct = GetHctsByHue().at(hue); | ||||||
|  |     double temp = GetRelativeTemperature(hct); | ||||||
|  |     double temp_delta = abs(temp - last_temp); | ||||||
|  |     last_temp = temp; | ||||||
|  |     absolute_total_temp_delta += temp_delta; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   int hue_addend = 1; | ||||||
|  |   double temp_step = absolute_total_temp_delta / (double)divisions; | ||||||
|  |   double total_temp_delta = 0.0; | ||||||
|  |   last_temp = GetRelativeTemperature(start_hct); | ||||||
|  |   while (all_colors.size() < static_cast<size_t>(divisions)) { | ||||||
|  |     int hue = SanitizeDegreesInt(start_hue + hue_addend); | ||||||
|  |     Hct hct = GetHctsByHue().at(hue); | ||||||
|  |     double temp = GetRelativeTemperature(hct); | ||||||
|  |     double temp_delta = abs(temp - last_temp); | ||||||
|  |     total_temp_delta += temp_delta; | ||||||
|  | 
 | ||||||
|  |     double desired_total_temp_delta_for_index = (all_colors.size() * temp_step); | ||||||
|  |     bool index_satisfied = | ||||||
|  |         total_temp_delta >= desired_total_temp_delta_for_index; | ||||||
|  |     int index_addend = 1; | ||||||
|  |     // Keep adding this hue to the answers until its temperature is
 | ||||||
|  |     // insufficient. This ensures consistent behavior when there aren't
 | ||||||
|  |     // `divisions` discrete steps between 0 and 360 in hue with `temp_step`
 | ||||||
|  |     // delta in temperature between them.
 | ||||||
|  |     //
 | ||||||
|  |     // For example, white and black have no analogues: there are no other
 | ||||||
|  |     // colors at T100/T0. Therefore, they should just be added to the array
 | ||||||
|  |     // as answers.
 | ||||||
|  |     while (index_satisfied && | ||||||
|  |            all_colors.size() < static_cast<size_t>(divisions)) { | ||||||
|  |       all_colors.push_back(hct); | ||||||
|  |       desired_total_temp_delta_for_index = | ||||||
|  |           ((all_colors.size() + index_addend) * temp_step); | ||||||
|  |       index_satisfied = total_temp_delta >= desired_total_temp_delta_for_index; | ||||||
|  |       index_addend++; | ||||||
|  |     } | ||||||
|  |     last_temp = temp; | ||||||
|  |     hue_addend++; | ||||||
|  | 
 | ||||||
|  |     if (hue_addend > 360) { | ||||||
|  |       while (all_colors.size() < static_cast<size_t>(divisions)) { | ||||||
|  |         all_colors.push_back(hct); | ||||||
|  |       } | ||||||
|  |       break; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   std::vector<Hct> answers; | ||||||
|  |   answers.push_back(input_); | ||||||
|  | 
 | ||||||
|  |   int ccw_count = (int)floor(((double)count - 1.0) / 2.0); | ||||||
|  |   for (int i = 1; i < (ccw_count + 1); i++) { | ||||||
|  |     int index = 0 - i; | ||||||
|  |     while (index < 0) { | ||||||
|  |       index = all_colors.size() + index; | ||||||
|  |     } | ||||||
|  |     if (static_cast<size_t>(index) >= all_colors.size()) { | ||||||
|  |       index = index % all_colors.size(); | ||||||
|  |     } | ||||||
|  |     answers.insert(answers.begin(), all_colors.at(index)); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   int cw_count = count - ccw_count - 1; | ||||||
|  |   for (int i = 1; i < (cw_count + 1); i++) { | ||||||
|  |     size_t index = i; | ||||||
|  |     while (index < 0) { | ||||||
|  |       index = all_colors.size() + index; | ||||||
|  |     } | ||||||
|  |     if (index >= all_colors.size()) { | ||||||
|  |       index = index % all_colors.size(); | ||||||
|  |     } | ||||||
|  |     answers.push_back(all_colors.at(index)); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   return answers; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | double TemperatureCache::GetRelativeTemperature(Hct hct) { | ||||||
|  |   double range = | ||||||
|  |       GetTempsByHct().at(GetWarmest()) - GetTempsByHct().at(GetColdest()); | ||||||
|  |   double difference_from_coldest = | ||||||
|  |       GetTempsByHct().at(hct) - GetTempsByHct().at(GetColdest()); | ||||||
|  |   // Handle when there's no difference in temperature between warmest and
 | ||||||
|  |   // coldest: for example, at T100, only one color is available, white.
 | ||||||
|  |   if (range == 0.) { | ||||||
|  |     return 0.5; | ||||||
|  |   } | ||||||
|  |   return difference_from_coldest / range; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | double TemperatureCache::RawTemperature(Hct color) { | ||||||
|  |   Lab lab = LabFromInt(color.ToInt()); | ||||||
|  |   double hue = SanitizeDegreesDouble(atan2(lab.b, lab.a) * 180.0 / kPi); | ||||||
|  |   double chroma = hypot(lab.a, lab.b); | ||||||
|  |   return -0.5 + 0.02 * pow(chroma, 1.07) * | ||||||
|  |                     cos(SanitizeDegreesDouble(hue - 50.) * kPi / 180); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | Hct TemperatureCache::GetColdest() { return GetHctsByTemp().at(0); } | ||||||
|  | 
 | ||||||
|  | std::vector<Hct> TemperatureCache::GetHctsByHue() { | ||||||
|  |   if (precomputed_hcts_by_hue_.has_value()) { | ||||||
|  |     return precomputed_hcts_by_hue_.value(); | ||||||
|  |   } | ||||||
|  |   std::vector<Hct> hcts; | ||||||
|  |   for (double hue = 0.; hue <= 360.; hue += 1.) { | ||||||
|  |     Hct color_at_hue(hue, input_.get_chroma(), input_.get_tone()); | ||||||
|  |     hcts.push_back(color_at_hue); | ||||||
|  |   } | ||||||
|  |   precomputed_hcts_by_hue_ = hcts; | ||||||
|  |   return precomputed_hcts_by_hue_.value(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | std::vector<Hct> TemperatureCache::GetHctsByTemp() { | ||||||
|  |   if (precomputed_hcts_by_temp_.has_value()) { | ||||||
|  |     return precomputed_hcts_by_temp_.value(); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   std::vector<Hct> hcts(GetHctsByHue()); | ||||||
|  |   hcts.push_back(input_); | ||||||
|  |   std::map<Hct, double> temps_by_hct(GetTempsByHct()); | ||||||
|  |   sort(hcts.begin(), hcts.end(), | ||||||
|  |        [temps_by_hct](const Hct a, const Hct b) -> bool { | ||||||
|  |          return temps_by_hct.at(a) < temps_by_hct.at(b); | ||||||
|  |        }); | ||||||
|  |   precomputed_hcts_by_temp_ = hcts; | ||||||
|  |   return precomputed_hcts_by_temp_.value(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | std::map<Hct, double> TemperatureCache::GetTempsByHct() { | ||||||
|  |   if (precomputed_temps_by_hct_.has_value()) { | ||||||
|  |     return precomputed_temps_by_hct_.value(); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   std::vector<Hct> all_hcts(GetHctsByHue()); | ||||||
|  |   all_hcts.push_back(input_); | ||||||
|  | 
 | ||||||
|  |   std::map<Hct, double> temperatures_by_hct; | ||||||
|  |   for (Hct hct : all_hcts) { | ||||||
|  |     temperatures_by_hct[hct] = RawTemperature(hct); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   precomputed_temps_by_hct_ = temperatures_by_hct; | ||||||
|  |   return precomputed_temps_by_hct_.value(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | Hct TemperatureCache::GetWarmest() { | ||||||
|  |   return GetHctsByTemp().at(GetHctsByTemp().size() - 1); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool TemperatureCache::IsBetween(double angle, double a, double b) { | ||||||
|  |   if (a < b) { | ||||||
|  |     return a <= angle && angle <= b; | ||||||
|  |   } | ||||||
|  |   return a <= angle || angle <= b; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | }  // namespace material_color_utilities
 | ||||||
							
								
								
									
										139
									
								
								src/material-colors/cpp/temperature/temperature_cache.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										139
									
								
								src/material-colors/cpp/temperature/temperature_cache.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,139 @@ | ||||||
|  | /*
 | ||||||
|  |  * Copyright 2022 Google LLC | ||||||
|  |  * | ||||||
|  |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * | ||||||
|  |  *      http://www.apache.org/licenses/LICENSE-2.0
 | ||||||
|  |  * | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | #ifndef CPP_TEMPERATURE_TEMPERATURE_CACHE_H_ | ||||||
|  | #define CPP_TEMPERATURE_TEMPERATURE_CACHE_H_ | ||||||
|  | 
 | ||||||
|  | #include <map> | ||||||
|  | #include <vector> | ||||||
|  | 
 | ||||||
|  | #include "cpp/cam/hct.h" | ||||||
|  | 
 | ||||||
|  | namespace material_color_utilities { | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Design utilities using color temperature theory. | ||||||
|  |  * | ||||||
|  |  * <p>Analogous colors, complementary color, and cache to efficiently, lazily, | ||||||
|  |  * generate data for calculations when needed. | ||||||
|  |  */ | ||||||
|  | class TemperatureCache { | ||||||
|  |  public: | ||||||
|  |   /**
 | ||||||
|  |    * Create a cache that allows calculation of ex. complementary and analogous | ||||||
|  |    * colors. | ||||||
|  |    * | ||||||
|  |    * @param input Color to find complement/analogous colors of. Any colors will | ||||||
|  |    * have the same tone, and chroma as the input color, modulo any restrictions | ||||||
|  |    * due to the other hues having lower limits on chroma. | ||||||
|  |    */ | ||||||
|  |   explicit TemperatureCache(Hct input); | ||||||
|  | 
 | ||||||
|  |   /**
 | ||||||
|  |    * A color that complements the input color aesthetically. | ||||||
|  |    * | ||||||
|  |    * <p>In art, this is usually described as being across the color wheel. | ||||||
|  |    * History of this shows intent as a color that is just as cool-warm as the | ||||||
|  |    * input color is warm-cool. | ||||||
|  |    */ | ||||||
|  |   Hct GetComplement(); | ||||||
|  | 
 | ||||||
|  |   /**
 | ||||||
|  |    * 5 colors that pair well with the input color. | ||||||
|  |    * | ||||||
|  |    * <p>The colors are equidistant in temperature and adjacent in hue. | ||||||
|  |    */ | ||||||
|  |   std::vector<Hct> GetAnalogousColors(); | ||||||
|  | 
 | ||||||
|  |   /**
 | ||||||
|  |    * A set of colors with differing hues, equidistant in temperature. | ||||||
|  |    * | ||||||
|  |    * <p>In art, this is usually described as a set of 5 colors on a color wheel | ||||||
|  |    * divided into 12 sections. This method allows provision of either of those | ||||||
|  |    * values. | ||||||
|  |    * | ||||||
|  |    * <p>Behavior is undefined when count or divisions is 0. When divisions < | ||||||
|  |    * count, colors repeat. | ||||||
|  |    * | ||||||
|  |    * @param count The number of colors to return, includes the input color. | ||||||
|  |    * @param divisions The number of divisions on the color wheel. | ||||||
|  |    */ | ||||||
|  |   std::vector<Hct> GetAnalogousColors(int count, int divisions); | ||||||
|  | 
 | ||||||
|  |   /**
 | ||||||
|  |    * Temperature relative to all colors with the same chroma and tone. | ||||||
|  |    * | ||||||
|  |    * @param hct HCT to find the relative temperature of. | ||||||
|  |    * @return Value on a scale from 0 to 1. | ||||||
|  |    */ | ||||||
|  |   double GetRelativeTemperature(Hct hct); | ||||||
|  | 
 | ||||||
|  |   /**
 | ||||||
|  |    * Value representing cool-warm factor of a color. Values below 0 are | ||||||
|  |    * considered cool, above, warm. | ||||||
|  |    * | ||||||
|  |    * <p>Color science has researched emotion and harmony, which art uses to | ||||||
|  |    * select colors. Warm-cool is the foundation of analogous and complementary | ||||||
|  |    * colors. See: - Li-Chen Ou's Chapter 19 in Handbook of Color Psychology | ||||||
|  |    * (2015). - Josef Albers' Interaction of Color chapters 19 and 21. | ||||||
|  |    * | ||||||
|  |    * <p>Implementation of Ou, Woodcock and Wright's algorithm, which uses | ||||||
|  |    * Lab/LCH color space. Return value has these properties:<br> | ||||||
|  |    * - Values below 0 are cool, above 0 are warm.<br> | ||||||
|  |    * - Lower bound: -9.66. Chroma is infinite. Assuming max of Lab chroma | ||||||
|  |    * 130.<br> | ||||||
|  |    * - Upper bound: 8.61. Chroma is infinite. Assuming max of Lab chroma 130. | ||||||
|  |    */ | ||||||
|  |   static double RawTemperature(Hct color); | ||||||
|  | 
 | ||||||
|  |  private: | ||||||
|  |   Hct input_; | ||||||
|  | 
 | ||||||
|  |   std::optional<Hct> precomputed_complement_; | ||||||
|  |   std::optional<std::vector<Hct>> precomputed_hcts_by_temp_; | ||||||
|  |   std::optional<std::vector<Hct>> precomputed_hcts_by_hue_; | ||||||
|  |   std::optional<std::map<Hct, double>> precomputed_temps_by_hct_; | ||||||
|  | 
 | ||||||
|  |   /** Coldest color with same chroma and tone as input. */ | ||||||
|  |   Hct GetColdest(); | ||||||
|  | 
 | ||||||
|  |   /** Warmest color with same chroma and tone as input. */ | ||||||
|  |   Hct GetWarmest(); | ||||||
|  | 
 | ||||||
|  |   /** Determines if an angle is between two other angles, rotating clockwise. */ | ||||||
|  |   static bool IsBetween(double angle, double a, double b); | ||||||
|  | 
 | ||||||
|  |   /**
 | ||||||
|  |    * HCTs for all colors with the same chroma/tone as the input. | ||||||
|  |    * | ||||||
|  |    * <p>Sorted by hue, ex. index 0 is hue 0. | ||||||
|  |    */ | ||||||
|  |   std::vector<Hct> GetHctsByHue(); | ||||||
|  | 
 | ||||||
|  |   /**
 | ||||||
|  |    * HCTs for all colors with the same chroma/tone as the input. | ||||||
|  |    * | ||||||
|  |    * <p>Sorted from coldest first to warmest last. | ||||||
|  |    */ | ||||||
|  |   std::vector<Hct> GetHctsByTemp(); | ||||||
|  | 
 | ||||||
|  |   /** Keys of HCTs in GetHctsByTemp, values of raw temperature. */ | ||||||
|  |   std::map<Hct, double> GetTempsByHct(); | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | }  // namespace material_color_utilities
 | ||||||
|  | 
 | ||||||
|  | #endif  // CPP_TEMPERATURE_TEMPERATURE_CACHE_H_
 | ||||||
							
								
								
									
										115
									
								
								src/material-colors/cpp/temperature/temperature_cache_test.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										115
									
								
								src/material-colors/cpp/temperature/temperature_cache_test.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,115 @@ | ||||||
|  | /*
 | ||||||
|  |  * Copyright 2022 Google LLC | ||||||
|  |  * | ||||||
|  |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * | ||||||
|  |  *      http://www.apache.org/licenses/LICENSE-2.0
 | ||||||
|  |  * | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | #include "cpp/temperature/temperature_cache.h" | ||||||
|  | 
 | ||||||
|  | #include <vector> | ||||||
|  | 
 | ||||||
|  | #include "testing/base/public/gunit.h" | ||||||
|  | #include "cpp/cam/hct.h" | ||||||
|  | 
 | ||||||
|  | namespace material_color_utilities { | ||||||
|  | 
 | ||||||
|  | namespace { | ||||||
|  | 
 | ||||||
|  | TEST(TemperatureCacheTest, RawTemperature) { | ||||||
|  |   Hct blue_hct(0xff0000ff); | ||||||
|  |   double blue_temp = TemperatureCache::RawTemperature(blue_hct); | ||||||
|  |   EXPECT_NEAR(-1.393, blue_temp, 0.001); | ||||||
|  | 
 | ||||||
|  |   Hct red_hct(0xffff0000); | ||||||
|  |   double red_temp = TemperatureCache::RawTemperature(red_hct); | ||||||
|  |   EXPECT_NEAR(2.351, red_temp, 0.001); | ||||||
|  | 
 | ||||||
|  |   Hct green_hct(0xff00ff00); | ||||||
|  |   double green_temp = TemperatureCache::RawTemperature(green_hct); | ||||||
|  |   EXPECT_NEAR(-0.267, green_temp, 0.001); | ||||||
|  | 
 | ||||||
|  |   Hct white_hct(0xffffffff); | ||||||
|  |   double white_temp = TemperatureCache::RawTemperature(white_hct); | ||||||
|  |   EXPECT_NEAR(-0.5, white_temp, 0.001); | ||||||
|  | 
 | ||||||
|  |   Hct black_hct(0xff000000); | ||||||
|  |   double black_temp = TemperatureCache::RawTemperature(black_hct); | ||||||
|  |   EXPECT_NEAR(-0.5, black_temp, 0.001); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TEST(TemperatureCacheTest, Complement) { | ||||||
|  |   unsigned int blue_complement = | ||||||
|  |       TemperatureCache(Hct(0xff0000ff)).GetComplement().ToInt(); | ||||||
|  |   EXPECT_EQ(0xff9d0002, blue_complement); | ||||||
|  | 
 | ||||||
|  |   unsigned int red_complement = | ||||||
|  |       TemperatureCache(Hct(0xffff0000)).GetComplement().ToInt(); | ||||||
|  |   EXPECT_EQ(0xff007bfc, red_complement); | ||||||
|  | 
 | ||||||
|  |   unsigned int green_complement = | ||||||
|  |       TemperatureCache(Hct(0xff00ff00)).GetComplement().ToInt(); | ||||||
|  |   EXPECT_EQ(0xffffd2c9, green_complement); | ||||||
|  | 
 | ||||||
|  |   unsigned int white_complement = | ||||||
|  |       TemperatureCache(Hct(0xffffffff)).GetComplement().ToInt(); | ||||||
|  |   EXPECT_EQ(0xffffffff, white_complement); | ||||||
|  | 
 | ||||||
|  |   unsigned int black_complement = | ||||||
|  |       TemperatureCache(Hct(0xff000000)).GetComplement().ToInt(); | ||||||
|  |   EXPECT_EQ(0xff000000, black_complement); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TEST(TemperatureCacheTest, Analogous) { | ||||||
|  |   std::vector<Hct> blue_analogous = | ||||||
|  |       TemperatureCache(Hct(0xff0000ff)).GetAnalogousColors(); | ||||||
|  |   EXPECT_EQ(0xff00590c, blue_analogous.at(0).ToInt()); | ||||||
|  |   EXPECT_EQ(0xff00564e, blue_analogous.at(1).ToInt()); | ||||||
|  |   EXPECT_EQ(0xff0000ff, blue_analogous.at(2).ToInt()); | ||||||
|  |   EXPECT_EQ(0xff6700cc, blue_analogous.at(3).ToInt()); | ||||||
|  |   EXPECT_EQ(0xff81009f, blue_analogous.at(4).ToInt()); | ||||||
|  | 
 | ||||||
|  |   std::vector<Hct> red_analogous = | ||||||
|  |       TemperatureCache(Hct(0xffff0000)).GetAnalogousColors(); | ||||||
|  |   EXPECT_EQ(0xfff60082, red_analogous.at(0).ToInt()); | ||||||
|  |   EXPECT_EQ(0xfffc004c, red_analogous.at(1).ToInt()); | ||||||
|  |   EXPECT_EQ(0xffff0000, red_analogous.at(2).ToInt()); | ||||||
|  |   EXPECT_EQ(0xffd95500, red_analogous.at(3).ToInt()); | ||||||
|  |   EXPECT_EQ(0xffaf7200, red_analogous.at(4).ToInt()); | ||||||
|  | 
 | ||||||
|  |   std::vector<Hct> green_analogous = | ||||||
|  |       TemperatureCache(Hct(0xff00ff00)).GetAnalogousColors(); | ||||||
|  |   EXPECT_EQ(0xffcee900, green_analogous.at(0).ToInt()); | ||||||
|  |   EXPECT_EQ(0xff92f500, green_analogous.at(1).ToInt()); | ||||||
|  |   EXPECT_EQ(0xff00ff00, green_analogous.at(2).ToInt()); | ||||||
|  |   EXPECT_EQ(0xff00fd6f, green_analogous.at(3).ToInt()); | ||||||
|  |   EXPECT_EQ(0xff00fab3, green_analogous.at(4).ToInt()); | ||||||
|  | 
 | ||||||
|  |   std::vector<Hct> black_analogous = | ||||||
|  |       TemperatureCache(Hct(0xff000000)).GetAnalogousColors(); | ||||||
|  |   EXPECT_EQ(0xff000000, black_analogous.at(0).ToInt()); | ||||||
|  |   EXPECT_EQ(0xff000000, black_analogous.at(1).ToInt()); | ||||||
|  |   EXPECT_EQ(0xff000000, black_analogous.at(2).ToInt()); | ||||||
|  |   EXPECT_EQ(0xff000000, black_analogous.at(3).ToInt()); | ||||||
|  |   EXPECT_EQ(0xff000000, black_analogous.at(4).ToInt()); | ||||||
|  | 
 | ||||||
|  |   std::vector<Hct> white_analogous = | ||||||
|  |       TemperatureCache(Hct(0xffffffff)).GetAnalogousColors(); | ||||||
|  |   EXPECT_EQ(0xffffffff, white_analogous.at(0).ToInt()); | ||||||
|  |   EXPECT_EQ(0xffffffff, white_analogous.at(1).ToInt()); | ||||||
|  |   EXPECT_EQ(0xffffffff, white_analogous.at(2).ToInt()); | ||||||
|  |   EXPECT_EQ(0xffffffff, white_analogous.at(3).ToInt()); | ||||||
|  |   EXPECT_EQ(0xffffffff, white_analogous.at(4).ToInt()); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | }  // namespace
 | ||||||
|  | }  // namespace material_color_utilities
 | ||||||
							
								
								
									
										178
									
								
								src/material-colors/cpp/utils/utils.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										178
									
								
								src/material-colors/cpp/utils/utils.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,178 @@ | ||||||
|  | /*
 | ||||||
|  |  * Copyright 2022 Google LLC | ||||||
|  |  * | ||||||
|  |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * | ||||||
|  |  *      http://www.apache.org/licenses/LICENSE-2.0
 | ||||||
|  |  * | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | #include "cpp/utils/utils.h" | ||||||
|  | 
 | ||||||
|  | #include <math.h> | ||||||
|  | 
 | ||||||
|  | #include <algorithm> | ||||||
|  | #include <cmath> | ||||||
|  | #include <cstdint> | ||||||
|  | #include <cstdio> | ||||||
|  | #include <string> | ||||||
|  | #include <format> | ||||||
|  | 
 | ||||||
|  | //#include "absl/strings/str_cat.h"
 | ||||||
|  | 
 | ||||||
|  | namespace material_color_utilities { | ||||||
|  | 
 | ||||||
|  | int RedFromInt(const Argb argb) { return (argb & 0x00ff0000) >> 16; } | ||||||
|  | 
 | ||||||
|  | int GreenFromInt(const Argb argb) { return (argb & 0x0000ff00) >> 8; } | ||||||
|  | 
 | ||||||
|  | int BlueFromInt(const Argb argb) { return (argb & 0x000000ff); } | ||||||
|  | 
 | ||||||
|  | Argb ArgbFromRgb(const int red, const int green, const int blue) { | ||||||
|  |   return 0xFF000000 | ((red & 0xff) << 16) | ((green & 0xff) << 8) | | ||||||
|  |          (blue & 0xff); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Converts a color from linear RGB components to ARGB format.
 | ||||||
|  | Argb ArgbFromLinrgb(Vec3 linrgb) { | ||||||
|  |   int r = Delinearized(linrgb.a); | ||||||
|  |   int g = Delinearized(linrgb.b); | ||||||
|  |   int b = Delinearized(linrgb.c); | ||||||
|  | 
 | ||||||
|  |   return 0xFF000000 | ((r & 0x0ff) << 16) | ((g & 0x0ff) << 8) | (b & 0x0ff); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | int Delinearized(const double rgb_component) { | ||||||
|  |   double normalized = rgb_component / 100; | ||||||
|  |   double delinearized; | ||||||
|  |   if (normalized <= 0.0031308) { | ||||||
|  |     delinearized = normalized * 12.92; | ||||||
|  |   } else { | ||||||
|  |     delinearized = 1.055 * std::pow(normalized, 1.0 / 2.4) - 0.055; | ||||||
|  |   } | ||||||
|  |   return std::clamp((int)round(delinearized * 255.0), 0, 255); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | double Linearized(const int rgb_component) { | ||||||
|  |   double normalized = rgb_component / 255.0; | ||||||
|  |   if (normalized <= 0.040449936) { | ||||||
|  |     return normalized / 12.92 * 100.0; | ||||||
|  |   } else { | ||||||
|  |     return std::pow((normalized + 0.055) / 1.055, 2.4) * 100.0; | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | int AlphaFromInt(Argb argb) { return (argb & 0xff000000) >> 24; } | ||||||
|  | 
 | ||||||
|  | bool IsOpaque(Argb argb) { return AlphaFromInt(argb) == 255; } | ||||||
|  | 
 | ||||||
|  | double LstarFromArgb(Argb argb) { | ||||||
|  |   // xyz from argb
 | ||||||
|  |   int red = (argb & 0x00ff0000) >> 16; | ||||||
|  |   int green = (argb & 0x0000ff00) >> 8; | ||||||
|  |   int blue = (argb & 0x000000ff); | ||||||
|  |   double red_l = Linearized(red); | ||||||
|  |   double green_l = Linearized(green); | ||||||
|  |   double blue_l = Linearized(blue); | ||||||
|  |   double y = 0.2126 * red_l + 0.7152 * green_l + 0.0722 * blue_l; | ||||||
|  |   return LstarFromY(y); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | double YFromLstar(double lstar) { | ||||||
|  |   static const double ke = 8.0; | ||||||
|  |   if (lstar > ke) { | ||||||
|  |     double cube_root = (lstar + 16.0) / 116.0; | ||||||
|  |     double cube = cube_root * cube_root * cube_root; | ||||||
|  |     return cube * 100.0; | ||||||
|  |   } else { | ||||||
|  |     return lstar / (24389.0 / 27.0) * 100.0; | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | double LstarFromY(double y) { | ||||||
|  |   static const double e = 216.0 / 24389.0; | ||||||
|  |   double yNormalized = y / 100.0; | ||||||
|  |   if (yNormalized <= e) { | ||||||
|  |     return (24389.0 / 27.0) * yNormalized; | ||||||
|  |   } else { | ||||||
|  |     return 116.0 * std::pow(yNormalized, 1.0 / 3.0) - 16.0; | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | int SanitizeDegreesInt(const int degrees) { | ||||||
|  |   if (degrees < 0) { | ||||||
|  |     return (degrees % 360) + 360; | ||||||
|  |   } else if (degrees >= 360.0) { | ||||||
|  |     return degrees % 360; | ||||||
|  |   } else { | ||||||
|  |     return degrees; | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Sanitizes a degree measure as a floating-point number.
 | ||||||
|  | //
 | ||||||
|  | // Returns a degree measure between 0.0 (inclusive) and 360.0 (exclusive).
 | ||||||
|  | double SanitizeDegreesDouble(const double degrees) { | ||||||
|  |   if (degrees < 0.0) { | ||||||
|  |     return fmod(degrees, 360.0) + 360; | ||||||
|  |   } else if (degrees >= 360.0) { | ||||||
|  |     return fmod(degrees, 360.0); | ||||||
|  |   } else { | ||||||
|  |     return degrees; | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | double DiffDegrees(const double a, const double b) { | ||||||
|  |   return 180.0 - abs(abs(a - b) - 180.0); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | double RotationDirection(const double from, const double to) { | ||||||
|  |   double increasing_difference = SanitizeDegreesDouble(to - from); | ||||||
|  |   return increasing_difference <= 180.0 ? 1.0 : -1.0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Converts a color in ARGB format to a hexadecimal string in lowercase.
 | ||||||
|  | //
 | ||||||
|  | // For instance: hex_from_argb(0xff012345) == "ff012345"
 | ||||||
|  | std::string HexFromArgb(Argb argb) { return std::to_string(argb); } | ||||||
|  | 
 | ||||||
|  | Argb IntFromLstar(const double lstar) { | ||||||
|  |   double y = YFromLstar(lstar); | ||||||
|  |   int component = Delinearized(y); | ||||||
|  |   return ArgbFromRgb(component, component, component); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // The signum function.
 | ||||||
|  | //
 | ||||||
|  | // Returns 1 if num > 0, -1 if num < 0, and 0 if num = 0
 | ||||||
|  | int Signum(double num) { | ||||||
|  |   if (num < 0) { | ||||||
|  |     return -1; | ||||||
|  |   } else if (num == 0) { | ||||||
|  |     return 0; | ||||||
|  |   } else { | ||||||
|  |     return 1; | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | double Lerp(double start, double stop, double amount) { | ||||||
|  |   return (1.0 - amount) * start + amount * stop; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | Vec3 MatrixMultiply(Vec3 input, const double matrix[3][3]) { | ||||||
|  |   double a = | ||||||
|  |       input.a * matrix[0][0] + input.b * matrix[0][1] + input.c * matrix[0][2]; | ||||||
|  |   double b = | ||||||
|  |       input.a * matrix[1][0] + input.b * matrix[1][1] + input.c * matrix[1][2]; | ||||||
|  |   double c = | ||||||
|  |       input.a * matrix[2][0] + input.b * matrix[2][1] + input.c * matrix[2][2]; | ||||||
|  |   return (Vec3){a, b, c}; | ||||||
|  | } | ||||||
|  | }  // namespace material_color_utilities
 | ||||||
							
								
								
									
										211
									
								
								src/material-colors/cpp/utils/utils.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										211
									
								
								src/material-colors/cpp/utils/utils.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,211 @@ | ||||||
|  | /*
 | ||||||
|  |  * Copyright 2022 Google LLC | ||||||
|  |  * | ||||||
|  |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * | ||||||
|  |  *      http://www.apache.org/licenses/LICENSE-2.0
 | ||||||
|  |  * | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | #ifndef CPP_UTILS_UTILS_H_ | ||||||
|  | #define CPP_UTILS_UTILS_H_ | ||||||
|  | 
 | ||||||
|  | #include <cstdint> | ||||||
|  | #include <string> | ||||||
|  | 
 | ||||||
|  | namespace material_color_utilities { | ||||||
|  | 
 | ||||||
|  | using Argb = uint32_t; | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * A vector with three floating-point numbers as components. | ||||||
|  |  */ | ||||||
|  | struct Vec3 { | ||||||
|  |   double a = 0.0; | ||||||
|  |   double b = 0.0; | ||||||
|  |   double c = 0.0; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Value of pi. | ||||||
|  |  */ | ||||||
|  | inline constexpr double kPi = 3.141592653589793; | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Returns the standard white point; white on a sunny day. | ||||||
|  |  */ | ||||||
|  | inline constexpr double kWhitePointD65[] = {95.047, 100.0, 108.883}; | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Returns the red component of a color in ARGB format. | ||||||
|  |  */ | ||||||
|  | int RedFromInt(const Argb argb); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Returns the green component of a color in ARGB format. | ||||||
|  |  */ | ||||||
|  | int GreenFromInt(const Argb argb); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Returns the blue component of a color in ARGB format. | ||||||
|  |  */ | ||||||
|  | int BlueFromInt(const Argb argb); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Returns the alpha component of a color in ARGB format. | ||||||
|  |  */ | ||||||
|  | int AlphaFromInt(const Argb argb); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Converts a color from RGB components to ARGB format. | ||||||
|  |  */ | ||||||
|  | Argb ArgbFromRgb(const int red, const int green, const int blue); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Converts a color from linear RGB components to ARGB format. | ||||||
|  |  */ | ||||||
|  | Argb ArgbFromLinrgb(Vec3 linrgb); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Returns whether a color in ARGB format is opaque. | ||||||
|  |  */ | ||||||
|  | bool IsOpaque(const Argb argb); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Sanitizes a degree measure as an integer. | ||||||
|  |  * | ||||||
|  |  * @return a degree measure between 0 (inclusive) and 360 (exclusive). | ||||||
|  |  */ | ||||||
|  | int SanitizeDegreesInt(const int degrees); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Sanitizes a degree measure as an floating-point number. | ||||||
|  |  * | ||||||
|  |  * @return a degree measure between 0.0 (inclusive) and 360.0 (exclusive). | ||||||
|  |  */ | ||||||
|  | double SanitizeDegreesDouble(const double degrees); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Distance of two points on a circle, represented using degrees. | ||||||
|  |  */ | ||||||
|  | double DiffDegrees(const double a, const double b); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Sign of direction change needed to travel from one angle to | ||||||
|  |  * another. | ||||||
|  |  * | ||||||
|  |  * For angles that are 180 degrees apart from each other, both | ||||||
|  |  * directions have the same travel distance, so either direction is | ||||||
|  |  * shortest. The value 1.0 is returned in this case. | ||||||
|  |  * | ||||||
|  |  * @param from The angle travel starts from, in degrees. | ||||||
|  |  * | ||||||
|  |  * @param to The angle travel ends at, in degrees. | ||||||
|  |  * | ||||||
|  |  * @return -1 if decreasing from leads to the shortest travel | ||||||
|  |  * distance, 1 if increasing from leads to the shortest travel | ||||||
|  |  * distance. | ||||||
|  |  */ | ||||||
|  | double RotationDirection(const double from, const double to); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Computes the L* value of a color in ARGB representation. | ||||||
|  |  * | ||||||
|  |  * @param argb ARGB representation of a color | ||||||
|  |  * | ||||||
|  |  * @return L*, from L*a*b*, coordinate of the color | ||||||
|  |  */ | ||||||
|  | double LstarFromArgb(const Argb argb); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Returns the hexadecimal representation of a color. | ||||||
|  |  */ | ||||||
|  | std::string HexFromArgb(Argb argb); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Linearizes an RGB component. | ||||||
|  |  * | ||||||
|  |  * @param rgb_component 0 <= rgb_component <= 255, represents R/G/B | ||||||
|  |  * channel | ||||||
|  |  * | ||||||
|  |  * @return 0.0 <= output <= 100.0, color channel converted to | ||||||
|  |  * linear RGB space | ||||||
|  |  */ | ||||||
|  | double Linearized(const int rgb_component); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Delinearizes an RGB component. | ||||||
|  |  * | ||||||
|  |  * @param rgb_component 0.0 <= rgb_component <= 100.0, represents linear | ||||||
|  |  * R/G/B channel | ||||||
|  |  * | ||||||
|  |  * @return 0 <= output <= 255, color channel converted to regular | ||||||
|  |  * RGB space | ||||||
|  |  */ | ||||||
|  | int Delinearized(const double rgb_component); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Converts an L* value to a Y value. | ||||||
|  |  * | ||||||
|  |  * L* in L*a*b* and Y in XYZ measure the same quantity, luminance. | ||||||
|  |  * | ||||||
|  |  * L* measures perceptual luminance, a linear scale. Y in XYZ | ||||||
|  |  * measures relative luminance, a logarithmic scale. | ||||||
|  |  * | ||||||
|  |  * @param lstar L* in L*a*b*. 0.0 <= L* <= 100.0 | ||||||
|  |  * | ||||||
|  |  * @return Y in XYZ. 0.0 <= Y <= 100.0 | ||||||
|  |  */ | ||||||
|  | double YFromLstar(const double lstar); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Converts a Y value to an L* value. | ||||||
|  |  * | ||||||
|  |  * L* in L*a*b* and Y in XYZ measure the same quantity, luminance. | ||||||
|  |  * | ||||||
|  |  * L* measures perceptual luminance, a linear scale. Y in XYZ | ||||||
|  |  * measures relative luminance, a logarithmic scale. | ||||||
|  |  * | ||||||
|  |  * @param y Y in XYZ. 0.0 <= Y <= 100.0 | ||||||
|  |  * | ||||||
|  |  * @return L* in L*a*b*. 0.0 <= L* <= 100.0 | ||||||
|  |  */ | ||||||
|  | double LstarFromY(const double y); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Converts an L* value to an ARGB representation. | ||||||
|  |  * | ||||||
|  |  * @param lstar L* in L*a*b*. 0.0 <= L* <= 100.0 | ||||||
|  |  * | ||||||
|  |  * @return ARGB representation of grayscale color with lightness matching L* | ||||||
|  |  */ | ||||||
|  | Argb IntFromLstar(const double lstar); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * The signum function. | ||||||
|  |  * | ||||||
|  |  * @return 1 if num > 0, -1 if num < 0, and 0 if num = 0 | ||||||
|  |  */ | ||||||
|  | int Signum(double num); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * The linear interpolation function. | ||||||
|  |  * | ||||||
|  |  * @return start if amount = 0 and stop if amount = 1 | ||||||
|  |  */ | ||||||
|  | double Lerp(double start, double stop, double amount); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Multiplies a 1x3 row vector with a 3x3 matrix, returning the product. | ||||||
|  |  */ | ||||||
|  | Vec3 MatrixMultiply(Vec3 input, const double matrix[3][3]); | ||||||
|  | 
 | ||||||
|  | }  // namespace material_color_utilities
 | ||||||
|  | #endif  // CPP_UTILS_UTILS_H_
 | ||||||
							
								
								
									
										363
									
								
								src/material-colors/cpp/utils/utils_test.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										363
									
								
								src/material-colors/cpp/utils/utils_test.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,363 @@ | ||||||
|  | /*
 | ||||||
|  |  * Copyright 2022 Google LLC | ||||||
|  |  * | ||||||
|  |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * | ||||||
|  |  *      http://www.apache.org/licenses/LICENSE-2.0
 | ||||||
|  |  * | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | #include "cpp/utils/utils.h" | ||||||
|  | 
 | ||||||
|  | #include <cstdint> | ||||||
|  | 
 | ||||||
|  | #include "testing/base/public/gmock.h" | ||||||
|  | #include "testing/base/public/gunit.h" | ||||||
|  | 
 | ||||||
|  | namespace material_color_utilities { | ||||||
|  | 
 | ||||||
|  | namespace { | ||||||
|  | 
 | ||||||
|  | using testing::DoubleNear; | ||||||
|  | 
 | ||||||
|  | constexpr double kMatrix[3][3] = { | ||||||
|  |     {1, 2, 3}, | ||||||
|  |     {-4, 5, -6}, | ||||||
|  |     {-7, -8, -9}, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | TEST(ArgbFromRgbTest, ReturnsCorrectValueForBlack) { | ||||||
|  |   EXPECT_EQ(ArgbFromRgb(0, 0, 0), 0xff000000); | ||||||
|  |   EXPECT_EQ(ArgbFromRgb(0, 0, 0), 4278190080); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TEST(ArgbFromRgbTest, ReturnsCorrectValueForWhite) { | ||||||
|  |   EXPECT_EQ(ArgbFromRgb(255, 255, 255), 0xffffffff); | ||||||
|  |   EXPECT_EQ(ArgbFromRgb(255, 255, 255), 4294967295); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TEST(ArgbFromRgbTest, ReturnsCorrectValueForRandomColor) { | ||||||
|  |   EXPECT_EQ(ArgbFromRgb(50, 150, 250), 0xff3296fa); | ||||||
|  |   EXPECT_EQ(ArgbFromRgb(50, 150, 250), 4281505530); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TEST(UtilsTest, Signum) { | ||||||
|  |   EXPECT_EQ(Signum(0.001), 1); | ||||||
|  |   EXPECT_EQ(Signum(3.0), 1); | ||||||
|  |   EXPECT_EQ(Signum(100.0), 1); | ||||||
|  |   EXPECT_EQ(Signum(-0.002), -1); | ||||||
|  |   EXPECT_EQ(Signum(-4.0), -1); | ||||||
|  |   EXPECT_EQ(Signum(-101.0), -1); | ||||||
|  |   EXPECT_EQ(Signum(0.0), 0); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TEST(UtilsTest, RotationIsPositiveForCounterclockwise) { | ||||||
|  |   EXPECT_EQ(RotationDirection(0.0, 30.0), 1.0); | ||||||
|  |   EXPECT_EQ(RotationDirection(0.0, 60.0), 1.0); | ||||||
|  |   EXPECT_EQ(RotationDirection(0.0, 150.0), 1.0); | ||||||
|  |   EXPECT_EQ(RotationDirection(90.0, 240.0), 1.0); | ||||||
|  |   EXPECT_EQ(RotationDirection(300.0, 30.0), 1.0); | ||||||
|  |   EXPECT_EQ(RotationDirection(270.0, 60.0), 1.0); | ||||||
|  |   EXPECT_EQ(RotationDirection(360.0 * 2, 15.0), 1.0); | ||||||
|  |   EXPECT_EQ(RotationDirection(360.0 * 3 + 15.0, -360.0 * 4 + 30.0), 1.0); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TEST(UtilsTest, RotationIsNegativeForClockwise) { | ||||||
|  |   EXPECT_EQ(RotationDirection(30.0, 0.0), -1.0); | ||||||
|  |   EXPECT_EQ(RotationDirection(60.0, 0.0), -1.0); | ||||||
|  |   EXPECT_EQ(RotationDirection(150.0, 0.0), -1.0); | ||||||
|  |   EXPECT_EQ(RotationDirection(240.0, 90.0), -1.0); | ||||||
|  |   EXPECT_EQ(RotationDirection(30.0, 300.0), -1.0); | ||||||
|  |   EXPECT_EQ(RotationDirection(60.0, 270.0), -1.0); | ||||||
|  |   EXPECT_EQ(RotationDirection(15.0, -360.0 * 2), -1.0); | ||||||
|  |   EXPECT_EQ(RotationDirection(-360.0 * 4 + 270.0, 360.0 * 5 + 180.0), -1.0); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TEST(UtilsTest, AngleDifference) { | ||||||
|  |   EXPECT_EQ(DiffDegrees(0.0, 30.0), 30.0); | ||||||
|  |   EXPECT_EQ(DiffDegrees(0.0, 60.0), 60.0); | ||||||
|  |   EXPECT_EQ(DiffDegrees(0.0, 150.0), 150.0); | ||||||
|  |   EXPECT_EQ(DiffDegrees(90.0, 240.0), 150.0); | ||||||
|  |   EXPECT_EQ(DiffDegrees(300.0, 30.0), 90.0); | ||||||
|  |   EXPECT_EQ(DiffDegrees(270.0, 60.0), 150.0); | ||||||
|  | 
 | ||||||
|  |   EXPECT_EQ(DiffDegrees(30.0, 0.0), 30.0); | ||||||
|  |   EXPECT_EQ(DiffDegrees(60.0, 0.0), 60.0); | ||||||
|  |   EXPECT_EQ(DiffDegrees(150.0, 0.0), 150.0); | ||||||
|  |   EXPECT_EQ(DiffDegrees(240.0, 90.0), 150.0); | ||||||
|  |   EXPECT_EQ(DiffDegrees(30.0, 300.0), 90.0); | ||||||
|  |   EXPECT_EQ(DiffDegrees(60.0, 270.0), 150.0); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TEST(UtilsTest, AngleSanitation) { | ||||||
|  |   EXPECT_EQ(SanitizeDegreesInt(30), 30); | ||||||
|  |   EXPECT_EQ(SanitizeDegreesInt(240), 240); | ||||||
|  |   EXPECT_EQ(SanitizeDegreesInt(360), 0); | ||||||
|  |   EXPECT_EQ(SanitizeDegreesInt(-30), 330); | ||||||
|  |   EXPECT_EQ(SanitizeDegreesInt(-750), 330); | ||||||
|  |   EXPECT_EQ(SanitizeDegreesInt(-54321), 39); | ||||||
|  | 
 | ||||||
|  |   EXPECT_THAT(SanitizeDegreesDouble(30.0), DoubleNear(30.0, 1e-4)); | ||||||
|  |   EXPECT_THAT(SanitizeDegreesDouble(240.0), DoubleNear(240.0, 1e-4)); | ||||||
|  |   EXPECT_THAT(SanitizeDegreesDouble(360.0), DoubleNear(0.0, 1e-4)); | ||||||
|  |   EXPECT_THAT(SanitizeDegreesDouble(-30.0), DoubleNear(330.0, 1e-4)); | ||||||
|  |   EXPECT_THAT(SanitizeDegreesDouble(-750.0), DoubleNear(330.0, 1e-4)); | ||||||
|  |   EXPECT_THAT(SanitizeDegreesDouble(-54321.0), DoubleNear(39.0, 1e-4)); | ||||||
|  |   EXPECT_THAT(SanitizeDegreesDouble(360.125), DoubleNear(0.125, 1e-4)); | ||||||
|  |   EXPECT_THAT(SanitizeDegreesDouble(-11111.11), DoubleNear(48.89, 1e-4)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TEST(UtilsTest, MatrixMultiply) { | ||||||
|  |   Vec3 vector_one = MatrixMultiply({1, 3, 5}, kMatrix); | ||||||
|  |   EXPECT_THAT(vector_one.a, DoubleNear(22, 1e-4)); | ||||||
|  |   EXPECT_THAT(vector_one.b, DoubleNear(-19, 1e-4)); | ||||||
|  |   EXPECT_THAT(vector_one.c, DoubleNear(-76, 1e-4)); | ||||||
|  | 
 | ||||||
|  |   Vec3 vector_two = MatrixMultiply({-11.1, 22.2, -33.3}, kMatrix); | ||||||
|  |   EXPECT_THAT(vector_two.a, DoubleNear(-66.6, 1e-4)); | ||||||
|  |   EXPECT_THAT(vector_two.b, DoubleNear(355.2, 1e-4)); | ||||||
|  |   EXPECT_THAT(vector_two.c, DoubleNear(199.8, 1e-4)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TEST(UtilsTest, AlphaFromInt) { | ||||||
|  |   EXPECT_EQ(AlphaFromInt(0xff123456), 0xff); | ||||||
|  |   EXPECT_EQ(AlphaFromInt(0xffabcdef), 0xff); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TEST(UtilsTest, RedFromInt) { | ||||||
|  |   EXPECT_EQ(RedFromInt(0xff123456), 0x12); | ||||||
|  |   EXPECT_EQ(RedFromInt(0xffabcdef), 0xab); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TEST(UtilsTest, GreenFromInt) { | ||||||
|  |   EXPECT_EQ(GreenFromInt(0xff123456), 0x34); | ||||||
|  |   EXPECT_EQ(GreenFromInt(0xffabcdef), 0xcd); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TEST(UtilsTest, BlueFromInt) { | ||||||
|  |   EXPECT_EQ(BlueFromInt(0xff123456), 0x56); | ||||||
|  |   EXPECT_EQ(BlueFromInt(0xffabcdef), 0xef); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TEST(UtilsTest, Opaqueness) { | ||||||
|  |   EXPECT_TRUE(IsOpaque(0xff123456)); | ||||||
|  |   EXPECT_FALSE(IsOpaque(0xf0123456)); | ||||||
|  |   EXPECT_FALSE(IsOpaque(0x00123456)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TEST(UtilsTest, LinearizedComponents) { | ||||||
|  |   EXPECT_THAT(Linearized(0), DoubleNear(0.0, 1e-4)); | ||||||
|  |   EXPECT_THAT(Linearized(1), DoubleNear(0.0303527, 1e-4)); | ||||||
|  |   EXPECT_THAT(Linearized(2), DoubleNear(0.0607054, 1e-4)); | ||||||
|  |   EXPECT_THAT(Linearized(8), DoubleNear(0.242822, 1e-4)); | ||||||
|  |   EXPECT_THAT(Linearized(9), DoubleNear(0.273174, 1e-4)); | ||||||
|  |   EXPECT_THAT(Linearized(16), DoubleNear(0.518152, 1e-4)); | ||||||
|  |   EXPECT_THAT(Linearized(32), DoubleNear(1.44438, 1e-4)); | ||||||
|  |   EXPECT_THAT(Linearized(64), DoubleNear(5.12695, 1e-4)); | ||||||
|  |   EXPECT_THAT(Linearized(128), DoubleNear(21.5861, 1e-4)); | ||||||
|  |   EXPECT_THAT(Linearized(255), DoubleNear(100.0, 1e-4)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TEST(UtilsTest, DelinearizedComponents) { | ||||||
|  |   EXPECT_EQ(Delinearized(0.0), 0); | ||||||
|  |   EXPECT_EQ(Delinearized(0.0303527), 1); | ||||||
|  |   EXPECT_EQ(Delinearized(0.0607054), 2); | ||||||
|  |   EXPECT_EQ(Delinearized(0.242822), 8); | ||||||
|  |   EXPECT_EQ(Delinearized(0.273174), 9); | ||||||
|  |   EXPECT_EQ(Delinearized(0.518152), 16); | ||||||
|  |   EXPECT_EQ(Delinearized(1.44438), 32); | ||||||
|  |   EXPECT_EQ(Delinearized(5.12695), 64); | ||||||
|  |   EXPECT_EQ(Delinearized(21.5861), 128); | ||||||
|  |   EXPECT_EQ(Delinearized(100.0), 255); | ||||||
|  | 
 | ||||||
|  |   EXPECT_EQ(Delinearized(25.0), 137); | ||||||
|  |   EXPECT_EQ(Delinearized(50.0), 188); | ||||||
|  |   EXPECT_EQ(Delinearized(75.0), 225); | ||||||
|  | 
 | ||||||
|  |   // Delinearized clamps out-of-range inputs.
 | ||||||
|  |   EXPECT_EQ(Delinearized(-1.0), 0); | ||||||
|  |   EXPECT_EQ(Delinearized(-10000.0), 0); | ||||||
|  |   EXPECT_EQ(Delinearized(101.0), 255); | ||||||
|  |   EXPECT_EQ(Delinearized(10000.0), 255); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TEST(UtilsTest, DelinearizedIsLeftInverseOfLinearized) { | ||||||
|  |   EXPECT_EQ(Delinearized(Linearized(0)), 0); | ||||||
|  |   EXPECT_EQ(Delinearized(Linearized(1)), 1); | ||||||
|  |   EXPECT_EQ(Delinearized(Linearized(2)), 2); | ||||||
|  |   EXPECT_EQ(Delinearized(Linearized(8)), 8); | ||||||
|  |   EXPECT_EQ(Delinearized(Linearized(9)), 9); | ||||||
|  |   EXPECT_EQ(Delinearized(Linearized(16)), 16); | ||||||
|  |   EXPECT_EQ(Delinearized(Linearized(32)), 32); | ||||||
|  |   EXPECT_EQ(Delinearized(Linearized(64)), 64); | ||||||
|  |   EXPECT_EQ(Delinearized(Linearized(128)), 128); | ||||||
|  |   EXPECT_EQ(Delinearized(Linearized(255)), 255); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TEST(UtilsTest, ArgbFromLinrgb) { | ||||||
|  |   EXPECT_EQ(static_cast<uint32_t>(ArgbFromLinrgb({25.0, 50.0, 75.0})), | ||||||
|  |             0xff89bce1); | ||||||
|  |   EXPECT_EQ(static_cast<uint32_t>(ArgbFromLinrgb({0.03, 0.06, 0.12})), | ||||||
|  |             0xff010204); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TEST(UtilsTest, LstarFromArgb) { | ||||||
|  |   EXPECT_THAT(LstarFromArgb(0xff89bce1), DoubleNear(74.011, 1e-4)); | ||||||
|  |   EXPECT_THAT(LstarFromArgb(0xff010204), DoubleNear(0.529651, 1e-4)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TEST(UtilsTest, HexFromArgb) { | ||||||
|  |   EXPECT_EQ(HexFromArgb(0xff89bce1), "ff89bce1"); | ||||||
|  |   EXPECT_EQ(HexFromArgb(0xff010204), "ff010204"); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TEST(UtilsTest, IntFromLstar) { | ||||||
|  |   // Given an L* brightness value in [0, 100], IntFromLstar returns a greyscale
 | ||||||
|  |   // color in ARGB format with that brightness.
 | ||||||
|  |   // For L* outside the domain [0, 100], returns black or white.
 | ||||||
|  | 
 | ||||||
|  |   EXPECT_EQ(static_cast<uint32_t>(IntFromLstar(0.0)), 0xff000000); | ||||||
|  |   EXPECT_EQ(static_cast<uint32_t>(IntFromLstar(0.25)), 0xff010101); | ||||||
|  |   EXPECT_EQ(static_cast<uint32_t>(IntFromLstar(0.5)), 0xff020202); | ||||||
|  |   EXPECT_EQ(static_cast<uint32_t>(IntFromLstar(1.0)), 0xff040404); | ||||||
|  |   EXPECT_EQ(static_cast<uint32_t>(IntFromLstar(2.0)), 0xff070707); | ||||||
|  |   EXPECT_EQ(static_cast<uint32_t>(IntFromLstar(4.0)), 0xff0e0e0e); | ||||||
|  |   EXPECT_EQ(static_cast<uint32_t>(IntFromLstar(8.0)), 0xff181818); | ||||||
|  |   EXPECT_EQ(static_cast<uint32_t>(IntFromLstar(25.0)), 0xff3b3b3b); | ||||||
|  |   EXPECT_EQ(static_cast<uint32_t>(IntFromLstar(50.0)), 0xff777777); | ||||||
|  |   EXPECT_EQ(static_cast<uint32_t>(IntFromLstar(75.0)), 0xffb9b9b9); | ||||||
|  |   EXPECT_EQ(static_cast<uint32_t>(IntFromLstar(99.0)), 0xfffcfcfc); | ||||||
|  |   EXPECT_EQ(static_cast<uint32_t>(IntFromLstar(100.0)), 0xffffffff); | ||||||
|  | 
 | ||||||
|  |   EXPECT_EQ(static_cast<uint32_t>(IntFromLstar(-1.0)), 0xff000000); | ||||||
|  |   EXPECT_EQ(static_cast<uint32_t>(IntFromLstar(-2.0)), 0xff000000); | ||||||
|  |   EXPECT_EQ(static_cast<uint32_t>(IntFromLstar(-3.0)), 0xff000000); | ||||||
|  |   EXPECT_EQ(static_cast<uint32_t>(IntFromLstar(-9999999.0)), 0xff000000); | ||||||
|  | 
 | ||||||
|  |   EXPECT_EQ(static_cast<uint32_t>(IntFromLstar(101.0)), 0xffffffff); | ||||||
|  |   EXPECT_EQ(static_cast<uint32_t>(IntFromLstar(111.0)), 0xffffffff); | ||||||
|  |   EXPECT_EQ(static_cast<uint32_t>(IntFromLstar(9999999.0)), 0xffffffff); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TEST(UtilsTest, LstarArgbRoundtripProperty) { | ||||||
|  |   // Confirms that L* -> ARGB -> L* preserves original value
 | ||||||
|  |   // (taking ARGB rounding into consideration).
 | ||||||
|  |   EXPECT_THAT(LstarFromArgb(IntFromLstar(0.0)), DoubleNear(0.0, 1.0)); | ||||||
|  |   EXPECT_THAT(LstarFromArgb(IntFromLstar(1.0)), DoubleNear(1.0, 1.0)); | ||||||
|  |   EXPECT_THAT(LstarFromArgb(IntFromLstar(2.0)), DoubleNear(2.0, 1.0)); | ||||||
|  |   EXPECT_THAT(LstarFromArgb(IntFromLstar(8.0)), DoubleNear(8.0, 1.0)); | ||||||
|  |   EXPECT_THAT(LstarFromArgb(IntFromLstar(25.0)), DoubleNear(25.0, 1.0)); | ||||||
|  |   EXPECT_THAT(LstarFromArgb(IntFromLstar(50.0)), DoubleNear(50.0, 1.0)); | ||||||
|  |   EXPECT_THAT(LstarFromArgb(IntFromLstar(75.0)), DoubleNear(75.0, 1.0)); | ||||||
|  |   EXPECT_THAT(LstarFromArgb(IntFromLstar(99.0)), DoubleNear(99.0, 1.0)); | ||||||
|  |   EXPECT_THAT(LstarFromArgb(IntFromLstar(100.0)), DoubleNear(100.0, 1.0)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TEST(UtilsTest, ArgbLstarRoundtripProperty) { | ||||||
|  |   // Confirms that ARGB -> L* -> ARGB preserves original value
 | ||||||
|  |   // for greyscale colors.
 | ||||||
|  |   EXPECT_EQ(static_cast<uint32_t>(IntFromLstar(LstarFromArgb(0xff000000))), | ||||||
|  |             0xff000000); | ||||||
|  |   EXPECT_EQ(static_cast<uint32_t>(IntFromLstar(LstarFromArgb(0xff010101))), | ||||||
|  |             0xff010101); | ||||||
|  |   EXPECT_EQ(static_cast<uint32_t>(IntFromLstar(LstarFromArgb(0xff020202))), | ||||||
|  |             0xff020202); | ||||||
|  |   EXPECT_EQ(static_cast<uint32_t>(IntFromLstar(LstarFromArgb(0xff111111))), | ||||||
|  |             0xff111111); | ||||||
|  |   EXPECT_EQ(static_cast<uint32_t>(IntFromLstar(LstarFromArgb(0xff333333))), | ||||||
|  |             0xff333333); | ||||||
|  |   EXPECT_EQ(static_cast<uint32_t>(IntFromLstar(LstarFromArgb(0xff777777))), | ||||||
|  |             0xff777777); | ||||||
|  |   EXPECT_EQ(static_cast<uint32_t>(IntFromLstar(LstarFromArgb(0xffbbbbbb))), | ||||||
|  |             0xffbbbbbb); | ||||||
|  |   EXPECT_EQ(static_cast<uint32_t>(IntFromLstar(LstarFromArgb(0xfffefefe))), | ||||||
|  |             0xfffefefe); | ||||||
|  |   EXPECT_EQ(static_cast<uint32_t>(IntFromLstar(LstarFromArgb(0xffffffff))), | ||||||
|  |             0xffffffff); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TEST(UtilsTest, YFromLstar) { | ||||||
|  |   EXPECT_THAT(YFromLstar(0.0), DoubleNear(0.0, 1e-5)); | ||||||
|  |   EXPECT_THAT(YFromLstar(0.1), DoubleNear(0.0110705, 1e-5)); | ||||||
|  |   EXPECT_THAT(YFromLstar(0.2), DoubleNear(0.0221411, 1e-5)); | ||||||
|  |   EXPECT_THAT(YFromLstar(0.3), DoubleNear(0.0332116, 1e-5)); | ||||||
|  |   EXPECT_THAT(YFromLstar(0.4), DoubleNear(0.0442822, 1e-5)); | ||||||
|  |   EXPECT_THAT(YFromLstar(0.5), DoubleNear(0.0553528, 1e-5)); | ||||||
|  |   EXPECT_THAT(YFromLstar(1.0), DoubleNear(0.1107056, 1e-5)); | ||||||
|  |   EXPECT_THAT(YFromLstar(2.0), DoubleNear(0.2214112, 1e-5)); | ||||||
|  |   EXPECT_THAT(YFromLstar(3.0), DoubleNear(0.3321169, 1e-5)); | ||||||
|  |   EXPECT_THAT(YFromLstar(4.0), DoubleNear(0.4428225, 1e-5)); | ||||||
|  |   EXPECT_THAT(YFromLstar(5.0), DoubleNear(0.5535282, 1e-5)); | ||||||
|  |   EXPECT_THAT(YFromLstar(8.0), DoubleNear(0.8856451, 1e-5)); | ||||||
|  |   EXPECT_THAT(YFromLstar(10.0), DoubleNear(1.1260199, 1e-5)); | ||||||
|  |   EXPECT_THAT(YFromLstar(15.0), DoubleNear(1.9085832, 1e-5)); | ||||||
|  |   EXPECT_THAT(YFromLstar(20.0), DoubleNear(2.9890524, 1e-5)); | ||||||
|  |   EXPECT_THAT(YFromLstar(25.0), DoubleNear(4.4154767, 1e-5)); | ||||||
|  |   EXPECT_THAT(YFromLstar(30.0), DoubleNear(6.2359055, 1e-5)); | ||||||
|  |   EXPECT_THAT(YFromLstar(40.0), DoubleNear(11.2509737, 1e-5)); | ||||||
|  |   EXPECT_THAT(YFromLstar(50.0), DoubleNear(18.4186518, 1e-5)); | ||||||
|  |   EXPECT_THAT(YFromLstar(60.0), DoubleNear(28.1233342, 1e-5)); | ||||||
|  |   EXPECT_THAT(YFromLstar(70.0), DoubleNear(40.7494157, 1e-5)); | ||||||
|  |   EXPECT_THAT(YFromLstar(80.0), DoubleNear(56.6812907, 1e-5)); | ||||||
|  |   EXPECT_THAT(YFromLstar(90.0), DoubleNear(76.3033539, 1e-5)); | ||||||
|  |   EXPECT_THAT(YFromLstar(95.0), DoubleNear(87.6183294, 1e-5)); | ||||||
|  |   EXPECT_THAT(YFromLstar(99.0), DoubleNear(97.4360239, 1e-5)); | ||||||
|  |   EXPECT_THAT(YFromLstar(100.0), DoubleNear(100.0, 1e-5)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TEST(UtilsTest, LstarFromY) { | ||||||
|  |   EXPECT_THAT(LstarFromY(0.0), DoubleNear(0.0, 1e-5)); | ||||||
|  |   EXPECT_THAT(LstarFromY(0.1), DoubleNear(0.9032962, 1e-5)); | ||||||
|  |   EXPECT_THAT(LstarFromY(0.2), DoubleNear(1.8065925, 1e-5)); | ||||||
|  |   EXPECT_THAT(LstarFromY(0.3), DoubleNear(2.7098888, 1e-5)); | ||||||
|  |   EXPECT_THAT(LstarFromY(0.4), DoubleNear(3.6131851, 1e-5)); | ||||||
|  |   EXPECT_THAT(LstarFromY(0.5), DoubleNear(4.5164814, 1e-5)); | ||||||
|  |   EXPECT_THAT(LstarFromY(0.8856451), DoubleNear(8.0, 1e-5)); | ||||||
|  |   EXPECT_THAT(LstarFromY(1.0), DoubleNear(8.9914424, 1e-5)); | ||||||
|  |   EXPECT_THAT(LstarFromY(2.0), DoubleNear(15.4872443, 1e-5)); | ||||||
|  |   EXPECT_THAT(LstarFromY(3.0), DoubleNear(20.0438970, 1e-5)); | ||||||
|  |   EXPECT_THAT(LstarFromY(4.0), DoubleNear(23.6714419, 1e-5)); | ||||||
|  |   EXPECT_THAT(LstarFromY(5.0), DoubleNear(26.7347653, 1e-5)); | ||||||
|  |   EXPECT_THAT(LstarFromY(10.0), DoubleNear(37.8424304, 1e-5)); | ||||||
|  |   EXPECT_THAT(LstarFromY(15.0), DoubleNear(45.6341970, 1e-5)); | ||||||
|  |   EXPECT_THAT(LstarFromY(20.0), DoubleNear(51.8372115, 1e-5)); | ||||||
|  |   EXPECT_THAT(LstarFromY(25.0), DoubleNear(57.0754208, 1e-5)); | ||||||
|  |   EXPECT_THAT(LstarFromY(30.0), DoubleNear(61.6542222, 1e-5)); | ||||||
|  |   EXPECT_THAT(LstarFromY(40.0), DoubleNear(69.4695307, 1e-5)); | ||||||
|  |   EXPECT_THAT(LstarFromY(50.0), DoubleNear(76.0692610, 1e-5)); | ||||||
|  |   EXPECT_THAT(LstarFromY(60.0), DoubleNear(81.8381891, 1e-5)); | ||||||
|  |   EXPECT_THAT(LstarFromY(70.0), DoubleNear(86.9968642, 1e-5)); | ||||||
|  |   EXPECT_THAT(LstarFromY(80.0), DoubleNear(91.6848609, 1e-5)); | ||||||
|  |   EXPECT_THAT(LstarFromY(90.0), DoubleNear(95.9967686, 1e-5)); | ||||||
|  |   EXPECT_THAT(LstarFromY(95.0), DoubleNear(98.0335184, 1e-5)); | ||||||
|  |   EXPECT_THAT(LstarFromY(99.0), DoubleNear(99.6120372, 1e-5)); | ||||||
|  |   EXPECT_THAT(LstarFromY(100.0), DoubleNear(100.0, 1e-5)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TEST(UtilsTest, YLstarRoundtripProperty) { | ||||||
|  |   // Confirms that Y -> L* -> Y preserves original value.
 | ||||||
|  |   for (double y = 0.0; y <= 100.0; y += 0.1) { | ||||||
|  |     double lstar = LstarFromY(y); | ||||||
|  |     double reconstructedY = YFromLstar(lstar); | ||||||
|  |     EXPECT_THAT(reconstructedY, DoubleNear(y, 1e-8)); | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TEST(UtilsTest, LstarYRoundtripProperty) { | ||||||
|  |   // Confirms that L* -> Y -> L* preserves original value.
 | ||||||
|  |   for (double lstar = 0.0; lstar <= 100.0; lstar += 0.1) { | ||||||
|  |     double y = YFromLstar(lstar); | ||||||
|  |     double reconstructedLstar = LstarFromY(y); | ||||||
|  |     EXPECT_THAT(reconstructedLstar, DoubleNear(lstar, 1e-8)); | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | }  // namespace
 | ||||||
|  | }  // namespace material_color_utilities
 | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue