// Copyright 2007 Google Inc. All Rights Reserved. // // Licensed 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 com.google.scrollview.ui; import edu.umd.cs.piccolo.nodes.PImage; import java.awt.image.BufferedImage; import java.util.HashMap; /** * The ScrollViewImageHandler is a helper class which takes care of image * processing. It is used to construct an Image from the message-stream and * basically consists of a number of utility functions to process the input * stream. * * @author wanke@google.com */ public class SVImageHandler { /** * Stores a mapping from the name of the string to its actual image. It * enables us to re-use images without having to load or transmit them again */ static HashMap images = new HashMap(); /** A global flag stating whether we are currently expecting Image data */ static boolean readImageData = false; // TODO(wanke) Consider moving all this into an SVImage class. /** These are all values belonging to the image which is currently being read */ static String imageName = null; // Image name static int bytesRead = 0; // Nr. of bytes already read static int bpp = 0; // Bit depth static int pictureArray[]; // The array holding the actual image static int bytePerPixel = 0; // # of used bytes to transmit a pixel (32 bpp // -> 7 BPP) static int width = 0; static int height = 0; /* All methods are static, so we forbid to construct SVImageHandler objects */ private SVImageHandler() { } /** * Takes a binary input string (consisting of '0' and '1' characters) and * converts it to an integer representation usable as image data. */ private static int[] processBinaryImage(String inputLine) { int BLACK = 0; int WHITE = Integer.MAX_VALUE; int[] imgData = new int[inputLine.length()]; for (int i = 0; i < inputLine.length(); i++) { if (inputLine.charAt(i) == '0') { imgData[i] = WHITE; } else if (inputLine.charAt(i) == '1') { imgData[i] = BLACK; } // BLACK is default anyway else { // Something is wrong: We did get unexpected data System.out.println("Error: unexpected non-image-data: (" + SVImageHandler.bytesRead + "," + inputLine.length() + "," + (SVImageHandler.height * SVImageHandler.width) + ")"); System.exit(1); } } return imgData; } /** * Takes an input string with pixel depth of 8 (represented by 2 bytes in * hexadecimal format, e.g. FF for white) and converts it to an * integer representation usable as image data */ private static int[] processGrayImage(String inputLine) { int[] imgData = new int[inputLine.length() / 2]; // Note: This is really inefficient, splitting it 2-byte-arrays in one pass // would be wa faster than substring everytime. for (int i = 0; i < inputLine.length(); i +=2) { String s = inputLine.substring(i, i+1); imgData[i] = Integer.parseInt(s, 16); } return imgData; } /** * Takes an input string with pixel depth of 32 (represented by HTML-like * colors in hexadecimal format, e.g. #00FF00 for green) and converts it to an * integer representation usable as image data */ private static int[] process32bppImage(String inputLine) { String[] strData = inputLine.split("#"); int[] imgData = new int[strData.length - 1]; for (int i = 1; i < strData.length; i++) { imgData[i - 1] = Integer.parseInt(strData[i], 16); } return imgData; } /** * Called when all image data is transmitted. Generates the actual image used * by java and puts it into the images-hashmap. */ private static void closeImage() { BufferedImage bi = null; if (bpp == 1) { bi = new BufferedImage(width, height, BufferedImage.TYPE_BYTE_BINARY); } else if (bpp == 8) { bi = new BufferedImage(width, height, BufferedImage.TYPE_BYTE_GRAY); } else if (bpp == 32) { bi = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); } else { System.out.println("Unsupported Image Type: " + bpp + " bpp"); System.exit(1); } bi.setRGB(0, 0, width, height, pictureArray, 0, width); PImage img = new PImage(bi); images.put(imageName, img); imageName = null; readImageData = false; System.out.println("(server, #Bytes:" + bytesRead + ") Image Completed"); bytesRead = 0; bpp = 0; } /** Starts creation of a new image. */ public static void createImage(String name, int width, int height, int bitsPerPixel) { // Create buffered image that does not support transparency bpp = bitsPerPixel; if (bpp == 1) { bytePerPixel = 1; } else if (bpp == 8) { bytePerPixel = 2; } else if (bpp == 32) { bytePerPixel = 7; } else { throw new IllegalArgumentException( "bpp should be 1 (binary), 8 (gray) or 32 (argb), is " + bpp); } if (imageName != null) { throw new IllegalArgumentException("Image " + imageName + " already opened!"); } else { imageName = name; bytesRead = 0; readImageData = true; SVImageHandler.height = height; SVImageHandler.width = width; pictureArray = new int[width * height]; } System.out.println("Processing Image with " + bpp + " bpp, size " + width + "x" + height); } /** * Opens an Image from location. This means the image does not have to be * actually transfered over the network. Thus, it is a lot faster than using * the createImage method. * * @param location The (local) location from where to open the file. This is * also the internal name associated with the image (if you want to draw it). */ public static void openImage(String location) { PImage img = new PImage(location); images.put(location, img); } /** Find the image corresponding to a given name */ public static PImage getImage(String name) { return images.get(name); } /** * Gets called while currently reading image data. Decides, how to process it * (which image type, whether all data is there). */ public static void parseData(String inputLine) { int[] data = null; if (bpp == 1) { data = processBinaryImage(inputLine); } else if (bpp == 8) { data = processGrayImage(inputLine); } else if (bpp == 32) { data = process32bppImage(inputLine); } else { System.out.println("Unsupported Bit Type: " + bpp); } System.arraycopy(data, 0, pictureArray, bytesRead, data.length); bytesRead += data.length; // We have read all image data - close the image if (bytesRead == (height * width)) { closeImage(); } } /** Returns whether we a currently reading image data or not */ public static boolean getReadImageData() { return readImageData; } /** Computes how many bytes of the image data are still missing */ public static int getMissingRemainingBytes() { return (height * width * bytePerPixel) - bytesRead; } }