1/*
2 * Copyright (c) 2023 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 "VirtualScreenImpl.h"
17
18#include "draw/draw_utils.h"
19#include "hal_tick.h"
20#include "image_decode_ability.h"
21
22#define boolean jpegboolean
23#include "jpeglib.h"
24#undef boolean
25#include "task_manager.h"
26#include "CommandParser.h"
27#include "ModelManager.h"
28#include "PreviewerEngineLog.h"
29#include "TraceTool.h"
30
31void VirtualScreenImpl::InitAll(std::string pipeName, std::string pipePort)
32{
33    OHOS::ImageDecodeAbility& ability = OHOS::ImageDecodeAbility::GetInstance();
34    ability.SetImageDecodeAbility(OHOS::IMG_SUPPORT_BITMAP | OHOS::IMG_SUPPORT_JPEG | OHOS::IMG_SUPPORT_PNG);
35    if (CommandParser::GetInstance().GetDeviceType() == "liteWearable") {
36        ability.SetImageDecodeAbility(OHOS::IMG_SUPPORT_BITMAP);
37    }
38
39    InitPipe(pipeName, pipePort);
40    if ((!CommandParser::GetInstance().IsResolutionValid(orignalResolutionWidth)) ||
41        (!CommandParser::GetInstance().IsResolutionValid(orignalResolutionHeight))) {
42        ELOG("VirtualScreen::InitAll invalid resolution, width : %d height : %d", orignalResolutionWidth,
43             orignalResolutionHeight);
44        return;
45    }
46
47    bufferSize = orignalResolutionWidth * orignalResolutionHeight * pixelSize + headSize;
48    wholeBuffer = new(std::nothrow) uint8_t[LWS_PRE + bufferSize];
49    if (!wholeBuffer) {
50        ELOG("Memory allocation failed : wholeBuffer.");
51        return;
52    }
53    regionWholeBuffer = new(std::nothrow) uint8_t[LWS_PRE + bufferSize];
54    if (!regionWholeBuffer) {
55        ELOG("Memory allocation failed: regionWholeBuffer.");
56        return;
57    }
58    screenBuffer = wholeBuffer + LWS_PRE;
59    regionBuffer = regionWholeBuffer + LWS_PRE;
60    osBuffer = new(std::nothrow) uint8_t[bufferSize];
61    if (!osBuffer) {
62        ELOG("Memory allocation failed: osBuffer.");
63        return;
64    }
65    if (screenBuffer == nullptr) {
66        ELOG("VirtualScreen::InitAll wholeBuffer memory allocation failed");
67        return;
68    }
69    InitBuffer();
70}
71
72bool VirtualScreenImpl::IsRectValid(int32_t x1, int32_t y1, int32_t x2, int32_t y2) const
73{
74    if (x1 < 0 || y1 < 0) {
75        return false;
76    }
77
78    if (x2 >= orignalResolutionWidth || y2 >= orignalResolutionHeight) {
79        return false;
80    }
81    return true;
82}
83
84void VirtualScreenImpl::WriteRefreshRegion()
85{
86    currentPos = VERSION_POS;
87    WriteBuffer(protocolVersion);
88    WriteBuffer(regionX1);
89    WriteBuffer(regionY1);
90    regionWidth = static_cast<uint16_t>(orignalResolutionWidth);
91    WriteBuffer(regionWidth);
92    regionHeight = static_cast<uint16_t>(orignalResolutionHeight);
93    WriteBuffer(regionHeight);
94}
95
96void VirtualScreenImpl::UpdateRegion(int32_t x1, int32_t y1, int32_t x2, int32_t y2)
97{
98    regionX1 = x1;
99    regionY1 = y1;
100    regionX2 = (x2 < compressionResolutionWidth - extendPix)
101                   ? (x2 + extendPix) : (compressionResolutionWidth - 1);
102    regionY2 = (y2 < compressionResolutionHeight - extendPix)
103                   ? (y2 + extendPix) : (compressionResolutionHeight - 1);
104    regionWidth = regionX2 - regionX1 + 1;
105    regionHeight = regionY2 - regionY1 + 1;
106}
107
108void VirtualScreenImpl::InitBuffer()
109{
110    currentPos = 0;
111    WriteBuffer(headStart);
112    WriteBuffer(orignalResolutionWidth);
113    WriteBuffer(orignalResolutionHeight);
114    WriteBuffer(compressionResolutionWidth);
115    WriteBuffer(compressionResolutionHeight);
116}
117
118void VirtualScreenImpl::ScheduleBufferSend()
119{
120    if (!isChanged) {
121        return;
122    }
123
124    if (!isWebSocketConfiged) {
125        ELOG("image socket is not ready");
126        return;
127    }
128    isFrameUpdated = true;
129    if (CommandParser::GetInstance().IsRegionRefresh()) {
130        SendFullBuffer();
131    } else {
132        SendFullBuffer();
133    }
134    if (isFirstSend) {
135        ILOG("Send first buffer finish");
136        TraceTool::GetInstance().HandleTrace("Send first buffer finish");
137        isFirstSend = false;
138    }
139
140    {
141        std::lock_guard<std::mutex> guard(WebSocketServer::GetInstance().mutex);
142        if (!WebSocketServer::GetInstance().firstImageBuffer) {
143            WebSocketServer::GetInstance().firstImageBuffer = new(std::nothrow) uint8_t[LWS_PRE + bufferSize];
144            if (!WebSocketServer::GetInstance().firstImageBuffer) {
145                ELOG("Memory allocation failed: firstImageBuffer.");
146                return;
147            }
148            WebSocketServer::GetInstance().firstImagebufferSize = headSize + jpgBufferSize;
149        }
150        std::copy(regionBuffer,
151                  regionBuffer + headSize + jpgBufferSize,
152                  WebSocketServer::GetInstance().firstImageBuffer + LWS_PRE);
153    }
154
155    sendFrameCountPerMinute++;
156    isChanged = false;
157}
158
159void VirtualScreenImpl::Send(unsigned char* data, int32_t width, int32_t height)
160{
161    if (CommandParser::GetInstance().GetScreenMode() == CommandParser::ScreenMode::STATIC
162        && VirtualScreen::isOutOfSeconds) {
163        return;
164    }
165    // if websocket is config, use websocet, else use localsocket
166    VirtualScreen::RgbToJpg(data + headSize, width, height);
167    std::copy(jpgScreenBuffer, jpgScreenBuffer + jpgBufferSize, regionBuffer + headSize);
168    WebSocketServer::GetInstance().WriteData(regionBuffer, headSize + jpgBufferSize);
169    FreeJpgMemory();
170}
171
172void VirtualScreenImpl::SendFullBuffer()
173{
174    WriteRefreshRegion();
175    std::copy(screenBuffer, screenBuffer + headSize, regionBuffer);
176    Send(reinterpret_cast<unsigned char*>(screenBuffer),
177         compressionResolutionWidth,
178         compressionResolutionHeight);
179}
180
181void VirtualScreenImpl::SendRegionBuffer()
182{
183    WriteRefreshRegion();
184    std::copy(screenBuffer, screenBuffer + headSize, regionBuffer);
185    for (int i = regionY1; i <= regionY2; ++i) {
186        uint8_t* startPos = screenBuffer + (i * compressionResolutionWidth + regionX1) * jpgPix + headSize;
187        std::copy(startPos,
188                  startPos + regionWidth * jpgPix,
189                  regionBuffer + ((i - regionY1) * regionWidth) * jpgPix + headSize);
190    }
191    Send(reinterpret_cast<unsigned char*>(regionBuffer), regionWidth, regionHeight);
192}
193
194void VirtualScreenImpl::FreeJpgMemory()
195{
196    if (jpgScreenBuffer != nullptr) {
197        free(jpgScreenBuffer);
198        jpgScreenBuffer = NULL;
199    }
200}
201
202VirtualScreenImpl& VirtualScreenImpl::GetInstance()
203{
204    static VirtualScreenImpl virtualScreen;
205    BaseGfxEngine::InitGfxEngine(&virtualScreen);
206    return virtualScreen;
207}
208
209void VirtualScreenImpl::CheckBufferSend()
210{
211    VirtualScreenImpl::GetInstance().ScheduleBufferSend();
212}
213
214VirtualScreenImpl::VirtualScreenImpl()
215    : wholeBuffer(nullptr),
216      regionWholeBuffer(nullptr),
217      screenBuffer(nullptr),
218      regionBuffer(nullptr),
219      osBuffer(nullptr),
220      isChanged(false),
221      currentPos(0),
222      bufferSize(0),
223      isFirstRender(true),
224      isFirstSend(true),
225      regionX1(0),
226      regionY1(0),
227      regionX2(0),
228      regionY2(0),
229      regionWidth(0),
230      regionHeight(0),
231      bufferInfo(nullptr)
232{
233}
234
235VirtualScreenImpl::~VirtualScreenImpl()
236{
237    if (wholeBuffer != nullptr) {
238        delete [] wholeBuffer;
239        wholeBuffer = nullptr;
240        screenBuffer = nullptr;
241    }
242    FreeJpgMemory();
243    if (WebSocketServer::GetInstance().firstImageBuffer) {
244        delete [] WebSocketServer::GetInstance().firstImageBuffer;
245        WebSocketServer::GetInstance().firstImageBuffer = nullptr;
246    }
247}
248
249void VirtualScreenImpl::Flush(const OHOS::Rect& flushRect)
250{
251    if (isFirstRender) {
252        ILOG("Get first render buffer");
253        TraceTool::GetInstance().HandleTrace("Get first render buffer");
254        isFirstRender = false;
255    }
256
257    bool staticRet = VirtualScreen::JudgeStaticImage(SEND_IMG_DURATION_MS);
258    if (!staticRet) {
259        return;
260    }
261
262    for (int i = 0; i <= compressionResolutionHeight - 1; ++i) {
263        for (int j = 0; j <= compressionResolutionWidth - 1; ++j) {
264            uint8_t* curPixel = screenBuffer + (i * compressionResolutionWidth + j) * jpgPix + headSize;
265            uint8_t* osPixel = osBuffer + (i * compressionResolutionWidth + j) * pixelSize + headSize;
266            *(curPixel + redPos) = *(osPixel + bluePos);
267            *(curPixel + greenPos) = *(osPixel + greenPos);
268            *(curPixel + bluePos) = *(osPixel + redPos);
269        }
270    }
271
272    validFrameCountPerMinute++;
273    isChanged = true;
274    ScheduleBufferSend();
275}
276
277OHOS::BufferInfo* VirtualScreenImpl::GetFBBufferInfo()
278{
279    if (bufferInfo == nullptr) {
280        bufferInfo = new(std::nothrow) OHOS::BufferInfo;
281        if (!bufferInfo) {
282            ELOG("Memory allocation failed: osBuffer.");
283            return bufferInfo;
284        }
285        bufferInfo->rect = {0, 0, compressionResolutionWidth - 1, compressionResolutionHeight - 1};
286        bufferInfo->mode = OHOS::ARGB8888;
287
288        bufferInfo->color = 0x44;
289        bufferInfo->phyAddr = bufferInfo->virAddr = osBuffer + headSize;
290        // 3: Shift right 3 bits
291        bufferInfo->stride = orignalResolutionWidth * (OHOS::DrawUtils::GetPxSizeByColorMode(bufferInfo->mode) >> 3);
292        bufferInfo->width = orignalResolutionWidth;
293        bufferInfo->height = orignalResolutionHeight;
294    }
295
296    return bufferInfo;
297}
298
299uint16_t VirtualScreenImpl::GetScreenWidth()
300{
301    return orignalResolutionWidth;
302}
303
304uint16_t VirtualScreenImpl::GetScreenHeight()
305{
306    return orignalResolutionHeight;
307}
308