001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017: /**
018: * @author Igor V. Stolyarov
019: * @version $Revision$
020: * Created on 18.11.2005
021: *
022: */package org.apache.harmony.awt.gl.render;
023:
024: import java.awt.AlphaComposite;
025: import java.awt.Color;
026: import java.awt.Composite;
027: import java.awt.CompositeContext;
028: import java.awt.Rectangle;
029: import java.awt.geom.AffineTransform;
030: import java.awt.geom.NoninvertibleTransformException;
031: import java.awt.geom.Rectangle2D;
032: import java.awt.image.ColorModel;
033: import java.awt.image.Raster;
034: import java.awt.image.WritableRaster;
035:
036: import org.apache.harmony.awt.gl.ImageSurface;
037: import org.apache.harmony.awt.gl.MultiRectArea;
038: import org.apache.harmony.awt.gl.Surface;
039: import org.apache.harmony.awt.gl.XORComposite;
040: import org.apache.harmony.awt.internal.nls.Messages;
041:
042: /**
043: * Java implenetation of the Blitter interface. Using when we can't
044: * draw images natively.
045: */
046: public class JavaBlitter implements Blitter {
047:
048: /**
049: * Instead of multiplication and division we are using values from
050: * Lookup tables.
051: */
052: static byte mulLUT[][]; // Lookup table for multiplication
053: static byte divLUT[][]; // Lookup table for division
054:
055: static {
056: mulLUT = new byte[256][256];
057: for (int i = 0; i < 256; i++) {
058: for (int j = 0; j < 256; j++) {
059: mulLUT[i][j] = (byte) ((float) (i * j) / 255 + 0.5f);
060: }
061: }
062: divLUT = new byte[256][256];
063: for (int i = 1; i < 256; i++) {
064: for (int j = 0; j < i; j++) {
065: divLUT[i][j] = (byte) (((float) j / 255)
066: / ((float) i / 255) * 255 + 0.5f);
067: }
068: for (int j = i; j < 256; j++) {
069: divLUT[i][j] = (byte) 255;
070: }
071: }
072: }
073:
074: final static int AlphaCompositeMode = 1;
075: final static int XORMode = 2;
076:
077: final static JavaBlitter inst = new JavaBlitter();
078:
079: public static JavaBlitter getInstance() {
080: return inst;
081: }
082:
083: public void blit(int srcX, int srcY, Surface srcSurf, int dstX,
084: int dstY, Surface dstSurf, int width, int height,
085: AffineTransform sysxform, AffineTransform xform,
086: Composite comp, Color bgcolor, MultiRectArea clip) {
087:
088: if (xform == null) {
089: blit(srcX, srcY, srcSurf, dstX, dstY, dstSurf, width,
090: height, sysxform, comp, bgcolor, clip);
091: } else {
092: double scaleX = xform.getScaleX();
093: double scaleY = xform.getScaleY();
094: double scaledX = dstX / scaleX;
095: double scaledY = dstY / scaleY;
096: AffineTransform at = new AffineTransform();
097: at.setToTranslation(scaledX, scaledY);
098: xform.concatenate(at);
099: sysxform.concatenate(xform);
100: blit(srcX, srcY, srcSurf, 0, 0, dstSurf, width, height,
101: sysxform, comp, bgcolor, clip);
102: }
103:
104: }
105:
106: public void blit(int srcX, int srcY, Surface srcSurf, int dstX,
107: int dstY, Surface dstSurf, int width, int height,
108: AffineTransform sysxform, Composite comp, Color bgcolor,
109: MultiRectArea clip) {
110:
111: if (sysxform == null) {
112: sysxform = new AffineTransform();
113: }
114: int type = sysxform.getType();
115: switch (type) {
116: case AffineTransform.TYPE_TRANSLATION:
117: dstX += sysxform.getTranslateX();
118: dstY += sysxform.getTranslateY();
119: case AffineTransform.TYPE_IDENTITY:
120: blit(srcX, srcY, srcSurf, dstX, dstY, dstSurf, width,
121: height, comp, bgcolor, clip);
122: break;
123: default:
124: int srcW = srcSurf.getWidth();
125: int srcH = srcSurf.getHeight();
126:
127: int w = srcX + width < srcW ? width : srcW - srcX;
128: int h = srcY + height < srcH ? height : srcH - srcY;
129:
130: ColorModel srcCM = srcSurf.getColorModel();
131: Raster srcR = srcSurf.getRaster().createChild(srcX, srcY,
132: w, h, 0, 0, null);
133:
134: ColorModel dstCM = dstSurf.getColorModel();
135: WritableRaster dstR = dstSurf.getRaster();
136:
137: transformedBlit(srcCM, srcR, 0, 0, dstCM, dstR, dstX, dstY,
138: w, h, sysxform, comp, bgcolor, clip);
139:
140: Rectangle dirtyReg = JavaBlitter.getBounds2D(sysxform,
141: new Rectangle(dstX, dstY, w, h)).getBounds();
142: Rectangle bounds = new Rectangle(dstSurf.getWidth(),
143: dstSurf.getHeight()).getBounds();
144: dstSurf.addDirtyRegion(bounds.intersection(dirtyReg));
145:
146: }
147: }
148:
149: public void blit(int srcX, int srcY, Surface srcSurf, int dstX,
150: int dstY, Surface dstSurf, int width, int height,
151: Composite comp, Color bgcolor, MultiRectArea clip) {
152:
153: javaBlt(srcX, srcY, srcSurf.getWidth(), srcSurf.getHeight(),
154: srcSurf.getColorModel(), srcSurf.getRaster(), dstX,
155: dstY, dstSurf.getWidth(), dstSurf.getHeight(), dstSurf
156: .getColorModel(), dstSurf.getRaster(), width,
157: height, comp, bgcolor, clip);
158:
159: dstSurf
160: .addDirtyRegion(new Rectangle(dstX, dstY, width, height));
161:
162: }
163:
164: public void javaBlt(int srcX, int srcY, int srcW, int srcH,
165: ColorModel srcCM, Raster srcRast, int dstX, int dstY,
166: int dstW, int dstH, ColorModel dstCM,
167: WritableRaster dstRast, int width, int height,
168: Composite comp, Color bgcolor, MultiRectArea clip) {
169:
170: int srcX2 = srcW - 1;
171: int srcY2 = srcH - 1;
172: int dstX2 = dstW - 1;
173: int dstY2 = dstH - 1;
174:
175: if (srcX < 0) {
176: width += srcX;
177: srcX = 0;
178: }
179: if (srcY < 0) {
180: height += srcY;
181: srcY = 0;
182: }
183:
184: if (dstX < 0) {
185: width += dstX;
186: srcX -= dstX;
187: dstX = 0;
188: }
189: if (dstY < 0) {
190: height += dstY;
191: srcY -= dstY;
192: dstY = 0;
193: }
194:
195: if (srcX > srcX2 || srcY > srcY2) {
196: return;
197: }
198: if (dstX > dstX2 || dstY > dstY2) {
199: return;
200: }
201:
202: if (srcX + width > srcX2) {
203: width = srcX2 - srcX + 1;
204: }
205: if (srcY + height > srcY2) {
206: height = srcY2 - srcY + 1;
207: }
208: if (dstX + width > dstX2) {
209: width = dstX2 - dstX + 1;
210: }
211: if (dstY + height > dstY2) {
212: height = dstY2 - dstY + 1;
213: }
214:
215: if (width <= 0 || height <= 0) {
216: return;
217: }
218:
219: int clipRects[];
220: if (clip != null) {
221: clipRects = clip.rect;
222: } else {
223: clipRects = new int[] { 5, 0, 0, dstW - 1, dstH - 1 };
224: }
225:
226: boolean isAlphaComp = false;
227: int rule = 0;
228: float alpha = 0;
229: boolean isXORComp = false;
230: Color xorcolor = null;
231: CompositeContext cont = null;
232:
233: if (comp instanceof AlphaComposite) {
234: isAlphaComp = true;
235: AlphaComposite ac = (AlphaComposite) comp;
236: rule = ac.getRule();
237: alpha = ac.getAlpha();
238: } else if (comp instanceof XORComposite) {
239: isXORComp = true;
240: XORComposite xcomp = (XORComposite) comp;
241: xorcolor = xcomp.getXORColor();
242: } else {
243: cont = comp.createContext(srcCM, dstCM, null);
244: }
245:
246: for (int i = 1; i < clipRects[0]; i += 4) {
247: int _sx = srcX;
248: int _sy = srcY;
249:
250: int _dx = dstX;
251: int _dy = dstY;
252:
253: int _w = width;
254: int _h = height;
255:
256: int cx = clipRects[i]; // Clipping left top X
257: int cy = clipRects[i + 1]; // Clipping left top Y
258: int cx2 = clipRects[i + 2]; // Clipping right bottom X
259: int cy2 = clipRects[i + 3]; // Clipping right bottom Y
260:
261: if (_dx > cx2 || _dy > cy2 || dstX2 < cx || dstY2 < cy) {
262: continue;
263: }
264:
265: if (cx > _dx) {
266: int shx = cx - _dx;
267: _w -= shx;
268: _dx = cx;
269: _sx += shx;
270: }
271:
272: if (cy > _dy) {
273: int shy = cy - _dy;
274: _h -= shy;
275: _dy = cy;
276: _sy += shy;
277: }
278:
279: if (_dx + _w > cx2 + 1) {
280: _w = cx2 - _dx + 1;
281: }
282:
283: if (_dy + _h > cy2 + 1) {
284: _h = cy2 - _dy + 1;
285: }
286:
287: if (_sx > srcX2 || _sy > srcY2) {
288: continue;
289: }
290:
291: if (isAlphaComp) {
292: alphaCompose(_sx, _sy, srcCM, srcRast, _dx, _dy, dstCM,
293: dstRast, _w, _h, rule, alpha, bgcolor);
294: } else if (isXORComp) {
295: xorCompose(_sx, _sy, srcCM, srcRast, _dx, _dy, dstCM,
296: dstRast, _w, _h, xorcolor);
297: } else {
298: Raster sr = srcRast.createChild(_sx, _sy, _w, _h, 0, 0,
299: null);
300: WritableRaster dr = dstRast.createWritableChild(_dx,
301: _dy, _w, _h, 0, 0, null);
302: cont.compose(sr, dr, dr);
303: }
304: }
305: }
306:
307: void alphaCompose(int srcX, int srcY, ColorModel srcCM,
308: Raster srcRast, int dstX, int dstY, ColorModel dstCM,
309: WritableRaster dstRast, int width, int height, int rule,
310: float alpha, Color bgcolor) {
311:
312: Object srcPixel, dstPixel;
313: int srcConstAllpha = (int) (alpha * 255 + 0.5f);
314: int srcRGB, dstRGB = 0;
315:
316: if (bgcolor != null) {
317: dstRGB = bgcolor.getRGB();
318: }
319:
320: for (int sy = srcY, dy = dstY, srcYMax = srcY + height; sy < srcYMax; sy++, dy++) {
321: for (int sx = srcX, dx = dstX, srcXMax = srcX + width; sx < srcXMax; sx++, dx++) {
322: srcPixel = srcRast.getDataElements(sx, sy, null);
323: srcRGB = srcCM.getRGB(srcPixel);
324: if (bgcolor == null) {
325: dstPixel = dstRast.getDataElements(dx, dy, null);
326: dstRGB = dstCM.getRGB(dstPixel);
327: }
328:
329: dstRGB = compose(srcRGB, srcCM.isAlphaPremultiplied(),
330: dstRGB, dstCM.hasAlpha(), dstCM
331: .isAlphaPremultiplied(), rule,
332: srcConstAllpha);
333:
334: dstPixel = dstCM.getDataElements(dstRGB, null);
335: dstRast.setDataElements(dx, dy, dstPixel);
336: }
337: }
338: }
339:
340: void xorCompose(int srcX, int srcY, ColorModel srcCM,
341: Raster srcRast, int dstX, int dstY, ColorModel dstCM,
342: WritableRaster dstRast, int width, int height,
343: Color xorcolor) {
344:
345: Object srcPixel, dstPixel;
346: int xorRGB = xorcolor.getRGB();
347: int srcRGB, dstRGB;
348:
349: for (int sy = srcY, dy = dstY, srcYMax = srcY + height; sy < srcYMax; sy++, dy++) {
350: for (int sx = srcX, dx = dstX, srcXMax = srcX + width; sx < srcXMax; sx++, dx++) {
351: srcPixel = srcRast.getDataElements(sx, sy, null);
352: dstPixel = dstRast.getDataElements(dx, dy, null);
353:
354: srcRGB = srcCM.getRGB(srcPixel);
355: dstRGB = dstCM.getRGB(dstPixel);
356: dstRGB = srcRGB ^ xorRGB ^ dstRGB;
357:
358: dstRGB = 0xff000000 | dstRGB;
359: dstPixel = dstCM.getDataElements(dstRGB, dstPixel);
360: dstRast.setDataElements(dx, dy, dstPixel);
361:
362: }
363: }
364:
365: }
366:
367: private void transformedBlit(ColorModel srcCM, Raster srcR,
368: int srcX, int srcY, ColorModel dstCM, WritableRaster dstR,
369: int dstX, int dstY, int width, int height,
370: AffineTransform at, Composite comp, Color bgcolor,
371: MultiRectArea clip) {
372:
373: Rectangle srcBounds = new Rectangle(srcX, srcY, width, height);
374: Rectangle dstBlitBounds = new Rectangle(dstX, dstY, width,
375: height);
376:
377: Rectangle transSrcBounds = getBounds2D(at, srcBounds)
378: .getBounds();
379: Rectangle transDstBlitBounds = getBounds2D(at, dstBlitBounds)
380: .getBounds();
381:
382: int translateX = transDstBlitBounds.x - transSrcBounds.x;
383: int translateY = transDstBlitBounds.y - transSrcBounds.y;
384:
385: AffineTransform inv = null;
386: try {
387: inv = at.createInverse();
388: } catch (NoninvertibleTransformException e) {
389: return;
390: }
391:
392: double[] m = new double[6];
393: inv.getMatrix(m);
394:
395: int clipRects[];
396: if (clip != null) {
397: clipRects = clip.rect;
398: } else {
399: clipRects = new int[] { 5, 0, 0, dstR.getWidth(),
400: dstR.getHeight() };
401: }
402:
403: int compType = 0;
404: int srcConstAlpha = 0;
405: int rule = 0;
406: int bgRGB = bgcolor == null ? 0 : bgcolor.getRGB();
407: int srcRGB = 0, dstRGB = 0;
408: Object srcVal = null, dstVal = null;
409: if (comp instanceof AlphaComposite) {
410: compType = AlphaCompositeMode;
411: AlphaComposite ac = (AlphaComposite) comp;
412: rule = ac.getRule();
413: srcConstAlpha = (int) (ac.getAlpha() * 255 + 0.5f);
414: } else if (comp instanceof XORComposite) {
415: compType = XORMode;
416: XORComposite xor = (XORComposite) comp;
417: bgRGB = xor.getXORColor().getRGB();
418: }
419:
420: for (int i = 1; i < clipRects[0]; i += 4) {
421: Rectangle dstBounds = new Rectangle(clipRects[i],
422: clipRects[i + 1], 0, 0);
423: dstBounds.add(clipRects[i + 2] + 1, clipRects[i + 1]);
424: dstBounds.add(clipRects[i + 2] + 1, clipRects[i + 3] + 1);
425: dstBounds.add(clipRects[i], clipRects[i + 3] + 1);
426:
427: Rectangle bounds = dstBounds
428: .intersection(transDstBlitBounds);
429:
430: int minSrcX = srcBounds.x;
431: int minSrcY = srcBounds.y;
432: int maxSrcX = minSrcX + srcBounds.width;
433: int maxSrcY = minSrcY + srcBounds.height;
434:
435: int minX = bounds.x;
436: int minY = bounds.y;
437: int maxX = minX + bounds.width;
438: int maxY = minY + bounds.height;
439:
440: int hx = (int) ((m[0] * 256) + 0.5);
441: int hy = (int) ((m[1] * 256) + 0.5);
442: int vx = (int) ((m[2] * 256) + 0.5);
443: int vy = (int) ((m[3] * 256) + 0.5);
444: int sx = (int) ((m[4] + m[0] * (bounds.x - translateX) + m[2]
445: * (bounds.y - translateY)) * 256 + 0.5);
446: int sy = (int) ((m[5] + m[1] * (bounds.x - translateX) + m[3]
447: * (bounds.y - translateY)) * 256 + 0.5);
448:
449: vx -= hx * bounds.width;
450: vy -= hy * bounds.width;
451:
452: for (int y = minY; y < maxY; y++) {
453: for (int x = minX; x < maxX; x++) {
454: int px = sx >> 8;
455: int py = sy >> 8;
456: if (px >= minSrcX && py >= minSrcY && px < maxSrcX
457: && py < maxSrcY) {
458: switch (compType) {
459: case AlphaCompositeMode:
460: srcVal = srcR.getDataElements(px, py, null);
461: srcRGB = srcCM.getRGB(srcVal);
462: if (bgcolor != null) {
463: dstRGB = bgRGB;
464: } else {
465: dstVal = dstR.getDataElements(x, y,
466: null);
467: dstRGB = dstCM.getRGB(dstVal);
468: }
469: dstRGB = compose(srcRGB, srcCM
470: .isAlphaPremultiplied(), dstRGB,
471: dstCM.hasAlpha(), dstCM
472: .isAlphaPremultiplied(),
473: rule, srcConstAlpha);
474: dstVal = dstCM
475: .getDataElements(dstRGB, null);
476: dstR.setDataElements(x, y, dstVal);
477: break;
478:
479: case XORMode:
480: srcVal = srcR.getDataElements(px, py, null);
481: srcRGB = srcCM.getRGB(srcVal);
482: dstVal = dstR.getDataElements(x, y, null);
483: dstRGB = dstCM.getRGB(dstVal);
484: dstRGB = srcRGB ^ bgRGB;
485:
486: dstRGB = 0xff000000 | dstRGB;
487: dstVal = dstCM
488: .getDataElements(dstRGB, null);
489: dstR.setDataElements(x, y, dstVal);
490: break;
491:
492: default:
493: // awt.37=Unknown composite type {0}
494: throw new IllegalArgumentException(Messages
495: .getString("awt.37", //$NON-NLS-1$
496: comp.getClass()));
497: }
498: }
499: sx += hx;
500: sy += hy;
501: }
502: sx += vx;
503: sy += vy;
504: }
505: }
506:
507: }
508:
509: public static Rectangle2D getBounds2D(AffineTransform at,
510: Rectangle r) {
511: int x = r.x;
512: int y = r.y;
513: int width = r.width;
514: int height = r.height;
515:
516: float[] corners = { x, y, x + width + 1, y, x + width + 1,
517: y + height + 1, x, y + height + 1 };
518:
519: at.transform(corners, 0, corners, 0, 4);
520:
521: Rectangle2D.Float bounds = new Rectangle2D.Float(corners[0],
522: corners[1], 0, 0);
523: bounds.add(corners[2], corners[3]);
524: bounds.add(corners[4], corners[5]);
525: bounds.add(corners[6], corners[7]);
526:
527: return bounds;
528: }
529:
530: private int compose(int srcRGB, boolean isSrcAlphaPre, int dstRGB,
531: boolean dstHasAlpha, boolean isDstAlphaPre, int rule,
532: int srcConstAlpha) {
533:
534: int sa, sr, sg, sb, da, dr, dg, db;
535:
536: sa = (srcRGB >> 24) & 0xff;
537: sr = (srcRGB >> 16) & 0xff;
538: sg = (srcRGB >> 8) & 0xff;
539: sb = srcRGB & 0xff;
540:
541: if (isSrcAlphaPre) {
542: sa = mulLUT[srcConstAlpha][sa] & 0xff;
543: sr = mulLUT[srcConstAlpha][sr] & 0xff;
544: sg = mulLUT[srcConstAlpha][sg] & 0xff;
545: sb = mulLUT[srcConstAlpha][sb] & 0xff;
546: } else {
547: sa = mulLUT[srcConstAlpha][sa] & 0xff;
548: sr = mulLUT[sa][sr] & 0xff;
549: sg = mulLUT[sa][sg] & 0xff;
550: sb = mulLUT[sa][sb] & 0xff;
551: }
552:
553: da = (dstRGB >> 24) & 0xff;
554: dr = (dstRGB >> 16) & 0xff;
555: dg = (dstRGB >> 8) & 0xff;
556: db = dstRGB & 0xff;
557:
558: if (!isDstAlphaPre) {
559: dr = mulLUT[da][dr] & 0xff;
560: dg = mulLUT[da][dg] & 0xff;
561: db = mulLUT[da][db] & 0xff;
562: }
563:
564: int Fs = 0;
565: int Fd = 0;
566: switch (rule) {
567: case AlphaComposite.CLEAR:
568: break;
569:
570: case AlphaComposite.DST:
571: Fd = 255;
572: break;
573:
574: case AlphaComposite.DST_ATOP:
575: Fs = 255 - da;
576: Fd = sa;
577: break;
578:
579: case AlphaComposite.DST_IN:
580: Fd = sa;
581: break;
582:
583: case AlphaComposite.DST_OUT:
584: Fd = 255 - sa;
585: break;
586:
587: case AlphaComposite.DST_OVER:
588: Fs = 255 - da;
589: Fd = 255;
590: break;
591:
592: case AlphaComposite.SRC:
593: Fs = 255;
594: break;
595:
596: case AlphaComposite.SRC_ATOP:
597: Fs = da;
598: Fd = 255 - sa;
599: break;
600:
601: case AlphaComposite.SRC_IN:
602: Fs = da;
603: break;
604:
605: case AlphaComposite.SRC_OUT:
606: Fs = 255 - da;
607: break;
608:
609: case AlphaComposite.SRC_OVER:
610: Fs = 255;
611: Fd = 255 - sa;
612: break;
613:
614: case AlphaComposite.XOR:
615: Fs = 255 - da;
616: Fd = 255 - sa;
617: break;
618: }
619: dr = (mulLUT[sr][Fs] & 0xff) + (mulLUT[dr][Fd] & 0xff);
620: dg = (mulLUT[sg][Fs] & 0xff) + (mulLUT[dg][Fd] & 0xff);
621: db = (mulLUT[sb][Fs] & 0xff) + (mulLUT[db][Fd] & 0xff);
622:
623: da = (mulLUT[sa][Fs] & 0xff) + (mulLUT[da][Fd] & 0xff);
624:
625: if (!isDstAlphaPre) {
626: if (da != 255) {
627: dr = divLUT[da][dr] & 0xff;
628: dg = divLUT[da][dg] & 0xff;
629: db = divLUT[da][db] & 0xff;
630: }
631: }
632: if (!dstHasAlpha) {
633: da = 0xff;
634: }
635: dstRGB = (da << 24) | (dr << 16) | (dg << 8) | db;
636:
637: return dstRGB;
638:
639: }
640:
641: }
|