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