+ {
+# define IMAGE_WALK_PREFIX(x) walk_##x
+# define IMAGE_WALK_INLINE
+# define IMAGE_WALK_IMAGE dest
+# define IMAGE_WALK_UNROLL 4
+# define IMAGE_WALK_DO_STEP do{ walk_pos[dest->channels - 1] = 255; }while(0)
+# include "images/image-walk.h"
+ }
+}
+
+static void
+image_conv_copy_alpha(struct image *dest, struct image *src)
+{
+ if (dest->pixels != src->pixels || dest->channels != src->channels)
+ {
+# define IMAGE_WALK_PREFIX(x) walk_##x
+# define IMAGE_WALK_INLINE
+# define IMAGE_WALK_DOUBLE
+# define IMAGE_WALK_IMAGE dest
+# define IMAGE_WALK_SEC_IMAGE src
+# define IMAGE_WALK_UNROLL 4
+# define IMAGE_WALK_DO_STEP do{ walk_pos[dest->channels - 1] = walk_sec_pos[src->channels - 1]; }while(0)
+# include "images/image-walk.h"
+ }
+}
+
+static inline uns
+image_conv_alpha_func(uns value, uns alpha, uns acoef, uns bcoef)
+{
+ return ((uns)(acoef + (int)alpha * (int)(value - bcoef)) * (0xffffffffU / 255 / 255)) >> 24;
+}
+
+static int
+image_conv_apply_alpha_from(struct image_context *ctx, struct image *dest, struct image *src, struct image_conv_options *opt)
+{
+ if (!opt->background.color_space)
+ return 1;
+ byte background[IMAGE_MAX_CHANNELS];
+ if (unlikely(!color_put(ctx, &opt->background, background, dest->flags & IMAGE_COLOR_SPACE)))
+ return 0;
+ uns a[IMAGE_MAX_CHANNELS], b[IMAGE_MAX_CHANNELS];
+ for (uns i = 0; i < dest->channels; i++)
+ a[i] = 255 * (b[i] = background[i]);
+ switch (dest->channels)
+ {
+ case 1:
+ {
+# define IMAGE_WALK_PREFIX(x) walk_##x
+# define IMAGE_WALK_INLINE
+# define IMAGE_WALK_IMAGE dest
+# define IMAGE_WALK_SEC_IMAGE src
+# define IMAGE_WALK_DOUBLE
+# define IMAGE_WALK_UNROLL 2
+# define IMAGE_WALK_DO_STEP do{ \
+ walk_pos[0] = image_conv_alpha_func(walk_pos[0], walk_sec_pos[src->channels - 1], a[0], b[0]); }while(0)
+# include "images/image-walk.h"
+ }
+ return 1;
+ case 3:
+ {
+# define IMAGE_WALK_PREFIX(x) walk_##x
+# define IMAGE_WALK_INLINE
+# define IMAGE_WALK_IMAGE dest
+# define IMAGE_WALK_SEC_IMAGE src
+# define IMAGE_WALK_DOUBLE
+# define IMAGE_WALK_DO_STEP do{ \
+ walk_pos[0] = image_conv_alpha_func(walk_pos[0], walk_sec_pos[src->channels - 1], a[0], b[0]); \
+ walk_pos[1] = image_conv_alpha_func(walk_pos[1], walk_sec_pos[src->channels - 1], a[1], b[1]); \
+ walk_pos[2] = image_conv_alpha_func(walk_pos[2], walk_sec_pos[src->channels - 1], a[2], b[2]); }while(0)
+# include "images/image-walk.h"
+ }
+ return 1;
+ }
+ {
+# define IMAGE_WALK_PREFIX(x) walk_##x
+# define IMAGE_WALK_INLINE
+# define IMAGE_WALK_IMAGE dest
+# define IMAGE_WALK_SEC_IMAGE src
+# define IMAGE_WALK_DOUBLE
+# define IMAGE_WALK_DO_STEP do{ for (uns i = 0; i < dest->channels; i++) \
+ walk_pos[i] = image_conv_alpha_func(walk_pos[i], walk_sec_pos[src->channels - 1], a[i], b[i]); }while(0)
+# include "images/image-walk.h"
+ }
+ return 1;
+}
+
+static int
+image_conv_apply_alpha_to(struct image_context *ctx, struct image *dest, struct image *src, struct image_conv_options *opt)
+{
+ if (!opt->background.color_space)
+ {
+ image_conv_copy(dest, src);
+ return 1;
+ }
+ byte background[IMAGE_MAX_CHANNELS];
+ if (unlikely(!color_put(ctx, &opt->background, background, dest->flags & IMAGE_COLOR_SPACE)))
+ return 0;
+ uns a[IMAGE_MAX_CHANNELS], b[IMAGE_MAX_CHANNELS];
+ for (uns i = 0; i < dest->channels; i++)
+ a[i] = 255 * (b[i] = background[i]);
+ switch (dest->channels)
+ {
+ case 1:
+ {
+# define IMAGE_WALK_PREFIX(x) walk_##x
+# define IMAGE_WALK_INLINE
+# define IMAGE_WALK_IMAGE dest
+# define IMAGE_WALK_SEC_IMAGE src
+# define IMAGE_WALK_DOUBLE
+# define IMAGE_WALK_UNROLL 2
+# define IMAGE_WALK_DO_STEP do{ \
+ walk_pos[0] = image_conv_alpha_func(walk_sec_pos[0], walk_sec_pos[src->channels - 1], a[0], b[0]); }while(0)
+# include "images/image-walk.h"
+ }
+ return 1;
+ case 3:
+ {
+# define IMAGE_WALK_PREFIX(x) walk_##x
+# define IMAGE_WALK_INLINE
+# define IMAGE_WALK_IMAGE dest
+# define IMAGE_WALK_SEC_IMAGE src
+# define IMAGE_WALK_DOUBLE
+# define IMAGE_WALK_DO_STEP do{ \
+ walk_pos[0] = image_conv_alpha_func(walk_sec_pos[0], walk_sec_pos[src->channels - 1], a[0], b[0]); \
+ walk_pos[1] = image_conv_alpha_func(walk_sec_pos[1], walk_sec_pos[src->channels - 1], a[1], b[1]); \
+ walk_pos[2] = image_conv_alpha_func(walk_sec_pos[2], walk_sec_pos[src->channels - 1], a[2], b[2]); }while(0)
+# include "images/image-walk.h"
+ }
+ return 1;
+ }
+ {
+# define IMAGE_WALK_PREFIX(x) walk_##x
+# define IMAGE_WALK_INLINE
+# define IMAGE_WALK_IMAGE dest
+# define IMAGE_WALK_SEC_IMAGE src
+# define IMAGE_WALK_DOUBLE
+# define IMAGE_WALK_DO_STEP do{ for (uns i = 0; i < dest->channels; i++) \
+ walk_pos[i] = image_conv_alpha_func(walk_sec_pos[i], walk_sec_pos[src->channels - 1], a[i], b[i]); }while(0)
+# include "images/image-walk.h"
+ }
+ return 1;
+}
+
+int
+image_conv(struct image_context *ctx, struct image *dest, struct image *src, struct image_conv_options *opt)
+{
+ ASSERT(dest->cols == src->cols && dest->rows == src->rows);
+ if (!((dest->flags ^ src->flags) & IMAGE_COLOR_SPACE))
+ {
+ if (!(src->flags & IMAGE_ALPHA) || (dest->flags & IMAGE_ALPHA))
+ image_conv_copy(dest, src);
+ else if (unlikely(!image_conv_apply_alpha_to(ctx, dest, src, opt)))
+ return 0;
+ }
+ else
+ {
+ if (!(src->flags & IMAGE_ALPHA))
+ {
+ if (unlikely(!image_conv_color_space(ctx, dest, src, opt)))
+ goto error;
+ if ((dest->flags & IMAGE_ALPHA) && (opt->flags & IMAGE_CONV_FILL_ALPHA))
+ image_conv_fill_alpha(dest);
+ }
+ else
+ {
+ if (dest->flags & IMAGE_ALPHA)
+ {
+ if (dest->channels <= src->channels)
+ {
+ if (unlikely(!image_conv_color_space(ctx, dest, src, opt)))
+ goto error;
+ if (opt->flags & IMAGE_CONV_COPY_ALPHA)
+ image_conv_copy_alpha(dest, src);
+ else if (opt->flags & IMAGE_CONV_FILL_ALPHA)
+ image_conv_fill_alpha(dest);
+ }
+ else
+ {
+ if (opt->flags & IMAGE_CONV_COPY_ALPHA)
+ image_conv_copy_alpha(dest, src);
+ else
+ image_conv_fill_alpha(dest);
+ if (unlikely(!image_conv_color_space(ctx, dest, src, opt)))
+ goto error;
+ }
+ }
+ else
+ {
+ if (dest->channels <= src->channels)
+ {
+ if (unlikely(!image_conv_color_space(ctx, dest, src, opt)))
+ goto error;
+ if (unlikely(!image_conv_apply_alpha_from(ctx, dest, src, opt)))
+ return 0;
+ }
+ else
+ {
+ if (unlikely(!image_conv_apply_alpha_to(ctx, dest, src, opt)))
+ return 0;
+ if (unlikely(!image_conv_color_space(ctx, dest, dest, opt)))
+ goto error;
+ }
+ }
+ }
+ }
+ return 1;
+error:
+ IMAGE_ERROR(ctx, IMAGE_ERROR_INVALID_PIXEL_FORMAT, "Image conversion not supported for such pixel formats");
+ return 0;