blob: ff535b1561ca7caa9bc3dbee8b98289940c8ce30 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.
*/
/**
* @author Oleg V. Khaschansky
*
*/
#include "NativeCMM.h"
#include <string.h>
static LCMSBOOL cmsInitialized = FALSE;
static char *errMsg = NULL;
int gl_cmsErrorHandler(int errorCode, const char *msg) {
if(errorCode == LCMS_ERRC_ABORTED) {
// Throw exception later, after returning control from cmm
#if defined(ZOS) || defined(LINUX) || defined(FREEBSD) || defined(MACOSX)
errMsg = strdup(msg);
#else
errMsg = _strdup(msg);
#endif
}
return 1;
}
/*
* Class: org_apache_harmony_awt_gl_color_NativeCMM
* Method: cmmOpenProfile
* Signature: ([B)J
*/
JNIEXPORT jlong JNICALL
Java_org_apache_harmony_awt_gl_color_NativeCMM_cmmOpenProfile(JNIEnv *env, jclass cls, jbyteArray data)
{
jbyte *byteData = (*env)->GetByteArrayElements (env, data, 0);
DWORD dataSize = (*env)->GetArrayLength (env, data);
cmsHPROFILE hProfile;
// Set up error handling if needed
if(!cmsInitialized) {
cmsErrorAction(LCMS_ERROR_SHOW);
cmsSetErrorHandler(gl_cmsErrorHandler);
cmsInitialized = TRUE;
}
hProfile = cmmOpenProfile((LPBYTE)byteData, dataSize);
(*env)->ReleaseByteArrayElements (env, data, byteData, 0);
if(hProfile == NULL) {
newCMMException(env, errMsg); // Throw java exception if error occured
free(errMsg);
errMsg = NULL;
}
// Return obtained handle
return (jlong) ((IDATA)hProfile);
}
/*
* Class: org_apache_harmony_awt_gl_color_NativeCMM
* Method: cmmCloseProfile
* Signature: (J)V
*/
JNIEXPORT void JNICALL
Java_org_apache_harmony_awt_gl_color_NativeCMM_cmmCloseProfile(JNIEnv *env, jclass cls, jlong profileID)
{
cmsHPROFILE hProfile = (cmsHPROFILE) ((IDATA)profileID);
if(!cmsCloseProfile(hProfile)) {
newCMMException(env, errMsg); // Throw java exception if error occured
free(errMsg);
errMsg = NULL;
}
}
/*
* Class: org_apache_harmony_awt_gl_color_NativeCMM
* Method: cmmGetProfileSize
* Signature: (J)I
*/
JNIEXPORT jint JNICALL
Java_org_apache_harmony_awt_gl_color_NativeCMM_cmmGetProfileSize(JNIEnv *env, jclass cls, jlong profileID)
{
cmsHPROFILE hProfile = (cmsHPROFILE) ((IDATA)profileID);
return (jint) cmmGetProfileSize(hProfile);
}
/*
* Class: org_apache_harmony_awt_gl_color_NativeCMM
* Method: cmmGetProfile
* Signature: (J[B)V
*/
JNIEXPORT void JNICALL Java_org_apache_harmony_awt_gl_color_NativeCMM_cmmGetProfile(
JNIEnv *env,
jclass cls,
jlong profileID,
jbyteArray data)
{
cmsHPROFILE hProfile = (cmsHPROFILE) ((IDATA)profileID);
unsigned profileSize = (unsigned) (*env)->GetArrayLength (env, data);
jbyte *byteData = (*env)->GetByteArrayElements(env, data, 0);
cmmGetProfile(hProfile, (LPBYTE)byteData, profileSize);
(*env)->ReleaseByteArrayElements (env, data, byteData, 0);
}
/*
* Class: org_apache_harmony_awt_gl_color_NativeCMM
* Method: cmmGetProfileElement
* Signature: (JI[B)V
*/
JNIEXPORT void JNICALL Java_org_apache_harmony_awt_gl_color_NativeCMM_cmmGetProfileElement
(JNIEnv *env, jclass cls, jlong profileID, jint tagSignature, jbyteArray data)
{
size_t dataSize = (*env)->GetArrayLength(env, data);
icTagSignature ts = tagSignature;
cmsHPROFILE hProfile = (cmsHPROFILE) ((IDATA)profileID);
jbyte *byteData = (*env)->GetByteArrayElements (env, data, 0);
if(ts == HEADER_TAG_ID) {
if(!cmmGetProfileHeader(hProfile, (LPBYTE)byteData, dataSize)) {
newCMMException(env, errMsg); // Throw java exception if error occured
free(errMsg);
errMsg = NULL;
}
} else {
if(!cmmGetProfileElement(hProfile, ts, (LPBYTE)byteData, &dataSize)) {
newCMMException(env, errMsg); // Throw java exception if error occured
free(errMsg);
errMsg = NULL;
}
}
(*env)->ReleaseByteArrayElements (env, data, byteData, 0);
}
/*
* Class: org_apache_harmony_awt_gl_color_NativeCMM
* Method: cmmGetProfileElementSize
* Signature: (JI)I
*/
JNIEXPORT jint JNICALL Java_org_apache_harmony_awt_gl_color_NativeCMM_cmmGetProfileElementSize
(JNIEnv *env, jclass cls, jlong profileID, jint tagSignature)
{
long size;
icTagSignature ts = tagSignature;
cmsHPROFILE hProfile = (cmsHPROFILE) ((IDATA)profileID);
if (ts == HEADER_TAG_ID) {
size = HEADER_SIZE;
} else {
size = cmmGetProfileElementSize(hProfile, ts);
}
if(size < 0)
newCMMException(env, "Profile element not found"); // Throw java exception if error occured
return (jint) size;
}
/*
* Class: org_apache_harmony_awt_gl_color_NativeCMM
* Method: cmmSetProfileElement
* Signature: (JI[B)V
*/
JNIEXPORT void JNICALL Java_org_apache_harmony_awt_gl_color_NativeCMM_cmmSetProfileElement
(JNIEnv *env, jclass cls, jlong profileID, jint tagSignature, jbyteArray data)
{
cmsHPROFILE hProfile = (cmsHPROFILE) ((IDATA)profileID);
jbyte *byteData = (*env)->GetByteArrayElements (env, data, 0);
size_t dataSize = (*env)->GetArrayLength(env, data);
icTagSignature ts = tagSignature;
if(ts == HEADER_TAG_ID) {
if(dataSize != sizeof(icHeader))
newCMMException(env, "Invalid size of the data"); // Throw java exception
if(!cmmSetProfileHeader(hProfile, (LPBYTE)byteData))
newCMMException(env, "Invalid header data"); // Throw java exception if error occured
} else {
if(!cmmSetProfileElement(hProfile, ts, byteData, dataSize)) {
newCMMException(env, errMsg); // Throw java exception if error occured
free(errMsg);
errMsg = NULL;
}
}
(*env)->ReleaseByteArrayElements (env, data, byteData, 0);
}
/*
* Class: org_apache_harmony_awt_gl_color_NativeCMM
* Method: cmmCreateMultiprofileTransform
* Signature: ([J[I)J
*/
JNIEXPORT jlong JNICALL Java_org_apache_harmony_awt_gl_color_NativeCMM_cmmCreateMultiprofileTransform
(JNIEnv *env, jclass cls, jlongArray jProfileHandles, jintArray jRenderingIntents) {
cmsHTRANSFORM xform;
jint *renderingIntentsData;
int intent, i;
int nProfiles = (*env)->GetArrayLength (env, jProfileHandles);
jlong *profileHandlesData = (*env)->GetLongArrayElements (env, jProfileHandles, 0);
// Convert to appropriate size
cmsHPROFILE *profileHandles = malloc(sizeof(cmsHPROFILE)*nProfiles);
for(i=0; i<nProfiles; i++) {
profileHandles[i] = (cmsHPROFILE) ((IDATA)profileHandlesData[i]);
}
// XXX - Todo - consider getting all rendering intents
renderingIntentsData = (*env)->GetIntArrayElements(env, jRenderingIntents, 0);
intent = renderingIntentsData[0];
xform = cmmCreateMultiprofileTransform(profileHandles, nProfiles, intent);
(*env)->ReleaseLongArrayElements(env, jProfileHandles, profileHandlesData, 0);
(*env)->ReleaseIntArrayElements(env, jRenderingIntents, renderingIntentsData, 0);
free(profileHandles);
if(xform == NULL)
newCMMException(env, "Can't create ICC transform"); // Throw java exception
return (jlong) ((IDATA)xform);
}
/*
* Class: org_apache_harmony_awt_gl_color_NativeCMM
* Method: cmmDeleteTransform
* Signature: (J)V
*/
JNIEXPORT void JNICALL Java_org_apache_harmony_awt_gl_color_NativeCMM_cmmDeleteTransform
(JNIEnv *env, jclass cls, jlong transformHandle)
{
cmsHTRANSFORM xform = (cmsHTRANSFORM) ((IDATA)transformHandle);
if(xform != NULL) {
cmsDeleteTransform(xform);
} else
throwNPException(env, "Invalid ICC transform passed to CMM");
}
static long getScanlineStrideFromFormat(ImageFormat *fmt) {
return
fmt->cols *
T_BYTES(fmt->cmmFormat) *
(T_CHANNELS(fmt->cmmFormat) + T_EXTRA(fmt->cmmFormat));
}
static long getPixelStrideFromFormat(ImageFormat *fmt) {
return
T_BYTES(fmt->cmmFormat) *
(T_CHANNELS(fmt->cmmFormat) + T_EXTRA(fmt->cmmFormat));
}
static long getSampleSizeFromFormat(ImageFormat *fmt) {
return T_BYTES(fmt->cmmFormat) ? T_BYTES(fmt->cmmFormat) : 8;
}
static void copyAlphaChannel(
BYTE *srcPtr, BYTE *dstPtr,
int srcPixelStride, int dstPixelStride,
int srcSampleSize, int dstSampleSize,
int nPixels
) {
register LPBYTE src = srcPtr, dst = dstPtr;
int i;
if(srcSampleSize == 1 && dstSampleSize == 1) {
for(i=0; i<nPixels; i++) {
*dst=*src;
src += srcPixelStride;
dst += dstPixelStride;
}
} else if(srcSampleSize == 2 && dstSampleSize == 2) {
for(i=0; i<nPixels; i++) {
*dst=*src;
*(dst+1)=*(src+1);
src += srcPixelStride;
dst += dstPixelStride;
}
} else if(srcSampleSize == 2 && dstSampleSize == 1) {
for(i=0; i<nPixels; i++) {
*((LPWORD)dst)=RGB_8_TO_16(*src);
src += srcPixelStride;
dst += dstPixelStride;
}
} else if(srcSampleSize == 1 && dstSampleSize == 2) {
for(i=0; i<nPixels; i++) {
*dst=RGB_16_TO_8(*((LPWORD)src));
src += srcPixelStride;
dst += dstPixelStride;
}
} else { // All other sample types, very slow
double d = 0;
double srcMax = 1 << srcSampleSize*8;
double dstMax = 1 << dstSampleSize*8;
for(i=0; i<nPixels; i++) {
if(srcSampleSize == 0) {
d = *((double *) (src));
} else {
int sh;
for(sh=0; sh<srcSampleSize; sh++) {
d+=((*(src+sh)) << (sh*8));
}
d /= (srcMax-1);
}
if(dstSampleSize == 0) {
*((double *) (src)) = d;
} else {
int sh;
long l = (long)(d * (dstMax-1) + 0.5);
for(sh=0; sh<dstSampleSize; sh++) {
*(dst+sh) = (BYTE) (l % 256);
l /= 256;
}
}
src += srcPixelStride;
dst += dstPixelStride;
}
}
}
/*
* Class: org_apache_harmony_awt_gl_color_NativeCMM
* Method: cmmTranslateColors
* Signature: (JLorg/apache/harmony/awt/gl/color/NativeImageFormat;Lorg/apache/harmony/awt/gl/color/NativeImageFormat;)V
*/
JNIEXPORT void JNICALL Java_org_apache_harmony_awt_gl_color_NativeCMM_cmmTranslateColors
(JNIEnv *env, jclass cls, jlong transformHandle, jobject src, jobject dst)
{
int srcPixels, dstPixels;
ImageFormat *srcFormat, *dstFormat;
int srcSampleSize, dstSampleSize;
int srcPixelStride, dstPixelStride;
BYTE *srcPtr, *dstPtr;
int i;
LCMSBOOL copyAlpha = FALSE;
LCMSBOOL fillAlpha = FALSE;
cmsHTRANSFORM xform = (cmsHTRANSFORM) ((IDATA)transformHandle);
srcFormat = getImageFormat(env, src);
dstFormat = getImageFormat(env, dst);
// Do we have to copy alpha?
copyAlpha = srcFormat->alphaOffset >= 0 && dstFormat->alphaOffset >= 0;
fillAlpha = dstFormat->alphaOffset >= 0;
if(copyAlpha) {
srcSampleSize = getSampleSizeFromFormat(srcFormat);
dstSampleSize = getSampleSizeFromFormat(dstFormat);
srcPixelStride = getPixelStrideFromFormat(srcFormat);
dstPixelStride = getPixelStrideFromFormat(dstFormat);
} else if(fillAlpha) {
dstPixelStride = getPixelStrideFromFormat(dstFormat);
}
srcPixels = srcFormat->cols * srcFormat->rows;
dstPixels = dstFormat->cols * dstFormat->rows;
cmsChangeBuffersFormat(xform, srcFormat->cmmFormat, dstFormat->cmmFormat);
srcPtr = srcFormat->imageData + srcFormat->dataOffset;
dstPtr = dstFormat->imageData + dstFormat->dataOffset;
if(srcFormat->scanlineStride < 0 && dstFormat->scanlineStride < 0) {
if(copyAlpha) { // Copy alpha
copyAlphaChannel(
srcPtr + srcFormat->alphaOffset, dstPtr + dstFormat->alphaOffset,
srcPixelStride, dstPixelStride,
srcSampleSize, dstSampleSize,
dstPixels
);
} else if(fillAlpha) { // Fill with 1's
memset(dstPtr, 0xFF, dstPixels*dstPixelStride);
}
cmsDoTransform(xform, srcPtr, dstPtr, dstPixels); // Call LCMS!
} else { // Process each scanline
if(srcFormat->scanlineStride < 0)
srcFormat->scanlineStride = getScanlineStrideFromFormat(srcFormat);
if(dstFormat->scanlineStride < 0)
dstFormat->scanlineStride = getScanlineStrideFromFormat(dstFormat);
for(i=0; i<srcFormat->rows; i++) {
if(copyAlpha) { // Copy Alpha
copyAlphaChannel(
srcPtr + srcFormat->alphaOffset, dstPtr + dstFormat->alphaOffset,
srcPixelStride, dstPixelStride,
srcSampleSize, dstSampleSize,
srcFormat->cols
);
} else if(fillAlpha) { // Fill with 1's
memset(dstPtr, 0xFF, dstFormat->cols*dstPixelStride);
}
cmsDoTransform(xform, srcPtr, dstPtr, dstFormat->cols);
srcPtr += srcFormat->scanlineStride;
dstPtr += dstFormat->scanlineStride;
}
}
releaseImageFormat(env, srcFormat);
releaseImageFormat(env, dstFormat);
}
/*
void main() {
DWORD size;
size_t tagSz;
LPLCMSICCPROFILE hPf, hPfGrey;
LPVOID pyccBuff, greyBuff;
DWORD sig;
unsigned char* data = NULL;
cmsHTRANSFORM xform, xform1;
FILE *f = fopen("C:\\jrockit-j2sdk1.4.2_04\\jre\\lib\\cmm\\srgb.pf", "rb");
fseek(f, 0L, SEEK_END);
size = ftell(f);
pyccBuff = malloc(size);
fseek(f, 0L, SEEK_SET);
fread(pyccBuff, 1, size, f);
fclose(f);
hPf = cmmOpenProfile(pyccBuff, size);
sig = TAG_SIGNATURE('w','t','p','t');
tagSz = cmmGetProfileElementSize(hPf, sig);
data = malloc(tagSz);
cmmGetProfileElement(hPf, sig, data, &tagSz);
memset(data, 0, tagSz);
data[0] = 0x58;
data[1] = 0x59;
data[2] = 0x5A;
data[3] = 0x20;
cmmSetProfileElement(hPf, sig, data, tagSz);
free(data);
// Save the profile
size = (DWORD) cmmGetProfileSize(hPf);
pyccBuff = realloc(pyccBuff, size);
cmmGetProfile(hPf, pyccBuff, size);
f = fopen("C:\\jrockit-j2sdk1.4.2_04\\jre\\lib\\cmm\\qq.icc", "wb");
fwrite(pyccBuff, 1, size, f);
fclose(f);
///
cmmCloseProfile(hPf);
hPf = cmmOpenProfile(pyccBuff, size);
///
// Open Grey profile
f = fopen("C:\\jrockit-j2sdk1.4.2_04\\jre\\lib\\cmm\\gray.pf", "rb");
fseek(f, 0L, SEEK_END);
size = ftell(f);
greyBuff = malloc(size);
fseek(f, 0L, SEEK_SET);
fread(greyBuff, 1, size, f);
fclose(f);
hPfGrey = cmmOpenProfile(greyBuff, size);
/////////////////////
// Transforms
{
unsigned char in[4] = {0, 0, 0, 0};
unsigned char gray[2] = {0,0};
unsigned char out[4];
xform = cmmCreateTransform(hPf, TYPE_RGBA_8, hPfGrey, TYPE_GRAYA_8, INTENT_PERCEPTUAL, 0);
xform1 = cmmCreateTransform(hPfGrey, TYPE_GRAYA_8, hPf, TYPE_RGBA_8, INTENT_PERCEPTUAL, 0);
cmsDoTransform(xform, in, gray, 1);
cmsDoTransform(xform1, gray, out, 1);
cmsDeleteTransform(xform);
cmsDeleteTransform(xform1);
// LPLCMSICCPROFILE profiles[3] = {hPf, hPfGrey, hPf};
// xform = cmmCreateMultiprofileTransform(profiles, 3, INTENT_PERCEPTUAL);
// cmsChangeBuffersFormat(xform, TYPE_RGBA_8, TYPE_RGBA_8);
// cmsDoTransform(xform, in, out, 1);
// cmsDeleteTransform(xform);
}
/////////////////////
cmmCloseProfile(hPf);
cmmCloseProfile(hPfGrey);
free(pyccBuff);
}
*/