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 "surface_draw.h"
17#include <algorithm>
18#include <hitrace_meter.h>
19#include <surface.h>
20#include <transaction/rs_interfaces.h>
21#include <ui/rs_surface_extractor.h>
22
23#include "image/bitmap.h"
24#include "image_source.h"
25#include "image_type.h"
26#include "image_utils.h"
27#include "render/rs_pixel_map_util.h"
28#include "surface_capture_future.h"
29#include "window_manager_hilog.h"
30
31namespace OHOS {
32namespace Rosen {
33namespace {
34constexpr HiviewDFX::HiLogLabel LABEL = {LOG_CORE, HILOG_DOMAIN_WINDOW, "SurfaceDraw"};
35constexpr uint32_t IMAGE_BYTES_STRIDE = 4;
36} // namespace
37
38bool SurfaceDraw::DrawImage(std::shared_ptr<RSSurfaceNode> surfaceNode, int32_t bufferWidth,
39    int32_t bufferHeight, const std::string& imagePath)
40{
41    sptr<OHOS::Surface> layer = GetLayer(surfaceNode);
42    if (layer == nullptr) {
43        WLOGFE("layer is nullptr");
44        return false;
45    }
46    sptr<OHOS::SurfaceBuffer> buffer = GetSurfaceBuffer(layer, bufferWidth, bufferHeight);
47    if (buffer == nullptr || buffer->GetVirAddr() == nullptr) {
48        return false;
49    }
50    auto addr = static_cast<uint8_t *>(buffer->GetVirAddr());
51    if (!DoDraw(addr, buffer->GetWidth(), buffer->GetHeight(), imagePath)) {
52        WLOGE("draw window pixel failed");
53        return false;
54    }
55    OHOS::BufferFlushConfig flushConfig = {
56        .damage = {
57            .w = buffer->GetWidth(),
58            .h = buffer->GetHeight(),
59        },
60    };
61    OHOS::SurfaceError ret = layer->FlushBuffer(buffer, -1, flushConfig);
62    if (ret != OHOS::SurfaceError::SURFACE_ERROR_OK) {
63        WLOGFE("draw pointer FlushBuffer ret:%{public}s", SurfaceErrorStr(ret).c_str());
64        return false;
65    }
66    return true;
67}
68
69bool SurfaceDraw::DrawImage(std::shared_ptr<RSSurfaceNode> surfaceNode, int32_t bufferWidth,
70    int32_t bufferHeight, std::shared_ptr<Media::PixelMap> pixelMap)
71{
72    sptr<OHOS::Surface> layer = GetLayer(surfaceNode);
73    if (layer == nullptr) {
74        WLOGFE("layer is nullptr");
75        return false;
76    }
77    sptr<OHOS::SurfaceBuffer> buffer = GetSurfaceBuffer(layer, bufferWidth, bufferHeight);
78    if (buffer == nullptr || buffer->GetVirAddr() == nullptr) {
79        return false;
80    }
81    auto addr = static_cast<uint8_t *>(buffer->GetVirAddr());
82    if (!DoDraw(addr, buffer->GetWidth(), buffer->GetHeight(), pixelMap)) {
83        WLOGE("draw window pixel failed");
84        return false;
85    }
86    OHOS::BufferFlushConfig flushConfig = {
87        .damage = {
88            .w = buffer->GetWidth(),
89            .h = buffer->GetHeight(),
90        },
91    };
92    OHOS::SurfaceError ret = layer->FlushBuffer(buffer, -1, flushConfig);
93    if (ret != OHOS::SurfaceError::SURFACE_ERROR_OK) {
94        WLOGFE("draw pointer FlushBuffer ret:%{public}s", SurfaceErrorStr(ret).c_str());
95        return false;
96    }
97    return true;
98}
99
100bool SurfaceDraw::DrawColor(std::shared_ptr<RSSurfaceNode> surfaceNode, int32_t bufferWidth,
101    int32_t bufferHeight, uint32_t color)
102{
103    sptr<OHOS::Surface> layer = GetLayer(surfaceNode);
104    if (layer == nullptr) {
105        WLOGFE("layer is nullptr");
106        return false;
107    }
108    sptr<OHOS::SurfaceBuffer> buffer = GetSurfaceBuffer(layer, bufferWidth, bufferHeight);
109    if (buffer == nullptr || buffer->GetVirAddr() == nullptr) {
110        return false;
111    }
112    auto addr = static_cast<uint8_t *>(buffer->GetVirAddr());
113    if (!DoDraw(addr, buffer->GetWidth(), buffer->GetHeight(), color)) {
114        WLOGE("draw window color failed");
115        return false;
116    }
117    OHOS::BufferFlushConfig flushConfig = {
118        .damage = {
119            .w = buffer->GetWidth(),
120            .h = buffer->GetHeight(),
121        },
122    };
123    OHOS::SurfaceError ret = layer->FlushBuffer(buffer, -1, flushConfig);
124    if (ret != OHOS::SurfaceError::SURFACE_ERROR_OK) {
125        WLOGFE("draw pointer FlushBuffer ret:%{public}s", SurfaceErrorStr(ret).c_str());
126        return false;
127    }
128    return true;
129}
130
131sptr<OHOS::Surface> SurfaceDraw::GetLayer(std::shared_ptr<RSSurfaceNode> surfaceNode)
132{
133    if (surfaceNode == nullptr) {
134        return nullptr;
135    }
136    return surfaceNode->GetSurface();
137}
138
139sptr<OHOS::SurfaceBuffer> SurfaceDraw::GetSurfaceBuffer(sptr<OHOS::Surface> layer,
140    int32_t bufferWidth, int32_t bufferHeight)
141{
142    sptr<OHOS::SurfaceBuffer> buffer;
143    int32_t releaseFence = 0;
144    OHOS::BufferRequestConfig config = {
145        .width = bufferWidth,
146        .height = bufferHeight,
147        .strideAlignment = 0x8,
148        .format = GRAPHIC_PIXEL_FMT_RGBA_8888,
149        .usage = BUFFER_USAGE_CPU_READ | BUFFER_USAGE_CPU_WRITE | BUFFER_USAGE_MEM_DMA,
150    };
151
152    OHOS::SurfaceError ret = layer->RequestBuffer(buffer, releaseFence, config);
153    if (ret != OHOS::SURFACE_ERROR_OK) {
154        WLOGFE("request buffer ret:%{public}s", SurfaceErrorStr(ret).c_str());
155        return nullptr;
156    }
157    return buffer;
158}
159
160std::unique_ptr<OHOS::Media::PixelMap> SurfaceDraw::DecodeImageToPixelMap(const std::string& imagePath)
161{
162    OHOS::Media::SourceOptions opts;
163    opts.formatHint = "image/png";
164    uint32_t ret = 0;
165    auto imageSource = OHOS::Media::ImageSource::CreateImageSource(imagePath, opts, ret);
166    if (imageSource == nullptr) {
167        WLOGFE("invalid image path.");
168        return nullptr;
169    }
170    std::set<std::string> formats;
171    ret = imageSource->GetSupportedFormats(formats);
172    WLOGFD("get supported format ret:%{public}u", ret);
173
174    OHOS::Media::DecodeOptions decodeOpts;
175    std::unique_ptr<OHOS::Media::PixelMap> pixelMap = imageSource->CreatePixelMap(decodeOpts, ret);
176    if (pixelMap == nullptr) {
177        WLOGFE("pixelMap is nullptr");
178    }
179    return pixelMap;
180}
181
182void SurfaceDraw::DrawPixelmap(Drawing::Canvas& canvas, const std::string& imagePath)
183{
184    std::unique_ptr<OHOS::Media::PixelMap> pixelmap = DecodeImageToPixelMap(imagePath);
185    if (pixelmap == nullptr) {
186        WLOGFE("drawing pixel map is nullptr");
187        return;
188    }
189    Drawing::Pen pen;
190    pen.SetAntiAlias(true);
191    pen.SetColor(Drawing::Color::COLOR_BLUE);
192    Drawing::scalar penWidth = 1;
193    pen.SetWidth(penWidth);
194    canvas.AttachPen(pen);
195    RSPixelMapUtil::DrawPixelMap(canvas, *pixelmap, 0, 0);
196}
197
198bool SurfaceDraw::DoDraw(uint8_t* addr, uint32_t width, uint32_t height, const std::string& imagePath)
199{
200    Drawing::Bitmap bitmap;
201    Drawing::BitmapFormat format { Drawing::ColorType::COLORTYPE_RGBA_8888,
202        Drawing::AlphaType::ALPHATYPE_OPAQUE };
203    bitmap.Build(width, height, format);
204    Drawing::Canvas canvas;
205    canvas.Bind(bitmap);
206    canvas.Clear(Drawing::Color::COLOR_TRANSPARENT);
207    DrawPixelmap(canvas, imagePath);
208    uint32_t addrSize = width * height * IMAGE_BYTES_STRIDE;
209    errno_t ret = memcpy_s(addr, addrSize, bitmap.GetPixels(), addrSize);
210    if (ret != EOK) {
211        WLOGFE("draw failed");
212        return false;
213    }
214    return true;
215}
216
217bool SurfaceDraw::DoDraw(uint8_t* addr, uint32_t width, uint32_t height, std::shared_ptr<Media::PixelMap> pixelMap)
218{
219    Drawing::Bitmap bitmap;
220    Drawing::Canvas canvas;
221    Drawing::BitmapFormat format { Drawing::ColorType::COLORTYPE_RGBA_8888, Drawing::AlphaType::ALPHATYPE_OPAQUE };
222    bitmap.Build(width, height, format);
223    canvas.Bind(bitmap);
224    canvas.Clear(Drawing::Color::COLOR_TRANSPARENT);
225
226    Drawing::Image image;
227    Drawing::Bitmap imageBitmap;
228    Drawing::SamplingOptions sampling = Drawing::SamplingOptions(Drawing::FilterMode::NEAREST,
229        Drawing::MipmapMode::NEAREST);
230    imageBitmap.Build(pixelMap->GetWidth(), pixelMap->GetHeight(), format);
231    imageBitmap.SetPixels(const_cast<uint8_t*>(pixelMap->GetPixels()));
232    image.BuildFromBitmap(imageBitmap);
233
234    Drawing::Rect dst(0, 0, width, height);
235    Drawing::Rect src(0, 0, pixelMap->GetWidth(), pixelMap->GetHeight());
236    canvas.DrawImageRect(image, src, dst, sampling);
237
238    uint32_t addrSize = width * height * IMAGE_BYTES_STRIDE;
239    errno_t ret = memcpy_s(addr, addrSize, bitmap.GetPixels(), addrSize);
240    if (ret != EOK) {
241        WLOGFE("draw failed");
242        return false;
243    }
244    return true;
245}
246
247bool SurfaceDraw::DoDraw(uint8_t* addr, uint32_t width, uint32_t height, uint32_t color)
248{
249    Drawing::Bitmap bitmap;
250    Drawing::BitmapFormat format { Drawing::ColorType::COLORTYPE_RGBA_8888,
251        Drawing::AlphaType::ALPHATYPE_OPAQUE };
252    bitmap.Build(width, height, format);
253    Drawing::Canvas canvas;
254    canvas.Bind(bitmap);
255    canvas.Clear(color);
256
257    uint32_t addrSize = width * height * IMAGE_BYTES_STRIDE;
258    errno_t ret = memcpy_s(addr, addrSize, bitmap.GetPixels(), addrSize);
259    if (ret != EOK) {
260        WLOGFE("draw failed");
261        return false;
262    }
263    return true;
264}
265
266bool SurfaceDraw::DrawImageRect(std::shared_ptr<RSSurfaceNode> surfaceNode, Rect rect,
267    std::shared_ptr<Media::PixelMap> pixelMap, uint32_t color, bool fillWindow)
268{
269    int32_t winHeight = static_cast<int32_t>(rect.height_);
270    int32_t winWidth = static_cast<int32_t>(rect.width_);
271    sptr<OHOS::Surface> layer = GetLayer(surfaceNode);
272    if (layer == nullptr) {
273        WLOGFE("layer is nullptr");
274        return false;
275    }
276    sptr<OHOS::SurfaceBuffer> buffer = GetSurfaceBuffer(layer, winWidth, winHeight);
277    if (buffer == nullptr || buffer->GetVirAddr() == nullptr) {
278        return false;
279    }
280    if (!DoDrawImageRect(buffer, rect, pixelMap, color, fillWindow)) {
281        WLOGE("draw image rect failed.");
282        return false;
283    }
284    OHOS::BufferFlushConfig flushConfig = {
285        .damage = {
286            .w = buffer->GetWidth(),
287            .h = buffer->GetHeight(),
288        },
289    };
290    OHOS::SurfaceError surfaceRet = layer->FlushBuffer(buffer, -1, flushConfig);
291    if (surfaceRet != OHOS::SurfaceError::SURFACE_ERROR_OK) {
292        WLOGFE("draw pointer FlushBuffer ret:%{public}s", SurfaceErrorStr(surfaceRet).c_str());
293        return false;
294    }
295    return true;
296}
297
298bool SurfaceDraw::DoDrawImageRect(sptr<OHOS::SurfaceBuffer> buffer, const Rect& rect,
299    std::shared_ptr<Media::PixelMap> pixelMap, uint32_t color, bool fillWindow)
300{
301    int32_t winWidth = static_cast<int32_t>(rect.width_);
302    int32_t winHeight = static_cast<int32_t>(rect.height_);
303    // actual width of the surface buffer after alignment
304    auto bufferStride = buffer->GetStride();
305    int32_t alignWidth = bufferStride / static_cast<int32_t>(IMAGE_BYTES_STRIDE);
306    if (pixelMap == nullptr) {
307        WLOGFE("drawing pixel map failed, because pixel map is nullptr.");
308        return false;
309    }
310    if (pixelMap->GetHeight() <= 0 || pixelMap->GetWidth() <= 0 || winWidth <= 0 || winHeight <= 0) {
311        WLOGFE("drawing pixel map failed, because width or height is invalid.");
312        return false;
313    }
314    Drawing::Bitmap bitmap;
315    Drawing::BitmapFormat format { Drawing::ColorType::COLORTYPE_RGBA_8888,
316        Drawing::AlphaType::ALPHATYPE_OPAQUE };
317    bitmap.Build(alignWidth, winHeight, format);
318    Drawing::Canvas canvas;
319    canvas.Bind(bitmap);
320    canvas.Clear(color);
321    float xAxis = static_cast<float>(winWidth) / pixelMap->GetWidth();
322    float yAxis = static_cast<float>(winHeight) / pixelMap->GetHeight();
323    float axis = std::min(xAxis, yAxis);
324    int scaledPixelMapW = pixelMap->GetWidth();
325    int scaledPixelMapH = pixelMap->GetHeight();
326    if (axis < 1.0) {
327        canvas.Scale(axis, axis);
328        scaledPixelMapW = scaledPixelMapW * axis;
329        scaledPixelMapH = scaledPixelMapH * axis;
330    } else if (fillWindow) {
331        // scale snapshot to whole window
332        canvas.Scale(xAxis, yAxis);
333        scaledPixelMapW = winWidth;
334        scaledPixelMapH = winHeight;
335    }
336    int left = (winWidth - scaledPixelMapW) / 2; // 2 is the left and right boundaries of the win
337    int top = (winHeight - scaledPixelMapH) / 2; // 2 is the top and bottom boundaries of the win
338    WLOGFD("pixelMap width: %{public}d win height: %{public}d left:%{public}d top:%{public}d.",
339        pixelMap->GetWidth(), pixelMap->GetHeight(), left, top);
340    RSPixelMapUtil::DrawPixelMap(canvas, *pixelMap, left, top);
341    // bufferSize is actual size of the surface buffer after alignment
342    int32_t bufferSize = bufferStride * winHeight;
343    uint8_t* bitmapAddr = static_cast<uint8_t*>(bitmap.GetPixels());
344    auto addr = static_cast<uint8_t *>(buffer->GetVirAddr());
345    errno_t ret = memcpy_s(addr, bufferSize, bitmapAddr, bufferSize);
346    if (ret != EOK) {
347        WLOGFE("draw image rect failed, because copy bitmap to buffer failed.");
348        return false;
349    }
350    return true;
351}
352
353bool SurfaceDraw::GetSurfaceSnapshot(const std::shared_ptr<RSSurfaceNode> surfaceNode,
354    std::shared_ptr<Media::PixelMap>&pixelMap, int32_t timeoutMs, float scaleW, float scaleH)
355{
356    if (surfaceNode == nullptr) {
357        WLOGFE("surfaceNode is null");
358        return false;
359    }
360    HITRACE_METER_FMT(HITRACE_TAG_WINDOW_MANAGER, "SurfaceDraw:GetSurfaceSnapshot(%llu)", surfaceNode->GetId());
361    std::shared_ptr<SurfaceCaptureFuture> callback = std::make_shared<SurfaceCaptureFuture>();
362    RSSurfaceCaptureConfig config = {
363        .scaleX = scaleW,
364        .scaleY = scaleH,
365    };
366    if (RSInterfaces::GetInstance().TakeSurfaceCapture(surfaceNode, callback, config)) {
367        pixelMap = callback->GetResult(timeoutMs); // get pixelmap time out ms
368    }
369    if (pixelMap == nullptr) {
370        WLOGE("get surface snapshot timeout.");
371        return false;
372    }
373    return true;
374}
375
376bool SurfaceDraw::DrawMasking(std::shared_ptr<RSSurfaceNode> surfaceNode, Rect screenRect,
377    Rect transparentRect)
378{
379    int32_t screenHeight = static_cast<int32_t>(screenRect.height_);
380    int32_t screenWidth = static_cast<int32_t>(screenRect.width_);
381    int32_t transparentHeight = static_cast<int32_t>(transparentRect.height_);
382    int32_t transparentWidth = static_cast<int32_t>(transparentRect.width_);
383    sptr<OHOS::Surface> layer = GetLayer(surfaceNode);
384    if (layer == nullptr) {
385        WLOGFE("layer is nullptr");
386        return false;
387    }
388    sptr<OHOS::SurfaceBuffer> buffer = GetSurfaceBuffer(layer, screenWidth, screenHeight);
389    if (buffer == nullptr || buffer->GetVirAddr() == nullptr) {
390        return false;
391    }
392    auto addr = static_cast<uint8_t *>(buffer->GetVirAddr());
393    Drawing::Bitmap fullbitmap;
394    Drawing::BitmapFormat fullBitmapFormat { Drawing::ColorType::COLORTYPE_RGBA_8888,
395        Drawing::AlphaType::ALPHATYPE_OPAQUE };
396    fullbitmap.Build(screenWidth, screenHeight, fullBitmapFormat);
397    Drawing::Canvas canvas;
398    canvas.Bind(fullbitmap);
399    canvas.Clear(0xFF000000);
400    Drawing::Bitmap transBitmap;
401    Drawing::BitmapFormat transBitmapFormat { Drawing::ColorType::COLORTYPE_RGBA_8888,
402        Drawing::AlphaType::ALPHATYPE_OPAQUE };
403    transBitmap.Build(transparentWidth, transparentHeight, transBitmapFormat);
404    transBitmap.ClearWithColor(0);
405    canvas.DrawBitmap(transBitmap, static_cast<Drawing::scalar>(transparentRect.posX_),
406        static_cast<Drawing::scalar>(transparentRect.posY_));
407
408    uint32_t addrSize = static_cast<uint32_t>(screenWidth * screenHeight * IMAGE_BYTES_STRIDE);
409    errno_t ret = memcpy_s(addr, addrSize, fullbitmap.GetPixels(), addrSize);
410    if (ret != EOK) {
411        WLOGFE("draw failed");
412        return false;
413    }
414    OHOS::BufferFlushConfig flushConfig = {
415        .damage = {
416            .w = buffer->GetWidth(),
417            .h = buffer->GetHeight(),
418        },
419    };
420    OHOS::SurfaceError surfaceRet = layer->FlushBuffer(buffer, -1, flushConfig);
421    if (surfaceRet != OHOS::SurfaceError::SURFACE_ERROR_OK) {
422        WLOGFE("draw masking FlushBuffer ret:%{public}s", SurfaceErrorStr(surfaceRet).c_str());
423        return false;
424    }
425    return true;
426}
427} // Rosen
428} // OHOS