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