193 lines
5.3 KiB
C++
193 lines
5.3 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 <assert.h>
|
||
|
#include <pixman.h>
|
||
|
|
||
|
#include "sommelier.h" // NOLINT(build/include_directory)
|
||
|
#include "sommelier-tracing.h" // NOLINT(build/include_directory)
|
||
|
#include "sommelier-xshape.h" // NOLINT(build/include_directory)
|
||
|
|
||
|
static void sl_clear_shape_region(sl_window* window) {
|
||
|
window->shaped = false;
|
||
|
pixman_region32_fini(&window->shape_rectangles);
|
||
|
}
|
||
|
|
||
|
static void sl_attach_shape_region(struct sl_context* ctx,
|
||
|
xcb_window_t window) {
|
||
|
sl_window* sl_window = nullptr;
|
||
|
xcb_shape_get_rectangles_reply_t* reply;
|
||
|
int i;
|
||
|
|
||
|
sl_window = sl_lookup_window(ctx, window);
|
||
|
if (!sl_window)
|
||
|
return;
|
||
|
|
||
|
reply = xcb_shape_get_rectangles_reply(
|
||
|
ctx->connection,
|
||
|
xcb_shape_get_rectangles(ctx->connection, window, XCB_SHAPE_SK_BOUNDING),
|
||
|
NULL);
|
||
|
|
||
|
if (!reply)
|
||
|
return;
|
||
|
|
||
|
int nrects = xcb_shape_get_rectangles_rectangles_length(reply);
|
||
|
xcb_rectangle_t* rects = xcb_shape_get_rectangles_rectangles(reply);
|
||
|
|
||
|
if (!rects || nrects <= 0)
|
||
|
return;
|
||
|
|
||
|
pixman_box32_t* boxes =
|
||
|
static_cast<pixman_box32_t*>(calloc(sizeof(pixman_box32_t), nrects));
|
||
|
|
||
|
if (!boxes) {
|
||
|
free(reply);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
for (i = 0; i < nrects; i++) {
|
||
|
boxes[i].x1 = rects[i].x;
|
||
|
boxes[i].y1 = rects[i].y;
|
||
|
|
||
|
boxes[i].x2 = rects[i].x + rects[i].width;
|
||
|
boxes[i].y2 = rects[i].y + rects[i].height;
|
||
|
}
|
||
|
|
||
|
pixman_region32_init_rects(&sl_window->shape_rectangles, boxes, nrects);
|
||
|
|
||
|
free(boxes);
|
||
|
free(reply);
|
||
|
sl_window->shaped = true;
|
||
|
}
|
||
|
|
||
|
void sl_handle_shape_notify(struct sl_context* ctx,
|
||
|
struct xcb_shape_notify_event_t* event) {
|
||
|
sl_window* window = nullptr;
|
||
|
|
||
|
window = sl_lookup_window(ctx, event->affected_window);
|
||
|
|
||
|
if (!window)
|
||
|
return;
|
||
|
|
||
|
sl_clear_shape_region(window);
|
||
|
|
||
|
if (event->shaped)
|
||
|
sl_attach_shape_region(ctx, event->affected_window);
|
||
|
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
void sl_shape_query(struct sl_context* ctx, xcb_window_t xwindow) {
|
||
|
xcb_shape_query_extents_reply_t* reply;
|
||
|
sl_window* sl_window = nullptr;
|
||
|
|
||
|
sl_window = sl_lookup_window(ctx, xwindow);
|
||
|
if (!sl_window)
|
||
|
return;
|
||
|
|
||
|
reply = xcb_shape_query_extents_reply(
|
||
|
ctx->connection, xcb_shape_query_extents(ctx->connection, xwindow), NULL);
|
||
|
|
||
|
if (!reply)
|
||
|
return;
|
||
|
|
||
|
sl_clear_shape_region(sl_window);
|
||
|
|
||
|
if (reply->bounding_shaped) {
|
||
|
sl_attach_shape_region(ctx, xwindow);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
pixman_format_code_t sl_pixman_format_for_shm_format(uint32_t shm_format) {
|
||
|
pixman_format_code_t fmt = PIXMAN_a1;
|
||
|
|
||
|
switch (shm_format) {
|
||
|
case WL_SHM_FORMAT_ARGB8888:
|
||
|
fmt = PIXMAN_a8r8g8b8;
|
||
|
break;
|
||
|
|
||
|
case WL_SHM_FORMAT_XRGB8888:
|
||
|
fmt = PIXMAN_x8r8g8b8;
|
||
|
break;
|
||
|
|
||
|
case WL_SHM_FORMAT_ABGR8888:
|
||
|
fmt = PIXMAN_a8b8g8r8;
|
||
|
break;
|
||
|
|
||
|
case WL_SHM_FORMAT_XBGR8888:
|
||
|
fmt = PIXMAN_x8b8g8r8;
|
||
|
break;
|
||
|
|
||
|
case WL_SHM_FORMAT_RGB565:
|
||
|
fmt = PIXMAN_r5g6b5;
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
assert(0);
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
return fmt;
|
||
|
}
|
||
|
|
||
|
void sl_xshape_generate_argb_image(struct sl_context* ctx,
|
||
|
pixman_region32_t* shape,
|
||
|
struct sl_mmap* src_mmap,
|
||
|
pixman_image_t* dst_image,
|
||
|
uint32_t src_shm_format) {
|
||
|
int buf_width, buf_height, nrects;
|
||
|
pixman_region32_t intersect_rects;
|
||
|
pixman_image_t* src;
|
||
|
|
||
|
assert(ctx);
|
||
|
assert(shape);
|
||
|
assert(src_mmap);
|
||
|
assert(dst_image);
|
||
|
|
||
|
buf_width = pixman_image_get_width(dst_image);
|
||
|
buf_height = pixman_image_get_height(dst_image);
|
||
|
|
||
|
if (buf_width <= 0 || buf_height <= 0)
|
||
|
return;
|
||
|
|
||
|
// Intersect with the pixmap bounds to ensure we do not perform
|
||
|
// any OOB accesses
|
||
|
// In addition, we can assume the dimensions of the dst_image is
|
||
|
// the same size as the input image
|
||
|
|
||
|
pixman_region32_init(&intersect_rects);
|
||
|
pixman_region32_intersect_rect(&intersect_rects, shape, 0, 0, buf_width,
|
||
|
buf_height);
|
||
|
|
||
|
// With the blank destination image, we will take the source image and the
|
||
|
// shape rectangles and generate the "stamped out" ARGB image.
|
||
|
//
|
||
|
// This is accomplished by clearing out the destination image to be
|
||
|
// completely transparent as a first step. Then for each rectangular
|
||
|
// region within the shape data, we will use pixman_image_composite to
|
||
|
// copy that portion of the image from the source to the ARGB stamp out
|
||
|
// buffer.
|
||
|
//
|
||
|
// pixman_image_composite is used as it will automatically perform pixel
|
||
|
// format conversion for us.
|
||
|
|
||
|
src = pixman_image_create_bits_no_clear(
|
||
|
sl_pixman_format_for_shm_format(src_shm_format), buf_width, buf_height,
|
||
|
reinterpret_cast<uint32_t*>(src_mmap->addr), src_mmap->stride[0]);
|
||
|
|
||
|
pixman_box32_t* rects = pixman_region32_rectangles(&intersect_rects, &nrects);
|
||
|
|
||
|
pixman_color_t clear = {.red = 0, .green = 0, .blue = 0, .alpha = 0};
|
||
|
pixman_box32_t dstbox = {.x1 = 0, .y1 = 0, .x2 = buf_width, .y2 = buf_height};
|
||
|
|
||
|
pixman_image_fill_boxes(PIXMAN_OP_SRC, dst_image, &clear, 1, &dstbox);
|
||
|
|
||
|
for (int i = 0; i < nrects; i++) {
|
||
|
pixman_image_composite(PIXMAN_OP_SRC, src, NULL, dst_image, rects[i].x1,
|
||
|
rects[i].y1, 0, 0, rects[i].x1, rects[i].y1,
|
||
|
(rects[i].x2 - rects[i].x1),
|
||
|
(rects[i].y2 - rects[i].y1));
|
||
|
}
|
||
|
}
|