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