Fixed bugs in scrollview and upgraded piccolo to 3.0

git-svn-id: https://tesseract-ocr.googlecode.com/svn/trunk@1026 d0cd1f9f-072b-0410-8dd7-cf729c803f20
This commit is contained in:
theraysmith@gmail.com 2014-01-29 02:24:32 +00:00
parent ad149844f0
commit 99e1f05952
7 changed files with 122 additions and 346 deletions

View File

@ -36,10 +36,10 @@ SCROLLVIEW_CLASSES = \
com/google/scrollview/ScrollView.class
SCROLLVIEW_LIBS = \
$(srcdir)/piccolo-1.2.jar \
$(srcdir)/piccolox-1.2.jar
$(srcdir)/piccolo2d-core-3.0.jar \
$(srcdir)/piccolo2d-extras-3.0.jar
CLASSPATH = $(srcdir)/piccolo-1.2.jar:$(srcdir)/piccolox-1.2.jar
CLASSPATH = $(srcdir)/piccolo2d-core-3.0.jar:$(srcdir)/piccolo2d-extras-3.0.jar
ScrollView.jar : $(SCROLLVIEW_CLASSES)
$(JAR) cf $@ com/google/scrollview/*.class \

View File

@ -13,6 +13,7 @@ package com.google.scrollview;
import com.google.scrollview.events.SVEvent;
import com.google.scrollview.ui.SVImageHandler;
import com.google.scrollview.ui.SVWindow;
import org.piccolo2d.nodes.PImage;
import java.io.BufferedReader;
import java.io.IOException;
@ -23,7 +24,6 @@ import java.net.Socket;
import java.util.ArrayList;
import java.util.regex.Pattern;
/**
* The ScrollView class is the main class which gets started from the command
* line. It sets up LUA and handles the network processing.
@ -115,29 +115,10 @@ public class ScrollView {
first = !first;
}
assert first;
} else if (SVImageHandler.getReadImageData() == false) {
// If we are currently not transmitting an image, process this
// normally.
} else {
// Process this normally.
processInput(inputLine);
}
// We are still transmitting image data, but there seems to be some
// command at the
// end of the message attached as well. Thus, we have to split it
// accordingly and
// first generate the image and afterwards process the remaining
// message.
else if (inputLine.length() >
SVImageHandler.getMissingRemainingBytes()) {
String luaCmd = inputLine.substring(
SVImageHandler.getMissingRemainingBytes());
String imgData = inputLine.substring(0,
SVImageHandler.getMissingRemainingBytes());
SVImageHandler.parseData(imgData);
processInput(luaCmd);
} else { // We are still in the middle of image data and have not
// reached the end yet.
SVImageHandler.parseData(inputLine);
}
}
}
// Some connection error
@ -228,6 +209,9 @@ public class ScrollView {
/** Executes the LUA command parsed as parameter. */
private static void processInput(String inputLine) {
if (inputLine == null) {
return;
}
// Execute a function encoded as a LUA statement! Yuk!
if (inputLine.charAt(0) == 'w') {
// This is a method call on a window. Parse it.
@ -298,11 +282,6 @@ public class ScrollView {
} else if (func.equals("drawText")) {
windows.get(windowID).drawText(intList.get(0), intList.get(1),
stringList.get(0));
} else if (func.equals("openImage")) {
windows.get(windowID).openImage(stringList.get(0));
} else if (func.equals("drawImage")) {
windows.get(windowID).drawImage(stringList.get(0),
intList.get(0), intList.get(1));
} else if (func.equals("addMenuBarItem")) {
if (boolList.size() > 0) {
windows.get(windowID).addMenuBarItem(stringList.get(0),
@ -337,12 +316,12 @@ public class ScrollView {
} else if (func.equals("zoomRectangle")) {
windows.get(windowID).zoomRectangle(intList.get(0), intList.get(1),
intList.get(2), intList.get(3));
} else if (func.equals("createImage")) {
windows.get(windowID).createImage(stringList.get(0), intList.get(0),
intList.get(1), intList.get(2));
} else if (func.equals("readImage")) {
PImage image = SVImageHandler.readImage(intList.get(2), in);
windows.get(windowID).drawImage(image, intList.get(0), intList.get(1));
} else if (func.equals("drawImage")) {
windows.get(windowID).drawImage(stringList.get(0),
intList.get(0), intList.get(1));
PImage image = new PImage(stringList.get(0));
windows.get(windowID).drawImage(image, intList.get(0), intList.get(1));
} else if (func.equals("destroy")) {
windows.get(windowID).destroy();
}

View File

@ -25,11 +25,11 @@ import java.awt.event.WindowListener;
import javax.swing.Timer;
import edu.umd.cs.piccolo.PCamera;
import edu.umd.cs.piccolo.PNode;
import edu.umd.cs.piccolo.event.PBasicInputEventHandler;
import edu.umd.cs.piccolo.event.PInputEvent;
import edu.umd.cs.piccolo.nodes.PPath;
import org.piccolo2d.PCamera;
import org.piccolo2d.PNode;
import org.piccolo2d.event.PBasicInputEventHandler;
import org.piccolo2d.event.PInputEvent;
import org.piccolo2d.nodes.PPath;
/**
* The ScrollViewEventHandler takes care of any events which might happen on the

View File

@ -1,5 +1,5 @@
// 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
@ -10,219 +10,60 @@
package com.google.scrollview.ui;
import edu.umd.cs.piccolo.nodes.PImage;
import org.piccolo2d.nodes.PImage;
import java.awt.image.BufferedImage;
import java.util.HashMap;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.xml.bind.DatatypeConverter;
/**
* 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<String, PImage> images = new HashMap<String, PImage>();
/** 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.
* Reads size bytes from the stream in and interprets it as an image file,
* encoded as png, and then text-encoded as base 64, returning the decoded
* bitmap.
*
* @param size The size of the image file.
* @param in The input stream from which to read the bytes.
*/
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);
public static PImage readImage(int size, BufferedReader in) {
char[] charbuffer = new char[size];
int numRead = 0;
while (numRead < size) {
int newRead = -1;
try {
newRead = in.read(charbuffer, numRead, size - numRead);
} catch (IOException e) {
System.out.println("Failed to read image data from socket" + e.getMessage());
}
if (newRead < 0) {
return null;
}
numRead += newRead;
}
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);
// Convert the character data to binary.
byte[] binarydata = DatatypeConverter.parseBase64Binary(new String(charbuffer));
// Convert the binary data to a byte stream and parse to image.
ByteArrayInputStream byteStream = new ByteArrayInputStream(binarydata);
try {
PImage img = new PImage(ImageIO.read(byteStream));
return img;
} catch (IOException e) {
System.out.println("Failed to decode image data from socket" + e.getMessage());
}
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;
return null;
}
}

View File

@ -14,19 +14,18 @@ import com.google.scrollview.ScrollView;
import com.google.scrollview.events.SVEvent;
import com.google.scrollview.events.SVEventHandler;
import com.google.scrollview.events.SVEventType;
import com.google.scrollview.ui.SVImageHandler;
import com.google.scrollview.ui.SVMenuBar;
import com.google.scrollview.ui.SVPopupMenu;
import edu.umd.cs.piccolo.PCamera;
import edu.umd.cs.piccolo.PCanvas;
import edu.umd.cs.piccolo.PLayer;
import org.piccolo2d.PCamera;
import org.piccolo2d.PCanvas;
import org.piccolo2d.PLayer;
import edu.umd.cs.piccolo.nodes.PImage;
import edu.umd.cs.piccolo.nodes.PPath;
import edu.umd.cs.piccolo.nodes.PText;
import edu.umd.cs.piccolo.util.PPaintContext;
import edu.umd.cs.piccolox.swing.PScrollPane;
import org.piccolo2d.nodes.PImage;
import org.piccolo2d.nodes.PPath;
import org.piccolo2d.nodes.PText;
import org.piccolo2d.util.PPaintContext;
import org.piccolo2d.extras.swing.PScrollPane;
import java.awt.BasicStroke;
import java.awt.BorderLayout;
@ -136,21 +135,6 @@ public class SVWindow extends JFrame {
layer.removeAllChildren();
}
/**
* Start setting up a new image. The server will now expect image data until
* the image is complete.
*
* @param internalName The unique name of the new image
* @param width Image width
* @param height Image height
* @param bitsPerPixel The bit depth (currently supported: 1 (binary) and 32
* (ARGB))
*/
public void createImage(String internalName, int width, int height,
int bitsPerPixel) {
SVImageHandler.createImage(internalName, width, height, bitsPerPixel);
}
/**
* Start setting up a new polyline. The server will now expect
* polyline data until the polyline is complete.
@ -168,8 +152,20 @@ public class SVWindow extends JFrame {
* Draw the now complete polyline.
*/
public void drawPolyline() {
PPath pn = PPath.createPolyline(ScrollView.polylineXCoords,
ScrollView.polylineYCoords);
int numCoords = ScrollView.polylineXCoords.length;
if (numCoords < 2) {
return;
}
PPath pn = PPath.createLine(ScrollView.polylineXCoords[0],
ScrollView.polylineYCoords[0],
ScrollView.polylineXCoords[1],
ScrollView.polylineYCoords[1]);
pn.reset();
pn.moveTo(ScrollView.polylineXCoords[0], ScrollView.polylineYCoords[0]);
for (int p = 1; p < numCoords; ++p) {
pn.lineTo(ScrollView.polylineXCoords[p], ScrollView.polylineYCoords[p]);
}
pn.closePath();
ScrollView.polylineSize = 0;
pn.setStrokePaint(currentPenColor);
pn.setPaint(null); // Don't fill the polygon - this is just a polyline.
@ -323,8 +319,7 @@ public class SVWindow extends JFrame {
* memory, so if you intend to redraw an image, you do not have to use
* createImage again.
*/
public void drawImage(String internalName, int x_pos, int y_pos) {
PImage img = SVImageHandler.getImage(internalName);
public void drawImage(PImage img, int x_pos, int y_pos) {
img.setX(x_pos);
img.setY(y_pos);
layer.addChild(img);
@ -629,15 +624,4 @@ public class SVWindow extends JFrame {
setVisible(false);
// dispose();
}
/**
* Open an image from a given file location and store it in memory. Pro:
* Faster than createImage. Con: Works only on the local file system.
*
* @param filename The path to the image.
*/
public void openImage(String filename) {
SVImageHandler.openImage(filename);
}
}

View File

@ -172,6 +172,8 @@ void* ScrollView::MessageReceiver(void* a) {
sv->Signal();
sv->Signal();
}
} else {
delete cur; // Applied to no window.
}
svmap_mu->Unlock();
@ -382,6 +384,9 @@ ScrollView::~ScrollView() {
delete mutex_;
delete semaphore_;
delete points_;
for (int i = 0; i < SVET_COUNT; i++) {
delete event_table_[i];
}
#endif // GRAPHICS_DISABLED
}
@ -766,77 +771,44 @@ void ScrollView::ZoomToRectangle(int x1, int y1, int x2, int y2) {
// Send an image of type Pix.
void ScrollView::Image(struct Pix* image, int x_pos, int y_pos) {
int width = image->w;
int height = image->h;
l_uint32 bpp = image->d;
++image_index_;
// PIX* do not have a unique identifier/name associated, so name them "lept".
SendMsg("createImage('lept%d',%d,%d,%d)", image_index_, width, height, bpp);
if (bpp == 32) {
Transfer32bppImage(image);
} else if (bpp == 8) {
TransferGrayImage(image);
} else if (bpp == 1) {
TransferBinaryImage(image);
}
// PIX* do not have a unique identifier/name associated, so name them "lept".
SendMsg("drawImage('lept%d',%d,%d)", image_index_, x_pos, y_pos);
}
// Sends each pixel as hex value like html, e.g. #00FF00 for green.
void ScrollView::Transfer32bppImage(PIX* image) {
int ppL = pixGetWidth(image);
int h = pixGetHeight(image);
int wpl = pixGetWpl(image);
int transfer_size= ppL * 7 + 2;
char* pixel_data = new char[transfer_size];
for (int y = 0; y < h; ++y) {
l_uint32* data = pixGetData(image) + y*wpl;
for (int x = 0; x < ppL; ++x, ++data) {
snprintf(&pixel_data[x*7], 7, "#%.2x%.2x%.2x",
GET_DATA_BYTE(data, COLOR_RED),
GET_DATA_BYTE(data, COLOR_GREEN),
GET_DATA_BYTE(data, COLOR_BLUE));
}
pixel_data[transfer_size - 2] = '\n';
pixel_data[transfer_size - 1] = '\0';
SendRawMessage(pixel_data);
}
delete[] pixel_data;
}
// Sends for each pixel either '1' or '0'.
void ScrollView::TransferGrayImage(PIX* image) {
char* pixel_data = new char[image->w * 2 + 2];
for (int y = 0; y < image->h; y++) {
l_uint32* data = pixGetData(image) + y * pixGetWpl(image);
for (int x = 0; x < image->w; x++) {
snprintf(&pixel_data[x*2], 2, "%.2x", (GET_DATA_BYTE(data, x)));
pixel_data[image->w * 2] = '\n';
pixel_data[image->w * 2 + 1] = '\0';
SendRawMessage(pixel_data);
l_uint8* data;
size_t size;
pixWriteMem(&data, &size, image, IFF_PNG);
int base64_len = (size + 2) / 3 * 4;
SendMsg("readImage(%d,%d,%d)", x_pos, y_pos, base64_len);
// Base64 encode the data.
const char kBase64Table[64] = {
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
'w', 'x', 'y', 'z', '0', '1', '2', '3',
'4', '5', '6', '7', '8', '9', '+', '/',
};
char* base64 = new char[base64_len + 1];
memset(base64, '=', base64_len);
base64[base64_len] = '\0';
int remainder = 0;
int bits_left = 0;
int code_len = 0;
for (int i = 0; i < size; ++i) {
int code = (data[i] >> (bits_left + 2)) | remainder;
base64[code_len++] = kBase64Table[code & 63];
bits_left += 2;
remainder = data[i] << (6 - bits_left);
if (bits_left == 6) {
base64[code_len++] = kBase64Table[remainder & 63];
bits_left = 0;
remainder = 0;
}
}
delete [] pixel_data;
}
// Sends for each pixel either '1' or '0'.
void ScrollView::TransferBinaryImage(PIX* image) {
char* pixel_data = new char[image->w + 2];
for (int y = 0; y < image->h; y++) {
l_uint32* data = pixGetData(image) + y * pixGetWpl(image);
for (int x = 0; x < image->w; x++) {
if (GET_DATA_BIT(data, x))
pixel_data[x] = '1';
else
pixel_data[x] = '0';
}
pixel_data[image->w] = '\n';
pixel_data[image->w + 1] = '\0';
SendRawMessage(pixel_data);
}
delete [] pixel_data;
if (bits_left > 0)
base64[code_len++] = kBase64Table[remainder & 63];
SendRawMessage(base64);
delete [] base64;
free(data);
}
// Escapes the ' character with a \, so it can be processed by LUA.

View File

@ -297,12 +297,12 @@ static std::string ScrollViewCommand(std::string scrollview_path) {
// Also the path has to be separated by ; on windows and : otherwise.
#ifdef _WIN32
const char* cmd_template = "-Djava.library.path=%s -cp %s/ScrollView.jar;"
"%s/piccolo-1.2.jar;%s/piccolox-1.2.jar"
"%s/piccolo2d-core-3.0.jar:%s/piccolo2d-extras-3.0.jar"
" com.google.scrollview.ScrollView";
#else
const char* cmd_template = "-c \"trap 'kill %%1' 0 1 2 ; java "
"-Xms1024m -Xmx2048m -Djava.library.path=%s -cp %s/ScrollView.jar:"
"%s/piccolo-1.2.jar:%s/piccolox-1.2.jar"
"%s/piccolo2d-core-3.0.jar:%s/piccolo2d-extras-3.0.jar"
" com.google.scrollview.ScrollView"
" & wait\"";
#endif