/*
 * Copyright © 2012 Intel Corporation
 * Copyright © 2013 Vasily Khoruzhick <anarsoul@gmail.com>
 * Copyright © 2015 Collabora, Ltd.
 *
 * Permission is hereby granted, free of charge, to any person obtaining
 * a copy of this software and associated documentation files (the
 * "Software"), to deal in the Software without restriction, including
 * without limitation the rights to use, copy, modify, merge, publish,
 * distribute, sublicense, and/or sell copies of the Software, and to
 * permit persons to whom the Software is furnished to do so, subject to
 * the following conditions:
 *
 * The above copyright notice and this permission notice (including the
 * next paragraph) shall be included in all copies or substantial
 * portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT.  IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

#include "config.h"

#include <errno.h>
#include <stdint.h>
#include <stdlib.h>
#include <assert.h>

#include "pixman-renderer.h"
#include "color.h"
#include "pixel-formats.h"
#include "output-capture.h"
#include "shared/helpers.h"
#include "shared/weston-drm-fourcc.h"
#include "shared/xalloc.h"

#include <linux/input.h>

struct pixman_output_state {
	pixman_image_t *shadow_image;
	const struct pixel_format_info *shadow_format;
	pixman_image_t *hw_buffer;
	const struct pixel_format_info *hw_format;
	struct weston_size fb_size;
	struct wl_list renderbuffer_list;
};

struct pixman_surface_state {
	struct weston_surface *surface;

	pixman_image_t *image;
	struct weston_buffer_reference buffer_ref;
	struct weston_buffer_release_reference buffer_release_ref;

	struct wl_listener buffer_destroy_listener;
	struct wl_listener surface_destroy_listener;
	struct wl_listener renderer_destroy_listener;
};

struct pixman_renderbuffer {
#if !defined(NDEBUG)
	struct weston_output *output;
#endif
	pixman_region32_t damage;
	pixman_image_t *image;
	bool stale;
	weston_renderbuffer_discarded_func discarded_cb;
	void *user_data;
	struct wl_list link;
};

struct pixman_renderer {
	struct weston_renderer base;

	int repaint_debug;
	pixman_image_t *debug_color;
	struct weston_binding *debug_binding;

	struct wl_signal destroy_signal;
};

static inline struct pixman_output_state *
get_output_state(struct weston_output *output)
{
	return (struct pixman_output_state *)output->renderer_state;
}

static int
pixman_renderer_create_surface(struct weston_surface *surface);

static inline struct pixman_surface_state *
get_surface_state(struct weston_surface *surface)
{
	if (!surface->renderer_state)
		pixman_renderer_create_surface(surface);

	return (struct pixman_surface_state *)surface->renderer_state;
}

static inline struct pixman_renderer *
get_renderer(struct weston_compositor *ec)
{
	return (struct pixman_renderer *)ec->renderer;
}

#define D2F(v) pixman_double_to_fixed((double)v)

static void
weston_matrix_to_pixman_transform(pixman_transform_t *pt,
				  const struct weston_matrix *wm)
{
	/* Pixman supports only 2D transform matrix, but Weston uses 3D, *
	 * so we're omitting Z coordinate here. */
	pt->matrix[0][0] = pixman_double_to_fixed(wm->M.col[0].x);
	pt->matrix[0][1] = pixman_double_to_fixed(wm->M.col[1].x);
	pt->matrix[0][2] = pixman_double_to_fixed(wm->M.col[3].x);
	pt->matrix[1][0] = pixman_double_to_fixed(wm->M.col[0].y);
	pt->matrix[1][1] = pixman_double_to_fixed(wm->M.col[1].y);
	pt->matrix[1][2] = pixman_double_to_fixed(wm->M.col[3].y);
	pt->matrix[2][0] = pixman_double_to_fixed(wm->M.col[0].w);
	pt->matrix[2][1] = pixman_double_to_fixed(wm->M.col[1].w);
	pt->matrix[2][2] = pixman_double_to_fixed(wm->M.col[3].w);
}

static bool
view_transformation_is_translation(struct weston_view *view)
{
	if (!view->transform.enabled)
		return true;

	if (view->transform.matrix.type <= WESTON_MATRIX_TRANSFORM_TRANSLATE)
		return true;

	return false;
}

static void
region_intersect_only_translation(pixman_region32_t *result_global,
				  pixman_region32_t *global,
				  pixman_region32_t *surf,
				  struct weston_view *view)
{
	struct weston_coord_surface cs;
	struct weston_coord_global cg;

	cs = weston_coord_surface(0, 0, view->surface);
	assert(view_transformation_is_translation(view));

	/* Convert from surface to global coordinates */
	pixman_region32_copy(result_global, surf);
	cg = weston_coord_surface_to_global(view, cs);
	pixman_region32_translate(result_global, cg.c.x, cg.c.y);

	pixman_region32_intersect(result_global, result_global, global);
}

static void
composite_whole(pixman_op_t op,
		pixman_image_t *src,
		pixman_image_t *mask,
		pixman_image_t *dest,
		const pixman_transform_t *transform,
		pixman_filter_t filter)
{
	int32_t dest_width;
	int32_t dest_height;

	dest_width = pixman_image_get_width(dest);
	dest_height = pixman_image_get_height(dest);

	pixman_image_set_transform(src, transform);
	pixman_image_set_filter(src, filter, NULL, 0);

	/* bilinear filtering needs the equivalent of OpenGL CLAMP_TO_EDGE */
	if (filter == PIXMAN_FILTER_NEAREST)
		pixman_image_set_repeat(src, PIXMAN_REPEAT_NONE);
	else
		pixman_image_set_repeat(src, PIXMAN_REPEAT_PAD);

	pixman_image_composite32(op, src, mask, dest,
				 0, 0, /* src_x, src_y */
				 0, 0, /* mask_x, mask_y */
				 0, 0, /* dest_x, dest_y */
				 dest_width, dest_height);
}

static void
composite_clipped(struct weston_output *output,
		  pixman_image_t *src,
		  pixman_image_t *mask,
		  pixman_image_t *dest,
		  const pixman_transform_t *transform,
		  pixman_filter_t filter,
		  pixman_region32_t *src_clip)
{
	int n_box;
	pixman_box32_t *boxes;
	int32_t dest_width;
	int32_t dest_height;
	int src_stride;
	int bitspp;
	pixman_format_code_t src_format;
	void *src_data;
	int i;

	/*
	 * Hardcoded to use PIXMAN_OP_OVER, because sampling outside of
	 * a Pixman image produces (0,0,0,0) instead of discarding the
	 * fragment.
	 *
	 * Also repeat mode must be PIXMAN_REPEAT_NONE (the default) to
	 * actually sample (0,0,0,0). This may cause issues for clients that
	 * expect OpenGL CLAMP_TO_EDGE sampling behavior on their buffer.
	 * Using temporary 'boximg' it is not possible to apply CLAMP_TO_EDGE
	 * correctly with bilinear filter. Maybe trapezoid rendering could be
	 * the answer instead of source clip?
	 */

	dest_width = pixman_image_get_width(dest);
	dest_height = pixman_image_get_height(dest);
	src_format = pixman_image_get_format(src);
	src_stride = pixman_image_get_stride(src);
	bitspp = PIXMAN_FORMAT_BPP(src_format);
	src_data = pixman_image_get_data(src);

	assert(src_format);

	/* This would be massive overdraw, except when n_box is 1. */
	boxes = pixman_region32_rectangles(src_clip, &n_box);
	for (i = 0; i < n_box; i++) {
		uint8_t *ptr = src_data;
		pixman_image_t *boximg;
		pixman_transform_t adj = *transform;

		ptr += boxes[i].y1 * src_stride;
		ptr += boxes[i].x1 * bitspp / 8;
		boximg = pixman_image_create_bits_no_clear(src_format,
					boxes[i].x2 - boxes[i].x1,
					boxes[i].y2 - boxes[i].y1,
					(uint32_t *)ptr, src_stride);

		pixman_transform_translate(&adj, NULL,
					   pixman_int_to_fixed(-boxes[i].x1),
					   pixman_int_to_fixed(-boxes[i].y1));
		pixman_image_set_transform(boximg, &adj);

		pixman_image_set_filter(boximg, filter, NULL, 0);
		pixman_image_composite32(PIXMAN_OP_OVER, boximg, mask, dest,
					 0, 0, /* src_x, src_y */
					 0, 0, /* mask_x, mask_y */
					 0, 0, /* dest_x, dest_y */
					 dest_width, dest_height);

		pixman_image_unref(boximg);
	}

	if (n_box > 1) {
		weston_log_paced(&output->pixman_overdraw_pacer, 1, 0,
				 "Pixman-renderer warning: %dx overdraw\n",
				 n_box);
	}
}

/** Paint an intersected region
 *
 * \param pnode The paint node to be painted.
 * \param repaint_output The region to be painted in output coordinates.
 * \param source_clip The region of the source image to use, in source image
 *                    coordinates. If NULL, use the whole source image.
 * \param pixman_op Compositing operator, either SRC or OVER.
 */
static void
repaint_region(struct weston_paint_node *pnode,
	       pixman_region32_t *repaint_output,
	       pixman_region32_t *source_clip,
	       pixman_op_t pixman_op)
{
	struct weston_output *output = pnode->output;
	struct pixman_renderer *pr =
		(struct pixman_renderer *) output->compositor->renderer;
	struct pixman_surface_state *ps = get_surface_state(pnode->surface);
	struct pixman_output_state *po = get_output_state(output);
	pixman_image_t *target_image;
	pixman_transform_t transform;
	pixman_filter_t filter;
	pixman_image_t *mask_image;
	pixman_color_t mask = { 0, };

	if (po->shadow_image)
		target_image = po->shadow_image;
	else
		target_image = po->hw_buffer;

 	/* Clip rendering to the damaged output region */
	pixman_image_set_clip_region32(target_image, repaint_output);

	weston_matrix_to_pixman_transform(&transform,
					  &pnode->output_to_buffer_matrix);

	if (pnode->needs_filtering)
		filter = PIXMAN_FILTER_BILINEAR;
	else
		filter = PIXMAN_FILTER_NEAREST;

	if (ps->buffer_ref.buffer)
		wl_shm_buffer_begin_access(ps->buffer_ref.buffer->shm_buffer);

	if (pnode->alpha < 1.0) {
		mask.alpha = 0xffff * pnode->alpha;
		mask_image = pixman_image_create_solid_fill(&mask);
	} else {
		mask_image = NULL;
	}

	if (source_clip)
		composite_clipped(output, ps->image, mask_image, target_image,
				  &transform, filter, source_clip);
	else
		composite_whole(pixman_op, ps->image, mask_image,
				target_image, &transform, filter);

	if (mask_image)
		pixman_image_unref(mask_image);

	if (ps->buffer_ref.buffer)
		wl_shm_buffer_end_access(ps->buffer_ref.buffer->shm_buffer);

	if (pr->repaint_debug)
		pixman_image_composite32(PIXMAN_OP_OVER,
					 pr->debug_color, /* src */
					 NULL /* mask */,
					 target_image, /* dest */
					 0, 0, /* src_x, src_y */
					 0, 0, /* mask_x, mask_y */
					 0, 0, /* dest_x, dest_y */
					 po->fb_size.width, /* width */
					 po->fb_size.height /* height */);

	pixman_image_set_clip_region32(target_image, NULL);
}

static void
draw_node_translated(struct weston_paint_node *pnode,
		     pixman_region32_t *repaint_global)
{
	struct weston_output *output = pnode->output;
	struct weston_surface *surface = pnode->surface;
	struct weston_view *view = pnode->view;

	/* non-opaque region in surface coordinates: */
	pixman_region32_t surface_blend;
	/* region to be painted in output coordinates: */
	pixman_region32_t repaint_output;

	pixman_region32_init(&repaint_output);

	/* Blended region is whole surface minus opaque region,
	 * unless surface alpha forces us to blend all.
	 */
	pixman_region32_init_rect(&surface_blend, 0, 0,
				  surface->width, surface->height);

	if (!(pnode->alpha < 1.0)) {
		pixman_region32_subtract(&surface_blend, &surface_blend,
					 &surface->opaque);

		if (pixman_region32_not_empty(&surface->opaque)) {
			region_intersect_only_translation(&repaint_output,
							  repaint_global,
							  &surface->opaque,
							  view);
			weston_region_global_to_output(&repaint_output,
						       output,
						       &repaint_output);

			repaint_region(pnode, &repaint_output, NULL,
				       PIXMAN_OP_SRC);
		}
	}

	if (pixman_region32_not_empty(&surface_blend)) {
		region_intersect_only_translation(&repaint_output,
						  repaint_global,
						  &surface_blend, view);
		weston_region_global_to_output(&repaint_output,
					       output,
					       &repaint_output);

		repaint_region(pnode, &repaint_output, NULL, PIXMAN_OP_OVER);
	}

	pixman_region32_fini(&surface_blend);
	pixman_region32_fini(&repaint_output);
}

static void
draw_node_source_clipped(struct weston_paint_node *pnode,
			 pixman_region32_t *repaint_global)
{
	struct weston_surface *surface = pnode->surface;
	struct weston_output *output = pnode->output;
	struct weston_view *view = pnode->view;
	pixman_region32_t surf_region;
	pixman_region32_t buffer_region;
	pixman_region32_t repaint_output;

	/* Do not bother separating the opaque region from non-opaque.
	 * Source clipping requires PIXMAN_OP_OVER in all cases, so painting
	 * opaque separately has no benefit.
	 */

	pixman_region32_init_rect(&surf_region, 0, 0,
				  surface->width, surface->height);
	if (view->geometry.scissor_enabled)
		pixman_region32_intersect(&surf_region, &surf_region,
					  &view->geometry.scissor);

	pixman_region32_init(&buffer_region);
	weston_surface_to_buffer_region(surface, &surf_region, &buffer_region);

	pixman_region32_init(&repaint_output);
	pixman_region32_copy(&repaint_output, repaint_global);
	weston_region_global_to_output(&repaint_output, output,
				       &repaint_output);

	repaint_region(pnode, &repaint_output, &buffer_region, PIXMAN_OP_OVER);

	pixman_region32_fini(&repaint_output);
	pixman_region32_fini(&buffer_region);
	pixman_region32_fini(&surf_region);
}

static void
draw_paint_node(struct weston_paint_node *pnode,
		pixman_region32_t *damage /* in global coordinates */)
{
	struct pixman_surface_state *ps = get_surface_state(pnode->surface);
	/* repaint bounding region in global coordinates: */
	pixman_region32_t repaint;

	if (!pnode->surf_xform_valid)
		return;

	assert(pnode->surf_xform.transform == NULL);

	/* No buffer attached */
	if (!ps->image)
		return;

	/* if we still have a reference, but the underlying buffer is no longer
	 * available signal that we should unref image_t as well. This happens
	 * when using close animations, with the reference surviving the
	 * animation while the underlying buffer went away as the client was
	 * terminated. This is a particular use-case and should probably be
	 * refactored to provide some analogue with the GL-renderer (as in, to
	 * still maintain the buffer and let the compositor dispose of it). */
	if (ps->buffer_ref.buffer && !ps->buffer_ref.buffer->shm_buffer) {
		pixman_image_unref(ps->image);
		ps->image = NULL;
		return;
	}

	pixman_region32_init(&repaint);
	pixman_region32_intersect(&repaint,
				  &pnode->visible, damage);

	if (!pixman_region32_not_empty(&repaint))
		goto out;

	if (view_transformation_is_translation(pnode->view)) {
		/* The simple case: The surface regions opaque, non-opaque,
		 * etc. are convertible to global coordinate space.
		 * There is no need to use a source clip region.
		 * It is possible to paint opaque region as PIXMAN_OP_SRC.
		 * Also the boundingbox is accurate rather than an
		 * approximation.
		 */
		draw_node_translated(pnode, &repaint);
	} else {
		/* The complex case: the view transformation does not allow
		 * converting opaque etc. regions into global coordinate space.
		 * Therefore we need source clipping to avoid sampling from
		 * unwanted source image areas, unless the source image is
		 * to be used whole. Source clipping does not work with
		 * PIXMAN_OP_SRC.
		 */
		draw_node_source_clipped(pnode, &repaint);
	}

out:
	pixman_region32_fini(&repaint);
}
static void
repaint_surfaces(struct weston_output *output, pixman_region32_t *damage)
{
	struct weston_paint_node *pnode;

	wl_list_for_each_reverse(pnode, &output->paint_node_z_order_list,
				 z_order_link) {
		if (pnode->plane == &output->primary_plane)
			draw_paint_node(pnode, damage);
	}
}

static void
copy_to_hw_buffer(struct weston_output *output, pixman_region32_t *region)
{
	struct pixman_output_state *po = get_output_state(output);
	pixman_region32_t output_region;

	pixman_region32_init(&output_region);
	pixman_region32_copy(&output_region, region);

	weston_region_global_to_output(&output_region, output, &output_region);

	pixman_image_set_clip_region32 (po->hw_buffer, &output_region);
	pixman_region32_fini(&output_region);

	pixman_image_composite32(PIXMAN_OP_SRC,
				 po->shadow_image, /* src */
				 NULL /* mask */,
				 po->hw_buffer, /* dest */
				 0, 0, /* src_x, src_y */
				 0, 0, /* mask_x, mask_y */
				 0, 0, /* dest_x, dest_y */
				 po->fb_size.width, /* width */
				 po->fb_size.height /* height */);

	pixman_image_set_clip_region32 (po->hw_buffer, NULL);
}

static void
pixman_renderer_do_capture(struct weston_buffer *into, pixman_image_t *from)
{
	struct wl_shm_buffer *shm = into->shm_buffer;
	pixman_image_t *dest;

	assert(into->type == WESTON_BUFFER_SHM);
	assert(shm);

	wl_shm_buffer_begin_access(shm);

	dest = pixman_image_create_bits(into->pixel_format->pixman_format,
					into->width, into->height,
					wl_shm_buffer_get_data(shm),
					into->stride);
	abort_oom_if_null(dest);

	pixman_image_composite32(PIXMAN_OP_SRC, from, NULL /* mask */, dest,
				 0, 0, /* src_x, src_y */
				 0, 0, /* mask_x, mask_y */
				 0, 0, /* dest_x, dest_y */
				 into->width, into->height);

	pixman_image_unref(dest);

	wl_shm_buffer_end_access(shm);
}

static void
pixman_renderer_do_capture_tasks(struct weston_output *output,
				 enum weston_output_capture_source source,
				 pixman_image_t *from,
				 const struct pixel_format_info *pfmt)
{
	int width = pixman_image_get_width(from);
	int height = pixman_image_get_height(from);
	struct weston_capture_task *ct;

	assert(source != WESTON_OUTPUT_CAPTURE_SOURCE_WRITEBACK);

	while ((ct = weston_output_pull_capture_task(output, source,
						     width, height,
						     pfmt, NULL))) {
		struct weston_buffer *buffer = weston_capture_task_get_buffer(ct);

		assert(buffer->width == width);
		assert(buffer->height == height);
		assert(buffer->pixel_format->format == pfmt->format);

		if (buffer->type != WESTON_BUFFER_SHM) {
			weston_capture_task_retire_failed(ct, "pixman: unsupported buffer");
			continue;
		}

		pixman_renderer_do_capture(buffer, from);
		weston_capture_task_retire_complete(ct);
	}
}

static void
pixman_renderer_output_set_buffer(struct weston_output *output,
				  pixman_image_t *buffer);

static void
pixman_renderer_repaint_output(struct weston_output *output,
			       pixman_region32_t *output_damage,
			       weston_renderbuffer_t renderbuffer)
{
	struct pixman_output_state *po = get_output_state(output);
	struct pixman_renderbuffer *rb;

	assert(po);
	assert(((struct pixman_renderbuffer *) renderbuffer)->output == output);

	/* Accumulate damage in all renderbuffers */
	wl_list_for_each(rb, &po->renderbuffer_list, link) {
		pixman_region32_union(&rb->damage, &rb->damage, output_damage);
	}

	rb = (struct pixman_renderbuffer *) renderbuffer;

	pixman_renderer_output_set_buffer(output, rb->image);

	assert(output->from_blend_to_output_by_backend ||
	       output->color_outcome->from_blend_to_output == NULL);

	if (!po->hw_buffer)
 		return;

	if (po->shadow_image) {
		repaint_surfaces(output, output_damage);
		pixman_renderer_do_capture_tasks(output,
						 WESTON_OUTPUT_CAPTURE_SOURCE_BLENDING,
						 po->shadow_image, po->shadow_format);
		copy_to_hw_buffer(output, &rb->damage);
	} else {
		repaint_surfaces(output, &rb->damage);
	}
	pixman_renderer_do_capture_tasks(output,
					 WESTON_OUTPUT_CAPTURE_SOURCE_FRAMEBUFFER,
					 po->hw_buffer, po->hw_format);
	pixman_region32_clear(&rb->damage);

	wl_signal_emit(&output->frame_signal, output_damage);

	/* Actual flip should be done by caller */
}

static void
pixman_renderer_flush_damage(struct weston_paint_node *pnode)
{
	/* No-op for pixman renderer */
}

static void
buffer_state_handle_buffer_destroy(struct wl_listener *listener, void *data)
{
	struct pixman_surface_state *ps;

	ps = container_of(listener, struct pixman_surface_state,
			  buffer_destroy_listener);

	if (ps->image) {
		pixman_image_unref(ps->image);
		ps->image = NULL;
	}

	ps->buffer_destroy_listener.notify = NULL;
}

static void
pixman_renderer_surface_set_color(struct weston_surface *es,
		 float red, float green, float blue, float alpha)
{
	struct pixman_surface_state *ps = get_surface_state(es);
	pixman_color_t color;

	color.red = red * 0xffff;
	color.green = green * 0xffff;
	color.blue = blue * 0xffff;
	color.alpha = alpha * 0xffff;

	if (ps->image) {
		pixman_image_unref(ps->image);
		ps->image = NULL;
	}

	ps->image = pixman_image_create_solid_fill(&color);
}

static void
pixman_renderer_attach(struct weston_paint_node *pnode)
{
	struct weston_surface *es = pnode->surface;
	struct weston_buffer *buffer = pnode->surface->buffer_ref.buffer;
	struct pixman_surface_state *ps = get_surface_state(es);
	struct wl_shm_buffer *shm_buffer;
	const struct pixel_format_info *pixel_info;

	weston_buffer_reference(&ps->buffer_ref, buffer,
				buffer ? BUFFER_MAY_BE_ACCESSED :
					 BUFFER_WILL_NOT_BE_ACCESSED);
	weston_buffer_release_reference(&ps->buffer_release_ref,
					es->buffer_release_ref.buffer_release);

	if (ps->buffer_destroy_listener.notify) {
		wl_list_remove(&ps->buffer_destroy_listener.link);
		ps->buffer_destroy_listener.notify = NULL;
	}

	if (ps->image) {
		pixman_image_unref(ps->image);
		ps->image = NULL;
	}

	if (!buffer)
		return;

	if (buffer->type == WESTON_BUFFER_SOLID) {
		pixman_renderer_surface_set_color(es,
						  buffer->solid.r,
						  buffer->solid.g,
						  buffer->solid.b,
						  buffer->solid.a);
		weston_buffer_reference(&ps->buffer_ref, NULL,
					BUFFER_WILL_NOT_BE_ACCESSED);
		weston_buffer_release_reference(&ps->buffer_release_ref, NULL);
		return;
	}

	if (buffer->type != WESTON_BUFFER_SHM) {
		weston_log("Pixman renderer supports only SHM buffers\n");
		weston_buffer_reference(&ps->buffer_ref, NULL,
					BUFFER_WILL_NOT_BE_ACCESSED);
		weston_buffer_release_reference(&ps->buffer_release_ref, NULL);
		return;
	}

	shm_buffer = buffer->shm_buffer;
	/* This can happen if a SHM wl_buffer gets destroyed before we attach,
	 * because wayland-server just nukes the wl_shm_buffer from underneath
	 * us. */
	if (!shm_buffer)
		return;

	pixel_info = pixel_format_get_info_shm(wl_shm_buffer_get_format(shm_buffer));
	if (!pixel_info || !pixman_format_supported_source(pixel_info->pixman_format)) {
		weston_log("Unsupported SHM buffer format 0x%x\n",
			wl_shm_buffer_get_format(shm_buffer));
		weston_buffer_reference(&ps->buffer_ref, NULL,
					BUFFER_WILL_NOT_BE_ACCESSED);
		weston_buffer_release_reference(&ps->buffer_release_ref, NULL);
		weston_buffer_send_server_error(buffer,
			"disconnecting due to unhandled buffer type");
		return;
	}

	ps->image = pixman_image_create_bits(pixel_info->pixman_format,
		buffer->width, buffer->height,
		wl_shm_buffer_get_data(shm_buffer),
		buffer->stride);

	ps->buffer_destroy_listener.notify =
		buffer_state_handle_buffer_destroy;
	wl_signal_add(&buffer->destroy_signal,
		      &ps->buffer_destroy_listener);
}

static void
pixman_renderer_surface_state_destroy(struct pixman_surface_state *ps)
{
	wl_list_remove(&ps->surface_destroy_listener.link);
	wl_list_remove(&ps->renderer_destroy_listener.link);
	if (ps->buffer_destroy_listener.notify) {
		wl_list_remove(&ps->buffer_destroy_listener.link);
		ps->buffer_destroy_listener.notify = NULL;
	}

	ps->surface->renderer_state = NULL;

	if (ps->image) {
		pixman_image_unref(ps->image);
		ps->image = NULL;
	}
	weston_buffer_reference(&ps->buffer_ref, NULL,
				BUFFER_WILL_NOT_BE_ACCESSED);
	weston_buffer_release_reference(&ps->buffer_release_ref, NULL);
	free(ps);
}

static void
surface_state_handle_surface_destroy(struct wl_listener *listener, void *data)
{
	struct pixman_surface_state *ps;

	ps = container_of(listener, struct pixman_surface_state,
			  surface_destroy_listener);

	pixman_renderer_surface_state_destroy(ps);
}

static void
surface_state_handle_renderer_destroy(struct wl_listener *listener, void *data)
{
	struct pixman_surface_state *ps;

	ps = container_of(listener, struct pixman_surface_state,
			  renderer_destroy_listener);

	pixman_renderer_surface_state_destroy(ps);
}

static int
pixman_renderer_create_surface(struct weston_surface *surface)
{
	struct pixman_surface_state *ps;
	struct pixman_renderer *pr = get_renderer(surface->compositor);

	ps = zalloc(sizeof *ps);
	if (ps == NULL)
		return -1;

	surface->renderer_state = ps;

	ps->surface = surface;

	ps->surface_destroy_listener.notify =
		surface_state_handle_surface_destroy;
	wl_signal_add(&surface->destroy_signal,
		      &ps->surface_destroy_listener);

	ps->renderer_destroy_listener.notify =
		surface_state_handle_renderer_destroy;
	wl_signal_add(&pr->destroy_signal,
		      &ps->renderer_destroy_listener);

	return 0;
}

static void
pixman_renderer_destroy(struct weston_compositor *ec)
{
	struct pixman_renderer *pr = get_renderer(ec);

	wl_signal_emit(&pr->destroy_signal, pr);
	weston_binding_destroy(pr->debug_binding);
	free(pr);

	ec->renderer = NULL;
}

static int
pixman_renderer_surface_copy_content(struct weston_surface *surface,
				     void *target, size_t size,
				     int src_x, int src_y,
				     int width, int height)
{
	const pixman_format_code_t format = PIXMAN_a8b8g8r8;
	const size_t bytespp = 4; /* PIXMAN_a8b8g8r8 */
	struct pixman_surface_state *ps = get_surface_state(surface);
	pixman_image_t *out_buf;

	if (!ps->image)
		return -1;

	out_buf = pixman_image_create_bits(format, width, height,
					   target, width * bytespp);

	pixman_image_set_transform(ps->image, NULL);
	pixman_image_composite32(PIXMAN_OP_SRC,
				 ps->image,    /* src */
				 NULL,         /* mask */
				 out_buf,      /* dest */
				 src_x, src_y, /* src_x, src_y */
				 0, 0,         /* mask_x, mask_y */
				 0, 0,         /* dest_x, dest_y */
				 width, height);

	pixman_image_unref(out_buf);

	return 0;
}

static weston_renderbuffer_t
pixman_renderer_create_renderbuffer(struct weston_output *output,
				    const struct pixel_format_info *format,
				    void *buffer, int stride,
				    weston_renderbuffer_discarded_func discarded_cb,
				    void *user_data)
{
	struct pixman_output_state *po = get_output_state(output);
	struct pixman_renderbuffer *renderbuffer;

	assert(po);

	renderbuffer = xzalloc(sizeof(*renderbuffer));

	if (buffer)
		renderbuffer->image =
			pixman_image_create_bits(format->pixman_format,
						 output->current_mode->width,
						 output->current_mode->height,
						 buffer, stride);
	else
		renderbuffer->image =
			pixman_image_create_bits_no_clear(format->pixman_format,
							  output->current_mode->width,
							  output->current_mode->height,
							  NULL, 0);

	if (!renderbuffer->image) {
		free(renderbuffer);
		return NULL;
	}

#if !defined(NDEBUG)
	renderbuffer->output = output;
#endif
	pixman_region32_init(&renderbuffer->damage);
	pixman_region32_copy(&renderbuffer->damage, &output->region);
	renderbuffer->discarded_cb = discarded_cb;
	renderbuffer->user_data = user_data;
	wl_list_insert(&po->renderbuffer_list, &renderbuffer->link);

	return (weston_renderbuffer_t) renderbuffer;
}

static void
pixman_renderbuffer_fini(struct pixman_renderbuffer *renderbuffer)
{
	assert(!renderbuffer->stale);

	pixman_region32_fini(&renderbuffer->damage);
	pixman_image_unref(renderbuffer->image);

	renderbuffer->stale = true;
}

static void
pixman_renderer_destroy_renderbuffer(weston_renderbuffer_t renderbuffer)
{
	struct pixman_renderbuffer *rb =
		(struct pixman_renderbuffer *) renderbuffer;

	wl_list_remove(&rb->link);

	if (!rb->stale)
		pixman_renderbuffer_fini(rb);

	free(rb);
}

static bool
pixman_renderer_can_render_straight_alpha(struct weston_compositor *wc)
{
	return false;
}

static bool
pixman_renderer_discard_renderbuffers(struct pixman_output_state *po,
				      bool destroy)
{
	struct pixman_renderbuffer *rb, *tmp;
	bool success = true;

	/* A renderbuffer goes stale after being discarded. Most resources are
	 * released. It's kept in the output state's renderbuffer list while
	 * waiting for the backend to destroy it. */
	wl_list_for_each_safe(rb, tmp, &po->renderbuffer_list, link) {
		if (destroy) {
			pixman_renderer_destroy_renderbuffer((weston_renderbuffer_t) rb);
		} else if (!rb->stale) {
			pixman_renderbuffer_fini(rb);
			if (success && rb->discarded_cb)
				success = rb->discarded_cb((weston_renderbuffer_t) rb,
							   rb->user_data);
		}
	}

	return success;
}

static bool
pixman_renderer_resize_output(struct weston_output *output,
			      const struct weston_size *fb_size,
			      const struct weston_geometry *area)
{
	struct pixman_output_state *po = get_output_state(output);

	check_compositing_area(fb_size, area);

	/*
	 * Pixman-renderer does not implement output decorations blitting,
	 * wayland-backend does it on its own.
	 */
	assert(area->x == 0);
	assert(area->y == 0);
	assert(fb_size->width == area->width);
	assert(fb_size->height == area->height);

	pixman_renderer_output_set_buffer(output, NULL);

	po->fb_size = *fb_size;

	/*
	 * Have a hw_format only after the first call to
	 * pixman_renderer_output_set_buffer().
	 */
	if (po->hw_format) {
		weston_output_update_capture_info(output,
						  WESTON_OUTPUT_CAPTURE_SOURCE_FRAMEBUFFER,
						  po->fb_size.width,
						  po->fb_size.height,
						  po->hw_format,
						  NULL);
	}

	/* Discard renderbuffers as a last step in order to emit discarded
	 * callbacks once the renderer has correctly been updated. */
	if (!pixman_renderer_discard_renderbuffers(po, false))
		return false;

	if (!po->shadow_format)
		return true;

	if (po->shadow_image)
		pixman_image_unref(po->shadow_image);

	po->shadow_image =
		pixman_image_create_bits_no_clear(po->shadow_format->pixman_format,
						  fb_size->width, fb_size->height,
						  NULL, 0);

	weston_output_update_capture_info(output,
					  WESTON_OUTPUT_CAPTURE_SOURCE_BLENDING,
					  po->fb_size.width,
					  po->fb_size.height,
					  po->shadow_format,
					  NULL);

	return !!po->shadow_image;
}

static void
debug_binding(struct weston_keyboard *keyboard, const struct timespec *time,
	      uint32_t key, void *data)
{
	struct weston_compositor *ec = data;
	struct pixman_renderer *pr = (struct pixman_renderer *) ec->renderer;

	pr->repaint_debug ^= 1;

	if (pr->repaint_debug) {
		pixman_color_t red = {
			0x3fff, 0x0000, 0x0000, 0x3fff
		};

		pr->debug_color = pixman_image_create_solid_fill(&red);
	} else {
		pixman_image_unref(pr->debug_color);
		weston_compositor_damage_all(ec);
	}
}

static struct pixman_renderer_interface pixman_renderer_interface;

WL_EXPORT int
pixman_renderer_init(struct weston_compositor *ec)
{
	struct pixman_renderer *renderer;
	const struct pixel_format_info *pixel_info, *info_argb8888, *info_xrgb8888;
	unsigned int i, num_formats;

	renderer = zalloc(sizeof *renderer);
	if (renderer == NULL)
		return -1;

	renderer->repaint_debug = 0;
	renderer->debug_color = NULL;
	renderer->base.repaint_output = pixman_renderer_repaint_output;
	renderer->base.resize_output = pixman_renderer_resize_output;
	renderer->base.flush_damage = pixman_renderer_flush_damage;
	renderer->base.attach = pixman_renderer_attach;
	renderer->base.destroy = pixman_renderer_destroy;
	renderer->base.surface_copy_content =
		pixman_renderer_surface_copy_content;
	renderer->base.create_renderbuffer =
		pixman_renderer_create_renderbuffer;
	renderer->base.create_renderbuffer_dmabuf = NULL;
	renderer->base.destroy_renderbuffer =
		pixman_renderer_destroy_renderbuffer;
	renderer->base.can_render_straight_alpha =
		pixman_renderer_can_render_straight_alpha;
	renderer->base.type = WESTON_RENDERER_PIXMAN;
	renderer->base.pixman = &pixman_renderer_interface;
	ec->renderer = &renderer->base;
	ec->capabilities |= WESTON_CAP_ROTATION_ANY;
	ec->capabilities |= WESTON_CAP_VIEW_CLIP_MASK;

	renderer->debug_binding =
		weston_compositor_add_debug_binding(ec, KEY_R,
						    debug_binding, ec);

	info_argb8888 = pixel_format_get_info_shm(WL_SHM_FORMAT_ARGB8888);
	info_xrgb8888 = pixel_format_get_info_shm(WL_SHM_FORMAT_XRGB8888);

	num_formats = pixel_format_get_info_count();
	for (i = 0; i < num_formats; i++) {
		pixel_info = pixel_format_get_info_by_index(i);
		if (!pixman_format_supported_source(pixel_info->pixman_format))
			continue;

		/* skip formats which libwayland registers by default */
		if (pixel_info == info_argb8888 || pixel_info == info_xrgb8888)
			continue;

		wl_display_add_shm_format(ec->wl_display, pixel_info->format);
	}

	wl_signal_init(&renderer->destroy_signal);

	return 0;
}

static void
pixman_renderer_output_set_buffer(struct weston_output *output,
				  pixman_image_t *buffer)
{
	struct weston_compositor *compositor = output->compositor;
	struct pixman_output_state *po = get_output_state(output);
	pixman_format_code_t pixman_format;

	if (po->hw_buffer)
		pixman_image_unref(po->hw_buffer);
	po->hw_buffer = buffer;

	if (!po->hw_buffer)
		return;

	pixman_format = pixman_image_get_format(po->hw_buffer);
	po->hw_format = pixel_format_get_info_by_pixman(pixman_format);
	compositor->read_format = po->hw_format;
	assert(po->hw_format);

	pixman_image_ref(po->hw_buffer);

	assert(po->fb_size.width == pixman_image_get_width(po->hw_buffer));
	assert(po->fb_size.height == pixman_image_get_height(po->hw_buffer));

	/*
	 * The size cannot change, but the format might, or we did not have
	 * hw_format in pixman_renderer_resize_output() yet.
	 */
	weston_output_update_capture_info(output,
					  WESTON_OUTPUT_CAPTURE_SOURCE_FRAMEBUFFER,
					  po->fb_size.width,
					  po->fb_size.height,
					  po->hw_format,
					  NULL);
}

static int
pixman_renderer_output_create(struct weston_output *output,
			      const struct pixman_renderer_output_options *options)
{
	struct pixman_output_state *po;
	struct weston_geometry area = {
		.x = 0,
		.y = 0,
		.width = options->fb_size.width,
		.height = options->fb_size.height
	};

	assert(!get_output_state(output));

	if (output->fb_alpha_encoding == WESTON_OUTPUT_FB_ALPHA_STRAIGHT &&
	    !pixman_renderer_can_render_straight_alpha(output->compositor)) {
		weston_log("Error: straight alpha framebuffers required for output '%s' but\n"
			   "Pixman-renderer does not support that.", output->name);
		return -1;
	}

	po = zalloc(sizeof *po);
	if (po == NULL)
		return -1;

	output->renderer_state = po;

	if (options->use_shadow)
		po->shadow_format = pixel_format_get_info(DRM_FORMAT_XRGB8888);

	wl_list_init(&po->renderbuffer_list);

	if (!pixman_renderer_resize_output(output, &options->fb_size, &area)) {
		output->renderer_state = NULL;
		free(po);
		return -1;
	}

	weston_output_update_capture_info(output,
					  WESTON_OUTPUT_CAPTURE_SOURCE_FRAMEBUFFER,
					  area.width, area.height,
					  options->format,
					  NULL);

	return 0;
}

static void
pixman_renderer_output_destroy(struct weston_output *output)
{
	struct pixman_output_state *po = get_output_state(output);

	assert(po);

	if (po->shadow_image)
		pixman_image_unref(po->shadow_image);

	if (po->hw_buffer)
		pixman_image_unref(po->hw_buffer);

	po->shadow_image = NULL;
	po->hw_buffer = NULL;

	pixman_renderer_discard_renderbuffers(po, true);

	free(po);
	output->renderer_state = NULL;
}

static struct pixman_renderer_interface pixman_renderer_interface = {
	.output_create = pixman_renderer_output_create,
	.output_destroy = pixman_renderer_output_destroy,
};
