/*
* @(#)DirectColorModel.java 1.28 06/10/10
*
* Copyright 1990-2008 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License version
* 2 only, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License version 2 for more details (a copy is
* included at /legal/license.txt).
*
* You should have received a copy of the GNU General Public License
* version 2 along with this work; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
* Clara, CA 95054 or visit www.sun.com if you need additional
* information or have any questions.
*
*/
package java.awt.image;
import java.awt.AWTException;
import java.awt.Transparency;
import java.awt.color.ColorSpace;
/**
* A ColorModel class that specifies a translation from pixel values
* to alpha, red, green, and blue color components for pixels which
* have the color components embedded directly in the bits of the
* pixel itself. This color model is similar to an X11 TrueColor
* visual.
* <p>Many of the methods in this class are final. This is because the
* underlying native graphics code makes assumptions about the layout
* and operation of this class and those assumptions are reflected in
* the implementations of the methods here that are marked final. You
* can subclass this class for other reaons, but you cannot override
* or modify the behaviour of those methods.
*
* @see ColorModel
*
* @version 1.24 08/21/02
* @author Jim Graham
*/
public class DirectColorModel extends ColorModel {
private int red_mask;
private int green_mask;
private int blue_mask;
private int alpha_mask;
private int red_offset;
private int green_offset;
private int blue_offset;
private int alpha_offset;
private int red_scale;
private int green_scale;
private int blue_scale;
private int alpha_scale;
/**
* Constructs a DirectColorModel from the given masks specifying
* which bits in the pixel contain the red, green and blue color
* components. Pixels described by this color model will all
* have alpha components of 255 (fully opaque). All of the bits
* in each mask must be contiguous and fit in the specified number
* of least significant bits of the integer.
*/
public DirectColorModel(int bits,
int rmask, int gmask, int bmask) {
this(bits, rmask, gmask, bmask, 0);
}
/**
* Constructs a DirectColorModel from the given masks specifying
* which bits in the pixel contain the alhpa, red, green and blue
* color components. All of the bits in each mask must be contiguous
* and fit in the specified number of least significant bits of the
* integer.
*/
public DirectColorModel(int bits,
int rmask, int gmask, int bmask, int amask) {
super(bits);
nBits = createBitsArray(rmask, gmask, bmask, amask);
red_mask = rmask;
green_mask = gmask;
blue_mask = bmask;
alpha_mask = amask;
calculateOffsets();
/* 4709812
* Set transparency to OPAQUE if amask is 0.
*/
if (amask == 0) {
supportsAlpha = false;
transparency = Transparency.OPAQUE;
}
}
/**
* Constructs a <code>DirectColorModel</code> from the specified
* parameters. Color components are in the specified
* <code>ColorSpace</code>, which must be of type ColorSpace.TYPE_RGB.
* The masks specify which bits in an <code>int</code> pixel
* representation contain the red, green and blue color samples and
* the alpha sample, if present. If <code>amask</code> is 0, pixel
* values do not contain alpha information and all pixels are treated
* as opaque, which means that alpha = 1.0. All of the
* bits in each mask must be contiguous and fit in the specified number
* of least significant bits of an <code>int</code> pixel
* representation. If there is alpha, the <code>boolean</code>
* <code>isAlphaPremultiplied</code> specifies how to interpret
* color and alpha samples in pixel values. If the <code>boolean</code>
* is <code>true</code>, color samples are assumed to have been
* multiplied by the alpha sample. The transparency value is
* Transparency.OPAQUE, if no alpha is present, or
* Transparency.TRANSLUCENT otherwise. The transfer type
* is the type of primitive array used to represent pixel values and
* must be one of DataBuffer.TYPE_BYTE, DataBuffer.TYPE_USHORT, or
* DataBuffer.TYPE_INT.
* @param space the specified <code>ColorSpace</code>
* @param bits the number of bits in the pixel values; for example,
* the sum of the number of bits in the masks.
* @param rmask specifies a mask indicating which bits in an
* integer pixel contain the red component
* @param gmask specifies a mask indicating which bits in an
* integer pixel contain the green component
* @param bmask specifies a mask indicating which bits in an
* integer pixel contain the blue component
* @param amask specifies a mask indicating which bits in an
* integer pixel contain the alpha component
* @param isAlphaPremultiplied <code>true</code> if color samples are
* premultiplied by the alpha sample; <code>false</code> otherwise
* @param transferType the type of array used to represent pixel values
*/
public DirectColorModel(ColorSpace space, int bits, int rmask,
int gmask, int bmask, int amask,
boolean isAlphaPremultiplied,
int transferType) {
super(bits,
createBitsArray(rmask, gmask, bmask, amask),
space,
amask == 0 ? false : true,
isAlphaPremultiplied,
amask == 0 ? Transparency.OPAQUE : Transparency.TRANSLUCENT,
transferType);
red_mask = rmask;
green_mask = gmask;
blue_mask = bmask;
alpha_mask = amask;
calculateOffsets();
}
private final static int[] createBitsArray(int rmask, int gmask, int bmask,
int amask) {
int[] arr = new int[3 + (amask == 0 ? 0 : 1)];
arr[0] = countBits(rmask);
arr[1] = countBits(gmask);
arr[2] = countBits(bmask);
if (arr[0] < 0) {
throw new IllegalArgumentException("Noncontiguous red mask ("
+ Integer.toHexString(rmask));
} else if (arr[1] < 0) {
throw new IllegalArgumentException("Noncontiguous green mask ("
+ Integer.toHexString(gmask));
} else if (arr[2] < 0) {
throw new IllegalArgumentException("Noncontiguous blue mask ("
+ Integer.toHexString(bmask));
}
if (amask != 0) {
arr[3] = countBits(amask);
if (arr[3] < 0) {
throw new IllegalArgumentException("Noncontiguous alpha mask ("
+ Integer.toHexString(amask));
}
}
return arr;
}
private final static int countBits(int mask) {
int saveMask = mask;
int count = 0;
if (mask != 0) {
while ((mask & 1) == 0) {
mask >>>= 1;
}
while ((mask & 1) == 1) {
mask >>>= 1;
count++;
}
}
if (mask != 0) {
return -1;
}
return count;
}
/**
* Returns the mask indicating which bits in a pixel contain the red
* color component.
*/
final public int getRedMask() {
return red_mask;
}
/**
* Returns the mask indicating which bits in a pixel contain the green
* color component.
*/
final public int getGreenMask() {
return green_mask;
}
/**
* Returns the mask indicating which bits in a pixel contain the blue
* color component.
*/
final public int getBlueMask() {
return blue_mask;
}
/**
* Returns the mask indicating which bits in a pixel contain the alpha
* transparency component.
*/
final public int getAlphaMask() {
return alpha_mask;
}
private int accum_mask = 0;
/*
* A utility function to decompose a single mask and verify that it
* fits in the specified pixel size, and that it does not overlap any
* other color component. The values necessary to decompose and
* manipulate pixels are calculated as a side effect.
*/
private void decomposeMask(int mask, String componentName, int values[]) {
if ((mask & accum_mask) != 0) {
throw new IllegalArgumentException(componentName + " mask bits not unique");
}
int off = 0;
int count = 0;
if (mask != 0) {
while ((mask & 1) == 0) {
mask >>>= 1;
off++;
}
while ((mask & 1) == 1) {
mask >>>= 1;
count++;
}
}
if (mask != 0) {
throw new IllegalArgumentException(componentName + " mask bits not contiguous");
}
if (off + count > pixel_bits) {
throw new IllegalArgumentException(componentName + " mask overflows pixel");
}
int scale;
if (count < 8) {
scale = (1 << count) - 1;
} else {
scale = 0;
if (count > 8) {
off += (count - 8);
}
}
values[0] = off;
values[1] = scale;
}
/*
* A utility function to verify all of the masks and to store
* the auxilliary values needed to manipulate the pixels.
*/
private void calculateOffsets() {
int values[] = new int[2];
decomposeMask(red_mask, "red", values);
red_offset = values[0];
red_scale = values[1];
decomposeMask(green_mask, "green", values);
green_offset = values[0];
green_scale = values[1];
decomposeMask(blue_mask, "blue", values);
blue_offset = values[0];
blue_scale = values[1];
decomposeMask(alpha_mask, "alpha", values);
alpha_offset = values[0];
alpha_scale = values[1];
}
/**
* Returns the red color compoment for the specified pixel in the
* range 0-255.
*/
final public int getRed(int pixel) {
int r = ((pixel & red_mask) >>> red_offset);
if (red_scale != 0) {
r = r * 255 / red_scale;
}
if (isAlphaPremultiplied) {
int alpha = getAlpha(pixel);
if (alpha == 0)
return 0;
r *= 255;
r /= alpha;
r = Math.min(255, r);
}
return r;
}
/**
* Returns the green color compoment for the specified pixel in the
* range 0-255.
*/
final public int getGreen(int pixel) {
int g = ((pixel & green_mask) >>> green_offset);
if (green_scale != 0) {
g = g * 255 / green_scale;
}
if (isAlphaPremultiplied) {
int alpha = getAlpha(pixel);
if (alpha == 0)
return 0;
g *= 255;
g /= alpha;
g = Math.min(255, g);
}
return g;
}
/**
* Returns the blue color compoment for the specified pixel in the
* range 0-255.
*/
final public int getBlue(int pixel) {
int b = ((pixel & blue_mask) >>> blue_offset);
if (blue_scale != 0) {
b = b * 255 / blue_scale;
}
if (isAlphaPremultiplied) {
int alpha = getAlpha(pixel);
if (alpha == 0)
return 0;
b *= 255;
b /= alpha;
b = Math.min(255, b);
}
return b;
}
/**
* Return the alpha transparency value for the specified pixel in the
* range 0-255.
*/
final public int getAlpha(int pixel) {
if (alpha_mask == 0) return 255;
int a = ((pixel & alpha_mask) >>> alpha_offset);
if (alpha_scale != 0) {
a = a * 255 / alpha_scale;
}
return a;
}
/**
* Returns the color of the pixel in the default RGB color model.
* @see ColorModel#getRGBdefault
*/
final public int getRGB(int pixel) {
return (getAlpha(pixel) << 24)
| (getRed(pixel) << 16)
| (getGreen(pixel) << 8)
| (getBlue(pixel) << 0);
}
}