blob: de356a0ade45a8330be98c07cc4013e674af902a [file] [log] [blame]
// Copyright 2023 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 "src/graphics/display/testing/software-compositor/software-compositor.h"
#include <lib/stdcompat/span.h>
#include <zircon/assert.h>
#include <cstddef>
#include <cstdint>
#include <cstdio>
#include "src/graphics/display/testing/software-compositor/pixel.h"
namespace software_compositor {
namespace {
inline std::pair<cpp20::span<uint8_t>::iterator, cpp20::span<const uint8_t>::iterator> CopyPixel(
cpp20::span<uint8_t>::iterator canvas_pixel_it, PixelFormat canvas_pixel_format,
cpp20::span<const uint8_t>::iterator source_pixel_it, PixelFormat source_pixel_format) {
if (canvas_pixel_format == source_pixel_format) {
std::tie(canvas_pixel_it[0], canvas_pixel_it[1], canvas_pixel_it[2], canvas_pixel_it[3]) =
std::tie(source_pixel_it[0], source_pixel_it[1], source_pixel_it[2], source_pixel_it[3]);
} else if ((canvas_pixel_format == PixelFormat::kBgra8888 &&
source_pixel_format == PixelFormat::kRgba8888) ||
(canvas_pixel_format == PixelFormat::kRgba8888 &&
source_pixel_format == PixelFormat::kBgra8888)) {
std::tie(canvas_pixel_it[0], canvas_pixel_it[1], canvas_pixel_it[2], canvas_pixel_it[3]) =
std::tie(source_pixel_it[2], source_pixel_it[1], source_pixel_it[0], source_pixel_it[3]);
} else {
// This should never happen.
ZX_DEBUG_ASSERT_MSG(false, "not implemented");
}
return {canvas_pixel_it + GetBytesPerPixel(canvas_pixel_format),
source_pixel_it + GetBytesPerPixel(source_pixel_format)};
}
} // namespace
PixelData OutputImage::At(const Offset2D& offset) const {
cpp20::span<const uint8_t> data{reinterpret_cast<const uint8_t*>(buffer.data()),
static_cast<size_t>(properties.height) * properties.stride_bytes};
const int bytes_per_pixel = GetBytesPerPixel(properties.pixel_format);
return PixelData::FromRaw(buffer.subspan(
offset.y * properties.stride_bytes + offset.x * bytes_per_pixel, bytes_per_pixel));
}
void OutputImage::SetPixelData(const Offset2D& offset, const PixelData& pixel) const {
const int bytes_per_pixel = GetBytesPerPixel(properties.pixel_format);
const auto color_to_write = buffer.subspan(
offset.y * properties.stride_bytes + offset.x * bytes_per_pixel, bytes_per_pixel);
std::copy(pixel.data.begin(), pixel.data.end(), color_to_write.begin());
}
PixelData InputImage::At(const Offset2D& offset) const {
const int bytes_per_pixel = GetBytesPerPixel(properties.pixel_format);
return PixelData::FromRaw(buffer.subspan(
offset.y * properties.stride_bytes + offset.x * bytes_per_pixel, bytes_per_pixel));
}
SoftwareCompositor::SoftwareCompositor(const OutputImage& canvas) : canvas_(canvas) {}
void SoftwareCompositor::ClearCanvas(const PixelData& color, PixelFormat pixel_format) const {
const PixelData color_to_fill = color.Convert(pixel_format, canvas_.properties.pixel_format);
for (int row = 0; row < canvas_.properties.height; ++row) {
cpp20::span<uint8_t> bytes_to_fill = canvas_.buffer.subspan(
row * canvas_.properties.stride_bytes, canvas_.properties.stride_bytes);
auto it = bytes_to_fill.begin();
for (int column = 0; column < canvas_.properties.width; ++column) {
it = std::copy(color_to_fill.data.begin(), color_to_fill.data.end(), it);
}
}
}
void SoftwareCompositor::CompositeImage(const InputImage& input_image,
const CompositionProperties& composition_properties) const {
// TODO(https://fxbug.dev/42075534): Currently alpha compositing is not supported.
// Callers must guarantee that alpha blending is disabled.
ZX_ASSERT(composition_properties.alpha_mode == ::display::AlphaMode::kDisable);
// TODO(https://fxbug.dev/42075534): Currently image transformation is not supported.
// Callers must guarantee that the image to draw is not rotated nor flipped.
ZX_ASSERT(composition_properties.transform == ::display::Transform::kIdentity);
const ::display::Frame& canvas_frame = composition_properties.canvas_frame;
const ::display::Frame& source_frame = composition_properties.source_frame;
// TODO(https://fxbug.dev/42075534): Currently this doesn't support clipping of the
// input image. Callers must guarantee that the source frame starts at (0, 0)
// and has the same size as the input image.
ZX_ASSERT(source_frame.y_pos == 0);
ZX_ASSERT(source_frame.x_pos == 0);
ZX_ASSERT(source_frame.height == input_image.properties.height);
ZX_ASSERT(source_frame.width == input_image.properties.width);
// TODO(https://fxbug.dev/42075534): Currently this doesn't support scaling.
// Callers must guarantee that the destination frame size is the same as the
// original image size.
ZX_ASSERT(canvas_frame.height == input_image.properties.height);
ZX_ASSERT(canvas_frame.width == input_image.properties.width);
// TODO(https://fxbug.dev/42075534): Currently this doesn't support clipping.
// Callers must guarantee that the destination frame falls within the canvas.
ZX_ASSERT(canvas_frame.y_pos + canvas_frame.height <= canvas_.properties.height);
ZX_ASSERT(canvas_frame.x_pos + canvas_frame.width <= canvas_.properties.width);
const int src_bytes_per_pixel = GetBytesPerPixel(input_image.properties.pixel_format);
const int dst_bytes_per_pixel = GetBytesPerPixel(canvas_.properties.pixel_format);
for (int row = 0; row < canvas_frame.height; row++) {
cpp20::span<const uint8_t> source_row = input_image.buffer.subspan(
(row + source_frame.y_pos) * input_image.properties.stride_bytes +
source_frame.x_pos * src_bytes_per_pixel,
/*count=*/source_frame.width * src_bytes_per_pixel);
cpp20::span<uint8_t> canvas_row =
canvas_.buffer.subspan((row + canvas_frame.y_pos) * canvas_.properties.stride_bytes +
canvas_frame.x_pos * dst_bytes_per_pixel,
/*count=*/canvas_frame.width * dst_bytes_per_pixel);
auto source_row_it = source_row.begin();
auto canvas_row_it = canvas_row.begin();
for (int column = 0; column < canvas_frame.width; column++) {
std::tie(canvas_row_it, source_row_it) =
CopyPixel(canvas_row_it, canvas_.properties.pixel_format, source_row_it,
input_image.properties.pixel_format);
}
}
}
void SoftwareCompositor::CompositeImageLayers(
cpp20::span<const ImageLayerForComposition> image_layers) const {
constexpr PixelData kBlack = {0x00, 0x00, 0x00, 0xff};
ClearCanvas(kBlack, PixelFormat::kRgba8888);
for (const ImageLayerForComposition& image_layer : image_layers) {
CompositeImage(image_layer.image, image_layer.properties);
}
}
} // namespace software_compositor