=== modified file 'src/com/goldencode/p2j/ui/client/driver/web/index.html'
--- src/com/goldencode/p2j/ui/client/driver/web/index.html	2020-10-16 18:08:44 +0000
+++ src/com/goldencode/p2j/ui/client/driver/web/index.html	2020-11-02 10:01:46 +0000
@@ -114,6 +114,8 @@
       <script type="text/javascript" src="${webRoot}/common/p2j.sound.js"></script>
       <script type="text/javascript" src="${webRoot}/client/p2j.strokes.js"></script>
       <script type="text/javascript" src="${webRoot}/client/p2j.canvas_renderer.js"></script>
+      <script type="text/javascript" src="${webRoot}/client/p2j.strokes.webgl.js"></script>
+      <script type="text/javascript" src="${webRoot}/client/p2j.canvas_renderer_webgl.js"></script>
       <script type="text/javascript" src="${webRoot}/client/p2j.virtual_desktop.js"></script>
       <script type="text/javascript" src="${webRoot}/client/p2j.mouse.js"></script>
       <script type="text/javascript" src="${webRoot}/client/p2j.screen.js"></script>

=== modified file 'src/com/goldencode/p2j/ui/client/gui/driver/web/GuiWebSocket.java'
--- src/com/goldencode/p2j/ui/client/gui/driver/web/GuiWebSocket.java	2020-10-26 23:51:46 +0000
+++ src/com/goldencode/p2j/ui/client/gui/driver/web/GuiWebSocket.java	2020-11-05 13:58:26 +0000
@@ -324,7 +324,7 @@
       
       // window ID = 0 means the global cache
       this.perWidgetCache.addCacheExpiryListener(new DrawCacheExpiryListener(0));
-      this.graphicsCached = graphicsCached;
+      this.graphicsCached = false;//graphicsCached;
    }
    
    /**

=== modified file 'src/com/goldencode/p2j/ui/client/gui/driver/web/res/p2j.canvas_renderer_webgl.js'
--- src/com/goldencode/p2j/ui/client/gui/driver/web/res/p2j.canvas_renderer_webgl.js	2020-11-03 19:09:39 +0000
+++ src/com/goldencode/p2j/ui/client/gui/driver/web/res/p2j.canvas_renderer_webgl.js	2020-11-05 21:14:15 +0000
@@ -1969,6 +1969,17 @@
          }
       }
    }
+   else
+   {
+      var renderer = this.strokesManager.getStrokePathRenderer(this, this.strokeStyleId,
+            this.strokeWidth, color);
+      
+      renderer.beginStroke();
+      this.drawLineSegment(x, y, x + width, y, renderer);
+      this.drawLineSegment(x + width, y + 1, x + width, y + height, renderer);
+      this.drawLineSegment(x + width - 1, y + height, x + 1, y + height, renderer);
+      this.drawLineSegment(x, y + height, x, y + 1, renderer);// close the path
+   }
 };
 
 /**

=== modified file 'src/com/goldencode/p2j/ui/client/gui/driver/web/res/p2j.perf_tests.js'
--- src/com/goldencode/p2j/ui/client/gui/driver/web/res/p2j.perf_tests.js	2020-11-03 19:09:39 +0000
+++ src/com/goldencode/p2j/ui/client/gui/driver/web/res/p2j.perf_tests.js	2020-11-05 12:04:54 +0000
@@ -734,15 +734,37 @@
       return result;
    };
 
+   function drawTriangleTest()
+   {
+      clear();
+      var x1 = 100;
+      var y1 = 76;
+      var x2 = 300;
+      var y2 = 300;
+      var x3 = 400;
+      var y3 = 77;
+      var black = [0, 0, 0];
+      var t = [x1, y1, x2, y2, x3, y3];
+      append("Triangle" + JSON.stringify(t));
+      me.canvasRenderer.setColor(black);
+      me.canvasRenderer.drawLine(x1, y1, x2, y2, black);
+      me.canvasRenderer.drawLine(x3, y1-20, x3, y2+20, black);
+      me.canvasRenderer.flash();
+      me.canvasRenderer.drawLine(x2, y2, x3, y3, black);
+      me.canvasRenderer.flash();
+      me.canvasRenderer.drawLine(x3, y3, x1, y1, black);
+      me.canvasRenderer.flash();
+   }
+   
    function clipTrianglesTest()
    {
       clear();
       var x1 = 100;
-      var y1 = 100;
+      var y1 = 100 + Math.floor(Math.random() * 30) * Math.sign(Math.random() - 0.5);
       var x2 = 300;
       var y2 = 300;
       var x3 = 400;
-      var y3 = 100;
+      var y3 = 100 - Math.floor(Math.random() * 30) * Math.sign(Math.random() - 0.5);
       clipRegions = [{x: 100, y: 100, width: 101, height: 201},
                      {x: 280, y: 290, width: 101, height: 201},
                      {x: 265, y: 275, width: 101, height: 1},
@@ -754,6 +776,8 @@
                      {x: 460, y: 100, width: 101, height: 101}];
       var black = [0, 0, 0];
       var yellow = [255, 255, 0];
+      var t = [x1, y1, x2, y2, x3, y3, 0, 0, 0, 1];
+      append("Split triangle" + JSON.stringify(t) + " into areas:");
       me.canvasRenderer.setColor(black);
       me.canvasRenderer.drawLine(x1, y1, x2, y2, black);
       me.canvasRenderer.drawLine(x2, y2, x3, y3, black);
@@ -768,7 +792,7 @@
          me.canvasRenderer.drawRect(rect.x, rect.y, rect.width, rect.height, red, false);
       }
       me.canvasRenderer.flash();
-      var test = clipTriangles([[x1, y1, x2, y2, x3, y3, 0, 0, 0, 1]]);
+      var test = clipTriangles([t]);
       
       me.canvasRenderer.setColor(yellow);
       for (var i = 0; i < test.length; i++)
@@ -1604,6 +1628,13 @@
                   clipTrianglesTest();
                }
             }));
+            pSubMenu2.addChild(new MenuItem({
+               label: "Draw Triangle Test",
+               onClick: function(event)
+               {
+                  drawTriangleTest();
+               }
+            }));
             /*
             pSubMenu2.addChild(new MenuItem({
                 label: "Copy",

=== 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	2020-10-28 14:44:09 +0000
+++ src/com/goldencode/p2j/ui/client/gui/driver/web/res/p2j.screen.js	2020-11-05 19:23:50 +0000
@@ -340,7 +340,7 @@
     * Creates a line strokes object that plays a manager role, used to apply stroke styles
     * to lines and to the native canvas context.
     */
-   var strokesManager = new LineStrokes();
+   var strokesManager = new LineStrokesWebGL();
    
    /**
     * Holds the virtual desktop with task bar and bottom and right drop zones.
@@ -1352,7 +1352,7 @@
       this.iconHeight = 0;
       
       /** Creates canvas renderer. */
-      this.canvasRenderer = new CanvasRenderer(this.offscreenCanvas, this.offscreenContext,
+      this.canvasRenderer = new WebGLCanvasRenderer(this.offscreenCanvas, this.offscreenContext,
                                                this.canvas, strokesManager, p2j.fonts, p2j.logger);
       
       /** 

=== modified file 'src/com/goldencode/p2j/ui/client/gui/driver/web/res/p2j.strokes.webgl.js'
--- src/com/goldencode/p2j/ui/client/gui/driver/web/res/p2j.strokes.webgl.js	2020-10-28 16:53:59 +0000
+++ src/com/goldencode/p2j/ui/client/gui/driver/web/res/p2j.strokes.webgl.js	2020-11-05 19:31:11 +0000
@@ -282,6 +282,30 @@
       return LineStrokeEnum.DEFAULT;
    }
 
+   this.applyStrokeToCanvas = function(context, strokeStyle, width)
+   {
+      if (context instanceof CanvasRenderingContext2D)
+      {
+         var basicStroke = getLineStroke(strokeStyle, width);
+         context.lineWidth = basicStroke.getWidth();
+         
+         context.lineCap = basicStroke.getCap();
+         
+         context.lineJoin = basicStroke.getJoin();
+         
+         context.miterLimit = basicStroke.getMiterLimit();
+         if (basicStroke.getDash())
+         {
+            context.setLineDash(basicStroke.getDash());
+         }
+         else
+         {
+            context.setLineDash([]);
+         }
+         context.lineDashOffset = basicStroke.getDashPhase();
+      }
+   }
+
    /**
     * Defines StrokeRenderer class
     */
@@ -344,12 +368,30 @@
     */
    function DotsPathRenderer(canvasRenderer, basicStroke, strokeColor)
    {
-      var pixel = canvasRenderer.createImageData(1, 1);
+      var state = {};
+      var color = strokeColor.concat([255]);
+      var width = basicStroke.getWidth();
+      var pixel = canvasRenderer.createImageData(width, width);
       var pixelData = pixel.data;
-      pixelData[3] = 0xFF;
-      var state = {};
-      var color = strokeColor.concat([255]);
-      var width = basicStroke.getWidth();
+      
+      var length = 4 * width * width;
+
+      // Fill a line image with the given stroke color
+      pixelData[0] = strokeColor[0];
+      pixelData[1] = strokeColor[1];
+      pixelData[2] = strokeColor[2];
+      pixelData[3] = 255;
+
+      var pos = 4;
+      var seqLen = 4;
+      while(pos < length)
+      {
+         pixelData.copyWithin(pos, 0, seqLen);
+         // increment position by the length of the current segment
+         pos += seqLen;
+         // double the length of the next segment
+         seqLen <<= 1;
+      }
       
       /**
        * Normalizes a dash offset distance according to the given dash pattern.
@@ -437,7 +479,7 @@
          var x = x1;
          var y = y1;
          var triangles = [];
-         var dots = [];
+//         var dots = [];
          var a;
          var b;
          // precondition: y2 should be greater or equal than y1.
@@ -448,35 +490,35 @@
                idx    = (idx + 1) % len;
                rest   = state.dash[idx];
                dashOn = !dashOn;
-               if (dots.length > 0)
-               {
-                  a = dots[0];
-                  b = dots.pop();
-                  dots.length = 0;
-                  if (dx === 0)
-                  {
-                     triangles.push([a.x, a.y, a.x + width, a.y, b.x, b.y, color[0], color[1], color[2], color[3]]);
-                     triangles.push([b.x, b.y, b.x + width, b.y, a.x + width, a.y, color[0], color[1], color[2], color[3]]);
-                  }
-                  else
-                  {
-                     triangles.push([a.x, a.y, a.x, a.y + width, b.x, b.y, color[0], color[1], color[2], color[3]]);
-                     triangles.push([b.x, b.y, b.x, b.y + width, a.x, a.y + width, color[0], color[1], color[2], color[3]]);
-                  }
-               }
+//               if (dots.length > 0)
+//               {
+//                  a = dots[0];
+//                  b = dots.pop();
+//                  dots.length = 0;
+//                  if (dx === 0)
+//                  {
+//                     triangles.push([a.x, a.y, a.x + width, a.y, b.x, b.y, color[0], color[1], color[2], color[3]]);
+//                     triangles.push([b.x, b.y, b.x + width, b.y, a.x + width, a.y, color[0], color[1], color[2], color[3]]);
+//                  }
+//                  else
+//                  {
+//                     triangles.push([a.x, a.y, a.x, a.y + width, b.x, b.y, color[0], color[1], color[2], color[3]]);
+//                     triangles.push([b.x, b.y, b.x, b.y + width, a.x, a.y + width, color[0], color[1], color[2], color[3]]);
+//                  }
+//               }
             }
             rest -= 1;
             
             if (dashOn)
             {
-               dots.push({x:x,y:y});
+//               dots.push({x:x,y:y});
                // fill a pixel and put it on the canvas.
 //               pixelData[0] = strokeColor[0];
 //               pixelData[1] = strokeColor[1];
 //               pixelData[2] = strokeColor[2];
 
                // TODO
-               //canvasRenderer.putImageData(pixel, x, y);
+               canvasRenderer.drawImage(x, y, width, width, pixelData);
             }
             // vertical line: increase Y-coordinate
             if (dx === 0)
@@ -520,9 +562,10 @@
          if (dashOn)
          {
             // fill a pixel and put it on the canvas.
-            pixelData[0] = strokeColor[0];
-            pixelData[1] = strokeColor[1];
-            pixelData[2] = strokeColor[2];
+            canvasRenderer.drawImage(x, y, width, width, pixelData);
+//            pixelData[0] = strokeColor[0];
+//            pixelData[1] = strokeColor[1];
+//            pixelData[2] = strokeColor[2];
 
             // TODO
             //canvasRenderer.putImageData(pixel, x, y);

