/*
 * Copyright (C) 2004 Wim Taymans <wim@fluendo.com>
 * Copyright (C) 2006 Mindfruit Bv.
 *   Author: Sjoerd Simons <sjoerd@luon.net>
 *   Author: Alex Ugarte <alexugarte@gmail.com>
 * Copyright (C) 2009 Alex Ugarte <augarte@vicomtech.org>
 * Copyright (C) 2009 Sebastian Dröge <sebastian.droege@collabora.co.uk>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
 * Boston, MA 02110-1301, USA.
 */

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include "blend.h"
#include "compositororc.h"

#include <string.h>

#include <gst/video/video.h>

GST_DEBUG_CATEGORY_STATIC (gst_compositor_blend_debug);
#define GST_CAT_DEFAULT gst_compositor_blend_debug

/* Below are the implementations of everything */

/* A32 is for AYUV, VUYA, ARGB and BGRA */
#define BLEND_A32(name, method, LOOP)		\
static void \
method##_ ##name (GstVideoFrame * srcframe, gint xpos, gint ypos, \
    gdouble src_alpha, GstVideoFrame * destframe, gint dst_y_start, \
    gint dst_y_end, GstCompositorBlendMode mode) \
{ \
  guint s_alpha; \
  gint src_stride, dest_stride; \
  gint dest_width, dest_height; \
  guint8 *src, *dest; \
  gint src_width, src_height; \
  \
  src_width = GST_VIDEO_FRAME_WIDTH (srcframe); \
  src_height = GST_VIDEO_FRAME_HEIGHT (srcframe); \
  src = GST_VIDEO_FRAME_PLANE_DATA (srcframe, 0); \
  src_stride = GST_VIDEO_FRAME_COMP_STRIDE (srcframe, 0); \
  dest = GST_VIDEO_FRAME_PLANE_DATA (destframe, 0); \
  dest_stride = GST_VIDEO_FRAME_COMP_STRIDE (destframe, 0); \
  dest_width = GST_VIDEO_FRAME_COMP_WIDTH (destframe, 0); \
  dest_height = GST_VIDEO_FRAME_COMP_HEIGHT (destframe, 0); \
  \
  s_alpha = CLAMP ((gint) (src_alpha * 255), 0, 255); \
  \
  /* If it's completely transparent... we just return */ \
  if (G_UNLIKELY (s_alpha == 0)) \
    return; \
  \
  if (dst_y_end > dest_height) { \
    dst_y_end = dest_height; \
  } \
  /* adjust src pointers for negative sizes */ \
  if (xpos < 0) { \
    src += -xpos * 4; \
    src_width -= -xpos; \
    xpos = 0; \
  } \
  if (ypos < dst_y_start) { \
    src += (dst_y_start - ypos) * src_stride; \
    src_height -= dst_y_start - ypos; \
    ypos = dst_y_start; \
  } \
  /* adjust width/height if the src is bigger than dest */ \
  if (xpos + src_width > dest_width) { \
    src_width = dest_width - xpos; \
  } \
  if (ypos + src_height > dst_y_end) { \
    src_height = dst_y_end - ypos; \
  } \
  \
  if (src_height > 0 && src_width > 0) { \
    dest = dest + 4 * xpos + (ypos * dest_stride); \
  \
    LOOP (dest, src, src_height, src_width, src_stride, dest_stride, s_alpha, \
        mode); \
  } \
}

#define OVERLAY_A32_LOOP(name)			\
static inline void \
_overlay_loop_##name (guint8 * dest, const guint8 * src, gint src_height, \
    gint src_width, gint src_stride, gint dest_stride, guint s_alpha, \
    GstCompositorBlendMode mode) \
{ \
  s_alpha = MIN (255, s_alpha); \
  switch (mode) { \
    case COMPOSITOR_BLEND_MODE_SOURCE:\
      if (s_alpha == 255) { \
        guint y; \
        for (y = 0; y < src_height; y++) { \
          memcpy (dest, src, 4 * src_width); \
          dest += dest_stride; \
          src += src_stride; \
        } \
      } else { \
        compositor_orc_source_##name (dest, dest_stride, src, src_stride, \
          s_alpha, src_width, src_height); \
      } \
      break;\
    case COMPOSITOR_BLEND_MODE_OVER:\
      compositor_orc_overlay_##name (dest, dest_stride, src, src_stride, \
        s_alpha, src_width, src_height); \
      break;\
    case COMPOSITOR_BLEND_MODE_ADD:\
      compositor_orc_overlay_##name##_addition (dest, dest_stride, src, src_stride, \
        s_alpha, src_width, src_height); \
      break;\
  }\
}

#define BLEND_A32_LOOP(name) \
static inline void \
_blend_loop_##name (guint8 * dest, const guint8 * src, gint src_height, \
    gint src_width, gint src_stride, gint dest_stride, guint s_alpha, \
    GstCompositorBlendMode mode) \
{ \
  s_alpha = MIN (255, s_alpha); \
  switch (mode) { \
    case COMPOSITOR_BLEND_MODE_SOURCE:\
      if (s_alpha == 255) { \
        guint y; \
        for (y = 0; y < src_height; y++) { \
          memcpy (dest, src, 4 * src_width); \
          dest += dest_stride; \
          src += src_stride; \
        } \
      } else { \
        compositor_orc_source_##name (dest, dest_stride, src, src_stride, \
          s_alpha, src_width, src_height); \
      } \
      break;\
    case COMPOSITOR_BLEND_MODE_OVER:\
    case COMPOSITOR_BLEND_MODE_ADD:\
      /* both modes are the same for opaque background */ \
      compositor_orc_blend_##name (dest, dest_stride, src, src_stride, \
        s_alpha, src_width, src_height); \
      break;\
  }\
}

OVERLAY_A32_LOOP (argb);
OVERLAY_A32_LOOP (bgra);
BLEND_A32_LOOP (argb);
BLEND_A32_LOOP (bgra);

#if G_BYTE_ORDER == G_LITTLE_ENDIAN
BLEND_A32 (argb, blend, _blend_loop_argb);
BLEND_A32 (bgra, blend, _blend_loop_bgra);
BLEND_A32 (argb, overlay, _overlay_loop_argb);
BLEND_A32 (bgra, overlay, _overlay_loop_bgra);
#else
BLEND_A32 (argb, blend, _blend_loop_bgra);
BLEND_A32 (bgra, blend, _blend_loop_argb);
BLEND_A32 (argb, overlay, _overlay_loop_bgra);
BLEND_A32 (bgra, overlay, _overlay_loop_argb);
#endif

#define A32_CHECKER_C(name, RGB, A, C1, C2, C3) \
static void \
fill_checker_##name##_c (GstVideoFrame * frame, guint y_start, guint y_end) \
{ \
  gint i, j; \
  gint val; \
  static const gint tab[] = { 80, 160, 80, 160 }; \
  gint width, stride; \
  guint8 *dest; \
  \
  dest = GST_VIDEO_FRAME_PLANE_DATA (frame, 0); \
  width = GST_VIDEO_FRAME_COMP_WIDTH (frame, 0); \
  stride = GST_VIDEO_FRAME_COMP_STRIDE (frame, 0); \
  \
  dest += y_start * stride; \
  if (!RGB) { \
    for (i = y_start; i < y_end; i++) { \
      for (j = 0; j < width; j++) { \
        dest[A] = 0xff; \
        dest[C1] = tab[((i & 0x8) >> 3) + ((j & 0x8) >> 3)]; \
        dest[C2] = 128; \
        dest[C3] = 128; \
        dest += 4; \
      } \
    } \
  } else { \
    for (i = y_start; i < y_end; i++) { \
      for (j = 0; j < width; j++) { \
        val = tab[((i & 0x8) >> 3) + ((j & 0x8) >> 3)]; \
        dest[A] = 0xFF; \
        dest[C1] = val; \
        dest[C2] = val; \
        dest[C3] = val; \
        dest += 4; \
      } \
    } \
  } \
}

A32_CHECKER_C (argb, TRUE, 0, 1, 2, 3);
A32_CHECKER_C (bgra, TRUE, 3, 2, 1, 0);
A32_CHECKER_C (ayuv, FALSE, 0, 1, 2, 3);
A32_CHECKER_C (vuya, FALSE, 3, 2, 1, 0);

#define YUV_TO_R(Y,U,V) (CLAMP (1.164 * (Y - 16) + 1.596 * (V - 128), 0, 255))
#define YUV_TO_G(Y,U,V) (CLAMP (1.164 * (Y - 16) - 0.813 * (V - 128) - 0.391 * (U - 128), 0, 255))
#define YUV_TO_B(Y,U,V) (CLAMP (1.164 * (Y - 16) + 2.018 * (U - 128), 0, 255))

#define A32_COLOR(name, RGB, A, C1, C2, C3) \
static void \
fill_color_##name (GstVideoFrame * frame, guint y_start, guint y_end, gint Y, gint U, gint V) \
{ \
  gint c1, c2, c3; \
  guint32 val; \
  gint stride; \
  guint8 *dest; \
  \
  dest = GST_VIDEO_FRAME_PLANE_DATA (frame, 0); \
  stride = GST_VIDEO_FRAME_COMP_STRIDE (frame, 0); \
  \
  dest += y_start * stride; \
  if (RGB) { \
    c1 = YUV_TO_R (Y, U, V); \
    c2 = YUV_TO_G (Y, U, V); \
    c3 = YUV_TO_B (Y, U, V); \
  } else { \
    c1 = Y; \
    c2 = U; \
    c3 = V; \
  } \
  val = GUINT32_FROM_BE ((0xff << A) | (c1 << C1) | (c2 << C2) | (c3 << C3)); \
  \
  compositor_orc_splat_u32 ((guint32 *) dest, val, (y_end - y_start) * (stride / 4)); \
}

A32_COLOR (argb, TRUE, 24, 16, 8, 0);
A32_COLOR (bgra, TRUE, 0, 8, 16, 24);
A32_COLOR (abgr, TRUE, 24, 0, 8, 16);
A32_COLOR (rgba, TRUE, 0, 24, 16, 8);
A32_COLOR (ayuv, FALSE, 24, 16, 8, 0);
A32_COLOR (vuya, FALSE, 0, 8, 16, 24);

/* Y444, Y42B, I420, YV12, Y41B */
#define PLANAR_YUV_BLEND(format_name,format_enum,x_round,y_round,MEMCPY,BLENDLOOP) \
inline static void \
_blend_##format_name (const guint8 * src, guint8 * dest, \
    gint src_stride, gint dest_stride, gint src_width, gint src_height, \
    gdouble src_alpha, GstCompositorBlendMode mode) \
{ \
  gint i; \
  gint b_alpha; \
  \
  /* in source mode we just have to copy over things */ \
  if (mode == COMPOSITOR_BLEND_MODE_SOURCE) { \
    src_alpha = 1.0; \
  } \
  \
  /* If it's completely transparent... we just return */ \
  if (G_UNLIKELY (src_alpha == 0.0)) { \
    GST_LOG ("Fast copy (alpha == 0.0)"); \
    return; \
  } \
  \
  /* If it's completely opaque, we do a fast copy */ \
  if (G_UNLIKELY (src_alpha == 1.0)) { \
    GST_LOG ("Fast copy (alpha == 1.0)"); \
    for (i = 0; i < src_height; i++) { \
      MEMCPY (dest, src, src_width); \
      src += src_stride; \
      dest += dest_stride; \
    } \
    return; \
  } \
  \
  b_alpha = CLAMP ((gint) (src_alpha * 255), 0, 255); \
  \
  BLENDLOOP(dest, dest_stride, src, src_stride, b_alpha, src_width, src_height);\
} \
\
static void \
blend_##format_name (GstVideoFrame * srcframe, gint xpos, gint ypos, \
    gdouble src_alpha, GstVideoFrame * destframe, gint dst_y_start, \
    gint dst_y_end, GstCompositorBlendMode mode) \
{ \
  const guint8 *b_src; \
  guint8 *b_dest; \
  gint b_src_width; \
  gint b_src_height; \
  gint xoffset = 0; \
  gint yoffset = 0; \
  gint src_comp_rowstride, dest_comp_rowstride; \
  gint src_comp_height; \
  gint src_comp_width; \
  gint comp_ypos, comp_xpos; \
  gint comp_yoffset, comp_xoffset; \
  gint dest_width, dest_height; \
  const GstVideoFormatInfo *info; \
  gint src_width, src_height; \
  \
  src_width = GST_VIDEO_FRAME_WIDTH (srcframe); \
  src_height = GST_VIDEO_FRAME_HEIGHT (srcframe); \
  \
  info = srcframe->info.finfo; \
  dest_width = GST_VIDEO_FRAME_WIDTH (destframe); \
  dest_height = GST_VIDEO_FRAME_HEIGHT (destframe); \
  \
  if (dst_y_end > dest_height) { \
    dst_y_end = dest_height; \
  } \
  xpos = x_round (xpos); \
  ypos = y_round (ypos); \
  \
  b_src_width = src_width; \
  b_src_height = src_height; \
  \
  /* adjust src pointers for negative sizes */ \
  if (xpos < 0) { \
    xoffset = -xpos; \
    b_src_width -= -xpos; \
    xpos = 0; \
  } \
  if (ypos < dst_y_start) { \
    yoffset = dst_y_start - ypos; \
    b_src_height -= dst_y_start - ypos; \
    ypos = dst_y_start; \
  } \
  /* If x or y offset are larger then the source it's outside of the picture */ \
  if (xoffset >= src_width || yoffset >= src_height) { \
    return; \
  } \
  \
  /* adjust width/height if the src is bigger than dest */ \
  if (xpos + b_src_width > dest_width) { \
    b_src_width = dest_width - xpos; \
  } \
  if (ypos + b_src_height > dst_y_end) { \
    b_src_height = dst_y_end - ypos; \
  } \
  if (b_src_width <= 0 || b_src_height <= 0) { \
    return; \
  } \
  \
  /* First mix Y, then U, then V */ \
  b_src = GST_VIDEO_FRAME_COMP_DATA (srcframe, 0); \
  b_dest = GST_VIDEO_FRAME_COMP_DATA (destframe, 0); \
  src_comp_rowstride = GST_VIDEO_FRAME_COMP_STRIDE (srcframe, 0); \
  dest_comp_rowstride = GST_VIDEO_FRAME_COMP_STRIDE (destframe, 0); \
  src_comp_width = GST_VIDEO_FORMAT_INFO_SCALE_WIDTH(info, 0, b_src_width); \
  src_comp_height = GST_VIDEO_FORMAT_INFO_SCALE_HEIGHT(info, 0, b_src_height); \
  comp_xpos = (xpos == 0) ? 0 : GST_VIDEO_FORMAT_INFO_SCALE_WIDTH (info, 0, xpos); \
  comp_ypos = (ypos == 0) ? 0 : GST_VIDEO_FORMAT_INFO_SCALE_HEIGHT (info, 0, ypos); \
  comp_xoffset = (xoffset == 0) ? 0 : GST_VIDEO_FORMAT_INFO_SCALE_WIDTH (info, 0, xoffset); \
  comp_yoffset = (yoffset == 0) ? 0 : GST_VIDEO_FORMAT_INFO_SCALE_HEIGHT (info, 0, yoffset); \
  _blend_##format_name (b_src + comp_xoffset + comp_yoffset * src_comp_rowstride, \
      b_dest + comp_xpos + comp_ypos * dest_comp_rowstride, \
      src_comp_rowstride, \
      dest_comp_rowstride, src_comp_width, src_comp_height, \
      src_alpha, mode); \
  \
  b_src = GST_VIDEO_FRAME_COMP_DATA (srcframe, 1); \
  b_dest = GST_VIDEO_FRAME_COMP_DATA (destframe, 1); \
  src_comp_rowstride = GST_VIDEO_FRAME_COMP_STRIDE (srcframe, 1); \
  dest_comp_rowstride = GST_VIDEO_FRAME_COMP_STRIDE (destframe, 1); \
  src_comp_width = GST_VIDEO_FORMAT_INFO_SCALE_WIDTH(info, 1, b_src_width); \
  src_comp_height = GST_VIDEO_FORMAT_INFO_SCALE_HEIGHT(info, 1, b_src_height); \
  comp_xpos = (xpos == 0) ? 0 : GST_VIDEO_FORMAT_INFO_SCALE_WIDTH (info, 1, xpos); \
  comp_ypos = (ypos == 0) ? 0 : ypos >> info->h_sub[1]; \
  comp_xoffset = (xoffset == 0) ? 0 : GST_VIDEO_FORMAT_INFO_SCALE_WIDTH (info, 1, xoffset); \
  comp_yoffset = (yoffset == 0) ? 0 : yoffset >> info->h_sub[1]; \
  _blend_##format_name (b_src + comp_xoffset + comp_yoffset * src_comp_rowstride, \
      b_dest + comp_xpos + comp_ypos * dest_comp_rowstride, \
      src_comp_rowstride, \
      dest_comp_rowstride, src_comp_width, src_comp_height, \
      src_alpha, mode); \
  \
  b_src = GST_VIDEO_FRAME_COMP_DATA (srcframe, 2); \
  b_dest = GST_VIDEO_FRAME_COMP_DATA (destframe, 2); \
  src_comp_rowstride = GST_VIDEO_FRAME_COMP_STRIDE (srcframe, 2); \
  dest_comp_rowstride = GST_VIDEO_FRAME_COMP_STRIDE (destframe, 2); \
  src_comp_width = GST_VIDEO_FORMAT_INFO_SCALE_WIDTH(info, 2, b_src_width); \
  src_comp_height = GST_VIDEO_FORMAT_INFO_SCALE_HEIGHT(info, 2, b_src_height); \
  comp_xpos = (xpos == 0) ? 0 : GST_VIDEO_FORMAT_INFO_SCALE_WIDTH (info, 2, xpos); \
  comp_ypos = (ypos == 0) ? 0 : ypos >> info->h_sub[2]; \
  comp_xoffset = (xoffset == 0) ? 0 : GST_VIDEO_FORMAT_INFO_SCALE_WIDTH (info, 2, xoffset); \
  comp_yoffset = (yoffset == 0) ? 0 : yoffset >> info->h_sub[2]; \
  _blend_##format_name (b_src + comp_xoffset + comp_yoffset * src_comp_rowstride, \
      b_dest + comp_xpos + comp_ypos * dest_comp_rowstride, \
      src_comp_rowstride, \
      dest_comp_rowstride, src_comp_width, src_comp_height, \
      src_alpha, mode); \
}

#define PLANAR_YUV_FILL_CHECKER(format_name, format_enum, MEMSET) \
static void \
fill_checker_##format_name (GstVideoFrame * frame, guint y_start, guint y_end) \
{ \
  gint i, j; \
  static const int tab[] = { 80, 160, 80, 160 }; \
  guint8 *p; \
  gint comp_width, comp_height; \
  gint rowstride, comp_yoffset; \
  const GstVideoFormatInfo *info; \
  \
  info = frame->info.finfo; \
  p = GST_VIDEO_FRAME_COMP_DATA (frame, 0); \
  comp_width = GST_VIDEO_FRAME_COMP_WIDTH (frame, 0); \
  comp_height = GST_VIDEO_FORMAT_INFO_SCALE_HEIGHT(info, 0, y_end - y_start); \
  rowstride = GST_VIDEO_FRAME_COMP_STRIDE (frame, 0); \
  comp_yoffset = (y_start == 0) ? 0 : GST_VIDEO_FORMAT_INFO_SCALE_HEIGHT (info, 0, y_start); \
  p += comp_yoffset * rowstride; \
  \
  for (i = 0; i < comp_height; i++) { \
    for (j = 0; j < comp_width; j++) { \
      *p++ = tab[(((i + y_start) & 0x8) >> 3) + ((j & 0x8) >> 3)]; \
    } \
    p += rowstride - comp_width; \
  } \
  \
  p = GST_VIDEO_FRAME_COMP_DATA (frame, 1); \
  comp_width = GST_VIDEO_FRAME_COMP_WIDTH (frame, 1); \
  comp_height = GST_VIDEO_FORMAT_INFO_SCALE_HEIGHT(info, 1, y_end - y_start); \
  rowstride = GST_VIDEO_FRAME_COMP_STRIDE (frame, 1); \
  comp_yoffset = (y_start == 0) ? 0 : y_start >> info->h_sub[1]; \
  p += comp_yoffset * rowstride; \
  \
  for (i = 0; i < comp_height; i++) { \
    MEMSET (p, 0x80, comp_width); \
    p += rowstride; \
  } \
  \
  p = GST_VIDEO_FRAME_COMP_DATA (frame, 2); \
  comp_width = GST_VIDEO_FRAME_COMP_WIDTH (frame, 2); \
  comp_height = GST_VIDEO_FORMAT_INFO_SCALE_HEIGHT(info, 2, y_end - y_start); \
  rowstride = GST_VIDEO_FRAME_COMP_STRIDE (frame, 2); \
  comp_yoffset = (y_start == 0) ? 0 : y_start >> info->h_sub[2]; \
  p += comp_yoffset * rowstride; \
  \
  for (i = 0; i < comp_height; i++) { \
    MEMSET (p, 0x80, comp_width); \
    p += rowstride; \
  } \
}

#define PLANAR_YUV_FILL_COLOR(format_name,format_enum,MEMSET) \
static void \
fill_color_##format_name (GstVideoFrame * frame, \
    guint y_start, guint y_end, gint colY, gint colU, gint colV) \
{ \
  guint8 *p; \
  gint comp_width, comp_height; \
  gint rowstride, comp_yoffset; \
  gint i; \
  const GstVideoFormatInfo *info; \
  \
  info = frame->info.finfo; \
  p = GST_VIDEO_FRAME_COMP_DATA (frame, 0); \
  comp_width = GST_VIDEO_FRAME_COMP_WIDTH (frame, 0); \
  comp_height = GST_VIDEO_FORMAT_INFO_SCALE_HEIGHT(info, 0, y_end - y_start); \
  rowstride = GST_VIDEO_FRAME_COMP_STRIDE (frame, 0); \
  comp_yoffset = (y_start == 0) ? 0 : GST_VIDEO_FORMAT_INFO_SCALE_HEIGHT (info, 0, y_start); \
  p += comp_yoffset * rowstride; \
  \
  for (i = 0; i < comp_height; i++) { \
    MEMSET (p, colY, comp_width); \
    p += rowstride; \
  } \
  \
  p = GST_VIDEO_FRAME_COMP_DATA (frame, 1); \
  comp_width = GST_VIDEO_FRAME_COMP_WIDTH (frame, 1); \
  comp_height = GST_VIDEO_FORMAT_INFO_SCALE_HEIGHT(info, 1, y_end - y_start); \
  rowstride = GST_VIDEO_FRAME_COMP_STRIDE (frame, 1); \
  comp_yoffset = (y_start == 0) ? 0 : y_start >> info->h_sub[1]; \
  p += comp_yoffset * rowstride; \
  \
  for (i = 0; i < comp_height; i++) { \
    MEMSET (p, colU, comp_width); \
    p += rowstride; \
  } \
  \
  p = GST_VIDEO_FRAME_COMP_DATA (frame, 2); \
  comp_width = GST_VIDEO_FRAME_COMP_WIDTH (frame, 2); \
  comp_height = GST_VIDEO_FORMAT_INFO_SCALE_HEIGHT(info, 2, y_end - y_start); \
  rowstride = GST_VIDEO_FRAME_COMP_STRIDE (frame, 2); \
  comp_yoffset = (y_start == 0) ? 0 : y_start >> info->h_sub[2]; \
  p += comp_yoffset * rowstride; \
  \
  for (i = 0; i < comp_height; i++) { \
    MEMSET (p, colV, comp_width); \
    p += rowstride; \
  } \
}

#define GST_ROUND_UP_1(x) (x)

PLANAR_YUV_BLEND (i420, GST_VIDEO_FORMAT_I420, GST_ROUND_UP_2,
    GST_ROUND_UP_2, memcpy, compositor_orc_blend_u8);
PLANAR_YUV_FILL_CHECKER (i420, GST_VIDEO_FORMAT_I420, memset);
PLANAR_YUV_FILL_COLOR (i420, GST_VIDEO_FORMAT_I420, memset);
PLANAR_YUV_FILL_COLOR (yv12, GST_VIDEO_FORMAT_YV12, memset);
PLANAR_YUV_BLEND (y444, GST_VIDEO_FORMAT_Y444, GST_ROUND_UP_1,
    GST_ROUND_UP_1, memcpy, compositor_orc_blend_u8);
PLANAR_YUV_FILL_CHECKER (y444, GST_VIDEO_FORMAT_Y444, memset);
PLANAR_YUV_FILL_COLOR (y444, GST_VIDEO_FORMAT_Y444, memset);
PLANAR_YUV_BLEND (y42b, GST_VIDEO_FORMAT_Y42B, GST_ROUND_UP_2,
    GST_ROUND_UP_1, memcpy, compositor_orc_blend_u8);
PLANAR_YUV_FILL_CHECKER (y42b, GST_VIDEO_FORMAT_Y42B, memset);
PLANAR_YUV_FILL_COLOR (y42b, GST_VIDEO_FORMAT_Y42B, memset);
PLANAR_YUV_BLEND (y41b, GST_VIDEO_FORMAT_Y41B, GST_ROUND_UP_4,
    GST_ROUND_UP_1, memcpy, compositor_orc_blend_u8);
PLANAR_YUV_FILL_CHECKER (y41b, GST_VIDEO_FORMAT_Y41B, memset);
PLANAR_YUV_FILL_COLOR (y41b, GST_VIDEO_FORMAT_Y41B, memset);

/* NV12, NV21 */
#define NV_YUV_BLEND(format_name,MEMCPY,BLENDLOOP) \
inline static void \
_blend_##format_name (const guint8 * src, guint8 * dest, \
    gint src_stride, gint dest_stride, gint src_width, gint src_height, \
    gdouble src_alpha, GstCompositorBlendMode mode) \
{ \
  gint i; \
  gint b_alpha; \
  \
  /* in source mode we just have to copy over things */ \
  if (mode == COMPOSITOR_BLEND_MODE_SOURCE) { \
    src_alpha = 1.0; \
  } \
  \
  /* If it's completely transparent... we just return */ \
  if (G_UNLIKELY (src_alpha == 0.0)) { \
    GST_LOG ("Fast copy (alpha == 0.0)"); \
    return; \
  } \
  \
  /* If it's completely opaque, we do a fast copy */ \
  if (G_UNLIKELY (src_alpha == 1.0)) { \
    GST_LOG ("Fast copy (alpha == 1.0)"); \
    for (i = 0; i < src_height; i++) { \
      MEMCPY (dest, src, src_width); \
      src += src_stride; \
      dest += dest_stride; \
    } \
    return; \
  } \
  \
  b_alpha = CLAMP ((gint) (src_alpha * 255), 0, 255); \
  \
  BLENDLOOP(dest, dest_stride, src, src_stride, b_alpha, src_width, src_height); \
} \
\
static void \
blend_##format_name (GstVideoFrame * srcframe, gint xpos, gint ypos, \
    gdouble src_alpha, GstVideoFrame * destframe, gint dst_y_start, \
    gint dst_y_end, GstCompositorBlendMode mode)                    \
{ \
  const guint8 *b_src; \
  guint8 *b_dest; \
  gint b_src_width; \
  gint b_src_height; \
  gint xoffset = 0; \
  gint yoffset = 0; \
  gint src_comp_rowstride, dest_comp_rowstride; \
  gint src_comp_height; \
  gint src_comp_width; \
  gint comp_ypos, comp_xpos; \
  gint comp_yoffset, comp_xoffset; \
  gint dest_width, dest_height; \
  const GstVideoFormatInfo *info; \
  gint src_width, src_height; \
  \
  src_width = GST_VIDEO_FRAME_WIDTH (srcframe); \
  src_height = GST_VIDEO_FRAME_HEIGHT (srcframe); \
  \
  info = srcframe->info.finfo; \
  dest_width = GST_VIDEO_FRAME_WIDTH (destframe); \
  dest_height = GST_VIDEO_FRAME_HEIGHT (destframe); \
  \
  if (dst_y_end > dest_height) { \
    dst_y_end = dest_height; \
  } \
  xpos = GST_ROUND_UP_2 (xpos); \
  ypos = GST_ROUND_UP_2 (ypos); \
  \
  b_src_width = src_width; \
  b_src_height = src_height; \
  \
  /* adjust src pointers for negative sizes */ \
  if (xpos < 0) { \
    xoffset = -xpos; \
    b_src_width -= -xpos; \
    xpos = 0; \
  } \
  if (ypos < dst_y_start) { \
    yoffset += dst_y_start - ypos; \
    b_src_height -= dst_y_start - ypos; \
    ypos = dst_y_start; \
  } \
  /* If x or y offset are larger then the source it's outside of the picture */ \
  if (xoffset > src_width || yoffset > src_height) { \
    return; \
  } \
  \
  /* adjust width/height if the src is bigger than dest */ \
  if (xpos + b_src_width > dest_width) { \
    b_src_width = dest_width - xpos; \
  } \
  if (ypos + b_src_height > dst_y_end) { \
    b_src_height = dst_y_end - ypos; \
  } \
  if (b_src_width < 0 || b_src_height < 0) { \
    return; \
  } \
  \
  /* First mix Y, then UV */ \
  b_src = GST_VIDEO_FRAME_COMP_DATA (srcframe, 0); \
  b_dest = GST_VIDEO_FRAME_COMP_DATA (destframe, 0); \
  src_comp_rowstride = GST_VIDEO_FRAME_COMP_STRIDE (srcframe, 0); \
  dest_comp_rowstride = GST_VIDEO_FRAME_COMP_STRIDE (destframe, 0); \
  src_comp_width = GST_VIDEO_FORMAT_INFO_SCALE_WIDTH(info, 0, b_src_width); \
  src_comp_height = GST_VIDEO_FORMAT_INFO_SCALE_HEIGHT(info, 0, b_src_height); \
  comp_xpos = (xpos == 0) ? 0 : GST_VIDEO_FORMAT_INFO_SCALE_WIDTH (info, 0, xpos); \
  comp_ypos = (ypos == 0) ? 0 : GST_VIDEO_FORMAT_INFO_SCALE_HEIGHT (info, 0, ypos); \
  comp_xoffset = (xoffset == 0) ? 0 : GST_VIDEO_FORMAT_INFO_SCALE_WIDTH (info, 0, xoffset); \
  comp_yoffset = (yoffset == 0) ? 0 : GST_VIDEO_FORMAT_INFO_SCALE_HEIGHT (info, 0, yoffset); \
  _blend_##format_name (b_src + comp_xoffset + comp_yoffset * src_comp_rowstride, \
      b_dest + comp_xpos + comp_ypos * dest_comp_rowstride, \
      src_comp_rowstride, \
      dest_comp_rowstride, src_comp_width, src_comp_height, \
      src_alpha, mode); \
  \
  b_src = GST_VIDEO_FRAME_PLANE_DATA (srcframe, 1); \
  b_dest = GST_VIDEO_FRAME_PLANE_DATA (destframe, 1); \
  src_comp_rowstride = GST_VIDEO_FRAME_COMP_STRIDE (srcframe, 1); \
  dest_comp_rowstride = GST_VIDEO_FRAME_COMP_STRIDE (destframe, 1); \
  src_comp_width = GST_VIDEO_FORMAT_INFO_SCALE_WIDTH(info, 1, b_src_width); \
  src_comp_height = GST_VIDEO_FORMAT_INFO_SCALE_HEIGHT(info, 1, b_src_height); \
  comp_xpos = (xpos == 0) ? 0 : GST_VIDEO_FORMAT_INFO_SCALE_WIDTH (info, 1, xpos); \
  comp_ypos = (ypos == 0) ? 0 : ypos >> info->h_sub[1]; \
  comp_xoffset = (xoffset == 0) ? 0 : GST_VIDEO_FORMAT_INFO_SCALE_WIDTH (info, 1, xoffset); \
  comp_yoffset = (yoffset == 0) ? 0 : yoffset >> info->h_sub[1]; \
  _blend_##format_name (b_src + comp_xoffset * 2 + comp_yoffset * src_comp_rowstride, \
      b_dest + comp_xpos * 2 + comp_ypos * dest_comp_rowstride, \
      src_comp_rowstride, \
      dest_comp_rowstride, 2 * src_comp_width, src_comp_height, \
      src_alpha, mode); \
}

#define NV_YUV_FILL_CHECKER(format_name, MEMSET)        \
static void \
fill_checker_##format_name (GstVideoFrame * frame, guint y_start, guint y_end) \
{ \
  gint i, j; \
  static const int tab[] = { 80, 160, 80, 160 }; \
  guint8 *p; \
  gint comp_width, comp_height; \
  gint rowstride, comp_yoffset; \
  const GstVideoFormatInfo *info; \
  \
  info = frame->info.finfo; \
  p = GST_VIDEO_FRAME_COMP_DATA (frame, 0); \
  comp_width = GST_VIDEO_FRAME_COMP_WIDTH (frame, 0); \
  comp_height = GST_VIDEO_FORMAT_INFO_SCALE_HEIGHT(info, 0, y_end - y_start); \
  rowstride = GST_VIDEO_FRAME_COMP_STRIDE (frame, 0); \
  comp_yoffset = (y_start == 0) ? 0 : GST_VIDEO_FORMAT_INFO_SCALE_HEIGHT (info, 0, y_start); \
  p += comp_yoffset * rowstride; \
  \
  for (i = 0; i < comp_height; i++) { \
    for (j = 0; j < comp_width; j++) { \
      *p++ = tab[(((i + y_start) & 0x8) >> 3) + ((j & 0x8) >> 3)]; \
    } \
    p += rowstride - comp_width; \
  } \
  \
  p = GST_VIDEO_FRAME_PLANE_DATA (frame, 1); \
  comp_width = GST_VIDEO_FRAME_COMP_WIDTH (frame, 1); \
  comp_height = GST_VIDEO_FORMAT_INFO_SCALE_HEIGHT(info, 1, y_end - y_start); \
  rowstride = GST_VIDEO_FRAME_COMP_STRIDE (frame, 1); \
  comp_yoffset = (y_start == 0) ? 0 : y_start >> info->h_sub[1]; \
  p += comp_yoffset * rowstride; \
  \
  for (i = 0; i < comp_height; i++) { \
    MEMSET (p, 0x80, comp_width * 2); \
    p += rowstride; \
  } \
}

#define NV_YUV_FILL_COLOR(format_name,MEMSET) \
static void \
fill_color_##format_name (GstVideoFrame * frame, \
    guint y_start, guint y_end, gint colY, gint colU, gint colV) \
{ \
  guint8 *y, *u, *v; \
  gint comp_width, comp_height; \
  gint rowstride, comp_yoffset; \
  gint i, j; \
  const GstVideoFormatInfo *info; \
  \
  info = frame->info.finfo; \
  y = GST_VIDEO_FRAME_COMP_DATA (frame, 0); \
  comp_width = GST_VIDEO_FRAME_COMP_WIDTH (frame, 0); \
  comp_height = GST_VIDEO_FORMAT_INFO_SCALE_HEIGHT(info, 0, y_end - y_start); \
  rowstride = GST_VIDEO_FRAME_COMP_STRIDE (frame, 0); \
  comp_yoffset = (y_start == 0) ? 0 : GST_VIDEO_FORMAT_INFO_SCALE_HEIGHT (info, 0, y_start); \
  \
  y += comp_yoffset * rowstride; \
  for (i = 0; i < comp_height; i++) { \
    MEMSET (y, colY, comp_width); \
    y += rowstride; \
  } \
  \
  u = GST_VIDEO_FRAME_COMP_DATA (frame, 1); \
  v = GST_VIDEO_FRAME_COMP_DATA (frame, 2); \
  comp_width = GST_VIDEO_FRAME_COMP_WIDTH (frame, 1); \
  comp_height = GST_VIDEO_FORMAT_INFO_SCALE_HEIGHT(info, 1, y_end - y_start); \
  rowstride = GST_VIDEO_FRAME_COMP_STRIDE (frame, 1); \
  comp_yoffset = (y_start == 0) ? 0 : y_start >> info->h_sub[1]; \
  \
  u += comp_yoffset * rowstride; \
  v += comp_yoffset * rowstride; \
  for (i = 0; i < comp_height; i++) { \
    for (j = 0; j < comp_width; j++) { \
      u[j*2] = colU; \
      v[j*2] = colV; \
    } \
    u += rowstride; \
    v += rowstride; \
  } \
}

NV_YUV_BLEND (nv12, memcpy, compositor_orc_blend_u8);
NV_YUV_FILL_CHECKER (nv12, memset);
NV_YUV_FILL_COLOR (nv12, memset);
NV_YUV_BLEND (nv21, memcpy, compositor_orc_blend_u8);
NV_YUV_FILL_CHECKER (nv21, memset);

/* RGB, BGR, xRGB, xBGR, RGBx, BGRx */

#define RGB_BLEND(name, bpp, MEMCPY, BLENDLOOP) \
static void \
blend_##name (GstVideoFrame * srcframe, gint xpos, gint ypos, \
    gdouble src_alpha, GstVideoFrame * destframe, gint dst_y_start, \
    gint dst_y_end, GstCompositorBlendMode mode) \
{ \
  gint b_alpha; \
  gint i; \
  gint src_stride, dest_stride; \
  gint dest_width, dest_height; \
  guint8 *dest, *src; \
  gint src_width, src_height; \
  \
  src_width = GST_VIDEO_FRAME_WIDTH (srcframe); \
  src_height = GST_VIDEO_FRAME_HEIGHT (srcframe); \
  \
  src = GST_VIDEO_FRAME_PLANE_DATA (srcframe, 0); \
  dest = GST_VIDEO_FRAME_PLANE_DATA (destframe, 0); \
  \
  dest_width = GST_VIDEO_FRAME_WIDTH (destframe); \
  dest_height = GST_VIDEO_FRAME_HEIGHT (destframe); \
  \
  src_stride = GST_VIDEO_FRAME_COMP_STRIDE (srcframe, 0); \
  dest_stride = GST_VIDEO_FRAME_COMP_STRIDE (destframe, 0); \
  \
  b_alpha = CLAMP ((gint) (src_alpha * 255), 0, 255); \
  \
  if (dst_y_end > dest_height) { \
    dst_y_end = dest_height; \
  } \
  /* adjust src pointers for negative sizes */ \
  if (xpos < 0) { \
    src += -xpos * bpp; \
    src_width -= -xpos; \
    xpos = 0; \
  } \
  if (ypos < dst_y_start) { \
    src += (dst_y_start - ypos) * src_stride; \
    src_height -= dst_y_start - ypos; \
    ypos = dst_y_start; \
  } \
  /* adjust width/height if the src is bigger than dest */ \
  if (xpos + src_width > dest_width) { \
    src_width = dest_width - xpos; \
  } \
  if (ypos + src_height > dst_y_end) { \
    src_height = dst_y_end - ypos; \
  } \
  \
  dest = dest + bpp * xpos + (ypos * dest_stride); \
  \
  /* in source mode we just have to copy over things */ \
  if (mode == COMPOSITOR_BLEND_MODE_SOURCE) { \
    src_alpha = 1.0; \
  } \
  \
  /* If it's completely transparent... we just return */ \
  if (G_UNLIKELY (src_alpha == 0.0)) { \
    GST_LOG ("Fast copy (alpha == 0.0)"); \
    return; \
  } \
  \
  /* If it's completely opaque, we do a fast copy */ \
  if (G_UNLIKELY (src_alpha == 1.0)) { \
    GST_LOG ("Fast copy (alpha == 1.0)"); \
    for (i = 0; i < src_height; i++) { \
      MEMCPY (dest, src, bpp * src_width); \
      src += src_stride; \
      dest += dest_stride; \
    } \
    return; \
  } \
  \
  BLENDLOOP(dest, dest_stride, src, src_stride, b_alpha, src_width * bpp, src_height); \
}

#define RGB_FILL_CHECKER_C(name, bpp, r, g, b) \
static void \
fill_checker_##name##_c (GstVideoFrame * frame, guint y_start, guint y_end) \
{ \
  gint i, j; \
  static const int tab[] = { 80, 160, 80, 160 }; \
  gint stride, dest_add, width, height; \
  guint8 *dest; \
  \
  width = GST_VIDEO_FRAME_WIDTH (frame); \
  height = y_end - y_start; \
  dest = GST_VIDEO_FRAME_PLANE_DATA (frame, 0); \
  stride = GST_VIDEO_FRAME_COMP_STRIDE (frame, 0); \
  dest_add = stride - width * bpp; \
  \
  dest += y_start * stride; \
  for (i = 0; i < height; i++) { \
    for (j = 0; j < width; j++) { \
      dest[r] = tab[(((i + y_start) & 0x8) >> 3) + ((j & 0x8) >> 3)];       /* red */ \
      dest[g] = tab[(((i + y_start) & 0x8) >> 3) + ((j & 0x8) >> 3)];       /* green */ \
      dest[b] = tab[(((i + y_start) & 0x8) >> 3) + ((j & 0x8) >> 3)];       /* blue */ \
      dest += bpp; \
    } \
    dest += dest_add; \
  } \
}

#define RGB_FILL_COLOR(name, bpp, MEMSET_RGB) \
static void \
fill_color_##name (GstVideoFrame * frame, \
    guint y_start, guint y_end, gint colY, gint colU, gint colV) \
{ \
  gint red, green, blue; \
  gint i; \
  gint dest_stride; \
  gint width, height; \
  guint8 *dest; \
  \
  width = GST_VIDEO_FRAME_WIDTH (frame); \
  height = y_end - y_start; \
  dest = GST_VIDEO_FRAME_PLANE_DATA (frame, 0); \
  dest_stride = GST_VIDEO_FRAME_COMP_STRIDE (frame, 0); \
  \
  red = YUV_TO_R (colY, colU, colV); \
  green = YUV_TO_G (colY, colU, colV); \
  blue = YUV_TO_B (colY, colU, colV); \
  \
  dest += y_start * dest_stride; \
  for (i = 0; i < height; i++) { \
    MEMSET_RGB (dest, red, green, blue, width); \
    dest += dest_stride; \
  } \
}

#define MEMSET_RGB_C(name, r, g, b) \
static inline void \
_memset_##name##_c (guint8* dest, gint red, gint green, gint blue, gint width) { \
  gint j; \
  \
  for (j = 0; j < width; j++) { \
    dest[r] = red; \
    dest[g] = green; \
    dest[b] = blue; \
    dest += 3; \
  } \
}

#define MEMSET_XRGB(name, r, g, b) \
static inline void \
_memset_##name (guint8* dest, gint red, gint green, gint blue, gint width) { \
  guint32 val; \
  \
  val = GUINT32_FROM_BE ((red << r) | (green << g) | (blue << b)); \
  compositor_orc_splat_u32 ((guint32 *) dest, val, width); \
}

#define _orc_memcpy_u32(dest,src,len) compositor_orc_memcpy_u32((guint32 *) dest, (const guint32 *) src, len/4)

RGB_BLEND (rgb, 3, memcpy, compositor_orc_blend_u8);
RGB_FILL_CHECKER_C (rgb, 3, 0, 1, 2);
MEMSET_RGB_C (rgb, 0, 1, 2);
RGB_FILL_COLOR (rgb_c, 3, _memset_rgb_c);

MEMSET_RGB_C (bgr, 2, 1, 0);
RGB_FILL_COLOR (bgr_c, 3, _memset_bgr_c);

RGB_BLEND (xrgb, 4, _orc_memcpy_u32, compositor_orc_blend_u8);
RGB_FILL_CHECKER_C (xrgb, 4, 1, 2, 3);
MEMSET_XRGB (xrgb, 24, 16, 0);
RGB_FILL_COLOR (xrgb, 4, _memset_xrgb);

MEMSET_XRGB (xbgr, 0, 16, 24);
RGB_FILL_COLOR (xbgr, 4, _memset_xbgr);

RGB_FILL_CHECKER_C (rgbx, 4, 0, 1, 2);
MEMSET_XRGB (rgbx, 24, 16, 8);
RGB_FILL_COLOR (rgbx, 4, _memset_rgbx);

MEMSET_XRGB (bgrx, 8, 16, 24);
RGB_FILL_COLOR (bgrx, 4, _memset_bgrx);

/* YUY2, YVYU, UYVY */

#define PACKED_422_BLEND(name, MEMCPY, BLENDLOOP) \
static void \
blend_##name (GstVideoFrame * srcframe, gint xpos, gint ypos, \
    gdouble src_alpha, GstVideoFrame * destframe, gint dst_y_start, \
    gint dst_y_end, GstCompositorBlendMode mode) \
{ \
  gint b_alpha; \
  gint i; \
  gint src_stride, dest_stride; \
  gint dest_width, dest_height; \
  guint8 *src, *dest; \
  gint src_width, src_height; \
  \
  src_width = GST_VIDEO_FRAME_WIDTH (srcframe); \
  src_height = GST_VIDEO_FRAME_HEIGHT (srcframe); \
  \
  dest_width = GST_VIDEO_FRAME_WIDTH (destframe); \
  dest_height = GST_VIDEO_FRAME_HEIGHT (destframe); \
  \
  src = GST_VIDEO_FRAME_PLANE_DATA (srcframe, 0); \
  dest = GST_VIDEO_FRAME_PLANE_DATA (destframe, 0); \
  \
  src_stride = GST_VIDEO_FRAME_COMP_STRIDE (srcframe, 0); \
  dest_stride = GST_VIDEO_FRAME_COMP_STRIDE (destframe, 0); \
  \
  b_alpha = CLAMP ((gint) (src_alpha * 255), 0, 255); \
  \
  xpos = GST_ROUND_UP_2 (xpos); \
  \
  if (dst_y_end > dest_height) { \
    dst_y_end = dest_height; \
  } \
  /* adjust src pointers for negative sizes */ \
  if (xpos < 0) { \
    src += -xpos * 2; \
    src_width -= -xpos; \
    xpos = 0; \
  } \
  if (ypos < dst_y_start) { \
    src += (dst_y_start - ypos) * src_stride; \
    src_height -= dst_y_start - ypos; \
    ypos = dst_y_start; \
  } \
  \
  /* adjust width/height if the src is bigger than dest */ \
  if (xpos + src_width > dest_width) { \
    src_width = dest_width - xpos; \
  } \
  if (ypos + src_height > dst_y_end) { \
    src_height = dst_y_end - ypos; \
  } \
  \
  dest = dest + 2 * xpos + (ypos * dest_stride); \
  \
  /* in source mode we just have to copy over things */ \
  if (mode == COMPOSITOR_BLEND_MODE_SOURCE) { \
    src_alpha = 1.0; \
  } \
  \
  /* If it's completely transparent... we just return */ \
  if (G_UNLIKELY (src_alpha == 0.0)) { \
    GST_LOG ("Fast copy (alpha == 0.0)"); \
    return; \
  } \
  \
  /* If it's completely opaque, we do a fast copy */ \
  if (G_UNLIKELY (src_alpha == 1.0)) { \
    GST_LOG ("Fast copy (alpha == 1.0)"); \
    for (i = 0; i < src_height; i++) { \
      MEMCPY (dest, src, 2 * src_width); \
      src += src_stride; \
      dest += dest_stride; \
    } \
    return; \
  } \
  \
  BLENDLOOP(dest, dest_stride, src, src_stride, b_alpha, 2 * src_width, src_height); \
}

#define PACKED_422_FILL_CHECKER_C(name, Y1, U, Y2, V) \
static void \
fill_checker_##name##_c (GstVideoFrame * frame, guint y_start, guint y_end) \
{ \
  gint i, j; \
  static const int tab[] = { 80, 160, 80, 160 }; \
  gint dest_add; \
  gint width, height; \
  guint8 *dest; \
  \
  width = GST_VIDEO_FRAME_WIDTH (frame); \
  width = GST_ROUND_UP_2 (width); \
  height = y_end - y_start; \
  dest = GST_VIDEO_FRAME_PLANE_DATA (frame, 0); \
  dest_add = GST_VIDEO_FRAME_COMP_STRIDE (frame, 0) - width * 2; \
  width /= 2; \
  \
  dest += GST_VIDEO_FRAME_COMP_STRIDE (frame, 0) * y_start; \
  for (i = 0; i < height; i++) { \
    for (j = 0; j < width; j++) { \
      dest[Y1] = tab[(((i + y_start) & 0x8) >> 3) + (((2 * j + 0) & 0x8) >> 3)]; \
      dest[Y2] = tab[(((i + y_start) & 0x8) >> 3) + (((2 * j + 1) & 0x8) >> 3)]; \
      dest[U] = 128; \
      dest[V] = 128; \
      dest += 4; \
    } \
    dest += dest_add; \
  } \
}

#define PACKED_422_FILL_COLOR(name, Y1, U, Y2, V) \
static void \
fill_color_##name (GstVideoFrame * frame, \
    guint y_start, guint y_end, gint colY, gint colU, gint colV) \
{ \
  gint i; \
  gint dest_stride; \
  guint32 val; \
  gint width, height; \
  guint8 *dest; \
  \
  width = GST_VIDEO_FRAME_WIDTH (frame); \
  width = GST_ROUND_UP_2 (width); \
  height = y_end - y_start; \
  dest = GST_VIDEO_FRAME_PLANE_DATA (frame, 0); \
  dest_stride = GST_VIDEO_FRAME_COMP_STRIDE (frame, 0); \
  width /= 2; \
  \
  val = GUINT32_FROM_BE ((colY << Y1) | (colY << Y2) | (colU << U) | (colV << V)); \
  \
  dest += dest_stride * y_start; \
  for (i = 0; i < height; i++) { \
    compositor_orc_splat_u32 ((guint32 *) dest, val, width); \
    dest += dest_stride; \
  } \
}

PACKED_422_BLEND (yuy2, memcpy, compositor_orc_blend_u8);
PACKED_422_FILL_CHECKER_C (yuy2, 0, 1, 2, 3);
PACKED_422_FILL_CHECKER_C (uyvy, 1, 0, 3, 2);
PACKED_422_FILL_COLOR (yuy2, 24, 16, 8, 0);
PACKED_422_FILL_COLOR (yvyu, 24, 0, 8, 16);
PACKED_422_FILL_COLOR (uyvy, 16, 24, 0, 8);

/* Init function */
BlendFunction gst_compositor_blend_argb;
BlendFunction gst_compositor_blend_bgra;
BlendFunction gst_compositor_overlay_argb;
BlendFunction gst_compositor_overlay_bgra;
/* AYUV/ABGR is equal to ARGB, RGBA is equal to BGRA */
BlendFunction gst_compositor_blend_y444;
BlendFunction gst_compositor_blend_y42b;
BlendFunction gst_compositor_blend_i420;
/* I420 is equal to YV12 */
BlendFunction gst_compositor_blend_nv12;
BlendFunction gst_compositor_blend_nv21;
BlendFunction gst_compositor_blend_y41b;
BlendFunction gst_compositor_blend_rgb;
/* BGR is equal to RGB */
BlendFunction gst_compositor_blend_rgbx;
/* BGRx, xRGB, xBGR are equal to RGBx */
BlendFunction gst_compositor_blend_yuy2;
/* YVYU and UYVY are equal to YUY2 */

FillCheckerFunction gst_compositor_fill_checker_argb;
FillCheckerFunction gst_compositor_fill_checker_bgra;
/* ABGR is equal to ARGB, RGBA is equal to BGRA */
FillCheckerFunction gst_compositor_fill_checker_ayuv;
FillCheckerFunction gst_compositor_fill_checker_vuya;
FillCheckerFunction gst_compositor_fill_checker_y444;
FillCheckerFunction gst_compositor_fill_checker_y42b;
FillCheckerFunction gst_compositor_fill_checker_i420;
/* I420 is equal to YV12 */
FillCheckerFunction gst_compositor_fill_checker_nv12;
FillCheckerFunction gst_compositor_fill_checker_nv21;
FillCheckerFunction gst_compositor_fill_checker_y41b;
FillCheckerFunction gst_compositor_fill_checker_rgb;
/* BGR is equal to RGB */
FillCheckerFunction gst_compositor_fill_checker_xrgb;
FillCheckerFunction gst_compositor_fill_checker_rgbx;
/* BGRx, xRGB, xBGR are equal to RGBx */
FillCheckerFunction gst_compositor_fill_checker_yuy2;
/* YVYU is equal to YUY2 */
FillCheckerFunction gst_compositor_fill_checker_uyvy;

FillColorFunction gst_compositor_fill_color_argb;
FillColorFunction gst_compositor_fill_color_bgra;
FillColorFunction gst_compositor_fill_color_abgr;
FillColorFunction gst_compositor_fill_color_rgba;
FillColorFunction gst_compositor_fill_color_ayuv;
FillColorFunction gst_compositor_fill_color_vuya;
FillColorFunction gst_compositor_fill_color_y444;
FillColorFunction gst_compositor_fill_color_y42b;
FillColorFunction gst_compositor_fill_color_i420;
FillColorFunction gst_compositor_fill_color_yv12;
FillColorFunction gst_compositor_fill_color_nv12;
/* NV21 is equal to NV12 */
FillColorFunction gst_compositor_fill_color_y41b;
FillColorFunction gst_compositor_fill_color_rgb;
FillColorFunction gst_compositor_fill_color_bgr;
FillColorFunction gst_compositor_fill_color_xrgb;
FillColorFunction gst_compositor_fill_color_xbgr;
FillColorFunction gst_compositor_fill_color_rgbx;
FillColorFunction gst_compositor_fill_color_bgrx;
FillColorFunction gst_compositor_fill_color_yuy2;
FillColorFunction gst_compositor_fill_color_yvyu;
FillColorFunction gst_compositor_fill_color_uyvy;

void
gst_compositor_init_blend (void)
{
  GST_DEBUG_CATEGORY_INIT (gst_compositor_blend_debug, "compositor_blend", 0,
      "video compositor blending functions");

  gst_compositor_blend_argb = GST_DEBUG_FUNCPTR (blend_argb);
  gst_compositor_blend_bgra = GST_DEBUG_FUNCPTR (blend_bgra);
  gst_compositor_overlay_argb = GST_DEBUG_FUNCPTR (overlay_argb);
  gst_compositor_overlay_bgra = GST_DEBUG_FUNCPTR (overlay_bgra);
  gst_compositor_blend_i420 = GST_DEBUG_FUNCPTR (blend_i420);
  gst_compositor_blend_nv12 = GST_DEBUG_FUNCPTR (blend_nv12);
  gst_compositor_blend_nv21 = GST_DEBUG_FUNCPTR (blend_nv21);
  gst_compositor_blend_y444 = GST_DEBUG_FUNCPTR (blend_y444);
  gst_compositor_blend_y42b = GST_DEBUG_FUNCPTR (blend_y42b);
  gst_compositor_blend_y41b = GST_DEBUG_FUNCPTR (blend_y41b);
  gst_compositor_blend_rgb = GST_DEBUG_FUNCPTR (blend_rgb);
  gst_compositor_blend_xrgb = GST_DEBUG_FUNCPTR (blend_xrgb);
  gst_compositor_blend_yuy2 = GST_DEBUG_FUNCPTR (blend_yuy2);

  gst_compositor_fill_checker_argb = GST_DEBUG_FUNCPTR (fill_checker_argb_c);
  gst_compositor_fill_checker_bgra = GST_DEBUG_FUNCPTR (fill_checker_bgra_c);
  gst_compositor_fill_checker_ayuv = GST_DEBUG_FUNCPTR (fill_checker_ayuv_c);
  gst_compositor_fill_checker_vuya = GST_DEBUG_FUNCPTR (fill_checker_vuya_c);
  gst_compositor_fill_checker_i420 = GST_DEBUG_FUNCPTR (fill_checker_i420);
  gst_compositor_fill_checker_nv12 = GST_DEBUG_FUNCPTR (fill_checker_nv12);
  gst_compositor_fill_checker_nv21 = GST_DEBUG_FUNCPTR (fill_checker_nv21);
  gst_compositor_fill_checker_y444 = GST_DEBUG_FUNCPTR (fill_checker_y444);
  gst_compositor_fill_checker_y42b = GST_DEBUG_FUNCPTR (fill_checker_y42b);
  gst_compositor_fill_checker_y41b = GST_DEBUG_FUNCPTR (fill_checker_y41b);
  gst_compositor_fill_checker_rgb = GST_DEBUG_FUNCPTR (fill_checker_rgb_c);
  gst_compositor_fill_checker_xrgb = GST_DEBUG_FUNCPTR (fill_checker_xrgb_c);
  gst_compositor_fill_checker_rgbx = GST_DEBUG_FUNCPTR (fill_checker_rgbx_c);
  gst_compositor_fill_checker_yuy2 = GST_DEBUG_FUNCPTR (fill_checker_yuy2_c);
  gst_compositor_fill_checker_uyvy = GST_DEBUG_FUNCPTR (fill_checker_uyvy_c);

  gst_compositor_fill_color_argb = GST_DEBUG_FUNCPTR (fill_color_argb);
  gst_compositor_fill_color_bgra = GST_DEBUG_FUNCPTR (fill_color_bgra);
  gst_compositor_fill_color_abgr = GST_DEBUG_FUNCPTR (fill_color_abgr);
  gst_compositor_fill_color_rgba = GST_DEBUG_FUNCPTR (fill_color_rgba);
  gst_compositor_fill_color_ayuv = GST_DEBUG_FUNCPTR (fill_color_ayuv);
  gst_compositor_fill_color_vuya = GST_DEBUG_FUNCPTR (fill_color_vuya);
  gst_compositor_fill_color_i420 = GST_DEBUG_FUNCPTR (fill_color_i420);
  gst_compositor_fill_color_yv12 = GST_DEBUG_FUNCPTR (fill_color_yv12);
  gst_compositor_fill_color_nv12 = GST_DEBUG_FUNCPTR (fill_color_nv12);
  gst_compositor_fill_color_y444 = GST_DEBUG_FUNCPTR (fill_color_y444);
  gst_compositor_fill_color_y42b = GST_DEBUG_FUNCPTR (fill_color_y42b);
  gst_compositor_fill_color_y41b = GST_DEBUG_FUNCPTR (fill_color_y41b);
  gst_compositor_fill_color_rgb = GST_DEBUG_FUNCPTR (fill_color_rgb_c);
  gst_compositor_fill_color_bgr = GST_DEBUG_FUNCPTR (fill_color_bgr_c);
  gst_compositor_fill_color_xrgb = GST_DEBUG_FUNCPTR (fill_color_xrgb);
  gst_compositor_fill_color_xbgr = GST_DEBUG_FUNCPTR (fill_color_xbgr);
  gst_compositor_fill_color_rgbx = GST_DEBUG_FUNCPTR (fill_color_rgbx);
  gst_compositor_fill_color_bgrx = GST_DEBUG_FUNCPTR (fill_color_bgrx);
  gst_compositor_fill_color_yuy2 = GST_DEBUG_FUNCPTR (fill_color_yuy2);
  gst_compositor_fill_color_yvyu = GST_DEBUG_FUNCPTR (fill_color_yvyu);
  gst_compositor_fill_color_uyvy = GST_DEBUG_FUNCPTR (fill_color_uyvy);
}
