* Copyright (c) 2026 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "native_render.h"
#include <common/common.h>
#include <algorithm>
#include <cmath>
#include <native_image/native_image.h>
#include <poll.h>
#include <unistd.h>
#include <cerrno>
#include <cstdint>
#include <hilog/log.h>
namespace {
constexpr double ANIMATION_SPEED_INCREMENT = 0.6;
constexpr int INVALID_FD = -1;
constexpr uint32_t POLL_TIMEOUT_MS = 3000;
constexpr nfds_t NUM_POLL_FDS = 1;
constexpr int NO_FENCE = -1;
constexpr int MAX_RETRY = 3;
constexpr int SUCCESS = 0;
constexpr int FAILURE = -1;
constexpr uint8_t MAX_COLOR_VALUE = 255;
constexpr int32_t BYTES_PER_PIXEL = 4;
constexpr uint8_t ALPHA_SHIFT = 24;
constexpr uint8_t RED_SHIFT = 16;
constexpr uint8_t GREEN_SHIFT = 8;
constexpr uint8_t BLUE_SHIFT = 0;
constexpr double MAX_INTENSITY = 1.0;
constexpr double MIN_INTENSITY = 0.5;
constexpr double INTENSITY_MULTIPLIER = 2.0;
constexpr double INTENSITY_LIMIT = 1.0;
constexpr int ARROW_SIZE_DIVISOR = 2;
constexpr int STEM_WIDTH_DIVISOR = 6;
constexpr int HEAD_WIDTH_DIVISOR = 2;
constexpr int HEAD_LENGTH_DIVISOR = 3;
constexpr double HEAD_SLOPE_MULTIPLIER = 0.5;
}
OHNativeRender::~OHNativeRender()
{
if (nativeWindow_ != nullptr) {
(void)OH_NativeWindow_DestroyNativeWindow(nativeWindow_);
nativeWindow_ = nullptr;
}
}
bool OHNativeRender::SetSurfaceWidthAndHeight(OH_NativeImage* image, uint64_t surfaceId, uint64_t width, uint64_t height)
{
if (nativeWindow_ != nullptr) {
(void)OH_NativeWindow_NativeObjectUnreference(nativeWindow_);
nativeWindow_ = nullptr;
}
width_ = width;
height_ = height;
nativeWindow_ = OH_NativeImage_AcquireNativeWindow(image);
int ret = OH_NativeWindow_CreateNativeWindowFromSurfaceId(surfaceId, &nativeWindow_);
if (ret != SUCCESS) {
OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "OHNativeRender",
"Failed to create NativeWindow from SurfaceId.");
return false;
}
(void)OH_NativeWindow_NativeObjectReference(nativeWindow_);
int32_t result = OH_NativeWindow_NativeWindowHandleOpt(nativeWindow_, SET_BUFFER_GEOMETRY,
static_cast<int32_t>(width_), static_cast<int32_t>(height_));
if (result != SUCCESS) {
OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "OHNativeRender", "Failed to set buffer geometry.");
return false;
}
return true;
}
void OHNativeRender::RenderFrame()
{
OHNativeWindowBuffer *buffer = nullptr;
int releaseFenceFd = INVALID_FD;
int32_t result = OH_NativeWindow_NativeWindowRequestBuffer(nativeWindow_, &buffer, &releaseFenceFd);
if (result != SUCCESS || buffer == nullptr) {
OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN,
"OHNativeRender", "Failed to request buffer, ret : %{public}d.", result);
return;
}
int retCode = FAILURE;
uint32_t timeout = POLL_TIMEOUT_MS;
if (releaseFenceFd != INVALID_FD) {
struct pollfd pollfds = {};
pollfds.fd = releaseFenceFd;
pollfds.events = POLLIN;
int retryCount = 0;
do {
retCode = poll(&pollfds, NUM_POLL_FDS, POLL_TIMEOUT_MS);
retryCount++;
if (retryCount >= MAX_RETRY) {
OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN,
"OHNativeRender", "Poll reached maximum retry count.");
break;
}
} while (retCode == FAILURE && (errno == EINTR || errno == EAGAIN));
close(releaseFenceFd);
}
BufferHandle *handle = OH_NativeWindow_GetBufferHandleFromNative(buffer);
if (handle == nullptr) {
OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "OHNativeRender", "Failed to get buffer handle.");
return;
}
void *mappedAddr = mmap(nullptr, handle->size, PROT_READ | PROT_WRITE, MAP_SHARED, handle->fd, 0);
if (mappedAddr == MAP_FAILED) {
OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "OHNativeRender", "Failed to mmap buffer.");
return;
}
uint32_t *pixel = static_cast<uint32_t *>(mappedAddr);
DrawGradient(pixel, handle->stride / BYTES_PER_PIXEL, height_);
result = munmap(mappedAddr, handle->size);
if (result == FAILURE) {
OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "OHNativeRender", "Failed to munmap buffer.");
}
std::lock_guard<std::mutex> lock(mutex_);
Region region{nullptr, 0};
result = OH_NativeWindow_NativeWindowFlushBuffer(nativeWindow_, buffer, NO_FENCE, region);
if (result != SUCCESS) {
OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN,
"OHNativeRender", "Failed to flush buffer, result : %{public}d.", result);
}
}
void OHNativeRender::DrawGradient(uint32_t* pixel, uint64_t width, uint64_t height)
{
static double time = 0.0;
time += ANIMATION_SPEED_INCREMENT;
double offset = (sin(time) + MAX_INTENSITY) / INTENSITY_MULTIPLIER;
const int arrowSize = std::min(width, height) / ARROW_SIZE_DIVISOR;
const int arrowX = width / ARROW_SIZE_DIVISOR;
const int arrowY = height / ARROW_SIZE_DIVISOR;
const int stemWidth = arrowSize / STEM_WIDTH_DIVISOR;
const int headWidth = arrowSize / HEAD_WIDTH_DIVISOR;
const int headLength = arrowSize / HEAD_LENGTH_DIVISOR;
const int stemStart = arrowX - arrowSize / ARROW_SIZE_DIVISOR;
const int stemEnd = arrowX + arrowSize / ARROW_SIZE_DIVISOR - headLength;
for (uint64_t y = 0; y < height; y++) {
for (uint64_t x = 0; x < width; x++) {
double normalizedX = static_cast<double>(x) / static_cast<double>(width - 1);
bool isArrow = false;
if ((x >= stemStart && x <= stemEnd && y >= arrowY - stemWidth * HEAD_SLOPE_MULTIPLIER &&
y <= arrowY + stemWidth * HEAD_SLOPE_MULTIPLIER) || (x >= stemEnd && x <= stemEnd + headLength &&
fabs(static_cast<int>(y - arrowY)) <= (headWidth * HEAD_SLOPE_MULTIPLIER) *
(1.0 - static_cast<double>(x - stemEnd) / headLength))) {
isArrow = true;
}
uint8_t red = static_cast<uint8_t>((1.0 - normalizedX) * MAX_COLOR_VALUE);
uint8_t blue = static_cast<uint8_t>(normalizedX * MAX_COLOR_VALUE);
uint8_t green = 0;
uint8_t alpha = MAX_COLOR_VALUE;
if (isArrow) {
red = green = blue = MAX_COLOR_VALUE;
}
double intensity = fabs(normalizedX - offset);
intensity = MAX_INTENSITY - std::min(INTENSITY_MULTIPLIER * intensity, INTENSITY_LIMIT);
intensity = std::max(intensity, MIN_INTENSITY);
red = static_cast<uint8_t>(red * intensity);
green = static_cast<uint8_t>(green * intensity);
blue = static_cast<uint8_t>(blue * intensity);
*pixel++ = (static_cast<uint32_t>(alpha) << ALPHA_SHIFT) | (static_cast<uint32_t>(red) << RED_SHIFT) |
(static_cast<uint32_t>(green) << GREEN_SHIFT) | (static_cast<uint32_t>(blue) << BLUE_SHIFT);
}
}
}