8263482: Make access to the ICC color profiles data multithread-friendly
Reviewed-by: azvegint
This commit is contained in:
parent
d185655c27
commit
1a21f77971
@ -91,7 +91,19 @@ public class ICC_Profile implements Serializable {
|
||||
@Serial
|
||||
private static final long serialVersionUID = -3938515861990936766L;
|
||||
|
||||
/**
|
||||
* The implementation specific CMM profile, {@code null} if this
|
||||
* {@code ICC_Profile} is not activated by the {@link #cmmProfile()} method.
|
||||
* This field must not be used directly and only via {@link #cmmProfile()}.
|
||||
*/
|
||||
private transient volatile Profile cmmProfile;
|
||||
|
||||
/**
|
||||
* Stores some information about {@code ICC_Profile} without causing a
|
||||
* deferred profile to be loaded. Note that we can defer the loading of
|
||||
* standard profiles only. If this field is null, then {@link #cmmProfile}
|
||||
* should be used to access profile information.
|
||||
*/
|
||||
private transient volatile ProfileDeferralInfo deferralInfo;
|
||||
|
||||
/**
|
||||
@ -917,32 +929,37 @@ public class ICC_Profile implements Serializable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Activates the deferred standard profiles. Implementation of this method
|
||||
* mimics the old behaviour when the CMMException and IOException were
|
||||
* wrapped by the ProfileDataException, and the ProfileDataException itself
|
||||
* was ignored during activation.
|
||||
* Activates and returns the deferred standard profiles. Implementation of
|
||||
* this method mimics the old behaviour when the {@code CMMException} and
|
||||
* {@code IOException} were wrapped by the {@code ProfileDataException}, and
|
||||
* the {@code ProfileDataException} itself was ignored during activation.
|
||||
*
|
||||
* @return the implementation specific CMM profile, or {@code null}
|
||||
*/
|
||||
private void activate() {
|
||||
if (cmmProfile == null) {
|
||||
synchronized (this) {
|
||||
if (cmmProfile != null) {
|
||||
return;
|
||||
}
|
||||
var is = getStandardProfileInputStream(deferralInfo.filename);
|
||||
if (is == null) {
|
||||
return;
|
||||
}
|
||||
try (is) {
|
||||
byte[] data = getProfileDataFromStream(is);
|
||||
if (data != null) {
|
||||
cmmProfile = CMSManager.getModule().loadProfile(data);
|
||||
// from now we cannot use the deferred value, drop it
|
||||
deferralInfo = null;
|
||||
}
|
||||
} catch (CMMException | IOException ignore) {
|
||||
private Profile cmmProfile() {
|
||||
Profile p = cmmProfile;
|
||||
if (p != null) {
|
||||
return p; // one volatile read on common path
|
||||
}
|
||||
synchronized (this) {
|
||||
if (cmmProfile != null) {
|
||||
return cmmProfile;
|
||||
}
|
||||
var is = getStandardProfileInputStream(deferralInfo.filename);
|
||||
if (is == null) {
|
||||
return null;
|
||||
}
|
||||
try (is) {
|
||||
byte[] data = getProfileDataFromStream(is);
|
||||
if (data != null) {
|
||||
p = cmmProfile = CMSManager.getModule().loadProfile(data);
|
||||
// from now we cannot use the deferred value, drop it
|
||||
deferralInfo = null;
|
||||
}
|
||||
} catch (CMMException | IOException ignore) {
|
||||
}
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1006,8 +1023,7 @@ public class ICC_Profile implements Serializable {
|
||||
if (info != null) {
|
||||
return info.colorSpaceType;
|
||||
}
|
||||
activate();
|
||||
return getColorSpaceType(cmmProfile);
|
||||
return getColorSpaceType(cmmProfile());
|
||||
}
|
||||
|
||||
private static int getColorSpaceType(Profile p) {
|
||||
@ -1030,8 +1046,7 @@ public class ICC_Profile implements Serializable {
|
||||
* {@code ColorSpace} class
|
||||
*/
|
||||
public int getPCSType() {
|
||||
activate();
|
||||
byte[] theHeader = getData(cmmProfile, icSigHead);
|
||||
byte[] theHeader = getData(icSigHead);
|
||||
int thePCSSig = intFromBigEndian(theHeader, icHdrPcs);
|
||||
return iccCStoJCS(thePCSSig);
|
||||
}
|
||||
@ -1067,8 +1082,7 @@ public class ICC_Profile implements Serializable {
|
||||
* @see #setData(int, byte[])
|
||||
*/
|
||||
public byte[] getData() {
|
||||
activate();
|
||||
return CMSManager.getModule().getProfileData(cmmProfile);
|
||||
return CMSManager.getModule().getProfileData(cmmProfile());
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1085,8 +1099,8 @@ public class ICC_Profile implements Serializable {
|
||||
* @see #setData(int, byte[])
|
||||
*/
|
||||
public byte[] getData(int tagSignature) {
|
||||
activate();
|
||||
return getData(cmmProfile, tagSignature);
|
||||
byte[] t = getData(cmmProfile(), tagSignature);
|
||||
return t != null ? t.clone() : null;
|
||||
}
|
||||
|
||||
private static byte[] getData(Profile p, int tagSignature) {
|
||||
@ -1115,8 +1129,7 @@ public class ICC_Profile implements Serializable {
|
||||
* @see #getData
|
||||
*/
|
||||
public void setData(int tagSignature, byte[] tagData) {
|
||||
activate();
|
||||
CMSManager.getModule().setTagData(cmmProfile, tagSignature, tagData);
|
||||
CMSManager.getModule().setTagData(cmmProfile(), tagSignature, tagData);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1170,7 +1183,7 @@ public class ICC_Profile implements Serializable {
|
||||
* Returns a float array of length 3 containing the X, Y, and Z components
|
||||
* encoded in an XYZType tag.
|
||||
*/
|
||||
float[] getXYZTag(int tagSignature) {
|
||||
final float[] getXYZTag(int tagSignature) {
|
||||
byte[] theData = getData(tagSignature);
|
||||
float[] theXYZNumber = new float[3]; /* array to return */
|
||||
|
||||
|
@ -33,9 +33,14 @@ import sun.security.action.GetPropertyAction;
|
||||
|
||||
public final class CMSManager {
|
||||
|
||||
private static PCMM cmmImpl = null;
|
||||
private static volatile PCMM cmmImpl;
|
||||
|
||||
public static synchronized PCMM getModule() {
|
||||
public static PCMM getModule() {
|
||||
PCMM loc = cmmImpl;
|
||||
return loc != null ? loc : createModule();
|
||||
}
|
||||
|
||||
private static synchronized PCMM createModule() {
|
||||
if (cmmImpl != null) {
|
||||
return cmmImpl;
|
||||
}
|
||||
|
@ -31,9 +31,8 @@ import java.awt.color.ICC_Profile;
|
||||
import sun.java2d.cmm.ColorTransform;
|
||||
import sun.java2d.cmm.PCMM;
|
||||
import sun.java2d.cmm.Profile;
|
||||
import sun.java2d.cmm.lcms.LCMSProfile.TagData;
|
||||
|
||||
public class LCMS implements PCMM {
|
||||
final class LCMS implements PCMM {
|
||||
|
||||
/* methods invoked from ICC_Profile */
|
||||
@Override
|
||||
@ -48,54 +47,13 @@ public class LCMS implements PCMM {
|
||||
return null;
|
||||
}
|
||||
|
||||
private native long loadProfileNative(byte[] data, Object ref);
|
||||
|
||||
private LCMSProfile getLcmsProfile(Profile p) {
|
||||
private static LCMSProfile getLcmsProfile(Profile p) {
|
||||
if (p instanceof LCMSProfile) {
|
||||
return (LCMSProfile)p;
|
||||
}
|
||||
throw new CMMException("Invalid profile: " + p);
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] getProfileData(final Profile p) {
|
||||
LCMSProfile lcmsProfile = getLcmsProfile(p);
|
||||
synchronized (lcmsProfile) {
|
||||
return getProfileDataNative(lcmsProfile.getLcmsPtr());
|
||||
}
|
||||
}
|
||||
|
||||
private native byte[] getProfileDataNative(long ptr);
|
||||
|
||||
static native byte[] getTagNative(long profileID, int signature);
|
||||
|
||||
@Override
|
||||
public byte[] getTagData(Profile p, int tagSignature) {
|
||||
final LCMSProfile lcmsProfile = getLcmsProfile(p);
|
||||
synchronized (lcmsProfile) {
|
||||
TagData t = lcmsProfile.getTag(tagSignature);
|
||||
return t != null ? t.getData() : null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void setTagData(Profile p, int tagSignature, byte[] data) {
|
||||
final LCMSProfile profile = getLcmsProfile(p);
|
||||
|
||||
synchronized (profile) {
|
||||
profile.clearTagCache();
|
||||
|
||||
// Now we are going to update the profile with new tag data
|
||||
// In some cases, we may change the pointer to the native
|
||||
// profile.
|
||||
//
|
||||
// If we fail to write tag data for any reason, the old pointer
|
||||
// should be used.
|
||||
setTagDataNative(profile.getLcmsPtr(),
|
||||
tagSignature, data);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes supplied data as a tag into the profile.
|
||||
* Destroys old profile, if new one was successfully
|
||||
@ -106,10 +64,27 @@ public class LCMS implements PCMM {
|
||||
* Throws CMMException if operation fails, preserve old profile from
|
||||
* destruction.
|
||||
*/
|
||||
private native void setTagDataNative(long ptr, int tagSignature,
|
||||
byte[] data);
|
||||
static native void setTagDataNative(long ptr, int tagSignature, byte[] data);
|
||||
static native byte[] getProfileDataNative(long ptr);
|
||||
static native byte[] getTagNative(long profileID, int signature);
|
||||
private static native long loadProfileNative(byte[] data, Object ref);
|
||||
|
||||
public static synchronized native LCMSProfile getProfileID(ICC_Profile profile);
|
||||
@Override
|
||||
public byte[] getProfileData(Profile p) {
|
||||
return getLcmsProfile(p).getProfileData();
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] getTagData(Profile p, int tagSignature) {
|
||||
return getLcmsProfile(p).getTag(tagSignature);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void setTagData(Profile p, int tagSignature, byte[] data) {
|
||||
getLcmsProfile(p).setTag(tagSignature, data);
|
||||
}
|
||||
|
||||
static synchronized native LCMSProfile getProfileID(ICC_Profile profile);
|
||||
|
||||
/* Helper method used from LCMSColorTransfrom */
|
||||
static long createTransform(
|
||||
|
@ -25,69 +25,63 @@
|
||||
|
||||
package sun.java2d.cmm.lcms;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.locks.StampedLock;
|
||||
|
||||
import sun.java2d.cmm.Profile;
|
||||
|
||||
final class LCMSProfile extends Profile {
|
||||
private final TagCache tagCache;
|
||||
|
||||
private final Object disposerReferent;
|
||||
private final Map<Integer, byte[]> tags = new ConcurrentHashMap<>();
|
||||
private final StampedLock lock = new StampedLock();
|
||||
|
||||
LCMSProfile(long ptr, Object ref) {
|
||||
super(ptr);
|
||||
|
||||
disposerReferent = ref;
|
||||
|
||||
tagCache = new TagCache(this);
|
||||
}
|
||||
|
||||
long getLcmsPtr() {
|
||||
return this.getNativePtr();
|
||||
return getNativePtr();
|
||||
}
|
||||
|
||||
TagData getTag(int sig) {
|
||||
return tagCache.getTag(sig);
|
||||
}
|
||||
|
||||
void clearTagCache() {
|
||||
tagCache.clear();
|
||||
}
|
||||
|
||||
private static final class TagCache {
|
||||
private final LCMSProfile profile;
|
||||
private final HashMap<Integer, TagData> tags = new HashMap<>();
|
||||
|
||||
private TagCache(LCMSProfile p) {
|
||||
profile = p;
|
||||
byte[] getProfileData() {
|
||||
long stamp = lock.readLock();
|
||||
try {
|
||||
return LCMS.getProfileDataNative(getNativePtr());
|
||||
} finally {
|
||||
lock.unlockRead(stamp);
|
||||
}
|
||||
}
|
||||
|
||||
private TagData getTag(int sig) {
|
||||
TagData t = tags.get(sig);
|
||||
if (t == null) {
|
||||
byte[] tagData = LCMS.getTagNative(profile.getNativePtr(), sig);
|
||||
if (tagData != null) {
|
||||
t = new TagData(tagData);
|
||||
tags.put(sig, t);
|
||||
}
|
||||
}
|
||||
byte[] getTag(int sig) {
|
||||
byte[] t = tags.get(sig);
|
||||
if (t != null) {
|
||||
return t;
|
||||
}
|
||||
|
||||
private void clear() {
|
||||
tags.clear();
|
||||
long stamp = lock.readLock();
|
||||
try {
|
||||
return tags.computeIfAbsent(sig, (key) -> {
|
||||
return LCMS.getTagNative(getNativePtr(), key);
|
||||
});
|
||||
} finally {
|
||||
lock.unlockRead(stamp);
|
||||
}
|
||||
}
|
||||
|
||||
static final class TagData {
|
||||
private final byte[] data;
|
||||
|
||||
TagData(byte[] data) {
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
byte[] getData() {
|
||||
return data.clone();
|
||||
void setTag(int tagSignature, byte[] data) {
|
||||
long stamp = lock.writeLock();
|
||||
try {
|
||||
tags.clear();
|
||||
// Now we are going to update the profile with new tag data
|
||||
// In some cases, we may change the pointer to the native profile.
|
||||
//
|
||||
// If we fail to write tag data for any reason, the old pointer
|
||||
// should be used.
|
||||
LCMS.setTagDataNative(getNativePtr(), tagSignature, data);
|
||||
} finally {
|
||||
lock.unlockWrite(stamp);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -128,7 +128,7 @@ void LCMS_freeTransform(JNIEnv *env, jlong ID)
|
||||
/*
|
||||
* Class: sun_java2d_cmm_lcms_LCMS
|
||||
* Method: createNativeTransform
|
||||
* Signature: ([JI)J
|
||||
* Signature: ([JIIZIZLjava/lang/Object;)J
|
||||
*/
|
||||
JNIEXPORT jlong JNICALL Java_sun_java2d_cmm_lcms_LCMS_createNativeTransform
|
||||
(JNIEnv *env, jclass cls, jlongArray profileIDs, jint renderType,
|
||||
@ -214,11 +214,11 @@ JNIEXPORT jlong JNICALL Java_sun_java2d_cmm_lcms_LCMS_createNativeTransform
|
||||
|
||||
/*
|
||||
* Class: sun_java2d_cmm_lcms_LCMS
|
||||
* Method: loadProfile
|
||||
* Signature: ([B,Lsun/java2d/cmm/lcms/LCMSProfile;)V
|
||||
* Method: loadProfileNative
|
||||
* Signature: ([BLjava/lang/Object;)J
|
||||
*/
|
||||
JNIEXPORT jlong JNICALL Java_sun_java2d_cmm_lcms_LCMS_loadProfileNative
|
||||
(JNIEnv *env, jobject obj, jbyteArray data, jobject disposerRef)
|
||||
(JNIEnv *env, jclass cls, jbyteArray data, jobject disposerRef)
|
||||
{
|
||||
jbyte* dataArray;
|
||||
jint dataSize;
|
||||
@ -278,10 +278,10 @@ JNIEXPORT jlong JNICALL Java_sun_java2d_cmm_lcms_LCMS_loadProfileNative
|
||||
/*
|
||||
* Class: sun_java2d_cmm_lcms_LCMS
|
||||
* Method: getProfileDataNative
|
||||
* Signature: (J[B)V
|
||||
* Signature: (J)[B
|
||||
*/
|
||||
JNIEXPORT jbyteArray JNICALL Java_sun_java2d_cmm_lcms_LCMS_getProfileDataNative
|
||||
(JNIEnv *env, jobject obj, jlong id)
|
||||
(JNIEnv *env, jclass cls, jlong id)
|
||||
{
|
||||
lcmsProfile_p sProf = (lcmsProfile_p)jlong_to_ptr(id);
|
||||
cmsUInt32Number pfSize = 0;
|
||||
@ -325,11 +325,11 @@ static cmsHPROFILE _writeCookedTag(cmsHPROFILE pfTarget, cmsTagSignature sig, jb
|
||||
|
||||
/*
|
||||
* Class: sun_java2d_cmm_lcms_LCMS
|
||||
* Method: getTagData
|
||||
* Signature: (JI[B)V
|
||||
* Method: getTagNative
|
||||
* Signature: (JI)[B
|
||||
*/
|
||||
JNIEXPORT jbyteArray JNICALL Java_sun_java2d_cmm_lcms_LCMS_getTagNative
|
||||
(JNIEnv *env, jobject obj, jlong id, jint tagSig)
|
||||
(JNIEnv *env, jclass cls, jlong id, jint tagSig)
|
||||
{
|
||||
lcmsProfile_p sProf = (lcmsProfile_p)jlong_to_ptr(id);
|
||||
TagSignature_t sig;
|
||||
@ -410,11 +410,11 @@ JNIEXPORT jbyteArray JNICALL Java_sun_java2d_cmm_lcms_LCMS_getTagNative
|
||||
|
||||
/*
|
||||
* Class: sun_java2d_cmm_lcms_LCMS
|
||||
* Method: setTagData
|
||||
* Method: setTagDataNative
|
||||
* Signature: (JI[B)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_sun_java2d_cmm_lcms_LCMS_setTagDataNative
|
||||
(JNIEnv *env, jobject obj, jlong id, jint tagSig, jbyteArray data)
|
||||
(JNIEnv *env, jclass cls, jlong id, jint tagSig, jbyteArray data)
|
||||
{
|
||||
lcmsProfile_p sProf = (lcmsProfile_p)jlong_to_ptr(id);
|
||||
cmsHPROFILE pfReplace = NULL;
|
||||
@ -510,7 +510,7 @@ void releaseILData (JNIEnv *env, void* pData, jint dataType,
|
||||
* Signature: (Lsun/java2d/cmm/lcms/LCMSTransform;Lsun/java2d/cmm/lcms/LCMSImageLayout;Lsun/java2d/cmm/lcms/LCMSImageLayout;)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_sun_java2d_cmm_lcms_LCMS_colorConvert
|
||||
(JNIEnv *env, jclass obj, jobject trans, jobject src, jobject dst)
|
||||
(JNIEnv *env, jclass cls, jobject trans, jobject src, jobject dst)
|
||||
{
|
||||
cmsHTRANSFORM sTrans = NULL;
|
||||
int srcDType, dstDType;
|
||||
@ -579,50 +579,32 @@ JNIEXPORT void JNICALL Java_sun_java2d_cmm_lcms_LCMS_colorConvert
|
||||
/*
|
||||
* Class: sun_java2d_cmm_lcms_LCMS
|
||||
* Method: getProfileID
|
||||
* Signature: (Ljava/awt/color/ICC_Profile;)Lsun/java2d/cmm/lcms/LCMSProfile
|
||||
* Signature: (Ljava/awt/color/ICC_Profile;)Lsun/java2d/cmm/lcms/LCMSProfile;
|
||||
*/
|
||||
JNIEXPORT jobject JNICALL Java_sun_java2d_cmm_lcms_LCMS_getProfileID
|
||||
(JNIEnv *env, jclass cls, jobject pf)
|
||||
{
|
||||
jclass clsLcmsProfile;
|
||||
jobject cmmProfile;
|
||||
jfieldID fid;
|
||||
|
||||
if (pf == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
jclass pcls = (*env)->GetObjectClass(env, pf);
|
||||
if (pcls == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
jmethodID mid = (*env)->GetMethodID(env, pcls, "activate", "()V");
|
||||
jmethodID mid = (*env)->GetMethodID(env, pcls, "cmmProfile",
|
||||
"()Lsun/java2d/cmm/Profile;");
|
||||
if (mid == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
(*env)->CallVoidMethod(env, pf, mid);
|
||||
jobject cmmProfile = (*env)->CallObjectMethod(env, pf, mid);
|
||||
if ((*env)->ExceptionOccurred(env)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
fid = (*env)->GetFieldID(env, pcls, "cmmProfile",
|
||||
"Lsun/java2d/cmm/Profile;");
|
||||
if (fid == NULL) {
|
||||
jclass lcmsPCls = (*env)->FindClass(env, "sun/java2d/cmm/lcms/LCMSProfile");
|
||||
if (lcmsPCls == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
clsLcmsProfile = (*env)->FindClass(env,
|
||||
"sun/java2d/cmm/lcms/LCMSProfile");
|
||||
if (clsLcmsProfile == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
cmmProfile = (*env)->GetObjectField (env, pf, fid);
|
||||
|
||||
if (JNU_IsNull(env, cmmProfile)) {
|
||||
return NULL;
|
||||
}
|
||||
if ((*env)->IsInstanceOf(env, cmmProfile, clsLcmsProfile)) {
|
||||
if ((*env)->IsInstanceOf(env, cmmProfile, lcmsPCls)) {
|
||||
return cmmProfile;
|
||||
}
|
||||
return NULL;
|
||||
|
Loading…
x
Reference in New Issue
Block a user