File: pass.c

package info (click to toggle)
phoc 0.51.0-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 6,948 kB
  • sloc: ansic: 106,056; xml: 3,765; sh: 138; makefile: 33; javascript: 5
file content (253 lines) | stat: -rw-r--r-- 8,939 bytes parent folder | download | duplicates (4)
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
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
#include <assert.h>
#include <stdlib.h>
#include "render/pixman.h"

static const struct wlr_render_pass_impl render_pass_impl;

static struct wlr_pixman_render_pass *get_render_pass(struct wlr_render_pass *wlr_pass) {
	assert(wlr_pass->impl == &render_pass_impl);
	struct wlr_pixman_render_pass *pass = wl_container_of(wlr_pass, pass, base);
	return pass;
}

static struct wlr_pixman_texture *get_texture(struct wlr_texture *wlr_texture) {
	assert(wlr_texture_is_pixman(wlr_texture));
	struct wlr_pixman_texture *texture = wl_container_of(wlr_texture, texture, wlr_texture);
	return texture;
}

static bool render_pass_submit(struct wlr_render_pass *wlr_pass) {
	struct wlr_pixman_render_pass *pass = get_render_pass(wlr_pass);

	wlr_buffer_end_data_ptr_access(pass->buffer->buffer);
	wlr_buffer_unlock(pass->buffer->buffer);
	free(pass);

	return true;
}

static pixman_op_t get_pixman_blending(enum wlr_render_blend_mode mode) {
	switch (mode) {
	case WLR_RENDER_BLEND_MODE_PREMULTIPLIED:
		return PIXMAN_OP_OVER;
	case WLR_RENDER_BLEND_MODE_NONE:
		return PIXMAN_OP_SRC;
	}
	abort();
}

static void render_pass_add_texture(struct wlr_render_pass *wlr_pass,
		const struct wlr_render_texture_options *options) {
	struct wlr_pixman_render_pass *pass = get_render_pass(wlr_pass);
	struct wlr_pixman_texture *texture = get_texture(options->texture);
	struct wlr_pixman_buffer *buffer = pass->buffer;

	if (texture->buffer != NULL && !begin_pixman_data_ptr_access(texture->buffer,
			&texture->image, WLR_BUFFER_DATA_PTR_ACCESS_READ)) {
		return;
	}

	pixman_op_t op = get_pixman_blending(options->blend_mode);
	pixman_image_set_clip_region32(buffer->image, options->clip);

	struct wlr_fbox src_fbox;
	wlr_render_texture_options_get_src_box(options, &src_fbox);
	struct wlr_box src_box = {
		.x = roundf(src_fbox.x),
		.y = roundf(src_fbox.y),
		.width = roundf(src_fbox.width),
		.height = roundf(src_fbox.height),
	};

	struct wlr_box dst_box;
	wlr_render_texture_options_get_dst_box(options, &dst_box);

	pixman_image_t *mask = NULL;
	float alpha = wlr_render_texture_options_get_alpha(options);
	if (alpha != 1) {
		mask = pixman_image_create_solid_fill(&(struct pixman_color){
			.alpha = 0xFFFF * alpha,
		});
	}

	// Rotate the source size into destination coordinates
	struct wlr_box src_box_transformed;
	wlr_box_transform(&src_box_transformed, &src_box, options->transform,
		buffer->buffer->width, buffer->buffer->height);

	if (options->transform != WL_OUTPUT_TRANSFORM_NORMAL ||
			src_box_transformed.width != dst_box.width ||
			src_box_transformed.height != dst_box.height) {
		// Cosinus/sinus values are extact integers for enum wl_output_transform entries
		int tr_cos = 1, tr_sin = 0, tr_x = 0, tr_y = 0;
		switch (options->transform) {
		case WL_OUTPUT_TRANSFORM_NORMAL:
		case WL_OUTPUT_TRANSFORM_FLIPPED:
			break;
		case WL_OUTPUT_TRANSFORM_90:
		case WL_OUTPUT_TRANSFORM_FLIPPED_90:
			tr_cos = 0;
			tr_sin = 1;
			tr_y = src_box.width;
			break;
		case WL_OUTPUT_TRANSFORM_180:
		case WL_OUTPUT_TRANSFORM_FLIPPED_180:
			tr_cos = -1;
			tr_sin = 0;
			tr_x = src_box.width;
			tr_y = src_box.height;
			break;
		case WL_OUTPUT_TRANSFORM_270:
		case WL_OUTPUT_TRANSFORM_FLIPPED_270:
			tr_cos = 0;
			tr_sin = -1;
			tr_x = src_box.height;
			break;
		}

		// Pixman transforms are generally the opposite of what you expect because they
		// apply to the coordinate system rather than the image.  The comments here
		// refer to what happens to the image, so all the code between
		// pixman_transform_init_identity() and pixman_image_set_transform() is probably
		// best read backwards.  Also this means translations are in the opposite
		// direction, imagine them as moving the origin around rather than moving the
		// image.
		//
		// Beware that this doesn't work quite the same as wp_viewporter: We apply crop
		// before transform and scale, whereas it defines crop in post-transform-scale
		// coordinates.  But this only applies to internal wlroots code - the viewporter
		// extension code makes sure that to clients everything works as it should.

		struct pixman_transform transform;
		pixman_transform_init_identity(&transform);

		// Apply scaling to get to the dst_box size.  Because the scaling is applied last
		// it depends on the whether the rotation swapped width and height, which is why
		// we use src_box_transformed instead of src_box.
		pixman_transform_scale(&transform, NULL,
			pixman_double_to_fixed(src_box_transformed.width / (double)dst_box.width),
			pixman_double_to_fixed(src_box_transformed.height / (double)dst_box.height));

		// pixman rotates about the origin which again leaves everything outside of the
		// viewport.  Translate the result so that its new top-left corner is back at the
		// origin.
		pixman_transform_translate(&transform, NULL,
			-pixman_int_to_fixed(tr_x), -pixman_int_to_fixed(tr_y));

		// Apply the rotation
		pixman_transform_rotate(&transform, NULL,
			pixman_int_to_fixed(tr_cos), pixman_int_to_fixed(tr_sin));

		// Apply flip before rotation
		if (options->transform >= WL_OUTPUT_TRANSFORM_FLIPPED) {
			// The flip leaves everything left of the Y axis which is outside the
			// viewport. So translate everything back into the viewport.
			pixman_transform_translate(&transform, NULL,
				-pixman_int_to_fixed(src_box.width), pixman_int_to_fixed(0));
			// Flip by applying a scale of -1 to the X axis
			pixman_transform_scale(&transform, NULL,
				pixman_int_to_fixed(-1), pixman_int_to_fixed(1));
		}

		// Apply the translation for source crop so the origin is now at the top-left of
		// the region we're actually using.  Do this last so all the other transforms
		// apply on top of this.
		pixman_transform_translate(&transform, NULL,
			pixman_int_to_fixed(src_box.x), pixman_int_to_fixed(src_box.y));

		pixman_image_set_transform(texture->image, &transform);

		switch (options->filter_mode) {
		case WLR_SCALE_FILTER_BILINEAR:
			pixman_image_set_filter(texture->image, PIXMAN_FILTER_BILINEAR, NULL, 0);
			break;
		case WLR_SCALE_FILTER_NEAREST:
			pixman_image_set_filter(texture->image, PIXMAN_FILTER_NEAREST, NULL, 0);
			break;
		}

		// Now composite the result onto the pass buffer.  We specify a source origin of 0,0
		// because the x,y part of source crop is already done using the transform. The
		// width,height part of source crop is done here by the width and height we pass:
		// because of the scaling, cropping at the end by dst_box.{width,height} is
		// equivalent to if we cropped at the start by src_box.{width,height}.
		pixman_image_composite32(op, texture->image, mask, buffer->image,
			0, 0, // source x,y
			0, 0, // mask x,y
			dst_box.x, dst_box.y, // dest x,y
			dst_box.width, dst_box.height // composite width,height
		);

		pixman_image_set_transform(texture->image, NULL);
	} else {
		// No transforms or crop needed, just a straight blit from the source
		pixman_image_set_transform(texture->image, NULL);
		pixman_image_composite32(op, texture->image, mask, buffer->image,
			src_box.x, src_box.y, 0, 0, dst_box.x, dst_box.y,
			src_box.width, src_box.height);
	}

	pixman_image_set_clip_region32(buffer->image, NULL);

	if (texture->buffer != NULL) {
		wlr_buffer_end_data_ptr_access(texture->buffer);
	}

	if (mask != NULL) {
		pixman_image_unref(mask);
	}
}

static void render_pass_add_rect(struct wlr_render_pass *wlr_pass,
		const struct wlr_render_rect_options *options) {
	struct wlr_pixman_render_pass *pass = get_render_pass(wlr_pass);
	struct wlr_pixman_buffer *buffer = pass->buffer;
	struct wlr_box box;
	wlr_render_rect_options_get_box(options, pass->buffer->buffer, &box);

	pixman_op_t op = get_pixman_blending(options->color.a == 1 ?
		WLR_RENDER_BLEND_MODE_NONE : options->blend_mode);

	struct pixman_color color = {
		.red = options->color.r * 0xFFFF,
		.green = options->color.g * 0xFFFF,
		.blue = options->color.b * 0xFFFF,
		.alpha = options->color.a * 0xFFFF,
	};

	pixman_image_t *fill = pixman_image_create_solid_fill(&color);

	pixman_image_set_clip_region32(buffer->image, options->clip);
	pixman_image_composite32(op, fill, NULL, buffer->image,
		0, 0, 0, 0, box.x, box.y, box.width, box.height);
	pixman_image_set_clip_region32(buffer->image, NULL);

	pixman_image_unref(fill);
}

static const struct wlr_render_pass_impl render_pass_impl = {
	.submit = render_pass_submit,
	.add_texture = render_pass_add_texture,
	.add_rect = render_pass_add_rect,
};

struct wlr_pixman_render_pass *begin_pixman_render_pass(
		struct wlr_pixman_buffer *buffer) {
	struct wlr_pixman_render_pass *pass = calloc(1, sizeof(*pass));
	if (pass == NULL) {
		return NULL;
	}

	wlr_render_pass_init(&pass->base, &render_pass_impl);

	if (!begin_pixman_data_ptr_access(buffer->buffer, &buffer->image,
			WLR_BUFFER_DATA_PTR_ACCESS_READ | WLR_BUFFER_DATA_PTR_ACCESS_WRITE)) {
		free(pass);
		return NULL;
	}

	wlr_buffer_lock(buffer->buffer);
	pass->buffer = buffer;

	return pass;
}