src/demo/share/jfc/J2Ddemo/java2d/Surface.java
author prr
Mon, 14 May 2018 08:58:32 -0700
changeset 50146 0bb0e464ee76
permissions -rw-r--r--
8198991: Move Java2D demo to the open repository Reviewed-by: serb, jeff

/*
 *
 * Copyright (c) 2007, 2011, Oracle and/or its affiliates. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *   - Redistributions of source code must retain the above copyright
 *     notice, this list of conditions and the following disclaimer.
 *
 *   - Redistributions in binary form must reproduce the above copyright
 *     notice, this list of conditions and the following disclaimer in the
 *     documentation and/or other materials provided with the distribution.
 *
 *   - Neither the name of Oracle nor the names of its
 *     contributors may be used to endorse or promote products derived
 *     from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
package java2d;


import static java.awt.RenderingHints.KEY_ANTIALIASING;
import static java.awt.RenderingHints.KEY_RENDERING;
import static java.awt.RenderingHints.VALUE_ANTIALIAS_OFF;
import static java.awt.RenderingHints.VALUE_ANTIALIAS_ON;
import static java.awt.RenderingHints.VALUE_RENDER_QUALITY;
import static java.awt.RenderingHints.VALUE_RENDER_SPEED;
import java.awt.AlphaComposite;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Frame;
import java.awt.GradientPaint;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Paint;
import java.awt.Toolkit;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.image.BufferedImage;
import java.awt.image.DataBuffer;
import java.awt.image.DataBufferByte;
import java.awt.image.DataBufferInt;
import java.awt.image.DataBufferUShort;
import java.awt.image.DirectColorModel;
import java.awt.image.IndexColorModel;
import java.awt.image.Raster;
import java.awt.image.WritableRaster;
import java.awt.print.PageFormat;
import java.awt.print.Printable;
import java.awt.print.PrinterException;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JPanel;
import javax.swing.RepaintManager;


/**
 * Surface is the base class for the 2d rendering demos.  Demos must
 * implement the render() method. Subclasses for Surface are
 * AnimatingSurface, ControlsSurface and AnimatingControlsSurface.
 */
@SuppressWarnings("serial")
public abstract class Surface extends JPanel implements Printable {

    public Object AntiAlias = VALUE_ANTIALIAS_ON;
    public Object Rendering = VALUE_RENDER_SPEED;
    public AlphaComposite composite;
    public Paint texture;
    public String perfStr;            // PerformanceMonitor
    public BufferedImage bimg;
    public int imageType;
    public String name;
    public boolean clearSurface = true;
    // Demos using animated gif's that implement ImageObserver set dontThread.
    public boolean dontThread;
    public AnimatingSurface animating;
    protected long sleepAmount = 50;
    private long orig, start, frame;
    private Toolkit toolkit;
    private boolean perfMonitor, outputPerf;
    private int biw, bih;
    private boolean clearOnce;
    private boolean toBeInitialized = true;

    public Surface() {
        setDoubleBuffered(this instanceof AnimatingSurface);
        toolkit = getToolkit();
        name = this.getClass().getSimpleName();
        setImageType(0);

        // To launch an individual demo with the performance str output  :
        //    java -Dj2ddemo.perf= -cp J2Ddemo.jar demos.Clipping.ClipAnim
        try {
            if (System.getProperty("j2ddemo.perf") != null) {
                perfMonitor = outputPerf = true;
            }
        } catch (Exception ex) {
        }
        if (this instanceof AnimatingSurface) {
            animating = (AnimatingSurface) this;
        }
    }

    protected Image getImage(String name) {
        return DemoImages.getImage(name, this);
    }

    protected Font getFont(String name) {
        return DemoFonts.getFont(name);
    }

    public int getImageType() {
        return imageType;
    }

    public final void setImageType(int imgType) {
        if (imgType == 0) {
            imageType = 1;
        } else {
            imageType = imgType;
        }
        bimg = null;
    }

    public void setAntiAlias(boolean aa) {
        AntiAlias = aa ? VALUE_ANTIALIAS_ON : VALUE_ANTIALIAS_OFF;
    }

    public void setRendering(boolean rd) {
        Rendering = rd ? VALUE_RENDER_QUALITY : VALUE_RENDER_SPEED;
    }

    public void setTexture(Object obj) {
        if (obj instanceof GradientPaint) {
            texture = new GradientPaint(0, 0, Color.white,
                    getSize().width * 2, 0, Color.green);
        } else {
            texture = (Paint) obj;
        }
    }

    public void setComposite(boolean cp) {
        composite = cp
                ? AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.5f)
                : null;
    }

    public void setMonitor(boolean pm) {
        perfMonitor = pm;
    }

    public void setSleepAmount(long amount) {
        sleepAmount = amount;
    }

    public long getSleepAmount() {
        return sleepAmount;
    }

    public BufferedImage createBufferedImage(Graphics2D g2,
            int w,
            int h,
            int imgType) {
        BufferedImage bi = null;
        if (imgType == 0) {
            bi = g2.getDeviceConfiguration().
                    createCompatibleImage(w, h);
        } else if (imgType > 0 && imgType < 14) {
            bi = new BufferedImage(w, h, imgType);
        } else if (imgType == 14) {
            bi = createBinaryImage(w, h, 2);
        } else if (imgType == 15) {
            bi = createBinaryImage(w, h, 4);
        } else if (imgType == 16) {
            bi = createSGISurface(w, h, 32);
        } else if (imgType == 17) {
            bi = createSGISurface(w, h, 16);
        }
        return bi;
    }
    // Lookup tables for BYTE_BINARY 1, 2 and 4 bits.
    private static final byte[] lut1Arr = new byte[] { 0, (byte) 255 };
    private static final byte[] lut2Arr = new byte[] { 0, (byte) 85, (byte) 170, (byte) 255 };
    private static final byte[] lut4Arr = new byte[] { 0, (byte) 17, (byte) 34, (byte) 51,
        (byte) 68, (byte) 85, (byte) 102, (byte) 119,
        (byte) 136, (byte) 153, (byte) 170, (byte) 187,
        (byte) 204, (byte) 221, (byte) 238, (byte) 255 };

    private BufferedImage createBinaryImage(int w, int h, int pixelBits) {
        int bytesPerRow = w * pixelBits / 8;
        if (w * pixelBits % 8 != 0) {
            bytesPerRow++;
        }
        byte[] imageData = new byte[h * bytesPerRow];
        IndexColorModel cm = null;
        switch (pixelBits) {
            case 1:
                cm = new IndexColorModel(pixelBits, lut1Arr.length,
                        lut1Arr, lut1Arr, lut1Arr);
                break;
            case 2:
                cm = new IndexColorModel(pixelBits, lut2Arr.length,
                        lut2Arr, lut2Arr, lut2Arr);
                break;
            case 4:
                cm = new IndexColorModel(pixelBits, lut4Arr.length,
                        lut4Arr, lut4Arr, lut4Arr);
                break;
            default:
                Logger.getLogger(Surface.class.getName()).log(Level.SEVERE,
                        null, new Exception("Invalid # of bit per pixel"));
        }

        DataBuffer db = new DataBufferByte(imageData, imageData.length);
        WritableRaster r = Raster.createPackedRaster(db, w, h, pixelBits, null);
        return new BufferedImage(cm, r, false, null);
    }

    private BufferedImage createSGISurface(int w, int h, int pixelBits) {
        int rMask32 = 0xFF000000;
        int rMask16 = 0xF800;
        int gMask32 = 0x00FF0000;
        int gMask16 = 0x07C0;
        int bMask32 = 0x0000FF00;
        int bMask16 = 0x003E;

        DirectColorModel dcm = null;
        DataBuffer db = null;
        WritableRaster wr = null;
        switch (pixelBits) {
            case 16:
                short[] imageDataUShort = new short[w * h];
                dcm = new DirectColorModel(16, rMask16, gMask16, bMask16);
                db = new DataBufferUShort(imageDataUShort,
                        imageDataUShort.length);
                wr = Raster.createPackedRaster(db, w, h, w,
                        new int[] { rMask16, gMask16, bMask16 },
                        null);
                break;
            case 32:
                int[] imageDataInt = new int[w * h];
                dcm = new DirectColorModel(32, rMask32, gMask32, bMask32);
                db = new DataBufferInt(imageDataInt, imageDataInt.length);
                wr = Raster.createPackedRaster(db, w, h, w,
                        new int[] { rMask32, gMask32, bMask32 },
                        null);
                break;
            default:
                Logger.getLogger(Surface.class.getName()).log(Level.SEVERE,
                        null, new Exception("Invalid # of bit per pixel"));
        }

        return new BufferedImage(dcm, wr, false, null);
    }

    public Graphics2D createGraphics2D(int width,
            int height,
            BufferedImage bi,
            Graphics g) {

        Graphics2D g2 = null;

        if (bi != null) {
            g2 = bi.createGraphics();
        } else {
            g2 = (Graphics2D) g;
        }

        g2.setBackground(getBackground());
        g2.setRenderingHint(KEY_ANTIALIASING, AntiAlias);
        g2.setRenderingHint(KEY_RENDERING, Rendering);

        if (clearSurface || clearOnce) {
            g2.clearRect(0, 0, width, height);
            clearOnce = false;
        }

        if (texture != null) {
            // set composite to opaque for texture fills
            g2.setComposite(AlphaComposite.SrcOver);
            g2.setPaint(texture);
            g2.fillRect(0, 0, width, height);
        }

        if (composite != null) {
            g2.setComposite(composite);
        }

        return g2;
    }

    // ...demos that extend Surface must implement this routine...
    public abstract void render(int w, int h, Graphics2D g2);

    /**
     * It's possible to turn off double-buffering for just the repaint
     * calls invoked directly on the non double buffered component.
     * This can be done by overriding paintImmediately() (which is called
     * as a result of repaint) and getting the current RepaintManager and
     * turning off double buffering in the RepaintManager before calling
     * super.paintImmediately(g).
     */
    @Override
    public void paintImmediately(int x, int y, int w, int h) {
        RepaintManager repaintManager = null;
        boolean save = true;
        if (!isDoubleBuffered()) {
            repaintManager = RepaintManager.currentManager(this);
            save = repaintManager.isDoubleBufferingEnabled();
            repaintManager.setDoubleBufferingEnabled(false);
        }
        super.paintImmediately(x, y, w, h);

        if (repaintManager != null) {
            repaintManager.setDoubleBufferingEnabled(save);
        }
    }

    @Override
    public void paint(Graphics g) {

        super.paint(g);

        Dimension d = getSize();

        if (biw != d.width || bih != d.height) {
            toBeInitialized = true;
            biw = d.width;
            bih = d.height;
        }

        if (imageType == 1) {
            bimg = null;
        } else if (bimg == null || toBeInitialized) {
            bimg = createBufferedImage((Graphics2D) g,
                    d.width, d.height, imageType - 2);
            clearOnce = true;
        }

        if (toBeInitialized) {
            if (animating != null) {
                animating.reset(d.width, d.height);
            }
            toBeInitialized = false;
            startClock();
        }

        if (animating != null && animating.running()) {
            animating.step(d.width, d.height);
        }
        Graphics2D g2 = createGraphics2D(d.width, d.height, bimg, g);
        render(d.width, d.height, g2);
        g2.dispose();

        if (bimg != null) {
            g.drawImage(bimg, 0, 0, null);
            toolkit.sync();
        }

        if (perfMonitor) {
            LogPerformance();
        }
    }

    @Override
    public int print(Graphics g, PageFormat pf, int pi) throws PrinterException {
        if (pi >= 1) {
            return Printable.NO_SUCH_PAGE;
        }

        Graphics2D g2d = (Graphics2D) g;
        g2d.translate(pf.getImageableX(), pf.getImageableY());
        g2d.translate(pf.getImageableWidth() / 2,
                pf.getImageableHeight() / 2);

        Dimension d = getSize();

        double scale = Math.min(pf.getImageableWidth() / d.width,
                pf.getImageableHeight() / d.height);
        if (scale < 1.0) {
            g2d.scale(scale, scale);
        }

        g2d.translate(-d.width / 2.0, -d.height / 2.0);

        if (bimg == null) {
            Graphics2D g2 = createGraphics2D(d.width, d.height, null, g2d);
            render(d.width, d.height, g2);
            g2.dispose();
        } else {
            g2d.drawImage(bimg, 0, 0, this);
        }

        return Printable.PAGE_EXISTS;
    }

    public void startClock() {
        orig = System.currentTimeMillis();
        start = orig;
        frame = 0;
    }
    private static final int REPORTFRAMES = 30;

    private void LogPerformance() {
        if ((frame % REPORTFRAMES) == 0) {
            long end = System.currentTimeMillis();
            long rel = (end - start);
            if (frame == 0) {
                perfStr = name + " " + rel + " ms";
                if (animating == null || !animating.running()) {
                    frame = -1;
                }
            } else {
                String s1 = Float.toString((REPORTFRAMES / (rel / 1000.0f)));
                s1 = (s1.length() < 4) ? s1.substring(0, s1.length()) : s1.
                        substring(0, 4);
                perfStr = name + " " + s1 + " fps";
            }
            if (outputPerf) {
                System.out.println(perfStr);
            }
            start = end;
        }
        ++frame;
    }

    // System.out graphics state information.
    public void verbose(GlobalControls controls) {
        String str = "  " + name + " ";
        if (animating != null && animating.running()) {
            str = str.concat(" Running");
        } else if (this instanceof AnimatingSurface) {
            str = str.concat(" Stopped");
        }

        if (controls != null) {
            str = str.concat(" " + controls.screenCombo.getSelectedItem());
        }

        str.concat((AntiAlias == VALUE_ANTIALIAS_ON) ? " ANTIALIAS_ON "
                : " ANTIALIAS_OFF ");
        str.concat((Rendering == VALUE_RENDER_QUALITY) ? "RENDER_QUALITY "
                : "RENDER_SPEED ");

        if (texture != null) {
            str = str.concat("Texture ");
        }

        if (composite != null) {
            str = str.concat("Composite=" + composite.getAlpha() + " ");
        }

        Runtime r = Runtime.getRuntime();
        r.gc();
        float freeMemory = r.freeMemory();
        float totalMemory = r.totalMemory();
        str = str.concat(((totalMemory - freeMemory) / 1024) + "K used");
        System.out.println(str);
    }

    public static void createDemoFrame(Surface surface) {
        final DemoPanel dp = new DemoPanel(surface, new DemoInstVarsAccessorImplBase());
        Frame f = new Frame("J2D Demo - " + surface.name);
        f.addWindowListener(new WindowAdapter() {

            @Override
            public void windowClosing(WindowEvent e) {
                System.exit(0);
            }

            @Override
            public void windowDeiconified(WindowEvent e) {
                dp.start();
            }

            @Override
            public void windowIconified(WindowEvent e) {
                dp.stop();
            }
        });
        f.add("Center", dp);
        f.pack();
        f.setSize(new Dimension(500, 300));
        f.setVisible(true);
        if (surface.animating != null) {
            surface.animating.start();
        }
    }
}