0001: /*
0002: * $RCSfile: IHSColorSpace.java,v $
0003: *
0004: * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved.
0005: *
0006: * Use is subject to license terms.
0007: *
0008: * $Revision: 1.2 $
0009: * $Date: 2005/11/16 22:58:16 $
0010: * $State: Exp $
0011: */
0012: package javax.media.jai;
0013:
0014: import java.awt.Point;
0015: import java.awt.color.ColorSpace;
0016: import java.awt.image.Raster;
0017: import java.awt.image.WritableRaster;
0018: import java.awt.image.DataBuffer;
0019: import java.awt.image.DataBufferByte;
0020: import java.awt.image.DataBufferShort;
0021: import java.awt.image.DataBufferInt;
0022: import java.awt.image.SampleModel;
0023: import java.lang.ref.SoftReference;
0024:
0025: /**
0026: * Singleton class representing the IHS (<i>I</i>ntensity, <i>H</i>ue,
0027: * <i>S</i>aturation) color space (also known as HSI or HIS).
0028: *
0029: * <p> The RGB-to-IHS transformation is defined by the equations:
0030: * <ul>
0031: * <li> I = (R+G+B)/3</li>
0032: * <li> S = 1-min(R, G, B)/I </li>
0033: * <li> H = G > B ? h : 2*<code>PI</code> - h, where <br>
0034: * h = cos<sup>-1</sup>{[(R-G)+(R-B)]/2[(R-G)<sup>2</sup>+
0035: * (R-G)(G-B)]<sup>1/2</sup>}</li>
0036: * </ul>
0037: * where the R, G, B values have been normalized to the range [0.0, 1.0].
0038: * Refer to <i>Practical Algorithms for Image Analysis</i>, Seul, et. al.,
0039: * (Cambridge, 2000), pages 50-55, for more information.
0040: *
0041: * The inverse transformation (IHS-to-RGB) is defined as:
0042: *
0043: * <p>When <code>H</code> is in <code>[2PI/3, 4PI/3]</code>, <code>R</code>
0044: * should be the smallest. Then, there exists
0045: * <p><code>R = (1-S)I</code>
0046: * <p>and <code><ul><li>G = (c1 + c2)/2</li>
0047: * <li>B = (c1 - c2)/2</li></ul></code>
0048: * <p>where <code>c1 = 3I-R</code> and <code>c2 = sqrt(3)(R-I)tg(H)</code>
0049: *
0050: * <p>when <code>H</code> is in <code>[4PI/3, 2PI]</code>,
0051: *
0052: * <p><code>G = (1-S)I</code>
0053: * <p>and <code><ul><li>B = (c1 + c2)/2</li>
0054: * <li>R = (c1 - c2)/2</li></ul></code>
0055: * <p>where c1 = 3I-G and c2 = sqrt(3)(G-I)tg(H-2PI/3)
0056: *
0057: * <p> when <code>H</code> is in <code>[0, 2PI/3]</code>,
0058: *
0059: * <p><code>B = (1-S)I</code>
0060: * <p>and <code><ul><li>R = (c1 + c2)/2</li>
0061: * <li>G = (c1 - c2)/2</li></ul></code>
0062: *
0063: * <p>where <code>c1 = 3I-B</code> and <code>c2 = sqrt(3)(B-I)tg(H-4PI/3)</code>
0064: * <p> Methods defined in the superclasses are not commented extensively.
0065: *
0066: * @see ColorSpaceJAI
0067: *
0068: * @since JAI 1.1
0069: */
0070: // Old RGB-to-IHS equations ("Practical Algorithms for Image Analysis",
0071: // Seul, et. al., Cambridge, 2000):
0072: //
0073: // <ul>
0074: // <li> I = (R+G+B)/3</li>
0075: // <li> S = 1-min(R, G, B)/I </li>
0076: // <li> H = G > B ? h : 2*<code>PI</code> - h, where <br>
0077: // h = cos<sup>-1</sup>{[(R-G)+(R-B)]/2[(R-G)<sup>2</sup>+
0078: // (R-G)(G-B)]<sup>1/2</sup>}</li>
0079: // </ul>
0080: // where the R, G, B values have been normalized to the range [0.0, 1.0].
0081: // <p> The RGB-to-IHS transformation is defined by the equations:
0082: // <ul>
0083: // <li><pre>
0084: // [ I ] [ 1/3 1/3 1/3 ] [ R ]
0085: // [ U ] = [ -1/sqrt(6) -1/sqrt(6) 2/sqrt(6) ] [ G ]
0086: // [ V ] [ 1/sqrt(6) -2/sqrt(6) 0 ] [ B ]
0087: // </pre></li>
0088: // <li> H = atan(V/U) </li>
0089: // <li> S = sqrt(U<sup>2</sup> + V<sup>2</sup>) </li>
0090: // </ul>
0091: //
0092: // The inverse IHS-to-RGB transformation is defined by:
0093: // <ul>
0094: // <li> U = S*cos(H) </li>
0095: // <li> V = S*sin(H) </li>
0096: // <li><pre>
0097: // [ R ] = [ 4/3 -2*sqrt(6)/9 sqrt(6)/3 ] [ I ]
0098: // [ G ] = [ 2/3 sqrt(6)/9 -sqrt(6)/3 ] [ U ]
0099: // [ B ] [ 1 sqrt(6)/3 0 ] [ V ]
0100: // </pre></li>
0101: // </ul>
0102: //
0103: // Refer to <i>Digital Image Processing</i>, Second Edition, William K. Pratt
0104: // (Wiley, 1991), pages 71-72, for more information.
0105: public final class IHSColorSpace extends ColorSpaceJAI {
0106: // Constant for rgb-to-ihs
0107: private static final double PI2 = Math.PI * 2.0;
0108:
0109: // Constants for ihs-to-rgb
0110: private static final double PI23 = PI2 / 3.0;
0111: private static final double PI43 = PI23 * 2.0;
0112: private static final double SQRT3 = Math.sqrt(3.0);
0113:
0114: private final static double BYTESCALE = 127.5 / Math.PI;
0115:
0116: private static SoftReference reference = new SoftReference(null);
0117:
0118: /* tables and their softrefrences */
0119: private static byte[] acosTable = null;
0120: private static double[] sqrtTable = null;
0121: private static double[] tanTable = null;
0122: private static SoftReference acosSoftRef;
0123: private static SoftReference sqrtSoftRef;
0124: private static SoftReference tanSoftRef;
0125:
0126: /**
0127: * Retrieves the unique instance of this class the construction of
0128: * which is deferred until the first invocation of this method.
0129: */
0130: public static IHSColorSpace getInstance() {
0131: synchronized (reference) {
0132: Object referent = reference.get();
0133: IHSColorSpace cs;
0134: if (referent == null) {
0135: // First invocation or SoftReference has been cleared.
0136: reference = new SoftReference(cs = new IHSColorSpace());
0137: } else {
0138: // SoftReference has not been cleared.
0139: cs = (IHSColorSpace) referent;
0140: }
0141:
0142: return cs;
0143: }
0144: }
0145:
0146: /**
0147: * Constructs an instance of this class with <code>type</code>
0148: * <code>ColorSpace.TYPE_HSV</code>, 3 components, and preferred
0149: * intermediary space sRGB.
0150: */
0151: protected IHSColorSpace() {
0152: super (ColorSpace.TYPE_HSV, 3, true);
0153: }
0154:
0155: // generate the cos table used in RGBtoIHS for byte type
0156: private synchronized void generateACosTable() {
0157:
0158: if ((acosSoftRef == null) || (acosSoftRef.get() == null)) {
0159: acosTable = new byte[1001];
0160: acosSoftRef = new SoftReference(acosTable);
0161:
0162: for (int i = 0; i <= 1000; i++)
0163: acosTable[i] = (byte) (BYTESCALE
0164: * Math.acos((i - 500) * 0.002) + 0.5);
0165: }
0166: }
0167:
0168: // generate the square root table used in RGBtoIHS for byte type
0169: private synchronized void generateSqrtTable() {
0170: if ((sqrtSoftRef == null) || (sqrtSoftRef.get() == null)) {
0171: sqrtTable = new double[1001];
0172: sqrtSoftRef = new SoftReference(sqrtTable);
0173:
0174: for (int i = 0; i <= 1000; i++)
0175: sqrtTable[i] = Math.sqrt(i / 1000.0);
0176: }
0177: }
0178:
0179: // generate the tangent table used in IHStoRGB for byte type
0180: private synchronized void generateTanTable() {
0181: if ((tanSoftRef == null) || (tanSoftRef.get() == null)) {
0182: tanTable = new double[256];
0183: tanSoftRef = new SoftReference(tanTable);
0184:
0185: for (int i = 0; i < 256; i++)
0186: tanTable[i] = Math.tan(i * PI2 / 255.0);
0187: }
0188: }
0189:
0190: /**
0191: * Converts a single color value from CIEXYZ to IHS.
0192: */
0193: public float[] fromCIEXYZ(float[] colorValue) {
0194: float[] rgb = new float[3];
0195: XYZ2RGB(colorValue, rgb);
0196:
0197: float r = rgb[0];
0198: float g = rgb[1];
0199: float b = rgb[2];
0200:
0201: float[] ihs = new float[3];
0202:
0203: ihs[0] = (r + g + b) / 3.0f;
0204: float drg = r - g;
0205: float drb = r - b;
0206: float temp = (float) Math.sqrt(drg * (double) drg + drb
0207: * (double) (drb - drg));
0208:
0209: // when temp is zero, R=G=B. Hue should be NaN. To make
0210: // numerically consistent, set it to 2PI
0211: if (temp != 0.0f) {
0212: temp = (float) Math.acos((drg + drb) / (double) temp / 2);
0213: if (g < b)
0214: ihs[1] = (float) (PI2 - temp);
0215: else
0216: ihs[1] = temp;
0217: } else
0218: ihs[1] = (float) PI2;
0219:
0220: float min = (r < g) ? r : g;
0221: min = (min < b) ? min : b;
0222:
0223: // when intensity is 0, means R=G=B=0. S can be set to 0 to indicate
0224: // R=G=B.
0225: if (ihs[0] == 0.0f)
0226: ihs[2] = 0.0f;
0227: else
0228: ihs[2] = 1.0f - min / ihs[0];
0229:
0230: return ihs;
0231: }
0232:
0233: /**
0234: * Converts a single color value from sRGB to IHS.
0235: */
0236: public float[] fromRGB(float[] rgbValue) {
0237: float r = rgbValue[0];
0238: float g = rgbValue[1];
0239: float b = rgbValue[2];
0240:
0241: r = (r < 0.0f) ? 0.0f : ((r > 1.0f) ? 1.0f : r);
0242: g = (g < 0.0f) ? 0.0f : ((g > 1.0f) ? 1.0f : g);
0243: b = (b < 0.0f) ? 0.0f : ((b > 1.0f) ? 1.0f : b);
0244:
0245: float[] ihs = new float[3];
0246:
0247: ihs[0] = (r + g + b) / 3.0f;
0248: float drg = r - g;
0249: float drb = r - b;
0250: float temp = (float) Math.sqrt(drg * (double) drg + drb
0251: * (double) (drb - drg));
0252:
0253: // when temp is zero, R=G=B. Hue should be NaN. To make
0254: // numerically consistent, set it to 2PI
0255: if (temp != 0.0f) {
0256: temp = (float) Math.acos((drg + drb) / (double) temp / 2);
0257: if (g < b)
0258: ihs[1] = (float) (PI2 - temp);
0259: else
0260: ihs[1] = temp;
0261: } else
0262: ihs[1] = (float) PI2;
0263:
0264: float min = (r < g) ? r : g;
0265: min = (min < b) ? min : b;
0266:
0267: // when intensity is 0, means R=G=B=0. S can be set to 0 to indicate
0268: // R=G=B.
0269: if (ihs[0] == 0.0f)
0270: ihs[2] = 0.0f;
0271: else
0272: ihs[2] = 1.0f - min / ihs[0];
0273:
0274: return ihs;
0275: }
0276:
0277: /**
0278: * Converts a single color value from IHS to CIEXYZ.
0279: */
0280: public float[] toCIEXYZ(float[] colorValue) {
0281: float i = colorValue[0];
0282: float h = colorValue[1];
0283: float s = colorValue[2];
0284:
0285: i = (i < 0.0f) ? 0.0f : ((i > 1.0f) ? 1.0f : i);
0286: h = (h < 0.0f) ? 0.0f : ((h > (float) PI2) ? (float) PI2 : h);
0287: s = (s < 0.0f) ? 0.0f : ((s > 1.0f) ? 1.0f : s);
0288:
0289: float r = 0.0f, g = 0.0f, b = 0.0f;
0290:
0291: // when the saturation is zero, means this color is grey color.
0292: // so R=G=B.
0293: if (s == 0.0f) {
0294: r = g = b = i;
0295: } else {
0296: if (h >= PI23 && h < PI43) {
0297: r = (1 - s) * i;
0298: float c1 = 3 * i - r;
0299: float c2 = (float) (SQRT3 * (r - i) * Math.tan(h));
0300: g = (c1 + c2) / 2;
0301: b = (c1 - c2) / 2;
0302: } else if (h > PI43) {
0303: g = (1 - s) * i;
0304: float c1 = 3 * i - g;
0305: float c2 = (float) (SQRT3 * (g - i) * Math
0306: .tan(h - PI23));
0307: b = (c1 + c2) / 2;
0308: r = (c1 - c2) / 2;
0309: } else if (h < PI23) {
0310: b = (1 - s) * i;
0311: float c1 = 3 * i - b;
0312: float c2 = (float) (SQRT3 * (b - i) * Math
0313: .tan(h - PI43));
0314: r = (c1 + c2) / 2;
0315: g = (c1 - c2) / 2;
0316: }
0317: }
0318:
0319: float[] xyz = new float[3];
0320: float[] rgb = new float[3];
0321: rgb[0] = r;
0322: rgb[1] = g;
0323: rgb[2] = b;
0324:
0325: RGB2XYZ(rgb, xyz);
0326:
0327: return xyz;
0328: }
0329:
0330: /**
0331: * Converts a single color value from IHS to sRGB.
0332: */
0333: public float[] toRGB(float[] colorValue) {
0334: float i = colorValue[0];
0335: float h = colorValue[1];
0336: float s = colorValue[2];
0337:
0338: i = (i < 0.0f) ? 0.0f : ((i > 1.0f) ? 1.0f : i);
0339: h = (h < 0.0f) ? 0.0f : ((h > (float) PI2) ? (float) PI2 : h);
0340: s = (s < 0.0f) ? 0.0f : ((s > 1.0f) ? 1.0f : s);
0341:
0342: float[] rgb = new float[3];
0343:
0344: // when the saturation is 0, the color is grey. so R=G=B=I.
0345: if (s == 0.0f) {
0346: rgb[0] = rgb[1] = rgb[2] = i;
0347: } else {
0348: if (h >= PI23 && h <= PI43) {
0349: float r = (1 - s) * i;
0350: float c1 = 3 * i - r;
0351: float c2 = (float) (SQRT3 * (r - i) * Math.tan(h));
0352: rgb[0] = r;
0353: rgb[1] = (c1 + c2) / 2;
0354: rgb[2] = (c1 - c2) / 2;
0355: } else if (h > PI43) {
0356: float g = (1 - s) * i;
0357: float c1 = 3 * i - g;
0358: float c2 = (float) (SQRT3 * (g - i) * Math
0359: .tan(h - PI23));
0360: rgb[0] = (c1 - c2) / 2;
0361: rgb[1] = g;
0362: rgb[2] = (c1 + c2) / 2;
0363: } else if (h < PI23) {
0364: float b = (1 - s) * i;
0365: float c1 = 3 * i - b;
0366: float c2 = (float) (SQRT3 * (b - i) * Math
0367: .tan(h - PI43));
0368: rgb[0] = (c1 + c2) / 2;
0369: rgb[1] = (c1 - c2) / 2;
0370: rgb[2] = b;
0371: }
0372: }
0373:
0374: return rgb;
0375: }
0376:
0377: /**
0378: * Converts a <code>Raster</code> of colors represented as pixels
0379: * from CIEXYZ to IHS.
0380: */
0381: public WritableRaster fromCIEXYZ(Raster src,
0382: int[] srcComponentSize, WritableRaster dest,
0383: int[] destComponentSize) {
0384: WritableRaster tempRas = CIEXYZToRGB(src, srcComponentSize,
0385: null, null);
0386: return fromRGB(tempRas, tempRas.getSampleModel()
0387: .getSampleSize(), dest, destComponentSize);
0388: }
0389:
0390: /**
0391: * Converts a <code>Raster</code> of colors represented as pixels
0392: * from sRGB to IHS.
0393: */
0394: public WritableRaster fromRGB(Raster src, int[] srcComponentSize,
0395: WritableRaster dest, int[] destComponentSize) {
0396: //Validate the parameters
0397: checkParameters(src, srcComponentSize, dest, destComponentSize);
0398:
0399: SampleModel srcSampleModel = src.getSampleModel();
0400:
0401: // When the parameter is not provided by the caller, set it as the
0402: // sample sizes of the source
0403: if (srcComponentSize == null)
0404: srcComponentSize = srcSampleModel.getSampleSize();
0405:
0406: //when the destination ratser is not provided, create a new one
0407: if (dest == null) {
0408: Point origin = new Point(src.getMinX(), src.getMinY());
0409: dest = RasterFactory.createWritableRaster(srcSampleModel,
0410: origin);
0411: }
0412:
0413: SampleModel dstSampleModel = dest.getSampleModel();
0414: if (destComponentSize == null)
0415: destComponentSize = dstSampleModel.getSampleSize();
0416:
0417: PixelAccessor srcAcc = new PixelAccessor(srcSampleModel, null);
0418: UnpackedImageData srcUid = srcAcc.getPixels(src, src
0419: .getBounds(), srcSampleModel.getDataType(), false);
0420:
0421: switch (srcSampleModel.getDataType()) {
0422:
0423: case DataBuffer.TYPE_BYTE:
0424: fromRGBByte(srcUid, srcComponentSize, dest,
0425: destComponentSize);
0426: break;
0427: case DataBuffer.TYPE_USHORT:
0428: case DataBuffer.TYPE_SHORT:
0429: fromRGBShort(srcUid, srcComponentSize, dest,
0430: destComponentSize);
0431: break;
0432: case DataBuffer.TYPE_INT:
0433: fromRGBInt(srcUid, srcComponentSize, dest,
0434: destComponentSize);
0435: break;
0436: case DataBuffer.TYPE_FLOAT:
0437: fromRGBFloat(srcUid, srcComponentSize, dest,
0438: destComponentSize);
0439: break;
0440: case DataBuffer.TYPE_DOUBLE:
0441: fromRGBDouble(srcUid, srcComponentSize, dest,
0442: destComponentSize);
0443: break;
0444: }
0445: return dest;
0446: }
0447:
0448: // convert a byte type raster from RGB to IHS
0449: private void fromRGBByte(UnpackedImageData src,
0450: int[] srcComponentSize, WritableRaster dest,
0451: int[] destComponentSize) {
0452: byte[] rBuf = src.getByteData(0);
0453: byte[] gBuf = src.getByteData(1);
0454: byte[] bBuf = src.getByteData(2);
0455:
0456: int normr = 8 - srcComponentSize[0];
0457: int normg = 8 - srcComponentSize[1];
0458: int normb = 8 - srcComponentSize[2];
0459:
0460: double normi = 1.0 / 255.0, normh = 1.0, norms = 1.0;
0461:
0462: int bnormi = 0, bnormh = 0, bnorms = 0;
0463:
0464: int dstType = dest.getSampleModel().getDataType();
0465: boolean isByte = (dstType == DataBuffer.TYPE_BYTE);
0466:
0467: if (isByte) {
0468: bnormi = 8 - destComponentSize[0];
0469: bnormh = 8 - destComponentSize[1];
0470: bnorms = 8 - destComponentSize[2];
0471: generateACosTable();
0472: generateSqrtTable();
0473: } else if (dstType < DataBuffer.TYPE_FLOAT) {
0474: normi = ((1l << destComponentSize[0]) - 1) / 255.0;
0475: normh = ((1l << destComponentSize[1]) - 1) / PI2;
0476: norms = ((1l << destComponentSize[2]) - 1);
0477: }
0478:
0479: int height = dest.getHeight();
0480: int width = dest.getWidth();
0481:
0482: double[] dstPixels = null;
0483: int[] dstIntPixels = null;
0484:
0485: if (isByte)
0486: dstIntPixels = new int[3 * height * width];
0487: else
0488: dstPixels = new double[3 * height * width];
0489:
0490: int rStart = src.bandOffsets[0];
0491: int gStart = src.bandOffsets[1];
0492: int bStart = src.bandOffsets[2];
0493: int srcPixelStride = src.pixelStride;
0494: int srcLineStride = src.lineStride;
0495:
0496: int dIndex = 0;
0497: for (int j = 0; j < height; j++, rStart += srcLineStride, gStart += srcLineStride, bStart += srcLineStride) {
0498: for (int i = 0, rIndex = rStart, gIndex = gStart, bIndex = bStart; i < width; i++, rIndex += srcPixelStride, gIndex += srcPixelStride, bIndex += srcPixelStride) {
0499: short R = (short) ((rBuf[rIndex] & 0xFF) << normr);
0500: short G = (short) ((gBuf[gIndex] & 0xFF) << normg);
0501: short B = (short) ((bBuf[bIndex] & 0xFF) << normb);
0502:
0503: if (isByte) {
0504: float intensity = (R + G + B) / 3.0f;
0505: dstIntPixels[dIndex++] = ((short) (intensity + 0.5f)) >> bnormi;
0506:
0507: short drg = (short) (R - G);
0508: short drb = (short) (R - B);
0509:
0510: int tint = drg * drg + drb * (drb - drg);
0511:
0512: short sum = (short) (drg + drb);
0513: double temp;
0514: if (tint != 0)
0515: temp = sqrtTable[(int) (250.0 * sum * sum
0516: / tint + 0.5)];
0517: else
0518: temp = -1.0;
0519:
0520: int hue;
0521: if (sum > 0)
0522: hue = acosTable[(int) (500 * temp + 0.5) + 500];
0523: else
0524: hue = acosTable[(int) (-500 * temp - 0.5) + 500];
0525:
0526: if (B >= G)
0527: dstIntPixels[dIndex++] = (255 - hue) >> bnormh;
0528: else
0529: dstIntPixels[dIndex++] = hue >> bnormh;
0530:
0531: short min = (G > B) ? B : G;
0532: min = (R > min) ? min : R;
0533: dstIntPixels[dIndex++] = (255 - (int) (255 * min
0534: / intensity + 0.5f)) >> bnorms;
0535: } else {
0536: float intensity = (R + G + B) / 3.0f;
0537: dstPixels[dIndex++] = normi * intensity;
0538:
0539: double drg = R - G;
0540: double drb = R - B;
0541: double temp = Math.sqrt(drg * drg + drb
0542: * (drb - drg));
0543:
0544: if (temp != 0) {
0545: temp = Math.acos((drg + drb) / temp / 2);
0546: if (B >= G)
0547: temp = PI2 - temp;
0548: } else
0549: temp = PI2;
0550:
0551: dstPixels[dIndex++] = normh * temp;
0552:
0553: double min = (G > B) ? B : G;
0554: min = (R > min) ? min : R;
0555: dstPixels[dIndex++] = norms * (1 - min / intensity);
0556: }
0557: }
0558: }
0559: if (isByte)
0560: dest.setPixels(dest.getMinX(), dest.getMinY(), width,
0561: height, dstIntPixels);
0562: else {
0563: convertToSigned(dstPixels, dstType);
0564: dest.setPixels(dest.getMinX(), dest.getMinY(), width,
0565: height, dstPixels);
0566: }
0567: }
0568:
0569: // convert a short type ratser from RGB to IHS
0570: private void fromRGBShort(UnpackedImageData src,
0571: int[] srcComponentSize, WritableRaster dest,
0572: int[] destComponentSize) {
0573: short[] rBuf = src.getShortData(0);
0574: short[] gBuf = src.getShortData(1);
0575: short[] bBuf = src.getShortData(2);
0576:
0577: // used to left-shift the input to fill all the bits
0578: int normr = 16 - srcComponentSize[0];
0579: int normg = 16 - srcComponentSize[1];
0580: int normb = 16 - srcComponentSize[2];
0581:
0582: //map the output to the desired range
0583: double normi = 1.0 / 65535.0, normh = 1.0, norms = 1.0;
0584:
0585: //right-shift the output values to the desired range
0586: int bnormi = 0, bnormh = 0, bnorms = 0;
0587:
0588: int dstType = dest.getSampleModel().getDataType();
0589: boolean isByte = (dstType == DataBuffer.TYPE_BYTE);
0590:
0591: if (isByte) {
0592: bnormi = 16 - destComponentSize[0];
0593: bnormh = 8 - destComponentSize[1];
0594: bnorms = 8 - destComponentSize[2];
0595: generateACosTable();
0596: generateSqrtTable();
0597: } else if (dstType < DataBuffer.TYPE_FLOAT) {
0598: normi = ((1l << destComponentSize[0]) - 1) / 65535.0;
0599: normh = ((1l << destComponentSize[1]) - 1) / PI2;
0600: norms = ((1l << destComponentSize[2]) - 1);
0601: }
0602:
0603: int height = dest.getHeight();
0604: int width = dest.getWidth();
0605:
0606: double[] dstPixels = null;
0607: int[] dstIntPixels = null;
0608:
0609: if (isByte)
0610: dstIntPixels = new int[3 * height * width];
0611: else
0612: dstPixels = new double[3 * height * width];
0613:
0614: int rStart = src.bandOffsets[0];
0615: int gStart = src.bandOffsets[1];
0616: int bStart = src.bandOffsets[2];
0617: int srcPixelStride = src.pixelStride;
0618: int srcLineStride = src.lineStride;
0619:
0620: int dIndex = 0;
0621: for (int j = 0; j < height; j++, rStart += srcLineStride, gStart += srcLineStride, bStart += srcLineStride) {
0622: for (int i = 0, rIndex = rStart, gIndex = gStart, bIndex = bStart; i < width; i++, rIndex += srcPixelStride, gIndex += srcPixelStride, bIndex += srcPixelStride) {
0623: int R = (rBuf[rIndex] & 0xFFFF) << normr;
0624: int G = (gBuf[gIndex] & 0xFFFF) << normg;
0625: int B = (bBuf[bIndex] & 0xFFFF) << normb;
0626:
0627: if (isByte) {
0628: float intensity = (R + G + B) / 3.0f;
0629: dstIntPixels[dIndex++] = ((int) (intensity + 0.5f)) >> bnormi;
0630:
0631: int drg = R - G;
0632: int drb = R - B;
0633:
0634: double tint = drg * (double) drg + drb
0635: * (double) (drb - drg);
0636:
0637: double sum = drg + drb;
0638: double temp;
0639: if (tint != 0)
0640: temp = sqrtTable[(int) (250.0 * sum * sum
0641: / tint + 0.5)];
0642: else
0643: temp = -1.0;
0644:
0645: int hue;
0646: if (sum > 0)
0647: hue = acosTable[(int) (500 * temp + 0.5) + 500];
0648: else
0649: hue = acosTable[(int) (-500 * temp - 0.5) + 500];
0650:
0651: if (B >= G)
0652: dstIntPixels[dIndex++] = (255 - hue) >> bnormh;
0653: else
0654: dstIntPixels[dIndex++] = hue >> bnormh;
0655:
0656: int min = (G > B) ? B : G;
0657: min = (R > min) ? min : R;
0658: dstIntPixels[dIndex++] = (255 - (int) (255 * min
0659: / intensity + 0.5f)) >> bnorms;
0660:
0661: } else {
0662: float intensity = (R + G + B) / 3.0f;
0663: dstPixels[dIndex++] = normi * intensity;
0664:
0665: double drg = R - G;
0666: double drb = R - B;
0667: double temp = Math.sqrt(drg * drg + drb
0668: * (drb - drg));
0669:
0670: if (temp != 0) {
0671: temp = Math.acos((drg + drb) / temp / 2);
0672: if (B >= G)
0673: temp = PI2 - temp;
0674: } else
0675: temp = PI2;
0676:
0677: dstPixels[dIndex++] = normh * temp;
0678:
0679: double min = (G > B) ? B : G;
0680: min = (R > min) ? min : R;
0681: dstPixels[dIndex++] = norms * (1 - min / intensity);
0682: }
0683: }
0684: }
0685:
0686: if (isByte)
0687: dest.setPixels(dest.getMinX(), dest.getMinY(), width,
0688: height, dstIntPixels);
0689: else {
0690: convertToSigned(dstPixels, dstType);
0691: dest.setPixels(dest.getMinX(), dest.getMinY(), width,
0692: height, dstPixels);
0693: }
0694: }
0695:
0696: // convert an int type raster from RGB to IHS
0697: private void fromRGBInt(UnpackedImageData src,
0698: int[] srcComponentSize, WritableRaster dest,
0699: int[] destComponentSize) {
0700: int[] rBuf = src.getIntData(0);
0701: int[] gBuf = src.getIntData(1);
0702: int[] bBuf = src.getIntData(2);
0703:
0704: int normr = 32 - srcComponentSize[0];
0705: int normg = 32 - srcComponentSize[1];
0706: int normb = 32 - srcComponentSize[2];
0707:
0708: double range = Integer.MAX_VALUE - (double) Integer.MIN_VALUE;
0709: double normi = 1.0 / range, normh = 1.0, norms = 1.0;
0710:
0711: int bnormi = 0, bnormh = 0, bnorms = 0;
0712:
0713: int dstType = dest.getSampleModel().getDataType();
0714: boolean isByte = (dstType == DataBuffer.TYPE_BYTE);
0715:
0716: if (isByte) {
0717: bnormi = 32 - destComponentSize[0];
0718: bnormh = 8 - destComponentSize[1];
0719: bnorms = 8 - destComponentSize[2];
0720: generateACosTable();
0721: generateSqrtTable();
0722: } else if (dstType < DataBuffer.TYPE_FLOAT) {
0723: normi = ((1l << destComponentSize[0]) - 1) / range;
0724: normh = ((1l << destComponentSize[1]) - 1) / PI2;
0725: norms = ((1l << destComponentSize[2]) - 1);
0726: }
0727:
0728: int height = dest.getHeight();
0729: int width = dest.getWidth();
0730:
0731: double[] dstPixels = null;
0732: int[] dstIntPixels = null;
0733:
0734: if (isByte)
0735: dstIntPixels = new int[3 * height * width];
0736: else
0737: dstPixels = new double[3 * height * width];
0738:
0739: int rStart = src.bandOffsets[0];
0740: int gStart = src.bandOffsets[1];
0741: int bStart = src.bandOffsets[2];
0742: int srcPixelStride = src.pixelStride;
0743: int srcLineStride = src.lineStride;
0744:
0745: int dIndex = 0;
0746: for (int j = 0; j < height; j++, rStart += srcLineStride, gStart += srcLineStride, bStart += srcLineStride) {
0747: for (int i = 0, rIndex = rStart, gIndex = gStart, bIndex = bStart; i < width; i++, rIndex += srcPixelStride, gIndex += srcPixelStride, bIndex += srcPixelStride) {
0748: long R = (rBuf[rIndex] & 0xFFFFFFFFl) << normr;
0749: long G = (gBuf[gIndex] & 0xFFFFFFFFl) << normg;
0750: long B = (bBuf[bIndex] & 0xFFFFFFFFl) << normb;
0751:
0752: if (isByte) {
0753: float intensity = (R + G + B) / 3.0f;
0754: dstIntPixels[dIndex++] = (int) (((long) (intensity + 0.5f)) >> bnormi);
0755:
0756: long drg = R - G;
0757: long drb = R - B;
0758:
0759: double tint = drg * (double) drg + drb
0760: * (double) (drb - drg);
0761:
0762: double sum = drg + drb;
0763: double temp;
0764: if (tint != 0)
0765: temp = sqrtTable[(int) (250.0 * sum * sum
0766: / tint + 0.5)];
0767: else
0768: temp = -1.0;
0769:
0770: int hue;
0771: if (sum > 0)
0772: hue = acosTable[(int) (500 * temp + 0.5) + 500];
0773: else
0774: hue = acosTable[(int) (-500 * temp - 0.5) + 500];
0775:
0776: if (B >= G)
0777: dstIntPixels[dIndex++] = (255 - hue) >> bnormh;
0778: else
0779: dstIntPixels[dIndex++] = hue >> bnormh;
0780:
0781: long min = (G > B) ? B : G;
0782: min = (R > min) ? min : R;
0783: dstIntPixels[dIndex++] = (255 - (int) (255 * min
0784: / intensity + 0.5f)) >> bnorms;
0785: } else {
0786: float intensity = (R + G + B) / 3.0f;
0787: dstPixels[dIndex++] = normi * intensity;
0788:
0789: double drg = R - G;
0790: double drb = R - B;
0791: double temp = Math.sqrt(drg * drg + drb
0792: * (drb - drg));
0793:
0794: if (temp != 0) {
0795: temp = Math.acos((drg + drb) / temp / 2);
0796: if (B >= G)
0797: temp = PI2 - temp;
0798: } else
0799: temp = PI2;
0800:
0801: dstPixels[dIndex++] = normh * temp;
0802:
0803: double min = (G > B) ? B : G;
0804: min = (R > min) ? min : R;
0805: dstPixels[dIndex++] = norms * (1 - min / intensity);
0806: }
0807: }
0808: }
0809:
0810: if (isByte)
0811: dest.setPixels(dest.getMinX(), dest.getMinY(), width,
0812: height, dstIntPixels);
0813: else {
0814: convertToSigned(dstPixels, dstType);
0815: dest.setPixels(dest.getMinX(), dest.getMinY(), width,
0816: height, dstPixels);
0817: }
0818: }
0819:
0820: // convert a float type raster from RGB to IHS
0821: private void fromRGBFloat(UnpackedImageData src,
0822: int[] srcComponentSize, WritableRaster dest,
0823: int[] destComponentSize) {
0824: float[] rBuf = src.getFloatData(0);
0825: float[] gBuf = src.getFloatData(1);
0826: float[] bBuf = src.getFloatData(2);
0827:
0828: double normi = 1.0, normh = 1.0, norms = 1.0;
0829:
0830: int bnormi = 0, bnormh = 0, bnorms = 0;
0831:
0832: int dstType = dest.getSampleModel().getDataType();
0833: boolean isByte = (dstType == DataBuffer.TYPE_BYTE);
0834:
0835: if (isByte) {
0836: bnormi = (1 << destComponentSize[0]) - 1;
0837: bnormh = 8 - destComponentSize[1];
0838: bnorms = 8 - destComponentSize[2];
0839: generateACosTable();
0840: generateSqrtTable();
0841: } else if (dstType < DataBuffer.TYPE_FLOAT) {
0842: normi = (1l << destComponentSize[0]) - 1;
0843: normh = ((1l << destComponentSize[1]) - 1) / PI2;
0844: norms = (1l << destComponentSize[2]) - 1;
0845: }
0846:
0847: int height = dest.getHeight();
0848: int width = dest.getWidth();
0849:
0850: double[] dstPixels = null;
0851: int[] dstIntPixels = null;
0852:
0853: if (isByte)
0854: dstIntPixels = new int[3 * height * width];
0855: else
0856: dstPixels = new double[3 * height * width];
0857:
0858: int rStart = src.bandOffsets[0];
0859: int gStart = src.bandOffsets[1];
0860: int bStart = src.bandOffsets[2];
0861: int srcPixelStride = src.pixelStride;
0862: int srcLineStride = src.lineStride;
0863:
0864: int dIndex = 0;
0865: for (int j = 0; j < height; j++, rStart += srcLineStride, gStart += srcLineStride, bStart += srcLineStride) {
0866: for (int i = 0, rIndex = rStart, gIndex = gStart, bIndex = bStart; i < width; i++, rIndex += srcPixelStride, gIndex += srcPixelStride, bIndex += srcPixelStride) {
0867: float R = rBuf[rIndex];
0868: float G = gBuf[gIndex];
0869: float B = bBuf[bIndex];
0870:
0871: if (isByte) {
0872: float intensity = (R + G + B) / 3.0f;
0873: dstIntPixels[dIndex++] = (int) (intensity * bnormi + 0.5f);
0874:
0875: float drg = R - G;
0876: float drb = R - B;
0877:
0878: double tint = drg * (double) drg + drb
0879: * (double) (drb - drg);
0880:
0881: double sum = drg + drb;
0882: double temp;
0883: if (tint != 0)
0884: temp = sqrtTable[(int) (250.0 * sum * sum
0885: / tint + 0.5)];
0886: else
0887: temp = -1.0;
0888:
0889: int hue;
0890: if (sum > 0)
0891: hue = acosTable[(int) (500 * temp + 0.5) + 500];
0892: else
0893: hue = acosTable[(int) (-500 * temp - 0.5) + 500];
0894:
0895: if (B >= G)
0896: dstIntPixels[dIndex++] = (255 - hue) >> bnormh;
0897: else
0898: dstIntPixels[dIndex++] = hue >> bnormh;
0899:
0900: float min = (G > B) ? B : G;
0901: min = (R > min) ? min : R;
0902: dstIntPixels[dIndex++] = (255 - (int) (255 * min
0903: / intensity + 0.5f)) >> bnorms;
0904: } else {
0905: float intensity = (R + G + B) / 3.0f;
0906: dstPixels[dIndex++] = normi * intensity;
0907:
0908: double drg = R - G;
0909: double drb = R - B;
0910: double temp = Math.sqrt(drg * drg + drb
0911: * (drb - drg));
0912:
0913: if (temp != 0) {
0914: temp = Math.acos((drg + drb) / temp / 2);
0915: if (B >= G)
0916: temp = PI2 - temp;
0917: } else
0918: temp = PI2;
0919:
0920: dstPixels[dIndex++] = normh * temp;
0921:
0922: double min = (G > B) ? B : G;
0923: min = (R > min) ? min : R;
0924: dstPixels[dIndex++] = norms * (1 - min / intensity);
0925: }
0926: }
0927: }
0928:
0929: if (isByte)
0930: dest.setPixels(dest.getMinX(), dest.getMinY(), width,
0931: height, dstIntPixels);
0932: else {
0933: convertToSigned(dstPixels, dstType);
0934: dest.setPixels(dest.getMinX(), dest.getMinY(), width,
0935: height, dstPixels);
0936: }
0937: }
0938:
0939: // convert a double type raster from RGB to IHS
0940: private void fromRGBDouble(UnpackedImageData src,
0941: int[] srcComponentSize, WritableRaster dest,
0942: int[] destComponentSize) {
0943: double[] rBuf = src.getDoubleData(0);
0944: double[] gBuf = src.getDoubleData(1);
0945: double[] bBuf = src.getDoubleData(2);
0946:
0947: double normi = 1.0, normh = 1.0, norms = 1.0;
0948:
0949: int bnormi = 0, bnormh = 0, bnorms = 0;
0950:
0951: int dstType = dest.getSampleModel().getDataType();
0952: boolean isByte = (dstType == DataBuffer.TYPE_BYTE);
0953:
0954: if (isByte) {
0955: bnormi = (1 << destComponentSize[0]) - 1;
0956: bnormh = 8 - destComponentSize[1];
0957: bnorms = 8 - destComponentSize[2];
0958: generateACosTable();
0959: generateSqrtTable();
0960: } else if (dstType < DataBuffer.TYPE_FLOAT) {
0961: normi = (1l << destComponentSize[0]) - 1;
0962: normh = ((1l << destComponentSize[1]) - 1) / PI2;
0963: norms = (1l << destComponentSize[2]) - 1;
0964: }
0965:
0966: int height = dest.getHeight();
0967: int width = dest.getWidth();
0968:
0969: double[] dstPixels = null;
0970: int[] dstIntPixels = null;
0971:
0972: if (isByte)
0973: dstIntPixels = new int[3 * height * width];
0974: else
0975: dstPixels = new double[3 * height * width];
0976:
0977: int rStart = src.bandOffsets[0];
0978: int gStart = src.bandOffsets[1];
0979: int bStart = src.bandOffsets[2];
0980: int srcPixelStride = src.pixelStride;
0981: int srcLineStride = src.lineStride;
0982:
0983: int dIndex = 0;
0984: for (int j = 0; j < height; j++, rStart += srcLineStride, gStart += srcLineStride, bStart += srcLineStride) {
0985: for (int i = 0, rIndex = rStart, gIndex = gStart, bIndex = bStart; i < width; i++, rIndex += srcPixelStride, gIndex += srcPixelStride, bIndex += srcPixelStride) {
0986: double R = rBuf[rIndex];
0987: double G = gBuf[gIndex];
0988: double B = bBuf[bIndex];
0989:
0990: if (isByte) {
0991: double intensity = (R + G + B) / 3.0f;
0992: dstIntPixels[dIndex++] = (int) (intensity * bnormi + 0.5);
0993:
0994: double drg = R - G;
0995: double drb = R - B;
0996:
0997: double tint = drg * drg + drb * (drb - drg);
0998:
0999: double sum = drg + drb;
1000: double temp;
1001: if (tint != 0.0)
1002: temp = sqrtTable[(int) (250.0 * sum * sum
1003: / tint + 0.5)];
1004: else
1005: temp = -1.0;
1006:
1007: int hue;
1008: if (sum > 0)
1009: hue = acosTable[(int) (500 * temp + 0.5) + 500];
1010: else
1011: hue = acosTable[(int) (-500 * temp - 0.5) + 500];
1012:
1013: if (B >= G)
1014: dstIntPixels[dIndex++] = (255 - hue) >> bnormh;
1015: else
1016: dstIntPixels[dIndex++] = hue >> bnormh;
1017:
1018: double min = (G > B) ? B : G;
1019: min = (R > min) ? min : R;
1020: dstIntPixels[dIndex++] = (255 - (int) (255 * min
1021: / intensity + 0.5f)) >> bnorms;
1022: } else {
1023: double intensity = (R + G + B) / 3.0f;
1024: dstPixels[dIndex++] = normi * intensity;
1025:
1026: double drg = R - G;
1027: double drb = R - B;
1028: double temp = Math.sqrt(drg * drg + drb
1029: * (drb - drg));
1030:
1031: if (temp != 0) {
1032: temp = Math.acos((drg + drb) / temp / 2);
1033: if (B >= G)
1034: temp = PI2 - temp;
1035: } else
1036: temp = PI2;
1037:
1038: dstPixels[dIndex++] = normh * temp;
1039:
1040: double min = (G > B) ? B : G;
1041: min = (R > min) ? min : R;
1042: dstPixels[dIndex++] = norms * (1 - min / intensity);
1043: }
1044: }
1045: }
1046: if (isByte)
1047: dest.setPixels(dest.getMinX(), dest.getMinY(), width,
1048: height, dstIntPixels);
1049: else {
1050: convertToSigned(dstPixels, dstType);
1051: dest.setPixels(dest.getMinX(), dest.getMinY(), width,
1052: height, dstPixels);
1053: }
1054: }
1055:
1056: /**
1057: * Converts a <code>Raster</code> of colors represented as pixels
1058: * from IHS to CIEXYZ.
1059: */
1060: public WritableRaster toCIEXYZ(Raster src, int[] srcComponentSize,
1061: WritableRaster dest, int[] destComponentSize) {
1062: WritableRaster tempRas = toRGB(src, srcComponentSize, null,
1063: null);
1064: return RGBToCIEXYZ(tempRas, tempRas.getSampleModel()
1065: .getSampleSize(), dest, destComponentSize);
1066: }
1067:
1068: /**
1069: * Converts a <code>Raster</code> of colors represented as pixels
1070: * from IHS to sRGB.
1071: */
1072: public WritableRaster toRGB(Raster src, int[] srcComponentSize,
1073: WritableRaster dest, int[] destComponentSize) {
1074:
1075: checkParameters(src, srcComponentSize, dest, destComponentSize);
1076:
1077: SampleModel srcSampleModel = src.getSampleModel();
1078:
1079: if (srcComponentSize == null)
1080: srcComponentSize = srcSampleModel.getSampleSize();
1081:
1082: if (dest == null) {
1083: Point origin = new Point(src.getMinX(), src.getMinY());
1084: dest = RasterFactory.createWritableRaster(srcSampleModel,
1085: origin);
1086: }
1087:
1088: SampleModel dstSampleModel = dest.getSampleModel();
1089: if (destComponentSize == null)
1090: destComponentSize = dstSampleModel.getSampleSize();
1091:
1092: PixelAccessor srcAcc = new PixelAccessor(srcSampleModel, null);
1093: UnpackedImageData srcUid = srcAcc.getPixels(src, src
1094: .getBounds(), srcSampleModel.getDataType(), false);
1095:
1096: switch (srcSampleModel.getDataType()) {
1097:
1098: case DataBuffer.TYPE_BYTE:
1099: toRGBByte(srcUid, srcComponentSize, dest, destComponentSize);
1100: break;
1101: case DataBuffer.TYPE_USHORT:
1102: case DataBuffer.TYPE_SHORT:
1103: toRGBShort(srcUid, srcComponentSize, dest,
1104: destComponentSize);
1105: break;
1106: case DataBuffer.TYPE_INT:
1107: toRGBInt(srcUid, srcComponentSize, dest, destComponentSize);
1108: break;
1109: case DataBuffer.TYPE_FLOAT:
1110: toRGBFloat(srcUid, srcComponentSize, dest,
1111: destComponentSize);
1112: break;
1113: case DataBuffer.TYPE_DOUBLE:
1114: toRGBDouble(srcUid, srcComponentSize, dest,
1115: destComponentSize);
1116: break;
1117: }
1118: return dest;
1119: }
1120:
1121: // convert a byte type raster from IHS to RGB
1122: private void toRGBByte(UnpackedImageData src,
1123: int[] srcComponentSize, WritableRaster dest,
1124: int[] destComponentSize) {
1125: byte[] iBuf = src.getByteData(0);
1126: byte[] hBuf = src.getByteData(1);
1127: byte[] sBuf = src.getByteData(2);
1128:
1129: double normi = 1.0 / ((1 << srcComponentSize[0]) - 1);
1130: double normh = 1.0 / ((1 << srcComponentSize[1]) - 1) * PI2;
1131: double norms = 1.0 / ((1 << srcComponentSize[2]) - 1);
1132:
1133: double normr = 1.0, normg = 1.0, normb = 1.0;
1134:
1135: int dstType = dest.getSampleModel().getDataType();
1136: boolean isByte = (dstType == DataBuffer.TYPE_BYTE);
1137:
1138: if (isByte) {
1139: generateTanTable();
1140: }
1141: if (dstType < DataBuffer.TYPE_FLOAT) {
1142: normr = (1l << destComponentSize[0]) - 1;
1143: normg = (1l << destComponentSize[1]) - 1;
1144: normb = (1l << destComponentSize[2]) - 1;
1145: }
1146:
1147: int height = dest.getHeight();
1148: int width = dest.getWidth();
1149:
1150: double[] dstPixels = null;
1151: int[] dstIntPixels = null;
1152:
1153: if (isByte)
1154: dstIntPixels = new int[3 * height * width];
1155: else
1156: dstPixels = new double[3 * height * width];
1157:
1158: int iStart = src.bandOffsets[0];
1159: int hStart = src.bandOffsets[1];
1160: int sStart = src.bandOffsets[2];
1161: int srcPixelStride = src.pixelStride;
1162: int srcLineStride = src.lineStride;
1163:
1164: int dIndex = 0;
1165: for (int j = 0; j < height; j++, iStart += srcLineStride, hStart += srcLineStride, sStart += srcLineStride) {
1166: for (int i = 0, iIndex = iStart, hIndex = hStart, sIndex = sStart; i < width; i++, iIndex += srcPixelStride, hIndex += srcPixelStride, sIndex += srcPixelStride) {
1167: double I = (iBuf[iIndex] & 0xFF) * normi;
1168: int h = hBuf[hIndex] & 0xFF;
1169: double S = (sBuf[sIndex] & 0xFF) * norms;
1170:
1171: if (isByte) {
1172: float r, g, b;
1173:
1174: r = g = b = (float) I;
1175:
1176: if (S != 0.0) {
1177: if (h >= 85 && h <= 170) {
1178: r = (float) ((1 - S) * I);
1179: float c1 = (float) (3 * I - r);
1180: float c2 = (float) (SQRT3 * (r - I) * tanTable[h]);
1181: g = (float) ((c1 + c2) / 2);
1182: b = (float) ((c1 - c2) / 2);
1183: } else if (h > 170) {
1184: g = (float) ((1 - S) * I);
1185: float c1 = (float) (3 * I - g);
1186: float c2 = (float) (SQRT3 * (g - I) * tanTable[h - 85]);
1187: b = (c1 + c2) / 2;
1188: r = (c1 - c2) / 2;
1189: } else if (h < 85) {
1190: b = (float) ((1 - S) * I);
1191: float c1 = (float) (3 * I - b);
1192: float c2 = (float) (SQRT3 * (b - I) * tanTable[h + 85]);
1193: r = (c1 + c2) / 2;
1194: g = (c1 - c2) / 2;
1195: }
1196: }
1197: dstIntPixels[dIndex++] = (int) (((r < 0.0f) ? 0.0f
1198: : ((r > 1.0f) ? 1.0f : r))
1199: * normr + 0.5);
1200: dstIntPixels[dIndex++] = (int) (((g < 0.0f) ? 0.0f
1201: : ((g > 1.0f) ? 1.0f : g))
1202: * normg + 0.5);
1203: dstIntPixels[dIndex++] = (int) (((b < 0.0f) ? 0.0f
1204: : ((b > 1.0f) ? 1.0f : b))
1205: * normb + 0.5);
1206: } else {
1207: double R, G, B;
1208:
1209: R = G = B = I;
1210: if (S != 0) {
1211: double H = h * normh;
1212: if (H >= PI23 && H <= PI43) {
1213: R = (1 - S) * I;
1214: double c1 = 3 * I - R;
1215: double c2 = SQRT3 * (R - I) * Math.tan(H);
1216: G = (c1 + c2) / 2;
1217: B = (c1 - c2) / 2;
1218: } else if (H > PI43) {
1219: G = (1 - S) * I;
1220: double c1 = 3 * I - G;
1221: double c2 = SQRT3 * (G - I)
1222: * Math.tan(H - PI23);
1223: B = (c1 + c2) / 2;
1224: R = (c1 - c2) / 2;
1225: } else if (H < PI23) {
1226: B = (1 - S) * I;
1227: double c1 = 3 * I - B;
1228: double c2 = SQRT3 * (B - I)
1229: * Math.tan(H - PI43);
1230: R = (c1 + c2) / 2;
1231: G = (c1 - c2) / 2;
1232: }
1233: }
1234: dstPixels[dIndex++] = ((R < 0) ? 0
1235: : ((R > 1.0) ? 1.0 : R))
1236: * normr;
1237: dstPixels[dIndex++] = ((G < 0) ? 0
1238: : ((G > 1.0) ? 1.0 : G))
1239: * normg;
1240: dstPixels[dIndex++] = ((B < 0) ? 0
1241: : ((B > 1.0) ? 1.0 : B))
1242: * normb;
1243: }
1244: }
1245: }
1246:
1247: if (isByte)
1248: dest.setPixels(dest.getMinX(), dest.getMinY(), width,
1249: height, dstIntPixels);
1250: else {
1251: convertToSigned(dstPixels, dstType);
1252: dest.setPixels(dest.getMinX(), dest.getMinY(), width,
1253: height, dstPixels);
1254: }
1255:
1256: }
1257:
1258: // convert a short type ratser from IHS to RGB
1259: private void toRGBShort(UnpackedImageData src,
1260: int[] srcComponentSize, WritableRaster dest,
1261: int[] destComponentSize) {
1262: short[] iBuf = src.getShortData(0);
1263: short[] hBuf = src.getShortData(1);
1264: short[] sBuf = src.getShortData(2);
1265:
1266: double normi = 1.0 / ((1 << srcComponentSize[0]) - 1);
1267: double normh = 1.0 / ((1 << srcComponentSize[1]) - 1) * PI2;
1268: double norms = 1.0 / ((1 << srcComponentSize[2]) - 1);
1269:
1270: double normr = 1.0, normg = 1.0, normb = 1.0;
1271:
1272: int dstType = dest.getSampleModel().getDataType();
1273:
1274: if (dstType < DataBuffer.TYPE_FLOAT) {
1275: normr = (1l << destComponentSize[0]) - 1;
1276: normg = (1l << destComponentSize[1]) - 1;
1277: normb = (1l << destComponentSize[2]) - 1;
1278: }
1279:
1280: int height = dest.getHeight();
1281: int width = dest.getWidth();
1282:
1283: double[] dstPixels = new double[3 * height * width];
1284:
1285: int iStart = src.bandOffsets[0];
1286: int hStart = src.bandOffsets[1];
1287: int sStart = src.bandOffsets[2];
1288: int srcPixelStride = src.pixelStride;
1289: int srcLineStride = src.lineStride;
1290:
1291: int dIndex = 0;
1292: for (int j = 0; j < height; j++, iStart += srcLineStride, hStart += srcLineStride, sStart += srcLineStride) {
1293: for (int i = 0, iIndex = iStart, hIndex = hStart, sIndex = sStart; i < width; i++, iIndex += srcPixelStride, hIndex += srcPixelStride, sIndex += srcPixelStride) {
1294: double I = (iBuf[iIndex] & 0xFFFF) * normi;
1295: double H = (hBuf[hIndex] & 0xFFFF) * normh;
1296: double S = (sBuf[sIndex] & 0xFFFF) * norms;
1297: double R, G, B;
1298:
1299: R = G = B = I;
1300: if (S != 0.0) {
1301: if (H >= PI23 && H <= PI43) {
1302: R = (1 - S) * I;
1303: double c1 = 3 * I - R;
1304: double c2 = SQRT3 * (R - I) * Math.tan(H);
1305: G = (c1 + c2) / 2;
1306: B = (c1 - c2) / 2;
1307: } else if (H > PI43) {
1308: G = (1 - S) * I;
1309: double c1 = 3 * I - G;
1310: double c2 = SQRT3 * (G - I)
1311: * Math.tan(H - PI23);
1312: B = (c1 + c2) / 2;
1313: R = (c1 - c2) / 2;
1314: } else if (H < PI23) {
1315: B = (1 - S) * I;
1316: double c1 = 3 * I - B;
1317: double c2 = SQRT3 * (B - I)
1318: * Math.tan(H - PI43);
1319: R = (c1 + c2) / 2;
1320: G = (c1 - c2) / 2;
1321: }
1322: }
1323:
1324: dstPixels[dIndex++] = ((R < 0) ? 0 : ((R > 1.0) ? 1.0
1325: : R))
1326: * normr;
1327: dstPixels[dIndex++] = ((G < 0) ? 0 : ((G > 1.0) ? 1.0
1328: : G))
1329: * normg;
1330: dstPixels[dIndex++] = ((B < 0) ? 0 : ((B > 1.0) ? 1.0
1331: : B))
1332: * normb;
1333: }
1334: }
1335:
1336: convertToSigned(dstPixels, dstType);
1337: dest.setPixels(dest.getMinX(), dest.getMinY(), width, height,
1338: dstPixels);
1339: }
1340:
1341: private void toRGBInt(UnpackedImageData src,
1342: int[] srcComponentSize, WritableRaster dest,
1343: int[] destComponentSize) {
1344: int[] iBuf = src.getIntData(0);
1345: int[] hBuf = src.getIntData(1);
1346: int[] sBuf = src.getIntData(2);
1347:
1348: double normi = 1.0 / ((1l << srcComponentSize[0]) - 1);
1349: double normh = 1.0 / ((1l << srcComponentSize[1]) - 1) * PI2;
1350: double norms = 1.0 / ((1l << srcComponentSize[2]) - 1);
1351:
1352: double normr = 1.0, normg = 1.0, normb = 1.0;
1353:
1354: int dstType = dest.getSampleModel().getDataType();
1355:
1356: if (dstType < DataBuffer.TYPE_FLOAT) {
1357: normr = (1l << destComponentSize[0]) - 1;
1358: normg = (1l << destComponentSize[1]) - 1;
1359: normb = (1l << destComponentSize[2]) - 1;
1360: }
1361:
1362: int height = dest.getHeight();
1363: int width = dest.getWidth();
1364:
1365: double[] dstPixels = new double[3 * height * width];
1366:
1367: int iStart = src.bandOffsets[0];
1368: int hStart = src.bandOffsets[1];
1369: int sStart = src.bandOffsets[2];
1370: int srcPixelStride = src.pixelStride;
1371: int srcLineStride = src.lineStride;
1372:
1373: int dIndex = 0;
1374: for (int j = 0; j < height; j++, iStart += srcLineStride, hStart += srcLineStride, sStart += srcLineStride) {
1375: for (int i = 0, iIndex = iStart, hIndex = hStart, sIndex = sStart; i < width; i++, iIndex += srcPixelStride, hIndex += srcPixelStride, sIndex += srcPixelStride) {
1376: double I = (iBuf[iIndex] & 0xFFFFFFFFl) * normi;
1377: double H = (hBuf[hIndex] & 0xFFFFFFFFl) * normh;
1378: double S = (sBuf[sIndex] & 0xFFFFFFFFl) * norms;
1379:
1380: double R, G, B;
1381:
1382: R = G = B = I;
1383: if (S != 0) {
1384: if (H >= PI23 && H <= PI43) {
1385: R = (1 - S) * I;
1386: double c1 = 3 * I - R;
1387: double c2 = SQRT3 * (R - I) * Math.tan(H);
1388: G = (c1 + c2) / 2;
1389: B = (c1 - c2) / 2;
1390: } else if (H > PI43) {
1391: G = (1 - S) * I;
1392: double c1 = 3 * I - G;
1393: double c2 = SQRT3 * (G - I)
1394: * Math.tan(H - PI23);
1395: B = (c1 + c2) / 2;
1396: R = (c1 - c2) / 2;
1397: } else if (H < PI23) {
1398: B = (1 - S) * I;
1399: double c1 = 3 * I - B;
1400: double c2 = SQRT3 * (B - I)
1401: * Math.tan(H - PI43);
1402: R = (c1 + c2) / 2;
1403: G = (c1 - c2) / 2;
1404: }
1405: }
1406:
1407: dstPixels[dIndex++] = ((R < 0) ? 0 : ((R > 1.0) ? 1.0
1408: : R))
1409: * normr;
1410: dstPixels[dIndex++] = ((G < 0) ? 0 : ((G > 1.0) ? 1.0
1411: : G))
1412: * normg;
1413: dstPixels[dIndex++] = ((B < 0) ? 0 : ((B > 1.0) ? 1.0
1414: : B))
1415: * normb;
1416: }
1417: }
1418: convertToSigned(dstPixels, dstType);
1419: dest.setPixels(dest.getMinX(), dest.getMinY(), width, height,
1420: dstPixels);
1421: }
1422:
1423: private void toRGBFloat(UnpackedImageData src,
1424: int[] srcComponentSize, WritableRaster dest,
1425: int[] destComponentSize) {
1426: float[] iBuf = src.getFloatData(0);
1427: float[] hBuf = src.getFloatData(1);
1428: float[] sBuf = src.getFloatData(2);
1429:
1430: double normr = 1.0, normg = 1.0, normb = 1.0;
1431:
1432: int dstType = dest.getSampleModel().getDataType();
1433:
1434: if (dstType < DataBuffer.TYPE_FLOAT) {
1435: normr = (1l << destComponentSize[0]) - 1;
1436: normg = (1l << destComponentSize[1]) - 1;
1437: normb = (1l << destComponentSize[2]) - 1;
1438: }
1439:
1440: int height = dest.getHeight();
1441: int width = dest.getWidth();
1442:
1443: double[] dstPixels = new double[3 * height * width];
1444:
1445: int iStart = src.bandOffsets[0];
1446: int hStart = src.bandOffsets[1];
1447: int sStart = src.bandOffsets[2];
1448: int srcPixelStride = src.pixelStride;
1449: int srcLineStride = src.lineStride;
1450:
1451: int dIndex = 0;
1452: for (int j = 0; j < height; j++, iStart += srcLineStride, hStart += srcLineStride, sStart += srcLineStride) {
1453: for (int i = 0, iIndex = iStart, hIndex = hStart, sIndex = sStart; i < width; i++, iIndex += srcPixelStride, hIndex += srcPixelStride, sIndex += srcPixelStride) {
1454: double I = iBuf[iIndex];
1455: double H = hBuf[hIndex];
1456: double S = sBuf[sIndex];
1457: double R, G, B;
1458:
1459: R = G = B = I;
1460: if (S != 0) {
1461: if (H >= PI23 && H <= PI43) {
1462: R = (1 - S) * I;
1463: double c1 = 3 * I - R;
1464: double c2 = SQRT3 * (R - I) * Math.tan(H);
1465: G = (c1 + c2) / 2;
1466: B = (c1 - c2) / 2;
1467: } else if (H > PI43) {
1468: G = (1 - S) * I;
1469: double c1 = 3 * I - G;
1470: double c2 = SQRT3 * (G - I)
1471: * Math.tan(H - PI23);
1472: B = (c1 + c2) / 2;
1473: R = (c1 - c2) / 2;
1474: } else if (H < PI23) {
1475: B = (1 - S) * I;
1476: double c1 = 3 * I - B;
1477: double c2 = SQRT3 * (B - I)
1478: * Math.tan(H - PI43);
1479: R = (c1 + c2) / 2;
1480: G = (c1 - c2) / 2;
1481: }
1482: }
1483:
1484: dstPixels[dIndex++] = ((R < 0) ? 0 : ((R > 1.0) ? 1.0
1485: : R))
1486: * normr;
1487: dstPixels[dIndex++] = ((G < 0) ? 0 : ((G > 1.0) ? 1.0
1488: : G))
1489: * normg;
1490: dstPixels[dIndex++] = ((B < 0) ? 0 : ((B > 1.0) ? 1.0
1491: : B))
1492: * normb;
1493: }
1494: }
1495: convertToSigned(dstPixels, dstType);
1496: dest.setPixels(dest.getMinX(), dest.getMinY(), width, height,
1497: dstPixels);
1498: }
1499:
1500: private void toRGBDouble(UnpackedImageData src,
1501: int[] srcComponentSize, WritableRaster dest,
1502: int[] destComponentSize) {
1503: double[] iBuf = src.getDoubleData(0);
1504: double[] hBuf = src.getDoubleData(1);
1505: double[] sBuf = src.getDoubleData(2);
1506:
1507: double normr = 1.0, normg = 1.0, normb = 1.0;
1508:
1509: int dstType = dest.getSampleModel().getDataType();
1510:
1511: if (dstType < DataBuffer.TYPE_FLOAT) {
1512: normr = (1l << destComponentSize[0]) - 1;
1513: normg = (1l << destComponentSize[1]) - 1;
1514: normb = (1l << destComponentSize[2]) - 1;
1515: }
1516:
1517: int height = dest.getHeight();
1518: int width = dest.getWidth();
1519:
1520: double[] dstPixels = new double[3 * height * width];
1521:
1522: int iStart = src.bandOffsets[0];
1523: int hStart = src.bandOffsets[1];
1524: int sStart = src.bandOffsets[2];
1525: int srcPixelStride = src.pixelStride;
1526: int srcLineStride = src.lineStride;
1527:
1528: int dIndex = 0;
1529: for (int j = 0; j < height; j++, iStart += srcLineStride, hStart += srcLineStride, sStart += srcLineStride) {
1530: for (int i = 0, iIndex = iStart, hIndex = hStart, sIndex = sStart; i < width; i++, iIndex += srcPixelStride, hIndex += srcPixelStride, sIndex += srcPixelStride) {
1531: double I = iBuf[iIndex];
1532: double H = hBuf[hIndex];
1533: double S = sBuf[sIndex];
1534:
1535: double R, G, B;
1536:
1537: R = G = B = I;
1538: if (S != 0) {
1539: if (H >= PI23 && H <= PI43) {
1540: R = (1 - S) * I;
1541: double c1 = 3 * I - R;
1542: double c2 = SQRT3 * (R - I) * Math.tan(H);
1543: G = (c1 + c2) / 2;
1544: B = (c1 - c2) / 2;
1545: } else if (H > PI43) {
1546: G = (1 - S) * I;
1547: double c1 = 3 * I - G;
1548: double c2 = SQRT3 * (G - I)
1549: * Math.tan(H - PI23);
1550: B = (c1 + c2) / 2;
1551: R = (c1 - c2) / 2;
1552: } else if (H < PI23) {
1553: B = (1 - S) * I;
1554: double c1 = 3 * I - B;
1555: double c2 = SQRT3 * (B - I)
1556: * Math.tan(H - PI43);
1557: R = (c1 + c2) / 2;
1558: G = (c1 - c2) / 2;
1559: }
1560: }
1561:
1562: dstPixels[dIndex++] = ((R < 0) ? 0 : ((R > 1.0) ? 1.0
1563: : R))
1564: * normr;
1565: dstPixels[dIndex++] = ((G < 0) ? 0 : ((G > 1.0) ? 1.0
1566: : G))
1567: * normg;
1568: dstPixels[dIndex++] = ((B < 0) ? 0 : ((B > 1.0) ? 1.0
1569: : B))
1570: * normb;
1571: }
1572: }
1573: convertToSigned(dstPixels, dstType);
1574: dest.setPixels(dest.getMinX(), dest.getMinY(), width, height,
1575: dstPixels);
1576: }
1577:
1578: }
|