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