1 /*
2  * Copyright (c) 2022 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 "core/components_ng/pattern/canvas/canvas_paint_method.h"
17 
18 #include "base/log/ace_trace.h"
19 #include "core/components_ng/pattern/canvas/custom_paint_util.h"
20 
21 #ifndef ACE_UNITTEST
22 #include "include/utils/SkBase64.h"
23 #include "core/components/common/painter/rosen_decoration_painter.h"
24 #include "core/components/font/constants_converter.h"
25 #include "core/components/font/rosen_font_collection.h"
26 #include "core/components_ng/render/adapter/rosen_render_context.h"
27 #include "core/image/sk_image_cache.h"
28 #endif
29 
30 #include "base/i18n/localization.h"
31 #include "base/image/pixel_map.h"
32 #include "base/utils/utils.h"
33 #include "core/common/container.h"
34 #include "core/components_ng/image_provider/image_object.h"
35 #include "core/components_ng/pattern/canvas/canvas_paint_op.h"
36 #include "core/components_ng/render/drawing.h"
37 
38 namespace OHOS::Ace::NG {
39 constexpr Dimension DEFAULT_FONT_SIZE = 14.0_px;
CanvasPaintMethod(RefPtr<CanvasModifier> contentModifier,const RefPtr<FrameNode> & frameNode)40 CanvasPaintMethod::CanvasPaintMethod(RefPtr<CanvasModifier> contentModifier, const RefPtr<FrameNode>& frameNode)
41     : frameNode_(frameNode)
42 {
43     matrix_.Reset();
44     context_ = frameNode ? frameNode->GetContextRefPtr() : nullptr;
45     imageShadow_ = std::make_unique<Shadow>();
46     contentModifier_ = contentModifier;
47     // The initial value of the font size in canvas is 14px.
48     SetFontSize(DEFAULT_FONT_SIZE);
49 }
50 
51 #ifndef USE_FAST_TASKPOOL
PushTask(const TaskFunc & task)52 void CanvasPaintMethod::PushTask(const TaskFunc& task)
53 {
54     static constexpr uint32_t suggestSize = 100000;
55     tasks_.emplace_back(task);
56     if (tasks_.size() >= suggestSize && tasks_.size() % suggestSize == 0) {
57         TAG_LOGI(AceLogTag::ACE_CANVAS, "[%{public}s] Canvas task size: %{public}zu", customNodeName_.c_str(),
58             tasks_.size());
59     }
60     CHECK_EQUAL_VOID(needMarkDirty_, false);
61     needMarkDirty_ = false;
62     auto host = frameNode_.Upgrade();
63     CHECK_NULL_VOID(host);
64     host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
65 }
66 #endif
67 
HasTask() const68 bool CanvasPaintMethod::HasTask() const
69 {
70 #ifndef USE_FAST_TASKPOOL
71     return !tasks_.empty();
72 #else
73     return fastTaskPool_ && !fastTaskPool_->Empty();
74 #endif
75 }
76 
FlushTask()77 void CanvasPaintMethod::FlushTask()
78 {
79 #ifndef USE_FAST_TASKPOOL
80     ACE_SCOPED_TRACE("Canvas tasks count: %zu.", tasks_.size());
81     for (auto& task : tasks_) {
82         task(*this);
83     }
84     tasks_.clear();
85 #else
86     CHECK_NULL_VOID(fastTaskPool_);
87     fastTaskPool_->Draw(this);
88     fastTaskPool_->Reset();
89 #endif
90     needMarkDirty_ = true;
91 }
92 
UpdateContentModifier(PaintWrapper * paintWrapper)93 void CanvasPaintMethod::UpdateContentModifier(PaintWrapper* paintWrapper)
94 {
95     ACE_SCOPED_TRACE("CanvasPaintMethod::UpdateContentModifier");
96     auto host = frameNode_.Upgrade();
97     CHECK_NULL_VOID(host);
98     auto geometryNode = host->GetGeometryNode();
99     CHECK_NULL_VOID(geometryNode);
100     auto pixelGridRoundSize = geometryNode->GetPixelGridRoundSize();
101     lastLayoutSize_.SetSizeT(pixelGridRoundSize);
102     auto recordingCanvas = std::static_pointer_cast<RSRecordingCanvas>(rsCanvas_);
103     CHECK_NULL_VOID(recordingCanvas);
104     auto context = context_.Upgrade();
105     CHECK_NULL_VOID(context);
106     auto fontManager = context->GetFontManager();
107     if (fontManager) {
108         recordingCanvas->SetIsCustomTextType(fontManager->IsDefaultFontChanged());
109     }
110 
111     if (!HasTask()) {
112         return;
113     }
114 
115     FireOnModifierUpdateFunc();
116     recordingCanvas->Scale(1.0, 1.0);
117     FlushTask();
118     CHECK_NULL_VOID(contentModifier_);
119     contentModifier_->MarkModifierDirty();
120 }
121 
UpdateRecordingCanvas(float width,float height)122 void CanvasPaintMethod::UpdateRecordingCanvas(float width, float height)
123 {
124     rsCanvas_ = std::make_shared<RSRecordingCanvas>(width, height);
125     contentModifier_->UpdateCanvas(std::static_pointer_cast<RSRecordingCanvas>(rsCanvas_));
126     CHECK_NULL_VOID(rsCanvas_);
127     rsCanvas_->Save();
128     FireRSCanvasCallback(width, height);
129     if (Container::GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_THIRTEEN)) {
130         ResetStates();
131     }
132     needMarkDirty_ = true;
133 }
134 
DrawPixelMap(RefPtr<PixelMap> pixelMap,const Ace::CanvasImage & canvasImage)135 void CanvasPaintMethod::DrawPixelMap(RefPtr<PixelMap> pixelMap, const Ace::CanvasImage& canvasImage)
136 {
137 #ifndef ACE_UNITTEST
138     InitImagePaint(nullptr, &imageBrush_, sampleOptions_);
139     imageBrush_.SetAntiAlias(antiAlias_);
140     RSBrush compositeOperationpBrush;
141     InitPaintBlend(compositeOperationpBrush);
142     RSSaveLayerOps layerOps(nullptr, &compositeOperationpBrush);
143     if (state_.globalState.GetType() != CompositeOperation::SOURCE_OVER) {
144         rsCanvas_->SaveLayer(layerOps);
145     }
146 
147     if (state_.globalState.HasGlobalAlpha()) {
148         imageBrush_.SetAlphaF(state_.globalState.GetAlpha());
149     }
150 
151     if (HasShadow()) {
152         RSRect rec = RSRect(
153             canvasImage.dx, canvasImage.dy, canvasImage.dx + canvasImage.dWidth, canvasImage.dy + canvasImage.dHeight);
154         RSPath path;
155         path.AddRect(rec);
156         PaintImageShadow(path, state_.shadow, &imageBrush_, nullptr,
157             (state_.globalState.GetType() != CompositeOperation::SOURCE_OVER) ? &layerOps : nullptr);
158     }
159     DrawPixelMapInternal(pixelMap, canvasImage);
160     if (state_.globalState.GetType() != CompositeOperation::SOURCE_OVER) {
161         rsCanvas_->Restore();
162     }
163 #endif
164 }
165 
DrawPixelMapInternal(RefPtr<PixelMap> pixelMap,const Ace::CanvasImage & canvasImage)166 void CanvasPaintMethod::DrawPixelMapInternal(RefPtr<PixelMap> pixelMap, const Ace::CanvasImage& canvasImage)
167 {
168 #ifndef ACE_UNITTEST
169     const std::shared_ptr<Media::PixelMap> tempPixelMap = pixelMap->GetPixelMapSharedPtr();
170     CHECK_NULL_VOID(tempPixelMap);
171     RSRect srcRect;
172     RSRect dstRect;
173     switch (canvasImage.flag) {
174         case DrawImageType::THREE_PARAMS: {
175             srcRect = RSRect(0, 0, tempPixelMap->GetWidth(), tempPixelMap->GetHeight());
176             dstRect = RSRect(canvasImage.dx, canvasImage.dy, canvasImage.dx + tempPixelMap->GetWidth(),
177                 canvasImage.dy + tempPixelMap->GetHeight());
178             break;
179         }
180         case DrawImageType::FIVE_PARAMS: {
181             srcRect = RSRect(0, 0, tempPixelMap->GetWidth(), tempPixelMap->GetHeight());
182             dstRect = RSRect(canvasImage.dx, canvasImage.dy, canvasImage.dx + canvasImage.dWidth,
183                 canvasImage.dy + canvasImage.dHeight);
184             break;
185         }
186         case DrawImageType::NINE_PARAMS: {
187             srcRect = RSRect(canvasImage.sx, canvasImage.sy, canvasImage.sx + canvasImage.sWidth,
188                 canvasImage.sy + canvasImage.sHeight);
189             dstRect = RSRect(canvasImage.dx, canvasImage.dy, canvasImage.dx + canvasImage.dWidth,
190                 canvasImage.dy + canvasImage.dHeight);
191             break;
192         }
193         default:
194             break;
195     }
196     auto recordingCanvas = std::static_pointer_cast<RSRecordingCanvas>(rsCanvas_);
197     CHECK_NULL_VOID(recordingCanvas);
198     recordingCanvas->AttachBrush(imageBrush_);
199     recordingCanvas->DrawPixelMapRect(tempPixelMap, srcRect, dstRect, sampleOptions_);
200     recordingCanvas->DetachBrush();
201 #endif
202 }
203 
CloseImageBitmap(const std::string & src)204 void CanvasPaintMethod::CloseImageBitmap(const std::string& src)
205 {
206 #ifndef ACE_UNITTEST
207     CHECK_NULL_VOID(imageCache_);
208     auto cacheImage = imageCache_->GetCacheImage(src);
209     CHECK_NULL_VOID(cacheImage);
210     CHECK_NULL_VOID(cacheImage->imagePtr);
211     imageCache_->ClearCacheImage(src);
212 #endif
213 }
214 
GetImageData(double left,double top,double width,double height)215 std::unique_ptr<Ace::ImageData> CanvasPaintMethod::GetImageData(
216     double left, double top, double width, double height)
217 {
218     auto host = frameNode_.Upgrade();
219     CHECK_NULL_RETURN(host, nullptr);
220     auto renderContext = host->GetRenderContext();
221     CHECK_NULL_RETURN(renderContext, nullptr);
222     double dirtyWidth = std::abs(width);
223     double dirtyHeight = std::abs(height);
224     double scaledLeft = left + std::min(width, 0.0);
225     double scaledTop = top + std::min(height, 0.0);
226 
227     // copy the bitmap to tempCanvas
228     RSBitmap currentBitmap;
229     if (!DrawBitmap(renderContext, currentBitmap)) {
230         return nullptr;
231     }
232 
233     RSBitmapFormat format { RSColorType::COLORTYPE_BGRA_8888, RSAlphaType::ALPHATYPE_OPAQUE };
234     RSBitmap tempCache;
235     tempCache.Build(dirtyWidth, dirtyHeight, format);
236     int32_t size = dirtyWidth * dirtyHeight;
237 
238     RSCanvas tempCanvas;
239     tempCanvas.Bind(tempCache);
240     auto srcRect = RSRect(scaledLeft, scaledTop, dirtyWidth + scaledLeft, dirtyHeight + scaledTop);
241     auto dstRect = RSRect(0.0, 0.0, dirtyWidth, dirtyHeight);
242     RSImage rsImage;
243     rsImage.BuildFromBitmap(currentBitmap);
244     tempCanvas.DrawImageRect(rsImage, srcRect, dstRect, RSSamplingOptions());
245     const uint8_t* pixels = static_cast<const uint8_t*>(tempCache.GetPixels());
246     CHECK_NULL_RETURN(pixels, nullptr);
247     std::unique_ptr<Ace::ImageData> imageData = std::make_unique<Ace::ImageData>();
248     imageData->dirtyWidth = dirtyWidth;
249     imageData->dirtyHeight = dirtyHeight;
250     // a pixel include 4 data(blue, green, red, alpha)
251     for (int i = 0; i < size * 4; i += 4) {
252         auto blue = pixels[i];
253         auto green = pixels[i + 1];
254         auto red = pixels[i + 2];
255         auto alpha = pixels[i + 3];
256         imageData->data.emplace_back(Color::FromARGB(alpha, red, green, blue).GetValue());
257     }
258     return imageData;
259 }
260 
GetImageData(const std::shared_ptr<Ace::ImageData> & imageData)261 void CanvasPaintMethod::GetImageData(
262     const std::shared_ptr<Ace::ImageData>& imageData)
263 {
264 #ifndef ACE_UNITTEST
265     auto host = frameNode_.Upgrade();
266     CHECK_NULL_VOID(host);
267     auto renderContext = host->GetRenderContext();
268     auto rosenRenderContext = AceType::DynamicCast<RosenRenderContext>(renderContext);
269     CHECK_NULL_VOID(rosenRenderContext);
270     CHECK_NULL_VOID(imageData);
271     auto dirtyWidth = std::abs(imageData->dirtyWidth);
272     auto dirtyHeight = std::abs(imageData->dirtyHeight);
273     auto scaledLeft = imageData->dirtyX + std::min(imageData->dirtyWidth, 0);
274     auto scaledTop = imageData->dirtyY + std::min(imageData->dirtyHeight, 0);
275 
276     auto recordingCanvas = std::static_pointer_cast<RSRecordingCanvas>(rsCanvas_);
277     CHECK_NULL_VOID(recordingCanvas);
278     auto drawCmdList = recordingCanvas->GetDrawCmdList();
279     auto rect = RSRect(scaledLeft, scaledTop, dirtyWidth + scaledLeft, dirtyHeight + scaledTop);
280     auto pixelMap = imageData->pixelMap;
281     CHECK_NULL_VOID(pixelMap);
282     auto sharedPixelMap = pixelMap->GetPixelMapSharedPtr();
283     auto ret = rosenRenderContext->GetPixelMap(sharedPixelMap, drawCmdList, &rect);
284     if (!ret) {
285         if (!drawCmdList || drawCmdList->IsEmpty()) {
286             return;
287         }
288         RSBitmap bitmap;
289         RSImageInfo info = RSImageInfo(rect.GetWidth(), rect.GetHeight(),
290             RSColorType::COLORTYPE_RGBA_8888, RSAlphaType::ALPHATYPE_PREMUL);
291         bitmap.InstallPixels(info, pixelMap->GetWritablePixels(), pixelMap->GetRowBytes());
292         RSCanvas canvas;
293         canvas.Bind(bitmap);
294         canvas.Translate(-rect.GetLeft(), -rect.GetTop());
295         drawCmdList->Playback(canvas, &rect);
296     }
297 #endif
298 }
299 
300 #ifdef PIXEL_MAP_SUPPORTED
TransferFromImageBitmap(const RefPtr<PixelMap> & pixelMap)301 void CanvasPaintMethod::TransferFromImageBitmap(const RefPtr<PixelMap>& pixelMap)
302 {
303     CHECK_NULL_VOID(pixelMap);
304     InitImagePaint(nullptr, &imageBrush_, sampleOptions_);
305     auto recordingCanvas = std::static_pointer_cast<RSRecordingCanvas>(rsCanvas_);
306     CHECK_NULL_VOID(recordingCanvas);
307     const std::shared_ptr<Media::PixelMap> tempPixelMap = pixelMap->GetPixelMapSharedPtr();
308     CHECK_NULL_VOID(tempPixelMap);
309     RSRect srcRect = RSRect(0, 0, tempPixelMap->GetWidth(), tempPixelMap->GetHeight());
310     RSRect dstRect = RSRect(0, 0, tempPixelMap->GetWidth(), tempPixelMap->GetHeight());
311     recordingCanvas->AttachBrush(imageBrush_);
312     recordingCanvas->DrawPixelMapRect(tempPixelMap, srcRect, dstRect, sampleOptions_);
313     recordingCanvas->DetachBrush();
314 }
315 #endif
316 
ToDataURL(const std::string & type,const double quality)317 std::string CanvasPaintMethod::ToDataURL(const std::string& type, const double quality)
318 {
319 #ifndef ACE_UNITTEST
320     auto host = frameNode_.Upgrade();
321     CHECK_NULL_RETURN(host, UNSUPPORTED);
322     auto renderContext = host->GetRenderContext();
323     CHECK_NULL_RETURN(renderContext, UNSUPPORTED);
324     std::string mimeType = GetMimeType(type);
325     // Quality needs to be between 0.0 and 1.0 for MimeType jpeg and webp
326     double qua = GetQuality(mimeType, quality);
327     double width = lastLayoutSize_.Width();
328     double height = lastLayoutSize_.Height();
329     auto imageInfo = SkImageInfo::Make(width, height, SkColorType::kBGRA_8888_SkColorType,
330         (mimeType == IMAGE_JPEG) ? SkAlphaType::kOpaque_SkAlphaType : SkAlphaType::kUnpremul_SkAlphaType);
331     RSBitmapFormat format { RSColorType::COLORTYPE_BGRA_8888,
332         (mimeType == IMAGE_JPEG) ? RSAlphaType::ALPHATYPE_OPAQUE : RSAlphaType::ALPHATYPE_UNPREMUL };
333     RSBitmap tempCache;
334     tempCache.Build(width, height, format);
335     RSBitmap currentBitmap;
336     if (!DrawBitmap(renderContext, currentBitmap)) {
337         return UNSUPPORTED;
338     }
339     bool success = currentBitmap.GetPixmap().ScalePixels(
340         tempCache.GetPixmap(), RSSamplingOptions(RSCubicResampler { 1 / 3.0f, 1 / 3.0f }));
341     CHECK_NULL_RETURN(success, UNSUPPORTED);
342     RSPixmap rsSrc = tempCache.GetPixmap();
343     SkPixmap src { imageInfo, rsSrc.GetAddr(), rsSrc.GetRowBytes() };
344     SkDynamicMemoryWStream dst;
345     success = EncodeImage(mimeType, qua, src, dst);
346     CHECK_NULL_RETURN(success, UNSUPPORTED);
347     auto result = dst.detachAsData();
348     CHECK_NULL_RETURN(result, UNSUPPORTED);
349     size_t len = SkBase64::Encode(result->data(), result->size(), nullptr);
350     if (len > MAX_LENGTH) {
351         return UNSUPPORTED;
352     }
353     SkString info(len);
354     SkBase64::Encode(result->data(), result->size(), info.writable_str());
355     return std::string(URL_PREFIX).append(mimeType).append(URL_SYMBOL).append(info.c_str());
356 #else
357     return UNSUPPORTED;
358 #endif
359 }
360 
DrawBitmap(RefPtr<RenderContext> renderContext,RSBitmap & currentBitmap)361 bool CanvasPaintMethod::DrawBitmap(RefPtr<RenderContext> renderContext, RSBitmap& currentBitmap)
362 {
363 #ifndef ACE_UNITTEST
364     auto recordingCanvas = std::static_pointer_cast<RSRecordingCanvas>(rsCanvas_);
365     CHECK_NULL_RETURN(recordingCanvas, false);
366     auto rosenRenderContext = AceType::DynamicCast<RosenRenderContext>(renderContext);
367     CHECK_NULL_RETURN(rosenRenderContext, false);
368     auto drawCmdList = recordingCanvas->GetDrawCmdList();
369     CHECK_NULL_RETURN(drawCmdList, false);
370     bool res = rosenRenderContext->GetBitmap(currentBitmap, drawCmdList);
371     if (res) {
372         return true;
373     }
374     if (drawCmdList->IsEmpty()) {
375         return false;
376     }
377     currentBitmap.Free();
378     RSBitmapFormat format;
379     if (Container::GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_FOURTEEN)) {
380         format = { RSColorType::COLORTYPE_BGRA_8888, RSAlphaType::ALPHATYPE_PREMUL };
381     } else {
382         format = { RSColorType::COLORTYPE_BGRA_8888, RSAlphaType::ALPHATYPE_OPAQUE };
383     }
384     currentBitmap.Build(lastLayoutSize_.Width(), lastLayoutSize_.Height(), format);
385 
386     RSCanvas currentCanvas;
387     currentCanvas.Bind(currentBitmap);
388     drawCmdList->Playback(currentCanvas);
389     return true;
390 #else
391     return false;
392 #endif
393 }
394 
GetJsonData(const std::string & path)395 std::string CanvasPaintMethod::GetJsonData(const std::string& path)
396 {
397 #ifndef ACE_UNITTEST
398     AssetImageLoader imageLoader;
399     return imageLoader.LoadJsonData(path, context_);
400 #else
401     return "";
402 #endif
403 }
404 
Reset()405 void CanvasPaintMethod::Reset()
406 {
407     ResetStates();
408     CHECK_NULL_VOID(rsCanvas_);
409     if (rsCanvas_->GetSaveCount() >= DEFAULT_SAVE_COUNT) {
410         rsCanvas_->RestoreToCount(0);
411     }
412     rsCanvas_->ResetMatrix();
413     rsCanvas_->Clear(RSColor::COLOR_TRANSPARENT);
414     rsCanvas_->Save();
415 }
416 #ifndef ACE_UNITTEST
ConvertTxtStyle(const TextStyle & textStyle,Rosen::TextStyle & txtStyle)417 void CanvasPaintMethod::ConvertTxtStyle(const TextStyle& textStyle, Rosen::TextStyle& txtStyle)
418 {
419     Constants::ConvertTxtStyle(textStyle, context_, txtStyle);
420 }
421 #endif
422 
GetDumpInfo()423 std::string CanvasPaintMethod::GetDumpInfo()
424 {
425     CHECK_NULL_RETURN(rsCanvas_, "Canvas is nullptr");
426     // translate
427     std::string trans = "TRANS: " + std::to_string(rsCanvas_->GetTotalMatrix().Get(RSMatrix::TRANS_X)) + ", " +
428                         std::to_string(rsCanvas_->GetTotalMatrix().Get(RSMatrix::TRANS_Y)) + "; ";
429     // scale
430     std::string scale = "SCALE: " + std::to_string(rsCanvas_->GetTotalMatrix().Get(RSMatrix::SCALE_X)) + ", " +
431                         std::to_string(rsCanvas_->GetTotalMatrix().Get(RSMatrix::SCALE_Y)) + "; ";
432     // rotate
433     std::string skew = "SKEW: " + std::to_string(rsCanvas_->GetTotalMatrix().Get(RSMatrix::SKEW_X)) + ", " +
434                        std::to_string(rsCanvas_->GetTotalMatrix().Get(RSMatrix::SKEW_Y)) + "; ";
435     return trans.append(scale).append(skew);
436 }
437 
SetHostCustomNodeName()438 void CanvasPaintMethod::SetHostCustomNodeName()
439 {
440     auto frameNode = frameNode_.Upgrade();
441     CHECK_NULL_VOID(frameNode);
442     auto customNode = frameNode->GetParentCustomNode();
443     CHECK_NULL_VOID(customNode);
444     customNodeName_ = customNode->GetJSViewName();
445 }
446 
GetSimplifyDumpInfo(std::unique_ptr<JsonValue> & json)447 void CanvasPaintMethod::GetSimplifyDumpInfo(std::unique_ptr<JsonValue>& json)
448 {
449     CHECK_NULL_VOID(rsCanvas_);
450     auto matrix = rsCanvas_->GetTotalMatrix();
451     json->Put("Trans",
452         (std::to_string(matrix.Get(RSMatrix::TRANS_X)) + "," + std::to_string(matrix.Get(RSMatrix::TRANS_Y))).c_str());
453     json->Put("Scale",
454         (std::to_string(matrix.Get(RSMatrix::SCALE_X)) + "," + std::to_string(matrix.Get(RSMatrix::SCALE_Y))).c_str());
455     json->Put("Skew",
456         (std::to_string(matrix.Get(RSMatrix::SKEW_X)) + "," + std::to_string(matrix.Get(RSMatrix::SKEW_Y))).c_str());
457 }
458 } // namespace OHOS::Ace::NG
459