blob: c70829989b246826ccb72e1d9085f6c66c51ab81 [file] [log] [blame]
J. Duke319a3b92007-12-01 00:00:00 +00001/*
2 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
3 *
4 * This code is free software; you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License version 2 only, as
6 * published by the Free Software Foundation. Sun designates this
7 * particular file as subject to the "Classpath" exception as provided
8 * by Sun in the LICENSE file that accompanied this code.
9 *
10 * This code is distributed in the hope that it will be useful, but WITHOUT
11 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
13 * version 2 for more details (a copy is included in the LICENSE file that
14 * accompanied this code).
15 *
16 * You should have received a copy of the GNU General Public License version
17 * 2 along with this work; if not, write to the Free Software Foundation,
18 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
19 *
20 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
21 * CA 95054 USA or visit www.sun.com if you need additional information or
22 * have any questions.
23 */
24
25// This file is available under and governed by the GNU General Public
26// License version 2 only, as published by the Free Software Foundation.
27// However, the following notice accompanied the original version of this
28// file:
29//
30//
31// Little cms
32// Copyright (C) 1998-2006 Marti Maria
33//
34// Permission is hereby granted, free of charge, to any person obtaining
35// a copy of this software and associated documentation files (the "Software"),
36// to deal in the Software without restriction, including without limitation
37// the rights to use, copy, modify, merge, publish, distribute, sublicense,
38// and/or sell copies of the Software, and to permit persons to whom the Software
39// is furnished to do so, subject to the following conditions:
40//
41// The above copyright notice and this permission notice shall be included in
42// all copies or substantial portions of the Software.
43//
44// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
45// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
46// THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
47// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
48// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
49// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
50// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
51
52
53#include "lcms.h"
54
55
56// Shaper/Matrix handling
57// This routines handles the matrix-shaper method. A note about domain
58// is here required. If the shaper-matrix is invoked on INPUT profiles,
59// after the shaper process, we have a value between 0 and 0xFFFF. Thus,
60// for proper matrix handling, we must convert it to 15fix16, so
61// ToFixedDomain might be called. But cmsLinearInterpFixed() returns
62// data yet in fixed point, so no additional process is required.
63// Then, we obtain data on 15.16, so we need to shift >> by 1 to
64// obtain 1.15 PCS format.
65// On OUTPUT profiles, things are inverse, we must first expand 1 bit
66// by shifting left, and then convert result between 0 and 1.000 to
67// RGB, so FromFixedDomain() must be called before pass values to
68// shaper. Trickly, there is a situation where this shifts works
69// little different. Sometimes, lcms smelts input/output
70// matrices into a single, one shaper, process. In such cases, since
71// input is encoded from 0 to 0xffff, we must first use the shaper and
72// then the matrix, an additional FromFixedDomain() must be used to
73// accomodate output values.
74// For a sake of simplicity, I will handle this three behaviours
75// with different routines, so the flags MATSHAPER_INPUT and MATSHAPER_OUTPUT
76// can be conbined to signal smelted matrix-shapers
77
78
79
80static
81int ComputeTables(LPGAMMATABLE Table[3], LPWORD Out[3], LPL16PARAMS p16)
82{
83 int i, AllLinear;
84
85 cmsCalcL16Params(Table[0] -> nEntries, p16);
86
87 AllLinear = 0;
88 for (i=0; i < 3; i++)
89 {
90 LPWORD PtrW;
91
92 PtrW = (LPWORD) malloc(sizeof(WORD) * p16 -> nSamples);
93
94 if (PtrW == NULL) return -1; // Signal error
95
96 CopyMemory(PtrW, Table[i] -> GammaTable, sizeof(WORD) * Table[i] -> nEntries);
97
98 Out[i] = PtrW; // Set table pointer
99
100 // Linear after all?
101
102 AllLinear += cmsIsLinear(PtrW, p16 -> nSamples);
103 }
104
105 // If is all linear, then supress table interpolation (this
106 // will speed greately some trivial operations.
107 // Return 1 if present, 0 if all linear
108
109
110 if (AllLinear != 3) return 1;
111
112 return 0;
113
114}
115
116
117LPMATSHAPER cmsAllocMatShaper2(LPMAT3 Matrix, LPGAMMATABLE In[], LPGAMMATABLE Out[], DWORD Behaviour)
118{
119 LPMATSHAPER NewMatShaper;
120 int rc;
121
122 NewMatShaper = (LPMATSHAPER) malloc(sizeof(MATSHAPER));
123 if (NewMatShaper)
124 ZeroMemory(NewMatShaper, sizeof(MATSHAPER));
125
126 NewMatShaper->dwFlags = Behaviour & (MATSHAPER_ALLSMELTED);
127
128 // Fill matrix part
129
130 MAT3toFix(&NewMatShaper -> Matrix, Matrix);
131
132 // Reality check
133
134 if (!MAT3isIdentity(&NewMatShaper -> Matrix, 0.00001))
135 NewMatShaper -> dwFlags |= MATSHAPER_HASMATRIX;
136
137 // Now, on the table characteristics
138
139 if (Out) {
140
141 rc = ComputeTables(Out, NewMatShaper ->L, &NewMatShaper ->p16);
142 if (rc < 0) {
143 cmsFreeMatShaper(NewMatShaper);
144 return NULL;
145 }
146 if (rc == 1) NewMatShaper -> dwFlags |= MATSHAPER_HASSHAPER;
147 }
148
149
150 if (In) {
151
152 rc = ComputeTables(In, NewMatShaper ->L2, &NewMatShaper ->p2_16);
153 if (rc < 0) {
154 cmsFreeMatShaper(NewMatShaper);
155 return NULL;
156 }
157 if (rc == 1) NewMatShaper -> dwFlags |= MATSHAPER_HASINPSHAPER;
158 }
159
160
161 return NewMatShaper;
162
163}
164
165
166
167// Creation & Destruction
168
169LPMATSHAPER cmsAllocMatShaper(LPMAT3 Matrix, LPGAMMATABLE Tables[], DWORD Behaviour)
170{
171 LPMATSHAPER NewMatShaper;
172 int i, AllLinear;
173
174 NewMatShaper = (LPMATSHAPER) malloc(sizeof(MATSHAPER));
175 if (NewMatShaper)
176 ZeroMemory(NewMatShaper, sizeof(MATSHAPER));
177
178 NewMatShaper->dwFlags = Behaviour & (MATSHAPER_ALLSMELTED);
179
180 // Fill matrix part
181
182 MAT3toFix(&NewMatShaper -> Matrix, Matrix);
183
184 // Reality check
185
186 if (!MAT3isIdentity(&NewMatShaper -> Matrix, 0.00001))
187 NewMatShaper -> dwFlags |= MATSHAPER_HASMATRIX;
188
189 // Now, on the table characteristics
190
191 cmsCalcL16Params(Tables[0] -> nEntries, &NewMatShaper -> p16);
192
193 // Copy tables
194
195 AllLinear = 0;
196 for (i=0; i < 3; i++)
197 {
198 LPWORD PtrW;
199
200 PtrW = (LPWORD) malloc(sizeof(WORD) * NewMatShaper -> p16.nSamples);
201
202 if (PtrW == NULL) {
203 cmsFreeMatShaper(NewMatShaper);
204 return NULL;
205 }
206
207 CopyMemory(PtrW, Tables[i] -> GammaTable,
208 sizeof(WORD) * Tables[i] -> nEntries);
209
210 NewMatShaper -> L[i] = PtrW; // Set table pointer
211
212 // Linear after all?
213
214 AllLinear += cmsIsLinear(PtrW, NewMatShaper -> p16.nSamples);
215 }
216
217 // If is all linear, then supress table interpolation (this
218 // will speed greately some trivial operations
219
220 if (AllLinear != 3)
221 NewMatShaper -> dwFlags |= MATSHAPER_HASSHAPER;
222
223 return NewMatShaper;
224}
225
226
227
228// Free associated memory
229
230void cmsFreeMatShaper(LPMATSHAPER MatShaper)
231{
232 int i;
233
234 if (!MatShaper) return;
235
236 for (i=0; i < 3; i++)
237 {
238 if (MatShaper -> L[i]) free(MatShaper ->L[i]);
239 if (MatShaper -> L2[i]) free(MatShaper ->L2[i]);
240 }
241
242 free(MatShaper);
243}
244
245
246// All smelted must postpose gamma to last stage.
247
248static
249void AllSmeltedBehaviour(LPMATSHAPER MatShaper, WORD In[], WORD Out[])
250{
251
252 WORD tmp[3];
253 WVEC3 InVect, OutVect;
254
255 if (MatShaper -> dwFlags & MATSHAPER_HASINPSHAPER)
256 {
257 InVect.n[VX] = cmsLinearInterpFixed(In[0], MatShaper -> L2[0], &MatShaper -> p2_16);
258 InVect.n[VY] = cmsLinearInterpFixed(In[1], MatShaper -> L2[1], &MatShaper -> p2_16);
259 InVect.n[VZ] = cmsLinearInterpFixed(In[2], MatShaper -> L2[2], &MatShaper -> p2_16);
260 }
261 else
262 {
263 InVect.n[VX] = ToFixedDomain(In[0]);
264 InVect.n[VY] = ToFixedDomain(In[1]);
265 InVect.n[VZ] = ToFixedDomain(In[2]);
266 }
267
268
269 if (MatShaper -> dwFlags & MATSHAPER_HASMATRIX)
270 {
271
272 MAT3evalW(&OutVect, &MatShaper -> Matrix, &InVect);
273 }
274 else {
275
276 OutVect.n[VX] = InVect.n[VX];
277 OutVect.n[VY] = InVect.n[VY];
278 OutVect.n[VZ] = InVect.n[VZ];
279 }
280
281
282 tmp[0] = _cmsClampWord(FromFixedDomain(OutVect.n[VX]));
283 tmp[1] = _cmsClampWord(FromFixedDomain(OutVect.n[VY]));
284 tmp[2] = _cmsClampWord(FromFixedDomain(OutVect.n[VZ]));
285
286
287
288 if (MatShaper -> dwFlags & MATSHAPER_HASSHAPER)
289 {
290 Out[0] = cmsLinearInterpLUT16(tmp[0], MatShaper -> L[0], &MatShaper -> p16);
291 Out[1] = cmsLinearInterpLUT16(tmp[1], MatShaper -> L[1], &MatShaper -> p16);
292 Out[2] = cmsLinearInterpLUT16(tmp[2], MatShaper -> L[2], &MatShaper -> p16);
293 }
294 else
295 {
296 Out[0] = tmp[0];
297 Out[1] = tmp[1];
298 Out[2] = tmp[2];
299 }
300
301}
302
303
304static
305void InputBehaviour(LPMATSHAPER MatShaper, WORD In[], WORD Out[])
306{
307 WVEC3 InVect, OutVect;
308
309
310 if (MatShaper -> dwFlags & MATSHAPER_HASSHAPER)
311 {
312 InVect.n[VX] = cmsLinearInterpFixed(In[0], MatShaper -> L[0], &MatShaper -> p16);
313 InVect.n[VY] = cmsLinearInterpFixed(In[1], MatShaper -> L[1], &MatShaper -> p16);
314 InVect.n[VZ] = cmsLinearInterpFixed(In[2], MatShaper -> L[2], &MatShaper -> p16);
315 }
316 else
317 {
318 InVect.n[VX] = ToFixedDomain(In[0]);
319 InVect.n[VY] = ToFixedDomain(In[1]);
320 InVect.n[VZ] = ToFixedDomain(In[2]);
321 }
322
323 if (MatShaper -> dwFlags & MATSHAPER_HASMATRIX)
324 {
325 MAT3evalW(&OutVect, &MatShaper -> Matrix, &InVect);
326 }
327 else
328 {
329 OutVect = InVect;
330 }
331
332 // PCS in 1Fixed15 format, adjusting
333
334 Out[0] = _cmsClampWord((OutVect.n[VX]) >> 1);
335 Out[1] = _cmsClampWord((OutVect.n[VY]) >> 1);
336 Out[2] = _cmsClampWord((OutVect.n[VZ]) >> 1);
337
338}
339
340
341static
342void OutputBehaviour(LPMATSHAPER MatShaper, WORD In[], WORD Out[])
343{
344 WVEC3 InVect, OutVect;
345 int i;
346
347 // We need to convert from XYZ to RGB, here we must
348 // shift << 1 to pass between 1.15 to 15.16 formats
349
350 InVect.n[VX] = (Fixed32) In[0] << 1;
351 InVect.n[VY] = (Fixed32) In[1] << 1;
352 InVect.n[VZ] = (Fixed32) In[2] << 1;
353
354 if (MatShaper -> dwFlags & MATSHAPER_HASMATRIX)
355 {
356 MAT3evalW(&OutVect, &MatShaper -> Matrix, &InVect);
357 }
358 else
359 {
360 OutVect = InVect;
361 }
362
363
364 if (MatShaper -> dwFlags & MATSHAPER_HASSHAPER)
365 {
366 for (i=0; i < 3; i++)
367 {
368
369 Out[i] = cmsLinearInterpLUT16(
370 _cmsClampWord(FromFixedDomain(OutVect.n[i])),
371 MatShaper -> L[i],
372 &MatShaper ->p16);
373 }
374 }
375 else
376 {
377 // Result from fixed domain to RGB
378
379 Out[0] = _cmsClampWord(FromFixedDomain(OutVect.n[VX]));
380 Out[1] = _cmsClampWord(FromFixedDomain(OutVect.n[VY]));
381 Out[2] = _cmsClampWord(FromFixedDomain(OutVect.n[VZ]));
382 }
383
384}
385
386
387// Master on evaluating shapers, 3 different behaviours
388
389void cmsEvalMatShaper(LPMATSHAPER MatShaper, WORD In[], WORD Out[])
390{
391
392 if ((MatShaper -> dwFlags & MATSHAPER_ALLSMELTED) == MATSHAPER_ALLSMELTED)
393 {
394 AllSmeltedBehaviour(MatShaper, In, Out);
395 return;
396 }
397 if (MatShaper -> dwFlags & MATSHAPER_INPUT)
398 {
399 InputBehaviour(MatShaper, In, Out);
400 return;
401 }
402
403 OutputBehaviour(MatShaper, In, Out);
404}