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);
}
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;
}
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;
}
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;
}
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;
}
Aggregations