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 */
21package java.awt.image;
22
23import java.awt.Image;
24import java.util.Hashtable;
25
26import org.apache.harmony.awt.internal.nls.Messages;
27
28public class PixelGrabber implements ImageConsumer {
29
30    int width;
31    int height;
32    int X;
33    int Y;
34    int offset;
35    int scanline;
36    ImageProducer producer;
37
38    byte bData[];
39    int iData[];
40    ColorModel cm;
41
42    private int grabberStatus;
43    private int dataType;
44    private boolean isGrabbing;
45    private boolean isRGB;
46
47
48    private static final int DATA_TYPE_BYTE = 0;
49    private static final int DATA_TYPE_INT = 1;
50    private static final int DATA_TYPE_UNDEFINED = 2;
51
52    private static final int ALL_BITS = (ImageObserver.FRAMEBITS |
53            ImageObserver.ALLBITS);
54
55    private static final int GRABBING_STOP = ALL_BITS | ImageObserver.ERROR;
56
57
58
59    public PixelGrabber(ImageProducer ip, int x, int y, int w, int h, int[] pix,
60            int off, int scansize) {
61        initialize(ip, x, y, w, h, pix, off, scansize, true);
62    }
63
64    public PixelGrabber(Image img, int x, int y, int w, int h, int[] pix,
65            int off, int scansize) {
66        initialize(img.getSource(), x, y, w, h, pix, off, scansize, true);
67    }
68
69    public PixelGrabber(Image img, int x, int y, int w, int h, boolean forceRGB) {
70        initialize(img.getSource(), x, y, w, h, null, 0, 0, forceRGB);
71    }
72
73    public void setProperties(Hashtable<?, ?> props) {
74        return;
75    }
76
77    public synchronized Object getPixels() {
78        switch(dataType){
79        case DATA_TYPE_BYTE:
80            return bData;
81        case DATA_TYPE_INT:
82            return iData;
83        default:
84            return null;
85        }
86    }
87
88    public void setColorModel(ColorModel model) {
89        return;
90    }
91
92    public void setPixels(int srcX, int srcY, int srcW, int srcH,
93            ColorModel model, byte[] pixels, int srcOff, int srcScan) {
94        if(srcY < Y){
95            int delta = Y - srcY;
96            if(delta >= height) {
97                return;
98            }
99            srcY += delta;
100            srcH -= delta;
101            srcOff += srcScan * delta;
102        }
103
104        if(srcY + srcH > Y + height){
105            srcH = Y + height - srcY;
106            if(srcH <= 0) {
107                return;
108            }
109        }
110
111        if(srcX < X){
112            int delta = X - srcX;
113            if(delta >= width) {
114                return;
115            }
116            srcW -= delta;
117            srcX += delta;
118            srcOff += delta;
119        }
120
121        if(srcX + srcW > X + width){
122            srcW = X + width - srcX;
123            if(srcW <= 0) {
124                return;
125            }
126        }
127        if(scanline == 0) {
128            scanline = width;
129        }
130        int realOff = offset + (srcY - Y) * scanline + (srcX - X);
131        switch(dataType){
132        case DATA_TYPE_UNDEFINED:
133            cm = model;
134            if(model != ColorModel.getRGBdefault()){
135                bData = new byte[width * height];
136                isRGB = false;
137                dataType = DATA_TYPE_BYTE;
138            }else{
139                iData = new int[width * height];
140                isRGB = true;
141                dataType = DATA_TYPE_INT;
142            }
143        case DATA_TYPE_BYTE:
144            if(!isRGB && cm == model){
145                for(int y = 0; y < srcH; y++){
146                    System.arraycopy(pixels, srcOff, bData, realOff, srcW);
147                    srcOff += srcScan;
148                    realOff += scanline;
149                }
150                break;
151            }
152            forceToRGB();
153        case DATA_TYPE_INT:
154            for(int y = 0; y < srcH; y++){
155                for(int x = 0; x < srcW; x++){
156                    iData[realOff + x] = cm.getRGB(pixels[srcOff + x] & 0xff);
157                }
158                srcOff += srcScan;
159                realOff += scanline;
160            }
161        }
162
163        return;
164    }
165
166    public void setPixels(int srcX, int srcY, int srcW, int srcH,
167            ColorModel model, int[] pixels, int srcOff, int srcScan) {
168
169        if(srcY < Y){
170            int delta = Y - srcY;
171            if(delta >= height) {
172                return;
173            }
174            srcY += delta;
175            srcH -= delta;
176            srcOff += srcScan * delta;
177        }
178
179        if(srcY + srcH > Y + height){
180            srcH = Y + height - srcY;
181            if(srcH <= 0) {
182                return;
183            }
184        }
185
186        if(srcX < X){
187            int delta = X - srcX;
188            if(delta >= width) {
189                return;
190            }
191            srcW -= delta;
192            srcX += delta;
193            srcOff += delta;
194        }
195
196        if(srcX + srcW > X + width){
197            srcW = X + width - srcX;
198            if(srcW <= 0) {
199                return;
200            }
201        }
202        if(scanline == 0) {
203            scanline = width;
204        }
205        int realOff = offset + (srcY - Y) * scanline + (srcX - X);
206
207        int mask = 0xFF;
208
209        switch(dataType){
210        case DATA_TYPE_UNDEFINED:
211            cm = model;
212            iData = new int[width * height];
213            dataType = DATA_TYPE_INT;
214            isRGB = (cm == ColorModel.getRGBdefault());
215
216        case DATA_TYPE_INT:
217            if(cm == model){
218                for(int y = 0; y < srcH; y++){
219                    System.arraycopy(pixels, srcOff, iData, realOff, srcW);
220                    srcOff += srcScan;
221                    realOff += scanline;
222                }
223                break;
224            }
225            mask = 0xFFFFFFFF;
226
227        case DATA_TYPE_BYTE:
228            forceToRGB();
229            for(int y = 0; y < srcH; y++){
230                for(int x = 0; x < srcW; x++){
231                    iData[realOff+x] = cm.getRGB(pixels[srcOff+x] & mask);
232                }
233                srcOff += srcScan;
234                realOff += scanline;
235            }
236        }
237    }
238
239    public synchronized ColorModel getColorModel() {
240        return cm;
241    }
242
243    public synchronized boolean grabPixels(long ms)
244    throws InterruptedException {
245        if((grabberStatus & GRABBING_STOP) != 0){
246            return ((grabberStatus & ALL_BITS) != 0);
247        }
248
249        long start = System.currentTimeMillis();
250
251        if(!isGrabbing){
252            isGrabbing = true;
253            grabberStatus &= ~ImageObserver.ABORT;
254            producer.startProduction(this);
255        }
256        while((grabberStatus & GRABBING_STOP) == 0){
257            if(ms != 0){
258                ms = start + ms - System.currentTimeMillis();
259                if(ms <= 0) {
260                    break;
261                }
262            }
263            wait(ms);
264        }
265
266        return ((grabberStatus & ALL_BITS) != 0);
267    }
268
269    public void setDimensions(int w, int h) {
270        if(width < 0) {
271            width = w - X;
272        }
273        if(height < 0) {
274            height = h - Y;
275        }
276
277        grabberStatus |= ImageObserver.WIDTH | ImageObserver.HEIGHT;
278
279        if(width <=0 || height <=0){
280            imageComplete(STATICIMAGEDONE);
281            return;
282        }
283
284        if(isRGB && dataType == DATA_TYPE_UNDEFINED){
285            iData = new int[width * height];
286            dataType = DATA_TYPE_INT;
287            scanline = width;
288        }
289    }
290
291    public void setHints(int hints) {
292        return;
293    }
294
295    public synchronized void imageComplete(int status) {
296        switch(status){
297        case IMAGEABORTED:
298            grabberStatus |= ImageObserver.ABORT;
299            break;
300        case IMAGEERROR:
301            grabberStatus |= ImageObserver.ERROR | ImageObserver.ABORT;
302            break;
303        case SINGLEFRAMEDONE:
304            grabberStatus |= ImageObserver.FRAMEBITS;
305            break;
306        case STATICIMAGEDONE:
307            grabberStatus |= ImageObserver.ALLBITS;
308            break;
309        default:
310            // awt.26A=Incorrect ImageConsumer completion status
311            throw new IllegalArgumentException(Messages.getString("awt.26A")); //$NON-NLS-1$
312        }
313        isGrabbing = false;
314        producer.removeConsumer(this);
315        notifyAll();
316    }
317
318    public boolean grabPixels() throws InterruptedException {
319        return grabPixels(0);
320    }
321
322    public synchronized void startGrabbing() {
323        if((grabberStatus & GRABBING_STOP) != 0){
324            return;
325        }
326        if(!isGrabbing){
327            isGrabbing = true;
328            grabberStatus &= ~ImageObserver.ABORT;
329            producer.startProduction(this);
330        }
331    }
332
333    public synchronized void abortGrabbing() {
334        imageComplete(IMAGEABORTED);
335    }
336
337    public synchronized int status() {
338        return grabberStatus;
339    }
340
341    public synchronized int getWidth() {
342        if(width < 0) {
343            return -1;
344        }
345        return width;
346    }
347
348    public synchronized int getStatus() {
349        return grabberStatus;
350    }
351
352    public synchronized int getHeight() {
353        if(height < 0) {
354            return -1;
355        }
356        return height;
357    }
358
359    private void initialize(ImageProducer ip, int x, int y, int w, int h,
360            int pixels[], int off, int scansize, boolean forceRGB){
361
362        producer = ip;
363        X = x;
364        Y = y;
365        width = w;
366        height = h;
367        iData = pixels;
368        dataType = (pixels == null) ? DATA_TYPE_UNDEFINED : DATA_TYPE_INT;
369        offset = off;
370        scanline = scansize;
371        if(forceRGB){
372            cm = ColorModel.getRGBdefault();
373            isRGB = true;
374        }
375    }
376
377    /**
378     * Force pixels to INT RGB mode
379     */
380    private void forceToRGB(){
381        if (isRGB)
382            return;
383
384        switch(dataType){
385        case DATA_TYPE_BYTE:
386            iData = new int[width * height];
387            for(int i = 0; i < iData.length; i++){
388                iData[i] = cm.getRGB(bData[i] & 0xff);
389            }
390            dataType = DATA_TYPE_INT;
391            bData = null;
392            break;
393
394        case DATA_TYPE_INT:
395            int buff[] = new int[width * height];
396            for(int i = 0; i < iData.length; i++){
397                buff[i] = cm.getRGB(iData[i]);
398            }
399            iData = buff;
400            break;
401        }
402        offset = 0;
403        scanline = width;
404        cm = ColorModel.getRGBdefault();
405        isRGB = true;
406    }
407
408}
409