363 lines
12 KiB
C++
363 lines
12 KiB
C++
|
// Copyright 2022 The ChromiumOS Authors
|
||
|
// Use of this source code is governed by a BSD-style license that can be
|
||
|
// found in the LICENSE file.
|
||
|
|
||
|
#include <math.h>
|
||
|
|
||
|
#include "sommelier-tracing.h" // NOLINT(build/include_directory)
|
||
|
#include "sommelier-transform.h" // NOLINT(build/include_directory)
|
||
|
|
||
|
static void sl_transform_get_scale_factors(
|
||
|
struct sl_context* ctx,
|
||
|
const struct sl_host_surface* surface,
|
||
|
double* scalex,
|
||
|
double* scaley) {
|
||
|
if (ctx->use_direct_scale && surface && surface->has_own_scale) {
|
||
|
*scalex = surface->xdg_scale_x;
|
||
|
*scaley = surface->xdg_scale_y;
|
||
|
} else if (surface && surface->output) {
|
||
|
*scalex = surface->output.get()->xdg_scale_x;
|
||
|
*scaley = surface->output.get()->xdg_scale_y;
|
||
|
} else {
|
||
|
*scalex = ctx->xdg_scale_x;
|
||
|
*scaley = ctx->xdg_scale_y;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static double sl_transform_direct_axis_scale(struct sl_context* ctx,
|
||
|
struct sl_host_surface* surface,
|
||
|
uint32_t axis) {
|
||
|
double scalex, scaley;
|
||
|
|
||
|
sl_transform_get_scale_factors(ctx, surface, &scalex, &scaley);
|
||
|
return (axis == 0) ? scaley : scalex;
|
||
|
}
|
||
|
|
||
|
static void sl_transform_direct_to_host_damage(
|
||
|
struct sl_context* ctx,
|
||
|
const struct sl_host_surface* surface,
|
||
|
int64_t* x,
|
||
|
int64_t* y,
|
||
|
double scale_x,
|
||
|
double scale_y) {
|
||
|
double xwhole = trunc(static_cast<double>(*x) / scale_x);
|
||
|
double ywhole = trunc(static_cast<double>(*y) / scale_y);
|
||
|
|
||
|
*x = static_cast<int64_t>(xwhole);
|
||
|
*y = static_cast<int64_t>(ywhole);
|
||
|
}
|
||
|
|
||
|
static void sl_transform_direct_to_guest_fixed(struct sl_context* ctx,
|
||
|
struct sl_host_surface* surface,
|
||
|
wl_fixed_t* coord,
|
||
|
uint32_t axis) {
|
||
|
double scale = sl_transform_direct_axis_scale(ctx, surface, axis);
|
||
|
double result = wl_fixed_to_double(*coord) * scale;
|
||
|
|
||
|
*coord = wl_fixed_from_double(result);
|
||
|
}
|
||
|
|
||
|
static void sl_transform_direct_to_guest_fixed(struct sl_context* ctx,
|
||
|
struct sl_host_surface* surface,
|
||
|
wl_fixed_t* x,
|
||
|
wl_fixed_t* y) {
|
||
|
double scalex, scaley;
|
||
|
|
||
|
sl_transform_get_scale_factors(ctx, surface, &scalex, &scaley);
|
||
|
|
||
|
double resultx = wl_fixed_to_double(*x) * scalex;
|
||
|
double resulty = wl_fixed_to_double(*y) * scaley;
|
||
|
|
||
|
*x = wl_fixed_from_double(resultx);
|
||
|
*y = wl_fixed_from_double(resulty);
|
||
|
}
|
||
|
|
||
|
static void sl_transform_direct_to_host_fixed(struct sl_context* ctx,
|
||
|
struct sl_host_surface* surface,
|
||
|
wl_fixed_t* coord,
|
||
|
uint32_t axis) {
|
||
|
double scale = sl_transform_direct_axis_scale(ctx, surface, axis);
|
||
|
double result = wl_fixed_to_double(*coord) / scale;
|
||
|
|
||
|
*coord = wl_fixed_from_double(result);
|
||
|
}
|
||
|
|
||
|
static void sl_transform_direct_to_host_fixed(struct sl_context* ctx,
|
||
|
struct sl_host_surface* surface,
|
||
|
wl_fixed_t* x,
|
||
|
wl_fixed_t* y) {
|
||
|
double scalex, scaley;
|
||
|
|
||
|
sl_transform_get_scale_factors(ctx, surface, &scalex, &scaley);
|
||
|
|
||
|
double resultx = wl_fixed_to_double(*x) / scalex;
|
||
|
double resulty = wl_fixed_to_double(*y) / scaley;
|
||
|
|
||
|
*x = wl_fixed_from_double(resultx);
|
||
|
*y = wl_fixed_from_double(resulty);
|
||
|
}
|
||
|
|
||
|
static void sl_transform_direct_to_guest(struct sl_context* ctx,
|
||
|
struct sl_host_surface* surface,
|
||
|
int32_t* x,
|
||
|
int32_t* y) {
|
||
|
double scalex, scaley;
|
||
|
|
||
|
sl_transform_get_scale_factors(ctx, surface, &scalex, &scaley);
|
||
|
|
||
|
double inputx = scalex * static_cast<double>(*x);
|
||
|
double inputy = scaley * static_cast<double>(*y);
|
||
|
|
||
|
double xwhole =
|
||
|
(surface && surface->scale_round_on_x) ? lround(inputx) : trunc(inputx);
|
||
|
|
||
|
double ywhole =
|
||
|
(surface && surface->scale_round_on_y) ? lround(inputy) : trunc(inputy);
|
||
|
|
||
|
*x = static_cast<int32_t>(xwhole);
|
||
|
*y = static_cast<int32_t>(ywhole);
|
||
|
}
|
||
|
|
||
|
static void sl_transform_direct_to_host(struct sl_context* ctx,
|
||
|
struct sl_host_surface* surface,
|
||
|
int32_t* x,
|
||
|
int32_t* y) {
|
||
|
double scalex, scaley;
|
||
|
|
||
|
sl_transform_get_scale_factors(ctx, surface, &scalex, &scaley);
|
||
|
|
||
|
double xwhole = trunc(static_cast<double>(*x) / scalex);
|
||
|
double ywhole = trunc(static_cast<double>(*y) / scaley);
|
||
|
|
||
|
*x = static_cast<int32_t>(xwhole);
|
||
|
*y = static_cast<int32_t>(ywhole);
|
||
|
}
|
||
|
|
||
|
bool sl_transform_viewport_scale(struct sl_context* ctx,
|
||
|
struct sl_host_surface* surface,
|
||
|
double contents_scale,
|
||
|
int32_t* width,
|
||
|
int32_t* height) {
|
||
|
double scale = ctx->scale * contents_scale;
|
||
|
|
||
|
// TODO(mrisaacb): It may be beneficial to skip the set_destination call
|
||
|
// when the virtual and logical space match.
|
||
|
bool do_viewport = true;
|
||
|
|
||
|
if (ctx->use_direct_scale) {
|
||
|
sl_transform_direct_to_host(ctx, surface, width, height);
|
||
|
|
||
|
// For very small windows (in pixels), the resulting logical dimensions
|
||
|
// could be 0, which will cause issues with the viewporter interface.
|
||
|
//
|
||
|
// In these cases, fix it up here by forcing the logical output
|
||
|
// to be at least 1 pixel
|
||
|
|
||
|
if (*width <= 0)
|
||
|
*width = 1;
|
||
|
|
||
|
if (*height <= 0)
|
||
|
*height = 1;
|
||
|
|
||
|
} else {
|
||
|
*width = ceil(*width / scale);
|
||
|
*height = ceil(*height / scale);
|
||
|
}
|
||
|
|
||
|
return do_viewport;
|
||
|
}
|
||
|
|
||
|
void sl_transform_damage_coord(struct sl_context* ctx,
|
||
|
const struct sl_host_surface* surface,
|
||
|
double buffer_scalex,
|
||
|
double buffer_scaley,
|
||
|
int64_t* x1,
|
||
|
int64_t* y1,
|
||
|
int64_t* x2,
|
||
|
int64_t* y2) {
|
||
|
if (ctx->use_direct_scale) {
|
||
|
double scalex, scaley;
|
||
|
|
||
|
sl_transform_get_scale_factors(ctx, surface, &scalex, &scaley);
|
||
|
|
||
|
scalex *= buffer_scalex;
|
||
|
scaley *= buffer_scaley;
|
||
|
|
||
|
sl_transform_direct_to_host_damage(ctx, surface, x1, y1, scalex, scaley);
|
||
|
sl_transform_direct_to_host_damage(ctx, surface, x2, y2, scalex, scaley);
|
||
|
} else {
|
||
|
double sx = buffer_scalex * ctx->scale;
|
||
|
double sy = buffer_scaley * ctx->scale;
|
||
|
|
||
|
// Enclosing rect after scaling and outset by one pixel to account for
|
||
|
// potential filtering.
|
||
|
*x1 = MAX(MIN_SIZE, (*x1) - 1) / sx;
|
||
|
*y1 = MAX(MIN_SIZE, (*y1) - 1) / sy;
|
||
|
*x2 = ceil(MIN((*x2) + 1, MAX_SIZE) / sx);
|
||
|
*y2 = ceil(MIN((*y2) + 1, MAX_SIZE) / sy);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void sl_transform_host_to_guest(struct sl_context* ctx,
|
||
|
struct sl_host_surface* surface,
|
||
|
int32_t* x,
|
||
|
int32_t* y) {
|
||
|
if (ctx->use_direct_scale) {
|
||
|
sl_transform_direct_to_guest(ctx, surface, x, y);
|
||
|
} else {
|
||
|
(*x) *= ctx->scale;
|
||
|
(*y) *= ctx->scale;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void sl_transform_host_to_guest_fixed(struct sl_context* ctx,
|
||
|
struct sl_host_surface* surface,
|
||
|
wl_fixed_t* x,
|
||
|
wl_fixed_t* y) {
|
||
|
if (ctx->use_direct_scale) {
|
||
|
sl_transform_direct_to_guest_fixed(ctx, surface, x, y);
|
||
|
} else {
|
||
|
double dx = wl_fixed_to_double(*x);
|
||
|
double dy = wl_fixed_to_double(*y);
|
||
|
|
||
|
dx *= ctx->scale;
|
||
|
dy *= ctx->scale;
|
||
|
|
||
|
*x = wl_fixed_from_double(dx);
|
||
|
*y = wl_fixed_from_double(dy);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void sl_transform_host_to_guest_fixed(struct sl_context* ctx,
|
||
|
struct sl_host_surface* surface,
|
||
|
wl_fixed_t* coord,
|
||
|
uint32_t axis) {
|
||
|
if (ctx->use_direct_scale) {
|
||
|
sl_transform_direct_to_guest_fixed(ctx, surface, coord, axis);
|
||
|
} else {
|
||
|
double dx = wl_fixed_to_double(*coord);
|
||
|
|
||
|
dx *= ctx->scale;
|
||
|
*coord = wl_fixed_from_double(dx);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void sl_transform_guest_to_host(struct sl_context* ctx,
|
||
|
struct sl_host_surface* surface,
|
||
|
int32_t* x,
|
||
|
int32_t* y) {
|
||
|
if (ctx->use_direct_scale) {
|
||
|
sl_transform_direct_to_host(ctx, surface, x, y);
|
||
|
} else {
|
||
|
(*x) /= ctx->scale;
|
||
|
(*y) /= ctx->scale;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void sl_transform_guest_to_host_fixed(struct sl_context* ctx,
|
||
|
struct sl_host_surface* surface,
|
||
|
wl_fixed_t* x,
|
||
|
wl_fixed_t* y) {
|
||
|
if (ctx->use_direct_scale) {
|
||
|
sl_transform_direct_to_host_fixed(ctx, surface, x, y);
|
||
|
} else {
|
||
|
double dx = wl_fixed_to_double(*x);
|
||
|
double dy = wl_fixed_to_double(*y);
|
||
|
|
||
|
dx /= ctx->scale;
|
||
|
dy /= ctx->scale;
|
||
|
|
||
|
*x = wl_fixed_from_double(dx);
|
||
|
*y = wl_fixed_from_double(dy);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void sl_transform_guest_to_host_fixed(struct sl_context* ctx,
|
||
|
struct sl_host_surface* surface,
|
||
|
wl_fixed_t* coord,
|
||
|
uint32_t axis) {
|
||
|
if (ctx->use_direct_scale) {
|
||
|
sl_transform_direct_to_host_fixed(ctx, surface, coord, axis);
|
||
|
} else {
|
||
|
double dx = wl_fixed_to_double(*coord);
|
||
|
|
||
|
dx /= ctx->scale;
|
||
|
*coord = wl_fixed_from_double(dx);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void sl_transform_try_window_scale(struct sl_context* ctx,
|
||
|
struct sl_host_surface* surface,
|
||
|
int32_t width_in_pixels,
|
||
|
int32_t height_in_pixels) {
|
||
|
int32_t reverse_width = width_in_pixels;
|
||
|
int32_t reverse_height = height_in_pixels;
|
||
|
int32_t logical_width;
|
||
|
int32_t logical_height;
|
||
|
|
||
|
// This function should only have an effect in direct scale mode
|
||
|
if (!ctx->use_direct_scale)
|
||
|
return;
|
||
|
|
||
|
// Reset scale so that calls to sl_transform_get_scale_factors will not
|
||
|
// use the current scale.
|
||
|
sl_transform_reset_surface_scale(ctx, surface);
|
||
|
|
||
|
// Transform the window dimensions using the global/per-output scaling factors
|
||
|
sl_transform_guest_to_host(ctx, surface, &reverse_width, &reverse_height);
|
||
|
|
||
|
// Save the logical dimensions for later use
|
||
|
logical_width = reverse_width;
|
||
|
logical_height = reverse_height;
|
||
|
|
||
|
// Transform the logical dimensions back to the virtual pixel dimensions
|
||
|
sl_transform_host_to_guest(ctx, surface, &reverse_width, &reverse_height);
|
||
|
|
||
|
// If the computed logical width or height is zero, force the
|
||
|
// use of the global scaling factors
|
||
|
|
||
|
if ((reverse_width != width_in_pixels ||
|
||
|
reverse_height != height_in_pixels) &&
|
||
|
(logical_width > 0 && logical_height > 0)) {
|
||
|
// There is no match, let's override the scaling setting on our surface
|
||
|
surface->has_own_scale = 1;
|
||
|
surface->xdg_scale_x = static_cast<double>(width_in_pixels) /
|
||
|
static_cast<double>(logical_width);
|
||
|
surface->xdg_scale_y = static_cast<double>(height_in_pixels) /
|
||
|
static_cast<double>(logical_height);
|
||
|
|
||
|
surface->cached_logical_height = logical_height;
|
||
|
surface->cached_logical_width = logical_width;
|
||
|
|
||
|
// Try once more to do a full cycle (pixel -> logical -> pixel),
|
||
|
// if we aren't equal, we need to force a round up on the translation
|
||
|
// to the guest.
|
||
|
|
||
|
reverse_width = width_in_pixels;
|
||
|
reverse_height = height_in_pixels;
|
||
|
|
||
|
sl_transform_guest_to_host(ctx, surface, &reverse_width, &reverse_height);
|
||
|
sl_transform_host_to_guest(ctx, surface, &reverse_width, &reverse_height);
|
||
|
|
||
|
if (reverse_width != width_in_pixels)
|
||
|
surface->scale_round_on_x = true;
|
||
|
|
||
|
if (reverse_height != height_in_pixels)
|
||
|
surface->scale_round_on_y = true;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void sl_transform_reset_surface_scale(struct sl_context* ctx,
|
||
|
struct sl_host_surface* surface) {
|
||
|
surface->has_own_scale = 0;
|
||
|
surface->scale_round_on_x = surface->scale_round_on_y = false;
|
||
|
surface->xdg_scale_x = surface->xdg_scale_y = 0;
|
||
|
}
|
||
|
|
||
|
void sl_transform_output_dimensions(struct sl_context* ctx,
|
||
|
int32_t* width,
|
||
|
int32_t* height) {
|
||
|
*width = (*width) * ctx->scale;
|
||
|
*height = (*height) * ctx->scale;
|
||
|
}
|