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