blob: 13f712e4ad0c8d296cafd02959ebb02e3883dca5 [file] [log] [blame]
// Copyright 2013 The Flutter 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 "flutter/shell/platform/windows/compositor_software.h"
#include "flutter/shell/platform/windows/flutter_windows_engine.h"
#include "flutter/shell/platform/windows/flutter_windows_view.h"
namespace flutter {
constexpr int kOpaqueBlack = 0xff000000;
namespace {
/// Calculate the minimum and maximum x and y coordinates to enclose all layers.
FlutterRect CalculateBounds(const FlutterLayer** layers, size_t layers_count) {
double x_min = HUGE_VAL;
double x_max = -HUGE_VAL;
double y_min = HUGE_VAL;
double y_max = -HUGE_VAL;
for (const FlutterLayer** layer = layers; layer < layers + layers_count;
layer++) {
const FlutterPoint& offset = (*layer)->offset;
const FlutterSize& size = (*layer)->size;
x_min = std::min(x_min, offset.x);
y_min = std::min(y_min, offset.y);
x_max = std::max(x_max, offset.x + size.width);
y_max = std::max(y_max, offset.y + size.height);
}
return FlutterRect{x_min, y_min, x_max, y_max};
}
/// Blend layer in-place onto allocation, which already holds the previous
/// results of composition.
void BlendLayer(std::vector<uint32_t>& allocation,
const FlutterLayer& layer,
int x_min,
int y_min,
int width,
int height) {
FML_DCHECK(layer.type == kFlutterLayerContentTypeBackingStore);
auto& backing_store = *layer.backing_store;
FML_DCHECK(backing_store.type == kFlutterBackingStoreTypeSoftware);
auto src_data =
static_cast<const uint32_t*>(backing_store.software.allocation);
const FlutterPoint& offset = layer.offset;
const FlutterSize& size = layer.size;
// Bounds for iteration to prevent out-of-bounds destination coordinates.
int y_src_min = std::max(0., y_min - offset.y);
int y_src_max = std::min(size.height, height + y_min - offset.y);
int x_src_min = std::max(0., x_min - offset.x);
int x_src_max = std::min(size.width, width + x_min - offset.x);
for (int y_src = y_src_min; y_src < y_src_max; y_src++) {
int y_dst = y_src + offset.y - y_min;
for (int x_src = x_src_min; x_src < x_src_max; x_src++) {
int x_dst = x_src + offset.x + x_min;
size_t i_src = y_src * size.width + x_src;
size_t i_dst = y_dst * width + x_dst;
uint32_t src = src_data[i_src];
uint32_t dst = allocation[i_dst];
int r_src = (src >> 0) & 0xff;
int g_src = (src >> 8) & 0xff;
int b_src = (src >> 16) & 0xff;
int a_src = (src >> 24) & 0xff;
int r_dst = (dst >> 0) & 0xff;
int g_dst = (dst >> 8) & 0xff;
int b_dst = (dst >> 16) & 0xff;
int r = (r_dst * 255 + (r_src - r_dst) * a_src) / 255;
int g = (g_dst * 255 + (g_src - g_dst) * a_src) / 255;
int b = (b_dst * 255 + (b_src - b_dst) * a_src) / 255;
allocation[i_dst] = (r << 0) | (g << 8) | (b << 16) | (0xff << 24);
}
}
}
} // namespace
CompositorSoftware::CompositorSoftware() {}
bool CompositorSoftware::CreateBackingStore(
const FlutterBackingStoreConfig& config,
FlutterBackingStore* result) {
size_t size = config.size.width * config.size.height * 4;
void* allocation = std::calloc(size, sizeof(uint8_t));
if (!allocation) {
return false;
}
result->type = kFlutterBackingStoreTypeSoftware;
result->software.allocation = allocation;
result->software.height = config.size.height;
result->software.row_bytes = config.size.width * 4;
result->software.user_data = nullptr;
result->software.destruction_callback = [](void* user_data) {
// Backing store destroyed in `CompositorSoftware::CollectBackingStore`, set
// on FlutterCompositor.collect_backing_store_callback during engine start.
};
return true;
}
bool CompositorSoftware::CollectBackingStore(const FlutterBackingStore* store) {
std::free(const_cast<void*>(store->software.allocation));
return true;
}
bool CompositorSoftware::Present(FlutterWindowsView* view,
const FlutterLayer** layers,
size_t layers_count) {
FML_DCHECK(view != nullptr);
// Clear the view if there are no layers to present.
if (layers_count == 0) {
return view->ClearSoftwareBitmap();
}
// Bypass composition logic if there is only one layer.
if (layers_count == 1) {
const FlutterLayer* layer = layers[0];
FML_DCHECK(layer != nullptr);
if (layer->type == kFlutterLayerContentTypeBackingStore &&
layer->offset.x == 0 && layer->offset.y == 0) {
auto& backing_store = *layer->backing_store;
FML_DCHECK(backing_store.type == kFlutterBackingStoreTypeSoftware);
auto& software = backing_store.software;
return view->PresentSoftwareBitmap(software.allocation,
software.row_bytes, software.height);
}
}
// Composite many layers.
FlutterRect bounds = CalculateBounds(layers, layers_count);
// Truncate from double to integer to represent whole pixels.
int x_min = static_cast<int>(bounds.left);
int x_max = static_cast<int>(bounds.right);
int y_min = static_cast<int>(bounds.top);
int y_max = static_cast<int>(bounds.bottom);
int width = x_max - x_min;
int height = y_max - y_min;
std::vector<uint32_t> allocation(width * height, kOpaqueBlack);
for (const FlutterLayer** layer = layers; layer < layers + layers_count;
layer++) {
// TODO(schectman): handle platform view type layers.
// https://github.com/flutter/flutter/issues/143375
if ((*layer)->type == kFlutterLayerContentTypeBackingStore) {
BlendLayer(allocation, **layer, x_min, y_min, width, height);
} else {
FML_UNREACHABLE();
return false;
}
}
return view->PresentSoftwareBitmap(static_cast<void*>(allocation.data()),
width * sizeof(uint32_t), height);
}
} // namespace flutter