001: /*
002: * Copyright 2007 Sun Microsystems, Inc. All Rights Reserved.
003: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004: *
005: * This code is free software; you can redistribute it and/or modify it
006: * under the terms of the GNU General Public License version 2 only, as
007: * published by the Free Software Foundation. Sun designates this
008: * particular file as subject to the "Classpath" exception as provided
009: * by Sun in the LICENSE file that accompanied this code.
010: *
011: * This code is distributed in the hope that it will be useful, but WITHOUT
012: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
013: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
014: * version 2 for more details (a copy is included in the LICENSE file that
015: * accompanied this code).
016: *
017: * You should have received a copy of the GNU General Public License version
018: * 2 along with this work; if not, write to the Free Software Foundation,
019: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
020: *
021: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
022: * CA 95054 USA or visit www.sun.com if you need additional information or
023: * have any questions.
024: */
025:
026: /**********************************************************************
027: **********************************************************************
028: **********************************************************************
029: *** COPYRIGHT (c) Eastman Kodak Company, 1997 ***
030: *** As an unpublished work pursuant to Title 17 of the United ***
031: *** States Code. All rights reserved. ***
032: **********************************************************************
033: **********************************************************************
034: **********************************************************************/package sun.java2d.cmm.lcms;
035:
036: import java.awt.color.ICC_Profile;
037: import java.awt.color.ProfileDataException;
038: import java.awt.color.CMMException;
039: import java.awt.color.ColorSpace;
040: import java.awt.image.BufferedImage;
041: import java.awt.image.Raster;
042: import java.awt.image.WritableRaster;
043: import java.awt.image.ColorModel;
044: import java.awt.image.DirectColorModel;
045: import java.awt.image.ComponentColorModel;
046: import java.awt.image.SampleModel;
047: import java.awt.image.DataBuffer;
048: import java.awt.image.SinglePixelPackedSampleModel;
049: import java.awt.image.ComponentSampleModel;
050: import sun.java2d.cmm.*;
051: import sun.java2d.cmm.lcms.*;
052:
053: public class LCMSTransform implements ColorTransform {
054: long ID;
055: ICC_Profile[] profiles;
056: long[] profileIDs;
057: int renderType;
058: int transformType;
059:
060: private Object disposerReferent = new Object();
061:
062: /* the class initializer */
063: static {
064: if (ProfileDeferralMgr.deferring) {
065: ProfileDeferralMgr.activateProfiles();
066: }
067: }
068:
069: public LCMSTransform(ICC_Profile profile, int renderType,
070: int transformType) {
071: /* Actually, it is not a complete transform but just part of it */
072: profiles = new ICC_Profile[1];
073: profiles[0] = profile;
074: profileIDs = new long[1];
075: profileIDs[0] = LCMS.getProfileID(profile);
076: this .renderType = (renderType == ColorTransform.Any) ? ICC_Profile.icPerceptual
077: : renderType;
078: this .transformType = transformType;
079: }
080:
081: public LCMSTransform(ColorTransform[] transforms) {
082: int size = 0;
083: for (int i = 0; i < transforms.length; i++) {
084: size += ((LCMSTransform) transforms[i]).profiles.length;
085: }
086: profiles = new ICC_Profile[size];
087: profileIDs = new long[size];
088: int j = 0;
089: for (int i = 0; i < transforms.length; i++) {
090: LCMSTransform curTrans = (LCMSTransform) transforms[i];
091: System.arraycopy(curTrans.profiles, 0, profiles, j,
092: curTrans.profiles.length);
093: System.arraycopy(curTrans.profileIDs, 0, profileIDs, j,
094: curTrans.profileIDs.length);
095: j += curTrans.profiles.length;
096: }
097: renderType = ((LCMSTransform) transforms[0]).renderType;
098: ID = LCMS.createNativeTransform(profileIDs, renderType,
099: disposerReferent);
100: }
101:
102: public int getNumInComponents() {
103: return profiles[0].getNumComponents();
104: }
105:
106: public int getNumOutComponents() {
107: return profiles[profiles.length - 1].getNumComponents();
108: }
109:
110: public void colorConvert(BufferedImage src, BufferedImage dst) {
111: if (LCMSImageLayout.isSupported(src)
112: && LCMSImageLayout.isSupported(dst)) {
113: synchronized (this ) {
114: LCMS.colorConvert(this , new LCMSImageLayout(src),
115: new LCMSImageLayout(dst));
116: }
117: return;
118: }
119: LCMSImageLayout srcIL, dstIL;
120: Raster srcRas = src.getRaster();
121: WritableRaster dstRas = dst.getRaster();
122: ColorModel srcCM = src.getColorModel();
123: ColorModel dstCM = dst.getColorModel();
124: int w = src.getWidth();
125: int h = src.getHeight();
126: int srcNumComp = srcCM.getNumColorComponents();
127: int dstNumComp = dstCM.getNumColorComponents();
128: int precision = 8;
129: float maxNum = 255.0f;
130: for (int i = 0; i < srcNumComp; i++) {
131: if (srcCM.getComponentSize(i) > 8) {
132: precision = 16;
133: maxNum = 65535.0f;
134: }
135: }
136: for (int i = 0; i < dstNumComp; i++) {
137: if (dstCM.getComponentSize(i) > 8) {
138: precision = 16;
139: maxNum = 65535.0f;
140: }
141: }
142: float[] srcMinVal = new float[srcNumComp];
143: float[] srcInvDiffMinMax = new float[srcNumComp];
144: ColorSpace cs = srcCM.getColorSpace();
145: for (int i = 0; i < srcNumComp; i++) {
146: srcMinVal[i] = cs.getMinValue(i);
147: srcInvDiffMinMax[i] = maxNum
148: / (cs.getMaxValue(i) - srcMinVal[i]);
149: }
150: cs = dstCM.getColorSpace();
151: float[] dstMinVal = new float[dstNumComp];
152: float[] dstDiffMinMax = new float[dstNumComp];
153: for (int i = 0; i < dstNumComp; i++) {
154: dstMinVal[i] = cs.getMinValue(i);
155: dstDiffMinMax[i] = (cs.getMaxValue(i) - dstMinVal[i])
156: / maxNum;
157: }
158: boolean dstHasAlpha = dstCM.hasAlpha();
159: boolean needSrcAlpha = srcCM.hasAlpha() && dstHasAlpha;
160: float[] dstColor;
161: if (dstHasAlpha) {
162: dstColor = new float[dstNumComp + 1];
163: } else {
164: dstColor = new float[dstNumComp];
165: }
166: if (precision == 8) {
167: byte[] srcLine = new byte[w * srcNumComp];
168: byte[] dstLine = new byte[w * dstNumComp];
169: Object pixel;
170: float[] color;
171: float[] alpha = null;
172: if (needSrcAlpha) {
173: alpha = new float[w];
174: }
175: int idx;
176: // TODO check for src npixels = dst npixels
177: srcIL = new LCMSImageLayout(srcLine, srcLine.length
178: / getNumInComponents(), LCMSImageLayout
179: .CHANNELS_SH(getNumInComponents())
180: | LCMSImageLayout.BYTES_SH(1), getNumInComponents());
181: dstIL = new LCMSImageLayout(dstLine, dstLine.length
182: / getNumOutComponents(), LCMSImageLayout
183: .CHANNELS_SH(getNumOutComponents())
184: | LCMSImageLayout.BYTES_SH(1),
185: getNumOutComponents());
186: // process each scanline
187: for (int y = 0; y < h; y++) {
188: // convert src scanline
189: pixel = null;
190: color = null;
191: idx = 0;
192: for (int x = 0; x < w; x++) {
193: pixel = srcRas.getDataElements(x, y, pixel);
194: color = srcCM.getNormalizedComponents(pixel, color,
195: 0);
196: for (int i = 0; i < srcNumComp; i++) {
197: srcLine[idx++] = (byte) ((color[i] - srcMinVal[i])
198: * srcInvDiffMinMax[i] + 0.5f);
199: }
200: if (needSrcAlpha) {
201: alpha[x] = color[srcNumComp];
202: }
203: }
204: // color convert srcLine to dstLine
205: synchronized (this ) {
206: LCMS.colorConvert(this , srcIL, dstIL);
207: }
208: // convert dst scanline
209: pixel = null;
210: idx = 0;
211: for (int x = 0; x < w; x++) {
212: for (int i = 0; i < dstNumComp; i++) {
213: dstColor[i] = ((float) (dstLine[idx++] & 0xff))
214: * dstDiffMinMax[i] + dstMinVal[i];
215: }
216: if (needSrcAlpha) {
217: dstColor[dstNumComp] = alpha[x];
218: } else if (dstHasAlpha) {
219: dstColor[dstNumComp] = 1.0f;
220: }
221: pixel = dstCM.getDataElements(dstColor, 0, pixel);
222: dstRas.setDataElements(x, y, pixel);
223: }
224: }
225: } else {
226: short[] srcLine = new short[w * srcNumComp];
227: short[] dstLine = new short[w * dstNumComp];
228: Object pixel;
229: float[] color;
230: float[] alpha = null;
231: if (needSrcAlpha) {
232: alpha = new float[w];
233: }
234: int idx;
235: srcIL = new LCMSImageLayout(srcLine, srcLine.length
236: / getNumInComponents(), LCMSImageLayout
237: .CHANNELS_SH(getNumInComponents())
238: | LCMSImageLayout.BYTES_SH(2),
239: getNumInComponents() * 2);
240:
241: dstIL = new LCMSImageLayout(dstLine, dstLine.length
242: / getNumOutComponents(), LCMSImageLayout
243: .CHANNELS_SH(getNumOutComponents())
244: | LCMSImageLayout.BYTES_SH(2),
245: getNumOutComponents() * 2);
246:
247: // process each scanline
248: for (int y = 0; y < h; y++) {
249: // convert src scanline
250: pixel = null;
251: color = null;
252: idx = 0;
253: for (int x = 0; x < w; x++) {
254: pixel = srcRas.getDataElements(x, y, pixel);
255: color = srcCM.getNormalizedComponents(pixel, color,
256: 0);
257: for (int i = 0; i < srcNumComp; i++) {
258: srcLine[idx++] = (short) ((color[i] - srcMinVal[i])
259: * srcInvDiffMinMax[i] + 0.5f);
260: }
261: if (needSrcAlpha) {
262: alpha[x] = color[srcNumComp];
263: }
264: }
265: // color convert srcLine to dstLine
266: synchronized (this ) {
267: LCMS.colorConvert(this , srcIL, dstIL);
268: }
269: // convert dst scanline
270: pixel = null;
271: idx = 0;
272: for (int x = 0; x < w; x++) {
273: for (int i = 0; i < dstNumComp; i++) {
274: dstColor[i] = ((float) (dstLine[idx++] & 0xffff))
275: * dstDiffMinMax[i] + dstMinVal[i];
276: }
277: if (needSrcAlpha) {
278: dstColor[dstNumComp] = alpha[x];
279: } else if (dstHasAlpha) {
280: dstColor[dstNumComp] = 1.0f;
281: }
282: pixel = dstCM.getDataElements(dstColor, 0, pixel);
283: dstRas.setDataElements(x, y, pixel);
284: }
285: }
286: }
287: }
288:
289: public void colorConvert(Raster src, WritableRaster dst,
290: float[] srcMinVal, float[] srcMaxVal, float[] dstMinVal,
291: float[] dstMaxVal) {
292: LCMSImageLayout srcIL, dstIL;
293:
294: // Can't pass src and dst directly to CMM, so process per scanline
295: SampleModel srcSM = src.getSampleModel();
296: SampleModel dstSM = dst.getSampleModel();
297: int srcTransferType = src.getTransferType();
298: int dstTransferType = dst.getTransferType();
299: boolean srcIsFloat, dstIsFloat;
300: if ((srcTransferType == DataBuffer.TYPE_FLOAT)
301: || (srcTransferType == DataBuffer.TYPE_DOUBLE)) {
302: srcIsFloat = true;
303: } else {
304: srcIsFloat = false;
305: }
306: if ((dstTransferType == DataBuffer.TYPE_FLOAT)
307: || (dstTransferType == DataBuffer.TYPE_DOUBLE)) {
308: dstIsFloat = true;
309: } else {
310: dstIsFloat = false;
311: }
312: int w = src.getWidth();
313: int h = src.getHeight();
314: int srcNumBands = src.getNumBands();
315: int dstNumBands = dst.getNumBands();
316: float[] srcScaleFactor = new float[srcNumBands];
317: float[] dstScaleFactor = new float[dstNumBands];
318: float[] srcUseMinVal = new float[srcNumBands];
319: float[] dstUseMinVal = new float[dstNumBands];
320: for (int i = 0; i < srcNumBands; i++) {
321: if (srcIsFloat) {
322: srcScaleFactor[i] = 65535.0f / (srcMaxVal[i] - srcMinVal[i]);
323: srcUseMinVal[i] = srcMinVal[i];
324: } else {
325: if (srcTransferType == DataBuffer.TYPE_SHORT) {
326: srcScaleFactor[i] = 65535.0f / 32767.0f;
327: } else {
328: srcScaleFactor[i] = 65535.0f / ((float) ((1 << srcSM
329: .getSampleSize(i)) - 1));
330: }
331: srcUseMinVal[i] = 0.0f;
332: }
333: }
334: for (int i = 0; i < dstNumBands; i++) {
335: if (dstIsFloat) {
336: dstScaleFactor[i] = (dstMaxVal[i] - dstMinVal[i]) / 65535.0f;
337: dstUseMinVal[i] = dstMinVal[i];
338: } else {
339: if (dstTransferType == DataBuffer.TYPE_SHORT) {
340: dstScaleFactor[i] = 32767.0f / 65535.0f;
341: } else {
342: dstScaleFactor[i] = ((float) ((1 << dstSM
343: .getSampleSize(i)) - 1)) / 65535.0f;
344: }
345: dstUseMinVal[i] = 0.0f;
346: }
347: }
348: int ys = src.getMinY();
349: int yd = dst.getMinY();
350: int xs, xd;
351: float sample;
352: short[] srcLine = new short[w * srcNumBands];
353: short[] dstLine = new short[w * dstNumBands];
354: int idx;
355: srcIL = new LCMSImageLayout(srcLine, srcLine.length
356: / getNumInComponents(), LCMSImageLayout
357: .CHANNELS_SH(getNumInComponents())
358: | LCMSImageLayout.BYTES_SH(2), getNumInComponents() * 2);
359:
360: dstIL = new LCMSImageLayout(dstLine, dstLine.length
361: / getNumOutComponents(), LCMSImageLayout
362: .CHANNELS_SH(getNumOutComponents())
363: | LCMSImageLayout.BYTES_SH(2),
364: getNumOutComponents() * 2);
365:
366: // process each scanline
367: for (int y = 0; y < h; y++, ys++, yd++) {
368: // get src scanline
369: xs = src.getMinX();
370: idx = 0;
371: for (int x = 0; x < w; x++, xs++) {
372: for (int i = 0; i < srcNumBands; i++) {
373: sample = src.getSampleFloat(xs, ys, i);
374: srcLine[idx++] = (short) ((sample - srcUseMinVal[i])
375: * srcScaleFactor[i] + 0.5f);
376: }
377: }
378:
379: // color convert srcLine to dstLine
380: synchronized (this ) {
381: LCMS.colorConvert(this , srcIL, dstIL);
382: }
383:
384: // store dst scanline
385: xd = dst.getMinX();
386: idx = 0;
387: for (int x = 0; x < w; x++, xd++) {
388: for (int i = 0; i < dstNumBands; i++) {
389: sample = ((dstLine[idx++] & 0xffff) * dstScaleFactor[i])
390: + dstUseMinVal[i];
391: dst.setSample(xd, yd, i, sample);
392: }
393: }
394: }
395: }
396:
397: public void colorConvert(Raster src, WritableRaster dst) {
398:
399: LCMSImageLayout srcIL, dstIL;
400: // Can't pass src and dst directly to CMM, so process per scanline
401: SampleModel srcSM = src.getSampleModel();
402: SampleModel dstSM = dst.getSampleModel();
403: int srcTransferType = src.getTransferType();
404: int dstTransferType = dst.getTransferType();
405: int w = src.getWidth();
406: int h = src.getHeight();
407: int srcNumBands = src.getNumBands();
408: int dstNumBands = dst.getNumBands();
409: int precision = 8;
410: float maxNum = 255.0f;
411: for (int i = 0; i < srcNumBands; i++) {
412: if (srcSM.getSampleSize(i) > 8) {
413: precision = 16;
414: maxNum = 65535.0f;
415: }
416: }
417: for (int i = 0; i < dstNumBands; i++) {
418: if (dstSM.getSampleSize(i) > 8) {
419: precision = 16;
420: maxNum = 65535.0f;
421: }
422: }
423: float[] srcScaleFactor = new float[srcNumBands];
424: float[] dstScaleFactor = new float[dstNumBands];
425: for (int i = 0; i < srcNumBands; i++) {
426: if (srcTransferType == DataBuffer.TYPE_SHORT) {
427: srcScaleFactor[i] = maxNum / 32767.0f;
428: } else {
429: srcScaleFactor[i] = maxNum
430: / ((float) ((1 << srcSM.getSampleSize(i)) - 1));
431: }
432: }
433: for (int i = 0; i < dstNumBands; i++) {
434: if (dstTransferType == DataBuffer.TYPE_SHORT) {
435: dstScaleFactor[i] = 32767.0f / maxNum;
436: } else {
437: dstScaleFactor[i] = ((float) ((1 << dstSM
438: .getSampleSize(i)) - 1))
439: / maxNum;
440: }
441: }
442: int ys = src.getMinY();
443: int yd = dst.getMinY();
444: int xs, xd;
445: int sample;
446: if (precision == 8) {
447: byte[] srcLine = new byte[w * srcNumBands];
448: byte[] dstLine = new byte[w * dstNumBands];
449: int idx;
450: // TODO check for src npixels = dst npixels
451: srcIL = new LCMSImageLayout(srcLine, srcLine.length
452: / getNumInComponents(), LCMSImageLayout
453: .CHANNELS_SH(getNumInComponents())
454: | LCMSImageLayout.BYTES_SH(1), getNumInComponents());
455: dstIL = new LCMSImageLayout(dstLine, dstLine.length
456: / getNumOutComponents(), LCMSImageLayout
457: .CHANNELS_SH(getNumOutComponents())
458: | LCMSImageLayout.BYTES_SH(1),
459: getNumOutComponents());
460:
461: // process each scanline
462: for (int y = 0; y < h; y++, ys++, yd++) {
463: // get src scanline
464: xs = src.getMinX();
465: idx = 0;
466: for (int x = 0; x < w; x++, xs++) {
467: for (int i = 0; i < srcNumBands; i++) {
468: sample = src.getSample(xs, ys, i);
469: srcLine[idx++] = (byte) ((sample * srcScaleFactor[i]) + 0.5f);
470: }
471: }
472:
473: // color convert srcLine to dstLine
474: synchronized (this ) {
475: LCMS.colorConvert(this , srcIL, dstIL);
476: }
477:
478: // store dst scanline
479: xd = dst.getMinX();
480: idx = 0;
481: for (int x = 0; x < w; x++, xd++) {
482: for (int i = 0; i < dstNumBands; i++) {
483: sample = (int) (((dstLine[idx++] & 0xff) * dstScaleFactor[i]) + 0.5f);
484: dst.setSample(xd, yd, i, sample);
485: }
486: }
487: }
488: } else {
489: short[] srcLine = new short[w * srcNumBands];
490: short[] dstLine = new short[w * dstNumBands];
491: int idx;
492: srcIL = new LCMSImageLayout(srcLine, srcLine.length
493: / getNumInComponents(), LCMSImageLayout
494: .CHANNELS_SH(getNumInComponents())
495: | LCMSImageLayout.BYTES_SH(2),
496: getNumInComponents() * 2);
497:
498: dstIL = new LCMSImageLayout(dstLine, dstLine.length
499: / getNumOutComponents(), LCMSImageLayout
500: .CHANNELS_SH(getNumOutComponents())
501: | LCMSImageLayout.BYTES_SH(2),
502: getNumOutComponents() * 2);
503:
504: // process each scanline
505: for (int y = 0; y < h; y++, ys++, yd++) {
506: // get src scanline
507: xs = src.getMinX();
508: idx = 0;
509: for (int x = 0; x < w; x++, xs++) {
510: for (int i = 0; i < srcNumBands; i++) {
511: sample = src.getSample(xs, ys, i);
512: srcLine[idx++] = (short) ((sample * srcScaleFactor[i]) + 0.5f);
513: }
514: }
515:
516: // color convert srcLine to dstLine
517: synchronized (this ) {
518: LCMS.colorConvert(this , srcIL, dstIL);
519: }
520: // store dst scanline
521: xd = dst.getMinX();
522: idx = 0;
523: for (int x = 0; x < w; x++, xd++) {
524: for (int i = 0; i < dstNumBands; i++) {
525: sample = (int) (((dstLine[idx++] & 0xffff) * dstScaleFactor[i]) + 0.5f);
526: dst.setSample(xd, yd, i, sample);
527: }
528: }
529: }
530: }
531: }
532:
533: /* convert an array of colors in short format */
534: /* each color is a contiguous set of array elements */
535: /* the number of colors is (size of the array) / (number of input/output
536: components */
537: public short[] colorConvert(short[] src, short[] dst) {
538:
539: if (dst == null) {
540: dst = new short[(src.length / getNumInComponents())
541: * getNumOutComponents()];
542: }
543:
544: LCMSImageLayout srcIL = new LCMSImageLayout(src, src.length
545: / getNumInComponents(), LCMSImageLayout
546: .CHANNELS_SH(getNumInComponents())
547: | LCMSImageLayout.BYTES_SH(2), getNumInComponents() * 2);
548:
549: LCMSImageLayout dstIL = new LCMSImageLayout(dst, dst.length
550: / getNumOutComponents(), LCMSImageLayout
551: .CHANNELS_SH(getNumOutComponents())
552: | LCMSImageLayout.BYTES_SH(2),
553: getNumOutComponents() * 2);
554:
555: synchronized (this ) {
556: LCMS.colorConvert(this , srcIL, dstIL);
557: }
558:
559: return dst;
560: }
561:
562: public byte[] colorConvert(byte[] src, byte[] dst) {
563: if (dst == null) {
564: dst = new byte[(src.length / getNumInComponents())
565: * getNumOutComponents()];
566: }
567:
568: LCMSImageLayout srcIL = new LCMSImageLayout(src, src.length
569: / getNumInComponents(), LCMSImageLayout
570: .CHANNELS_SH(getNumInComponents())
571: | LCMSImageLayout.BYTES_SH(1), getNumInComponents());
572:
573: LCMSImageLayout dstIL = new LCMSImageLayout(dst, dst.length
574: / getNumOutComponents(), LCMSImageLayout
575: .CHANNELS_SH(getNumOutComponents())
576: | LCMSImageLayout.BYTES_SH(1), getNumOutComponents());
577:
578: synchronized (this) {
579: LCMS.colorConvert(this, srcIL, dstIL);
580: }
581:
582: return dst;
583: }
584: }
|