5181a826d1
In future commits, we want to be able to handle more complex textures, such as video frames which are encoded in a YUV-pixel format and have multiple planes (which each map to a separate texture). To accomplish this, we introduce a new object `MetaMultiTexture`: this object can deal with more complex formats by handling multiple `CoglTexture`s. It supports shaders for pixel format conversion from YUV to RGBA, as well as blending. While custom bleding is currently only required for YUV formats, we also implement it for RGB ones. This allows us to simplify code in other places and will be needed in the future once we want to support blending between different color spaces. Co-Authored-By: Robert Mader <robert.mader@collabora.com> Co-Authored-By: Sebastian Wick <sebastian.wick@redhat.com> Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/2191>
290 lines
8.8 KiB
C
290 lines
8.8 KiB
C
/*
|
|
* Authored By Niels De Graef <niels.degraef@barco.com>
|
|
*
|
|
* Copyright (C) 2018 Barco NV
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License as
|
|
* published by the Free Software Foundation; either version 2 of the
|
|
* License, or (at your option) any later version.
|
|
*
|
|
* This program 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
|
|
* General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
/**
|
|
* SECTION:meta-multi-texture
|
|
* @title: MetaMultiTexture
|
|
* @short_description: A texture that can have multiple planes.
|
|
*
|
|
* #MetaMultiTexture allows one to deal with non-trivial formats that
|
|
* have multiple planes, requires subsampling and/or aren't in RGB. A common
|
|
* example of this are decoded video frames, which often use something in the
|
|
* YUV colorspace, combined with subsampling.
|
|
*
|
|
* The basic idea of a #MetaMultiTexture is the following:
|
|
* - Each plane is represented by a separate #CoglTexture. That means that you
|
|
* should add each of these planes as a layer to your CoglPipeline.
|
|
* - When dealing with a color space that is not RGB, you can ask the
|
|
* #MetaMultiTexture to create a shader for you that does the conversion
|
|
* in the GPU.
|
|
* - In case you need to deal with memory access in a format with subsampling,
|
|
* you can use meta_multi_texture_get_width() and its analogous version
|
|
* for the height to get the correct size of the texture.
|
|
*/
|
|
|
|
#include "config.h"
|
|
|
|
#include "meta/meta-multi-texture.h"
|
|
|
|
#include "meta/meta-enum-types.h"
|
|
|
|
struct _MetaMultiTexture
|
|
{
|
|
GObject parent_instance;
|
|
|
|
MetaMultiTextureFormat format;
|
|
|
|
int n_planes;
|
|
CoglTexture **planes;
|
|
};
|
|
|
|
G_DEFINE_TYPE (MetaMultiTexture, meta_multi_texture, G_TYPE_OBJECT);
|
|
|
|
/**
|
|
* meta_multi_texture_get_format:
|
|
* @multi_texture: a #MetaMultiTexture
|
|
*
|
|
* Returns the #MetaMultiTextureFormat that is used by this texture.
|
|
*
|
|
* Returns: The texture format that is used by this #MetaMultiTexture.
|
|
*/
|
|
MetaMultiTextureFormat
|
|
meta_multi_texture_get_format (MetaMultiTexture *multi_texture)
|
|
{
|
|
g_return_val_if_fail (META_IS_MULTI_TEXTURE (multi_texture), META_MULTI_TEXTURE_FORMAT_SIMPLE);
|
|
|
|
return multi_texture->format;
|
|
}
|
|
|
|
/**
|
|
* meta_multi_texture_is_simple:
|
|
* @multi_texture: a #MetaMultiTexture
|
|
*
|
|
* A small function that checks whether the given multi texture uses a "simple"
|
|
* format, i.e. one that can be represented by a #CoglPixelFormat.
|
|
*
|
|
* Returns: Whether the texture format is #META_MULTI_TEXTURE_FORMAT_SIMPLE
|
|
*/
|
|
gboolean
|
|
meta_multi_texture_is_simple (MetaMultiTexture *multi_texture)
|
|
{
|
|
g_return_val_if_fail (META_IS_MULTI_TEXTURE (multi_texture), FALSE);
|
|
|
|
return multi_texture->format == META_MULTI_TEXTURE_FORMAT_SIMPLE;
|
|
}
|
|
|
|
/**
|
|
* meta_multi_texture_get_n_planes:
|
|
* @multi_texture: a #MetaMultiTexture
|
|
*
|
|
* Returns the number of planes for this texture. Note that this is entirely
|
|
* dependent on the #CoglPixelFormat that is used. For example, simple RGB
|
|
* textures will have a single plane, while some more convoluted formats like
|
|
* NV12 and YUV 4:4:4 can have 2 and 3 planes respectively.
|
|
*
|
|
* Returns: The number of planes in this #MetaMultiTexture.
|
|
*/
|
|
int
|
|
meta_multi_texture_get_n_planes (MetaMultiTexture *multi_texture)
|
|
{
|
|
g_return_val_if_fail (META_IS_MULTI_TEXTURE (multi_texture), 0);
|
|
|
|
return multi_texture->n_planes;
|
|
}
|
|
|
|
/**
|
|
* meta_multi_texture_get_plane:
|
|
* @multi_texture: a #MetaMultiTexture
|
|
* @index: the index of the plane
|
|
*
|
|
* Returns the n'th plane of the #MetaMultiTexture. Note that it's a programming
|
|
* error to use with an index larger than meta_multi_texture_get_n_planes().
|
|
*
|
|
* Returns: (transfer none): The plane at the given @index.
|
|
*/
|
|
CoglTexture *
|
|
meta_multi_texture_get_plane (MetaMultiTexture *multi_texture,
|
|
int index)
|
|
{
|
|
g_return_val_if_fail (META_IS_MULTI_TEXTURE (multi_texture), 0);
|
|
g_return_val_if_fail (index < multi_texture->n_planes, NULL);
|
|
|
|
return multi_texture->planes[index];
|
|
}
|
|
|
|
/**
|
|
* meta_multi_texture_get_width:
|
|
* @multi_texture: a #MetaMultiTexture
|
|
*
|
|
* Returns the width of the #MetaMultiTexture. Prefer this over calling
|
|
* cogl_texture_get_width() on one of the textures, as that might give a
|
|
* different size when dealing with subsampling.
|
|
*
|
|
* Returns: The width of the texture.
|
|
*/
|
|
int
|
|
meta_multi_texture_get_width (MetaMultiTexture *multi_texture)
|
|
{
|
|
g_return_val_if_fail (META_IS_MULTI_TEXTURE (multi_texture), 0);
|
|
|
|
return cogl_texture_get_width (multi_texture->planes[0]);
|
|
}
|
|
|
|
/**
|
|
* meta_multi_texture_get_height:
|
|
* @multi_texture: a #MetaMultiTexture
|
|
*
|
|
* Returns the height of the #MetaMultiTexture. Prefer this over calling
|
|
* cogl_texture_get_height() on one of the textures, as that might give a
|
|
* different size when dealing with subsampling.
|
|
*
|
|
* Returns: The height of the texture.
|
|
*/
|
|
int
|
|
meta_multi_texture_get_height (MetaMultiTexture *multi_texture)
|
|
{
|
|
g_return_val_if_fail (META_IS_MULTI_TEXTURE (multi_texture), 0);
|
|
|
|
return cogl_texture_get_height (multi_texture->planes[0]);
|
|
}
|
|
|
|
static void
|
|
meta_multi_texture_finalize (GObject *object)
|
|
{
|
|
MetaMultiTexture *multi_texture = META_MULTI_TEXTURE (object);
|
|
int i;
|
|
|
|
for (i = 0; i < multi_texture->n_planes; i++)
|
|
cogl_clear_object (&multi_texture->planes[i]);
|
|
|
|
g_free (multi_texture->planes);
|
|
|
|
G_OBJECT_CLASS (meta_multi_texture_parent_class)->finalize (object);
|
|
}
|
|
|
|
static void
|
|
meta_multi_texture_init (MetaMultiTexture *multi_texture)
|
|
{
|
|
}
|
|
|
|
static void
|
|
meta_multi_texture_class_init (MetaMultiTextureClass *klass)
|
|
{
|
|
GObjectClass *gobj_class = G_OBJECT_CLASS (klass);
|
|
|
|
gobj_class->finalize = meta_multi_texture_finalize;
|
|
}
|
|
|
|
/**
|
|
* meta_multi_texture_new:
|
|
* @format: The format of the #MetaMultiTexture
|
|
* @planes: (transfer full): The actual planes of the texture
|
|
* @n_planes: The number of planes
|
|
*
|
|
* Creates a #MetaMultiTexture with the given @format. Each of the
|
|
* #CoglTexture<!-- -->s represents a plane.
|
|
*
|
|
* Returns: (transfer full): A new #MetaMultiTexture. Use g_object_unref() when
|
|
* you're done with it.
|
|
*/
|
|
MetaMultiTexture *
|
|
meta_multi_texture_new (MetaMultiTextureFormat format,
|
|
CoglTexture **planes,
|
|
int n_planes)
|
|
{
|
|
MetaMultiTexture *multi_texture;
|
|
|
|
g_return_val_if_fail (planes != NULL, NULL);
|
|
g_return_val_if_fail (n_planes > 0, NULL);
|
|
|
|
multi_texture = g_object_new (META_TYPE_MULTI_TEXTURE, NULL);
|
|
multi_texture->format = format;
|
|
multi_texture->n_planes = n_planes;
|
|
multi_texture->planes = planes;
|
|
|
|
return multi_texture;
|
|
}
|
|
|
|
/**
|
|
* meta_multi_texture_new_simple:
|
|
* @plane: (transfer full): The single plane of the texture
|
|
*
|
|
* Creates a #MetaMultiTexture for a "simple" texture, i.e. with only one
|
|
* plane, in a format that can be represented using #CoglPixelFormat.
|
|
*
|
|
* Returns: (transfer full): A new #MetaMultiTexture. Use g_object_unref() when
|
|
* you're done with it.
|
|
*/
|
|
MetaMultiTexture *
|
|
meta_multi_texture_new_simple (CoglTexture *plane)
|
|
{
|
|
MetaMultiTexture *multi_texture;
|
|
|
|
g_return_val_if_fail (plane != NULL, NULL);
|
|
|
|
multi_texture = g_object_new (META_TYPE_MULTI_TEXTURE, NULL);
|
|
multi_texture->format = META_MULTI_TEXTURE_FORMAT_SIMPLE;
|
|
multi_texture->n_planes = 1;
|
|
multi_texture->planes = g_malloc (sizeof (CoglTexture *));
|
|
multi_texture->planes[0] = plane;
|
|
|
|
return multi_texture;
|
|
}
|
|
|
|
/**
|
|
* meta_multi_texture_to_string:
|
|
* @multi_texture: a #MetaMultiTexture
|
|
*
|
|
* Returns a string representation of @multi_texture, useful for debugging
|
|
* purposes.
|
|
*
|
|
* Returns: (transfer full): A string representation of @multi_texture. Use
|
|
* g_free() when done with it.
|
|
*/
|
|
char *
|
|
meta_multi_texture_to_string (MetaMultiTexture *multi_texture)
|
|
{
|
|
g_autoptr (GString) str = NULL;
|
|
g_autofree char *format_str = NULL;
|
|
g_autofree char *ret = NULL;
|
|
uint8_t i;
|
|
|
|
str = g_string_new ("");
|
|
g_string_append_printf (str, "MetaMultiTexture (%p) {\n", multi_texture);
|
|
format_str = g_enum_to_string (META_TYPE_MULTI_TEXTURE_FORMAT, multi_texture->format);
|
|
g_string_append_printf (str, " .format = %s;\n", format_str);
|
|
g_string_append_printf (str, " .n_planes = %u;\n", multi_texture->n_planes);
|
|
g_string_append (str, " .planes = {\n");
|
|
|
|
for (i = 0; i < multi_texture->n_planes; i++)
|
|
{
|
|
CoglTexture *plane = multi_texture->planes[i];
|
|
CoglPixelFormat plane_format = _cogl_texture_get_format (plane);
|
|
|
|
g_string_append_printf (str, " (%p) { .format = %s },\n",
|
|
plane,
|
|
cogl_pixel_format_to_string (plane_format));
|
|
}
|
|
|
|
g_string_append (str, " }\n");
|
|
g_string_append (str, "}");
|
|
|
|
ret = g_string_free (g_steal_pointer (&str), FALSE);
|
|
return g_steal_pointer (&ret);
|
|
}
|