Search in sources :

Example 1 with LinearScreenTransform

use of org.csstudio.javafx.rtplot.internal.util.LinearScreenTransform 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

Graphics2D (java.awt.Graphics2D)1 Rectangle (java.awt.Rectangle)1 BufferedImage (java.awt.image.BufferedImage)1 ToIntFunction (java.util.function.ToIntFunction)1 BufferUtil (org.csstudio.javafx.BufferUtil)1 ColorMappingFunction (org.csstudio.javafx.rtplot.ColorMappingFunction)1 RegionOfInterest (org.csstudio.javafx.rtplot.RegionOfInterest)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 IteratorNumber (org.diirt.util.array.IteratorNumber)1 ListNumber (org.diirt.util.array.ListNumber)1 VImageType (org.diirt.vtype.VImageType)1