Path: blob/master/src/java.desktop/share/classes/sun/awt/RepaintArea.java
41152 views
/*1* Copyright (c) 1999, 2018, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package sun.awt;2627import java.awt.Component;28import java.awt.Graphics;29import java.awt.Rectangle;30import java.awt.event.PaintEvent;3132/**33* The {@code RepaintArea} is a geometric construct created for the34* purpose of holding the geometry of several coalesced paint events.35* This geometry is accessed synchronously, although it is written such36* that painting may still be executed asynchronously.37*38* @author Eric Hawkes39* @since 1.340*/41public class RepaintArea {4243/**44* Maximum ratio of bounding rectangle to benefit for which45* both the vertical and horizontal unions are repainted.46* For smaller ratios the whole bounding rectangle is repainted.47* @see #paint48*/49private static final int MAX_BENEFIT_RATIO = 4;5051private static final int HORIZONTAL = 0;52private static final int VERTICAL = 1;53private static final int UPDATE = 2;5455private static final int RECT_COUNT = UPDATE + 1;5657private Rectangle[] paintRects = new Rectangle[RECT_COUNT];585960/**61* Constructs a new {@code RepaintArea}62* @since 1.363*/64public RepaintArea() {65}6667/**68* Constructs a new {@code RepaintArea} initialized to match69* the values of the specified RepaintArea.70*71* @param ra the {@code RepaintArea} from which to copy initial72* values to a newly constructed RepaintArea73* @since 1.374*/75private RepaintArea(RepaintArea ra) {76// This constructor is private because it should only be called77// from the cloneAndReset method78for (int i = 0; i < RECT_COUNT; i++) {79paintRects[i] = ra.paintRects[i];80}81}8283/**84* Adds a {@code Rectangle} to this {@code RepaintArea}.85* PAINT Rectangles are divided into mostly vertical and mostly horizontal.86* Each group is unioned together.87* UPDATE Rectangles are unioned.88*89* @param r the specified {@code Rectangle}90* @param id possible values PaintEvent.UPDATE or PaintEvent.PAINT91* @since 1.392*/93public synchronized void add(Rectangle r, int id) {94// Make sure this new rectangle has positive dimensions95if (r.isEmpty()) {96return;97}98int addTo = UPDATE;99if (id == PaintEvent.PAINT) {100addTo = (r.width > r.height) ? HORIZONTAL : VERTICAL;101}102if (paintRects[addTo] != null) {103paintRects[addTo].add(r);104} else {105paintRects[addTo] = new Rectangle(r);106}107}108109110/**111* Creates a new {@code RepaintArea} with the same geometry as this112* RepaintArea, then removes all of the geometry from this113* RepaintArea and restores it to an empty RepaintArea.114*115* @return ra a new {@code RepaintArea} having the same geometry as116* this RepaintArea.117* @since 1.3118*/119private synchronized RepaintArea cloneAndReset() {120RepaintArea ra = new RepaintArea(this);121for (int i = 0; i < RECT_COUNT; i++) {122paintRects[i] = null;123}124return ra;125}126127public boolean isEmpty() {128for (int i = 0; i < RECT_COUNT; i++) {129if (paintRects[i] != null) {130return false;131}132}133return true;134}135136/**137* Constrains the size of the repaint area to the passed in bounds.138*/139public synchronized void constrain(int x, int y, int w, int h) {140for (int i = 0; i < RECT_COUNT; i++) {141Rectangle rect = paintRects[i];142if (rect != null) {143if (rect.x < x) {144rect.width -= (x - rect.x);145rect.x = x;146}147if (rect.y < y) {148rect.height -= (y - rect.y);149rect.y = y;150}151int xDelta = rect.x + rect.width - x - w;152if (xDelta > 0) {153rect.width -= xDelta;154}155int yDelta = rect.y + rect.height - y - h;156if (yDelta > 0) {157rect.height -= yDelta;158}159if (rect.width <= 0 || rect.height <= 0) {160paintRects[i] = null;161}162}163}164}165166/**167* Marks the passed in region as not needing to be painted. It's possible168* this will do nothing.169*/170public synchronized void subtract(int x, int y, int w, int h) {171Rectangle subtract = new Rectangle(x, y, w, h);172for (int i = 0; i < RECT_COUNT; i++) {173if (subtract(paintRects[i], subtract)) {174if (paintRects[i] != null && paintRects[i].isEmpty()) {175paintRects[i] = null;176}177}178}179}180181/**182* Invokes paint and update on target Component with optimal183* rectangular clip region.184* If PAINT bounding rectangle is less than185* MAX_BENEFIT_RATIO times the benefit, then the vertical and horizontal unions are186* painted separately. Otherwise the entire bounding rectangle is painted.187*188* @param target Component to {@code paint} or {@code update}189* @since 1.4190*/191public void paint(Object target, boolean shouldClearRectBeforePaint) {192Component comp = (Component)target;193194if (isEmpty()) {195return;196}197198if (!comp.isVisible()) {199return;200}201202RepaintArea ra = this.cloneAndReset();203204if (!subtract(ra.paintRects[VERTICAL], ra.paintRects[HORIZONTAL])) {205subtract(ra.paintRects[HORIZONTAL], ra.paintRects[VERTICAL]);206}207208if (ra.paintRects[HORIZONTAL] != null && ra.paintRects[VERTICAL] != null) {209Rectangle paintRect = ra.paintRects[HORIZONTAL].union(ra.paintRects[VERTICAL]);210int square = paintRect.width * paintRect.height;211int benefit = square - ra.paintRects[HORIZONTAL].width212* ra.paintRects[HORIZONTAL].height - ra.paintRects[VERTICAL].width213* ra.paintRects[VERTICAL].height;214// if benefit is comparable with bounding box215if (MAX_BENEFIT_RATIO * benefit < square) {216ra.paintRects[HORIZONTAL] = paintRect;217ra.paintRects[VERTICAL] = null;218}219}220for (int i = 0; i < paintRects.length; i++) {221if (ra.paintRects[i] != null222&& !ra.paintRects[i].isEmpty())223{224// Should use separate Graphics for each paint() call,225// since paint() can change Graphics state for next call.226Graphics g = comp.getGraphics();227if (g != null) {228try {229g.setClip(ra.paintRects[i]);230if (i == UPDATE) {231updateComponent(comp, g);232} else {233if (shouldClearRectBeforePaint) {234g.clearRect( ra.paintRects[i].x,235ra.paintRects[i].y,236ra.paintRects[i].width,237ra.paintRects[i].height);238}239paintComponent(comp, g);240}241} finally {242g.dispose();243}244}245}246}247}248249/**250* Calls {@code Component.update(Graphics)} with given Graphics.251*/252protected void updateComponent(Component comp, Graphics g) {253if (comp != null) {254comp.update(g);255}256}257258/**259* Calls {@code Component.paint(Graphics)} with given Graphics.260*/261protected void paintComponent(Component comp, Graphics g) {262if (comp != null) {263comp.paint(g);264}265}266267/**268* Subtracts subtr from rect. If the result is rectangle269* changes rect and returns true. Otherwise false.270*/271static boolean subtract(Rectangle rect, Rectangle subtr) {272if (rect == null || subtr == null) {273return true;274}275Rectangle common = rect.intersection(subtr);276if (common.isEmpty()) {277return true;278}279if (rect.x == common.x && rect.y == common.y) {280if (rect.width == common.width) {281rect.y += common.height;282rect.height -= common.height;283return true;284} else285if (rect.height == common.height) {286rect.x += common.width;287rect.width -= common.width;288return true;289}290} else291if (rect.x + rect.width == common.x + common.width292&& rect.y + rect.height == common.y + common.height)293{294if (rect.width == common.width) {295rect.height -= common.height;296return true;297} else298if (rect.height == common.height) {299rect.width -= common.width;300return true;301}302}303return false;304}305306public String toString() {307return super.toString() + "[ horizontal=" + paintRects[0] +308" vertical=" + paintRects[1] +309" update=" + paintRects[2] + "]";310}311}312313314