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