~ubuntu-branches/ubuntu/trusty/monodevelop/trusty-proposed

« back to all changes in this revision

Viewing changes to external/ikvm/openjdk/java/awt/image/RescaleOp.java

  • Committer: Package Import Robot
  • Author(s): Jo Shields
  • Date: 2013-05-12 09:46:03 UTC
  • mto: This revision was merged to the branch mainline in revision 29.
  • Revision ID: package-import@ubuntu.com-20130512094603-mad323bzcxvmcam0
Tags: upstream-4.0.5+dfsg
ImportĀ upstreamĀ versionĀ 4.0.5+dfsg

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* Copyright (C) 2004, 2006  Free Software Foundation
 
2
 
 
3
This file is part of GNU Classpath.
 
4
 
 
5
GNU Classpath is free software; you can redistribute it and/or modify
 
6
it under the terms of the GNU General Public License as published by
 
7
the Free Software Foundation; either version 2, or (at your option)
 
8
any later version.
 
9
 
 
10
GNU Classpath is distributed in the hope that it will be useful, but
 
11
WITHOUT ANY WARRANTY; without even the implied warranty of
 
12
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 
13
General Public License for more details.
 
14
 
 
15
You should have received a copy of the GNU General Public License
 
16
along with GNU Classpath; see the file COPYING.  If not, write to the
 
17
Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
 
18
02110-1301 USA.
 
19
 
 
20
Linking this library statically or dynamically with other modules is
 
21
making a combined work based on this library.  Thus, the terms and
 
22
conditions of the GNU General Public License cover the whole
 
23
combination.
 
24
 
 
25
As a special exception, the copyright holders of this library give you
 
26
permission to link this library with independent modules to produce an
 
27
executable, regardless of the license terms of these independent
 
28
modules, and to copy and distribute the resulting executable under
 
29
terms of your choice, provided that you also meet, for each linked
 
30
independent module, the terms and conditions of the license of that
 
31
module.  An independent module is a module which is not derived from
 
32
or based on this library.  If you modify this library, you may extend
 
33
this exception to your version of the library, but you are not
 
34
obligated to do so.  If you do not wish to do so, delete this
 
35
exception statement from your version. */
 
36
 
 
37
 
 
38
package java.awt.image;
 
39
 
 
40
import java.awt.RenderingHints;
 
41
import java.awt.geom.Point2D;
 
42
import java.awt.geom.Rectangle2D;
 
43
import java.util.Arrays;
 
44
 
 
45
/**
 
46
 * RescaleOp is a filter that changes each pixel by a scaling factor and offset.
 
47
 * 
 
48
 * For filtering Rasters, either one scaling factor and offset can be specified,
 
49
 * which will be applied to all bands; or a scaling factor and offset can be
 
50
 * specified for each band.
 
51
 * 
 
52
 * For BufferedImages, the scaling may apply to both color and alpha components.
 
53
 * If only one scaling factor is provided, or if the number of factors provided
 
54
 * equals the number of color components, the scaling is performed on all color
 
55
 * components.  Otherwise, the scaling is performed on all components including
 
56
 * alpha.  Alpha premultiplication is ignored.
 
57
 * 
 
58
 * After filtering, if color conversion is necessary, the conversion happens,
 
59
 * taking alpha premultiplication into account.
 
60
 * 
 
61
 * @author Jerry Quinn (jlquinn@optonline.net)
 
62
 * @author Francis Kung (fkung@redhat.com)
 
63
 */
 
64
public class RescaleOp implements BufferedImageOp, RasterOp
 
65
{
 
66
  private float[] scale;
 
67
  private float[] offsets;
 
68
  private RenderingHints hints = null;
 
69
  
 
70
  /**
 
71
   * Create a new RescaleOp object using the given scale factors and offsets.
 
72
   * 
 
73
   * The length of the arrays must be equal to the number of bands (or number of
 
74
   * data or color components) of the raster/image that this Op will be used on,
 
75
   * otherwise an IllegalArgumentException will be thrown when calling the
 
76
   * filter method.
 
77
   *  
 
78
   * @param scaleFactors an array of scale factors.
 
79
   * @param offsets an array of offsets.
 
80
   * @param hints any rendering hints to use (can be null).
 
81
   * @throws NullPointerException if the scaleFactors or offsets array is null.
 
82
   */
 
83
  public RescaleOp(float[] scaleFactors,
 
84
                   float[] offsets,
 
85
                   RenderingHints hints)
 
86
  {
 
87
    int length = Math.min(scaleFactors.length, offsets.length);
 
88
    
 
89
    scale = new float[length];
 
90
    System.arraycopy(scaleFactors, 0, this.scale, 0, length);
 
91
    
 
92
    this.offsets = new float[length];
 
93
    System.arraycopy(offsets, 0, this.offsets, 0, length);
 
94
    
 
95
    this.hints = hints;
 
96
  }
 
97
  
 
98
  /**
 
99
   * Create a new RescaleOp object using the given scale factor and offset.
 
100
   * 
 
101
   * The same scale factor and offset will be used on all bands/components.
 
102
   *  
 
103
   * @param scaleFactor the scale factor to use.
 
104
   * @param offset the offset to use.
 
105
   * @param hints any rendering hints to use (can be null).
 
106
   */
 
107
  public RescaleOp(float scaleFactor,
 
108
                   float offset,
 
109
                   RenderingHints hints)
 
110
  {
 
111
    scale = new float[]{ scaleFactor };
 
112
    offsets = new float[]{offset};
 
113
    this.hints = hints;
 
114
  }
 
115
 
 
116
  /**
 
117
   * Returns the scaling factors.  This method accepts an optional array, which
 
118
   * will be used to store the factors if not null (this avoids allocating a 
 
119
   * new array).  If this array is too small to hold all the scaling factors,
 
120
   * the array will be filled and the remaining factors discarded.
 
121
   * 
 
122
   * @param scaleFactors array to store the scaling factors in (can be null).
 
123
   * @return an array of scaling factors.
 
124
   */
 
125
  public final float[] getScaleFactors(float[] scaleFactors)
 
126
  {
 
127
    if (scaleFactors == null)
 
128
      scaleFactors = new float[scale.length];
 
129
    System.arraycopy(scale, 0, scaleFactors, 0, Math.min(scale.length,
 
130
                                                         scaleFactors.length));
 
131
    return scaleFactors;
 
132
  }
 
133
 
 
134
  /**
 
135
   * Returns the offsets.  This method accepts an optional array, which
 
136
   * will be used to store the offsets if not null (this avoids allocating a 
 
137
   * new array).  If this array is too small to hold all the offsets, the array 
 
138
   * will be filled and the remaining factors discarded.
 
139
   * 
 
140
   * @param offsets array to store the offsets in (can be null).
 
141
   * @return an array of offsets.
 
142
   */
 
143
  public final float[] getOffsets(float[] offsets)
 
144
  {
 
145
    if (offsets == null)
 
146
      offsets = new float[this.offsets.length];
 
147
    System.arraycopy(this.offsets, 0, offsets, 0, Math.min(this.offsets.length,
 
148
                                                           offsets.length));
 
149
    return offsets;
 
150
  }
 
151
 
 
152
  /**
 
153
   * Returns the number of scaling factors / offsets.
 
154
   * 
 
155
   * @return the number of scaling factors / offsets.
 
156
   */
 
157
  public final int getNumFactors()
 
158
  {
 
159
    return scale.length;
 
160
  }
 
161
 
 
162
  /* (non-Javadoc)
 
163
   * @see java.awt.image.BufferedImageOp#getRenderingHints()
 
164
   */
 
165
  public final RenderingHints getRenderingHints()
 
166
  {
 
167
    return hints;
 
168
  }
 
169
 
 
170
  /**
 
171
   * Converts the source image using the scale factors and offsets specified in 
 
172
   * the constructor.  The resulting image is stored in the destination image if 
 
173
   * one is provided; otherwise a new BufferedImage is created and returned. 
 
174
   * 
 
175
   * The source image cannot use an IndexColorModel, and the destination image
 
176
   * (if one is provided) must have the same size.
 
177
   * 
 
178
   * If the final value of a sample is beyond the range of the color model, it
 
179
   * will be clipped to the appropriate maximum / minimum.
 
180
   *
 
181
   * @param src The source image.
 
182
   * @param dst The destination image.
 
183
   * @throws IllegalArgumentException if the rasters and/or color spaces are
 
184
   *            incompatible.
 
185
   * @return The rescaled image.
 
186
   */
 
187
  public final BufferedImage filter(BufferedImage src, BufferedImage dst)
 
188
  {
 
189
    // Initial checks
 
190
    if (scale.length != 1
 
191
        && scale.length != src.getColorModel().getNumComponents()
 
192
        && (scale.length != src.getColorModel().getNumColorComponents()))
 
193
      throw new IllegalArgumentException("Source image has wrong number of "
 
194
                                         + "bands for these scaling factors.");
 
195
 
 
196
    if (dst == null)
 
197
      dst = createCompatibleDestImage(src, null);
 
198
    else if (src.getHeight() != dst.getHeight()
 
199
             || src.getWidth() != dst.getWidth())
 
200
      throw new IllegalArgumentException("Source and destination images are "
 
201
                                         + "different sizes.");
 
202
 
 
203
    // Prepare for possible colorspace conversion
 
204
    BufferedImage dst2 = dst;
 
205
    if (dst.getColorModel().getColorSpace().getType() != src.getColorModel().getColorSpace().getType())
 
206
      dst2 = createCompatibleDestImage(src, src.getColorModel());
 
207
 
 
208
    // Figure out how many bands to scale
 
209
    int numBands = scale.length;
 
210
    if (scale.length == 1)
 
211
      numBands = src.getColorModel().getNumColorComponents();
 
212
    boolean[] bands = new boolean[numBands];
 
213
    // this assumes the alpha, if present, is the last band
 
214
    Arrays.fill(bands, true);
 
215
 
 
216
    // Perform rescaling
 
217
    filter(src.getRaster(), dst2.getRaster(), bands);
 
218
 
 
219
    // Copy alpha band if needed (ie if it exists and wasn't scaled)
 
220
    // NOTE: This assumes the alpha component is the last band!
 
221
    if (src.getColorModel().hasAlpha()
 
222
        && numBands == src.getColorModel().getNumColorComponents())
 
223
      {
 
224
 
 
225
        dst2.getRaster().setSamples(0, 0, src.getWidth(), src.getHeight(),
 
226
                                    numBands,
 
227
                                    src.getRaster().getSamples(0, 0,
 
228
                                                               src.getWidth(),
 
229
                                                               src.getHeight(),
 
230
                                                               numBands,
 
231
                                                               (int[]) null));
 
232
      }
 
233
 
 
234
    // Perform colorspace conversion if needed
 
235
    if (dst != dst2)
 
236
      new ColorConvertOp(hints).filter(dst2, dst);
 
237
 
 
238
    return dst;
 
239
  }
 
240
 
 
241
  /* (non-Javadoc)
 
242
   * @see java.awt.image.RasterOp#filter(java.awt.image.Raster, java.awt.image.WritableRaster)
 
243
   */
 
244
  public final WritableRaster filter(Raster src, WritableRaster dest)
 
245
  {
 
246
    // Required sanity checks
 
247
    if (scale.length != 1 && scale.length != src.numBands)
 
248
      throw new IllegalArgumentException("Number of rasters is incompatible "
 
249
                                             + "with the number of scaling "
 
250
                                             + "factors provided.");
 
251
 
 
252
    if (dest == null)
 
253
      dest = src.createCompatibleWritableRaster();
 
254
    else if (src.getHeight() != dest.getHeight()
 
255
             || src.getWidth() != dest.getWidth())
 
256
      throw new IllegalArgumentException("Source and destination rasters are "
 
257
                                         + "different sizes.");
 
258
    else if (src.numBands != dest.numBands)
 
259
      throw new IllegalArgumentException("Source and destination rasters "
 
260
                                         + "are incompatible.");
 
261
 
 
262
    // Filter all bands
 
263
    boolean[] bands = new boolean[src.getNumBands()];
 
264
    Arrays.fill(bands, true);
 
265
    return filter(src, dest, bands);
 
266
  }
 
267
  
 
268
  /**
 
269
   * Perform raster-based filtering on a selected number of bands.
 
270
   * 
 
271
   * The length of the bands array should equal the number of bands; a true
 
272
   * element indicates filtering should happen on the corresponding band, while
 
273
   * a false element will skip the band.
 
274
   * 
 
275
   * The rasters are assumed to be compatible and non-null.
 
276
   * 
 
277
   * @param src the source raster.
 
278
   * @param dest the destination raster.
 
279
   * @param bands an array indicating which bands to filter.
 
280
   * @throws NullPointerException if any parameter is null.
 
281
   * @throws ArrayIndexOutOfBoundsException if the bands array is too small.
 
282
   * @return the destination raster.
 
283
   */
 
284
  private WritableRaster filter(Raster src, WritableRaster dest, boolean[] bands)
 
285
  {
 
286
    int[] values = new int[src.getHeight() * src.getWidth()];
 
287
    float scaleFactor, offset;
 
288
    
 
289
    // Find max sample value, to be used for clipping later
 
290
    int[] maxValue = src.getSampleModel().getSampleSize();
 
291
    for (int i = 0; i < maxValue.length; i++)
 
292
      maxValue[i] = (int)Math.pow(2, maxValue[i]) - 1;
 
293
    
 
294
    // TODO: can this be optimized further?
 
295
    // Filter all samples of all requested bands
 
296
    for (int band = 0; band < bands.length; band++)
 
297
      if (bands[band])
 
298
        {
 
299
          values = src.getSamples(src.getMinX(), src.getMinY(), src.getWidth(),
 
300
                                  src.getHeight(), band, values);
 
301
 
 
302
          if (scale.length == 1)
 
303
            {
 
304
              scaleFactor = scale[0];
 
305
              offset = offsets[0];
 
306
            }
 
307
          else
 
308
            {
 
309
              scaleFactor = scale[band];
 
310
              offset = offsets[band];
 
311
            }
 
312
 
 
313
          for (int i = 0; i < values.length; i++)
 
314
            {
 
315
              values[i] = (int) (values[i] * scaleFactor + offset);
 
316
 
 
317
              // Clip if needed
 
318
              if (values[i] < 0)
 
319
                values[i] = 0;
 
320
              if (values[i] > maxValue[band])
 
321
                values[i] = maxValue[band];
 
322
            }
 
323
 
 
324
          dest.setSamples(dest.getMinX(), dest.getMinY(), dest.getWidth(),
 
325
                          dest.getHeight(), band, values);
 
326
        }
 
327
    
 
328
    return dest;
 
329
  }
 
330
 
 
331
  /*
 
332
   * (non-Javadoc)
 
333
   * 
 
334
   * @see java.awt.image.BufferedImageOp#createCompatibleDestImage(java.awt.image.BufferedImage,
 
335
   *      java.awt.image.ColorModel)
 
336
   */
 
337
  public BufferedImage createCompatibleDestImage(BufferedImage src,
 
338
                                                 ColorModel dstCM)
 
339
  {
 
340
    if (dstCM == null)
 
341
      return new BufferedImage(src.getWidth(), src.getHeight(), src.getType());
 
342
    
 
343
    return new BufferedImage(dstCM,
 
344
                             src.getRaster().createCompatibleWritableRaster(),
 
345
                             src.isAlphaPremultiplied(), null);
 
346
  }
 
347
 
 
348
  /* (non-Javadoc)
 
349
   * @see java.awt.image.RasterOp#createCompatibleDestRaster(java.awt.image.Raster)
 
350
   */
 
351
  public WritableRaster createCompatibleDestRaster(Raster src)
 
352
  {
 
353
    return src.createCompatibleWritableRaster();
 
354
  }
 
355
  
 
356
  /* (non-Javadoc)
 
357
   * @see java.awt.image.BufferedImageOp#getBounds2D(java.awt.image.BufferedImage)
 
358
   */
 
359
  public final Rectangle2D getBounds2D(BufferedImage src)
 
360
  {
 
361
    return src.getRaster().getBounds();
 
362
  }
 
363
 
 
364
  /* (non-Javadoc)
 
365
   * @see java.awt.image.RasterOp#getBounds2D(java.awt.image.Raster)
 
366
   */
 
367
  public final Rectangle2D getBounds2D(Raster src)
 
368
  {
 
369
    return src.getBounds();
 
370
  }
 
371
 
 
372
  /* (non-Javadoc)
 
373
   * @see java.awt.image.BufferedImageOp#getPoint2D(java.awt.geom.Point2D, java.awt.geom.Point2D)
 
374
   */
 
375
  public final Point2D getPoint2D(Point2D src, Point2D dst)
 
376
  {
 
377
    if (dst == null)
 
378
      dst = (Point2D) src.clone();
 
379
    else
 
380
      dst.setLocation(src);
 
381
    
 
382
    return dst;
 
383
  }
 
384
 
 
385
}