aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--java/com/tigervnc/rdr/InStream.java10
-rw-r--r--java/com/tigervnc/rdr/JavaInStream.java10
-rw-r--r--java/com/tigervnc/rfb/CMsgHandler.java4
-rw-r--r--java/com/tigervnc/rfb/PixelFormat.java148
-rw-r--r--java/com/tigervnc/rfb/TightDecoder.java224
-rw-r--r--java/com/tigervnc/vncviewer/CConn.java8
-rw-r--r--java/com/tigervnc/vncviewer/DesktopWindow.java4
-rw-r--r--java/com/tigervnc/vncviewer/PixelBufferImage.java2
8 files changed, 282 insertions, 128 deletions
diff --git a/java/com/tigervnc/rdr/InStream.java b/java/com/tigervnc/rdr/InStream.java
index 60967991..a18ea4ed 100644
--- a/java/com/tigervnc/rdr/InStream.java
+++ b/java/com/tigervnc/rdr/InStream.java
@@ -107,16 +107,6 @@ abstract public class InStream {
}
}
- public void readBytes(int[] data, int dataPtr, int length) {
- int dataEnd = dataPtr + length;
- while (dataPtr < dataEnd) {
- int n = check(1, dataEnd - dataPtr, true);
- System.arraycopy(b, ptr, data, dataPtr, n);
- ptr += n;
- dataPtr += n;
- }
- }
-
// readOpaqueN() reads a quantity "without byte-swapping". Because java has
// no byte-ordering, we just use big-endian.
diff --git a/java/com/tigervnc/rdr/JavaInStream.java b/java/com/tigervnc/rdr/JavaInStream.java
index d2eda520..faa968ac 100644
--- a/java/com/tigervnc/rdr/JavaInStream.java
+++ b/java/com/tigervnc/rdr/JavaInStream.java
@@ -98,7 +98,14 @@ public class JavaInStream extends InStream {
ptr = 0;
while (end < itemSize) {
- int n = read(b, end, bufSize - end, wait);
+ int bytes_to_read = bufSize - end;
+
+ if (!timing) {
+ bytes_to_read = Math.min(bytes_to_read, Math.max(itemSize*nItems, 8));
+ }
+
+ int n = read(b, end, bytes_to_read, wait);
+
end += n;
}
@@ -121,6 +128,7 @@ public class JavaInStream extends InStream {
}
if (n < 0) throw new EndOfStream();
+ if (n == 0) return 0;
if (timing) {
long after = System.nanoTime();
diff --git a/java/com/tigervnc/rfb/CMsgHandler.java b/java/com/tigervnc/rfb/CMsgHandler.java
index 81fd2a1b..31182003 100644
--- a/java/com/tigervnc/rfb/CMsgHandler.java
+++ b/java/com/tigervnc/rfb/CMsgHandler.java
@@ -22,7 +22,7 @@
package com.tigervnc.rfb;
-public class CMsgHandler {
+abstract public class CMsgHandler {
public CMsgHandler() {
cp = new ConnParams();
@@ -82,6 +82,8 @@ public class CMsgHandler {
public void imageRect(Rect r, int[] pixels) {}
public void copyRect(Rect r, int srcX, int srcY) {}
+ abstract public PixelFormat getPreferredPF();
+
public ConnParams cp;
static LogWriter vlog = new LogWriter("CMsgHandler");
diff --git a/java/com/tigervnc/rfb/PixelFormat.java b/java/com/tigervnc/rfb/PixelFormat.java
index a8ab5f11..07f8487e 100644
--- a/java/com/tigervnc/rfb/PixelFormat.java
+++ b/java/com/tigervnc/rfb/PixelFormat.java
@@ -23,6 +23,7 @@
package com.tigervnc.rfb;
import com.tigervnc.rdr.*;
+import java.awt.image.ColorModel;
public class PixelFormat {
@@ -102,14 +103,123 @@ public class PixelFormat {
return true;
}
- public void bufferFromRGB(int dst, byte[] src) {
+ public int pixelFromRGB(int red, int green, int blue, ColorModel cm)
+ {
+ if (trueColour) {
+ int r = (red * redMax + 32767) / 65535;
+ int g = (green * greenMax + 32767) / 65535;
+ int b = (blue * blueMax + 32767) / 65535;
+
+ return (r << redShift) | (g << greenShift) | (b << blueShift);
+ } else if (cm != null) {
+ // Try to find the closest pixel by Cartesian distance
+ int colours = 1 << depth;
+ int diff = 256 * 256 * 4;
+ int col = 0;
+ for (int i=0; i<colours; i++) {
+ int r, g, b;
+ r = cm.getRed(i);
+ g = cm.getGreen(i);
+ b = cm.getBlue(i);
+ int rd = (r-red) >> 8;
+ int gd = (g-green) >> 8;
+ int bd = (b-blue) >> 8;
+ int d = rd*rd + gd*gd + bd*bd;
+ if (d < diff) {
+ col = i;
+ diff = d;
+ }
+ }
+ return col;
+ }
+ // XXX just return 0 for colour map?
+ return 0;
+ }
+
+ public void bufferFromRGB(int[] dst, int dstPtr, byte[] src,
+ int srcPtr, int pixels) {
+ if (is888()) {
+ // Optimised common case
+ int r, g, b;
+
+ for (int i=srcPtr; i < pixels; i++) {
+ if (bigEndian) {
+ r = (src[3*i+0] & 0xff) << (24 - redShift);
+ g = (src[3*i+1] & 0xff) << (24 - greenShift);
+ b = (src[3*i+2] & 0xff) << (24 - blueShift);
+ dst[dstPtr+i] = r | g | b | 0xff;
+ } else {
+ r = (src[3*i+0] & 0xff) << redShift;
+ g = (src[3*i+1] & 0xff) << greenShift;
+ b = (src[3*i+2] & 0xff) << blueShift;
+ dst[dstPtr+i] = (0xff << 24) | r | g | b;
+ }
+ }
+ } else {
+ // Generic code
+ int p, r, g, b;
+ int[] rgb = new int[4];
+
+ int i = srcPtr; int j = dstPtr;
+ while (i < pixels) {
+ r = src[i++] & 0xff;
+ g = src[i++] & 0xff;
+ b = src[i++] & 0xff;
+
+ //p = pixelFromRGB(r, g, b, cm);
+ p = ColorModel.getRGBdefault().getDataElement(new int[] {0xff, r, g, b}, 0);
+
+ bufferFromPixel(dst, j, p);
+ j += bpp/8;
+ }
+ }
+ }
+
+ public void rgbFromBuffer(byte[] dst, int dstPtr, byte[] src, int srcPtr, int pixels, ColorModel cm)
+ {
+ int p;
+ byte r, g, b;
+
+ for (int i=0; i < pixels; i++) {
+ p = pixelFromBuffer(src, srcPtr);
+ srcPtr += bpp/8;
+
+ dst[dstPtr++] = (byte)cm.getRed(p);
+ dst[dstPtr++] = (byte)cm.getGreen(p);
+ dst[dstPtr++] = (byte)cm.getBlue(p);
+ }
+ }
+
+ public int pixelFromBuffer(byte[] buffer, int bufferPtr)
+ {
+ int p;
+
+ p = 0;
+
if (bigEndian) {
- dst =
- (src[0] & 0xFF) << 16 | (src[1] & 0xFF) << 8 | (src[2] & 0xFF) | 0xFF << 24;
+ switch (bpp) {
+ case 32:
+ p = (buffer[0] & 0xff) << 24 | (buffer[1] & 0xff) << 16 | (buffer[2] & 0xff) << 8 | 0xff;
+ break;
+ case 16:
+ p = (buffer[0] & 0xff) << 8 | (buffer[1] & 0xff);
+ break;
+ case 8:
+ p = (buffer[0] & 0xff);
+ break;
+ }
} else {
- dst =
- (src[2] & 0xFF) << 16 | (src[1] & 0xFF) << 8 | (src[0] & 0xFF) | 0xFF << 24;
+ p = (buffer[0] & 0xff);
+ if (bpp >= 16) {
+ p |= (buffer[1] & 0xff) << 8;
+ if (bpp == 32) {
+ p |= (buffer[2] & 0xff) << 16;
+ p |= (buffer[3] & 0xff) << 24;
+ }
+ }
}
+
+ return p;
}
public String print() {
@@ -150,6 +260,34 @@ public class PixelFormat {
return s.toString();
}
+ public void bufferFromPixel(int[] buffer, int bufPtr, int p)
+ {
+ if (bigEndian) {
+ switch (bpp) {
+ case 32:
+ buffer[bufPtr++] = (p >> 24) & 0xff;
+ buffer[bufPtr++] = (p >> 16) & 0xff;
+ break;
+ case 16:
+ buffer[bufPtr++] = (p >> 8) & 0xff;
+ break;
+ case 8:
+ buffer[bufPtr++] = (p >> 0) & 0xff;
+ break;
+ }
+ } else {
+ buffer[0] = (p >> 0) & 0xff;
+ if (bpp >= 16) {
+ buffer[1] = (p >> 8) & 0xff;
+ if (bpp == 32) {
+ buffer[2] = (p >> 16) & 0xff;
+ buffer[3] = (p >> 24) & 0xff;
+ }
+ }
+ }
+ }
+
+
public int bpp;
public int depth;
public boolean bigEndian;
diff --git a/java/com/tigervnc/rfb/TightDecoder.java b/java/com/tigervnc/rfb/TightDecoder.java
index 76986277..6f87ffdc 100644
--- a/java/com/tigervnc/rfb/TightDecoder.java
+++ b/java/com/tigervnc/rfb/TightDecoder.java
@@ -52,12 +52,13 @@ public class TightDecoder extends Decoder {
public void readRect(Rect r, CMsgHandler handler)
{
InStream is = reader.getInStream();
- int[] buf = reader.getImageBuf(r.width() * r.height());
boolean cutZeros = false;
- PixelFormat myFormat = handler.cp.pf();
- int bpp = handler.cp.pf().bpp;
+ clientpf = handler.getPreferredPF();
+ serverpf = handler.cp.pf();
+ int bpp = serverpf.bpp;
+ cutZeros = false;
if (bpp == 32) {
- if (myFormat.is888()) {
+ if (serverpf.is888()) {
cutZeros = true;
}
}
@@ -76,38 +77,21 @@ public class TightDecoder extends Decoder {
// "Fill" compression type.
if (comp_ctl == rfbTightFill) {
- int pix;
+ int[] pix = new int[1];
if (cutZeros) {
- pix = is.readPixel(3, !bigEndian);
+ byte[] bytebuf = new byte[3];
+ is.readBytes(bytebuf, 0, 3);
+ serverpf.bufferFromRGB(pix, 0, bytebuf, 0, 1);
} else {
- pix = (bpp == 8) ? is.readOpaque8() : is.readOpaque24B();
+ pix[0] = is.readPixel(serverpf.bpp/8, serverpf.bigEndian);
}
- handler.fillRect(r, pix);
+ handler.fillRect(r, pix[0]);
return;
}
// "JPEG" compression type.
if (comp_ctl == rfbTightJpeg) {
- // Read length
- int compressedLen = is.readCompactLength();
- if (compressedLen <= 0)
- vlog.info("Incorrect data received from the server.");
-
- // Allocate netbuf and read in data
- byte[] netbuf = new byte[compressedLen];
- is.readBytes(netbuf, 0, compressedLen);
-
- // Create an Image object from the JPEG data.
- BufferedImage jpeg = new BufferedImage(r.width(), r.height(), BufferedImage.TYPE_4BYTE_ABGR_PRE);
- jpeg.setAccelerationPriority(1);
- try {
- jpeg = ImageIO.read(new ByteArrayInputStream(netbuf));
- } catch (java.io.IOException e) {
- e.printStackTrace();
- }
- jpeg.getRGB(0, 0, r.width(), r.height(), buf, 0, r.width());
- jpeg = null;
- handler.imageRect(r, buf);
+ DECOMPRESS_JPEG_RECT(r, is, handler);
return;
}
@@ -127,12 +111,13 @@ public class TightDecoder extends Decoder {
switch (filterId) {
case rfbTightFilterPalette:
palSize = is.readU8() + 1;
+ byte[] tightPalette;
if (cutZeros) {
- is.readPixels(palette, palSize, 3, !bigEndian);
+ tightPalette = new byte[256 * 3];
+ is.readBytes(tightPalette, 0, palSize * 3);
+ serverpf.bufferFromRGB(palette, 0, tightPalette, 0, palSize);
} else {
- for (int i = 0; i < palSize; i++) {
- palette[i] = (bpp == 8) ? is.readOpaque8() : is.readOpaque24B();
- }
+ is.readPixels(palette, palSize, serverpf.bpp/8, serverpf.bigEndian);
}
break;
case rfbTightFilterGradient:
@@ -166,56 +151,66 @@ public class TightDecoder extends Decoder {
input = (ZlibInStream)zis[streamId];
}
+ // Allocate netbuf and read in data
+ byte[] netbuf = new byte[dataSize];
+ input.readBytes(netbuf, 0, dataSize);
+
+ int stride = r.width();
+ int[] buf = reader.getImageBuf(r.area());
+
+
if (palSize == 0) {
// Truecolor data.
if (useGradient) {
- vlog.info("useGradient");
if (bpp == 32 && cutZeros) {
- vlog.info("FilterGradient24");
- FilterGradient24(r, input, dataSize, buf, handler);
+ FilterGradient24(netbuf, buf, stride, r);
} else {
- vlog.info("FilterGradient");
- FilterGradient(r, input, dataSize, buf, handler);
+ FilterGradient(netbuf, buf, stride, r);
}
} else {
+ // Copy
+ int h = r.height();
+ int w = r.width();
if (cutZeros) {
- input.readPixels(buf, r.area(), 3, !bigEndian);
+ serverpf.bufferFromRGB(buf, 0, netbuf, 0, w*h);
} else {
- byte[] netbuf = new byte[dataSize];
- input.readBytes(netbuf, 0, dataSize);
for (int i = 0; i < dataSize; i++)
buf[i] = netbuf[i] & 0xff;
}
}
} else {
- int x, y, b;
- int ptr = 0;
- int bits;
+ // Indexed color
+ int x, h = r.height(), w = r.width(), b, pad = stride - w;
+ int ptr = 0;
+ int srcPtr = 0, bits;
if (palSize <= 2) {
// 2-color palette
- int height = r.height();
- int width = r.width();
- for (y = 0; y < height; y++) {
- for (x = 0; x < width / 8; x++) {
- bits = input.readU8();
+ while (h > 0) {
+ for (x = 0; x < w / 8; x++) {
+ bits = netbuf[srcPtr++];
for(b = 7; b >= 0; b--) {
buf[ptr++] = palette[bits >> b & 1];
}
}
- if (width % 8 != 0) {
- bits = input.readU8();
- for (b = 7; b >= 8 - width % 8; b--) {
+ if (w % 8 != 0) {
+ bits = netbuf[srcPtr++];
+ for (b = 7; b >= 8 - w % 8; b--) {
buf[ptr++] = palette[bits >> b & 1];
}
}
+ ptr += pad;
+ h--;
}
} else {
// 256-color palette
- int area = r.area();
- byte[] netbuf = new byte[area];
- input.readBytes(netbuf, 0, area);
- for (int i = 0; i < area; i++)
- buf[ptr++] = palette[netbuf[i] & 0xff];
+ while (h > 0) {
+ int endOfRow = ptr + w;
+ while (ptr < endOfRow) {
+ buf[ptr++] = palette[netbuf[srcPtr++] & 0xff];
+ }
+ ptr += pad;
+ h--;
+ }
}
}
@@ -226,118 +221,129 @@ public class TightDecoder extends Decoder {
}
}
- private CMsgReader reader;
- private ZlibInStream[] zis;
- static LogWriter vlog = new LogWriter("TightDecoder");
+ final private void DECOMPRESS_JPEG_RECT(Rect r, InStream is, CMsgHandler handler)
+ {
+ // Read length
+ int compressedLen = is.readCompactLength();
+ if (compressedLen <= 0)
+ vlog.info("Incorrect data received from the server.");
- //
- // Decode data processed with the "Gradient" filter.
- //
+ // Allocate netbuf and read in data
+ byte[] netbuf = new byte[compressedLen];
+ is.readBytes(netbuf, 0, compressedLen);
+
+ // Create an Image object from the JPEG data.
+ int imageType = BufferedImage.TYPE_4BYTE_ABGR_PRE;
+
+ BufferedImage jpeg =
+ new BufferedImage(r.width(), r.height(), imageType);
+ jpeg.setAccelerationPriority(1);
+ try {
+ jpeg = ImageIO.read(new ByteArrayInputStream(netbuf));
+ } catch (java.io.IOException e) {
+ e.printStackTrace();
+ }
+ int[] buf = reader.getImageBuf(r.area());
+ jpeg.getRGB(0, 0, r.width(), r.height(), buf, 0, r.width());
+ jpeg = null;
+ handler.imageRect(r, buf);
+ }
- final private void FilterGradient24(Rect r, InStream is, int dataSize, int[] buf, CMsgHandler handler) {
+ final private void FilterGradient24(byte[] netbuf, int[] buf, int stride,
+ Rect r)
+ {
int x, y, c;
- int[] prevRow = new int[TIGHT_MAX_WIDTH * 3];
- int[] thisRow = new int[TIGHT_MAX_WIDTH * 3];
- int[] pix = new int[3];
+ byte[] prevRow = new byte[TIGHT_MAX_WIDTH*3];
+ byte[] thisRow = new byte[TIGHT_MAX_WIDTH*3];
+ byte[] pix = new byte[3];
int[] est = new int[3];
- PixelFormat myFormat = handler.cp.pf();
-
- // Allocate netbuf and read in data
- int[] netbuf = new int[dataSize];
- is.readBytes(netbuf, 0, dataSize);
+ // Set up shortcut variables
int rectHeight = r.height();
int rectWidth = r.width();
for (y = 0; y < rectHeight; y++) {
/* First pixel in a row */
for (c = 0; c < 3; c++) {
- pix[c] = netbuf[y*rectWidth*3+c] + prevRow[c];
+ pix[c] = (byte)(netbuf[y*rectWidth*3+c] + prevRow[c]);
thisRow[c] = pix[c];
}
- if (myFormat.bigEndian) {
- buf[y*rectWidth] = 0xff000000 | (pix[2] & 0xff)<<16 | (pix[1] & 0xff)<<8 | (pix[0] & 0xff);
- } else {
- buf[y*rectWidth] = 0xff000000 | (pix[0] & 0xff)<<16 | (pix[1] & 0xff)<<8 | (pix[2] & 0xff);
- }
+ serverpf.bufferFromRGB(buf, y*stride, pix, 0, 1);
/* Remaining pixels of a row */
for (x = 1; x < rectWidth; x++) {
for (c = 0; c < 3; c++) {
- est[c] = prevRow[x*3+c] + pix[c] - prevRow[(x-1)*3+c];
+ est[c] = (int)(prevRow[x*3+c] + pix[c] - prevRow[(x-1)*3+c]);
if (est[c] > 0xFF) {
est[c] = 0xFF;
} else if (est[c] < 0) {
est[c] = 0;
}
- pix[c] = netbuf[(y*rectWidth+x)*3+c] + est[c];
+ pix[c] = (byte)(netbuf[(y*rectWidth+x)*3+c] + est[c]);
thisRow[x*3+c] = pix[c];
}
- if (myFormat.bigEndian) {
- buf[y*rectWidth+x] = 0xff000000 | (pix[2] & 0xff)<<16 | (pix[1] & 0xff)<<8 | (pix[0] & 0xff);
- } else {
- buf[y*rectWidth+x] = 0xff000000 | (pix[0] & 0xff)<<16 | (pix[1] & 0xff)<<8 | (pix[2] & 0xff);
- }
+ serverpf.bufferFromRGB(buf, y*stride+x, pix, 0, 1);
}
System.arraycopy(thisRow, 0, prevRow, 0, prevRow.length);
}
}
- final private void FilterGradient(Rect r, InStream is, int dataSize, int[] buf, CMsgHandler handler) {
+ final private void FilterGradient(byte[] netbuf, int[] buf, int stride,
+ Rect r)
+ {
int x, y, c;
- int[] prevRow = new int[TIGHT_MAX_WIDTH];
- int[] thisRow = new int[TIGHT_MAX_WIDTH];
- int[] pix = new int[3];
+ byte[] prevRow = new byte[TIGHT_MAX_WIDTH];
+ byte[] thisRow = new byte[TIGHT_MAX_WIDTH];
+ byte[] pix = new byte[3];
int[] est = new int[3];
- PixelFormat myFormat = handler.cp.pf();
-
- // Allocate netbuf and read in data
- int[] netbuf = new int[dataSize];
- is.readBytes(netbuf, 0, dataSize);
+ // Set up shortcut variables
int rectHeight = r.height();
int rectWidth = r.width();
for (y = 0; y < rectHeight; y++) {
/* First pixel in a row */
- if (myFormat.bigEndian) {
- buf[y*rectWidth] = 0xff000000 | (pix[2] & 0xff)<<16 | (pix[1] & 0xff)<<8 | (pix[0] & 0xff);
- } else {
- buf[y*rectWidth] = 0xff000000 | (pix[0] & 0xff)<<16 | (pix[1] & 0xff)<<8 | (pix[2] & 0xff);
- }
+ // FIXME
+ //serverpf.rgbFromBuffer(pix, 0, netbuf, y*rectWidth, 1, cm);
for (c = 0; c < 3; c++)
pix[c] += prevRow[c];
+ System.arraycopy(pix, 0, thisRow, 0, pix.length);
+
+ serverpf.bufferFromRGB(buf, y*stride, pix, 0, 1);
+
/* Remaining pixels of a row */
for (x = 1; x < rectWidth; x++) {
for (c = 0; c < 3; c++) {
- est[c] = prevRow[x*3+c] + pix[c] - prevRow[(x-1)*3+c];
- if (est[c] > 255) {
- est[c] = 255;
+ est[c] = (int)(prevRow[x*3+c] + pix[c] - prevRow[(x-1)*3+c]);
+ if (est[c] > 0xff) {
+ est[c] = 0xff;
} else if (est[c] < 0) {
est[c] = 0;
}
}
- // FIXME?
- System.arraycopy(pix, 0, netbuf, 0, netbuf.length);
+ // FIXME
+ //serverpf.rgbFromBuffer(pix, 0, netbuf, y*rectWidth+x, 1, cm);
for (c = 0; c < 3; c++)
pix[c] += est[c];
- System.arraycopy(thisRow, x*3, pix, 0, pix.length);
+ System.arraycopy(pix, 0, thisRow, x*3, pix.length);
- if (myFormat.bigEndian) {
- buf[y*rectWidth+x] = 0xff000000 | (pix[2] & 0xff)<<16 | (pix[1] & 0xff)<<8 | (pix[0] & 0xff);
- } else {
- buf[y*rectWidth+x] = 0xff000000 | (pix[0] & 0xff)<<16 | (pix[1] & 0xff)<<8 | (pix[2] & 0xff);
- }
+ serverpf.bufferFromRGB(buf, y*stride+x, pix, 0, 1);
}
System.arraycopy(thisRow, 0, prevRow, 0, prevRow.length);
}
}
+ private CMsgReader reader;
+ private ZlibInStream[] zis;
+ private PixelFormat serverpf;
+ private PixelFormat clientpf;
+ static LogWriter vlog = new LogWriter("TightDecoder");
+
}
diff --git a/java/com/tigervnc/vncviewer/CConn.java b/java/com/tigervnc/vncviewer/CConn.java
index 136ad7c5..ae03b524 100644
--- a/java/com/tigervnc/vncviewer/CConn.java
+++ b/java/com/tigervnc/vncviewer/CConn.java
@@ -309,7 +309,7 @@ public class CConn extends CConnection
desktop = new DesktopWindow(cp.width, cp.height, serverPF, this);
//desktopEventHandler = desktop.setEventHandler(this);
//desktop.addEventMask(KeyPressMask | KeyReleaseMask);
- fullColourPF = desktop.getPF();
+ fullColourPF = desktop.getPreferredPF();
if (!serverPF.trueColour)
fullColour = true;
recreateViewport();
@@ -468,13 +468,19 @@ public class CConn extends CConnection
public void fillRect(Rect r, int p) {
desktop.fillRect(r.tl.x, r.tl.y, r.width(), r.height(), p);
}
+
public void imageRect(Rect r, int[] p) {
desktop.imageRect(r.tl.x, r.tl.y, r.width(), r.height(), p);
}
+
public void copyRect(Rect r, int sx, int sy) {
desktop.copyRect(r.tl.x, r.tl.y, r.width(), r.height(), sx, sy);
}
+ public PixelFormat getPreferredPF() {
+ return fullColourPF;
+ }
+
public void setCursor(int width, int height, Point hotspot,
int[] data, byte[] mask) {
desktop.setCursor(width, height, hotspot, data, mask);
diff --git a/java/com/tigervnc/vncviewer/DesktopWindow.java b/java/com/tigervnc/vncviewer/DesktopWindow.java
index c0071dad..087b58fd 100644
--- a/java/com/tigervnc/vncviewer/DesktopWindow.java
+++ b/java/com/tigervnc/vncviewer/DesktopWindow.java
@@ -179,6 +179,10 @@ class DesktopWindow extends JPanel implements
return;
}
+ public PixelFormat getPreferredPF() {
+ return im.getNativePF();
+ }
+
// setColourMapEntries() changes some of the entries in the colourmap.
// Unfortunately these messages are often sent one at a time, so we delay the
// settings taking effect unless the whole colourmap has changed. This is
diff --git a/java/com/tigervnc/vncviewer/PixelBufferImage.java b/java/com/tigervnc/vncviewer/PixelBufferImage.java
index 78697d8d..648e6388 100644
--- a/java/com/tigervnc/vncviewer/PixelBufferImage.java
+++ b/java/com/tigervnc/vncviewer/PixelBufferImage.java
@@ -72,7 +72,7 @@ public class PixelBufferImage extends PixelBuffer implements ImageProducer
data, width() * i, copyWidth);
}
- private PixelFormat getNativePF() {
+ public PixelFormat getNativePF() {
PixelFormat pf;
cm = java.awt.Toolkit.getDefaultToolkit().getColorModel();
if (cm.getColorSpace().getType() == java.awt.color.ColorSpace.TYPE_RGB) {