<commit_before>/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package net.sf.jaer.graphics;

import com.sun.opengl.util.j2d.TextRenderer;
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.util.ArrayList;
import java.util.Random;
import javax.media.opengl.*;
import java.nio.FloatBuffer;
import java.util.HashMap;
import java.util.logging.Logger;
import java.util.prefs.Preferences;
import javax.media.opengl.glu.GLU;
import javax.swing.JFrame;

/**
 * OpenGL display of 2d data as color image. See the main method for example of use.
 *
 *ImageDisplay should be OK for displaying the spectrogram and similar 2d arrays of float info.
 *
 *   You create a new ImageDisplay by using the static factory method createImageDisplay,
 * set the x,y size on it, and then you access the frame pixmap directly by settings RGB or gray values at
 * chosen x, y locations. It takes care of scaling, centering, etc for you.  When you are ready to show the frame,
 * call display() or repaint() on the ImageDisplay.
 *
 *
 *
 *<pre>

 *     public static void main(String[] args) {

final ImageDisplay disp = ImageDisplay.createOpenGLCanvas(); // makde a new ImageDisplay GLCanvas with default OpenGL capabilities
JFrame frame = new JFrame("ImageFrame");  // make a JFrame to hold it
frame.setPreferredSize(new Dimension(400, 400));  // set the window size
frame.getContentPane().add(disp, BorderLayout.CENTER); // add the GLCanvas to the center of the window
int size = 200;  // used later to define image size

disp.setSize(size,size); // set dimensions of image

disp.addKeyListener(new KeyAdapter() { // add some key listeners to the ImageDisplay

public void keyReleased(KeyEvent e) {
System.out.println(e.toString());
int k = e.getKeyCode();
if (k == KeyEvent.VK_ESCAPE || k == KeyEvent.VK_X) {
System.exit(0);
} else if (k == KeyEvent.VK_UP) {
disp.setSizeY(disp.getSizeY() * 2); // UP arrow incrases vertical dimension
} else if (k == KeyEvent.VK_DOWN) {
disp.setSizeY(disp.getSizeY() / 2);
} else if (k == KeyEvent.VK_RIGHT) {
disp.setSizeX(disp.getSizeX() * 2);
} else if (k == KeyEvent.VK_LEFT) {
disp.setSizeX(disp.getSizeX() / 2);
} else if (k == KeyEvent.VK_G) { // 'g' resets the frame to gray level 0.5f
disp.resetFrame(.5f);
}
}
});
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); // closing the frame exits
frame.pack(); // otherwise it wont fill up the display
frame.setVisible(true); // make the frame visible

disp.setxLabel("x label"); // add xaxis label and some tick markers
disp.addXTick(0, "0");
disp.addXTick(size, Integer.toString(size));
disp.addXTick(size / 2, Integer.toString(size / 2));

disp.setyLabel("y label"); // same for y axis
disp.addYTick(0, "0");
disp.addYTick(size, Integer.toString(size));
disp.addYTick(size / 2, Integer.toString(size / 2));


Random r = new Random();  // will use to fill display with noise

int frameCounter = 0; // iterate over frame updates
while (true) {
disp.checkPixmapAllocation(); // make sure we have a pixmaps (not resally necessary since setting size will allocate pixmap
int n = size * size;
float[] f = disp.getPixmapArray(); // get reference to pixmap array so we can set pixel values
int sx = disp.getSizeX(), sy = disp.getSizeY();
// randomly update all pixels
//                for (int x = 0; x < sx; x++) {
//                    for (int y = 0; y < sy; y++) {
//                        int ind = imageDisplay.getPixMapIndex(x, y);
//                        f[ind + 0] = r.nextFloat();
//                        f[ind + 1] = r.nextFloat();
//                        f[ind + 2] = r.nextFloat();
//                    }
//                }
// randomly select one color of one pixel to change
//                int ind = disp.getPixMapIndex(r.nextInt(disp.getSizeX()), r.nextInt(disp.getSizeY())) + r.nextInt(3);
//                f[ind] = r.nextFloat();
//
// randomly set one pixel to some gray level
//            disp.resetFrame(0);
//            int xx = r.nextInt(disp.getSizeX());
//            int yy = r.nextInt(disp.getSizeY());
//            disp.setPixmapGray(xx, yy, r.nextFloat());

// clear frame to black
//            disp.resetFrame(0);

// randomly set a pixel to some RGB value
int xx = r.nextInt(disp.getSizeX());
int yy = r.nextInt(disp.getSizeY());
disp.setPixmapRGB(xx, yy, r.nextFloat(), r.nextFloat(), r.nextFloat());

// randomly change axes font size
//            if(frameCounter%1000==0){
//                disp.setFontSize(r.nextInt(60));
//            }


disp.setTitleLabel("Frame " + (frameCounter++));

// ask for a repaint
disp.repaint();

}
}

 *
 *
</pre>
 * @author <NAME>
 */
public class ImageDisplay extends GLCanvas implements GLEventListener {

    protected Preferences prefs = Preferences.userNodeForPackage(ImageDisplay.class);
    protected Logger log = Logger.getLogger("ImageDisplay");
    private int fontSize = 20;
    private int sizeX = 0, sizeY = 0;
    /** The gray value. Default is 0. */
    private float grayValue = 0;
    /** The rendered pixel map, ordered by rgb/row/col. The first 3 elements are the RBB float values of the LL pixel (x=0,y=0). The next 3 are
     * the RGB of the second pixel from the left in the bottom row (x=1,y=0). Pixel (0,1) is at position starting at 3*(chip.getSizeX()).
     */
    protected FloatBuffer pixmap;
    private FloatBuffer grayBuffer;
    private GLU glu = new GLU();
    private TextRenderer textRenderer;
    public final int BORDER_SPACE_PIXELS_DEFAULT = 40;
    private int borderPixels =  BORDER_SPACE_PIXELS_DEFAULT;
    private boolean fillsVertically;
    private boolean fillsHorizontally;
    private final float ZCLIP = 1;
    /** The actual borders in model space around the chip area. */
    private Borders borders = new Borders();
    private String xLabel = null, yLabel = null, titleLabel = null;
    boolean reshapePending = false;
    private HashMap<Integer, String> xticks, yticks;
    private float[] textColor = new float[]{1, 1, 1};
    private ArrayList<Legend> legends = new ArrayList();

    /** Creates a new ImageDisplay, given some Open GL capabilities.
     *
     * @param caps the capabilities desired. See factory method.
     * @see #createOpenGLCanvas() for factory method with predefined capabilities.
     */
    public ImageDisplay(GLCapabilities caps) {
        super(caps);

        this.setLocale(java.util.Locale.US); // to avoid problems with other language support in JOGL

        // this.setSize(300,200);
        this.setVisible(true);

        addGLEventListener(this);

    }

    /** Factory method for creating an ImageDisplay with following capabilities:
     * <pre>
    caps.setDoubleBuffered(true);
    caps.setHardwareAccelerated(true);
    caps.setAlphaBits(8);
    caps.setRedBits(8);
    caps.setGreenBits(8);
    caps.setBlueBits(8);
    </pre>
     *
     * @return a new ImageDisplay
     */
    public static ImageDisplay createOpenGLCanvas() {
        // design capabilities of opengl canvas
        GLCapabilities caps = new GLCapabilities();
        caps.setDoubleBuffered(true);
        caps.setHardwareAccelerated(true);
        caps.setAlphaBits(8);
        caps.setRedBits(8);
        caps.setGreenBits(8);
        caps.setBlueBits(8);

        ImageDisplay trackDisplay = new ImageDisplay(caps);
        return trackDisplay;
    }

    @Override
    public void displayChanged(GLAutoDrawable drawable, boolean modeChanged, boolean deviceChanged) {
    }

    /** Called when the canvas is updated. To update the display, call either
     * <code>repaint()</code> or <code>display()</code>.
     * @param drawable the Open GL context.
     */
    @Override
    public synchronized void display(GLAutoDrawable drawable) {

        GL gl = this.getGL();
        if (reshapePending) {
            reshapePending = false;
            reshape(drawable, 0, 0, getWidth(), getHeight());
        }
        try {
            gl.glClear(GL.GL_COLOR_BUFFER_BIT);
            displayPixmap(drawable);
            drawText(gl);
            gl.glFlush();
        } catch (GLException e) {
            log.warning(e.toString());
        }
    }

    /** Called on initialization
     */
    @Override
    public void init(GLAutoDrawable drawable) {

//        log.info("init");

        GL gl = getGL();

        gl.setSwapInterval(1);
        gl.glShadeModel(GL.GL_FLAT);

        // clears to black
        gl.glClearColor(0, 0, 0, 0f);
        gl.glClear(GL.GL_COLOR_BUFFER_BIT);
        gl.glLoadIdentity();

        // color set to white, raster to LL  corner
        gl.glRasterPos3f(0, 0, 0);
        gl.glColor3f(1, 1, 1);


        textRenderer = new TextRenderer(new Font("SansSerif", Font.PLAIN, getFontSize()), true, true);

    }

    /** Displays the pixmap of pixel values.
     *
     * @param drawable
     */
    synchronized private void displayPixmap(GLAutoDrawable drawable) {
        GL gl = drawable.getGL();
        if (gl == null) {
            return;
        }

        checkGLError(gl, "before pixmap");
        final int wi = drawable.getWidth(), hi = drawable.getHeight();
        float scale = 1;
        if (fillsVertically) {// tall chip, use chip height
            scale = ((float) hi - 2 * borderPixels) / getSizeY();
        } else if (fillsHorizontally) {
            scale = ((float) wi - 2 * borderPixels) / getSizeX();
        }

        gl.glPixelZoom(scale, scale);
        gl.glRasterPos2f(-.5f, -.5f); // to LL corner of chip, but must be inside viewport or else it is ignored, breaks on zoom     if (zoom.isZoomEnabled() == false) {

        checkPixmapAllocation();
        {
            try {
                pixmap.rewind();
                gl.glDrawPixels(sizeX, sizeY, GL.GL_RGB, GL.GL_FLOAT, pixmap);
            } catch (IndexOutOfBoundsException e) {
                log.warning(e.toString());
            }
        }
//        FloatBuffer minMax=FloatBuffer.allocate(6);
//        gl.glGetMinmax(GL.GL_MINMAX, true, GL.GL_RGB, GL.GL_FLOAT, minMax);
//        gl.glDisable(GL.GL_MINMAX);
        checkGLError(gl, "after rendering image");


        // outline frame
        gl.glColor4f(0, 0, 1f, 0f);
        gl.glLineWidth(1f);
        {
            gl.glBegin(GL.GL_LINE_LOOP);
            final float o = .5f;
            final float w = sizeX - 1;
            final float h = sizeY - 1;
            gl.glVertex2f(-o, -o);
            gl.glVertex2f(w + o, -o);
            gl.glVertex2f(w + o, h + o);
            gl.glVertex2f(-o, h + o);
            gl.glEnd();
        }
        checkGLError(gl, "after rendering frame");
    }

    /** Utility method to check for GL errors. Prints stacked up errors up to a limit.
    @param g the GL context
    @param glu the GLU used to obtain the error strings
    @param msg an error message to log to e.g., show the context
     */
    private void checkGLError(GL g, String msg) {
        int error = g.glGetError();
        int nerrors = 3;
        while (error != GL.GL_NO_ERROR && nerrors-- != 0) {
            StackTraceElement[] trace = Thread.currentThread().getStackTrace();
            if (trace.length > 1) {
                String className = trace[2].getClassName();
                String methodName = trace[2].getMethodName();
                int lineNumber = trace[2].getLineNumber();
                log.warning("GL error number " + error + " " + glu.gluErrorString(error) + " : " + msg + " at " + className + "." + methodName + " (line " + lineNumber + ")");
            } else {
                log.warning("GL error number " + error + " " + glu.gluErrorString(error) + " : " + msg);
            }
            error = g.glGetError();
        }
    }

    /**
     * The rendered pixel map, ordered by rgb/row/col. The first 3 elements are the RBB float values of the LL pixel (x=0,y=0). The next 3 are
     * the RGB of the second pixel from the left in the bottom row (x=1,y=0). Pixel (0,1) is at position starting at 3*(chip.getSizeX()).
     *
     * @return the pixmap
     * @see #getPixmapArray()  to return a float[] array
     */
    public FloatBuffer getPixmap() {
        return pixmap;
    }

    /** Sets the pixel RGB value.
     *
     * @param x row
     * @param y column
     * @param rgb an array of RGB values in range 0-1.
     */
    synchronized public void setPixmapRGB(int x, int y, float[] rgb) {
        setPixmapPosition(x, y);
        pixmap.put(rgb);
    }

    /** Sets the pixel RGB value.
     * 
     * @param x row
     * @param y column
     * @param r red value, 0-1 range.
     * @param g green value, 0-1 range.
     * @param b blue value, 0-1 range.
     */
    synchronized public void setPixmapRGB(int x, int y, float r, float g, float b) {
        setPixmapPosition(x, y);
        pixmap.put(r);
        pixmap.put(g);
        pixmap.put(b);
    }

    /** Sets a gray value for pixel x,y. Convenience wrapper around setPixmapRGB.
     *
     * @param x
     * @param y
     * @param gray the gray value, in range 0-1.
     */
    synchronized public void setPixmapGray(int x, int y, float gray) {
        setPixmapPosition(x, y);
        pixmap.put(new float[]{gray, gray, gray});
    }
    /** Sets a gray value for pixel x,y. Convenience wrapper around setPixmapRGB.
     *
     * @param x
     * @param y
     * @param gray the gray value, in range 0-1.
     */
    synchronized public void changePixmapGrayValueBy(int x, int y, float grayChange) {
        setPixmapPosition(x, y);
        float[] rgb=new float[3];
        pixmap.get(rgb);
        rgb[0]+=grayChange;
        rgb[1]+=grayChange;
        rgb[2]+=grayChange;
        setPixmapPosition(x, y);
        pixmap.put(rgb);
    }

    private float[] rgb = new float[3];

    /** Returns a re-used float[] of pixmap RGB values at location x,y.
     *
     * @param x column
     * @param y row
     * @return float[3] array of RGB values.
     */
    synchronized public float[] getPixmapRGB(int x, int y) {
        setPixmapPosition(x, y);
        pixmap.get(rgb);
        return rgb;
    }

    /** Returns an int that can be used to index to a particular pixel's RGB start location in the pixmap.
     * The successive 3 entries are the float (0-1) RGB values.
     *
     * @param x pixel x, 0 is left side.
     * @param y pixel y, 0 is bottom.
     * @return index into pixmap.
     * @see #getPixmapArray()
     */
    public int getPixMapIndex(int x, int y) {
        return 3 * (x + y * sizeX);
    }

    /** Returns the pixmap 1-d array of pixel RGB values.
     *
     * @return the array.
     * @see #getPixMapIndex(int, int)
     */
    public float[] getPixmapArray() {
        return pixmap.array();
    }
    
    /** Sets the whole pixmap array - a float[] of the kind R1,G1,B1,R2,G2,...
     *
     * @param valueArray
     */
    public void setPixmapArray(float[] array) {
        checkPixmapAllocation();
        System.arraycopy(array, 0, pixmap.array(), 0, array.length);
        pixmap.rewind();
        pixmap.limit(array.length);
    }
    
    /** Sets the whole pixmap array in gray values - a float[] of the kind grey1, grey2,...
     *
     * @param valueArray
     */
    public void setPixmapGreyArray(float[] array) {
        checkPixmapAllocation();
        final int n = 3 * array.length;<commit_msg>Added a new method (setEventPixmapNbd) to ImageDisplay to facilitate event-based blurring using any desired kernel <commit_after>/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package net.sf.jaer.graphics;

import com.sun.opengl.util.j2d.TextRenderer;
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.util.ArrayList;
import java.util.Random;
import javax.media.opengl.*;
import java.nio.FloatBuffer;
import java.util.HashMap;
import java.util.logging.Logger;
import java.util.prefs.Preferences;
import javax.media.opengl.glu.GLU;
import javax.swing.JFrame;

/**
 * OpenGL display of 2d data as color image. See the main method for example of use.
 *
 *ImageDisplay should be OK for displaying the spectrogram and similar 2d arrays of float info.
 *
 *   You create a new ImageDisplay by using the static factory method createImageDisplay,
 * set the x,y size on it, and then you access the frame pixmap directly by settings RGB or gray values at
 * chosen x, y locations. It takes care of scaling, centering, etc for you.  When you are ready to show the frame,
 * call display() or repaint() on the ImageDisplay.
 *
 *
 *
 *<pre>

 *     public static void main(String[] args) {

final ImageDisplay disp = ImageDisplay.createOpenGLCanvas(); // makde a new ImageDisplay GLCanvas with default OpenGL capabilities
JFrame frame = new JFrame("ImageFrame");  // make a JFrame to hold it
frame.setPreferredSize(new Dimension(400, 400));  // set the window size
frame.getContentPane().add(disp, BorderLayout.CENTER); // add the GLCanvas to the center of the window
int size = 200;  // used later to define image size

disp.setSize(size,size); // set dimensions of image

disp.addKeyListener(new KeyAdapter() { // add some key listeners to the ImageDisplay

public void keyReleased(KeyEvent e) {
System.out.println(e.toString());
int k = e.getKeyCode();
if (k == KeyEvent.VK_ESCAPE || k == KeyEvent.VK_X) {
System.exit(0);
} else if (k == KeyEvent.VK_UP) {
disp.setSizeY(disp.getSizeY() * 2); // UP arrow incrases vertical dimension
} else if (k == KeyEvent.VK_DOWN) {
disp.setSizeY(disp.getSizeY() / 2);
} else if (k == KeyEvent.VK_RIGHT) {
disp.setSizeX(disp.getSizeX() * 2);
} else if (k == KeyEvent.VK_LEFT) {
disp.setSizeX(disp.getSizeX() / 2);
} else if (k == KeyEvent.VK_G) { // 'g' resets the frame to gray level 0.5f
disp.resetFrame(.5f);
}
}
});
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); // closing the frame exits
frame.pack(); // otherwise it wont fill up the display
frame.setVisible(true); // make the frame visible

disp.setxLabel("x label"); // add xaxis label and some tick markers
disp.addXTick(0, "0");
disp.addXTick(size, Integer.toString(size));
disp.addXTick(size / 2, Integer.toString(size / 2));

disp.setyLabel("y label"); // same for y axis
disp.addYTick(0, "0");
disp.addYTick(size, Integer.toString(size));
disp.addYTick(size / 2, Integer.toString(size / 2));


Random r = new Random();  // will use to fill display with noise

int frameCounter = 0; // iterate over frame updates
while (true) {
disp.checkPixmapAllocation(); // make sure we have a pixmaps (not resally necessary since setting size will allocate pixmap
int n = size * size;
float[] f = disp.getPixmapArray(); // get reference to pixmap array so we can set pixel values
int sx = disp.getSizeX(), sy = disp.getSizeY();
// randomly update all pixels
//                for (int x = 0; x < sx; x++) {
//                    for (int y = 0; y < sy; y++) {
//                        int ind = imageDisplay.getPixMapIndex(x, y);
//                        f[ind + 0] = r.nextFloat();
//                        f[ind + 1] = r.nextFloat();
//                        f[ind + 2] = r.nextFloat();
//                    }
//                }
// randomly select one color of one pixel to change
//                int ind = disp.getPixMapIndex(r.nextInt(disp.getSizeX()), r.nextInt(disp.getSizeY())) + r.nextInt(3);
//                f[ind] = r.nextFloat();
//
// randomly set one pixel to some gray level
//            disp.resetFrame(0);
//            int xx = r.nextInt(disp.getSizeX());
//            int yy = r.nextInt(disp.getSizeY());
//            disp.setPixmapGray(xx, yy, r.nextFloat());

// clear frame to black
//            disp.resetFrame(0);

// randomly set a pixel to some RGB value
int xx = r.nextInt(disp.getSizeX());
int yy = r.nextInt(disp.getSizeY());
disp.setPixmapRGB(xx, yy, r.nextFloat(), r.nextFloat(), r.nextFloat());

// randomly change axes font size
//            if(frameCounter%1000==0){
//                disp.setFontSize(r.nextInt(60));
//            }


disp.setTitleLabel("Frame " + (frameCounter++));

// ask for a repaint
disp.repaint();

}
}

 *
 *
</pre>
 * @author <NAME>
 */
public class ImageDisplay extends GLCanvas implements GLEventListener {

   
    protected Preferences prefs = Preferences.userNodeForPackage(ImageDisplay.class);
    protected Logger log = Logger.getLogger("ImageDisplay");
    private int fontSize = 20;
    private int sizeX = 0, sizeY = 0;
    /** The gray value. Default is 0. */
    private float grayValue = 0;
    /** The rendered pixel map, ordered by rgb/row/col. The first 3 elements are the RBB float values of the LL pixel (x=0,y=0). The next 3 are
     * the RGB of the second pixel from the left in the bottom row (x=1,y=0). Pixel (0,1) is at position starting at 3*(chip.getSizeX()).
     */
    protected FloatBuffer pixmap;
    private FloatBuffer grayBuffer;
    private GLU glu = new GLU();
    private TextRenderer textRenderer;
    public final int BORDER_SPACE_PIXELS_DEFAULT = 40;
    private int borderPixels =  BORDER_SPACE_PIXELS_DEFAULT;
    private boolean fillsVertically;
    private boolean fillsHorizontally;
    private final float ZCLIP = 1;
    /** The actual borders in model space around the chip area. */
    private Borders borders = new Borders();
    private String xLabel = null, yLabel = null, titleLabel = null;
    boolean reshapePending = false;
    private HashMap<Integer, String> xticks, yticks;
    private float[] textColor = new float[]{1, 1, 1};
    private ArrayList<Legend> legends = new ArrayList();

    /** Creates a new ImageDisplay, given some Open GL capabilities.
     *
     * @param caps the capabilities desired. See factory method.
     * @see #createOpenGLCanvas() for factory method with predefined capabilities.
     */
    public ImageDisplay(GLCapabilities caps) {
        super(caps);

        this.setLocale(java.util.Locale.US); // to avoid problems with other language support in JOGL

        // this.setSize(300,200);
        this.setVisible(true);

        addGLEventListener(this);

    }

    /** Factory method for creating an ImageDisplay with following capabilities:
     * <pre>
    caps.setDoubleBuffered(true);
    caps.setHardwareAccelerated(true);
    caps.setAlphaBits(8);
    caps.setRedBits(8);
    caps.setGreenBits(8);
    caps.setBlueBits(8);
    </pre>
     *
     * @return a new ImageDisplay
     */
    public static ImageDisplay createOpenGLCanvas() {
        // design capabilities of opengl canvas
        GLCapabilities caps = new GLCapabilities();
        caps.setDoubleBuffered(true);
        caps.setHardwareAccelerated(true);
        caps.setAlphaBits(8);
        caps.setRedBits(8);
        caps.setGreenBits(8);
        caps.setBlueBits(8);

        ImageDisplay trackDisplay = new ImageDisplay(caps);
        return trackDisplay;
    }

    @Override
    public void displayChanged(GLAutoDrawable drawable, boolean modeChanged, boolean deviceChanged) {
    }

    /** Called when the canvas is updated. To update the display, call either
     * <code>repaint()</code> or <code>display()</code>.
     * @param drawable the Open GL context.
     */
    @Override
    public synchronized void display(GLAutoDrawable drawable) {

        GL gl = this.getGL();
        if (reshapePending) {
            reshapePending = false;
            reshape(drawable, 0, 0, getWidth(), getHeight());
        }
        try {
            gl.glClear(GL.GL_COLOR_BUFFER_BIT);
            displayPixmap(drawable);
            drawText(gl);
            gl.glFlush();
        } catch (GLException e) {
            log.warning(e.toString());
        }
    }

    /** Called on initialization
     */
    @Override
    public void init(GLAutoDrawable drawable) {

//        log.info("init");

        GL gl = getGL();

        gl.setSwapInterval(1);
        gl.glShadeModel(GL.GL_FLAT);

        // clears to black
        gl.glClearColor(0, 0, 0, 0f);
        gl.glClear(GL.GL_COLOR_BUFFER_BIT);
        gl.glLoadIdentity();

        // color set to white, raster to LL  corner
        gl.glRasterPos3f(0, 0, 0);
        gl.glColor3f(1, 1, 1);


        textRenderer = new TextRenderer(new Font("SansSerif", Font.PLAIN, getFontSize()), true, true);

    }

    /** Displays the pixmap of pixel values.
     *
     * @param drawable
     */
    synchronized private void displayPixmap(GLAutoDrawable drawable) {
        GL gl = drawable.getGL();
        if (gl == null) {
            return;
        }

        checkGLError(gl, "before pixmap");
        final int wi = drawable.getWidth(), hi = drawable.getHeight();
        float scale = 1;
        if (fillsVertically) {// tall chip, use chip height
            scale = ((float) hi - 2 * borderPixels) / getSizeY();
        } else if (fillsHorizontally) {
            scale = ((float) wi - 2 * borderPixels) / getSizeX();
        }

        gl.glPixelZoom(scale, scale);
        gl.glRasterPos2f(-.5f, -.5f); // to LL corner of chip, but must be inside viewport or else it is ignored, breaks on zoom     if (zoom.isZoomEnabled() == false) {

        checkPixmapAllocation();
        {
            try {
                pixmap.rewind();
                gl.glDrawPixels(sizeX, sizeY, GL.GL_RGB, GL.GL_FLOAT, pixmap);
            } catch (IndexOutOfBoundsException e) {
                log.warning(e.toString());
            }
        }
//        FloatBuffer minMax=FloatBuffer.allocate(6);
//        gl.glGetMinmax(GL.GL_MINMAX, true, GL.GL_RGB, GL.GL_FLOAT, minMax);
//        gl.glDisable(GL.GL_MINMAX);
        checkGLError(gl, "after rendering image");


        // outline frame
        gl.glColor4f(0, 0, 1f, 0f);
        gl.glLineWidth(1f);
        {
            gl.glBegin(GL.GL_LINE_LOOP);
            final float o = .5f;
            final float w = sizeX - 1;
            final float h = sizeY - 1;
            gl.glVertex2f(-o, -o);
            gl.glVertex2f(w + o, -o);
            gl.glVertex2f(w + o, h + o);
            gl.glVertex2f(-o, h + o);
            gl.glEnd();
        }
        checkGLError(gl, "after rendering frame");
    }

    /** Utility method to check for GL errors. Prints stacked up errors up to a limit.
    @param g the GL context
    @param glu the GLU used to obtain the error strings
    @param msg an error message to log to e.g., show the context
     */
    private void checkGLError(GL g, String msg) {
        int error = g.glGetError();
        int nerrors = 3;
        while (error != GL.GL_NO_ERROR && nerrors-- != 0) {
            StackTraceElement[] trace = Thread.currentThread().getStackTrace();
            if (trace.length > 1) {
                String className = trace[2].getClassName();
                String methodName = trace[2].getMethodName();
                int lineNumber = trace[2].getLineNumber();
                log.warning("GL error number " + error + " " + glu.gluErrorString(error) + " : " + msg + " at " + className + "." + methodName + " (line " + lineNumber + ")");
            } else {
                log.warning("GL error number " + error + " " + glu.gluErrorString(error) + " : " + msg);
            }
            error = g.glGetError();
        }
    }

    /**
     * The rendered pixel map, ordered by rgb/row/col. The first 3 elements are the RBB float values of the LL pixel (x=0,y=0). The next 3 are
     * the RGB of the second pixel from the left in the bottom row (x=1,y=0). Pixel (0,1) is at position starting at 3*(chip.getSizeX()).
     *
     * @return the pixmap
     * @see #getPixmapArray()  to return a float[] array
     */
    public FloatBuffer getPixmap() {
        return pixmap;
    }

    /** Sets the pixel RGB value.
     *
     * @param x row
     * @param y column
     * @param rgb an array of RGB values in range 0-1.
     */
    synchronized public void setPixmapRGB(int x, int y, float[] rgb) {
        setPixmapPosition(x, y);
        pixmap.put(rgb);
    }

    /** Sets the pixel RGB value.
     * 
     * @param x row
     * @param y column
     * @param r red value, 0-1 range.
     * @param g green value, 0-1 range.
     * @param b blue value, 0-1 range.
     */
    synchronized public void setPixmapRGB(int x, int y, float r, float g, float b) {
        setPixmapPosition(x, y);
        pixmap.put(r);
        pixmap.put(g);
        pixmap.put(b);
    }

    /** Sets a gray value for pixel x,y. Convenience wrapper around setPixmapRGB.
     *
     * @param x
     * @param y
     * @param gray the gray value, in range 0-1.
     */
    synchronized public void setPixmapGray(int x, int y, float gray) {
        setPixmapPosition(x, y);
        pixmap.put(new float[]{gray, gray, gray});
    }
    /** Sets a gray value for pixel x,y. Convenience wrapper around setPixmapRGB.
     *
     * @param x
     * @param y
     * @param gray the gray value, in range 0-1.
     */
    synchronized public void changePixmapGrayValueBy(int x, int y, float grayChange) {
        setPixmapPosition(x, y);
        float[] rgb=new float[3];
        pixmap.get(rgb);
        rgb[0]+=grayChange;
        rgb[1]+=grayChange;
        rgb[2]+=grayChange;
        setPixmapPosition(x, y);
        pixmap.put(rgb);
    }

    private float[] rgb = new float[3];

    /** Returns a re-used float[] of pixmap RGB values at location x,y.
     *
     * @param x column
     * @param y row
     * @return float[3] array of RGB values.
     */
    synchronized public float[] getPixmapRGB(int x, int y) {
        setPixmapPosition(x, y);
        pixmap.get(rgb);
        return rgb;
    }

    /** Returns an int that can be used to index to a particular pixel's RGB start location in the pixmap.
     * The successive 3 entries are the float (0-1) RGB values.
     *
     * @param x pixel x, 0 is left side.
     * @param y pixel y, 0 is bottom.
     * @return index into pixmap.
     * @see #getPixmapArray()
     */
    public int getPixMapIndex(int x, int y) {
        return 3 * (x + y * sizeX);
    }

    /** Returns the pixmap 1-d array of pixel RGB values.
     *
     * @return the array.
     * @see #getPixMapIndex(int, int)
     */
    public float[] getPixmapArray() {
        return pixmap.array();
    }
    
    /** Sets the whole pixmap array - a float[] of the kind R1,G1,B1,R2,G2,...
     *
     * @param valueArray
     */    
    
    public void setPixmapArray(float[] array){
        checkPixmapAllocation();
        System.arraycopy(array, 0, pixmap.array(), 0, array.length);
        pixmap.rewind();
        pixmap.limit(array.length);
    }
    
    /** Updates pixmap values with the values in array only in the region that comes within the blurring kernel, centred at x, y.
     * 
     * @param x
     * @param y
     * @param kernelextent is the half the width of the kernel
     * @param chipsize
     * @param array 
     */
    public void setEventPixmapNbd(int x, int y, int kernelextent, int chipsize, float[] array) {
        checkPixmapAllocation();
        
        for(int i = -kernelextent; i < kernelextent+1; i++){            
            System.arraycopy(array, (3*((x-kernelextent)+((y+i) * chipsize))), pixmap.array(), (3*((x-kernelextent)+((y+i) * chipsize))), (3*(2*kernelextent + 1)));            
        }
        
        pixmap.rewind();
    }
    
    /** Sets the whole pixmap array in gray values - a float[] of the kind grey1, grey2,...
     *
     * @param valueArray
     */
    public void setPixmapGreyArray(float[] array) {
        checkPixmapAllocation();
        final int n = 3 * array.length;
{'fraction_non_alphanumeric': 0.11978626434071979, 'fraction_numerical': 0.010623919534810624, 'mean_word_length': 2.375344791003607, 'pattern_counts': {'":': 0, '<': 30, '<?xml version=': 0, '>': 27, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5205807', 'n_tokens_mistral': 10252, 'n_tokens_neox': 9743, 'n_words': 3020}