Project

General

Profile

1811o_20150807_1.txt

Sergey Ivanovskiy, 08/07/2015 12:20 PM

Download (23.1 KB)

 
1
=== modified file 'src/com/goldencode/p2j/ui/client/driver/web/res/p2j.socket.js'
2
--- src/com/goldencode/p2j/ui/client/driver/web/res/p2j.socket.js	2015-07-20 21:45:43 +0000
3
+++ src/com/goldencode/p2j/ui/client/driver/web/res/p2j.socket.js	2015-08-07 15:56:12 +0000
4
@@ -18,8 +18,9 @@
5
 ** 007 GES 20150324 Moved this class to a common resource base and made it generic (so that it
6
 **                  can be used for both ChUI and GUI web clients.
7
 ** 008 SBI 20150731 Applied "use strict" directive, fixed undeclared variables.
8
-** 009 GES 20170709 Added support for MSG_READ_CLIPBOARD (server-driven clipboard request) and
9
+** 009 GES 20150709 Added support for MSG_READ_CLIPBOARD (server-driven clipboard request) and
10
 **                  MSG_WRITE_CLIPBOARD.
11
+** 010 SBI 20150807 Fixed a network order for reading int32 from binaries message
12
 */
13
 
14
 "use strict";
15
@@ -131,7 +132,7 @@
16
 
17
       for (var i = 0; i < 4; i++)
18
       {
19
-         num |= message[offset + i] << (8 * i);
20
+         num |= message[offset + i] << (8 * (3-i));
21
       }
22
       
23
       return num;
24

    
25
=== added file 'src/com/goldencode/p2j/ui/client/gui/driver/web/DrawImageOp.java'
26
--- src/com/goldencode/p2j/ui/client/gui/driver/web/DrawImageOp.java	1970-01-01 00:00:00 +0000
27
+++ src/com/goldencode/p2j/ui/client/gui/driver/web/DrawImageOp.java	2015-08-07 15:25:53 +0000
28
@@ -0,0 +1,122 @@
29
+/*
30
+** Module   : DrawImageOp.java
31
+** Abstract : Defines a draw image command.
32
+**
33
+** Copyright (c) 2015, Golden Code Development Corporation.
34
+** ALL RIGHTS RESERVED. Use is subject to license terms.
35
+**
36
+**         Golden Code Development Corporation
37
+**                 CONFIDENTIAL
38
+**
39
+** -#- -I- --Date-- --------------------------------Description----------------------------------
40
+** 001 SBI 20150807 First version which is a draw image command.
41
+** 
42
+** 
43
+*/
44
+
45
+package com.goldencode.p2j.ui.client.gui.driver.web;
46
+
47
+import java.awt.Image;
48
+import java.util.HashSet;
49
+
50
+import com.goldencode.p2j.ui.client.gui.driver.PaintPrimitives;
51
+import com.goldencode.p2j.ui.client.gui.driver.PaintStructure;
52
+
53
+/**
54
+ * 
55
+ * DrawImageOp performs a draw image on a virtual screen and prepares
56
+ * a draw message to transfer via networks, holds a hash map of loaded images.
57
+ *
58
+ */
59
+public class DrawImageOp
60
+{
61
+
62
+   private final VirtualScreen virtualScreen;
63
+
64
+   private final HashSet<String> loadedImages;
65
+   /**
66
+    * 
67
+    * @param virtualScreen
68
+    */
69
+   public DrawImageOp(VirtualScreen virtualScreen)
70
+   {
71
+      this.virtualScreen = virtualScreen;
72
+      this.loadedImages = new HashSet<String>();
73
+   }
74
+
75
+   private static String md5HashToString(byte[] md5Hash) {
76
+      StringBuilder builder = new StringBuilder(8);
77
+      for (int i = 0; i < 16; i++) {
78
+         builder.append(Integer.toHexString(md5Hash[i]));
79
+      }
80
+      return builder.toString();
81
+   }
82
+   
83
+   /**
84
+    * Write a 32-bit integer value into a binary message at a given offset.
85
+    *
86
+    * @param    message
87
+    *           Message content as an byte array.
88
+    * @param    offset
89
+    *           Offset into the message at which the integer should be written.
90
+    * @param    value
91
+    *           The 32-bit value to write.
92
+    */
93
+   private void writeMessageInt32(byte[] message, int offset, int value)
94
+   {
95
+      message[offset]     = (byte)((value >> 24) & 0x00FF);
96
+      message[offset + 1] = (byte)((value >> 16) & 0x00FF);
97
+      message[offset + 2] = (byte)((value >> 8)  & 0x00FF);
98
+      message[offset + 3] = (byte)( value        & 0x00FF);
99
+   }
100
+
101
+   /**
102
+    * Perform a draw image on a virtual screen, the image parameters provided
103
+    * in PaintStructure. A binary message format is defined as
104
+    * DRAW_IMAGE, 1 byte, operation id
105
+    * x, 4 bytes, an image x-offset
106
+    * y, 4 bytes, an image y-offset
107
+    * width, 4 bytes, an image width
108
+    * height, 4 bytes, an image width
109
+    * encoding, 4 bytes, an image encoding schema, Raw or MD5 hash
110
+    * imageMd5Hash, 16 bytes, an image MD5 hash
111
+    * image binaries, 4*width*height bytes, an encoded image, it is omitted
112
+    * if an encoding is MD5 hash.
113
+    * @param ps PaintStructure parameters for an image drawing
114
+    * @return byte[] a drawing image prepared to transfer via network. 
115
+    */
116
+   public byte[] execute(PaintStructure ps) {
117
+      int x = ps.xOffset;//4
118
+      int y = ps.yOffset;//4
119
+      Image image = (Image) ps.img.getImage();
120
+      int width = image.getWidth(null);//4
121
+      int height = image.getHeight(null);//4
122
+      final byte[] md5ImageHash = virtualScreen.drawImage(image, x, y);
123
+      final String md5ImageKey = md5HashToString(md5ImageHash);
124
+      ImageEncoding encoding;
125
+      if (!loadedImages.contains(md5ImageKey)) {
126
+         loadedImages.add(md5ImageKey);
127
+         encoding = ImageEncoding.Raw;
128
+      } else {
129
+         encoding = ImageEncoding.Md5Hash;
130
+      }
131
+      final byte[] encodedImage = new RawEncoder().packToBinaries(virtualScreen, x, y, width, height);
132
+      int msgLen = 37;
133
+      if (encoding == ImageEncoding.Raw) { 
134
+         msgLen += encodedImage.length;
135
+      }
136
+      byte[] drawImageMsg = new byte[msgLen];
137
+      drawImageMsg[0] = (byte) (PaintPrimitives.DRAW_IMAGE.ordinal());
138
+      writeMessageInt32(drawImageMsg, 1, x);
139
+      writeMessageInt32(drawImageMsg, 5, y);
140
+      writeMessageInt32(drawImageMsg, 9, width);
141
+      writeMessageInt32(drawImageMsg, 13, height);
142
+      writeMessageInt32(drawImageMsg, 17, encoding.getValue());
143
+      System.arraycopy(md5ImageHash, 0, drawImageMsg, 21, 16);
144
+      if (encoding == ImageEncoding.Raw) {
145
+         System.arraycopy(encodedImage, 0, drawImageMsg, 37, encodedImage.length);
146
+      }
147
+      
148
+      return drawImageMsg;
149
+   }
150
+}
151

    
152
=== modified file 'src/com/goldencode/p2j/ui/client/gui/driver/web/GuiWebEmulatedWindow.java'
153
--- src/com/goldencode/p2j/ui/client/gui/driver/web/GuiWebEmulatedWindow.java	2015-07-30 23:09:31 +0000
154
+++ src/com/goldencode/p2j/ui/client/gui/driver/web/GuiWebEmulatedWindow.java	2015-08-07 15:18:59 +0000
155
@@ -13,6 +13,7 @@
156
 ** 002 HC  20150612 Compatibility changes related to support of paragraph layout.
157
 ** 003 HC  20150702 Improved positioning of top-level windows on systems with multiple displays.
158
 ** 004 GES 20150701 Added GUI implementation for previously stubbed-out methods.
159
+** 005 SBI 20150807 Added an images drawing operation.
160
 */
161
 
162
 package com.goldencode.p2j.ui.client.gui.driver.web;
163
@@ -378,6 +379,7 @@
164
             websock.drawRoundRect(ps.x, ps.y, ps.width, ps.height, ps.arcDiameter);
165
             break;
166
          case DRAW_IMAGE:
167
+            websock.drawImage(ps);
168
             break;
169
          case FILL_RECT:
170
             websock.fillRect(ps.x, ps.y, ps.width, ps.height);
171

    
172
=== modified file 'src/com/goldencode/p2j/ui/client/gui/driver/web/GuiWebSocket.java'
173
--- src/com/goldencode/p2j/ui/client/gui/driver/web/GuiWebSocket.java	2015-07-30 23:09:31 +0000
174
+++ src/com/goldencode/p2j/ui/client/gui/driver/web/GuiWebSocket.java	2015-08-07 15:25:55 +0000
175
@@ -10,16 +10,19 @@
176
 **
177
 ** -#- -I- --Date-- --------------------------------Description----------------------------------
178
 ** 001 GES 20150331 First version which is a placeholder using the common parent code as ChUI.
179
-** 002 GES 20150701 Added messages for a first pass at GUI support. 
180
+** 002 GES 20150701 Added messages for a first pass at GUI support.
181
+** 003 SBI 20150807 Added an images drawing operation.
182
 */
183
 
184
 package com.goldencode.p2j.ui.client.gui.driver.web;
185
 
186
+import java.awt.color.ColorSpace;
187
+import java.awt.image.DataBuffer;
188
+import java.awt.image.DirectColorModel;
189
 import java.util.*;
190
 
191
 import com.goldencode.p2j.ui.client.driver.web.*;
192
 import com.goldencode.p2j.ui.client.*;
193
-import com.goldencode.p2j.ui.client.widget.*;
194
 import com.goldencode.p2j.ui.client.gui.driver.*;
195
 
196
 /**
197
@@ -49,6 +52,21 @@
198
    private int pendingBytes = 0;
199
    
200
    /**
201
+    * Defines a pixels model as sRGB 32bits per a pixel
202
+    */
203
+   private final DirectColorModel colorModel = new DirectColorModel(ColorSpace.getInstance(ColorSpace.CS_sRGB),
204
+            32, 0xFF, 0xFF00, 0xFF0000, 0xFF000000, true, DataBuffer.TYPE_INT);
205
+   /**
206
+    * TODO to get client screen width and height.
207
+    */
208
+   private final VirtualScreen virtualScreen = new VirtualScreenImpl(colorModel, 800, 600);
209
+   
210
+   /**
211
+    * Image drawing manager holds a hash map of loaded images
212
+    */
213
+   private final DrawImageOp drawImageOp = new DrawImageOp(virtualScreen);
214
+   
215
+   /**
216
     * Create a new GUI web socket instance.
217
     * 
218
     * @param    lock
219
@@ -257,7 +275,17 @@
220
    {
221
       allocateAndSend(PaintPrimitives.FILL_3D_RECT, x, y, width, height, raised);
222
    }
223
-   
224
+
225
+   /**
226
+    * Draw a given image on the virtual screen and prepare it to transfer. 
227
+    * @param imageParameters PaintStructure 
228
+    */
229
+   public void drawImage(PaintStructure imageParameters) {
230
+      byte[] message = drawImageOp.execute(imageParameters);
231
+      drawingOps.add(message);
232
+      pendingBytes += message.length;
233
+   }
234
+
235
    /**
236
     * Draw a filled, closed polygon in the current color/stroke.
237
     * 
238
@@ -719,6 +747,15 @@
239
    }
240
    
241
    /**
242
+    * Add a prepared drawing to the pending operations list
243
+    * @param message byte[] prepared drawing to transfer
244
+    */
245
+   private void addDrawingOp(byte[] message) {
246
+      drawingOps.add(message);
247
+      pendingBytes += message.length;
248
+   }
249
+   
250
+   /**
251
     * Defines the possible ways that the clipboard contents can be requested.
252
     */
253
    private enum ClipboardRequest
254

    
255
=== added file 'src/com/goldencode/p2j/ui/client/gui/driver/web/ImageEncoding.java'
256
--- src/com/goldencode/p2j/ui/client/gui/driver/web/ImageEncoding.java	1970-01-01 00:00:00 +0000
257
+++ src/com/goldencode/p2j/ui/client/gui/driver/web/ImageEncoding.java	2015-08-07 14:49:54 +0000
258
@@ -0,0 +1,44 @@
259
+/*
260
+** Module   : ImageEncoding.java
261
+** Abstract : Enumerates encoding schemas to transfer screens images via network.
262
+**
263
+** Copyright (c) 2015, Golden Code Development Corporation.
264
+** ALL RIGHTS RESERVED. Use is subject to license terms.
265
+**
266
+**         Golden Code Development Corporation
267
+**                 CONFIDENTIAL
268
+**
269
+** -#- -I- --Date-- --------------------------------Description----------------------------------
270
+** 001 SBI 20150807 First version which describes a schema for Raw encoding and MD5 hash encoding
271
+**                  that is used to transfer images md5 hashes via networks.
272
+** 
273
+*/
274
+
275
+package com.goldencode.p2j.ui.client.gui.driver.web;
276
+
277
+/**
278
+ * 
279
+ * ImageEncoding defines Raw encoding and MD5 hash encoding
280
+ * that is used to transfer images md5 hashes via networks.
281
+ * 
282
+ */
283
+public enum ImageEncoding
284
+{
285
+   Raw(0), Md5Hash(1);
286
+
287
+   private final int value;
288
+
289
+   private ImageEncoding(int value)
290
+   {
291
+      this.value = value;
292
+   }
293
+
294
+   /**
295
+    * Returns an encoding well-known id.
296
+    * @return int an encoding id
297
+    */
298
+   public int getValue()
299
+   {
300
+      return value;
301
+   }
302
+}
303

    
304
=== added file 'src/com/goldencode/p2j/ui/client/gui/driver/web/PixelsEncoder.java'
305
--- src/com/goldencode/p2j/ui/client/gui/driver/web/PixelsEncoder.java	1970-01-01 00:00:00 +0000
306
+++ src/com/goldencode/p2j/ui/client/gui/driver/web/PixelsEncoder.java	2015-08-07 14:49:54 +0000
307
@@ -0,0 +1,35 @@
308
+/*
309
+** Module   : PixelsEncoder.java
310
+** Abstract : Describes a functionality to encode screens rectangles to transfer via networks.
311
+**
312
+** Copyright (c) 2015, Golden Code Development Corporation.
313
+** ALL RIGHTS RESERVED. Use is subject to license terms.
314
+**
315
+**         Golden Code Development Corporation
316
+**                 CONFIDENTIAL
317
+**
318
+** -#- -I- --Date-- --------------------------------Description----------------------------------
319
+** 001 SBI 20150807 First version which describes a schema for encoding of a rectangle
320
+**                  on a provided virtual screen.
321
+*/
322
+
323
+package com.goldencode.p2j.ui.client.gui.driver.web;
324
+
325
+/**
326
+ * 
327
+ * PixelsEncoder
328
+ *
329
+ */
330
+public interface PixelsEncoder
331
+{
332
+   /**
333
+    * Encode the rectangular area on the screen to transfer.
334
+    * @param screen virtual screen with the defined color model
335
+    * @param x int a position of the left side of the target rectangle along the screen horizon
336
+    * @param y int a position of the upper side of the target rectangle along the screen's vertical
337
+    * @param w int a width of the target rectangular area
338
+    * @param h int a height of the target rectangular area
339
+    * @return
340
+    */
341
+   byte[] packToBinaries(VirtualScreen screen, int x, int y, int w, int h);
342
+}
343

    
344
=== added file 'src/com/goldencode/p2j/ui/client/gui/driver/web/RawEncoder.java'
345
--- src/com/goldencode/p2j/ui/client/gui/driver/web/RawEncoder.java	1970-01-01 00:00:00 +0000
346
+++ src/com/goldencode/p2j/ui/client/gui/driver/web/RawEncoder.java	2015-08-07 14:49:54 +0000
347
@@ -0,0 +1,70 @@
348
+/*
349
+** Module   : RawEncoder.java
350
+** Abstract : Implements Raw encoding.
351
+**
352
+** Copyright (c) 2015, Golden Code Development Corporation.
353
+** ALL RIGHTS RESERVED. Use is subject to license terms.
354
+**
355
+**         Golden Code Development Corporation
356
+**                 CONFIDENTIAL
357
+**
358
+** -#- -I- --Date-- --------------------------------Description----------------------------------
359
+** 001 SBI 20150807 First version which describes a raw encoding of a rectangle
360
+**                  on a provided virtual screen based on sRGB model.
361
+*/
362
+
363
+
364
+package com.goldencode.p2j.ui.client.gui.driver.web;
365
+
366
+import java.awt.image.DirectColorModel;
367
+
368
+/**
369
+ * 
370
+ * RawEncoder implements Raw encoding based on RFB specification documents:
371
+ * https://www.realvnc.com/docs/rfbproto.pdf
372
+ * https://tools.ietf.org/html/rfc6143
373
+ * This implementation supports only sRGB pixels models with 32 bits per a pixel.
374
+ */
375
+class RawEncoder implements PixelsEncoder
376
+{
377
+
378
+   private final static int TRUE_COLOR_BITS_PER_PIXEL = 32;
379
+   
380
+   @Override
381
+   public byte[] packToBinaries(VirtualScreen screen, int x, int y,
382
+            int w, int h)
383
+   {
384
+      byte[] encodedRectangle = null;
385
+      int b = 0;
386
+      int i = 0;
387
+      int s = 0;
388
+      int pixel;
389
+      int numberOfPixels = w * h;
390
+      int scanline = screen.getScreenWidth();
391
+      int jump = scanline - w;
392
+      int p = y * scanline + x;
393
+      int[] pixels = screen.getPixels();
394
+      DirectColorModel colorModel = screen.getColorModel();
395
+      if (colorModel.getPixelSize() == TRUE_COLOR_BITS_PER_PIXEL) {
396
+         encodedRectangle = new byte[ numberOfPixels << 2 ];
397
+         for( ; i < numberOfPixels; i++, s++, p++ )
398
+         {
399
+            if( s == w )
400
+            {
401
+               s = 0;
402
+               p += jump;
403
+            }
404
+            pixel = pixels[p];
405
+            encodedRectangle[b++] = (byte) colorModel.getRed(pixel);
406
+            encodedRectangle[b++] = (byte) colorModel.getGreen(pixel);
407
+            encodedRectangle[b++] = (byte) colorModel.getBlue(pixel);
408
+            encodedRectangle[b++] = (byte) colorModel.getAlpha(pixel);
409
+         }
410
+      } else {
411
+         throw new UnsupportedOperationException("Supported only 32 bits per a pixel");
412
+      }
413
+      
414
+      return encodedRectangle;
415
+   }
416
+
417
+}
418

    
419
=== added file 'src/com/goldencode/p2j/ui/client/gui/driver/web/VirtualScreen.java'
420
--- src/com/goldencode/p2j/ui/client/gui/driver/web/VirtualScreen.java	1970-01-01 00:00:00 +0000
421
+++ src/com/goldencode/p2j/ui/client/gui/driver/web/VirtualScreen.java	2015-08-07 15:16:33 +0000
422
@@ -0,0 +1,52 @@
423
+/*
424
+** Module   : VirtualScreen.java
425
+** Abstract : Define methods to work with screen
426
+**
427
+** Copyright (c) 2015, Golden Code Development Corporation.
428
+** ALL RIGHTS RESERVED. Use is subject to license terms.
429
+**
430
+**         Golden Code Development Corporation
431
+**                 CONFIDENTIAL
432
+**
433
+** -#- -I- --Date-- --------------------------------Description----------------------------------
434
+** 001 SBI 20150807 First version which describes a virtual screen based on sRGB model.
435
+*/
436
+
437
+package com.goldencode.p2j.ui.client.gui.driver.web;
438
+
439
+import java.awt.Image;
440
+import java.awt.image.DirectColorModel;
441
+
442
+/**
443
+ * VirtualScreen describes a virtual screen based on sRGB model. It provides
444
+ * an access to a screen pixel model and perform drawing operations.
445
+ */
446
+public interface VirtualScreen
447
+{
448
+   /**
449
+    * 
450
+    * @return pixels int[] RGBA model via pixels 
451
+    */
452
+   int[] getPixels();
453
+   /**
454
+    * Gets a screen width.
455
+    * @return int a screen width
456
+    */
457
+   int getScreenWidth();
458
+   /**
459
+    * Gets a screen height.
460
+    * @return height a screen height
461
+    */
462
+   int getScreenHeight();
463
+   /**
464
+    * Describes a pixel format used for drawing.
465
+    * @return DirectColorModel color model
466
+    */
467
+   DirectColorModel getColorModel();
468
+   /**
469
+    * Draw an image at the given point on the screen
470
+    * @param image Image
471
+    * @return unique bytes as unique image identification
472
+    */
473
+   byte[] drawImage(Image image, int x, int y);
474
+}
475

    
476
=== added file 'src/com/goldencode/p2j/ui/client/gui/driver/web/VirtualScreenImpl.java'
477
--- src/com/goldencode/p2j/ui/client/gui/driver/web/VirtualScreenImpl.java	1970-01-01 00:00:00 +0000
478
+++ src/com/goldencode/p2j/ui/client/gui/driver/web/VirtualScreenImpl.java	2015-08-07 14:53:22 +0000
479
@@ -0,0 +1,123 @@
480
+/*
481
+** Module   : VirtualScreenImpl.java
482
+** Abstract : Implements virtual screen functionalities.
483
+**
484
+** Copyright (c) 2015, Golden Code Development Corporation.
485
+** ALL RIGHTS RESERVED. Use is subject to license terms.
486
+**
487
+**         Golden Code Development Corporation
488
+**                 CONFIDENTIAL
489
+**
490
+** -#- -I- --Date-- --------------------------------Description----------------------------------
491
+** 001 SBI 20150807 First version which implements a virtual screen based on sRGB model.
492
+*/
493
+
494
+package com.goldencode.p2j.ui.client.gui.driver.web;
495
+
496
+import java.awt.Color;
497
+import java.awt.Graphics2D;
498
+import java.awt.Image;
499
+import java.awt.image.BufferedImage;
500
+import java.awt.image.DataBuffer;
501
+import java.awt.image.DataBufferInt;
502
+import java.awt.image.DirectColorModel;
503
+import java.awt.image.Raster;
504
+import java.awt.image.SampleModel;
505
+import java.awt.image.SinglePixelPackedSampleModel;
506
+import java.awt.image.WritableRaster;
507
+import java.nio.ByteBuffer;
508
+import java.nio.IntBuffer;
509
+import java.security.MessageDigest;
510
+import java.security.NoSuchAlgorithmException;
511
+
512
+
513
+/**
514
+ * 
515
+ * VirtualScreenImpl is an implementation of VirtualScreen drawings operations.
516
+ *
517
+ */
518
+class VirtualScreenImpl implements VirtualScreen
519
+{
520
+
521
+   private final DirectColorModel colorModel;
522
+   private final int width;
523
+   private final int height;
524
+   private final int[] pixels;
525
+   private final BufferedImage screen;
526
+
527
+   /**
528
+    * Constructs a virtual screen of the given width and height
529
+    * based on the given pixels model.
530
+    * 
531
+    * @param colorModel DirectColorModel pixels model based on sRGB
532
+    * @param width int a screen width
533
+    * @param height int a screen height 
534
+    */
535
+   public VirtualScreenImpl(DirectColorModel colorModel, int width, int height)
536
+   {
537
+      this.colorModel = colorModel;
538
+      this.width = width;
539
+      this.height = height;
540
+      this.pixels = new int[width*height];
541
+      final DataBuffer data = new DataBufferInt(pixels, pixels.length);
542
+      final SampleModel sampleModel = new SinglePixelPackedSampleModel(
543
+               DataBuffer.TYPE_INT, width,  height, colorModel.getMasks());
544
+      final WritableRaster raster = Raster.createWritableRaster(sampleModel, data, null);
545
+      this.screen = new BufferedImage(colorModel, raster, true, null);
546
+   }
547
+
548
+   @Override
549
+   public int[] getPixels()
550
+   {
551
+      return pixels;
552
+   }
553
+
554
+   @Override
555
+   public int getScreenWidth()
556
+   {
557
+      return width;
558
+   }
559
+
560
+   @Override
561
+   public int getScreenHeight()
562
+   {
563
+      return height;
564
+   }
565
+
566
+   @Override
567
+   public DirectColorModel getColorModel()
568
+   {
569
+      return colorModel;
570
+   }
571
+
572
+   private byte[] digest(int scanline, int imagePixelsSize, int x, int y)
573
+   {
574
+      byte[] md5ImageHash = null;
575
+      try
576
+      {
577
+         MessageDigest md = MessageDigest.getInstance("MD5");
578
+         ByteBuffer byteBuffer = ByteBuffer.allocate(imagePixelsSize * 4);
579
+         IntBuffer intBuffer = byteBuffer.asIntBuffer();
580
+         intBuffer.put(pixels, x + y * scanline, imagePixelsSize);
581
+         md.update(byteBuffer);
582
+         md5ImageHash = md.digest();
583
+      }
584
+      catch (NoSuchAlgorithmException e)
585
+      {
586
+         e.printStackTrace();
587
+      }
588
+      return md5ImageHash;
589
+   }
590
+
591
+   @Override
592
+   public byte[] drawImage(Image image, int x, int y)
593
+   {
594
+      Graphics2D g2 = screen.createGraphics();
595
+      g2.setColor(new Color(0,0,0));
596
+      g2.fillRect(0, 0, getScreenWidth(), getScreenHeight());
597
+      g2.drawImage(image, x, y, null);
598
+      g2.dispose();
599
+      return digest(getScreenWidth(), image.getWidth(null)*image.getHeight(null), x, y);
600
+   }
601
+
602
+}
603

    
604
=== modified file 'src/com/goldencode/p2j/ui/client/gui/driver/web/res/p2j.screen.js'
605
--- src/com/goldencode/p2j/ui/client/gui/driver/web/res/p2j.screen.js	2015-07-30 23:09:31 +0000
606
+++ src/com/goldencode/p2j/ui/client/gui/driver/web/res/p2j.screen.js	2015-08-07 15:52:02 +0000
607
@@ -14,6 +14,7 @@
608
 **                  ChUI). Moved some logic to common code locations.
609
 ** 002 SBI 20150731 Applied "use strict" directive, fixed undeclared variables.
610
 ** 003 GES 20150720 Total rewrite to implement a first pass at GUI support.
611
+** 004 SBI 20150807 Added a drawing image operation
612
 */
613
 
614
 "use strict";
615
@@ -32,6 +33,11 @@
616
    /** List of active windows. */
617
    var winlist = new Array();
618
 
619
+   /**
620
+    * Loaded images map
621
+    */
622
+   var loadedImages = new Map();
623
+   
624
    /** Normal canvas font. */
625
    var font;
626
 
627
@@ -352,6 +358,38 @@
628
                drawRoundRect(ctx, x, y, width, height, diameter, this.rawColor, false);
629
                break;
630
             case ops.DRAW_IMAGE:
631
+               console.debug("drawing type: " + message[idx]);
632
+               x = p2j.socket.readInt32BinaryMessage(message, idx + 1);
633
+               y = p2j.socket.readInt32BinaryMessage(message, idx + 5);
634
+               width = p2j.socket.readInt32BinaryMessage(message, idx + 9);
635
+               height = p2j.socket.readInt32BinaryMessage(message, idx + 13);
636
+               console.debug(x + "x" + y + ", " + width + "x" + height);
637
+               var encoding = p2j.socket.readInt32BinaryMessage(message, idx + 17);
638
+               console.debug("encoding type: " + encoding);
639
+               var md5ImageHash = message.subarray(idx + 21, idx + 37);
640
+               var key = md5ImageHash.join('');
641
+               console.debug("hash: " + key);
642
+               var img = ctx.createImageData(width, height);
643
+               var data = img.data;
644
+               var pixelsInBytes = width * height * 4;
645
+               var imgData;
646
+               var imgDataOffset;
647
+               if(loadedImages.has(key)) {
648
+                  imgData = loadedImages.get(key);
649
+                  imgDataOffset = 0;
650
+               } else {
651
+                  imgData = message;
652
+                  imgDataOffset = idx + 37;
653
+                  loadedImages.set(key, message.subarray(imgDataOffset, imgDataOffset + pixelsInBytes));
654
+               }
655
+               for (var i = 0, j = imgDataOffset; i < pixelsInBytes; i += 4, j += 4) {
656
+                   data[i]     = imgData[j];
657
+                   data[i + 1] = imgData[j + 1];
658
+                   data[i + 2] = imgData[j + 2];
659
+                   data[i + 3] = imgData[j + 3];
660
+               }
661
+               ctx.putImageData(img, x, y);
662
+
663
                break;
664
             case ops.FILL_RECT:
665
                x      = p2j.socket.readInt32BinaryMessage(message, idx + 1);
666