mutter/clutter/clutter/clutter-paint-context.c
Jonas Ådahl b275af95b7 clutter/paint-context: Add content and target color state tracking
The initial target color state is the color state of the stage view
being painted to. If we're painting to an arbitrary framebuffer, it's
currently hard coded to sRGB/electrical.

The content color state is not set on construction, but when starting to
paint, it's set to the color state of the stage itself. Whenever an
actor is painted, it'll set the color state to the color state of
itself. The intention is that offscreen rendering pushes a target color
state that causes painting to it to not necessarily be in the stage view
color state.

Pass color state with offscreen framebuffer, as this avoids hard coding
sRGB in the lower level bits of paint contexts. It's still practically
hard coded, only that it's derived from somewhere else (e.g. the stage
or window actor).

Nothing is actually using this yet, but will eventually.

Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/3433>
2024-07-09 18:51:30 +02:00

318 lines
9.4 KiB
C

/*
* Copyright (C) 2019 Red Hat Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*/
#include "config.h"
#include "clutter/clutter-paint-context-private.h"
#include "clutter/clutter-color-state.h"
#include "clutter/clutter-frame.h"
#include "clutter/clutter-stage-view-private.h"
struct _ClutterPaintContext
{
grefcount ref_count;
ClutterPaintFlag paint_flags;
GList *framebuffers;
ClutterStageView *view;
ClutterFrame *frame;
MtkRegion *redraw_clip;
GArray *clip_frusta;
GList *target_color_states;
GList *color_states;
ClutterColorState *framebuffer_color_state;
};
G_DEFINE_BOXED_TYPE (ClutterPaintContext, clutter_paint_context,
clutter_paint_context_ref,
clutter_paint_context_unref)
ClutterPaintContext *
clutter_paint_context_new_for_view (ClutterStageView *view,
const MtkRegion *redraw_clip,
GArray *clip_frusta,
ClutterPaintFlag paint_flags)
{
ClutterPaintContext *paint_context;
ClutterColorState *target_color_state;
CoglFramebuffer *framebuffer;
paint_context = g_new0 (ClutterPaintContext, 1);
g_ref_count_init (&paint_context->ref_count);
paint_context->view = view;
paint_context->redraw_clip = mtk_region_copy (redraw_clip);
paint_context->clip_frusta = g_array_ref (clip_frusta);
paint_context->paint_flags = paint_flags;
g_set_object (&paint_context->framebuffer_color_state,
clutter_stage_view_get_color_state (view));
target_color_state = paint_context->framebuffer_color_state;
clutter_paint_context_push_target_color_state (paint_context,
target_color_state);
framebuffer = clutter_stage_view_get_framebuffer (view);
clutter_paint_context_push_framebuffer (paint_context, framebuffer);
return paint_context;
}
/**
* clutter_paint_context_new_for_framebuffer: (skip)
*/
ClutterPaintContext *
clutter_paint_context_new_for_framebuffer (CoglFramebuffer *framebuffer,
const MtkRegion *redraw_clip,
ClutterPaintFlag paint_flags,
ClutterColorState *color_state)
{
ClutterPaintContext *paint_context;
ClutterColorState *target_color_state;
paint_context = g_new0 (ClutterPaintContext, 1);
g_ref_count_init (&paint_context->ref_count);
paint_context->paint_flags = paint_flags;
g_set_object (&paint_context->framebuffer_color_state, color_state);
target_color_state = paint_context->framebuffer_color_state;
clutter_paint_context_push_target_color_state (paint_context,
target_color_state);
if (redraw_clip)
paint_context->redraw_clip = mtk_region_copy (redraw_clip);
clutter_paint_context_push_framebuffer (paint_context, framebuffer);
return paint_context;
}
ClutterPaintContext *
clutter_paint_context_ref (ClutterPaintContext *paint_context)
{
g_ref_count_inc (&paint_context->ref_count);
return paint_context;
}
static void
clutter_paint_context_dispose (ClutterPaintContext *paint_context)
{
if (paint_context->framebuffer_color_state)
{
clutter_paint_context_pop_target_color_state (paint_context);
g_clear_object (&paint_context->framebuffer_color_state);
}
g_warn_if_fail (!paint_context->color_states);
g_warn_if_fail (!paint_context->target_color_states);
g_list_free_full (paint_context->framebuffers, g_object_unref);
paint_context->framebuffers = NULL;
g_clear_pointer (&paint_context->redraw_clip, mtk_region_unref);
g_clear_pointer (&paint_context->clip_frusta, g_array_unref);
g_clear_pointer (&paint_context->frame, clutter_frame_unref);
}
void
clutter_paint_context_unref (ClutterPaintContext *paint_context)
{
if (g_ref_count_dec (&paint_context->ref_count))
{
clutter_paint_context_dispose (paint_context);
g_free (paint_context);
}
}
void
clutter_paint_context_destroy (ClutterPaintContext *paint_context)
{
clutter_paint_context_dispose (paint_context);
clutter_paint_context_unref (paint_context);
}
void
clutter_paint_context_push_framebuffer (ClutterPaintContext *paint_context,
CoglFramebuffer *framebuffer)
{
paint_context->framebuffers = g_list_prepend (paint_context->framebuffers,
g_object_ref (framebuffer));
}
void
clutter_paint_context_pop_framebuffer (ClutterPaintContext *paint_context)
{
g_return_if_fail (paint_context->framebuffers);
g_object_unref (paint_context->framebuffers->data);
paint_context->framebuffers =
g_list_delete_link (paint_context->framebuffers,
paint_context->framebuffers);
}
const MtkRegion *
clutter_paint_context_get_redraw_clip (ClutterPaintContext *paint_context)
{
return paint_context->redraw_clip;
}
const GArray *
clutter_paint_context_get_clip_frusta (ClutterPaintContext *paint_context)
{
return paint_context->clip_frusta;
}
/**
* clutter_paint_context_get_framebuffer:
* @paint_context: The #ClutterPaintContext
*
* Returns: (transfer none): The #CoglFramebuffer used for drawing
*/
CoglFramebuffer *
clutter_paint_context_get_framebuffer (ClutterPaintContext *paint_context)
{
g_return_val_if_fail (paint_context->framebuffers, NULL);
return paint_context->framebuffers->data;
}
CoglFramebuffer *
clutter_paint_context_get_base_framebuffer (ClutterPaintContext *paint_context)
{
return g_list_last (paint_context->framebuffers)->data;
}
/**
* clutter_paint_context_get_stage_view: (skip)
*/
ClutterStageView *
clutter_paint_context_get_stage_view (ClutterPaintContext *paint_context)
{
return paint_context->view;
}
/**
* clutter_paint_context_is_drawing_off_stage: (skip)
*
* Return %TRUE if the paint context is currently drawing off stage.
* This happens if there are any framebuffers pushed, and the base framebuffer
* comes from the stage view.
*/
gboolean
clutter_paint_context_is_drawing_off_stage (ClutterPaintContext *paint_context)
{
if (g_list_length (paint_context->framebuffers) > 1)
return TRUE;
return !paint_context->view;
}
/**
* clutter_paint_context_get_paint_flags: (skip)
*/
ClutterPaintFlag
clutter_paint_context_get_paint_flags (ClutterPaintContext *paint_context)
{
return paint_context->paint_flags;
}
void
clutter_paint_context_assign_frame (ClutterPaintContext *paint_context,
ClutterFrame *frame)
{
g_assert (paint_context != NULL);
g_assert (paint_context->frame == NULL);
g_assert (frame != NULL);
paint_context->frame = clutter_frame_ref (frame);
}
/**
* clutter_paint_context_get_frame: (skip)
* @paint_context: The #ClutterPaintContext
*
* Retrieves the #ClutterFrame assigned to @paint_context, if any. A frame is
* only assigned when the paint context is created as part of a frame scheduled
* by the frame clock, and won't be assigned e.g. on offscreen paints.
*
* Returns: (transfer none)(nullable): The #ClutterFrame associated with the
* @paint_context, or %NULL
*/
ClutterFrame *
clutter_paint_context_get_frame (ClutterPaintContext *paint_context)
{
return paint_context->frame;
}
void
clutter_paint_context_push_target_color_state (ClutterPaintContext *paint_context,
ClutterColorState *color_state)
{
paint_context->target_color_states =
g_list_prepend (paint_context->target_color_states, color_state);
}
void
clutter_paint_context_pop_target_color_state (ClutterPaintContext *paint_context)
{
g_return_if_fail (paint_context->target_color_states);
paint_context->target_color_states =
g_list_delete_link (paint_context->target_color_states,
paint_context->target_color_states);
}
void
clutter_paint_context_push_color_state (ClutterPaintContext *paint_context,
ClutterColorState *color_state)
{
paint_context->color_states = g_list_prepend (paint_context->color_states,
color_state);
}
void
clutter_paint_context_pop_color_state (ClutterPaintContext *paint_context)
{
g_return_if_fail (paint_context->color_states);
paint_context->color_states =
g_list_delete_link (paint_context->color_states,
paint_context->color_states);
}
/**
* clutter_paint_context_get_target_color_state: (skip)
*/
ClutterColorState *
clutter_paint_context_get_target_color_state (ClutterPaintContext *paint_context)
{
return CLUTTER_COLOR_STATE (paint_context->target_color_states->data);
}
/**
* clutter_paint_context_get_color_state: (skip)
*/
ClutterColorState *
clutter_paint_context_get_color_state (ClutterPaintContext *paint_context)
{
g_return_val_if_fail (paint_context->color_states, NULL);
return CLUTTER_COLOR_STATE (paint_context->color_states->data);
}