/**************************************************************************
 *
 * Copyright 2009 VMware, Inc.  All Rights Reserved.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the
 * "Software"), to deal in the Software without restriction, including
 * without limitation the rights to use, copy, modify, merge, publish,
 * distribute, sub license, and/or sell copies of the Software, and to
 * permit persons to whom the Software is furnished to do so, subject to
 * the following conditions:
 *
 * The above copyright notice and this permission notice (including the
 * next paragraph) shall be included in all copies or substantial portions
 * of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
 * IN NO EVENT SHALL VMWARE AND/OR ITS SUPPLIERS BE LIABLE FOR
 * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 *
 **************************************************************************/

#ifndef ASM_FILL_H
#define ASM_FILL_H

#include "tgsi/tgsi_ureg.h"

typedef void (* ureg_func)( struct ureg_program *ureg,
                            struct ureg_dst *out,
                            struct ureg_src *in,
                            struct ureg_src *sampler,
                            struct ureg_dst *temp,
                            struct ureg_src *constant);

static INLINE void
solid_fill( struct ureg_program *ureg,
            struct ureg_dst *out,
            struct ureg_src *in,
            struct ureg_src *sampler,
            struct ureg_dst *temp,
            struct ureg_src *constant)
{
   ureg_MOV(ureg, *out, constant[2]);
}

/**
 * Perform frag-coord-to-paint-coord transform.  The transformation is in
 * CONST[4..6].
 */
#define PAINT_TRANSFORM                                                 \
   ureg_MOV(ureg, ureg_writemask(temp[0], TGSI_WRITEMASK_XY), in[0]);   \
   ureg_MOV(ureg,                                                       \
            ureg_writemask(temp[0], TGSI_WRITEMASK_Z),                  \
            ureg_scalar(constant[3], TGSI_SWIZZLE_Y));                  \
   ureg_DP3(ureg, temp[1], constant[4], ureg_src(temp[0]));             \
   ureg_DP3(ureg, temp[2], constant[5], ureg_src(temp[0]));             \
   ureg_DP3(ureg, temp[3], constant[6], ureg_src(temp[0]));             \
   ureg_RCP(ureg, temp[3], ureg_src(temp[3]));                          \
   ureg_MUL(ureg, temp[1], ureg_src(temp[1]), ureg_src(temp[3]));       \
   ureg_MUL(ureg, temp[2], ureg_src(temp[2]), ureg_src(temp[3]));       \
   ureg_MOV(ureg,                                                       \
            ureg_writemask(temp[4], TGSI_WRITEMASK_X),                  \
            ureg_src(temp[1]));                                         \
   ureg_MOV(ureg,                                                       \
            ureg_writemask(temp[4], TGSI_WRITEMASK_Y),                  \
            ureg_src(temp[2]));

static INLINE void
linear_grad( struct ureg_program *ureg,
             struct ureg_dst *out,
             struct ureg_src *in,
             struct ureg_src *sampler,
             struct ureg_dst *temp,
             struct ureg_src *constant)
{
   PAINT_TRANSFORM

   /* grad = DP2((x, y), CONST[2].xy) * CONST[2].z */
   ureg_MUL(ureg, temp[0],
            ureg_scalar(constant[2], TGSI_SWIZZLE_Y),
            ureg_scalar(ureg_src(temp[4]), TGSI_SWIZZLE_Y));
   ureg_MAD(ureg, temp[1],
            ureg_scalar(constant[2], TGSI_SWIZZLE_X),
            ureg_scalar(ureg_src(temp[4]), TGSI_SWIZZLE_X),
            ureg_src(temp[0]));
   ureg_MUL(ureg, temp[2], ureg_src(temp[1]),
            ureg_scalar(constant[2], TGSI_SWIZZLE_Z));

   ureg_TEX(ureg, *out, TGSI_TEXTURE_1D, ureg_src(temp[2]), sampler[0]);
}

static INLINE void
radial_grad( struct ureg_program *ureg,
             struct ureg_dst *out,
             struct ureg_src *in,
             struct ureg_src *sampler,
             struct ureg_dst *temp,
             struct ureg_src *constant)
{
   PAINT_TRANSFORM

   /*
    * Calculate (sqrt(B^2 + AC) - B) / A, where
    *
    *   A is CONST[2].z,
    *   B is DP2((x, y), CONST[2].xy), and
    *   C is DP2((x, y), (x, y)).
    */

   /* B and C */
   ureg_DP2(ureg, temp[0], ureg_src(temp[4]), constant[2]);
   ureg_DP2(ureg, temp[1], ureg_src(temp[4]), ureg_src(temp[4]));

   /* the square root */
   ureg_MUL(ureg, temp[2], ureg_src(temp[0]), ureg_src(temp[0]));
   ureg_MAD(ureg, temp[3], ureg_src(temp[1]),
         ureg_scalar(constant[2], TGSI_SWIZZLE_Z), ureg_src(temp[2]));
   ureg_RSQ(ureg, temp[3], ureg_src(temp[3]));
   ureg_RCP(ureg, temp[3], ureg_src(temp[3]));

   ureg_SUB(ureg, temp[3], ureg_src(temp[3]), ureg_src(temp[0]));
   ureg_RCP(ureg, temp[0], ureg_scalar(constant[2], TGSI_SWIZZLE_Z));
   ureg_MUL(ureg, temp[0], ureg_src(temp[0]), ureg_src(temp[3]));

   ureg_TEX(ureg, *out, TGSI_TEXTURE_1D, ureg_src(temp[0]), sampler[0]);
}


static INLINE void
pattern( struct ureg_program *ureg,
         struct ureg_dst     *out,
         struct ureg_src     *in,
         struct ureg_src     *sampler,
         struct ureg_dst     *temp,
         struct ureg_src     *constant)
{
   PAINT_TRANSFORM

   /* (s, t) = (x / tex_width, y / tex_height) */
   ureg_RCP(ureg, temp[0],
            ureg_swizzle(constant[3],
                         TGSI_SWIZZLE_Z,
                         TGSI_SWIZZLE_W,
                         TGSI_SWIZZLE_Z,
                         TGSI_SWIZZLE_W));
   ureg_MOV(ureg, temp[1], ureg_src(temp[4]));
   ureg_MUL(ureg,
            ureg_writemask(temp[1], TGSI_WRITEMASK_X),
            ureg_src(temp[1]),
            ureg_src(temp[0]));
   ureg_MUL(ureg,
            ureg_writemask(temp[1], TGSI_WRITEMASK_Y),
            ureg_src(temp[1]),
            ureg_src(temp[0]));

   ureg_TEX(ureg, *out, TGSI_TEXTURE_2D, ureg_src(temp[1]), sampler[0]);
}

static INLINE void
paint_degenerate( struct ureg_program *ureg,
                  struct ureg_dst *out,
                  struct ureg_src *in,
                  struct ureg_src *sampler,
                  struct ureg_dst *temp,
                  struct ureg_src *constant)
{
   /* CONST[3].y is 1.0f */
   ureg_MOV(ureg, temp[1], ureg_scalar(constant[3], TGSI_SWIZZLE_Y));
   ureg_TEX(ureg, *out, TGSI_TEXTURE_1D, ureg_src(temp[1]), sampler[0]);
}

static INLINE void
image_normal( struct ureg_program *ureg,
              struct ureg_dst *out,
              struct ureg_src *in,
              struct ureg_src *sampler,
              struct ureg_dst *temp,
              struct ureg_src *constant)
{
   /* store and pass image color in TEMP[1] */
   ureg_TEX(ureg, temp[1], TGSI_TEXTURE_2D, in[1], sampler[3]);
   ureg_MOV(ureg, *out, ureg_src(temp[1]));
}


static INLINE void
image_multiply( struct ureg_program *ureg,
                struct ureg_dst *out,
                struct ureg_src *in,
                struct ureg_src *sampler,
                struct ureg_dst *temp,
                struct ureg_src *constant)
{
   /* store and pass image color in TEMP[1] */
   ureg_TEX(ureg, temp[1], TGSI_TEXTURE_2D, in[1], sampler[3]);
   ureg_MUL(ureg, *out, ureg_src(temp[0]), ureg_src(temp[1]));
}


static INLINE void
image_stencil( struct ureg_program *ureg,
               struct ureg_dst *out,
               struct ureg_src *in,
               struct ureg_src *sampler,
               struct ureg_dst *temp,
               struct ureg_src *constant)
{
   /* store and pass image color in TEMP[1] */
   ureg_TEX(ureg, temp[1], TGSI_TEXTURE_2D, in[1], sampler[3]);
   ureg_MOV(ureg, *out, ureg_src(temp[0]));
}

static INLINE void
color_transform( struct ureg_program *ureg,
                 struct ureg_dst *out,
                 struct ureg_src *in,
                 struct ureg_src *sampler,
                 struct ureg_dst *temp,
                 struct ureg_src *constant)
{
   /* note that TEMP[1] may already be used for image color */

   ureg_MAD(ureg, temp[2], ureg_src(temp[0]), constant[0], constant[1]);
   /* clamp to [0.0f, 1.0f] */
   ureg_CLAMP(ureg, temp[2],
              ureg_src(temp[2]),
              ureg_scalar(constant[3], TGSI_SWIZZLE_X),
              ureg_scalar(constant[3], TGSI_SWIZZLE_Y));
   ureg_MOV(ureg, *out, ureg_src(temp[2]));
}

static INLINE void
alpha_normal( struct ureg_program *ureg,
              struct ureg_dst *out,
              struct ureg_src *in,
              struct ureg_src *sampler,
              struct ureg_dst *temp,
              struct ureg_src *constant)
{
   /* save per-channel alpha in TEMP[1] */
   ureg_MOV(ureg, temp[1], ureg_scalar(ureg_src(temp[0]), TGSI_SWIZZLE_W));

   ureg_MOV(ureg, *out, ureg_src(temp[0]));
}

static INLINE void
alpha_per_channel( struct ureg_program *ureg,
                   struct ureg_dst *out,
                   struct ureg_src *in,
                   struct ureg_src *sampler,
                   struct ureg_dst *temp,
                   struct ureg_src *constant)
{
   /* save per-channel alpha in TEMP[1] */
   ureg_MUL(ureg,
            ureg_writemask(temp[1], TGSI_WRITEMASK_W),
            ureg_src(temp[0]),
            ureg_src(temp[1]));
   ureg_MUL(ureg,
            ureg_writemask(temp[1], TGSI_WRITEMASK_XYZ),
            ureg_src(temp[1]),
            ureg_scalar(ureg_src(temp[1]), TGSI_SWIZZLE_W));

   /* update alpha */
   ureg_MOV(ureg,
            ureg_writemask(temp[0], TGSI_WRITEMASK_W),
            ureg_src(temp[1]));
   ureg_MOV(ureg, *out, ureg_src(temp[0]));
}

/**
 * Premultiply src and dst.
 */
static INLINE void
blend_premultiply( struct ureg_program *ureg,
                   struct ureg_src src,
                   struct ureg_src src_channel_alpha,
                   struct ureg_src dst)
{
   /* premultiply src */
   ureg_MUL(ureg,
            ureg_writemask(ureg_dst(src), TGSI_WRITEMASK_XYZ),
            src,
            src_channel_alpha);
   /* premultiply dst */
   ureg_MUL(ureg,
            ureg_writemask(ureg_dst(dst), TGSI_WRITEMASK_XYZ),
            dst,
            ureg_scalar(dst, TGSI_SWIZZLE_W));
}

/**
 * Unpremultiply src.
 */
static INLINE void
blend_unpremultiply( struct ureg_program *ureg,
                     struct ureg_src src,
                     struct ureg_src one,
                     struct ureg_dst temp[1])
{
   /* replace 0.0f by 1.0f before calculating reciprocal */
   ureg_CMP(ureg,
            temp[0],
            ureg_negate(ureg_scalar(src, TGSI_SWIZZLE_W)),
            ureg_scalar(src, TGSI_SWIZZLE_W),
            one);
   ureg_RCP(ureg, temp[0], ureg_src(temp[0]));

   ureg_MUL(ureg,
            ureg_writemask(ureg_dst(src), TGSI_WRITEMASK_XYZ),
            src,
            ureg_src(temp[0]));
}

/**
 * Emit instructions for the specified blend mode.  Colors will be
 * unpremultiplied.  Two temporary registers are required.
 *
 * The output is written back to src.
 */
static INLINE void
blend_generic(struct ureg_program *ureg,
              VGBlendMode mode,
              struct ureg_src src,
              struct ureg_src src_channel_alpha,
              struct ureg_src dst,
              struct ureg_src one,
              struct ureg_dst temp[2])
{
   struct ureg_dst out;

   blend_premultiply(ureg, src, src_channel_alpha, dst);

   /* blend in-place */
   out = ureg_dst(src);

   switch (mode) {
   case VG_BLEND_SRC:
      ureg_MOV(ureg, out, src);
      break;
   case VG_BLEND_SRC_OVER:
      /* RGBA_out = RGBA_src + (1 - A_src) * RGBA_dst */
      ureg_SUB(ureg, temp[0], one, src_channel_alpha);
      ureg_MAD(ureg, out, ureg_src(temp[0]), dst, src);
      break;
   case VG_BLEND_DST_OVER:
      /* RGBA_out = RGBA_dst + (1 - A_dst) * RGBA_src */
      ureg_SUB(ureg, temp[0], one, ureg_scalar(dst, TGSI_SWIZZLE_W));
      ureg_MAD(ureg, out, ureg_src(temp[0]), src, dst);
      break;
   case VG_BLEND_SRC_IN:
      ureg_MUL(ureg, out, src, ureg_scalar(dst, TGSI_SWIZZLE_W));
      break;
   case VG_BLEND_DST_IN:
      ureg_MUL(ureg, out, dst, src_channel_alpha);
      break;
   case VG_BLEND_MULTIPLY:
      /*
       * RGB_out = (1 - A_dst) * RGB_src + (1 - A_src) * RGB_dst +
       *           RGB_src * RGB_dst
       */
      ureg_MAD(ureg, temp[0],
            ureg_scalar(dst, TGSI_SWIZZLE_W), ureg_negate(src), src);
      ureg_MAD(ureg, temp[1],
            src_channel_alpha, ureg_negate(dst), dst);
      ureg_MAD(ureg, temp[0], src, dst, ureg_src(temp[0]));
      ureg_ADD(ureg, out, ureg_src(temp[0]), ureg_src(temp[1]));
      /* alpha is src over */
      ureg_ADD(ureg, ureg_writemask(out, TGSI_WRITEMASK_W),
            src, ureg_src(temp[1]));
      break;
   case VG_BLEND_SCREEN:
      /* RGBA_out = RGBA_src + (1 - RGBA_src) * RGBA_dst */
      ureg_SUB(ureg, temp[0], one, src);
      ureg_MAD(ureg, out, ureg_src(temp[0]), dst, src);
      break;
   case VG_BLEND_DARKEN:
   case VG_BLEND_LIGHTEN:
      /* src over */
      ureg_SUB(ureg, temp[0], one, src_channel_alpha);
      ureg_MAD(ureg, temp[0], ureg_src(temp[0]), dst, src);
      /* dst over */
      ureg_SUB(ureg, temp[1], one, ureg_scalar(dst, TGSI_SWIZZLE_W));
      ureg_MAD(ureg, temp[1], ureg_src(temp[1]), src, dst);
      /* take min/max for colors */
      if (mode == VG_BLEND_DARKEN) {
         ureg_MIN(ureg, ureg_writemask(out, TGSI_WRITEMASK_XYZ),
               ureg_src(temp[0]), ureg_src(temp[1]));
      }
      else {
         ureg_MAX(ureg, ureg_writemask(out, TGSI_WRITEMASK_XYZ),
               ureg_src(temp[0]), ureg_src(temp[1]));
      }
      break;
   case VG_BLEND_ADDITIVE:
      /* RGBA_out = RGBA_src + RGBA_dst */
      ureg_ADD(ureg, temp[0], src, dst);
      ureg_MIN(ureg, out, ureg_src(temp[0]), one);
      break;
   default:
      assert(0);
      break;
   }

   blend_unpremultiply(ureg, src, one, temp);
}

#define BLEND_GENERIC(mode) \
   do { \
      ureg_TEX(ureg, temp[2], TGSI_TEXTURE_2D, in[0], sampler[2]);         \
      blend_generic(ureg, (mode), ureg_src(temp[0]), ureg_src(temp[1]),    \
                    ureg_src(temp[2]),                                     \
                    ureg_scalar(constant[3], TGSI_SWIZZLE_Y), temp + 3);   \
      ureg_MOV(ureg, *out, ureg_src(temp[0]));                             \
   } while (0)

static INLINE void
blend_src( struct ureg_program *ureg,
           struct ureg_dst *out,
           struct ureg_src *in,
           struct ureg_src *sampler,
           struct ureg_dst *temp,
           struct ureg_src *constant)
{
   BLEND_GENERIC(VG_BLEND_SRC);
}

static INLINE void
blend_src_over( struct ureg_program *ureg,
                struct ureg_dst *out,
                struct ureg_src *in,
                struct ureg_src *sampler,
                struct ureg_dst *temp,
                struct ureg_src *constant)
{
   BLEND_GENERIC(VG_BLEND_SRC_OVER);
}

static INLINE void
blend_dst_over( struct ureg_program *ureg,
                struct ureg_dst *out,
                struct ureg_src *in,
                struct ureg_src *sampler,
                struct ureg_dst *temp,
                struct ureg_src *constant)
{
   BLEND_GENERIC(VG_BLEND_DST_OVER);
}

static INLINE void
blend_src_in( struct ureg_program *ureg,
              struct ureg_dst *out,
              struct ureg_src *in,
              struct ureg_src *sampler,
              struct ureg_dst *temp,
              struct ureg_src *constant)
{
   BLEND_GENERIC(VG_BLEND_SRC_IN);
}

static INLINE void
blend_dst_in( struct ureg_program *ureg,
              struct ureg_dst *out,
              struct ureg_src *in,
              struct ureg_src *sampler,
              struct ureg_dst *temp,
              struct ureg_src *constant)
{
   BLEND_GENERIC(VG_BLEND_DST_IN);
}

static INLINE void
blend_multiply( struct ureg_program *ureg,
                struct ureg_dst *out,
                struct ureg_src *in,
                struct ureg_src *sampler,
                struct ureg_dst *temp,
                struct ureg_src *constant)
{
   BLEND_GENERIC(VG_BLEND_MULTIPLY);
}

static INLINE void
blend_screen( struct ureg_program *ureg,
              struct ureg_dst     *out,
              struct ureg_src     *in,
              struct ureg_src     *sampler,
              struct ureg_dst     *temp,
              struct ureg_src     *constant)
{
   BLEND_GENERIC(VG_BLEND_SCREEN);
}

static INLINE void
blend_darken( struct ureg_program *ureg,
              struct ureg_dst     *out,
              struct ureg_src     *in,
              struct ureg_src     *sampler,
              struct ureg_dst     *temp,
              struct ureg_src     *constant)
{
   BLEND_GENERIC(VG_BLEND_DARKEN);
}

static INLINE void
blend_lighten( struct ureg_program *ureg,
               struct ureg_dst     *out,
               struct ureg_src     *in,
               struct ureg_src     *sampler,
               struct ureg_dst *temp,
               struct ureg_src     *constant)
{
   BLEND_GENERIC(VG_BLEND_LIGHTEN);
}

static INLINE void
blend_additive( struct ureg_program *ureg,
                struct ureg_dst *out,
                struct ureg_src *in,
                struct ureg_src *sampler,
                struct ureg_dst *temp,
                struct ureg_src *constant)
{
   BLEND_GENERIC(VG_BLEND_ADDITIVE);
}

static INLINE void
mask( struct ureg_program *ureg,
      struct ureg_dst *out,
      struct ureg_src *in,
      struct ureg_src *sampler,
      struct ureg_dst *temp,
      struct ureg_src *constant)
{
   ureg_TEX(ureg, temp[1], TGSI_TEXTURE_2D, in[0], sampler[1]);
   ureg_MUL(ureg, ureg_writemask(temp[0], TGSI_WRITEMASK_W),
            ureg_scalar(ureg_src(temp[0]), TGSI_SWIZZLE_W),
            ureg_scalar(ureg_src(temp[1]), TGSI_SWIZZLE_W));
   ureg_MOV(ureg, *out, ureg_src(temp[0]));
}

static INLINE void
premultiply( struct ureg_program *ureg,
                struct ureg_dst *out,
                struct ureg_src *in,
                struct ureg_src *sampler,
                struct ureg_dst *temp,
                struct ureg_src *constant)
{
   ureg_MUL(ureg,
            ureg_writemask(temp[0], TGSI_WRITEMASK_XYZ),
            ureg_src(temp[0]),
            ureg_scalar(ureg_src(temp[0]), TGSI_SWIZZLE_W));
}

static INLINE void
unpremultiply( struct ureg_program *ureg,
                struct ureg_dst *out,
                struct ureg_src *in,
                struct ureg_src *sampler,
                struct ureg_dst *temp,
                struct ureg_src *constant)
{
   ureg_TEX(ureg, temp[0], TGSI_TEXTURE_2D, in[0], sampler[1]);
}


static INLINE void
color_bw( struct ureg_program *ureg,
                struct ureg_dst *out,
                struct ureg_src *in,
                struct ureg_src *sampler,
                struct ureg_dst *temp,
                struct ureg_src *constant)
{
   ureg_ADD(ureg, temp[1],
            ureg_scalar(constant[3], TGSI_SWIZZLE_Y),
            ureg_scalar(constant[3], TGSI_SWIZZLE_Y));
   ureg_RCP(ureg, temp[2], ureg_src(temp[1]));
   ureg_ADD(ureg, temp[1],
            ureg_scalar(constant[3], TGSI_SWIZZLE_Y),
            ureg_src(temp[2]));
   ureg_ADD(ureg, ureg_writemask(temp[2], TGSI_WRITEMASK_X),
            ureg_scalar(ureg_src(temp[0]), TGSI_SWIZZLE_X),
            ureg_scalar(ureg_src(temp[0]), TGSI_SWIZZLE_Y));
   ureg_ADD(ureg, ureg_writemask(temp[2], TGSI_WRITEMASK_X),
            ureg_scalar(ureg_src(temp[0]), TGSI_SWIZZLE_Z),
            ureg_scalar(ureg_src(temp[0]), TGSI_SWIZZLE_X));
   ureg_SGE(ureg,
            ureg_writemask(temp[0], TGSI_WRITEMASK_XYZ),
            ureg_scalar(ureg_src(temp[2]), TGSI_SWIZZLE_X),
            ureg_src(temp[1]));
  ureg_SGE(ureg,
           ureg_writemask(temp[0], TGSI_WRITEMASK_W),
           ureg_scalar(ureg_src(temp[0]), TGSI_SWIZZLE_W),
           ureg_scalar(ureg_src(temp[2]), TGSI_SWIZZLE_Y));
  ureg_MOV(ureg, *out, ureg_src(temp[0]));
}


struct shader_asm_info {
   VGint id;
   ureg_func func;

   VGboolean needs_position;

   VGint start_const;
   VGint num_consts;

   VGint start_sampler;
   VGint num_samplers;

   VGint start_temp;
   VGint num_temps;
};


/* paint types */
static const struct shader_asm_info shaders_paint_asm[] = {
   {VEGA_SOLID_FILL_SHADER, solid_fill,
    VG_FALSE, 2, 1, 0, 0, 0, 0},
   {VEGA_LINEAR_GRADIENT_SHADER, linear_grad,
    VG_TRUE,  2, 5, 0, 1, 0, 5},
   {VEGA_RADIAL_GRADIENT_SHADER, radial_grad,
    VG_TRUE,  2, 5, 0, 1, 0, 5},
   {VEGA_PATTERN_SHADER, pattern,
    VG_TRUE,  3, 4, 0, 1, 0, 5},
   {VEGA_PAINT_DEGENERATE_SHADER, paint_degenerate,
    VG_FALSE,  3, 1, 0, 1, 0, 2}
};

/* image draw modes */
static const struct shader_asm_info shaders_image_asm[] = {
   {VEGA_IMAGE_NORMAL_SHADER, image_normal,
    VG_TRUE,  0, 0, 3, 1, 0, 2},
   {VEGA_IMAGE_MULTIPLY_SHADER, image_multiply,
    VG_TRUE,  0, 0, 3, 1, 0, 2},
   {VEGA_IMAGE_STENCIL_SHADER, image_stencil,
    VG_TRUE,  0, 0, 3, 1, 0, 2}
};

static const struct shader_asm_info shaders_color_transform_asm[] = {
   {VEGA_COLOR_TRANSFORM_SHADER, color_transform,
    VG_FALSE, 0, 4, 0, 0, 0, 3}
};

static const struct shader_asm_info shaders_alpha_asm[] = {
   {VEGA_ALPHA_NORMAL_SHADER, alpha_normal,
    VG_FALSE, 0, 0, 0, 0, 0, 2},
   {VEGA_ALPHA_PER_CHANNEL_SHADER, alpha_per_channel,
    VG_FALSE, 0, 0, 0, 0, 0, 2}
};

/* extra blend modes */
static const struct shader_asm_info shaders_blend_asm[] = {
#define BLEND_ASM_INFO(id, func) { (id), (func), VG_TRUE, 3, 1, 2, 1, 0, 5 }
   BLEND_ASM_INFO(VEGA_BLEND_SRC_SHADER, blend_src),
   BLEND_ASM_INFO(VEGA_BLEND_SRC_OVER_SHADER, blend_src_over),
   BLEND_ASM_INFO(VEGA_BLEND_DST_OVER_SHADER, blend_dst_over),
   BLEND_ASM_INFO(VEGA_BLEND_SRC_IN_SHADER, blend_src_in),
   BLEND_ASM_INFO(VEGA_BLEND_DST_IN_SHADER, blend_dst_in),
   BLEND_ASM_INFO(VEGA_BLEND_MULTIPLY_SHADER, blend_multiply),
   BLEND_ASM_INFO(VEGA_BLEND_SCREEN_SHADER, blend_screen),
   BLEND_ASM_INFO(VEGA_BLEND_DARKEN_SHADER, blend_darken),
   BLEND_ASM_INFO(VEGA_BLEND_LIGHTEN_SHADER, blend_lighten),
   BLEND_ASM_INFO(VEGA_BLEND_ADDITIVE_SHADER, blend_additive)
#undef BLEND_ASM_INFO
};

static const struct shader_asm_info shaders_mask_asm[] = {
   {VEGA_MASK_SHADER, mask,
    VG_TRUE,  0, 0, 1, 1, 0, 2}
};

/* premultiply */
static const struct shader_asm_info shaders_premultiply_asm[] = {
   {VEGA_PREMULTIPLY_SHADER, premultiply,
    VG_FALSE,  0, 0, 0, 0, 0, 1},
   {VEGA_UNPREMULTIPLY_SHADER, unpremultiply,
    VG_FALSE,  0, 0, 0, 0, 0, 1},
};

/* color transform to black and white */
static const struct shader_asm_info shaders_bw_asm[] = {
   {VEGA_BW_SHADER, color_bw,
    VG_FALSE,  3, 1, 0, 0, 0, 3},
};

#endif