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