API  1.0.0
CGContextVML.j
Go to the documentation of this file.
1 /*
2  * CGContextVML.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 VML_TRUTH_TABLE = [ "f", "t"],
24  VML_LINECAP_TABLE = [ "flat", "round", "square" ],
25  VML_LINEJOIN_TABLE = [ "miter", "round", "bevel" ],
26  VML_ELEMENT_TABLE = [ " m ", " l ", "qb", " c ", " x ", [" at ", " wa "]];
27 
28 var _CGBitmapGraphicsContextCreate = CGBitmapGraphicsContextCreate;
29 
31 {
32  // The first time around, we have to set up our environment to support vml.
33  document.namespaces.add("cg_vml_", "urn:schemas-microsoft-com:vml");
34  document.createStyleSheet().cssText = "cg_vml_\\:*{behavior:url(#default#VML)}";
35 
36  CGBitmapGraphicsContextCreate = _CGBitmapGraphicsContextCreate;
37 
38  return _CGBitmapGraphicsContextCreate();
39 }
40 
41 function CGContextSetFillColor(aContext, aColor)
42 {
43  if ([aColor patternImage])
44  // Prefix a marker character to the string so we know it's a pattern image filename
45  aContext.gState.fillStyle = "!" + [[aColor patternImage] filename];
46  else
47  aContext.gState.fillStyle = [aColor cssString];
48 }
49 
50 // FIXME: aRect is ignored.
51 function CGContextClearRect(aContext, aRect)
52 {
53  if (aContext.buffer != nil)
54  aContext.buffer = "";
55  else
56  aContext.DOMElement.innerHTML = "";
57 
58  aContext.path = NULL;
59 }
60 
61 var W = 10.0,
62  H = 10.0,
63  Z = 10.0,
64  Z_2 = Z / 2.0;
65 
66 #define COORD(aCoordinate) (aCoordinate === 0.0 ? 0 : ROUND(Z * (aCoordinate) - Z_2))
67 
68 function CGContextDrawImage(aContext, aRect, anImage)
69 {
70  var string = "";
71 
72  if (anImage.buffer != nil)
73  string = anImage.buffer;
74  else
75  {
76  var ctm = aContext.gState.CTM,
77  origin = CGPointApplyAffineTransform(aRect.origin, ctm),
78  similarity = ctm.a == ctm.d && ctm.b == -ctm.c,
79  vml = ["<cg_vml_:group coordsize=\"1,1\" coordorigin=\"0,0\" style=\"width:1;height:1;position:absolute"];
80 
81  /*if (similarity)
82  {
83  var angle = CGPointMake(1.0, 0.0);
84 
85  angle = CGPointApplyAffineTransform(angle, ctm);
86 
87  vml.push(";rotation:", ATAN2(angle.y - ctm.ty, angle.x - ctm.tx) * 180 / PI);
88  }*/
89 
90  // Only create a filter if absolutely necessary. This actually
91  // turns out to only be the case if our transform matrix is not
92  // a similarity matrix, that is to say, the transform actually
93  // morphs the shape beyond scaling and rotation.
94  //if (!similarity && (ctm.a != 1.0 || ctm.b || ctm.c || ctm.d != 1.0))
95  {
96  var transformedRect = CGRectApplyAffineTransform(aRect, ctm);
97 
98  vml.push( ";padding:0 ", ROUND(CGRectGetMaxX(transformedRect)), "px ", ROUND(CGRectGetMaxY(transformedRect)),
99  "px 0;filter:progid:DXImageTransform.Microsoft.Matrix(",
100  "M11='", ctm.a, "',M12='", ctm.c, "',M21='", ctm.b, "',M22='", ctm.d, "',",
101  "Dx='", ROUND(origin.x), "', Dy='", ROUND(origin.y), "', sizingmethod='clip');");
102  }
103  //else
104  // vml.push(";top:", ROUND(origin.y - 0.5), "px;left:", ROUND(origin.x - 0.5), "px;");
105 
106  vml.push( "\"><cg_vml_:image src=\"", anImage._image.src,
107  "\" style=\"width:", CGRectGetWidth(aRect), "px;height:", CGRectGetHeight(aRect),
108  "px;\"/></g_vml_:group>");
109 
110  string = vml.join("");
111  }
112 
113  if (aContext.buffer != nil)
114  aContext.buffer += string;
115  else
116  aContext.DOMElement.insertAdjacentHTML("BeforeEnd", string);
117 }
118 
119 function CGContextDrawPath(aContext, aMode)
120 {
121  if (!aContext || CGPathIsEmpty(aContext.path))
122  return;
123 
124  var elements = aContext.path.elements,
125 
126  i = 0,
127  count = aContext.path.count,
128 
129  gState = aContext.gState,
130  fill = (aMode == kCGPathFill || aMode == kCGPathFillStroke) ? 1 : 0,
131  stroke = (aMode == kCGPathStroke || aMode == kCGPathFillStroke) ? 1 : 0,
132  opacity = gState.alpha,
133  vml = ["<cg_vml_:shape"];
134 
135  if (gState.fillStyle.charAt(0) !== "!")
136  vml.push(" fillcolor=\"", gState.fillStyle, "\"");
137 
138  vml.push( " filled=\"", VML_TRUTH_TABLE[fill],
139  "\" style=\"position:absolute;width:", W, ";height:", H,
140  ";\" coordorigin=\"0 0\" coordsize=\"", Z * W, " ", Z * H,
141  "\" stroked=\"", VML_TRUTH_TABLE[stroke],
142  "\" strokeweight=\"", gState.lineWidth,
143  "\" strokecolor=\"", gState.strokeStyle,
144  "\" path=\"");
145 
146  for (; i < count; ++i)
147  {
148  var element = elements[i],
149  type = element.type;
150 
151  switch (type)
152  {
154  case kCGPathElementAddLineToPoint: vml.push(VML_ELEMENT_TABLE[type], COORD(element.x), ',', COORD(element.y));
155  break;
156 
158  COORD(element.cpx), ',', COORD(element.cpy), ',',
159  COORD(element.x), ',', COORD(element.y));
160  break;
161 
163  COORD(element.cp1x), ',', COORD(element.cp1y), ',',
164  COORD(element.cp2x), ',', COORD(element.cp2y), ',',
165  COORD(element.x), ',', COORD(element.y));
166  break;
167 
168  case kCGPathElementCloseSubpath: vml.push(VML_ELEMENT_TABLE[type]);
169  break;
170 
171  case kCGPathElementAddArc: var x = element.x,
172  y = element.y,
173  radius = element.radius,
174  clockwise = element.clockwise ? 1 : 0,
175  endAngle = element.endAngle,
176  startAngle = element.startAngle,
177 
178  start = CGPointMake(x + radius * COS(startAngle), y + radius * SIN(startAngle));
179 
180  // If the angle's are equal, then we won't actually draw an arc, but instead
181  // simply move to its start/end to get the proper fill.
182  // We only need this special case for anti-clockwise because start == end is
183  // interpreted as a full circle with anti-clockwise, but empty for clockwise.
184  if (startAngle == endAngle && !clockwise)
185  {
187 
188  continue;
189  }
190 
191  var end = CGPointMake(x + radius * COS(endAngle), y + radius * SIN(endAngle));
192 
193  // Only do the start correction if the angles aren't equal. If they are, then
194  // let the circle be empty.
195  // FIXME: Should this be |star.x - end.x| < 0.125 ?
196  if (clockwise && startAngle != endAngle && CGPointEqualToPoint(start, end))
197  if (start.x >= x)
198  {
199  if (start.y < y)
200  start.x += 0.125;
201  else
202  start.y += 0.125;
203  }
204  else
205  {
206  if (end.y <= y)
207  end.x += 0.125;
208  else
209  end.y += 0.125;
210  }
211 
212  vml.push(VML_ELEMENT_TABLE[type][clockwise],
213  COORD(x - radius), ',', COORD(y - radius), " ",
214  COORD(x + radius), ',', COORD(y + radius), " ",
215  COORD(start.x), ',', COORD(start.y), " ",
216  COORD(end.x), ',', COORD(end.y));
217  break;
218  case kCGPathElementAddArcToPoint: break;
219  }
220 
221  // TODO: Following is broken for curves due to
222  // move to proper paths.
223 
224  // Figure out dimensions so we can do gradient fills
225  // properly
226  /*if(c) {
227  if (min.x == null || c.x < min.x) {
228  min.x = c.x;
229  }
230  if (max.x == null || c.x > max.x) {
231  max.x = c.x;
232  }
233  if (min.y == null || c.y < min.y) {
234  min.y = c.y;
235  }
236  if (max.y == null || c.y > max.y) {
237  max.y = c.y;
238  }
239  }*/
240  }
241 
242  vml.push("\">");
243 
244  if (gState.gradient)
245  vml.push(gState.gradient);
246 
247  else if (fill)
248  {
249  if (gState.fillStyle.charAt(0) === "!")
250  vml.push("<cg_vml_:fill type=\"tile\" src=\"", gState.fillStyle.substring(1), "\" opacity=\"", opacity, "\" />");
251  else // should be a CSS color spec
252  vml.push("<cg_vml_:fill color=\"", gState.fillStyle, "\" opacity=\"", opacity, "\" />");
253  }
254 
255  if (stroke)
256  vml.push( "<cg_vml_:stroke opacity=\"", opacity,
257  "\" joinstyle=\"", VML_LINEJOIN_TABLE[gState.lineJoin],
258  "\" miterlimit=\"", gState.miterLimit,
259  "\" endcap=\"", VML_LINECAP_TABLE[gState.lineCap],
260  "\" weight=\"", gState.lineWidth, "",
261  "px\" color=\"", gState.strokeStyle,"\" />");
262 
263  var shadowColor = gState.shadowColor;
264  //\"", [shadowColor cssString], "\"
265  if (shadowColor)
266  {
267  var shadowOffset = gState.shadowOffset;
268 
269  vml.push("<cg_vml_:shadow on=\"t\" offset=\"",
270  shadowOffset.width, "pt ", shadowOffset.height, "pt\" opacity=\"", [shadowColor alphaComponent], "\" color=black />");
271  }
272 
273  vml.push("</cg_vml_:shape>");
274 
275  if (aContext.buffer != nil)
276  aContext.buffer += vml.join("");
277  else
278  aContext.DOMElement.insertAdjacentHTML("BeforeEnd", vml.join(""));
279 }
280 
281 function to_string(aColor)
282 {
283  return "rgb(" + ROUND(aColor.components[0] * 255) + ", " + ROUND(aColor.components[1] * 255) + ", " + ROUND(255 * aColor.components[2]) + ")";
284 }
285 
286 function CGContextDrawLinearGradient(aContext, aGradient, aStartPoint, anEndPoint, options)
287 {
288  if (!aContext || !aGradient)
289  return;
290 
291  var vml = nil;
292 
293  if (aGradient.vml_gradient)
294  {
295  var stops = [[aGradient.vml_gradient stops] sortedArrayUsingSelector:@selector(comparePosition:)],
296  count = [stops count];
297 
298  vml = ["<cg_vml_:fill type=\"gradient\" method=\"linear sigma\" "];
299  vml.push("angle=\"" + ([aGradient.vml_gradient angle] + 90) + "\" ");
300 
301  vml.push("colors=\"");
302 
303  for (var i = 0; i < count; i++)
304  {
305  vml.push(([stops[i] position] * 100).toFixed(0) + "% ");
306  vml.push([[[stops[i] color] colorForSlideBase:nil] cssString]);
307 
308  if (i < count - 1)
309  vml.push(",");
310  }
311 
312  vml.push("\" />");
313  }
314  else
315  {
316  var colors = aGradient.colors,
317  count = colors.length;
318 
319  vml = ["<cg_vml_:fill type=\"gradient\" "];
320 
321  vml.push("colors=\"");
322 
323  for (var i = 0; i < count; i++)
324  vml.push((aGradient.locations[i] * 100).toFixed(0)+"% " + to_string(colors[i])+(i < count - 1 ? "," : ""));
325 
326  vml.push("\" />");
327  }
328 
329  aContext.gState.gradient = vml.join("");
330 
331  // if (aContext.buffer != nil)
332  // aContext.buffer += vml.join("");
333  // else
334  // aContext.DOMElement.innerHTML = vml.join("");
335 }
var VML_ELEMENT_TABLE
Definition: CGContextVML.j:26
CGPoint position()
Definition: CALayer.j:225
CGPath kCGPathElementMoveToPoint
Definition: CGPath.j:26
function CGContextDrawImage(aContext, aRect, anImage)
Definition: CGContextVML.j:68
var VML_TRUTH_TABLE
Definition: CGContextVML.j:23
function CGContextDrawPath(aContext, aMode)
Definition: CGContextVML.j:119
kCGPathElementAddLineToPoint
Definition: CGPath.j:27
kCGPathFillStroke
Definition: CGContext.j:37
kCGPathElementAddArcToPoint
Definition: CGPath.j:33
float opacity()
Definition: CALayer.j:556
var Z
Definition: CGContextVML.j:63
function CGPathIsEmpty(aPath)
Definition: CGPath.j:502
kCGPathElementAddArc
Definition: CGPath.j:32
kCGPathElementCloseSubpath
Definition: CGPath.j:30
kCGPathStroke
Definition: CGContext.j:36
function CGContextClearRect(aContext, aRect)
Definition: CGContextVML.j:51
#define COORD(aCoordinate)
Definition: CGContextVML.j:66
function CGContextSetFillColor(aContext, aColor)
Definition: CGContextVML.j:41
var Z_2
Definition: CGContextVML.j:64
var VML_LINEJOIN_TABLE
Definition: CGContextVML.j:25
var H
Definition: CGContextVML.j:62
function CGContextDrawLinearGradient(aContext, aGradient, aStartPoint, anEndPoint, options)
Definition: CGContextVML.j:286
var VML_LINECAP_TABLE
Definition: CGContextVML.j:24
kCGPathElementAddCurveToPoint
Definition: CGPath.j:29
FrameUpdater prototype start
function CGBitmapGraphicsContextCreate()
Definition: CGContextVML.j:30
var W
Definition: CGContextVML.j:61
kCGPathElementAddQuadCurveToPoint
Definition: CGPath.j:28
function CGPointApplyAffineTransform(aPoint, aTransform)
kCGPathFill
Definition: CGContext.j:34
function to_string(aColor)
Definition: CGContextVML.j:281