Package javax.swing

Source Code of javax.swing.RepaintManager

/*
*  Licensed to the Apache Software Foundation (ASF) under one or more
*  contributor license agreements.  See the NOTICE file distributed with
*  this work for additional information regarding copyright ownership.
*  The ASF licenses this file to You under the Apache License, Version 2.0
*  (the "License"); you may not use this file except in compliance with
*  the License.  You may obtain a copy of the License at
*
*     http://www.apache.org/licenses/LICENSE-2.0
*
*  Unless required by applicable law or agreed to in writing, software
*  distributed under the License is distributed on an "AS IS" BASIS,
*  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*  See the License for the specific language governing permissions and
*  limitations under the License.
*/

/**
* @author Anton Avtamonov
*/
package javax.swing;

import java.awt.Color;
import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.GraphicsEnvironment;
import java.awt.Image;
import java.awt.Rectangle;
import java.awt.Toolkit;
import java.awt.Window;
import java.awt.image.VolatileImage;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.harmony.awt.ClipRegion;
import org.apache.harmony.awt.ComponentInternals;
import org.apache.harmony.awt.gl.MultiRectArea;

public class RepaintManager {
    private Set invalidRoots = Collections.synchronizedSet(new HashSet());
    private Image offscreenImage;
    private VolatileImage volatileOffscreenImage;

    private Dimension maximumSize;
    private boolean doubleBufferingEnabled = true;
    private Map dirtyRegions = new Hashtable();
    private Map optimizedDirtyRegions = new HashMap();
    private int numberOfScheduledPaintEvents;

    private static final Rectangle COMPLETELY_DIRTY_RECT = new Rectangle(0, 0, Integer.MAX_VALUE, Integer.MAX_VALUE);

    private static RepaintManager instance;
   
    public RepaintManager() {
        maximumSize = GraphicsEnvironment.getLocalGraphicsEnvironment().isHeadlessInstance() ?
                new Dimension(0,0)
                : Toolkit.getDefaultToolkit().getScreenSize();
    }

    private final Runnable paintEvent = new Runnable() {
        public void run() {
            boolean shouldPaint = false;
            synchronized(RepaintManager.this) {
                numberOfScheduledPaintEvents--;
                shouldPaint = numberOfScheduledPaintEvents == 0;
            }
            if (shouldPaint) {
                validateInvalidComponents();
                paintDirtyRegions();
            }
        }
    };


    public static RepaintManager currentManager(final Component c) {
        if (instance == null) {
            instance = new RepaintManager();
        }

        return instance;
    }

    public static RepaintManager currentManager(final JComponent c) {
        return currentManager((Component)c);
    }


    public static void setCurrentManager(final RepaintManager repaintManager) {
        instance = repaintManager;
    }

    /**
     * Method doesn't perform component invalidation. It just adds component
     * validation root to the list of roots waiting for validation and schedules
     * it.
     */
    public void addInvalidComponent(final JComponent invalidComponent) {
        // implementation is done according to the black-box testing and contradict to the
        // spec: component is not marked as invalid (needed layout)
        final Component root = getValidationRoot(invalidComponent);
        if (root != null && !invalidRoots.contains(root) && !root.isValid() && root.isShowing()) {
            invalidRoots.add(root);
            scheduleProcessingEvent();
        }
    }

    public void removeInvalidComponent(final JComponent component) {
        invalidRoots.remove(component);
    }

    public void validateInvalidComponents() {
        while(!invalidRoots.isEmpty()) {
            List processingRoots;
            synchronized(invalidRoots) {
                processingRoots = new ArrayList(invalidRoots);
                invalidRoots.clear();
            }
            for (Iterator it = processingRoots.iterator(); it.hasNext(); ) {
                Component c = (Component)it.next();
                c.validate();
            }
        }
    }

    public void addDirtyRegion(final JComponent c, final int x, final int y, final int w, final int h) {
        if (c == null || w <= 0 || h <= 0 || !c.isShowing()) {
            return;
        }

        Window ancestingWindow = SwingUtilities.getWindowAncestor(c);
        if (ancestingWindow == null || !ComponentInternals.getComponentInternals().wasPainted(ancestingWindow)) {
            return;
        }

        Rectangle dirtyRect = new Rectangle(x, y, w, h);
        MultiRectArea previousValue = (MultiRectArea)dirtyRegions.get(c);
        if (previousValue != null) {
            previousValue.add(dirtyRect);
        } else {
            dirtyRegions.put(c, new MultiRectArea(dirtyRect));
        }

        scheduleProcessingEvent();
    }

    public Rectangle getDirtyRegion(final JComponent c) {
        MultiRectArea result = (MultiRectArea)dirtyRegions.get(c);
        return result != null ? result.getBounds() : new Rectangle();
    }

    public void markCompletelyDirty(final JComponent c) {
        addDirtyRegion(c, COMPLETELY_DIRTY_RECT.x, COMPLETELY_DIRTY_RECT.y, COMPLETELY_DIRTY_RECT.width, COMPLETELY_DIRTY_RECT.height);
    }

    public void markCompletelyClean(final JComponent c) {
        dirtyRegions.remove(c);
    }

    public boolean isCompletelyDirty(final JComponent c) {
        MultiRectArea dirtyRect = (MultiRectArea)dirtyRegions.get(c);
        if (dirtyRect == null) {
            return false;
        }
        Rectangle dirtyBounds = dirtyRect.getBounds();
        return dirtyBounds.width == COMPLETELY_DIRTY_RECT.width
               && dirtyBounds.height == COMPLETELY_DIRTY_RECT.height;
    }

    public void paintDirtyRegions() {
        prepareOptimizedDirtyRegions();
        for (Iterator it = optimizedDirtyRegions.entrySet().iterator(); it.hasNext(); ) {
            Map.Entry entry = (Map.Entry)it.next();
            MultiRectArea repaintRegion = (MultiRectArea)entry.getValue();
            if (!repaintRegion.isEmpty()) {
                ((JComponent)entry.getKey()).paintImmediately(new ClipRegion(repaintRegion));
            }
        }
    }

    // According to Spec as a offscreen buffer we can use offscreen image of any type,
    // which can keep own content. Our implementation of VolatileImage based on GDI Bitmap and
    // can't lost content. For performance reason as a offscreen buffer we use VolatileImage.
    public Image getOffscreenBuffer(final Component c, final int proposedWidth, final int proposedHeight) {
        int adjustedWidth = Math.min(proposedWidth, maximumSize.width);
        int adjustedHeight = Math.min(proposedHeight, maximumSize.height);

        if (offscreenImage != null
            && offscreenImage.getWidth(c) >= adjustedWidth
            && offscreenImage.getHeight(c) >= adjustedHeight) {

            fillImage(offscreenImage, c.getBackground(), adjustedWidth, adjustedHeight);
        } else {
            if (offscreenImage != null) {
                offscreenImage.flush();
            }

            offscreenImage = c.createVolatileImage(adjustedWidth, adjustedHeight);
        }

        return offscreenImage;
    }

    public Image getVolatileOffscreenBuffer(final Component c, final int proposedWidth, final int proposedHeight) {
        int adjustedWidth = Math.min(proposedWidth, maximumSize.width);
        int adjustedHeight = Math.min(proposedHeight, maximumSize.height);

        if (volatileOffscreenImage != null
            && volatileOffscreenImage.getWidth(c) >= adjustedWidth
            && volatileOffscreenImage.getHeight(c) >= adjustedHeight) {

            if (volatileOffscreenImage.contentsLost()) {
                volatileOffscreenImage.validate(c.getGraphicsConfiguration());
            }

            fillImage(volatileOffscreenImage, c.getBackground(), adjustedWidth, adjustedHeight);
        } else {
            if (volatileOffscreenImage != null) {
                volatileOffscreenImage.flush();
            }

            volatileOffscreenImage = c.createVolatileImage(adjustedWidth, adjustedHeight);
        }

        return volatileOffscreenImage;
    }

    public void setDoubleBufferMaximumSize(final Dimension d) {
        maximumSize = d;
    }

    public Dimension getDoubleBufferMaximumSize() {
        return maximumSize;
    }

    public void setDoubleBufferingEnabled(final boolean isEnabled) {
        doubleBufferingEnabled = isEnabled;
    }

    public boolean isDoubleBufferingEnabled() {
        return doubleBufferingEnabled;
    }


    /*
     * That is not the best way to do scheduling.
     * There are two issues with it:
     * 1. Each repaint() call puts an event to the queue. All such events excepting
     *    the latest one do nothing, but queue is loaded anyway
     * 2. There could be a case when no painting is done at all.
     *    Such situation could be if repaint() is scheduled regularly.
     *    <code>
     *    An example:
     *       final Runnable overload = new Runnable() {
     *           public void run() {
     *               SwingUtilities.invokeLater(this);
     *               component.repaint();
     *           }
     *       };
     *    </code>
     *    In this example component will never be painted since because every time
     *    repaint() is processed another repaint is sceduled and therefore effective
     *    painting is delayed.
     */
    private void scheduleProcessingEvent() {
        synchronized(this) {
            numberOfScheduledPaintEvents++;
        }
        EventQueue.invokeLater(paintEvent);
    }

    private Component getValidationRoot(final Component c) {
        if (c == null) {
            return null;
        }
        Component root = c;
        while (!(root instanceof JComponent)
                || !((JComponent) root).isValidateRoot()) {
            Container parent = root.getParent();

            if (parent == null) {
                break;
            } else {
                root = parent;
            }
        }
        return root;
    }

    private Map prepareOptimizedDirtyRegions() {
        optimizedDirtyRegions.clear();
        Set dirtyRegionsCopy;
        synchronized(dirtyRegions) {
            dirtyRegionsCopy = new HashSet(dirtyRegions.entrySet());
            dirtyRegions.clear();
        }
        for (Iterator dirties = dirtyRegionsCopy.iterator(); dirties.hasNext(); ) {
            Map.Entry entry = (Map.Entry)dirties.next();
            JComponent c = (JComponent)entry.getKey();
            MultiRectArea dirtyRect = (MultiRectArea)entry.getValue();
            dirtyRect.intersect(c.getVisibleRect());

            if (mergeWithParent(c, dirtyRect)) {
                continue;
            }
            if (mergeWithChildren(c, dirtyRect)) {
                continue;
            }
            optimizedDirtyRegions.put(c, dirtyRect);
        }

        return optimizedDirtyRegions;
    }

    private boolean mergeWithParent(final Component comp, final MultiRectArea compDirtyRegion) {
        Iterator optimized = optimizedDirtyRegions.entrySet().iterator();
        while (optimized.hasNext()) {
            Map.Entry optEntry = (Map.Entry)optimized.next();
            JComponent optC = (JComponent)optEntry.getKey();
            MultiRectArea optDirtyRegion = (MultiRectArea)optEntry.getValue();
            if (SwingUtilities.isDescendingFrom(comp, optC)) {
                ClipRegion.convertRegion(comp, compDirtyRegion, optC);
                optDirtyRegion.add(compDirtyRegion);
                return true;
            }
        }

        return false;
    }

    private boolean mergeWithChildren(final Component comp, final MultiRectArea compDirtyRegion) {
        Iterator optimized = optimizedDirtyRegions.entrySet().iterator();
        boolean foundChildren = false;
        while (optimized.hasNext()) {
            Map.Entry optEntry = (Map.Entry)optimized.next();
            JComponent optC = (JComponent)optEntry.getKey();
            MultiRectArea optDirtyRegion = (MultiRectArea)optEntry.getValue();
            if (SwingUtilities.isDescendingFrom(optC, comp)) {
                ClipRegion.convertRegion(optC, optDirtyRegion, comp);
                compDirtyRegion.add(optDirtyRegion);
                optimized.remove();
                foundChildren = true;
            }
        }
        if (foundChildren) {
            optimizedDirtyRegions.put(comp, compDirtyRegion);
        }

        return foundChildren;
    }


    private static void fillImage(final Image image, final Color c, final int width, final int height) {
        Graphics g = image.getGraphics();
        g.setColor(c);
        g.fillRect(0, 0, width, height);
        g.dispose();
    }
}
TOP

Related Classes of javax.swing.RepaintManager

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.