/*
 * 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.
 */

/* $Id: Any2LsRGBRed.java 1732018 2016-02-24 04:51:06Z gadams $ */

package org.apache.xmlgraphics.image.rendered;

import java.awt.Point;
import java.awt.Rectangle;
import java.awt.color.ColorSpace;
import java.awt.image.BandCombineOp;
import java.awt.image.BufferedImage;
import java.awt.image.ColorConvertOp;
import java.awt.image.ColorModel;
import java.awt.image.DataBuffer;
import java.awt.image.Raster;
import java.awt.image.SampleModel;
import java.awt.image.SinglePixelPackedSampleModel;
import java.awt.image.WritableRaster;

import org.apache.xmlgraphics.image.GraphicsUtil;

// CSOFF: ConstantName
// CSOFF: NeedBraces
// CSOFF: WhitespaceAfter
// CSOFF: WhitespaceAround

This function will tranform an image from any colorspace into a luminance image. The alpha channel if any will be copied to the new image.
Version:$Id: Any2LsRGBRed.java 1732018 2016-02-24 04:51:06Z gadams $ Originally authored by Thomas DeWeese.
/** * This function will tranform an image from any colorspace into a * luminance image. The alpha channel if any will be copied to the * new image. * * @version $Id: Any2LsRGBRed.java 1732018 2016-02-24 04:51:06Z gadams $ * * Originally authored by Thomas DeWeese. */
public class Any2LsRGBRed extends AbstractRed { boolean srcIssRGB;
Construct a luminace image from src.
Params:
  • src – The image to convert to a luminance image
/** * Construct a luminace image from src. * * @param src The image to convert to a luminance image */
public Any2LsRGBRed(CachableRed src) { super(src, src.getBounds(), fixColorModel(src), fixSampleModel(src), src.getTileGridXOffset(), src.getTileGridYOffset(), null); ColorModel srcCM = src.getColorModel(); if (srcCM == null) { return; } ColorSpace srcCS = srcCM.getColorSpace(); if (srcCS == ColorSpace.getInstance(ColorSpace.CS_sRGB)) { srcIssRGB = true; } }
Gamma for linear to sRGB convertion
/** * Gamma for linear to sRGB convertion */
private static final double GAMMA = 2.4; private static final double LFACT = 1.0 / 12.92; public static final double sRGBToLsRGB(double value) { if (value <= 0.003928) { return value * LFACT; } return Math.pow((value + 0.055) / 1.055, GAMMA); }
Lookup tables for RGB lookups. The linearToSRGBLut is used when noise values are considered to be on a linearScale. The linearToLinear table is used when the values are considered to be on the sRGB scale to begin with.
/** * Lookup tables for RGB lookups. The linearToSRGBLut is used * when noise values are considered to be on a linearScale. The * linearToLinear table is used when the values are considered to * be on the sRGB scale to begin with. */
private static final int[] sRGBToLsRGBLut = new int[256]; static { final double scale = 1.0 / 255; // System.out.print("S2L: "); for (int i = 0; i < 256; i++) { double value = sRGBToLsRGB(i * scale); sRGBToLsRGBLut[i] = (int)Math.round(value * 255.0); // System.out.print(sRGBToLsRGBLut[i] + ","); } // System.out.println(""); } public WritableRaster copyData(WritableRaster wr) { // Get my source. CachableRed src = (CachableRed)getSources().get(0); ColorModel srcCM = src.getColorModel(); SampleModel srcSM = src.getSampleModel(); // Fast case, SRGB source, INT Pack writable raster... if (srcIssRGB && Any2sRGBRed.is_INT_PACK_COMP(wr.getSampleModel())) { src.copyData(wr); if (srcCM.hasAlpha()) { GraphicsUtil.coerceData(wr, srcCM, false); } Any2sRGBRed.applyLut_INT(wr, sRGBToLsRGBLut); return wr; } if (srcCM == null) { // We don't really know much about this source, let's // guess based on the number of bands... float [][] matrix = null; switch (srcSM.getNumBands()) { case 1: matrix = new float[1][3]; matrix[0][0] = 1; // Red matrix[0][1] = 1; // Grn matrix[0][2] = 1; // Blu break; case 2: matrix = new float[2][4]; matrix[0][0] = 1; // Red matrix[0][1] = 1; // Grn matrix[0][2] = 1; // Blu matrix[1][3] = 1; // Alpha break; case 3: matrix = new float[3][3]; matrix[0][0] = 1; // Red matrix[1][1] = 1; // Grn matrix[2][2] = 1; // Blu break; default: matrix = new float[srcSM.getNumBands()][4]; matrix[0][0] = 1; // Red matrix[1][1] = 1; // Grn matrix[2][2] = 1; // Blu matrix[3][3] = 1; // Alpha break; } Raster srcRas = src.getData(wr.getBounds()); BandCombineOp op = new BandCombineOp(matrix, null); op.filter(srcRas, wr); } else { ColorModel dstCM = getColorModel(); BufferedImage dstBI; if (!dstCM.hasAlpha()) { // No alpha ao we don't have to work around the bug // in the color convert op. dstBI = new BufferedImage( dstCM, wr.createWritableTranslatedChild(0, 0), dstCM.isAlphaPremultiplied(), null); } else { // All this nonsense is to work around the fact that // the Color convert op doesn't properly copy the // Alpha from src to dst. SinglePixelPackedSampleModel dstSM; dstSM = (SinglePixelPackedSampleModel)wr.getSampleModel(); int [] masks = dstSM.getBitMasks(); SampleModel dstSMNoA = new SinglePixelPackedSampleModel( dstSM.getDataType(), dstSM.getWidth(), dstSM.getHeight(), dstSM.getScanlineStride(), new int[] {masks[0], masks[1], masks[2]}); ColorModel dstCMNoA = GraphicsUtil.Linear_sRGB; WritableRaster dstWr; dstWr = Raster.createWritableRaster(dstSMNoA, wr.getDataBuffer(), new Point(0, 0)); dstWr = dstWr.createWritableChild( wr.getMinX() - wr.getSampleModelTranslateX(), wr.getMinY() - wr.getSampleModelTranslateY(), wr.getWidth(), wr.getHeight(), 0, 0, null); dstBI = new BufferedImage(dstCMNoA, dstWr, false, null); } // Divide out alpha if we have it. We need to do this since // the color convert may not be a linear operation which may // lead to out of range values. ColorModel srcBICM = srcCM; WritableRaster srcWr; if (srcCM.hasAlpha() && srcCM.isAlphaPremultiplied()) { Rectangle wrR = wr.getBounds(); SampleModel sm = srcCM.createCompatibleSampleModel( wrR.width, wrR.height); srcWr = Raster.createWritableRaster( sm, new Point(wrR.x, wrR.y)); src.copyData(srcWr); srcBICM = GraphicsUtil.coerceData(srcWr, srcCM, false); } else { Raster srcRas = src.getData(wr.getBounds()); srcWr = GraphicsUtil.makeRasterWritable(srcRas); } BufferedImage srcBI; srcBI = new BufferedImage(srcBICM, srcWr.createWritableTranslatedChild(0, 0), false, null); /* * System.out.println("src: " + srcBI.getWidth() + "x" + * srcBI.getHeight()); * System.out.println("dst: " + dstBI.getWidth() + "x" + * dstBI.getHeight()); */ ColorConvertOp op = new ColorConvertOp(null); op.filter(srcBI, dstBI); if (dstCM.hasAlpha()) { copyBand(srcWr, srcSM.getNumBands() - 1, wr, getSampleModel().getNumBands() - 1); } } return wr; }
This function 'fixes' the source's color model. Right now it just selects if it should have one or two bands based on if the source had an alpha channel.
/** * This function 'fixes' the source's color model. Right now * it just selects if it should have one or two bands based on * if the source had an alpha channel. */
protected static ColorModel fixColorModel(CachableRed src) { ColorModel cm = src.getColorModel(); if (cm != null) { if (cm.hasAlpha()) { return GraphicsUtil.Linear_sRGB_Unpre; } return GraphicsUtil.Linear_sRGB; } else { // No ColorModel so try to make some intelligent // decisions based just on the number of bands... // 1 bands -> replicated into RGB // 2 bands -> Band 0 replicated into RGB & Band 1 -> alpha premult // 3 bands -> sRGB (not-linear?) // 4 bands -> sRGB premult (not-linear?) SampleModel sm = src.getSampleModel(); switch (sm.getNumBands()) { case 1: return GraphicsUtil.Linear_sRGB; case 2: return GraphicsUtil.Linear_sRGB_Unpre; case 3: return GraphicsUtil.Linear_sRGB; default: return GraphicsUtil.Linear_sRGB_Unpre; } } }
This function 'fixes' the source's sample model. Right now it just selects if it should have 3 or 4 bands based on if the source had an alpha channel.
/** * This function 'fixes' the source's sample model. * Right now it just selects if it should have 3 or 4 bands * based on if the source had an alpha channel. */
protected static SampleModel fixSampleModel(CachableRed src) { SampleModel sm = src.getSampleModel(); ColorModel cm = src.getColorModel(); boolean alpha = false; if (cm != null) { alpha = cm.hasAlpha(); } else { switch (sm.getNumBands()) { case 1: case 3: alpha = false; break; default: alpha = true; break; } } if (alpha) { return new SinglePixelPackedSampleModel( DataBuffer.TYPE_INT, sm.getWidth(), sm.getHeight(), new int [] {0xFF0000, 0xFF00, 0xFF, 0xFF000000}); } else { return new SinglePixelPackedSampleModel( DataBuffer.TYPE_INT, sm.getWidth(), sm.getHeight(), new int [] {0xFF0000, 0xFF00, 0xFF}); } } }