1 /*
2 * Copyright (C) 2021 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
16 #include "basic_transformer.h"
17 #include <iostream>
18 #include <new>
19 #include <unistd.h>
20 #include "image_utils.h"
21 #include "pixel_convert.h"
22 #include "pixel_map.h"
23 #ifndef _WIN32
24 #include "securec.h"
25 #else
26 #include "memory.h"
27 #endif
28
29 #if !defined(_WIN32) && !defined(_APPLE) &&!defined(IOS_PLATFORM) &&!defined(A_PLATFORM)
30 #include "ashmem.h"
31 #include <sys/mman.h>
32 #endif
33
34 #undef LOG_DOMAIN
35 #define LOG_DOMAIN LOG_TAG_DOMAIN_ID_IMAGE
36
37 #undef LOG_TAG
38 #define LOG_TAG "BasicTransformer"
39
40 namespace {
41 constexpr uint32_t RGB24_R_MASK = 0x00ff0000;
42 constexpr uint32_t RGB24_G_MASK = 0x0000ff00;
43 constexpr uint32_t RGB24_B_MASK = 0x000000ff;
44 constexpr uint16_t RGB16_R_MASK = 0xf800;
45 constexpr uint16_t RGB16_G_MASK = 0x07e0;
46 constexpr uint16_t RGB16_B_MASK = 0x001f;
47
48 constexpr uint32_t RGB32_RGB16_R_SHIFT = 0x13;
49 constexpr uint32_t RGB32_RGB16_G_SHIFT = 0xA;
50 constexpr uint32_t RGB32_RGB16_B_SHIFT = 0x3;
51
52 constexpr uint32_t RGB16_RGB32_R_SHIFT = 0x8;
53 constexpr uint32_t RGB16_RGB32_G_SHIFT = 0x3;
54 constexpr uint32_t RGB16_RGB32_B_SHIFT = 0x3;
55
56 constexpr uint32_t RGB24_R_SHIFT = 0x10;
57 constexpr uint32_t RGB24_G_SHIFT = 0x8;
58 constexpr uint32_t OFFSET_0 = 0;
59 constexpr uint32_t OFFSET_1 = 1;
60 constexpr uint32_t OFFSET_2 = 2;
61 }
62 namespace OHOS {
63 namespace Media {
64 using namespace std;
ResetParam()65 void BasicTransformer::ResetParam()
66 {
67 matrix_ = Matrix();
68 minX_ = 0.0f;
69 minY_ = 0.0f;
70 }
71
SetScaleParam(const float sx,const float sy)72 void BasicTransformer::SetScaleParam(const float sx, const float sy)
73 {
74 Matrix m;
75 m.SetScale(sx, sy);
76 matrix_.SetConcat(m);
77 }
78
SetTranslateParam(const float tx,const float ty)79 void BasicTransformer::SetTranslateParam(const float tx, const float ty)
80 {
81 Matrix m;
82 m.SetTranslate(tx, ty);
83 matrix_.SetConcat(m);
84 }
85
SetRotateParam(const float degrees,const float px,const float py)86 void BasicTransformer::SetRotateParam(const float degrees, const float px, const float py)
87 {
88 Matrix m;
89 m.SetRotate(degrees, px, py);
90 matrix_.SetConcat(m);
91 }
92
GetDstDimension(const Size & srcSize,Size & dstSize)93 void BasicTransformer::GetDstDimension(const Size &srcSize, Size &dstSize)
94 {
95 Matrix::OperType operType = matrix_.GetOperType();
96 if ((static_cast<uint8_t>(operType) & Matrix::SCALE) == Matrix::SCALE) {
97 dstSize.width = static_cast<int32_t>(srcSize.width * fabs(matrix_.GetScaleX()) + FHALF);
98 dstSize.height = static_cast<int32_t>(srcSize.height * fabs(matrix_.GetScaleY()) + FHALF);
99 }
100
101 if ((static_cast<uint8_t>(operType) & Matrix::ROTATEORSKEW) == Matrix::ROTATEORSKEW) {
102 Matrix::CalcXYProc fInvProc = Matrix::GetXYProc(operType);
103 GetRotateDimension(fInvProc, srcSize, dstSize);
104 }
105
106 if ((static_cast<uint8_t>(operType) & Matrix::TRANSLATE) == Matrix::TRANSLATE) {
107 if (matrix_.GetTransX() > 0) {
108 dstSize.width = static_cast<int32_t>(srcSize.width + matrix_.GetTransX() + FHALF);
109 }
110 if (matrix_.GetTranY() > 0) {
111 dstSize.height = static_cast<int32_t>(srcSize.height + matrix_.GetTranY() + FHALF);
112 }
113 }
114 }
115
CheckAllocateBuffer(PixmapInfo & outPixmap,AllocateMem allocate,int & fd,uint64_t & bufferSize,Size & dstSize)116 bool BasicTransformer::CheckAllocateBuffer(PixmapInfo &outPixmap, AllocateMem allocate,
117 int &fd, uint64_t &bufferSize, Size &dstSize)
118 {
119 if (bufferSize == 0 || bufferSize > PIXEL_MAP_MAX_RAM_SIZE) {
120 IMAGE_LOGE("[BasicTransformer]Invalid value of bufferSize");
121 return false;
122 }
123 if (allocate == nullptr) {
124 outPixmap.data = static_cast<uint8_t *>(malloc(bufferSize));
125 } else {
126 outPixmap.data = allocate(dstSize, bufferSize, fd, outPixmap.uniqueId);
127 auto tmp = std::make_unique<int32_t>();
128 *tmp = fd;
129 outPixmap.context = tmp.release();
130 }
131 if (outPixmap.data == nullptr) {
132 IMAGE_LOGE("[BasicTransformer]apply heap memory failed");
133 return false;
134 }
135 return true;
136 }
137
ReleaseBuffer(AllocatorType allocatorType,int fd,int dataSize,uint8_t * buffer)138 void BasicTransformer::ReleaseBuffer(AllocatorType allocatorType, int fd, int dataSize, uint8_t *buffer)
139 {
140 #if !defined(_WIN32) && !defined(_APPLE) &&!defined(IOS_PLATFORM) &&!defined(A_PLATFORM)
141 if (allocatorType == AllocatorType::SHARE_MEM_ALLOC) {
142 if (buffer != nullptr) {
143 ::munmap(buffer, dataSize);
144 ::close(fd);
145 }
146 return;
147 }
148 #endif
149
150 if (allocatorType == AllocatorType::HEAP_ALLOC) {
151 if (buffer != nullptr) {
152 free(buffer);
153 }
154 return;
155 }
156 }
157
TransformPixmap(const PixmapInfo & inPixmap,PixmapInfo & outPixmap,AllocateMem allocate)158 uint32_t BasicTransformer::TransformPixmap(const PixmapInfo &inPixmap, PixmapInfo &outPixmap, AllocateMem allocate)
159 {
160 if (inPixmap.data == nullptr) {
161 IMAGE_LOGE("[BasicTransformer]input data is null.");
162 return ERR_IMAGE_GENERAL_ERROR;
163 }
164 int32_t pixelBytes = ImageUtils::GetPixelBytes(inPixmap.imageInfo.pixelFormat);
165 if (pixelBytes == 0) {
166 IMAGE_LOGE("[BasicTransformer]input pixel is invalid.");
167 return ERR_IMAGE_INVALID_PIXEL;
168 }
169
170 Size dstSize = inPixmap.imageInfo.size;
171 GetDstDimension(inPixmap.imageInfo.size, dstSize);
172 outPixmap.imageInfo.size = dstSize;
173 if (dstSize.width <= 0 || dstSize.height <= 0) {
174 IMAGE_LOGE("[BasicTransformer]buffer size is invalid.");
175 return ERR_IMAGE_ALLOC_MEMORY_FAILED;
176 }
177
178 uint64_t bufferSize = static_cast<uint64_t>(dstSize.width) * dstSize.height * pixelBytes;
179 if (bufferSize > PIXEL_MAP_MAX_RAM_SIZE) {
180 IMAGE_LOGE("[BasicTransformer] buffer size:%{public}llu out of range.",
181 static_cast<unsigned long long>(bufferSize));
182 return ERR_IMAGE_ALLOC_MEMORY_FAILED;
183 }
184 int fd = 0;
185 if (!(CheckAllocateBuffer(outPixmap, allocate, fd, bufferSize, dstSize))) {
186 return ERR_IMAGE_ALLOC_MEMORY_FAILED;
187 }
188 outPixmap.bufferSize = bufferSize;
189 outPixmap.imageInfo.pixelFormat = inPixmap.imageInfo.pixelFormat;
190 outPixmap.imageInfo.colorSpace = inPixmap.imageInfo.colorSpace;
191 outPixmap.imageInfo.alphaType = inPixmap.imageInfo.alphaType;
192 outPixmap.imageInfo.baseDensity = inPixmap.imageInfo.baseDensity;
193
194 if (memset_s(outPixmap.data, bufferSize * sizeof(uint8_t), COLOR_DEFAULT, bufferSize * sizeof(uint8_t)) != EOK) {
195 IMAGE_LOGE("[BasicTransformer]apply heap memory failed.");
196 ReleaseBuffer((allocate == nullptr) ? AllocatorType::HEAP_ALLOC : AllocatorType::SHARE_MEM_ALLOC,
197 fd, bufferSize, outPixmap.data);
198 return ERR_IMAGE_GENERAL_ERROR;
199 }
200
201 if (!DrawPixelmap(inPixmap, pixelBytes, dstSize, outPixmap.data)) {
202 IMAGE_LOGE("[BasicTransformer] the matrix can not invert.");
203 ReleaseBuffer((allocate == nullptr) ? AllocatorType::HEAP_ALLOC : AllocatorType::SHARE_MEM_ALLOC,
204 fd, bufferSize, outPixmap.data);
205 return ERR_IMAGE_MATRIX_NOT_INVERT;
206 }
207 return IMAGE_SUCCESS;
208 }
209
pointLoop(Point & pt,const Size & size)210 static inline void pointLoop(Point &pt, const Size &size)
211 {
212 if (pt.x < 0) {
213 pt.x = size.width + pt.x;
214 }
215 if (pt.y < 0) {
216 pt.y = size.height + pt.y;
217 }
218 }
219
DrawPixelmap(const PixmapInfo & pixmapInfo,const int32_t pixelBytes,const Size & size,uint8_t * data)220 bool BasicTransformer::DrawPixelmap(const PixmapInfo &pixmapInfo, const int32_t pixelBytes, const Size &size,
221 uint8_t *data)
222 {
223 Matrix invertMatrix;
224 if (!(matrix_.Invert(invertMatrix))) {
225 return false;
226 }
227
228 uint32_t rb = pixmapInfo.imageInfo.size.width * pixelBytes;
229 Matrix::OperType operType = matrix_.GetOperType();
230 Matrix::CalcXYProc fInvProc = Matrix::GetXYProc(operType);
231
232 for (int32_t y = 0; y < size.height; ++y) {
233 for (int32_t x = 0; x < size.width; ++x) {
234 Point srcPoint;
235 // Center coordinate alignment, need to add 0.5, so the boundary can also be considered
236 fInvProc(invertMatrix, static_cast<float>(x) + minX_ + FHALF, static_cast<float>(y) + minY_ + FHALF,
237 srcPoint);
238 if ((static_cast<uint8_t>(operType) & Matrix::OperType::SCALE) == Matrix::OperType::SCALE) {
239 pointLoop(srcPoint, pixmapInfo.imageInfo.size);
240 }
241 if (CheckOutOfRange(srcPoint, pixmapInfo.imageInfo.size)) {
242 continue;
243 }
244 uint32_t shiftBytes = (y * size.width + x) * pixelBytes;
245 BilinearProc(srcPoint, pixmapInfo, rb, shiftBytes, data);
246 }
247 }
248
249 return true;
250 }
251
GetRotateDimension(Matrix::CalcXYProc fInvProc,const Size & srcSize,Size & dstSize)252 void BasicTransformer::GetRotateDimension(Matrix::CalcXYProc fInvProc, const Size &srcSize, Size &dstSize)
253 {
254 Point dstP1;
255 Point dstP2;
256 Point dstP3;
257 Point dstP4;
258
259 float fx = static_cast<float>(srcSize.width);
260 float fy = static_cast<float>(srcSize.height);
261 fInvProc(matrix_, 0.0f, 0.0f, dstP1);
262 fInvProc(matrix_, fx, 0.0f, dstP2);
263 fInvProc(matrix_, 0.0f, fy, dstP3);
264 fInvProc(matrix_, fx, fy, dstP4);
265
266 // For rotation, the width and height will change, so you need to take the maximum of the two diagonals.
267 dstSize.width = static_cast<int32_t>(fmaxf(fabsf(dstP4.x - dstP1.x), fabsf(dstP3.x - dstP2.x)) + FHALF);
268 dstSize.height = static_cast<int32_t>(fmaxf(fabsf(dstP4.y - dstP1.y), fabsf(dstP3.y - dstP2.y)) + FHALF);
269
270 float min14X = std::min(dstP1.x, dstP4.x);
271 float min23X = std::min(dstP2.x, dstP3.x);
272 minX_ = std::min(min14X, min23X);
273
274 float min14Y = std::min(dstP1.y, dstP4.y);
275 float min23Y = std::min(dstP2.y, dstP3.y);
276 minY_ = std::min(min14Y, min23Y);
277 }
278
RGB565to32(uint16_t c)279 static uint32_t RGB565to32(uint16_t c)
280 {
281 uint32_t color = c;
282 uint32_t r = (color & RGB16_R_MASK) >> RGB16_RGB32_R_SHIFT;
283 uint32_t g = (color & RGB16_G_MASK) >> RGB16_RGB32_G_SHIFT;
284 uint32_t b = (color & RGB16_B_MASK) << RGB16_RGB32_B_SHIFT;
285 return (r << SHIFT_16_BIT) | (g << SHIFT_8_BIT) | b;
286 }
287
Color32toRGB565(uint32_t c)288 static uint16_t Color32toRGB565(uint32_t c)
289 {
290 uint16_t r = (c & RGB24_R_MASK) >> RGB32_RGB16_R_SHIFT;
291 uint16_t g = (c & RGB24_G_MASK) >> RGB32_RGB16_G_SHIFT;
292 uint16_t b = (c & RGB24_B_MASK) >> RGB32_RGB16_B_SHIFT;
293 return (r << SHIFT_11_BIT) | (g << SHIFT_5_BIT) | b;
294 }
295
296 struct BilinearPixelProcArgs {
297 PixelFormat format;
298 uint8_t* in;
299 uint8_t* out;
300 uint32_t rowBytes;
301 uint32_t subx;
302 uint32_t suby;
303 };
304
BilinearPixelProc(const AroundPos aroundPos,struct BilinearPixelProcArgs & args)305 void BasicTransformer::BilinearPixelProc(const AroundPos aroundPos, struct BilinearPixelProcArgs &args)
306 {
307 AroundPixels aroundPixels;
308 uint32_t filterColor = OFFSET_0;
309
310 switch (args.format) {
311 case PixelFormat::RGBA_8888:
312 case PixelFormat::ARGB_8888:
313 case PixelFormat::BGRA_8888:
314 {
315 GetAroundPixelRGBA(aroundPos, args.in, args.rowBytes, aroundPixels);
316 uint32_t *tmp32 = reinterpret_cast<uint32_t *>(args.out);
317 *tmp32 = FilterProc(args.subx, args.suby, aroundPixels);
318 break;
319 }
320 case PixelFormat::RGB_565:
321 { GetAroundPixelRGB565(aroundPos, args.in, args.rowBytes, aroundPixels);
322 filterColor = FilterProc(args.subx, args.suby, aroundPixels);
323 uint16_t *tmp16 = reinterpret_cast<uint16_t *>(args.out);
324 *tmp16 = Color32toRGB565(filterColor);
325 break;
326 }
327 case PixelFormat::RGB_888:
328 {
329 GetAroundPixelRGB888(aroundPos, args.in, args.rowBytes, aroundPixels);
330 filterColor = FilterProc(args.subx, args.suby, aroundPixels);
331 *(args.out) = static_cast<uint8_t>((filterColor & RGB24_R_MASK) >> RGB24_R_SHIFT);
332 *((args.out) + OFFSET_1) =
333 static_cast<uint8_t>((filterColor & RGB24_G_MASK) >> RGB24_G_SHIFT);
334 *((args.out) + OFFSET_2) = static_cast<uint8_t>(filterColor & RGB24_B_MASK);
335 break;
336 }
337 case PixelFormat::ALPHA_8:
338 {
339 GetAroundPixelALPHA8(aroundPos, args.in, args.rowBytes, aroundPixels);
340 filterColor = FilterProc(args.subx, args.suby, aroundPixels);
341 *(args.out) = static_cast<uint8_t>(filterColor & RGB24_B_MASK);
342 break;
343 }
344 default:
345 IMAGE_LOGE("[BasicTransformer] pixel format not supported, format:%{public}d",
346 args.format);
347 }
348 }
349
BilinearProc(const Point & pt,const PixmapInfo & pixmapInfo,const uint32_t rb,const int32_t shiftBytes,uint8_t * data)350 void BasicTransformer::BilinearProc(const Point &pt, const PixmapInfo &pixmapInfo, const uint32_t rb,
351 const int32_t shiftBytes, uint8_t *data)
352 {
353 uint32_t srcX = (pt.x * MULTI_65536) - HALF_BASIC < 0 ? 0 : (pt.x * MULTI_65536) - HALF_BASIC;
354 uint32_t srcY = (pt.y * MULTI_65536) - HALF_BASIC < 0 ? 0 : (pt.y * MULTI_65536) - HALF_BASIC;
355
356 struct BilinearPixelProcArgs procArgs;
357 procArgs.format = pixmapInfo.imageInfo.pixelFormat;
358 procArgs.in = pixmapInfo.data;
359 procArgs.out = data + shiftBytes;
360 procArgs.rowBytes = rb;
361 procArgs.subx = GetSubValue(srcX);
362 procArgs.suby = GetSubValue(srcY);
363
364 AroundPos aroundPos;
365 aroundPos.x0 = RightShift16Bit(srcX, pixmapInfo.imageInfo.size.width - 1);
366 aroundPos.x1 = RightShift16Bit(srcX + BASIC, pixmapInfo.imageInfo.size.width - 1);
367 aroundPos.y0 = RightShift16Bit(srcY, pixmapInfo.imageInfo.size.height - 1);
368 aroundPos.y1 = RightShift16Bit(srcY + BASIC, pixmapInfo.imageInfo.size.height - 1);
369
370 BilinearPixelProc(aroundPos, procArgs);
371 }
372
GetAroundPixelRGB565(const AroundPos aroundPos,uint8_t * data,uint32_t rb,AroundPixels & aroundPixels)373 void BasicTransformer::GetAroundPixelRGB565(const AroundPos aroundPos, uint8_t *data, uint32_t rb,
374 AroundPixels &aroundPixels)
375 {
376 const uint16_t *row0 = reinterpret_cast<uint16_t *>(data + aroundPos.y0 * rb);
377 const uint16_t *row1 = reinterpret_cast<uint16_t *>(data + aroundPos.y1 * rb);
378
379 aroundPixels.color00 = RGB565to32(row0[aroundPos.x0]);
380 aroundPixels.color01 = RGB565to32(row0[aroundPos.x1]);
381 aroundPixels.color10 = RGB565to32(row1[aroundPos.x0]);
382 aroundPixels.color11 = RGB565to32(row1[aroundPos.x1]);
383 }
384
GetAroundPixelRGB888(const AroundPos aroundPos,uint8_t * data,uint32_t rb,AroundPixels & aroundPixels)385 void BasicTransformer::GetAroundPixelRGB888(const AroundPos aroundPos, uint8_t *data, uint32_t rb,
386 AroundPixels &aroundPixels)
387 {
388 const uint8_t *row0 = data + aroundPos.y0 * rb;
389 const uint8_t *row1 = data + aroundPos.y1 * rb;
390 uint32_t current0 = aroundPos.x0 * RGB888_BYTE;
391 uint32_t current1 = aroundPos.x1 * RGB888_BYTE;
392 // The RGB888 format occupies 3 bytes, and an int integer is formed by OR operation.
393 aroundPixels.color00 =
394 (row0[current0] << SHIFT_16_BIT) | (row0[current0 + 1] << SHIFT_8_BIT) | (row0[current0 + 2]);
395 aroundPixels.color01 =
396 (row0[current1] << SHIFT_16_BIT) | (row0[current1 + 1] << SHIFT_8_BIT) | (row0[current1 + 2]);
397 aroundPixels.color10 =
398 (row1[current0] << SHIFT_16_BIT) | (row1[current0 + 1] << SHIFT_8_BIT) | (row1[current0 + 2]);
399 aroundPixels.color11 =
400 (row1[current1] << SHIFT_16_BIT) | (row1[current1 + 1] << SHIFT_8_BIT) | (row1[current1 + 2]);
401 }
402
GetAroundPixelRGBA(const AroundPos aroundPos,uint8_t * data,uint32_t rb,AroundPixels & aroundPixels)403 void BasicTransformer::GetAroundPixelRGBA(const AroundPos aroundPos, uint8_t *data,
404 uint32_t rb, AroundPixels &aroundPixels)
405 {
406 const uint32_t *row0 = reinterpret_cast<uint32_t *>(data + aroundPos.y0 * rb);
407 const uint32_t *row1 = reinterpret_cast<uint32_t *>(data + aroundPos.y1 * rb);
408 aroundPixels.color00 = row0[aroundPos.x0];
409 aroundPixels.color01 = row0[aroundPos.x1];
410 aroundPixels.color10 = row1[aroundPos.x0];
411 aroundPixels.color11 = row1[aroundPos.x1];
412 }
413
GetAroundPixelALPHA8(const AroundPos aroundPos,uint8_t * data,uint32_t rb,AroundPixels & aroundPixels)414 void BasicTransformer::GetAroundPixelALPHA8(const AroundPos aroundPos, uint8_t *data, uint32_t rb,
415 AroundPixels &aroundPixels)
416 {
417 const uint8_t *row0 = data + aroundPos.y0 * rb;
418 const uint8_t *row1 = data + aroundPos.y1 * rb;
419 aroundPixels.color00 = row0[aroundPos.x0];
420 aroundPixels.color01 = row0[aroundPos.x1];
421 aroundPixels.color10 = row1[aroundPos.x0];
422 aroundPixels.color11 = row1[aroundPos.x1];
423 }
424
RightShift16Bit(uint32_t num,int32_t maxNum)425 uint32_t BasicTransformer::RightShift16Bit(uint32_t num, int32_t maxNum)
426 {
427 /*
428 * When the original image coordinates are obtained,
429 * the first 16 bits are shifted to the left, so the right shift is 16 bits here.
430 */
431 return ClampMax(num >> 16, maxNum);
432 }
433
FilterProc(const uint32_t subx,const uint32_t suby,const AroundPixels & aroundPixels)434 uint32_t BasicTransformer::FilterProc(const uint32_t subx, const uint32_t suby, const AroundPixels &aroundPixels)
435 {
436 int32_t xy = subx * suby;
437 // Mask 0xFF00FF ensures that high and low 16 bits can be calculated simultaneously
438 const uint32_t mask = 0xFF00FF;
439
440 /* All values are first magnified 16 times (left shift 4bit) and then divide 256 (right shift 8bit).
441 * Reference formula f(i+u,j+v) = (1-u)(1-v)f(i,j) + (1-u)vf(i,j+1) + u(1-v)f(i+1,j) + uvf(i+1,j+1),
442 * The subx is u, the suby is y,
443 * color00 is f(i,j), color 01 is f(i,j+1), color 10 is f(i+1,j), color11 is f(i+1,j+1).
444 */
445 int32_t scale = 256 - 16 * suby - 16 * subx + xy;
446 uint32_t lo = (aroundPixels.color00 & mask) * scale;
447 uint32_t hi = ((aroundPixels.color00 >> 8) & mask) * scale;
448
449 scale = 16 * subx - xy;
450 lo += (aroundPixels.color01 & mask) * scale;
451 hi += ((aroundPixels.color01 >> 8) & mask) * scale;
452
453 scale = 16 * suby - xy;
454 lo += (aroundPixels.color10 & mask) * scale;
455 hi += ((aroundPixels.color10 >> 8) & mask) * scale;
456
457 lo += (aroundPixels.color11 & mask) * xy;
458 hi += ((aroundPixels.color11 >> 8) & mask) * xy;
459
460 return ((lo >> 8) & mask) | (hi & ~mask);
461 }
462 } // namespace Media
463 } // namespace OHOS
464