blob: c98e114979cf4f981f2d63c94ff484adae997ac4 [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
* @version $Revision$
*/
package org.apache.harmony.awt.gl.color;
import java.awt.color.ColorSpace;
import java.awt.image.BufferedImage;
import java.awt.image.ColorModel;
import java.awt.image.Raster;
import java.awt.image.WritableRaster;
/**
* This class combines ColorScaler, ICC_Transform and NativeImageFormat functionality
* in the workflows for different types of input/output pixel data.
*/
public class ColorConverter {
private ColorScaler scaler = new ColorScaler();
public void loadScalingData(ColorSpace cs) {
scaler.loadScalingData(cs);
}
/**
* Translates pixels, stored in source buffered image and writes the data
* to the destination image.
* @param t - ICC transform
* @param src - source image
* @param dst - destination image
*/
public void translateColor(ICC_Transform t,
BufferedImage src, BufferedImage dst) {
NativeImageFormat srcIF = NativeImageFormat.createNativeImageFormat(src);
NativeImageFormat dstIF = NativeImageFormat.createNativeImageFormat(dst);
if (srcIF != null && dstIF != null) {
t.translateColors(srcIF, dstIF);
return;
}
srcIF = createImageFormat(src);
dstIF = createImageFormat(dst);
short srcChanData[] = (short[]) srcIF.getChannelData();
short dstChanData[] = (short[]) dstIF.getChannelData();
ColorModel srcCM = src.getColorModel();
int nColorChannels = srcCM.getNumColorComponents();
scaler.loadScalingData(srcCM.getColorSpace()); // input scaling data
ColorModel dstCM = dst.getColorModel();
// Prepare array for alpha channel
float alpha[] = null;
boolean saveAlpha = srcCM.hasAlpha() && dstCM.hasAlpha();
if (saveAlpha) {
alpha = new float[src.getWidth()*src.getHeight()];
}
WritableRaster wr = src.getRaster();
int srcDataPos = 0, alphaPos = 0;
float normalizedVal[];
for (int row=0, nRows = srcIF.getNumRows(); row<nRows; row++) {
for (int col=0, nCols = srcIF.getNumCols(); col<nCols; col++) {
normalizedVal = srcCM.getNormalizedComponents(
wr.getDataElements(col, row, null),
null, 0);
// Save alpha channel
if (saveAlpha) {
// We need nColorChannels'th element cause it's nChannels - 1
alpha[alphaPos++] = normalizedVal[nColorChannels];
}
scaler.scale(normalizedVal, srcChanData, srcDataPos);
srcDataPos += nColorChannels;
}
}
t.translateColors(srcIF, dstIF);
nColorChannels = dstCM.getNumColorComponents();
boolean fillAlpha = dstCM.hasAlpha();
scaler.loadScalingData(dstCM.getColorSpace()); // output scaling data
float dstPixel[] = new float[dstCM.getNumComponents()];
int dstDataPos = 0;
alphaPos = 0;
wr = dst.getRaster();
for (int row=0, nRows = dstIF.getNumRows(); row<nRows; row++) {
for (int col=0, nCols = dstIF.getNumCols(); col<nCols; col++) {
scaler.unscale(dstPixel, dstChanData, dstDataPos);
dstDataPos += nColorChannels;
if (fillAlpha) {
if (saveAlpha) {
dstPixel[nColorChannels] = alpha[alphaPos++];
} else {
dstPixel[nColorChannels] = 1f;
}
}
wr.setDataElements(col, row,
dstCM.getDataElements(dstPixel, 0 , null));
}
}
}
/**
* Translates pixels, stored in the float data buffer.
* Each pixel occupies separate array. Input pixels passed in the buffer
* are replaced by output pixels and then the buffer is returned
* @param t - ICC transform
* @param buffer - data buffer
* @param srcCS - source color space
* @param dstCS - destination color space
* @param nPixels - number of pixels
* @return translated pixels
*/
public float[][] translateColor(ICC_Transform t,
float buffer[][],
ColorSpace srcCS,
ColorSpace dstCS,
int nPixels) {
// Scale source data
if (srcCS != null) { // if it is null use old scaling data
scaler.loadScalingData(srcCS);
}
int nSrcChannels = t.getNumInputChannels();
short srcShortData[] = new short[nPixels*nSrcChannels];
for (int i=0, srcDataPos = 0; i<nPixels; i++) {
scaler.scale(buffer[i], srcShortData, srcDataPos);
srcDataPos += nSrcChannels;
}
// Apply transform
short dstShortData[] = this.translateColor(t, srcShortData, null);
int nDstChannels = t.getNumOutputChannels();
int bufferSize = buffer[0].length;
if (bufferSize < nDstChannels + 1) { // Re-allocate buffer if needed
for (int i=0; i<nPixels; i++) {
// One extra element reserved for alpha
buffer[i] = new float[nDstChannels + 1];
}
}
// Unscale destination data
if (dstCS != null) { // if it is null use old scaling data
scaler.loadScalingData(dstCS);
}
for (int i=0, dstDataPos = 0; i<nPixels; i++) {
scaler.unscale(buffer[i], dstShortData, dstDataPos);
dstDataPos += nDstChannels;
}
return buffer;
}
/**
* Translates pixels stored in a raster.
* All data types are supported
* @param t - ICC transform
* @param src - source pixels
* @param dst - destination pixels
*/
public void translateColor(ICC_Transform t, Raster src, WritableRaster dst) {
try{
NativeImageFormat srcFmt = NativeImageFormat.createNativeImageFormat(src);
NativeImageFormat dstFmt = NativeImageFormat.createNativeImageFormat(dst);
if (srcFmt != null && dstFmt != null) {
t.translateColors(srcFmt, dstFmt);
return;
}
} catch (IllegalArgumentException e) {
}
// Go ahead and rescale the source image
scaler.loadScalingData(src, t.getSrc());
short srcData[] = scaler.scale(src);
short dstData[] = translateColor(t, srcData, null);
scaler.loadScalingData(dst, t.getDst());
scaler.unscale(dstData, dst);
}
/**
* Translates pixels stored in an array of shorts.
* Samples are stored one-by-one, i.e. array structure is like following: RGBRGBRGB...
* The number of pixels is (size of the array) / (number of components).
* @param t - ICC transform
* @param src - source pixels
* @param dst - destination pixels
* @return destination pixels, stored in the array, passed in dst
*/
public short[] translateColor(ICC_Transform t, short src[], short dst[]) {
NativeImageFormat srcFmt = createImageFormat(t, src, 0, true);
NativeImageFormat dstFmt = createImageFormat(t, dst, srcFmt.getNumCols(), false);
t.translateColors(srcFmt, dstFmt);
return (short[]) dstFmt.getChannelData();
}
/**
* Creates NativeImageFormat from buffered image.
* @param bi - buffered image
* @return created NativeImageFormat
*/
private NativeImageFormat createImageFormat(BufferedImage bi) {
int nRows = bi.getHeight();
int nCols = bi.getWidth();
int nComps = bi.getColorModel().getNumColorComponents();
short imgData[] = new short[nRows*nCols*nComps];
return new NativeImageFormat(
imgData, nComps, nRows, nCols);
}
/**
* Creates one-row NativeImageFormat, using either nCols if it is positive,
* or arr.length to determine the number of pixels
*
* @param t - transform
* @param arr - short array or null if nCols is positive
* @param nCols - number of pixels in the array or 0 if array is not null
* @param in - is it an input or output array
* @return one-row NativeImageFormat
*/
private NativeImageFormat createImageFormat(
ICC_Transform t, short arr[], int nCols, boolean in
) {
int nComponents = in ? t.getNumInputChannels() : t.getNumOutputChannels();
if (arr == null || arr.length < nCols*nComponents) {
arr = new short[nCols*nComponents];
}
if (nCols == 0)
nCols = arr.length / nComponents;
return new NativeImageFormat(arr, nComponents, 1, nCols);
}
}