+/* combine a line of image data with an output line, both the input
+ and output lines include an alpha channel.
+
+ Both input and output lines have I<channels> of data, channels
+ should be either 2 or 4.
+
+ This variant does not modify the output alpha channel.
+*/
+
+static void
+IM_SUFFIX(combine_line_alpha_na)(IM_COLOR *out, IM_COLOR const *in,
+ int channels, i_img_dim count) {
+ int ch;
+ int alpha_channel = channels - 1;
+
+ while (count) {
+ IM_WORK_T src_alpha = in->channel[alpha_channel];
+
+ if (src_alpha == IM_SAMPLE_MAX)
+ *out = *in;
+ else if (src_alpha) {
+ IM_WORK_T remains = IM_SAMPLE_MAX - src_alpha;
+ IM_WORK_T orig_alpha = out->channel[alpha_channel];
+ IM_WORK_T dest_alpha = src_alpha + (remains * orig_alpha) / IM_SAMPLE_MAX;
+
+ for (ch = 0; ch < alpha_channel; ++ch) {
+ out->channel[ch] = ( src_alpha * in->channel[ch]
+ + remains * out->channel[ch] * orig_alpha / IM_SAMPLE_MAX
+ ) / dest_alpha;
+ }
+ }
+
+ ++out;
+ ++in;
+ --count;
+ }
+}
+
+static void
+IM_SUFFIX(combine_line)(IM_COLOR *out, IM_COLOR const *in, int channels, i_img_dim count) {
+ if (channels == 2 || channels == 4)
+ IM_SUFFIX(combine_line_alpha)(out, in, channels, count);
+ else
+ IM_SUFFIX(combine_line_noalpha)(out, in, channels, count);
+}
+
+static void
+IM_SUFFIX(combine_line_na)(IM_COLOR *out, IM_COLOR const *in, int channels, i_img_dim count) {
+ if (channels == 2 || channels == 4)
+ IM_SUFFIX(combine_line_alpha_na)(out, in, channels, count);
+ else
+ IM_SUFFIX(combine_line_noalpha)(out, in, channels, count);
+}
+
+static void IM_SUFFIX(combine_alphablend)(IM_COLOR *, IM_COLOR *, int, i_img_dim);
+static void IM_SUFFIX(combine_mult)(IM_COLOR *, IM_COLOR *, int, i_img_dim);
+static void IM_SUFFIX(combine_dissolve)(IM_COLOR *, IM_COLOR *, int, i_img_dim);
+static void IM_SUFFIX(combine_add)(IM_COLOR *, IM_COLOR *, int, i_img_dim);
+static void IM_SUFFIX(combine_subtract)(IM_COLOR *, IM_COLOR *, int, i_img_dim);
+static void IM_SUFFIX(combine_diff)(IM_COLOR *, IM_COLOR *, int, i_img_dim);
+static void IM_SUFFIX(combine_darken)(IM_COLOR *, IM_COLOR *, int, i_img_dim);
+static void IM_SUFFIX(combine_lighten)(IM_COLOR *, IM_COLOR *, int, i_img_dim);
+static void IM_SUFFIX(combine_hue)(IM_COLOR *, IM_COLOR *, int, i_img_dim);
+static void IM_SUFFIX(combine_sat)(IM_COLOR *, IM_COLOR *, int, i_img_dim);
+static void IM_SUFFIX(combine_value)(IM_COLOR *, IM_COLOR *, int, i_img_dim);
+static void IM_SUFFIX(combine_color)(IM_COLOR *, IM_COLOR *, int, i_img_dim);
+
+static const IM_FILL_COMBINE_F IM_SUFFIX(combines)[] =
+{
+ NULL,
+ IM_SUFFIX(combine_alphablend),
+ IM_SUFFIX(combine_mult),
+ IM_SUFFIX(combine_dissolve),
+ IM_SUFFIX(combine_add),
+ IM_SUFFIX(combine_subtract),
+ IM_SUFFIX(combine_diff),
+ IM_SUFFIX(combine_lighten),
+ IM_SUFFIX(combine_darken),
+ IM_SUFFIX(combine_hue),
+ IM_SUFFIX(combine_sat),
+ IM_SUFFIX(combine_value),
+ IM_SUFFIX(combine_color)
+};
+
+#/code
+
+/*
+=item i_get_combine(combine, color_func, fcolor_func)
+
+=cut
+*/
+
+void i_get_combine(int combine, i_fill_combine_f *color_func,
+ i_fill_combinef_f *fcolor_func) {
+ if (combine < 0 || combine > sizeof(combines_8) / sizeof(*combines_8))
+ combine = 0;
+
+ *color_func = combines_8[combine];
+ *fcolor_func = combines_double[combine];
+}
+
+#code
+
+/*
+ Three good references for implementing combining modes:
+
+ http://www.w3.org/TR/2004/WD-SVG12-20041027/rendering.html
+ referenced as [svg1.2]
+
+ http://gimp-savvy.com/BOOK/index.html?node55.html
+ ("The Blending Modes", if it changes)
+ referenced as [savvy]
+
+ http://www.pegtop.net/delphi/articles/blendmodes/
+ referenced as [pegtop]
+
+ Where differences exist, I follow the SVG practice, the gimp
+ practice, and lastly pegtop.
+*/
+
+
+static void
+IM_SUFFIX(combine_alphablend)(IM_COLOR *out, IM_COLOR *in, int channels, i_img_dim count) {
+ IM_SUFFIX(combine_line)(out, in, channels, count);
+}
+
+/*
+Dca' = Sca.Dca + Sca.(1 - Da) + Dca.(1 - Sa)
+Da' = Sa.Da + Sa.(1 - Da) + Da.(1 - Sa)
+ = Sa + Da - Sa.Da
+
+When Da=1
+
+Dc' = Sc.Sa.Dc + Dc.(1 - Sa)
+ */
+static void
+IM_SUFFIX(combine_mult)(IM_COLOR *out, IM_COLOR *in, int channels, i_img_dim count) {
+ int ch;
+ IM_COLOR *inp = in;
+ IM_COLOR *outp = out;
+ i_img_dim work_count = count;
+ int color_channels = i_color_channels(channels);
+
+ if (i_has_alpha(channels)) {
+ while (work_count--) {
+ IM_WORK_T orig_alpha = outp->channel[color_channels];
+ IM_WORK_T src_alpha = inp->channel[color_channels];
+
+ if (src_alpha) {
+ IM_WORK_T dest_alpha = src_alpha + orig_alpha
+ - (src_alpha * orig_alpha) / IM_SAMPLE_MAX;
+
+ for (ch = 0; ch < color_channels; ++ch) {
+ outp->channel[ch] =
+ (inp->channel[ch] * src_alpha * outp->channel[ch] / IM_SAMPLE_MAX
+ * orig_alpha
+ + inp->channel[ch] * src_alpha * (IM_SAMPLE_MAX - orig_alpha)
+ + outp->channel[ch] * orig_alpha * (IM_SAMPLE_MAX - src_alpha))
+ / IM_SAMPLE_MAX / dest_alpha;
+ }
+ outp->channel[color_channels] = dest_alpha;
+ }
+ ++outp;
+ ++inp;
+ }
+ }
+ else {
+ while (work_count--) {
+ IM_WORK_T src_alpha = inp->channel[color_channels];
+ IM_WORK_T remains = IM_SAMPLE_MAX - src_alpha;
+
+ if (src_alpha) {
+ for (ch = 0; ch < color_channels; ++ch) {
+ outp->channel[ch] =
+ (src_alpha * inp->channel[ch] * outp->channel[ch] / IM_SAMPLE_MAX
+ + outp->channel[ch] * remains) / IM_SAMPLE_MAX;
+ }
+ }
+ ++outp;
+ ++inp;
+ }
+ }
+}
+
+static void
+IM_SUFFIX(combine_dissolve)(IM_COLOR *out, IM_COLOR *in, int channels, i_img_dim count) {
+ int color_channels = i_color_channels(channels);
+ int ch;
+
+ if (i_has_alpha(channels)) {
+ while (count--) {
+ if (in->channel[channels-1] > rand() * ((double)IM_SAMPLE_MAX / RAND_MAX)) {
+ for (ch = 0; ch < color_channels; ++ch) {
+ out->channel[ch] = in->channel[ch];
+ }
+ out->channel[color_channels] = IM_SAMPLE_MAX;
+ }
+ ++out;
+ ++in;
+ }
+ }
+ else {
+ while (count--) {
+ if (in->channel[channels] > rand() * ((double)IM_SAMPLE_MAX / RAND_MAX)) {
+ for (ch = 0; ch < color_channels; ++ch) {
+ out->channel[ch] = in->channel[ch];
+ }
+ }
+ ++out;
+ ++in;
+ }
+ }
+}
+
+/*
+Dca' = Sca.Da + Dca.Sa + Sca.(1 - Da) + Dca.(1 - Sa)
+ = Sca + Dca
+Da' = Sa.Da + Da.Sa + Sa.(1 - Da) + Da.(1 - Sa)
+ = Sa + Da
+*/
+
+static void
+IM_SUFFIX(combine_add)(IM_COLOR *out, IM_COLOR *in, int channels, i_img_dim count) {
+ int ch;
+ int color_channels = i_color_channels(channels);
+ i_img_dim work_count = count;
+ IM_COLOR *inp = in;
+ IM_COLOR *outp = out;
+
+ if (i_has_alpha(channels)) {
+ while (work_count--) {
+ IM_WORK_T src_alpha = inp->channel[color_channels];
+ if (src_alpha) {
+ IM_WORK_T orig_alpha = outp->channel[color_channels];
+ IM_WORK_T dest_alpha = src_alpha + orig_alpha;
+ if (dest_alpha > IM_SAMPLE_MAX)
+ dest_alpha = IM_SAMPLE_MAX;
+ for (ch = 0; ch < color_channels; ++ch) {
+ IM_WORK_T total = (outp->channel[ch] * orig_alpha + inp->channel[ch] * src_alpha) / dest_alpha;
+ if (total > IM_SAMPLE_MAX)
+ total = IM_SAMPLE_MAX;
+ outp->channel[ch] = total;
+ }
+ outp->channel[color_channels] = dest_alpha;
+ }
+
+ ++outp;
+ ++inp;
+ }
+ }
+ else {
+ while (work_count--) {
+ IM_WORK_T src_alpha = inp->channel[color_channels];
+ if (src_alpha) {
+ for (ch = 0; ch < color_channels; ++ch) {
+ IM_WORK_T total = outp->channel[ch] + inp->channel[ch] * src_alpha / IM_SAMPLE_MAX;
+ if (total > IM_SAMPLE_MAX)
+ total = IM_SAMPLE_MAX;
+ outp->channel[ch] = total;
+ }
+ }
+
+ ++outp;
+ ++inp;
+ }
+ }
+}
+
+/*
+ [pegtop] documents this as max(A+B-256, 0) while [savvy] documents
+ it as max(A-B, 0). [svg1.2] doesn't cover it.
+
+ [savvy] doesn't document how it works with an alpha channel. GIMP
+ actually seems to calculate the final value then use the alpha
+ channel to apply that to the target.
+ */
+static void
+IM_SUFFIX(combine_subtract)(IM_COLOR *out, IM_COLOR *in, int channels, i_img_dim count) {
+ int ch;
+ IM_COLOR const *inp = in;
+ IM_COLOR *outp = out;
+ i_img_dim work_count = count;
+ int color_channels = i_color_channels(channels);
+
+ if (i_has_alpha(channels)) {
+ while (work_count--) {
+ IM_WORK_T src_alpha = inp->channel[color_channels];
+ if (src_alpha) {
+ IM_WORK_T orig_alpha = outp->channel[color_channels];
+ IM_WORK_T dest_alpha = src_alpha + orig_alpha;
+ if (dest_alpha > IM_SAMPLE_MAX)
+ dest_alpha = IM_SAMPLE_MAX;
+ for (ch = 0; ch < color_channels; ++ch) {
+ IM_WORK_T total =
+ (outp->channel[ch] * orig_alpha - inp->channel[ch] * src_alpha)
+ / dest_alpha;
+ if (total < 0)
+ total = 0;
+ outp->channel[ch] = total;
+ }
+ outp->channel[color_channels] = dest_alpha;
+ }
+ ++outp;
+ ++inp;
+ }
+ }
+ else {
+ while (work_count--) {
+ IM_WORK_T src_alpha = inp->channel[color_channels];
+ if (src_alpha) {
+ for (ch = 0; ch < color_channels; ++ch) {
+ IM_WORK_T total = outp->channel[ch] - inp->channel[ch] * src_alpha / IM_SAMPLE_MAX;
+ if (total < 0)
+ total = 0;
+ outp->channel[ch] = total;
+ }
+ }
+ ++outp;
+ ++inp;
+ }
+ }
+}
+
+#ifdef IM_EIGHT_BIT
+#define IM_abs(x) abs(x)
+#else
+#define IM_abs(x) fabs(x)
+#endif
+
+/*
+Dca' = abs(Dca.Sa - Sca.Da) + Sca.(1 - Da) + Dca.(1 - Sa)
+ = Sca + Dca - 2.min(Sca.Da, Dca.Sa)
+Da' = Sa + Da - Sa.Da
+*/
+static void
+IM_SUFFIX(combine_diff)(IM_COLOR *out, IM_COLOR *in, int channels, i_img_dim count) {
+ int ch;
+ IM_COLOR const *inp = in;
+ IM_COLOR *outp = out;
+ i_img_dim work_count = count;
+ int color_channels = i_color_channels(channels);
+
+ if (i_has_alpha(channels)) {
+ while (work_count--) {
+ IM_WORK_T src_alpha = inp->channel[color_channels];
+ if (src_alpha) {
+ IM_WORK_T orig_alpha = outp->channel[color_channels];
+ IM_WORK_T dest_alpha = src_alpha + orig_alpha
+ - src_alpha * orig_alpha / IM_SAMPLE_MAX;
+ for (ch = 0; ch < color_channels; ++ch) {
+ IM_WORK_T src = inp->channel[ch] * src_alpha;
+ IM_WORK_T orig = outp->channel[ch] * orig_alpha;
+ IM_WORK_T src_da = src * orig_alpha;
+ IM_WORK_T dest_sa = orig * src_alpha;
+ IM_WORK_T diff = src_da < dest_sa ? src_da : dest_sa;
+ outp->channel[ch] = (src + orig - 2 * diff / IM_SAMPLE_MAX) / dest_alpha;
+ }
+ outp->channel[color_channels] = dest_alpha;
+ }
+ ++inp;
+ ++outp;
+ }
+ }
+ else {
+ while (work_count--) {
+ IM_WORK_T src_alpha = inp->channel[color_channels];
+ if (src_alpha) {
+ for (ch = 0; ch < color_channels; ++ch) {
+ IM_WORK_T src = inp->channel[ch] * src_alpha;
+ IM_WORK_T orig = outp->channel[ch] * IM_SAMPLE_MAX;
+ IM_WORK_T src_da = src * IM_SAMPLE_MAX;
+ IM_WORK_T dest_sa = orig * src_alpha;
+ IM_WORK_T diff = src_da < dest_sa ? src_da : dest_sa;
+ outp->channel[ch] = (src + orig - 2 * diff / IM_SAMPLE_MAX) / IM_SAMPLE_MAX;
+ }
+ }
+ ++inp;
+ ++outp;
+ }
+ }
+}
+
+#undef IM_abs
+
+/*
+ Dca' = min(Sca.Da, Dca.Sa) + Sca.(1 - Da) + Dca(1 - Sa)
+ Da' = Sa + Da - Sa.Da
+
+ To hoist some code:
+
+ Dca' = min(Sc.Sa.Da, Dc.Da.Sa) + Sca - Sca.Da + Dca - Dca.Sa
+ = Sa.Da.min(Sc, Dc) + Sca - Sca.Da + Dca - Dca.Sa
+
+ When Da=1:
+
+ Dca' = min(Sca.1, Dc.1.Sa) + Sca.(1 - 1) + Dc.1(1 - Sa)
+ = min(Sca, Dc.Sa) + Dc(1-Sa)
+ = Sa.min(Sc, Dc) + Dc - Dc.Sa
+ Da' = Sa + 1 - Sa.1
+ = 1
+ */
+static void
+IM_SUFFIX(combine_darken)(IM_COLOR *out, IM_COLOR *in, int channels,
+ i_img_dim count) {
+ int ch;
+ IM_COLOR const *inp = in;
+ IM_COLOR *outp = out;
+ i_img_dim work_count = count;
+ int color_channels = i_color_channels(channels);
+
+ if (i_has_alpha(channels)) {
+ while (work_count--) {
+ IM_WORK_T src_alpha = inp->channel[color_channels];
+
+ if (src_alpha) {
+ IM_WORK_T orig_alpha = outp->channel[color_channels];
+ IM_WORK_T dest_alpha = src_alpha + orig_alpha
+ - src_alpha * orig_alpha / IM_SAMPLE_MAX;
+ for (ch = 0; ch < color_channels; ++ch) {
+ IM_WORK_T Sca = inp->channel[ch] * src_alpha;
+ IM_WORK_T Dca = outp->channel[ch] * orig_alpha;
+ IM_WORK_T ScaDa = Sca * orig_alpha;
+ IM_WORK_T DcaSa = Dca * src_alpha;
+ IM_WORK_T minc = ScaDa < DcaSa ? ScaDa : DcaSa;
+ outp->channel[ch] =
+ (
+ minc + (Sca + Dca) * IM_SAMPLE_MAX
+ - ScaDa - DcaSa
+ ) / (IM_SAMPLE_MAX * dest_alpha);
+ }
+ outp->channel[color_channels] = dest_alpha;
+ }
+ ++outp;
+ ++inp;
+ }
+ }
+ else {
+ while (work_count--) {
+ IM_WORK_T src_alpha = inp->channel[color_channels];
+
+ if (src_alpha) {
+ for (ch = 0; ch < color_channels; ++ch) {
+ IM_WORK_T minc = outp->channel[ch] < inp->channel[ch]
+ ? outp->channel[ch] : inp->channel[ch];
+ outp->channel[ch] =
+ (
+ src_alpha * minc +
+ outp->channel[ch] * ( IM_SAMPLE_MAX - src_alpha )
+ ) / IM_SAMPLE_MAX;
+ }
+ }
+ ++outp;
+ ++inp;
+ }
+ }
+}
+
+static void
+IM_SUFFIX(combine_lighten)(IM_COLOR *out, IM_COLOR *in, int channels, i_img_dim count) {
+ int ch;
+ IM_COLOR const *inp = in;
+ IM_COLOR *outp = out;
+ i_img_dim work_count = count;
+ int color_channels = i_color_channels(channels);
+
+ if (i_has_alpha(channels)) {
+ while (work_count--) {
+ IM_WORK_T src_alpha = inp->channel[color_channels];
+
+ if (src_alpha) {
+ IM_WORK_T orig_alpha = outp->channel[color_channels];
+ IM_WORK_T dest_alpha = src_alpha + orig_alpha
+ - src_alpha * orig_alpha / IM_SAMPLE_MAX;
+ for (ch = 0; ch < color_channels; ++ch) {
+ IM_WORK_T Sca = inp->channel[ch] * src_alpha;
+ IM_WORK_T Dca = outp->channel[ch] * orig_alpha;
+ IM_WORK_T ScaDa = Sca * orig_alpha;
+ IM_WORK_T DcaSa = Dca * src_alpha;
+ IM_WORK_T maxc = ScaDa > DcaSa ? ScaDa : DcaSa;
+ outp->channel[ch] =
+ (
+ maxc + (Sca + Dca) * IM_SAMPLE_MAX
+ - ScaDa - DcaSa
+ ) / (IM_SAMPLE_MAX * dest_alpha);
+ }
+ outp->channel[color_channels] = dest_alpha;
+ }
+ ++outp;
+ ++inp;
+ }
+ }
+ else {
+ while (work_count--) {
+ IM_WORK_T src_alpha = inp->channel[color_channels];
+
+ if (src_alpha) {
+ for (ch = 0; ch < color_channels; ++ch) {
+ IM_WORK_T maxc = outp->channel[ch] > inp->channel[ch]
+ ? outp->channel[ch] : inp->channel[ch];
+ outp->channel[ch] =
+ (
+ src_alpha * maxc +
+ outp->channel[ch] * ( IM_SAMPLE_MAX - src_alpha )
+ ) / IM_SAMPLE_MAX;
+ }
+ }
+ ++outp;
+ ++inp;
+ }
+ }
+}
+
+#if IM_EIGHT_BIT
+#define IM_RGB_TO_HSV i_rgb_to_hsv
+#define IM_HSV_TO_RGB i_hsv_to_rgb
+#else
+#define IM_RGB_TO_HSV i_rgb_to_hsvf
+#define IM_HSV_TO_RGB i_hsv_to_rgbf
+#endif
+
+static void
+IM_SUFFIX(combine_hue)(IM_COLOR *out, IM_COLOR *in, int channels, i_img_dim count) {
+ if (channels > 2) {
+ IM_COLOR *inp = in;
+ IM_COLOR const *outp = out;
+ i_img_dim work_count = count;
+
+ if (i_has_alpha(channels)) {
+ while (work_count--) {
+ IM_COLOR c = *inp;
+ IM_RGB_TO_HSV(&c);
+ /* only transfer hue if there's saturation */
+ if (c.channel[1] && inp->channel[3] && outp->channel[3]) {
+ *inp = *outp;
+ IM_RGB_TO_HSV(inp);
+ /* and no point in setting the target hue if the target has no sat */
+ if (inp->channel[1]) {
+ inp->channel[0] = c.channel[0];
+ IM_HSV_TO_RGB(inp);
+ inp->channel[3] = c.channel[3];
+ }
+ else {
+ inp->channel[3] = 0;
+ }
+ }
+ else {
+ inp->channel[3] = 0;
+ }
+
+ ++outp;
+ ++inp;
+ }
+ }
+ else {
+ while (work_count--) {
+ IM_COLOR c = *inp;
+ IM_RGB_TO_HSV(&c);
+ /* only transfer hue if there's saturation */
+ if (c.channel[1] && inp->channel[3]) {
+ *inp = *outp;
+ IM_RGB_TO_HSV(inp);
+ /* and no point in setting the target hue if the target has no sat */
+ if (inp->channel[1]) {
+ inp->channel[0] = c.channel[0];
+ IM_HSV_TO_RGB(inp);
+ inp->channel[3] = c.channel[3];
+ }
+ }
+ else {
+ inp->channel[3] = 0;
+ }
+
+ ++outp;
+ ++inp;
+ }
+ }
+
+ IM_SUFFIX(combine_line_na)(out, in, channels, count);
+ }
+}
+
+static void
+IM_SUFFIX(combine_sat)(IM_COLOR *out, IM_COLOR *in, int channels, i_img_dim count) {
+ if (channels > 2) {
+ IM_COLOR *inp = in;
+ IM_COLOR const *outp = out;
+ i_img_dim work_count = count;
+
+ while (work_count--) {
+ IM_COLOR c = *inp;
+ *inp = *outp;
+ IM_RGB_TO_HSV(&c);
+ IM_RGB_TO_HSV(inp);
+ inp->channel[1] = c.channel[1];
+ IM_HSV_TO_RGB(inp);
+ inp->channel[3] = c.channel[3];
+ ++outp;
+ ++inp;
+ }
+
+ IM_SUFFIX(combine_line_na)(out, in, channels, count);
+ }
+}
+
+static void
+IM_SUFFIX(combine_value)(IM_COLOR *out, IM_COLOR *in, int channels, i_img_dim count) {
+ if (channels > 2) {
+ IM_COLOR *inp = in;
+ IM_COLOR const *outp = out;
+ i_img_dim work_count = count;
+
+ while (work_count--) {
+ IM_COLOR c = *inp;
+ *inp = *outp;
+ IM_RGB_TO_HSV(&c);
+ IM_RGB_TO_HSV(inp);
+ inp->channel[2] = c.channel[2];
+ IM_HSV_TO_RGB(inp);
+ inp->channel[3] = c.channel[3];
+ ++outp;
+ ++inp;
+ }
+ }
+
+ /* all images have a "value channel" - for greyscale it's the only
+ colour channel */
+ IM_SUFFIX(combine_line_na)(out, in, channels, count);
+}
+
+static void
+IM_SUFFIX(combine_color)(IM_COLOR *out, IM_COLOR *in, int channels, i_img_dim count) {
+ if (channels > 2) {
+ IM_COLOR *inp = in;
+ IM_COLOR const *outp = out;
+ i_img_dim work_count = count;
+
+ while (work_count--) {
+ IM_COLOR c = *inp;
+ *inp = *outp;
+ IM_RGB_TO_HSV(&c);
+ IM_RGB_TO_HSV(inp);
+ inp->channel[0] = c.channel[0];
+ inp->channel[1] = c.channel[1];
+ IM_HSV_TO_RGB(inp);
+ inp->channel[3] = c.channel[3];
+ ++outp;
+ ++inp;
+ }
+
+ IM_SUFFIX(combine_line_na)(out, in, channels, count);
+ }
+}
+
+#undef IM_RGB_TO_HSV
+#undef IM_HSV_TO_RGB
+