Move files from gst-plugins-base into the "subprojects/gst-plugins-base/" subdir
[platform/upstream/gstreamer.git] / subprojects / gst-plugins-base / gst-libs / gst / video / video-overlay-composition.c
1 /* GStreamer Video Overlay Composition
2  * Copyright (C) 2011 Intel Corporation
3  * Copyright (C) 2011 Collabora Ltd.
4  * Copyright (C) 2011 Tim-Philipp Müller <tim centricular net>
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Library General Public
8  * License as published by the Free Software Foundation; either
9  * version 2 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Library General Public License for more details.
15  *
16  * You should have received a copy of the GNU Library General Public
17  * License along with this library; if not, write to the
18  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
19  * Boston, MA 02110-1301, USA.
20  */
21
22 /**
23  * SECTION:gstvideooverlaycomposition
24  * @title: GstVideoOverlayRectangle
25  * @short_description: Video Buffer Overlay Compositions (Subtitles, Logos)
26  *
27  * Functions to create and handle overlay compositions on video buffers.
28  *
29  * An overlay composition describes one or more overlay rectangles to be
30  * blended on top of a video buffer.
31  *
32  * This API serves two main purposes:
33  *
34  * * it can be used to attach overlay information (subtitles or logos)
35  *   to non-raw video buffers such as GL/VAAPI/VDPAU surfaces. The actual
36  *   blending of the overlay can then be done by e.g. the video sink that
37  *   processes these non-raw buffers.
38  *
39  * * it can also be used to blend overlay rectangles on top of raw video
40  *   buffers, thus consolidating blending functionality for raw video in
41  *   one place.
42  *
43  * Together, this allows existing overlay elements to easily handle raw
44  * and non-raw video as input in without major changes (once the overlays
45  * have been put into a #GstVideoOverlayComposition object anyway) - for raw
46  * video the overlay can just use the blending function to blend the data
47  * on top of the video, and for surface buffers it can just attach them to
48  * the buffer and let the sink render the overlays.
49  *
50  */
51
52 /* TODO:
53  *  - provide accessors for seq_num and other fields (as needed)
54  *  - allow overlay to set/get original pango markup string on/from rectangle
55  */
56
57 #ifdef HAVE_CONFIG_H
58 #include "config.h"
59 #endif
60
61 #include "video-overlay-composition.h"
62 #include "video-blend.h"
63 #include "gstvideometa.h"
64 #include <string.h>
65
66 struct _GstVideoOverlayComposition
67 {
68   GstMiniObject parent;
69
70   guint num_rectangles;
71   GstVideoOverlayRectangle **rectangles;
72
73   /* lowest rectangle sequence number still used by the upstream
74    * overlay element. This way a renderer maintaining some kind of
75    * rectangles <-> surface cache can know when to free cached
76    * surfaces/rectangles. */
77   guint min_seq_num_used;
78
79   /* sequence number for the composition (same series as rectangles) */
80   guint seq_num;
81 };
82
83 struct _GstVideoOverlayRectangle
84 {
85   GstMiniObject parent;
86
87   /* Position on video frame and dimension of output rectangle in
88    * output frame terms (already adjusted for the PAR of the output
89    * frame). x/y can be negative (overlay will be clipped then) */
90   gint x, y;
91   guint render_width, render_height;
92
93   /* Info on overlay pixels (format, width, height) */
94   GstVideoInfo info;
95
96   /* The flags associated to this rectangle */
97   GstVideoOverlayFormatFlags flags;
98
99   /* Refcounted blob of memory, no caps or timestamps */
100   GstBuffer *pixels;
101
102   /* FIXME: how to express source like text or pango markup?
103    *        (just add source type enum + source buffer with data)
104    *
105    * FOR 0.10: always send pixel blobs, but attach source data in
106    * addition (reason: if downstream changes, we can't renegotiate
107    * that properly, if we just do a query of supported formats from
108    * the start). Sink will just ignore pixels and use pango markup
109    * from source data if it supports that.
110    *
111    * FOR 0.11: overlay should query formats (pango markup, pixels)
112    * supported by downstream and then only send that. We can
113    * renegotiate via the reconfigure event.
114    */
115
116   /* sequence number: useful for backends/renderers/sinks that want
117    * to maintain a cache of rectangles <-> surfaces. The value of
118    * the min_seq_num_used in the composition tells the renderer which
119    * rectangles have expired. */
120   guint seq_num;
121
122   /* global alpha: global alpha value of the rectangle. Each each per-pixel
123    * alpha value of image-data will be multiplied with the global alpha value
124    * during blending.
125    * Can be used for efficient fading in/out of overlay rectangles.
126    * GstElements that render OverlayCompositions and don't support global alpha
127    * should simply ignore it.*/
128   gfloat global_alpha;
129
130   /* track alpha-values already applied: */
131   gfloat applied_global_alpha;
132   /* store initial per-pixel alpha values: */
133   guint8 *initial_alpha;
134
135   /* FIXME: we may also need a (private) way to cache converted/scaled
136    * pixel blobs */
137   GMutex lock;
138
139   GList *scaled_rectangles;
140 };
141
142 #define GST_RECTANGLE_LOCK(rect)   g_mutex_lock(&rect->lock)
143 #define GST_RECTANGLE_UNLOCK(rect) g_mutex_unlock(&rect->lock)
144
145 /* --------------------------- utility functions --------------------------- */
146
147 #ifndef GST_DISABLE_GST_DEBUG
148
149 #define GST_CAT_DEFAULT ensure_debug_category()
150
151 static GstDebugCategory *
152 ensure_debug_category (void)
153 {
154   static gsize cat_gonce = 0;
155
156   if (g_once_init_enter (&cat_gonce)) {
157     gsize cat_done;
158
159     cat_done = (gsize) _gst_debug_category_new ("video-composition", 0,
160         "video overlay composition");
161
162     g_once_init_leave (&cat_gonce, cat_done);
163   }
164
165   return (GstDebugCategory *) cat_gonce;
166 }
167
168 #else
169
170 #define ensure_debug_category() /* NOOP */
171
172 #endif /* GST_DISABLE_GST_DEBUG */
173
174 static guint
175 gst_video_overlay_get_seqnum (void)
176 {
177   static gint seqnum;           /* 0 */
178
179   return (guint) g_atomic_int_add (&seqnum, 1);
180 }
181
182 static gboolean
183 gst_video_overlay_composition_meta_init (GstMeta * meta, gpointer params,
184     GstBuffer * buf)
185 {
186   GstVideoOverlayCompositionMeta *ometa;
187
188   ometa = (GstVideoOverlayCompositionMeta *) meta;
189
190   ometa->overlay = NULL;
191
192   return TRUE;
193 }
194
195 static void
196 gst_video_overlay_composition_meta_free (GstMeta * meta, GstBuffer * buf)
197 {
198   GstVideoOverlayCompositionMeta *ometa;
199
200   ometa = (GstVideoOverlayCompositionMeta *) meta;
201
202   if (ometa->overlay)
203     gst_video_overlay_composition_unref (ometa->overlay);
204 }
205
206 static gboolean
207 gst_video_overlay_composition_meta_transform (GstBuffer * dest, GstMeta * meta,
208     GstBuffer * buffer, GQuark type, gpointer data)
209 {
210   GstVideoOverlayCompositionMeta *dmeta, *smeta;
211
212   smeta = (GstVideoOverlayCompositionMeta *) meta;
213
214   if (GST_META_TRANSFORM_IS_COPY (type)) {
215     GstMetaTransformCopy *copy = data;
216
217     if (!copy->region) {
218       GST_DEBUG ("copy video overlay composition metadata");
219
220       /* only copy if the complete data is copied as well */
221       dmeta =
222           (GstVideoOverlayCompositionMeta *) gst_buffer_add_meta (dest,
223           GST_VIDEO_OVERLAY_COMPOSITION_META_INFO, NULL);
224       if (!dmeta)
225         return FALSE;
226
227       dmeta->overlay = gst_video_overlay_composition_ref (smeta->overlay);
228     }
229   } else {
230     /* return FALSE, if transform type is not supported */
231     return FALSE;
232   }
233   return TRUE;
234 }
235
236 GType
237 gst_video_overlay_composition_meta_api_get_type (void)
238 {
239   static GType type = 0;
240   static const gchar *tags[] = { NULL };
241
242   if (g_once_init_enter (&type)) {
243     GType _type =
244         gst_meta_api_type_register ("GstVideoOverlayCompositionMetaAPI", tags);
245     g_once_init_leave (&type, _type);
246   }
247   return type;
248 }
249
250 /* video overlay composition metadata */
251 const GstMetaInfo *
252 gst_video_overlay_composition_meta_get_info (void)
253 {
254   static const GstMetaInfo *video_overlay_composition_meta_info = NULL;
255
256   if (g_once_init_enter ((GstMetaInfo **) &
257           video_overlay_composition_meta_info)) {
258     const GstMetaInfo *meta =
259         gst_meta_register (GST_VIDEO_OVERLAY_COMPOSITION_META_API_TYPE,
260         "GstVideoOverlayCompositionMeta",
261         sizeof (GstVideoOverlayCompositionMeta),
262         (GstMetaInitFunction) gst_video_overlay_composition_meta_init,
263         (GstMetaFreeFunction) gst_video_overlay_composition_meta_free,
264         (GstMetaTransformFunction)
265         gst_video_overlay_composition_meta_transform);
266     g_once_init_leave ((GstMetaInfo **) & video_overlay_composition_meta_info,
267         (GstMetaInfo *) meta);
268   }
269   return video_overlay_composition_meta_info;
270 }
271
272 /**
273  * gst_buffer_add_video_overlay_composition_meta:
274  * @buf: a #GstBuffer
275  * @comp: (allow-none): a #GstVideoOverlayComposition
276  *
277  * Sets an overlay composition on a buffer. The buffer will obtain its own
278  * reference to the composition, meaning this function does not take ownership
279  * of @comp.
280  *
281  * Returns: (transfer none): a #GstVideoOverlayCompositionMeta
282  */
283 GstVideoOverlayCompositionMeta *
284 gst_buffer_add_video_overlay_composition_meta (GstBuffer * buf,
285     GstVideoOverlayComposition * comp)
286 {
287   GstVideoOverlayCompositionMeta *ometa;
288
289   g_return_val_if_fail (gst_buffer_is_writable (buf), NULL);
290
291   ometa = (GstVideoOverlayCompositionMeta *)
292       gst_buffer_add_meta (buf, GST_VIDEO_OVERLAY_COMPOSITION_META_INFO, NULL);
293
294   ometa->overlay = gst_video_overlay_composition_ref (comp);
295
296   return ometa;
297 }
298
299 /* ------------------------------ composition ------------------------------ */
300
301 #define RECTANGLE_ARRAY_STEP 4  /* premature optimization */
302
303 GST_DEFINE_MINI_OBJECT_TYPE (GstVideoOverlayComposition,
304     gst_video_overlay_composition);
305
306 static void
307 gst_video_overlay_composition_free (GstMiniObject * mini_obj)
308 {
309   GstVideoOverlayComposition *comp = (GstVideoOverlayComposition *) mini_obj;
310   guint num;
311
312   num = comp->num_rectangles;
313
314   while (num > 0) {
315     gst_mini_object_remove_parent (GST_MINI_OBJECT_CAST (comp->rectangles[num -
316                 1]), GST_MINI_OBJECT_CAST (comp));
317     gst_video_overlay_rectangle_unref (comp->rectangles[num - 1]);
318     --num;
319   }
320
321   g_free (comp->rectangles);
322   comp->rectangles = NULL;
323   comp->num_rectangles = 0;
324
325   g_slice_free (GstVideoOverlayComposition, comp);
326 }
327
328 /**
329  * gst_video_overlay_composition_new:
330  * @rectangle: (transfer none) (nullable): a #GstVideoOverlayRectangle to add to the
331  *     composition
332  *
333  * Creates a new video overlay composition object to hold one or more
334  * overlay rectangles.
335  *
336  * Note that since 1.20 this allows to pass %NULL for @rectangle.
337  *
338  * Returns: (transfer full): a new #GstVideoOverlayComposition. Unref with
339  *     gst_video_overlay_composition_unref() when no longer needed.
340  */
341 GstVideoOverlayComposition *
342 gst_video_overlay_composition_new (GstVideoOverlayRectangle * rectangle)
343 {
344   GstVideoOverlayComposition *comp;
345
346   g_return_val_if_fail (GST_IS_VIDEO_OVERLAY_RECTANGLE (rectangle)
347       || NULL, NULL);
348
349   comp = g_slice_new0 (GstVideoOverlayComposition);
350
351   gst_mini_object_init (GST_MINI_OBJECT_CAST (comp), 0,
352       GST_TYPE_VIDEO_OVERLAY_COMPOSITION,
353       (GstMiniObjectCopyFunction) gst_video_overlay_composition_copy,
354       NULL, (GstMiniObjectFreeFunction) gst_video_overlay_composition_free);
355
356   comp->rectangles = g_new0 (GstVideoOverlayRectangle *, RECTANGLE_ARRAY_STEP);
357
358   comp->seq_num = gst_video_overlay_get_seqnum ();
359
360   /* since the rectangle was created earlier, its seqnum is smaller than ours */
361   comp->min_seq_num_used = rectangle->seq_num;
362
363   GST_LOG ("new composition %p: seq_num %u", comp, comp->seq_num);
364
365   if (rectangle)
366     gst_video_overlay_composition_add_rectangle (comp, rectangle);
367
368   return comp;
369 }
370
371 /**
372  * gst_video_overlay_composition_add_rectangle:
373  * @comp: a #GstVideoOverlayComposition
374  * @rectangle: (transfer none): a #GstVideoOverlayRectangle to add to the
375  *     composition
376  *
377  * Adds an overlay rectangle to an existing overlay composition object. This
378  * must be done right after creating the overlay composition.
379  */
380 void
381 gst_video_overlay_composition_add_rectangle (GstVideoOverlayComposition * comp,
382     GstVideoOverlayRectangle * rectangle)
383 {
384   g_return_if_fail (GST_IS_VIDEO_OVERLAY_COMPOSITION (comp));
385   g_return_if_fail (GST_IS_VIDEO_OVERLAY_RECTANGLE (rectangle));
386   g_return_if_fail (gst_mini_object_is_writable (GST_MINI_OBJECT_CAST (comp)));
387
388   if (comp->num_rectangles % RECTANGLE_ARRAY_STEP == 0) {
389     comp->rectangles =
390         g_renew (GstVideoOverlayRectangle *, comp->rectangles,
391         comp->num_rectangles + RECTANGLE_ARRAY_STEP);
392   }
393
394   comp->rectangles[comp->num_rectangles] =
395       gst_video_overlay_rectangle_ref (rectangle);
396   gst_mini_object_add_parent (GST_MINI_OBJECT_CAST (rectangle),
397       GST_MINI_OBJECT_CAST (comp));
398   comp->num_rectangles += 1;
399
400   comp->min_seq_num_used = MIN (comp->min_seq_num_used, rectangle->seq_num);
401
402   GST_LOG ("composition %p: added rectangle %p", comp, rectangle);
403 }
404
405 /**
406  * gst_video_overlay_composition_n_rectangles:
407  * @comp: a #GstVideoOverlayComposition
408  *
409  * Returns the number of #GstVideoOverlayRectangle<!-- -->s contained in @comp.
410  *
411  * Returns: the number of rectangles
412  */
413 guint
414 gst_video_overlay_composition_n_rectangles (GstVideoOverlayComposition * comp)
415 {
416   g_return_val_if_fail (GST_IS_VIDEO_OVERLAY_COMPOSITION (comp), 0);
417
418   return comp->num_rectangles;
419 }
420
421 /**
422  * gst_video_overlay_composition_get_rectangle:
423  * @comp: a #GstVideoOverlayComposition
424  * @n: number of the rectangle to get
425  *
426  * Returns the @n-th #GstVideoOverlayRectangle contained in @comp.
427  *
428  * Returns: (transfer none): the @n-th rectangle, or NULL if @n is out of
429  *     bounds. Will not return a new reference, the caller will need to
430  *     obtain her own reference using gst_video_overlay_rectangle_ref()
431  *     if needed.
432  */
433 GstVideoOverlayRectangle *
434 gst_video_overlay_composition_get_rectangle (GstVideoOverlayComposition * comp,
435     guint n)
436 {
437   g_return_val_if_fail (GST_IS_VIDEO_OVERLAY_COMPOSITION (comp), NULL);
438
439   if (n >= comp->num_rectangles)
440     return NULL;
441
442   return comp->rectangles[n];
443 }
444
445 static gboolean
446 gst_video_overlay_rectangle_needs_scaling (GstVideoOverlayRectangle * r)
447 {
448   return (GST_VIDEO_INFO_WIDTH (&r->info) != r->render_width ||
449       GST_VIDEO_INFO_HEIGHT (&r->info) != r->render_height);
450 }
451
452 /**
453  * gst_video_overlay_composition_blend:
454  * @comp: a #GstVideoOverlayComposition
455  * @video_buf: a #GstVideoFrame containing raw video data in a
456  *             supported format. It should be mapped using GST_MAP_READWRITE
457  *
458  * Blends the overlay rectangles in @comp on top of the raw video data
459  * contained in @video_buf. The data in @video_buf must be writable and
460  * mapped appropriately.
461  *
462  * Since @video_buf data is read and will be modified, it ought be
463  * mapped with flag GST_MAP_READWRITE.
464  */
465 /* FIXME: formats with more than 8 bit per component which get unpacked into
466  * ARGB64 or AYUV64 (such as v210, v216, UYVP, GRAY16_LE and GRAY16_BE)
467  * are not supported yet by the code in video-blend.c.
468  */
469 gboolean
470 gst_video_overlay_composition_blend (GstVideoOverlayComposition * comp,
471     GstVideoFrame * video_buf)
472 {
473   GstVideoInfo scaled_info;
474   GstVideoInfo *vinfo;
475   GstVideoFrame rectangle_frame;
476   GstVideoFormat fmt;
477   GstBuffer *pixels = NULL;
478   gboolean ret = TRUE;
479   guint n, num;
480   int w, h;
481
482   g_return_val_if_fail (GST_IS_VIDEO_OVERLAY_COMPOSITION (comp), FALSE);
483   g_return_val_if_fail (video_buf != NULL, FALSE);
484
485   w = GST_VIDEO_FRAME_WIDTH (video_buf);
486   h = GST_VIDEO_FRAME_HEIGHT (video_buf);
487   fmt = GST_VIDEO_FRAME_FORMAT (video_buf);
488
489   num = comp->num_rectangles;
490   GST_LOG ("Blending composition %p with %u rectangles onto video buffer %p "
491       "(%ux%u, format %u)", comp, num, video_buf, w, h, fmt);
492
493   for (n = 0; n < num; ++n) {
494     GstVideoOverlayRectangle *rect;
495     gboolean needs_scaling;
496
497     rect = comp->rectangles[n];
498
499     GST_LOG (" rectangle %u %p: %ux%u, format %u", n, rect,
500         GST_VIDEO_INFO_WIDTH (&rect->info), GST_VIDEO_INFO_HEIGHT (&rect->info),
501         GST_VIDEO_INFO_FORMAT (&rect->info));
502
503     needs_scaling = gst_video_overlay_rectangle_needs_scaling (rect);
504     if (needs_scaling) {
505       gst_video_blend_scale_linear_RGBA (&rect->info, rect->pixels,
506           rect->render_height, rect->render_width, &scaled_info, &pixels);
507       vinfo = &scaled_info;
508     } else {
509       pixels = gst_buffer_ref (rect->pixels);
510       vinfo = &rect->info;
511     }
512
513     gst_video_frame_map (&rectangle_frame, vinfo, pixels, GST_MAP_READ);
514
515     ret = gst_video_blend (video_buf, &rectangle_frame, rect->x, rect->y,
516         rect->global_alpha);
517     gst_video_frame_unmap (&rectangle_frame);
518     if (!ret) {
519       GST_WARNING ("Could not blend overlay rectangle onto video buffer");
520     }
521
522     /* FIXME: should cache scaled pixels in the rectangle struct */
523     gst_buffer_unref (pixels);
524   }
525
526   return ret;
527 }
528
529 /**
530  * gst_video_overlay_composition_copy:
531  * @comp: (transfer none): a #GstVideoOverlayComposition to copy
532  *
533  * Makes a copy of @comp and all contained rectangles, so that it is possible
534  * to modify the composition and contained rectangles (e.g. add additional
535  * rectangles or change the render co-ordinates or render dimension). The
536  * actual overlay pixel data buffers contained in the rectangles are not
537  * copied.
538  *
539  * Returns: (transfer full): a new #GstVideoOverlayComposition equivalent
540  *     to @comp.
541  */
542 GstVideoOverlayComposition *
543 gst_video_overlay_composition_copy (GstVideoOverlayComposition * comp)
544 {
545   GstVideoOverlayComposition *copy;
546   GstVideoOverlayRectangle *rect;
547   guint n;
548
549   g_return_val_if_fail (GST_IS_VIDEO_OVERLAY_COMPOSITION (comp), NULL);
550
551   if (G_LIKELY (comp->num_rectangles == 0))
552     return gst_video_overlay_composition_new (NULL);
553
554   rect = gst_video_overlay_rectangle_copy (comp->rectangles[0]);
555   copy = gst_video_overlay_composition_new (rect);
556   gst_video_overlay_rectangle_unref (rect);
557
558   for (n = 1; n < comp->num_rectangles; ++n) {
559     rect = gst_video_overlay_rectangle_copy (comp->rectangles[n]);
560     gst_video_overlay_composition_add_rectangle (copy, rect);
561     gst_video_overlay_rectangle_unref (rect);
562   }
563
564   return copy;
565 }
566
567 /**
568  * gst_video_overlay_composition_make_writable:
569  * @comp: (transfer full): a #GstVideoOverlayComposition to copy
570  *
571  * Takes ownership of @comp and returns a version of @comp that is writable
572  * (i.e. can be modified). Will either return @comp right away, or create a
573  * new writable copy of @comp and unref @comp itself. All the contained
574  * rectangles will also be copied, but the actual overlay pixel data buffers
575  * contained in the rectangles are not copied.
576  *
577  * Returns: (transfer full): a writable #GstVideoOverlayComposition
578  *     equivalent to @comp.
579  */
580 GstVideoOverlayComposition *
581 gst_video_overlay_composition_make_writable (GstVideoOverlayComposition * comp)
582 {
583   GstVideoOverlayComposition *writable_comp;
584
585   g_return_val_if_fail (GST_IS_VIDEO_OVERLAY_COMPOSITION (comp), NULL);
586
587   if (gst_mini_object_is_writable (GST_MINI_OBJECT_CAST (comp))) {
588     guint n;
589
590     for (n = 0; n < comp->num_rectangles; ++n) {
591       if (!gst_mini_object_is_writable (GST_MINI_OBJECT_CAST (comp->rectangles
592                   [n])))
593         goto copy;
594     }
595     return comp;
596   }
597
598 copy:
599
600   writable_comp = gst_video_overlay_composition_copy (comp);
601   gst_video_overlay_composition_unref (comp);
602
603   return writable_comp;
604 }
605
606 /**
607  * gst_video_overlay_composition_get_seqnum:
608  * @comp: a #GstVideoOverlayComposition
609  *
610  * Returns the sequence number of this composition. Sequence numbers are
611  * monotonically increasing and unique for overlay compositions and rectangles
612  * (meaning there will never be a rectangle with the same sequence number as
613  * a composition).
614  *
615  * Returns: the sequence number of @comp
616  */
617 guint
618 gst_video_overlay_composition_get_seqnum (GstVideoOverlayComposition * comp)
619 {
620   g_return_val_if_fail (GST_IS_VIDEO_OVERLAY_COMPOSITION (comp), 0);
621
622   return comp->seq_num;
623 }
624
625 /* ------------------------------ rectangles ------------------------------ -*/
626
627 GST_DEFINE_MINI_OBJECT_TYPE (GstVideoOverlayRectangle,
628     gst_video_overlay_rectangle);
629
630 static void
631 gst_video_overlay_rectangle_free (GstMiniObject * mini_obj)
632 {
633   GstVideoOverlayRectangle *rect = (GstVideoOverlayRectangle *) mini_obj;
634
635   gst_mini_object_remove_parent (GST_MINI_OBJECT_CAST (rect->pixels),
636       GST_MINI_OBJECT_CAST (rect));
637   gst_buffer_replace (&rect->pixels, NULL);
638
639   while (rect->scaled_rectangles != NULL) {
640     GstVideoOverlayRectangle *scaled_rect = rect->scaled_rectangles->data;
641
642     gst_video_overlay_rectangle_unref (scaled_rect);
643
644     rect->scaled_rectangles =
645         g_list_delete_link (rect->scaled_rectangles, rect->scaled_rectangles);
646   }
647
648   g_free (rect->initial_alpha);
649   g_mutex_clear (&rect->lock);
650
651   g_slice_free (GstVideoOverlayRectangle, rect);
652 }
653
654 static inline gboolean
655 gst_video_overlay_rectangle_check_flags (GstVideoOverlayFormatFlags flags)
656 {
657   /* Check flags only contains flags we know about */
658   return (flags & ~(GST_VIDEO_OVERLAY_FORMAT_FLAG_PREMULTIPLIED_ALPHA |
659           GST_VIDEO_OVERLAY_FORMAT_FLAG_GLOBAL_ALPHA)) == 0;
660 }
661
662 static gboolean
663 gst_video_overlay_rectangle_is_same_alpha_type (GstVideoOverlayFormatFlags
664     flags1, GstVideoOverlayFormatFlags flags2)
665 {
666   return ((flags1 ^ flags2) & GST_VIDEO_OVERLAY_FORMAT_FLAG_PREMULTIPLIED_ALPHA)
667       == 0;
668 }
669
670
671 /**
672  * gst_video_overlay_rectangle_new_raw:
673  * @pixels: (transfer none): a #GstBuffer pointing to the pixel memory
674  * @render_x: the X co-ordinate on the video where the top-left corner of this
675  *     overlay rectangle should be rendered to
676  * @render_y: the Y co-ordinate on the video where the top-left corner of this
677  *     overlay rectangle should be rendered to
678  * @render_width: the render width of this rectangle on the video
679  * @render_height: the render height of this rectangle on the video
680  * @flags: flags
681  *
682  * Creates a new video overlay rectangle with ARGB or AYUV pixel data.
683  * The layout in case of ARGB of the components in memory is B-G-R-A
684  * on little-endian platforms
685  * (corresponding to #GST_VIDEO_FORMAT_BGRA) and A-R-G-B on big-endian
686  * platforms (corresponding to #GST_VIDEO_FORMAT_ARGB). In other words,
687  * pixels are treated as 32-bit words and the lowest 8 bits then contain
688  * the blue component value and the highest 8 bits contain the alpha
689  * component value. Unless specified in the flags, the RGB values are
690  * non-premultiplied. This is the format that is used by most hardware,
691  * and also many rendering libraries such as Cairo, for example.
692  * The pixel data buffer must have #GstVideoMeta set.
693  *
694  * Returns: (transfer full): a new #GstVideoOverlayRectangle. Unref with
695  *     gst_video_overlay_rectangle_unref() when no longer needed.
696  */
697 GstVideoOverlayRectangle *
698 gst_video_overlay_rectangle_new_raw (GstBuffer * pixels,
699     gint render_x, gint render_y, guint render_width, guint render_height,
700     GstVideoOverlayFormatFlags flags)
701 {
702   GstVideoOverlayRectangle *rect;
703   GstVideoMeta *vmeta;
704   GstVideoFormat format;
705   guint width, height;
706
707   g_return_val_if_fail (GST_IS_BUFFER (pixels), NULL);
708   g_return_val_if_fail (render_height > 0 && render_width > 0, NULL);
709   g_return_val_if_fail (gst_video_overlay_rectangle_check_flags (flags), NULL);
710
711   /* buffer must have video meta with some expected settings */
712   vmeta = gst_buffer_get_video_meta (pixels);
713   g_return_val_if_fail (vmeta, NULL);
714   g_return_val_if_fail (vmeta->format ==
715       GST_VIDEO_OVERLAY_COMPOSITION_FORMAT_RGB ||
716       vmeta->format == GST_VIDEO_OVERLAY_COMPOSITION_FORMAT_YUV, NULL);
717   g_return_val_if_fail (vmeta->flags == GST_VIDEO_FRAME_FLAG_NONE, NULL);
718
719   format = vmeta->format;
720   width = vmeta->width;
721   height = vmeta->height;
722
723   /* technically ((height-1)*stride)+width might be okay too */
724   g_return_val_if_fail (gst_buffer_get_size (pixels) >= height * width * 4,
725       NULL);
726   g_return_val_if_fail (height > 0 && width > 0, NULL);
727
728   rect = g_slice_new0 (GstVideoOverlayRectangle);
729
730   gst_mini_object_init (GST_MINI_OBJECT_CAST (rect), 0,
731       GST_TYPE_VIDEO_OVERLAY_RECTANGLE,
732       (GstMiniObjectCopyFunction) gst_video_overlay_rectangle_copy,
733       NULL, (GstMiniObjectFreeFunction) gst_video_overlay_rectangle_free);
734
735   g_mutex_init (&rect->lock);
736
737   rect->pixels = gst_buffer_ref (pixels);
738   gst_mini_object_add_parent (GST_MINI_OBJECT_CAST (pixels),
739       GST_MINI_OBJECT_CAST (rect));
740   rect->scaled_rectangles = NULL;
741
742   gst_video_info_init (&rect->info);
743   if (!gst_video_info_set_format (&rect->info, format, width, height)) {
744     gst_mini_object_unref (GST_MINI_OBJECT_CAST (rect));
745     return NULL;
746   }
747   if (flags & GST_VIDEO_OVERLAY_FORMAT_FLAG_PREMULTIPLIED_ALPHA)
748     rect->info.flags |= GST_VIDEO_FLAG_PREMULTIPLIED_ALPHA;
749
750   rect->x = render_x;
751   rect->y = render_y;
752   rect->render_width = render_width;
753   rect->render_height = render_height;
754
755   rect->global_alpha = 1.0;
756   rect->applied_global_alpha = 1.0;
757   rect->initial_alpha = NULL;
758
759   rect->flags = flags;
760
761   rect->seq_num = gst_video_overlay_get_seqnum ();
762
763   GST_LOG ("new rectangle %p: %ux%u => %ux%u @ %u,%u, seq_num %u, format %u, "
764       "flags %x, pixels %p, global_alpha=%f", rect, width, height, render_width,
765       render_height, render_x, render_y, rect->seq_num, format,
766       rect->flags, pixels, rect->global_alpha);
767
768   return rect;
769 }
770
771 /**
772  * gst_video_overlay_rectangle_get_render_rectangle:
773  * @rectangle: a #GstVideoOverlayRectangle
774  * @render_x: (out) (allow-none): address where to store the X render offset
775  * @render_y: (out) (allow-none): address where to store the Y render offset
776  * @render_width: (out) (allow-none): address where to store the render width
777  * @render_height: (out) (allow-none): address where to store the render height
778  *
779  * Retrieves the render position and render dimension of the overlay
780  * rectangle on the video.
781  *
782  * Returns: TRUE if valid render dimensions were retrieved.
783  */
784 gboolean
785 gst_video_overlay_rectangle_get_render_rectangle (GstVideoOverlayRectangle *
786     rectangle, gint * render_x, gint * render_y, guint * render_width,
787     guint * render_height)
788 {
789   g_return_val_if_fail (GST_IS_VIDEO_OVERLAY_RECTANGLE (rectangle), FALSE);
790
791   if (render_x)
792     *render_x = rectangle->x;
793   if (render_y)
794     *render_y = rectangle->y;
795   if (render_width)
796     *render_width = rectangle->render_width;
797   if (render_height)
798     *render_height = rectangle->render_height;
799
800   return TRUE;
801 }
802
803 /**
804  * gst_video_overlay_rectangle_set_render_rectangle:
805  * @rectangle: a #GstVideoOverlayRectangle
806  * @render_x: render X position of rectangle on video
807  * @render_y: render Y position of rectangle on video
808  * @render_width: render width of rectangle
809  * @render_height: render height of rectangle
810  *
811  * Sets the render position and dimensions of the rectangle on the video.
812  * This function is mainly for elements that modify the size of the video
813  * in some way (e.g. through scaling or cropping) and need to adjust the
814  * details of any overlays to match the operation that changed the size.
815  *
816  * @rectangle must be writable, meaning its refcount must be 1. You can
817  * make the rectangles inside a #GstVideoOverlayComposition writable using
818  * gst_video_overlay_composition_make_writable() or
819  * gst_video_overlay_composition_copy().
820  */
821 void
822 gst_video_overlay_rectangle_set_render_rectangle (GstVideoOverlayRectangle *
823     rectangle, gint render_x, gint render_y, guint render_width,
824     guint render_height)
825 {
826   g_return_if_fail (GST_IS_VIDEO_OVERLAY_RECTANGLE (rectangle));
827   g_return_if_fail (gst_mini_object_is_writable (GST_MINI_OBJECT_CAST
828           (rectangle)));
829
830   rectangle->x = render_x;
831   rectangle->y = render_y;
832   rectangle->render_width = render_width;
833   rectangle->render_height = render_height;
834 }
835
836 /* FIXME: orc-ify */
837 static void
838 gst_video_overlay_rectangle_premultiply_0 (GstVideoFrame * frame)
839 {
840   int i, j;
841   int width = GST_VIDEO_FRAME_WIDTH (frame);
842   int height = GST_VIDEO_FRAME_HEIGHT (frame);
843   int stride = GST_VIDEO_FRAME_PLANE_STRIDE (frame, 0);
844   guint8 *data = GST_VIDEO_FRAME_PLANE_DATA (frame, 0);
845
846   for (j = 0; j < height; ++j) {
847     guint8 *line;
848
849     line = data;
850     line += stride * j;
851     for (i = 0; i < width; ++i) {
852       int a = line[0];
853       line[1] = line[1] * a / 255;
854       line[2] = line[2] * a / 255;
855       line[3] = line[3] * a / 255;
856       line += 4;
857     }
858   }
859 }
860
861 static void
862 gst_video_overlay_rectangle_premultiply_3 (GstVideoFrame * frame)
863 {
864   int i, j;
865   int width = GST_VIDEO_FRAME_WIDTH (frame);
866   int height = GST_VIDEO_FRAME_HEIGHT (frame);
867   int stride = GST_VIDEO_FRAME_PLANE_STRIDE (frame, 0);
868   guint8 *data = GST_VIDEO_FRAME_PLANE_DATA (frame, 0);
869
870   for (j = 0; j < height; ++j) {
871     guint8 *line;
872
873     line = data;
874     line += stride * j;
875     for (i = 0; i < width; ++i) {
876       int a = line[3];
877       line[0] = line[0] * a / 255;
878       line[1] = line[1] * a / 255;
879       line[2] = line[2] * a / 255;
880       line += 4;
881     }
882   }
883 }
884
885 static void
886 gst_video_overlay_rectangle_premultiply (GstVideoFrame * frame)
887 {
888   gint alpha_offset;
889
890   alpha_offset = GST_VIDEO_FRAME_COMP_POFFSET (frame, 3);
891   switch (alpha_offset) {
892     case 0:
893       gst_video_overlay_rectangle_premultiply_0 (frame);
894       break;
895     case 3:
896       gst_video_overlay_rectangle_premultiply_3 (frame);
897       break;
898     default:
899       g_assert_not_reached ();
900       break;
901   }
902 }
903
904 /* FIXME: orc-ify */
905 static void
906 gst_video_overlay_rectangle_unpremultiply_0 (GstVideoFrame * frame)
907 {
908   int i, j;
909   int width = GST_VIDEO_FRAME_WIDTH (frame);
910   int height = GST_VIDEO_FRAME_HEIGHT (frame);
911   int stride = GST_VIDEO_FRAME_PLANE_STRIDE (frame, 0);
912   guint8 *data = GST_VIDEO_FRAME_PLANE_DATA (frame, 0);
913
914   for (j = 0; j < height; ++j) {
915     guint8 *line;
916
917     line = data;
918     line += stride * j;
919     for (i = 0; i < width; ++i) {
920       int a = line[0];
921       if (a) {
922         line[1] = MIN ((line[1] * 255 + a / 2) / a, 255);
923         line[2] = MIN ((line[2] * 255 + a / 2) / a, 255);
924         line[3] = MIN ((line[3] * 255 + a / 2) / a, 255);
925       }
926       line += 4;
927     }
928   }
929 }
930
931 static void
932 gst_video_overlay_rectangle_unpremultiply_3 (GstVideoFrame * frame)
933 {
934   int i, j;
935   int width = GST_VIDEO_FRAME_WIDTH (frame);
936   int height = GST_VIDEO_FRAME_HEIGHT (frame);
937   int stride = GST_VIDEO_FRAME_PLANE_STRIDE (frame, 0);
938   guint8 *data = GST_VIDEO_FRAME_PLANE_DATA (frame, 0);
939
940   for (j = 0; j < height; ++j) {
941     guint8 *line;
942
943     line = data;
944     line += stride * j;
945     for (i = 0; i < width; ++i) {
946       int a = line[3];
947       if (a) {
948         line[0] = MIN ((line[0] * 255 + a / 2) / a, 255);
949         line[1] = MIN ((line[1] * 255 + a / 2) / a, 255);
950         line[2] = MIN ((line[2] * 255 + a / 2) / a, 255);
951       }
952       line += 4;
953     }
954   }
955 }
956
957 static void
958 gst_video_overlay_rectangle_unpremultiply (GstVideoFrame * frame)
959 {
960   gint alpha_offset;
961
962   alpha_offset = GST_VIDEO_FRAME_COMP_POFFSET (frame, 3);
963   switch (alpha_offset) {
964     case 0:
965       gst_video_overlay_rectangle_unpremultiply_0 (frame);
966       break;
967     case 3:
968       gst_video_overlay_rectangle_unpremultiply_3 (frame);
969       break;
970     default:
971       g_assert_not_reached ();
972       break;
973   }
974 }
975
976
977 static void
978 gst_video_overlay_rectangle_extract_alpha (GstVideoOverlayRectangle * rect)
979 {
980   guint8 *src, *dst;
981   GstVideoFrame frame;
982   gint i, j, w, h, stride, alpha_offset;
983
984   alpha_offset = GST_VIDEO_INFO_COMP_POFFSET (&rect->info, 3);
985   g_return_if_fail (alpha_offset == 0 || alpha_offset == 3);
986
987   gst_video_frame_map (&frame, &rect->info, rect->pixels, GST_MAP_READ);
988   src = GST_VIDEO_FRAME_PLANE_DATA (&frame, 0);
989   w = GST_VIDEO_INFO_WIDTH (&rect->info);
990   h = GST_VIDEO_INFO_HEIGHT (&rect->info);
991   stride = GST_VIDEO_INFO_PLANE_STRIDE (&rect->info, 0);
992
993   g_free (rect->initial_alpha);
994   rect->initial_alpha = g_malloc (w * h);
995   dst = rect->initial_alpha;
996
997   for (i = 0; i < h; i++) {
998     for (j = 0; j < w; j++) {
999       *dst = src[alpha_offset];
1000       dst++;
1001       src += 4;
1002     }
1003     src += stride - 4 * w;
1004   }
1005   gst_video_frame_unmap (&frame);
1006 }
1007
1008
1009 static void
1010 gst_video_overlay_rectangle_apply_global_alpha (GstVideoOverlayRectangle * rect,
1011     float global_alpha)
1012 {
1013   guint8 *src, *dst;
1014   GstVideoFrame frame;
1015   gint i, j, w, h, stride;
1016   gint argb_a, argb_r, argb_g, argb_b;
1017   gint alpha_offset;
1018
1019   g_assert (!(rect->applied_global_alpha != 1.0
1020           && rect->initial_alpha == NULL));
1021
1022   alpha_offset = GST_VIDEO_INFO_COMP_POFFSET (&rect->info, 3);
1023   g_return_if_fail (alpha_offset == 0 || alpha_offset == 3);
1024
1025   if (global_alpha == rect->applied_global_alpha)
1026     return;
1027
1028   if (rect->initial_alpha == NULL)
1029     gst_video_overlay_rectangle_extract_alpha (rect);
1030
1031   src = rect->initial_alpha;
1032   if (!gst_buffer_is_writable (rect->pixels)) {
1033     gst_mini_object_remove_parent (GST_MINI_OBJECT_CAST (rect->pixels),
1034         GST_MINI_OBJECT_CAST (rect));
1035     rect->pixels = gst_buffer_copy (rect->pixels);
1036     gst_mini_object_add_parent (GST_MINI_OBJECT_CAST (rect->pixels),
1037         GST_MINI_OBJECT_CAST (rect));
1038   }
1039
1040   gst_video_frame_map (&frame, &rect->info, rect->pixels, GST_MAP_READ);
1041   dst = GST_VIDEO_FRAME_PLANE_DATA (&frame, 0);
1042   w = GST_VIDEO_INFO_WIDTH (&rect->info);
1043   h = GST_VIDEO_INFO_HEIGHT (&rect->info);
1044   stride = GST_VIDEO_INFO_PLANE_STRIDE (&rect->info, 0);
1045
1046   argb_a = GST_VIDEO_INFO_COMP_POFFSET (&rect->info, 3);
1047   argb_r = (argb_a + 1) % 4;
1048   argb_g = (argb_a + 2) % 4;
1049   argb_b = (argb_a + 3) % 4;
1050
1051   for (i = 0; i < h; i++) {
1052     for (j = 0; j < w; j++) {
1053       guint8 na = (guint8) (*src * global_alpha);
1054
1055       if (! !(rect->flags & GST_VIDEO_OVERLAY_FORMAT_FLAG_PREMULTIPLIED_ALPHA)) {
1056         dst[argb_r] =
1057             (guint8) ((double) (dst[argb_r] * 255) / (double) dst[argb_a]) *
1058             na / 255;
1059         dst[argb_g] =
1060             (guint8) ((double) (dst[argb_g] * 255) / (double) dst[argb_a]) *
1061             na / 255;
1062         dst[argb_b] =
1063             (guint8) ((double) (dst[argb_b] * 255) / (double) dst[argb_a]) *
1064             na / 255;
1065       }
1066       dst[argb_a] = na;
1067       src++;
1068       dst += 4;
1069     }
1070     dst += stride - 4 * w;
1071   }
1072   gst_video_frame_unmap (&frame);
1073
1074   rect->applied_global_alpha = global_alpha;
1075 }
1076
1077 static void
1078 gst_video_overlay_rectangle_convert (const GstVideoInfo * src,
1079     GstBuffer * src_buffer, GstVideoFormat dest_format, GstVideoInfo * dest,
1080     GstBuffer ** dest_buffer)
1081 {
1082   gint width, height, stride;
1083   GstVideoFrame src_frame, dest_frame;
1084   GstVideoFormat format;
1085   gint k, l;
1086   guint8 *sdata, *ddata;
1087
1088   format = GST_VIDEO_INFO_FORMAT (src);
1089
1090   width = GST_VIDEO_INFO_WIDTH (src);
1091   height = GST_VIDEO_INFO_HEIGHT (src);
1092
1093   gst_video_info_init (dest);
1094   if (!gst_video_info_set_format (dest, dest_format, width, height)) {
1095     g_warn_if_reached ();
1096     return;
1097   }
1098
1099   *dest_buffer = gst_buffer_new_and_alloc (GST_VIDEO_INFO_SIZE (dest));
1100
1101   gst_video_frame_map (&src_frame, src, src_buffer, GST_MAP_READ);
1102   gst_video_frame_map (&dest_frame, dest, *dest_buffer, GST_MAP_WRITE);
1103
1104   sdata = GST_VIDEO_FRAME_PLANE_DATA (&src_frame, 0);
1105   ddata = GST_VIDEO_FRAME_PLANE_DATA (&dest_frame, 0);
1106   stride = GST_VIDEO_FRAME_PLANE_STRIDE (&src_frame, 0);
1107
1108   if (format == GST_VIDEO_OVERLAY_COMPOSITION_FORMAT_YUV &&
1109       dest_format == GST_VIDEO_OVERLAY_COMPOSITION_FORMAT_RGB) {
1110     gint ayuv;
1111     gint a, y, u, v, r, g, b;
1112
1113     for (k = 0; k < height; k++) {
1114       for (l = 0; l < width; l++) {
1115         ayuv = GST_READ_UINT32_BE (sdata);
1116         a = ayuv >> 24;
1117         y = (ayuv >> 16) & 0xff;
1118         u = (ayuv >> 8) & 0xff;
1119         v = (ayuv & 0xff);
1120
1121         r = (298 * y + 459 * v - 63514) >> 8;
1122         g = (298 * y - 55 * u - 136 * v + 19681) >> 8;
1123         b = (298 * y + 541 * u - 73988) >> 8;
1124
1125         r = CLAMP (r, 0, 255);
1126         g = CLAMP (g, 0, 255);
1127         b = CLAMP (b, 0, 255);
1128
1129         /* native endian ARGB */
1130         *(guint32 *) ddata = ((a << 24) | (r << 16) | (g << 8) | b);
1131
1132         sdata += 4;
1133         ddata += 4;
1134       }
1135       sdata += stride - 4 * width;
1136     }
1137   } else if (format == GST_VIDEO_OVERLAY_COMPOSITION_FORMAT_RGB &&
1138       dest_format == GST_VIDEO_OVERLAY_COMPOSITION_FORMAT_YUV) {
1139     gint argb;
1140     gint a, y, u, v, r, g, b;
1141
1142     for (k = 0; k < height; k++) {
1143       for (l = 0; l < width; l++) {
1144         /* native endian ARGB */
1145         argb = *(guint32 *) sdata;
1146         a = argb >> 24;
1147         r = (argb >> 16) & 0xff;
1148         g = (argb >> 8) & 0xff;
1149         b = (argb & 0xff);
1150
1151         y = (47 * r + 157 * g + 16 * b + 4096) >> 8;
1152         u = (-26 * r - 87 * g + 112 * b + 32768) >> 8;
1153         v = (112 * r - 102 * g - 10 * b + 32768) >> 8;
1154
1155         y = CLAMP (y, 0, 255);
1156         u = CLAMP (u, 0, 255);
1157         v = CLAMP (v, 0, 255);
1158
1159         GST_WRITE_UINT32_BE (ddata, ((a << 24) | (y << 16) | (u << 8) | v));
1160
1161         sdata += 4;
1162         ddata += 4;
1163       }
1164       sdata += stride - 4 * width;
1165     }
1166   } else {
1167     GST_ERROR ("unsupported conversion");
1168     g_assert_not_reached ();
1169   }
1170
1171   gst_video_frame_unmap (&src_frame);
1172   gst_video_frame_unmap (&dest_frame);
1173 }
1174
1175 static GstBuffer *
1176 gst_video_overlay_rectangle_get_pixels_raw_internal (GstVideoOverlayRectangle *
1177     rectangle, GstVideoOverlayFormatFlags flags, gboolean unscaled,
1178     GstVideoFormat wanted_format)
1179 {
1180   GstVideoOverlayFormatFlags new_flags;
1181   GstVideoOverlayRectangle *scaled_rect = NULL, *conv_rect = NULL;
1182   GstVideoInfo info;
1183   GstVideoFrame frame;
1184   GstBuffer *buf;
1185   GList *l;
1186   guint width, height;
1187   guint wanted_width;
1188   guint wanted_height;
1189   gboolean apply_global_alpha;
1190   gboolean revert_global_alpha;
1191   GstVideoFormat format;
1192
1193   g_return_val_if_fail (GST_IS_VIDEO_OVERLAY_RECTANGLE (rectangle), NULL);
1194   g_return_val_if_fail (gst_video_overlay_rectangle_check_flags (flags), NULL);
1195
1196   width = GST_VIDEO_INFO_WIDTH (&rectangle->info);
1197   height = GST_VIDEO_INFO_HEIGHT (&rectangle->info);
1198   wanted_width = unscaled ? width : rectangle->render_width;
1199   wanted_height = unscaled ? height : rectangle->render_height;
1200   format = GST_VIDEO_INFO_FORMAT (&rectangle->info);
1201
1202   apply_global_alpha =
1203       (! !(rectangle->flags & GST_VIDEO_OVERLAY_FORMAT_FLAG_GLOBAL_ALPHA)
1204       && !(flags & GST_VIDEO_OVERLAY_FORMAT_FLAG_GLOBAL_ALPHA));
1205   revert_global_alpha =
1206       (! !(rectangle->flags & GST_VIDEO_OVERLAY_FORMAT_FLAG_GLOBAL_ALPHA)
1207       && ! !(flags & GST_VIDEO_OVERLAY_FORMAT_FLAG_GLOBAL_ALPHA));
1208
1209   /* This assumes we don't need to adjust the format */
1210   if (wanted_width == width &&
1211       wanted_height == height &&
1212       wanted_format == format &&
1213       gst_video_overlay_rectangle_is_same_alpha_type (rectangle->flags,
1214           flags)) {
1215     /* don't need to apply/revert global-alpha either: */
1216     if ((!apply_global_alpha
1217             || rectangle->applied_global_alpha == rectangle->global_alpha)
1218         && (!revert_global_alpha || rectangle->applied_global_alpha == 1.0)) {
1219       return rectangle->pixels;
1220     } else {
1221       /* only apply/revert global-alpha */
1222       scaled_rect = rectangle;
1223       goto done;
1224     }
1225   }
1226
1227   /* see if we've got one cached already */
1228   GST_RECTANGLE_LOCK (rectangle);
1229   for (l = rectangle->scaled_rectangles; l != NULL; l = l->next) {
1230     GstVideoOverlayRectangle *r = l->data;
1231
1232     if (GST_VIDEO_INFO_WIDTH (&r->info) == wanted_width &&
1233         GST_VIDEO_INFO_HEIGHT (&r->info) == wanted_height &&
1234         GST_VIDEO_INFO_FORMAT (&r->info) == wanted_format &&
1235         gst_video_overlay_rectangle_is_same_alpha_type (r->flags, flags)) {
1236       /* we'll keep these rectangles around until finalize, so it's ok not
1237        * to take our own ref here */
1238       scaled_rect = r;
1239       break;
1240     }
1241   }
1242   GST_RECTANGLE_UNLOCK (rectangle);
1243
1244   if (scaled_rect != NULL)
1245     goto done;
1246
1247   /* maybe have one in the right format though */
1248   if (format != wanted_format) {
1249     GST_RECTANGLE_LOCK (rectangle);
1250     for (l = rectangle->scaled_rectangles; l != NULL; l = l->next) {
1251       GstVideoOverlayRectangle *r = l->data;
1252
1253       if (GST_VIDEO_INFO_FORMAT (&r->info) == wanted_format &&
1254           gst_video_overlay_rectangle_is_same_alpha_type (r->flags, flags)) {
1255         /* we'll keep these rectangles around until finalize, so it's ok not
1256          * to take our own ref here */
1257         conv_rect = r;
1258         break;
1259       }
1260     }
1261     GST_RECTANGLE_UNLOCK (rectangle);
1262   } else {
1263     conv_rect = rectangle;
1264   }
1265
1266   if (conv_rect == NULL) {
1267     GstVideoInfo conv_info;
1268
1269     gst_video_overlay_rectangle_convert (&rectangle->info, rectangle->pixels,
1270         wanted_format, &conv_info, &buf);
1271     gst_buffer_add_video_meta (buf, GST_VIDEO_FRAME_FLAG_NONE,
1272         GST_VIDEO_INFO_FORMAT (&conv_info), width, height);
1273     conv_rect = gst_video_overlay_rectangle_new_raw (buf,
1274         0, 0, width, height, rectangle->flags);
1275     if (rectangle->global_alpha != 1.0)
1276       gst_video_overlay_rectangle_set_global_alpha (scaled_rect,
1277           rectangle->global_alpha);
1278     gst_buffer_unref (buf);
1279     /* keep this converted one around as well in any case */
1280     GST_RECTANGLE_LOCK (rectangle);
1281     rectangle->scaled_rectangles =
1282         g_list_prepend (rectangle->scaled_rectangles, conv_rect);
1283     GST_RECTANGLE_UNLOCK (rectangle);
1284   }
1285
1286   /* now we continue from conv_rect */
1287   width = GST_VIDEO_INFO_WIDTH (&conv_rect->info);
1288   height = GST_VIDEO_INFO_HEIGHT (&conv_rect->info);
1289   format = GST_VIDEO_INFO_FORMAT (&conv_rect->info);
1290
1291   /* not cached yet, do the preprocessing and put the result into our cache */
1292   if (wanted_width != width || wanted_height != height) {
1293     GstVideoInfo scaled_info;
1294
1295     /* we could check the cache for a scaled rect with global_alpha == 1 here */
1296     gst_video_blend_scale_linear_RGBA (&conv_rect->info, conv_rect->pixels,
1297         wanted_height, wanted_width, &scaled_info, &buf);
1298     info = scaled_info;
1299     gst_buffer_add_video_meta (buf, GST_VIDEO_FRAME_FLAG_NONE,
1300         GST_VIDEO_INFO_FORMAT (&conv_rect->info), wanted_width, wanted_height);
1301   } else if (!gst_video_overlay_rectangle_is_same_alpha_type (conv_rect->flags,
1302           flags)) {
1303     /* if we don't have to scale, we have to modify the alpha values, so we
1304      * need to make a copy of the pixel memory (and we take ownership below) */
1305     buf = gst_buffer_copy (conv_rect->pixels);
1306     info = conv_rect->info;
1307   } else {
1308     /* do not need to scale or modify alpha values, almost done then */
1309     scaled_rect = conv_rect;
1310     goto done;
1311   }
1312
1313   new_flags = conv_rect->flags;
1314   gst_video_frame_map (&frame, &info, buf, GST_MAP_READWRITE);
1315   if (!gst_video_overlay_rectangle_is_same_alpha_type (conv_rect->flags, flags)) {
1316     if (rectangle->flags & GST_VIDEO_OVERLAY_FORMAT_FLAG_PREMULTIPLIED_ALPHA) {
1317       gst_video_overlay_rectangle_unpremultiply (&frame);
1318       new_flags &= ~GST_VIDEO_OVERLAY_FORMAT_FLAG_PREMULTIPLIED_ALPHA;
1319     } else {
1320       gst_video_overlay_rectangle_premultiply (&frame);
1321       new_flags |= GST_VIDEO_OVERLAY_FORMAT_FLAG_PREMULTIPLIED_ALPHA;
1322     }
1323   }
1324   gst_video_frame_unmap (&frame);
1325
1326   scaled_rect = gst_video_overlay_rectangle_new_raw (buf,
1327       0, 0, wanted_width, wanted_height, new_flags);
1328   if (conv_rect->global_alpha != 1.0)
1329     gst_video_overlay_rectangle_set_global_alpha (scaled_rect,
1330         conv_rect->global_alpha);
1331   gst_buffer_unref (buf);
1332
1333   GST_RECTANGLE_LOCK (rectangle);
1334   rectangle->scaled_rectangles =
1335       g_list_prepend (rectangle->scaled_rectangles, scaled_rect);
1336   GST_RECTANGLE_UNLOCK (rectangle);
1337
1338 done:
1339
1340   GST_RECTANGLE_LOCK (rectangle);
1341   if (apply_global_alpha
1342       && scaled_rect->applied_global_alpha != rectangle->global_alpha) {
1343     gst_video_overlay_rectangle_apply_global_alpha (scaled_rect,
1344         rectangle->global_alpha);
1345     gst_video_overlay_rectangle_set_global_alpha (scaled_rect,
1346         rectangle->global_alpha);
1347   } else if (revert_global_alpha && scaled_rect->applied_global_alpha != 1.0) {
1348     gst_video_overlay_rectangle_apply_global_alpha (scaled_rect, 1.0);
1349   }
1350   GST_RECTANGLE_UNLOCK (rectangle);
1351
1352   return scaled_rect->pixels;
1353 }
1354
1355
1356 /**
1357  * gst_video_overlay_rectangle_get_pixels_raw:
1358  * @rectangle: a #GstVideoOverlayRectangle
1359  * @flags: flags
1360  *    If a global_alpha value != 1 is set for the rectangle, the caller
1361  *    should set the #GST_VIDEO_OVERLAY_FORMAT_FLAG_GLOBAL_ALPHA flag
1362  *    if he wants to apply global-alpha himself. If the flag is not set
1363  *    global_alpha is applied internally before returning the pixel-data.
1364  *
1365  * Returns: (transfer none): a #GstBuffer holding the pixel data with
1366  *    format as originally provided and specified in video meta with
1367  *    width and height of the render dimensions as per
1368  *    gst_video_overlay_rectangle_get_render_rectangle(). This function does
1369  *    not return a reference, the caller should obtain a reference of her own
1370  *    with gst_buffer_ref() if needed.
1371  */
1372 GstBuffer *
1373 gst_video_overlay_rectangle_get_pixels_raw (GstVideoOverlayRectangle *
1374     rectangle, GstVideoOverlayFormatFlags flags)
1375 {
1376   return gst_video_overlay_rectangle_get_pixels_raw_internal (rectangle,
1377       flags, FALSE, GST_VIDEO_INFO_FORMAT (&rectangle->info));
1378 }
1379
1380 /**
1381  * gst_video_overlay_rectangle_get_pixels_argb:
1382  * @rectangle: a #GstVideoOverlayRectangle
1383  * @flags: flags
1384  *    If a global_alpha value != 1 is set for the rectangle, the caller
1385  *    should set the #GST_VIDEO_OVERLAY_FORMAT_FLAG_GLOBAL_ALPHA flag
1386  *    if he wants to apply global-alpha himself. If the flag is not set
1387  *    global_alpha is applied internally before returning the pixel-data.
1388  *
1389  * Returns: (transfer none): a #GstBuffer holding the ARGB pixel data with
1390  *    width and height of the render dimensions as per
1391  *    gst_video_overlay_rectangle_get_render_rectangle(). This function does
1392  *    not return a reference, the caller should obtain a reference of her own
1393  *    with gst_buffer_ref() if needed.
1394  */
1395 GstBuffer *
1396 gst_video_overlay_rectangle_get_pixels_argb (GstVideoOverlayRectangle *
1397     rectangle, GstVideoOverlayFormatFlags flags)
1398 {
1399   return gst_video_overlay_rectangle_get_pixels_raw_internal (rectangle,
1400       flags, FALSE, GST_VIDEO_OVERLAY_COMPOSITION_FORMAT_RGB);
1401 }
1402
1403 /**
1404  * gst_video_overlay_rectangle_get_pixels_ayuv:
1405  * @rectangle: a #GstVideoOverlayRectangle
1406  * @flags: flags
1407  *    If a global_alpha value != 1 is set for the rectangle, the caller
1408  *    should set the #GST_VIDEO_OVERLAY_FORMAT_FLAG_GLOBAL_ALPHA flag
1409  *    if he wants to apply global-alpha himself. If the flag is not set
1410  *    global_alpha is applied internally before returning the pixel-data.
1411  *
1412  * Returns: (transfer none): a #GstBuffer holding the AYUV pixel data with
1413  *    width and height of the render dimensions as per
1414  *    gst_video_overlay_rectangle_get_render_rectangle(). This function does
1415  *    not return a reference, the caller should obtain a reference of her own
1416  *    with gst_buffer_ref() if needed.
1417  */
1418 GstBuffer *
1419 gst_video_overlay_rectangle_get_pixels_ayuv (GstVideoOverlayRectangle *
1420     rectangle, GstVideoOverlayFormatFlags flags)
1421 {
1422   return gst_video_overlay_rectangle_get_pixels_raw_internal (rectangle,
1423       flags, FALSE, GST_VIDEO_OVERLAY_COMPOSITION_FORMAT_YUV);
1424 }
1425
1426 /**
1427  * gst_video_overlay_rectangle_get_pixels_unscaled_raw:
1428  * @rectangle: a #GstVideoOverlayRectangle
1429  * @flags: flags.
1430  *    If a global_alpha value != 1 is set for the rectangle, the caller
1431  *    should set the #GST_VIDEO_OVERLAY_FORMAT_FLAG_GLOBAL_ALPHA flag
1432  *    if he wants to apply global-alpha himself. If the flag is not set
1433  *    global_alpha is applied internally before returning the pixel-data.
1434  *
1435  * Retrieves the pixel data as it is. This is useful if the caller can
1436  * do the scaling itself when handling the overlaying. The rectangle will
1437  * need to be scaled to the render dimensions, which can be retrieved using
1438  * gst_video_overlay_rectangle_get_render_rectangle().
1439  *
1440  * Returns: (transfer none): a #GstBuffer holding the pixel data with
1441  *    #GstVideoMeta set. This function does not return a reference, the caller
1442  *    should obtain a reference of her own with gst_buffer_ref() if needed.
1443  */
1444 GstBuffer *
1445 gst_video_overlay_rectangle_get_pixels_unscaled_raw (GstVideoOverlayRectangle *
1446     rectangle, GstVideoOverlayFormatFlags flags)
1447 {
1448   g_return_val_if_fail (GST_IS_VIDEO_OVERLAY_RECTANGLE (rectangle), NULL);
1449
1450   return gst_video_overlay_rectangle_get_pixels_raw_internal (rectangle,
1451       flags, TRUE, GST_VIDEO_INFO_FORMAT (&rectangle->info));
1452 }
1453
1454 /**
1455  * gst_video_overlay_rectangle_get_pixels_unscaled_argb:
1456  * @rectangle: a #GstVideoOverlayRectangle
1457  * @flags: flags.
1458  *    If a global_alpha value != 1 is set for the rectangle, the caller
1459  *    should set the #GST_VIDEO_OVERLAY_FORMAT_FLAG_GLOBAL_ALPHA flag
1460  *    if he wants to apply global-alpha himself. If the flag is not set
1461  *    global_alpha is applied internally before returning the pixel-data.
1462  *
1463  * Retrieves the pixel data as it is. This is useful if the caller can
1464  * do the scaling itself when handling the overlaying. The rectangle will
1465  * need to be scaled to the render dimensions, which can be retrieved using
1466  * gst_video_overlay_rectangle_get_render_rectangle().
1467  *
1468  * Returns: (transfer none): a #GstBuffer holding the ARGB pixel data with
1469  *    #GstVideoMeta set. This function does not return a reference, the caller
1470  *    should obtain a reference of her own with gst_buffer_ref() if needed.
1471  */
1472 GstBuffer *
1473 gst_video_overlay_rectangle_get_pixels_unscaled_argb (GstVideoOverlayRectangle *
1474     rectangle, GstVideoOverlayFormatFlags flags)
1475 {
1476   g_return_val_if_fail (GST_IS_VIDEO_OVERLAY_RECTANGLE (rectangle), NULL);
1477
1478   return gst_video_overlay_rectangle_get_pixels_raw_internal (rectangle,
1479       flags, TRUE, GST_VIDEO_OVERLAY_COMPOSITION_FORMAT_RGB);
1480 }
1481
1482 /**
1483  * gst_video_overlay_rectangle_get_pixels_unscaled_ayuv:
1484  * @rectangle: a #GstVideoOverlayRectangle
1485  * @flags: flags.
1486  *    If a global_alpha value != 1 is set for the rectangle, the caller
1487  *    should set the #GST_VIDEO_OVERLAY_FORMAT_FLAG_GLOBAL_ALPHA flag
1488  *    if he wants to apply global-alpha himself. If the flag is not set
1489  *    global_alpha is applied internally before returning the pixel-data.
1490  *
1491  * Retrieves the pixel data as it is. This is useful if the caller can
1492  * do the scaling itself when handling the overlaying. The rectangle will
1493  * need to be scaled to the render dimensions, which can be retrieved using
1494  * gst_video_overlay_rectangle_get_render_rectangle().
1495  *
1496  * Returns: (transfer none): a #GstBuffer holding the AYUV pixel data with
1497  *    #GstVideoMeta set. This function does not return a reference, the caller
1498  *    should obtain a reference of her own with gst_buffer_ref() if needed.
1499  */
1500 GstBuffer *
1501 gst_video_overlay_rectangle_get_pixels_unscaled_ayuv (GstVideoOverlayRectangle *
1502     rectangle, GstVideoOverlayFormatFlags flags)
1503 {
1504   g_return_val_if_fail (GST_IS_VIDEO_OVERLAY_RECTANGLE (rectangle), NULL);
1505
1506   return gst_video_overlay_rectangle_get_pixels_raw_internal (rectangle,
1507       flags, TRUE, GST_VIDEO_OVERLAY_COMPOSITION_FORMAT_YUV);
1508 }
1509
1510 /**
1511  * gst_video_overlay_rectangle_get_flags:
1512  * @rectangle: a #GstVideoOverlayRectangle
1513  *
1514  * Retrieves the flags associated with a #GstVideoOverlayRectangle.
1515  * This is useful if the caller can handle both premultiplied alpha and
1516  * non premultiplied alpha, for example. By knowing whether the rectangle
1517  * uses premultiplied or not, it can request the pixel data in the format
1518  * it is stored in, to avoid unnecessary conversion.
1519  *
1520  * Returns: the #GstVideoOverlayFormatFlags associated with the rectangle.
1521  */
1522 GstVideoOverlayFormatFlags
1523 gst_video_overlay_rectangle_get_flags (GstVideoOverlayRectangle * rectangle)
1524 {
1525   g_return_val_if_fail (GST_IS_VIDEO_OVERLAY_RECTANGLE (rectangle),
1526       GST_VIDEO_OVERLAY_FORMAT_FLAG_NONE);
1527
1528   return rectangle->flags;
1529 }
1530
1531 /**
1532  * gst_video_overlay_rectangle_get_global_alpha:
1533  * @rectangle: a #GstVideoOverlayRectangle
1534  *
1535  * Retrieves the global-alpha value associated with a #GstVideoOverlayRectangle.
1536  *
1537  * Returns: the global-alpha value associated with the rectangle.
1538  */
1539 gfloat
1540 gst_video_overlay_rectangle_get_global_alpha (GstVideoOverlayRectangle *
1541     rectangle)
1542 {
1543   g_return_val_if_fail (GST_IS_VIDEO_OVERLAY_RECTANGLE (rectangle), -1);
1544
1545   return rectangle->global_alpha;
1546 }
1547
1548 /**
1549  * gst_video_overlay_rectangle_set_global_alpha:
1550  * @rectangle: a #GstVideoOverlayRectangle
1551  * @global_alpha: Global alpha value (0 to 1.0)
1552  *
1553  * Sets the global alpha value associated with a #GstVideoOverlayRectangle. Per-
1554  * pixel alpha values are multiplied with this value. Valid
1555  * values: 0 <= global_alpha <= 1; 1 to deactivate.
1556  *
1557  * @rectangle must be writable, meaning its refcount must be 1. You can
1558  * make the rectangles inside a #GstVideoOverlayComposition writable using
1559  * gst_video_overlay_composition_make_writable() or
1560  * gst_video_overlay_composition_copy().
1561  */
1562 void
1563 gst_video_overlay_rectangle_set_global_alpha (GstVideoOverlayRectangle *
1564     rectangle, gfloat global_alpha)
1565 {
1566   g_return_if_fail (GST_IS_VIDEO_OVERLAY_RECTANGLE (rectangle));
1567   g_return_if_fail (gst_mini_object_is_writable (GST_MINI_OBJECT_CAST
1568           (rectangle)));
1569   g_return_if_fail (global_alpha >= 0 && global_alpha <= 1);
1570
1571   if (rectangle->global_alpha != global_alpha) {
1572     rectangle->global_alpha = global_alpha;
1573     if (global_alpha != 1)
1574       rectangle->flags |= GST_VIDEO_OVERLAY_FORMAT_FLAG_GLOBAL_ALPHA;
1575     else
1576       rectangle->flags &= ~GST_VIDEO_OVERLAY_FORMAT_FLAG_GLOBAL_ALPHA;
1577     /* update seq_num automatically to signal the consumer, that data has changed
1578      * note, that this might mislead renderers, that can handle global-alpha
1579      * themselves, because what they want to know is whether the actual pixel data
1580      * has changed. */
1581     rectangle->seq_num = gst_video_overlay_get_seqnum ();
1582   }
1583 }
1584
1585 /**
1586  * gst_video_overlay_rectangle_copy:
1587  * @rectangle: (transfer none): a #GstVideoOverlayRectangle to copy
1588  *
1589  * Makes a copy of @rectangle, so that it is possible to modify it
1590  * (e.g. to change the render co-ordinates or render dimension). The
1591  * actual overlay pixel data buffers contained in the rectangle are not
1592  * copied.
1593  *
1594  * Returns: (transfer full): a new #GstVideoOverlayRectangle equivalent
1595  *     to @rectangle.
1596  */
1597 GstVideoOverlayRectangle *
1598 gst_video_overlay_rectangle_copy (GstVideoOverlayRectangle * rectangle)
1599 {
1600   GstVideoOverlayRectangle *copy;
1601
1602   g_return_val_if_fail (GST_IS_VIDEO_OVERLAY_RECTANGLE (rectangle), NULL);
1603
1604   copy = gst_video_overlay_rectangle_new_raw (rectangle->pixels,
1605       rectangle->x, rectangle->y,
1606       rectangle->render_width, rectangle->render_height, rectangle->flags);
1607   if (rectangle->global_alpha != 1)
1608     gst_video_overlay_rectangle_set_global_alpha (copy,
1609         rectangle->global_alpha);
1610
1611   return copy;
1612 }
1613
1614 /**
1615  * gst_video_overlay_rectangle_get_seqnum:
1616  * @rectangle: a #GstVideoOverlayRectangle
1617  *
1618  * Returns the sequence number of this rectangle. Sequence numbers are
1619  * monotonically increasing and unique for overlay compositions and rectangles
1620  * (meaning there will never be a rectangle with the same sequence number as
1621  * a composition).
1622  *
1623  * Using the sequence number of a rectangle as an indicator for changed
1624  * pixel-data of a rectangle is dangereous. Some API calls, like e.g.
1625  * gst_video_overlay_rectangle_set_global_alpha(), automatically update
1626  * the per rectangle sequence number, which is misleading for renderers/
1627  * consumers, that handle global-alpha themselves. For them  the
1628  * pixel-data returned by gst_video_overlay_rectangle_get_pixels_*()
1629  * won't be different for different global-alpha values. In this case a
1630  * renderer could also use the GstBuffer pointers as a hint for changed
1631  * pixel-data.
1632  *
1633  * Returns: the sequence number of @rectangle
1634  */
1635 guint
1636 gst_video_overlay_rectangle_get_seqnum (GstVideoOverlayRectangle * rectangle)
1637 {
1638   g_return_val_if_fail (GST_IS_VIDEO_OVERLAY_RECTANGLE (rectangle), 0);
1639
1640   return rectangle->seq_num;
1641 }