/* RdesktopCanvas.java
* Component: ProperJavaRDP
*
* Revision: $Revision: 1.8 $
* Author: $Author: telliott $
* Date: $Date: 2005/09/27 14:15:39 $
*
* Copyright (c) 2005 Propero Limited
*
* Purpose: Canvas component, handles drawing requests from server,
* and passes user input to Input class.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or (at
* your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
* USA
*
* (See gpl.txt for details of the GNU General Public License.)
*
*/
package org.jopenray.rdp;
import java.awt.Canvas;
import java.awt.Cursor;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.image.BufferedImage;
import java.awt.image.IndexColorModel;
import java.awt.image.MemoryImageSource;
import org.apache.log4j.Logger;
import org.jopenray.rdp.keymapping.KeyCode;
import org.jopenray.rdp.keymapping.KeyCodeFileBased;
import org.jopenray.rdp.orders.BoundsOrder;
import org.jopenray.rdp.orders.Brush;
import org.jopenray.rdp.orders.DestBltOrder;
import org.jopenray.rdp.orders.LineOrder;
import org.jopenray.rdp.orders.MemBltOrder;
import org.jopenray.rdp.orders.PatBltOrder;
import org.jopenray.rdp.orders.PolyLineOrder;
import org.jopenray.rdp.orders.RectangleOrder;
import org.jopenray.rdp.orders.ScreenBltOrder;
import org.jopenray.rdp.orders.TriBltOrder;
// import org.apache.log4j.NDC;
public abstract class RDPCanvas extends Canvas {
static Logger logger = Logger.getLogger(RDPCanvas.class);
private RasterOp rop = null;
protected WrappedImage backstore;
// Graphics backstore_graphics;
private Cursor previous_cursor = null; // for setBusyCursor and
// unsetBusyCursor
private Input input = null;
public static final int ROP2_COPY = 0xc;
private static final int ROP2_XOR = 0x6;
private static final int ROP2_AND = 0x8;
private static final int ROP2_NXOR = 0x9;
private static final int ROP2_OR = 0xe;
private static final int MIX_TRANSPARENT = 0;
private static final int MIX_OPAQUE = 1;
private static final int TEXT2_VERTICAL = 0x04;
private static final int TEXT2_IMPLICIT_X = 0x20;
public KeyCode keys = null;
public KeyCodeFileBased fbKeys = null;
public String sKeys = null;
public int width = 0;
public int height = 0;
// private int[] colors = null; // needed for integer backstore
protected IndexColorModel colormap = null;
private Cache cache = null;
public Rdp rdp = null;
// protected int[] backstore_int = null;
// Clip region
protected int top = 0;
protected int left = 0;
protected int right = 0;
protected int bottom = 0;
public RDPCanvas() {
super();
}
protected void init(int width, int height) {
rop = new RasterOp();
this.width = width;
this.height = height;
this.right = width - 1;
this.bottom = height - 1;
setSize(width, height);
backstore = new WrappedImage(width, height, BufferedImage.TYPE_INT_RGB);
}
public void paint(Graphics g) {
update(g);
}
public abstract void update(Graphics g);
/**
* Register a colour palette with this canvas
*
* @param cm
* Colour model to be used with this canvas
*/
public void registerPalette(IndexColorModel cm) {
this.colormap = cm;
backstore.setIndexColorModel(cm);
}
/**
* Register the Rdp layer to act as the communications interface to this
* canvas
*
* @param rdp
* Rdp object controlling Rdp layer communication
*/
public void registerCommLayer(Rdp rdp) {
this.rdp = rdp;
if (fbKeys != null)
input = new Input(this, rdp, fbKeys);
}
/**
* Register keymap
*
* @param keys
* Keymapping object for use in handling keyboard events
*/
public void registerKeyboard(KeyCodeFileBased keys) {
this.fbKeys = keys;
if (rdp != null) {
// rdp and keys have been registered...
input = new Input(this, rdp, keys);
}
}
/**
* Set cache for this session
*
* @param cache
* Cache to be used in this session
*/
public void registerCache(Cache cache) {
this.cache = cache;
}
/**
* Display a compressed bitmap direct to the backstore NOTE: Currently not
* functioning correctly, see Bitmap.decompressImgDirect Does not call
* repaint. Image is drawn to canvas on next update
*
* @param x
* x coordinate within backstore for drawing of bitmap
* @param y
* y coordinate within backstore for drawing of bitmap
* @param width
* Width of bitmap
* @param height
* Height of bitmap
* @param size
* Size (bytes) of compressed bitmap data
* @param data
* Packet containing compressed bitmap data at current read
* position
* @param Bpp
* Bytes-per-pixel for bitmap
* @param cm
* Colour model currently in use, if any
* @throws RdesktopException
*/
public void displayCompressed(int x, int y, int width, int height,
int size, RdpPacket_Localised data, int Bpp, IndexColorModel cm)
throws RdesktopException {
backstore = Bitmap.decompressImgDirect(width, height, size, data, Bpp,
cm, x, y, backstore);
}
/**
* Draw an image object to the backstore, does not call repaint. Image is
* drawn to canvas on next update.
*
* @param img
* Image to draw to backstore
* @param x
* x coordinate for drawing location
* @param y
* y coordinate for drawing location
* @throws RdesktopException
*/
public void displayImage(Image img, int x, int y) throws RdesktopException {
Graphics g = backstore.getGraphics();
g.drawImage(img, x, y, null);
/* Useful test for identifying image boundaries */
// g.setColor(Color.RED);
// g.drawRect(x,y,data.getWidth(null),data.getHeight(null));
g.dispose();
}
/**
* Draw an image (from an integer array of colour data) to the backstore,
* does not call repaint. Image is drawn to canvas on next update.
*
* @param data
* Integer array of pixel colour information
* @param w
* Width of image
* @param h
* Height of image
* @param x
* x coordinate for drawing location
* @param y
* y coordinate for drawing location
* @param cx
* Width of drawn image (clips, does not scale)
* @param cy
* Height of drawn image (clips, does not scale)
* @throws RdesktopException
*/
public void displayImage(int[] data, int w, int h, int x, int y, int cx,
int cy) throws RdesktopException {
backstore.setRGBNoConversion(x, y, cx, cy, data, 0, w);
// repaint(x, y, cx, cy);
/* Useful test for identifying image boundaries */
// Graphics g = backstore.getGraphics();
// g.drawImage(data,x,y,null);
// g.setColor(Color.RED);
// g.drawRect(x,y,cx,cy);
// g.dispose();
}
/**
* Retrieve an image from the backstore, as integer pixel information
*
* @param x
* x coordinate of image to retrieve
* @param y
* y coordinage of image to retrieve
* @param cx
* width of image to retrieve
* @param cy
* height of image to retrieve
* @return Requested area of backstore, as an array of integer pixel colours
*/
public int[] getImage(int x, int y, int cx, int cy) {
int[] data = new int[cx * cy];
data = backstore.getRGB(x, y, cx, cy, null, // no existing image data to
// add to
0, // retrieving as complete image, no offset needed
cx);
return data;
}
/**
* Draw an image (from an integer array of colour data) to the backstore,
* also calls repaint to draw image to canvas
*
* @param x
* x coordinate at which to draw image
* @param y
* y coordinate at which to draw image
* @param cx
* Width of drawn image (clips, does not scale)
* @param cy
* Height of drawn image (clips, does not scale)
* @param data
* Image to draw, represented as an array of integer pixel
* colours
*/
public void putImage(int x, int y, int cx, int cy, int[] data) {
backstore.setRGBNoConversion(x, y, cx, cy, data, 0, // drawing entire
// image, no
// offset needed
cx);
this.repaint(x, y, cx, cy);
}
/**
* Reset clipping boundaries for canvas
*/
public void resetClip() {
Graphics g = this.getGraphics();
Rectangle bounds = this.getBounds();
g.setClip(bounds.x, bounds.y, bounds.width, bounds.height);
this.top = 0;
this.left = 0;
this.right = this.width - 1; // changed
this.bottom = this.height - 1; // changed
}
/**
* Set clipping boundaries for canvas, based on a bounds order
*
* @param bounds
* Order defining new boundaries
*/
public void setClip(BoundsOrder bounds) {
Graphics g = this.getGraphics();
g.setClip(bounds.getLeft(), bounds.getTop(), bounds.getRight()
- bounds.getLeft(), bounds.getBottom() - bounds.getTop());
this.top = bounds.getTop();
this.left = bounds.getLeft();
this.right = bounds.getRight();
this.bottom = bounds.getBottom();
}
/**
* Move the mouse pointer (only available in Java 1.3+)
*
* @param x
* x coordinate for mouse move
* @param y
* y coordinate for mouse move
*/
public void movePointer(int x, int y) {
// need Java 1.3+ to move mouse with Robot
}
/**
* Draw a filled rectangle to the screen
*
* @param x
* x coordinate (left) of rectangle
* @param y
* y coordinate (top) of rectangle
* @param cx
* Width of rectangle
* @param cy
* Height of rectangle
* @param color
* Colour of rectangle
*/
public void fillRectangle(int x, int y, int cx, int cy, int color) {
// clip here instead
if (x > this.right || y > this.bottom)
return; // off screen
int Bpp = Options.Bpp;
// convert to 24-bit colour
color = Bitmap.convertTo24(color);
// correction for 24-bit colour
if (Bpp == 3)
color = ((color & 0xFF) << 16) | (color & 0xFF00)
| ((color & 0xFF0000) >> 16);
// Perform standard clipping checks, x-axis
int clipright = x + cx - 1;
if (clipright > this.right)
clipright = this.right;
if (x < this.left)
x = this.left;
cx = clipright - x + 1;
// Perform standard clipping checks, y-axis
int clipbottom = y + cy - 1;
if (clipbottom > this.bottom)
clipbottom = this.bottom;
if (y < this.top)
y = this.top;
cy = clipbottom - y + 1;
// construct rectangle as integer array, filled with color
int[] rect = new int[cx * cy];
for (int i = 0; i < rect.length; i++)
rect[i] = color;
// draw rectangle to backstore
backFill(x, y, cx, cy, color);
backstore.setRGBNoConversion(x, y, cx, cy, rect, 0, cx);
// if(logger.isInfoEnabled()) logger.info("rect
// \t(\t"+x+",\t"+y+"),(\t"+(x+cx-1)+",\t"+(y+cy-1)+")");
// this.repaint(x, y, cx, cy); // seems to be faster than
// Graphics.fillRect
// according to JProbe
}
public void backFill(int x, int y, int cx, int cy, int color) {
// TODO Auto-generated method stub
}
/**
* Draw a line to the screen
*
* @param x1
* x coordinate of start point of line
* @param y1
* y coordinate of start point of line
* @param x2
* x coordinate of end point of line
* @param y2
* y coordinate of end point of line
* @param color
* colour of line
* @param opcode
* Operation code defining operation to perform on pixels within
* the line
*/
public void drawLine(int x1, int y1, int x2, int y2, int color, int opcode) {
// convert to 24-bit colour
color = Bitmap.convertTo24(color);
if (x1 == x2 || y1 == y2) {
drawLineVerticalHorizontal(x1, y1, x2, y2, color, opcode);
return;
}
int deltax = Math.abs(x2 - x1); // The difference between the x's
int deltay = Math.abs(y2 - y1); // The difference between the y's
int x = x1; // Start x off at the first pixel
int y = y1; // Start y off at the first pixel
int xinc1, xinc2, yinc1, yinc2;
int num, den, numadd, numpixels;
if (x2 >= x1) { // The x-values are increasing
xinc1 = 1;
xinc2 = 1;
} else { // The x-values are decreasing
xinc1 = -1;
xinc2 = -1;
}
if (y2 >= y1) { // The y-values are increasing
yinc1 = 1;
yinc2 = 1;
} else { // The y-values are decreasing
yinc1 = -1;
yinc2 = -1;
}
if (deltax >= deltay) { // There is at least one x-value for every
// y-value
xinc1 = 0; // Don't change the x when numerator >= denominator
yinc2 = 0; // Don't change the y for every iteration
den = deltax;
num = deltax / 2;
numadd = deltay;
numpixels = deltax; // There are more x-values than y-values
} else { // There is at least one y-value for every x-value
xinc2 = 0; // Don't change the x for every iteration
yinc1 = 0; // Don't change the y when numerator >= denominator
den = deltay;
num = deltay / 2;
numadd = deltax;
numpixels = deltay; // There are more y-values than x-values
}
for (int curpixel = 0; curpixel <= numpixels; curpixel++) {
setPixel(opcode, x, y, color); // Draw the current pixel
num += numadd; // Increase the numerator by the top of the fraction
if (num >= den) { // Check if numerator >= denominator
num -= den; // Calculate the new numerator value
x += xinc1; // Change the x as appropriate
y += yinc1; // Change the y as appropriate
}
x += xinc2; // Change the x as appropriate
y += yinc2; // Change the y as appropriate
}
int x_min = x1 < x2 ? x1 : x2;
int x_max = x1 > x2 ? x1 : x2;
int y_min = y1 < y2 ? y1 : y2;
int y_max = y1 > y2 ? y1 : y2;
this.repaint(x_min, y_min, x_max - x_min + 1, y_max - y_min + 1);
}
/**
* Helper function for drawLine, draws a horizontal or vertical line using a
* much faster method than used for diagonal lines
*
* @param x1
* x coordinate of start point of line
* @param y1
* y coordinate of start point of line
* @param x2
* x coordinate of end point of line
* @param y2
* y coordinate of end point of line
* @param color
* colour of line
* @param opcode
* Operation code defining operation to perform on pixels within
* the line
*/
public void drawLineVerticalHorizontal(int x1, int y1, int x2, int y2,
int color, int opcode) {
int pbackstore;
int i;
// only vertical or horizontal lines
if (y1 == y2) { // HORIZONTAL
if (y1 >= this.top && y1 <= this.bottom) { // visible
if (x2 > x1) { // x inc, y1=y2
if (x1 < this.left)
x1 = this.left;
if (x2 > this.right)
x2 = this.right;
pbackstore = y1 * this.width + x1;
for (i = 0; i < x2 - x1; i++) {
rop.do_pixel(opcode, backstore, x1 + i, y1, color);
pbackstore++;
}
repaint(x1, y1, x2 - x1 + 1, 1);
} else { // x dec, y1=y2
if (x2 < this.left)
x2 = this.left;
if (x1 > this.right)
x1 = this.right;
pbackstore = y1 * this.width + x1;
for (i = 0; i < x1 - x2; i++) {
rop.do_pixel(opcode, backstore, x2 + i, y1, color);
pbackstore--;
}
repaint(x2, y1, x1 - x2 + 1, 1);
}
}
} else { // x1==x2 VERTICAL
if (x1 >= this.left && x1 <= this.right) { // visible
if (y2 > y1) { // x1=x2, y inc
if (y1 < this.top)
y1 = this.top;
if (y2 > this.bottom)
y2 = this.bottom;
pbackstore = y1 * this.width + x1;
for (i = 0; i < y2 - y1; i++) {
rop.do_pixel(opcode, backstore, x1, y1 + i, color);
pbackstore += this.width;
}
repaint(x1, y1, 1, y2 - y1 + 1);
} else { // x1=x2, y dec
if (y2 < this.top)
y2 = this.top;
if (y1 > this.bottom)
y1 = this.bottom;
pbackstore = y1 * this.width + x1;
for (i = 0; i < y1 - y2; i++) {
rop.do_pixel(opcode, backstore, x1, y2 + i, color);
pbackstore -= this.width;
}
repaint(x1, y2, 1, y1 - y2 + 1);
}
}
}
// if(logger.isInfoEnabled()) logger.info("line
// \t(\t"+x1+",\t"+y1+"),(\t"+x2+",\t"+y2+")");
}
/**
* Draw a line to the screen
*
* @param line
* LineOrder describing line to be drawn
*/
public void drawLineOrder(LineOrder line) {
int x1 = line.getStartX();
int y1 = line.getStartY();
int x2 = line.getEndX();
int y2 = line.getEndY();
int fgcolor = line.getPen().getColor();
int opcode = line.getOpcode() - 1;
drawLine(x1, y1, x2, y2, fgcolor, opcode);
}
/**
* Perform a dest blt
*
* @param destblt
* DestBltOrder describing the blit to be performed
*/
public void drawDestBltOrder(DestBltOrder destblt) {
int x = destblt.getX();
int y = destblt.getY();
if (x > this.right || y > this.bottom)
return; // off screen
int cx = destblt.getCX();
int cy = destblt.getCY();
int clipright = x + cx - 1;
if (clipright > this.right)
clipright = this.right;
if (x < this.left)
x = this.left;
cx = clipright - x + 1;
int clipbottom = y + cy - 1;
if (clipbottom > this.bottom)
clipbottom = this.bottom;
if (y < this.top)
y = this.top;
cy = clipbottom - y + 1;
rop.do_array(destblt.getOpcode(), backstore, this.width, x, y, cx, cy,
null, 0, 0, 0);
this.repaint(x, y, cx, cy);
}
/**
* Perform a screen blit
*
* @param screenblt
* ScreenBltOrder describing the blit to be performed
*/
public void drawScreenBltOrder(ScreenBltOrder screenblt) {
int x = screenblt.getX();
int y = screenblt.getY();
if (x > this.right || y > this.bottom)
return; // off screen
int cx = screenblt.getCX();
int cy = screenblt.getCY();
int srcx = screenblt.getSrcX();
int srcy = screenblt.getSrcY();
int clipright = x + cx - 1;
if (clipright > this.right)
clipright = this.right;
if (x < this.left)
x = this.left;
cx = clipright - x + 1;
int clipbottom = y + cy - 1;
if (clipbottom > this.bottom)
clipbottom = this.bottom;
if (y < this.top)
y = this.top;
cy = clipbottom - y + 1;
srcx += x - screenblt.getX();
srcy += y - screenblt.getY();
// System.out.println("RdesktopCanvas.drawScreenBltOrder():" + x + "," +
// y
// + "," + cx + "," + cy);
rop.do_array(screenblt.getOpcode(), backstore, this.width, x, y, cx,
cy, null, this.width, srcx, srcy);
// this.repaint(x, y, cx, cy);
blit(x, y, cx, cy, srcx, srcy);
}
public void blit(int x, int y, int w, int h, int srcx, int srcy) {
}
/**
* Perform a memory blit
*
* @param memblt
* MemBltOrder describing the blit to be performed
*/
public void drawMemBltOrder(MemBltOrder memblt) {
int x = memblt.getX();
int y = memblt.getY();
if (x > this.right || y > this.bottom)
return; // off screen
int cx = memblt.getCX();
int cy = memblt.getCY();
int srcx = memblt.getSrcX();
int srcy = memblt.getSrcY();
// Perform standard clipping checks, x-axis
int clipright = x + cx - 1;
if (clipright > this.right)
clipright = this.right;
if (x < this.left)
x = this.left;
cx = clipright - x + 1;
// Perform standard clipping checks, y-axis
int clipbottom = y + cy - 1;
if (clipbottom > this.bottom)
clipbottom = this.bottom;
if (y < this.top)
y = this.top;
cy = clipbottom - y + 1;
srcx += x - memblt.getX();
srcy += y - memblt.getY();
// if (logger.isInfoEnabled())
System.out.println("MEMBLT x=" + x + " y=" + y + " cx=" + cx + " cy="
+ cy + " srcx=" + srcx + " srcy=" + srcy + " opcode="
+ memblt.getOpcode());
try {
Bitmap bitmap = cache.getBitmap(memblt.getCacheID(), memblt
.getCacheIDX());
// IndexColorModel cm = cache.get_colourmap(memblt.getColorTable());
// should use the colormap, but requires high color backstore...
rop.do_array(memblt.getOpcode(), backstore, this.width, x, y, cx,
cy, bitmap.getBitmapData(), bitmap.getWidth(), srcx, srcy);
this.repaint(x, y, cx, cy);
} catch (RdesktopException e) {
}
}
/**
* Draw a pattern to the screen (pattern blit)
*
* @param opcode
* Code defining operation to be performed
* @param x
* x coordinate for left of blit area
* @param y
* y coordinate for top of blit area
* @param cx
* Width of blit area
* @param cy
* Height of blit area
* @param fgcolor
* Foreground colour for pattern
* @param bgcolor
* Background colour for pattern
* @param brush
* Brush object defining pattern to be drawn
*/
public void patBltOrder(int opcode, int x, int y, int cx, int cy,
int fgcolor, int bgcolor, Brush brush) {
// convert to 24-bit colour
fgcolor = Bitmap.convertTo24(fgcolor);
bgcolor = Bitmap.convertTo24(bgcolor);
// Perform standard clipping checks, x-axis
int clipright = x + cx - 1;
if (clipright > this.right)
clipright = this.right;
if (x < this.left)
x = this.left;
cx = clipright - x + 1;
// Perform standard clipping checks, y-axis
int clipbottom = y + cy - 1;
if (clipbottom > this.bottom)
clipbottom = this.bottom;
if (y < this.top)
y = this.top;
cy = clipbottom - y + 1;
int i;
int[] src = null;
switch (brush.getStyle()) {
case 0: // solid
// make efficient version of rop later with int fgcolor and boolean
// usearray set to false for single colour
src = new int[cx * cy];
for (i = 0; i < src.length; i++)
src[i] = fgcolor;
// System.out.println("RdesktopCanvas.patBltOrder():" + x + "," + y
// + "," + cx + "," + cy);
rop.do_array(opcode, backstore, this.width, x, y, cx, cy, src, cx,
0, 0);
this.repaint(x, y, cx, cy);
break;
case 2: // hatch
System.out.println("hatch");
break;
case 3: // pattern
int brushx = brush.getXOrigin();
int brushy = brush.getYOrigin();
byte[] pattern = brush.getPattern();
byte[] ipattern = pattern;
/*
* // not sure if this inversion is needed byte[] ipattern = new
* byte[8]; for(i=0;i<ipattern.length;i++) {
* ipattern[ipattern.length-1-i] = pattern[i]; }
*/
src = new int[cx * cy];
int psrc = 0;
for (i = 0; i < cy; i++) {
for (int j = 0; j < cx; j++) {
if ((ipattern[(i + brushy) % 8] & (0x01 << ((j + brushx) % 8))) == 0)
src[psrc] = fgcolor;
else
src[psrc] = bgcolor;
psrc++;
}
}
rop.do_array(opcode, backstore, this.width, x, y, cx, cy, src, cx,
0, 0);
this.repaint(x, y, cx, cy);
break;
default:
logger.warn("Unsupported brush style " + brush.getStyle());
}
}
/**
* Perform a pattern blit on the screen
*
* @param patblt
* PatBltOrder describing the blit to be performed
*/
public void drawPatBltOrder(PatBltOrder patblt) {
Brush brush = patblt.getBrush();
int x = patblt.getX();
int y = patblt.getY();
if (x > this.right || y > this.bottom)
return; // off screen
int cx = patblt.getCX();
int cy = patblt.getCY();
int fgcolor = patblt.getForegroundColor();
int bgcolor = patblt.getBackgroundColor();
int opcode = patblt.getOpcode();
patBltOrder(opcode, x, y, cx, cy, fgcolor, bgcolor, brush);
}
/**
* Perform a tri blit on the screen
*
* @param triblt
* TriBltOrder describing the blit
*/
public void drawTriBltOrder(TriBltOrder triblt) {
int x = triblt.getX();
int y = triblt.getY();
if (x > this.right || y > this.bottom)
return; // off screen
int cx = triblt.getCX();
int cy = triblt.getCY();
int srcx = triblt.getSrcX();
int srcy = triblt.getSrcY();
int fgcolor = triblt.getForegroundColor();
int bgcolor = triblt.getBackgroundColor();
Brush brush = triblt.getBrush();
// convert to 24-bit colour
fgcolor = Bitmap.convertTo24(fgcolor);
bgcolor = Bitmap.convertTo24(bgcolor);
// Perform standard clipping checks, x-axis
int clipright = x + cx - 1;
if (clipright > this.right)
clipright = this.right;
if (x < this.left)
x = this.left;
cx = clipright - x + 1;
// Perform standard clipping checks, y-axis
int clipbottom = y + cy - 1;
if (clipbottom > this.bottom)
clipbottom = this.bottom;
if (y < this.top)
y = this.top;
cy = clipbottom - y + 1;
try {
Bitmap bitmap = cache.getBitmap(triblt.getCacheID(), triblt
.getCacheIDX());
switch (triblt.getOpcode()) {
case 0x69: // PDSxxn
rop.do_array(ROP2_XOR, backstore, this.width, x, y, cx, cy,
bitmap.getBitmapData(), bitmap.getWidth(), srcx, srcy);
patBltOrder(ROP2_NXOR, x, y, cx, cy, fgcolor, bgcolor, brush);
break;
case 0xb8: // PSDPxax
patBltOrder(ROP2_XOR, x, y, cx, cy, fgcolor, bgcolor, brush);
rop.do_array(ROP2_AND, backstore, this.width, x, y, cx, cy,
bitmap.getBitmapData(), bitmap.getWidth(), srcx, srcy);
patBltOrder(ROP2_XOR, x, y, cx, cy, fgcolor, bgcolor, brush);
break;
case 0xc0: // PSa
rop.do_array(ROP2_COPY, backstore, this.width, x, y, cx, cy,
bitmap.getBitmapData(), bitmap.getWidth(), srcx, srcy);
patBltOrder(ROP2_AND, x, y, cx, cy, fgcolor, bgcolor, brush);
break;
default:
logger
.warn("Unimplemented Triblt opcode:"
+ triblt.getOpcode());
rop.do_array(ROP2_COPY, backstore, this.width, x, y, cx, cy,
bitmap.getBitmapData(), bitmap.getWidth(), srcx, srcy);
}
} catch (RdesktopException e) {
}
}
/**
* Parse a delta co-ordinate in polyline order form
*
* @param buffer
* @param offset
* @return
*/
static int parse_delta(byte[] buffer, int[] offset) {
int value = buffer[offset[0]++] & 0xff;
int two_byte = value & 0x80;
if ((value & 0x40) != 0) /* sign bit */
value |= ~0x3f;
else
value &= 0x3f;
if (two_byte != 0)
value = (value << 8) | (buffer[offset[0]++] & 0xff);
return value;
}
/**
* Draw a multi-point set of lines to the screen
*
* @param polyline
* PolyLineOrder describing the set of lines to draw
*/
public void drawPolyLineOrder(PolyLineOrder polyline) {
int x = polyline.getX();
int y = polyline.getY();
int fgcolor = polyline.getForegroundColor();
int datasize = polyline.getDataSize();
byte[] databytes = polyline.getData();
int lines = polyline.getLines();
// convert to 24-bit colour
fgcolor = Bitmap.convertTo24(fgcolor);
// hack - data as single element byte array so can pass by ref to
// parse_delta
// see http://www.rgagnon.com/javadetails/java-0035.html
int[] data = new int[1];
data[0] = ((lines - 1) / 4) + 1;
int flags = 0;
int index = 0;
int opcode = polyline.getOpcode() - 1;
for (int line = 0; (line < lines) && (data[0] < datasize); line++) {
int xfrom = x;
int yfrom = y;
if (line % 4 == 0)
flags = databytes[index++];
if ((flags & 0xc0) == 0)
flags |= 0xc0; /* none = both */
if ((flags & 0x40) != 0)
x += parse_delta(databytes, data);
if ((flags & 0x80) != 0)
y += parse_delta(databytes, data);
// logger.info("polyline
// "+line+","+xfrom+","+yfrom+","+x+","+y+","+fgcolor+","+opcode);
drawLine(xfrom, yfrom, x, y, fgcolor, opcode);
flags <<= 2;
}
}
/**
* Draw a rectangle to the screen
*
* @param rect
* RectangleOrder defining the rectangle to be drawn
*/
public void drawRectangleOrder(RectangleOrder rect) {
// if(logger.isInfoEnabled()) logger.info("RectangleOrder!");
fillRectangle(rect.getX(), rect.getY(), rect.getCX(), rect.getCY(),
rect.getColor());
}
/**
* Perform an operation on a pixel in the backstore
*
* @param opcode
* ID of operation to perform
* @param x
* x coordinate of pixel
* @param y
* y coordinate of pixel
* @param color
* Colour value to be used in operation
*/
public void setPixel(int opcode, int x, int y, int color) {
int Bpp = Options.Bpp;
// correction for 24-bit colour
if (Bpp == 3)
color = ((color & 0xFF) << 16) | (color & 0xFF00)
| ((color & 0xFF0000) >> 16);
if ((x < this.left) || (x > this.right) || (y < this.top)
|| (y > this.bottom)) { // Clip
} else {
rop.do_pixel(opcode, backstore, x, y, color);
}
}
/**
* Draw a single glyph to the screen
*
* @param mixmode
* 0 for transparent background, specified colour for background
* otherwide
* @param x
* x coordinate on screen at which to draw glyph
* @param y
* y coordinate on screen at which to draw glyph
* @param cx
* Width of clipping area for glyph
* @param cy
* Height of clipping area for glyph
* @param data
* Set of values defining glyph's pattern
* @param bgcolor
* Background colour for glyph pattern
* @param fgcolor
* Foreground colour for glyph pattern
*/
public void drawGlyph(int mixmode, int x, int y, int cx, int cy,
byte[] data, int bgcolor, int fgcolor) {
int pdata = 0;
int index = 0x80;
int bytes_per_row = (cx - 1) / 8 + 1;
int newx, newy, newcx, newcy;
int Bpp = Options.Bpp;
// convert to 24-bit colour
fgcolor = Bitmap.convertTo24(fgcolor);
bgcolor = Bitmap.convertTo24(bgcolor);
// correction for 24-bit colour
if (Bpp == 3) {
fgcolor = ((fgcolor & 0xFF) << 16) | (fgcolor & 0xFF00)
| ((fgcolor & 0xFF0000) >> 16);
bgcolor = ((bgcolor & 0xFF) << 16) | (bgcolor & 0xFF00)
| ((bgcolor & 0xFF0000) >> 16);
}
// clip here instead
if (x > this.right || y > this.bottom)
return; // off screen
int clipright = x + cx - 1;
if (clipright > this.right)
clipright = this.right;
// if (x < this.left)
// newx = this.left;
// else
newx = x;
newcx = clipright - x + 1; // not clipright - newx - 1
int clipbottom = y + cy - 1;
if (clipbottom > this.bottom)
clipbottom = this.bottom;
if (y < this.top)
newy = this.top;
else
newy = y;
newcy = clipbottom - newy + 1;
int pbackstore = (newy * this.width) + x;
pdata = bytes_per_row * (newy - y); // offset y, but not x
if (mixmode == MIX_TRANSPARENT) { // FillStippled
for (int i = 0; i < newcy; i++) {
for (int j = 0; j < newcx; j++) {
if (index == 0) { // next row
pdata++;
index = 0x80;
}
if ((data[pdata] & index) != 0) {
if ((x + j >= newx) && (newx + j > 0) && (newy + i > 0))
// since haven't offset x
backstore.setRGB(newx + j, newy + i, fgcolor);
}
index >>= 1;
}
pdata++;
index = 0x80;
pbackstore += this.width;
if (pdata == data.length) {
pdata = 0;
}
}
} else { // FillOpaqueStippled
for (int i = 0; i < newcy; i++) {
for (int j = 0; j < newcx; j++) {
if (index == 0) { // next row
pdata++;
index = 0x80;
}
if (x + j >= newx) {
if ((x + j > 0) && (y + i > 0)) {
if ((data[pdata] & index) != 0)
backstore.setRGB(x + j, y + i, fgcolor);
else
backstore.setRGB(x + j, y + i, bgcolor);
}
}
index >>= 1;
}
pdata++;
index = 0x80;
pbackstore += this.width;
if (pdata == data.length) {
pdata = 0;
}
}
}
// if(logger.isInfoEnabled()) logger.info("glyph
// \t(\t"+x+",\t"+y+"),(\t"+(x+cx-1)+",\t"+(y+cy-1)+")");
// System.out.println("RdesktopCanvas.drawGlyph():" + x + "," + y + ","
// + cx + "," + cy);
this.repaint(newx, newy, newcx, newcy);
}
/**
* Create an AWT Cursor object
*
* @param x
* @param y
* @param w
* @param h
* @param andmask
* @param xormask
* @param cache_idx
* @return Created Cursor
*/
public Cursor createCursor(int x, int y, int w, int h, byte[] andmask,
byte[] xormask, int cache_idx) {
int pxormask = 0;
int pandmask = 0;
Point p = new Point(x, y);
int size = w * h;
int scanline = w / 8;
int offset = 0;
byte[] mask = new byte[size];
int[] cursor = new int[size];
int pcursor = 0, pmask = 0;
offset = size;
for (int i = 0; i < h; i++) {
offset -= w;
pmask = offset;
for (int j = 0; j < scanline; j++) {
for (int bit = 0x80; bit > 0; bit >>= 1) {
if ((andmask[pandmask] & bit) != 0) {
mask[pmask] = 0;
} else {
mask[pmask] = 1;
}
pmask++;
}
pandmask++;
}
}
offset = size;
pcursor = 0;
for (int i = 0; i < h; i++) {
offset -= w;
pcursor = offset;
for (int j = 0; j < w; j++) {
cursor[pcursor] = ((xormask[pxormask + 2] << 16) & 0x00ff0000)
| ((xormask[pxormask + 1] << 8) & 0x0000ff00)
| (xormask[pxormask] & 0x000000ff);
pxormask += 3;
pcursor++;
}
}
offset = size;
pmask = 0;
pcursor = 0;
pxormask = 0;
for (int i = 0; i < h; i++) {
for (int j = 0; j < w; j++) {
if ((mask[pmask] == 0) && (cursor[pcursor] != 0)) {
cursor[pcursor] = ~(cursor[pcursor]);
cursor[pcursor] |= 0xff000000;
} else if ((mask[pmask] == 1) || (cursor[pcursor] != 0)) {
cursor[pcursor] |= 0xff000000;
}
pcursor++;
pmask++;
}
}
Image wincursor = this.createImage(new MemoryImageSource(w, h, cursor,
0, w));
return createCustomCursor(wincursor, p, "", cache_idx);
}
/**
* Create an AWT Cursor from an image
*
* @param wincursor
* @param p
* @param s
* @param cache_idx
* @return Generated Cursor object
*/
protected Cursor createCustomCursor(Image wincursor, Point p, String s,
int cache_idx) {
if (cache_idx == 1)
return Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR);
return Cursor.getDefaultCursor();
}
/**
* Handle the window losing focus, notify input classes
*/
public void lostFocus() {
if (input != null)
input.lostFocus();
}
/**
* Handle the window gaining focus, notify input classes
*/
public void gainedFocus() {
if (input != null)
input.gainedFocus();
}
/**
* Notify the input classes that the connection is ready for sending
* messages
*/
public void triggerReadyToSend() {
input.triggerReadyToSend();
}
public void repaint(int x, int y, int width, int height) {
// System.err.println("RdesktopCanvas.repaint():" + x + "," + y + ","
// + width + "," + height);
// Thread.dumpStack();
repaint(0, x, y, width, height);
}
public void setCursorFromIdx(int cache_idx) {
// TODO Auto-generated method stub
}
}