001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017: /**
018: * @author Oleg V. Khaschansky
019: * @version $Revision$
020: *
021: * @date: Oct 6, 2005
022: */package java.awt.image;
023:
024: import java.awt.geom.Point2D;
025: import java.awt.geom.Rectangle2D;
026: import java.awt.*;
027: import java.util.Arrays;
028:
029: import org.apache.harmony.awt.gl.AwtImageBackdoorAccessor;
030: import org.apache.harmony.awt.internal.nls.Messages;
031:
032: public class RescaleOp implements BufferedImageOp, RasterOp {
033: private float scaleFactors[];
034: private float offsets[];
035: private RenderingHints hints;
036:
037: static {
038: // TODO
039: //System.loadLibrary("imageops");
040: }
041:
042: public RescaleOp(float[] scaleFactors, float[] offsets,
043: RenderingHints hints) {
044: int numFactors = Math.min(scaleFactors.length, offsets.length);
045:
046: this .scaleFactors = new float[numFactors];
047: this .offsets = new float[numFactors];
048:
049: System.arraycopy(scaleFactors, 0, this .scaleFactors, 0,
050: numFactors);
051: System.arraycopy(offsets, 0, this .offsets, 0, numFactors);
052:
053: this .hints = hints;
054: }
055:
056: public RescaleOp(float scaleFactor, float offset,
057: RenderingHints hints) {
058: scaleFactors = new float[1];
059: offsets = new float[1];
060:
061: scaleFactors[0] = scaleFactor;
062: offsets[0] = offset;
063:
064: this .hints = hints;
065: }
066:
067: public final int getNumFactors() {
068: return scaleFactors.length;
069: }
070:
071: public final RenderingHints getRenderingHints() {
072: return hints;
073: }
074:
075: public final float[] getScaleFactors(float[] scaleFactors) {
076: if (scaleFactors == null) {
077: scaleFactors = new float[this .scaleFactors.length];
078: }
079:
080: int minLength = Math.min(scaleFactors.length,
081: this .scaleFactors.length);
082: System.arraycopy(this .scaleFactors, 0, scaleFactors, 0,
083: minLength);
084: return scaleFactors;
085: }
086:
087: public final float[] getOffsets(float[] offsets) {
088: if (offsets == null) {
089: offsets = new float[this .offsets.length];
090: }
091:
092: int minLength = Math.min(offsets.length, this .offsets.length);
093: System.arraycopy(this .offsets, 0, offsets, 0, minLength);
094: return offsets;
095: }
096:
097: public final Point2D getPoint2D(Point2D srcPt, Point2D dstPt) {
098: if (dstPt == null) {
099: dstPt = new Point2D.Float();
100: }
101:
102: dstPt.setLocation(srcPt);
103: return dstPt;
104: }
105:
106: public final Rectangle2D getBounds2D(Raster src) {
107: return src.getBounds();
108: }
109:
110: public final Rectangle2D getBounds2D(BufferedImage src) {
111: return getBounds2D(src.getRaster());
112: }
113:
114: public WritableRaster createCompatibleDestRaster(Raster src) {
115: return src.createCompatibleWritableRaster();
116: }
117:
118: public BufferedImage createCompatibleDestImage(BufferedImage src,
119: ColorModel dstCM) {
120: if (dstCM == null) {
121: dstCM = src.getColorModel();
122: }
123:
124: if (dstCM instanceof IndexColorModel) {
125: dstCM = ColorModel.getRGBdefault();
126: }
127:
128: WritableRaster r = dstCM.isCompatibleSampleModel(src
129: .getSampleModel()) ? src.getRaster()
130: .createCompatibleWritableRaster(src.getWidth(),
131: src.getHeight()) : dstCM
132: .createCompatibleWritableRaster(src.getWidth(), src
133: .getHeight());
134:
135: return new BufferedImage(dstCM, r,
136: dstCM.isAlphaPremultiplied(), null);
137: }
138:
139: public final WritableRaster filter(Raster src, WritableRaster dst) {
140: if (dst == null) {
141: dst = createCompatibleDestRaster(src);
142: } else {
143: if (src.getNumBands() != dst.getNumBands()) {
144: // awt.21D=Number of src bands ({0}) does not match number of dst bands ({1})
145: throw new IllegalArgumentException(Messages.getString(
146: "awt.21D", //$NON-NLS-1$
147: src.getNumBands(), dst.getNumBands()));
148: }
149: }
150:
151: if (this .scaleFactors.length != 1
152: && this .scaleFactors.length != src.getNumBands()) {
153: // awt.21E=Number of scaling constants is not equal to the number of bands
154: throw new IllegalArgumentException(Messages
155: .getString("awt.21E")); //$NON-NLS-1$
156: }
157:
158: // TODO
159: //if (ippFilter(src, dst, BufferedImage.TYPE_CUSTOM, false) != 0)
160: if (slowFilter(src, dst, false) != 0) {
161: // awt.21F=Unable to transform source
162: throw new ImagingOpException(Messages.getString("awt.21F")); //$NON-NLS-1$
163: }
164:
165: return dst;
166: }
167:
168: private final int slowFilter(Raster src, WritableRaster dst,
169: boolean skipAlpha) {
170: SampleModel sm = src.getSampleModel();
171:
172: int numBands = src.getNumBands();
173: int srcHeight = src.getHeight();
174: int srcWidth = src.getWidth();
175:
176: int srcMinX = src.getMinX();
177: int srcMinY = src.getMinY();
178: int dstMinX = dst.getMinX();
179: int dstMinY = dst.getMinY();
180:
181: int[] maxValues = new int[numBands];
182: int[] masks = new int[numBands];
183: int[] sampleSizes = sm.getSampleSize();
184:
185: for (int i = 0; i < numBands; i++) {
186: maxValues[i] = (1 << sampleSizes[i]) - 1;
187: masks[i] = ~(maxValues[i]);
188: }
189:
190: // Processing bounds
191: float[] pixels = null;
192: pixels = src.getPixels(srcMinX, srcMinY, srcWidth, srcHeight,
193: pixels);
194:
195: // Cycle over pixels to be calculated
196: if (skipAlpha) { // Always suppose that alpha channel is the last band
197: if (scaleFactors.length > 1) {
198: for (int i = 0; i < pixels.length;) {
199: for (int bandIdx = 0; bandIdx < numBands - 1; bandIdx++, i++) {
200: pixels[i] = pixels[i] * scaleFactors[bandIdx]
201: + offsets[bandIdx];
202: // Check for overflow now
203: if (((int) pixels[i] & masks[bandIdx]) != 0) {
204: if (pixels[i] < 0) {
205: pixels[i] = 0;
206: } else {
207: pixels[i] = maxValues[bandIdx];
208: }
209: }
210: }
211:
212: i++;
213: }
214: } else {
215: for (int i = 0; i < pixels.length;) {
216: for (int bandIdx = 0; bandIdx < numBands - 1; bandIdx++, i++) {
217: pixels[i] = pixels[i] * scaleFactors[0]
218: + offsets[0];
219: // Check for overflow now
220: if (((int) pixels[i] & masks[bandIdx]) != 0) {
221: if (pixels[i] < 0) {
222: pixels[i] = 0;
223: } else {
224: pixels[i] = maxValues[bandIdx];
225: }
226: }
227: }
228:
229: i++;
230: }
231: }
232: } else {
233: if (scaleFactors.length > 1) {
234: for (int i = 0; i < pixels.length;) {
235: for (int bandIdx = 0; bandIdx < numBands; bandIdx++, i++) {
236: pixels[i] = pixels[i] * scaleFactors[bandIdx]
237: + offsets[bandIdx];
238: // Check for overflow now
239: if (((int) pixels[i] & masks[bandIdx]) != 0) {
240: if (pixels[i] < 0) {
241: pixels[i] = 0;
242: } else {
243: pixels[i] = maxValues[bandIdx];
244: }
245: }
246: }
247: }
248: } else {
249: for (int i = 0; i < pixels.length;) {
250: for (int bandIdx = 0; bandIdx < numBands; bandIdx++, i++) {
251: pixels[i] = pixels[i] * scaleFactors[0]
252: + offsets[0];
253: // Check for overflow now
254: if (((int) pixels[i] & masks[bandIdx]) != 0) {
255: if (pixels[i] < 0) {
256: pixels[i] = 0;
257: } else {
258: pixels[i] = maxValues[bandIdx];
259: }
260: }
261: }
262: }
263: }
264: }
265:
266: dst.setPixels(dstMinX, dstMinY, srcWidth, srcHeight, pixels);
267:
268: return 0;
269: }
270:
271: public final BufferedImage filter(BufferedImage src,
272: BufferedImage dst) {
273: ColorModel srcCM = src.getColorModel();
274:
275: if (srcCM instanceof IndexColorModel) {
276: // awt.220=Source should not have IndexColorModel
277: throw new IllegalArgumentException(Messages
278: .getString("awt.220")); //$NON-NLS-1$
279: }
280:
281: // Check if the number of scaling factors matches the number of bands
282: int nComponents = srcCM.getNumComponents();
283: boolean skipAlpha;
284: if (srcCM.hasAlpha()) {
285: if (scaleFactors.length == 1
286: || scaleFactors.length == nComponents - 1) {
287: skipAlpha = true;
288: } else if (scaleFactors.length == nComponents) {
289: skipAlpha = false;
290: } else {
291: // awt.21E=Number of scaling constants is not equal to the number of bands
292: throw new IllegalArgumentException(Messages
293: .getString("awt.21E")); //$NON-NLS-1$
294: }
295: } else if (scaleFactors.length == 1
296: || scaleFactors.length == nComponents) {
297: skipAlpha = false;
298: } else {
299: // awt.21E=Number of scaling constants is not equal to the number of bands
300: throw new IllegalArgumentException(Messages
301: .getString("awt.21E")); //$NON-NLS-1$
302: }
303:
304: BufferedImage finalDst = null;
305: if (dst == null) {
306: finalDst = dst;
307: dst = createCompatibleDestImage(src, srcCM);
308: } else if (!srcCM.equals(dst.getColorModel())) {
309: // Treat BufferedImage.TYPE_INT_RGB and BufferedImage.TYPE_INT_ARGB as same
310: if (!((src.getType() == BufferedImage.TYPE_INT_RGB || src
311: .getType() == BufferedImage.TYPE_INT_ARGB) && (dst
312: .getType() == BufferedImage.TYPE_INT_RGB || dst
313: .getType() == BufferedImage.TYPE_INT_ARGB))) {
314: finalDst = dst;
315: dst = createCompatibleDestImage(src, srcCM);
316: }
317: }
318:
319: // TODO
320: //if (ippFilter(src.getRaster(), dst.getRaster(), src.getType(), skipAlpha) != 0)
321: if (slowFilter(src.getRaster(), dst.getRaster(), skipAlpha) != 0) {
322: // awt.21F=Unable to transform source
323: throw new ImagingOpException(Messages.getString("awt.21F")); //$NON-NLS-1$
324: }
325:
326: if (finalDst != null) {
327: Graphics2D g = finalDst.createGraphics();
328: g.setComposite(AlphaComposite.Src);
329: g.drawImage(dst, 0, 0, null);
330: } else {
331: finalDst = dst;
332: }
333:
334: return finalDst;
335: }
336:
337: // Don't forget to pass allocated arrays for levels and values, size should be numBands*4
338: private final void createLevels(SampleModel sm, int numBands,
339: boolean skipAlpha, int levels[], int values[],
340: int channelsOrder[]) {
341: // Suppose same sample size for all channels, otherwise use slow filter
342: int maxValue = (1 << sm.getSampleSize(0)) - 1;
343:
344: // For simplicity introduce these arrays
345: float extScaleFactors[] = new float[numBands];
346: float extOffsets[] = new float[numBands];
347:
348: if (scaleFactors.length != 1) {
349: System.arraycopy(scaleFactors, 0, extScaleFactors, 0,
350: scaleFactors.length);
351: System.arraycopy(offsets, 0, extOffsets, 0,
352: scaleFactors.length);
353: } else {
354: for (int i = 0; i < numBands; i++) {
355: extScaleFactors[i] = scaleFactors[0];
356: extOffsets[i] = offsets[0];
357: }
358: }
359:
360: if (skipAlpha) {
361: extScaleFactors[numBands - 1] = 1;
362: extOffsets[numBands - 1] = 0;
363: }
364:
365: // Create a levels
366: for (int i = 0; i < numBands; i++) {
367: if (extScaleFactors[i] == 0) {
368: levels[i * 4] = 0;
369: levels[i * 4 + 1] = 0;
370: levels[i * 4 + 2] = maxValue + 1;
371: levels[i * 4 + 3] = maxValue + 1;
372: }
373:
374: float minLevel = -extOffsets[i] / extScaleFactors[i];
375: float maxLevel = (maxValue - extOffsets[i])
376: / extScaleFactors[i];
377:
378: if (minLevel < 0) {
379: minLevel = 0;
380: } else if (minLevel > maxValue) {
381: minLevel = maxValue;
382: }
383:
384: if (maxLevel < 0) {
385: maxLevel = 0;
386: } else if (maxLevel > maxValue) {
387: maxLevel = maxValue;
388: }
389:
390: levels[i * 4] = 0;
391: if (minLevel > maxLevel) {
392: levels[i * 4 + 1] = (int) maxLevel;
393: levels[i * 4 + 2] = (int) minLevel;
394: } else {
395: levels[i * 4 + 1] = (int) minLevel;
396: levels[i * 4 + 2] = (int) maxLevel;
397: }
398: levels[i * 4 + 3] = maxValue + 1;
399:
400: // Fill values
401: for (int k = 0; k < 4; k++) {
402: int idx = i * 4 + k;
403: values[idx] = (int) (extScaleFactors[i] * levels[idx] + extOffsets[i]);
404: if (values[idx] < 0) {
405: values[idx] = 0;
406: } else if (values[idx] > maxValue) {
407: values[idx] = maxValue;
408: }
409: }
410: }
411:
412: // Reorder data if channels are stored in different order
413: if (channelsOrder != null) {
414: int len = numBands * 4;
415: int savedLevels[] = new int[len];
416: int savedValues[] = new int[len];
417: System.arraycopy(levels, 0, savedLevels, 0, len);
418: System.arraycopy(values, 0, savedValues, 0, len);
419: for (int i = 0; i < channelsOrder.length; i++) {
420: System.arraycopy(savedLevels, i * 4, levels,
421: channelsOrder[i] * 4, 4);
422: System.arraycopy(savedValues, i * 4, values,
423: channelsOrder[i] * 4, 4);
424: }
425: }
426: }
427:
428: // TODO remove when this method is used
429: @SuppressWarnings("unused")
430: private final int ippFilter(Raster src, WritableRaster dst,
431: int imageType, boolean skipAlpha) {
432: int res;
433:
434: int srcStride, dstStride;
435: int channels;
436: int offsets[] = null;
437: int channelsOrder[] = null;
438:
439: switch (imageType) {
440: case BufferedImage.TYPE_INT_ARGB:
441: case BufferedImage.TYPE_INT_ARGB_PRE:
442: case BufferedImage.TYPE_INT_RGB: {
443: channels = 4;
444: srcStride = src.getWidth() * 4;
445: dstStride = dst.getWidth() * 4;
446: channelsOrder = new int[] { 2, 1, 0, 3 };
447: break;
448: }
449:
450: case BufferedImage.TYPE_4BYTE_ABGR:
451: case BufferedImage.TYPE_4BYTE_ABGR_PRE:
452: case BufferedImage.TYPE_INT_BGR: {
453: channels = 4;
454: srcStride = src.getWidth() * 4;
455: dstStride = dst.getWidth() * 4;
456: break;
457: }
458:
459: case BufferedImage.TYPE_BYTE_GRAY: {
460: channels = 1;
461: srcStride = src.getWidth();
462: dstStride = dst.getWidth();
463: break;
464: }
465:
466: case BufferedImage.TYPE_3BYTE_BGR: {
467: channels = 3;
468: srcStride = src.getWidth() * 3;
469: dstStride = dst.getWidth() * 3;
470: channelsOrder = new int[] { 2, 1, 0 };
471: break;
472: }
473:
474: case BufferedImage.TYPE_USHORT_GRAY:
475: case BufferedImage.TYPE_USHORT_565_RGB:
476: case BufferedImage.TYPE_USHORT_555_RGB:
477: case BufferedImage.TYPE_BYTE_BINARY: {
478: return slowFilter(src, dst, skipAlpha);
479: }
480:
481: default: {
482: SampleModel srcSM = src.getSampleModel();
483: SampleModel dstSM = dst.getSampleModel();
484:
485: if (srcSM instanceof PixelInterleavedSampleModel
486: && dstSM instanceof PixelInterleavedSampleModel) {
487: // Check PixelInterleavedSampleModel
488: if (srcSM.getDataType() != DataBuffer.TYPE_BYTE
489: || dstSM.getDataType() != DataBuffer.TYPE_BYTE) {
490: return slowFilter(src, dst, skipAlpha);
491: }
492:
493: channels = srcSM.getNumBands(); // Have IPP functions for 1, 3 and 4 channels
494: if (!(channels == 1 || channels == 3 || channels == 4)) {
495: return slowFilter(src, dst, skipAlpha);
496: }
497:
498: srcStride = ((ComponentSampleModel) srcSM)
499: .getScanlineStride();
500: dstStride = ((ComponentSampleModel) dstSM)
501: .getScanlineStride();
502:
503: channelsOrder = ((ComponentSampleModel) srcSM)
504: .getBandOffsets();
505: } else if (srcSM instanceof SinglePixelPackedSampleModel
506: && dstSM instanceof SinglePixelPackedSampleModel) {
507: // Check SinglePixelPackedSampleModel
508: SinglePixelPackedSampleModel sppsm1 = (SinglePixelPackedSampleModel) srcSM;
509: SinglePixelPackedSampleModel sppsm2 = (SinglePixelPackedSampleModel) dstSM;
510:
511: channels = sppsm1.getNumBands();
512:
513: // TYPE_INT_RGB, TYPE_INT_ARGB...
514: if (sppsm1.getDataType() != DataBuffer.TYPE_INT
515: || sppsm2.getDataType() != DataBuffer.TYPE_INT
516: || !(channels == 3 || channels == 4)) {
517: return slowFilter(src, dst, skipAlpha);
518: }
519:
520: // Check compatibility of sample models
521: if (!Arrays.equals(sppsm1.getBitOffsets(), sppsm2
522: .getBitOffsets())
523: || !Arrays.equals(sppsm1.getBitMasks(), sppsm2
524: .getBitMasks())) {
525: return slowFilter(src, dst, skipAlpha);
526: }
527:
528: for (int i = 0; i < channels; i++) {
529: if (sppsm1.getSampleSize(i) != 8) {
530: return slowFilter(src, dst, skipAlpha);
531: }
532: }
533:
534: channelsOrder = new int[channels];
535: int bitOffsets[] = sppsm1.getBitOffsets();
536: for (int i = 0; i < channels; i++) {
537: channelsOrder[i] = bitOffsets[i] / 8;
538: }
539:
540: if (channels == 3) { // Don't skip channel now, could be optimized
541: channels = 4;
542: }
543:
544: srcStride = sppsm1.getScanlineStride() * 4;
545: dstStride = sppsm2.getScanlineStride() * 4;
546: } else {
547: return slowFilter(src, dst, skipAlpha);
548: }
549:
550: // Fill offsets if there's a child raster
551: if (src.getParent() != null || dst.getParent() != null) {
552: if (src.getSampleModelTranslateX() != 0
553: || src.getSampleModelTranslateY() != 0
554: || dst.getSampleModelTranslateX() != 0
555: || dst.getSampleModelTranslateY() != 0) {
556: offsets = new int[4];
557: offsets[0] = -src.getSampleModelTranslateX()
558: + src.getMinX();
559: offsets[1] = -src.getSampleModelTranslateY()
560: + src.getMinY();
561: offsets[2] = -dst.getSampleModelTranslateX()
562: + dst.getMinX();
563: offsets[3] = -dst.getSampleModelTranslateY()
564: + dst.getMinY();
565: }
566: }
567: }
568: }
569:
570: int levels[] = new int[4 * channels];
571: int values[] = new int[4 * channels];
572:
573: createLevels(src.getSampleModel(), channels, skipAlpha, levels,
574: values, channelsOrder);
575:
576: Object srcData, dstData;
577: AwtImageBackdoorAccessor dbAccess = AwtImageBackdoorAccessor
578: .getInstance();
579: try {
580: srcData = dbAccess.getData(src.getDataBuffer());
581: dstData = dbAccess.getData(dst.getDataBuffer());
582: } catch (IllegalArgumentException e) {
583: return -1; // Unknown data buffer type
584: }
585:
586: res = LookupOp.ippLUT(srcData, src.getWidth(), src.getHeight(),
587: srcStride, dstData, dst.getWidth(), dst.getHeight(),
588: dstStride, levels, values, channels, offsets, true);
589:
590: return res;
591: }
592: }
|