OpenCores
URL https://opencores.org/ocsvn/openrisc/openrisc/trunk

Subversion Repositories openrisc

[/] [openrisc/] [trunk/] [gnu-dev/] [or1k-gcc/] [libjava/] [classpath/] [java/] [awt/] [image/] [AffineTransformOp.java] - Blame information for rev 771

Details | Compare with Previous | View Log

Line No. Rev Author Line
1 771 jeremybenn
/* AffineTransformOp.java --  This class performs affine
2
   transformation between two images or rasters in 2 dimensions.
3
   Copyright (C) 2004, 2006 Free Software Foundation
4
 
5
This file is part of GNU Classpath.
6
 
7
GNU Classpath is free software; you can redistribute it and/or modify
8
it under the terms of the GNU General Public License as published by
9
the Free Software Foundation; either version 2, or (at your option)
10
any later version.
11
 
12
GNU Classpath is distributed in the hope that it will be useful, but
13
WITHOUT ANY WARRANTY; without even the implied warranty of
14
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15
General Public License for more details.
16
 
17
You should have received a copy of the GNU General Public License
18
along with GNU Classpath; see the file COPYING.  If not, write to the
19
Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
20
02110-1301 USA.
21
 
22
Linking this library statically or dynamically with other modules is
23
making a combined work based on this library.  Thus, the terms and
24
conditions of the GNU General Public License cover the whole
25
combination.
26
 
27
As a special exception, the copyright holders of this library give you
28
permission to link this library with independent modules to produce an
29
executable, regardless of the license terms of these independent
30
modules, and to copy and distribute the resulting executable under
31
terms of your choice, provided that you also meet, for each linked
32
independent module, the terms and conditions of the license of that
33
module.  An independent module is a module which is not derived from
34
or based on this library.  If you modify this library, you may extend
35
this exception to your version of the library, but you are not
36
obligated to do so.  If you do not wish to do so, delete this
37
exception statement from your version. */
38
 
39
package java.awt.image;
40
 
41
import java.awt.Graphics2D;
42
import java.awt.Point;
43
import java.awt.Rectangle;
44
import java.awt.RenderingHints;
45
import java.awt.geom.AffineTransform;
46
import java.awt.geom.NoninvertibleTransformException;
47
import java.awt.geom.Point2D;
48
import java.awt.geom.Rectangle2D;
49
import java.util.Arrays;
50
 
51
/**
52
 * AffineTransformOp performs matrix-based transformations (translations,
53
 * scales, flips, rotations, and shears).
54
 *
55
 * If interpolation is required, nearest neighbour, bilinear, and bicubic
56
 * methods are available.
57
 *
58
 * @author Olga Rodimina (rodimina@redhat.com)
59
 * @author Francis Kung (fkung@redhat.com)
60
 */
61
public class AffineTransformOp implements BufferedImageOp, RasterOp
62
{
63
    public static final int TYPE_NEAREST_NEIGHBOR = 1;
64
 
65
    public static final int TYPE_BILINEAR = 2;
66
 
67
    /**
68
     * @since 1.5.0
69
     */
70
    public static final int TYPE_BICUBIC = 3;
71
 
72
    private AffineTransform transform;
73
    private RenderingHints hints;
74
 
75
    /**
76
     * Construct AffineTransformOp with the given xform and interpolationType.
77
     * Interpolation type can be TYPE_BILINEAR, TYPE_BICUBIC or
78
     * TYPE_NEAREST_NEIGHBOR.
79
     *
80
     * @param xform AffineTransform that will applied to the source image
81
     * @param interpolationType type of interpolation used
82
     * @throws ImagingOpException if the transform matrix is noninvertible
83
     */
84
    public AffineTransformOp (AffineTransform xform, int interpolationType)
85
    {
86
      this.transform = xform;
87
      if (xform.getDeterminant() == 0)
88
        throw new ImagingOpException(null);
89
 
90
      switch (interpolationType)
91
      {
92
      case TYPE_BILINEAR:
93
        hints = new RenderingHints (RenderingHints.KEY_INTERPOLATION,
94
                                    RenderingHints.VALUE_INTERPOLATION_BILINEAR);
95
        break;
96
      case TYPE_BICUBIC:
97
        hints = new RenderingHints (RenderingHints.KEY_INTERPOLATION,
98
                                    RenderingHints.VALUE_INTERPOLATION_BICUBIC);
99
        break;
100
      default:
101
        hints = new RenderingHints (RenderingHints.KEY_INTERPOLATION,
102
                                    RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR);
103
      }
104
    }
105
 
106
    /**
107
     * Construct AffineTransformOp with the given xform and rendering hints.
108
     *
109
     * @param xform AffineTransform that will applied to the source image
110
     * @param hints rendering hints that will be used during transformation
111
     * @throws ImagingOpException if the transform matrix is noninvertible
112
     */
113
    public AffineTransformOp (AffineTransform xform, RenderingHints hints)
114
    {
115
      this.transform = xform;
116
      this.hints = hints;
117
      if (xform.getDeterminant() == 0)
118
        throw new ImagingOpException(null);
119
    }
120
 
121
    /**
122
     * Creates a new BufferedImage with the size equal to that of the
123
     * transformed image and the correct number of bands. The newly created
124
     * image is created with the specified ColorModel.
125
     * If a ColorModel is not specified, an appropriate ColorModel is used.
126
     *
127
     * @param src the source image.
128
     * @param destCM color model for the destination image (can be null).
129
     * @return a new compatible destination image.
130
     */
131
    public BufferedImage createCompatibleDestImage (BufferedImage src,
132
                                                    ColorModel destCM)
133
    {
134
      if (destCM != null)
135
        return new BufferedImage(destCM,
136
                                 createCompatibleDestRaster(src.getRaster()),
137
                                 src.isAlphaPremultiplied(), null);
138
 
139
      // This behaviour was determined by Mauve testcases, and is compatible
140
      // with the reference implementation
141
      if (src.getType() == BufferedImage.TYPE_INT_ARGB_PRE
142
          || src.getType() == BufferedImage.TYPE_4BYTE_ABGR
143
          || src.getType() == BufferedImage.TYPE_4BYTE_ABGR_PRE)
144
        return new BufferedImage(src.getWidth(), src.getHeight(), src.getType());
145
 
146
      else
147
        return new BufferedImage(src.getWidth(), src.getHeight(),
148
                                 BufferedImage.TYPE_INT_ARGB);
149
    }
150
 
151
    /**
152
     * Creates a new WritableRaster with the size equal to the transformed
153
     * source raster and correct number of bands .
154
     *
155
     * @param src the source raster.
156
     * @throws RasterFormatException if resulting width or height of raster is 0.
157
     * @return a new compatible raster.
158
     */
159
    public WritableRaster createCompatibleDestRaster (Raster src)
160
    {
161
      Rectangle2D rect = getBounds2D(src);
162
 
163
      if (rect.getWidth() == 0 || rect.getHeight() == 0)
164
        throw new RasterFormatException("width or height is 0");
165
 
166
      return src.createCompatibleWritableRaster((int) rect.getWidth(),
167
                                                (int) rect.getHeight());
168
    }
169
 
170
    /**
171
     * Transforms source image using transform specified at the constructor.
172
     * The resulting transformed image is stored in the destination image if one
173
     * is provided; otherwise a new BufferedImage is created and returned.
174
     *
175
     * @param src source image
176
     * @param dst destination image
177
     * @throws IllegalArgumentException if the source and destination image are
178
     *          the same
179
     * @return transformed source image.
180
     */
181
    public final BufferedImage filter (BufferedImage src, BufferedImage dst)
182
    {
183
      if (dst == src)
184
        throw new IllegalArgumentException("src image cannot be the same as "
185
                                         + "the dst image");
186
 
187
      // If the destination image is null, then use a compatible BufferedImage
188
      if (dst == null)
189
        dst = createCompatibleDestImage(src, null);
190
 
191
      Graphics2D gr = dst.createGraphics();
192
      gr.setRenderingHints(hints);
193
      gr.drawImage(src, transform, null);
194
      return dst;
195
    }
196
 
197
    /**
198
     * Transforms source raster using transform specified at the constructor.
199
     * The resulting raster is stored in the destination raster if it is not
200
     * null, otherwise a new raster is created and returned.
201
     *
202
     * @param src source raster
203
     * @param dst destination raster
204
     * @throws IllegalArgumentException if the source and destination are not
205
     *          compatible
206
     * @return transformed raster.
207
     */
208
    public final WritableRaster filter(Raster src, WritableRaster dst)
209
    {
210
      // Initial checks
211
      if (dst == src)
212
        throw new IllegalArgumentException("src image cannot be the same as"
213
                                           + " the dst image");
214
 
215
      if (dst == null)
216
        dst = createCompatibleDestRaster(src);
217
 
218
      if (src.getNumBands() != dst.getNumBands())
219
        throw new IllegalArgumentException("src and dst must have same number"
220
                                           + " of bands");
221
 
222
      // Optimization for rasters that can be represented in the RGB colormodel:
223
      // wrap the rasters in images, and let Cairo do the transformation
224
      if (ColorModel.getRGBdefault().isCompatibleSampleModel(src.getSampleModel())
225
          && ColorModel.getRGBdefault().isCompatibleSampleModel(dst.getSampleModel()))
226
        {
227
          WritableRaster src2 = Raster.createWritableRaster(src.getSampleModel(),
228
                                                            src.getDataBuffer(),
229
                                                            new Point(src.getMinX(),
230
                                                                      src.getMinY()));
231
          BufferedImage iSrc = new BufferedImage(ColorModel.getRGBdefault(),
232
                                                 src2, false, null);
233
          BufferedImage iDst = new BufferedImage(ColorModel.getRGBdefault(), dst,
234
                                                 false, null);
235
 
236
          return filter(iSrc, iDst).getRaster();
237
        }
238
 
239
      // Otherwise, we need to do the transformation in java code...
240
      // Create arrays to hold all the points
241
      double[] dstPts = new double[dst.getHeight() * dst.getWidth() * 2];
242
      double[] srcPts = new double[dst.getHeight() * dst.getWidth() * 2];
243
 
244
      // Populate array with all points in the *destination* raster
245
      int i = 0;
246
      for (int x = 0; x < dst.getWidth(); x++)
247
        {
248
          for (int y = 0; y < dst.getHeight(); y++)
249
            {
250
              dstPts[i++] = x;
251
              dstPts[i++] = y;
252
            }
253
        }
254
      Rectangle srcbounds = src.getBounds();
255
 
256
      // Use an inverse transform to map each point in the destination to
257
      // a point in the source.  Note that, while all points in the destination
258
      // matrix are integers, this is not necessarily true for points in the
259
      // source (hence why interpolation is required)
260
      try
261
        {
262
          AffineTransform inverseTx = transform.createInverse();
263
          inverseTx.transform(dstPts, 0, srcPts, 0, dstPts.length / 2);
264
        }
265
      catch (NoninvertibleTransformException e)
266
        {
267
          // Shouldn't happen since the constructor traps this
268
          throw new ImagingOpException(e.getMessage());
269
        }
270
 
271
      // Different interpolation methods...
272
      if (hints.containsValue(RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR))
273
        filterNearest(src, dst, dstPts, srcPts);
274
 
275
      else if (hints.containsValue(RenderingHints.VALUE_INTERPOLATION_BILINEAR))
276
        filterBilinear(src, dst, dstPts, srcPts);
277
 
278
      else          // bicubic
279
        filterBicubic(src, dst, dstPts, srcPts);
280
 
281
      return dst;
282
    }
283
 
284
    /**
285
     * Transforms source image using transform specified at the constructor and
286
     * returns bounds of the transformed image.
287
     *
288
     * @param src image to be transformed
289
     * @return bounds of the transformed image.
290
     */
291
    public final Rectangle2D getBounds2D (BufferedImage src)
292
    {
293
      return getBounds2D (src.getRaster());
294
    }
295
 
296
    /**
297
     * Returns bounds of the transformed raster.
298
     *
299
     * @param src raster to be transformed
300
     * @return bounds of the transformed raster.
301
     */
302
    public final Rectangle2D getBounds2D (Raster src)
303
    {
304
      return transform.createTransformedShape(src.getBounds()).getBounds2D();
305
    }
306
 
307
    /**
308
     * Returns interpolation type used during transformations.
309
     *
310
     * @return interpolation type
311
     */
312
    public final int getInterpolationType ()
313
    {
314
      if (hints.containsValue(RenderingHints.VALUE_INTERPOLATION_BILINEAR))
315
        return TYPE_BILINEAR;
316
 
317
      else if (hints.containsValue(RenderingHints.VALUE_INTERPOLATION_BICUBIC))
318
        return TYPE_BICUBIC;
319
 
320
      else
321
        return TYPE_NEAREST_NEIGHBOR;
322
    }
323
 
324
    /**
325
     * Returns location of the transformed source point. The resulting point
326
     * is stored in the dstPt if one is specified.
327
     *
328
     * @param srcPt point to be transformed
329
     * @param dstPt destination point
330
     * @return the location of the transformed source point.
331
     */
332
    public final Point2D getPoint2D (Point2D srcPt, Point2D dstPt)
333
    {
334
      return transform.transform (srcPt, dstPt);
335
    }
336
 
337
    /**
338
     * Returns rendering hints that are used during transformation.
339
     *
340
     * @return the rendering hints used in this Op.
341
     */
342
    public final RenderingHints getRenderingHints ()
343
    {
344
      return hints;
345
    }
346
 
347
    /**
348
     * Returns transform used in transformation between source and destination
349
     * image.
350
     *
351
     * @return the transform used in this Op.
352
     */
353
    public final AffineTransform getTransform ()
354
    {
355
      return transform;
356
    }
357
 
358
    /**
359
     * Perform nearest-neighbour filtering
360
     *
361
     * @param src the source raster
362
     * @param dst the destination raster
363
     * @param dpts array of points on the destination raster
364
     * @param pts array of corresponding points on the source raster
365
     */
366
    private void filterNearest(Raster src, WritableRaster dst, double[] dpts,
367
                               double[] pts)
368
    {
369
      Rectangle srcbounds = src.getBounds();
370
 
371
      // For all points on the destination raster, copy the value from the
372
      // corrosponding (rounded) source point
373
      for (int i = 0; i < dpts.length; i += 2)
374
        {
375
          int srcX = (int) Math.round(pts[i]) + src.getMinX();
376
          int srcY = (int) Math.round(pts[i + 1]) + src.getMinY();
377
 
378
          if (srcbounds.contains(srcX, srcY))
379
            dst.setDataElements((int) dpts[i] + dst.getMinX(),
380
                                (int) dpts[i + 1] + dst.getMinY(),
381
                                src.getDataElements(srcX, srcY, null));
382
        }
383
    }
384
 
385
    /**
386
     * Perform bilinear filtering
387
     *
388
     * @param src the source raster
389
     * @param dst the destination raster
390
     * @param dpts array of points on the destination raster
391
     * @param pts array of corresponding points on the source raster
392
     */
393
    private void filterBilinear(Raster src, WritableRaster dst, double[] dpts,
394
                              double[] pts)
395
    {
396
      Rectangle srcbounds = src.getBounds();
397
 
398
      Object xyarr = null;
399
      Object xp1arr = null;
400
      Object yp1arr = null;
401
      Object xyp1arr = null;
402
 
403
      double xy;
404
      double xp1;
405
      double yp1;
406
      double xyp1;
407
 
408
      double[] result = new double[src.getNumBands()];
409
 
410
      // For all points in the destination raster, use bilinear interpolation
411
      // to find the value from the corrosponding source points
412
      for (int i = 0; i < dpts.length; i += 2)
413
        {
414
          int srcX = (int) Math.round(pts[i]) + src.getMinX();
415
          int srcY = (int) Math.round(pts[i + 1]) + src.getMinY();
416
 
417
          if (srcbounds.contains(srcX, srcY))
418
            {
419
              // Corner case at the bottom or right edge; use nearest neighbour
420
              if (pts[i] >= src.getWidth() - 1
421
                  || pts[i + 1] >= src.getHeight() - 1)
422
                dst.setDataElements((int) dpts[i] + dst.getMinX(),
423
                                    (int) dpts[i + 1] + dst.getMinY(),
424
                                    src.getDataElements(srcX, srcY, null));
425
 
426
              // Standard case, apply the bilinear formula
427
              else
428
                {
429
                  int x = (int) Math.floor(pts[i] + src.getMinX());
430
                  int y = (int) Math.floor(pts[i + 1] + src.getMinY());
431
                  double xdiff = pts[i] + src.getMinX() - x;
432
                  double ydiff = pts[i + 1] + src.getMinY() - y;
433
 
434
                  // Get surrounding pixels used in interpolation... optimized
435
                  // to use the smallest datatype possible.
436
                  if (src.getTransferType() == DataBuffer.TYPE_DOUBLE
437
                      || src.getTransferType() == DataBuffer.TYPE_FLOAT)
438
                    {
439
                      xyarr = src.getPixel(x, y, (double[])xyarr);
440
                      xp1arr  = src.getPixel(x+1, y, (double[])xp1arr);
441
                      yp1arr = src.getPixel(x, y+1, (double[])yp1arr);
442
                      xyp1arr = src.getPixel(x+1, y+1, (double[])xyp1arr);
443
                    }
444
                  else
445
                    {
446
                      xyarr = src.getPixel(x, y, (int[])xyarr);
447
                      xp1arr  = src.getPixel(x+1, y, (int[])xp1arr);
448
                      yp1arr = src.getPixel(x, y+1, (int[])yp1arr);
449
                      xyp1arr = src.getPixel(x+1, y+1, (int[])xyp1arr);
450
                    }
451
                  // using
452
                  // array[] pixels = src.getPixels(x, y, 2, 2, pixels);
453
                  // instead of doing four individual src.getPixel() calls
454
                  // should be faster, but benchmarking shows that it's not...
455
 
456
                  // Run interpolation for each band
457
                  for (int j = 0; j < src.getNumBands(); j++)
458
                    {
459
                      // Pull individual sample values out of array
460
                      if (src.getTransferType() == DataBuffer.TYPE_DOUBLE
461
                          || src.getTransferType() == DataBuffer.TYPE_FLOAT)
462
                        {
463
                          xy = ((double[])xyarr)[j];
464
                          xp1  = ((double[])xp1arr)[j];
465
                          yp1 = ((double[])yp1arr)[j];
466
                          xyp1 = ((double[])xyp1arr)[j];
467
                        }
468
                      else
469
                        {
470
                          xy = ((int[])xyarr)[j];
471
                          xp1  = ((int[])xp1arr)[j];
472
                          yp1 = ((int[])yp1arr)[j];
473
                          xyp1 = ((int[])xyp1arr)[j];
474
                        }
475
 
476
                      // If all four samples are identical, there's no need to
477
                      // calculate anything
478
                      if (xy == xp1 && xy == yp1 && xy == xyp1)
479
                        result[j] = xy;
480
 
481
                      // Run bilinear interpolation formula
482
                      else
483
                        result[j] = (xy * (1-xdiff) + xp1 * xdiff)
484
                                      * (1-ydiff)
485
                                    + (yp1 * (1-xdiff) + xyp1 * xdiff)
486
                                      * ydiff;
487
                    }
488
 
489
                  dst.setPixel((int)dpts[i] + dst.getMinX(),
490
                               (int)dpts[i+1] + dst.getMinY(),
491
                               result);
492
                }
493
            }
494
        }
495
    }
496
 
497
    /**
498
     * Perform bicubic filtering
499
     * based on http://local.wasp.uwa.edu.au/~pbourke/colour/bicubic/
500
     *
501
     * @param src the source raster
502
     * @param dst the destination raster
503
     * @param dpts array of points on the destination raster
504
     * @param pts array of corresponding points on the source raster
505
     */
506
    private void filterBicubic(Raster src, WritableRaster dst, double[] dpts,
507
                               double[] pts)
508
    {
509
      Rectangle srcbounds = src.getBounds();
510
      double[] result = new double[src.getNumBands()];
511
      Object pixels = null;
512
 
513
      // For all points on the destination raster, perform bicubic interpolation
514
      // from corrosponding source points
515
      for (int i = 0; i < dpts.length; i += 2)
516
        {
517
          if (srcbounds.contains((int) Math.round(pts[i]) + src.getMinX(),
518
                                 (int) Math.round(pts[i + 1]) + src.getMinY()))
519
            {
520
              int x = (int) Math.floor(pts[i] + src.getMinX());
521
              int y = (int) Math.floor(pts[i + 1] + src.getMinY());
522
              double dx = pts[i] + src.getMinX() - x;
523
              double dy = pts[i + 1] + src.getMinY() - y;
524
              Arrays.fill(result, 0);
525
 
526
              for (int m = - 1; m < 3; m++)
527
                for (int n = - 1; n < 3; n++)
528
                  {
529
                    // R(x) = ( P(x+2)^3 - 4 P(x+1)^3 + 6 P(x)^3 - 4 P(x-1)^3 ) / 6
530
                    double r1 = 0;
531
                    double r2 = 0;
532
 
533
                    // Calculate R(m - dx)
534
                    double rx = m - dx + 2;
535
                    r1 += rx * rx * rx;
536
 
537
                    rx = m - dx + 1;
538
                    if (rx > 0)
539
                      r1 -= 4 * rx * rx * rx;
540
 
541
                    rx = m - dx;
542
                    if (rx > 0)
543
                      r1 += 6 * rx * rx * rx;
544
 
545
                    rx = m - dx - 1;
546
                    if (rx > 0)
547
                      r1 -= 4 * rx * rx * rx;
548
 
549
                    r1 /= 6;
550
 
551
                    // Calculate R(dy - n);
552
                    rx = dy - n + 2;
553
                    if (rx > 0)
554
                      r2 += rx * rx * rx;
555
 
556
                    rx = dy - n + 1;
557
                    if (rx > 0)
558
                      r2 -= 4 * rx * rx * rx;
559
 
560
                    rx = dy - n;
561
                    if (rx > 0)
562
                      r2 += 6 * rx * rx * rx;
563
 
564
                    rx = dy - n - 1;
565
                    if (rx > 0)
566
                      r2 -= 4 * rx * rx * rx;
567
 
568
                    r2 /= 6;
569
 
570
                    // Calculate F(i+m, j+n) R(m - dx) R(dy - n)
571
                    // Check corner cases
572
                    int srcX = x + m;
573
                    if (srcX >= src.getMinX() + src.getWidth())
574
                      srcX = src.getMinX() + src.getWidth() - 1;
575
                    else if (srcX < src.getMinX())
576
                      srcX = src.getMinX();
577
 
578
                    int srcY = y + n;
579
                    if (srcY >= src.getMinY() + src.getHeight())
580
                      srcY = src.getMinY() + src.getHeight() - 1;
581
                    else if (srcY < src.getMinY())
582
                      srcY = src.getMinY();
583
 
584
                    // Calculate once for each band, using the smallest
585
                    // datatype possible
586
                    if (src.getTransferType() == DataBuffer.TYPE_DOUBLE
587
                        || src.getTransferType() == DataBuffer.TYPE_FLOAT)
588
                      {
589
                        pixels = src.getPixel(srcX, srcY, (double[])pixels);
590
                        for (int j = 0; j < result.length; j++)
591
                          result[j] += ((double[])pixels)[j] * r1 * r2;
592
                      }
593
                    else
594
                      {
595
                        pixels = src.getPixel(srcX, srcY, (int[])pixels);
596
                        for (int j = 0; j < result.length; j++)
597
                          result[j] += ((int[])pixels)[j] * r1 * r2;
598
                      }
599
                  }
600
 
601
              // Put it all together
602
              dst.setPixel((int)dpts[i] + dst.getMinX(),
603
                           (int)dpts[i+1] + dst.getMinY(),
604
                           result);
605
            }
606
        }
607
    }
608
}

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.