• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2025-2025 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 "pointer_renderer.h"
17 
18 #include <regex>
19 #include "image_source.h"
20 #include "window_info.h"
21 #include "util.h"
22 
23 #undef MMI_LOG_DOMAIN
24 #define MMI_LOG_DOMAIN MMI_LOG_CURSOR
25 #undef MMI_LOG_TAG
26 #define MMI_LOG_TAG "PointerRenderer"
27 
28 constexpr uint32_t RENDER_STRIDE{4};
29 constexpr int32_t DEVCIE_INDEPENDENT_PIXELS{40};
30 constexpr float INCREASE_RATIO{1.22f};
31 constexpr int32_t MIN_POINTER_COLOR{0x000000};
32 constexpr int32_t MAX_POINTER_COLOR{0xFFFFFF};
33 constexpr int32_t OTHER_POINTER_COLOR{0x171717};
34 constexpr float CALCULATE_IMAGE_MIDDLE{2.0f};
35 constexpr uint32_t FOCUS_POINT{256};
36 constexpr float CALCULATE_MOUSE_ICON_BIAS{ 5.0f / 33.0f };
37 constexpr float ROTATION_ANGLE90 {90.0f};
38 const std::string IMAGE_POINTER_DEFAULT_PATH = "/system/etc/multimodalinput/mouse_icon/";
39 
40 namespace OHOS::MMI {
41 
GetImageSize() const42 int32_t RenderConfig::GetImageSize() const
43 {
44     return pow(INCREASE_RATIO, size - 1) * dpi * DEVCIE_INDEPENDENT_PIXELS;
45 }
46 
ToString() const47 std::string RenderConfig::ToString() const
48 {
49     std::ostringstream oss;
50     oss << "{style=" << style << ", align=" << align << ", path" << path << ", color=" << color
51         << ", size=" << size << ", rotationAngle=" << rotationAngle
52         << ", [" << rotationFocusX << " " <<rotationFocusY << "]"
53         <<", dpi=" << dpi
54         << ", isHard=" << isHard << ", ImageSize=" << GetImageSize() << "}";
55     return oss.str();
56 }
57 
GetOffsetX() const58 int32_t RenderConfig::GetOffsetX() const
59 {
60     int32_t width = this->GetImageSize();
61     switch (this->align) {
62         case ANGLE_E:
63             return FOCUS_POINT;
64         case ANGLE_S:
65             return FOCUS_POINT - width / CALCULATE_IMAGE_MIDDLE;
66         case ANGLE_W:
67             return FOCUS_POINT - width;
68         case ANGLE_N:
69             return FOCUS_POINT - width / CALCULATE_IMAGE_MIDDLE;
70         case ANGLE_SE:
71             return FOCUS_POINT - width;
72         case ANGLE_NE:
73             return FOCUS_POINT - width;
74         case ANGLE_SW:
75             return FOCUS_POINT;
76         case ANGLE_NW:
77             return FOCUS_POINT - this->userIconHotSpotX;
78         case ANGLE_CENTER:
79             return FOCUS_POINT - width / CALCULATE_IMAGE_MIDDLE;
80         case ANGLE_NW_RIGHT:
81             return FOCUS_POINT - width * CALCULATE_MOUSE_ICON_BIAS;
82         default:
83             MMI_HILOGW("No need calculate physicalX offset");
84             return FOCUS_POINT;
85     }
86 }
87 
GetOffsetY() const88 int32_t RenderConfig::GetOffsetY() const
89 {
90     int32_t height = this->GetImageSize();
91     switch (this->align) {
92         case ANGLE_E:
93             return FOCUS_POINT - height / CALCULATE_IMAGE_MIDDLE;
94         case ANGLE_S:
95             return FOCUS_POINT;
96         case ANGLE_W:
97             return FOCUS_POINT - height;
98         case ANGLE_N:
99             return FOCUS_POINT - height;
100         case ANGLE_SE:
101             return FOCUS_POINT - height;
102         case ANGLE_NE:
103             return FOCUS_POINT;
104         case ANGLE_SW:
105             return FOCUS_POINT - height;
106         case ANGLE_NW:
107             return FOCUS_POINT - this->userIconHotSpotY;
108         case ANGLE_CENTER:
109             return FOCUS_POINT - height / CALCULATE_IMAGE_MIDDLE;
110         case ANGLE_NW_RIGHT:
111             return FOCUS_POINT;
112         default:
113             MMI_HILOGW("No need calculate physicalY offset");
114             return FOCUS_POINT;
115     }
116 }
117 
UserIconScale(uint32_t width,uint32_t height,const RenderConfig & cfg)118 image_ptr_t PointerRenderer::UserIconScale(uint32_t width, uint32_t height, const RenderConfig &cfg)
119 {
120     image_ptr_t image = nullptr;
121     image = ExtractDrawingImage(cfg.userIconPixelMap);
122     return image;
123 }
124 
Render(uint8_t * addr,uint32_t width,uint32_t height,const RenderConfig & cfg)125 int32_t PointerRenderer::Render(uint8_t *addr, uint32_t width, uint32_t height, const RenderConfig &cfg)
126 {
127     CHKPR(addr, RET_ERR);
128     MMI_HILOGI("Render %{public}s", cfg.ToString().c_str());
129 
130     uint32_t addrSize = width * height * RENDER_STRIDE;
131     if (cfg.style == MOUSE_ICON::TRANSPARENT_ICON) {
132         memset_s(addr, addrSize, 0, addrSize);
133         return RET_OK;
134     }
135 
136     // construct bitmap
137     OHOS::Rosen::Drawing::Bitmap bitmap;
138     OHOS::Rosen::Drawing::BitmapFormat format {
139         OHOS::Rosen::Drawing::COLORTYPE_RGBA_8888,
140         OHOS::Rosen::Drawing::ALPHATYPE_OPAQUE,
141     };
142     bitmap.Build(width, height, format);
143 
144     // construct canvas and bind to bitmap
145     OHOS::Rosen::Drawing::Canvas canvas;
146     canvas.Bind(bitmap);
147     canvas.Clear(OHOS::Rosen::Drawing::Color::COLOR_TRANSPARENT);
148     if (cfg.direction) {
149         int32_t directionFlag = cfg.isHard ? -1 : 0;
150         int32_t degree = static_cast<int32_t>(directionFlag * static_cast<int32_t>(cfg.direction) * ROTATION_ANGLE90);
151         canvas.Rotate(degree, FOCUS_POINT, FOCUS_POINT);
152     }
153     image_ptr_t image = nullptr;
154     if (cfg.userIconPixelMap == nullptr) {
155         image = LoadPointerImage(cfg);
156     } else {
157         image = UserIconScale(width, height, cfg);
158     }
159     CHKPR(image, RET_ERR);
160     //Draw image on canvas
161     canvas.DrawImage(*image, cfg.GetOffsetX(), cfg.GetOffsetY(), Rosen::Drawing::SamplingOptions());
162 
163     errno_t ret = memcpy_s(addr, addrSize, bitmap.GetPixels(), addrSize);
164     if (ret != EOK) {
165         return RET_ERR;
166     }
167     return RET_OK;
168 }
169 
LoadPointerImage(const RenderConfig & cfg)170 image_ptr_t PointerRenderer::LoadPointerImage(const RenderConfig &cfg)
171 {
172     auto pixelmap = LoadCursorSvgWithColor(cfg);
173     return ExtractDrawingImage(pixelmap);
174 }
175 
ChangeSvgCursorColor(std::string & str,int32_t color)176 static void ChangeSvgCursorColor(std::string& str, int32_t color)
177 {
178     std::string targetColor = IntToHexRGB(color);
179     StringReplace(str, "#000000", targetColor);
180     if (color == MAX_POINTER_COLOR) {
181         // stroke=\"#FFFFFF" fill="#000000" stroke-linejoin="round" transform="xxx"
182         std::regex re("(<path.*)(stroke=\"#[a-fA-F0-9]{6}\")(.*path>)");
183         str = std::regex_replace(str, re, "$1stroke=\"#000000\"$3");
184     }
185 }
186 
SetCursorColorBaseOnStyle(const RenderConfig & cfg,OHOS::Media::DecodeOptions & decodeOpts)187 void SetCursorColorBaseOnStyle(const RenderConfig &cfg, OHOS::Media::DecodeOptions &decodeOpts)
188 {
189     const bool isHandColor =
190         (cfg.style == HAND_GRABBING) ||(cfg.style == HAND_OPEN) || (cfg.style == HAND_POINTING);
191     if (isHandColor) {
192         if (cfg.color == MAX_POINTER_COLOR ||
193             cfg.color == MIN_POINTER_COLOR ||
194             cfg.color == OTHER_POINTER_COLOR) {
195             decodeOpts.SVGOpts.fillColor = {.isValidColor = true, .color = MAX_POINTER_COLOR};
196             decodeOpts.SVGOpts.strokeColor = {.isValidColor = true, .color = MIN_POINTER_COLOR};
197         } else {
198             decodeOpts.SVGOpts.fillColor = {.isValidColor = true, .color = cfg.color};
199             if (cfg.color == MAX_POINTER_COLOR) {
200                 decodeOpts.SVGOpts.strokeColor = {.isValidColor = true, .color = MIN_POINTER_COLOR};
201             } else {
202                 decodeOpts.SVGOpts.strokeColor = {.isValidColor = true, .color = MAX_POINTER_COLOR};
203             }
204         }
205     }
206 }
207 
LoadCursorSvgWithColor(const RenderConfig & cfg)208 pixelmap_ptr_t PointerRenderer::LoadCursorSvgWithColor(const RenderConfig &cfg)
209 {
210     std::string svgContent;
211     if (!ReadFile(cfg.path, svgContent)) {
212         MMI_HILOGE("read file failed");
213         return nullptr;
214     }
215 
216     const bool isPartColor = (cfg.style == CURSOR_COPY) || (cfg.style == CURSOR_FORBID) || (cfg.style == HELP);
217     if (isPartColor) {
218         ChangeSvgCursorColor(svgContent, cfg.color);
219     }
220     OHOS::Media::SourceOptions opts;
221     std::unique_ptr<std::istream> isp(std::make_unique<std::istringstream>(svgContent));
222     uint32_t ret = 0;
223     auto imageSource = OHOS::Media::ImageSource::CreateImageSource(std::move(isp), opts, ret);
224     if (!imageSource || ret != ERR_OK) {
225         MMI_HILOGE("Get ImageSource failed, ret=%{public}d", ret);
226     }
227     CHKPP(imageSource);
228 
229     int32_t imgSize = cfg.GetImageSize();
230     OHOS::Media::DecodeOptions decodeOpts;
231     decodeOpts.desiredSize = {.width = imgSize, .height = imgSize};
232     if (!isPartColor) {
233         decodeOpts.SVGOpts.fillColor = {.isValidColor = true, .color = cfg.color};
234         if (cfg.color == MAX_POINTER_COLOR) {
235             decodeOpts.SVGOpts.strokeColor = {.isValidColor = true, .color = MIN_POINTER_COLOR};
236         } else {
237             decodeOpts.SVGOpts.strokeColor = {.isValidColor = true, .color = MAX_POINTER_COLOR};
238         }
239         SetCursorColorBaseOnStyle(cfg, decodeOpts);
240     }
241 
242     pixelmap_ptr_t pmap = imageSource->CreatePixelMap(decodeOpts, ret);
243     return pmap;
244 }
245 
246 class PixelMapContext {
247 public:
PixelMapContext(pixelmap_ptr_t pixelMap)248     explicit PixelMapContext(pixelmap_ptr_t pixelMap) : pixelMap_(pixelMap) {}
~PixelMapContext()249     ~PixelMapContext()
250     {
251         pixelMap_ = nullptr;
252     }
253 
254 private:
255     pixelmap_ptr_t pixelMap_{nullptr};
256 };
257 
PixelMapReleaseProc(const void *,void * context)258 static void PixelMapReleaseProc(const void * /* pixels */, void *context)
259 {
260     PixelMapContext *ctx = static_cast<PixelMapContext *>(context);
261     if (ctx != nullptr) {
262         delete ctx;
263     }
264 }
265 
266 
PixelFormatToColorType(Media::PixelFormat pixelFormat)267 static Rosen::Drawing::ColorType PixelFormatToColorType(Media::PixelFormat pixelFormat)
268 {
269     switch (pixelFormat) {
270         case Media::PixelFormat::RGB_565:
271             return Rosen::Drawing::ColorType::COLORTYPE_RGB_565;
272         case Media::PixelFormat::RGBA_8888:
273             return Rosen::Drawing::ColorType::COLORTYPE_RGBA_8888;
274         case Media::PixelFormat::BGRA_8888:
275             return Rosen::Drawing::ColorType::COLORTYPE_BGRA_8888;
276         case Media::PixelFormat::ALPHA_8:
277             return Rosen::Drawing::ColorType::COLORTYPE_ALPHA_8;
278         case Media::PixelFormat::RGBA_F16:
279             return Rosen::Drawing::ColorType::COLORTYPE_RGBA_F16;
280         case Media::PixelFormat::UNKNOWN:
281         case Media::PixelFormat::ARGB_8888:
282         case Media::PixelFormat::RGB_888:
283         case Media::PixelFormat::NV21:
284         case Media::PixelFormat::NV12:
285         case Media::PixelFormat::CMYK:
286         default:
287             return Rosen::Drawing::ColorType::COLORTYPE_UNKNOWN;
288     }
289 }
290 
AlphaTypeToAlphaType(Media::AlphaType alphaType)291 static Rosen::Drawing::AlphaType AlphaTypeToAlphaType(Media::AlphaType alphaType)
292 {
293     switch (alphaType) {
294         case Media::AlphaType::IMAGE_ALPHA_TYPE_UNKNOWN:
295             return Rosen::Drawing::AlphaType::ALPHATYPE_UNKNOWN;
296         case Media::AlphaType::IMAGE_ALPHA_TYPE_OPAQUE:
297             return Rosen::Drawing::AlphaType::ALPHATYPE_OPAQUE;
298         case Media::AlphaType::IMAGE_ALPHA_TYPE_PREMUL:
299             return Rosen::Drawing::AlphaType::ALPHATYPE_PREMUL;
300         case Media::AlphaType::IMAGE_ALPHA_TYPE_UNPREMUL:
301             return Rosen::Drawing::AlphaType::ALPHATYPE_UNPREMUL;
302         default:
303             return Rosen::Drawing::AlphaType::ALPHATYPE_UNKNOWN;
304     }
305 }
306 
ConvertToColorSpace(Media::ColorSpace colorSpace)307 static std::shared_ptr<Rosen::Drawing::ColorSpace> ConvertToColorSpace(Media::ColorSpace colorSpace)
308 {
309     switch (colorSpace) {
310         case Media::ColorSpace::DISPLAY_P3:
311             return Rosen::Drawing::ColorSpace::CreateRGB(
312                 Rosen::Drawing::CMSTransferFuncType::SRGB, Rosen::Drawing::CMSMatrixType::DCIP3);
313         case Media::ColorSpace::LINEAR_SRGB:
314             return Rosen::Drawing::ColorSpace::CreateSRGBLinear();
315         case Media::ColorSpace::SRGB:
316             return Rosen::Drawing::ColorSpace::CreateSRGB();
317         default:
318             return Rosen::Drawing::ColorSpace::CreateSRGB();
319     }
320 }
321 
ExtractDrawingImage(pixelmap_ptr_t pixelMap)322 image_ptr_t PointerRenderer::ExtractDrawingImage(pixelmap_ptr_t pixelMap)
323 {
324     CHKPP(pixelMap);
325     Media::ImageInfo imageInfo;
326     pixelMap->GetImageInfo(imageInfo);
327     Rosen::Drawing::ImageInfo drawingImageInfo {
328         imageInfo.size.width,
329         imageInfo.size.height,
330         PixelFormatToColorType(imageInfo.pixelFormat),
331         AlphaTypeToAlphaType(imageInfo.alphaType),
332         ConvertToColorSpace(imageInfo.colorSpace),
333     };
334     Rosen::Drawing::Pixmap imagePixmap(drawingImageInfo, reinterpret_cast<const void*>(pixelMap->GetPixels()),
335         pixelMap->GetRowBytes());
336     PixelMapContext *releaseContext = new (std::nothrow) PixelMapContext(pixelMap);
337     CHKPP(releaseContext);
338     auto image = Rosen::Drawing::Image::MakeFromRaster(imagePixmap, PixelMapReleaseProc, releaseContext);
339     if (image == nullptr) {
340         MMI_HILOGE("ExtractDrawingImage image fail");
341         delete releaseContext;
342     }
343     return image;
344 }
345 
DrawImage(OHOS::Rosen::Drawing::Canvas & canvas,const RenderConfig & cfg)346 int32_t PointerRenderer::DrawImage(OHOS::Rosen::Drawing::Canvas &canvas, const RenderConfig &cfg)
347 {
348     if (cfg.style == MOUSE_ICON::LOADING) {
349         auto loadingImg = FindImg(cfg);
350         if (loadingImg == nullptr) {
351             loadingImg = LoadPointerImage(cfg);
352             CHKPR(loadingImg, RET_ERR);
353             PushImg(cfg, loadingImg);
354         }
355         canvas.Rotate(cfg.rotationAngle, cfg.rotationFocusX, cfg.rotationFocusY);
356         canvas.DrawImage(*loadingImg, cfg.GetOffsetX(), cfg.GetOffsetY(), Rosen::Drawing::SamplingOptions());
357     } else {
358         RenderConfig runingLCfg = cfg;
359         runingLCfg.style = MOUSE_ICON::RUNNING_LEFT;
360         runingLCfg.align = ANGLE_NW;
361         runingLCfg.path = IMAGE_POINTER_DEFAULT_PATH + "Loading_Left.svg";
362         auto runningImgLeft = FindImg(runingLCfg);
363         if (runningImgLeft == nullptr) {
364             runningImgLeft = LoadPointerImage(runingLCfg);
365             CHKPR(runningImgLeft, RET_ERR);
366             PushImg(runingLCfg, runningImgLeft);
367         }
368         CHKPR(runningImgLeft, RET_ERR);
369         canvas.DrawImage(*runningImgLeft, runingLCfg.GetOffsetX(), runingLCfg.GetOffsetY(),
370             Rosen::Drawing::SamplingOptions());
371 
372         RenderConfig runingRCfg = cfg;
373         runingRCfg.style = MOUSE_ICON::RUNNING_RIGHT;
374         runingRCfg.align = ANGLE_NW;
375         runingRCfg.path = IMAGE_POINTER_DEFAULT_PATH + "Loading_Right.svg";
376         auto runningImgRight = FindImg(runingRCfg);
377         if (runningImgRight == nullptr) {
378             runningImgRight = LoadPointerImage(runingRCfg);
379             CHKPR(runningImgRight, RET_ERR);
380             PushImg(runingRCfg, runningImgRight);
381         }
382         canvas.Rotate(runingRCfg.rotationAngle, runingRCfg.rotationFocusX, runingRCfg.rotationFocusY);
383         CHKPR(runningImgRight, RET_ERR);
384         canvas.DrawImage(*runningImgRight, runingRCfg.GetOffsetX(), runingRCfg.GetOffsetY(),
385             Rosen::Drawing::SamplingOptions());
386     }
387     return RET_OK;
388 }
389 
DynamicRender(uint8_t * addr,uint32_t width,uint32_t height,const RenderConfig & cfg)390 int32_t PointerRenderer::DynamicRender(uint8_t *addr, uint32_t width, uint32_t height, const RenderConfig &cfg)
391 {
392     CHKPR(addr, RET_ERR);
393     uint32_t addrSize = width * height * RENDER_STRIDE;
394     if (cfg.style == MOUSE_ICON::TRANSPARENT_ICON) {
395         memset_s(addr, addrSize, 0, addrSize);
396         return RET_OK;
397     }
398 
399     OHOS::Rosen::Drawing::Bitmap bitmap;
400     OHOS::Rosen::Drawing::BitmapFormat format { OHOS::Rosen::Drawing::COLORTYPE_RGBA_8888,
401         OHOS::Rosen::Drawing::ALPHATYPE_OPAQUE };
402     bitmap.Build(width, height, format);
403 
404     OHOS::Rosen::Drawing::Canvas canvas;
405     canvas.Bind(bitmap);
406     canvas.Clear(OHOS::Rosen::Drawing::Color::COLOR_TRANSPARENT);
407 
408     OHOS::Rosen::Drawing::Pen pen;
409     pen.SetAntiAlias(true);
410     pen.SetColor(OHOS::Rosen::Drawing::Color::COLOR_BLUE);
411     OHOS::Rosen::Drawing::scalar penWidth = 1;
412     pen.SetWidth(penWidth);
413     canvas.AttachPen(pen);
414 
415     OHOS::Rosen::Drawing::Brush brush;
416     brush.SetColor(Rosen::Drawing::Color::COLOR_TRANSPARENT);
417     canvas.DrawBackground(brush);
418 
419     if (cfg.direction) {
420         int32_t directionFlag = cfg.isHard ? -1 : 0;
421         int32_t degree = static_cast<int32_t>(directionFlag * static_cast<int32_t>(cfg.direction) * ROTATION_ANGLE90);
422         canvas.Rotate(degree, FOCUS_POINT, FOCUS_POINT);
423     }
424 
425     if (DrawImage(canvas, cfg) != RET_OK) {
426         return RET_ERR;
427     }
428     errno_t ret = memcpy_s(addr, addrSize, bitmap.GetPixels(), addrSize);
429     if (ret != EOK) {
430         return RET_ERR;
431     }
432     return RET_OK;
433 }
434 } // namespace OHOS::MMI