blob: 0573a021876ba0e29128aca25d79402400315e92 [file] [log] [blame]
J. Duke319a3b92007-12-01 00:00:00 +00001/*
2 * Copyright 2007 Sun Microsystems, Inc. All Rights Reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Sun designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Sun in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
22 * CA 95054 USA or visit www.sun.com if you need additional information or
23 * have any questions.
24 */
25
26#include <stdio.h>
27#include "sun_java2d_cmm_lcms_LCMS.h"
28#include "jni_util.h"
29#include "Trace.h"
30#include "Disposer.h"
31#include "lcms.h"
32
33#define SigMake(a,b,c,d) \
34 ( ( ((int) ((unsigned char) (a))) << 24) | \
35 ( ((int) ((unsigned char) (b))) << 16) | \
36 ( ((int) ((unsigned char) (c))) << 8) | \
37 (int) ((unsigned char) (d)))
38
39#define TagIdConst(a, b, c, d) \
40 ((int) SigMake ((a), (b), (c), (d)))
41
42#define SigHead TagIdConst('h','e','a','d')
43
44#define DT_BYTE 0
45#define DT_SHORT 1
46#define DT_INT 2
47#define DT_DOUBLE 3
48
49/* Default temp profile list size */
50#define DF_ICC_BUF_SIZE 32
51
52#define ERR_MSG_SIZE 20
53
54typedef union storeID_s { /* store SProfile stuff in a Java Long */
55 cmsHPROFILE pf;
56 cmsHTRANSFORM xf;
57 jobject jobj;
58 jlong j;
59} storeID_t, *storeID_p;
60
61static jfieldID Trans_profileIDs_fID;
62static jfieldID Trans_renderType_fID;
63static jfieldID Trans_ID_fID;
64static jfieldID IL_isIntPacked_fID;
65static jfieldID IL_dataType_fID;
66static jfieldID IL_pixelType_fID;
67static jfieldID IL_dataArray_fID;
68static jfieldID IL_offset_fID;
69static jfieldID IL_nextRowOffset_fID;
70static jfieldID IL_width_fID;
71static jfieldID IL_height_fID;
72static jfieldID PF_ID_fID;
73
74JavaVM *javaVM;
75
76int errorHandler(int errorCode, const char *errorText) {
77 JNIEnv *env;
78 char errMsg[ERR_MSG_SIZE];
79 /* We can safely use sprintf here because error message has limited size */
80 sprintf(errMsg, "LCMS error %d", errorCode);
81
82 (*javaVM)->AttachCurrentThread(javaVM, (void**)&env, NULL);
83 JNU_ThrowByName(env, "java/awt/color/CMMException", errMsg);
84 return 1;
85}
86
87JNIEXPORT int JNICALL JNI_OnLoad(JavaVM *jvm, void *reserved) {
88 javaVM = jvm;
89
90 cmsSetErrorHandler(errorHandler);
91 return JNI_VERSION_1_6;
92}
93
94void LCMS_freeTransform(JNIEnv *env, jlong ID)
95{
96 storeID_t sTrans;
97 sTrans.j = ID;
98 /* Passed ID is always valid native ref so there is no check for zero */
99 cmsDeleteTransform(sTrans.xf);
100}
101
102/*
103 * Class: sun_java2d_cmm_lcms_LCMS
104 * Method: createNativeTransform
105 * Signature: ([JI)J
106 */
107JNIEXPORT jlong JNICALL Java_sun_java2d_cmm_lcms_LCMS_createNativeTransform
108 (JNIEnv *env, jclass cls, jlongArray profileIDs, jint renderType,
109 jobject disposerRef)
110{
111 LPLCMSICCPROFILE _iccArray[DF_ICC_BUF_SIZE];
112 LPLCMSICCPROFILE *iccArray = &_iccArray[0];
113 cmsHTRANSFORM transform;
114 storeID_t sTrans;
115 int i, j, size;
116 jlong* ids;
117
118 size = (*env)->GetArrayLength (env, profileIDs);
119 ids = (*env)->GetPrimitiveArrayCritical(env, profileIDs, 0);
120
121 if (DF_ICC_BUF_SIZE < size*2) {
122 iccArray = (LPLCMSICCPROFILE*) malloc(
123 size*2*sizeof(LPLCMSICCPROFILE));
124 if (iccArray == NULL) {
125 J2dRlsTraceLn(J2D_TRACE_ERROR, "getXForm: iccArray == NULL");
126 return NULL;
127 }
128 }
129
130 j = 0;
131 for (i = 0; i < size; i++) {
132 LPLCMSICCPROFILE icc;
133 sTrans.j = ids[i];
134 icc = sTrans.pf;
135 iccArray[j++] = icc;
136
137 /* Middle non-abstract profiles should be doubled before passing to
138 * the cmsCreateMultiprofileTransform function
139 */
140 if (size > 2 && i != 0 && i != size - 1 &&
141 icc->ColorSpace != icSigXYZData &&
142 icc->ColorSpace != icSigLabData)
143 {
144 iccArray[j++] = icc;
145 }
146 }
147
148 sTrans.xf = cmsCreateMultiprofileTransform(iccArray, j,
149 0, 0, renderType, 0);
150
151 (*env)->ReleasePrimitiveArrayCritical(env, profileIDs, ids, 0);
152
153 if (sTrans.xf == NULL) {
154 J2dRlsTraceLn(J2D_TRACE_ERROR, "LCMS_createNativeTransform: "
155 "sTrans.xf == NULL");
156 JNU_ThrowByName(env, "java/awt/color/CMMException",
157 "Cannot get color transform");
158 }
159
160 if (iccArray != &_iccArray[0]) {
161 free(iccArray);
162 }
163 Disposer_AddRecord(env, disposerRef, LCMS_freeTransform, sTrans.j);
164 return sTrans.j;
165}
166
167
168/*
169 * Class: sun_java2d_cmm_lcms_LCMS
170 * Method: loadProfile
171 * Signature: ([B)J
172 */
173JNIEXPORT jlong JNICALL Java_sun_java2d_cmm_lcms_LCMS_loadProfile
174 (JNIEnv *env, jobject obj, jbyteArray data)
175{
176 jbyte* dataArray;
177 jint dataSize;
178 storeID_t sProf;
179
180 dataArray = (*env)->GetByteArrayElements (env, data, 0);
181 dataSize = (*env)->GetArrayLength (env, data);
182
183 sProf.pf = cmsOpenProfileFromMem((LPVOID)dataArray, (DWORD) dataSize);
184
185 if (sProf.pf == NULL) {
186 JNU_ThrowIllegalArgumentException(env, "Invalid profile data");
187 }
188
189 return sProf.j;
190}
191
192/*
193 * Class: sun_java2d_cmm_lcms_LCMS
194 * Method: freeProfile
195 * Signature: (J)V
196 */
197JNIEXPORT void JNICALL Java_sun_java2d_cmm_lcms_LCMS_freeProfile
198 (JNIEnv *env, jobject obj, jlong id)
199{
200 storeID_t sProf;
201
202 sProf.j = id;
203 if (cmsCloseProfile(sProf.pf) == 0) {
204 J2dRlsTraceLn1(J2D_TRACE_ERROR, "LCMS_freeProfile: cmsCloseProfile(%d)"
205 "== 0", id);
206 JNU_ThrowByName(env, "java/awt/color/CMMException",
207 "Cannot close profile");
208 }
209
210}
211
212/*
213 * Class: sun_java2d_cmm_lcms_LCMS
214 * Method: getProfileSize
215 * Signature: (J)I
216 */
217JNIEXPORT jint JNICALL Java_sun_java2d_cmm_lcms_LCMS_getProfileSize
218 (JNIEnv *env, jobject obj, jlong id)
219{
220 LPLCMSICCPROFILE Icc;
221 storeID_t sProf;
222 unsigned char pfSize[4];
223
224 sProf.j = id;
225 Icc = (LPLCMSICCPROFILE) sProf.pf;
226 Icc -> Seek(Icc, 0);
227 Icc -> Read(pfSize, 4, 1, Icc);
228
229 /* TODO: It's a correct but non-optimal for BE machines code, so should
230 * be special cased for them
231 */
232 return (pfSize[0]<<24) | (pfSize[1]<<16) | (pfSize[2]<<8) |
233 pfSize[3];
234}
235
236/*
237 * Class: sun_java2d_cmm_lcms_LCMS
238 * Method: getProfileData
239 * Signature: (J[B)V
240 */
241JNIEXPORT void JNICALL Java_sun_java2d_cmm_lcms_LCMS_getProfileData
242 (JNIEnv *env, jobject obj, jlong id, jbyteArray data)
243{
244 LPLCMSICCPROFILE Icc;
245 storeID_t sProf;
246 unsigned char pfSize[4];
247 jint size;
248 jbyte* dataArray;
249
250 sProf.j = id;
251 Icc = (LPLCMSICCPROFILE) sProf.pf;
252 Icc -> Seek(Icc, 0);
253 Icc -> Read(pfSize, 4, 1, Icc);
254
255 dataArray = (*env)->GetByteArrayElements (env, data, 0);
256 Icc->Seek(Icc, 0);
257
258 /* TODO: It's a correct but non-optimal for BE machines code, so should
259 * be special cased for them
260 */
261 Icc->Read(dataArray, 1,
262 (pfSize[0]<<24) | (pfSize[1]<<16) | (pfSize[2]<<8) | pfSize[3],
263 Icc);
264 (*env)->ReleaseByteArrayElements (env, data, dataArray, 0);
265}
266
267/*
268 * Class: sun_java2d_cmm_lcms_LCMS
269 * Method: getTagSize
270 * Signature: (JI)I
271 */
272JNIEXPORT jint JNICALL Java_sun_java2d_cmm_lcms_LCMS_getTagSize
273 (JNIEnv *env, jobject obj, jlong id, jint tagSig)
274{
275 LPLCMSICCPROFILE Icc;
276 storeID_t sProf;
277 int i;
278 jint result;
279
280 sProf.j = id;
281 Icc = (LPLCMSICCPROFILE) sProf.pf;
282
283 if (tagSig == SigHead) {
284 result = sizeof(icHeader);
285 } else {
286 i = _cmsSearchTag(Icc, tagSig, FALSE);
287 if (i >= 0) {
288 result = Icc->TagSizes[i];
289 } else {
290 JNU_ThrowByName(env, "java/awt/color/CMMException",
291 "ICC profile tag not found");
292 result = -1;
293 }
294 }
295
296 return result;
297}
298
299/*
300 * Class: sun_java2d_cmm_lcms_LCMS
301 * Method: getTagData
302 * Signature: (JI[B)V
303 */
304JNIEXPORT void JNICALL Java_sun_java2d_cmm_lcms_LCMS_getTagData
305 (JNIEnv *env, jobject obj, jlong id, jint tagSig, jbyteArray data)
306{
307 LPLCMSICCPROFILE Icc;
308 storeID_t sProf;
309 jbyte* dataArray;
310 int i, tagSize;
311
312 sProf.j = id;
313 Icc = (LPLCMSICCPROFILE) sProf.pf;
314
315 if (tagSig == SigHead) {
316 dataArray = (*env)->GetByteArrayElements (env, data, 0);
317 tagSize =(*env)->GetArrayLength(env, data);
318 Icc -> Seek(Icc, 0);
319 Icc -> Read(dataArray, sizeof(icHeader), 1, Icc);
320 (*env)->ReleaseByteArrayElements (env, data, dataArray, 0);
321 return;
322 }
323
324
325 i = _cmsSearchTag(Icc, tagSig, FALSE);
326 if (i >=0) {
327 tagSize = Icc->TagSizes[i];
328 dataArray = (*env)->GetByteArrayElements (env, data, 0);
329 Icc->Seek(Icc, Icc->TagOffsets[i]);
330 Icc->Read(dataArray, 1, tagSize, Icc);
331 (*env)->ReleaseByteArrayElements (env, data, dataArray, 0);
332 return;
333 }
334
335 JNU_ThrowByName(env, "java/awt/color/CMMException",
336 "ICC profile tag not found");
337 return;
338}
339
340/*
341 * Class: sun_java2d_cmm_lcms_LCMS
342 * Method: setTagData
343 * Signature: (JI[B)V
344 */
345JNIEXPORT void JNICALL Java_sun_java2d_cmm_lcms_LCMS_setTagData
346 (JNIEnv *env, jobject obj, jlong id, jint tagSig, jbyteArray data)
347{
348 fprintf(stderr, "setTagData operation is not implemented");
349}
350
351void* getILData (JNIEnv *env, jobject img, jint* pDataType,
352 jobject* pDataObject) {
353 void* result = NULL;
354 *pDataType = (*env)->GetIntField (env, img, IL_dataType_fID);
355 *pDataObject = (*env)->GetObjectField(env, img, IL_dataArray_fID);
356 switch (*pDataType) {
357 case DT_BYTE:
358 result = (*env)->GetByteArrayElements (env, *pDataObject, 0);
359 break;
360 case DT_SHORT:
361 result = (*env)->GetShortArrayElements (env, *pDataObject, 0);
362 break;
363 case DT_INT:
364 result = (*env)->GetIntArrayElements (env, *pDataObject, 0);
365 break;
366 case DT_DOUBLE:
367 result = (*env)->GetDoubleArrayElements (env, *pDataObject, 0);
368 break;
369 }
370
371 return result;
372}
373
374void releaseILData (JNIEnv *env, void* pData, jint dataType,
375 jobject dataObject) {
376 switch (dataType) {
377 case DT_BYTE:
378 (*env)->ReleaseByteArrayElements(env,dataObject,(jbyte*)pData,0);
379 break;
380 case DT_SHORT:
381 (*env)->ReleaseShortArrayElements(env,dataObject,(jshort*)pData, 0);
382 break;
383 case DT_INT:
384 (*env)->ReleaseIntArrayElements(env,dataObject,(jint*)pData,0);
385 break;
386 case DT_DOUBLE:
387 (*env)->ReleaseDoubleArrayElements(env,dataObject,(jdouble*)pData,
388 0);
389 break;
390 }
391}
392
393/*
394 * Class: sun_java2d_cmm_lcms_LCMS
395 * Method: colorConvert
396 * Signature: (Lsun/java2d/cmm/lcms/LCMSTransform;Lsun/java2d/cmm/lcms/LCMSImageLayout;Lsun/java2d/cmm/lcms/LCMSImageLayout;)V
397 */
398JNIEXPORT void JNICALL Java_sun_java2d_cmm_lcms_LCMS_colorConvert
399 (JNIEnv *env, jclass obj, jobject trans, jobject src, jobject dst)
400{
401 storeID_t sTrans;
402 int size, inFmt, outFmt, srcDType, dstDType, outSize, renderType;
403 int srcOffset, srcNextRowOffset, dstOffset, dstNextRowOffset;
404 int width, height, i;
405 void* inputBuffer;
406 void* outputBuffer;
407 char* inputRow;
408 char* outputRow;
409 jobject srcData, dstData;
410
411 inFmt = (*env)->GetIntField (env, src, IL_pixelType_fID);
412 outFmt = (*env)->GetIntField (env, dst, IL_pixelType_fID);
413 srcOffset = (*env)->GetIntField (env, src, IL_offset_fID);
414 srcNextRowOffset = (*env)->GetIntField (env, src, IL_nextRowOffset_fID);
415 dstOffset = (*env)->GetIntField (env, dst, IL_offset_fID);
416 dstNextRowOffset = (*env)->GetIntField (env, dst, IL_nextRowOffset_fID);
417 width = (*env)->GetIntField (env, src, IL_width_fID);
418 height = (*env)->GetIntField (env, src, IL_height_fID);
419#ifdef _LITTLE_ENDIAN
420 /* Reversing data packed into int for LE archs */
421 if ((*env)->GetBooleanField (env, src, IL_isIntPacked_fID) == JNI_TRUE) {
422 inFmt ^= DOSWAP_SH(1);
423 }
424 if ((*env)->GetBooleanField (env, dst, IL_isIntPacked_fID) == JNI_TRUE) {
425 outFmt ^= DOSWAP_SH(1);
426 }
427#endif
428 sTrans.j = (*env)->GetLongField (env, trans, Trans_ID_fID);
429 cmsChangeBuffersFormat(sTrans.xf, inFmt, outFmt);
430
431
432 if (sTrans.xf == NULL) {
433 J2dRlsTraceLn(J2D_TRACE_ERROR, "LCMS_colorConvert: transform == NULL");
434 JNU_ThrowByName(env, "java/awt/color/CMMException",
435 "Cannot get color transform");
436 return;
437 }
438
439
440 inputBuffer = getILData (env, src, &srcDType, &srcData);
441
442 if (inputBuffer == NULL) {
443 J2dRlsTraceLn(J2D_TRACE_ERROR, "");
444 JNU_ThrowByName(env, "java/awt/color/CMMException",
445 "Cannot get input data");
446 return;
447 }
448
449 outputBuffer = getILData (env, dst, &dstDType, &dstData);
450
451 if (outputBuffer == NULL) {
452 releaseILData(env, inputBuffer, srcDType, srcData);
453 JNU_ThrowByName(env, "java/awt/color/CMMException",
454 "Cannot get output data");
455 return;
456 }
457
458 inputRow = (char*)inputBuffer + srcOffset;
459 outputRow = (char*)outputBuffer + dstOffset;
460
461 for (i = 0; i < height; i++) {
462 cmsDoTransform(sTrans.xf, inputRow, outputRow, width);
463 inputRow += srcNextRowOffset;
464 outputRow += dstNextRowOffset;
465 }
466
467 releaseILData(env, inputBuffer, srcDType, srcData);
468 releaseILData(env, outputBuffer, dstDType, dstData);
469}
470
471/*
472 * Class: sun_java2d_cmm_lcms_LCMS
473 * Method: getProfileID
474 * Signature: (Ljava/awt/color/ICC_Profile;)J
475 */
476JNIEXPORT jlong JNICALL Java_sun_java2d_cmm_lcms_LCMS_getProfileID
477 (JNIEnv *env, jclass cls, jobject pf)
478{
479 return (*env)->GetLongField (env, pf, PF_ID_fID);
480}
481
482/*
483 * Class: sun_java2d_cmm_lcms_LCMS
484 * Method: initLCMS
485 * Signature: (Ljava/lang/Class;Ljava/lang/Class;Ljava/lang/Class;)V
486 */
487JNIEXPORT void JNICALL Java_sun_java2d_cmm_lcms_LCMS_initLCMS
488 (JNIEnv *env, jclass cls, jclass Trans, jclass IL, jclass Pf)
489{
490 /* TODO: move initialization of the IDs to the static blocks of
491 * corresponding classes to avoid problems with invalidating ids by class
492 * unloading
493 */
494 Trans_profileIDs_fID = (*env)->GetFieldID (env, Trans, "profileIDs", "[J");
495 Trans_renderType_fID = (*env)->GetFieldID (env, Trans, "renderType", "I");
496 Trans_ID_fID = (*env)->GetFieldID (env, Trans, "ID", "J");
497
498 IL_isIntPacked_fID = (*env)->GetFieldID (env, IL, "isIntPacked", "Z");
499 IL_dataType_fID = (*env)->GetFieldID (env, IL, "dataType", "I");
500 IL_pixelType_fID = (*env)->GetFieldID (env, IL, "pixelType", "I");
501 IL_dataArray_fID = (*env)->GetFieldID(env, IL, "dataArray",
502 "Ljava/lang/Object;");
503 IL_width_fID = (*env)->GetFieldID (env, IL, "width", "I");
504 IL_height_fID = (*env)->GetFieldID (env, IL, "height", "I");
505 IL_offset_fID = (*env)->GetFieldID (env, IL, "offset", "I");
506 IL_nextRowOffset_fID = (*env)->GetFieldID (env, IL, "nextRowOffset", "I");
507
508 PF_ID_fID = (*env)->GetFieldID (env, Pf, "ID", "J");
509}