/**
* Copyright (C) 2009 Michael A. MacDonald
*/
package android.androidVNC;
import java.io.IOException;
import com.antlersoft.android.drawing.OverlappingCopy;
import com.antlersoft.android.drawing.RectList;
import com.antlersoft.util.ObjectPool;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import ClouDesk.AppConnection;
/**
* @author Michael A. MacDonald
*
*/
class LargeBitmapData extends AbstractBitmapData {
/**
* Multiply this times total number of pixels to get estimate of process size with all buffers plus
* safety factor
*/
static final int CAPACITY_MULTIPLIER = 21;
int xoffset;
int yoffset;
int scrolledToX;
int scrolledToY;
private Rect bitmapRect;
private Paint defaultPaint;
private RectList invalidList;
private RectList pendingList;
/**
* Pool of temporary rectangle objects. Need to synchronize externally access from
* multiple threads.
*/
private static ObjectPool<Rect> rectPool = new ObjectPool<Rect>() {
/* (non-Javadoc)
* @see com.antlersoft.util.ObjectPool#itemForPool()
*/
@Override
protected Rect itemForPool() {
return new Rect();
}
};
class LargeBitmapDrawable extends AbstractBitmapDrawable
{
LargeBitmapDrawable()
{
super(LargeBitmapData.this);
}
/* (non-Javadoc)
* @see android.graphics.drawable.DrawableContainer#draw(android.graphics.Canvas)
*/
@Override
public void draw(Canvas canvas) {
//android.util.Log.i("LBM", "Drawing "+xoffset+" "+yoffset);
int xoff, yoff;
synchronized ( LargeBitmapData.this )
{
xoff=xoffset;
yoff=yoffset;
}
draw(canvas, xoff, yoff);
}
}
/**
*
* @param p Protocol implementation
* @param c View that will display screen
* @param displayWidth
* @param displayHeight
* @param capacity Max process heap size in bytes
*/
LargeBitmapData(RfbProto p, VncCanvas c, int displayWidth, int displayHeight, int capacity)
{
super(p,c);
double scaleMultiplier = Math.sqrt((double)(capacity * 1024 * 1024) / (double)(CAPACITY_MULTIPLIER * framebufferwidth * framebufferheight));
if (scaleMultiplier > 1)
scaleMultiplier = 1;
bitmapwidth=(int)((double)framebufferwidth * scaleMultiplier);
if (bitmapwidth < displayWidth)
bitmapwidth = displayWidth;
bitmapheight=(int)((double)framebufferheight * scaleMultiplier);
if (bitmapheight < displayHeight)
bitmapheight = displayHeight;
android.util.Log.i("LBM", "bitmapsize = ("+bitmapwidth+","+bitmapheight+")");
mbitmap = Bitmap.createBitmap(bitmapwidth, bitmapheight, Bitmap.Config.RGB_565);
memGraphics = new Canvas(mbitmap);
bitmapPixels = new int[bitmapwidth * bitmapheight];
invalidList = new RectList(rectPool);
pendingList = new RectList(rectPool);
bitmapRect=new Rect(0,0,bitmapwidth,bitmapheight);
defaultPaint = new Paint();
}
@Override
AbstractBitmapDrawable createDrawable()
{
return new LargeBitmapDrawable();
}
/* (non-Javadoc)
* @see android.androidVNC.AbstractBitmapData#copyRect(android.graphics.Rect, android.graphics.Rect, android.graphics.Paint)
*/
@Override
void copyRect(Rect src, Rect dest, Paint paint) {
// TODO copy rect working?
throw new RuntimeException( "copyrect Not implemented");
}
/* (non-Javadoc)
* @see android.androidVNC.AbstractBitmapData#drawRect(int, int, int, int, android.graphics.Paint)
*/
@Override
void drawRect(int x, int y, int w, int h, Paint paint) {
x-=xoffset;
y-=yoffset;
memGraphics.drawRect(x, y, x+w, y+h, paint);
}
/* (non-Javadoc)
* @see android.androidVNC.AbstractBitmapData#offset(int, int)
*/
@Override
int offset(int x, int y) {
return (y - yoffset) * bitmapwidth + x - xoffset;
}
/* (non-Javadoc)
* @see android.androidVNC.AbstractBitmapData#scrollChanged(int, int)
*/
@Override
synchronized void scrollChanged(int newx, int newy) {
//android.util.Log.i("LBM","scroll "+newx+" "+newy);
int newScrolledToX = scrolledToX;
int newScrolledToY = scrolledToY;
int visibleWidth = vncCanvas.getVisibleWidth();
int visibleHeight = vncCanvas.getVisibleHeight();
if (newx - xoffset < 0 )
{
newScrolledToX = newx + visibleWidth / 2 - bitmapwidth / 2;
if (newScrolledToX < 0)
newScrolledToX = 0;
}
else if (newx - xoffset + visibleWidth > bitmapwidth)
{
newScrolledToX = newx + visibleWidth / 2 - bitmapwidth / 2;
if (newScrolledToX + bitmapwidth > framebufferwidth)
newScrolledToX = framebufferwidth - bitmapwidth;
}
if (newy - yoffset < 0 )
{
newScrolledToY = newy + visibleHeight / 2 - bitmapheight / 2;
if (newScrolledToY < 0)
newScrolledToY = 0;
}
else if (newy - yoffset + visibleHeight > bitmapheight)
{
newScrolledToY = newy + visibleHeight / 2 - bitmapheight / 2;
if (newScrolledToY + bitmapheight > framebufferheight)
newScrolledToY = framebufferheight - bitmapheight;
}
if (newScrolledToX != scrolledToX || newScrolledToY != scrolledToY)
{
scrolledToX = newScrolledToX;
scrolledToY = newScrolledToY;
if ( waitingForInput)
syncScroll();
}
}
/* (non-Javadoc)
* @see android.androidVNC.AbstractBitmapData#updateBitmap(int, int, int, int)
*/
@Override
void updateBitmap(int x, int y, int w, int h) {
mbitmap.setPixels(bitmapPixels, offset(x,y), bitmapwidth, x-xoffset, y-yoffset, w, h);
}
/* (non-Javadoc)
* @see android.androidVNC.AbstractBitmapData#validDraw(int, int, int, int)
*/
@Override
synchronized boolean validDraw(int x, int y, int w, int h) {
//android.util.Log.i("LBM", "Validate Drawing "+x+" "+y+" "+w+" "+h+" "+xoffset+" "+yoffset+" "+(x-xoffset>=0 && x-xoffset+w<=bitmapwidth && y-yoffset>=0 && y-yoffset+h<=bitmapheight));
boolean result = x-xoffset>=0 && x-xoffset+w<=bitmapwidth && y-yoffset>=0 && y-yoffset+h<=bitmapheight;
ObjectPool.Entry<Rect> entry = rectPool.reserve();
Rect r = entry.get();
r.set(x, y, x+w, y+h);
pendingList.subtract(r);
if ( ! result)
{
invalidList.add(r);
}
else
invalidList.subtract(r);
rectPool.release(entry);
return result;
}
/* (non-Javadoc)
* @see android.androidVNC.AbstractBitmapData#writeFullUpdateRequest(boolean)
*/
@Override
synchronized void writeFullUpdateRequest(boolean incremental) throws IOException {
if (! incremental) {
ObjectPool.Entry<Rect> entry = rectPool.reserve();
Rect r = entry.get();
r.left=xoffset;
r.top=yoffset;
r.right=xoffset + bitmapwidth;
r.bottom=yoffset + bitmapheight;
pendingList.add(r);
invalidList.add(r);
rectPool.release(entry);
}
rfb.writeFramebufferUpdateRequest(xoffset, yoffset, bitmapwidth, bitmapheight, incremental);
}
/* (non-Javadoc)
* @see android.androidVNC.AbstractBitmapData#syncScroll()
*/
@Override
synchronized void syncScroll() {
int deltaX = xoffset - scrolledToX;
int deltaY = yoffset - scrolledToY;
xoffset=scrolledToX;
yoffset=scrolledToY;
bitmapRect.top=scrolledToY;
bitmapRect.bottom=scrolledToY+bitmapheight;
bitmapRect.left=scrolledToX;
bitmapRect.right=scrolledToX+bitmapwidth;
invalidList.intersect(bitmapRect);
if ( deltaX != 0 || deltaY != 0)
{
boolean didOverlapping = false;
if (Math.abs(deltaX) < bitmapwidth && Math.abs(deltaY) < bitmapheight) {
ObjectPool.Entry<Rect> sourceEntry = rectPool.reserve();
ObjectPool.Entry<Rect> addedEntry = rectPool.reserve();
try
{
Rect added = addedEntry.get();
Rect sourceRect = sourceEntry.get();
sourceRect.set(deltaX<0 ? -deltaX : 0,
deltaY<0 ? -deltaY : 0,
deltaX<0 ? bitmapwidth : bitmapwidth - deltaX,
deltaY < 0 ? bitmapheight : bitmapheight - deltaY);
if (! invalidList.testIntersect(sourceRect)) {
didOverlapping = true;
OverlappingCopy.Copy(mbitmap, memGraphics, defaultPaint, sourceRect, deltaX + sourceRect.left, deltaY + sourceRect.top, rectPool);
// Write request for side pixels
if (deltaX != 0) {
added.left = deltaX < 0 ? bitmapRect.right + deltaX : bitmapRect.left;
added.right = added.left + Math.abs(deltaX);
added.top = bitmapRect.top;
added.bottom = bitmapRect.bottom;
invalidList.add(added);
}
if (deltaY != 0) {
added.left = deltaX < 0 ? bitmapRect.left : bitmapRect.left + deltaX;
added.top = deltaY < 0 ? bitmapRect.bottom + deltaY : bitmapRect.top;
added.right = added.left + bitmapwidth - Math.abs(deltaX);
added.bottom = added.top + Math.abs(deltaY);
invalidList.add(added);
}
}
}
finally {
rectPool.release(addedEntry);
rectPool.release(sourceEntry);
}
}
if (! didOverlapping)
{
try
{
//android.util.Log.i("LBM","update req "+xoffset+" "+yoffset);
mbitmap.eraseColor(Color.GREEN);
writeFullUpdateRequest(false);
}
catch ( IOException ioe)
{
// TODO log this
}
}
}
int size = pendingList.getSize();
for (int i=0; i<size; i++) {
invalidList.subtract(pendingList.get(i));
}
size = invalidList.getSize();
for (int i=0; i<size; i++) {
Rect invalidRect = invalidList.get(i);
try
{
rfb.writeFramebufferUpdateRequest(invalidRect.left, invalidRect.top, invalidRect.right-invalidRect.left, invalidRect.bottom-invalidRect.top, false);
pendingList.add(invalidRect);
}
catch (IOException ioe)
{
//TODO Log this
}
}
waitingForInput=true;
//android.util.Log.i("LBM", "pending "+pendingList.toString() + "invalid "+invalidList.toString());
}
}