PageRenderTime 208ms CodeModel.GetById 8ms RepoModel.GetById 0ms app.codeStats 1ms

/lib/svgweb/tests/non-licensed/perf/test7/src/org/svgweb/core/SVGNode.as

https://github.com/dionjwa/Hydrax
ActionScript | 1123 lines | 628 code | 84 blank | 411 comment | 164 complexity | 97744adbbd4360b91bd5b929d3769533 MD5 | raw file
  1. /*
  2. Copyright (c) 2009 by contributors:
  3. * James Hight (http://labs.zavoo.com/)
  4. * Richard R. Masters
  5. * Google Inc. (Brad Neuberg -- http://codinginparadise.org)
  6. Licensed under the Apache License, Version 2.0 (the "License");
  7. you may not use this file except in compliance with the License.
  8. You may obtain a copy of the License at
  9. http://www.apache.org/licenses/LICENSE-2.0
  10. Unless required by applicable law or agreed to in writing, software
  11. distributed under the License is distributed on an "AS IS" BASIS,
  12. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. See the License for the specific language governing permissions and
  14. limitations under the License.
  15. */
  16. package org.svgweb.core
  17. {
  18. import org.svgweb.core.SVGViewer;
  19. import org.svgweb.SVGViewerWeb;
  20. import org.svgweb.nodes.*;
  21. import org.svgweb.utils.SVGColors;
  22. import org.svgweb.utils.SVGUnits;
  23. import flash.display.CapsStyle;
  24. import flash.display.DisplayObject;
  25. import flash.display.JointStyle;
  26. import flash.display.LineScaleMode;
  27. import flash.display.Shape;
  28. import flash.display.Sprite;
  29. import flash.events.Event;
  30. import flash.events.EventDispatcher;
  31. import flash.events.MouseEvent;
  32. import flash.geom.Matrix;
  33. import flash.utils.getDefinitionByName;
  34. import flash.utils.getQualifiedClassName;
  35. /** Base node extended by all other SVG Nodes **/
  36. public class SVGNode extends EventDispatcher
  37. {
  38. public static const ATTRIBUTES_NOT_INHERITED:Array = ['id', 'x', 'y', 'width', 'height', 'rotate', 'transform',
  39. 'gradientTransform', 'opacity', 'mask', 'clip-path', 'href', 'target',
  40. 'viewBox', 'preserveAspectRatio'];
  41. public namespace xlink = 'http://www.w3.org/1999/xlink';
  42. public namespace svg = 'http://www.w3.org/2000/svg';
  43. public namespace aaa = 'http://www.w3.org/XML/1998/namespace';
  44. public var svgRoot:SVGSVGNode = null;
  45. public var svgParent:SVGNode = null;
  46. public var svgChildren:Array = new Array();
  47. public var isMask:Boolean = false;
  48. //Used for gradients
  49. public var xMin:Number;
  50. public var xMax:Number;
  51. public var yMin:Number;
  52. public var yMax:Number;
  53. protected var _parsedChildren:Boolean = false;
  54. public var _initialRenderDone:Boolean = false;
  55. protected var _firstX:Boolean = true;
  56. protected var _firstY:Boolean = true;
  57. protected var _xml:XML;
  58. protected var _invalidDisplay:Boolean = false;
  59. protected var _id:String = null;
  60. protected var _graphicsCommands:Array;
  61. protected var _styles:Object;
  62. protected var original:SVGNode;
  63. protected var _isClone:Boolean = false;
  64. protected var _clones:Array = new Array();
  65. protected var animations:Array = new Array();
  66. // cache the matrix to aid any zooming and panning operations
  67. // later
  68. protected var _lastVBMatrix:Matrix;
  69. /**
  70. *
  71. * To handle certain flash quirks, and to support certain SVG features,
  72. * the implementation of one SVG node is split into one to four
  73. * Sprites which perform the functions of transforming, clipping, and drawing.
  74. * Here are the specific functions performed by each sprite:
  75. *
  76. * topSprite:
  77. * * top sprite, used display list handling (parent/child relationships)
  78. * * handles the transform attribute
  79. * * mouse, stage, frame events
  80. * * handles visibility
  81. * * is the parent of the clip-path mask
  82. * * has mask set to 'default' mask (top level bounding box)
  83. *
  84. * clipSprite:
  85. * * has mask set to 'clip-path' mask
  86. *
  87. * drawSprite:
  88. * * handles all graphics access
  89. * * handles x,y,rotate,opacity attributes
  90. * * filters added here
  91. *
  92. * viewBoxSprite:
  93. * * handles viewBox transform
  94. * * parent of SVG children
  95. *
  96. */
  97. public var topSprite:SVGSprite;
  98. public var clipSprite:SVGSprite;
  99. public var drawSprite:SVGSprite;
  100. public var viewBoxSprite:SVGSprite;
  101. /**
  102. * Constructor
  103. *
  104. * @param xml XML object containing the SVG document. @default null
  105. *
  106. * @return void.
  107. */
  108. public function SVGNode(svgRoot:SVGSVGNode, xml:XML = null, original:SVGNode = null):void {
  109. this.svgRoot = svgRoot;
  110. this.xml = xml;
  111. if (original) {
  112. this.original = original;
  113. this._isClone = true;
  114. }
  115. if (topSprite) {
  116. topSprite.addEventListener(Event.ADDED_TO_STAGE, onAddedToStage);
  117. topSprite.addEventListener(Event.REMOVED_FROM_STAGE, onRemovedFromStage);
  118. }
  119. //increment('SVGNode_Constructor', new Date().getTime() - t);
  120. }
  121. public function createSVGSprites():void {
  122. //dbg("createSVGSprites, this="+this.xml.localName());
  123. topSprite = new SVGSprite(this);
  124. // This handle strange gradient bugs with negative transforms
  125. // by separating the transform from the drawing object
  126. /*if ( (xml.@['transform']).toString() != ""
  127. || (xml.@['clip-path']).toString() != "" ) {
  128. clipSprite = new SVGSprite(this);
  129. topSprite.addChild(clipSprite);
  130. }
  131. else {
  132. clipSprite = topSprite;
  133. }*/
  134. // ADDED!!!
  135. clipSprite = topSprite;
  136. // If the object has a gaussian filter, flash will blur the object mask,
  137. // even if the mask is not drawn with a blur. This is not correct rendering.
  138. // So, we use a stub parent object to hold the mask, in order to isolate the
  139. // mask from the filter. A child is created for drawing and the
  140. // filter is applied to the child.
  141. // FIXME: Currently x and y are on the drawSprite. Try to move
  142. // to transform sprite.
  143. /*if ( (xml.@['clip-path']).toString() != ""
  144. || (xml.@['x']).toString() != ""
  145. || (xml.@['y']).toString() != "" ) {
  146. drawSprite = new SVGSprite(this);
  147. clipSprite.addChild(drawSprite);
  148. }
  149. else {
  150. drawSprite = clipSprite;
  151. }*/
  152. // ADDED!!!
  153. drawSprite = topSprite;
  154. // If the object has a viewBox, the resulting transform should only apply to the
  155. // children of the object, so create a child sprite to hold the transform.
  156. // If the object is a text node, we put the TextField on the drawSprite instead of
  157. // the viewBoxSprite because it is simpler all children of viewBoxSprite are SVGSprites.
  158. /*if ( (xml.@['viewBox']).toString() != ""
  159. || (this is SVGSVGNode) || (this is SVGTextNode) ) {
  160. viewBoxSprite = new SVGSprite(this);
  161. drawSprite.addChild(viewBoxSprite);
  162. }
  163. else {
  164. viewBoxSprite = drawSprite;
  165. }*/
  166. // ADDED!!!
  167. viewBoxSprite = drawSprite;
  168. }
  169. /**
  170. * Parse the SVG XML.
  171. * This handles creation of child nodes.
  172. **/
  173. protected function parseChildren():void {
  174. this.dbg("parseChildren, this="+this.xml.localName());
  175. var text:String = '';
  176. for each (var childXML:XML in this._xml.children()) {
  177. if (childXML.nodeKind() == 'element') {
  178. // If we support text values then set them
  179. /*if (this.hasText()) {
  180. if (childXML.localName() == '__text'
  181. && childXML.children().length() > 0) {
  182. // for the SVGViewerWeb we use a nested
  183. // SVGDOMTextNode to store the actual value; this
  184. // class is necessary so that we can do text
  185. // node detection in the browser and have a unique
  186. // GUID per DOM text node
  187. text += childXML.children().toString();
  188. }
  189. }*/
  190. this.dbg("trying to parse node");
  191. var newChildNode:SVGNode = this.parseNode(childXML);
  192. this.dbg("node parsed");
  193. if (!newChildNode) {
  194. continue;
  195. }
  196. this.dbg("adding SVG child");
  197. this.addSVGChild(newChildNode);
  198. this.dbg("svg child added");
  199. }/* else if (childXML.nodeKind() == 'text'
  200. && this.hasText()) {
  201. text = this._xml.text().toString();
  202. } */
  203. }
  204. /*if (this.hasText()) {
  205. this.setText(text);
  206. }*/
  207. }
  208. public function parseNode(childXML:XML):SVGNode {
  209. var childNode:SVGNode = null;
  210. var nodeName:String = childXML.localName();
  211. nodeName = nodeName.toLowerCase();
  212. switch(nodeName) {
  213. /*case "a":
  214. childNode = new SVGANode(this.svgRoot, childXML);
  215. break;
  216. case "animate":
  217. childNode = new SVGAnimateNode(this.svgRoot, childXML);
  218. break;
  219. case "animatemotion":
  220. childNode = new SVGAnimateMotionNode(this.svgRoot, childXML);
  221. break;
  222. case "animatecolor":
  223. childNode = new SVGAnimateColorNode(this.svgRoot, childXML);
  224. break;
  225. case "animatetransform":
  226. childNode = new SVGAnimateTransformNode(this.svgRoot, childXML);
  227. break;
  228. case "audio":
  229. childNode = new SVGAudioNode(this.svgRoot, childXML);
  230. break;
  231. case "circle":
  232. childNode = new SVGCircleNode(this.svgRoot, childXML);
  233. break;
  234. case "clippath":
  235. childNode = new SVGClipPathNode(this.svgRoot, childXML);
  236. break;
  237. case "desc":
  238. childNode = new SVGDescNode(this.svgRoot, childXML);
  239. break;
  240. case "defs":
  241. childNode = new SVGDefsNode(this.svgRoot, childXML);
  242. break;
  243. case "ellipse":
  244. childNode = new SVGEllipseNode(this.svgRoot, childXML);
  245. break;
  246. case "filter":
  247. childNode = new SVGFilterNode(this.svgRoot, childXML);
  248. break;
  249. case "font":
  250. childNode = new SVGFontNode(this.svgRoot, childXML);
  251. break;
  252. case "font-face":
  253. childNode = new SVGFontFaceNode(this.svgRoot, childXML);
  254. break;*/
  255. case "g":
  256. childNode = new SVGGroupNode(this.svgRoot, childXML);
  257. break;
  258. /*case "glyph":
  259. childNode = new SVGGlyphNode(this.svgRoot, childXML);
  260. break;
  261. case "image":
  262. childNode = new SVGImageNode(this.svgRoot, childXML);
  263. break;
  264. case "line":
  265. childNode = new SVGLineNode(this.svgRoot, childXML);
  266. break;
  267. case "lineargradient":
  268. childNode = new SVGLinearGradient(this.svgRoot, childXML);
  269. break;
  270. case "mask":
  271. childNode = new SVGMaskNode(this.svgRoot, childXML);
  272. break;
  273. case "metadata":
  274. childNode = new SVGMetadataNode(this.svgRoot, childXML);
  275. break;
  276. case "missing-glyph":
  277. childNode = new SVGMissingGlyphNode(this.svgRoot, childXML);
  278. break;
  279. case "pattern":
  280. childNode = new SVGPatternNode(this.svgRoot, childXML);
  281. break;
  282. case "polygon":
  283. childNode = new SVGPolygonNode(this.svgRoot, childXML);
  284. break;
  285. case "polyline":
  286. childNode = new SVGPolylineNode(this.svgRoot, childXML);
  287. break;*/
  288. case "path":
  289. childNode = new SVGPathNode(this.svgRoot, childXML);
  290. break;
  291. /*case "radialgradient":
  292. childNode = new SVGRadialGradient(this.svgRoot, childXML);
  293. break;
  294. case "rect":
  295. childNode = new SVGRectNode(this.svgRoot, childXML);
  296. break;
  297. case "script":
  298. childNode = new SVGScriptNode(this.svgRoot, childXML);
  299. break;
  300. case "set":
  301. childNode = new SVGSetNode(this.svgRoot, childXML);
  302. break;
  303. case "stop":
  304. childNode = new SVGStopNode(this.svgRoot, childXML);
  305. break;*/
  306. case "svg":
  307. childNode = new SVGSVGNode(this.svgRoot, childXML, null, this.svgRoot.viewer);
  308. break;
  309. /*case "symbol":
  310. childNode = new SVGSymbolNode(this.svgRoot, childXML);
  311. break;
  312. case "text":
  313. childNode = new SVGTextNode(this.svgRoot, childXML);
  314. break;
  315. case "title":
  316. childNode = new SVGTitleNode(this.svgRoot, childXML);
  317. break;
  318. case "tspan":
  319. childNode = new SVGTspanNode(this.svgRoot, childXML);
  320. break;
  321. case "use":
  322. childNode = new SVGUseNode(this.svgRoot, childXML);
  323. break;
  324. case "video":
  325. childNode = new SVGVideoNode(this.svgRoot, childXML);
  326. break;*/
  327. //case "__text":
  328. /** These are fake text nodes necessary for integration
  329. with the browser through JavaScript. */
  330. /* childNode = new SVGDOMTextNode(this.svgRoot, childXML);
  331. break;
  332. case "null":
  333. break;*/
  334. default:
  335. trace("Unknown Element: " + nodeName);
  336. childNode = new SVGUnknownNode(this.svgRoot, childXML);
  337. break;
  338. }
  339. return childNode;
  340. }
  341. /**
  342. * Called when a node is created after page load with XML content
  343. * added as a child. Forces a parse.
  344. */
  345. public function forceParse():void {
  346. if (this._xml != null && !this._parsedChildren) {
  347. this.parseChildren();
  348. for (var i:uint = 0; i < this.svgChildren.length; i++) {
  349. var child:SVGNode = this.svgChildren[i];
  350. child.forceParse();
  351. }
  352. this._parsedChildren = true;
  353. }
  354. }
  355. /**
  356. * Triggers on ENTER_FRAME event
  357. * Redraws node graphics if _invalidDisplay == true
  358. **/
  359. protected function drawNode(event:Event = null):void {
  360. if ( this.topSprite.parent != null && this._invalidDisplay) {
  361. this.dbg("drawNode, this="+this.xml.localName());
  362. // are we in the middle of a suspendRedraw operation?
  363. if (this.svgRoot.viewer && this.svgRoot.viewer.isSuspended) {
  364. return;
  365. }
  366. //var t:int;
  367. //var pieceTime:int;
  368. this._invalidDisplay = false;
  369. if (this._xml != null) {
  370. this.dbg("1");
  371. //t = new Date().getTime();
  372. //pieceTime = new Date().getTime();
  373. //drawSprite.graphics.clear();
  374. //increment('drawNode_graphics.clear', new Date().getTime() - pieceTime);
  375. this.dbg("2");
  376. //pieceTime = new Date().getTime();
  377. if (!this._parsedChildren) {
  378. this.parseChildren();
  379. this._parsedChildren = true;
  380. }
  381. //increment('drawNode_parseChildren', new Date().getTime() - pieceTime);
  382. this.dbg("3");
  383. // sets x, y, rotate, and opacity
  384. //pieceTime = new Date().getTime();
  385. //this.setAttributes();
  386. //increment('drawNode_setAttributes', new Date().getTime() - pieceTime);
  387. this.dbg("4");
  388. //pieceTime = new Date().getTime();
  389. /*if (this.getStyleOrAttr('visibility') == 'hidden') {
  390. // SVG spec says visibility='hidden' should fully draw
  391. // the shape with full stroke widths, etc.,
  392. // just make it invisible. It also states that
  393. // all children should be invisible _unless_ they
  394. // explicitly have a visibility set to 'visible'.
  395. this.setVisibility('hidden');
  396. } else {
  397. //increment('drawNode_getStyleOrAttr(visibility)', new Date().getTime() - pieceTime);
  398. //pieceTime = new Date().getTime();
  399. this.setVisibility('visible');
  400. //increment('drawNode_setVisibility', new Date().getTime() - pieceTime);
  401. }*/
  402. this.dbg("5");
  403. //pieceTime = new Date().getTime();
  404. /*if (this.getStyleOrAttr('display') == 'none') {
  405. this.topSprite.visible = false;
  406. }
  407. else {*/
  408. this.dbg("6");
  409. //increment('drawNode_getStyleOrAttr(display)', new Date().getTime() - pieceTime);
  410. //this.topSprite.visible = true;
  411. this.dbg("6.1");
  412. //pieceTime = new Date().getTime();
  413. // <svg> nodes get an implicit mask of their height and width
  414. /*if (this is SVGSVGNode) {
  415. this.dbg("6.2");
  416. this.applyDefaultMask();
  417. this.dbg("6.3");
  418. }*/
  419. //increment('drawNode_applyDefaultMask', new Date().getTime() - pieceTime);
  420. this.dbg("7");
  421. //pieceTime = new Date().getTime();
  422. this.generateGraphicsCommands();
  423. this.dbg("8");
  424. //increment('drawNode_generateGraphicsCommands', new Date().getTime() - pieceTime);
  425. //pieceTime = new Date().getTime();
  426. //this.transformNode();
  427. this.dbg("9");
  428. //increment('drawNode_transformNode', new Date().getTime() - pieceTime);
  429. //pieceTime = new Date().getTime();
  430. this.draw();
  431. //increment('drawNode_draw', new Date().getTime() - pieceTime);
  432. this.dbg("10");
  433. //pieceTime = new Date().getTime();
  434. //this.applyClipPathMask();
  435. this.dbg("11");
  436. //increment('drawNode_applyClipPathMask', new Date().getTime() - pieceTime);
  437. //pieceTime = new Date().getTime();
  438. this.applyViewBox();
  439. this.dbg("12");
  440. //increment('drawNode_applyViewBox', new Date().getTime() - pieceTime);
  441. //pieceTime = new Date().getTime();
  442. //this.setupFilters();
  443. this.dbg("13");
  444. //increment('drawNode_setupFilters', new Date().getTime() - pieceTime);
  445. //}
  446. }
  447. //pieceTime = new Date().getTime();
  448. topSprite.removeEventListener(Event.ENTER_FRAME, drawNode);
  449. //increment('drawNode_removeEventListener', new Date().getTime() - pieceTime);
  450. //pieceTime = new Date().getTime();
  451. if (this.xml.@id) {
  452. //increment('drawNode_xml.@id', new Date().getTime() - pieceTime);
  453. //pieceTime = new Date().getTime();
  454. this.svgRoot.invalidateReferers(this.xml.@id);
  455. //increment('drawNode_invalidateReferers', new Date().getTime() - pieceTime);
  456. }
  457. //pieceTime = new Date().getTime();
  458. if (getPatternAncestor() != null) {
  459. //increment('drawNode_getPatternAncestor', new Date().getTime() - pieceTime);
  460. //pieceTime = new Date().getTime();
  461. this.svgRoot.invalidateReferers(getPatternAncestor().id);
  462. //increment('drawNode_invalidateReferers', new Date().getTime() - pieceTime);
  463. }
  464. //increment('drawNodeTOTAL', new Date().getTime() - t);
  465. }
  466. if (!this._initialRenderDone && this.topSprite.parent) {
  467. this.attachEventListeners();
  468. this._initialRenderDone = true;
  469. this.svgRoot.renderFinished();
  470. }
  471. }
  472. protected function setAttributes():void {
  473. this.loadAttribute('x');
  474. this.loadAttribute('y');
  475. this.loadAttribute('rotate','rotation');
  476. this.loadAttribute('opacity', 'alpha', true);
  477. if (this.getStyleOrAttr('pointer-events') == 'none') {
  478. topSprite.mouseEnabled = false;
  479. topSprite.mouseChildren = false;
  480. }
  481. else {
  482. topSprite.mouseEnabled = true;
  483. topSprite.mouseChildren = true;
  484. }
  485. }
  486. /**
  487. * Load an XML attribute into the current node
  488. *
  489. * @param name Name of the XML attribute to load
  490. * @param field Name of the node field to set. If null, the name attribute will be used as the field attribute.
  491. * @param applyStyle Boolean Optional parameter that controls whether we
  492. * will look in this node's style value if the given name is not in
  493. * the node's attribute list. Defaults to false.
  494. **/
  495. protected function loadAttribute(name:String, field:String = null,
  496. applyStyle:Boolean = false):void {
  497. if (field == null) {
  498. field = name;
  499. }
  500. var tmp:String = this.getStyleOrAttr(name);
  501. if (tmp != null) {
  502. switch (name) {
  503. case 'x':
  504. drawSprite[field] = SVGColors.cleanNumber2(tmp, this.getWidth());
  505. break;
  506. case 'y':
  507. drawSprite[field] = SVGColors.cleanNumber2(tmp, this.getHeight());
  508. break;
  509. case 'rotate':
  510. drawSprite[field] = SVGColors.cleanNumber(tmp);
  511. break;
  512. case 'opacity':
  513. drawSprite[field] = SVGColors.cleanNumber(tmp);
  514. break;
  515. }
  516. }
  517. }
  518. /** Sets this node and all its children to the given visibility value.
  519. If a child has an explicit visibility setting then that is retained
  520. independent of what we set here.
  521. @param visible - If 'visible', sets this node to be visible.
  522. The value 'hidden' will hide it.
  523. @param recursive - Internal variable. Should not set. */
  524. protected function setVisibility(visible:String,
  525. recursive:Boolean = false):void {
  526. // FIXME: Turn this into an iterative rather than a recursive
  527. // method
  528. //this.dbg('setVisibility, this='+this.xml.localName()+', visible='+visible+', recursive='+recursive);
  529. // ignore if we have our own visibility value and we are a recursive
  530. // call
  531. if (drawSprite && (this.getStyleOrAttr('visibility', null, false) == null
  532. || recursive == false) ) {
  533. if (visible == 'visible') {
  534. drawSprite.alpha = SVGColors.cleanNumber(this.getStyleOrAttr('opacity'));
  535. } else {
  536. drawSprite.alpha = 0;
  537. }
  538. }
  539. // set on all our children
  540. var child:SVGNode;
  541. for (var i:uint = 0; i < this.svgChildren.length; i++) {
  542. child = this.svgChildren[i];
  543. child.setVisibility(visible, true);
  544. }
  545. }
  546. // <svg> and <image> nodes get an implicit mask of their height and width
  547. public function applyDefaultMask():void {
  548. if ( (this is SVGImageNode &&
  549. this.getAttribute('width') != null &&
  550. this.getAttribute('height') != null )
  551. || this is SVGSVGNode ) {
  552. if (topSprite.mask == null) {
  553. var myMask:Shape = new Shape();
  554. topSprite.parent.addChild(myMask);
  555. topSprite.mask = myMask;
  556. }
  557. if (topSprite.mask is Shape) {
  558. Shape(topSprite.mask).graphics.clear();
  559. Shape(topSprite.mask).graphics.beginFill(0x000000);
  560. Shape(topSprite.mask).transform.matrix=topSprite.transform.matrix.clone();
  561. drawSprite.graphics.clear();
  562. drawSprite.graphics.beginFill(0x000000, 0);
  563. var canvasWidth:Number;
  564. var canvasHeight:Number;
  565. if (this.topSprite.parent is SVGViewerWeb) {
  566. var clipMode:String= SVGViewerWeb(this.topSprite.parent).getClipMode();
  567. //this.dbg("<svg> clip mode: "+ clipMode);
  568. switch (clipMode) {
  569. case 'height':
  570. canvasWidth = SVGViewer(this.topSprite.parent).getWidth();
  571. canvasHeight = this.getHeight();
  572. break;
  573. case 'neither':
  574. canvasWidth = SVGViewer(this.topSprite.parent).getWidth();
  575. canvasHeight = SVGViewer(this.topSprite.parent).getHeight();
  576. break;
  577. case 'width':
  578. canvasWidth = this.getWidth();
  579. canvasHeight = SVGViewer(this.topSprite.parent).getHeight();
  580. break;
  581. default:
  582. this.dbg("invalid <svg> clip mode: "+ clipMode);
  583. case 'both':
  584. canvasWidth = this.getWidth();
  585. canvasHeight = this.getHeight();
  586. break;
  587. }
  588. }
  589. else {
  590. canvasWidth = this.getWidth();
  591. canvasHeight = this.getHeight();
  592. }
  593. Shape(topSprite.mask).graphics.drawRect(drawSprite.x, drawSprite.y,
  594. canvasWidth, canvasHeight);
  595. Shape(topSprite.mask).graphics.endFill();
  596. // Draw an invisible rect to receive mouse events.
  597. drawSprite.graphics.drawRect(drawSprite.x, drawSprite.y,
  598. canvasWidth, canvasHeight);
  599. drawSprite.graphics.endFill();
  600. }
  601. }
  602. }
  603. /**
  604. * Called to generate AS3 graphics commands from the SVG instructions
  605. **/
  606. protected function generateGraphicsCommands():void {
  607. this._graphicsCommands = new Array();
  608. }
  609. /**
  610. * Perform transformations defined by the transform attribute
  611. **/
  612. public function transformNode():void {
  613. topSprite.transform.matrix = new Matrix();
  614. this.loadAttribute('x');
  615. this.loadAttribute('y');
  616. // Tspan x,y replaces the parent text x,y instead of
  617. // offsetting the parent like every other node.
  618. // However, if the x or y is not specified, then use the parent.
  619. if (this is SVGTspanNode) {
  620. if (this.getAttribute('x',null) != null) {
  621. drawSprite.x = drawSprite.x - this.svgParent.getAttribute('x',0);
  622. }
  623. if (this.getAttribute('y',null) != null) {
  624. drawSprite.y = drawSprite.y - this.svgParent.getAttribute('y',0);
  625. }
  626. }
  627. this.loadAttribute('rotate', 'rotation');
  628. // Apply transform attribute
  629. var trans:String = this.getAttribute('transform');
  630. if (trans) {
  631. topSprite.transform.matrix = this.parseTransform(trans,
  632. topSprite.transform.matrix.clone());
  633. }
  634. var animResult:Array = this.getAllAnimsTransform();
  635. var animMatrix:Matrix = animResult[0];
  636. var isAdditive:Boolean = animResult[1];
  637. if (animMatrix != null) {
  638. // See Issue 311: Tranforms must be applied in correct order
  639. // and must account for additive attribute.
  640. if (isAdditive) {
  641. var newMatrix:Matrix = topSprite.transform.matrix.clone();
  642. animMatrix.concat(newMatrix);
  643. }
  644. topSprite.transform.matrix = animMatrix;
  645. }
  646. }
  647. public function parseTransform(trans:String, baseMatrix:Matrix = null):Matrix {
  648. if (!baseMatrix) {
  649. baseMatrix = new Matrix();
  650. }
  651. if (trans != null) {
  652. var transArray:Array = trans.match(/\S+\(.*?\)/sg);
  653. transArray.reverse();
  654. for each(var tran:String in transArray) {
  655. var tranArray:Array = tran.split('(',2);
  656. if (tranArray.length == 2)
  657. {
  658. var command:String = String(tranArray[0]);
  659. var args:String = String(tranArray[1]);
  660. args = args.replace(')','');
  661. args = args.replace(/\s+/sg,","); //Replace spaces with a comma
  662. args = args.replace(/,{2,}/sg,","); // Remove any extra commas
  663. args = args.replace(/^,/, ''); //Remove leading comma
  664. args = args.replace(/,$/, ''); //Remove trailing comma
  665. var argsArray:Array = args.split(',');
  666. var nodeMatrix:Matrix = new Matrix();
  667. switch (command) {
  668. case "matrix":
  669. if (argsArray.length == 6) {
  670. nodeMatrix.a = argsArray[0];
  671. nodeMatrix.b = argsArray[1];
  672. nodeMatrix.c = argsArray[2];
  673. nodeMatrix.d = argsArray[3];
  674. nodeMatrix.tx = argsArray[4];
  675. nodeMatrix.ty = argsArray[5];
  676. }
  677. break;
  678. case "translate":
  679. if (argsArray.length == 1) {
  680. nodeMatrix.tx = argsArray[0];
  681. }
  682. else if (argsArray.length == 2) {
  683. nodeMatrix.tx = argsArray[0];
  684. nodeMatrix.ty = argsArray[1];
  685. }
  686. break;
  687. case "scale":
  688. if (argsArray.length == 1) {
  689. nodeMatrix.a = argsArray[0];
  690. nodeMatrix.d = argsArray[0];
  691. }
  692. else if (argsArray.length == 2) {
  693. nodeMatrix.a = argsArray[0];
  694. nodeMatrix.d = argsArray[1];
  695. }
  696. break;
  697. case "skewX":
  698. nodeMatrix.c = Math.tan(argsArray[0] * Math.PI / 180.0);
  699. break;
  700. case "skewY":
  701. nodeMatrix.b = Math.tan(argsArray[0] * Math.PI / 180.0);
  702. break;
  703. case "rotate":
  704. if (argsArray.length == 3) {
  705. nodeMatrix.translate(-argsArray[1], -argsArray[2]);
  706. nodeMatrix.rotate(Number(argsArray[0])* Math.PI / 180.0);
  707. nodeMatrix.translate(argsArray[1], argsArray[2]);
  708. }
  709. else {
  710. nodeMatrix.rotate(Number(argsArray[0])* Math.PI / 180.0);
  711. }
  712. break;
  713. default:
  714. //this.dbg('Unknown Transformation: ' + command);
  715. }
  716. baseMatrix.concat(nodeMatrix);
  717. }
  718. }
  719. }
  720. return baseMatrix;
  721. }
  722. protected function draw():void {
  723. var firstX:Number = 0;
  724. var firstY:Number = 0;
  725. for each (var command:Array in this._graphicsCommands) {
  726. switch(command[0]) {
  727. case "SF":
  728. this.nodeBeginFill();
  729. //drawSprite.graphics.lineStyle(0, 0, 0, false, LineScaleMode.NORMAL, "none");
  730. //line_width=0, line_color=0, line_alpha=0, linescalemode=NORMAL,, capsStyle=none, jointStyle=miter, miterLimit=4
  731. //drawSprite.graphics.lineStyle(1, 0x000000);
  732. //drawSprite.graphics.beginFill(0x000000);
  733. break;
  734. case "EF":
  735. //drawSprite.graphics.lineStyle(0, 0, 0);
  736. //drawSprite.graphics.endFill();
  737. this.nodeEndFill();
  738. break;
  739. /*case "SF":
  740. this.nodeBeginFill();
  741. break;*/
  742. /*case "EF":
  743. drawSprite.graphics.lineStyle(0, 0, 0);
  744. this.nodeEndFill();
  745. break;*/
  746. case "M":
  747. drawSprite.graphics.lineStyle(0, 0, 0);
  748. drawSprite.graphics.moveTo(command[1], command[2]);
  749. firstX = command[1];
  750. firstY = command[2];
  751. this.nodeBeginStroke();
  752. break;
  753. case "L":
  754. drawSprite.graphics.lineTo(command[1], command[2]);
  755. break;
  756. case "C":
  757. //dbg("curveTo: " + command[1] + ", " + command[2] + ", " + command[3] + ", " + command[4]);
  758. /*if (isNaN(command[1]) || isNaN(command[2])
  759. || isNaN(command[3]) || isNaN(command[4])) {
  760. dbg("Invalid curve path data: " + command[1]
  761. + ", " + command[2] + ", " + command[3]
  762. + ", " + command[4]);
  763. continue;
  764. }*/
  765. drawSprite.graphics.curveTo(command[1], command[2],command[3], command[4]);
  766. // DELETE ME!!!
  767. //drawSprite.graphics.lineTo(command[3], command[4]);
  768. break;
  769. case "Z":
  770. drawSprite.graphics.lineTo(firstX, firstY);
  771. break;
  772. case "LINE":
  773. this.nodeBeginFill();
  774. drawSprite.graphics.moveTo(command[1], command[2]);
  775. drawSprite.graphics.lineTo(command[3], command[4]);
  776. this.nodeEndFill();
  777. break;
  778. case "RECT":
  779. this.nodeBeginFill();
  780. if (command.length == 5) {
  781. drawSprite.graphics.drawRect(command[1], command[2],command[3], command[4]);
  782. }
  783. else {
  784. drawSprite.graphics.drawRoundRect(command[1], command[2],command[3], command[4], command[5], command[6]);
  785. }
  786. this.nodeEndFill();
  787. break;
  788. case "CIRCLE":
  789. this.nodeBeginFill();
  790. drawSprite.graphics.drawCircle(command[1], command[2], command[3]);
  791. this.nodeEndFill();
  792. break;
  793. case "ELLIPSE":
  794. this.nodeBeginFill();
  795. drawSprite.graphics.drawEllipse(command[1], command[2],command[3], command[4]);
  796. this.nodeEndFill();
  797. break;
  798. }
  799. }
  800. }
  801. /**
  802. * Called at the start of drawing an SVG element.
  803. * Sets fill and stroke styles
  804. **/
  805. protected function nodeBeginFill():void {
  806. //Fill
  807. var color_and_alpha:Array = [0, 0];
  808. var color_core:Number = 0;
  809. var color_alpha:Number = 0;
  810. var fill_alpha:Number = 0;
  811. var fill:String = this.getStyleOrAttr('fill');
  812. if ( (fill != 'none') && (fill != null) && (this.getStyleOrAttr('visibility') != 'hidden') ) {
  813. var matches:Array = fill.match(/url\(#([^\)]+)\)/si);
  814. if (matches != null && matches.length > 0) {
  815. var fillName:String = matches[1];
  816. this.svgRoot.addReference(this, fillName);
  817. var fillNode:SVGNode = this.svgRoot.getNode(fillName);
  818. if (!fillNode) {
  819. // this happens normally
  820. //this.dbg("Gradient " + fillName + " not (yet?) available for " + this.xml.@id);
  821. }
  822. if (fillNode is SVGGradient) {
  823. SVGGradient(fillNode).beginGradientFill(this);
  824. }
  825. else if (fillNode is SVGPatternNode) {
  826. SVGPatternNode(fillNode).beginPatternFill(this);
  827. }
  828. }
  829. else {
  830. if (fill == 'currentColor') {
  831. fill = this.getStyleOrAttr('color');
  832. }
  833. color_and_alpha = SVGColors.getColorAndAlpha(fill);
  834. color_core = color_and_alpha[0];
  835. color_alpha = color_and_alpha[1];
  836. fill_alpha = SVGColors.cleanNumber( this.getStyleOrAttr('fill-opacity') ) * color_alpha;
  837. drawSprite.graphics.beginFill(color_core, fill_alpha);
  838. }
  839. }
  840. this.dbg("calling nodeBeginStroke, the one that matters");
  841. nodeBeginStroke();
  842. }
  843. protected function nodeBeginStroke():void {
  844. //Stroke
  845. var line_color:Number;
  846. var line_alpha:Number;
  847. var line_width:Number;
  848. var stroke:String = this.getStyleOrAttr('stroke');
  849. if ( (stroke == 'none') || (stroke == '') || (this.getStyleOrAttr('visibility') == 'hidden') ) {
  850. line_alpha = 0;
  851. line_color = 0;
  852. line_width = 0;
  853. }
  854. else {
  855. if (stroke == 'currentColor') {
  856. stroke = this.getStyleOrAttr('color');
  857. }
  858. line_color = SVGColors.cleanNumber(SVGColors.getColor(stroke));
  859. line_alpha = SVGColors.cleanNumber(this.getStyleOrAttr('stroke-opacity'));
  860. line_width = SVGColors.cleanNumber(this.getStyleOrAttr('stroke-width'));
  861. }
  862. var capsStyle:String = this.getStyleOrAttr('stroke-linecap');
  863. if (capsStyle == 'round'){
  864. capsStyle = CapsStyle.ROUND;
  865. }
  866. else if (capsStyle == 'square'){
  867. capsStyle = CapsStyle.SQUARE;
  868. }
  869. else {
  870. capsStyle = CapsStyle.NONE;
  871. }
  872. var jointStyle:String = this.getStyleOrAttr('stroke-linejoin');
  873. if (jointStyle == 'round'){
  874. jointStyle = JointStyle.ROUND;
  875. }
  876. else if (jointStyle == 'bevel'){
  877. jointStyle = JointStyle.BEVEL;
  878. }
  879. else {
  880. jointStyle = JointStyle.MITER;
  881. }
  882. var miterLimit:String = this.getStyleOrAttr('stroke-miterlimit');
  883. if (miterLimit == null) {
  884. miterLimit = '4';
  885. }
  886. //this.dbg("line_width="+line_width+", line_color="+line_color+", line_alpha="+line_alpha+", linescalemode=NORMAL,"
  887. // + ", capsStyle="+capsStyle+", jointStyle="+jointStyle+", miterLimit="+miterLimit);
  888. // jointStyle="miter" is VERY slow!!! HANDLE!!!
  889. drawSprite.graphics.lineStyle(line_width, line_color, line_alpha, false, LineScaleMode.NORMAL,
  890. capsStyle); /*,*jointStyle, SVGColors.cleanNumber(miterLimit));*/
  891. if ( (stroke != 'none') && (stroke != '') && (this.getStyleOrAttr('visibility') != 'hidden') ) {
  892. var strokeMatches:Array = stroke.match(/url\(#([^\)]+)\)/si);
  893. if (strokeMatches != null && strokeMatches.length > 0) {
  894. var strokeName:String = strokeMatches[1];
  895. this.svgRoot.addReference(this, strokeName);
  896. var strokeNode:SVGNode = this.svgRoot.getNode(strokeName);
  897. if (!strokeNode) {
  898. // this happens normally
  899. //this.dbg("stroke gradient " + strokeName + " not (yet?) available for " + this.xml.@id);
  900. }
  901. if (strokeNode is SVGGradient) {
  902. SVGGradient(strokeNode).lineGradientStyle(this, line_alpha);
  903. }
  904. }
  905. }
  906. }
  907. /**
  908. * Called at the end of drawing an SVG element
  909. **/
  910. protected function nodeEndFill():void {
  911. drawSprite.graphics.endFill();
  912. }
  913. /**
  914. * Check value of x against _minX and _maxX,
  915. * Update values when appropriate
  916. **/
  917. protected function setXMinMax(value:Number):void {
  918. if (_firstX) {
  919. _firstX = false;
  920. this.xMax = value;
  921. this.xMin = value;
  922. return;
  923. }
  924. if (value < this.xMin) {
  925. this.xMin = value;
  926. }
  927. if (value > this.xMax) {
  928. this.xMax = value;
  929. }
  930. }
  931. /**
  932. * Check value of y against _minY and _maxY,
  933. * Update values when appropriate
  934. **/
  935. protected function setYMinMax(value:Number):void {
  936. if (_firstY) {
  937. _firstY = false;
  938. this.yMax = value;
  939. this.yMin = value;
  940. return;
  941. }
  942. if (value < this.yMin) {
  943. this.yMin = value;
  944. }
  945. if (value > this.yMax) {
  946. this.yMax = value;
  947. }
  948. }
  949. public function applyViewBox():void {
  950. // Apply viewbox transform
  951. var viewBox:String = this.getAttribute('viewBox');
  952. var preserveAspectRatio:String = this.getAttribute('preserveAspectRatio');
  953. if ( (viewBox != null) || (preserveAspectRatio != null) ) {
  954. var newMatrix:Matrix = new Matrix();
  955. if (preserveAspectRatio == null) {
  956. preserveAspectRatio = 'xMidYMid meet';
  957. }
  958. /**
  959. * Canvas, the viewport
  960. **/
  961. var canvasWidth:Number;
  962. var canvasHeight:Number;
  963. if (topSprite.parent is SVGViewerWeb) {
  964. canvasWidth = SVGViewerWeb(topSprite.parent).getWidth();
  965. canvasHeight = SVGViewerWeb(topSprite.parent).getHeight();
  966. }
  967. else {
  968. canvasWidth = this.getWidth();
  969. canvasHeight = this.getHeight();
  970. }
  971. /**
  972. * Viewbox
  973. **/
  974. var viewX:Number;
  975. var viewY:Number;
  976. var viewWidth:Number;
  977. var viewHeight:Number;
  978. if (viewBox != null) {
  979. viewBox = viewBox.replace(/,/sg," "); //Replace commas with spaces
  980. var points:Array = viewBox.split(/\s+/); //Split by white space
  981. viewX = SVGColors.cleanNumber(points[0]);
  982. viewY = SVGColors.cleanNumber(points[1]);
  983. viewWidth = SVGColors.cleanNumber(points[2]);
  984. viewHeight = SVGColors.cleanNumber(points[3]);
  985. }
  986. else {
  987. viewX = 0;
  988. viewY = 0;
  989. viewWidth = canvasWidth;
  990. viewHeight = canvasHeight;
  991. }
  992. var oldAspectRes:Number = viewWidth / viewHeight;
  993. var newAspectRes:Number = canvasWidth / canvasHeight;
  994. var cropWidth:Number;
  995. var cropHeight:Number;
  996. var alignMode:String = preserveAspectRatio.substr(0,8);
  997. var meetOrSlice:String = 'meet';
  998. if (preserveAspectRatio.indexOf('slice') != -1) {
  999. meetOrSlice = 'slice';
  1000. }
  1001. /**
  1002. * Handle Scaling
  1003. **/
  1004. if (alignMode == 'none') {
  1005. // stretch to fit viewport width and height
  1006. cropWidth = canvasWidth;
  1007. cropHeight = canvasHeight;
  1008. }
  1009. else {
  1010. if (meetOrSlice == 'meet') {
  1011. // shrink to fit inside viewport
  1012. if (newAspectRes > oldAspectRes) {
  1013. cropWidth = canvasHeight * oldAspectRes;
  1014. cropHeight = canvasHeight;
  1015. }
  1016. else {
  1017. cropWidth = canvasWidth;
  1018. cropHeight = canvasWidth / oldAspectRes;
  1019. }
  1020. }
  1021. else {
  1022. // meetOrSlice == 'slice'
  1023. // Expand to cover viewport.
  1024. if (newAspectRes > oldAspectRes) {
  1025. cropWidth = canvasWidth;
  1026. cropHeight = canvasWidth / oldAspectRes;
  1027. }
  1028. else {
  1029. cropWidth = canvasHeight * oldAspectRes;
  1030. cropHeight = canvasHeight;
  1031. }
  1032. }
  1033. }
  1034. var scale