Fix some premultiplied problems.
authorRobin Watts <[email protected]>
Wed, 25 Aug 2021 12:08:52 +0000 (13:08 +0100)
committerRobin Watts <[email protected]>
Wed, 25 Aug 2021 14:54:55 +0000 (15:54 +0100)
First, fix a typo in lcms2 that caused a non-premultiplied
transform to be used when a premultiplied one was required.

Next, allow for the limitations in lcms2's premultiplication
support by reinstating the 'do it by steam' routines that
undo/redo premultiplication for some cases.

LCMS2 can handle premultiplication in the cases where the
number of spot channels does not change. In rare cases (such
as folding spots down to process colors after overprint simulation)
we can need to operate not in this way.

We now make use of the faster lcms2 processing where possible,
and do it ourselves otherwise.

source/fitz/color-lcms.c
source/fitz/colorspace.c
thirdparty/lcms2

index d46cad4f297082d95336e88f8c0616af74c03d14..2a454a9d43ed24e55cfc3a8ba7405f6617908907 100644 (file)
 #include "lcms2.h"
 #endif
 
+static void fz_premultiply_row(fz_context *ctx, int n, int c, int w, unsigned char *s)
+{
+       unsigned char a;
+       int k;
+       int n1 = n-1;
+       for (; w > 0; w--)
+       {
+               a = s[n1];
+               if (a == 0)
+                       memset(s, 0, c);
+               else if (a != 255)
+                       for (k = 0; k < c; k++)
+                               s[k] = fz_mul255(s[k], a);
+               s += n;
+       }
+}
+
+static void fz_premultiply_row_0or1(fz_context *ctx, int n, int c, int w, unsigned char *s)
+{
+       unsigned char a;
+       int n1 = n-1;
+       for (; w > 0; w--)
+       {
+               a = s[n1];
+               if (a == 0)
+                       memset(s, 0, c);
+               s += n;
+       }
+}
+
+/* Returns 0 for all the alphas being 0, 1 for them being 0 or 255, 2 otherwise. */
+static int fz_unmultiply_row(fz_context *ctx, int n, int c, int w, unsigned char *s, const unsigned char *in)
+{
+       int a, inva;
+       int k;
+       int n1 = n-1;
+       for (; w > 0; w--)
+       {
+               a = in[n1];
+               if (a != 0)
+                       goto nonzero;
+               for (k = 0; k < c; k++)
+                       s[k] = 0;
+               for (;k < n1; k++)
+                       s[k] = in[k];
+               s[n1] = 0;
+               s += n;
+               in += n;
+       }
+       return 0;
+       for (; w > 0; w--)
+       {
+               a = in[n1];
+nonzero:
+               if (a != 0 && a != 255)
+                       goto varying;
+               k = 0;
+               if (a == 0)
+                       for (; k < c; k++)
+                               s[k] = 0;
+               for (;k < n; k++)
+                       s[k] = in[k];
+               s += n;
+               in += n;
+       }
+       return 1;
+       for (; w > 0; w--)
+       {
+               a = in[n1];
+varying:
+               if (a == 0)
+               {
+                       for (k = 0; k < c; k++)
+                               s[k] = 0;
+                       for (;k < n1; k++)
+                               s[k] = in[k];
+                       s[k] = 0;
+               }
+               else if (a == 255)
+               {
+                       memcpy(s, in, n);
+               }
+               else
+               {
+                       inva = 255 * 256 / a;
+                       for (k = 0; k < c; k++)
+                               s[k] = (in[k] * inva) >> 8;
+                       for (;k < n1; k++)
+                               s[k] = in[k];
+                       s[n1] = a;
+               }
+               s += n;
+               in += n;
+       }
+       return 2;
+}
+
 struct fz_icc_link
 {
        fz_storable storable;
@@ -325,10 +422,11 @@ fz_icc_transform_pixmap(fz_context *ctx, fz_icc_link *link, const fz_pixmap *src
 {
        GLOINIT
        int cmm_num_src, cmm_num_dst, cmm_extras;
-       unsigned char *inputpos, *outputpos;
+       unsigned char *inputpos, *outputpos, *buffer;
        int ss = src->stride;
        int ds = dst->stride;
        int sw = src->w;
+       int dw = dst->w;
        int sn = src->n;
        int dn = dst->n;
        int sa = src->alpha;
@@ -351,12 +449,40 @@ fz_icc_transform_pixmap(fz_context *ctx, fz_icc_link *link, const fz_pixmap *src
 
        inputpos = src->samples;
        outputpos = dst->samples;
-       for (; h > 0; h--)
+       /* LCMS can only handle premultiplied data if the number of 'extra'
+        * channels is the same. If not, do it by steam. */
+       if (sa && cmm_extras != (int)T_EXTRA(dst_format))
        {
-               cmsDoTransform(GLO link->handle, inputpos, outputpos, sw);
-               inputpos += ss;
-               outputpos += ds;
+               buffer = fz_malloc(ctx, ss);
+               for (; h > 0; h--)
+               {
+                       int mult = fz_unmultiply_row(ctx, sn, sc, sw, buffer, inputpos);
+                       if (mult == 0)
+                       {
+                               /* Solid transparent row. No point in doing the transform
+                                * because it will premultiplied back to 0. */
+                               memset(outputpos, 0, ds);
+                       }
+                       else
+                       {
+                               cmsDoTransform(GLO link->handle, buffer, outputpos, sw);
+                               if (mult == 1)
+                                       fz_premultiply_row_0or1(ctx, dn, dc, dw, outputpos);
+                               else if (mult == 2)
+                                       fz_premultiply_row(ctx, dn, dc, dw, outputpos);
+                       }
+                       inputpos += ss;
+                       outputpos += ds;
+               }
+               fz_free(ctx, buffer);
        }
+       else
+               for (; h > 0; h--)
+               {
+                       cmsDoTransform(GLO link->handle, inputpos, outputpos, sw);
+                       inputpos += ss;
+                       outputpos += ds;
+               }
 }
 
 #endif
index 33b5e229b3e6cc7f081a1a335a353113dfb85ef6..7ee401974fdb723735b9adaac31f8839ba52b681 100644 (file)
@@ -1458,7 +1458,12 @@ fz_convert_pixmap_samples(fz_context *ctx, const fz_pixmap *src, fz_pixmap *dst,
                        {
                                int sx = src->s + src->alpha;
                                int dx = dst->s + dst->alpha;
-                               link = fz_find_icc_link(ctx, ss, sx, ds, dx, prf, params, 0, copy_spots, src->alpha);
+                               /* If we have alpha, we're preserving spots and we have the same number
+                                * of 'extra' (non process, spots+alpha) channels (i.e. sx == dx), then
+                                * we get lcms2 to do the premultiplication handling for us. If not,
+                                * fz_icc_transform_pixmap will have to do it by steam. */
+                               int premult = src->alpha && (sx == dx) && copy_spots;
+                               link = fz_find_icc_link(ctx, ss, sx, ds, dx, prf, params, 0, copy_spots, premult);
                                fz_icc_transform_pixmap(ctx, link, src, dst, copy_spots);
                        }
                        fz_catch(ctx)
index 690605973933770ac31a67b814e747b0bc6f2bd3..558b336e330e7816c22df900b8100c029b16b0bc 160000 (submodule)
@@ -1 +1 @@
-Subproject commit 690605973933770ac31a67b814e747b0bc6f2bd3
+Subproject commit 558b336e330e7816c22df900b8100c029b16b0bc