blob: 274996a35392fc4d99931a29607db528358b76dd [file] [log] [blame]
/*
* Copyright (C) 2023 The Android Open Source Project
*
* 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.
*/
// #define LOG_NDEBUG 0
#define LOG_TAG "TestPatternHelper"
#include "TestPatternHelper.h"
#include <complex>
#include <cstdint>
#include "log/log.h"
#include "nativebase/nativebase.h"
#include "system/graphics.h"
#include "ui/GraphicBuffer.h"
#include "utils/Errors.h"
namespace android {
namespace companion {
namespace virtualcamera {
namespace {
using namespace std::chrono_literals;
static constexpr std::chrono::milliseconds kAcquireFenceTimeout = 500ms;
uint8_t julia(const std::complex<float> n, const std::complex<float> c) {
std::complex<float> z = n;
for (int i = 0; i < 64; i++) {
z = z * z + c;
if (std::abs(z) > 2.0) return i * 4;
}
return 0xff;
}
uint8_t pixelToFractal(const int x, const int y, const int width,
const int height, const std::complex<float> c) {
std::complex<float> n(float(x) / float(width) - 0.5,
float(y) / float(height) - 0.5);
return julia(n * 5.f, c);
}
void renderTestPatternYcbCr420(const android_ycbcr& ycbr, const int width,
const int height, const int frameNumber) {
float time = float(frameNumber) / 120.0f;
const std::complex<float> c(std::sin(time), std::cos(time));
uint8_t* y = reinterpret_cast<uint8_t*>(ycbr.y);
uint8_t* cb = reinterpret_cast<uint8_t*>(ycbr.cb);
uint8_t* cr = reinterpret_cast<uint8_t*>(ycbr.cr);
for (int row = 0; row < height; row++) {
for (int col = 0; col < width; col++) {
y[row * ycbr.ystride + col] =
pixelToFractal(col, row, width, height, c * 0.78f);
}
}
int cWidth = width / 2;
int cHeight = height / 2;
for (int row = 0; row < cHeight; row++) {
for (int col = 0; col < cWidth; col++) {
cb[row * ycbr.cstride + col * ycbr.chroma_step] =
static_cast<uint8_t>((float(col) / float(cWidth)) * 255.f);
cr[row * ycbr.cstride + col * ycbr.chroma_step] =
static_cast<uint8_t>((float(row) / float(cHeight)) * 255.f);
}
}
}
} // namespace
void renderTestPatternYCbCr420(sp<Surface> surface, int frameNumber) {
if (surface == nullptr) {
ALOGE("%s: null surface, skipping render", __func__);
return;
}
ANativeWindowBuffer* buffer;
int fenceFd;
int ret = ANativeWindow_dequeueBuffer(surface.get(), &buffer, &fenceFd);
if (ret != NO_ERROR) {
ALOGE(
"%s: Error while deuqueing buffer from surface, "
"ANativeWindow_dequeueBuffer returned %d",
__func__, ret);
return;
}
if (buffer == nullptr) {
ALOGE("%s: ANativeWindowBuffer is null after dequeing", __func__);
return;
}
sp<Fence> fence = sp<Fence>::make(fenceFd);
if (fence->isValid()) {
ret = fence->wait(kAcquireFenceTimeout.count());
if (ret != NO_ERROR) {
ALOGE("%s: Timeout while waiting for the fence to clear", __func__);
ANativeWindow_queueBuffer(surface.get(), buffer, fence->dup());
return;
}
}
sp<GraphicBuffer> gBuffer = GraphicBuffer::from(buffer);
android_ycbcr ycbr;
ret = gBuffer->lockAsyncYCbCr(GraphicBuffer::USAGE_SW_WRITE_OFTEN, &ycbr,
fence->dup());
if (ret != NO_ERROR) {
ALOGE("%s: Failed to lock buffer retrieved from surface, ret %d", __func__,
ret);
return;
}
renderTestPatternYcbCr420(ycbr, gBuffer->getWidth(), gBuffer->getHeight(),
frameNumber);
ret = gBuffer->unlock();
if (ret != NO_ERROR) {
ALOGE("%s: Failed to unlock buffer, ret %d", __func__, ret);
return;
}
ret = ANativeWindow_queueBuffer(surface.get(), buffer, /*fenceFd=*/-1);
if (ret != NO_ERROR) {
ALOGE(
"%s: Error while queing buffer to surface, ANativeWindow_queueBuffer "
"returned %d",
__func__, ret);
return;
}
}
} // namespace virtualcamera
} // namespace companion
} // namespace android