1 | /* |
2 | * Copyright (c) 2007, 2015, Oracle and/or its affiliates. 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. Oracle designates this |
8 | * particular file as subject to the "Classpath" exception as provided |
9 | * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
22 | * or visit www.oracle.com if you need additional information or have any |
23 | * questions. |
24 | */ |
25 | |
26 | #include <stdio.h> |
27 | #include <stdlib.h> |
28 | #include <memory.h> |
29 | #include "sun_java2d_cmm_lcms_LCMS.h" |
30 | #include "jni_util.h" |
31 | #include "Trace.h" |
32 | #include "Disposer.h" |
33 | #include <lcms2.h> |
34 | #include "jlong.h" |
35 | |
36 | |
37 | #define ALIGNLONG(x) (((x)+3) & ~(3)) // Aligns to DWORD boundary |
38 | |
39 | #ifdef USE_BIG_ENDIAN |
40 | #define AdjustEndianess32(a) |
41 | #else |
42 | |
43 | static |
44 | void AdjustEndianess32(cmsUInt8Number *pByte) |
45 | { |
46 | cmsUInt8Number temp1; |
47 | cmsUInt8Number temp2; |
48 | |
49 | temp1 = *pByte++; |
50 | temp2 = *pByte++; |
51 | *(pByte-1) = *pByte; |
52 | *pByte++ = temp2; |
53 | *(pByte-3) = *pByte; |
54 | *pByte = temp1; |
55 | } |
56 | |
57 | #endif |
58 | |
59 | // Transports to properly encoded values - note that icc profiles does use |
60 | // big endian notation. |
61 | |
62 | static |
63 | cmsInt32Number TransportValue32(cmsInt32Number Value) |
64 | { |
65 | cmsInt32Number Temp = Value; |
66 | |
67 | AdjustEndianess32((cmsUInt8Number*) &Temp); |
68 | return Temp; |
69 | } |
70 | |
71 | #define SigMake(a,b,c,d) \ |
72 | ( ( ((int) ((unsigned char) (a))) << 24) | \ |
73 | ( ((int) ((unsigned char) (b))) << 16) | \ |
74 | ( ((int) ((unsigned char) (c))) << 8) | \ |
75 | (int) ((unsigned char) (d))) |
76 | |
77 | #define TagIdConst(a, b, c, d) \ |
78 | ((int) SigMake ((a), (b), (c), (d))) |
79 | |
80 | #define SigHead TagIdConst('h','e','a','d') |
81 | |
82 | #define DT_BYTE 0 |
83 | #define DT_SHORT 1 |
84 | #define DT_INT 2 |
85 | #define DT_DOUBLE 3 |
86 | |
87 | /* Default temp profile list size */ |
88 | #define DF_ICC_BUF_SIZE 32 |
89 | |
90 | #define ERR_MSG_SIZE 256 |
91 | |
92 | #ifdef _MSC_VER |
93 | # ifndef snprintf |
94 | # define snprintf _snprintf |
95 | # endif |
96 | #endif |
97 | |
98 | typedef struct lcmsProfile_s { |
99 | cmsHPROFILE pf; |
100 | } lcmsProfile_t, *lcmsProfile_p; |
101 | |
102 | typedef union { |
103 | cmsTagSignature cms; |
104 | jint j; |
105 | } TagSignature_t, *TagSignature_p; |
106 | |
107 | static jfieldID Trans_renderType_fID; |
108 | static jfieldID Trans_ID_fID; |
109 | static jfieldID IL_isIntPacked_fID; |
110 | static jfieldID IL_dataType_fID; |
111 | static jfieldID IL_pixelType_fID; |
112 | static jfieldID IL_dataArray_fID; |
113 | static jfieldID IL_offset_fID; |
114 | static jfieldID IL_nextRowOffset_fID; |
115 | static jfieldID IL_width_fID; |
116 | static jfieldID IL_height_fID; |
117 | static jfieldID IL_imageAtOnce_fID; |
118 | |
119 | JavaVM *javaVM; |
120 | |
121 | void errorHandler(cmsContext ContextID, cmsUInt32Number errorCode, |
122 | const char *errorText) { |
123 | JNIEnv *env; |
124 | char errMsg[ERR_MSG_SIZE]; |
125 | |
126 | int count = snprintf(errMsg, ERR_MSG_SIZE, |
127 | "LCMS error %d: %s" , errorCode, errorText); |
128 | if (count < 0 || count >= ERR_MSG_SIZE) { |
129 | count = ERR_MSG_SIZE - 1; |
130 | } |
131 | errMsg[count] = 0; |
132 | |
133 | (*javaVM)->AttachCurrentThread(javaVM, (void**)&env, NULL); |
134 | JNU_ThrowByName(env, "java/awt/color/CMMException" , errMsg); |
135 | } |
136 | |
137 | JNIEXPORT jint JNICALL DEF_JNI_OnLoad(JavaVM *jvm, void *reserved) { |
138 | javaVM = jvm; |
139 | |
140 | cmsSetLogErrorHandler(errorHandler); |
141 | return JNI_VERSION_1_6; |
142 | } |
143 | |
144 | void LCMS_freeProfile(JNIEnv *env, jlong ptr) { |
145 | lcmsProfile_p p = (lcmsProfile_p)jlong_to_ptr(ptr); |
146 | |
147 | if (p != NULL) { |
148 | if (p->pf != NULL) { |
149 | cmsCloseProfile(p->pf); |
150 | } |
151 | free(p); |
152 | } |
153 | } |
154 | |
155 | void LCMS_freeTransform(JNIEnv *env, jlong ID) |
156 | { |
157 | cmsHTRANSFORM sTrans = jlong_to_ptr(ID); |
158 | /* Passed ID is always valid native ref so there is no check for zero */ |
159 | cmsDeleteTransform(sTrans); |
160 | } |
161 | |
162 | /* |
163 | * Class: sun_java2d_cmm_lcms_LCMS |
164 | * Method: createNativeTransform |
165 | * Signature: ([JI)J |
166 | */ |
167 | JNIEXPORT jlong JNICALL Java_sun_java2d_cmm_lcms_LCMS_createNativeTransform |
168 | (JNIEnv *env, jclass cls, jlongArray profileIDs, jint renderType, |
169 | jint inFormatter, jboolean isInIntPacked, |
170 | jint outFormatter, jboolean isOutIntPacked, jobject disposerRef) |
171 | { |
172 | cmsHPROFILE _iccArray[DF_ICC_BUF_SIZE]; |
173 | cmsHPROFILE *iccArray = &_iccArray[0]; |
174 | cmsHTRANSFORM sTrans = NULL; |
175 | int i, j, size; |
176 | jlong* ids; |
177 | |
178 | size = (*env)->GetArrayLength (env, profileIDs); |
179 | ids = (*env)->GetLongArrayElements(env, profileIDs, 0); |
180 | if (ids == NULL) { |
181 | // An exception should have already been thrown. |
182 | return 0L; |
183 | } |
184 | |
185 | #ifdef _LITTLE_ENDIAN |
186 | /* Reversing data packed into int for LE archs */ |
187 | if (isInIntPacked) { |
188 | inFormatter ^= DOSWAP_SH(1); |
189 | } |
190 | if (isOutIntPacked) { |
191 | outFormatter ^= DOSWAP_SH(1); |
192 | } |
193 | #endif |
194 | |
195 | if (DF_ICC_BUF_SIZE < size*2) { |
196 | iccArray = (cmsHPROFILE*) malloc( |
197 | size*2*sizeof(cmsHPROFILE)); |
198 | if (iccArray == NULL) { |
199 | (*env)->ReleaseLongArrayElements(env, profileIDs, ids, 0); |
200 | |
201 | J2dRlsTraceLn(J2D_TRACE_ERROR, "getXForm: iccArray == NULL" ); |
202 | return 0L; |
203 | } |
204 | } |
205 | |
206 | j = 0; |
207 | for (i = 0; i < size; i++) { |
208 | cmsColorSpaceSignature cs; |
209 | lcmsProfile_p profilePtr = (lcmsProfile_p)jlong_to_ptr(ids[i]); |
210 | cmsHPROFILE icc = profilePtr->pf; |
211 | |
212 | iccArray[j++] = icc; |
213 | |
214 | /* Middle non-abstract profiles should be doubled before passing to |
215 | * the cmsCreateMultiprofileTransform function |
216 | */ |
217 | |
218 | cs = cmsGetColorSpace(icc); |
219 | if (size > 2 && i != 0 && i != size - 1 && |
220 | cs != cmsSigXYZData && cs != cmsSigLabData) |
221 | { |
222 | iccArray[j++] = icc; |
223 | } |
224 | } |
225 | |
226 | sTrans = cmsCreateMultiprofileTransform(iccArray, j, |
227 | inFormatter, outFormatter, renderType, 0); |
228 | |
229 | (*env)->ReleaseLongArrayElements(env, profileIDs, ids, 0); |
230 | |
231 | if (sTrans == NULL) { |
232 | J2dRlsTraceLn(J2D_TRACE_ERROR, "LCMS_createNativeTransform: " |
233 | "sTrans == NULL" ); |
234 | if ((*env)->ExceptionOccurred(env) == NULL) { |
235 | JNU_ThrowByName(env, "java/awt/color/CMMException" , |
236 | "Cannot get color transform" ); |
237 | } |
238 | } else { |
239 | Disposer_AddRecord(env, disposerRef, LCMS_freeTransform, ptr_to_jlong(sTrans)); |
240 | } |
241 | |
242 | if (iccArray != &_iccArray[0]) { |
243 | free(iccArray); |
244 | } |
245 | return ptr_to_jlong(sTrans); |
246 | } |
247 | |
248 | |
249 | /* |
250 | * Class: sun_java2d_cmm_lcms_LCMS |
251 | * Method: loadProfile |
252 | * Signature: ([B,Lsun/java2d/cmm/lcms/LCMSProfile;)V |
253 | */ |
254 | JNIEXPORT jlong JNICALL Java_sun_java2d_cmm_lcms_LCMS_loadProfileNative |
255 | (JNIEnv *env, jobject obj, jbyteArray data, jobject disposerRef) |
256 | { |
257 | jbyte* dataArray; |
258 | jint dataSize; |
259 | lcmsProfile_p sProf = NULL; |
260 | cmsHPROFILE pf; |
261 | |
262 | if (JNU_IsNull(env, data)) { |
263 | JNU_ThrowIllegalArgumentException(env, "Invalid profile data" ); |
264 | return 0L; |
265 | } |
266 | |
267 | dataArray = (*env)->GetByteArrayElements (env, data, 0); |
268 | if (dataArray == NULL) { |
269 | // An exception should have already been thrown. |
270 | return 0L; |
271 | } |
272 | |
273 | dataSize = (*env)->GetArrayLength (env, data); |
274 | |
275 | pf = cmsOpenProfileFromMem((const void *)dataArray, |
276 | (cmsUInt32Number) dataSize); |
277 | |
278 | (*env)->ReleaseByteArrayElements (env, data, dataArray, 0); |
279 | |
280 | if (pf == NULL) { |
281 | JNU_ThrowIllegalArgumentException(env, "Invalid profile data" ); |
282 | } else { |
283 | /* Sanity check: try to save the profile in order |
284 | * to force basic validation. |
285 | */ |
286 | cmsUInt32Number pfSize = 0; |
287 | if (!cmsSaveProfileToMem(pf, NULL, &pfSize) || |
288 | pfSize < sizeof(cmsICCHeader)) |
289 | { |
290 | JNU_ThrowIllegalArgumentException(env, "Invalid profile data" ); |
291 | |
292 | cmsCloseProfile(pf); |
293 | pf = NULL; |
294 | } |
295 | } |
296 | |
297 | if (pf != NULL) { |
298 | // create profile holder |
299 | sProf = (lcmsProfile_p)malloc(sizeof(lcmsProfile_t)); |
300 | if (sProf != NULL) { |
301 | // register the disposer record |
302 | sProf->pf = pf; |
303 | Disposer_AddRecord(env, disposerRef, LCMS_freeProfile, ptr_to_jlong(sProf)); |
304 | } else { |
305 | cmsCloseProfile(pf); |
306 | } |
307 | } |
308 | |
309 | return ptr_to_jlong(sProf); |
310 | } |
311 | |
312 | /* |
313 | * Class: sun_java2d_cmm_lcms_LCMS |
314 | * Method: getProfileSizeNative |
315 | * Signature: (J)I |
316 | */ |
317 | JNIEXPORT jint JNICALL Java_sun_java2d_cmm_lcms_LCMS_getProfileSizeNative |
318 | (JNIEnv *env, jobject obj, jlong id) |
319 | { |
320 | lcmsProfile_p sProf = (lcmsProfile_p)jlong_to_ptr(id); |
321 | cmsUInt32Number pfSize = 0; |
322 | |
323 | if (cmsSaveProfileToMem(sProf->pf, NULL, &pfSize) && ((jint)pfSize > 0)) { |
324 | return (jint)pfSize; |
325 | } else { |
326 | JNU_ThrowByName(env, "java/awt/color/CMMException" , |
327 | "Can not access specified profile." ); |
328 | return -1; |
329 | } |
330 | } |
331 | |
332 | /* |
333 | * Class: sun_java2d_cmm_lcms_LCMS |
334 | * Method: getProfileDataNative |
335 | * Signature: (J[B)V |
336 | */ |
337 | JNIEXPORT void JNICALL Java_sun_java2d_cmm_lcms_LCMS_getProfileDataNative |
338 | (JNIEnv *env, jobject obj, jlong id, jbyteArray data) |
339 | { |
340 | lcmsProfile_p sProf = (lcmsProfile_p)jlong_to_ptr(id); |
341 | jint size; |
342 | jbyte* dataArray; |
343 | cmsUInt32Number pfSize = 0; |
344 | cmsBool status; |
345 | |
346 | // determine actual profile size |
347 | if (!cmsSaveProfileToMem(sProf->pf, NULL, &pfSize)) { |
348 | JNU_ThrowByName(env, "java/awt/color/CMMException" , |
349 | "Can not access specified profile." ); |
350 | return; |
351 | } |
352 | |
353 | // verify java buffer capacity |
354 | size = (*env)->GetArrayLength(env, data); |
355 | if (0 >= size || pfSize > (cmsUInt32Number)size) { |
356 | JNU_ThrowByName(env, "java/awt/color/CMMException" , |
357 | "Insufficient buffer capacity." ); |
358 | return; |
359 | } |
360 | |
361 | dataArray = (*env)->GetByteArrayElements (env, data, 0); |
362 | if (dataArray == NULL) { |
363 | // An exception should have already been thrown. |
364 | return; |
365 | } |
366 | |
367 | status = cmsSaveProfileToMem(sProf->pf, dataArray, &pfSize); |
368 | |
369 | (*env)->ReleaseByteArrayElements (env, data, dataArray, 0); |
370 | |
371 | if (!status) { |
372 | JNU_ThrowByName(env, "java/awt/color/CMMException" , |
373 | "Can not access specified profile." ); |
374 | return; |
375 | } |
376 | } |
377 | |
378 | /* Get profile header info */ |
379 | static cmsBool _getHeaderInfo(cmsHPROFILE pf, jbyte* pBuffer, jint bufferSize); |
380 | static cmsBool _setHeaderInfo(cmsHPROFILE pf, jbyte* pBuffer, jint bufferSize); |
381 | static cmsHPROFILE _writeCookedTag(cmsHPROFILE pfTarget, cmsTagSignature sig, jbyte *pData, jint size); |
382 | |
383 | |
384 | /* |
385 | * Class: sun_java2d_cmm_lcms_LCMS |
386 | * Method: getTagData |
387 | * Signature: (JI[B)V |
388 | */ |
389 | JNIEXPORT jbyteArray JNICALL Java_sun_java2d_cmm_lcms_LCMS_getTagNative |
390 | (JNIEnv *env, jobject obj, jlong id, jint tagSig) |
391 | { |
392 | lcmsProfile_p sProf = (lcmsProfile_p)jlong_to_ptr(id); |
393 | TagSignature_t sig; |
394 | cmsUInt32Number tagSize; |
395 | |
396 | jbyte* dataArray = NULL; |
397 | jbyteArray data = NULL; |
398 | |
399 | cmsUInt32Number bufSize; |
400 | |
401 | sig.j = tagSig; |
402 | |
403 | if (tagSig == SigHead) { |
404 | cmsBool status; |
405 | |
406 | // allocate java array |
407 | bufSize = sizeof(cmsICCHeader); |
408 | data = (*env)->NewByteArray(env, bufSize); |
409 | |
410 | if (data == NULL) { |
411 | // An exception should have already been thrown. |
412 | return NULL; |
413 | } |
414 | |
415 | dataArray = (*env)->GetByteArrayElements (env, data, 0); |
416 | |
417 | if (dataArray == NULL) { |
418 | // An exception should have already been thrown. |
419 | return NULL; |
420 | } |
421 | |
422 | status = _getHeaderInfo(sProf->pf, dataArray, bufSize); |
423 | |
424 | (*env)->ReleaseByteArrayElements (env, data, dataArray, 0); |
425 | |
426 | if (!status) { |
427 | JNU_ThrowByName(env, "java/awt/color/CMMException" , |
428 | "ICC Profile header not found" ); |
429 | return NULL; |
430 | } |
431 | |
432 | return data; |
433 | } |
434 | |
435 | if (cmsIsTag(sProf->pf, sig.cms)) { |
436 | tagSize = cmsReadRawTag(sProf->pf, sig.cms, NULL, 0); |
437 | } else { |
438 | JNU_ThrowByName(env, "java/awt/color/CMMException" , |
439 | "ICC profile tag not found" ); |
440 | return NULL; |
441 | } |
442 | |
443 | // allocate java array |
444 | data = (*env)->NewByteArray(env, tagSize); |
445 | if (data == NULL) { |
446 | // An exception should have already been thrown. |
447 | return NULL; |
448 | } |
449 | |
450 | dataArray = (*env)->GetByteArrayElements (env, data, 0); |
451 | |
452 | if (dataArray == NULL) { |
453 | // An exception should have already been thrown. |
454 | return NULL; |
455 | } |
456 | |
457 | bufSize = cmsReadRawTag(sProf->pf, sig.cms, dataArray, tagSize); |
458 | |
459 | (*env)->ReleaseByteArrayElements (env, data, dataArray, 0); |
460 | |
461 | if (bufSize != tagSize) { |
462 | JNU_ThrowByName(env, "java/awt/color/CMMException" , |
463 | "Can not get tag data." ); |
464 | return NULL; |
465 | } |
466 | return data; |
467 | } |
468 | |
469 | /* |
470 | * Class: sun_java2d_cmm_lcms_LCMS |
471 | * Method: setTagData |
472 | * Signature: (JI[B)V |
473 | */ |
474 | JNIEXPORT void JNICALL Java_sun_java2d_cmm_lcms_LCMS_setTagDataNative |
475 | (JNIEnv *env, jobject obj, jlong id, jint tagSig, jbyteArray data) |
476 | { |
477 | lcmsProfile_p sProf = (lcmsProfile_p)jlong_to_ptr(id); |
478 | cmsHPROFILE pfReplace = NULL; |
479 | |
480 | TagSignature_t sig; |
481 | cmsBool status = FALSE; |
482 | jbyte* dataArray; |
483 | int tagSize; |
484 | |
485 | sig.j = tagSig; |
486 | |
487 | if (JNU_IsNull(env, data)) { |
488 | JNU_ThrowIllegalArgumentException(env, "Can not write tag data." ); |
489 | return; |
490 | } |
491 | |
492 | tagSize =(*env)->GetArrayLength(env, data); |
493 | |
494 | dataArray = (*env)->GetByteArrayElements(env, data, 0); |
495 | |
496 | if (dataArray == NULL) { |
497 | // An exception should have already been thrown. |
498 | return; |
499 | } |
500 | |
501 | if (tagSig == SigHead) { |
502 | status = _setHeaderInfo(sProf->pf, dataArray, tagSize); |
503 | } else { |
504 | /* |
505 | * New strategy for generic tags: create a place holder, |
506 | * dump all existing tags there, dump externally supplied |
507 | * tag, and return the new profile to the java. |
508 | */ |
509 | pfReplace = _writeCookedTag(sProf->pf, sig.cms, dataArray, tagSize); |
510 | status = (pfReplace != NULL); |
511 | } |
512 | |
513 | (*env)->ReleaseByteArrayElements(env, data, dataArray, 0); |
514 | |
515 | if (!status) { |
516 | JNU_ThrowIllegalArgumentException(env, "Can not write tag data." ); |
517 | } else if (pfReplace != NULL) { |
518 | cmsCloseProfile(sProf->pf); |
519 | sProf->pf = pfReplace; |
520 | } |
521 | } |
522 | |
523 | void* getILData (JNIEnv *env, jobject img, jint* pDataType, |
524 | jobject* pDataObject) { |
525 | void* result = NULL; |
526 | *pDataType = (*env)->GetIntField (env, img, IL_dataType_fID); |
527 | *pDataObject = (*env)->GetObjectField(env, img, IL_dataArray_fID); |
528 | switch (*pDataType) { |
529 | case DT_BYTE: |
530 | result = (*env)->GetByteArrayElements (env, *pDataObject, 0); |
531 | break; |
532 | case DT_SHORT: |
533 | result = (*env)->GetShortArrayElements (env, *pDataObject, 0); |
534 | break; |
535 | case DT_INT: |
536 | result = (*env)->GetIntArrayElements (env, *pDataObject, 0); |
537 | break; |
538 | case DT_DOUBLE: |
539 | result = (*env)->GetDoubleArrayElements (env, *pDataObject, 0); |
540 | break; |
541 | } |
542 | |
543 | return result; |
544 | } |
545 | |
546 | void releaseILData (JNIEnv *env, void* pData, jint dataType, |
547 | jobject dataObject) { |
548 | switch (dataType) { |
549 | case DT_BYTE: |
550 | (*env)->ReleaseByteArrayElements(env,dataObject,(jbyte*)pData,0); |
551 | break; |
552 | case DT_SHORT: |
553 | (*env)->ReleaseShortArrayElements(env,dataObject,(jshort*)pData, 0); |
554 | break; |
555 | case DT_INT: |
556 | (*env)->ReleaseIntArrayElements(env,dataObject,(jint*)pData,0); |
557 | break; |
558 | case DT_DOUBLE: |
559 | (*env)->ReleaseDoubleArrayElements(env,dataObject,(jdouble*)pData, |
560 | 0); |
561 | break; |
562 | } |
563 | } |
564 | |
565 | /* |
566 | * Class: sun_java2d_cmm_lcms_LCMS |
567 | * Method: colorConvert |
568 | * Signature: (Lsun/java2d/cmm/lcms/LCMSTransform;Lsun/java2d/cmm/lcms/LCMSImageLayout;Lsun/java2d/cmm/lcms/LCMSImageLayout;)V |
569 | */ |
570 | JNIEXPORT void JNICALL Java_sun_java2d_cmm_lcms_LCMS_colorConvert |
571 | (JNIEnv *env, jclass obj, jobject trans, jobject src, jobject dst) |
572 | { |
573 | cmsHTRANSFORM sTrans = NULL; |
574 | int srcDType, dstDType; |
575 | int srcOffset, srcNextRowOffset, dstOffset, dstNextRowOffset; |
576 | int width, height, i; |
577 | void* inputBuffer; |
578 | void* outputBuffer; |
579 | char* inputRow; |
580 | char* outputRow; |
581 | jobject srcData, dstData; |
582 | jboolean srcAtOnce = JNI_FALSE, dstAtOnce = JNI_FALSE; |
583 | |
584 | srcOffset = (*env)->GetIntField (env, src, IL_offset_fID); |
585 | srcNextRowOffset = (*env)->GetIntField (env, src, IL_nextRowOffset_fID); |
586 | dstOffset = (*env)->GetIntField (env, dst, IL_offset_fID); |
587 | dstNextRowOffset = (*env)->GetIntField (env, dst, IL_nextRowOffset_fID); |
588 | width = (*env)->GetIntField (env, src, IL_width_fID); |
589 | height = (*env)->GetIntField (env, src, IL_height_fID); |
590 | |
591 | srcAtOnce = (*env)->GetBooleanField(env, src, IL_imageAtOnce_fID); |
592 | dstAtOnce = (*env)->GetBooleanField(env, dst, IL_imageAtOnce_fID); |
593 | |
594 | sTrans = jlong_to_ptr((*env)->GetLongField (env, trans, Trans_ID_fID)); |
595 | |
596 | if (sTrans == NULL) { |
597 | J2dRlsTraceLn(J2D_TRACE_ERROR, "LCMS_colorConvert: transform == NULL" ); |
598 | JNU_ThrowByName(env, "java/awt/color/CMMException" , |
599 | "Cannot get color transform" ); |
600 | return; |
601 | } |
602 | |
603 | |
604 | inputBuffer = getILData (env, src, &srcDType, &srcData); |
605 | |
606 | if (inputBuffer == NULL) { |
607 | J2dRlsTraceLn(J2D_TRACE_ERROR, "" ); |
608 | // An exception should have already been thrown. |
609 | return; |
610 | } |
611 | |
612 | outputBuffer = getILData (env, dst, &dstDType, &dstData); |
613 | |
614 | if (outputBuffer == NULL) { |
615 | releaseILData(env, inputBuffer, srcDType, srcData); |
616 | // An exception should have already been thrown. |
617 | return; |
618 | } |
619 | |
620 | inputRow = (char*)inputBuffer + srcOffset; |
621 | outputRow = (char*)outputBuffer + dstOffset; |
622 | |
623 | if (srcAtOnce && dstAtOnce) { |
624 | cmsDoTransform(sTrans, inputRow, outputRow, width * height); |
625 | } else { |
626 | for (i = 0; i < height; i++) { |
627 | cmsDoTransform(sTrans, inputRow, outputRow, width); |
628 | inputRow += srcNextRowOffset; |
629 | outputRow += dstNextRowOffset; |
630 | } |
631 | } |
632 | |
633 | releaseILData(env, inputBuffer, srcDType, srcData); |
634 | releaseILData(env, outputBuffer, dstDType, dstData); |
635 | } |
636 | |
637 | /* |
638 | * Class: sun_java2d_cmm_lcms_LCMS |
639 | * Method: getProfileID |
640 | * Signature: (Ljava/awt/color/ICC_Profile;)Lsun/java2d/cmm/lcms/LCMSProfile |
641 | */ |
642 | JNIEXPORT jobject JNICALL Java_sun_java2d_cmm_lcms_LCMS_getProfileID |
643 | (JNIEnv *env, jclass cls, jobject pf) |
644 | { |
645 | jclass clsLcmsProfile; |
646 | jobject cmmProfile; |
647 | jfieldID fid; |
648 | |
649 | if (pf == NULL) { |
650 | return NULL; |
651 | } |
652 | fid = (*env)->GetFieldID (env, |
653 | (*env)->GetObjectClass(env, pf), |
654 | "cmmProfile" , "Lsun/java2d/cmm/Profile;" ); |
655 | if (fid == NULL) { |
656 | return NULL; |
657 | } |
658 | |
659 | clsLcmsProfile = (*env)->FindClass(env, |
660 | "sun/java2d/cmm/lcms/LCMSProfile" ); |
661 | if (clsLcmsProfile == NULL) { |
662 | return NULL; |
663 | } |
664 | |
665 | cmmProfile = (*env)->GetObjectField (env, pf, fid); |
666 | |
667 | if (JNU_IsNull(env, cmmProfile)) { |
668 | return NULL; |
669 | } |
670 | if ((*env)->IsInstanceOf(env, cmmProfile, clsLcmsProfile)) { |
671 | return cmmProfile; |
672 | } |
673 | return NULL; |
674 | } |
675 | |
676 | /* |
677 | * Class: sun_java2d_cmm_lcms_LCMS |
678 | * Method: initLCMS |
679 | * Signature: (Ljava/lang/Class;Ljava/lang/Class;Ljava/lang/Class;)V |
680 | */ |
681 | JNIEXPORT void JNICALL Java_sun_java2d_cmm_lcms_LCMS_initLCMS |
682 | (JNIEnv *env, jclass cls, jclass Trans, jclass IL, jclass Pf) |
683 | { |
684 | /* TODO: move initialization of the IDs to the static blocks of |
685 | * corresponding classes to avoid problems with invalidating ids by class |
686 | * unloading |
687 | */ |
688 | Trans_renderType_fID = (*env)->GetFieldID (env, Trans, "renderType" , "I" ); |
689 | if (Trans_renderType_fID == NULL) { |
690 | return; |
691 | } |
692 | Trans_ID_fID = (*env)->GetFieldID (env, Trans, "ID" , "J" ); |
693 | if (Trans_ID_fID == NULL) { |
694 | return; |
695 | } |
696 | |
697 | IL_isIntPacked_fID = (*env)->GetFieldID (env, IL, "isIntPacked" , "Z" ); |
698 | if (IL_isIntPacked_fID == NULL) { |
699 | return; |
700 | } |
701 | IL_dataType_fID = (*env)->GetFieldID (env, IL, "dataType" , "I" ); |
702 | if (IL_dataType_fID == NULL) { |
703 | return; |
704 | } |
705 | IL_pixelType_fID = (*env)->GetFieldID (env, IL, "pixelType" , "I" ); |
706 | if (IL_pixelType_fID == NULL) { |
707 | return; |
708 | } |
709 | IL_dataArray_fID = (*env)->GetFieldID(env, IL, "dataArray" , |
710 | "Ljava/lang/Object;" ); |
711 | if (IL_dataArray_fID == NULL) { |
712 | return; |
713 | } |
714 | IL_width_fID = (*env)->GetFieldID (env, IL, "width" , "I" ); |
715 | if (IL_width_fID == NULL) { |
716 | return; |
717 | } |
718 | IL_height_fID = (*env)->GetFieldID (env, IL, "height" , "I" ); |
719 | if (IL_height_fID == NULL) { |
720 | return; |
721 | } |
722 | IL_offset_fID = (*env)->GetFieldID (env, IL, "offset" , "I" ); |
723 | if (IL_offset_fID == NULL) { |
724 | return; |
725 | } |
726 | IL_imageAtOnce_fID = (*env)->GetFieldID (env, IL, "imageAtOnce" , "Z" ); |
727 | if (IL_imageAtOnce_fID == NULL) { |
728 | return; |
729 | } |
730 | IL_nextRowOffset_fID = (*env)->GetFieldID (env, IL, "nextRowOffset" , "I" ); |
731 | if (IL_nextRowOffset_fID == NULL) { |
732 | return; |
733 | } |
734 | } |
735 | |
736 | static cmsBool (cmsHPROFILE pf, jbyte* pBuffer, jint bufferSize) |
737 | { |
738 | cmsUInt32Number pfSize = 0; |
739 | cmsUInt8Number* pfBuffer = NULL; |
740 | cmsBool status = FALSE; |
741 | |
742 | if (!cmsSaveProfileToMem(pf, NULL, &pfSize) || |
743 | pfSize < sizeof(cmsICCHeader) || |
744 | bufferSize < (jint)sizeof(cmsICCHeader)) |
745 | { |
746 | return FALSE; |
747 | } |
748 | |
749 | pfBuffer = malloc(pfSize); |
750 | if (pfBuffer == NULL) { |
751 | return FALSE; |
752 | } |
753 | |
754 | // load raw profile data into the buffer |
755 | if (cmsSaveProfileToMem(pf, pfBuffer, &pfSize)) { |
756 | memcpy(pBuffer, pfBuffer, sizeof(cmsICCHeader)); |
757 | status = TRUE; |
758 | } |
759 | free(pfBuffer); |
760 | return status; |
761 | } |
762 | |
763 | static cmsBool (cmsHPROFILE pf, jbyte* pBuffer, jint bufferSize) |
764 | { |
765 | cmsICCHeader ; |
766 | |
767 | if (pBuffer == NULL || bufferSize < (jint)sizeof(cmsICCHeader)) { |
768 | return FALSE; |
769 | } |
770 | |
771 | memcpy(&pfHeader, pBuffer, sizeof(cmsICCHeader)); |
772 | |
773 | // now set header fields, which we can access using the lcms2 public API |
774 | cmsSetHeaderFlags(pf, pfHeader.flags); |
775 | cmsSetHeaderManufacturer(pf, pfHeader.manufacturer); |
776 | cmsSetHeaderModel(pf, pfHeader.model); |
777 | cmsSetHeaderAttributes(pf, pfHeader.attributes); |
778 | cmsSetHeaderProfileID(pf, (cmsUInt8Number*)&(pfHeader.profileID)); |
779 | cmsSetHeaderRenderingIntent(pf, pfHeader.renderingIntent); |
780 | cmsSetPCS(pf, pfHeader.pcs); |
781 | cmsSetColorSpace(pf, pfHeader.colorSpace); |
782 | cmsSetDeviceClass(pf, pfHeader.deviceClass); |
783 | cmsSetEncodedICCversion(pf, pfHeader.version); |
784 | |
785 | return TRUE; |
786 | } |
787 | |
788 | /* Returns new profile handler, if it was created successfully, |
789 | NULL otherwise. |
790 | */ |
791 | static cmsHPROFILE _writeCookedTag(const cmsHPROFILE pfTarget, |
792 | const cmsTagSignature sig, |
793 | jbyte *pData, jint size) |
794 | { |
795 | cmsUInt32Number pfSize = 0; |
796 | const cmsInt32Number tagCount = cmsGetTagCount(pfTarget); |
797 | cmsInt32Number i; |
798 | cmsHPROFILE pfSanity = NULL; |
799 | |
800 | cmsICCHeader hdr; |
801 | |
802 | cmsHPROFILE p = cmsCreateProfilePlaceholder(NULL); |
803 | |
804 | if (NULL == p) { |
805 | return NULL; |
806 | } |
807 | memset(&hdr, 0, sizeof(cmsICCHeader)); |
808 | |
809 | // Populate the placeholder's header according to target profile |
810 | hdr.flags = cmsGetHeaderFlags(pfTarget); |
811 | hdr.renderingIntent = cmsGetHeaderRenderingIntent(pfTarget); |
812 | hdr.manufacturer = cmsGetHeaderManufacturer(pfTarget); |
813 | hdr.model = cmsGetHeaderModel(pfTarget); |
814 | hdr.pcs = cmsGetPCS(pfTarget); |
815 | hdr.colorSpace = cmsGetColorSpace(pfTarget); |
816 | hdr.deviceClass = cmsGetDeviceClass(pfTarget); |
817 | hdr.version = cmsGetEncodedICCversion(pfTarget); |
818 | cmsGetHeaderAttributes(pfTarget, &hdr.attributes); |
819 | cmsGetHeaderProfileID(pfTarget, (cmsUInt8Number*)&hdr.profileID); |
820 | |
821 | cmsSetHeaderFlags(p, hdr.flags); |
822 | cmsSetHeaderManufacturer(p, hdr.manufacturer); |
823 | cmsSetHeaderModel(p, hdr.model); |
824 | cmsSetHeaderAttributes(p, hdr.attributes); |
825 | cmsSetHeaderProfileID(p, (cmsUInt8Number*)&(hdr.profileID)); |
826 | cmsSetHeaderRenderingIntent(p, hdr.renderingIntent); |
827 | cmsSetPCS(p, hdr.pcs); |
828 | cmsSetColorSpace(p, hdr.colorSpace); |
829 | cmsSetDeviceClass(p, hdr.deviceClass); |
830 | cmsSetEncodedICCversion(p, hdr.version); |
831 | |
832 | // now write the user supplied tag |
833 | if (size <= 0 || !cmsWriteRawTag(p, sig, pData, size)) { |
834 | cmsCloseProfile(p); |
835 | return NULL; |
836 | } |
837 | |
838 | // copy tags from the original profile |
839 | for (i = 0; i < tagCount; i++) { |
840 | cmsBool isTagReady = FALSE; |
841 | const cmsTagSignature s = cmsGetTagSignature(pfTarget, i); |
842 | const cmsUInt32Number tagSize = cmsReadRawTag(pfTarget, s, NULL, 0); |
843 | |
844 | if (s == sig) { |
845 | // skip the user supplied tag |
846 | continue; |
847 | } |
848 | |
849 | // read raw tag from the original profile |
850 | if (tagSize > 0) { |
851 | cmsUInt8Number* buf = (cmsUInt8Number*)malloc(tagSize); |
852 | if (buf != NULL) { |
853 | if (tagSize == cmsReadRawTag(pfTarget, s, buf, tagSize)) { |
854 | // now we are ready to write the tag |
855 | isTagReady = cmsWriteRawTag(p, s, buf, tagSize); |
856 | } |
857 | free(buf); |
858 | } |
859 | } |
860 | |
861 | if (!isTagReady) { |
862 | cmsCloseProfile(p); |
863 | return NULL; |
864 | } |
865 | } |
866 | |
867 | // now we have all tags moved to the new profile. |
868 | // do some sanity checks: write it to a memory buffer and read again. |
869 | if (cmsSaveProfileToMem(p, NULL, &pfSize)) { |
870 | void* buf = malloc(pfSize); |
871 | if (buf != NULL) { |
872 | // load raw profile data into the buffer |
873 | if (cmsSaveProfileToMem(p, buf, &pfSize)) { |
874 | pfSanity = cmsOpenProfileFromMem(buf, pfSize); |
875 | } |
876 | free(buf); |
877 | } |
878 | } |
879 | |
880 | if (pfSanity == NULL) { |
881 | // for some reason, we failed to save and read the updated profile |
882 | // It likely indicates that the profile is not correct, so we report |
883 | // a failure here. |
884 | cmsCloseProfile(p); |
885 | p = NULL; |
886 | } else { |
887 | // do final check whether we can read and handle the target tag. |
888 | const void* pTag = cmsReadTag(pfSanity, sig); |
889 | if (pTag == NULL) { |
890 | // the tag can not be cooked |
891 | cmsCloseProfile(p); |
892 | p = NULL; |
893 | } |
894 | cmsCloseProfile(pfSanity); |
895 | pfSanity = NULL; |
896 | } |
897 | |
898 | return p; |
899 | } |
900 | |