blob: d7e5b3e6f54043b63dd511af337d0d2405b2969c [file] [log] [blame]
// Copyright 2018 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "yuv_view.h"
#include <lib/fxl/log_level.h>
#include <lib/fxl/logging.h>
#include <lib/images/cpp/images.h>
#include <lib/ui/scenic/cpp/commands.h>
#include <iostream>
#include "garnet/lib/ui/yuv/yuv.h"
namespace {
constexpr uint32_t kShapeWidth = 640;
constexpr uint32_t kShapeHeight = 480;
constexpr float kDisplayHeight = 50;
constexpr float kInitialWindowXPos = 320;
constexpr float kInitialWindowYPos = 240;
} // namespace
YuvView::YuvView(scenic::ViewContext context,
fuchsia::images::PixelFormat pixel_format)
: V1BaseView(std::move(context), "YuvView Example"),
node_(session()),
pixel_format_(pixel_format),
stride_(static_cast<uint32_t>(
kShapeWidth * images::StrideBytesPerWidthPixel(pixel_format_))) {
FXL_VLOG(4) << "Creating View";
// Create an ImagePipe and use it.
uint32_t image_pipe_id = session()->AllocResourceId();
session()->Enqueue(
scenic::NewCreateImagePipeCmd(image_pipe_id, image_pipe_.NewRequest()));
// Create a material that has our image pipe mapped onto it:
scenic::Material material(session());
material.SetTexture(image_pipe_id);
session()->ReleaseResource(image_pipe_id);
// Create a rectangle shape to display the YUV on.
scenic::Rectangle shape(session(), kShapeWidth, kShapeHeight);
node_.SetShape(shape);
node_.SetMaterial(material);
parent_node().AddChild(node_);
// Translation of 0, 0 is the middle of the screen
node_.SetTranslation(kInitialWindowXPos, kInitialWindowYPos, kDisplayHeight);
InvalidateScene();
StartYuv();
}
YuvView::~YuvView() = default;
void YuvView::OnSceneInvalidated(
fuchsia::images::PresentationInfo presentation_info) {
if (!has_logical_size()) {
return;
}
// Compute the amount of time that has elapsed since the view was created.
double seconds =
static_cast<double>(presentation_info.presentation_time) / 1'000'000'000;
const float kHalfWidth = logical_size().width * 0.5f;
const float kHalfHeight = logical_size().height * 0.5f;
// Compute the translation for the window to swirl around the screen.
// Why do this? Well, this is an example of what a View can do, and it helps
// debug to know if scenic is still running.
node_.SetTranslation(kHalfWidth * (1. + .1 * sin(seconds * 0.8)),
kHalfHeight * (1. + .1 * sin(seconds * 0.6)),
kDisplayHeight);
// The recangle is constantly animating; invoke InvalidateScene() to guarantee
// that OnSceneInvalidated() will be called again.
InvalidateScene();
}
void YuvView::StartYuv() {
constexpr uint32_t kImageId = 1;
fuchsia::images::ImageInfo image_info{
.width = kShapeWidth,
.height = kShapeHeight,
.stride = stride_,
.pixel_format = pixel_format_,
};
uint64_t image_vmo_bytes = images::ImageSize(image_info);
::zx::vmo image_vmo;
zx_status_t status = ::zx::vmo::create(image_vmo_bytes, 0, &image_vmo);
if (status != ZX_OK) {
FXL_LOG(FATAL) << "::zx::vmo::create() failed";
FXL_NOTREACHED();
}
uint8_t* vmo_base;
status = zx::vmar::root_self()->map(0, image_vmo, 0, image_vmo_bytes,
ZX_VM_PERM_WRITE | ZX_VM_PERM_READ,
reinterpret_cast<uintptr_t*>(&vmo_base));
SetVmoPixels(vmo_base);
constexpr uint64_t kMemoryOffset = 0;
image_pipe_->AddImage(kImageId, image_info, std::move(image_vmo),
kMemoryOffset, image_vmo_bytes,
fuchsia::images::MemoryType::HOST_MEMORY);
::std::vector<::zx::event> acquire_fences;
::std::vector<::zx::event> release_fences;
uint64_t now_ns = zx_clock_get(ZX_CLOCK_MONOTONIC);
image_pipe_->PresentImage(
kImageId, now_ns, std::move(acquire_fences), std::move(release_fences),
[this](fuchsia::images::PresentationInfo presentation_info) {
std::cout << "PresentImageCallback() called" << std::endl;
});
}
void YuvView::SetVmoPixels(uint8_t* vmo_base) {
switch (pixel_format_) {
case fuchsia::images::PixelFormat::BGRA_8:
SetBgra8Pixels(vmo_base);
break;
case fuchsia::images::PixelFormat::YUY2:
SetYuy2Pixels(vmo_base);
break;
case fuchsia::images::PixelFormat::NV12:
SetNv12Pixels(vmo_base);
break;
case fuchsia::images::PixelFormat::YV12:
SetYv12Pixels(vmo_base);
break;
}
}
void YuvView::SetBgra8Pixels(uint8_t* vmo_base) {
for (uint32_t y_iter = 0; y_iter < kShapeHeight; y_iter++) {
double y = static_cast<double>(y_iter) / kShapeHeight;
for (uint32_t x_iter = 0; x_iter < kShapeWidth; x_iter++) {
double x = static_cast<double>(x_iter) / kShapeWidth;
uint8_t y_value = GetYValue(x, y) * 255;
uint8_t u_value = GetUValue(x, y) * 255;
uint8_t v_value = GetVValue(x, y) * 255;
yuv::YuvToBgra(y_value, u_value, v_value,
&vmo_base[y_iter * stride_ + x_iter * sizeof(uint32_t)]);
}
}
}
void YuvView::SetYuy2Pixels(uint8_t* vmo_base) {
for (uint32_t y_iter = 0; y_iter < kShapeHeight; y_iter++) {
double y = static_cast<double>(y_iter) / kShapeHeight;
for (uint32_t x_iter = 0; x_iter < kShapeWidth; x_iter += 2) {
double x0 = static_cast<double>(x_iter) / kShapeWidth;
double x1 = static_cast<double>(x_iter + 1) / kShapeWidth;
uint8_t* two_pixels =
&vmo_base[y_iter * stride_ + x_iter * sizeof(uint16_t)];
two_pixels[0] = GetYValue(x0, y) * 255;
two_pixels[1] = GetUValue(x0, y) * 255;
two_pixels[2] = GetYValue(x1, y) * 255;
two_pixels[3] = GetVValue(x0, y) * 255;
}
}
}
void YuvView::SetNv12Pixels(uint8_t* vmo_base) {
// Y plane
uint8_t* y_base = vmo_base;
for (uint32_t y_iter = 0; y_iter < kShapeHeight; y_iter++) {
double y = static_cast<double>(y_iter) / kShapeHeight;
for (uint32_t x_iter = 0; x_iter < kShapeWidth; x_iter++) {
double x = static_cast<double>(x_iter) / kShapeWidth;
y_base[y_iter * stride_ + x_iter] = GetYValue(x, y) * 255;
}
}
// UV interleaved
uint8_t* uv_base = y_base + kShapeHeight * stride_;
for (uint32_t y_iter = 0; y_iter < kShapeHeight / 2; y_iter++) {
double y = static_cast<double>(y_iter * 2) / kShapeHeight;
for (uint32_t x_iter = 0; x_iter < kShapeWidth / 2; x_iter++) {
double x = static_cast<double>(x_iter * 2) / kShapeWidth;
uv_base[y_iter * stride_ + x_iter * 2] = GetUValue(x, y) * 255;
uv_base[y_iter * stride_ + x_iter * 2 + 1] = GetVValue(x, y) * 255;
}
}
}
void YuvView::SetYv12Pixels(uint8_t* vmo_base) {
// Y plane
uint8_t* y_base = vmo_base;
for (uint32_t y_iter = 0; y_iter < kShapeHeight; y_iter++) {
double y = static_cast<double>(y_iter) / kShapeHeight;
for (uint32_t x_iter = 0; x_iter < kShapeWidth; x_iter++) {
double x = static_cast<double>(x_iter) / kShapeWidth;
y_base[y_iter * stride_ + x_iter] = GetYValue(x, y) * 255;
}
}
// U and V work the same as each other, so do them together
uint8_t* u_base =
y_base + kShapeHeight * stride_ + kShapeHeight / 2 * stride_ / 2;
uint8_t* v_base = y_base + kShapeHeight * stride_;
for (uint32_t y_iter = 0; y_iter < kShapeHeight / 2; y_iter++) {
double y = static_cast<double>(y_iter * 2) / kShapeHeight;
for (uint32_t x_iter = 0; x_iter < kShapeWidth / 2; x_iter++) {
double x = static_cast<double>(x_iter * 2) / kShapeWidth;
u_base[y_iter * stride_ / 2 + x_iter] = GetUValue(x, y) * 255;
v_base[y_iter * stride_ / 2 + x_iter] = GetVValue(x, y) * 255;
}
}
}
double YuvView::GetYValue(double x, double y) { return x; }
double YuvView::GetUValue(double x, double y) { return y; }
double YuvView::GetVValue(double x, double y) { return 1 - y; }