1 /*
2  * This file is part of libbluray
3  * Copyright (C) 2012-2014  libbluray
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2.1 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this library. If not, see
17  * <http://www.gnu.org/licenses/>.
18  */
19 
20 package java.awt;
21 
22 import java.lang.reflect.Field;
23 import java.text.AttributedCharacterIterator;
24 import java.util.ArrayList;
25 import java.util.Arrays;
26 import java.util.Collections;
27 import java.util.HashSet;
28 import java.awt.image.AreaAveragingScaleFilter;
29 import java.awt.image.BufferedImage;
30 import java.awt.image.ImageConsumer;
31 import java.awt.image.ImageObserver;
32 
33 import org.dvb.ui.DVBBufferedImage;
34 import org.dvb.ui.DVBAlphaComposite;
35 import org.dvb.ui.DVBGraphics;
36 import org.dvb.ui.UnsupportedDrawingOperationException;
37 
38 import sun.awt.ConstrainableGraphics;
39 
40 import org.videolan.GUIManager;
41 import org.videolan.Logger;
42 
43 abstract class BDGraphicsBase extends DVBGraphics implements ConstrainableGraphics {
44     private static final Color DEFAULT_COLOR = Color.BLACK;
45     private static Font DEFAULT_FONT;
46 
47     private int width;
48     private int height;
49     private int[] backBuffer;
50     private Area dirty;
51     private GraphicsConfiguration gc;
52     private Color foreground;
53     protected Color background;
54     private Font font;
55     private BDFontMetrics fontMetrics;
56     private AlphaComposite composite;
57 
58     /** The current xor color. If null then we are in paint mode. */
59     private Color xorColor;
60 
61     /** Translated X, Y offset from native offset. */
62     private int originX;
63     private int originY;
64 
65     /** The actual clip rectangle that is intersection of user clip and constrained rectangle. */
66     private Rectangle actualClip;
67 
68     /** The current user clip rectangle or null if no clip has been set. This is stored in the
69      native coordinate system and not the (possibly) translated Java coordinate system. */
70     private Rectangle clip = null;
71 
72     /** The rectangle this graphics object has been constrained too. This is stored in the
73      native coordinate system and not the (possibly) translated Java coordinate system.
74      If it is null then this graphics has not been constrained. The constrained rectangle
75      is another layer of clipping independant of the user clip. */
76     private Rectangle constrainedRect = null;
77 
BDGraphicsBase(BDGraphicsBase g)78     BDGraphicsBase(BDGraphicsBase g) {
79         backBuffer = g.backBuffer;
80         dirty = g.dirty;
81         width = g.width;
82         height = g.height;
83         gc = g.gc;
84         foreground = g.foreground;
85         background = g.background;
86         composite = g.composite;
87         font = g.font;
88         fontMetrics = g.fontMetrics;
89         originX = g.originX;
90         originY = g.originY;
91         if (g.clip != null) {
92             clip = new Rectangle(g.clip);
93         }
94         setupClip();
95     }
96 
BDGraphicsBase(BDRootWindow window)97     BDGraphicsBase(BDRootWindow window) {
98         width = window.getWidth();
99         height = window.getHeight();
100         backBuffer = window.getBdBackBuffer();
101         dirty = window.getDirtyArea();
102         gc = window.getGraphicsConfiguration();
103         foreground = window.getForeground();
104         background = window.getBackground();
105         font = window.getFont();
106 
107         postInit();
108     }
109 
BDGraphicsBase(BDImage image)110     BDGraphicsBase(BDImage image) {
111         width = image.getWidth();
112         height = image.getHeight();
113         backBuffer = image.getBdBackBuffer();
114         dirty = image.getDirtyArea();
115 
116         gc = image.getGraphicsConfiguration();
117         Component component = image.getComponent();
118         if (component != null) {
119             foreground = component.getForeground();
120             background = component.getBackground();
121             font = component.getFont();
122         }
123         if (background == null)
124             background = new Color(0, 0, 0, 0);
125 
126         postInit();
127     }
128 
getDefaultFont()129     private static Font getDefaultFont() {
130         if (DEFAULT_FONT == null)
131             DEFAULT_FONT = new Font("Dialog", Font.PLAIN, 12);
132         return DEFAULT_FONT;
133     }
134 
postInit()135     private void postInit() {
136         if (foreground == null)
137             foreground = DEFAULT_COLOR;
138         if (background == null)
139             background = DEFAULT_COLOR;
140 
141         /* if font is not set, use AWT default font from BDJO */
142         if (font == null) {
143             font = GUIManager.getInstance().getDefaultFont();
144             if (font == null) {
145                 font = getDefaultFont();
146             }
147         }
148         fontMetrics = null;
149 
150         composite = AlphaComposite.SrcOver;
151         setupClip();
152     }
153 
create()154     public Graphics create() {
155         return new BDGraphics((BDGraphics)this);
156     }
157 
158     /*
159      * DVBGraphics methods
160      */
161 
getAvailableCompositeRules()162     public int[] getAvailableCompositeRules()
163     {
164         /*
165         int[] rules = { DVBAlphaComposite.CLEAR, DVBAlphaComposite.SRC,
166                         DVBAlphaComposite.SRC_OVER, DVBAlphaComposite.DST_OVER,
167                         DVBAlphaComposite.SRC_IN, DVBAlphaComposite.DST_IN,
168                         DVBAlphaComposite.SRC_OUT, DVBAlphaComposite.DST_OUT };
169         */
170         int[] rules = {
171             DVBAlphaComposite.CLEAR,
172             DVBAlphaComposite.SRC,
173             DVBAlphaComposite.SRC_OVER };
174 
175         return rules;
176     }
177 
getDVBComposite()178     public DVBAlphaComposite getDVBComposite()
179     {
180         Composite comp = getComposite();
181         if (!(comp instanceof AlphaComposite))
182             return null;
183         return DVBAlphaComposite.getInstance(
184                                              ((AlphaComposite)comp).getRule(),
185                                              ((AlphaComposite)comp).getAlpha());
186     }
187 
setDVBComposite(DVBAlphaComposite comp)188     public void setDVBComposite(DVBAlphaComposite comp)
189                     throws UnsupportedDrawingOperationException
190     {
191         if ((comp.getRule() < DVBAlphaComposite.CLEAR) ||
192             (comp.getRule() > DVBAlphaComposite.SRC_OVER)) {
193             logger.error("setDVBComposite() FAILED: unsupported rule " + comp.getRule());
194             throw new UnsupportedDrawingOperationException("Unsupported composition rule: " + comp.getRule());
195         }
196 
197         setComposite(AlphaComposite.getInstance(comp.getRule(), comp.getAlpha()));
198     }
199 
200     /*
201      *
202      */
203 
translate(int x, int y)204     public void translate(int x, int y) {
205         originX += x;
206         originY += y;
207     }
208 
setFont(Font font)209     public void setFont(Font font) {
210         if (font != null && !font.equals(this.font)) {
211             this.font = font;
212             fontMetrics = null;
213         }
214     }
215 
getFont()216     public Font getFont() {
217         if (font == null)
218             return getDefaultFont();
219         return font;
220     }
221 
getFontMetrics()222     public FontMetrics getFontMetrics() {
223         if (font != null && fontMetrics == null) {
224             fontMetrics = BDFontMetrics.getFontMetrics(font);
225         }
226         if (fontMetrics == null) {
227             logger.error("getFontMetrics() failed");
228         }
229         return fontMetrics;
230     }
231 
getFontMetrics(Font font)232     public FontMetrics getFontMetrics(Font font) {
233         if (font != null) {
234             return BDFontMetrics.getFontMetrics(font);
235         }
236         logger.error("getFontMetrics(null) from " + Logger.dumpStack());
237         return null;
238     }
239 
setColor(Color c)240     public void setColor(Color c) {
241         if ((c != null) && (c != foreground))
242             foreground = c;
243     }
244 
getColor()245     public Color getColor() {
246         return foreground;
247     }
248 
getComposite()249     public Composite getComposite() {
250         return composite;
251     }
252 
getDeviceConfiguration()253     public GraphicsConfiguration getDeviceConfiguration() {
254         if (gc == null)
255             logger.error("getDeviceConfiguration() failed");
256         return gc;
257     }
258 
setComposite(Composite comp)259     public void setComposite(Composite comp) {
260         if ((comp != null) && (comp != composite)) {
261             if (!(comp instanceof AlphaComposite)) {
262                 logger.error("Composite is not AlphaComposite");
263                 throw new IllegalArgumentException("Only AlphaComposite is supported");
264             }
265             composite = (AlphaComposite) comp;
266         }
267     }
268 
setPaintMode()269     public void setPaintMode() {
270         xorColor = null;
271         composite = AlphaComposite.SrcOver;
272     }
273 
setXORMode(Color color)274     public void setXORMode(Color color) {
275         xorColor = color;
276     }
277 
278     /** Gets the current clipping area. */
getClipBounds()279     public Rectangle getClipBounds() {
280         if (clip != null)
281             return new Rectangle (clip.x - originX, clip.y - originY, clip.width, clip.height);
282         return null;
283     }
284 
constrain(int x, int y, int w, int h)285     public void constrain(int x, int y, int w, int h) {
286         Rectangle rect;
287         if (constrainedRect != null)
288             rect = constrainedRect;
289         else
290             rect = new Rectangle(0, 0, width, height);
291         constrainedRect = rect.intersection(new Rectangle(rect.x + x, rect.y + y, w, h));
292         originX = constrainedRect.x;
293         originY = constrainedRect.y;
294         setupClip();
295     }
296 
297     /** Returns a Shape object representing the clip. */
getClip()298     public Shape getClip() {
299         return getClipBounds();
300     }
301 
302     /** Crops the clipping rectangle. */
clipRect(int x, int y, int w, int h)303     public void clipRect(int x, int y, int w, int h) {
304         Rectangle rect = new Rectangle(x + originX, y + originY, w, h);
305         if (clip != null)
306             clip = clip.intersection(rect);
307         else
308             clip = rect;
309         setupClip();
310     }
311 
312     /** Sets the clipping rectangle. */
setClip(int x, int y, int w, int h)313     public void setClip(int x, int y, int w, int h) {
314         clip = new Rectangle (x + originX, y + originY, w, h);
315         setupClip();
316     }
317 
318     /** Sets the clip to a Shape (only Rectangle allowed). */
setClip(Shape clip)319     public void setClip(Shape clip) {
320         if (clip == null) {
321             this.clip = null;
322             setupClip();
323         } else if (clip instanceof Rectangle) {
324             Rectangle rect = (Rectangle) clip;
325             setClip(rect.x, rect.y, rect.width, rect.height);
326         } else {
327             logger.error("Shape is not Rectangle: " + clip.getClass().getName());
328             throw new IllegalArgumentException("setClip(Shape) only supports Rectangle objects");
329         }
330     }
331 
setupClip()332     private void setupClip() {
333         Rectangle rect;
334         if (constrainedRect != null)
335             rect = constrainedRect;
336         else
337             rect = new Rectangle(0, 0, width, height);
338         if (clip != null)
339             actualClip = clip.intersection(rect);
340         else
341             actualClip = rect;
342     }
343 
alphaBlend(int dest, int src)344     private int alphaBlend(int dest, int src) {
345         int As = src >>> 24;
346         if (As == 0)
347             return dest;
348         if (As == 255)
349             return src;
350         int Ad = (dest >>> 24);
351         if (Ad == 0)
352             return src;
353         int R, G, B;
354         R = ((src >>> 16) & 255) * As * 255;
355         G = ((src >>> 8) & 255) * As * 255;
356         B = (src & 255) * As * 255;
357         Ad = Ad * (255 - As);
358         As = As * 255 + Ad;
359         R = (R + ((dest >>> 16) & 255) * Ad) / As;
360         G = (G + ((dest >>> 8) & 255) * Ad) / As;
361         B = (B + (dest & 255) * Ad) / As;
362         R = Math.min(255, R);
363         G = Math.min(255, G);
364         B = Math.min(255, B);
365         Ad = As / 255;
366         Ad = Math.min(255, Ad);
367         return (Ad << 24) | (R << 16) | (G << 8) | B;
368     }
369 
applyComposite(int rgb)370     private int applyComposite(int rgb) {
371         return ((int)((rgb >>> 24) * composite.getAlpha()) << 24) | (rgb & 0x00FFFFFF);
372     }
373 
drawSpanN(int x, int y, int length, int rgb)374     private void drawSpanN(int x, int y, int length, int rgb) {
375 
376         Rectangle rect = new Rectangle(x, y, length, 1);
377         rect = actualClip.intersection(rect);
378 
379         if (rect.width <= 0 || rect.height <= 0 || rect.x < 0 || rect.y < 0 || backBuffer == null) {
380             return;
381         }
382 
383         x      = rect.x;
384         length = rect.width;
385 
386         if (xorColor != null) {
387             for (int i = 0; i < length; i++) {
388                 backBuffer[y * width + x + i] ^= xorColor.getRGB() ^ rgb;
389             }
390 
391             dirty.add(rect);
392             return;
393         }
394 
395         switch (composite.getRule()) {
396             case AlphaComposite.CLEAR:
397                 for (int i = 0; i < length; i++) {
398                     backBuffer[y * width + x + i] = 0;
399                 }
400                 break;
401             case AlphaComposite.SRC:
402                 rgb = applyComposite(rgb);
403                 for (int i = 0; i < length; i++) {
404                     backBuffer[y * width + x + i] = rgb;
405                 }
406                 break;
407             case AlphaComposite.SRC_OVER:
408                 rgb = applyComposite(rgb);
409                 for (int i = 0; i < length; i++) {
410                     backBuffer[y * width + x + i] = alphaBlend(backBuffer[y * width + x + i], rgb);
411                 }
412                 break;
413         }
414 
415         dirty.add(rect);
416     }
417 
drawSpanN(int x, int y, int length, int src[], int srcOffset, boolean flipX)418     private void drawSpanN(int x, int y, int length, int src[], int srcOffset, boolean flipX) {
419 
420         /* avoid overreading source */
421         if (srcOffset + length > src.length) {
422             length -= srcOffset + length - src.length;
423         }
424         /* avoid underreading source */
425         if (srcOffset < 0) {
426             length += srcOffset;
427             x -= srcOffset;
428             srcOffset = 0;
429         }
430         if (length <= 0) {
431             return;
432         }
433 
434         Rectangle rect = new Rectangle(x, y, length, 1);
435         rect = actualClip.intersection(rect);
436 
437         if (rect.width <= 0 || rect.height <= 0 || rect.x < 0 || rect.y < 0 || backBuffer == null) {
438             return;
439         }
440 
441         int dstOffset;
442 
443         srcOffset += rect.x - x;
444         x          = rect.x;
445         length     = rect.width;
446         dstOffset  = y * width + x;
447 
448         if (xorColor != null) {
449 
450             if (flipX) {
451                 for (int i = 0; i < length; i++) {
452                     backBuffer[dstOffset + length -1 - i] ^= xorColor.getRGB() ^ src[srcOffset + i];
453                 }
454             } else {
455                 for (int i = 0; i < length; i++) {
456                     backBuffer[dstOffset + i] ^= xorColor.getRGB() ^ src[srcOffset + i];
457                 }
458             }
459 
460             dirty.add(rect);
461             return;
462         }
463 
464         switch (composite.getRule()) {
465             case AlphaComposite.CLEAR:
466                 for (int i = 0; i < length; i++) {
467                     backBuffer[dstOffset + i] = 0;
468                 }
469                 break;
470             case AlphaComposite.SRC:
471                 if (flipX) {
472                     for (int i = 0; i < length; i++) {
473                         backBuffer[dstOffset + length -1 - i] = applyComposite(src[srcOffset + i]);
474                     }
475                 } else {
476                     for (int i = 0; i < length; i++) {
477                         backBuffer[dstOffset + i] = applyComposite(src[srcOffset + i]);
478                     }
479                 }
480                 break;
481             case AlphaComposite.SRC_OVER:
482                 if (flipX) {
483                     for (int i = 0; i < length; i++) {
484                         backBuffer[dstOffset + length -1 - i] = alphaBlend(backBuffer[dstOffset + length -1 - i], applyComposite(src[srcOffset + i]));
485                     }
486                 } else {
487                     for (int i = 0; i < length; i++) {
488                         backBuffer[dstOffset + i] = alphaBlend(backBuffer[dstOffset + i], applyComposite(src[srcOffset + i]));
489                     }
490                 }
491                 break;
492         }
493 
494         dirty.add(rect);
495     }
496 
drawSpan(int x, int y, int length, int rgb)497     private void drawSpan(int x, int y, int length, int rgb) {
498         x += originX;
499         y += originY;
500         drawSpanN(x, y, length, rgb);
501     }
502 
drawSpan(int x, int y, int length, int src[], int srcOffset, boolean flipX)503     private void drawSpan(int x, int y, int length, int src[], int srcOffset, boolean flipX) {
504         x += originX;
505         y += originY;
506         drawSpanN(x, y, length, src, srcOffset, flipX);
507     }
508 
drawPointN(int x, int y, int rgb)509     private void drawPointN(int x, int y, int rgb) {
510         drawSpanN(x, y, 1, rgb);
511     }
512 
drawGlyph(int[] rgbArray, int x0, int y0, int w, int h)513     private void drawGlyph(int[] rgbArray, int x0, int y0, int w, int h) {
514         for (int y = 0; y < h; y++)
515             for (int x = 0; x < w; x++)
516                 drawPoint(x + x0, y + y0, rgbArray[y * w + x]);
517     }
518 
drawPoint(int x, int y, int rgb)519     private void drawPoint(int x, int y, int rgb) {
520         x += originX;
521         y += originY;
522         if (actualClip.contains(x, y))
523             drawPointN(x, y, rgb);
524     }
525 
clearRect(int x, int y, int w, int h)526     public void clearRect(int x, int y, int w, int h) {
527         x += originX;
528         y += originY;
529         Rectangle rect = new Rectangle(x, y, w, h);
530         rect = actualClip.intersection(rect);
531         if (rect.isEmpty() || backBuffer == null) {
532             return;
533         }
534         x = rect.x;
535         y = rect.y;
536         w = rect.width;
537         h = rect.height;
538         int rgb = background.getRGB();
539         for (int i = 0; i < h; i++)
540             Arrays.fill(backBuffer, (y + i) * width + x, (y + i) * width + x + w, rgb);
541 
542         dirty.add(rect);
543     }
544 
fillRect(int x, int y, int w, int h)545     public void fillRect(int x, int y, int w, int h) {
546         x += originX;
547         y += originY;
548         Rectangle rect = new Rectangle(x, y, w, h);
549         rect = actualClip.intersection(rect);
550         x = rect.x;
551         y = rect.y;
552         w = rect.width;
553         h = rect.height;
554         int rgb = foreground.getRGB();
555         for (int Y = y; Y < (y + h); Y++)
556             drawSpanN(x, Y, w, rgb);
557     }
558 
drawRect(int x, int y, int w, int h)559     public void drawRect(int x, int y, int w, int h) {
560         x += originX;
561         y += originY;
562 
563         drawLineN(x, y, x + w, y);
564         drawLineN(x, y + h, x + w, y + h);
565         drawLineN(x, y, x, y + h);
566         drawLineN(x + w, y, x + w, y + h);
567     }
568 
drawLineN(int x1, int y1, int x2, int y2)569     private void drawLineN(int x1, int y1, int x2, int y2) {
570         int rgb = foreground.getRGB();
571         int dy = y2 - y1;
572         int dx = x2 - x1;
573         int stepx, stepy;
574         int fraction;
575         if (dy < 0) {
576             dy = -dy;
577             stepy = -1;
578         } else {
579             stepy = 1;
580         }
581         if (dx < 0) {
582             dx = -dx;
583             stepx = -1;
584         } else {
585             stepx = 1;
586         }
587         dy <<= 1;
588         dx <<= 1;
589 
590         drawPointN(x1, y1, rgb);
591 
592         if (dx > dy) {
593             fraction = dy - (dx >> 1);
594             while (x1 != x2) {
595                 if (fraction >= 0) {
596                     y1 += stepy;
597                     fraction -= dx;
598                 }
599                 x1 += stepx;
600                 fraction += dy;
601                 drawPointN(x1, y1, rgb);
602             }
603         } else {
604             fraction = dx - (dy >> 1);
605             while (y1 != y2) {
606                 if (fraction >= 0) {
607                     x1 += stepx;
608                     fraction -= dy;
609                 }
610                 y1 += stepy;
611                 fraction += dx;
612                 drawPointN(x1, y1, rgb);
613             }
614         }
615     }
616 
drawLine(int x1, int y1, int x2, int y2)617     public void drawLine(int x1, int y1, int x2, int y2) {
618 
619         x1 += originX;
620         y1 += originY;
621 
622         x2 += originX;
623         y2 += originY;
624 
625         drawLineN(x1, y1, x2, y2);
626     }
627 
628     /**
629      * Copies an area of the canvas that this graphics context paints to.
630      * @param X the x-coordinate of the source.
631      * @param Y the y-coordinate of the source.
632      * @param W the width.
633      * @param H the height.
634      * @param dx the horizontal distance to copy the pixels.
635      * @param dy the vertical distance to copy the pixels.
636      */
copyArea(int x, int y, int w, int h, int dx, int dy)637     public void copyArea(int x, int y, int w, int h, int dx, int dy) {
638 
639         x += originX;
640         y += originY;
641 
642         Rectangle rect = new Rectangle(x, y, w, h);
643         rect = actualClip.intersection(rect);
644 
645         if (rect.width <= 0 || rect.height <= 0 || backBuffer == null) {
646             return;
647         }
648 
649         x = rect.x;
650         y = rect.y;
651         w = rect.width;
652         h = rect.height;
653 
654         int subImage[] = new int[w * h];
655 
656         // copy back buffer
657         for (int i = 0; i < h; i++) {
658             System.arraycopy(backBuffer, ((y + i) * width) + x, subImage, w * i, w);
659         }
660 
661         // draw sub image
662         for (int i = 0; i < h; i++) {
663             drawSpanN(x + dx, y + i + dy, w, subImage, w * i, false);
664         }
665     }
666 
667     /** Draws lines defined by an array of x points and y points */
drawPolyline(int xPoints[], int yPoints[], int nPoints)668     public void drawPolyline(int xPoints[], int yPoints[], int nPoints) {
669         if (nPoints == 1) {
670             drawPoint(xPoints[0], yPoints[0], foreground.getRGB());
671         } else {
672             for (int i = 0; i < (nPoints - 1); i++)
673                 drawLine(xPoints[i], yPoints[i], xPoints[i + 1], yPoints[i + 1]);
674         }
675     }
676 
677     /** Draws a polygon defined by an array of x points and y points */
drawPolygon(int xPoints[], int yPoints[], int nPoints)678     public void drawPolygon(int xPoints[], int yPoints[], int nPoints) {
679         if (nPoints == 1) {
680             drawPoint(xPoints[0], yPoints[0], foreground.getRGB());
681         } else {
682             for (int i = 0; i < (nPoints - 1); i++)
683                 drawLine(xPoints[i], yPoints[i], xPoints[i + 1], yPoints[i + 1]);
684             if (nPoints > 2)
685                 drawLine(xPoints[0], yPoints[0], xPoints[nPoints - 1], yPoints[nPoints - 1]);
686         }
687     }
688 
689     /** Fills a polygon with the current fill mask */
fillPolygon(int xPoints[], int yPoints[], int nPoints)690     public void fillPolygon(int xPoints[], int yPoints[], int nPoints) {
691 
692         int minY = Integer.MAX_VALUE;
693         int maxY = Integer.MIN_VALUE;
694         int colour = foreground.getRGB();
695 
696         if (nPoints < 3) {
697             return;
698         }
699 
700         for (int i = 0; i < nPoints; i++) {
701             if (yPoints[i] > maxY) {
702                 maxY = yPoints[i];
703             }
704             if (yPoints[i] < minY) {
705                 minY = yPoints[i];
706             }
707         }
708 
709         // check the last point to see if its the same as the first
710         if (xPoints[0] == xPoints[nPoints - 1] && yPoints[0] == yPoints[nPoints - 1]) {
711             nPoints--;
712         }
713 
714         PolyEdge[] polyEdges = new PolyEdge[nPoints];
715 
716         for (int i = 0; i < nPoints - 1; i++) {
717             polyEdges[i] = new PolyEdge(xPoints[i], yPoints[i], xPoints[i + 1], yPoints[i + 1]);
718         }
719 
720         // add the last one
721         polyEdges[nPoints - 1] = new PolyEdge(xPoints[nPoints - 1], yPoints[nPoints - 1], xPoints[0], yPoints[0]);
722         ArrayList xList = new ArrayList();
723         for (int i = minY; i <= maxY; i++) {
724             for (int j = 0; j < nPoints; j++) {
725                 if (polyEdges[j].intersects(i)) {
726                     int x = polyEdges[j].intersectionX(i);
727                     xList.add(new Integer(x));
728                 }
729             }
730 
731             // probably a better way of doing this (removing duplicates);
732             HashSet hs = new HashSet();
733             hs.addAll(xList);
734             xList.clear();
735             xList.addAll(hs);
736 
737             if (xList.size() % 2 > 0) {
738                 xList.clear();
739                 continue;                       // this should be impossible unless the poly is open somewhere
740             }
741 
742             Collections.sort(xList);
743 
744             for (int j = 0; j < xList.size(); j +=2 ) {
745                 int x1 = ((Integer)xList.get(j)).intValue();
746                 int x2 = ((Integer)xList.get(j + 1)).intValue();
747 
748                 drawSpan(x1, i, x2 - x1, colour);
749             }
750 
751             xList.clear();
752         }
753     }
754 
755     /** Draws an oval to fit in the given rectangle */
drawOval(int x, int y, int w, int h)756     public void drawOval(int x, int y, int w, int h) {
757 
758         int     startX;
759         int     endX;
760         int     offset;
761         int[]   xList;
762         int[]   yList;
763         int     numPoints;
764         int     count;
765         float   as;
766         float   bs;
767 
768         if (w <= 0 || h <=0 ) {
769             return;
770         }
771 
772         count = 0;
773         numPoints = ((h/2) + (h/2) + 1) * 2;
774         numPoints += 1; // to close
775         xList = new int[numPoints];
776         yList = new int[numPoints];
777 
778         as = (w/2.0f) * (w/2.0f);
779         bs = (h/2.0f) * (h/2.0f);
780 
781         for (int i = -h/2; i <= h/2; i++) {
782             offset = (int) Math.sqrt( (1.0 - ((i*i)/bs)) * as );
783             startX  = x - offset + w/2;
784 
785             xList[count] = startX;
786             yList[count] = y + i + h/2;
787             count++;
788         }
789 
790         for (int i = h/2; i >= -h/2; i--) {
791             offset = (int) Math.sqrt( (1.0 - ((i*i)/bs)) * as );
792             endX    = x + offset + w/2;
793 
794             xList[count] = endX;
795             yList[count] = y + i + h/2;
796             count++;
797         }
798 
799         xList[count] = xList[0];        // close the loop
800         yList[count] = yList[0];        // close the loop
801 
802         drawPolyline(xList, yList, numPoints);
803     }
804 
805     /** Fills an oval to fit in the given rectangle */
fillOval(int x, int y, int w, int h)806     public void fillOval(int x, int y, int w, int h) {
807 
808         int     startX;
809         int     endX;
810         int     offset;
811         int     colour;
812         float   as;
813         float   bs;
814 
815         if (w <= 0 || h <= 0) {
816             return;
817         }
818 
819         as = (w/2.0f) * (w/2.0f);
820         bs = (h/2.0f) * (h/2.0f);
821         colour = foreground.getRGB();
822 
823         for(int i=-h/2; i<=h/2; i++) {
824             offset  = (int) Math.sqrt( (1.0 - ((i*i)/bs)) * as );
825             startX  = x - offset + w/2;
826             endX    = x + offset + w/2;
827 
828             drawSpan(startX, y + i + h/2, endX - startX + 1, colour);
829         }
830     }
831 
getAngle(int centreX, int centreY, int pointX, int pointY)832     private int getAngle(int centreX, int centreY, int pointX, int pointY) {
833 
834         float  vStartX;
835         float  vStartY;
836         float  vEndX;
837         float  vEndY;
838         float  length;
839         double angle;
840 
841         vStartX = 1;    // vector pointing right (this is where angle starts for arcs)
842         vStartY = 0;
843 
844         vEndX = pointX - centreX;
845         vEndY = pointY - centreY;
846 
847         length = (float) Math.sqrt(vEndX*vEndX + vEndY*vEndY);
848 
849         vEndX /= length;
850         vEndY /= length;
851 
852         angle = Math.acos(vStartX*vEndX + vStartY*vEndY);
853         angle = angle * 180.0 / Math.PI;
854 
855         if (vEndY > 0) {
856             angle = 360 - angle;
857         }
858 
859         return (int)(angle + 0.5);
860     }
861 
drawArcI(boolean fill, int x, int y, int width, int height, int startAngle, int arcAngle)862     private void drawArcI(boolean fill, int x, int y, int width, int height, int startAngle, int arcAngle) {
863 
864         int     endAngle;
865         int     startX;
866         int     endX;
867         int     offset;
868         int[]   xList;
869         int[]   yList;
870         int     count;
871         int     numPoints;
872         int     tempX;
873         int     tempY;
874         int     angle;
875         int     widthDiv2;
876         int     heightDiv2;
877         float   as;
878         float   bs;
879         boolean addedZero;
880         boolean circle;
881 
882         // sanity checks
883         if (width <= 0 || height <= 0 || arcAngle == 0) {
884             return;
885         }
886 
887         // init variables
888         count           = 0;
889         addedZero       = false;
890         circle          = false;
891         widthDiv2       = (int)(width/2.0f + 0.5f);
892         heightDiv2      = (int)(height/2.0f + 0.5f);
893         numPoints       = ((height + 1/2) + (height + 1/2) + 1) * 2 + 1;
894         xList           = new int[numPoints];
895         yList           = new int[numPoints];
896 
897         as = (width/2.0f)  * (width/2.0f);
898         bs = (height/2.0f) * (height/2.0f);
899 
900         // check if we actually want to draw a circle
901         if (Math.abs(arcAngle) >= 360) {
902             circle = true;
903         }
904 
905         if (startAngle < 0) {
906             startAngle %= 360;
907             startAngle = Math.abs(startAngle);
908             startAngle = 360 - startAngle;
909         }
910 
911         if (arcAngle < 0) {
912             int temp;
913             temp = startAngle;
914             endAngle = startAngle;
915             startAngle = 360 + arcAngle + temp;
916         } else {
917             endAngle = startAngle + arcAngle;
918         }
919 
920         startAngle %= 360;
921         endAngle   %= 360;
922 
923         for (int i = heightDiv2; i >= -heightDiv2; i--) {
924             boolean hit = false;
925             int offsetAngle;
926             int startXAngle;
927 
928             offset = (int) Math.sqrt( (1.0 - i*i/bs) * as );
929             startX = x + offset + widthDiv2;
930 
931             offsetAngle = (int) Math.sqrt( (1.0 - i*i/bs) * bs );       // we calculate these as if it were a circle
932             startXAngle = x + offsetAngle + height/2;
933 
934             tempX = startX;
935             tempY = y + i + height/2;
936 
937             angle = getAngle(x + height/2, y + height/2, startXAngle, tempY);
938 
939             if (startAngle < endAngle) {
940                 if (angle < endAngle && angle >= startAngle) {
941                     xList[count] = tempX;
942                     yList[count] = tempY;
943                     count++;
944                     hit = true;
945                 }
946             } else {
947                 if (!(angle > endAngle && angle < startAngle)) {
948                     xList[count] = tempX;
949                     yList[count] = tempY;
950                     count++;
951                     hit = true;
952                 }
953             }
954 
955             if (!hit && !addedZero && !circle && fill) {
956                 xList[count] = x + width/2;
957                 yList[count] = y + height/2;
958                 count++;
959                 addedZero = true;
960             }
961 
962             if (!hit && !fill && !circle && count > 1) {
963                 drawPolyline(xList, yList, count);
964                 count = 0;
965             }
966         }
967 
968 
969         for (int i = -heightDiv2; i <= heightDiv2; i++) {
970             boolean hit = false;
971             int offsetAngle;
972             int endXAngle;
973 
974             offset = (int) Math.sqrt( (1.0 - i*i/bs) * as );
975             endX   = x - offset + width/2;
976 
977             offsetAngle = (int) Math.sqrt( (1.0 - i*i/bs) * bs );   // we calculate these as if it were a circle
978             endXAngle   = x - offsetAngle + height/2;
979 
980             tempX = endX;
981             tempY = y + i + height/2;
982 
983             angle = getAngle(x + height/2, y + height/2, endXAngle, tempY);
984 
985             if (startAngle < endAngle) {
986                 if (angle <= endAngle && angle >= startAngle) {
987                     xList[count] = tempX;
988                     yList[count] = tempY;
989                     count++;
990                     hit = true;
991                 }
992             } else {
993                 if (!(angle > endAngle && angle < startAngle)) {
994                     xList[count] = tempX;
995                     yList[count] = tempY;
996                     count++;
997                     hit = true;
998                 }
999             }
1000 
1001             if (!hit && !addedZero && !circle && fill) {
1002                 xList[count] = x + width/2;
1003                 yList[count] = y + height/2;
1004                 count++;
1005                 addedZero = true;
1006             }
1007 
1008             if (!hit && !fill && !circle && count > 1) {
1009                 drawPolyline(xList, yList, count);
1010                 count = 0;
1011             }
1012         }
1013 
1014         if (fill) {
1015             fillPolygon(xList, yList, count);
1016         } else {
1017             if (circle) {
1018                 drawPolygon(xList, yList, count);   // we need to connect start to end in the case of 360
1019             } else {
1020                 drawPolyline(xList, yList, count);  // shape must be open so no connection
1021             }
1022         }
1023     }
1024 
1025 
1026     /**
1027      * Draws an arc bounded by the given rectangle from startAngle to
1028      * endAngle. 0 degrees is a vertical line straight up from the
1029      * center of the rectangle. Positive start angle indicate clockwise
1030      * rotations, negative angle are counter-clockwise.
1031      */
drawArc(int x, int y, int w, int h, int startAngle, int endAngle)1032     public void drawArc(int x, int y, int w, int h, int startAngle, int endAngle) {
1033         drawArcI(false, x, y, w, h, startAngle, endAngle);
1034     }
1035 
1036     /** fills an arc. arguments are the same as drawArc. */
fillArc(int x, int y, int w, int h, int startAngle, int endAngle)1037     public void fillArc(int x, int y, int w, int h, int startAngle, int endAngle) {
1038         drawArcI(true, x, y, w, h, startAngle, endAngle);
1039     }
1040 
1041     /** Draws a rounded rectangle. */
drawRoundRect(int x, int y, int w, int h, int arcWidth, int arcHeight)1042     public void drawRoundRect(int x, int y, int w, int h, int arcWidth, int arcHeight) {
1043 
1044         int[]   xList;
1045         int[]   yList;
1046         int     numPoints;
1047         int     count;
1048         int     startX;
1049         int     endX;
1050         int     offset;
1051 
1052         if (w <= 0 || h <= 0) {
1053             return;
1054         }
1055 
1056         if (arcWidth == 0 || arcHeight == 0) {
1057             drawRect(x, y, w, h);
1058             return;
1059         }
1060 
1061         if (arcWidth < 0) {                // matches behaviour of normal java version
1062             arcWidth *= -1;
1063         }
1064 
1065         if (arcHeight < 0) {
1066             arcHeight *= -1;
1067         }
1068 
1069         count = 0;
1070         numPoints = ((arcHeight/2) + 1) * 2;
1071         numPoints += ((arcHeight/2) + 1) * 2;
1072         numPoints += 1; // last point to close the loop
1073 
1074         xList = new int[numPoints];
1075         yList = new int[numPoints];
1076 
1077         float as = (arcWidth/2.0f)  * (arcWidth/2.0f);
1078         float bs = (arcHeight/2.0f) * (arcHeight/2.0f);
1079 
1080         // draw top curved half of box
1081 
1082         for (int i = 0; -arcHeight/2 <= i; i--) {
1083             offset = (int) Math.sqrt( (1.0 - ((i*i)/bs)) * as );
1084             startX  = x - offset + arcWidth/2;
1085 
1086             xList[count] = startX;
1087             yList[count] = y+i+(arcHeight/2);
1088             count++;
1089         }
1090 
1091         for (int i = -arcHeight / 2; i <= 0; i++) {
1092             offset = (int) Math.sqrt( (1.0 - ((i*i)/bs)) * as );
1093             endX    = x + offset + (w-arcWidth) + arcWidth/2;
1094 
1095             xList[count] = endX;
1096             yList[count] = y + i + (arcHeight/2);
1097             count++;
1098         }
1099 
1100         // draw bottom box
1101         for (int i = 0; i <= arcHeight / 2; i++) {
1102             offset = (int) Math.sqrt( (1.0 - ((i*i)/bs)) * as );
1103             startX  = x - offset + arcWidth/2;
1104             endX    = x + offset + (w - arcWidth) + arcWidth/2;
1105 
1106             xList[count] = endX;
1107             yList[count] = y + i + h - arcHeight/2;
1108             count++;
1109         }
1110 
1111         // draw bottom box
1112         for (int i = arcHeight / 2; i >= 0; i--) {
1113             offset = (int) Math.sqrt( (1.0 - ((i*i)/bs)) * as );
1114             startX  = x - offset + arcWidth/2;
1115             endX    = x + offset + (w-arcWidth) + arcWidth/2;
1116 
1117             xList[count] = startX;
1118             yList[count] = y+i+h-arcHeight/2;
1119             count++;
1120         }
1121 
1122         xList[count] = xList[0];
1123         yList[count] = yList[0];
1124 
1125         drawPolyline(xList, yList, numPoints);
1126     }
1127 
1128     /** Draws a filled rounded rectangle. */
fillRoundRect(int x, int y, int w, int h, int arcWidth, int arcHeight)1129     public void fillRoundRect(int x, int y, int w, int h, int arcWidth, int arcHeight) {
1130 
1131         int startX;
1132         int endX;
1133         int offset;
1134         int colour;
1135 
1136         if (w <= 0 || h <= 0) {
1137             return;
1138         }
1139 
1140         if (arcWidth == 0 || arcHeight == 0) {
1141             fillRect(x,y,w,h);
1142             return;
1143         }
1144 
1145         if (arcWidth < 0) {                // matches behaviour of normal java version
1146             arcWidth *= -1;
1147         }
1148 
1149         if (arcHeight < 0) {
1150             arcHeight *= -1;
1151         }
1152 
1153         float as = (arcWidth/2.0f)  * (arcWidth/2.0f);
1154         float bs = (arcHeight/2.0f) * (arcHeight/2.0f);
1155 
1156         colour = foreground.getRGB();
1157 
1158         // draw top curved half of box
1159         for (int i = -arcHeight/2; i < 0; i++) {
1160             offset = (int) Math.sqrt( (1.0 - ((i*i)/bs)) * as );
1161             startX  = x - offset + arcWidth/2;
1162             endX    = x + offset + (w - arcWidth) + arcWidth/2;
1163 
1164             drawSpan(startX, y + i + (arcHeight/2), endX - startX + 1, colour);
1165         }
1166 
1167         // draw middle section
1168         for (int i = 0; i < h - arcHeight; i++) {
1169             drawSpan(x, y + i + arcHeight/2, w, colour);
1170         }
1171 
1172         // draw bottom box
1173         for (int i = 0; i <= arcHeight/2; i++) {
1174             offset = (int) Math.sqrt( (1.0 - ((i*i)/bs)) * as );
1175             startX  = x - offset + arcWidth/2;
1176             endX    = x + offset + (w - arcWidth) + arcWidth/2;
1177 
1178             drawSpan(startX, y + i + h - 1 - arcHeight/2, endX - startX + 1, colour);
1179         }
1180     }
1181 
drawStringN(long ftFace, String string, int x, int y, int rgb)1182     protected native void drawStringN(long ftFace, String string, int x, int y, int rgb);
1183 
1184     /** Draws the given string. */
drawString(String string, int x, int y)1185     public void drawString(String string, int x, int y) {
1186         getFontMetrics();
1187         if (fontMetrics != null) {
1188             fontMetrics.drawString((BDGraphics)this, string, x, y, foreground.getRGB());
1189         } else {
1190             logger.error("drawString skipped: no font metrics. string=\"" + string + "\"");
1191         }
1192     }
1193 
1194     /** Draws the given character array. */
drawChars(char chars[], int offset, int length, int x, int y)1195     public void drawChars(char chars[], int offset, int length, int x, int y) {
1196         drawString(new String(chars, offset, length), x, y);
1197     }
1198 
drawString(AttributedCharacterIterator arg0, int arg1, int arg2)1199     public void drawString(AttributedCharacterIterator arg0, int arg1, int arg2) {
1200         logger.unimplemented("drawString");
1201     }
1202 
1203     /**
1204      * Draws an image at x,y in nonblocking mode with a callback object.
1205      */
drawImage(Image img, int x, int y, ImageObserver observer)1206     public boolean drawImage(Image img, int x, int y, ImageObserver observer) {
1207         return drawImage(img, x, y, null, observer);
1208     }
1209 
1210     /**
1211      * Draws an image at x,y in nonblocking mode with a solid background
1212      * color and a callback object.
1213      */
drawImage(Image img, int x, int y, Color bg, ImageObserver observer)1214     public boolean drawImage(Image img, int x, int y, Color bg,
1215         ImageObserver observer) {
1216         return drawImageN(img, x, y, -1, -1, 0, 0, -1, -1, false, false, bg, observer);
1217     }
1218 
1219     /**
1220      * Draws an image scaled to x,y,w,h in nonblocking mode with a
1221      * callback object.
1222      */
drawImage(Image img, int x, int y, int w, int h, ImageObserver observer)1223     public boolean drawImage(Image img, int x, int y, int w, int h,
1224         ImageObserver observer) {
1225         return drawImage(img, x, y, w, h, null, observer);
1226     }
1227 
1228     /**
1229      * Draws an image scaled to x,y,w,h in nonblocking mode with a
1230      * solid background color and a callback object.
1231      */
drawImage(Image img, int x, int y, int w, int h, Color bg, ImageObserver observer)1232     public boolean drawImage(Image img, int x, int y, int w, int h,
1233         Color bg, ImageObserver observer) {
1234         return drawImageN(img, x, y, w, h, 0, 0, -1, -1, false, false, bg, observer);
1235     }
1236 
1237     /**
1238      * Draws a subrectangle of an image scaled to a destination rectangle
1239      * in nonblocking mode with a callback object.
1240      */
drawImage(Image img, int dx1, int dy1, int dx2, int dy2, int sx1, int sy1, int sx2, int sy2, ImageObserver observer)1241     public boolean drawImage(Image img,
1242         int dx1, int dy1, int dx2, int dy2,
1243         int sx1, int sy1, int sx2, int sy2,
1244         ImageObserver observer) {
1245         return drawImage(img, dx1, dy1, dx2, dy2, sx1, sy1, sx2, sy2, null, observer);
1246     }
1247 
1248     /**
1249      * Draws a subrectangle of an image scaled to a destination rectangle in
1250      * nonblocking mode with a solid background color and a callback object.
1251      */
drawImage(Image img, int dx1, int dy1, int dx2, int dy2, int sx1, int sy1, int sx2, int sy2, Color bg, ImageObserver observer)1252     public boolean drawImage(Image img,
1253         int dx1, int dy1, int dx2, int dy2,
1254         int sx1, int sy1, int sx2, int sy2,
1255         Color bg, ImageObserver observer) {
1256 
1257         boolean flipX = false;
1258         boolean flipY = false;
1259 
1260         if (dx1 > dx2) {
1261             int swap = dx1;
1262             dx1 = dx2;
1263             dx2 = swap;
1264             flipX = !flipX;
1265         }
1266 
1267         if (dy1 > dy2) {
1268             int swap = dy1;
1269             dy1 = dy2;
1270             dy2 = swap;
1271             flipY = !flipY;
1272         }
1273 
1274         if (sx1 > sx2) {
1275             int swap = sx1;
1276             sx1 = sx2;
1277             sx2 = swap;
1278             flipX = !flipX;
1279         }
1280 
1281         if (sy1 > sy2) {
1282             int swap = sy1;
1283             sy1 = sy2;
1284             sy2 = swap;
1285             flipY = !flipY;
1286         }
1287 
1288         return drawImageN(img, dx1, dy1, dx2 - dx1, dy2 - dy1,
1289                           sx1, sy1, sx2 - sx1, sy2 - sy1,
1290                           flipX, flipY, bg, observer);
1291     }
1292 
1293     /**
1294      * Draws a subrectangle of an image scaled to a destination rectangle in
1295      * nonblocking mode with a solid background color and a callback object.
1296      */
drawImageN(Image img, int dx, int dy, int dw, int dh, int sx, int sy, int sw, int sh, boolean flipX, boolean flipY, Color bg, ImageObserver observer)1297     protected boolean drawImageN(Image img,
1298         int dx, int dy, int dw, int dh,
1299         int sx, int sy, int sw, int sh,
1300         boolean flipX, boolean flipY,
1301         Color bg, ImageObserver observer) {
1302 
1303         if ((sw == 0) || (sh == 0) || (dw == 0) || (dh == 0))
1304             return false;
1305 
1306         BDImage bdImage;
1307         if (img instanceof BDImage) {
1308             bdImage = (BDImage)img;
1309         } else if (img instanceof DVBBufferedImage) {
1310             bdImage = (BDImage)getBufferedImagePeer(
1311                       (BufferedImage)(((DVBBufferedImage)img).getImage()));
1312         } else if (img instanceof BufferedImage) {
1313             bdImage = (BDImage)getBufferedImagePeer((BufferedImage)img);
1314         } else {
1315             logger.unimplemented("drawImageN: unsupported image type " + img.getClass().getName());
1316             return false;
1317         }
1318 
1319         if (bdImage instanceof BDImageConsumer) {
1320             BDImageConsumer consumer = (BDImageConsumer)bdImage;
1321             if (!consumer.isComplete(observer)) {
1322                 return false;
1323             }
1324         }
1325 
1326         if (sw < 0) sw = bdImage.width;
1327         if (sh < 0) sh = bdImage.height;
1328         if (dw < 0) dw = bdImage.width;
1329         if (dh < 0) dh = bdImage.height;
1330 
1331         int   stride   = bdImage.width;
1332         int[] rgbArray = bdImage.getBdBackBuffer();
1333         int   bgColor  = 0;
1334 
1335         if (bg != null) {
1336             bgColor = bg.getRGB();
1337         }
1338 
1339         // draw background colour
1340         for (int i = 0; i < dh && bg != null; i++) {
1341             drawSpan(dx, dy + i, dw, bgColor);
1342         }
1343 
1344         // resize if needed
1345         if (dw != sw || dh != sh) {
1346             drawResizeBilinear(rgbArray, (sy * stride) + sx, stride, sw, sh,
1347                                dx, dy, dw, dh, flipX, flipY);
1348             return true;
1349         }
1350 
1351         // draw actual colour array
1352         if (flipY) {
1353             for (int i = 0; i < dh; i++) {
1354                 drawSpan(dx, dy + dh - 1 - i, dw, rgbArray, (stride * (i + sy)) + sx, flipX);
1355             }
1356         } else {
1357             for (int i = 0; i < dh; i++) {
1358                 drawSpan(dx, dy + i, dw, rgbArray, (stride * (i + sy)) + sx, flipX);
1359             }
1360         }
1361 
1362         return true;
1363     }
1364 
1365     /**
1366      * Bilinear resize ARGB image.
1367      *
1368      * @param pixels Source image pixels.
1369      * @param sw Source image width.
1370      * @param sh Source image height.
1371      * @param dw New width.
1372      * @param dh New height.
1373      * @return New array with size dw * dh.
1374      */
1375     private int[] tmpLine = null;
drawResizeBilinear(int[] pixels, int offset, int scansize, int sw, int sh, int dx, int dy, int dw, int dh, boolean flipX, boolean flipY)1376     private void drawResizeBilinear(int[] pixels, int offset, int scansize, int sw, int sh,
1377                                     int dx, int dy, int dw, int dh, boolean flipX, boolean flipY) {
1378 
1379         if (sh == 1) {
1380             // crop source width if needed for 1d arrays
1381             if (offset + sw > pixels.length) {
1382                 sw = (pixels.length - offset);
1383             }
1384         } else {
1385             // crop source height to prevent possible over reads
1386             if (offset + (scansize*sh) > pixels.length) {
1387                 sh = (pixels.length - offset) / scansize;
1388             }
1389         }
1390         if (sw < 1 || sh < 1 || pixels.length < 1) {
1391             return;
1392         }
1393 
1394         if (sw == 1 && sh == 1) {
1395             for (int Y = dy; Y < (dy + dh); Y++)
1396                 drawSpan(dx, Y, dw, pixels[offset]);
1397             return;
1398         }
1399 
1400         // a quick hack for 1D arrays, stretch them to make them 2D
1401         if (sw == 1) {
1402             int[] temp = new int[2 * sh];
1403 
1404             for (int i = 0; i < sw * sh; i++) {
1405                 temp[(i * 2) + 0] = pixels[offset + i];
1406                 temp[(i * 2) + 1] = pixels[offset + i];
1407             }
1408 
1409             scansize = 2;
1410             pixels = temp;
1411             offset = 0;
1412             sw = 2;
1413 
1414         } else if (sh == 1) {
1415             int[] temp = new int[sw * 2];
1416 
1417             System.arraycopy(pixels, offset, temp,  0, sw);
1418             System.arraycopy(pixels, offset, temp, sw, sw);
1419 
1420             scansize = sw;
1421             pixels = temp;
1422             offset = 0;
1423             sh = 2;
1424         }
1425 
1426         if (tmpLine == null || tmpLine.length < dw + 1) {
1427             tmpLine = new int[Math.max(1920, dw + 1)];
1428         }
1429 
1430         int a, b, c, d, x, y, index;
1431         float x_ratio = ((float)(sw - 1)) / dw;
1432         float y_ratio = ((float)(sh - 1)) / dh;
1433         float x_diff, y_diff, blue, red, green, alpha;
1434         int position = 0;
1435         for (int i = 0; i < dh; i++) {
1436             for (int j = 0; j < dw; j++) {
1437                 x      = (int)(x_ratio * j);
1438                 y      = (int)(y_ratio * i);
1439                 x_diff = (x_ratio * j) - x;
1440                 y_diff = (y_ratio * i) - y;
1441                 index  = (y * scansize + x);
1442                 index += offset;
1443 
1444                 a = pixels[index];
1445                 b = pixels[index + 1];
1446                 c = pixels[index + scansize];
1447                 d = pixels[index + scansize + 1];
1448 
1449                 int aA = a >>> 24;
1450                 int bA = b >>> 24;
1451                 int cA = c >>> 24;
1452                 int dA = d >>> 24;
1453 
1454                 if (aA + bA + cA + dA < 1) {
1455                     tmpLine[position++] = 0;
1456                     continue;
1457                 }
1458 
1459                 /* calculate areas, weighted with alpha */
1460                 float aFactor = (1-x_diff) * (1-y_diff) * aA;
1461                 float bFactor = x_diff     * (1-y_diff) * bA;
1462                 float cFactor = (1-x_diff) * y_diff     * cA;
1463                 float dFactor = x_diff     * y_diff     * dA;
1464 
1465                 // alpha element
1466                 // Yr = Ar(1-w)(1-h) + Br(w)(1-h) + Cr(h)(1-w) + Dr(wh)
1467                 alpha = aFactor + bFactor + cFactor + dFactor;
1468 
1469                 // blue element
1470                 // Yb = Ab(1-w)(1-h) + Bb(w)(1-h) + Cb(h)(1-w) + Db(wh)
1471                 blue = (a & 0xff) * aFactor +
1472                        (b & 0xff) * bFactor +
1473                        (c & 0xff) * cFactor +
1474                        (d & 0xff) * dFactor;
1475 
1476                 // green element
1477                 // Yg = Ag(1-w)(1-h) + Bg(w)(1-h) + Cg(h)(1-w) + Dg(wh)
1478                 green = ((a >> 8) & 0xff) * aFactor +
1479                         ((b >> 8) & 0xff) * bFactor +
1480                         ((c >> 8) & 0xff) * cFactor +
1481                         ((d >> 8) & 0xff) * dFactor;
1482 
1483                 // red element
1484                 // Yr = Ar(1-w)(1-h) + Br(w)(1-h) + Cr(h)(1-w) + Dr(wh)
1485                 red = ((a >> 16) & 0xff) * aFactor +
1486                       ((b >> 16) & 0xff) * bFactor +
1487                       ((c >> 16) & 0xff) * cFactor +
1488                       ((d >> 16) & 0xff) * dFactor;
1489 
1490                 blue  /= alpha;
1491                 green /= alpha;
1492                 red   /= alpha;
1493 
1494                 tmpLine[position++] =
1495                     ((((int)alpha) << 24) & 0xff000000) |
1496                     ((((int)red  ) << 16) & 0x00ff0000) |
1497                     ((((int)green) << 8 ) & 0x0000ff00) |
1498                     ((((int)blue )      ) & 0x000000ff);
1499             }
1500 
1501             if (flipY) {
1502                 drawSpan(dx, dy + dh - 1 - i, dw, tmpLine, 0, flipX);
1503             } else {
1504                 drawSpan(dx, dy + i, dw, tmpLine, 0, flipX);
1505             }
1506 
1507             position = 0;
1508         }
1509     }
1510 
getStroke()1511     public Stroke getStroke() {
1512         logger.unimplemented("getStroke");
1513         throw new Error();
1514     }
1515 
setStroke(Stroke stroke)1516     public void setStroke(Stroke stroke) {
1517         logger.unimplemented("setStroke");
1518     }
1519 
dispose()1520     public void dispose() {
1521         tmpLine = null;
1522         font = null;
1523         fontMetrics = null;
1524         gc = null;
1525         backBuffer = null;
1526     }
1527 
toString()1528     public String toString() {
1529         return getClass().getName() + "[" + originX + "," + originY + "]";
1530     }
1531 
getBufferedImagePeer(BufferedImage image)1532     private static Image getBufferedImagePeer(BufferedImage image) {
1533         try {
1534             return (Image)bufferedImagePeer.get(image);
1535         } catch (IllegalArgumentException e) {
1536             logger.error("Failed getting buffered image peer: " + e + "\n" +
1537                          Logger.dumpStack(e));
1538         } catch (IllegalAccessException e) {
1539             logger.error("Failed getting buffered image peer: " + e + "\n" +
1540                          Logger.dumpStack(e));
1541         }
1542         return null;
1543     }
1544 
1545     private static Field bufferedImagePeer;
1546 
1547     static {
1548         try {
1549             Class c = Class.forName("java.awt.image.BufferedImage");
1550             bufferedImagePeer = c.getDeclaredField("peer");
1551             bufferedImagePeer.setAccessible(true);
1552         } catch (ClassNotFoundException e) {
1553             throw new AWTError("java.awt.image.BufferedImage not found");
1554         } catch (SecurityException e) {
1555             throw new AWTError("java.awt.image.BufferedImage.peer not accessible");
1556         } catch (NoSuchFieldException e) {
1557             throw new AWTError("java.awt.image.BufferedImage.peer not found");
1558         }
1559     }
1560 
1561     private static final Logger logger = Logger.getLogger(BDGraphics.class.getName());
1562 }
1563