API  0.9.6
 All Classes Files Functions Variables Macros Groups Pages
CGContextCanvas.j
Go to the documentation of this file.
1 /*
2  * CGContextCanvas.j
3  * AppKit
4  *
5  * Created by Francisco Tolmasky.
6  * Copyright 2008, 280 North, Inc.
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public
10  * License as published by the Free Software Foundation; either
11  * version 2.1 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this library; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21  */
22 
23 var CANVAS_LINECAP_TABLE = [ "butt", "round", "square" ],
24  CANVAS_LINEJOIN_TABLE = [ "miter", "round", "bevel" ],
25  CANVAS_COMPOSITE_TABLE = [ "source-over", "source-over", "source-over", "source-over", "darker",
26  "lighter", "source-over", "source-over", "source-over", "source-over",
27  "source-over", "source-over", "source-over", "source-over", "source-over",
28  "source-over", "source-over",
29  "copy", "source-in", "source-out", "source-atop",
30  "destination-over", "destination-in", "destination-out", "destination-atop",
31  "xor", "source-over", "source-over" ];
32 
33 #define _CGContextAddArcCanvas(aContext, x, y, radius, startAngle, endAngle, anticlockwise) aContext.arc(x, y, radius, startAngle, endAngle, anticlockwise)
34 #define _CGContextAddArcToPointCanvas(aContext, x1, y1, x2, y2, radius) aContext.arcTo(x1, y1, x2, y2, radius)
35 #define _CGContextAddCurveToPointCanvas(aContext, cp1x, cp1y, cp2x, cp2y, x, y) aContext.bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y)
36 #define _CGContextAddQuadCurveToPointCanvas(aContext, cpx, cpy, x, y) aContext.quadraticCurveTo(cpx, cpy, x, y)
37 #define _CGContextAddLineToPointCanvas(aContext, x, y) aContext.lineTo(x, y)
38 #define _CGContextClosePathCanvas(aContext) aContext.closePath()
39 #define _CGContextMoveToPointCanvas(aContext, x, y) aContext.moveTo(x, y)
40 
41 #define _CGContextAddRectCanvas(aContext, aRect) aContext.rect(_CGRectGetMinX(aRect), _CGRectGetMinY(aRect), _CGRectGetWidth(aRect), _CGRectGetHeight(aRect))
42 #define _CGContextBeginPathCanvas(aContext) aContext.beginPath()
43 #define _CGContextFillRectCanvas(aContext, aRect) aContext.fillRect(_CGRectGetMinX(aRect), _CGRectGetMinY(aRect), _CGRectGetWidth(aRect), _CGRectGetHeight(aRect))
44 #define _CGContextClipCanvas(aContext) aContext.clip()
45 
46 function CGContextSaveGState(aContext)
47 {
48  aContext.save();
49 }
50 
51 function CGContextRestoreGState(aContext)
52 {
53  aContext.restore();
54 }
55 
56 function CGContextSetLineCap(aContext, aLineCap)
57 {
58  aContext.lineCap = CANVAS_LINECAP_TABLE[aLineCap];
59 }
60 
61 function CGContextSetLineJoin(aContext, aLineJoin)
62 {
63  aContext.lineJoin = CANVAS_LINEJOIN_TABLE[aLineJoin];
64 }
65 
66 function CGContextSetLineWidth(aContext, aLineWidth)
67 {
68  aContext.lineWidth = aLineWidth;
69 }
70 
71 function CGContextSetMiterLimit(aContext, aMiterLimit)
72 {
73  aContext.miterLimit = aMiterLimit;
74 }
75 
76 function CGContextSetBlendMode(aContext, aBlendMode)
77 {
78  aContext.globalCompositeOperation = CANVAS_COMPOSITE_TABLE[aBlendMode];
79 }
80 
81 function CGContextAddArc(aContext, x, y, radius, startAngle, endAngle, clockwise)
82 {
83  // Despite the documentation saying otherwise, the last parameter is anti-clockwise not clockwise.
84  // http://developer.mozilla.org/en/docs/Canvas_tutorial:Drawing_shapes#Arcs
85  _CGContextAddArcCanvas(aContext, x, y, radius, startAngle, endAngle, !clockwise);
86 }
87 
88 function CGContextAddArcToPoint(aContext, x1, y1, x2, y2, radius)
89 {
90  _CGContextAddArcToPointCanvas(aContext, x1, y1, x2, y2, radius);
91 }
92 
93 function CGContextAddCurveToPoint(aContext, cp1x, cp1y, cp2x, cp2y, x, y)
94 {
95  _CGContextAddCurveToPointCanvas(aContext, cp1x, cp1y, cp2x, cp2y, x, y);
96 }
97 
98 function CGContextAddLineToPoint(aContext, x, y)
99 {
100  _CGContextAddLineToPointCanvas(aContext, x, y);
101 }
102 
103 function CGContextAddPath(aContext, aPath)
104 {
105  if (!aContext || CGPathIsEmpty(aPath))
106  return;
107 
108  var elements = aPath.elements,
109 
110  i = 0,
111  count = aPath.count;
112 
113  for (; i < count; ++i)
114  {
115  var element = elements[i],
116  type = element.type;
117 
118  switch (type)
119  {
120  case kCGPathElementMoveToPoint: _CGContextMoveToPointCanvas(aContext, element.x, element.y);
121  break;
122  case kCGPathElementAddLineToPoint: _CGContextAddLineToPointCanvas(aContext, element.x, element.y);
123  break;
124  case kCGPathElementAddQuadCurveToPoint: _CGContextAddQuadCurveToPointCanvas(aContext, element.cpx, element.cpy, element.x, element.y);
125  break;
126  case kCGPathElementAddCurveToPoint: _CGContextAddCurveToPointCanvas(aContext, element.cp1x, element.cp1y, element.cp2x, element.cp2y, element.x, element.y);
127  break;
128  case kCGPathElementCloseSubpath: _CGContextClosePathCanvas(aContext);
129  break;
130  case kCGPathElementAddArc: _CGContextAddArcCanvas(aContext, element.x, element.y, element.radius, element.startAngle, element.endAngle, element.clockwise);
131  break;
132  case kCGPathElementAddArcToPoint: _CGContextAddArcToPointCanvas(aContext, element.p1x, element.p1y, element.p2x, element.p2y, element.radius);
133  break;
134  }
135  }
136 }
137 
138 function CGContextAddRect(aContext, aRect)
139 {
140  _CGContextAddRectCanvas(aContext, aRect);
141 }
142 
143 function CGContextAddRects(aContext, rects, count)
144 {
145  var i = 0;
146 
147  if (count === NULL)
148  var count = rects.length;
149 
150  for (; i < count; ++i)
151  {
152  var rect = rects[i];
153  _CGContextAddRectCanvas(aContext, rect);
154  }
155 }
156 
157 function CGContextBeginPath(aContext)
158 {
159  _CGContextBeginPathCanvas(aContext);
160 }
161 
162 function CGContextClosePath(aContext)
163 {
164  _CGContextClosePathCanvas(aContext);
165 }
166 
167 function CGContextMoveToPoint(aContext, x, y)
168 {
169  _CGContextMoveToPointCanvas(aContext, x, y);
170 }
171 
172 function CGContextClearRect(aContext, aRect)
173 {
174  aContext.clearRect(_CGRectGetMinX(aRect), _CGRectGetMinY(aRect), _CGRectGetWidth(aRect), _CGRectGetHeight(aRect));
175 }
176 
177 function CGContextDrawPath(aContext, aMode)
178 {
179  if (aMode == kCGPathFill || aMode == kCGPathFillStroke)
180  aContext.fill();
181  else if (aMode == kCGPathEOFill || aMode == kCGPathEOFillStroke)
182  alert("not implemented!!!");
183 
184  if (aMode == kCGPathStroke || aMode == kCGPathFillStroke || aMode == kCGPathEOFillStroke)
185  aContext.stroke();
186 }
187 
188 function CGContextFillRect(aContext, aRect)
189 {
190  _CGContextFillRectCanvas(aContext, aRect);
191 }
192 
193 function CGContextFillRects(aContext, rects, count)
194 {
195  var i = 0;
196 
197  if (count === NULL)
198  var count = rects.length;
199 
200  for (; i < count; ++i)
201  {
202  var rect = rects[i];
203  _CGContextFillRectCanvas(aContext, rect);
204  }
205 }
206 
207 function CGContextStrokeRect(aContext, aRect)
208 {
209  aContext.strokeRect(_CGRectGetMinX(aRect), _CGRectGetMinY(aRect), _CGRectGetWidth(aRect), _CGRectGetHeight(aRect));
210 }
211 
212 function CGContextClip(aContext)
213 {
214  _CGContextClipCanvas(aContext);
215 }
216 
217 function CGContextClipToRect(aContext, aRect)
218 {
219  _CGContextBeginPathCanvas(aContext);
220  _CGContextAddRectCanvas(aContext, aRect);
221  _CGContextClosePathCanvas(aContext);
222 
223  _CGContextClipCanvas(aContext);
224 }
225 
226 function CGContextClipToRects(aContext, rects, count)
227 {
228  if (count === NULL)
229  var count = rects.length;
230 
231  _CGContextBeginPathCanvas(aContext);
232  CGContextAddRects(aContext, rects, count);
233  _CGContextClipCanvas(aContext);
234 }
235 
236 function CGContextSetAlpha(aContext, anAlpha)
237 {
238  aContext.globalAlpha = anAlpha;
239 }
240 
241 function CGContextSetFillColor(aContext, aColor)
242 {
243  if ([aColor patternImage])
244  {
245  var patternImg = [aColor patternImage],
246  size = [patternImg size],
247  img;
248 
249  if (size)
250  img = new Image(size.width, size.height);
251  else
252  img = new Image();
253 
254  img.src = [patternImg filename];
255 
256  var pattern = aContext.createPattern(img, "repeat");
257 
258  aContext.fillStyle = pattern;
259  }
260  else
261  aContext.fillStyle = [aColor cssString];
262 }
263 
264 function CGContextSetStrokeColor(aContext, aColor)
265 {
266  aContext.strokeStyle = [aColor cssString];
267 }
268 
269 function CGContextSetShadow(aContext, aSize, aBlur)
270 {
271  aContext.shadowOffsetX = aSize.width;
272  aContext.shadowOffsetY = aSize.height;
273  aContext.shadowBlur = aBlur;
274 }
275 
276 function CGContextSetShadowWithColor(aContext, aSize, aBlur, aColor)
277 {
278  aContext.shadowOffsetX = aSize.width;
279  aContext.shadowOffsetY = aSize.height;
280  aContext.shadowBlur = aBlur;
281  aContext.shadowColor = [aColor cssString];
282 }
283 
284 function CGContextRotateCTM(aContext, anAngle)
285 {
286  aContext.rotate(anAngle);
287 }
288 
289 function CGContextScaleCTM(aContext, sx, sy)
290 {
291  aContext.scale(sx, sy);
292 }
293 
294 function CGContextTranslateCTM(aContext, tx, ty)
295 {
296  aContext.translate(tx, ty);
297 }
298 
299 #define scale_rotate(a, b, c, d) \
300  var sign = (a * d < 0.0 || b * c > 0.0) ? -1.0 : 1.0, \
301  a2 = (ATAN2(b, d) + ATAN2(-sign * c, sign * a)) / 2.0, \
302  cos = COS(a2),\
303  sin = SIN(a2);\
304  \
305  if (cos == 0)\
306  {\
307  sx = -c / sin;\
308  sy = b / sin;\
309  }\
310  else if (sin == 0)\
311  {\
312  sx = a / cos;\
313  sy = d / cos;\
314  }\
315  else\
316  {\
317  abs_cos = ABS(cos);\
318  abs_sin = ABS(sin);\
319  \
320  sx = (abs_cos * a / cos + abs_sin * -c / sin) / (abs_cos + abs_sin);\
321  sy = (abs_cos * d / cos + abs_sin * b / sin) / (abs_cos + abs_sin);\
322  }\
323 
324 #define rotate_scale(a, b, c, d) \
325  var sign = (a * d < 0.0 || b * c > 0.0) ? -1.0 : 1.0;\
326  a1 = (ATAN2(sign * b, sign * a) + ATAN2(-c, d)) / 2.0,\
327  cos = COS(a1),\
328  sin = SIN(a1);\
329  \
330  if (cos == 0)\
331  {\
332  sx = b / sin;\
333  sy = -c / sin;\
334  }\
335  else if (sin == 0)\
336  {\
337  sx = a / cos;\
338  sy = d / cos;\
339  }\
340  else\
341  {\
342  abs_cos = ABS(cos);\
343  abs_sin = ABS(sin);\
344  \
345  sx = (abs_cos * a / cos + abs_sin * b / sin) / (abs_cos + abs_sin);\
346  sy = (abs_cos * d / cos + abs_sin * -c / sin) / (abs_cos + abs_sin);\
347  }\
348 
349 function eigen(anAffineTransform)
350 {
351  alert("IMPLEMENT ME!");
352 }
353 
354 
356 {
357 
358 CGContextConcatCTM = function(aContext, anAffineTransform)
359 {
360  aContext.transform(anAffineTransform.a, anAffineTransform.b, anAffineTransform.c, anAffineTransform.d, anAffineTransform.tx, anAffineTransform.ty);
361 };
362 
363 }
364 else
365 {
366 
367 CGContextConcatCTM = function(aContext, anAffineTransform)
368 {
369  var a = anAffineTransform.a,
370  b = anAffineTransform.b,
371  c = anAffineTransform.c,
372  d = anAffineTransform.d,
373  tx = anAffineTransform.tx,
374  ty = anAffineTransform.ty,
375  sx = 1.0,
376  sy = 1.0,
377  a1 = 0.0,
378  a2 = 0.0;
379 
380  // Detect the simple case of just scaling.
381  if (b == 0.0 && c == 0.0)
382  {
383  sx = a;
384  sy = d;
385  }
386 
387  // a scale followed by a rotate
388  else if (a * b == -c * d)
389  {
390  scale_rotate(a, b, c, d)
391  }
392 
393  // rotate, then scale.
394  else if (a * c == -b * d)
395  {
396  rotate_scale(a, b, c, d)
397  }
398  else
399  {
400  var transpose = CGAffineTransformMake(a, c, b, d, 0.0, 0.0), // inline
401  u = eigen(CGAffineTransformConcat(anAffineTransform, transpose)),
402  v = eigen(CGAffineTransformConcat(transpose, anAffineTransform)),
403  U = CGAffineTransformMake(u.vector_1.x, u.vector_2.x, u.vector_1.y, u.vector_2.y, 0.0, 0.0), // inline
404  VT = CGAffineTransformMake(v.vector_1.x, v.vector_1.y, v.vector_2.x, v.vector_2.y, 0.0, 0.0),
405  S = CGAffineTransformConcat(CGAffineTransformConcat(CGAffineTransformInvert(U), anAffineTransform), CGAffineTransformInvert(VT));
406 
407  a = VT.a;
408  b = VT.b;
409  c = VT.c;
410  d = VT.d;
411  scale_rotate(a, b, c, d)
412  S.a *= sx;
413  S.d *= sy;
414  a = U.a;
415  b = U.b;
416  c = U.c;
417  d = U.d;
418  rotate_scale(a, b, c, d)
419  sx = S.a * sx;
420  sy = S.d * sy;
421  }
422 
423  if (tx != 0 || ty != 0)
424  CGContextTranslateCTM(aContext, tx, ty);
425  if (a1 != 0.0)
426  CGContextRotateCTM(aContext, a1);
427  if (sx != 1.0 || sy != 1.0)
428  CGContextScaleCTM(aContext, sx, sy);
429  if (a2 != 0.0)
430  CGContextRotateCTM(aContext, a2);
431 };
432 
433 }
434 
435 function CGContextDrawImage(aContext, aRect, anImage)
436 {
437  aContext.drawImage(anImage._image, _CGRectGetMinX(aRect), _CGRectGetMinY(aRect), _CGRectGetWidth(aRect), _CGRectGetHeight(aRect));
438 }
439 
440 function to_string(aColor)
441 {
442  return "rgba(" + ROUND(aColor.components[0] * 255) + ", " + ROUND(aColor.components[1] * 255) + ", " + ROUND(255 * aColor.components[2]) + ", " + aColor.components[3] + ")";
443 }
444 
445 function CGContextDrawLinearGradient(aContext, aGradient, aStartPoint, anEndPoint, options)
446 {
447  var colors = aGradient.colors,
448  count = colors.length,
449 
450  linearGradient = aContext.createLinearGradient(aStartPoint.x, aStartPoint.y, anEndPoint.x, anEndPoint.y);
451 
452  while (count--)
453  linearGradient.addColorStop(aGradient.locations[count], to_string(colors[count]));
454 
455  aContext.fillStyle = linearGradient;
456  aContext.fill();
457 }
458 
460 {
461  var DOMElement = document.createElement("canvas"),
462  context = DOMElement.getContext("2d");
463 
464  context.DOMElement = DOMElement;
465 
466  return context;
467 }