1 // Copyright 2007 Google Inc. All Rights Reserved. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); You may not 4 // use this file except in compliance with the License. You may obtain a copy of 5 // the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by 6 // applicable law or agreed to in writing, software distributed under the 7 // License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS 8 // OF ANY KIND, either express or implied. See the License for the specific 9 // language governing permissions and limitations under the License. 10 11 package com.google.scrollview.ui; 12 13 import edu.umd.cs.piccolo.nodes.PImage; 14 15 import java.awt.image.BufferedImage; 16 import java.util.HashMap; 17 18 /** 19 * The ScrollViewImageHandler is a helper class which takes care of image 20 * processing. It is used to construct an Image from the message-stream and 21 * basically consists of a number of utility functions to process the input 22 * stream. 23 * 24 * @author wanke@google.com 25 */ 26 public class SVImageHandler { 27 /** 28 * Stores a mapping from the name of the string to its actual image. It 29 * enables us to re-use images without having to load or transmit them again 30 */ 31 static HashMap<String, PImage> images = new HashMap<String, PImage>(); 32 33 /** A global flag stating whether we are currently expecting Image data */ 34 static boolean readImageData = false; 35 36 // TODO(wanke) Consider moving all this into an SVImage class. 37 /** These are all values belonging to the image which is currently being read */ 38 static String imageName = null; // Image name 39 static int bytesRead = 0; // Nr. of bytes already read 40 static int bpp = 0; // Bit depth 41 static int pictureArray[]; // The array holding the actual image 42 43 static int bytePerPixel = 0; // # of used bytes to transmit a pixel (32 bpp 44 // -> 7 BPP) 45 static int width = 0; 46 static int height = 0; 47 48 /* All methods are static, so we forbid to construct SVImageHandler objects */ SVImageHandler()49 private SVImageHandler() { 50 } 51 52 /** 53 * Takes a binary input string (consisting of '0' and '1' characters) and 54 * converts it to an integer representation usable as image data. 55 */ processBinaryImage(String inputLine)56 private static int[] processBinaryImage(String inputLine) { 57 int BLACK = 0; 58 int WHITE = Integer.MAX_VALUE; 59 60 int[] imgData = new int[inputLine.length()]; 61 62 for (int i = 0; i < inputLine.length(); i++) { 63 if (inputLine.charAt(i) == '0') { 64 imgData[i] = WHITE; 65 } else if (inputLine.charAt(i) == '1') { 66 imgData[i] = BLACK; 67 } // BLACK is default anyway 68 else { // Something is wrong: We did get unexpected data 69 System.out.println("Error: unexpected non-image-data: (" 70 + SVImageHandler.bytesRead + "," + inputLine.length() + "," 71 + (SVImageHandler.height * SVImageHandler.width) + ")"); 72 System.exit(1); 73 } 74 } 75 return imgData; 76 } 77 78 /** 79 * Takes an input string with pixel depth of 8 (represented by 2 bytes in 80 * hexadecimal format, e.g. FF for white) and converts it to an 81 * integer representation usable as image data 82 */ processGrayImage(String inputLine)83 private static int[] processGrayImage(String inputLine) { 84 int[] imgData = new int[inputLine.length() / 2]; 85 // Note: This is really inefficient, splitting it 2-byte-arrays in one pass 86 // would be wa faster than substring everytime. 87 for (int i = 0; i < inputLine.length(); i +=2) { 88 String s = inputLine.substring(i, i+1); 89 imgData[i] = Integer.parseInt(s, 16); 90 } 91 92 return imgData; 93 } 94 95 /** 96 * Takes an input string with pixel depth of 32 (represented by HTML-like 97 * colors in hexadecimal format, e.g. #00FF00 for green) and converts it to an 98 * integer representation usable as image data 99 */ process32bppImage(String inputLine)100 private static int[] process32bppImage(String inputLine) { 101 102 String[] strData = inputLine.split("#"); 103 int[] imgData = new int[strData.length - 1]; 104 105 for (int i = 1; i < strData.length; i++) { 106 imgData[i - 1] = Integer.parseInt(strData[i], 16); 107 } 108 109 return imgData; 110 } 111 112 /** 113 * Called when all image data is transmitted. Generates the actual image used 114 * by java and puts it into the images-hashmap. 115 */ closeImage()116 private static void closeImage() { 117 118 BufferedImage bi = null; 119 if (bpp == 1) { 120 bi = new BufferedImage(width, height, BufferedImage.TYPE_BYTE_BINARY); 121 } else if (bpp == 8) { 122 bi = new BufferedImage(width, height, BufferedImage.TYPE_BYTE_GRAY); 123 } else if (bpp == 32) { 124 bi = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); 125 } else { 126 System.out.println("Unsupported Image Type: " + bpp + " bpp"); 127 System.exit(1); 128 } 129 130 bi.setRGB(0, 0, width, height, pictureArray, 0, width); 131 132 PImage img = new PImage(bi); 133 134 images.put(imageName, img); 135 136 imageName = null; 137 readImageData = false; 138 139 System.out.println("(server, #Bytes:" + bytesRead + ") Image Completed"); 140 141 bytesRead = 0; 142 bpp = 0; 143 } 144 145 /** Starts creation of a new image. */ createImage(String name, int width, int height, int bitsPerPixel)146 public static void createImage(String name, int width, int height, 147 int bitsPerPixel) { 148 // Create buffered image that does not support transparency 149 bpp = bitsPerPixel; 150 if (bpp == 1) { 151 bytePerPixel = 1; 152 } else if (bpp == 8) { 153 bytePerPixel = 2; 154 } else if (bpp == 32) { 155 bytePerPixel = 7; 156 } else { 157 throw new IllegalArgumentException( 158 "bpp should be 1 (binary), 8 (gray) or 32 (argb), is " + bpp); 159 } 160 if (imageName != null) { 161 throw new IllegalArgumentException("Image " + imageName + " already opened!"); 162 } 163 else { 164 imageName = name; 165 bytesRead = 0; 166 readImageData = true; 167 SVImageHandler.height = height; 168 SVImageHandler.width = width; 169 pictureArray = new int[width * height]; 170 } 171 172 System.out.println("Processing Image with " + bpp + " bpp, size " + width + "x" + height); 173 } 174 175 /** 176 * Opens an Image from location. This means the image does not have to be 177 * actually transfered over the network. Thus, it is a lot faster than using 178 * the createImage method. 179 * 180 * @param location The (local) location from where to open the file. This is 181 * also the internal name associated with the image (if you want to draw it). 182 */ openImage(String location)183 public static void openImage(String location) { 184 PImage img = new PImage(location); 185 images.put(location, img); 186 } 187 188 /** Find the image corresponding to a given name */ getImage(String name)189 public static PImage getImage(String name) { 190 return images.get(name); 191 } 192 193 /** 194 * Gets called while currently reading image data. Decides, how to process it 195 * (which image type, whether all data is there). 196 */ parseData(String inputLine)197 public static void parseData(String inputLine) { 198 int[] data = null; 199 200 if (bpp == 1) { 201 data = processBinaryImage(inputLine); 202 } else if (bpp == 8) { 203 data = processGrayImage(inputLine); 204 } else if (bpp == 32) { 205 data = process32bppImage(inputLine); 206 } else { 207 System.out.println("Unsupported Bit Type: " + bpp); 208 } 209 210 System.arraycopy(data, 0, pictureArray, bytesRead, data.length); 211 bytesRead += data.length; 212 213 // We have read all image data - close the image 214 if (bytesRead == (height * width)) { 215 closeImage(); 216 } 217 } 218 219 /** Returns whether we a currently reading image data or not */ getReadImageData()220 public static boolean getReadImageData() { 221 return readImageData; 222 } 223 224 /** Computes how many bytes of the image data are still missing */ getMissingRemainingBytes()225 public static int getMissingRemainingBytes() { 226 return (height * width * bytePerPixel) - bytesRead; 227 } 228 } 229