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