bundle/src/main/java/com/adobe/acs/commons/dam/impl/ColorConversionImpl.java
/*
* ACS AEM Commons
*
* Copyright (C) 2013 - 2023 Adobe
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.adobe.acs.commons.dam.impl;
import com.adobe.acs.commons.dam.ColorConversion;
import org.apache.felix.scr.annotations.Activate;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Property;
import org.apache.felix.scr.annotations.PropertyOption;
import org.apache.felix.scr.annotations.Service;
import org.apache.sling.commons.osgi.PropertiesUtil;
import java.awt.color.ColorSpace;
import java.awt.color.ICC_ColorSpace;
import java.awt.color.ICC_Profile;
import java.io.InputStream;
import java.util.Map;
@Component(label = "ACS AEM Commons - Color Conversion", description = "ACS AEM Commons - Color Conversion", metatype = true)
@Service
@SuppressWarnings({"checkstyle:abbreviationaswordinname", "checkstyle:localvariablename"})
public final class ColorConversionImpl implements ColorConversion { //nosonar
private static final String DEFAULT_CMYK_PROFILE = "JapanColor2001Coated";
@Property(label = "CMYK ICC Profile", description = "ICC Profile for CMYK to RGB Conversion",
value = DEFAULT_CMYK_PROFILE,
options = {
@PropertyOption(name = "CoatedFOGRA27", value = "CoatedFOGRA27"),
@PropertyOption(name = "CoatedFOGRA39", value = "CoatedFOGRA39"),
@PropertyOption(name = "JapanColor2001Coated", value = "JapanColor2001Coated"),
@PropertyOption(name = "JapanColor2001Uncoated", value = "JapanColor2001Uncoated"),
@PropertyOption(name = "JapanColor2002Newspaper", value = "JapanColor2002Newspaper"),
@PropertyOption(name = "JapanWebCoated", value = "JapanWebCoated"),
@PropertyOption(name = "UncoatedFOGRA29", value = "UncoatedFOGRA29"),
@PropertyOption(name = "USSheetfedCoated", value = "USSheetfedCoated"),
@PropertyOption(name = "USSheetfedUncoated", value = "USSheetfedUncoated"),
@PropertyOption(name = "USWebCoatedSWOP", value = "USWebCoatedSWOP"),
@PropertyOption(name = "USWebUncoated", value = "USWebUncoated"),
@PropertyOption(name = "WebCoatedFOGRA28", value = "WebCoatedFOGRA28")
})
private static final String PROP_CMKY_PROFILE = "cmyk.icc.profile";
/**
* XYZ to sRGB conversion matrix
*/
private static final double[][] xyzTosRgbMatrix = {{ 3.2406, -1.5372, -0.4986},
{-0.9689, 1.8758, 0.0415},
{ 0.0557, -0.2040, 1.0570}};
@Activate
protected void activate(Map<String, Object> properties) throws Exception {
String profileName = PropertiesUtil.toString(properties.get(PROP_CMKY_PROFILE), DEFAULT_CMYK_PROFILE);
InputStream iccData = getClass().getClassLoader().getResourceAsStream("icc/cmyk/" + profileName + ".icc");
ICC_Profile profile = ICC_Profile.getInstance(iccData);
cmykColorSpace = new ICC_ColorSpace(profile);
}
private ColorSpace cmykColorSpace;
@Override
public RGB toRGB(CMYK cymk) {
float[] input = new float[] {
((float) cymk.cyan) / 100,
((float) cymk.magenta) / 100,
((float) cymk.yellow) / 100,
((float) cymk.black) / 100
};
float[] output = cmykColorSpace.toRGB(input);
RGB rgb = new RGB(
Math.round(output[0] * 255),
Math.round(output[1] * 255),
Math.round(output[2] * 255)
);
return rgb;
}
@Override
public RGB toRGB(LAB lab) {
XYZ xyz = toXYZ(lab);
double x = xyz.x / 100.0;
double y = xyz.y / 100.0;
double z = xyz.z / 100.0;
// [r g b] = [X Y Z][xyzTosRgbMatrix]
double r = (x * xyzTosRgbMatrix[0][0]) + (y * xyzTosRgbMatrix[0][1]) + (z * xyzTosRgbMatrix[0][2]);
double g = (x * xyzTosRgbMatrix[1][0]) + (y * xyzTosRgbMatrix[1][1]) + (z * xyzTosRgbMatrix[1][2]);
double b = (x * xyzTosRgbMatrix[2][0]) + (y * xyzTosRgbMatrix[2][1]) + (z * xyzTosRgbMatrix[2][2]);
// assume sRGB
if (r > 0.0031308) {
r = ((1.055 * Math.pow(r, 1.0 / 2.4)) - 0.055);
} else {
r = (r * 12.92);
}
if (g > 0.0031308) {
g = ((1.055 * Math.pow(g, 1.0 / 2.4)) - 0.055);
} else {
g = (g * 12.92);
}
if (b > 0.0031308) {
b = ((1.055 * Math.pow(b, 1.0 / 2.4)) - 0.055);
} else {
b = (b * 12.92);
}
r = (r < 0) ? 0 : r;
g = (g < 0) ? 0 : g;
b = (b < 0) ? 0 : b;
// convert 0..1 into 0..255
RGB rgb = new RGB(
(int) Math.round(r * 255),
(int) Math.round(g * 255),
(int) Math.round(b * 255)
);
return rgb;
}
private XYZ toXYZ(LAB lab) {
double y = (lab.lightness + 16.0) / 116.0;
double y3 = Math.pow(y, 3.0);
double x = (lab.a / 500.0) + y;
double x3 = Math.pow(x, 3.0);
double z = y - (lab.b / 200.0);
double z3 = Math.pow(z, 3.0);
if (y3 > 0.008856) {
y = y3;
}
else {
y = (y - (16.0 / 116.0)) / 7.787;
}
if (x3 > 0.008856) {
x = x3;
}
else {
x = (x - (16.0 / 116.0)) / 7.787;
}
if (z3 > 0.008856) {
z = z3;
}
else {
z = (z - (16.0 / 116.0)) / 7.787;
}
XYZ result = new XYZ();
result.x = x * 95.0429;
result.y = y * 100;
result.z = z * 108.8900;
return result;
}
@SuppressWarnings("checkstyle.membername")
private class XYZ {
private double x;
private double y;
private double z;
}
}