3 * Copyright (C) 2013 Matthew Waters <ystreet00@gmail.com>
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Library General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Library General Public License for more details.
15 * You should have received a copy of the GNU Library General Public
16 * License along with this library; if not, write to the
17 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
18 * Boston, MA 02110-1301, USA.
24 * @short_description: some miscellaneous utilities for OpenGL
25 * @see_also: #GstGLContext
35 #include <glib/gprintf.h>
38 #include "gstglutils.h"
39 #include "gstglutils_private.h"
41 #if GST_GL_HAVE_WINDOW_X11
42 #include <gst/gl/x11/gstgldisplay_x11.h>
44 #if GST_GL_HAVE_WINDOW_WAYLAND
45 #include <gst/gl/wayland/gstgldisplay_wayland.h>
48 #define USING_OPENGL(context) (gst_gl_context_check_gl_version (context, GST_GL_API_OPENGL, 1, 0))
49 #define USING_OPENGL3(context) (gst_gl_context_check_gl_version (context, GST_GL_API_OPENGL3, 3, 1))
50 #define USING_GLES(context) (gst_gl_context_check_gl_version (context, GST_GL_API_GLES, 1, 0))
51 #define USING_GLES2(context) (gst_gl_context_check_gl_version (context, GST_GL_API_GLES2, 2, 0))
52 #define USING_GLES3(context) (gst_gl_context_check_gl_version (context, GST_GL_API_GLES2, 3, 0))
54 #ifndef GST_DISABLE_GST_DEBUG
55 GST_DEBUG_CATEGORY_STATIC (gst_gl_utils_debug);
56 static GstDebugCategory *
57 _init_gl_utils_debug_category (void)
59 static gsize _init = 0;
61 if (g_once_init_enter (&_init)) {
62 GST_DEBUG_CATEGORY_INIT (gst_gl_utils_debug, "glutils", 0,
64 g_once_init_leave (&_init, 1);
67 return gst_gl_utils_debug;
70 #define GST_CAT_DEFAULT _init_gl_utils_debug_category()
74 gst_gl_display_found (GstElement * element, GstGLDisplay * display)
77 GST_LOG_OBJECT (element, "already have a display (%p)", display);
84 GST_DEBUG_CATEGORY_STATIC (GST_CAT_CONTEXT);
87 _init_context_debug (void)
89 #ifndef GST_DISABLE_GST_DEBUG
90 static gsize _init = 0;
92 if (g_once_init_enter (&_init)) {
93 GST_DEBUG_CATEGORY_GET (GST_CAT_CONTEXT, "GST_CONTEXT");
94 g_once_init_leave (&_init, 1);
100 pad_query (const GValue * item, GValue * value, gpointer user_data)
102 GstPad *pad = g_value_get_object (item);
103 GstQuery *query = user_data;
106 _init_context_debug ();
108 res = gst_pad_peer_query (pad, query);
111 g_value_set_boolean (value, TRUE);
115 GST_CAT_INFO_OBJECT (GST_CAT_CONTEXT, pad, "pad peer query failed");
120 gst_gl_run_query (GstElement * element, GstQuery * query,
121 GstPadDirection direction)
124 GstIteratorFoldFunction func = pad_query;
127 g_value_init (&res, G_TYPE_BOOLEAN);
128 g_value_set_boolean (&res, FALSE);
131 if (direction == GST_PAD_SRC)
132 it = gst_element_iterate_src_pads (element);
134 it = gst_element_iterate_sink_pads (element);
136 while (gst_iterator_fold (it, func, &res, query) == GST_ITERATOR_RESYNC)
137 gst_iterator_resync (it);
139 gst_iterator_free (it);
141 return g_value_get_boolean (&res);
145 _gst_context_query (GstElement * element, const gchar * display_type)
150 _init_context_debug ();
152 /* 2a) Query downstream with GST_QUERY_CONTEXT for the context and
153 * check if downstream already has a context of the specific type
154 * 2b) Query upstream as above.
156 query = gst_query_new_context (display_type);
157 if (gst_gl_run_query (element, query, GST_PAD_SRC)) {
158 gst_query_parse_context (query, &ctxt);
159 GST_CAT_INFO_OBJECT (GST_CAT_CONTEXT, element,
160 "found context (%p) in downstream query", ctxt);
161 gst_element_set_context (element, ctxt);
162 } else if (gst_gl_run_query (element, query, GST_PAD_SINK)) {
163 gst_query_parse_context (query, &ctxt);
164 GST_CAT_INFO_OBJECT (GST_CAT_CONTEXT, element,
165 "found context (%p) in upstream query", ctxt);
166 gst_element_set_context (element, ctxt);
168 /* 3) Post a GST_MESSAGE_NEED_CONTEXT message on the bus with
169 * the required context type and afterwards check if a
170 * usable context was set now as in 1). The message could
171 * be handled by the parent bins of the element and the
176 GST_CAT_INFO_OBJECT (GST_CAT_CONTEXT, element,
177 "posting need context message");
178 msg = gst_message_new_need_context (GST_OBJECT_CAST (element),
180 gst_element_post_message (element, msg);
184 * Whomever responds to the need-context message performs a
185 * GstElement::set_context() with the required context in which the element
186 * is required to update the display_ptr or call gst_gl_handle_set_context().
189 gst_query_unref (query);
193 gst_gl_display_context_query (GstElement * element, GstGLDisplay ** display_ptr)
195 _gst_context_query (element, GST_GL_DISPLAY_CONTEXT_TYPE);
199 #if GST_GL_HAVE_WINDOW_X11
200 _gst_context_query (element, "gst.x11.display.handle");
205 #if GST_GL_HAVE_WINDOW_WAYLAND
206 _gst_context_query (element, "GstWaylandDisplayHandleContextType");
213 gst_gl_context_query (GstElement * element)
215 _gst_context_query (element, "gst.gl.app_context");
218 /* 4) Create a context by itself and post a GST_MESSAGE_HAVE_CONTEXT
222 gst_gl_element_propagate_display_context (GstElement * element,
223 GstGLDisplay * display)
229 GST_ERROR_OBJECT (element, "Could not get GL display connection");
233 _init_context_debug ();
235 context = gst_context_new (GST_GL_DISPLAY_CONTEXT_TYPE, TRUE);
236 gst_context_set_gl_display (context, display);
238 gst_element_set_context (element, context);
240 GST_CAT_INFO_OBJECT (GST_CAT_CONTEXT, element,
241 "posting have context (%p) message with display (%p)", context, display);
242 msg = gst_message_new_have_context (GST_OBJECT_CAST (element), context);
243 gst_element_post_message (GST_ELEMENT_CAST (element), msg);
247 * gst_gl_ensure_element_data:
248 * @element: the #GstElement running the query
249 * @display_ptr: (inout): the resulting #GstGLDisplay
250 * @other_context_ptr: (inout): the resulting #GstGLContext
252 * Perform the steps necessary for retrieving a #GstGLDisplay and (optionally)
253 * an application provided #GstGLContext from the surrounding elements or from
254 * the application using the #GstContext mechanism.
256 * If the contents of @display_ptr or @other_context_ptr are not %NULL, then no
257 * #GstContext query is necessary for #GstGLDisplay or #GstGLContext retrieval
260 * This performs #GstContext queries (if necessary) for a winsys display
261 * connection with %GST_GL_DISPLAY_CONTEXT_TYPE, "gst.x11.display.handle", and
262 * "GstWaylandDisplayHandleContextType" stopping after the first successful
265 * This also performs a #GstContext query (if necessary) for an optional
266 * application provided #GstGLContext using the name "gst.gl.app_context".
267 * The returned #GstGLContext will be shared with a GStreamer created OpenGL context.
269 * Returns: whether a #GstGLDisplay exists in @display_ptr
272 gst_gl_ensure_element_data (gpointer element, GstGLDisplay ** display_ptr,
273 GstGLContext ** other_context_ptr)
275 GstGLDisplay *display;
277 g_return_val_if_fail (element != NULL, FALSE);
278 g_return_val_if_fail (display_ptr != NULL, FALSE);
279 g_return_val_if_fail (other_context_ptr != NULL, FALSE);
281 /* 1) Check if the element already has a context of the specific
284 display = *display_ptr;
285 if (gst_gl_display_found (element, display))
288 gst_gl_display_context_query (element, display_ptr);
290 /* Neighbour found and it updated the display */
291 if (gst_gl_display_found (element, *display_ptr))
294 /* If no neighbor, or application not interested, use system default */
295 display = gst_gl_display_new ();
297 *display_ptr = display;
299 gst_gl_element_propagate_display_context (element, display);
302 if (*other_context_ptr)
305 gst_gl_context_query (element);
308 return *display_ptr != NULL;
312 * gst_gl_handle_set_context:
313 * @element: a #GstElement
314 * @context: a #GstContext
315 * @display: (inout) (transfer full): location of a #GstGLDisplay
316 * @other_context: (inout) (transfer full): location of a #GstGLContext
318 * Helper function for implementing #GstElementClass.set_context() in
319 * OpenGL capable elements.
321 * Retrieve's the #GstGLDisplay or #GstGLContext in @context and places the
322 * result in @display or @other_context respectively.
324 * Returns: whether the @display or @other_context could be set successfully
327 gst_gl_handle_set_context (GstElement * element, GstContext * context,
328 GstGLDisplay ** display, GstGLContext ** other_context)
330 GstGLDisplay *display_replacement = NULL;
331 GstGLContext *context_replacement = NULL;
332 const gchar *context_type;
334 g_return_val_if_fail (display != NULL, FALSE);
335 g_return_val_if_fail (other_context != NULL, FALSE);
340 context_type = gst_context_get_context_type (context);
342 if (g_strcmp0 (context_type, GST_GL_DISPLAY_CONTEXT_TYPE) == 0) {
343 if (!gst_context_get_gl_display (context, &display_replacement)) {
344 GST_WARNING_OBJECT (element, "Failed to get display from context");
348 #if GST_GL_HAVE_WINDOW_X11
349 else if (g_strcmp0 (context_type, "gst.x11.display.handle") == 0) {
350 const GstStructure *s;
353 s = gst_context_get_structure (context);
354 if (gst_structure_get (s, "display", G_TYPE_POINTER, &display, NULL))
355 display_replacement =
356 (GstGLDisplay *) gst_gl_display_x11_new_with_display (display);
359 #if GST_GL_HAVE_WINDOW_WAYLAND
360 else if (g_strcmp0 (context_type, "GstWaylandDisplayHandleContextType") == 0) {
361 const GstStructure *s;
362 struct wl_display *display;
364 s = gst_context_get_structure (context);
365 if (gst_structure_get (s, "display", G_TYPE_POINTER, &display, NULL))
366 display_replacement =
367 (GstGLDisplay *) gst_gl_display_wayland_new_with_display (display);
370 else if (g_strcmp0 (context_type, "gst.gl.app_context") == 0) {
371 const GstStructure *s = gst_context_get_structure (context);
372 GstGLDisplay *context_display;
373 GstGLDisplay *element_display;
375 if (gst_structure_get (s, "context", GST_TYPE_GL_CONTEXT,
376 &context_replacement, NULL)) {
377 context_display = gst_gl_context_get_display (context_replacement);
378 element_display = display_replacement ? display_replacement : *display;
380 && (gst_gl_display_get_handle_type (element_display) &
381 gst_gl_display_get_handle_type (context_display)) == 0) {
382 GST_ELEMENT_WARNING (element, LIBRARY, SETTINGS, ("%s",
383 "Cannot set a GL context with a different display type"), ("%s",
384 "Cannot set a GL context with a different display type"));
385 gst_object_unref (context_replacement);
386 context_replacement = NULL;
388 gst_object_unref (context_display);
392 if (display_replacement) {
393 GstGLDisplay *old = *display;
394 *display = display_replacement;
397 gst_object_unref (old);
400 if (context_replacement) {
401 GstGLContext *old = *other_context;
402 *other_context = context_replacement;
405 gst_object_unref (old);
412 * gst_gl_handle_context_query:
413 * @element: a #GstElement
414 * @query: a #GstQuery of type %GST_QUERY_CONTEXT
415 * @display: (transfer none) (nullable): a #GstGLDisplay
416 * @context: (transfer none) (nullable): a #GstGLContext
417 * @other_context: (transfer none) (nullable): application provided #GstGLContext
419 * Returns: Whether the @query was successfully responded to from the passed
420 * @display, @context, and @other_context.
423 gst_gl_handle_context_query (GstElement * element, GstQuery * query,
424 GstGLDisplay * display, GstGLContext * gl_context,
425 GstGLContext * other_context)
427 const gchar *context_type;
428 GstContext *context, *old_context;
430 g_return_val_if_fail (GST_IS_ELEMENT (element), FALSE);
431 g_return_val_if_fail (GST_IS_QUERY (query), FALSE);
432 g_return_val_if_fail (display == NULL || GST_IS_GL_DISPLAY (display), FALSE);
433 g_return_val_if_fail (gl_context == NULL
434 || GST_IS_GL_CONTEXT (gl_context), FALSE);
435 g_return_val_if_fail (other_context == NULL
436 || GST_IS_GL_CONTEXT (other_context), FALSE);
438 GST_LOG_OBJECT (element, "handle context query %" GST_PTR_FORMAT, query);
439 gst_query_parse_context_type (query, &context_type);
441 if (display && g_strcmp0 (context_type, GST_GL_DISPLAY_CONTEXT_TYPE) == 0) {
442 gst_query_parse_context (query, &old_context);
445 context = gst_context_copy (old_context);
447 context = gst_context_new (GST_GL_DISPLAY_CONTEXT_TYPE, TRUE);
449 gst_context_set_gl_display (context, display);
450 gst_query_set_context (query, context);
451 gst_context_unref (context);
452 GST_DEBUG_OBJECT (element, "successfully set %" GST_PTR_FORMAT
453 " on %" GST_PTR_FORMAT, display, query);
457 #if GST_GL_HAVE_WINDOW_X11
458 else if (display && g_strcmp0 (context_type, "gst.x11.display.handle") == 0) {
461 gst_query_parse_context (query, &old_context);
464 context = gst_context_copy (old_context);
466 context = gst_context_new ("gst.x11.display.handle", TRUE);
468 if (gst_gl_display_get_handle_type (display) & GST_GL_DISPLAY_TYPE_X11) {
469 Display *x11_display = (Display *) gst_gl_display_get_handle (display);
472 s = gst_context_writable_structure (context);
473 gst_structure_set (s, "display", G_TYPE_POINTER, x11_display, NULL);
475 gst_query_set_context (query, context);
476 gst_context_unref (context);
478 GST_DEBUG_OBJECT (element, "successfully set x11 display %p (from %"
479 GST_PTR_FORMAT ") on %" GST_PTR_FORMAT, x11_display, display,
487 #if GST_GL_HAVE_WINDOW_WAYLAND
489 && g_strcmp0 (context_type, "GstWaylandDisplayHandleContextType") == 0) {
492 gst_query_parse_context (query, &old_context);
495 context = gst_context_copy (old_context);
497 context = gst_context_new ("GstWaylandDisplayHandleContextType", TRUE);
499 if (gst_gl_display_get_handle_type (display) & GST_GL_DISPLAY_TYPE_WAYLAND) {
500 struct wl_display *wayland_display =
501 (struct wl_display *) gst_gl_display_get_handle (display);
503 if (wayland_display) {
504 s = gst_context_writable_structure (context);
505 gst_structure_set (s, "display", G_TYPE_POINTER, wayland_display, NULL);
507 gst_query_set_context (query, context);
508 gst_context_unref (context);
510 GST_DEBUG_OBJECT (element, "successfully set wayland display %p (from %"
511 GST_PTR_FORMAT ") on %" GST_PTR_FORMAT, wayland_display, display,
519 else if (other_context && g_strcmp0 (context_type, "gst.gl.app_context") == 0) {
522 gst_query_parse_context (query, &old_context);
525 context = gst_context_copy (old_context);
527 context = gst_context_new ("gst.gl.app_context", TRUE);
529 s = gst_context_writable_structure (context);
530 gst_structure_set (s, "context", GST_TYPE_GL_CONTEXT, other_context, NULL);
531 gst_query_set_context (query, context);
532 gst_context_unref (context);
534 GST_DEBUG_OBJECT (element, "successfully set application GL context %"
535 GST_PTR_FORMAT " on %" GST_PTR_FORMAT, other_context, query);
538 } else if (gl_context
539 && g_strcmp0 (context_type, "gst.gl.local_context") == 0) {
542 gst_query_parse_context (query, &old_context);
545 context = gst_context_copy (old_context);
547 context = gst_context_new ("gst.gl.local_context", TRUE);
549 s = gst_context_writable_structure (context);
550 gst_structure_set (s, "context", GST_TYPE_GL_CONTEXT, gl_context, NULL);
551 gst_query_set_context (query, context);
552 gst_context_unref (context);
554 GST_DEBUG_OBJECT (element, "successfully set GL context %"
555 GST_PTR_FORMAT " on %" GST_PTR_FORMAT, gl_context, query);
564 * gst_gl_query_local_gl_context:
565 * @element: a #GstElement to query from
566 * @direction: the #GstPadDirection to query
567 * @context_ptr: (inout): location containing the current and/or resulting
570 * Performs a GST_QUERY_CONTEXT query of type "gst.gl.local_context" on all
571 * #GstPads in @element of @direction for the local OpenGL context used by
572 * GStreamer elements.
574 * Returns: whether @context_ptr contains a #GstGLContext
577 gst_gl_query_local_gl_context (GstElement * element, GstPadDirection direction,
578 GstGLContext ** context_ptr)
582 const GstStructure *s;
584 g_return_val_if_fail (GST_IS_ELEMENT (element), FALSE);
585 g_return_val_if_fail (context_ptr != NULL, FALSE);
590 query = gst_query_new_context ("gst.gl.local_context");
591 if (gst_gl_run_query (GST_ELEMENT (element), query, direction)) {
592 gst_query_parse_context (query, &context);
594 s = gst_context_get_structure (context);
595 gst_structure_get (s, "context", GST_TYPE_GL_CONTEXT, context_ptr, NULL);
599 gst_query_unref (query);
601 return *context_ptr != NULL;
605 * gst_gl_get_plane_data_size:
606 * @info: a #GstVideoInfo
607 * @align: a #GstVideoAlignment or %NULL
608 * @plane: plane number in @info to retrieve the data size of
610 * Retrieve the size in bytes of a video plane of data with a certain alignment
613 gst_gl_get_plane_data_size (const GstVideoInfo * info,
614 const GstVideoAlignment * align, guint plane)
616 gint comp[GST_VIDEO_MAX_COMPONENTS];
620 gst_video_format_info_component (info->finfo, plane, comp);
622 padded_height = info->height;
625 padded_height += align->padding_top + align->padding_bottom;
628 GST_VIDEO_FORMAT_INFO_SCALE_HEIGHT (info->finfo, comp[0], padded_height);
630 plane_size = GST_VIDEO_INFO_PLANE_STRIDE (info, plane) * padded_height;
636 * gst_gl_get_plane_start:
637 * @info: a #GstVideoInfo
638 * @valign: a #GstVideoAlignment or %NULL
639 * @plane: plane number in @info to retrieve the data size of
641 * Returns: difference between the supposed start of the plane from the @info
642 * and where the data from the previous plane ends.
645 gst_gl_get_plane_start (const GstVideoInfo * info,
646 const GstVideoAlignment * valign, guint plane)
651 /* find the start of the plane data including padding */
653 for (i = 0; i < plane; i++) {
654 plane_start += gst_gl_get_plane_data_size (info, valign, i);
657 /* offset between the plane data start and where the video frame starts */
658 return (GST_VIDEO_INFO_PLANE_OFFSET (info, plane)) - plane_start;
662 * gst_gl_value_get_texture_target_mask:
663 * @value: an initialized #GValue of type G_TYPE_STRING
665 * See gst_gl_value_set_texture_target_from_mask() for what entails a mask
667 * Returns: the mask of #GstGLTextureTarget's in @value or
668 * %GST_GL_TEXTURE_TARGET_NONE on failure
671 gst_gl_value_get_texture_target_mask (const GValue * targets)
673 guint new_targets = 0;
675 g_return_val_if_fail (targets != NULL, GST_GL_TEXTURE_TARGET_NONE);
677 if (G_TYPE_CHECK_VALUE_TYPE (targets, G_TYPE_STRING)) {
678 GstGLTextureTarget target;
681 str = g_value_get_string (targets);
682 target = gst_gl_texture_target_from_string (str);
685 new_targets |= 1 << target;
686 } else if (G_TYPE_CHECK_VALUE_TYPE (targets, GST_TYPE_LIST)) {
689 m = gst_value_list_get_size (targets);
690 for (j = 0; j < m; j++) {
691 const GValue *val = gst_value_list_get_value (targets, j);
692 GstGLTextureTarget target;
695 str = g_value_get_string (val);
696 target = gst_gl_texture_target_from_string (str);
698 new_targets |= 1 << target;
706 * gst_gl_value_set_texture_target:
707 * @value: an initialized #GValue of type G_TYPE_STRING
708 * @target: a #GstGLTextureTarget's
710 * Returns: whether the @target could be set on @value
713 gst_gl_value_set_texture_target (GValue * value, GstGLTextureTarget target)
715 g_return_val_if_fail (value != NULL, FALSE);
716 g_return_val_if_fail (target != GST_GL_TEXTURE_TARGET_NONE, FALSE);
718 if (target == GST_GL_TEXTURE_TARGET_2D) {
719 g_value_set_static_string (value, GST_GL_TEXTURE_TARGET_2D_STR);
720 } else if (target == GST_GL_TEXTURE_TARGET_RECTANGLE) {
721 g_value_set_static_string (value, GST_GL_TEXTURE_TARGET_RECTANGLE_STR);
722 } else if (target == GST_GL_TEXTURE_TARGET_EXTERNAL_OES) {
723 g_value_set_static_string (value, GST_GL_TEXTURE_TARGET_EXTERNAL_OES_STR);
732 _gst_gl_log2_int64 (guint64 value)
743 * gst_gl_value_set_texture_target_from_mask:
744 * @value: an uninitialized #GValue
745 * @target_mask: a bitwise mask of #GstGLTextureTarget's
747 * A mask is a bitwise OR of (1 << target) where target is a valid
748 * #GstGLTextureTarget
750 * Returns: whether the @target_mask could be set on @value
753 gst_gl_value_set_texture_target_from_mask (GValue * value,
754 GstGLTextureTarget target_mask)
756 g_return_val_if_fail (value != NULL, FALSE);
757 g_return_val_if_fail (target_mask != GST_GL_TEXTURE_TARGET_NONE, FALSE);
759 if ((target_mask & (target_mask - 1)) == 0) {
760 /* only one texture target set */
761 g_value_init (value, G_TYPE_STRING);
762 return gst_gl_value_set_texture_target (value,
763 _gst_gl_log2_int64 (target_mask));
765 GValue item = G_VALUE_INIT;
766 gboolean ret = FALSE;
768 g_value_init (value, GST_TYPE_LIST);
769 g_value_init (&item, G_TYPE_STRING);
770 if (target_mask & (1 << GST_GL_TEXTURE_TARGET_2D)) {
771 gst_gl_value_set_texture_target (&item, GST_GL_TEXTURE_TARGET_2D);
772 gst_value_list_append_value (value, &item);
775 if (target_mask & (1 << GST_GL_TEXTURE_TARGET_RECTANGLE)) {
776 gst_gl_value_set_texture_target (&item, GST_GL_TEXTURE_TARGET_RECTANGLE);
777 gst_value_list_append_value (value, &item);
780 if (target_mask & (1 << GST_GL_TEXTURE_TARGET_EXTERNAL_OES)) {
781 gst_gl_value_set_texture_target (&item,
782 GST_GL_TEXTURE_TARGET_EXTERNAL_OES);
783 gst_value_list_append_value (value, &item);
787 g_value_unset (&item);
792 static const gfloat identity_matrix[] = {
799 static const gfloat from_ndc_matrix[] = {
806 static const gfloat to_ndc_matrix[] = {
810 -1.0, -1.0, -1.0, 1.0,
813 /* multiplies two 4x4 matrices, @a X @b, and stores the result in @result
814 * https://en.wikipedia.org/wiki/Matrix_multiplication
817 gst_gl_multiply_matrix4 (const gfloat * a, const gfloat * b, gfloat * result)
820 gfloat tmp[16] = { 0.0f };
822 if (!a || !b || !result)
824 for (i = 0; i < 4; i++) { /* column */
825 for (j = 0; j < 4; j++) { /* row */
826 for (k = 0; k < 4; k++) {
827 tmp[j + (i * 4)] += a[k + (i * 4)] * b[j + (k * 4)];
832 for (i = 0; i < 16; i++)
837 * gst_gl_get_affine_transformation_meta_as_ndc:
838 * @meta: (nullable): a #GstVideoAffineTransformationMeta
839 * @matrix: (out): result of the 4x4 matrix
841 * Retrieves the stored 4x4 affine transformation matrix stored in @meta in
842 * NDC coordinates. if @meta is NULL, an identity matrix is returned.
844 * NDC is a left-handed coordinate system
845 * - x - [-1, 1] - +ve X moves right
846 * - y - [-1, 1] - +ve Y moves up
847 * - z - [-1, 1] - +ve Z moves into
850 gst_gl_get_affine_transformation_meta_as_ndc (GstVideoAffineTransformationMeta *
851 meta, gfloat * matrix)
856 for (i = 0; i < 16; i++) {
857 matrix[i] = identity_matrix[i];
862 /* change of basis multiplications */
863 gst_gl_multiply_matrix4 (from_ndc_matrix, meta->matrix, tmp);
864 gst_gl_multiply_matrix4 (tmp, to_ndc_matrix, matrix);
868 void gst_gl_set_affine_transformation_meta_from_ndc
869 (GstVideoAffineTransformationMeta * meta, const gfloat * matrix)
873 g_return_if_fail (meta != NULL);
875 /* change of basis multiplications */
876 gst_gl_multiply_matrix4 (to_ndc_matrix, matrix, tmp);
877 gst_gl_multiply_matrix4 (tmp, from_ndc_matrix, meta->matrix);