Search in sources :

Example 1 with BufferUtil

use of org.csstudio.javafx.BufferUtil in project org.csstudio.display.builder by kasemir.

the class RTTank method updateImageBuffer.

/**
 * Draw all components into image buffer
 */
protected Image updateImageBuffer() {
    final Rectangle area_copy = area;
    if (area_copy.width <= 0 || area_copy.height <= 0)
        return null;
    final BufferUtil buffer = BufferUtil.getBufferedImage(area_copy.width, area_copy.height);
    if (buffer == null)
        return null;
    final BufferedImage image = buffer.getImage();
    final Graphics2D gc = buffer.getGraphics();
    gc.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
    gc.setRenderingHint(RenderingHints.KEY_ALPHA_INTERPOLATION, RenderingHints.VALUE_ALPHA_INTERPOLATION_SPEED);
    gc.setRenderingHint(RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_SPEED);
    gc.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_SPEED);
    if (need_layout.getAndSet(false))
        computeLayout(gc, area_copy);
    final Rectangle plot_bounds = plot_area.getBounds();
    gc.setColor(background);
    gc.fillRect(0, 0, area_copy.width, area_copy.height);
    scale.paint(gc, plot_bounds);
    plot_area.paint(gc);
    final AxisRange<Double> range = scale.getValueRange();
    final boolean normal = range.getLow() <= range.getHigh();
    final double min = Math.min(range.getLow(), range.getHigh());
    final double max = Math.max(range.getLow(), range.getHigh());
    final double current = value;
    final int level;
    if (current <= min)
        level = 0;
    else if (current >= max)
        level = plot_bounds.height;
    else if (max == min)
        level = 0;
    else
        level = (int) (plot_bounds.height * (current - min) / (max - min) + 0.5);
    final int arc = Math.min(plot_bounds.width, plot_bounds.height) / 10;
    gc.setPaint(new GradientPaint(plot_bounds.x, 0, empty, plot_bounds.x + plot_bounds.width / 2, 0, Color.GRAY, true));
    gc.fillRoundRect(plot_bounds.x, plot_bounds.y, plot_bounds.width, plot_bounds.height, arc, arc);
    gc.setPaint(new GradientPaint(plot_bounds.x, 0, fill, plot_bounds.x + plot_bounds.width / 2, 0, Color.WHITE, true));
    if (normal)
        gc.fillRoundRect(plot_bounds.x, plot_bounds.y + plot_bounds.height - level, plot_bounds.width, level, arc, arc);
    else
        gc.fillRoundRect(plot_bounds.x, plot_bounds.y, plot_bounds.width, level, arc, arc);
    gc.setColor(background);
    gc.dispose();
    // Convert to JFX
    return SwingFXUtils.toFXImage(image, null);
}
Also used : BufferUtil(org.csstudio.javafx.BufferUtil) Rectangle(java.awt.Rectangle) GradientPaint(java.awt.GradientPaint) BufferedImage(java.awt.image.BufferedImage) GradientPaint(java.awt.GradientPaint) Graphics2D(java.awt.Graphics2D)

Example 2 with BufferUtil

use of org.csstudio.javafx.BufferUtil in project org.csstudio.display.builder by kasemir.

the class Plot method updateImageBuffer.

/**
 * Draw all components into image buffer
 */
@Override
protected BufferedImage updateImageBuffer() {
    final Rectangle area_copy = area;
    if (area_copy.width <= 0 || area_copy.height <= 0)
        return null;
    plot_processor.autoscale();
    final BufferUtil buffer = buffers.getBufferedImage(area_copy.width, area_copy.height);
    if (buffer == null)
        return null;
    final BufferedImage image = buffer.getImage();
    final Graphics2D gc = buffer.getGraphics();
    // Really need AA for text to avoid anemic fonts.
    // AA for lines results in some fuzzyness,
    // but also required for any line that's not strictly horizontal or vertical.
    gc.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
    // Text AA is implied in general AA
    // gc.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_LCD_HRGB);
    gc.setRenderingHint(RenderingHints.KEY_ALPHA_INTERPOLATION, RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY);
    gc.setRenderingHint(RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_QUALITY);
    gc.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
    if (need_layout.getAndSet(false))
        computeLayout(gc, area_copy);
    final Rectangle plot_bounds = plot_area.getBounds();
    gc.setColor(background);
    gc.fillRect(0, 0, area_copy.width, area_copy.height);
    title_part.setColor(foreground);
    title_part.paint(gc, title_font);
    legend.paint(gc, legend_font, traces);
    // Fetch x_axis transformation and use that to paint all traces,
    // because X Axis tends to change from scrolling
    // while we're painting traces
    x_axis.setGridColor(grid);
    x_axis.paint(gc, plot_bounds);
    final ScreenTransform<XTYPE> x_transform = x_axis.getScreenTransform();
    for (YAxisImpl<XTYPE> y_axis : y_axes) {
        y_axis.setGridColor(grid);
        y_axis.paint(gc, plot_bounds);
    }
    gc.setClip(plot_bounds.x, plot_bounds.y, plot_bounds.width, plot_bounds.height);
    plot_area.paint(gc);
    for (YAxisImpl<XTYPE> y_axis : y_axes) for (Trace<XTYPE> trace : y_axis.getTraces()) trace_painter.paint(gc, plot_area.getBounds(), opacity, x_transform, y_axis, trace);
    drawPlotMarkers(gc);
    gc.setClip(null);
    // Annotations use label font
    for (AnnotationImpl<XTYPE> annotation : annotations) annotation.paint(gc, x_axis, y_axes.get(annotation.getTrace().getYAxis()));
    return image;
}
Also used : Trace(org.csstudio.javafx.rtplot.Trace) BufferUtil(org.csstudio.javafx.BufferUtil) Rectangle(java.awt.Rectangle) BufferedImage(java.awt.image.BufferedImage) Graphics2D(java.awt.Graphics2D)

Example 3 with BufferUtil

use of org.csstudio.javafx.BufferUtil in project org.csstudio.display.builder by kasemir.

the class ImagePlot method drawDataRGB.

/**
 * @param data_width
 *  @param data_height
 *  @param numbers
 *  @param next_rgbs
 *  @param type RGB type (RGB1, RGB2, or RGB3)
 *  @return {@link BufferedImage}, sized to match data
 */
private Object drawDataRGB(final int data_width, final int data_height, final ListNumber numbers, final ToIntFunction<IteratorNumber>[] next_rgbs, final VImageType type) {
    if (data_width <= 0 || data_height <= 0) {
        // With invalid size, cannot create a BufferedImage, not even for the error message
        return "Cannot draw image sized " + data_width + " x " + data_height;
    }
    final BufferUtil buffer = data_buffers.getBufferedImage(data_width, data_height);
    if (buffer == null)
        return "Cannot get buffer";
    final BufferedImage image = buffer.getImage();
    if (numbers.size() < data_width * data_height * 3)
        return "RGB image sized " + data_width + " x " + data_height + " received only " + numbers.size() + " data samples";
    // Using direct access to 'int' pixels in data buffer for speed. See other drawData() for details.
    final int[] data = ((DataBufferInt) image.getRaster().getDataBuffer()).getData();
    final IteratorNumber iter = numbers.iterator();
    switch(type) {
        case TYPE_RGB2:
            for (int y_times_width = 0; y_times_width < data_height * data_width; y_times_width += data_width) {
                // red
                for (int x = 0; x < data_width; ++x) data[y_times_width + x] = next_rgbs[0].applyAsInt(iter);
                ;
                // green
                for (int x = 0; x < data_width; ++x) data[y_times_width + x] |= next_rgbs[1].applyAsInt(iter);
                // blue
                for (int x = 0; x < data_width; ++x) data[y_times_width + x] |= next_rgbs[2].applyAsInt(iter);
            }
            break;
        case TYPE_RGB3:
            // red
            for (int i = 0; i < data_height * data_width; ++i) data[i] = next_rgbs[0].applyAsInt(iter);
            // green
            for (int i = 0; i < data_height * data_width; ++i) data[i] |= next_rgbs[1].applyAsInt(iter);
            // blue
            for (int i = 0; i < data_height * data_width; ++i) data[i] |= next_rgbs[2].applyAsInt(iter);
            break;
        default:
            throw new IllegalArgumentException("Image type must be an RGB type");
        // no "break;"
        case TYPE_RGB1:
            for (int i = 0; i < data_height * data_width; ++i) data[i] = next_rgbs[0].applyAsInt(iter) | next_rgbs[1].applyAsInt(iter) | next_rgbs[2].applyAsInt(iter);
    }
    return image;
}
Also used : BufferUtil(org.csstudio.javafx.BufferUtil) DataBufferInt(java.awt.image.DataBufferInt) BufferedImage(java.awt.image.BufferedImage) IteratorNumber(org.diirt.util.array.IteratorNumber)

Example 4 with BufferUtil

use of org.csstudio.javafx.BufferUtil in project org.csstudio.display.builder by kasemir.

the class ImagePlot method drawData.

/**
 * @param data_width
 *  @param data_height
 *  @param numbers
 *  @param next_sample_func
 *  @param min
 *  @param max
 *  @param color_mapping
 *  @return {@link BufferedImage}, sized to match data or String with error message
 */
private Object drawData(final int data_width, final int data_height, final ListNumber numbers, final ToDoubleFunction<IteratorNumber> next_sample_func, double min, double max, final ColorMappingFunction color_mapping) {
    if (data_width <= 0 || data_height <= 0) {
        // With invalid size, cannot create a BufferedImage, not even for the error message
        return "Cannot draw image sized " + data_width + " x " + data_height;
    }
    final BufferUtil buffer = data_buffers.getBufferedImage(data_width, data_height);
    if (buffer == null)
        return "Cannot get buffer";
    final BufferedImage image = buffer.getImage();
    if (numbers.size() < data_width * data_height)
        return "Image sized " + data_width + " x " + data_height + " received only " + numbers.size() + " data samples";
    if (// Implies min and max being finite, not-NaN
    !(min < max)) {
        logger.log(Level.WARNING, "Invalid value range {0} .. {1}", new Object[] { min, max });
        min = 0.0;
        max = 1.0;
    }
    // Direct access to 'int' pixels in data buffer is about twice as fast as access
    // via image.setRGB(x, y, color.getRGB()),
    // which in turn is about 3x faster than drawLine or fillRect.
    // Creating a byte[] with one byte per pixel and ColorModel based on color map is fastest,
    // but only 8 bits per pixel instead of 8 bits each for R, G and B isn't enough resolution.
    // Rounding of values into 8 bits creates artifacts.
    final int[] data = ((DataBufferInt) image.getRaster().getDataBuffer()).getData();
    final IteratorNumber iter = numbers.iterator();
    int idx = 0;
    if (colorbar_axis.isLogarithmic()) {
        final double lmin = Log10.log10(min), lmax = Log10.log10(max), span = lmax - lmin;
        for (int y = 0; y < data_height; ++y) for (int x = 0; x < data_width; ++x) {
            final double sample = Log10.log10(next_sample_func.applyAsDouble(iter));
            double scaled = (sample - lmin) / span;
            if (scaled < 0.0)
                scaled = 0;
            else if (scaled > 1.0)
                scaled = 1.0;
            data[idx++] = color_mapping.getRGB(scaled);
        }
    } else {
        final double span = max - min;
        for (int y = 0; y < data_height; ++y) for (int x = 0; x < data_width; ++x) {
            final double sample = next_sample_func.applyAsDouble(iter);
            double scaled = (sample - min) / span;
            if (scaled < 0.0)
                scaled = 0;
            else if (scaled > 1.0)
                scaled = 1.0;
            data[idx++] = color_mapping.getRGB(scaled);
        }
    }
    return image;
}
Also used : BufferUtil(org.csstudio.javafx.BufferUtil) DataBufferInt(java.awt.image.DataBufferInt) BufferedImage(java.awt.image.BufferedImage) IteratorNumber(org.diirt.util.array.IteratorNumber)

Example 5 with BufferUtil

use of org.csstudio.javafx.BufferUtil in project org.csstudio.display.builder by kasemir.

the class ImagePlot method updateImageBuffer.

/**
 * Draw all components into image buffer
 */
@Override
protected BufferedImage updateImageBuffer() {
    // Would like to use JFX WritableImage,
    // but rendering problem on Linux (sandbox.ImageScaling),
    // and no way to disable the color interpolation that 'smears'
    // the scaled image.
    // (http://bugs.java.com/bugdatabase/view_bug.do?bug_id=8091877).
    // So image is prepared in AWT and then converted to JFX
    logger.log(Level.FINE, "updateImageBuffer");
    final Rectangle area_copy = area;
    if (area_copy.width <= 0 || area_copy.height <= 0)
        return null;
    final BufferUtil buffer = buffers.getBufferedImage(area_copy.width, area_copy.height);
    if (buffer == null)
        return null;
    final BufferedImage image = buffer.getImage();
    final Graphics2D gc = buffer.getGraphics();
    gc.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
    gc.setRenderingHint(RenderingHints.KEY_ALPHA_INTERPOLATION, RenderingHints.VALUE_ALPHA_INTERPOLATION_SPEED);
    gc.setRenderingHint(RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_SPEED);
    gc.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_SPEED);
    // Get safe copy of the data
    // (not synchronized, i.e. width vs. data may be inconsistent,
    // but at least data won't change within this method)
    final int data_width = this.data_width, data_height = this.data_height;
    final ListNumber numbers = this.image_data;
    final boolean unsigned = this.unsigned_data;
    double min = this.min, max = this.max;
    final VImageType type = this.vimage_type;
    final ColorMappingFunction color_mapping = this.color_mapping;
    ToDoubleFunction<IteratorNumber> next_sample_func = IteratorNumber::nextDouble;
    boolean isRGB = type == VImageType.TYPE_RGB1 || type == VImageType.TYPE_RGB2 || type == VImageType.TYPE_RGB3;
    @SuppressWarnings("unchecked") final ToIntFunction<IteratorNumber>[] next_rgb = new ToIntFunction[3];
    if (numbers != null) {
        if (isRGB) {
            if (numbers instanceof ArrayShort) {
                if (unsigned) {
                    next_rgb[0] = (iter) -> getUShortForRGB(iter) << 8 & 0xFF0000;
                    next_rgb[1] = (iter) -> getUShortForRGB(iter) & 0xFF00;
                    next_rgb[2] = (iter) -> getUShortForRGB(iter) >>> 8;
                } else {
                    next_rgb[0] = (iter) -> getShortForRGB(iter) << 8 & 0xFF0000;
                    next_rgb[1] = (iter) -> getShortForRGB(iter) & 0xFF00;
                    next_rgb[2] = (iter) -> getShortForRGB(iter) >>> 8;
                }
            }
            if (numbers instanceof ArrayInt) {
                if (unsigned) {
                    next_rgb[0] = (iter) -> getUIntForRGB(iter) >>> 8 & 0xFF0000;
                    next_rgb[1] = (iter) -> getUIntForRGB(iter) >>> 16 & 0xFF00;
                    next_rgb[2] = (iter) -> getUIntForRGB(iter) >>> 24;
                } else {
                    next_rgb[0] = (iter) -> getIntForRGB(iter) >>> 8 & 0xFF0000;
                    next_rgb[1] = (iter) -> getIntForRGB(iter) >>> 16 & 0xFF00;
                    next_rgb[2] = (iter) -> getIntForRGB(iter) >>> 24;
                }
            } else {
                if (!(numbers instanceof ArrayByte))
                    logger.log(Level.WARNING, "Cannot handle rgb1 image data of type " + numbers.getClass().getName());
                if (unsigned) {
                    next_rgb[0] = (iter) -> getUByteForRGB(iter) << 16;
                    next_rgb[1] = (iter) -> getUByteForRGB(iter) << 8;
                    next_rgb[2] = (iter) -> getUByteForRGB(iter);
                } else {
                    next_rgb[0] = (iter) -> getByteForRGB(iter) << 16;
                    next_rgb[1] = (iter) -> getByteForRGB(iter) << 8;
                    next_rgb[2] = (iter) -> getByteForRGB(iter);
                }
            }
        } else // is not RGB
        {
            if (unsigned) {
                if (numbers instanceof ArrayShort)
                    next_sample_func = ImagePlot::getUnsignedShort;
                else if (numbers instanceof ArrayByte)
                    next_sample_func = ImagePlot::getUnsignedByte;
                else if (numbers instanceof ArrayInt)
                    next_sample_func = ImagePlot::getUnsignedInt;
                else
                    logger.log(Level.WARNING, "Cannot handle unsigned data of type " + numbers.getClass().getName());
            }
            if (autoscale) {
                // Compute min..max before layout of color bar
                final IteratorNumber iter = numbers.iterator();
                min = Double.MAX_VALUE;
                max = Double.NEGATIVE_INFINITY;
                while (iter.hasNext()) {
                    final double sample = next_sample_func.applyAsDouble(iter);
                    if (sample > max)
                        max = sample;
                    if (sample < min)
                        min = sample;
                }
                logger.log(Level.FINE, "Autoscale range {0} .. {1}", new Object[] { min, max });
            }
        }
    }
    // If log, min needs to be 1
    if (colorbar_axis.isLogarithmic() && min < 1.0)
        min = 1;
    colorbar_axis.setValueRange(min, max);
    if (need_layout.getAndSet(false))
        computeLayout(gc, area_copy, min, max);
    // Fill with a 'background' color
    gc.setColor(background);
    gc.fillRect(0, 0, area_copy.width, area_copy.height);
    if (numbers != null) {
        // Paint the image
        gc.setClip(image_area.x, image_area.y, image_area.width, image_area.height);
        final Object image_or_error = !isRGB ? drawData(data_width, data_height, numbers, next_sample_func, min, max, color_mapping) : drawDataRGB(data_width, data_height, numbers, next_rgb, type);
        if (image_or_error instanceof BufferedImage) {
            final BufferedImage unscaled = (BufferedImage) image_or_error;
            // Transform from full axis range into data range,
            // using the current 'zoom' state of each axis
            final LinearScreenTransform t = new LinearScreenTransform();
            AxisRange<Double> zoomed = x_axis.getValueRange();
            t.config(min_x, max_x, 0, data_width);
            // Round down .. up to always cover the image_area
            final int src_x1 = Math.max(0, (int) t.transform(zoomed.getLow()));
            final int src_x2 = Math.min(data_width, (int) (t.transform(zoomed.getHigh()) + 1));
            // Pixels of the image need to be aligned to their axis location,
            // especially when zoomed way in and the pixels are huge.
            // Turn pixel back into axis value, and then determine its destination on screen.
            final int dst_x1 = x_axis.getScreenCoord(t.inverse(src_x1));
            final int dst_x2 = x_axis.getScreenCoord(t.inverse(src_x2));
            // For Y axis, min_y == bottom == data_height
            zoomed = y_axis.getValueRange();
            t.config(min_y, max_y, data_height, 0);
            final int src_y1 = Math.max(0, (int) t.transform(zoomed.getHigh()));
            final int src_y2 = Math.min(data_height, (int) (t.transform(zoomed.getLow()) + 1));
            final int dst_y1 = y_axis.getScreenCoord(t.inverse(src_y1));
            final int dst_y2 = y_axis.getScreenCoord(t.inverse(src_y2));
            switch(interpolation) {
                case NONE:
                    gc.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR);
                    break;
                case INTERPOLATE:
                    gc.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC);
                    break;
                default:
                    // If image is smaller than screen area, show the actual pixels
                    if ((src_x2 - src_x1) < image_area.width && (src_y2 - src_y1) < image_area.height)
                        gc.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR);
                    else
                        // If image is larger than screen area, use best possible interpolation
                        // to avoid artifacts from statistically picking some specific nearest neighbor
                        gc.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC);
            }
            gc.drawImage(unscaled, dst_x1, dst_y1, dst_x2, dst_y2, src_x1, src_y1, src_x2, src_y2, /* ImageObserver */
            null);
        } else {
            gc.setColor(Color.RED);
            gc.setFont(x_axis.label_font);
            gc.drawString(Objects.toString(image_or_error), image_area.x + 10, image_area.y + 20);
        }
        gc.setClip(0, 0, area_copy.width, area_copy.height);
    }
    // Axes
    y_axis.paint(gc, image_area);
    x_axis.paint(gc, image_area);
    // Color bar
    if (colorbar_area != null) {
        final BufferedImage bar = drawColorBar(min, max, color_mapping);
        gc.drawImage(bar, colorbar_area.x, colorbar_area.y, colorbar_area.width, colorbar_area.height, null);
        colorbar_axis.paint(gc, colorbar_area);
    }
    // ROI uses X axis font
    gc.setFont(x_axis.label_font);
    for (RegionOfInterest roi : rois) drawROI(gc, roi);
    return image;
}
Also used : RegionOfInterest(org.csstudio.javafx.rtplot.RegionOfInterest) ColorMappingFunction(org.csstudio.javafx.rtplot.ColorMappingFunction) Rectangle(java.awt.Rectangle) LinearScreenTransform(org.csstudio.javafx.rtplot.internal.util.LinearScreenTransform) BufferedImage(java.awt.image.BufferedImage) Graphics2D(java.awt.Graphics2D) ListNumber(org.diirt.util.array.ListNumber) BufferUtil(org.csstudio.javafx.BufferUtil) VImageType(org.diirt.vtype.VImageType) ToIntFunction(java.util.function.ToIntFunction) ArrayByte(org.diirt.util.array.ArrayByte) ArrayInt(org.diirt.util.array.ArrayInt) ArrayShort(org.diirt.util.array.ArrayShort) IteratorNumber(org.diirt.util.array.IteratorNumber)

Aggregations

BufferedImage (java.awt.image.BufferedImage)5 BufferUtil (org.csstudio.javafx.BufferUtil)5 Graphics2D (java.awt.Graphics2D)3 Rectangle (java.awt.Rectangle)3 IteratorNumber (org.diirt.util.array.IteratorNumber)3 DataBufferInt (java.awt.image.DataBufferInt)2 GradientPaint (java.awt.GradientPaint)1 ToIntFunction (java.util.function.ToIntFunction)1 ColorMappingFunction (org.csstudio.javafx.rtplot.ColorMappingFunction)1 RegionOfInterest (org.csstudio.javafx.rtplot.RegionOfInterest)1 Trace (org.csstudio.javafx.rtplot.Trace)1 LinearScreenTransform (org.csstudio.javafx.rtplot.internal.util.LinearScreenTransform)1 ArrayByte (org.diirt.util.array.ArrayByte)1 ArrayInt (org.diirt.util.array.ArrayInt)1 ArrayShort (org.diirt.util.array.ArrayShort)1 ListNumber (org.diirt.util.array.ListNumber)1 VImageType (org.diirt.vtype.VImageType)1