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 "render/rs_color_extract.h"
16 #include <cmath>
17 #include <iostream>
18 #include <vector>
19 #include <memory>
20 
21 namespace OHOS {
22 namespace Rosen {
23 
RSColorExtract(std::shared_ptr<Drawing::Pixmap> pixmap)24 RSColorExtract::RSColorExtract(std::shared_ptr<Drawing::Pixmap> pixmap)
25 {
26     if (pixmap == nullptr) {
27         return ;
28     }
29     pixelmap_ = pixmap;
30     colorValLen_ = static_cast<uint32_t>(pixmap->GetWidth() * pixmap->GetHeight());
31     auto colorVal = new uint32_t[colorValLen_]();
32     std::shared_ptr<uint32_t> colorShared(colorVal, [](uint32_t *ptr) {
33         delete[] ptr;
34     });
35     colorVal_ = std::move(colorShared);
36     for (int i = 0; i < pixmap->GetHeight(); i++) {
37         for (int j = 0; j < pixmap->GetWidth(); j++) {
38             colorVal[i * pixmap->GetWidth() + j] = pixmap->GetColor(j, i);
39         }
40     }
41     GetNFeatureColors(specifiedFeatureColorNum_);
42 }
43 
RSColorExtract(std::shared_ptr<Drawing::Pixmap> pixmap,double * coordinates)44 RSColorExtract::RSColorExtract(std::shared_ptr<Drawing::Pixmap> pixmap, double* coordinates)
45 {
46     if (pixmap == nullptr) {
47         return;
48     }
49     pixelmap_ = pixmap;
50     uint32_t left = static_cast<uint32_t>(pixmap->GetWidth() * coordinates[0]); // 0 is index of left
51     uint32_t top = static_cast<uint32_t>(pixmap->GetHeight() * coordinates[1]); // 1 is index of top
52     uint32_t right = static_cast<uint32_t>(pixmap->GetWidth() * coordinates[2]); // 2 is index of right
53     uint32_t bottom = static_cast<uint32_t>(pixmap->GetHeight() * coordinates[3]); // 3 is index of bottom
54     colorValLen_ = (right - left) * (bottom -top);
55     if (colorValLen_ <= 0) {
56         return;
57     }
58     auto colorVal = new uint32_t[colorValLen_]();
59     std::shared_ptr<uint32_t> colorShared(colorVal, [](uint32_t *ptr) {
60         delete[] ptr;
61     });
62     colorVal_ = std::move(colorShared);
63     for (uint32_t i = top; i < bottom; i++) {
64         for (uint32_t j = left; j < right; j++) {
65             colorVal[(i - top) * (right - left) + (j - left)] = pixmap->GetColor(j, i);
66         }
67     }
68     GetNFeatureColors(specifiedFeatureColorNum_);
69 }
70 
71 // Return red component of a quantized color
QuantizedRed(uint32_t color)72 uint32_t RSColorExtract::QuantizedRed(uint32_t color)
73 {
74     return (color >> (QUANTIZE_WORD_WIDTH + QUANTIZE_WORD_WIDTH)) & static_cast<uint32_t>(QUANTIZE_WORD_MASK);
75 }
76 
77 // Return green component of a quantized color
QuantizedGreen(uint32_t color)78 uint32_t RSColorExtract::QuantizedGreen(uint32_t color)
79 {
80     return (color >> QUANTIZE_WORD_WIDTH) & static_cast<uint32_t>(QUANTIZE_WORD_MASK);
81 }
82 
83 // Return blue component of a quantized color
QuantizedBlue(uint32_t color)84 uint32_t RSColorExtract::QuantizedBlue(uint32_t color)
85 {
86     return color & static_cast<uint32_t>(QUANTIZE_WORD_MASK);
87 }
88 
ModifyWordWidth(uint8_t color,int inWidth,int outWidth)89 uint32_t RSColorExtract::ModifyWordWidth(uint8_t color, int inWidth, int outWidth)
90 {
91     uint32_t newValue;
92     if (outWidth > inWidth) {
93         newValue = color << (outWidth - inWidth);
94     } else {
95         newValue = color >> (inWidth - outWidth);
96     }
97     return newValue & ((1 << outWidth) - 1);
98 }
99 
GetARGB32ColorR(unsigned int color)100 uint8_t RSColorExtract::GetARGB32ColorR(unsigned int color)
101 {
102     return (color >> ARGB_R_SHIFT) & ARGB_MASK;
103 }
GetARGB32ColorG(unsigned int color)104 uint8_t RSColorExtract::GetARGB32ColorG(unsigned int color)
105 {
106     return (color >> ARGB_G_SHIFT) & ARGB_MASK;
107 }
GetARGB32ColorB(unsigned int color)108 uint8_t RSColorExtract::GetARGB32ColorB(unsigned int color)
109 {
110     return (color >> ARGB_B_SHIFT) & ARGB_MASK;
111 }
112 
113 
QuantizeFromRGB888(uint32_t color)114 uint32_t RSColorExtract::QuantizeFromRGB888(uint32_t color)
115 {
116     uint32_t r = ModifyWordWidth(GetARGB32ColorR(color), 8, QUANTIZE_WORD_WIDTH);
117     uint32_t g = ModifyWordWidth(GetARGB32ColorG(color), 8, QUANTIZE_WORD_WIDTH);
118     uint32_t b = ModifyWordWidth(GetARGB32ColorB(color), 8, QUANTIZE_WORD_WIDTH);
119     return (r << (QUANTIZE_WORD_WIDTH + QUANTIZE_WORD_WIDTH)) | (g << QUANTIZE_WORD_WIDTH) | b;
120 }
121 
122 
ApproximateToRGB888(uint32_t r,uint32_t g,uint32_t b)123 uint32_t RSColorExtract::ApproximateToRGB888(uint32_t r, uint32_t g, uint32_t b)
124 {
125     uint32_t approximatedRed = ModifyWordWidth(r, QUANTIZE_WORD_WIDTH, 8);
126     uint32_t approximatedGreen = ModifyWordWidth(g, QUANTIZE_WORD_WIDTH, 8);
127     uint32_t approximatedBlue = ModifyWordWidth(b, QUANTIZE_WORD_WIDTH, 8);
128     return (approximatedRed << ARGB_R_SHIFT) | (approximatedGreen << ARGB_G_SHIFT) | (approximatedBlue << ARGB_B_SHIFT);
129 }
130 
ApproximateToRGB888(uint32_t color)131 uint32_t RSColorExtract::ApproximateToRGB888(uint32_t color)
132 {
133     return ApproximateToRGB888(QuantizedRed(color), QuantizedGreen(color), QuantizedBlue(color));
134 }
135 
136 
QuantizePixels(int colorNum)137 std::vector<std::pair<uint32_t, uint32_t>> RSColorExtract::QuantizePixels(int colorNum)
138 {
139     // Create a priority queue which is sorted by volume descending.
140     std::priority_queue<VBox, std::vector<VBox>, std::less<VBox> > q;
141     VBox v(0, distinctColorCount_ - 1, this);
142     q.push(v);
143     SplitBoxes(q, colorNum);
144     return GenerateAverageColors(q);
145 }
146 
147 
SplitBoxes(std::priority_queue<VBox,std::vector<VBox>,std::less<VBox>> & queue,int maxSize)148 void RSColorExtract::SplitBoxes(std::priority_queue<VBox, std::vector<VBox>, std::less<VBox> > &queue, int maxSize)
149 {
150     while (queue.size() < static_cast<uint32_t>(maxSize)) {
151         VBox vBox = queue.top();
152         queue.pop();
153         if (vBox.CanSplit()) {
154             queue.push(vBox.SplitBox());
155             queue.push(vBox);
156         } else {
157             return;
158         }
159     }
160     return;
161 }
162 
cmp(std::pair<uint32_t,uint32_t> & a,std::pair<uint32_t,uint32_t> & b)163 bool RSColorExtract::cmp(std::pair<uint32_t, uint32_t> &a, std::pair<uint32_t, uint32_t> &b)
164 {
165     return a.second > b.second;
166 }
167 
GenerateAverageColors(std::priority_queue<VBox,std::vector<VBox>,std::less<VBox>> & queue)168 std::vector<std::pair<uint32_t, uint32_t>> RSColorExtract::GenerateAverageColors(std::priority_queue<VBox,
169     std::vector<VBox>, std::less<VBox> > &queue)
170 {
171     std::vector<std::pair<uint32_t, uint32_t>> featureColors;
172     while (!queue.empty()) {
173         VBox vBox = queue.top();
174         queue.pop();
175         featureColors.push_back(vBox.GetAverageColor());
176     }
177     return  featureColors;
178 }
179 
SetFeatureColorNum(int N)180 void RSColorExtract::SetFeatureColorNum(int N)
181 {
182     specifiedFeatureColorNum_ = N;
183     GetNFeatureColors(N);
184     return;
185 }
186 
Rgb2Gray(uint32_t color)187 uint8_t RSColorExtract::Rgb2Gray(uint32_t color)
188 {
189     uint32_t r = GetARGB32ColorR(color);
190     uint32_t g = GetARGB32ColorG(color);
191     uint32_t b = GetARGB32ColorB(color);
192     return static_cast<uint8_t>(r * RED_GRAY_RATIO + g * GREEN_GRAY_RATIO + b * BLUE_GRAY_RATIO);
193 }
194 
CalcGrayMsd() const195 uint32_t RSColorExtract::CalcGrayMsd() const
196 {
197     if (colorValLen_ == 0) {
198         return 0;
199     }
200     uint32_t *colorVal = colorVal_.get();
201     unsigned long long int graySum = 0;
202     unsigned long long int grayVar = 0;
203     for (uint32_t i = 0; i < colorValLen_; i++) {
204         graySum += Rgb2Gray(colorVal[i]);
205     }
206     uint32_t grayAve = graySum / colorValLen_;
207     for (uint32_t i = 0; i < colorValLen_; i++) {
208         grayVar += pow(static_cast<unsigned long long int>(Rgb2Gray(colorVal[i])) - grayAve, 2); // 2 is square
209     }
210     grayVar /= colorValLen_;
211     return static_cast<uint32_t>(grayVar);
212 }
213 
NormalizeRgb(uint32_t val)214 float RSColorExtract::NormalizeRgb(uint32_t val)
215 {
216     float res = static_cast<float>(val) / 255;
217     if (res <= 0.03928) { // 0.03928 is used to normalize rgb;
218         res /= 12.92; // 12.92 is used to normalize rgb;
219     } else {
220         res = pow((res + 0.055) / 1.055, 2.4); // 0.055, 1.055, 2.4 is used to normalize rgb;
221     }
222     return res;
223 }
224 
CalcRelativeLum(uint32_t color)225 float RSColorExtract::CalcRelativeLum(uint32_t color)
226 {
227     uint32_t r = GetARGB32ColorR(color);
228     uint32_t g = GetARGB32ColorG(color);
229     uint32_t b = GetARGB32ColorB(color);
230     float R = NormalizeRgb(r);
231     float G = NormalizeRgb(g);
232     float B = NormalizeRgb(b);
233     return R * RED_LUMINACE_RATIO + G * GREEN_LUMINACE_RATIO + B * BLUE_LUMINACE_RATIO;
234 }
235 
CalcContrastToWhite() const236 float RSColorExtract::CalcContrastToWhite() const
237 {
238     if (colorValLen_ == 0) {
239         return 0.0;
240     }
241     uint32_t *colorVal = colorVal_.get();
242     float lightDegree = 0;
243     float luminanceSum = 0;
244     for (uint32_t i = 0; i < colorValLen_; i++) {
245         luminanceSum += CalcRelativeLum(colorVal[i]);
246     }
247     float luminanceAve = luminanceSum / colorValLen_;
248      // 0.05 is used to ensure denominator is not 0;
249     lightDegree = (1 + 0.05) / (luminanceAve + 0.05);
250     return lightDegree;
251 }
252 
GetNFeatureColors(int colorNum)253 void RSColorExtract::GetNFeatureColors(int colorNum)
254 {
255     if (colorValLen_ == 0) {
256         return;
257     }
258     uint32_t *colorVal = colorVal_.get();
259     uint32_t histLen = (1 << (QUANTIZE_WORD_WIDTH * 3)); // 3 means left shift flag
260     auto hist = new uint32_t[histLen]();
261     std::shared_ptr<uint32_t> histShared(hist, [](uint32_t *ptr) {
262         delete[] ptr;
263     });
264     hist_ = move(histShared);
265     for (uint32_t i = 0; i < colorValLen_; i++) {
266         uint32_t quantizedColor = QuantizeFromRGB888(colorVal[i]);
267         hist[quantizedColor]++;
268     }
269 
270     for (uint32_t color = 0; color < histLen; color++) {
271         if (hist[color] > 0) {
272             distinctColorCount_++;
273         }
274     }
275 
276     // Create an array consisting of only distinct colors
277     auto colors = new uint32_t[distinctColorCount_]();
278     std::shared_ptr<uint32_t> colorsShared(colors, [](uint32_t *ptr) {
279         delete[] ptr;
280     });
281     colors_ = move(colorsShared);
282 
283     int distinctColorIndex = 0;
284     for (uint32_t color = 0; color < histLen; color++) {
285         if (hist[color] > 0) {
286             colors[distinctColorIndex++] = color;
287         }
288     }
289 
290     if (distinctColorCount_ < colorNum) {
291         //The picture has fewer colors than the maximum requested, just return the colors.
292         for (int i = 0; i < distinctColorCount_; ++i) {
293             std::pair<uint32_t, uint32_t> featureColor = \
294                     std::make_pair(ApproximateToRGB888(colors[i]), hist[colors[i]]);
295             featureColors_.push_back(featureColor);
296         }
297     } else {
298         // Use quantization to reduce the number of color.
299         featureColors_ = QuantizePixels(colorNum);
300     }
301     // Sort colors from more to less.
302     sort(featureColors_.begin(), featureColors_.end(), cmp);
303     return;
304 }
305 
306 } // namespace Rosen
307 } // namespace OHOS
308