/* * 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); } }