blob: 1c1ab7b6b1114c0e2d2b83a5d2506f8e120f4ff6 [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 "layer.h"
#include <math.h>
#include "runner.h"
#include "utils.h"
namespace {
uint32_t scale(uint32_t x, uint32_t from_limit, uint32_t to_limit) {
if (from_limit == to_limit) {
return x;
} else {
return (x * to_limit + (from_limit - 1)) / from_limit;
}
}
} // namespace
namespace display_test {
namespace internal {
LayerImpl::LayerImpl(Runner* runner) : runner_(runner) {
runner->display()->CreateLayer([this](zx_status_t status, uint64_t id) {
ZX_ASSERT(status == ZX_OK);
id_ = id;
runner_->OnResourceReady();
});
}
fuchsia::hardware::display::ControllerPtr& LayerImpl::controller() const {
return runner_->display();
}
} // namespace internal
PrimaryLayer::PrimaryLayer(internal::Runner* runner, uint32_t width,
uint32_t height)
: Layer(runner) {
config_ = {};
config_.width = width;
config_.height = height;
config_.pixel_format = internal::ImageImpl::kFormat;
pending_state_.set_position = false;
pending_state_.transform = fuchsia::hardware::display::Transform::IDENTITY;
pending_state_.src = {};
pending_state_.src.width = width;
pending_state_.src.height = height;
pending_state_.dest = pending_state_.src;
pending_state_.set_alpha = false;
pending_state_.alpha_mode = fuchsia::hardware::display::AlphaMode::DISABLE;
pending_state_.image = nullptr;
pending_state_.flip_image = false;
}
void PrimaryLayer::SetImage(const Image* image) {
pending_state_.image = internal::ImageImpl::GetImageImpl(image);
pending_state_.flip_image = true;
}
void PrimaryLayer::SetPosition(fuchsia::hardware::display::Transform transform,
fuchsia::hardware::display::Frame src,
fuchsia::hardware::display::Frame dest) {
src.x_pos = src.x_pos & ~1;
dest.x_pos = dest.x_pos & ~1;
src.width = src.width & ~1;
dest.width = dest.width & ~1;
ZX_ASSERT(src.width == dest.width || dest.width >= kMinScalableImageSize);
ZX_ASSERT(src.height == dest.height || dest.height >= kMinScalableImageSize);
pending_state_.transform = transform;
pending_state_.src = src;
pending_state_.dest = dest;
pending_state_.set_position = true;
}
void PrimaryLayer::SetAlpha(fuchsia::hardware::display::AlphaMode mode,
float val) {
pending_state_.alpha_mode = mode;
pending_state_.alpha_val = val;
pending_state_.set_alpha = true;
}
bool PrimaryLayer::GetPixel(const void* state, uint32_t x, uint32_t y,
uint32_t* value_out, bool* skip_out) const {
auto s = static_cast<const struct state*>(state);
// Transform the global x,y coordinate to a dest-frame coordinate
x -= s->dest.x_pos;
y -= s->dest.y_pos;
if (x >= s->dest.width || y >= s->dest.height) {
return false;
}
uint32_t dest_width = s->dest.width;
uint32_t dest_height = s->dest.height;
// Undo x reflection if necessary
if (s->transform == fuchsia::hardware::display::Transform::REFLECT_X ||
s->transform == fuchsia::hardware::display::Transform::ROT_180 ||
s->transform == fuchsia::hardware::display::Transform::ROT_270 ||
s->transform == fuchsia::hardware::display::Transform::ROT_90_REFLECT_X) {
x = (dest_width - 1) - x;
}
// Undo y reflection if necessary
if (s->transform == fuchsia::hardware::display::Transform::REFLECT_Y ||
s->transform == fuchsia::hardware::display::Transform::ROT_180 ||
s->transform == fuchsia::hardware::display::Transform::ROT_270 ||
s->transform == fuchsia::hardware::display::Transform::ROT_90_REFLECT_Y) {
y = (dest_height - 1) - y;
}
// Undo 90-degree counterclockwise rotation if necessary
if (s->transform == fuchsia::hardware::display::Transform::ROT_90 ||
s->transform == fuchsia::hardware::display::Transform::ROT_90_REFLECT_X ||
s->transform == fuchsia::hardware::display::Transform::ROT_90_REFLECT_Y ||
s->transform == fuchsia::hardware::display::Transform::ROT_270) {
dest_width = s->dest.height;
dest_height = s->dest.width;
uint32_t tmp = x;
x = (dest_width - 1) - y;
y = tmp;
}
// Scale from dest-coords back to src-coords
x = scale(x, dest_width, s->src.width);
y = scale(y, dest_height, s->src.height);
// If we're scaling, skip over pixels that depend on hardware interpolation
if (dest_width != s->src.width || dest_height != s->src.height) {
constexpr uint32_t bounds = kMinScalableImageSize / 4;
if ((x < bounds || dest_width - bounds <= x) &&
(y < bounds || dest_height - bounds <= y)) {
*skip_out = false;
} else {
*skip_out = true;
}
} else {
*skip_out = false;
}
// Now we can actually get the image pixel data
uint32_t val = s->image->get_pixel(s->src.x_pos + x, s->src.y_pos + y);
// If there's plane alpha, add combine it with the image pixel
if (!isnan(s->alpha_val)) {
uint8_t plane_alpha = static_cast<uint8_t>(round(s->alpha_val * 255));
uint32_t pixel_alpha = val >> 24;
pixel_alpha = ((pixel_alpha * plane_alpha) + 254) >> 8;
val = (val & ~(0xff000000)) | (pixel_alpha << 24);
// If the mode is premultiplied, the hardware is supposed to
// premultiply the alpha value before blending.
if (s->alpha_mode == fuchsia::hardware::display::AlphaMode::PREMULTIPLIED) {
val = internal::premultiply_color_channels(val, plane_alpha);
}
}
if (s->alpha_mode == fuchsia::hardware::display::AlphaMode::DISABLE) {
// Clobber the alpha value if it's disabled
val |= 0xff000000;
} else if (s->alpha_mode ==
fuchsia::hardware::display::AlphaMode::HW_MULTIPLY) {
// If blending is hwmultiply, then do the the channel multiplication
// here so the caller of GetPixel can treat everything is premultiplied.
val = internal::premultiply_color_channels(val, val >> 24);
}
*value_out = val;
return true;
}
const void* PrimaryLayer::ApplyState() {
auto state = new struct state;
*state = pending_state_;
pending_state_.set_config = false;
pending_state_.set_position = false;
pending_state_.set_alpha = false;
pending_state_.flip_image = false;
if (state->src.height != state->dest.height ||
state->src.width != state->dest.width) {
ZX_ASSERT(state->image->is_scalable());
}
return state;
}
void PrimaryLayer::SendState(const void* state) const {
auto s = static_cast<const struct state*>(state);
if (s->set_config) {
controller()->SetLayerPrimaryConfig(id(), config_);
}
if (s->set_position) {
controller()->SetLayerPrimaryPosition(id(), s->transform, s->src, s->dest);
}
if (s->set_alpha) {
controller()->SetLayerPrimaryAlpha(id(), s->alpha_mode, s->alpha_val);
}
if (s->flip_image) {
controller()->SetLayerImage(id(), s->image->id(), 0, 0);
}
}
void PrimaryLayer::DeleteState(const void* state) const {
delete static_cast<const struct state*>(state);
}
uint64_t PrimaryLayer::image_id(const void* state) const {
auto image = static_cast<const struct state*>(state)->image;
return image ? image->id() : 0;
}
} // namespace display_test