Project

General

Profile

screenImageWorkingCanvasDrawImageRevertedChanges.patch

Sergey Ivanovskiy, 10/14/2020 04:25 AM

Download (53.4 KB)

View differences:

src/com/goldencode/p2j/ui/client/gui/driver/web/res/p2j.canvas_renderer.js 2020-10-12 06:09:17 +0000
651 651
   {
652 652
      for (var i = 0; i < lastClip.length; i++)
653 653
      {
654
         if (lastClip[i].height == 0 || lastClip[i].width == 0)
655
         {
656
            console.error("clipRegion: " + lastClip[i].width + "x" + lastClip[i].height);
657
         }
654 658
         var top    = lastClip[i].y;
655 659
         var bottom = lastClip[i].y + Math.max(lastClip[i].height - 1, 0);
656 660
         var left   = lastClip[i].x;
......
868 872
 */
869 873
CanvasRenderer.prototype.drawCanvas = function(canvas, xDest, yDest, width, height)
870 874
{
871
   this.offscreenCtx.drawImage(canvas, xDest, yDest, width, height);
875
   this.offscreenCtx.drawImage(canvas, 0, 0, width, height, xDest, yDest, width, height);
872 876
};
873 877

  
874 878
/**
......
891 895
 */
892 896
CanvasRenderer.prototype.drawCanvas = function(canvas, xDest, yDest, width, height, cop, alpha)
893 897
{
894
   //save these global values
895
   var gcop = this.offscreenCtx.globalCompositeOperation;
896
   var galpha = this.offscreenCtx.globalAlpha;
897
   if (cop)
898
   var gcop;
899
   var galpha;
900
   var test = (cop) && (alpha);
901
   
902
   if (test)
898 903
   {
904
      //save these global values
905
      gcop = this.offscreenCtx.globalCompositeOperation;
906
      galpha = this.offscreenCtx.globalAlpha;
899 907
      this.offscreenCtx.globalCompositeOperation = cop;
900
   }
901
   if (alpha)
902
   {
903 908
      this.offscreenCtx.globalAlpha = alpha;
904 909
   }
905 910
   
906
   this.offscreenCtx.drawImage(canvas, xDest, yDest, width, height);
907
   // restore the global values for composite operation
908
   this.offscreenCtx.globalCompositeOperation = gcop;
909
   this.offscreenCtx.globalAlpha = galpha;
911
   this.offscreenCtx.drawImage(canvas, 0, 0, width, height, xDest, yDest, width, height);
912
   
913
   if (test)
914
   {
915
      // restore the global values for composite operation
916
      this.offscreenCtx.globalCompositeOperation = gcop;
917
      this.offscreenCtx.globalAlpha = galpha;
918
   }
910 919
};
911 920

  
912 921
/**
......
955 964
      // if dy == 0, then a line (x1, y1, x2, y2) is a horizontal
956 965
      if (dx === 0 || dy === 0)
957 966
      {
967
//         if (x2 < x1)
968
//         {
969
//            var a = x1;
970
//            x1 = x2;
971
//            x2 = a;
972
//         }
973
//         
974
//         if (y2 < y1)
975
//         {
976
//            var a = y1;
977
//            y1 = y2;
978
//            y2 = a;
979
//         }
980
         
958 981
         // to draw a line as an image for the default stroke and widen stroke styles
959 982
         // we have x2 >= x1 and y2 >= y1 and dx or dy equals zero.
960
         strokeRenderer.strokeLine(x1, y1, x2, y2);//optimization
983
         strokeRenderer.strokeLine(x1-this.origin.x, y1-this.origin.y, x2-this.origin.x, y2-this.origin.y);//optimization
961 984
      }
962 985
      else
963 986
      {
964
         this.drawSlopedLineSegment(x1, y1, x2, y2, strokeRenderer);
987
//         if (x2 < x1)
988
//         {
989
//            var a = x1;
990
//            x1 = x2;
991
//            x2 = a;
992
//            a = y1;
993
//            y1 = y2;
994
//            y2 = a;
995
//         }
996
         
997
         this.drawSlopedLineSegment(x1-this.origin.x, y1-this.origin.y, x2-this.origin.x, y2-this.origin.y, strokeRenderer);
965 998
      }
966 999
   }
967 1000
};
......
1058 1091
CanvasRenderer.prototype.drawLine = function(x1, y1, x2, y2, color)
1059 1092
{
1060 1093
   var strokeRenderer = this.strokesManager.getStrokePathRenderer(this, this.strokeStyleId,
1061
         this.strokeWidth, color);
1062
   strokeRenderer.beginStroke();
1094
         this.strokeWidth, color).setWorkingCanvas(this.tmpCanvas, this.tmpCtx);
1095
   var rx1, ry1, rx2, ry2;
1096
   if (x1 > x2)
1097
   {
1098
      rx1 = x2;
1099
      rx2 = x1;
1100
   }
1101
   else
1102
   {
1103
      rx1 = x1;
1104
      rx2 = x2;
1105
   }
1106
   if (y1 > y2)
1107
   {
1108
      ry1 = y2;
1109
      ry2 = y1;
1110
   }
1111
   else
1112
   {
1113
      ry1 = y1;
1114
      ry2 = y2;
1115
   }
1116
   this.tmpCanvas.width = rx2 - rx1 + this.strokeWidth;
1117
   this.tmpCanvas.height = ry2 - ry1 + this.strokeWidth;
1118
   strokeRenderer.setScreenImage(this.tmpCtx.createImageData(this.tmpCanvas.width, this.tmpCanvas.height));
1119
   strokeRenderer.beginStroke(rx1, ry1, rx2, ry2);
1063 1120
   this.drawLineSegment(x1, y1, x2, y2, strokeRenderer);
1121
   strokeRenderer.endStroke();
1064 1122
}
1065 1123

  
1066 1124
/**
......
1121 1179
CanvasRenderer.prototype.strokeLineSegment = function(x1, y1, x2, y2, color)
1122 1180
{
1123 1181
   var strokeRenderer = this.strokesManager.getStrokePathRenderer(this, this.strokeStyleId,
1124
         this.strokeWidth, color);
1125
   strokeRenderer.beginStroke();
1182
         this.strokeWidth, color).setWorkingCanvas(this.tmpCanvas, this.tmpCtx);
1183
   var rx1, ry1, rx2, ry2;
1184
   if (x1 > x2)
1185
   {
1186
      rx1 = x2;
1187
      rx2 = x1;
1188
   }
1189
   else
1190
   {
1191
      rx1 = x1;
1192
      rx2 = x2;
1193
   }
1194
   if (y1 > y2)
1195
   {
1196
      ry1 = y2;
1197
      ry2 = y1;
1198
   }
1199
   else
1200
   {
1201
      ry1 = y1;
1202
      ry2 = y2;
1203
   }
1204
   this.tmpCanvas.width = rx2 - rx1 + this.strokeWidth;
1205
   this.tmpCanvas.height = ry2 - ry1 + this.strokeWidth;
1206
   strokeRenderer.setScreenImage(this.tmpCtx.createImageData(this.tmpCanvas.width, this.tmpCanvas.height));
1207
   strokeRenderer.beginStroke(rx1, ry1, rx2, ry2);
1126 1208
   this.drawLineSegment(x1, y1, x2, y2, strokeRenderer);
1209
   strokeRenderer.endStroke();
1127 1210
}
1128 1211

  
1129 1212
/**
......
1160 1243
   // now overdraw the stroked portion to eliminate anti-aliasing, we draw in a
1161 1244
   // clockwise direction (since we are not using paths, this is not strictly necessary)
1162 1245
   var renderer = this.strokesManager.getStrokePathRenderer(this, this.strokeStyleId,
1163
         this.strokeWidth, color);
1164
   renderer.beginStroke();
1246
         this.strokeWidth, color).setWorkingCanvas(this.tmpCanvas, this.tmpCtx).setScreenImage(undefined);
1247
   this.tmpCanvas.width = width - inset + this.strokeWidth;
1248
   this.tmpCanvas.height = height - inset + this.strokeWidth;
1249
   renderer.beginStroke(x, y, x + width - inset, y + height - inset);
1165 1250
   this.drawLineSegment(x, y, x + width - inset, y, renderer);
1166 1251
   this.drawLineSegment(x + width - inset, y + 1, x + width - inset, y + height - inset, renderer);
1167 1252
   this.drawLineSegment(x + width - 1 - inset, y + height - inset, x + 1, y + height - inset, renderer);
1168 1253
   this.drawLineSegment(x, y + height - inset, x, y + 1, renderer);// close the path
1254
   renderer.endStroke();
1169 1255
};
1170 1256

  
1171 1257
/**
......
1189 1275
CanvasRenderer.prototype.strokeRect = function(x, y, width, height, color, fill)
1190 1276
{
1191 1277
   // filled rectangles draw 1 pixel smaller in 2 dimensions than stroked rectangles
1278
   if (width == 0 || height == 0)
1279
   {
1280
      /*
1281
      this.offscreenCtx.save();
1282
      this.offscreenCtx.strokeStyle = "yellow";
1283
      this.offscreenCtx.strokeWidth = 1;
1284
      this.offscreenCtx.beginPath();
1285
      this.offscreenCtx.rect(x, y, width, height);
1286
      this.offscreenCtx.stroke();
1287
      this.offscreenCtx.restore();
1288
      console.error("x = " + x + " y = " + y + " width = " + width + " height = " + height);
1289
      return;
1290
      */
1291
      if (width > 0)
1292
      {
1293
         height = 1;
1294
      }
1295
      else if (height > 0)
1296
      {
1297
         width = 1;
1298
      }
1299
   }
1300
   
1192 1301
   var inset = 0;
1193 1302
   // use vector operations for the interior of the rectangle
1194 1303
   if (fill)
......
1202 1311
   // now overdraw the stroked portion to eliminate anti-aliasing, we draw in a
1203 1312
   // clockwise direction (since we are not using paths, this is not strictly necessary)
1204 1313
   var renderer = this.strokesManager.getStrokePathRenderer(this, this.strokeStyleId,
1205
         this.strokeWidth, color);
1206
   
1207
   renderer.beginStroke();
1314
         this.strokeWidth, color).setWorkingCanvas(this.tmpCanvas, this.tmpCtx).setScreenImage(undefined);
1315
   
1316
   this.tmpCanvas.width = width - inset + this.strokeWidth;
1317
   this.tmpCanvas.height = height - inset +  this.strokeWidth;
1318
   
1319
   renderer.beginStroke(x, y, x + width - inset, y + height - inset);
1208 1320
   this.drawLineSegment(x, y, x + width - inset, y, renderer);
1209 1321
   this.drawLineSegment(x + width - inset, y + 1, x + width - inset, y + height - inset,
1210 1322
                        renderer);
1211 1323
   this.drawLineSegment(x + width - 1 - inset, y + height - inset, x + 1, y + height - inset,
1212 1324
                        renderer);
1213 1325
   this.drawLineSegment(x, y + height - inset, x, y + 1, renderer);// close the path
1326
   renderer.endStroke();
1214 1327
};
1215 1328

  
1216 1329
/**
......
1361 1474
 *           The coppied image width.
1362 1475
 * @param    {Number} height
1363 1476
 *           The coppied image height.
1364
 * @param    {Number} imgDataOffset
1365
 *           The coppied image data offset from the 0-index of its data array, imgData.
1366 1477
 * 
1367 1478
 * @return   {Uint8ClampedArray}
1368 1479
 *           Returns the modified screen image data array with the target image.
1369 1480
 *  
1370 1481
 */
1371
CanvasRenderer.prototype.copyImage = function(img, data, imgData, width, height, imgDataOffset)
1482
CanvasRenderer.prototype.copyImage = function(img, data, imgData, width, height)
1372 1483
{
1373 1484
   var awidth  = img.width;
1374 1485
   var aheight = img.height;
1375 1486
   var x = 0;
1376 1487
   var y = 0;
1377 1488
   var pixelsInBytes = awidth * aheight * 4;
1378
   var offset = imgDataOffset;
1489
   var offset = 0;
1379 1490
   for (var p = 0; p < pixelsInBytes; offset += 4)
1380 1491
   {
1381 1492
      data[p]     = imgData[offset];
......
1468 1579
   
1469 1580
   // overdraw the anti-aliased line segments              
1470 1581
   var renderer = this.strokesManager.getStrokePathRenderer(this, this.strokeStyleId,
1471
         this.strokeWidth, color);
1472
   renderer.beginStroke();
1582
         this.strokeWidth, color).setWorkingCanvas(this.tmpCanvas, this.tmpCtx).setScreenImage(undefined);
1583
   this.tmpCanvas.width = width + this.strokeWidth;
1584
   this.tmpCanvas.height = height + this.strokeWidth;
1585
   renderer.beginStroke(x, y, x + width, y + height);
1473 1586
   this.drawLineSegment(topLeftX, topLeftY, topRightX, topRightY, renderer);
1474 1587
   this.drawLineSegment(rightUpX, rightUpY, rightDownX, rightDownY, renderer);
1475 1588
   this.drawLineSegment(bottomLeftX, bottomLeftY, bottomRightX, bottomRightY, renderer);
1476 1589
   this.drawLineSegment(leftUpX, leftUpY, leftDownX, leftDownY, renderer);
1590
   renderer.endStroke();
1477 1591
};
1478 1592

  
1479 1593
/**
......
1583 1697
 */
1584 1698
CanvasRenderer.prototype.drawPolygon = function(xPoints, yPoints, num, color, fill)
1585 1699
{
1700
   if (num < 2)
1701
   {
1702
      return;
1703
   }
1704
   
1586 1705
   var i;
1587

  
1706
   
1588 1707
   // use vector operations for the interior of the polygon
1589 1708
   if (fill)
1590 1709
   {
......
1595 1714
         this.offscreenCtx.lineTo(xPoints[i], yPoints[i]);
1596 1715
      }
1597 1716
      this.offscreenCtx.closePath();
1717
      this.offscreenCtx.fill();
1718
      this.offscreenCtx.stroke();
1598 1719
      this.renderClosedPath(fill);
1599
   }
1600
   var renderer = this.strokesManager.getStrokePathRenderer(this, this.strokeStyleId,
1601
         this.strokeWidth, color);
1602
   renderer.beginStroke();
1603

  
1604
   // now overdraw the stroked portion
1605
   for (i = 0; i < (num - 1); i++)
1606
   {
1607
      this.drawLineSegment(xPoints[i], yPoints[i], xPoints[i + 1], yPoints[i + 1], renderer);
1608
   }
1609
   // close the path
1610
   this.drawLineSegment(xPoints[num - 1], yPoints[num - 1], xPoints[0], yPoints[0], renderer);
1720
      
1721
   }
1722
//   var renderer = this.strokesManager.getStrokePathRenderer(this, this.strokeStyleId,
1723
//         this.strokeWidth, color).setWorkingCanvas(this.tmpCanvas, this.tmpCtx);
1724
//   
1725
//   /** Define the minimal rectangle that covers all points */
1726
//   var rx1, ry1, rx2, ry2;
1727
//   
1728
//   var x1, y1, x2, y2;
1729
//   
1730
//   x1 = xPoints[0], y1 = yPoints[0], x2 = xPoints[1], y2 = yPoints[1];
1731
//   
1732
//   if (x1 > x2)
1733
//   {
1734
//      rx1 = x2;
1735
//      rx2 = x1;
1736
//   }
1737
//   else
1738
//   {
1739
//      rx1 = x1;
1740
//      rx2 = x2;
1741
//   }
1742
//   
1743
//   if (y1 > y2)
1744
//   {
1745
//      ry1 = y2;
1746
//      ry2 = y1;
1747
//   }
1748
//   else
1749
//   {
1750
//      ry1 = y1;
1751
//      ry2 = y2;
1752
//   }
1753
//   
1754
//   for (i = 2; i < num; i++)
1755
//   {
1756
//      x1 = xPoints[i], y1 = yPoints[i];
1757
//      
1758
//      if (rx1 > x1)
1759
//      {
1760
//         rx1 = x1;
1761
//      }
1762
//      
1763
//      if (ry1 > y1)
1764
//      {
1765
//         ry1 = y1;
1766
//      }
1767
//      
1768
//      if (rx2 < x1)
1769
//      {
1770
//         rx2 = x1;
1771
//      }
1772
//      
1773
//      if (ry2 < y1)
1774
//      {
1775
//         ry2 = y1;
1776
//      }
1777
//   }
1778
//   
1779
//   this.tmpCanvas.width = rx2 - rx1 + this.strokeWidth;
1780
//   
1781
//   this.tmpCanvas.height = ry2 - ry1 + this.strokeWidth;
1782
//   
1783
//   renderer.setScreenImage(this.tmpCtx.createImageData(this.tmpCanvas.width, this.tmpCanvas.height));
1784
//   renderer.beginStroke(rx1, ry1, rx2, ry2);
1785
//
1786
//   
1787
//   // now overdraw the stroked portion
1788
//   for (i = 0; i < (num - 1); i++)
1789
//   {
1790
//      this.drawLineSegment(xPoints[i], yPoints[i], xPoints[i + 1], yPoints[i + 1], renderer);
1791
//   }
1792
//   // close the path
1793
//   this.drawLineSegment(xPoints[num - 1], yPoints[num - 1], xPoints[0], yPoints[0], renderer);
1794
//   
1795
//   renderer.endStroke();
1611 1796
};
1612 1797

  
1613 1798
/**
......
1771 1956
 *           The stroke line width.
1772 1957
 */
1773 1958
CanvasRenderer.prototype.setLineStroke = function(strokeStyleId, width)
1774
{                               
1959
{
1960
   if (width < 1)
1961
   {
1962
      width = 1;
1963
   }
1964
   
1775 1965
   this.setStrokeStyleId(strokeStyleId);
1776 1966
   this.setStrokeWidth(width);
1777 1967
   this.strokesManager.applyStrokeToCanvas(this.offscreenCtx, strokeStyleId, width);
......
1846 2036

  
1847 2037
   for (var i = 0; i < newClip.length; i++)
1848 2038
   {
1849
      this.offscreenCtx.rect(newClip[i].x, newClip[i].y, newClip[i].width, newClip[i].height);
2039
      if (newClip[i].width != 0 && newClip[i].height != 0)
2040
      {
2041
         this.offscreenCtx.rect(newClip[i].x, newClip[i].y, newClip[i].width, newClip[i].height);
2042
      }
2043
      else
2044
      {
2045
         console.error("clip " + newClip[i].x + ", " + newClip[i].y + ", " + newClip[i].width + ", " + newClip[i].height);
2046
      }
1850 2047
   }
1851 2048

  
1852 2049
   this.offscreenCtx.clip();
......
1976 2173
      ya = clipped[i].top;
1977 2174
      xb = clipped[i].right;
1978 2175
      yb = clipped[i].bottom;
1979

  
1980 2176
      // check that it is vertical or horizontal
1981 2177
      if ((x1 == x2) || (y1 == y2))
1982 2178
      {
src/com/goldencode/p2j/ui/client/gui/driver/web/res/p2j.strokes.js 2020-10-11 12:41:43 +0000
13 13
**                  properly use of the xorLine() function.
14 14
** 006 SBI 20191002 Changed to use CanvasRenderer.createImageData(width, height).
15 15
** 007 HC  20200712 Canvas drawing performance improvements.
16
** 008 SBI 20201007 Rewrite using functional style, minor improvements in line drawings.
16 17
*/
17 18
/*
18 19
** This program is free software: you can redistribute it and/or modify
......
136 137
    * Represents a dot style with 2 pixels to draw and then 2 pixels to pass.
137 138
    */
138 139
   var dotStroke = new BasicStroke(LineStroke.DOT_LINE_WIDTH, CAP_BUTT, JOIN_BEVEL, 0.0,
139
         [LineStroke.DOT_STEP, LineStroke.DOT_STEP], LineStroke.DOT_STEP);
140
         [LineStroke.DOT_STEP, LineStroke.DOT_STEP], 0);
140 141

  
141 142
   /**
142 143
    * Represents a dot style with 1 pixel to draw and then 1 pixel to pass.
143 144
    */
144 145
   var dotSmallStroke  = new BasicStroke(LineStroke.DOT_LINE_WIDTH, CAP_BUTT, JOIN_BEVEL, 0.0,
145
         [LineStroke.DOT_STEP_SMALL, LineStroke.DOT_STEP_SMALL], LineStroke.DOT_STEP_SMALL);
146
         [LineStroke.DOT_STEP_SMALL, LineStroke.DOT_STEP_SMALL], 0);
146 147

  
147 148
   /**
148 149
    * Represents a dot style with 0.5 pixel to draw and then 0.5 pixel to pass.
149 150
    */
150 151
   var dotSmallThinStroke = new BasicStroke(LineStroke.DOT_LINE_THIN_WIDTH, CAP_BUTT,
151 152
         JOIN_BEVEL, 0.0, [LineStroke.DOT_STEP_SMALL, LineStroke.DOT_STEP_SMALL],
152
         LineStroke.DOT_STEP_SMALL);
153
         0);
153 154

  
154 155
   /**
155 156
    * Represents a dot style with 3 pixel to draw and then 3 pixel to pass.
......
170 171
    */
171 172
   var joins = [ "miter", "round", "bevel"];
172 173
   
174
   var workingCanvas = document.createElement('canvas');
175
   var workingCtx = workingCanvas.getContext('2d', {alpha : true});
176
   workingCtx.imageSmoothingEnabled = false;
177

  
173 178
   /**
174 179
    * Holds properties for line drawing styles.
175 180
    * 
......
233 238
   };
234 239
   
235 240
   /**
236
    * Returns the stroke path renderer.
237
    * 
238
    * @param    {CanvasRenderer} canvasRenderer
239
    *           The canvas renderer.
240
    * @param    {Number} strokeStyle
241
    *           The stroke style id according to the LineStroke enumeration.
242
    * @param    {Number} width
243
    *           The line width.
244
    * @param    {Number[]} strokeColor
245
    *           The array of 3 integer values between 0 and 255 inclusive, representing
246
    *           a stroke color.
247
    * 
248
    * @return   The stroke path renderer.
249
    */
250
   this.getStrokePathRenderer = function(canvasRenderer, strokeStyle, width, strokeColor)
251
   {
252
      var basicStroke = getLineStroke(strokeStyle, width);
253
      
254
      switch (strokeStyle)
255
      {
256
         case LineStrokeEnum.SPECIFIED_WIDTH:
257
            return new WidenPathRenderer(canvasRenderer, basicStroke, strokeColor);
258
         case LineStrokeEnum.DOTTED:
259
         case LineStrokeEnum.DOTTED_SMALL:
260
         case LineStrokeEnum.DOTTED_SMALL_THIN:
261
         case LineStrokeEnum.DOTTED_LARGE:
262
            return new DotsPathRenderer(canvasRenderer, basicStroke, strokeColor);
263
         default:
264
            return new DefaultPathRenderer(canvasRenderer, strokeColor);
265
      }
266
   };
267
   
268
   /**
269
    * Returns the stroke path renderer.
270
    * 
271
    * @param    {CanvasRenderer} canvasRenderer
272
    *           The canvas renderer.
273
    * @param    {Number[]} strokeColor
274
    *           The array of 3 integer values between 0 and 255 inclusive, representing
275
    *           a stroke color.
276
    * 
277
    * @return   The stroke path renderer.
278
    */
279
   this.getStrokeDefaultPathRenderer = function(canvasRenderer, strokeColor)
280
   {
281
      return new DefaultPathRenderer(canvasRenderer, strokeColor);
282
   }
283
   
284
   /**
285
    * Apply the given stroke style to the JS native canvas renderer.
286
    * 
287
    * @param    {CanvasRenderingContext2D} context
288
    *           The canvas renderer context.
289
    * @param    {Number} strokeStyle
290
    *           The stroke style id.
291
    * @param    {Number} width
292
    *           The line width.
293
    */
294
   this.applyStrokeToCanvas = function(context, strokeStyle, width)
295
   {
296
      if (context instanceof CanvasRenderingContext2D)
297
      {
298
         var basicStroke = getLineStroke(strokeStyle, width);
299
         context.lineWidth = basicStroke.getWidth();
300
         
301
         context.lineCap = basicStroke.getCap();
302
         
303
         context.lineJoin = basicStroke.getJoin();
304
         
305
         context.miterLimit = basicStroke.getMiterLimit();
306
         if (basicStroke.getDash())
307
         {
308
            context.setLineDash(basicStroke.getDash());
309
         }
310
         else
311
         {
312
            context.setLineDash([]);
313
         }
314
         context.lineDashOffset = basicStroke.getDashPhase();
315
      }
316
   }
317
   
318
   /**
319
    * Returns the default stroke that is a solid line style with 1 pixel line width.
320
    * 
321
    * @return   {Number} The default stroke style.
322
    */
323
   this.getDefaultStrokeStyle = function()
324
   {
325
      return LineStrokeEnum.DEFAULT;
326
   }
327

  
328
   /**
329
    * Defines StrokeRenderer class
330
    */
331
   function StrokeRenderer()
332
   {
333
   }
334

  
335
   /**
336
    * Defines the constructor function
337
    */
338
   StrokeRenderer.prototype.constructor = StrokeRenderer;
339

  
340
   /**
341
    * Prepare the object. This method is used to initialize and to reset the stroke renderer. 
342
    */
343
   StrokeRenderer.prototype.beginStroke = function()
344
   {
345
   };
346

  
347
   /**
348
    * Apply the stroke to lines given by the absolute coordinates. This method is used to stroke
349
    * only vertical and horizontal lines.
350
    * 
351
    * @param    {Number} x1
352
    *           X coordinate of the starting pixel to be drawn.
353
    * @param    {Number} y1
354
    *           Y coordinate of the starting pixel to be drawn.
355
    * @param    {Number} x2
356
    *           X coordinate of the ending pixel to be drawn.
357
    * @param    {Number} y2
358
    *           Y coordinate of the ending pixel to be drawn.
359
    */
360
   StrokeRenderer.prototype.strokeLine  = function(x1, y1, x2, y2)
361
   {
362
   };
363
   
364
   /**
365
    * Apply the stroke to the next point given by its coordinates (x, y). This method is used to
366
    * stroke sloped and curved lines. 
367
    * 
368
    * @param    {Number} x
369
    *           X coordinate of the point to be drawn.
370
    * @param    {Number} y
371
    *           Y coordinate of the point to be drawn.
372
    */
373
   StrokeRenderer.prototype.strokePoint = function(x, y)
374
   {
375
   };
376
   
377
   /**
378
    * Used to draw dash patterns on lines of pixels.
379
    * 
380
    * @param    {CanvasRenderer} canvasRenderer
381
    *           The canvas renderer.
382
    * @param    {Object} basicStroke
383
    *           The stroke style object.
384
    * @param    {Number[]} strokeColor
385
    *           The array of 3 integer values between 0 and 255 inclusive, representing
386
    *           a stroke color.
387
    */
388
   function DotsPathRenderer(canvasRenderer, basicStroke, strokeColor)
389
   {
390
      var pixel = canvasRenderer.createImageData(1, 1);
391
      var pixelData = pixel.data;
392
      pixelData[3] = 0xFF;
241
    * Defines the creator method of the base StrokeRenderer object.
242
    * 
243
    * @param    {Object} sharedObject
244
    *           The object shared between creators in the inheritance chain.
245
    * 
246
    * @return   The new instance of StrokeRenderer
247
    */
248
   function strokeRenderer(sharedObject)
249
   {
250
      /** This shared container holds the following objects:
251
       *  
252
       *  var canvasRenderer;
253
       *  var basicStroke;
254
       *  var strokeColor;
255
       *  var colorSpec;
256
       *  var penImage;
257
       *  var penData;
258
       *  var canvas;
259
       *  var ctx;
260
       *  var screenImage;
261
       *  var lineImage;
262
       */
263
      var me = sharedObject || {};
264
      
265
      var that = {};
266
      
267
      /**
268
       * Sets the canvas renderer.
269
       * 
270
       * @param    {CanvasRenderer} canvasRenderer
271
       *           The canvas renderer.
272
       */
273
      that.setCanvasRenderer = function(canvasRenderer)
274
      {
275
         me.canvasRenderer = canvasRenderer;
276
         
277
         return that;
278
      };
279
   
280
      /**
281
       * Set a current stroke.
282
       * 
283
       * @param    {Object} stroke
284
       *           The stroke style object.
285
       */
286
      that.setBasicStroke = function(stroke)
287
      {
288
         me.basicStroke = stroke;
289
         me.strokeWidth = stroke.getWidth();
290
         
291
         return that;
292
      };
293
   
294
      /**
295
       * Set a current stroke color.
296
       * 
297
       * @param    {Number[]} color
298
       *           The array of 3 integer values between 0 and 255 inclusive, representing a stroke color.
299
       */
300
      that.setStrokeColor = function(color)
301
      {
302
         me.strokeColor = color;
303
         
304
         assignPenColor();
305
         
306
         return that;
307
      };
308
      
309
      /**
310
       * Sets the image data for the pen.
311
       * 
312
       * @param    {ImageData} image
313
       *           The image data for the pen.
314
       */
315
      that.setPenImage = function(image)
316
      {
317
         me.penImage = image;
318
         
319
         me.penData = image.data;
320
         
321
         assignPenColor();
322
         
323
         return that;
324
      };
325
      
326
      /**
327
       * Set the current color for the pen.
328
       */
329
      function assignPenColor()
330
      {
331
         if (me.penImage && me.strokeColor)
332
         {
333
            var width = me.penImage.width;
334
            var height = me.penImage.height;
335
            
336
            for (var i = 0; i < 4 * width * height; i += 4)
337
            {
338
               me.penData[i]     = me.strokeColor[0];
339
               me.penData[i + 1] = me.strokeColor[1];
340
               me.penData[i + 2] = me.strokeColor[2];
341
               me.penData[i + 3] = 255;
342
            }
343
         }
344
      }
345
      
346
      /**
347
       * Sets the working canvas.
348
       * 
349
       * @param    {Canvas} workingCanvas
350
       *           The working canvas.
351
       * @param    {CanvasDrawingContext} workingCtx
352
       *           The working drawing context.
353
       */
354
      that.setWorkingCanvas = function(workingCanvas, workingCtx)
355
      {
356
         me.canvas = workingCanvas;
357
         me.ctx = workingCtx;
358
         
359
         return that;
360
      };
361
      
362
      /**
363
       * Set new detached screen image with the same width and height as its working canvas. It 
364
       * doesn't check that the new screen image has the width and height as its working canvas.
365
       * 
366
       * @param    {ImageData} screenImage
367
       *           New detached screen image with the same width and height as its working canvas
368
       */
369
      that.setScreenImage = function(screenImage)
370
      {
371
         me.screenImage = screenImage;
372
         
373
         return that;
374
      };
375
      
376
      /**
377
       * Prepare the object. This method is used to initialize and to reset the stroke renderer.
378
       * 
379
       * @param    {Number} rx1
380
       *           X coordinate of the top left point of the rectangular drawing area.
381
       * @param    {Number} ry1
382
       *           Y coordinate of the top left point of the rectangular drawing area.
383
       * @param    {Number} rx2
384
       *           X coordinate of the right bottom point of the rectangular drawing area.
385
       * @param    {Number} ry2
386
       *           Y coordinate of the right bottom point of the rectangular drawing area.
387
       */
388
      that.beginStroke = function(rx1, ry1, rx2, ry2)
389
      {
390
         if (rx1 != undefined && rx2 != undefined && ry1 != undefined && ry2 != undefined)
391
         {
392
            me.r = { x1 : rx1,
393
                       y1 : ry1,
394
                       x2 : rx2,
395
                       y2 : ry2 };
396
         }
397
         else
398
         {
399
            me.r = undefined;
400
         }
401
         
402
         return that;
403
      };
404
   
405
      /**
406
       * Apply the stroke to lines given by the absolute coordinates. This method is used to stroke
407
       * only vertical and horizontal lines.
408
       * 
409
       * @param    {Number} x1
410
       *           X coordinate of the starting pixel to be drawn.
411
       * @param    {Number} y1
412
       *           Y coordinate of the starting pixel to be drawn.
413
       * @param    {Number} x2
414
       *           X coordinate of the ending pixel to be drawn.
415
       * @param    {Number} y2
416
       *           Y coordinate of the ending pixel to be drawn.
417
       */
418
      that.strokeLine  = function(x1, y1, x2, y2)
419
      {
420
         // Put the line image on the canvas
421
         if (me.r)
422
         {
423
            if (me.screenImage)
424
            {
425
               var awidth = me.screenImage.width;
426
               var aheight = me.screenImage.height;
427
               var limit = (y2 * awidth + x2) * 4;
428
               var x = x1;
429
               var y = y1;
430
               var dp = awidth * 4;
431
               
432
               // precondition: y2 should be greater or equal than y1.
433
               for (var p = (y1 * awidth + x1) * 4; p <= limit; )
434
               {
435
                  strokePoint(x, y);
436
                  
437
                  if (x1 == x2)
438
                  {
439
                     y++;
440
                     p += dp;
441
                  }
442
                  else
443
                  {
444
                     x++;
445
                     p += 4;
446
                  }
447
               }
448
            }
449
            else if (me.lineImage)
450
            {
451
               var d = me.stokeWidth >> 1;
452
               me.ctx.putImageData(me.lineImage, x1 - d - me.r.x1, y1 - d - me.r.y1);
453
            }
454
         }
455
         else
456
         {
457
            me.ctx.putImageData(me.lineImage, 0, 0);
458
            var d = me.stokeWidth >> 1;
459
            /*
460
            me.canvasRenderer.offscreenCtx.save();
461
            me.canvasRenderer.offscreenCtx.strokeStyle = "red";
462
            me.canvasRenderer.offscreenCtx.strokeWidth = 1;
463
            me.canvasRenderer.offscreenCtx.beginPath();
464
            me.canvasRenderer.offscreenCtx.rect(x1 - d, y1 - d, me.lineWidth, me.lineHeight);
465
            me.canvasRenderer.offscreenCtx.stroke();
466
            me.canvasRenderer.offscreenCtx.restore();*/
467

  
468
            me.canvasRenderer.drawCanvas(me.canvas, x1 - d, y1 - d, me.lineWidth, me.lineHeight);
469
         }
470
         
471
         return that;
472
      };
473
      
474
      /**
475
       * Apply the stroke to the next point given by its coordinates (x, y). This method is used to
476
       * stroke sloped and curved lines.
477
       * 
478
       * @param    {Number} x
479
       *           X coordinate of the point to be drawn.
480
       * @param    {Number} y
481
       *           Y coordinate of the point to be drawn.
482
       */
483
      function strokePoint(x, y)
484
      {
485
         var d = me.strokeWidth >> 1;
486
         
487
         if (me.screenImage)
488
         {
489
            var awidth = me.screenImage.width;
490
            var p = 4 * (x - me.r.x1 + d) + 4 * (y - me.r.y1 + d) * awidth;
491
            var data = me.penImage.data;
492
            
493
            if (me.strokeWidth == 1)
494
            {
495
               me.screenImage.data.set(data, p);
496
            }
497
            else
498
            {
499
               var aPenWidth = me.penImage.width;
500
               var limit = me.strokeWidth - d;
501
               for (var dx = -d; dx < limit; dx++)
502
               {
503
                  for (var dy = -d; dy < limit; dy++)
504
                  {
505
                     var q  = 4 * (dx + d) + 4 * (dy + d) * aPenWidth;
506
                     var q1 = p + 4 * dx + 4 * dy * awidth;
507
                     if ((q1 >= 0) && (q1 < me.screenImage.data.length))
508
                     {
509
                        me.screenImage.data[q1] = data[q];
510
                        me.screenImage.data[q1 + 1] = data[q + 1];
511
                        me.screenImage.data[q1 + 2] = data[q + 2];
512
                        me.screenImage.data[q1 + 3] = data[q + 3];
513
                     }
514
                  }
515
               }
516
            }
517
         }
518
         else if (me.r)
519
         {
520
            me.ctx.putImageData(me.penImage, x - me.r.x1 /*+ d*/, y - me.r.y1 /*+ d*/);
521
         }
522
         else
523
         {
524
            me.canvasRenderer.putImageData(me.penImage, x - d, y - d);
525
         }
526
         
527
         return that;
528
      };
529
      
530
      that.strokePoint = strokePoint;
531
      
532
      /**
533
       * Close the current stroke operation. This method is used to draw the result image by canvas renderer.
534
       */
535
      that.endStroke = function()
536
      {
537
         if (me.screenImage)
538
         {
539
            me.ctx.putImageData(me.screenImage, 0, 0);
540
         }
541
         if (me.r)
542
         {
543
            var d = me.strokeWidth >> 1;
544
            /*
545
            me.canvasRenderer.offscreenCtx.save();
546
            me.canvasRenderer.offscreenCtx.strokeStyle = "blue";
547
            me.canvasRenderer.offscreenCtx.strokeWidth = 1;
548
            me.canvasRenderer.offscreenCtx.beginPath();
549
            me.canvasRenderer.offscreenCtx.rect(me.r.x1 - d, me.r.y1 - d,
550
                                           me.r.x2 - me.r.x1 + me.strokeWidth,
551
                                           me.r.y2 - me.r.y1 + me.strokeWidth);
552
            me.canvasRenderer.offscreenCtx.stroke();
553
            me.canvasRenderer.offscreenCtx.restore();*/
554
            me.canvasRenderer.drawCanvas(me.canvas, me.r.x1 /*- d*/, me.r.y1 /*- d*/,
555
                                         me.r.x2 - me.r.x1 + me.strokeWidth,
556
                                         me.r.y2 - me.r.y1 + me.strokeWidth);
557
         }
558
         
559
         return that;
560
      };
561
      
562
      return that;
563
   }
564
   
565
   /**
566
    * Defines the creator method of the DotsPathRenderer object. Used to draw lines with dash patterns.
567
    */
568
   function dotsPathRenderer()
569
   {
570
      var me = {};
571
      var that = strokeRenderer(me);
572
      var super_beginStroke = that["beginStroke"];
573
      var super_strokeLine = that["strokeLine"];
574
      var super_strokePoint = that["strokePoint"];
575
      
393 576
      var state = {};
394

  
577
      
395 578
      /**
396 579
       * Normalizes a dash offset distance according to the given dash pattern.
397 580
       * A normalized dash offset distance is a distance that is cut the corresponding segment
......
429 612
      /**
430 613
       * Prepares the given stroke to be applied to the line of pixels called a path.
431 614
       */
432
      this.beginStroke = function()
615
      that.beginStroke = function(rx1, ry1, rx2, ry2)
433 616
      {
434
         var dash = basicStroke.getDash();
435
         if (!dash || dash.length == 0)
436
         {
437
            return;
438
         }
439
         var dashOffset = basicStroke.getDashPhase();
617
         super_beginStroke(rx1, ry1, rx2, ry2);
618
         var dash = me.basicStroke.getDash();
619
         var dashOffset = me.basicStroke.getDashPhase();
440 620
         var len = dash.length;
441 621
         
442 622
         var patternObj = normalize(dash, dashOffset);
......
451 631
         state.dashOn = dashOn;
452 632
         state.idx    = idx;
453 633
         state.rest   = rest;
634
         
635
         return that;
454 636
      }
455
      
637

  
456 638
      /**
457 639
       * Apply the stroke to vertical or horizontal lines given by the absolute coordinates that
458 640
       * satisfy x1 <= x2 and y1 <= y2.
......
466 648
       * @param    {Number} y2
467 649
       *           Y coordinate of the ending pixel to be drawn.
468 650
       */
469
      this.strokeLine = function(x1, y1, x2, y2)
651
      that.strokeLine = function(x1, y1, x2, y2)
470 652
      {
471 653
         var rest   = state.rest;
472 654
         var idx    = state.idx;
......
475 657
         var dash   = state.dash;
476 658
         // precondition: dx should be greater or equal than zero.
477 659
         var dx = x2 - x1;
478
         var x = x1;
479
         var y = y1;
480
         // precondition: y2 should be greater or equal than y1.
481
         while(x <= x2 && y <= y2)
482
         {
483
            if (rest < 1)
484
            {
485
               idx    = (idx + 1) % len;
486
               rest   = state.dash[idx];
487
               dashOn = !dashOn;
488
            }
489
            rest -= 1;
490
            
491
            if (dashOn)
492
            {
493
               // fill a pixel and put it on the canvas.
494
               pixelData[0] = strokeColor[0];
495
               pixelData[1] = strokeColor[1];
496
               pixelData[2] = strokeColor[2];
497
               canvasRenderer.putImageData(pixel, x, y);
498
            }
499
            // vertical line: increase Y-coordinate
500
            if (dx === 0)
501
            {
502
               y = y + 1;
503
            }
504
            else // horizontal line: increase X-coordinate
505
            {
506
               x = x + 1;
507
            }
508
         }
660
         
661
         me.lineWidth  = x2 - x1 + 1;
662
         me.lineHeight = y2 - y1 + 1;
663
         
664
         if (!me.r)
665
         {
666
            me.canvas.width  = me.lineWidth;
667
            me.canvas.height = me.lineHeight;
668
         }
669
         me.lineImage = me.screenImage || me.ctx.createImageData(me.lineWidth, me.lineHeight);
670
         var data = me.lineImage.data;
671
         var awidth = me.lineImage.width;
672
         var aheight = me.lineImage.height;
673
         var length = 4 * awidth * aheight;
674
         
675
         if (!me.screenImage)
676
         {
677
            var x = 0;
678
            var y = 0;
679
            // precondition: y2 should be greater or equal than y1.
680
            for (var p = 0; p < length; )
681
            {
682
               if (rest < 1)
683
               {
684
                  idx    = (idx + 1) % len;
685
                  rest   = state.dash[idx];
686
                  dashOn = !dashOn;
687
               }
688
               rest -= 1;
689
               
690
               if (dashOn)
691
               {
692
                  // fill a pixel and put it on the canvas.
693
                  data[p]     = me.strokeColor[0];
694
                  data[p + 1] = me.strokeColor[1];
695
                  data[p + 2] = me.strokeColor[2];
696
                  data[p + 3] = 255;
697
               }
698
               
699
               x++;
700
               // p = (y * awidth + x) * 4
701
               // p1 - p0 = [(y1 - y0) * awidth + (x1 - x0)] * 4 = 4 or 4 * (awidth - width) + 4 
702
               if (x == me.lineWidth)
703
               {
704
                  y++;
705
                  x = 0;
706
                  p += ((awidth - me.lineWidth + 1) * 4);
707
               }
708
               else
709
               {
710
                  p += 4; 
711
               }
712
            }
713
   
714
            // Put the line image on the canvas
715
            super_strokeLine(x1, y1, x2, y2);
716
         }
717
         
509 718
         // save the dash pattern state in order to continue this dash pattern for next drawings 
510 719
         state.dashOn = dashOn;
511 720
         state.rest   = rest;
512 721
         state.idx    = idx;
722
         
723
         return that;
513 724
      }
514
      
725

  
515 726
      /**
516 727
       * Apply the stroke to the next point given by its coordinates (x, y).
517 728
       * 
......
520 731
       * @param    {Number} y
521 732
       *           Y coordinate of the point to be drawn.
522 733
       */
523
      this.strokePoint = function(x, y)
734
      that.strokePoint = function(x, y)
524 735
      {
525 736
         var rest   = state.rest;
526 737
         var idx    = state.idx;
......
535 746
         rest -= 1;
536 747
         if (dashOn)
537 748
         {
538
            // fill a pixel and put it on the canvas.
539
            pixelData[0] = strokeColor[0];
540
            pixelData[1] = strokeColor[1];
541
            pixelData[2] = strokeColor[2];
542
            canvasRenderer.putImageData(pixel, x, y);
749
            super_strokePoint(x, y);
543 750
         }
544 751
         // save the dash pattern state in order to continue this dash pattern for next drawings 
545 752
         state.dashOn = dashOn;
546 753
         state.rest   = rest;
547 754
         state.idx    = idx;
755
         
756
         return that;
548 757
      }
549 758
      
759
      return that;
550 760
   }
551 761
   
552 762
   /**
553
    * DotsPathRenderer implements StrokeRenderer methods
763
    * Defines the creator method of the WidenPathRenderer object. Used to draw wide solid lines having 
764
    * more than a one pixel width.
554 765
    */
555
   DotsPathRenderer.prototype = Object.create(StrokeRenderer.prototype);
766
   function widenPathRenderer()
767
   {
768
      var me = {};
769
      var that = strokeRenderer(me);
770
      var super_strokeLine  = that["strokeLine"];
771
      var super_strokePoint = that["strokePoint"];
556 772

  
557
   /**
558
    * Defines the constructor function
559
    */
560
   DotsPathRenderer.prototype.constructor = DotsPathRenderer;
561
   
562
   /**
563
    * Used to widen lines of pixels.
564
    * 
565
    * @param    {CanvasRenderer} canvasRenderer
566
    *           The canvas renderer.
567
    * @param    {Object} basicStroke
568
    *           The stroke style object.
569
    * @param    {Number[]} strokeColor
570
    *           The array of 3 integer values between 0 and 255 inclusive, representing
571
    *           a stroke color.
572
    */
573
   function WidenPathRenderer(canvasRenderer, basicStroke, strokeColor)
574
   {
575
      var width = basicStroke.getWidth();
576
      var image = canvasRenderer.createImageData(width, width);
577
      var imageData = image.data;
578
      
579
      for (var i = 0; i < 4 * width * width; i += 4)
580
      {
581
         imageData[i] = strokeColor[0];
582
         imageData[i + 1] = strokeColor[1];
583
         imageData[i + 2] = strokeColor[2];
584
         imageData[i + 3] = 0xFF;
585
      }
586
      
587
      /**
588
       * Prepares the given stroke to be applied to the line of pixels called a path.
589
       */
590
      this.beginStroke = function()
591
      {
592
      }
593
      
594 773
      /**
595 774
       * Apply the stroke to vertical or horizontal lines given by the absolute coordinates that
596 775
       * satisfy x1 <= x2 and y1 <= y2.
......
604 783
       * @param    {Number} y2
605 784
       *           Y coordinate of the ending pixel to be drawn.
606 785
       */
607
      this.strokeLine = function(x1, y1, x2, y2)
786
      that.strokeLine = function(x1, y1, x2, y2)
608 787
      {
609 788
         // calculate the minimal rectangle width and height that covers and fills
610 789
         //a line that should be widen to have the given width
611
         var lineWidth  = x2 - x1 + width;
612
         var lineHeight = y2 - y1 + width;
613
         var x0 = x1 - (width >> 1);
614
         var y0 = y1 - (width >> 1);
790
         me.lineWidth  = x2 - x1 + me.strokeWidth;
791
         me.lineHeight = y2 - y1 + me.strokeWidth;
792
         
793
         if (!me.r)
794
         {
795
            me.canvas.width = me.lineWidth;
796
            me.canvas.height = me.lineHeight;
797
         }
798
         
615 799
         // create an image to draw the widen line
616
         var screenImage = canvasRenderer.createImageData(lineWidth, lineHeight);
617
         var data = screenImage.data;
618
         var length = 4 * lineWidth * lineHeight;
800
         me.lineImage =  me.screenImage || me.ctx.createImageData(me.lineWidth, me.lineHeight);
801
         var data = me.lineImage.data;
802
         var length = 4 * me.lineWidth * me.lineHeight;
619 803
         
620
         for (var i = 0; i < length; i += 4)
804
         if (!me.screenImage)
621 805
         {
622
            data[i]     = strokeColor[0];
623
            data[i + 1] = strokeColor[1];
624
            data[i + 2] = strokeColor[2];
625
            data[i + 3] = 255;
806
            for (var i = 0; i < length; i += 4)
807
            {
808
               data[i]     = me.strokeColor[0];
809
               data[i + 1] = me.strokeColor[1];
810
               data[i + 2] = me.strokeColor[2];
811
               data[i + 3] = 255;
812
            }
626 813
         }
627
         canvasRenderer.putImageData(screenImage, x0, y0);
814
         
815
         // Put the line image on the canvas
816
         super_strokeLine(x1, y1, x2, y2);
817
         
818
         return that;
628 819
      }
629 820
      
630
      /**
631
       * Apply the stroke to the next point given by its coordinates (x, y).
632
       * 
633
       * @param    {Number} x
634
       *           X coordinate of the point to be drawn.
635
       * @param    {Number} y
636
       *           Y coordinate of the point to be drawn.
637
       */
638
      this.strokePoint = function(x, y)
639
      {
640
         canvasRenderer.putImageData(image, x - (width >> 1), y - (width >> 1));
641
      }
642

  
821
      return that;
643 822
   }
644
   
645
   /**
646
    * WidenPathRenderer implements StrokeRenderer methods
647
    */
648
   WidenPathRenderer.prototype = Object.create(StrokeRenderer.prototype);
649

  
650
   /**
651
    * Defines the constructor function
652
    */
653
   WidenPathRenderer.prototype.constructor = WidenPathRenderer;
654

  
655
   /**
656
    * The default lines renderer.
657
    * 
658
    * @param    {CanvasRenderer} canvasRenderer
659
    *           The canvas renderer.
660
    * @param    {Number[]} strokeColor
661
    *           The array of 3 integer values between 0 and 255 inclusive, representing
662
    *           a stroke color.
663
    */
664
   function DefaultPathRenderer(canvasRenderer, strokeColor)
823

  
824
   /**
825
    * Defines the creator method of the DefaultPathRenderer object to draw solid lines having a one pixel width.
826
    */
827
   function defaultPathRenderer()
665 828
   {
666
      var width = defaultStroke.getWidth();
667
      var image = canvasRenderer.createImageData(width, width);
668
      var imageData = image.data;
669
      
670
      for (var i = 0; i < 4 * width * width; i += 4)
671
      {
672
         imageData[i]     = strokeColor[0];
673
         imageData[i + 1] = strokeColor[1];
674
         imageData[i + 2] = strokeColor[2];
675
         imageData[i + 3] = 255;
676
      }
677
      
678
      /**
679
       * Prepares the given stroke to be applied to the line of pixels called a path.
680
       */
681
      this.beginStroke = function()
682
      {
683
      }
829
      var me = {};
830
      var that = strokeRenderer(me);
831
      
832
      var super_strokeLine = that["strokeLine"];
684 833
      
685 834
      /**
686 835
       * Apply the stroke to vertical or horizontal lines given by the absolute coordinates that
......
695 844
       * @param    {Number} y2
696 845
       *           Y coordinate of the ending pixel to be drawn.
697 846
       */
698
      this.strokeLine = function(x1, y1, x2, y2)
847
      that.strokeLine = function(x1, y1, x2, y2)
699 848
      {
700 849
         // The default line width is 1 pixel.
701
         // Calculate the line with and height to draw a line as an image
702
         var lineWidth  = x2 - x1 + 1;
703
         var lineHeight = y2 - y1 + 1;
850
         // Calculate the line width and height to draw a line as an image
851
         me.lineWidth  = x2 - x1 + 1;
852
         me.lineHeight = y2 - y1 + 1;
853
         
854
         if (!me.r)
855
         {
856
            me.canvas.width  = me.lineWidth;
857
            me.canvas.height = me.lineHeight;
858
         }
704 859
         // Create a line image
705
         var screenImage = canvasRenderer.createImageData(lineWidth, lineHeight);
706
         var data = screenImage.data;
707
         var length = 4 * lineWidth * lineHeight;
708

  
709
         // Fill a line image with the given stroke color
710
         data[0] = strokeColor[0];
711
         data[1] = strokeColor[1];
712
         data[2] = strokeColor[2];
713
         data[3] = 255;
714

  
715
         var pos = 4;
716
         var seqLen = 4;
717
         while(pos < length)
860
         me.lineImage = me.screenImage || me.ctx.createImageData(me.lineWidth, me.lineHeight);
861
         var data = me.lineImage.data;
862
         var length = 4 * me.lineWidth * me.lineHeight;
863

  
864
         if (!me.screenImage)
718 865
         {
719
            data.copyWithin(pos, 0, seqLen);
720
            // increment position by the length of the current segment
721
            pos += seqLen;
722
            // double the length of the next segment
723
            seqLen <<= 1;
866
            // Fill a line image with the given stroke color
867
            data[0] = me.strokeColor[0];
868
            data[1] = me.strokeColor[1];
869
            data[2] = me.strokeColor[2];
870
            data[3] = 255;
871
   
872
            var pos = 4;
873
            var seqLen = 4;
874
            while(pos < length)
875
            {
876
               data.copyWithin(pos, 0, seqLen);
877
               // increment position by the length of the current segment
878
               pos += seqLen;
879
               // double the length of the next segment
880
               seqLen <<= 1;
881
            }
724 882
         }
725

  
883
         
726 884
         // Put the line image on the canvas
727
         canvasRenderer.putImageData(screenImage, x1, y1);
885
         super_strokeLine(x1, y1, x2, y2);
886
         
887
         return that;
728 888
      }
729 889
      
730 890
      /**
......
748 908
       * @param    {Number} y2
749 909
       *           Y coordinate of the ending pixel to be drawn.
750 910
       */
751
      this.xorLine = function(x1, y1, x2, y2)
911
      that.xorLine = function(x1, y1, x2, y2)
752 912
      {
753 913
         // The default line width is 1 pixel.
754 914
         // Calculate the line with and height to draw a line as an image
755 915
         var lineWidth  = x2 - x1 + 1;
756 916
         var lineHeight = y2 - y1 + 1;
757 917
         // Get current image data from screen
758
         var screenImage = canvasRenderer.screenCtx.getImageData(x1, y1, lineWidth, lineHeight);
918
         var screenImage = me.canvasRenderer.screenCtx.getImageData(x1, y1, lineWidth, lineHeight);
759 919
         var data = screenImage.data;
760 920
         var length = 4 * lineWidth * lineHeight;
761 921
         // Fill a line image with the given stroke color
762 922
         for (var i = 0; i < length; i += 4)
763 923
         {
764
            if (data[i] != canvasRenderer.iBeamColor[0])
765
            {
766
               if (data[i] == strokeColor[0] || data[i + 3] == 0)
767
               {
768
                  data[i] = canvasRenderer.iBeamColor[0];
769
               }
770
               else
771
               {
772
                  data[i] = data[i] ^ strokeColor[0];
773
               }
774
            }
775
            if (data[i + 1] != canvasRenderer.iBeamColor[1])
776
            {
777
               if (data[i + 1] == strokeColor[1] || data[i + 3] == 0)
778
               {
779
                  data[i + 1] = canvasRenderer.iBeamColor[1];
780
               }
781
               else
782
               {
783
                  data[i + 1] = data[i + 1] ^ strokeColor[1];
784
               }
785
            }
786
            if (data[i + 2] != canvasRenderer.iBeamColor[2])
787
            {
788
               if (data[i + 2] == strokeColor[2] || data[i + 3] == 0)
789
               {
790
                  data[i + 2] = canvasRenderer.iBeamColor[2];
791
               }
792
               else
793
               {
794
                  data[i + 2] = data[i + 2] ^ strokeColor[2];
924
            if (data[i] != me.canvasRenderer.iBeamColor[0])
925
            {
926
               if (data[i] == me.strokeColor[0] || data[i + 3] == 0)
927
               {
928
                  data[i] = me.canvasRenderer.iBeamColor[0];
929
               }
930
               else
931
               {
932
                  data[i] = data[i] ^ me.strokeColor[0];
933
               }
934
            }
935
            if (data[i + 1] != me.canvasRenderer.iBeamColor[1])
936
            {
937
               if (data[i + 1] == me.strokeColor[1] || data[i + 3] == 0)
938
               {
939
                  data[i + 1] = me.canvasRenderer.iBeamColor[1];
940
               }
941
               else
942
               {
943
                  data[i + 1] = data[i + 1] ^ me.strokeColor[1];
944
               }
945
            }
946
            if (data[i + 2] != me.canvasRenderer.iBeamColor[2])
947
            {
948
               if (data[i + 2] == me.strokeColor[2] || data[i + 3] == 0)
949
               {
950
                  data[i + 2] = me.canvasRenderer.iBeamColor[2];
951
               }
952
               else
953
               {
954
                  data[i + 2] = data[i + 2] ^ me.strokeColor[2];
795 955
               }
796 956
            }
797 957
            data[i + 3] = 255;
798 958
         }
799 959
         // Put the line image on the canvas
800
         canvasRenderer.putImageData(screenImage, x1, y1);
801
      }
802
      
803
      /**
804
       * Apply the stroke to the next point given by its coordinates (x, y).
... This diff was truncated because it exceeds the maximum size that can be displayed.