1/*
2 *  Licensed to the Apache Software Foundation (ASF) under one or more
3 *  contributor license agreements.  See the NOTICE file distributed with
4 *  this work for additional information regarding copyright ownership.
5 *  The ASF licenses this file to You under the Apache License, Version 2.0
6 *  (the "License"); you may not use this file except in compliance with
7 *  the License.  You may obtain a copy of the License at
8 *
9 *     http://www.apache.org/licenses/LICENSE-2.0
10 *
11 *  Unless required by applicable law or agreed to in writing, software
12 *  distributed under the License is distributed on an "AS IS" BASIS,
13 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 *  See the License for the specific language governing permissions and
15 *  limitations under the License.
16 */
17/**
18 * @author Igor V. Stolyarov
19 * @version $Revision$
20 */
21
22package java.awt.image;
23
24import java.util.Arrays;
25
26import org.apache.harmony.awt.internal.nls.Messages;
27
28/**
29 * The ComponentSampleModel class represents a set of image data whose each
30 * element - the sample of a pixel - takes one data element of the DataBuffer.
31 * <p>
32 * The Bank indices denote the correspondence between the bank of data buffers
33 * and a band of image data. The Pixel stride is the number of data array
34 * elements between two samples for the same band on the same scanline. The
35 * pixel stride for a BandedSampleModel is one. The scanline stride represents
36 * the number of data array elements between a specified sample and the
37 * corresponding sample in the same column in the next scanline. The array of
38 * band offsets gives the starting offsets within each data banks of the in the
39 * DataBuffer. The bank indices represents the indices within each bank of the
40 * DataBuffer corresponding to a band of image data.
41 *
42 * @since Android 1.0
43 */
44public class ComponentSampleModel extends SampleModel {
45
46    /**
47     * The band offsets array of this ComponentSampleModel.
48     */
49    protected int bandOffsets[];
50
51    /**
52     * The bank indices array of this ComponentSampleModel.
53     */
54    protected int bankIndices[];
55
56    /**
57     * The number of bands in this ComponentSampleModel.
58     */
59    protected int numBands;
60
61    /**
62     * The number banks of this ComponentSampleModel.
63     */
64    protected int numBanks;
65
66    /**
67     * The scanline stride of this ComponentSampleModel.
68     */
69    protected int scanlineStride;
70
71    /**
72     * The pixel stride of this ComponentSampleModel.
73     */
74    protected int pixelStride;
75
76    /**
77     * Instantiates a new ComponentSampleModel with the specified properties.
78     *
79     * @param dataType
80     *            the data type of samples.
81     * @param w
82     *            the width of the image data.
83     * @param h
84     *            the height of the image data.
85     * @param pixelStride
86     *            the pixel stride of the image data.
87     * @param scanlineStride
88     *            the scanline stride of the image data.
89     * @param bankIndices
90     *            the array of the bank indices.
91     * @param bandOffsets
92     *            the array of the band offsets.
93     */
94    public ComponentSampleModel(int dataType, int w, int h, int pixelStride, int scanlineStride,
95            int bankIndices[], int bandOffsets[]) {
96
97        super(dataType, w, h, bandOffsets.length);
98
99        if (pixelStride < 0) {
100            // awt.24B=Pixel stride must be >= 0
101            throw new IllegalArgumentException(Messages.getString("awt.24B")); //$NON-NLS-1$
102        }
103
104        if (scanlineStride < 0) {
105            // awt.24C=Scanline stride must be >= 0
106            throw new IllegalArgumentException(Messages.getString("awt.24C")); //$NON-NLS-1$
107        }
108
109        if (bankIndices.length != bandOffsets.length) {
110            // awt.24D=Bank Indices length must be equal Bank Offsets length
111            throw new IllegalArgumentException(Messages.getString("awt.24D")); //$NON-NLS-1$
112        }
113
114        this.pixelStride = pixelStride;
115        this.scanlineStride = scanlineStride;
116        this.bandOffsets = bandOffsets.clone();
117        this.bankIndices = bankIndices.clone();
118        this.numBands = bandOffsets.length;
119
120        int maxBank = 0;
121        for (int i = 0; i < bankIndices.length; i++) {
122            if (bankIndices[i] < 0) {
123                // awt.24E=Index of {0} bank must be >= 0
124                throw new IllegalArgumentException(Messages.getString("awt.24E", i)); //$NON-NLS-1$
125            }
126            if (bankIndices[i] > maxBank) {
127                maxBank = bankIndices[i];
128            }
129        }
130        this.numBanks = maxBank + 1;
131
132    }
133
134    /**
135     * Instantiates a new ComponentSampleModel with the specified properties.
136     *
137     * @param dataType
138     *            the data type of the samples.
139     * @param w
140     *            the width of the image data.
141     * @param h
142     *            the height of the image data.
143     * @param pixelStride
144     *            the pixel stride of the image data.
145     * @param scanlineStride
146     *            the scanline stride of the image data.
147     * @param bandOffsets
148     *            the band offsets.
149     */
150    public ComponentSampleModel(int dataType, int w, int h, int pixelStride, int scanlineStride,
151            int bandOffsets[]) {
152
153        super(dataType, w, h, bandOffsets.length);
154        if (pixelStride < 0) {
155            // awt.24B=Pixel stride must be >= 0
156            throw new IllegalArgumentException(Messages.getString("awt.24B")); //$NON-NLS-1$
157        }
158
159        if (scanlineStride < 0) {
160            // awt.24C=Scanline stride must be >= 0
161            throw new IllegalArgumentException(Messages.getString("awt.24C")); //$NON-NLS-1$
162        }
163
164        this.pixelStride = pixelStride;
165        this.scanlineStride = scanlineStride;
166        this.bandOffsets = bandOffsets.clone();
167        this.numBands = bandOffsets.length;
168        this.numBanks = 1;
169
170        this.bankIndices = new int[numBands];
171        for (int i = 0; i < numBands; i++) {
172            bankIndices[i] = 0;
173        }
174    }
175
176    @Override
177    public Object getDataElements(int x, int y, Object obj, DataBuffer data) {
178        if (x < 0 || y < 0 || x >= this.width || y >= this.height) {
179            // awt.63=Coordinates are not in bounds
180            throw new ArrayIndexOutOfBoundsException(Messages.getString("awt.63")); //$NON-NLS-1$
181        }
182        switch (dataType) {
183            case DataBuffer.TYPE_BYTE:
184                byte bdata[];
185                if (obj == null) {
186                    bdata = new byte[numBands];
187                } else {
188                    bdata = (byte[])obj;
189                }
190
191                for (int i = 0; i < numBands; i++) {
192                    bdata[i] = (byte)getSample(x, y, i, data);
193                }
194
195                obj = bdata;
196                break;
197
198            case DataBuffer.TYPE_SHORT:
199            case DataBuffer.TYPE_USHORT:
200                short sdata[];
201                if (obj == null) {
202                    sdata = new short[numBands];
203                } else {
204                    sdata = (short[])obj;
205                }
206
207                for (int i = 0; i < numBands; i++) {
208                    sdata[i] = (short)getSample(x, y, i, data);
209                }
210
211                obj = sdata;
212                break;
213
214            case DataBuffer.TYPE_INT:
215                int idata[];
216                if (obj == null) {
217                    idata = new int[numBands];
218                } else {
219                    idata = (int[])obj;
220                }
221
222                for (int i = 0; i < numBands; i++) {
223                    idata[i] = getSample(x, y, i, data);
224                }
225
226                obj = idata;
227                break;
228
229            case DataBuffer.TYPE_FLOAT:
230                float fdata[];
231                if (obj == null) {
232                    fdata = new float[numBands];
233                } else {
234                    fdata = (float[])obj;
235                }
236
237                for (int i = 0; i < numBands; i++) {
238                    fdata[i] = getSampleFloat(x, y, i, data);
239                }
240
241                obj = fdata;
242                break;
243
244            case DataBuffer.TYPE_DOUBLE:
245                double ddata[];
246                if (obj == null) {
247                    ddata = new double[numBands];
248                } else {
249                    ddata = (double[])obj;
250                }
251
252                for (int i = 0; i < numBands; i++) {
253                    ddata[i] = getSampleDouble(x, y, i, data);
254                }
255
256                obj = ddata;
257                break;
258        }
259
260        return obj;
261    }
262
263    @Override
264    public void setDataElements(int x, int y, Object obj, DataBuffer data) {
265        if (x < 0 || y < 0 || x >= this.width || y >= this.height) {
266            // awt.63=Coordinates are not in bounds
267            throw new ArrayIndexOutOfBoundsException(Messages.getString("awt.63")); //$NON-NLS-1$
268        }
269        switch (dataType) {
270            case DataBuffer.TYPE_BYTE:
271                byte barr[] = (byte[])obj;
272                for (int i = 0; i < numBands; i++) {
273                    setSample(x, y, i, barr[i] & 0xff, data);
274                }
275                break;
276
277            case DataBuffer.TYPE_SHORT:
278            case DataBuffer.TYPE_USHORT:
279                short sarr[] = (short[])obj;
280                for (int i = 0; i < numBands; i++) {
281                    setSample(x, y, i, sarr[i] & 0xffff, data);
282                }
283                break;
284
285            case DataBuffer.TYPE_INT:
286                int iarr[] = (int[])obj;
287                for (int i = 0; i < numBands; i++) {
288                    setSample(x, y, i, iarr[i], data);
289                }
290                break;
291
292            case DataBuffer.TYPE_FLOAT:
293                float farr[] = (float[])obj;
294                for (int i = 0; i < numBands; i++) {
295                    setSample(x, y, i, farr[i], data);
296                }
297                break;
298
299            case DataBuffer.TYPE_DOUBLE:
300                double darr[] = (double[])obj;
301                for (int i = 0; i < numBands; i++) {
302                    setSample(x, y, i, darr[i], data);
303                }
304                break;
305        }
306    }
307
308    /**
309     * Compares this ComponentSampleModel with the specified Object.
310     *
311     * @param o
312     *            the Object.
313     * @return true, if the object is a ComponentSampleModel with identical data
314     *         values to this ComponentSampleModel, false otherwise.
315     */
316    @Override
317    public boolean equals(Object o) {
318        if ((o == null) || !(o instanceof ComponentSampleModel)) {
319            return false;
320        }
321        ComponentSampleModel model = (ComponentSampleModel)o;
322        return this.width == model.width && this.height == model.height
323                && this.numBands == model.numBands && this.dataType == model.dataType
324                && Arrays.equals(this.bandOffsets, model.bandOffsets)
325                && Arrays.equals(this.bankIndices, model.bankIndices)
326                && this.numBands == model.numBands && this.numBanks == model.numBanks
327                && this.scanlineStride == model.scanlineStride
328                && this.pixelStride == model.pixelStride;
329    }
330
331    /**
332     * @see java.awt.image.SampleModel#createSubsetSampleModel(int[])
333     */
334    @Override
335    public SampleModel createSubsetSampleModel(int bands[]) {
336        if (bands.length > this.numBands) {
337            // awt.64=The number of the bands in the subset is greater than the
338            // number of bands in the sample model
339            throw new RasterFormatException(Messages.getString("awt.64")); //$NON-NLS-1$
340        }
341
342        int indices[] = new int[bands.length];
343        int offsets[] = new int[bands.length];
344
345        for (int i = 0; i < bands.length; i++) {
346            indices[i] = bankIndices[bands[i]];
347            offsets[i] = bandOffsets[bands[i]];
348        }
349
350        return new ComponentSampleModel(dataType, width, height, pixelStride, scanlineStride,
351                indices, offsets);
352
353    }
354
355    @Override
356    public SampleModel createCompatibleSampleModel(int w, int h) {
357        return new ComponentSampleModel(dataType, w, h, pixelStride, pixelStride * w, bankIndices,
358                bandOffsets);
359    }
360
361    @Override
362    public int[] getPixel(int x, int y, int iArray[], DataBuffer data) {
363        int pixel[];
364
365        if (iArray == null) {
366            pixel = new int[numBands];
367        } else {
368            pixel = iArray;
369        }
370
371        for (int i = 0; i < numBands; i++) {
372            pixel[i] = getSample(x, y, i, data);
373        }
374
375        return pixel;
376    }
377
378    @Override
379    public void setPixel(int x, int y, int iArray[], DataBuffer data) {
380        for (int i = 0; i < numBands; i++) {
381            setSample(x, y, i, iArray[i], data);
382        }
383    }
384
385    @Override
386    public int getSample(int x, int y, int b, DataBuffer data) {
387        if (x < 0 || y < 0 || x >= this.width || y >= this.height) {
388            // awt.63=Coordinates are not in bounds
389            throw new ArrayIndexOutOfBoundsException(Messages.getString("awt.63")); //$NON-NLS-1$
390        }
391
392        return data.getElem(bankIndices[b], y * scanlineStride + x * pixelStride + bandOffsets[b]);
393    }
394
395    @Override
396    public float getSampleFloat(int x, int y, int b, DataBuffer data) {
397        if (x < 0 || y < 0 || x >= this.width || y >= this.height) {
398            // awt.63=Coordinates are not in bounds
399            throw new ArrayIndexOutOfBoundsException(Messages.getString("awt.63")); //$NON-NLS-1$
400        }
401
402        return data.getElemFloat(bankIndices[b], y * scanlineStride + x * pixelStride
403                + bandOffsets[b]);
404    }
405
406    @Override
407    public double getSampleDouble(int x, int y, int b, DataBuffer data) {
408        if (x < 0 || y < 0 || x >= this.width || y >= this.height) {
409            // awt.63=Coordinates are not in bounds
410            throw new ArrayIndexOutOfBoundsException(Messages.getString("awt.63")); //$NON-NLS-1$
411        }
412
413        return data.getElemDouble(bankIndices[b], y * scanlineStride + x * pixelStride
414                + bandOffsets[b]);
415    }
416
417    @Override
418    public int[] getPixels(int x, int y, int w, int h, int iArray[], DataBuffer data) {
419        if (x < 0 || y < 0 || x > this.width || x + w > this.width || y > this.height
420                || y + h > this.height) {
421            // awt.63=Coordinates are not in bounds
422            throw new ArrayIndexOutOfBoundsException(Messages.getString("awt.63")); //$NON-NLS-1$
423        }
424        int pixels[] = null;
425        int idx = 0;
426
427        if (iArray == null) {
428            pixels = new int[w * h * numBands];
429        } else {
430            pixels = iArray;
431        }
432
433        for (int i = y; i < y + h; i++) {
434            for (int j = x; j < x + w; j++) {
435                for (int n = 0; n < numBands; n++) {
436                    pixels[idx++] = getSample(j, i, n, data);
437                }
438            }
439        }
440
441        return pixels;
442    }
443
444    @Override
445    public void setPixels(int x, int y, int w, int h, int iArray[], DataBuffer data) {
446        if (x < 0 || y < 0 || x + w > this.width || y + h > this.height) {
447            // awt.63=Coordinates are not in bounds
448            throw new ArrayIndexOutOfBoundsException(Messages.getString("awt.63")); //$NON-NLS-1$
449        }
450        int idx = 0;
451        for (int i = y; i < y + h; i++) {
452            for (int j = x; j < x + w; j++) {
453                for (int n = 0; n < numBands; n++) {
454                    setSample(j, i, n, iArray[idx++], data);
455                }
456            }
457        }
458    }
459
460    @Override
461    public void setSample(int x, int y, int b, int s, DataBuffer data) {
462        if (x < 0 || y < 0 || x >= this.width || y >= this.height) {
463            // awt.63=Coordinates are not in bounds
464            throw new ArrayIndexOutOfBoundsException(Messages.getString("awt.63")); //$NON-NLS-1$
465        }
466
467        data.setElem(bankIndices[b], y * scanlineStride + x * pixelStride + bandOffsets[b], s);
468    }
469
470    @Override
471    public int[] getSamples(int x, int y, int w, int h, int b, int iArray[], DataBuffer data) {
472        if (x < 0 || y < 0 || x + w > this.width || y + h > this.height) {
473            // awt.63=Coordinates are not in bounds
474            throw new ArrayIndexOutOfBoundsException(Messages.getString("awt.63")); //$NON-NLS-1$
475        }
476        int samples[];
477        int idx = 0;
478
479        if (iArray == null) {
480            samples = new int[w * h];
481        } else {
482            samples = iArray;
483        }
484
485        if (data == null) {
486            // awt.295=data is null
487            throw new NullPointerException(Messages.getString("awt.295")); //$NON-NLS-1$
488        }
489
490        for (int i = y; i < y + h; i++) {
491            for (int j = x; j < x + w; j++) {
492                samples[idx++] = getSample(j, i, b, data);
493            }
494        }
495
496        return samples;
497    }
498
499    @Override
500    public void setSamples(int x, int y, int w, int h, int b, int iArray[], DataBuffer data) {
501        if (x < 0 || y < 0 || x + w > this.width || y + h > this.height) {
502            // awt.63=Coordinates are not in bounds
503            throw new ArrayIndexOutOfBoundsException(Messages.getString("awt.63")); //$NON-NLS-1$
504        }
505        int idx = 0;
506        for (int i = y; i < y + h; i++) {
507            for (int j = x; j < x + w; j++) {
508                setSample(j, i, b, iArray[idx++], data);
509            }
510        }
511    }
512
513    @Override
514    public void setSample(int x, int y, int b, float s, DataBuffer data) {
515        if (x < 0 || y < 0 || x >= this.width || y >= this.height) {
516            // awt.63=Coordinates are not in bounds
517            throw new ArrayIndexOutOfBoundsException(Messages.getString("awt.63")); //$NON-NLS-1$
518        }
519
520        data.setElemFloat(bankIndices[b], y * scanlineStride + x * pixelStride + bandOffsets[b], s);
521    }
522
523    @Override
524    public void setSample(int x, int y, int b, double s, DataBuffer data) {
525        if (x < 0 || y < 0 || x >= this.width || y >= this.height) {
526            // awt.63=Coordinates are not in bounds
527            throw new ArrayIndexOutOfBoundsException(Messages.getString("awt.63")); //$NON-NLS-1$
528        }
529
530        data
531                .setElemDouble(bankIndices[b], y * scanlineStride + x * pixelStride
532                        + bandOffsets[b], s);
533    }
534
535    @Override
536    public DataBuffer createDataBuffer() {
537        DataBuffer data = null;
538
539        int maxOffset = bandOffsets[0];
540        for (int i = 1; i < bandOffsets.length; i++) {
541            if (bandOffsets[i] > maxOffset) {
542                maxOffset = bandOffsets[i];
543            }
544        }
545        int size = (height - 1) * scanlineStride + (width - 1) * pixelStride + maxOffset + 1;
546
547        switch (dataType) {
548            case DataBuffer.TYPE_BYTE:
549                data = new DataBufferByte(size, numBanks);
550                break;
551            case DataBuffer.TYPE_SHORT:
552                data = new DataBufferShort(size, numBanks);
553                break;
554            case DataBuffer.TYPE_USHORT:
555                data = new DataBufferUShort(size, numBanks);
556                break;
557            case DataBuffer.TYPE_INT:
558                data = new DataBufferInt(size, numBanks);
559                break;
560            case DataBuffer.TYPE_FLOAT:
561                data = new DataBufferFloat(size, numBanks);
562                break;
563            case DataBuffer.TYPE_DOUBLE:
564                data = new DataBufferDouble(size, numBanks);
565                break;
566        }
567
568        return data;
569
570    }
571
572    /**
573     * Gets the offset of the specified band of the specified pixel.
574     *
575     * @param x
576     *            the X coordinate of the pixel.
577     * @param y
578     *            the Y coordinate of the pixel.
579     * @param b
580     *            the band.
581     * @return the offset of the specified band of the specified pixel.
582     */
583    public int getOffset(int x, int y, int b) {
584        return y * scanlineStride + x * pixelStride + bandOffsets[b];
585    }
586
587    /**
588     * Gets the offset of the first band of the specified pixel.
589     *
590     * @param x
591     *            the X coordinate of pixel.
592     * @param y
593     *            the Y coordinate of pixel.
594     * @return the offset of the first band of the specified pixel.
595     */
596    public int getOffset(int x, int y) {
597        return y * scanlineStride + x * pixelStride + bandOffsets[0];
598    }
599
600    @Override
601    public final int getSampleSize(int band) {
602        return DataBuffer.getDataTypeSize(dataType);
603    }
604
605    @Override
606    public final int[] getSampleSize() {
607        int sampleSizes[] = new int[numBands];
608        int size = DataBuffer.getDataTypeSize(dataType);
609
610        for (int i = 0; i < numBands; i++) {
611            sampleSizes[i] = size;
612        }
613        return sampleSizes;
614    }
615
616    /**
617     * Gets an array of bank indices corresponding to this ComponentSampleModel.
618     *
619     * @return the array of bank indices.
620     */
621    public final int[] getBankIndices() {
622        return bankIndices.clone();
623    }
624
625    /**
626     * Gets an array of the band offsets corresponding to this
627     * ComponentSampleModel.
628     *
629     * @return the array of band offsets.
630     */
631    public final int[] getBandOffsets() {
632        return bandOffsets.clone();
633    }
634
635    /**
636     * Gets a hash code of this ComponentSampleModel object.
637     *
638     * @return a hash code of this ComponentSampleModel object.
639     */
640    @Override
641    public int hashCode() {
642        int hash = 0;
643        int tmp = 0;
644
645        hash = width;
646        tmp = hash >>> 24;
647        hash <<= 8;
648        hash |= tmp;
649        hash ^= height;
650        tmp = hash >>> 24;
651        hash <<= 8;
652        hash |= tmp;
653        hash ^= numBands;
654        tmp = hash >>> 24;
655        hash <<= 8;
656        hash |= tmp;
657        hash ^= dataType;
658        tmp = hash >>> 24;
659        hash <<= 8;
660        hash |= tmp;
661        for (int element : bandOffsets) {
662            hash ^= element;
663            tmp = hash >>> 24;
664            hash <<= 8;
665            hash |= tmp;
666        }
667        for (int element : bankIndices) {
668            hash ^= element;
669            tmp = hash >>> 24;
670            hash <<= 8;
671            hash |= tmp;
672        }
673        hash ^= pixelStride;
674        tmp = hash >>> 24;
675        hash <<= 8;
676        hash |= tmp;
677
678        hash ^= scanlineStride;
679        return hash;
680    }
681
682    /**
683     * Gets the scanline stride of this ComponentSampleModel.
684     *
685     * @return the scanline stride of this ComponentSampleModel.
686     */
687    public final int getScanlineStride() {
688        return scanlineStride;
689    }
690
691    /**
692     * Gets the pixel stride.
693     *
694     * @return the pixel stride.
695     */
696    public final int getPixelStride() {
697        return pixelStride;
698    }
699
700    @Override
701    public final int getNumDataElements() {
702        return numBands;
703    }
704
705}
706