• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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