1 /* 2 * Copyright (c) 2023 Huawei Device Co., Ltd. 3 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * you may not use this file except in compliance with the License. 5 * You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software 10 * distributed under the License is distributed on an "AS IS" BASIS, 11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 * See the License for the specific language governing permissions and 13 * limitations under the License. 14 */ 15 #ifndef RS_COLOREXTRACT_H 16 #define RS_COLOREXTRACT_H 17 18 #include <cstdint> 19 #include <vector> 20 #include <utility> 21 #include <memory> 22 #include <limits> 23 #include <algorithm> 24 #include <math.h> 25 #include <queue> 26 27 #include "image/pixmap.h" 28 29 namespace OHOS { 30 namespace Rosen { 31 32 #ifdef _WIN32 33 #define NATIVEEXPORT __declspec(dllexport) 34 #else 35 #define NATIVEEXPORT 36 #endif 37 38 const uint32_t RS_COLOR_PICKER_ERR_EFFECT_INVALID_VALUE = 2; // Invalid value 39 const uint32_t RS_COLOR_PICKER_ERROR = 1; // Operation error 40 const uint32_t RS_COLOR_PICKER_SUCCESS = 0; // Operation success 41 42 class RSColorExtract { 43 public: ~RSColorExtract()44 virtual ~RSColorExtract() {}; 45 std::shared_ptr<Drawing::Pixmap> pixelmap_; 46 47 // Save the ARGB val of picture. 48 std::shared_ptr<uint32_t> colorVal_ = nullptr; 49 uint32_t colorValLen_ = 0; 50 uint32_t grayMsd_ = 0; 51 float contrastToWhite_ = 0; 52 53 // The first element is color, the second element is pixel num of this color. 54 std::vector<std::pair<uint32_t, uint32_t>> featureColors_; 55 56 // Specified number of extracted theme colors, default value is 10; 57 int specifiedFeatureColorNum_ = 10; 58 59 std::shared_ptr<uint32_t> hist_ = nullptr; 60 int distinctColorCount_ = 0; 61 std::shared_ptr<uint32_t> colors_ = nullptr; 62 63 static constexpr uint8_t ARGB_MASK = 0xFF; 64 static constexpr uint8_t ARGB_R_SHIFT = 16; 65 static constexpr uint8_t ARGB_G_SHIFT = 8; 66 static constexpr uint8_t ARGB_B_SHIFT = 0; 67 static uint8_t GetARGB32ColorR(unsigned int color); 68 static uint8_t GetARGB32ColorG(unsigned int color); 69 static uint8_t GetARGB32ColorB(unsigned int color); 70 NATIVEEXPORT void SetFeatureColorNum(int N); 71 void GetNFeatureColors(int colorNum); 72 protected: 73 RSColorExtract(std::shared_ptr<Drawing::Pixmap> pixmap); 74 RSColorExtract(std::shared_ptr<Drawing::Pixmap> pixmap, double* coordinates); 75 private: 76 static constexpr int COMPONENT_RED = -3; 77 static constexpr int COMPONENT_GREEN = -2; 78 static constexpr int COMPONENT_BLUE = -1; 79 static constexpr int QUANTIZE_WORD_WIDTH = 5; 80 static constexpr int QUANTIZE_WORD_MASK = (1 << QUANTIZE_WORD_WIDTH) - 1; 81 static uint32_t QuantizedRed(uint32_t color); 82 static uint32_t QuantizedGreen(uint32_t color); 83 static uint32_t QuantizedBlue(uint32_t color); 84 static uint32_t QuantizeFromRGB888(uint32_t color); 85 static uint32_t ModifyWordWidth(uint8_t color, int inWidth, int outWidth); 86 static uint32_t ApproximateToRGB888(uint32_t r, uint32_t g, uint32_t b); 87 static uint32_t ApproximateToRGB888(uint32_t color); 88 static bool cmp(std::pair<uint32_t, uint32_t>& a, std::pair<uint32_t, uint32_t>& b); 89 90 class VBox { 91 private: 92 int lowerIndex_, upperIndex_; 93 // total pixel nums in this box; 94 int pixelNums_; 95 uint32_t minRed_, maxRed_; 96 uint32_t minGreen_, maxGreen_; 97 uint32_t minBlue_, maxBlue_; 98 RSColorExtract *colorExtract_ = nullptr; 99 public: VBox(int lowerIndex,int upperIndex,RSColorExtract * colorExtract)100 VBox(int lowerIndex, int upperIndex, RSColorExtract *colorExtract) 101 { 102 lowerIndex_ = lowerIndex; 103 upperIndex_ = upperIndex; 104 colorExtract_ = colorExtract; 105 fitBox(); 106 } 107 GetVolume()108 uint32_t GetVolume() const 109 { 110 return (maxRed_ - minRed_ + 1) * (maxGreen_ - minGreen_ +1) * (maxBlue_ -minBlue_ + 1); 111 } 112 CanSplit()113 bool CanSplit() 114 { 115 return GetColorCount() > 1; 116 } 117 GetColorCount()118 int GetColorCount() const 119 { 120 return 1 + upperIndex_ - lowerIndex_; 121 } 122 123 bool operator < (const VBox &v) const 124 { 125 return this->GetVolume() < v.GetVolume(); 126 } 127 128 // Recomputes the boundaries of this box to tightly fit the color within the box. fitBox()129 void fitBox() 130 { 131 uint32_t *colors = colorExtract_->colors_.get(); 132 uint32_t *hist = colorExtract_->hist_.get(); 133 134 uint32_t minR = UINT32_MAX; 135 uint32_t minG = UINT32_MAX; 136 uint32_t minB = UINT32_MAX; 137 uint32_t maxRed = 0; 138 uint32_t maxGreen = 0; 139 uint32_t maxBlue = 0; 140 141 int count = 0; 142 143 for (int i = lowerIndex_; i <= upperIndex_; i++) { 144 uint32_t color = colors[i]; 145 count += static_cast<int>(hist[color]); 146 147 uint32_t r = QuantizedRed(color); 148 uint32_t g = QuantizedGreen(color); 149 uint32_t b = QuantizedBlue(color); 150 if (r > maxRed) { 151 maxRed = r; 152 } 153 if (r < minR) { 154 minR = r; 155 } 156 if (g > maxGreen) { 157 maxGreen = g; 158 } 159 if (g < minG) { 160 minG = g; 161 } 162 if (b > maxBlue) { 163 maxBlue = b; 164 } 165 if (b < minB) { 166 minB = b; 167 } 168 } 169 minRed_ = minR; 170 maxRed_ = maxRed; 171 minGreen_ = minG; 172 maxGreen_ = maxGreen; 173 minBlue_ = minB; 174 maxBlue_ = maxBlue; 175 pixelNums_ = count; 176 } 177 178 // Split the color box at the mid-point along its longest dimension SplitBox()179 VBox SplitBox() 180 { 181 int splitPoint = FindSplitPoint(); 182 183 VBox newBox = VBox(splitPoint + 1, upperIndex_, colorExtract_); 184 upperIndex_ = splitPoint; 185 fitBox(); 186 return newBox; 187 } 188 189 // return the longest dimension of the box GetLongestColorDimension()190 int GetLongestColorDimension() 191 { 192 uint32_t redLen = maxRed_ - minRed_; 193 uint32_t greenLen = maxGreen_ - minGreen_; 194 uint32_t blueLen = maxBlue_ - minBlue_; 195 196 if (redLen >=greenLen && redLen >= blueLen) { 197 return COMPONENT_RED; 198 } else if (greenLen >= redLen && greenLen >= blueLen) { 199 return COMPONENT_GREEN; 200 } else { 201 return COMPONENT_BLUE; 202 } 203 } 204 FindSplitPoint()205 int FindSplitPoint() 206 { 207 int longestDimension = GetLongestColorDimension(); 208 uint32_t *colors = colorExtract_->colors_.get(); 209 uint32_t *hist = colorExtract_->hist_.get(); 210 211 // Sort the color in the box based on the longest color dimension 212 ModifySignificantOctet(colors, longestDimension, lowerIndex_, upperIndex_); 213 std::sort(colors + lowerIndex_, colors + upperIndex_ + 1); 214 // Revert all of the colors so that they are packed as RGB again 215 ModifySignificantOctet(colors, longestDimension, lowerIndex_, upperIndex_); 216 217 int midPoint = pixelNums_ / 2; 218 for (int i = lowerIndex_, count = 0; i <= upperIndex_; i++) { 219 count += static_cast<int>(hist[colors[i]]); 220 if (count >= midPoint) { 221 return std::min(upperIndex_ - 1, i); 222 } 223 } 224 225 return lowerIndex_; 226 } 227 228 /** 229 * Modify the significant octet in a packed color int. Allows sorting based on the value of a 230 * single color component. 231 */ ModifySignificantOctet(uint32_t * colors,int dimension,int lower,int upper)232 void ModifySignificantOctet(uint32_t *colors, int dimension, int lower, int upper) 233 { 234 switch (dimension) { 235 case COMPONENT_RED: 236 break; 237 case COMPONENT_GREEN: 238 for (int i = lower; i <= upper ; i++) { 239 uint32_t color = colors[i]; 240 colors[i] = (QuantizedGreen(color) << (QUANTIZE_WORD_WIDTH + QUANTIZE_WORD_WIDTH)) 241 | (QuantizedRed(color) << QUANTIZE_WORD_WIDTH) 242 | QuantizedBlue(color); 243 } 244 break; 245 case COMPONENT_BLUE: 246 for (int i = lower; i <= upper ; i++) { 247 uint32_t color = colors[i]; 248 colors[i] = (QuantizedBlue(color) << (QUANTIZE_WORD_WIDTH + QUANTIZE_WORD_WIDTH)) 249 | (QuantizedGreen(color) << QUANTIZE_WORD_WIDTH) 250 | QuantizedRed(color); 251 } 252 break; 253 } 254 } 255 256 // Return the average color of the box GetAverageColor()257 std::pair<uint32_t, uint32_t> GetAverageColor() 258 { 259 uint32_t *colors = colorExtract_->colors_.get(); 260 uint32_t *hist = colorExtract_->hist_.get(); 261 uint32_t redSum = 0; 262 uint32_t greenSum = 0; 263 uint32_t blueSum = 0; 264 uint32_t totalPixelNum = 0; 265 for (int i = lowerIndex_; i <=upperIndex_ ; i++) { 266 uint32_t color = colors[i]; 267 uint32_t colorPixelNum = hist[color]; 268 totalPixelNum += colorPixelNum; 269 redSum += colorPixelNum * QuantizedRed(color); 270 greenSum += colorPixelNum * QuantizedGreen(color); 271 blueSum += colorPixelNum * QuantizedBlue(color); 272 } 273 if (totalPixelNum == 0) { 274 uint32_t error_color = 0; 275 return std::make_pair(error_color, error_color); 276 } 277 uint32_t redMean = round(redSum / (float)totalPixelNum); 278 uint32_t greenMean = round(greenSum / (float)totalPixelNum); 279 uint32_t blueMean = round(blueSum / (float)totalPixelNum); 280 281 return std::make_pair(ApproximateToRGB888(redMean, greenMean, blueMean), totalPixelNum); 282 } 283 }; // VBox 284 285 private: 286 static constexpr double RED_GRAY_RATIO = 0.299; 287 static constexpr double GREEN_GRAY_RATIO = 0.587; 288 static constexpr double BLUE_GRAY_RATIO = 0.114; 289 static constexpr double RED_LUMINACE_RATIO = 0.2126; 290 static constexpr double GREEN_LUMINACE_RATIO = 0.7152; 291 static constexpr double BLUE_LUMINACE_RATIO = 0.0722; 292 static uint8_t Rgb2Gray(uint32_t color); 293 uint32_t CalcGrayMsd() const; 294 static float NormalizeRgb(uint32_t val); 295 static float CalcRelativeLum(uint32_t color); 296 float CalcContrastToWhite() const; 297 std::vector<std::pair<uint32_t, uint32_t>> QuantizePixels(int colorNum); 298 void SplitBoxes(std::priority_queue<VBox, std::vector<VBox>, std::less<VBox> > &queue, int maxSize); 299 std::vector<std::pair<uint32_t, uint32_t>> GenerateAverageColors(std::priority_queue<VBox, 300 std::vector<VBox>, std::less<VBox> > &queue); 301 }; // ColorExtract 302 } // namespace Rosen 303 } // namespace OHOS 304 305 #endif // COLOREXTRACT_H 306