PageRenderTime 61ms CodeModel.GetById 29ms RepoModel.GetById 0ms app.codeStats 0ms

/src/as/com/threerings/util/DisplayUtils.as

http://libdamago.googlecode.com/
ActionScript | 529 lines | 413 code | 68 blank | 48 comment | 119 complexity | d03c5b05f14a0475a7ebbc05eb0022d2 MD5 | raw file
Possible License(s): Apache-2.0
  1. //
  2. // $Id: DisplayUtils.as 335 2010-06-07 20:09:10Z dionjw $
  3. package com.threerings.util {
  4. import flash.display.Bitmap;
  5. import flash.display.BitmapData;
  6. import flash.display.DisplayObject;
  7. import flash.display.DisplayObjectContainer;
  8. import flash.display.Loader;
  9. import flash.display.Sprite;
  10. import flash.events.Event;
  11. import flash.events.IOErrorEvent;
  12. import flash.geom.Matrix;
  13. import flash.geom.Point;
  14. import flash.geom.Rectangle;
  15. import flash.net.URLRequest;
  16. import flash.system.LoaderContext;
  17. import flash.text.TextField;
  18. import flash.text.TextFieldAutoSize;
  19. import com.threerings.display.DisplayUtil;
  20. import com.threerings.text.TextFieldUtil;
  21. import com.threerings.util.F;
  22. public class DisplayUtils
  23. {
  24. public static const BOTTOM_TO_TOP :int = 3;
  25. public static const LEFT_TO_RIGHT :int = 0;
  26. public static const RIGHT_TO_LEFT :int = 1;
  27. public static const TOP_TO_BOTTOM :int = 2;
  28. /**
  29. * Variation of com.threerings.display.DisplayUtil.applyToHierarchy.
  30. *
  31. * Instead of stopping completely if the callback returns true, only
  32. * stop delving into the display hierarchy, but keep applying to siblings.
  33. *
  34. * Call the specified function for the display object and all descendants.
  35. *
  36. * This is nearly exactly like mx.utils.DisplayUtil.walkDisplayObjects,
  37. * except this method copes with security errors when examining a child.
  38. *
  39. * @param callbackFunction Signature:
  40. * function (disp :DisplayObject) :void
  41. * or
  42. * function (disp :DisplayObject) :Boolean
  43. *
  44. * If you return a Boolean, you may return <code>true</code> to indicate that you've
  45. * found what you were looking for, and halt iteration.
  46. *
  47. * @return true if iteration was halted by callbackFunction returning true
  48. */
  49. public static function applyToHierarchy (disp :DisplayObject,
  50. callbackFunction :Function) :Boolean
  51. {
  52. // halt iteration if callbackFunction returns true
  53. if (Boolean(callbackFunction(disp))) {
  54. return true;
  55. }
  56. if (disp is DisplayObjectContainer) {
  57. var container :DisplayObjectContainer = disp as DisplayObjectContainer;
  58. var nn :int = container.numChildren;
  59. for (var ii :int = 0; ii < nn; ii++) {
  60. try {
  61. disp = container.getChildAt(ii);
  62. } catch (err :SecurityError) {
  63. continue;
  64. }
  65. // and then we apply outside of the try/catch block so that
  66. // we don't hide errors thrown by the callbackFunction.
  67. applyToHierarchy(disp, callbackFunction);
  68. }
  69. }
  70. return false;
  71. }
  72. public static function boundsUnion (displayObjects :Array, relativeTo :DisplayObject =
  73. null) :Rectangle
  74. {
  75. var bounds :Rectangle;
  76. for each (var disp :DisplayObject in displayObjects) {
  77. var dispBounds :Rectangle =
  78. disp.getBounds(relativeTo == null ? disp.stage : relativeTo);
  79. if (bounds != null) {
  80. bounds = bounds.union(dispBounds);
  81. } else {
  82. bounds = dispBounds
  83. }
  84. }
  85. return bounds;
  86. }
  87. public static function centerOn (d :DisplayObject, x :int = 0, y :int = 0) :DisplayObject
  88. {
  89. var bounds :Rectangle;
  90. if (d.parent != null) {
  91. bounds = d.getBounds(d.parent != null ? d.parent : d);
  92. var boundsCenterX :int = bounds.left + bounds.width / 2;
  93. var xDiff :int = d.x - boundsCenterX;
  94. d.x = x + xDiff;
  95. var boundsCenterY :int = bounds.top + bounds.height / 2;
  96. var yDiff :int = d.y - boundsCenterY;
  97. d.y = y + yDiff;
  98. } else { //If we're not attached to a parent yet, centering will be ok unless
  99. //the displayObject is scaled
  100. d.x = x;
  101. d.y = y;
  102. bounds = d.getBounds(d);
  103. d.x -= (bounds.width / 2) + bounds.left;
  104. d.y -= (bounds.height / 2) + bounds.top;
  105. }
  106. return d;
  107. }
  108. /**
  109. * Converts any DisplayObject into a Bitmap. This can increase the graphical
  110. * performance of complex MovieClips.
  111. */
  112. public static function convertToBitmap (d :DisplayObject, scale :Number = 1) :Bitmap
  113. {
  114. if (d == null) {
  115. return null;
  116. }
  117. var bounds :Rectangle = d.getBounds(d);
  118. if (bounds.width == 0 && bounds.height == 0) {
  119. log.error("convertToBitmap", "d", d, "d.name", d.name, "bounds", bounds);
  120. return null;
  121. }
  122. if (int(bounds.width) == 0 || int(bounds.height) == 0) {
  123. log.error("convertToBitmap", "int(bounds.width)", int(bounds.width),
  124. "int(bounds.height)", int(bounds.height));
  125. return null;
  126. }
  127. var bd :BitmapData = new BitmapData(int(bounds.width * scale), int(bounds.height * scale),
  128. true, 0xffffff);
  129. bd.draw(d, new Matrix(scale, 0, 0, scale, -bounds.left * scale, -bounds.top * scale));
  130. var bm :Bitmap = new Bitmap(bd);
  131. return bm;
  132. }
  133. public static function detach (d :DisplayObject) :void
  134. {
  135. if (d != null && d.parent != null) {
  136. d.parent.removeChild(d);
  137. }
  138. }
  139. public static function disposeAllBitmapData (d :DisplayObject) :void
  140. {
  141. DisplayUtil.applyToHierarchy(d, maybeDispose);
  142. }
  143. public static function distribute (seq :Array, startX :int, startY :int, endX :int,
  144. endY :int) :void
  145. {
  146. if (seq == null || seq.length == 0) {
  147. return;
  148. }
  149. var xInc :int = (endX - startX) / (seq.length + 1);
  150. startX += xInc / 2;
  151. var yInc :int = (endY - startY) / (seq.length + 1);
  152. startY += yInc / 2;
  153. for (var ii :int = 0; ii < seq.length; ++ii) {
  154. centerOn(seq[ii], startX + ii * xInc, startY + ii * yInc);
  155. }
  156. }
  157. public static function distributeChildrenVertically (disp :DisplayObject, startTop :Number =
  158. 0) :void
  159. {
  160. if (!(disp is DisplayObjectContainer)) {
  161. return;
  162. }
  163. var container :DisplayObjectContainer = DisplayObjectContainer(disp);
  164. var bounds :Rectangle;
  165. var child :DisplayObject;
  166. for (var ii :int = 0; ii < container.numChildren; ++ii) {
  167. child = container.getChildAt(ii) as DisplayObject;
  168. if (child == null) {
  169. continue;
  170. }
  171. bounds = child.getBounds(container);
  172. child.y += startTop - bounds.top;
  173. startTop = startTop + bounds.height;
  174. }
  175. }
  176. public static function distributionPoint (index :int, length :int, startX :int, startY :int,
  177. endX :int, endY :int) :Point
  178. {
  179. var xInc :int = (endX - startX) / (length + 1);
  180. startX += xInc;
  181. var yInc :int = (endY - startY) / (length + 1);
  182. startY += yInc;
  183. return new Point(startX + index * xInc, startY + index * yInc);
  184. }
  185. public static function drawText (parent :DisplayObjectContainer, text :String, x :int = 0,
  186. y :int = 0, center :Boolean = true, initProps :Object = null) :TextField
  187. {
  188. if (initProps == null) {
  189. initProps = {};
  190. }
  191. initProps.x = x;
  192. initProps.y = y;
  193. initProps.selectable = false;
  194. initProps.mouseEnabled = false;
  195. var tf :TextField = TextFieldUtil.createField(text, initProps);
  196. parent.addChild(tf);
  197. if (center) {
  198. tf.autoSize = TextFieldAutoSize.LEFT;
  199. tf.width = tf.textWidth;
  200. tf.height = tf.textHeight;
  201. tf.x -= tf.width / 2;
  202. tf.y -= tf.height / 2;
  203. } else {
  204. tf.autoSize = TextFieldAutoSize.LEFT;
  205. tf.width = tf.textWidth;
  206. tf.height = tf.textHeight;
  207. }
  208. return tf;
  209. }
  210. public static function getBoundsCenter (d :DisplayObject) :Point
  211. {
  212. var bounds :Rectangle = d.getBounds(d);
  213. return new Point(bounds.left + bounds.width / 2, bounds.top + bounds.height / 2);
  214. }
  215. public static function getBoundsCenterRelativeTo (d :DisplayObject,
  216. relativeTo :DisplayObject) :Point
  217. {
  218. var bounds :Rectangle = d.getBounds(relativeTo);
  219. return new Point(bounds.left + bounds.width / 2, bounds.top + bounds.height / 2);
  220. }
  221. public static function getCenterOffset (d :DisplayObject) :Point
  222. {
  223. return getCenterOffsetRelativeTo(d, d);
  224. }
  225. public static function getCenterOffsetRelativeTo (d :DisplayObject,
  226. relativeTo :DisplayObject) :Point
  227. {
  228. var bounds :Rectangle = d.getBounds(relativeTo);
  229. var centerX :Number = bounds.left + bounds.width / 2;
  230. var centerY :Number = bounds.top + bounds.height / 2;
  231. return new Point(centerX - d.x, centerY - d.y);
  232. }
  233. public static function getChildren (d :DisplayObjectContainer) :Array
  234. {
  235. var children :Array = [];
  236. if (d == null) {
  237. return children;
  238. }
  239. for (var ii :int = 0; ii < d.numChildren; ++ii) {
  240. if (d.getChildAt(ii) != null) {
  241. children.push(d.getChildAt(ii));
  242. }
  243. }
  244. return children;
  245. }
  246. public static function getGlobalScale (d :DisplayObject, currentScale :Number = 1) :Number
  247. {
  248. if (d == null || d.stage == null || d == d.stage) {
  249. return currentScale;
  250. } else {
  251. return getGlobalScale(d.parent, d.scaleX * currentScale);
  252. }
  253. }
  254. public static function loadBitmapFromUrl (url :String, loadedBitmapDataCallback :Function =
  255. null) :Bitmap
  256. {
  257. if (url == null) {
  258. return null;
  259. }
  260. var bm :Bitmap = new Bitmap();
  261. try {
  262. var imageLoader :Loader = new Loader();
  263. var loaderContext :LoaderContext = new LoaderContext();
  264. loaderContext.checkPolicyFile = true;
  265. function onComplete () :void {
  266. if (imageLoader.content != null && imageLoader.content is DisplayObject) {
  267. var bd :BitmapData = createBitmapData(imageLoader.content as DisplayObject);
  268. bm.bitmapData = bd;
  269. if (loadedBitmapDataCallback != null) {
  270. loadedBitmapDataCallback(bd);
  271. }
  272. }
  273. }
  274. var request :URLRequest = new URLRequest(url);
  275. imageLoader.contentLoaderInfo.addEventListener(Event.COMPLETE,
  276. F.justOnce(F.callback(onComplete)));
  277. imageLoader.contentLoaderInfo.addEventListener(IOErrorEvent.IO_ERROR,
  278. function (... ignored) :void {
  279. log.error("URL not found: " + url);
  280. });
  281. imageLoader.load(request, loaderContext);
  282. } catch (err :IOErrorEvent) {
  283. log.error("URL not found: " + url);
  284. }
  285. return bm;
  286. }
  287. /**
  288. * From a list of DisplayObjects on the stage, create a combined bitmap, with a
  289. * layer order of the supplied array.
  290. */
  291. public static function mergeDisplayObjects (dos :Array, scale :Number = 1,
  292. preserveBounds :Boolean = false) :Bitmap
  293. {
  294. if (dos == null || dos.length == 0) {
  295. return null;
  296. }
  297. // trace("mergeDisplayObjects, scale=" + scale);
  298. var bounds :Rectangle;
  299. var stageBounds :Rectangle;
  300. var disp :DisplayObject;
  301. var origins :Array = [];
  302. var origin :Point;
  303. for each (disp in dos) {
  304. stageBounds = disp.getBounds(disp.stage);
  305. origins.push(new Point(stageBounds.left, stageBounds.top));
  306. if (bounds != null) {
  307. bounds = bounds.union(stageBounds);
  308. } else {
  309. bounds = stageBounds
  310. }
  311. }
  312. var bd :BitmapData = new BitmapData(int(bounds.width * scale), int(bounds.height * scale),
  313. true, 0xffffff);
  314. var topLeft :Point = new Point(bounds.left, bounds.top);
  315. for (var ii :int = 0; ii < dos.length; ++ii) {
  316. disp = dos[ii] as DisplayObject;
  317. origin = origins[ii] as Point;
  318. var localBounds :Rectangle = disp.getBounds(disp);
  319. // bd.draw(disp, new Matrix(1, 0, 0, 1, -localBounds.left + (origin.x - topLeft.x),
  320. // -localBounds.top + (origin.y - topLeft.y)));
  321. bd.draw(disp, new Matrix(scale, 0, 0, scale,
  322. scale * (-localBounds.left + (origin.x - topLeft.x)),
  323. scale * (-localBounds.top + (origin.y - topLeft.y))));
  324. }
  325. var bm :Bitmap = new Bitmap(bd);
  326. return bm;
  327. }
  328. public static function placeSequence (parent :DisplayObjectContainer, seq :Array, startX :int,
  329. startY :int, direction :int = 0, gap :int = 5, center :Boolean = true) :void
  330. {
  331. if (seq == null || seq.length == 0 || parent == null) {
  332. return;
  333. }
  334. for each (var d :DisplayObject in seq) {
  335. if (d == null) {
  336. continue;
  337. }
  338. parent.addChild(d);
  339. var xAdjust :int = 0;
  340. var yAdjust :int = 0;
  341. if (center) {
  342. if (direction == LEFT_TO_RIGHT) {
  343. xAdjust = d.width / 2;
  344. } else if (direction == RIGHT_TO_LEFT) {
  345. xAdjust = -d.width / 2;
  346. } else if (direction == TOP_TO_BOTTOM) {
  347. yAdjust = d.height / 2;
  348. } else if (direction == BOTTOM_TO_TOP) {
  349. yAdjust = -d.height / 2;
  350. }
  351. centerOn(d, startX + xAdjust, startY + yAdjust);
  352. } else {
  353. d.x = startX + xAdjust;
  354. d.y = startY + yAdjust;
  355. }
  356. if (direction == LEFT_TO_RIGHT) {
  357. startX += d.width + gap;
  358. } else if (direction == RIGHT_TO_LEFT) {
  359. startX += -(d.width + gap);
  360. } else if (direction == TOP_TO_BOTTOM) {
  361. startY += d.height + gap;
  362. } else if (direction == BOTTOM_TO_TOP) {
  363. startY += -(d.height + gap);
  364. }
  365. }
  366. }
  367. public static function removeAllChildren (parent :DisplayObject) :void
  368. {
  369. if (parent == null || !(parent is DisplayObjectContainer)) {
  370. return;
  371. }
  372. while (DisplayObjectContainer(parent).numChildren > 0) {
  373. DisplayObjectContainer(parent).removeChildAt(0);
  374. }
  375. }
  376. /**
  377. * Creates a bitmap from the given DisplayObject, and positions the bitmap so that it is
  378. * visually in the same position as the argument.
  379. */
  380. public static function screenShot (disp :DisplayObject) :Bitmap
  381. {
  382. if (disp == null || disp.stage == null) {
  383. log.error("screenShot", "d", disp, "d.stage", disp == null ? null : disp.stage);
  384. return null;
  385. }
  386. var scale :Number = getGlobalScale(disp, 1);
  387. var bd :BitmapData = new BitmapData(disp.stage.stageWidth, disp.stage.stageHeight, true,
  388. 0xffffff);
  389. var stageBounds :Rectangle = disp.getBounds(disp.stage);
  390. var origin :Point = new Point(stageBounds.left, stageBounds.top);
  391. var localBounds :Rectangle = disp.getBounds(disp);
  392. var topLeft :Point = new Point(0, 0);
  393. bd.draw(disp, new Matrix(scale, 0, 0, scale,
  394. scale * (-localBounds.left + (origin.x - topLeft.x)),
  395. scale * (-localBounds.top + (origin.y - topLeft.y))));
  396. var bm :Bitmap = new Bitmap(bd);
  397. return bm;
  398. }
  399. public static function shrinkAndCenterOn (disp :DisplayObject, maxSize :int = 20) :DisplayObject
  400. {
  401. if (maxSize > 0) {
  402. var max :int = Math.max(disp.width, disp.height);
  403. if (max > maxSize) {
  404. disp.scaleX = disp.scaleY = Number(maxSize) / max;
  405. }
  406. }
  407. var s :Sprite = new Sprite()
  408. s.addChild(disp);
  409. DisplayUtils.centerOn(disp);
  410. return s;
  411. }
  412. /**
  413. * Creates a bitmap from the given DisplayObject, and positions the bitmap so that it is
  414. * visually in the same position as the argument. Optionally supply a resolution scale
  415. * factor, so if parent container is scaled, the Bitmap won't be pixellated.
  416. */
  417. public static function substituteBitmap (d :DisplayObject, resolutionFactor :Number = 1) :Bitmap
  418. {
  419. if (d == null) {
  420. return null;
  421. }
  422. var bm :Bitmap = convertToBitmap(d, resolutionFactor);
  423. if (bm == null) {
  424. log.error("substituteBitmap", "d", d, "resolutionFactor", resolutionFactor, "bm", bm);
  425. return null;
  426. }
  427. bm.scaleX = bm.scaleY = 1 / resolutionFactor;
  428. var bounds :Rectangle = d.getBounds(d);
  429. //Center it according to the offsets.
  430. bm.x = bounds.left;
  431. bm.y = bounds.top;
  432. return bm;
  433. }
  434. protected static function createBitmapData (disp :DisplayObject, width :int = -1, height :int =
  435. -1, uniformScale :Boolean = true) :BitmapData
  436. {
  437. var bounds :Rectangle = disp.getBounds(disp);
  438. if (width < 0) {
  439. width = bounds.width;
  440. }
  441. if (height < 0) {
  442. height = bounds.height;
  443. }
  444. var scaleX :Number = width / bounds.width;
  445. var scaleY :Number = height / bounds.height;
  446. if (uniformScale) {
  447. scaleX = scaleY = Math.min(scaleX, scaleY);
  448. }
  449. var bd :BitmapData = new BitmapData(width, height, true, 0);
  450. bd.draw(disp, new Matrix(scaleX, 0, 0, scaleY, -bounds.x * scaleX, -bounds.y * scaleY));
  451. return bd;
  452. }
  453. protected static function maybeDispose (d :DisplayObject) :void
  454. {
  455. if (d is Bitmap) {
  456. Bitmap(d).bitmapData.dispose();
  457. }
  458. }
  459. protected static const log :Log = Log.getLog(DisplayUtils);
  460. }
  461. }