/*****************************************************************************
* Copyright (C) The Apache Software Foundation. All rights reserved. *
* ------------------------------------------------------------------------- *
* This software is published under the terms of the Apache Software License *
* version 1.1, a copy of which has been included with this distribution in *
* the LICENSE file. *
*****************************************************************************/
package com.kitfox.svg.batik;
import java.awt.image.ColorModel;
import java.awt.image.ComponentSampleModel;
import java.awt.image.DataBuffer;
import java.awt.image.DataBufferByte;
import java.awt.image.DataBufferInt;
import java.awt.image.SampleModel;
import java.awt.image.SinglePixelPackedSampleModel;
import java.awt.image.WritableRaster;
/**
*
* @author kitfox
*/
public class GraphicsUtil {
/** Creates a new instance of GraphicsUtil */
public GraphicsUtil() {
}
/**
* Create a new ColorModel with it's alpha premultiplied state matching
* newAlphaPreMult.
*
* @param cm
* The ColorModel to change the alpha premult state of.
* @param newAlphaPreMult
* The new state of alpha premult.
* @return A new colorModel that has isAlphaPremultiplied() equal to
* newAlphaPreMult.
*/
public static ColorModel coerceColorModel(ColorModel cm,
boolean newAlphaPreMult) {
if (cm.isAlphaPremultiplied() == newAlphaPreMult) {
return cm;
}
// Easiest way to build proper colormodel for new Alpha state...
// Eventually this should switch on known ColorModel types and
// only fall back on this hack when the CM type is unknown.
WritableRaster wr = cm.createCompatibleWritableRaster(1, 1);
return cm.coerceData(wr, newAlphaPreMult);
}
/**
* Coerces data within a bufferedImage to match newAlphaPreMult, Note that
* this can not change the colormodel of bi so you
*
* @param wr
* The raster to change the state of.
* @param cm
* The colormodel currently associated with data in wr.
* @param newAlphaPreMult
* The desired state of alpha Premult for raster.
* @return A new colormodel that matches newAlphaPreMult.
*/
public static ColorModel coerceData(WritableRaster wr, ColorModel cm,
boolean newAlphaPreMult) {
// System.out.println("CoerceData: " + cm.isAlphaPremultiplied() +
// " Out: " + newAlphaPreMult);
if (cm.hasAlpha() == false) {
// Nothing to do no alpha channel
return cm;
}
if (cm.isAlphaPremultiplied() == newAlphaPreMult) {
// nothing to do alpha state matches...
return cm;
}
// System.out.println("CoerceData: " + wr.getSampleModel());
int[] pixel = null;
int bands = wr.getNumBands();
float norm;
if (newAlphaPreMult) {
if (is_BYTE_COMP_Data(wr.getSampleModel())) {
mult_BYTE_COMP_Data(wr);
} else if (is_INT_PACK_Data(wr.getSampleModel(), true)) {
mult_INT_PACK_Data(wr);
} else {
norm = 1f / 255f;
int x0, x1, y0, y1, a, b;
float alpha;
x0 = wr.getMinX();
x1 = x0 + wr.getWidth();
y0 = wr.getMinY();
y1 = y0 + wr.getHeight();
for (int y = y0; y < y1; y++) {
for (int x = x0; x < x1; x++) {
pixel = wr.getPixel(x, y, pixel);
a = pixel[bands - 1];
if ((a >= 0) && (a < 255)) {
alpha = a * norm;
for (b = 0; b < bands - 1; b++) {
pixel[b] = (int) (pixel[b] * alpha + 0.5f);
}
wr.setPixel(x, y, pixel);
}
}
}
}
} else {
if (is_BYTE_COMP_Data(wr.getSampleModel())) {
divide_BYTE_COMP_Data(wr);
} else if (is_INT_PACK_Data(wr.getSampleModel(), true)) {
divide_INT_PACK_Data(wr);
} else {
int x0, x1, y0, y1, a, b;
float ialpha;
x0 = wr.getMinX();
x1 = x0 + wr.getWidth();
y0 = wr.getMinY();
y1 = y0 + wr.getHeight();
for (int y = y0; y < y1; y++) {
for (int x = x0; x < x1; x++) {
pixel = wr.getPixel(x, y, pixel);
a = pixel[bands - 1];
if ((a > 0) && (a < 255)) {
ialpha = 255 / (float) a;
for (b = 0; b < bands - 1; b++) {
pixel[b] = (int) (pixel[b] * ialpha + 0.5f);
}
wr.setPixel(x, y, pixel);
}
}
}
}
}
return coerceColorModel(cm, newAlphaPreMult);
}
public static boolean is_INT_PACK_Data(SampleModel sm,
boolean requireAlpha) {
// Check ColorModel is of type DirectColorModel
if (!(sm instanceof SinglePixelPackedSampleModel)) {
return false;
}
// Check transfer type
if (sm.getDataType() != DataBuffer.TYPE_INT) {
return false;
}
SinglePixelPackedSampleModel sppsm;
sppsm = (SinglePixelPackedSampleModel) sm;
int[] masks = sppsm.getBitMasks();
if (masks.length == 3) {
if (requireAlpha) {
return false;
}
} else if (masks.length != 4) {
return false;
}
if (masks[0] != 0x00ff0000) {
return false;
}
if (masks[1] != 0x0000ff00) {
return false;
}
if (masks[2] != 0x000000ff) {
return false;
}
if ((masks.length == 4) && (masks[3] != 0xff000000)) {
return false;
}
return true;
}
protected static void mult_INT_PACK_Data(WritableRaster wr) {
// System.out.println("Multiply Int: " + wr);
SinglePixelPackedSampleModel sppsm;
sppsm = (SinglePixelPackedSampleModel) wr.getSampleModel();
final int width = wr.getWidth();
final int scanStride = sppsm.getScanlineStride();
DataBufferInt db = (DataBufferInt) wr.getDataBuffer();
final int base = (db.getOffset()
+ sppsm.getOffset(wr.getMinX() - wr.getSampleModelTranslateX(),
wr.getMinY() - wr.getSampleModelTranslateY()));
// Access the pixel data array
final int pixels[] = db.getBankData()[0];
for (int y = 0; y < wr.getHeight(); y++) {
int sp = base + y * scanStride;
final int end = sp + width;
while (sp < end) {
int pixel = pixels[sp];
int a = pixel >>> 24;
if ((a >= 0) && (a < 255)) {
pixels[sp] = ((a << 24)
| ((((pixel & 0xFF0000) * a) >> 8) & 0xFF0000)
| ((((pixel & 0x00FF00) * a) >> 8) & 0x00FF00)
| ((((pixel & 0x0000FF) * a) >> 8) & 0x0000FF));
}
sp++;
}
}
}
protected static void divide_INT_PACK_Data(WritableRaster wr) {
// System.out.println("Divide Int");
SinglePixelPackedSampleModel sppsm;
sppsm = (SinglePixelPackedSampleModel) wr.getSampleModel();
final int width = wr.getWidth();
final int scanStride = sppsm.getScanlineStride();
DataBufferInt db = (DataBufferInt) wr.getDataBuffer();
final int base = (db.getOffset()
+ sppsm.getOffset(wr.getMinX() - wr.getSampleModelTranslateX(),
wr.getMinY() - wr.getSampleModelTranslateY()));
int pixel, a, aFP;
// Access the pixel data array
final int pixels[] = db.getBankData()[0];
for (int y = 0; y < wr.getHeight(); y++) {
int sp = base + y * scanStride;
final int end = sp + width;
while (sp < end) {
pixel = pixels[sp];
a = pixel >>> 24;
if (a <= 0) {
pixels[sp] = 0x00FFFFFF;
} else if (a < 255) {
aFP = (0x00FF0000 / a);
pixels[sp] = ((a << 24)
| (((((pixel & 0xFF0000) >> 16) * aFP) & 0xFF0000))
| (((((pixel & 0x00FF00) >> 8) * aFP)
& 0xFF0000) >> 8)
| (((((pixel & 0x0000FF)) * aFP)
& 0xFF0000) >> 16));
}
sp++;
}
}
}
public static boolean is_BYTE_COMP_Data(SampleModel sm) {
// Check ColorModel is of type DirectColorModel
if (!(sm instanceof ComponentSampleModel)) {
return false;
}
// Check transfer type
if (sm.getDataType() != DataBuffer.TYPE_BYTE) {
return false;
}
return true;
}
protected static void mult_BYTE_COMP_Data(WritableRaster wr) {
// System.out.println("Multiply Int: " + wr);
ComponentSampleModel csm;
csm = (ComponentSampleModel) wr.getSampleModel();
final int width = wr.getWidth();
final int scanStride = csm.getScanlineStride();
final int pixStride = csm.getPixelStride();
final int[] bandOff = csm.getBandOffsets();
DataBufferByte db = (DataBufferByte) wr.getDataBuffer();
final int base = (db.getOffset()
+ csm.getOffset(wr.getMinX() - wr.getSampleModelTranslateX(),
wr.getMinY() - wr.getSampleModelTranslateY()));
int a = 0;
int aOff = bandOff[bandOff.length - 1];
int bands = bandOff.length - 1;
int b, i;
// Access the pixel data array
final byte pixels[] = db.getBankData()[0];
for (int y = 0; y < wr.getHeight(); y++) {
int sp = base + y * scanStride;
final int end = sp + width * pixStride;
while (sp < end) {
a = pixels[sp + aOff] & 0xFF;
if (a != 0xFF) {
for (b = 0; b < bands; b++) {
i = sp + bandOff[b];
pixels[i] = (byte) (((pixels[i] & 0xFF) * a) >> 8);
}
}
sp += pixStride;
}
}
}
protected static void divide_BYTE_COMP_Data(WritableRaster wr) {
// System.out.println("Multiply Int: " + wr);
ComponentSampleModel csm;
csm = (ComponentSampleModel) wr.getSampleModel();
final int width = wr.getWidth();
final int scanStride = csm.getScanlineStride();
final int pixStride = csm.getPixelStride();
final int[] bandOff = csm.getBandOffsets();
DataBufferByte db = (DataBufferByte) wr.getDataBuffer();
final int base = (db.getOffset()
+ csm.getOffset(wr.getMinX() - wr.getSampleModelTranslateX(),
wr.getMinY() - wr.getSampleModelTranslateY()));
int a = 0;
int aOff = bandOff[bandOff.length - 1];
int bands = bandOff.length - 1;
int b, i;
// Access the pixel data array
final byte pixels[] = db.getBankData()[0];
for (int y = 0; y < wr.getHeight(); y++) {
int sp = base + y * scanStride;
final int end = sp + width * pixStride;
while (sp < end) {
a = pixels[sp + aOff] & 0xFF;
if (a == 0) {
for (b = 0; b < bands; b++) {
pixels[sp + bandOff[b]] = (byte) 0xFF;
}
} else if (a < 255) {
int aFP = (0x00FF0000 / a);
for (b = 0; b < bands; b++) {
i = sp + bandOff[b];
pixels[i] = (byte) (((pixels[i] & 0xFF) * aFP) >>> 16);
}
}
sp += pixStride;
}
}
}
}