summaryrefslogtreecommitdiff
path: root/gs/lcms/src/cmsxform.h
blob: bf958cd91411f1d925bf181c15da0abbd9fd1a8c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
//
// Little cms

// Chameleonic header file to instantiate different versions of the
// transform routines.
//
// As a bare minimum the following must be defined on entry:
//   FUNCTION_NAME             the name of the function
//
// In addition, a range of other symbols can be optionally defined on entry
// to make the generated code more efficient. All these symbols (and
// FUNCTION_NAME) will be automatically undefined at the end of the file so
// that repeated #includes of this file are made simple.
//
// If caching is wanted, define CACHED. (INBYTES will only have an effect
// if CACHED is defined).
//
// To reduce the amount of surplus memory checking done, set INBYTES to the
// number of bytes in an unpacked data chunk.
//
// If you know the code to be used to unpack (or pack, or both) data to/from
// the simple 16 bit transform input/output format, then you can choose
// to this directly by defining UNPACK/PACK macros as follows:
//   UNPACK(T,TO,FROM) (Opt)   code to unpack input data (T = Transform
//                             TO = buffer to unpack into, FROM = data)
//   PACK(T,FROM,TO)   (Opt)   code to pack transformed input data
//                             (T = Transform, FROM = transformed data,
//                             TO = output buffer to pack into)
//
// As an alternative to the above, if you know the function name that would
// be called, supply that in UNPACKFN and PACKFN and inlining compilers
// should hopefully do the hard work for you.
//   UNPACKFN          (Opt)   function to unpack input data
//   PACKFN            (Opt)   function to pack input data
//
// Finally, GAMUTCHECK can be predefined if a gamut check needs to be done.

#ifndef CACHED
#undef INBYTES
#endif

#ifdef INBYTES
#if INBYTES <= 4
#define COMPARE(A,B) (*((int *)(A)) != *((int *)(B)))
#elif INBYTES <= 8
#define COMPARE(A,B) (*((LCMSULONGLONG *)(A)) != *((LCMSULONGLONG *)(B)))
/* Or we could use #define COMPARE(A,B) ((((int *)(A))[0] != ((int *)(B))[0]) || (((int *)(A))[1] != ((int *)(B))[1])) */
#else
#undef INBYTES
#endif
#endif

#ifndef COMPARE
#define COMPARE(A,B) memcmp((A),(B), (sizeof(WORD)*MAXCHANNELS))
#endif

#ifndef UNPACK
#ifdef UNPACKFN
#define UNPACK(T,TO,FROM) \
                  do { (FROM) = UNPACKFN((T),(TO),(FROM)); } while (0)
#else
#define UNPACK(T,TO,FROM) \
                  do { (FROM) = (T)->FromInput((T),(TO),(FROM)); } while (0)
#endif
#endif

#ifndef PACK
#ifdef PACKFN
#define PACK(T,FROM,TO) \
                  do { (TO) = PACKFN((T),(FROM),(TO)); } while (0)
#else
#define PACK(T,FROM,TO) \
                  do { (TO) = (T)->ToOutput((T),(FROM),(TO)); } while (0)
#endif
#endif

static
void FUNCTION_NAME(_LPcmsTRANSFORM p,
                   LPVOID in,
                   LPVOID out,
                   unsigned int n)
{
    register LPBYTE accum;
    register LPBYTE output;
    LCMSULONGLONG wIn[sizeof(WORD)*MAXCHANNELS*2/sizeof(LCMSULONGLONG)];
#define wIn0 (&((WORD *)wIn)[0])
#define wIn1 (&((WORD *)wIn)[MAXCHANNELS])
    WORD *currIn;
#ifdef CACHED
    WORD *prevIn;
#ifdef INBYTES
    int cacheValid = 1;
#endif /* INBYTES */
#endif /* CACHED */
    WORD wOut[MAXCHANNELS];
    LPLUT devLink = p->DeviceLink;
    accum  = (LPBYTE) in;
    output = (LPBYTE) out;

    if (n == 0)
        return;

#ifdef CACHED
    // Empty buffers for quick memcmp
    ZeroMemory(wIn1, sizeof(WORD) * MAXCHANNELS);

    LCMS_READ_LOCK(&p ->rwlock);
    CopyMemory(wIn0, p ->CacheIn,  sizeof(WORD) * MAXCHANNELS);
    CopyMemory(wOut, p ->CacheOut, sizeof(WORD) * MAXCHANNELS);
    LCMS_UNLOCK(&p ->rwlock);

#ifdef INBYTES
    /* We only check the first 'inBytes' of the cached version in the loop,
     * and the cached version might differ after that point. So check the
     * rest of the bytes now. */
    if (memcmp(((LPBYTE)wIn0)+INBYTES, wIn1,
               sizeof(WORD) * MAXCHANNELS - INBYTES) != 0)
    {
        cacheValid = 0;
        /* Zero the rest of the cached bytes to ensure what we copy back is
         * correct. */
        ZeroMemory(((LPBYTE)wIn0)+INBYTES,
                   sizeof(WORD) * MAXCHANNELS - INBYTES);
    }
#endif /* INBYTES */

    prevIn = wIn0;
#endif /* CACHED */
    currIn = wIn1;

    // Try to speedup things on plain devicelinks
#ifdef INBYTES
    UNPACK(p,currIn,accum);
#endif
#ifndef GAMUTCHECK
    if (devLink->wFlags == LUT_HAS3DGRID) {
#ifdef INBYTES
        if (cacheValid)
            goto enterCacheValid3d;
        else
            goto enterCacheInvalid3d;
#endif
        do {
            UNPACK(p,currIn,accum);
#ifdef INBYTES
          enterCacheValid3d:
#endif /* INBYTES */
#ifdef CACHED
            if (COMPARE(currIn, prevIn))
#endif /* CACHED */
            {
#ifdef INBYTES
              enterCacheInvalid3d:
#endif /* INBYTES */
                devLink->CLut16params.Interp3D(currIn, wOut,
                                               devLink -> T,
                                               &devLink -> CLut16params);
#ifdef CACHED
                {WORD *tmp = currIn; currIn = prevIn; prevIn = tmp;}
#endif /* CACHED */
            }
            PACK(p,wOut,output);
        } while (--n);
    }
    else
#endif /* GAMUTCHECK */
    {
#ifdef INBYTES
        if (cacheValid)
            goto enterCacheValid;
        else
            goto enterCacheInvalid;
#endif /* INBYTES */
        do {
            UNPACK(p,currIn,accum);
#ifdef INBYTES
          enterCacheValid:
#endif
#ifdef CACHED
            if (COMPARE(currIn, prevIn))
#endif /* CACHED */
            {
#ifdef INBYTES
              enterCacheInvalid:
#endif /* INBYTES */
#ifdef GAMUTCHECK
                {
                    WORD wOutOfGamut;

                    cmsEvalLUT(p->GamutCheck, currIn, &wOutOfGamut);
                    if (wOutOfGamut >= 1) {
                        ZeroMemory(wOut, sizeof(WORD) * MAXCHANNELS);

                        wOut[0] = _cmsAlarmR;
                        wOut[1] = _cmsAlarmG;
                        wOut[2] = _cmsAlarmB;
                    } else {
#endif /* GAMUTCHECK */
                        cmsEvalLUT(devLink, currIn, wOut);
#ifdef GAMUTCHECK
                    }
                }
#endif /* GAMUTCHECK */
#ifdef CACHED
                {WORD *tmp = currIn; currIn = prevIn; prevIn = tmp;}
#endif /* CACHED */
            }
            PACK(p,wOut,output);
        } while (--n);
    }
#ifdef CACHED
    LCMS_WRITE_LOCK(&p ->rwlock);
    CopyMemory(p->CacheIn,  prevIn, sizeof(WORD) * MAXCHANNELS);
    CopyMemory(p->CacheOut, wOut,   sizeof(WORD) * MAXCHANNELS);
    LCMS_UNLOCK(&p ->rwlock);
#endif /* CACHED */
}

#undef wIn0
#undef wIn1

#undef FUNCTION_NAME
#undef COMPARE
#undef INBYTES
#undef UNPACK
#undef PACK
#undef UNPACKFN
#undef PACKFN
#undef GAMUTCHECK
#undef CACHED