File: render.c

package info (click to toggle)
grim 1.1+ds-1
  • links: PTS
  • area: main
  • in suites: buster
  • size: 148 kB
  • sloc: ansic: 780; xml: 152; makefile: 4
file content (130 lines) | stat: -rw-r--r-- 3,950 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
#include <assert.h>
#include <stdio.h>

#include "buffer.h"
#include "output-layout.h"
#include "render.h"

static bool convert_buffer(struct grim_buffer *buffer) {
	// Formats are little-endian
	switch (buffer->format) {
	case WL_SHM_FORMAT_ARGB8888:
	case WL_SHM_FORMAT_XRGB8888:
		// Natively supported by Cairo
		return true;
	case WL_SHM_FORMAT_ABGR8888:
	case WL_SHM_FORMAT_XBGR8888:;
		// ABGR -> ARGB
		uint8_t *data = buffer->data;
		for (int i = 0; i < buffer->height; ++i) {
			for (int j = 0; j < buffer->width; ++j) {
				uint32_t *px = (uint32_t *)(data + i * buffer->stride + j * 4);
				uint8_t a = (*px >> 24) & 0xFF;
				uint8_t b = (*px >> 16) & 0xFF;
				uint8_t g = (*px >> 8) & 0xFF;
				uint8_t r = *px & 0xFF;
				*px = (a << 24) | (r << 16) | (g << 8) | b;
			}
		}
		if (buffer->format == WL_SHM_FORMAT_ABGR8888) {
			buffer->format = WL_SHM_FORMAT_ARGB8888;
		} else {
			buffer->format = WL_SHM_FORMAT_XRGB8888;
		}
		return true;
	default:
		fprintf(stderr, "unsupported format %d\n", buffer->format);
		return false;
	}
	assert(false);
}

static cairo_format_t get_cairo_format(enum wl_shm_format wl_fmt) {
	switch (wl_fmt) {
	case WL_SHM_FORMAT_ARGB8888:
		return CAIRO_FORMAT_ARGB32;
	case WL_SHM_FORMAT_XRGB8888:
		return CAIRO_FORMAT_RGB24;
	default:
		return CAIRO_FORMAT_INVALID;
	}
	assert(false);
}

cairo_surface_t *render(struct grim_state *state, struct grim_box *geometry,
		double scale) {
	cairo_surface_t *surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32,
		geometry->width * scale, geometry->height * scale);
	cairo_t *cairo = cairo_create(surface);

	// Clear
	cairo_save(cairo);
	cairo_set_source_rgba(cairo, 0, 0, 0, 0);
	cairo_set_operator(cairo, CAIRO_OPERATOR_SOURCE);
	cairo_paint(cairo);
	cairo_restore(cairo);

	struct grim_output *output;
	wl_list_for_each(output, &state->outputs, link) {
		struct grim_buffer *buffer = output->buffer;
		if (buffer == NULL) {
			continue;
		}

		if (!convert_buffer(buffer)) {
			return NULL;
		}

		cairo_format_t cairo_fmt = get_cairo_format(buffer->format);
		assert(cairo_fmt != CAIRO_FORMAT_INVALID);

		int32_t output_x = output->logical_geometry.x - geometry->x;
		int32_t output_y = output->logical_geometry.y - geometry->y;
		int32_t output_width = output->logical_geometry.width;
		int32_t output_height = output->logical_geometry.height;

		int32_t raw_output_width = output->geometry.width;
		int32_t raw_output_height = output->geometry.height;
		apply_output_transform(output->transform,
			&raw_output_width, &raw_output_height);

		int output_flipped_x = get_output_flipped(output->transform);
		int output_flipped_y = output->screencopy_frame_flags &
			ZWLR_SCREENCOPY_FRAME_V1_FLAGS_Y_INVERT ? -1 : 1;

		cairo_surface_t *output_surface = cairo_image_surface_create_for_data(
			buffer->data, cairo_fmt, buffer->width, buffer->height,
			buffer->stride);
		cairo_pattern_t *output_pattern =
			cairo_pattern_create_for_surface(output_surface);

		// All transformations are in pattern-local coordinates
		cairo_matrix_t matrix;
		cairo_matrix_init_identity(&matrix);
		cairo_matrix_translate(&matrix,
			(double)output->geometry.width / 2,
			(double)output->geometry.height / 2);
		cairo_matrix_rotate(&matrix, -get_output_rotation(output->transform));
		cairo_matrix_scale(&matrix,
			(double)raw_output_width / output_width * output_flipped_x,
			(double)raw_output_height / output_height * output_flipped_y);
		cairo_matrix_translate(&matrix,
			-(double)output_width / 2,
			-(double)output_height / 2);
		cairo_matrix_translate(&matrix, -output_x, -output_y);
		cairo_matrix_scale(&matrix, 1 / scale, 1 / scale);
		cairo_pattern_set_matrix(output_pattern, &matrix);

		cairo_pattern_set_filter(output_pattern, CAIRO_FILTER_BEST);

		cairo_set_source(cairo, output_pattern);
		cairo_pattern_destroy(output_pattern);

		cairo_paint(cairo);

		cairo_surface_destroy(output_surface);
	}

	cairo_destroy(cairo);
	return surface;
}