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 * Created on 18.11.2005
21 *
22 */
23package org.apache.harmony.awt.gl.render;
24
25import java.awt.AlphaComposite;
26import java.awt.Color;
27import java.awt.Composite;
28import java.awt.CompositeContext;
29import java.awt.Rectangle;
30import java.awt.geom.AffineTransform;
31import java.awt.geom.NoninvertibleTransformException;
32import java.awt.geom.Rectangle2D;
33import java.awt.image.ColorModel;
34import java.awt.image.Raster;
35import java.awt.image.WritableRaster;
36
37import org.apache.harmony.awt.gl.MultiRectArea;
38import org.apache.harmony.awt.gl.Surface;
39import org.apache.harmony.awt.gl.XORComposite;
40import org.apache.harmony.awt.internal.nls.Messages;
41
42/**
43 * Java implenetation of the Blitter interface. Using when we can't
44 * draw images natively.
45 */
46public class JavaBlitter implements Blitter {
47
48    /**
49     * Instead of multiplication and division we are using values from
50     * Lookup tables.
51     */
52    static byte mulLUT[][]; // Lookup table for multiplication
53    static byte divLUT[][]; // Lookup table for division
54
55    static{
56        mulLUT = new byte[256][256];
57        for(int i = 0; i < 256; i++){
58            for(int j = 0; j < 256; j++){
59                mulLUT[i][j] = (byte)((float)(i * j)/255 + 0.5f);
60            }
61        }
62        divLUT = new byte[256][256];
63        for(int i = 1; i < 256; i++){
64            for(int j = 0; j < i; j++){
65                divLUT[i][j] = (byte)(((float)j / 255) / ((float)i/ 255) * 255 + 0.5f);
66            }
67            for(int j = i; j < 256; j++){
68                divLUT[i][j] = (byte)255;
69            }
70        }
71    }
72
73    final static int AlphaCompositeMode = 1;
74    final static int XORMode = 2;
75
76    final static JavaBlitter inst = new JavaBlitter();
77
78    public static JavaBlitter getInstance(){
79        return inst;
80    }
81
82    public void blit(int srcX, int srcY, Surface srcSurf, int dstX, int dstY,
83            Surface dstSurf, int width, int height, AffineTransform sysxform,
84            AffineTransform xform, Composite comp, Color bgcolor,
85            MultiRectArea clip) {
86
87        if(xform == null){
88            blit(srcX, srcY, srcSurf, dstX, dstY, dstSurf, width, height,
89                    sysxform, comp, bgcolor, clip);
90        }else{
91            double scaleX = xform.getScaleX();
92            double scaleY = xform.getScaleY();
93            double scaledX = dstX / scaleX;
94            double scaledY = dstY / scaleY;
95            AffineTransform at = new AffineTransform();
96            at.setToTranslation(scaledX, scaledY);
97            xform.concatenate(at);
98            sysxform.concatenate(xform);
99            blit(srcX, srcY, srcSurf, 0, 0, dstSurf, width, height,
100                    sysxform, comp, bgcolor, clip);
101        }
102
103    }
104
105    public void blit(int srcX, int srcY, Surface srcSurf, int dstX, int dstY,
106            Surface dstSurf, int width, int height, AffineTransform sysxform,
107            Composite comp, Color bgcolor, MultiRectArea clip) {
108
109        if(sysxform == null) {
110            sysxform = new AffineTransform();
111        }
112        int type = sysxform.getType();
113        switch(type){
114            case AffineTransform.TYPE_TRANSLATION:
115                dstX += sysxform.getTranslateX();
116                dstY += sysxform.getTranslateY();
117            case AffineTransform.TYPE_IDENTITY:
118                 blit(srcX, srcY, srcSurf, dstX, dstY, dstSurf,
119                        width, height, comp, bgcolor, clip);
120                break;
121            default:
122                int srcW = srcSurf.getWidth();
123                int srcH = srcSurf.getHeight();
124
125                int w = srcX + width < srcW ? width : srcW - srcX;
126                int h = srcY + height < srcH ? height : srcH - srcY;
127
128                ColorModel srcCM = srcSurf.getColorModel();
129                Raster srcR = srcSurf.getRaster().createChild(srcX, srcY,
130                        w, h, 0, 0, null);
131
132                ColorModel dstCM = dstSurf.getColorModel();
133                WritableRaster dstR = dstSurf.getRaster();
134
135                transformedBlit(srcCM, srcR, 0, 0, dstCM, dstR, dstX, dstY, w, h,
136                        sysxform, comp, bgcolor, clip);
137
138        }
139    }
140
141    public void blit(int srcX, int srcY, Surface srcSurf, int dstX, int dstY,
142            Surface dstSurf, int width, int height, Composite comp,
143            Color bgcolor, MultiRectArea clip) {
144
145        javaBlt(srcX, srcY, srcSurf.getWidth(), srcSurf.getHeight(),
146                srcSurf.getColorModel(), srcSurf.getRaster(), dstX, dstY,
147                dstSurf.getWidth(), dstSurf.getHeight(),
148                dstSurf.getColorModel(), dstSurf.getRaster(),
149                width, height, comp, bgcolor, clip);
150
151    }
152    public void javaBlt(int srcX, int srcY, int srcW, int srcH,
153            ColorModel srcCM, Raster srcRast, int dstX, int dstY,
154            int dstW, int dstH, ColorModel dstCM, WritableRaster dstRast,
155            int width, int height, Composite comp, Color bgcolor,
156            MultiRectArea clip){
157
158        int srcX2 = srcW - 1;
159        int srcY2 = srcH - 1;
160        int dstX2 = dstW - 1;
161        int dstY2 = dstH - 1;
162
163        if(srcX < 0){
164            width += srcX;
165            srcX = 0;
166        }
167        if(srcY < 0){
168            height += srcY;
169            srcY = 0;
170        }
171
172        if(dstX < 0){
173            width += dstX;
174            srcX -= dstX;
175            dstX = 0;
176        }
177        if(dstY < 0){
178            height += dstY;
179            srcY -= dstY;
180            dstY = 0;
181        }
182
183        if(srcX > srcX2 || srcY > srcY2) {
184            return;
185        }
186        if(dstX > dstX2 || dstY > dstY2) {
187            return;
188        }
189
190        if(srcX + width > srcX2) {
191            width = srcX2 - srcX + 1;
192        }
193        if(srcY + height > srcY2) {
194            height = srcY2 - srcY + 1;
195        }
196        if(dstX + width > dstX2) {
197            width = dstX2 - dstX + 1;
198        }
199        if(dstY + height > dstY2) {
200            height = dstY2 - dstY + 1;
201        }
202
203        if(width <= 0 || height <= 0) {
204            return;
205        }
206
207        int clipRects[];
208        if(clip != null) {
209            clipRects = clip.rect;
210        } else {
211            clipRects = new int[]{5, 0, 0, dstW - 1, dstH - 1};
212        }
213
214        boolean isAlphaComp = false;
215        int rule = 0;
216        float alpha = 0;
217        boolean isXORComp = false;
218        Color xorcolor = null;
219        CompositeContext cont = null;
220
221        if(comp instanceof AlphaComposite){
222            isAlphaComp = true;
223            AlphaComposite ac = (AlphaComposite) comp;
224            rule = ac.getRule();
225            alpha = ac.getAlpha();
226        }else if(comp instanceof XORComposite){
227            isXORComp = true;
228            XORComposite xcomp = (XORComposite) comp;
229            xorcolor = xcomp.getXORColor();
230        }else{
231            cont = comp.createContext(srcCM, dstCM, null);
232        }
233
234        for(int i = 1; i < clipRects[0]; i += 4){
235            int _sx = srcX;
236            int _sy = srcY;
237
238            int _dx = dstX;
239            int _dy = dstY;
240
241            int _w = width;
242            int _h = height;
243
244            int cx = clipRects[i];          // Clipping left top X
245            int cy = clipRects[i + 1];      // Clipping left top Y
246            int cx2 = clipRects[i + 2];     // Clipping right bottom X
247            int cy2 = clipRects[i + 3];     // Clipping right bottom Y
248
249            if(_dx > cx2 || _dy > cy2 || dstX2 < cx || dstY2 < cy) {
250                continue;
251            }
252
253            if(cx > _dx){
254                int shx = cx - _dx;
255                _w -= shx;
256                _dx = cx;
257                _sx += shx;
258            }
259
260            if(cy > _dy){
261                int shy = cy - _dy;
262                _h -= shy;
263                _dy = cy;
264                _sy += shy;
265            }
266
267            if(_dx + _w > cx2 + 1){
268                _w = cx2 - _dx + 1;
269            }
270
271            if(_dy + _h > cy2 + 1){
272                _h = cy2 - _dy + 1;
273            }
274
275            if(_sx > srcX2 || _sy > srcY2) {
276                continue;
277            }
278
279            if(isAlphaComp){
280                alphaCompose(_sx, _sy, srcCM, srcRast, _dx, _dy,
281                        dstCM, dstRast, _w, _h, rule, alpha, bgcolor);
282            }else if(isXORComp){
283                xorCompose(_sx, _sy, srcCM, srcRast, _dx, _dy,
284                        dstCM, dstRast, _w, _h, xorcolor);
285            }else{
286                Raster sr = srcRast.createChild(_sx, _sy, _w, _h, 0, 0, null);
287                WritableRaster dr = dstRast.createWritableChild(_dx, _dy,
288                        _w, _h, 0, 0, null);
289                cont.compose(sr, dr, dr);
290            }
291        }
292    }
293
294    void alphaCompose(int srcX, int srcY, ColorModel srcCM, Raster srcRast,
295            int dstX, int dstY, ColorModel dstCM, WritableRaster dstRast,
296            int width, int height, int rule, float alpha, Color bgcolor){
297
298        Object srcPixel, dstPixel;
299        int srcConstAllpha = (int)(alpha * 255 + 0.5f);
300        int srcRGB, dstRGB = 0;
301
302        if(bgcolor != null){
303            dstRGB = bgcolor.getRGB();
304        }
305
306        for(int sy = srcY, dy = dstY, srcYMax = srcY + height; sy < srcYMax; sy++, dy++){
307            for(int sx = srcX, dx = dstX, srcXMax = srcX + width; sx < srcXMax; sx++, dx++){
308                srcPixel = srcRast.getDataElements(sx, sy, null);
309                srcRGB = srcCM.getRGB(srcPixel);
310                if(bgcolor == null){
311                    dstPixel = dstRast.getDataElements(dx, dy, null);
312                    dstRGB = dstCM.getRGB(dstPixel);
313                }
314
315                dstRGB = compose(srcRGB, srcCM.isAlphaPremultiplied(),
316                        dstRGB, dstCM.hasAlpha(), dstCM.isAlphaPremultiplied(),
317                        rule, srcConstAllpha);
318
319                dstPixel = dstCM.getDataElements(dstRGB, null);
320                dstRast.setDataElements(dx,dy,dstPixel);
321            }
322        }
323    }
324
325    void xorCompose(int srcX, int srcY, ColorModel srcCM, Raster srcRast,
326            int dstX, int dstY, ColorModel dstCM, WritableRaster dstRast,
327            int width, int height, Color xorcolor){
328
329        Object srcPixel, dstPixel;
330        int xorRGB = xorcolor.getRGB();
331        int srcRGB, dstRGB;
332
333        for(int sy = srcY, dy = dstY, srcYMax = srcY + height; sy < srcYMax; sy++, dy++){
334            for(int sx = srcX, dx = dstX, srcXMax = srcX + width; sx < srcXMax; sx++, dx++){
335                srcPixel = srcRast.getDataElements(sx, sy, null);
336                dstPixel = dstRast.getDataElements(dx, dy, null);
337
338                srcRGB = srcCM.getRGB(srcPixel);
339                dstRGB = dstCM.getRGB(dstPixel);
340                dstRGB = srcRGB ^ xorRGB ^ dstRGB;
341
342                dstRGB = 0xff000000 | dstRGB;
343                dstPixel = dstCM.getDataElements(dstRGB, dstPixel);
344                dstRast.setDataElements(dx,dy,dstPixel);
345
346            }
347        }
348
349    }
350
351    private void transformedBlit(ColorModel srcCM, Raster srcR, int srcX, int srcY,
352            ColorModel dstCM, WritableRaster dstR, int dstX, int dstY,
353            int width, int height, AffineTransform at, Composite comp,
354            Color bgcolor,MultiRectArea clip) {
355
356        Rectangle srcBounds = new Rectangle(width, height);
357        Rectangle dstBlitBounds = new Rectangle(dstX, dstY, srcR.getWidth(), srcR.getHeight());
358
359        Rectangle transSrcBounds = getBounds2D(at, srcBounds).getBounds();
360        Rectangle transDstBlitBounds = getBounds2D(at, dstBlitBounds).getBounds();
361
362        int translateX = transDstBlitBounds.x - transSrcBounds.x;
363        int translateY = transDstBlitBounds.y - transSrcBounds.y;
364
365        AffineTransform inv = null;
366        try {
367             inv = at.createInverse();
368        } catch (NoninvertibleTransformException e) {
369            return;
370        }
371
372        double[] m = new double[6];
373        inv.getMatrix(m);
374
375        int clipRects[];
376        if(clip != null) {
377            clipRects = clip.rect;
378        } else {
379            clipRects = new int[]{5, 0, 0, dstR.getWidth(), dstR.getHeight()};
380        }
381
382        int compType = 0;
383        int srcConstAlpha = 0;
384        int rule = 0;
385        int bgRGB = bgcolor == null ? 0 : bgcolor.getRGB();
386        int srcRGB = 0, dstRGB = 0;
387        Object srcVal = null, dstVal = null;
388        if(comp instanceof AlphaComposite){
389            compType = AlphaCompositeMode;
390            AlphaComposite ac = (AlphaComposite) comp;
391            rule = ac.getRule();
392            srcConstAlpha = (int)(ac.getAlpha() * 255 + 0.5f);
393        }else if(comp instanceof XORComposite){
394            compType = XORMode;
395            XORComposite xor = (XORComposite) comp;
396            bgRGB = xor.getXORColor().getRGB();
397        }
398
399        for(int i = 1; i < clipRects[0]; i += 4){
400            Rectangle dstBounds = new Rectangle(clipRects[i], clipRects[i + 1], 0, 0);
401            dstBounds.add(clipRects[i + 2] + 1, clipRects[i + 1]);
402            dstBounds.add(clipRects[i + 2] + 1, clipRects[i + 3] + 1);
403            dstBounds.add(clipRects[i], clipRects[i + 3] + 1);
404
405            Rectangle bounds = dstBounds.intersection(transDstBlitBounds);
406
407            int minSrcX = srcBounds.x;
408            int minSrcY = srcBounds.y;
409            int maxSrcX = minSrcX + srcBounds.width;
410            int maxSrcY = minSrcY + srcBounds.height;
411
412            int minX = bounds.x;
413            int minY = bounds.y;
414            int maxX = minX + bounds.width;
415            int maxY = minY + bounds.height;
416
417            int hx = (int)((m[0] * 256) + 0.5);
418            int hy = (int)((m[1] * 256) + 0.5);
419            int vx = (int)((m[2] * 256) + 0.5);
420            int vy = (int)((m[3] * 256) + 0.5);
421            int sx = (int)((m[4] + m[0] * (bounds.x - translateX) + m[2] * (bounds.y - translateY)) * 256 + 0.5);
422            int sy = (int)((m[5] + m[1] * (bounds.x - translateX) + m[3] * (bounds.y - translateY)) * 256 + 0.5);
423
424            vx -= hx * bounds.width;
425            vy -= hy * bounds.width;
426
427            for(int y = minY; y < maxY; y++) {
428                for(int x = minX; x < maxX; x++) {
429                    int px = sx >> 8;
430                    int py = sy >> 8;
431                    if (px >= minSrcX && py >= minSrcY && px < maxSrcX && py < maxSrcY) {
432                        switch(compType){
433                            case AlphaCompositeMode:
434                                srcVal = srcR.getDataElements(px , py , null);
435                                srcRGB = srcCM.getRGB(srcVal);
436                                if(bgcolor != null){
437                                    dstRGB = bgRGB;
438                                }else{
439                                    dstVal = dstR.getDataElements(x, y, null);
440                                    dstRGB = dstCM.getRGB(dstVal);
441                                }
442                                dstRGB = compose(srcRGB, srcCM.isAlphaPremultiplied(),
443                                        dstRGB, dstCM.hasAlpha(), dstCM.isAlphaPremultiplied(),
444                                        rule, srcConstAlpha);
445                                dstVal = dstCM.getDataElements(dstRGB, null);
446                                dstR.setDataElements(x, y, dstVal);
447                                break;
448
449                            case XORMode:
450                                srcVal = srcR.getDataElements(px , py , null);
451                                srcRGB = srcCM.getRGB(srcVal);
452                                dstVal = dstR.getDataElements(x, y, null);
453                                dstRGB = dstCM.getRGB(dstVal);
454                                dstRGB = srcRGB ^ bgRGB;
455
456                                dstRGB = 0xff000000 | dstRGB;
457                                dstVal = dstCM.getDataElements(dstRGB, null);
458                                dstR.setDataElements(x, y, dstVal);
459                                break;
460
461                            default:
462                                // awt.37=Unknown  composite type {0}
463                                throw new IllegalArgumentException(Messages.getString("awt.37", //$NON-NLS-1$
464                                        comp.getClass()));
465                        }
466                    }
467                    sx += hx;
468                    sy += hy;
469                }
470                sx += vx;
471                sy += vy;
472            }
473        }
474
475    }
476
477    private Rectangle2D getBounds2D(AffineTransform at, Rectangle r) {
478        int x = r.x;
479        int y = r.y;
480        int width = r.width;
481        int height = r.height;
482
483        float[] corners = {
484            x, y,
485            x + width, y,
486            x + width, y + height,
487            x, y + height
488        };
489
490        at.transform(corners, 0, corners, 0, 4);
491
492        Rectangle2D.Float bounds = new Rectangle2D.Float(corners[0], corners[1], 0 , 0);
493        bounds.add(corners[2], corners[3]);
494        bounds.add(corners[4], corners[5]);
495        bounds.add(corners[6], corners[7]);
496
497        return bounds;
498    }
499
500    private int compose(int srcRGB, boolean isSrcAlphaPre,
501            int dstRGB, boolean dstHasAlpha, boolean isDstAlphaPre,
502            int rule, int srcConstAlpha){
503
504        int sa, sr, sg, sb, da, dr, dg, db;
505
506        sa = (srcRGB >> 24) & 0xff;
507        sr = (srcRGB >> 16) & 0xff;
508        sg = (srcRGB >> 8) & 0xff;
509        sb = srcRGB & 0xff;
510
511        if(isSrcAlphaPre){
512            sa = mulLUT[srcConstAlpha][sa] & 0xff;
513            sr = mulLUT[srcConstAlpha][sr] & 0xff;
514            sg = mulLUT[srcConstAlpha][sg] & 0xff;
515            sb = mulLUT[srcConstAlpha][sb] & 0xff;
516        }else{
517            sa = mulLUT[srcConstAlpha][sa] & 0xff;
518            sr = mulLUT[sa][sr] & 0xff;
519            sg = mulLUT[sa][sg] & 0xff;
520            sb = mulLUT[sa][sb] & 0xff;
521        }
522
523        da = (dstRGB >> 24) & 0xff;
524        dr = (dstRGB >> 16) & 0xff;
525        dg = (dstRGB >> 8) & 0xff;
526        db = dstRGB & 0xff;
527
528        if(!isDstAlphaPre){
529            dr = mulLUT[da][dr] & 0xff;
530            dg = mulLUT[da][dg] & 0xff;
531            db = mulLUT[da][db] & 0xff;
532        }
533
534        int Fs = 0;
535        int Fd = 0;
536        switch(rule){
537        case AlphaComposite.CLEAR:
538            break;
539
540        case AlphaComposite.DST:
541            Fd = 255;
542            break;
543
544        case AlphaComposite.DST_ATOP:
545            Fs = 255 - da;
546            Fd = sa;
547            break;
548
549        case AlphaComposite.DST_IN:
550            Fd = sa;
551            break;
552
553        case AlphaComposite.DST_OUT:
554            Fd = 255 - sa;
555            break;
556
557        case AlphaComposite.DST_OVER:
558            Fs = 255 - da;
559            Fd = 255;
560            break;
561
562        case AlphaComposite.SRC:
563            Fs = 255;
564            break;
565
566        case AlphaComposite.SRC_ATOP:
567            Fs = da;
568            Fd = 255 - sa;
569            break;
570
571        case AlphaComposite.SRC_IN:
572            Fs = da;
573            break;
574
575        case AlphaComposite.SRC_OUT:
576            Fs = 255 - da;
577            break;
578
579        case AlphaComposite.SRC_OVER:
580            Fs = 255;
581            Fd = 255 - sa;
582            break;
583
584        case AlphaComposite.XOR:
585            Fs = 255 - da;
586            Fd = 255 - sa;
587            break;
588        }
589        dr = (mulLUT[sr][Fs] & 0xff) + (mulLUT[dr][Fd] & 0xff);
590        dg = (mulLUT[sg][Fs] & 0xff) + (mulLUT[dg][Fd] & 0xff);
591        db = (mulLUT[sb][Fs] & 0xff) + (mulLUT[db][Fd] & 0xff);
592
593        da = (mulLUT[sa][Fs] & 0xff) + (mulLUT[da][Fd] & 0xff);
594
595        if(!isDstAlphaPre){
596            if(da != 255){
597                dr = divLUT[da][dr] & 0xff;
598                dg = divLUT[da][dg] & 0xff;
599                db = divLUT[da][db] & 0xff;
600            }
601        }
602        if(!dstHasAlpha) {
603            da = 0xff;
604        }
605        dstRGB = (da << 24) | (dr << 16) | (dg << 8) | db;
606
607        return dstRGB;
608
609    }
610
611}
612