1 /*
2  * Copyright (c) 2024 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 "knuckle_dynamic_drawing_manager.h"
17 
18 #include "image/bitmap.h"
19 #include "image_source.h"
20 #include "image_type.h"
21 #include "image_utils.h"
22 
23 #include "mmi_log.h"
24 #ifndef USE_ROSEN_DRAWING
25 #include "pipeline/rs_recording_canvas.h"
26 #else
27 #include "recording/recording_canvas.h"
28 #include "ui/rs_canvas_drawing_node.h"
29 #endif // USE_ROSEN_DRAWING
30 #include "parameters.h"
31 #include "render/rs_pixel_map_util.h"
32 #include "touch_drawing_manager.h"
33 
34 #undef MMI_LOG_TAG
35 #define MMI_LOG_TAG "KnuckleDynamicDrawingManager"
36 
37 namespace OHOS {
38 namespace MMI {
39 namespace {
40 const std::string IMAGE_POINTER_PENTAGRAM_PATH = "/system/etc/multimodalinput/mouse_icon/";
41 const std::string PENT_ICON_PATH = IMAGE_POINTER_PENTAGRAM_PATH + "Knuckle_Sprite_360.png";
42 constexpr int32_t DENSITY_BASELINE { 160 };
43 constexpr int32_t INDEPENDENT_INNER_PIXELS { 20 };
44 constexpr int32_t INDEPENDENT_OUTER_PIXELS { 21 };
45 constexpr int32_t INDEPENDENT_WIDTH_PIXELS { 2 };
46 constexpr int32_t CALCULATE_MIDDLE { 2 };
47 constexpr int32_t DEFAULT_VALUE { -1 };
48 constexpr int32_t MAX_POINTER_COLOR { 0x00ffff };
49 constexpr int32_t TIME_DIMENSION { 1000 };
50 constexpr int32_t PATH_COLOR { 0xFFCCCCCC };
51 constexpr int32_t MIN_POINT_SIZE { 1 };
52 constexpr float PAINT_STROKE_WIDTH { 10.0f };
53 constexpr float DOUBLE { 2.0f };
54 constexpr int32_t POINT_TOTAL_SIZE { 5 };
55 constexpr int32_t POINT_SYSTEM_SIZE { 200 };
56 constexpr int32_t MAX_DIVERGENCE_NUM { 10 };
57 constexpr int32_t DEFAULT_POINTER_SIZE { 1 };
58 constexpr int32_t DESIRED_SIZE { 80 };
59 constexpr int64_t DOUBLE_CLICK_INTERVAL_TIME_SLOW { 450000 };
60 constexpr int64_t WAIT_DOUBLE_CLICK_INTERVAL_TIME { 100000 };
61 constexpr float DOUBLE_CLICK_DISTANCE_LONG_CONFIG { 96.0f };
62 constexpr float VPR_CONFIG { 3.25f };
63 constexpr int32_t POW_SQUARE { 2 };
64 constexpr int32_t IN_DRAWING_TIME { 23000 };
65 constexpr uint64_t FOLD_SCREEN_MAIN_ID { 5 };
66 constexpr std::string_view SCREEN_READ_ENABLE { "1" };
67 } // namespace
68 
KnuckleDynamicDrawingManager()69 KnuckleDynamicDrawingManager::KnuckleDynamicDrawingManager()
70 {
71     InitPointerPathPaint();
72 }
73 
DecodeImageToPixelMap(const std::string & imagePath)74 std::shared_ptr<OHOS::Media::PixelMap> KnuckleDynamicDrawingManager::DecodeImageToPixelMap(const std::string &imagePath)
75 {
76     CALL_DEBUG_ENTER;
77     OHOS::Media::SourceOptions opts;
78     uint32_t ret = 0;
79     auto imageSource = OHOS::Media::ImageSource::CreateImageSource(imagePath, opts, ret);
80     CHKPP(imageSource);
81     std::set<std::string> formats;
82     ret = imageSource->GetSupportedFormats(formats);
83     OHOS::Media::DecodeOptions decodeOpts;
84     decodeOpts.desiredSize = {
85         .width = DESIRED_SIZE,
86         .height = DESIRED_SIZE
87     };
88 
89     decodeOpts.SVGOpts.fillColor = {.isValidColor = true, .color = MAX_POINTER_COLOR};
90     decodeOpts.SVGOpts.strokeColor = {.isValidColor = true, .color = MAX_POINTER_COLOR};
91     std::shared_ptr<OHOS::Media::PixelMap> pixelMap = imageSource->CreatePixelMap(decodeOpts, ret);
92     CHKPL(pixelMap);
93     return pixelMap;
94 }
95 
InitPointerPathPaint()96 void KnuckleDynamicDrawingManager::InitPointerPathPaint()
97 {
98     CALL_DEBUG_ENTER;
99     pixelMap_ = DecodeImageToPixelMap(PENT_ICON_PATH);
100     CHKPV(pixelMap_);
101     if (glowTraceSystem_ == nullptr) {
102         glowTraceSystem_ = std::make_shared<KnuckleGlowTraceSystem>(POINT_SYSTEM_SIZE, pixelMap_, MAX_DIVERGENCE_NUM);
103     }
104     pointerPathPaint_.setStyle(SkPaint::Style::kStroke_Style);
105     pointerPathPaint_.setStrokeJoin(SkPaint::Join::kRound_Join);
106     pointerPathPaint_.setStrokeCap(SkPaint::Cap::kRound_Cap);
107     pointerPathPaint_.setStrokeWidth(PAINT_STROKE_WIDTH);
108     pointerPathPaint_.setAntiAlias(true);
109 }
110 
UpdateTrackColors()111 void KnuckleDynamicDrawingManager::UpdateTrackColors()
112 {
113     CALL_DEBUG_ENTER;
114     pointerPathPaint_.setColor(PATH_COLOR);
115 }
116 
KnuckleDynamicDrawHandler(std::shared_ptr<PointerEvent> pointerEvent)117 void KnuckleDynamicDrawingManager::KnuckleDynamicDrawHandler(std::shared_ptr<PointerEvent> pointerEvent)
118 {
119     CALL_DEBUG_ENTER;
120     CHKPV(pointerEvent);
121     if (!IsSingleKnuckle(pointerEvent)) {
122         return;
123     }
124     auto displayId = pointerEvent->GetTargetDisplayId();
125     CreateTouchWindow(displayId);
126     if (CheckPointerAction(pointerEvent)) {
127         StartTouchDraw(pointerEvent);
128     }
129 }
130 
IsSingleKnuckle(std::shared_ptr<PointerEvent> touchEvent)131 bool KnuckleDynamicDrawingManager::IsSingleKnuckle(std::shared_ptr<PointerEvent> touchEvent)
132 {
133     CALL_DEBUG_ENTER;
134     CHKPF(touchEvent);
135     auto id = touchEvent->GetPointerId();
136     PointerEvent::PointerItem item;
137     touchEvent->GetPointerItem(id, item);
138     auto itemToolType = item.GetToolType();
139     if (itemToolType != PointerEvent::TOOL_TYPE_KNUCKLE ||
140         touchEvent->GetPointerIds().size() != 1 || isRotate_) {
141         if (!traceControlPoints_.empty()) {
142             isStop_ = true;
143             isDrawing_ = true;
144             DestoryWindow();
145         } else if (isRotate_) {
146             isRotate_ = false;
147             if (item.GetToolType() == PointerEvent::TOOL_TYPE_KNUCKLE) {
148                 return true;
149             }
150         }
151         return false;
152     }
153     return true;
154 }
155 
CheckPointerAction(std::shared_ptr<PointerEvent> pointerEvent)156 bool KnuckleDynamicDrawingManager::CheckPointerAction(std::shared_ptr<PointerEvent> pointerEvent)
157 {
158     CALL_DEBUG_ENTER;
159     CHKPF(knuckleDrawMgr_);
160     if (knuckleDrawMgr_->GetScreenReadState() == SCREEN_READ_ENABLE) {
161         DestoryWindow();
162     }
163     size_t size = pointerEvent->GetPointerIds().size();
164     if (size > MIN_POINT_SIZE) {
165         pointerPath_.Reset();
166         CHKPF(glowTraceSystem_);
167         glowTraceSystem_->Clear();
168         return false;
169     }
170     switch (pointerEvent->GetPointerAction()) {
171         case PointerEvent::POINTER_ACTION_UP:
172         case PointerEvent::POINTER_ACTION_PULL_UP:
173             ProcessUpAndCancelEvent(pointerEvent);
174             break;
175         case PointerEvent::POINTER_ACTION_DOWN:
176         case PointerEvent::POINTER_ACTION_PULL_DOWN:
177             ProcessDownEvent(pointerEvent);
178             return true;
179         case PointerEvent::POINTER_ACTION_MOVE:
180         case PointerEvent::POINTER_ACTION_PULL_MOVE:
181             if (!isStop_ && !traceControlPoints_.empty()) {
182                 ProcessMoveEvent(pointerEvent);
183                 return true;
184             }
185             return false;
186         default:
187             return false;
188     }
189     return true;
190 }
191 
StartTouchDraw(std::shared_ptr<PointerEvent> pointerEvent)192 void KnuckleDynamicDrawingManager::StartTouchDraw(std::shared_ptr<PointerEvent> pointerEvent)
193 {
194     CHKPV(pointerEvent);
195     int32_t ret = DrawGraphic(pointerEvent);
196     if (ret != RET_OK) {
197         MMI_HILOGD("Can't get enough pointers to draw");
198         return;
199     }
200     Rosen::RSTransaction::FlushImplicitTransaction();
201 }
202 
ProcessUpAndCancelEvent(std::shared_ptr<PointerEvent> pointerEvent)203 void KnuckleDynamicDrawingManager::ProcessUpAndCancelEvent(std::shared_ptr<PointerEvent> pointerEvent)
204 {
205     CALL_DEBUG_ENTER;
206     CHKPV(glowTraceSystem_);
207     CHKPV(pointerEvent);
208     lastUpTime_ = pointerEvent->GetActionTime();
209     if (pointerPath_.IsValid()) {
210         auto id = pointerEvent->GetPointerId();
211         PointerEvent::PointerItem pointerItem;
212         pointerEvent->GetPointerItem(id, pointerItem);
213         auto displayXY = TOUCH_DRAWING_MGR->CalcDrawCoordinate(displayInfo_, pointerItem);
214         glowTraceSystem_->ResetDivergentPoints(displayXY.first, displayXY.second);
215     }
216     isDrawing_ = true;
217     DestoryWindow();
218 }
219 
ProcessDownEvent(std::shared_ptr<PointerEvent> pointerEvent)220 void KnuckleDynamicDrawingManager::ProcessDownEvent(std::shared_ptr<PointerEvent> pointerEvent)
221 {
222     CALL_DEBUG_ENTER;
223     CHKPV(pointerEvent);
224     if (traceControlPoints_.empty()) {
225         for (int32_t i = 0; i < POINT_TOTAL_SIZE; i++) {
226             Rosen::Drawing::Point point = Rosen::Drawing::Point();
227             traceControlPoints_.emplace_back(point);
228         }
229     }
230     int64_t intervalTime = pointerEvent->GetActionTime() - lastUpTime_;
231     firstDownTime_ = pointerEvent->GetActionTime();
232     bool isTimeIntervalReady = intervalTime > 0 && intervalTime <= DOUBLE_CLICK_INTERVAL_TIME_SLOW;
233 
234     UpdateTrackColors();
235     lastUpdateTimeMillis_ = pointerEvent->GetActionTime();
236     pointCounter_ = 0;
237     auto id = pointerEvent->GetPointerId();
238     PointerEvent::PointerItem pointerItem;
239     pointerEvent->GetPointerItem(id, pointerItem);
240     auto displayXY = TOUCH_DRAWING_MGR->CalcDrawCoordinate(displayInfo_, pointerItem);
241     float downToPrevDownDistance = static_cast<float>(sqrt(pow(lastDownX_ - displayXY.first, POW_SQUARE) +
242         pow(lastDownY_ - displayXY.second, POW_SQUARE)));
243     bool isDistanceReady = downToPrevDownDistance < DOUBLE_CLICK_DISTANCE_LONG_CONFIG * POW_SQUARE;
244     if (isTimeIntervalReady && isDistanceReady) {
245         MMI_HILOGE("Take a screenshot");
246         isDrawing_ = true;
247         isStop_ = true;
248         return;
249     }
250     lastDownX_ = displayXY.first;
251     lastDownY_ = displayXY.second;
252     traceControlPoints_[pointCounter_].Set(displayXY.first, displayXY.second);
253     isStop_ = false;
254 }
255 
ProcessMoveEvent(std::shared_ptr<PointerEvent> pointerEvent)256 void KnuckleDynamicDrawingManager::ProcessMoveEvent(std::shared_ptr<PointerEvent> pointerEvent)
257 {
258     CALL_DEBUG_ENTER;
259     CHKPV(pointerEvent);
260     pointCounter_++;
261     if (pointCounter_ >= POINT_TOTAL_SIZE) {
262         MMI_HILOGE("traceControlPoints_ index out of size");
263         return;
264     }
265     auto id = pointerEvent->GetPointerId();
266     PointerEvent::PointerItem pointerItem;
267     pointerEvent->GetPointerItem(id, pointerItem);
268     auto displayXY = TOUCH_DRAWING_MGR->CalcDrawCoordinate(displayInfo_, pointerItem);
269     traceControlPoints_[pointCounter_].Set(displayXY.first, displayXY.second);
270     int pointIndex4 = 4;
271     bool draw = (pointerEvent->GetActionTime() - firstDownTime_) > WAIT_DOUBLE_CLICK_INTERVAL_TIME;
272     if (pointCounter_ == pointIndex4) {
273         int pointIndex0 = 0;
274         int pointIndex1 = 1;
275         int pointIndex2 = 2;
276         int pointIndex3 = 3;
277 
278         traceControlPoints_[pointIndex3].Set(
279             (traceControlPoints_[pointIndex2].GetX() + traceControlPoints_[pointIndex4].GetX()) / DOUBLE,
280             (traceControlPoints_[pointIndex2].GetY() + traceControlPoints_[pointIndex4].GetY()) / DOUBLE);
281         // Add a cubic Bezier from pt[0] to pt[3] with control pointspt[1] and pt[2]
282         pointerPath_.MoveTo (traceControlPoints_[pointIndex0].GetX(), traceControlPoints_[pointIndex0].GetY());
283         pointerPath_.CubicTo(traceControlPoints_[pointIndex1].GetX(), traceControlPoints_[pointIndex1].GetY(),
284             traceControlPoints_[pointIndex2].GetX(), traceControlPoints_[pointIndex2].GetY(),
285             traceControlPoints_[pointIndex3].GetX(), traceControlPoints_[pointIndex3].GetY());
286         traceControlPoints_[pointIndex0].Set(traceControlPoints_[pointIndex3].GetX(),
287             traceControlPoints_[pointIndex3].GetY());
288         traceControlPoints_[pointIndex1].Set (traceControlPoints_[pointIndex4].GetX(),
289             traceControlPoints_[pointIndex4].GetY());
290         pointCounter_ = 1;
291         // Add glowing particles onto the last path segment that was drawn
292         int64_t now = pointerEvent->GetActionTime();
293         if (draw) {
294             glowTraceSystem_->AddGlowPoints(pointerPath_, (now - lastUpdateTimeMillis_) / TIME_DIMENSION);
295         }
296         pointerPath_.Reset();
297         lastUpdateTimeMillis_ = now;
298     }
299     if (draw) {
300         glowTraceSystem_->ResetDivergentPoints(displayXY.first, displayXY.second);
301         isDrawing_ = false;
302     }
303 }
304 
UpdateDisplayInfo(const DisplayInfo & displayInfo)305 void KnuckleDynamicDrawingManager::UpdateDisplayInfo(const DisplayInfo& displayInfo)
306 {
307     CALL_DEBUG_ENTER;
308     if (displayInfo_.direction != displayInfo.direction) {
309         MMI_HILOGD("DisplayInfo direction change");
310         isRotate_ = true;
311     }
312     scaleW_ = displayInfo.width > displayInfo.height ? displayInfo.width : displayInfo.height;
313     scaleH_ = displayInfo.width > displayInfo.height ? displayInfo.width : displayInfo.height;
314     displayInfo_ = displayInfo;
315 }
316 
SetKnuckleDrawingManager(std::shared_ptr<KnuckleDrawingManager> knuckleDrawMgr)317 void KnuckleDynamicDrawingManager::SetKnuckleDrawingManager(std::shared_ptr<KnuckleDrawingManager> knuckleDrawMgr)
318 {
319     CALL_DEBUG_ENTER;
320     knuckleDrawMgr_ = knuckleDrawMgr;
321 }
322 
DrawGraphic(std::shared_ptr<PointerEvent> pointerEvent)323 int32_t KnuckleDynamicDrawingManager::DrawGraphic(std::shared_ptr<PointerEvent> pointerEvent)
324 {
325     CALL_DEBUG_ENTER;
326     CHKPR(pointerEvent, RET_ERR);
327     CHKPR(canvasNode_, RET_ERR);
328     glowTraceSystem_->Update();
329     if ((pointerEvent->GetActionTime() - isInDrawingTime_) > IN_DRAWING_TIME) {
330         isInDrawingTime_ = pointerEvent->GetActionTime();
331     } else {
332         return RET_ERR;
333     }
334 #ifndef USE_ROSEN_DRAWING
335     auto canvas = static_cast<Rosen::RSRecordingCanvas *>(canvasNode_->
336         BeginRecording(scaleW_, scaleH_));
337 #else
338     auto canvas = static_cast<Rosen::ExtendRecordingCanvas *>(canvasNode_->
339         BeginRecording(scaleW_, scaleH_));
340 #endif // USE_ROSEN_DRAWING
341 
342     CHKPR(canvas, RET_ERR);
343 
344     if (!isDrawing_) {
345         glowTraceSystem_->Draw(canvas);
346     }
347     canvasNode_->ResetSurface(scaleW_, scaleH_);
348     canvasNode_->FinishRecording();
349     return RET_OK;
350 }
351 
CreateTouchWindow(const int32_t displayId)352 void KnuckleDynamicDrawingManager::CreateTouchWindow(const int32_t displayId)
353 {
354     CALL_DEBUG_ENTER;
355     if (surfaceNode_ != nullptr) {
356         MMI_HILOGD("surfaceNode_ is already exist");
357         return;
358     }
359     Rosen::RSSurfaceNodeConfig surfaceNodeConfig;
360     surfaceNodeConfig.SurfaceNodeName = "knuckle dynamic window";
361     Rosen::RSSurfaceNodeType surfaceNodeType = Rosen::RSSurfaceNodeType::SELF_DRAWING_WINDOW_NODE;
362     surfaceNode_ = Rosen::RSSurfaceNode::Create(surfaceNodeConfig, surfaceNodeType);
363     CHKPV(surfaceNode_);
364 
365     surfaceNode_->SetSkipLayer(true);
366     surfaceNode_->SetFrameGravity(Rosen::Gravity::RESIZE_ASPECT_FILL);
367     surfaceNode_->SetPositionZ(Rosen::RSSurfaceNode::POINTER_WINDOW_POSITION_Z);
368     surfaceNode_->SetBounds(0, 0, scaleW_, scaleH_);
369     surfaceNode_->SetFrame(0, 0, scaleW_, scaleH_);
370 
371 #ifndef USE_ROSEN_DRAWING
372     surfaceNode_->SetBackgroundColor(SK_ColorTRANSPARENT);
373 #else
374     surfaceNode_->SetBackgroundColor(Rosen::Drawing::Color::COLOR_TRANSPARENT);
375 #endif // USE_ROSEN_DRAWING
376 
377     screenId_ = static_cast<uint64_t>(displayId);
378     surfaceNode_->SetRotation(0);
379 
380     CreateCanvasNode();
381     surfaceNode_->AddChild(canvasNode_, DEFAULT_VALUE);
382     if (displayInfo_.displayMode == DisplayMode::MAIN) {
383         screenId_ = FOLD_SCREEN_MAIN_ID;
384     }
385     MMI_HILOGI("screenId_: %{public}" PRIu64, screenId_);
386     surfaceNode_->AttachToDisplay(screenId_);
387     CHKPV(knuckleDrawMgr_);
388     if (knuckleDrawMgr_->CheckRotatePolicy(displayInfo_)) {
389         knuckleDrawMgr_->RotationCanvasNode(canvasNode_, displayInfo_);
390     }
391     canvasNode_->ResetSurface(scaleW_, scaleH_);
392     Rosen::RSTransaction::FlushImplicitTransaction();
393 }
394 
CreateCanvasNode()395 void KnuckleDynamicDrawingManager::CreateCanvasNode()
396 {
397     CALL_DEBUG_ENTER;
398     canvasNode_ = Rosen::RSCanvasDrawingNode::Create();
399     CHKPV(canvasNode_);
400     canvasNode_->SetBounds(0, 0, scaleW_, scaleH_);
401     canvasNode_->SetFrame(0, 0, scaleW_, scaleH_);
402 
403 #ifndef USE_ROSEN_DRAWING
404     canvasNode_->SetBackgroundColor(SK_ColorTRANSPARENT);
405 #else
406     canvasNode_->SetBackgroundColor(Rosen::Drawing::Color::COLOR_TRANSPARENT);
407 #endif // USE_ROSEN_DRAWING
408     canvasNode_->SetCornerRadius(1);
409     canvasNode_->SetPositionZ(Rosen::RSSurfaceNode::POINTER_WINDOW_POSITION_Z);
410     canvasNode_->SetRotation(0);
411 }
412 
DestoryWindow()413 void KnuckleDynamicDrawingManager::DestoryWindow()
414 {
415     CALL_DEBUG_ENTER;
416     traceControlPoints_.clear();
417     pointerPath_.Reset();
418     if (glowTraceSystem_ != nullptr) {
419         glowTraceSystem_->Clear();
420     }
421     CHKPV(canvasNode_);
422 #ifndef USE_ROSEN_DRAWING
423     auto canvas = static_cast<Rosen::RSRecordingCanvas *>(canvasNode_->
424         BeginRecording(scaleW_, scaleH_));
425 #else
426     auto canvas = static_cast<Rosen::ExtendRecordingCanvas *>(canvasNode_->
427         BeginRecording(scaleW_, scaleH_));
428 #endif // USE_ROSEN_DRAWING
429     CHKPV(canvas);
430     canvas->Clear();
431     canvasNode_->FinishRecording();
432     CHKPV(surfaceNode_);
433     surfaceNode_->DetachToDisplay(screenId_);
434     surfaceNode_->RemoveChild(canvasNode_);
435     canvasNode_->ResetSurface(scaleW_, scaleH_);
436     canvasNode_.reset();
437     surfaceNode_.reset();
438     Rosen::RSTransaction::FlushImplicitTransaction();
439 }
440 } // namespace MMI
441 } // namespace OHOS
442