=== modified file 'src/com/goldencode/p2j/ui/client/driver/web/res/p2j.socket.js' --- src/com/goldencode/p2j/ui/client/driver/web/res/p2j.socket.js 2015-07-20 21:45:43 +0000 +++ src/com/goldencode/p2j/ui/client/driver/web/res/p2j.socket.js 2015-08-07 15:56:12 +0000 @@ -18,8 +18,9 @@ ** 007 GES 20150324 Moved this class to a common resource base and made it generic (so that it ** can be used for both ChUI and GUI web clients. ** 008 SBI 20150731 Applied "use strict" directive, fixed undeclared variables. -** 009 GES 20170709 Added support for MSG_READ_CLIPBOARD (server-driven clipboard request) and +** 009 GES 20150709 Added support for MSG_READ_CLIPBOARD (server-driven clipboard request) and ** MSG_WRITE_CLIPBOARD. +** 010 SBI 20150807 Fixed a network order for reading int32 from binaries message */ "use strict"; @@ -131,7 +132,7 @@ for (var i = 0; i < 4; i++) { - num |= message[offset + i] << (8 * i); + num |= message[offset + i] << (8 * (3-i)); } return num; === added file 'src/com/goldencode/p2j/ui/client/gui/driver/web/DrawImageOp.java' --- src/com/goldencode/p2j/ui/client/gui/driver/web/DrawImageOp.java 1970-01-01 00:00:00 +0000 +++ src/com/goldencode/p2j/ui/client/gui/driver/web/DrawImageOp.java 2015-08-07 15:25:53 +0000 @@ -0,0 +1,122 @@ +/* +** Module : DrawImageOp.java +** Abstract : Defines a draw image command. +** +** Copyright (c) 2015, Golden Code Development Corporation. +** ALL RIGHTS RESERVED. Use is subject to license terms. +** +** Golden Code Development Corporation +** CONFIDENTIAL +** +** -#- -I- --Date-- --------------------------------Description---------------------------------- +** 001 SBI 20150807 First version which is a draw image command. +** +** +*/ + +package com.goldencode.p2j.ui.client.gui.driver.web; + +import java.awt.Image; +import java.util.HashSet; + +import com.goldencode.p2j.ui.client.gui.driver.PaintPrimitives; +import com.goldencode.p2j.ui.client.gui.driver.PaintStructure; + +/** + * + * DrawImageOp performs a draw image on a virtual screen and prepares + * a draw message to transfer via networks, holds a hash map of loaded images. + * + */ +public class DrawImageOp +{ + + private final VirtualScreen virtualScreen; + + private final HashSet loadedImages; + /** + * + * @param virtualScreen + */ + public DrawImageOp(VirtualScreen virtualScreen) + { + this.virtualScreen = virtualScreen; + this.loadedImages = new HashSet(); + } + + private static String md5HashToString(byte[] md5Hash) { + StringBuilder builder = new StringBuilder(8); + for (int i = 0; i < 16; i++) { + builder.append(Integer.toHexString(md5Hash[i])); + } + return builder.toString(); + } + + /** + * Write a 32-bit integer value into a binary message at a given offset. + * + * @param message + * Message content as an byte array. + * @param offset + * Offset into the message at which the integer should be written. + * @param value + * The 32-bit value to write. + */ + private void writeMessageInt32(byte[] message, int offset, int value) + { + message[offset] = (byte)((value >> 24) & 0x00FF); + message[offset + 1] = (byte)((value >> 16) & 0x00FF); + message[offset + 2] = (byte)((value >> 8) & 0x00FF); + message[offset + 3] = (byte)( value & 0x00FF); + } + + /** + * Perform a draw image on a virtual screen, the image parameters provided + * in PaintStructure. A binary message format is defined as + * DRAW_IMAGE, 1 byte, operation id + * x, 4 bytes, an image x-offset + * y, 4 bytes, an image y-offset + * width, 4 bytes, an image width + * height, 4 bytes, an image width + * encoding, 4 bytes, an image encoding schema, Raw or MD5 hash + * imageMd5Hash, 16 bytes, an image MD5 hash + * image binaries, 4*width*height bytes, an encoded image, it is omitted + * if an encoding is MD5 hash. + * @param ps PaintStructure parameters for an image drawing + * @return byte[] a drawing image prepared to transfer via network. + */ + public byte[] execute(PaintStructure ps) { + int x = ps.xOffset;//4 + int y = ps.yOffset;//4 + Image image = (Image) ps.img.getImage(); + int width = image.getWidth(null);//4 + int height = image.getHeight(null);//4 + final byte[] md5ImageHash = virtualScreen.drawImage(image, x, y); + final String md5ImageKey = md5HashToString(md5ImageHash); + ImageEncoding encoding; + if (!loadedImages.contains(md5ImageKey)) { + loadedImages.add(md5ImageKey); + encoding = ImageEncoding.Raw; + } else { + encoding = ImageEncoding.Md5Hash; + } + final byte[] encodedImage = new RawEncoder().packToBinaries(virtualScreen, x, y, width, height); + int msgLen = 37; + if (encoding == ImageEncoding.Raw) { + msgLen += encodedImage.length; + } + byte[] drawImageMsg = new byte[msgLen]; + drawImageMsg[0] = (byte) (PaintPrimitives.DRAW_IMAGE.ordinal()); + writeMessageInt32(drawImageMsg, 1, x); + writeMessageInt32(drawImageMsg, 5, y); + writeMessageInt32(drawImageMsg, 9, width); + writeMessageInt32(drawImageMsg, 13, height); + writeMessageInt32(drawImageMsg, 17, encoding.getValue()); + System.arraycopy(md5ImageHash, 0, drawImageMsg, 21, 16); + if (encoding == ImageEncoding.Raw) { + System.arraycopy(encodedImage, 0, drawImageMsg, 37, encodedImage.length); + } + + return drawImageMsg; + } +} === modified file 'src/com/goldencode/p2j/ui/client/gui/driver/web/GuiWebEmulatedWindow.java' --- src/com/goldencode/p2j/ui/client/gui/driver/web/GuiWebEmulatedWindow.java 2015-07-30 23:09:31 +0000 +++ src/com/goldencode/p2j/ui/client/gui/driver/web/GuiWebEmulatedWindow.java 2015-08-07 15:18:59 +0000 @@ -13,6 +13,7 @@ ** 002 HC 20150612 Compatibility changes related to support of paragraph layout. ** 003 HC 20150702 Improved positioning of top-level windows on systems with multiple displays. ** 004 GES 20150701 Added GUI implementation for previously stubbed-out methods. +** 005 SBI 20150807 Added an images drawing operation. */ package com.goldencode.p2j.ui.client.gui.driver.web; @@ -378,6 +379,7 @@ websock.drawRoundRect(ps.x, ps.y, ps.width, ps.height, ps.arcDiameter); break; case DRAW_IMAGE: + websock.drawImage(ps); break; case FILL_RECT: websock.fillRect(ps.x, ps.y, ps.width, ps.height); === modified file 'src/com/goldencode/p2j/ui/client/gui/driver/web/GuiWebSocket.java' --- src/com/goldencode/p2j/ui/client/gui/driver/web/GuiWebSocket.java 2015-07-30 23:09:31 +0000 +++ src/com/goldencode/p2j/ui/client/gui/driver/web/GuiWebSocket.java 2015-08-07 15:25:55 +0000 @@ -10,16 +10,19 @@ ** ** -#- -I- --Date-- --------------------------------Description---------------------------------- ** 001 GES 20150331 First version which is a placeholder using the common parent code as ChUI. -** 002 GES 20150701 Added messages for a first pass at GUI support. +** 002 GES 20150701 Added messages for a first pass at GUI support. +** 003 SBI 20150807 Added an images drawing operation. */ package com.goldencode.p2j.ui.client.gui.driver.web; +import java.awt.color.ColorSpace; +import java.awt.image.DataBuffer; +import java.awt.image.DirectColorModel; import java.util.*; import com.goldencode.p2j.ui.client.driver.web.*; import com.goldencode.p2j.ui.client.*; -import com.goldencode.p2j.ui.client.widget.*; import com.goldencode.p2j.ui.client.gui.driver.*; /** @@ -49,6 +52,21 @@ private int pendingBytes = 0; /** + * Defines a pixels model as sRGB 32bits per a pixel + */ + private final DirectColorModel colorModel = new DirectColorModel(ColorSpace.getInstance(ColorSpace.CS_sRGB), + 32, 0xFF, 0xFF00, 0xFF0000, 0xFF000000, true, DataBuffer.TYPE_INT); + /** + * TODO to get client screen width and height. + */ + private final VirtualScreen virtualScreen = new VirtualScreenImpl(colorModel, 800, 600); + + /** + * Image drawing manager holds a hash map of loaded images + */ + private final DrawImageOp drawImageOp = new DrawImageOp(virtualScreen); + + /** * Create a new GUI web socket instance. * * @param lock @@ -257,7 +275,17 @@ { allocateAndSend(PaintPrimitives.FILL_3D_RECT, x, y, width, height, raised); } - + + /** + * Draw a given image on the virtual screen and prepare it to transfer. + * @param imageParameters PaintStructure + */ + public void drawImage(PaintStructure imageParameters) { + byte[] message = drawImageOp.execute(imageParameters); + drawingOps.add(message); + pendingBytes += message.length; + } + /** * Draw a filled, closed polygon in the current color/stroke. * @@ -719,6 +747,15 @@ } /** + * Add a prepared drawing to the pending operations list + * @param message byte[] prepared drawing to transfer + */ + private void addDrawingOp(byte[] message) { + drawingOps.add(message); + pendingBytes += message.length; + } + + /** * Defines the possible ways that the clipboard contents can be requested. */ private enum ClipboardRequest === added file 'src/com/goldencode/p2j/ui/client/gui/driver/web/ImageEncoding.java' --- src/com/goldencode/p2j/ui/client/gui/driver/web/ImageEncoding.java 1970-01-01 00:00:00 +0000 +++ src/com/goldencode/p2j/ui/client/gui/driver/web/ImageEncoding.java 2015-08-07 14:49:54 +0000 @@ -0,0 +1,44 @@ +/* +** Module : ImageEncoding.java +** Abstract : Enumerates encoding schemas to transfer screens images via network. +** +** Copyright (c) 2015, Golden Code Development Corporation. +** ALL RIGHTS RESERVED. Use is subject to license terms. +** +** Golden Code Development Corporation +** CONFIDENTIAL +** +** -#- -I- --Date-- --------------------------------Description---------------------------------- +** 001 SBI 20150807 First version which describes a schema for Raw encoding and MD5 hash encoding +** that is used to transfer images md5 hashes via networks. +** +*/ + +package com.goldencode.p2j.ui.client.gui.driver.web; + +/** + * + * ImageEncoding defines Raw encoding and MD5 hash encoding + * that is used to transfer images md5 hashes via networks. + * + */ +public enum ImageEncoding +{ + Raw(0), Md5Hash(1); + + private final int value; + + private ImageEncoding(int value) + { + this.value = value; + } + + /** + * Returns an encoding well-known id. + * @return int an encoding id + */ + public int getValue() + { + return value; + } +} === added file 'src/com/goldencode/p2j/ui/client/gui/driver/web/PixelsEncoder.java' --- src/com/goldencode/p2j/ui/client/gui/driver/web/PixelsEncoder.java 1970-01-01 00:00:00 +0000 +++ src/com/goldencode/p2j/ui/client/gui/driver/web/PixelsEncoder.java 2015-08-07 14:49:54 +0000 @@ -0,0 +1,35 @@ +/* +** Module : PixelsEncoder.java +** Abstract : Describes a functionality to encode screens rectangles to transfer via networks. +** +** Copyright (c) 2015, Golden Code Development Corporation. +** ALL RIGHTS RESERVED. Use is subject to license terms. +** +** Golden Code Development Corporation +** CONFIDENTIAL +** +** -#- -I- --Date-- --------------------------------Description---------------------------------- +** 001 SBI 20150807 First version which describes a schema for encoding of a rectangle +** on a provided virtual screen. +*/ + +package com.goldencode.p2j.ui.client.gui.driver.web; + +/** + * + * PixelsEncoder + * + */ +public interface PixelsEncoder +{ + /** + * Encode the rectangular area on the screen to transfer. + * @param screen virtual screen with the defined color model + * @param x int a position of the left side of the target rectangle along the screen horizon + * @param y int a position of the upper side of the target rectangle along the screen's vertical + * @param w int a width of the target rectangular area + * @param h int a height of the target rectangular area + * @return + */ + byte[] packToBinaries(VirtualScreen screen, int x, int y, int w, int h); +} === added file 'src/com/goldencode/p2j/ui/client/gui/driver/web/RawEncoder.java' --- src/com/goldencode/p2j/ui/client/gui/driver/web/RawEncoder.java 1970-01-01 00:00:00 +0000 +++ src/com/goldencode/p2j/ui/client/gui/driver/web/RawEncoder.java 2015-08-07 14:49:54 +0000 @@ -0,0 +1,70 @@ +/* +** Module : RawEncoder.java +** Abstract : Implements Raw encoding. +** +** Copyright (c) 2015, Golden Code Development Corporation. +** ALL RIGHTS RESERVED. Use is subject to license terms. +** +** Golden Code Development Corporation +** CONFIDENTIAL +** +** -#- -I- --Date-- --------------------------------Description---------------------------------- +** 001 SBI 20150807 First version which describes a raw encoding of a rectangle +** on a provided virtual screen based on sRGB model. +*/ + + +package com.goldencode.p2j.ui.client.gui.driver.web; + +import java.awt.image.DirectColorModel; + +/** + * + * RawEncoder implements Raw encoding based on RFB specification documents: + * https://www.realvnc.com/docs/rfbproto.pdf + * https://tools.ietf.org/html/rfc6143 + * This implementation supports only sRGB pixels models with 32 bits per a pixel. + */ +class RawEncoder implements PixelsEncoder +{ + + private final static int TRUE_COLOR_BITS_PER_PIXEL = 32; + + @Override + public byte[] packToBinaries(VirtualScreen screen, int x, int y, + int w, int h) + { + byte[] encodedRectangle = null; + int b = 0; + int i = 0; + int s = 0; + int pixel; + int numberOfPixels = w * h; + int scanline = screen.getScreenWidth(); + int jump = scanline - w; + int p = y * scanline + x; + int[] pixels = screen.getPixels(); + DirectColorModel colorModel = screen.getColorModel(); + if (colorModel.getPixelSize() == TRUE_COLOR_BITS_PER_PIXEL) { + encodedRectangle = new byte[ numberOfPixels << 2 ]; + for( ; i < numberOfPixels; i++, s++, p++ ) + { + if( s == w ) + { + s = 0; + p += jump; + } + pixel = pixels[p]; + encodedRectangle[b++] = (byte) colorModel.getRed(pixel); + encodedRectangle[b++] = (byte) colorModel.getGreen(pixel); + encodedRectangle[b++] = (byte) colorModel.getBlue(pixel); + encodedRectangle[b++] = (byte) colorModel.getAlpha(pixel); + } + } else { + throw new UnsupportedOperationException("Supported only 32 bits per a pixel"); + } + + return encodedRectangle; + } + +} === added file 'src/com/goldencode/p2j/ui/client/gui/driver/web/VirtualScreen.java' --- src/com/goldencode/p2j/ui/client/gui/driver/web/VirtualScreen.java 1970-01-01 00:00:00 +0000 +++ src/com/goldencode/p2j/ui/client/gui/driver/web/VirtualScreen.java 2015-08-07 15:16:33 +0000 @@ -0,0 +1,52 @@ +/* +** Module : VirtualScreen.java +** Abstract : Define methods to work with screen +** +** Copyright (c) 2015, Golden Code Development Corporation. +** ALL RIGHTS RESERVED. Use is subject to license terms. +** +** Golden Code Development Corporation +** CONFIDENTIAL +** +** -#- -I- --Date-- --------------------------------Description---------------------------------- +** 001 SBI 20150807 First version which describes a virtual screen based on sRGB model. +*/ + +package com.goldencode.p2j.ui.client.gui.driver.web; + +import java.awt.Image; +import java.awt.image.DirectColorModel; + +/** + * VirtualScreen describes a virtual screen based on sRGB model. It provides + * an access to a screen pixel model and perform drawing operations. + */ +public interface VirtualScreen +{ + /** + * + * @return pixels int[] RGBA model via pixels + */ + int[] getPixels(); + /** + * Gets a screen width. + * @return int a screen width + */ + int getScreenWidth(); + /** + * Gets a screen height. + * @return height a screen height + */ + int getScreenHeight(); + /** + * Describes a pixel format used for drawing. + * @return DirectColorModel color model + */ + DirectColorModel getColorModel(); + /** + * Draw an image at the given point on the screen + * @param image Image + * @return unique bytes as unique image identification + */ + byte[] drawImage(Image image, int x, int y); +} === added file 'src/com/goldencode/p2j/ui/client/gui/driver/web/VirtualScreenImpl.java' --- src/com/goldencode/p2j/ui/client/gui/driver/web/VirtualScreenImpl.java 1970-01-01 00:00:00 +0000 +++ src/com/goldencode/p2j/ui/client/gui/driver/web/VirtualScreenImpl.java 2015-08-07 14:53:22 +0000 @@ -0,0 +1,123 @@ +/* +** Module : VirtualScreenImpl.java +** Abstract : Implements virtual screen functionalities. +** +** Copyright (c) 2015, Golden Code Development Corporation. +** ALL RIGHTS RESERVED. Use is subject to license terms. +** +** Golden Code Development Corporation +** CONFIDENTIAL +** +** -#- -I- --Date-- --------------------------------Description---------------------------------- +** 001 SBI 20150807 First version which implements a virtual screen based on sRGB model. +*/ + +package com.goldencode.p2j.ui.client.gui.driver.web; + +import java.awt.Color; +import java.awt.Graphics2D; +import java.awt.Image; +import java.awt.image.BufferedImage; +import java.awt.image.DataBuffer; +import java.awt.image.DataBufferInt; +import java.awt.image.DirectColorModel; +import java.awt.image.Raster; +import java.awt.image.SampleModel; +import java.awt.image.SinglePixelPackedSampleModel; +import java.awt.image.WritableRaster; +import java.nio.ByteBuffer; +import java.nio.IntBuffer; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; + + +/** + * + * VirtualScreenImpl is an implementation of VirtualScreen drawings operations. + * + */ +class VirtualScreenImpl implements VirtualScreen +{ + + private final DirectColorModel colorModel; + private final int width; + private final int height; + private final int[] pixels; + private final BufferedImage screen; + + /** + * Constructs a virtual screen of the given width and height + * based on the given pixels model. + * + * @param colorModel DirectColorModel pixels model based on sRGB + * @param width int a screen width + * @param height int a screen height + */ + public VirtualScreenImpl(DirectColorModel colorModel, int width, int height) + { + this.colorModel = colorModel; + this.width = width; + this.height = height; + this.pixels = new int[width*height]; + final DataBuffer data = new DataBufferInt(pixels, pixels.length); + final SampleModel sampleModel = new SinglePixelPackedSampleModel( + DataBuffer.TYPE_INT, width, height, colorModel.getMasks()); + final WritableRaster raster = Raster.createWritableRaster(sampleModel, data, null); + this.screen = new BufferedImage(colorModel, raster, true, null); + } + + @Override + public int[] getPixels() + { + return pixels; + } + + @Override + public int getScreenWidth() + { + return width; + } + + @Override + public int getScreenHeight() + { + return height; + } + + @Override + public DirectColorModel getColorModel() + { + return colorModel; + } + + private byte[] digest(int scanline, int imagePixelsSize, int x, int y) + { + byte[] md5ImageHash = null; + try + { + MessageDigest md = MessageDigest.getInstance("MD5"); + ByteBuffer byteBuffer = ByteBuffer.allocate(imagePixelsSize * 4); + IntBuffer intBuffer = byteBuffer.asIntBuffer(); + intBuffer.put(pixels, x + y * scanline, imagePixelsSize); + md.update(byteBuffer); + md5ImageHash = md.digest(); + } + catch (NoSuchAlgorithmException e) + { + e.printStackTrace(); + } + return md5ImageHash; + } + + @Override + public byte[] drawImage(Image image, int x, int y) + { + Graphics2D g2 = screen.createGraphics(); + g2.setColor(new Color(0,0,0)); + g2.fillRect(0, 0, getScreenWidth(), getScreenHeight()); + g2.drawImage(image, x, y, null); + g2.dispose(); + return digest(getScreenWidth(), image.getWidth(null)*image.getHeight(null), x, y); + } + +} === modified file 'src/com/goldencode/p2j/ui/client/gui/driver/web/res/p2j.screen.js' --- src/com/goldencode/p2j/ui/client/gui/driver/web/res/p2j.screen.js 2015-07-30 23:09:31 +0000 +++ src/com/goldencode/p2j/ui/client/gui/driver/web/res/p2j.screen.js 2015-08-07 15:52:02 +0000 @@ -14,6 +14,7 @@ ** ChUI). Moved some logic to common code locations. ** 002 SBI 20150731 Applied "use strict" directive, fixed undeclared variables. ** 003 GES 20150720 Total rewrite to implement a first pass at GUI support. +** 004 SBI 20150807 Added a drawing image operation */ "use strict"; @@ -32,6 +33,11 @@ /** List of active windows. */ var winlist = new Array(); + /** + * Loaded images map + */ + var loadedImages = new Map(); + /** Normal canvas font. */ var font; @@ -352,6 +358,38 @@ drawRoundRect(ctx, x, y, width, height, diameter, this.rawColor, false); break; case ops.DRAW_IMAGE: + console.debug("drawing type: " + message[idx]); + x = p2j.socket.readInt32BinaryMessage(message, idx + 1); + y = p2j.socket.readInt32BinaryMessage(message, idx + 5); + width = p2j.socket.readInt32BinaryMessage(message, idx + 9); + height = p2j.socket.readInt32BinaryMessage(message, idx + 13); + console.debug(x + "x" + y + ", " + width + "x" + height); + var encoding = p2j.socket.readInt32BinaryMessage(message, idx + 17); + console.debug("encoding type: " + encoding); + var md5ImageHash = message.subarray(idx + 21, idx + 37); + var key = md5ImageHash.join(''); + console.debug("hash: " + key); + var img = ctx.createImageData(width, height); + var data = img.data; + var pixelsInBytes = width * height * 4; + var imgData; + var imgDataOffset; + if(loadedImages.has(key)) { + imgData = loadedImages.get(key); + imgDataOffset = 0; + } else { + imgData = message; + imgDataOffset = idx + 37; + loadedImages.set(key, message.subarray(imgDataOffset, imgDataOffset + pixelsInBytes)); + } + for (var i = 0, j = imgDataOffset; i < pixelsInBytes; i += 4, j += 4) { + data[i] = imgData[j]; + data[i + 1] = imgData[j + 1]; + data[i + 2] = imgData[j + 2]; + data[i + 3] = imgData[j + 3]; + } + ctx.putImageData(img, x, y); + break; case ops.FILL_RECT: x = p2j.socket.readInt32BinaryMessage(message, idx + 1);