1 /*
2 * Copyright (c) 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 "rs_window_keyframe_buffer.h"
17 #include <algorithm>
18 #include "draw/brush.h"
19 #include "draw/surface.h"
20 #include "platform/common/rs_log.h"
21 #include "pipeline/render_thread/rs_uni_render_thread.h"
22 #include "rs_trace.h"
23
24 namespace OHOS::Rosen {
25
RSWindowKeyframeBuffer(DrawableV2::RSCanvasRenderNodeDrawable & drawable)26 RSWindowKeyframeBuffer::RSWindowKeyframeBuffer(DrawableV2::RSCanvasRenderNodeDrawable& drawable)
27 : canvasNodeDrawable_(drawable)
28 {
29 }
30
NeedDrawWindowKeyFrame(const std::unique_ptr<RSRenderParams> & params)31 bool RSWindowKeyframeBuffer::NeedDrawWindowKeyFrame(const std::unique_ptr<RSRenderParams>& params)
32 {
33 if (UNLIKELY(params == nullptr)) {
34 RS_LOGE("RSWindowKeyframeBuffer::NeedDrawWindowKeyFrame params is nullptr");
35 return false;
36 }
37
38 if (LIKELY(!params->IsWindowKeyFrameEnabled())) {
39 cachedOffscreenImg_ = nullptr;
40 preCachedOffscreenImg_ = nullptr;
41 return false;
42 }
43
44 return true;
45 }
46
OnDraw(Drawing::Canvas & canvas,const RSRenderParams & params)47 bool RSWindowKeyframeBuffer::OnDraw(Drawing::Canvas& canvas, const RSRenderParams& params)
48 {
49 auto& uniParams = RSUniRenderThread::Instance().GetRSRenderThreadParams();
50 if (UNLIKELY(uniParams == nullptr)) {
51 RS_LOGE("RSWindowKeyframeBuffer::DrawOffscreenToBuffer uniParams is nullptr");
52 return false;
53 }
54
55 auto curCanvas = static_cast<RSPaintFilterCanvas*>(&canvas);
56 auto curSurface = curCanvas->GetSurface();
57 if (UNLIKELY(curSurface == nullptr)) {
58 RS_LOGE("RSWindowKeyframeBuffer::DrawOffscreenToBuffer surface is nullptr");
59 return false;
60 }
61
62 auto bounds = params.GetFrameRect();
63 auto cacheSurface = curSurface->MakeSurface(bounds.GetWidth(), bounds.GetHeight());
64 if (UNLIKELY(cacheSurface == nullptr)) {
65 RS_LOGE("RSWindowKeyframeBuffer::DrawOffscreenToBuffer make surface failed");
66 return false;
67 }
68
69 auto cacheCanvas = std::make_shared<RSPaintFilterCanvas>(cacheSurface.get());
70 if (UNLIKELY(cacheCanvas == nullptr)) {
71 RS_LOGE("RSWindowKeyframeBuffer::DrawOffscreenToBuffer make canvas failed");
72 return false;
73 }
74
75 // Copy HDR properties into offscreen canvas
76 cacheCanvas->CopyHDRConfiguration(*curCanvas);
77
78 // Copy current canvas properties into offscreen canvas
79 cacheCanvas->CopyConfigurationToOffscreenCanvas(*curCanvas);
80 cacheCanvas->SetDisableFilterCache(true);
81 RSAutoCanvasRestore acr(cacheCanvas, RSPaintFilterCanvas::SaveType::kCanvasAndAlpha);
82 cacheCanvas->Clear(Drawing::Color::COLOR_TRANSPARENT);
83
84 bool isOpDropped = uniParams->IsOpDropped();
85 uniParams->SetOpDropped(false); // temporarily close partial render
86 canvasNodeDrawable_.DrawableV2::RSCanvasRenderNodeDrawable::OnDraw(*cacheCanvas);
87 uniParams->SetOpDropped(isOpDropped);
88 params.ApplyAlphaAndMatrixToCanvas(*curCanvas, true);
89
90 return DrawOffscreenImgToBuffer(canvas, params, bounds, cacheSurface);
91 }
92
DrawOffscreenImgToBuffer(Drawing::Canvas & canvas,const RSRenderParams & params,const Drawing::Rect & bounds,std::shared_ptr<Drawing::Surface> & surface)93 bool RSWindowKeyframeBuffer::DrawOffscreenImgToBuffer(Drawing::Canvas& canvas, const RSRenderParams& params,
94 const Drawing::Rect& bounds, std::shared_ptr<Drawing::Surface>& surface)
95 {
96 // cache and draw snapshot of offscreen canvas onto target canvas
97 if (params.GetNeedSwapBuffer()) {
98 std::swap(cachedOffscreenImg_, preCachedOffscreenImg_);
99 }
100
101 cachedOffscreenImg_ = surface->GetImageSnapshot();
102 if (cachedOffscreenImg_ == nullptr) {
103 RS_LOGE("RSWindowKeyframeBuffer::DrawOffscreenToBuffer get snapshot nullptr");
104 return false;
105 }
106
107 if (preCachedOffscreenImg_ == nullptr && params.GetNeedSwapBuffer()) {
108 preCachedOffscreenImg_ = cachedOffscreenImg_;
109 }
110
111 Drawing::Brush paint;
112 paint.SetAntiAlias(true);
113 canvas.AttachBrush(paint);
114 auto samplingOptions = Drawing::SamplingOptions(Drawing::FilterMode::NEAREST, Drawing::MipmapMode::NONE);
115 Drawing::Rect cacheBounds = params.GetCacheNodeFrameRect();
116 if (!cacheBounds.IsEmpty()) {
117 // keep aspect ratio for scale
118 Drawing::Rect srcRect;
119 srcRect.left_ = 0;
120 srcRect.top_ = 0;
121 if (bounds.GetWidth() * cacheBounds.GetHeight() / bounds.GetHeight() >= cacheBounds.GetWidth()) {
122 srcRect.right_ = bounds.GetHeight() * cacheBounds.GetWidth() / cacheBounds.GetHeight();
123 srcRect.bottom_ = bounds.GetHeight();
124 } else {
125 srcRect.right_ = bounds.GetWidth();
126 srcRect.bottom_ = bounds.GetWidth() * cacheBounds.GetHeight() / cacheBounds.GetWidth();
127 }
128 canvas.DrawImageRect(*cachedOffscreenImg_, srcRect, cacheBounds, samplingOptions);
129 } else {
130 canvas.DrawImageRect(*cachedOffscreenImg_, bounds, samplingOptions);
131 }
132 canvas.DetachBrush();
133
134 return true;
135 }
136
DrawOffscreenBuffer(RSPaintFilterCanvas & canvas,const Drawing::Rect & bounds,float alpha,bool isFreezed)137 bool RSWindowKeyframeBuffer::DrawOffscreenBuffer(
138 RSPaintFilterCanvas& canvas, const Drawing::Rect& bounds, float alpha, bool isFreezed)
139 {
140 if (preCachedOffscreenImg_ == nullptr) {
141 RS_LOGE("RSWindowKeyframeBuffer::DrawOffscreenToBuffer preCachedOffscreenImg_ is nullptr");
142 return false;
143 }
144
145 float rootWidth = 0.0f;
146 float rootHeight = 0.0f;
147 const auto& params = canvasNodeDrawable_.GetRenderParams();
148 if (params != nullptr) {
149 rootWidth = params->GetFrameRect().GetWidth();
150 rootHeight = params->GetFrameRect().GetHeight();
151 }
152
153 RS_TRACE_NAME_FMT("DrawCacheNode rootNode:(%.0f, %.0f), canvasNode:(%.0f, %.0f) alpha:%.2f, freeze:%d",
154 rootWidth, rootHeight, bounds.GetWidth(), bounds.GetHeight(), alpha, isFreezed);
155
156 RSAutoCanvasRestore acr(&canvas);
157 Drawing::Brush paint;
158 paint.SetAntiAlias(true);
159 canvas.AttachBrush(paint);
160 auto samplingOptions = Drawing::SamplingOptions(Drawing::FilterMode::NEAREST, Drawing::MipmapMode::NONE);
161
162 Drawing::Rect srcRect;
163 srcRect.left_ = 0;
164 srcRect.top_ = 0;
165 int imgW = preCachedOffscreenImg_->GetWidth();
166 int imgH = preCachedOffscreenImg_->GetHeight();
167 if (imgW <= 0 || imgH <= 0 || bounds.IsEmpty()) {
168 RS_LOGE("RSWindowKeyframeBuffer::DrawOffscreenToBuffer preCachedOffscreenImg size or bounds is invalid");
169 return false;
170 }
171
172 // keep aspect ratio for scale
173 if (imgW * bounds.GetHeight() / imgH >= bounds.GetWidth()) {
174 srcRect.right_ = imgH * bounds.GetWidth() / bounds.GetHeight();
175 srcRect.bottom_ = imgH;
176 } else {
177 srcRect.right_ = imgW;
178 srcRect.bottom_ = imgW * bounds.GetHeight() / bounds.GetWidth();
179 }
180 canvas.DrawImageRect(*preCachedOffscreenImg_, srcRect, bounds, samplingOptions,
181 Drawing::SrcRectConstraint::STRICT_SRC_RECT_CONSTRAINT);
182 canvas.DetachBrush();
183
184 return true;
185 }
186
187 } // namespace OHOS::Rosen
188