| // Licensed to the Apache Software Foundation (ASF) under one | |
| // or more contributor license agreements. See the NOTICE file | |
| // distributed with this work for additional information | |
| // regarding copyright ownership. The ASF licenses this file | |
| // to you under the Apache License, Version 2.0 (the | |
| // "License"); you may not use this file except in compliance | |
| // with the License. You may obtain a copy of the License at | |
| // | |
| // http://www.apache.org/licenses/LICENSE-2.0 | |
| // | |
| // Unless required by applicable law or agreed to in writing, | |
| // software distributed under the License is distributed on an | |
| // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | |
| // KIND, either express or implied. See the License for the | |
| // specific language governing permissions and limitations | |
| // under the License. | |
| package common; | |
| import java.awt.image.BufferedImage; | |
| import java.awt.image.DataBuffer; | |
| import java.awt.image.DataBufferInt; | |
| import java.util.Arrays; | |
| import org.apache.logging.log4j.Logger; | |
| import org.apache.logging.log4j.LogManager; | |
| import streamer.BaseElement; | |
| import streamer.ByteBuffer; | |
| import streamer.Element; | |
| import streamer.Link; | |
| public class BufferedImagePixelsAdapter extends BaseElement { | |
| protected static Logger LOGGER = LogManager.getLogger(BufferedImagePixelsAdapter.class); | |
| public static final String TARGET_X = "x"; | |
| public static final String TARGET_Y = "y"; | |
| public static final String WIDTH = "width"; | |
| public static final String HEIGHT = "height"; | |
| public static final String RGB888LE32 = "RGB888LE32"; | |
| public static final String PIXEL_FORMAT = "pixel_format"; | |
| protected BufferedImageCanvas canvas; | |
| public BufferedImagePixelsAdapter(String id, BufferedImageCanvas canvas) { | |
| super(id); | |
| this.canvas = canvas; | |
| declarePads(); | |
| } | |
| private void declarePads() { | |
| inputPads.put(STDIN, null); | |
| } | |
| @Override | |
| public String toString() { | |
| return "Renderer(" + id + ")"; | |
| } | |
| @Override | |
| public void handleData(ByteBuffer buf, Link link) { | |
| if (verbose) | |
| LOGGER.debug("[" + this + "] INFO: Data received: " + buf + "."); | |
| int x = (Integer)buf.getMetadata(TARGET_X); | |
| int y = (Integer)buf.getMetadata(TARGET_Y); | |
| int rectWidth = (Integer)buf.getMetadata(WIDTH); | |
| int rectHeight = (Integer)buf.getMetadata(HEIGHT); | |
| String format = (String)buf.getMetadata(PIXEL_FORMAT); | |
| int bpp; | |
| // Support RGB888/32 little endian only | |
| if (format != null && RGB888LE32.equals(format)) { | |
| bpp = 4; | |
| // TODO: support more formats | |
| } else | |
| throw new RuntimeException("Unsupported format: " + format + ". Supported formats: " + RGB888LE32 + "."); | |
| int dataLength = rectWidth * rectHeight * bpp; | |
| if (!cap(buf, dataLength, dataLength, link, false)) | |
| return; | |
| // Draw rectangle on offline buffer | |
| BufferedImage image = canvas.getOfflineImage(); | |
| DataBuffer dataBuf = image.getRaster().getDataBuffer(); | |
| switch (dataBuf.getDataType()) { | |
| case DataBuffer.TYPE_INT: { | |
| // Convert array of bytes to array of int's | |
| int[] intArray = buf.toIntLEArray(); | |
| // We chose RGB888 model, so Raster will use DataBufferInt type | |
| DataBufferInt dataBuffer = (DataBufferInt)dataBuf; | |
| int imageWidth = image.getWidth(); | |
| int imageHeight = image.getHeight(); | |
| // Paint rectangle directly on buffer, line by line | |
| int[] imageBuffer = dataBuffer.getData(); | |
| for (int srcLine = 0, dstLine = y; srcLine < rectHeight && dstLine < imageHeight; srcLine++, dstLine++) { | |
| try { | |
| System.arraycopy(intArray, srcLine * rectWidth, imageBuffer, x + dstLine * imageWidth, rectWidth); | |
| } catch (IndexOutOfBoundsException e) { | |
| LOGGER.info("[ignored] copy error",e); | |
| } | |
| } | |
| break; | |
| } | |
| default: | |
| throw new RuntimeException("Unsupported data buffer in buffered image: expected data buffer of type int (DataBufferInt). Actual data buffer type: " | |
| + dataBuf.getClass().getSimpleName()); | |
| } | |
| // Request update of repainted area | |
| canvas.repaint(x, y, rectWidth, rectHeight); | |
| buf.unref(); | |
| } | |
| public static void main(String args[]) { | |
| System.setProperty("streamer.Element.debug", "true"); | |
| BufferedImageCanvas canvas = new BufferedImageCanvas(4, 4); | |
| Element renderer = new BufferedImagePixelsAdapter("renderer", canvas); | |
| byte[] pixels = new byte[] {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 1, 2, 3, 4, 5, | |
| 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}; | |
| int[] pixelsLE = new int[] {0x04030201, 0x08070605, 0x0c0b0a09, 0x100f0e0d, 0x04030201, 0x08070605, 0x0c0b0a09, 0x100f0e0d, 0x04030201, 0x08070605, | |
| 0x0c0b0a09, 0x100f0e0d, 0x04030201, 0x08070605, 0x0c0b0a09, 0x100f0e0d}; | |
| ByteBuffer buf = new ByteBuffer(pixels); | |
| buf.putMetadata(TARGET_X, 0); | |
| buf.putMetadata(TARGET_Y, 0); | |
| buf.putMetadata(WIDTH, 4); | |
| buf.putMetadata(HEIGHT, 4); | |
| buf.putMetadata(PIXEL_FORMAT, RGB888LE32); | |
| renderer.handleData(buf, null); | |
| String actualData = Arrays.toString(((DataBufferInt)canvas.getOfflineImage().getRaster().getDataBuffer()).getData()); | |
| String expectedData = Arrays.toString(pixelsLE); | |
| if (!actualData.equals(expectedData)) | |
| LOGGER.error("Actual image: " + actualData + "\nExpected image: " + expectedData + "."); | |
| } | |
| } |