Nixed the Flash utilities (which moved to flash-utils) and wired up a

dependency on same. The repackaged classes are going to break everything, but
I'll go through and fix the big stuff and smaller projects can languish or get
with the times.


git-svn-id: svn+ssh://src.earth.threerings.net/nenya/trunk@839 ed5b42cb-e716-0410-a449-f6a68f950b19
This commit is contained in:
Michael Bayne
2009-07-21 21:24:07 +00:00
parent 3c610e959a
commit fc4792d5cd
43 changed files with 4 additions and 6055 deletions
+1
View File
@@ -44,6 +44,7 @@
<external-library-path> <external-library-path>
<path-element>@flex_sdk_dir@/frameworks/libs/player/{targetPlayerMajorVersion}/playerglobal.swc</path-element> <path-element>@flex_sdk_dir@/frameworks/libs/player/{targetPlayerMajorVersion}/playerglobal.swc</path-element>
<path-element>lib/flexlib.swc</path-element> <path-element>lib/flexlib.swc</path-element>
<path-element>lib/ooolib.swc</path-element>
<path-element>lib/naryalib.swc</path-element> <path-element>lib/naryalib.swc</path-element>
<path-element>@flex_sdk_dir@/frameworks/libs</path-element> <path-element>@flex_sdk_dir@/frameworks/libs</path-element>
<path-element>@flex_sdk_dir@/frameworks/libs/player</path-element> <path-element>@flex_sdk_dir@/frameworks/libs/player</path-element>
+1
View File
@@ -33,6 +33,7 @@
<include name="jorbis-0.0.15.jar"/> <include name="jorbis-0.0.15.jar"/>
<include name="jl1.0.jar"/> <include name="jl1.0.jar"/>
<!-- ActionScript dependencies --> <!-- ActionScript dependencies -->
<include name="ooolib*.swc"/>
<include name="naryalib*.swc"/> <include name="naryalib*.swc"/>
<include name="flexlib*.swc"/> <include name="flexlib*.swc"/>
</fileset> </fileset>
-67
View File
@@ -1,67 +0,0 @@
//
// $Id$
//
// Nenya library - tools for developing networked games
// Copyright (C) 2002-2007 Three Rings Design, Inc., All Rights Reserved
// http://www.threerings.net/code/nenya/
//
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation; either version 2.1 of the License, or
// (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
package com.threerings.flash {
import flash.display.DisplayObject;
/**
* An Animation that linearly transitions the alpha attribute of a given display object from
* one value to another and optionally executes a callback function when the transition is
* complete.
*/
public class AlphaFade extends AnimationImpl
{
/**
* Constructs a new AlphaFade instance. The alpha values should lie in [0, 1] and the
* duration is measured in milliseconds.
*/
public function AlphaFade (disp :DisplayObject, from :Number = 0, to :Number = 1,
duration :Number = 1, done :Function = null)
{
_disp = disp;
_from = from;
_to = to;
_duration = duration;
_done = done;
}
override public function updateAnimation (elapsed :Number) :void
{
if (elapsed < _duration) {
_disp.alpha = _from + ((_to - _from) * elapsed) / _duration;
return;
}
_disp.alpha = _to;
stopAnimation();
if (_done != null) {
_done();
}
}
protected var _disp :DisplayObject;
protected var _from :Number;
protected var _to :Number;
protected var _duration :Number;
protected var _done :Function;
}
}
-32
View File
@@ -1,32 +0,0 @@
//
// $Id$
//
// Nenya library - tools for developing networked games
// Copyright (C) 2002-2007 Three Rings Design, Inc., All Rights Reserved
// http://www.threerings.net/code/nenya/
//
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation; either version 2.1 of the License, or
// (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
package com.threerings.flash {
public interface Animation
{
/**
* The primary working method for your animation. The argument indicates how
* many milliseconds have passed since the animation was started.
*/
function updateAnimation (elapsed :Number) :void;
}
}
@@ -1,46 +0,0 @@
//
// $Id$
//
// Nenya library - tools for developing networked games
// Copyright (C) 2002-2007 Three Rings Design, Inc., All Rights Reserved
// http://www.threerings.net/code/nenya/
//
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation; either version 2.1 of the License, or
// (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
package com.threerings.flash {
public class AnimationImpl implements Animation
{
public function startAnimation () :void
{
AnimationManager.start(this);
}
public function stopAnimation () :void
{
AnimationManager.stop(this);
}
public function isPlaying () :Boolean
{
return AnimationManager.isPlaying(this);
}
public function updateAnimation (elapsed :Number) :void
{
// please implement!
}
}
}
@@ -1,157 +0,0 @@
//
// $Id$
//
// Nenya library - tools for developing networked games
// Copyright (C) 2002-2007 Three Rings Design, Inc., All Rights Reserved
// http://www.threerings.net/code/nenya/
//
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation; either version 2.1 of the License, or
// (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
package com.threerings.flash {
import flash.display.DisplayObject;
import flash.display.Sprite;
import flash.events.Event;
import flash.utils.getTimer; // func import
import com.threerings.util.Log;
/**
* Manages animations.
*/
public class AnimationManager
{
public function AnimationManager ()
{
throw new Error("Static only");
}
/**
* Start (or restart) the specified animation.
*/
public static function start (anim :Animation) :void
{
var dex :int = _anims.indexOf(anim);
if (dex == -1) {
dex = _anims.length;
_anims.push(anim);
}
_anims[dex + 1] = getTimer(); // mark it as starting
// and update it immediately
anim.updateAnimation(0);
// we re-check that the anims are non-empty because the first animation
// to be added could be removed as a result of calling updateAnimation() on it.
if (!_framer && _anims.length > 0) {
_framer = new Sprite();
_framer.addEventListener(Event.ENTER_FRAME, frameHandler);
}
}
/**
* Stop the specified animation.
*/
public static function stop (anim :Animation) :void
{
var dex :int = _anims.indexOf(anim);
if (dex == -1) {
Log.getLog(AnimationManager).warning("Stopping unknown Animation: " + anim);
return;
}
// remove it
_anims.splice(dex, 2);
// See if we should clean up a bit
if (_anims.length == 0 && _framer) {
_framer.removeEventListener(Event.ENTER_FRAME, frameHandler);
_framer = null;
}
}
/**
* Test to see if the specified animation has been started and not stopped.
*/
public static function isPlaying (anim :Animation) :Boolean
{
return _anims.indexOf(anim) >= 0;
}
/**
* Track a DisplayObject that is also an Animation- it will
* automatically be started when added to the stage and
* stopped when removed.
*/
public static function addDisplayAnimation (disp :DisplayObject) :void
{
if (!(disp is Animation)) {
throw new ArgumentError("Must be an Animation");
}
disp.addEventListener(Event.ADDED_TO_STAGE, startDisplayAnim);
disp.addEventListener(Event.REMOVED_FROM_STAGE, stopDisplayAnim);
if (disp.stage != null) {
// it's on the stage now!
start(Animation(disp));
}
}
/**
* Stop tracking the specified DisplayObject animation.
*/
public static function removeDisplayAnimation (disp :DisplayObject) :void
{
disp.removeEventListener(Event.ADDED_TO_STAGE, startDisplayAnim);
disp.removeEventListener(Event.REMOVED_FROM_STAGE, stopDisplayAnim);
if (disp.stage != null) {
stop(Animation(disp));
}
}
protected static function startDisplayAnim (event :Event) :void
{
start(event.currentTarget as Animation);
}
protected static function stopDisplayAnim (event :Event) :void
{
stop(event.currentTarget as Animation);
}
/**
* Handle the ENTER_FRAME event.
*/
protected static function frameHandler (event :Event) :void
{
var now :Number = getTimer();
var anim :Animation;
var startStamp :Number;
for (var ii :int = _anims.length - 2; ii >= 0; ii -= 2) {
Animation(_anims[ii]).updateAnimation(now - Number(_anims[ii + 1]));
}
}
/** The current timestamp, accessable to all animations. */
protected static var _now :Number;
/** All the currently running animations. */
protected static var _anims :Array = [];
protected static var _framer :Sprite;
}
}
@@ -1,97 +0,0 @@
//
// $Id: ImageUtil.as 189 2007-04-07 00:25:46Z dhoover $
//
// Nenya library - tools for developing networked games
// Copyright (C) 2002-2007 Three Rings Design, Inc., All Rights Reserved
// http://www.threerings.net/code/nenya/
//
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation; either version 2.1 of the License, or
// (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
package com.threerings.flash {
import flash.display.BitmapData;
import flash.events.Event;
import flash.events.EventDispatcher;
import flash.events.TimerEvent;
import flash.utils.Timer;
import com.threerings.util.ValueEvent;
/**
* Dispatched when the jpeg is complete.
* The 'value' property will contain the received data.
*
* @eventType flash.events.Event.COMPLETE
*/
[Event(name="complete", type="com.threerings.util.ValueEvent")]
/**
* Class that will encode a jpeg in the background and fire an event when it's done.
*/
public class BackgroundJPGEncoder extends EventDispatcher
{
/**
* Construct a jpeg encoder. Once started, the encoder will encode a jpeg over the course
* of multiple frames, generating an event to deliver the finished jpeg when it is done.
*
* @param image The bitmap to encode.
* @param quality The jpeg quality from 1 to 100 which determines the compression level.
* @param timeSlice The number of milliseconds of processing to do per frame.
*/
public function BackgroundJPGEncoder (
image :BitmapData, quality :Number = 50, timeSlice :int = 100)
{
_timeSlice = timeSlice;
_encoder = new JPGEncoder(image, quality, PIXEL_GRANULARITY);
_timer = new Timer(1);
_timer.addEventListener(TimerEvent.TIMER, timerHandler);
}
/**
* Start encoding
*/
public function start () :void
{
_timer.start();
}
/**
* Cancel encoding and discard any intermediate results.
*/
public function cancel () :void
{
_timer.stop();
_timer = null;
_encoder = null;
}
protected function timerHandler (event :TimerEvent) :void
{
if (_encoder.process(_timeSlice)) {
_timer.stop();
// The jpeg is ready so we fire an event passing it to the consumer.
dispatchEvent(new ValueEvent(Event.COMPLETE, _encoder.getJpeg()));
}
}
protected var _timeSlice :int;
protected var _timer :Timer;
protected var _encoder :JPGEncoder;
protected const PIXEL_GRANULARITY :int = 100;
}
}
@@ -1,219 +0,0 @@
//
// $Id$
package com.threerings.flash {
import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.display.Sprite;
import flash.events.StatusEvent;
import flash.geom.Matrix;
import flash.media.Camera;
import flash.media.Video;
import flash.text.TextField;
import com.threerings.util.Log;
import com.threerings.util.MethodQueue;
public class CameraSnapshotter extends Sprite
{
/**
* Static method to determine if the user even has a camera.
*/
public static function hasCamera () :Boolean
{
var names :Array = Camera.names;
return (names != null && names.length > 0);
}
/**
* @see setCamera
*/
public function CameraSnapshotter (cameraName :String = null)
{
// create a label behind the videos that only shows up if a camera doesn't work
var tf :TextField = new TextField();
tf.text = "Camera unavailable.";
addChild(tf);
setCameraName(cameraName);
}
override public function get width () :Number
{
return (_camera == null) ? 0 : _camera.width;
}
override public function get height () :Number
{
return (_camera == null) ? 0 : _camera.height;
}
/**
* @param cameraName the actual name of the camera, not the index (as used to get it from
* the Camera class).
*/
public function setCameraName (cameraName :String = null) :void
{
// translate the real name of the camera into the "name" used to get it.
var nameIdx :String = null;
if (cameraName != null) {
var names :Array = Camera.names;
for (var ii :int = 0; ii < names.length; ii++) {
if (cameraName === names[ii]) {
nameIdx = String(ii);
break;
}
}
}
setCamera(Camera.getCamera(nameIdx));
}
public function setCamera (camera :Camera) :void
{
if (_video != null) {
if (_bitmap != null && _bitmap.parent != null) {
removeChild(_bitmap);
}
if (_video.parent != null) {
removeChild(_video);
}
_bitmap = null;
detachVideo();
}
if (_camera != null) {
_camera.removeEventListener(StatusEvent.STATUS, handleCameraStatus);
}
_camera = camera;
if (_camera == null) {
return;
}
_camera.addEventListener(StatusEvent.STATUS, handleCameraStatus);
if (_camera.muted) {
return;
} else if (_correctionWidth == 0) {
setCorrection();
}
attachVideo();
}
protected function handleCameraStatus (event :StatusEvent) :void
{
if (event.code == "Camera.Unmuted") {
setCorrection();
attachVideo();
} else {
detachVideo();
}
}
public function getCameraName () :String
{
return (_camera == null) ? null : _camera.name;
}
/**
* Just like Camera's setMode().
* @see flash.media.Camera#setMode()
*/
public function setMode (width :int, height :int, fps :Number, favorArea :Boolean = true) :void
{
clearSnapshot();
if (_video != null) {
removeChild(_video);
}
detachVideo();
_camera.setMode(width, height, fps, favorArea);
attachVideo();
}
public function takeSnapshot () :void
{
if (_camera == null) {
return; // throw exception?
}
_bitmap.bitmapData.draw(_video,
new Matrix(_camera.width / _correctionWidth, 0, 0, _camera.height / _correctionHeight));
if (_video.parent != null) {
removeChild(_video);
addChild(_bitmap);
}
}
public function clearSnapshot () :void
{
if (_camera == null) {
return;
}
if (_bitmap != null && _bitmap.parent != null) {
removeChild(_bitmap);
}
if (_video != null && _video.parent == null) {
addChild(_video);
}
}
public function getSnapshot () :BitmapData
{
if (_camera == null) {
return null;
}
return _bitmap.bitmapData;
}
/**
* There appears to be a bug with the camera that "locks" it to the size it was at when
* it was unmuted.
*/
protected function setCorrection () :void
{
_correctionWidth = _camera.width;
_correctionHeight = _camera.height;
}
protected function attachVideo () :void
{
// detach any old first
detachVideo();
_video = new Video(_camera.width, _camera.height);
// the constructor args don't seem to do dick, so we set the values again...
_video.width = _camera.width;
_video.height = _camera.height;
_video.attachCamera(_camera);
addChild(_video);
_bitmap = new Bitmap(new BitmapData(_camera.width, _camera.height, false));
}
protected function detachVideo () :void
{
if (_video != null) {
_video.attachCamera(null);
_video = null;
}
}
protected var _camera :Camera;
protected var _video :Video;
protected var _bitmap :Bitmap;
protected var _correctionWidth :int;
protected var _correctionHeight :int;
}
}
@@ -1,77 +0,0 @@
//
// $Id$
//
// Nenya library - tools for developing networked games
// Copyright (C) 2002-2007 Three Rings Design, Inc., All Rights Reserved
// http://www.threerings.net/code/nenya/
//
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation; either version 2.1 of the License, or
// (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
package com.threerings.flash {
import flash.events.TimerEvent;
import flash.text.TextField;
import flash.utils.Timer;
/**
* A simple TextField with a method for setting text that will auto-clear.
*/
public class ClearingTextField extends TextField
{
public function ClearingTextField ()
{
_timer = new Timer(1, 1);
_timer.addEventListener(TimerEvent.TIMER, handleTimer);
}
/**
* Set text that will not auto-clear.
*/
override public function set text (str :String) :void
{
setText(str, 0);
}
/**
* Set the specified text on this TextField, and clear the text
* after the specified delay. If more text is set prior to the
* delay elapsing, the clear is pushed back to that text's delay, if any.
*/
public function setText (str :String, secondsToClear :Number = 5) :void
{
super.text = str;
_timer.reset(); // stop any running timer
// maybe start a new countdown
if (secondsToClear > 0) {
_timer.delay = secondsToClear * 1000;
_timer.start();
}
}
protected function handleTimer (event :TimerEvent) :void
{
super.text = "";
// this should be the fragglin' default, and not updating
// should be the fragglin' exception. GAWD!
event.updateAfterEvent();
}
protected var _timer :Timer;
}
}
-66
View File
@@ -1,66 +0,0 @@
//
// $Id$
//
// Nenya library - tools for developing networked games
// Copyright (C) 2002-2007 Three Rings Design, Inc., All Rights Reserved
// http://www.threerings.net/code/nenya/
//
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation; either version 2.1 of the License, or
// (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
package com.threerings.flash {
/**
* Color utility methods.
*
* See also mx.utils.ColorUtil.
*/
public class ColorUtil
{
/**
* Blend the two colors, either 50-50 or according to the ratio specified.
*/
public static function blend (
first :uint, second :uint, firstPerc :Number = 0.5) :uint
{
var secondPerc :Number = 1 - firstPerc;
var result :uint = 0;
for (var shift :int = 0; shift <= 16; shift += 8) {
var c1 :uint = (first >> shift) & 0xFF;
var c2 :uint = (second >> shift) & 0xFF;
result |= uint(Math.max(0, Math.min(255,
(c1 * firstPerc) + (c2 * secondPerc)))) << shift;
}
return result;
}
/**
* Returns a color's Hue value, in degrees. 0<=Hue<=360.
* http://en.wikipedia.org/wiki/Hue
*/
public static function getHue (color :uint) :Number
{
var r :Number = (color >> 16) & 0xff;
var g :Number = (color >> 8) & 0xff;
var b :Number = color & 0xff;
var hue :Number = R2D * Math.atan2(ROOT_3 * (g - b), 2 * (r - g - b));
return (hue >= 0 ? hue : hue + 360);
}
protected static const ROOT_3 :Number = Math.sqrt(3);
protected static const R2D :Number = 180 / Math.PI; // radians to degrees
}
}
@@ -1,116 +0,0 @@
//
// $Id$
//
// Nenya library - tools for developing networked games
// Copyright (C) 2002-2007 Three Rings Design, Inc., All Rights Reserved
// http://www.threerings.net/code/nenya/
//
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation; either version 2.1 of the License, or
// (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
package com.threerings.flash
{
import flash.display.SimpleButton;
import flash.display.DisplayObject;
/**
* A SimpleButton subclass that includes a disabledState.
* DisablingButtons don't dispatch mouse events when disabled.
*/
public class DisablingButton extends SimpleButton
{
public function DisablingButton (
upState :DisplayObject = null,
overState :DisplayObject = null,
downState :DisplayObject = null,
hitTestState :DisplayObject = null,
disabledState :DisplayObject = null)
{
super(null, overState, downState, hitTestState);
_disabledState = disabledState;
_upState = upState;
updateState();
}
/**
* Returns the DisplayObject that will be displayed when the button is disabled.
*/
public function get disabledState () :DisplayObject
{
return _disabledState;
}
/**
* Sets the DisplayObject that will be displayed when the button is disabled.
*/
public function set disabledState (newState :DisplayObject) :void
{
_disabledState = newState;
updateState();
}
// from SimpleButton
override public function set upState (newState :DisplayObject) :void
{
_upState = newState;
updateState();
}
// from SimpleButton
override public function get upState () :DisplayObject
{
return _upState;
}
// from SimpleButton
override public function set enabled (val :Boolean) :void
{
super.enabled = val;
updateState();
}
override public function get mouseEnabled () :Boolean
{
return _mouseEnabled;
}
override public function set mouseEnabled (enabled :Boolean) :void
{
_mouseEnabled = enabled;
updateState();
}
protected function updateState () :void
{
super.upState = ((null != _disabledState && !super.enabled) ? _disabledState : _upState);
// mouseEnabled is always false when the button is disabled
super.mouseEnabled = super.enabled && _mouseEnabled;
// force a visual update.
super.enabled = !super.enabled;
super.enabled = !super.enabled;
}
protected var _disabledState :DisplayObject;
protected var _upState :DisplayObject;
protected var _mouseEnabled :Boolean = true;
}
}
-383
View File
@@ -1,383 +0,0 @@
//
// $Id$
//
// Nenya library - tools for developing networked games
// Copyright (C) 2002-2007 Three Rings Design, Inc., All Rights Reserved
// http://www.threerings.net/code/nenya/
//
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation; either version 2.1 of the License, or
// (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
package com.threerings.flash {
import com.threerings.util.ArrayUtil;
import com.threerings.util.ClassUtil;
import flash.display.DisplayObject;
import flash.display.DisplayObjectContainer;
import flash.geom.Point;
import flash.geom.Rectangle;
public class DisplayUtil
{
/**
* Transforms a point from one DisplayObject's coordinate space to another's.
*/
public static function transformPoint (p :Point, fromDisp :DisplayObject, toDisp :DisplayObject)
:Point
{
return toDisp.globalToLocal(fromDisp.localToGlobal(p));
}
/**
* Adds newChild to container, directly below another child of the container.
*/
public static function addChildBelow (container :DisplayObjectContainer,
newChild :DisplayObject,
below :DisplayObject) :void
{
container.addChildAt(newChild, container.getChildIndex(below));
}
/**
* Adds newChild to container, directly above another child of the container.
*/
public static function addChildAbove (container :DisplayObjectContainer,
newChild :DisplayObject,
above :DisplayObject) :void
{
container.addChildAt(newChild, container.getChildIndex(above) + 1);
}
/**
* Sets the top-left pixel of a DisplayObject to the given location, relative to another
* DisplayObject's coordinate space.
*/
public static function positionBoundsRelative (disp :DisplayObject, relativeTo :DisplayObject,
x :Number, y :Number) :void
{
var bounds :Rectangle = disp.getBounds(relativeTo);
disp.x = x - bounds.left;
disp.y = y - bounds.top;
}
/**
* Sets the top-left pixel of a DisplayObject to the given location, taking the
* object's bounds into account.
*/
public static function positionBounds (disp :DisplayObject, x :Number, y :Number) :void
{
positionBoundsRelative(disp, disp, x, y);
}
/**
* Sorts a container's children, using ArrayUtil.stableSort.
*
* comp is a function that takes two DisplayObjects, and returns int -1 if the first
* object should appear before the second in the container, 1 if it should appear after,
* and 0 if the order does not matter. If omitted, Comparators.COMPARABLE
* will be used- all the children should implement Comparable.
*/
public static function sortDisplayChildren (
container :DisplayObjectContainer, comp :Function = null) :void
{
var numChildren :int = container.numChildren;
// put all children in an array
var children :Array = new Array(numChildren);
var ii :int;
for (ii = 0; ii < numChildren; ii++) {
children[ii] = container.getChildAt(ii);
}
// stable sort the array
ArrayUtil.stableSort(children, comp);
// set their new indexes
for (ii = 0; ii < numChildren; ii++) {
container.setChildIndex(DisplayObject(children[ii]), ii);
}
}
/**
* Call the specified function for the display object and all descendants.
*
* This is nearly exactly like mx.utils.DisplayUtil.walkDisplayObjects,
* except this method copes with security errors when examining a child.
*/
public static function applyToHierarchy (
disp :DisplayObject, callbackFunction :Function) :void
{
callbackFunction(disp);
if (disp is DisplayObjectContainer) {
var container :DisplayObjectContainer = disp as DisplayObjectContainer;
var nn :int = container.numChildren;
for (var ii :int = 0; ii < nn; ii++) {
try {
disp = container.getChildAt(ii);
} catch (err :SecurityError) {
continue;
}
// and then we apply outside of the try/catch block so that
// we don't hide errors thrown by the callbackFunction.
applyToHierarchy(disp, callbackFunction);
}
}
}
/**
* Center the specified rectangle within the specified bounds. If the bounds are too
* small then the rectangle will be pinned to the upper-left.
*/
public static function centerRectInRect (rect :Rectangle, bounds :Rectangle) :Point
{
return new Point(
bounds.x + Math.max(0, (bounds.width - rect.width) / 2),
bounds.y + Math.max(0, (bounds.height - rect.height) / 2));
}
/**
* Returns the most reasonable position for the specified rectangle to
* be placed at so as to maximize its containment by the specified
* bounding rectangle while still placing it as near its original
* coordinates as possible.
*
* @param rect the rectangle to be positioned.
* @param bounds the containing rectangle.
*/
public static function fitRectInRect (rect :Rectangle, bounds :Rectangle) :Point
{
// Guarantee that the right and bottom edges will be contained
// and do our best for the top and left edges.
return new Point(
Math.min(bounds.right - rect.width, Math.max(rect.x, bounds.x)),
Math.min(bounds.bottom - rect.height, Math.max(rect.y, bounds.y)));
}
/**
* Position the specified rectangle within the bounds, avoiding
* any of the Rectangles in the avoid array, which may be destructively
* modified.
*
* @return true if the rectangle was successfully placed, given the
* constraints, or false if the positioning failed (the rectangle will
* be left at its original location.
*/
public static function positionRect (
r :Rectangle, bounds :Rectangle, avoid :Array) :Boolean
{
var origPos :Point = r.topLeft;
var pointSorter :Function = createPointSorter(origPos);
var possibles :Array = new Array();
// start things off with the passed-in point (adjusted to
// be inside the bounds, if needed)
possibles.push(fitRectInRect(r, bounds));
// keep track of area that doesn't generate new possibles
var dead :Array = new Array();
// Note: labeled breaks and continues are supposed to be legal,
// but they throw wacky runtime exceptions for me. So instead
// I'm throwing a boolean and using that to continue the while
/* CHECKPOSSIBLES: */ while (possibles.length > 0) {
try {
var p :Point = (possibles.shift() as Point);
r.x = p.x;
r.y = p.y;
// make sure the rectangle is in the view
if (!bounds.containsRect(r)) {
continue;
}
// and not over a dead area
for each (var deadRect :Rectangle in dead) {
if (deadRect.intersects(r)) {
//continue CHECKPOSSIBLES;
throw true; // continue outer loop
}
}
// see if it hits any rects we're trying to avoid
for (var ii :int = 0; ii < avoid.length; ii++) {
var avoidRect :Rectangle = (avoid[ii] as Rectangle);
if (avoidRect.intersects(r)) {
// remove it from the avoid list
avoid.splice(ii, 1);
// but add it to the dead list
dead.push(avoidRect);
// add 4 new possible points, each pushed in
// one direction
possibles.push(
new Point(avoidRect.x - r.width, r.y),
new Point(r.x, avoidRect.y - r.height),
new Point(avoidRect.x + avoidRect.width, r.y),
new Point(r.x, avoidRect.y + avoidRect.height));
// re-sort the list
possibles.sort(pointSorter);
//continue CHECKPOSSIBLES;
throw true; // continue outer loop
}
}
// hey! if we got here, then it worked!
return true;
} catch (continueWhile :Boolean) {
// simply catch the boolean and use it to continue inner loops
}
}
// we never found a match, move the rectangle back
r.x = origPos.x;
r.y = origPos.y;
return false;
}
/**
* Create a sort Function that can be used to compare Points in an
* Array according to their distance from the specified Point.
*
* Note: The function will always sort according to distance from the
* passed-in point, even if that point's coordinates change after
* the function is created.
*/
public static function createPointSorter (origin :Point) :Function
{
return function (p1 :Point, p2 :Point) :Number {
var dist1 :Number = Point.distance(origin, p1);
var dist2 :Number = Point.distance(origin, p2);
return (dist1 > dist2) ? 1 : ((dist1 < dist2) ? -1 : 0); // signum
};
}
/**
* Find a component with the specified name in the specified display hierarchy.
* Whether finding deeply or shallowly, if two components have the target name and are
* at the same depth, the first one found will be returned.
*
* Note: This method will not find rawChildren of flex componenets.
*/
public static function findInHierarchy (
top :DisplayObject, name :String, findShallow :Boolean = true,
maxDepth :int = int.MAX_VALUE) :DisplayObject
{
var result :Array = findInHierarchy0(top, name, findShallow, maxDepth);
return (result != null) ? DisplayObject(result[0]) : null;
}
/**
* Dump the display hierarchy to a String, each component on a newline, children indented
* two spaces:
* "instance0" flash.display.Sprite
* "instance1" flash.display.Sprite
* "entry_box" flash.text.TextField
*
* Note: This method will not dump rawChildren of flex componenets.
*/
public static function dumpHierarchy (top :DisplayObject) :String
{
return dumpHierarchy0(top);
}
/**
* Internal worker method for findInHierarchy.
*/
private static function findInHierarchy0 (
obj :DisplayObject, name :String, shallow :Boolean, maxDepth :int, curDepth :int = 0) :Array
{
if (obj == null) {
return null;
}
var bestResult :Array;
if (obj.name == name) {
if (shallow) {
return [ obj, curDepth ];
} else {
bestResult = [ obj, curDepth ];
}
} else {
bestResult = null;
}
if (curDepth < maxDepth && (obj is DisplayObjectContainer)) {
var cont :DisplayObjectContainer = obj as DisplayObjectContainer;
var nextDepth :int = curDepth + 1;
for (var ii :int = 0; ii < cont.numChildren; ii++) {
try {
var result :Array = findInHierarchy0(
cont.getChildAt(ii), name, shallow, maxDepth, nextDepth);
if (result != null) {
if (shallow) {
// we update maxDepth for every hit, so result is always
// shallower than any current bestResult
bestResult = result;
maxDepth = int(result[1]) - 1;
if (maxDepth == curDepth) {
break; // stop looking
}
} else {
// only replace if it's deeper
if (bestResult == null || int(result[1]) > int(bestResult[1])) {
bestResult = result;
}
}
}
} catch (err :SecurityError) {
// skip this child
}
}
}
return bestResult;
}
/**
* Internal worker method for dumpHierarchy.
*/
private static function dumpHierarchy0 (
obj :DisplayObject, spaces :String = "", inStr :String = "") :String
{
if (obj != null) {
if (inStr != "") {
inStr += "\n";
}
inStr += spaces + "\"" + obj.name + "\" " + ClassUtil.getClassName(obj);
if (obj is DisplayObjectContainer) {
spaces += " ";
var container :DisplayObjectContainer = obj as DisplayObjectContainer;
for (var ii :int = 0; ii < container.numChildren; ii++) {
try {
var child :DisplayObject = container.getChildAt(ii);
inStr = dumpHierarchy0(container.getChildAt(ii), spaces, inStr);
} catch (err :SecurityError) {
inStr += "\n" + spaces + "SECURITY-BLOCKED";
}
}
}
}
return inStr;
}
}
}
-37
View File
@@ -1,37 +0,0 @@
//
// $Id$
package com.threerings.flash {
/**
* Some easing functions.
*/
public class Easing
{
/**
* Interpolates cubically between two values, with beginning and end derivates set
* to zero. See http://en.wikipedia.org/wiki/Cubic_Hermite_spline for details.
*/
public static function cubicHermiteSpline (
t :Number, b :Number, c :Number, d :Number, p_params :Object = null) :Number
{
// convert t to 0-1
t = (t / d);
if (t <= 0) {
return b;
}
const end :Number = b + c;
if (t >= 1) {
return end;
}
const startSlope :Number = (p_params == null) ? 0 : Number(p_params["startSlope"]);
const endSlope :Number = (p_params == null) ? 0 : Number(p_params["endSlope"]);
const tt :Number = t * t;
const ttt :Number = tt * t;
return b * (2*ttt - 3*tt + 1) +
startSlope * (ttt - 2*tt + t) +
end * (-2*ttt + 3*tt) +
endSlope * (ttt -tt);
}
}
}
-68
View File
@@ -1,68 +0,0 @@
//
// $Id$
//
// Nenya library - tools for developing networked games
// Copyright (C) 2002-2007 Three Rings Design, Inc., All Rights Reserved
// http://www.threerings.net/code/nenya/
//
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation; either version 2.1 of the License, or
// (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
package com.threerings.flash {
import flash.events.Event;
import flash.text.TextField;
import flash.utils.getTimer; // function import
public class FPSDisplay extends TextField
{
public function FPSDisplay (framesToTrack :int = 150)
{
background = true;
text = "fps: 000.00";
width = textWidth + 5;
height = textHeight + 4;
_framesToTrack = framesToTrack;
addEventListener(Event.ENTER_FRAME, handleEnterFrame);
}
protected function handleEnterFrame (event :Event) :void
{
var curStamp :Number = getTimer();
_frameStamps.push(curStamp);
if (_frameStamps.length > _framesToTrack) {
_frameStamps.shift(); // forget the oldest timestamps
}
var firstStamp :Number = Number(_frameStamps[0]);
var seconds :Number = (curStamp - firstStamp) / 1000;
// subtract one from the frames, since we're measuring the time
// elapsed over the frames (when there are two timestamps, that's
// the difference between 1 frame)
var frames :Number = _frameStamps.length - 1;
this.text = "fps: " + (frames / seconds).toFixed(2);
}
/** Timestamps of past ENTER_FRAME events. */
protected var _frameStamps :Array = [];
/** The number of running frames we track. */
protected var _framesToTrack :int;
}
}
-253
View File
@@ -1,253 +0,0 @@
//
// $Id$
//
// Nenya library - tools for developing networked games
// Copyright (C) 2002-2007 Three Rings Design, Inc., All Rights Reserved
// http://www.threerings.net/code/nenya/
//
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation; either version 2.1 of the License, or
// (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
package com.threerings.flash {
import flash.display.DisplayObject;
import flash.filters.BevelFilter;
import flash.filters.BitmapFilter;
import flash.filters.BlurFilter;
import flash.filters.ColorMatrixFilter;
import flash.filters.ConvolutionFilter;
import flash.filters.DisplacementMapFilter;
import flash.filters.DropShadowFilter;
import flash.filters.GlowFilter;
import flash.filters.GradientBevelFilter;
import flash.filters.GradientGlowFilter;
import com.threerings.util.ArrayUtil;
import com.threerings.util.ClassUtil;
/**
* Useful utility methods that wouldn't be needed if the flash API were not so retarded.
*/
public class FilterUtil
{
/**
* Add the specified filter to the DisplayObject.
*/
public static function addFilter (disp :DisplayObject, filter :BitmapFilter) :void
{
checkArgs(disp, filter);
var filts :Array = disp.filters;
if (filts == null) {
filts = [];
}
filts.push(filter);
disp.filters = filts;
}
/**
* Remove the specified filter from the DisplayObject.
* Note that the filter set in the DisplayObject is a clone, so the values
* of the specified filter are used to find a match.
*/
public static function removeFilter (disp :DisplayObject, filter :BitmapFilter) :void
{
checkArgs(disp, filter);
var filts :Array = disp.filters;
if (filts != null) {
for (var ii :int = filts.length - 1; ii >= 0; ii--) {
var oldFilter :BitmapFilter = (filts[ii] as BitmapFilter);
if (equals(oldFilter, filter)) {
filts.splice(ii, 1);
if (filts.length == 0) {
filts = null;
}
disp.filters = filts;
return; // SUCCESS
}
}
}
}
/**
* Are the two filters equals?
*/
public static function equals (f1 :BitmapFilter, f2 :BitmapFilter) :Boolean
{
if (f1 === f2) { // catch same instance, or both null
return true;
} else if (f1 == null || f2 == null) { // otherwise nulls are no good
return false;
}
var c1 :Class = ClassUtil.getClass(f1);
if (c1 !== ClassUtil.getClass(f2)) {
return false;
}
// when we have two filters of the same class, figure it out by hand...
switch (c1) {
case BevelFilter:
var bf1 :BevelFilter = (f1 as BevelFilter);
var bf2 :BevelFilter = (f2 as BevelFilter);
return (bf1.angle == bf2.angle) && (bf1.blurX == bf2.blurX) &&
(bf1.blurY == bf2.blurY) && (bf1.distance == bf2.distance) &&
(bf1.highlightAlpha == bf2.highlightAlpha) &&
(bf1.highlightColor == bf2.highlightColor) && (bf1.knockout == bf2.knockout) &&
(bf1.quality == bf2.quality) && (bf1.shadowAlpha == bf2.shadowAlpha) &&
(bf1.shadowColor == bf2.shadowColor) && (bf1.strength == bf2.strength) &&
(bf1.type == bf2.type);
case BlurFilter:
var blur1 :BlurFilter = (f1 as BlurFilter);
var blur2 :BlurFilter = (f2 as BlurFilter);
return (blur1.blurX == blur2.blurX) && (blur1.blurY == blur2.blurY) &&
(blur1.quality == blur2.quality);
case ColorMatrixFilter:
var cmf1 :ColorMatrixFilter = (f1 as ColorMatrixFilter);
var cmf2 :ColorMatrixFilter = (f2 as ColorMatrixFilter);
return ArrayUtil.equals(cmf1.matrix, cmf2.matrix);
case ConvolutionFilter:
var cf1 :ConvolutionFilter = (f1 as ConvolutionFilter);
var cf2 :ConvolutionFilter = (f2 as ConvolutionFilter);
return (cf1.alpha == cf2.alpha) && (cf1.bias == cf2.bias) && (cf1.clamp == cf2.clamp) &&
(cf1.color == cf2.color) && (cf1.divisor == cf2.divisor) &&
ArrayUtil.equals(cf1.matrix, cf2.matrix) && (cf1.matrixX == cf2.matrixX) &&
(cf1.matrixY == cf2.matrixY) && (cf1.preserveAlpha == cf2.preserveAlpha);
case DisplacementMapFilter:
var dmf1 :DisplacementMapFilter = (f1 as DisplacementMapFilter);
var dmf2 :DisplacementMapFilter = (f2 as DisplacementMapFilter);
return (dmf1.alpha == dmf2.alpha) && (dmf1.color == dmf2.color) &&
(dmf1.componentX == dmf2.componentX) && (dmf1.componentY == dmf2.componentY) &&
(dmf1.mapBitmap == dmf2.mapBitmap) && dmf1.mapPoint.equals(dmf2.mapPoint) &&
(dmf1.mode == dmf2.mode) && (dmf1.scaleX == dmf2.scaleX) &&
(dmf1.scaleY == dmf2.scaleY);
case DropShadowFilter:
var dsf1 :DropShadowFilter = (f1 as DropShadowFilter);
var dsf2 :DropShadowFilter = (f2 as DropShadowFilter);
return (dsf1.alpha == dsf2.alpha) && (dsf1.angle = dsf2.angle) &&
(dsf1.blurX = dsf2.blurX) && (dsf1.blurY = dsf2.blurY) &&
(dsf1.color = dsf2.color) && (dsf1.distance = dsf2.distance) &&
(dsf1.hideObject = dsf2.hideObject) && (dsf1.inner = dsf2.inner) &&
(dsf1.knockout = dsf2.knockout) && (dsf1.quality = dsf2.quality) &&
(dsf1.strength = dsf2.strength);
case GlowFilter:
var gf1 :GlowFilter = (f1 as GlowFilter);
var gf2 :GlowFilter = (f2 as GlowFilter);
return (gf1.alpha == gf2.alpha) && (gf1.blurX == gf2.blurX) &&
(gf1.blurY == gf2.blurY) && (gf1.color == gf2.color) && (gf1.inner == gf2.inner) &&
(gf1.knockout == gf2.knockout) && (gf1.quality == gf2.quality) &&
(gf1.strength == gf2.strength);
case GradientBevelFilter:
var gbf1 :GradientBevelFilter = (f1 as GradientBevelFilter);
var gbf2 :GradientBevelFilter = (f2 as GradientBevelFilter);
return ArrayUtil.equals(gbf1.alphas, gbf2.alphas) && (gbf1.angle == gbf2.angle) &&
(gbf1.blurX == gbf2.blurX) && (gbf1.blurY == gbf2.blurY) &&
ArrayUtil.equals(gbf1.colors, gbf2.colors) && (gbf1.distance == gbf2.distance) &&
(gbf1.knockout == gbf2.knockout) && (gbf1.quality == gbf2.quality) &&
ArrayUtil.equals(gbf1.ratios, gbf2.ratios) && (gbf1.strength == gbf2.strength) &&
(gbf1.type == gbf2.type);
case GradientGlowFilter:
var ggf1 :GradientGlowFilter = (f1 as GradientGlowFilter);
var ggf2 :GradientGlowFilter = (f2 as GradientGlowFilter);
return ArrayUtil.equals(ggf1.alphas, ggf2.alphas) && (ggf1.angle == ggf2.angle) &&
(ggf1.blurX == ggf2.blurX) && (ggf1.blurY == ggf2.blurY) &&
ArrayUtil.equals(ggf1.colors, ggf2.colors) && (ggf1.distance == ggf2.distance) &&
(ggf1.knockout == ggf2.knockout) && (ggf1.quality == ggf2.quality) &&
ArrayUtil.equals(ggf1.ratios, ggf2.ratios) && (ggf1.strength == ggf2.strength) &&
(ggf1.type == ggf2.type);
default:
throw new ArgumentError("OMG! Unknown filter type: " + c1);
}
}
/**
* Create a filter that, if applied to a DisplayObject, will shift the hue of that object
* by the given value.
*
* @param hueShift a value, in degrees, between -180 and 180.
*/
public static function createHueShift (hue :int) :ColorMatrixFilter
{
return shiftHueBy(null, hue);
}
/**
* Shift the color matrix filter by the given amount. This is adapted from the code found at
* http://www.kirupa.com/forum/showthread.php?t=230706
*
* @param hueShift a value, in degrees, between -180 and 180.
*/
public static function shiftHueBy (original :ColorMatrixFilter,
hueShift :int) :ColorMatrixFilter
{
var cosMatrix :Array = [ 0.787, -0.715, -0.072,
-0.212, 0.285, -0.072,
-0.213, -0.715, 0.928 ];
var sinMatrix :Array = [-0.213, -0.715, 0.928,
0.143, 0.140, -0.283,
-0.787, 0.715, 0.072 ];
var multiplier :Array = [];
var cos :Number = Math.cos(hueShift * Math.PI / 180);
var sin :Number = Math.sin(hueShift * Math.PI / 180);
for (var ii :int = 0; ii < 9; ii++) {
multiplier.push([ 0.213, 0.715, 0.072 ][ii%3] + cosMatrix[ii] * cos +
sinMatrix[ii] * sin);
}
var originalMatrix :Array;
if (original == null || original.matrix == null) {
// thats the identity matrix for this filter
originalMatrix = [ 1, 0, 0, 0, 0,
0, 1, 0, 0, 0,
0, 0, 1, 0, 0,
0, 0, 0, 1, 0 ];
} else {
originalMatrix = original.matrix;
}
// this loop compresses a massive, wacky concatination function that was used in the code
// from the site listed above
var matrix :Array = [];
for (ii = 0; ii < 20; ii++) {
if ((ii % 5) > 2) {
matrix.push(originalMatrix[ii]);
} else {
var base :int = Math.floor(ii / 5) * 5;
matrix.push((originalMatrix[base] * multiplier[ii%5]) +
(originalMatrix[base+1] * multiplier[(ii%5)+3]) +
(originalMatrix[base+2] * multiplier[(ii%5)+6]));
}
}
return new ColorMatrixFilter(matrix);
}
protected static function checkArgs (disp :DisplayObject, filter :BitmapFilter) :void
{
if (disp == null || filter == null) {
throw new ArgumentError("args may not be null");
}
}
}
}
@@ -1,104 +0,0 @@
//
// $Id$
//
// Nenya library - tools for developing networked games
// Copyright (C) 2002-2007 Three Rings Design, Inc., All Rights Reserved
// http://www.threerings.net/code/nenya/
//
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation; either version 2.1 of the License, or
// (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
package com.threerings.flash {
import flash.display.Sprite;
import flash.events.Event;
import flash.filters.GlowFilter;
import flash.text.TextField;
import flash.text.TextFieldAutoSize;
import flash.text.TextFormat;
public class FloatingTextAnimation extends Sprite
implements Animation
{
/*
public static function create (
text :String, duration :Number = 1000, dy :int = -10,
size :int = 18, font :String = "Arial", bold :Boolean = true,
color :uint = 0x000000, borderColor :uint = 0xFFFFFF) :FloatingTextAnimation
{
var fta :FloatingTextAnimation = new FloatingTextAnimation();
var format :TextFormat = new TextFormat();
format.size = size;
format.font = font;
format.bold = bold;
format.color = color;
fta.defaultTextFormat = format;
fta.autoSize = TextFieldAutoSize.CENTER;
fta.text = text;
fta.width = fta.textWidth + 5;
fta.height = fta.textHeight + 4;
fta.duration = duration;
fta.dy = dy;
fta.filters = [ new GlowFilter(borderColor, 1, 2, 2, 255) ];
return fta;
}
*/
public var duration :Number;
public var dy :Number;
public function FloatingTextAnimation (
text :String, textArgs :Object = null, duration :Number = 1000, dy :int = -10)
{
var tf :TextField = TextFieldUtil.createField(text, textArgs);
tf.selectable = false;
tf.x = -(tf.width/2)
tf.y = -(tf.height/2);
addChild(tf);
this.duration = duration;
this.dy = dy;
AnimationManager.addDisplayAnimation(this);
}
override public function set y (val :Number) :void
{
super.y = _startY = val;
}
// from Animation
public function updateAnimation (elapsed :Number) :void
{
var perc :Number = elapsed / duration;
if (perc >= 1) {
AnimationManager.removeDisplayAnimation(this);
parent.removeChild(this);
return;
}
alpha = 1 - perc;
super.y = _startY + (dy * perc);
}
protected var _startY :Number;
}
}
@@ -1,78 +0,0 @@
//
// $Id$
//
// Nenya library - tools for developing networked games
// Copyright (C) 2002-2007 Three Rings Design, Inc., All Rights Reserved
// http://www.threerings.net/code/nenya/
//
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation; either version 2.1 of the License, or
// (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
package com.threerings.flash {
import flash.display.Sprite;
import flash.events.Event;
/**
* Convenience superclass to use for sprites that need to update every frame.
* (One must be very careful to remove all ENTER_FRAME listeners when not needed, as they
* will prevent an object from being garbage collected!)
*/
public class FrameSprite extends Sprite
{
/**
* @param renderFrameUponAdding if true, the handleFrame() method
* is called whenever an ADDED_TO_STAGE event is received.
*/
public function FrameSprite (renderFrameUponAdding :Boolean = true)
{
_renderOnAdd = renderFrameUponAdding;
addEventListener(Event.ADDED_TO_STAGE, handleAdded);
addEventListener(Event.REMOVED_FROM_STAGE, handleRemoved);
}
/**
* Called when we're added to the stage.
*/
protected function handleAdded (... ignored) :void
{
addEventListener(Event.ENTER_FRAME, handleFrame);
if (_renderOnAdd) {
handleFrame(); // update immediately
}
}
/**
* Called when we're added to the stage.
*/
protected function handleRemoved (... ignored) :void
{
removeEventListener(Event.ENTER_FRAME, handleFrame);
}
/**
* Called to update our visual appearance prior to each frame.
*/
protected function handleFrame (... ignored) :void
{
// nothing here. Override in yor subclass.
}
/** Should we call handleFrame() when we get ADDED_TO_STAGE? */
protected var _renderOnAdd :Boolean;
}
}
@@ -1,75 +0,0 @@
//
// $Id$
//
// Nenya library - tools for developing networked games
// Copyright (C) 2002-2007 Three Rings Design, Inc., All Rights Reserved
// http://www.threerings.net/code/nenya/
//
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation; either version 2.1 of the License, or
// (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
package com.threerings.flash {
import flash.display.Graphics;
public class GraphicsUtil
{
/**
* Draws a dashed rectangle using the currently-set lineStyle.
*/
public static function dashRect (
g :Graphics, x :Number, y :Number, width :Number, height :Number,
dashLength :Number = 5, spaceLength :Number = 5) :void
{
const x2 :Number = x + width;
const y2 :Number = y + height;
dashTo(g, x, y, x2, y, dashLength, spaceLength);
dashTo(g, x2, y, x2, y2, dashLength, spaceLength);
dashTo(g, x2, y2, x, y2, dashLength, spaceLength);
dashTo(g, x, y2, x, y, dashLength, spaceLength);
}
/**
* Draws a dashed line using the currently set lineStyle.
*/
public static function dashTo (
g :Graphics, x1 :Number, y1 :Number, x2 :Number, y2 :Number,
dashLength :Number = 5, spaceLength :Number = 5) :void
{
const dx :Number = (x2 - x1), dy :Number = (y2 - y1);
var length :Number = Math.sqrt(dx*dx + dy*dy);
const units :Number = length/(dashLength+spaceLength);
const dashSpaceRatio :Number = dashLength/(dashLength+spaceLength);
const dashX :Number = (dx/units)*dashSpaceRatio, dashY :Number = (dy/units)*dashSpaceRatio;
const spaceX :Number = (dx/units)-dashX, spaceY :Number = (dy/units)-dashY;
g.moveTo(x1, y1);
while (length > 0) {
x1 += dashX;
y1 += dashY;
length -= dashLength;
if (length < 0) {
x1 = x2;
y1 = y2;
}
g.lineTo(x1, y1);
x1 += spaceX;
y1 += spaceY;
g.moveTo(x1, y1);
length -= spaceLength;
}
g.moveTo(x2, y2);
}
}
}
-106
View File
@@ -1,106 +0,0 @@
//
// $Id$
//
// Nenya library - tools for developing networked games
// Copyright (C) 2002-2007 Three Rings Design, Inc., All Rights Reserved
// http://www.threerings.net/code/nenya/
//
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation; either version 2.1 of the License, or
// (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
package com.threerings.flash {
import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.display.DisplayObject;
import flash.display.Shape;
import flash.geom.Point;
import flash.geom.Rectangle;
import flash.text.TextField;
import flash.text.TextLineMetrics;
/**
* Image and Bitmap related utility functions.
*/
public class ImageUtil
{
/**
* Create a DisplayObject that will display an error message
* of the specified dimensions.
*/
public static function createErrorImage (width :int, height :int)
:DisplayObject
{
var shape :Shape = new Shape();
shape.graphics.beginBitmapFill(createErrorBitmap());
shape.graphics.drawRect(0, 0, width, height);
shape.graphics.endFill();
return shape;
/*
// Alternate implementation that creates an actual Bitmap object
var src :BitmapData = createErrorBitmap();
var data :BitmapData = new BitmapData(width, height, false);
data.draw(src);
var rect :Rectangle = new Rectangle(0, 0, src.width, src.height);
for (var xx :int = 0; xx < width; xx += src.width) {
for (var yy :int = 0; yy < height; yy += src.height) {
if (xx != 0 || yy != 0) {
data.copyPixels(data, rect, new Point(xx, yy));
}
}
}
return new Bitmap(data);
*/
}
/**
* Takes a BitmapData, Bitmap, or Class that will turn into either, and returns
* a reference to the BitmapData, or returns null.
*/
public static function toBitmapData (spec :*) :BitmapData
{
if (spec is Class) {
spec = new (Class(spec))();
}
if (spec is BitmapData) {
return BitmapData(spec);
} else if (spec is Bitmap) {
return Bitmap(spec).bitmapData;
} else {
return null;
}
}
/**
* Create a minimally-sized "error" BitmapData.
*/
public static function createErrorBitmap () :BitmapData
{
var txt :TextField = new TextField();
txt.text = "Error";
var metrics :TextLineMetrics = txt.getLineMetrics(0);
var data :BitmapData = new BitmapData(
metrics.width + ERROR_PADDING, metrics.height + ERROR_PADDING,
false, 0xFF4040);
data.draw(txt);
return data;
}
/** The amount to pad error messages by. */
private static const ERROR_PADDING :int = 5;
}
}
-712
View File
@@ -1,712 +0,0 @@
/**
* Forked from code from com.adobe.images.JPGEncoder in as3corelib under the license contained
* in that file to produce a derivative work.
*
* see:
* http://code.google.com/p/as3corelib/source/browse/trunk/src/com/adobe/images/JPGEncoder.as?r=25
*/
package com.threerings.flash
{
import flash.geom.*;
import flash.display.*;
import flash.utils.*;
/**
* Class for compressing bitmaps into jpegs. Each instance may only be used for a single image.
* When the encoder is constructed, the headers are generated, but no pixel encoding is done.
* Subsequent calls to 'process' will encode pixels for a specified length of time. Once the
* encoding is done (determined by process returning true, or a call to isComplete), the jpeg data
* can be accessed via getJpeg.
*/
public class JPGEncoder
{
// Static table initialization
private var ZigZag:Array = [
0, 1, 5, 6,14,15,27,28,
2, 4, 7,13,16,26,29,42,
3, 8,12,17,25,30,41,43,
9,11,18,24,31,40,44,53,
10,19,23,32,39,45,52,54,
20,22,33,38,46,51,55,60,
21,34,37,47,50,56,59,61,
35,36,48,49,57,58,62,63
];
private var YTable:Array = new Array(64);
private var UVTable:Array = new Array(64);
private var fdtbl_Y:Array = new Array(64);
private var fdtbl_UV:Array = new Array(64);
private function initQuantTables(sf:int):void
{
var i:int;
var t:Number;
var YQT:Array = [
16, 11, 10, 16, 24, 40, 51, 61,
12, 12, 14, 19, 26, 58, 60, 55,
14, 13, 16, 24, 40, 57, 69, 56,
14, 17, 22, 29, 51, 87, 80, 62,
18, 22, 37, 56, 68,109,103, 77,
24, 35, 55, 64, 81,104,113, 92,
49, 64, 78, 87,103,121,120,101,
72, 92, 95, 98,112,100,103, 99
];
for (i = 0; i < 64; i++) {
t = Math.floor((YQT[i]*sf+50)/100);
if (t < 1) {
t = 1;
} else if (t > 255) {
t = 255;
}
YTable[ZigZag[i]] = t;
}
var UVQT:Array = [
17, 18, 24, 47, 99, 99, 99, 99,
18, 21, 26, 66, 99, 99, 99, 99,
24, 26, 56, 99, 99, 99, 99, 99,
47, 66, 99, 99, 99, 99, 99, 99,
99, 99, 99, 99, 99, 99, 99, 99,
99, 99, 99, 99, 99, 99, 99, 99,
99, 99, 99, 99, 99, 99, 99, 99,
99, 99, 99, 99, 99, 99, 99, 99
];
for (i = 0; i < 64; i++) {
t = Math.floor((UVQT[i]*sf+50)/100);
if (t < 1) {
t = 1;
} else if (t > 255) {
t = 255;
}
UVTable[ZigZag[i]] = t;
}
var aasf:Array = [
1.0, 1.387039845, 1.306562965, 1.175875602,
1.0, 0.785694958, 0.541196100, 0.275899379
];
i = 0;
for (var row:int = 0; row < 8; row++)
{
for (var col:int = 0; col < 8; col++)
{
fdtbl_Y[i] = (1.0 / (YTable [ZigZag[i]] * aasf[row] * aasf[col] * 8.0));
fdtbl_UV[i] = (1.0 / (UVTable[ZigZag[i]] * aasf[row] * aasf[col] * 8.0));
i++;
}
}
}
private var YDC_HT:Array;
private var UVDC_HT:Array;
private var YAC_HT:Array;
private var UVAC_HT:Array;
private function computeHuffmanTbl(nrcodes:Array, std_table:Array):Array
{
var codevalue:int = 0;
var pos_in_table:int = 0;
var HT:Array = new Array();
for (var k:int=1; k<=16; k++) {
for (var j:int=1; j<=nrcodes[k]; j++) {
HT[std_table[pos_in_table]] = new BitString();
HT[std_table[pos_in_table]].val = codevalue;
HT[std_table[pos_in_table]].len = k;
pos_in_table++;
codevalue++;
}
codevalue*=2;
}
return HT;
}
private var std_dc_luminance_nrcodes:Array = [0,0,1,5,1,1,1,1,1,1,0,0,0,0,0,0,0];
private var std_dc_luminance_values:Array = [0,1,2,3,4,5,6,7,8,9,10,11];
private var std_ac_luminance_nrcodes:Array = [0,0,2,1,3,3,2,4,3,5,5,4,4,0,0,1,0x7d];
private var std_ac_luminance_values:Array = [
0x01,0x02,0x03,0x00,0x04,0x11,0x05,0x12,
0x21,0x31,0x41,0x06,0x13,0x51,0x61,0x07,
0x22,0x71,0x14,0x32,0x81,0x91,0xa1,0x08,
0x23,0x42,0xb1,0xc1,0x15,0x52,0xd1,0xf0,
0x24,0x33,0x62,0x72,0x82,0x09,0x0a,0x16,
0x17,0x18,0x19,0x1a,0x25,0x26,0x27,0x28,
0x29,0x2a,0x34,0x35,0x36,0x37,0x38,0x39,
0x3a,0x43,0x44,0x45,0x46,0x47,0x48,0x49,
0x4a,0x53,0x54,0x55,0x56,0x57,0x58,0x59,
0x5a,0x63,0x64,0x65,0x66,0x67,0x68,0x69,
0x6a,0x73,0x74,0x75,0x76,0x77,0x78,0x79,
0x7a,0x83,0x84,0x85,0x86,0x87,0x88,0x89,
0x8a,0x92,0x93,0x94,0x95,0x96,0x97,0x98,
0x99,0x9a,0xa2,0xa3,0xa4,0xa5,0xa6,0xa7,
0xa8,0xa9,0xaa,0xb2,0xb3,0xb4,0xb5,0xb6,
0xb7,0xb8,0xb9,0xba,0xc2,0xc3,0xc4,0xc5,
0xc6,0xc7,0xc8,0xc9,0xca,0xd2,0xd3,0xd4,
0xd5,0xd6,0xd7,0xd8,0xd9,0xda,0xe1,0xe2,
0xe3,0xe4,0xe5,0xe6,0xe7,0xe8,0xe9,0xea,
0xf1,0xf2,0xf3,0xf4,0xf5,0xf6,0xf7,0xf8,
0xf9,0xfa
];
private var std_dc_chrominance_nrcodes:Array = [0,0,3,1,1,1,1,1,1,1,1,1,0,0,0,0,0];
private var std_dc_chrominance_values:Array = [0,1,2,3,4,5,6,7,8,9,10,11];
private var std_ac_chrominance_nrcodes:Array = [0,0,2,1,2,4,4,3,4,7,5,4,4,0,1,2,0x77];
private var std_ac_chrominance_values:Array = [
0x00,0x01,0x02,0x03,0x11,0x04,0x05,0x21,
0x31,0x06,0x12,0x41,0x51,0x07,0x61,0x71,
0x13,0x22,0x32,0x81,0x08,0x14,0x42,0x91,
0xa1,0xb1,0xc1,0x09,0x23,0x33,0x52,0xf0,
0x15,0x62,0x72,0xd1,0x0a,0x16,0x24,0x34,
0xe1,0x25,0xf1,0x17,0x18,0x19,0x1a,0x26,
0x27,0x28,0x29,0x2a,0x35,0x36,0x37,0x38,
0x39,0x3a,0x43,0x44,0x45,0x46,0x47,0x48,
0x49,0x4a,0x53,0x54,0x55,0x56,0x57,0x58,
0x59,0x5a,0x63,0x64,0x65,0x66,0x67,0x68,
0x69,0x6a,0x73,0x74,0x75,0x76,0x77,0x78,
0x79,0x7a,0x82,0x83,0x84,0x85,0x86,0x87,
0x88,0x89,0x8a,0x92,0x93,0x94,0x95,0x96,
0x97,0x98,0x99,0x9a,0xa2,0xa3,0xa4,0xa5,
0xa6,0xa7,0xa8,0xa9,0xaa,0xb2,0xb3,0xb4,
0xb5,0xb6,0xb7,0xb8,0xb9,0xba,0xc2,0xc3,
0xc4,0xc5,0xc6,0xc7,0xc8,0xc9,0xca,0xd2,
0xd3,0xd4,0xd5,0xd6,0xd7,0xd8,0xd9,0xda,
0xe2,0xe3,0xe4,0xe5,0xe6,0xe7,0xe8,0xe9,
0xea,0xf2,0xf3,0xf4,0xf5,0xf6,0xf7,0xf8,
0xf9,0xfa
];
private function initHuffmanTbl():void
{
YDC_HT = computeHuffmanTbl(std_dc_luminance_nrcodes,std_dc_luminance_values);
UVDC_HT = computeHuffmanTbl(std_dc_chrominance_nrcodes,std_dc_chrominance_values);
YAC_HT = computeHuffmanTbl(std_ac_luminance_nrcodes,std_ac_luminance_values);
UVAC_HT = computeHuffmanTbl(std_ac_chrominance_nrcodes,std_ac_chrominance_values);
}
private var bitcode:Array = new Array(65535);
private var category:Array = new Array(65535);
private function initCategoryNumber():void
{
var nrlower:int = 1;
var nrupper:int = 2;
var nr:int;
for (var cat:int=1; cat<=15; cat++) {
//Positive numbers
for (nr=nrlower; nr<nrupper; nr++) {
category[32767+nr] = cat;
bitcode[32767+nr] = new BitString();
bitcode[32767+nr].len = cat;
bitcode[32767+nr].val = nr;
}
//Negative numbers
for (nr=-(nrupper-1); nr<=-nrlower; nr++) {
category[32767+nr] = cat;
bitcode[32767+nr] = new BitString();
bitcode[32767+nr].len = cat;
bitcode[32767+nr].val = nrupper-1+nr;
}
nrlower <<= 1;
nrupper <<= 1;
}
}
private function writeBits(bs:BitString):void
{
var value:int = bs.val;
var posval:int = bs.len-1;
while ( posval >= 0 ) {
if (value & uint(1 << posval) ) {
_bytenew |= uint(1 << _bytepos);
}
posval--;
_bytepos--;
if (_bytepos < 0) {
if (_bytenew == 0xFF) {
writeByte(0xFF);
writeByte(0);
}
else {
writeByte(_bytenew);
}
_bytepos=7;
_bytenew=0;
}
}
}
private function writeByte(value:int):void
{
_byteout.writeByte(value);
}
private function writeWord(value:int):void
{
writeByte((value>>8)&0xFF);
writeByte((value )&0xFF);
}
// DCT & quantization core
private function fDCTQuant(data:Array, fdtbl:Array):Array
{
var tmp0:Number, tmp1:Number, tmp2:Number, tmp3:Number, tmp4:Number, tmp5:Number, tmp6:Number, tmp7:Number;
var tmp10:Number, tmp11:Number, tmp12:Number, tmp13:Number;
var z1:Number, z2:Number, z3:Number, z4:Number, z5:Number, z11:Number, z13:Number;
var i:int;
/* Pass 1: process rows. */
var dataOff:int=0;
for (i=0; i<8; i++) {
tmp0 = data[dataOff+0] + data[dataOff+7];
tmp7 = data[dataOff+0] - data[dataOff+7];
tmp1 = data[dataOff+1] + data[dataOff+6];
tmp6 = data[dataOff+1] - data[dataOff+6];
tmp2 = data[dataOff+2] + data[dataOff+5];
tmp5 = data[dataOff+2] - data[dataOff+5];
tmp3 = data[dataOff+3] + data[dataOff+4];
tmp4 = data[dataOff+3] - data[dataOff+4];
/* Even part */
tmp10 = tmp0 + tmp3; /* phase 2 */
tmp13 = tmp0 - tmp3;
tmp11 = tmp1 + tmp2;
tmp12 = tmp1 - tmp2;
data[dataOff+0] = tmp10 + tmp11; /* phase 3 */
data[dataOff+4] = tmp10 - tmp11;
z1 = (tmp12 + tmp13) * 0.707106781; /* c4 */
data[dataOff+2] = tmp13 + z1; /* phase 5 */
data[dataOff+6] = tmp13 - z1;
/* Odd part */
tmp10 = tmp4 + tmp5; /* phase 2 */
tmp11 = tmp5 + tmp6;
tmp12 = tmp6 + tmp7;
/* The rotator is modified from fig 4-8 to avoid extra negations. */
z5 = (tmp10 - tmp12) * 0.382683433; /* c6 */
z2 = 0.541196100 * tmp10 + z5; /* c2-c6 */
z4 = 1.306562965 * tmp12 + z5; /* c2+c6 */
z3 = tmp11 * 0.707106781; /* c4 */
z11 = tmp7 + z3; /* phase 5 */
z13 = tmp7 - z3;
data[dataOff+5] = z13 + z2; /* phase 6 */
data[dataOff+3] = z13 - z2;
data[dataOff+1] = z11 + z4;
data[dataOff+7] = z11 - z4;
dataOff += 8; /* advance pointer to next row */
}
/* Pass 2: process columns. */
dataOff = 0;
for (i=0; i<8; i++) {
tmp0 = data[dataOff+ 0] + data[dataOff+56];
tmp7 = data[dataOff+ 0] - data[dataOff+56];
tmp1 = data[dataOff+ 8] + data[dataOff+48];
tmp6 = data[dataOff+ 8] - data[dataOff+48];
tmp2 = data[dataOff+16] + data[dataOff+40];
tmp5 = data[dataOff+16] - data[dataOff+40];
tmp3 = data[dataOff+24] + data[dataOff+32];
tmp4 = data[dataOff+24] - data[dataOff+32];
/* Even part */
tmp10 = tmp0 + tmp3; /* phase 2 */
tmp13 = tmp0 - tmp3;
tmp11 = tmp1 + tmp2;
tmp12 = tmp1 - tmp2;
data[dataOff+ 0] = tmp10 + tmp11; /* phase 3 */
data[dataOff+32] = tmp10 - tmp11;
z1 = (tmp12 + tmp13) * 0.707106781; /* c4 */
data[dataOff+16] = tmp13 + z1; /* phase 5 */
data[dataOff+48] = tmp13 - z1;
/* Odd part */
tmp10 = tmp4 + tmp5; /* phase 2 */
tmp11 = tmp5 + tmp6;
tmp12 = tmp6 + tmp7;
/* The rotator is modified from fig 4-8 to avoid extra negations. */
z5 = (tmp10 - tmp12) * 0.382683433; /* c6 */
z2 = 0.541196100 * tmp10 + z5; /* c2-c6 */
z4 = 1.306562965 * tmp12 + z5; /* c2+c6 */
z3 = tmp11 * 0.707106781; /* c4 */
z11 = tmp7 + z3; /* phase 5 */
z13 = tmp7 - z3;
data[dataOff+40] = z13 + z2; /* phase 6 */
data[dataOff+24] = z13 - z2;
data[dataOff+ 8] = z11 + z4;
data[dataOff+56] = z11 - z4;
dataOff++; /* advance pointer to next column */
}
// Quantize/descale the coefficients
for (i=0; i<64; i++) {
// Apply the quantization and scaling factor & Round to nearest integer
data[i] = Math.round((data[i]*fdtbl[i]));
}
return data;
}
// Chunk writing
private function writeAPP0():void
{
writeWord(0xFFE0); // marker
writeWord(16); // length
writeByte(0x4A); // J
writeByte(0x46); // F
writeByte(0x49); // I
writeByte(0x46); // F
writeByte(0); // = "JFIF",'\0'
writeByte(1); // versionhi
writeByte(1); // versionlo
writeByte(0); // xyunits
writeWord(1); // xdensity
writeWord(1); // ydensity
writeByte(0); // thumbnwidth
writeByte(0); // thumbnheight
}
private function writeSOF0(width:int, height:int):void
{
writeWord(0xFFC0); // marker
writeWord(17); // length, truecolor YUV JPG
writeByte(8); // precision
writeWord(height);
writeWord(width);
writeByte(3); // nrofcomponents
writeByte(1); // IdY
writeByte(0x11); // HVY
writeByte(0); // QTY
writeByte(2); // IdU
writeByte(0x11); // HVU
writeByte(1); // QTU
writeByte(3); // IdV
writeByte(0x11); // HVV
writeByte(1); // QTV
}
private function writeDQT():void
{
writeWord(0xFFDB); // marker
writeWord(132); // length
writeByte(0);
var i:int;
for (i=0; i<64; i++) {
writeByte(YTable[i]);
}
writeByte(1);
for (i=0; i<64; i++) {
writeByte(UVTable[i]);
}
}
private function writeDHT():void
{
writeWord(0xFFC4); // marker
writeWord(0x01A2); // length
var i:int;
writeByte(0); // HTYDCinfo
for (i=0; i<16; i++) {
writeByte(std_dc_luminance_nrcodes[i+1]);
}
for (i=0; i<=11; i++) {
writeByte(std_dc_luminance_values[i]);
}
writeByte(0x10); // HTYACinfo
for (i=0; i<16; i++) {
writeByte(std_ac_luminance_nrcodes[i+1]);
}
for (i=0; i<=161; i++) {
writeByte(std_ac_luminance_values[i]);
}
writeByte(1); // HTUDCinfo
for (i=0; i<16; i++) {
writeByte(std_dc_chrominance_nrcodes[i+1]);
}
for (i=0; i<=11; i++) {
writeByte(std_dc_chrominance_values[i]);
}
writeByte(0x11); // HTUACinfo
for (i=0; i<16; i++) {
writeByte(std_ac_chrominance_nrcodes[i+1]);
}
for (i=0; i<=161; i++) {
writeByte(std_ac_chrominance_values[i]);
}
}
private function writeSOS():void
{
writeWord(0xFFDA); // marker
writeWord(12); // length
writeByte(3); // nrofcomponents
writeByte(1); // IdY
writeByte(0); // HTY
writeByte(2); // IdU
writeByte(0x11); // HTU
writeByte(3); // IdV
writeByte(0x11); // HTV
writeByte(0); // Ss
writeByte(0x3f); // Se
writeByte(0); // Bf
}
// Core processing
private var DU:Array = new Array(64);
private function processDU(CDU:Array, fdtbl:Array, DC:Number, HTDC:Array, HTAC:Array):Number
{
var EOB:BitString = HTAC[0x00];
var M16zeroes:BitString = HTAC[0xF0];
var i:int;
var DU_DCT:Array = fDCTQuant(CDU, fdtbl);
//ZigZag reorder
for (i=0;i<64;i++) {
DU[ZigZag[i]]=DU_DCT[i];
}
var Diff:int = DU[0] - DC; DC = DU[0];
//Encode DC
if (Diff==0) {
writeBits(HTDC[0]); // Diff might be 0
} else {
writeBits(HTDC[category[32767+Diff]]);
writeBits(bitcode[32767+Diff]);
}
//Encode ACs
var end0pos:int = 63;
for (; (end0pos>0)&&(DU[end0pos]==0); end0pos--) {
};
//end0pos = first element in reverse order !=0
if ( end0pos == 0) {
writeBits(EOB);
return DC;
}
i = 1;
while ( i <= end0pos ) {
var startpos:int = i;
for (; (DU[i]==0) && (i<=end0pos); i++) {
}
var nrzeroes:int = i-startpos;
if ( nrzeroes >= 16 ) {
for (var nrmarker:int=1; nrmarker <= nrzeroes/16; nrmarker++) {
writeBits(M16zeroes);
}
nrzeroes = int(nrzeroes&0xF);
}
writeBits(HTAC[nrzeroes*16+category[32767+DU[i]]]);
writeBits(bitcode[32767+DU[i]]);
i++;
}
if ( end0pos != 63 ) {
writeBits(EOB);
}
return DC;
}
private var YDU:Array = new Array(64);
private var UDU:Array = new Array(64);
private var VDU:Array = new Array(64);
private function RGB2YUV(img:BitmapData, xpos:int, ypos:int):void
{
var pos:int=0;
for (var y:int=0; y<8; y++) {
for (var x:int=0; x<8; x++) {
var P:uint = img.getPixel32(xpos+x,ypos+y);
var R:Number = Number((P>>16)&0xFF);
var G:Number = Number((P>> 8)&0xFF);
var B:Number = Number((P )&0xFF);
YDU[pos]=((( 0.29900)*R+( 0.58700)*G+( 0.11400)*B))-128;
UDU[pos]=(((-0.16874)*R+(-0.33126)*G+( 0.50000)*B));
VDU[pos]=((( 0.50000)*R+(-0.41869)*G+(-0.08131)*B));
pos++;
}
}
}
/**
* Construct a new JPGEncoder object.
*
* @param image The image to encode
* @param quality Quality level between 1 and 100 determining the level of compression.s
* @param pixelGranularity The minumum number of pixels to process at a time.
*/
public function JPGEncoder(image :BitmapData, quality :Number = 50, pixelGranularity :int = 100)
{
_image = image;
_pixelGranularity = pixelGranularity;
if (quality <= 0) {
quality = 1;
}
if (quality > 100) {
quality = 100;
}
var sf:int = 0;
if (quality < 50) {
sf = int(5000 / quality);
} else {
sf = int(200 - quality*2);
}
// Create tables
initHuffmanTbl();
initCategoryNumber();
initQuantTables(sf);
writeHeader();
initializeEncoding();
}
/**
* Write the jpeg header into the byte array for output.
*/
protected function writeHeader () :void
{
// Initialize bit writer
_bytenew=0;
_bytepos=7;
// Add JPEG headers
writeWord(0xFFD8); // SOI
writeAPP0();
writeDQT();
writeSOF0(_image.width,_image.height);
writeDHT();
writeSOS();
}
protected function initializeEncoding () :void {
// Encode 8x8 macroblocks
_bytenew=0;
_bytepos=7;
}
/**
* Encode a specified number of pixels. Fewer pixels will be encoded if the end of the image
* is reached first.
*/
protected function encodePixels (unitSize: int) :void
{
for (var count :int = 0; !pixelsDone && count < unitSize; count++) {
RGB2YUV(_image, _xpos, _ypos);
_DCY = processDU(YDU, fdtbl_Y, _DCY, YDC_HT, YAC_HT);
_DCU = processDU(UDU, fdtbl_UV, _DCU, UVDC_HT, UVAC_HT);
_DCV = processDU(VDU, fdtbl_UV, _DCV, UVDC_HT, UVAC_HT);
_xpos += 8;
// If we have moved past the end of the line, move to the next one.
if (_xpos >= _image.width) {
_xpos = 0;
_ypos += 8;
// If we have moved past the last line, we are done.
if (_ypos >= _image.height) {
pixelsDone = true;
}
}
}
}
/**
* Add the EOI marker to the end of the buffer, and make the result available to the consumer
* of this class.
*/
protected function completeEncoding () :void
{
// Do the bit alignment of the EOI marker
if ( _bytepos >= 0 ) {
var fillbits:BitString = new BitString();
fillbits.len = _bytepos+1;
fillbits.val = (1<<(_bytepos+1))-1;
writeBits(fillbits);
}
writeWord(0xFFD9); //EOI
_encodedJpeg = _byteout;
}
/**
* Return true if the encoding is complete.
*/
public function isComplete () :Boolean
{
return null != _encodedJpeg;
}
/**
* Work on encoding the image for a specified number of milliseconds. Return true if there
* is no more processing to do.
*/
public function process (timeSlice :int) :Boolean
{
const endTime :int = getTimer() + timeSlice;
while (! pixelsDone && getTimer() < endTime) {
encodePixels(_pixelGranularity);
}
if (pixelsDone) {
completeEncoding();
return true;
}
return false;
}
/**
* Return a byte array containing the encoded jpeg.
*/
public function getJpeg () :ByteArray
{
return _encodedJpeg;
}
/** The image to encode **/
protected var _image :BitmapData;
/** Byte array to contain the resuling jpg **/
protected var _encodedJpeg :ByteArray;
/** Byte array to contain the jpg while encoding is in progress. **/
protected var _byteout :ByteArray = new ByteArray();
// Used to keep track of the positon of the writer in the byte array
protected var _bytenew:int = 0;
protected var _bytepos:int = 7;
// State for the pixel block encoding process
protected var _DCY:Number=0;
protected var _DCU:Number=0;
protected var _DCV:Number=0;
protected var _ypos :int = 0;
protected var _xpos :int = 0;
protected var pixelsDone:Boolean = false;
/** The minumum number of pixels that will be processed at a time. **/
protected var _pixelGranularity :int;
}
}
/**
* Helper class originally separate
*/
class BitString
{
public var len:int = 0;
public var val:int = 0;
}
@@ -1,143 +0,0 @@
//
// $Id$
//
// Nenya library - tools for developing networked games
// Copyright (C) 2002-2007 Three Rings Design, Inc., All Rights Reserved
// http://www.threerings.net/code/nenya/
//
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation; either version 2.1 of the License, or
// (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
package com.threerings.flash {
import flash.events.EventDispatcher;
import flash.events.IEventDispatcher;
import flash.events.KeyboardEvent;
import flash.events.TimerEvent;
import flash.utils.Dictionary;
import flash.utils.Timer;
import flash.utils.getTimer;
/**
* A very simple class that adapts the KeyboardEvents generated by some source and altering
* (or blocking) the key repeat rate.
*/
public class KeyRepeatLimiter extends EventDispatcher
{
/**
* Create a KeyRepeatLimiter that will be limiting key repeat events from
* the specified source.
*
* @param limitRate 0 to block all key repeats, or a millisecond value specifying how often
* to dispatch KEY_DOWN events while the key is being held down. The rate will be limted
* by the frame rate of the enclosing SWF.
*/
public function KeyRepeatLimiter (source :IEventDispatcher, limitRate :int = 0)
{
_source = source;
_limitRate = limitRate;
_source.addEventListener(KeyboardEvent.KEY_DOWN, handleKeyDown);
_source.addEventListener(KeyboardEvent.KEY_UP, handleKeyUp);
}
/**
* Dispose of this KeyRepeatBlocker.
*/
public function shutdown () :void
{
_source.removeEventListener(KeyboardEvent.KEY_DOWN, handleKeyDown);
_source.removeEventListener(KeyboardEvent.KEY_UP, handleKeyUp);
// shut down any timers
for each (var val :* in _down) {
if (val is Array) {
((val as Array)[0] as Timer).stop();
}
}
_down = new Dictionary();
}
protected function handleKeyDown (event :KeyboardEvent) :void
{
var oldVal :* = _down[event.keyCode];
if (oldVal === undefined) {
_down[event.keyCode] = (_limitRate > 0) ? getTimer() : 0;
} else if ((oldVal is Number) && _limitRate > 0) {
var timeUntilRepeat :Number = _limitRate - (getTimer() - Number(oldVal));
var t :Timer = new Timer((timeUntilRepeat <= 0) ? _limitRate : timeUntilRepeat);
t.addEventListener(TimerEvent.TIMER, handleTimerEvent);
_down[event.keyCode] = [ t, event ];
t.start();
if (timeUntilRepeat > 0) {
// don't dispatch this event, wait for our first repeat time
return;
}
} else {
// eat the event
return;
}
// otherwise, dispatch the event
dispatchEvent(event);
}
protected function handleKeyUp (event :KeyboardEvent) :void
{
var oldVal :* = _down[event.keyCode];
if (oldVal !== undefined) {
if (oldVal is Array) {
((oldVal as Array)[0] as Timer).stop();
}
delete _down[event.keyCode];
}
// always dispatch an up
dispatchEvent(event);
}
protected function handleTimerEvent (event :TimerEvent) :void
{
// dispatch the key event associated with this timer
for each (var val :* in _down) {
if (val is Array) {
var arr :Array = val as Array;
var timer :Timer = arr[0] as Timer;
if (timer == event.target) {
if (timer.delay != _limitRate) {
timer.reset();
timer.delay = _limitRate;
timer.start();
}
dispatchEvent(arr[1] as KeyboardEvent);
break;
}
}
}
}
/** Our source. */
protected var _source :IEventDispatcher;
/** Tracks whether a key is currently being held down. */
protected var _down :Dictionary = new Dictionary();
protected var _limitRate :int;
}
}
-37
View File
@@ -1,37 +0,0 @@
//
// $Id$
package com.threerings.flash {
import flash.display.Loader;
/**
* Contains a utility method for safely unloading a Loader.
*/
public class LoaderUtil
{
/**
* Safely unload the specified loader.
*/
public static function unload (loader :Loader) :void
{
try {
loader.close();
} catch (e1 :Error) {
// ignore
}
try {
//loader.unloadAndStop();
// always try calling it, so that if it's missing we can fall back
loader["unloadAndStop"]();
} catch (e2 :Error) {
// hmm, maybe they are using FP9 still
try {
loader.unload();
} catch (e3 :Error) {
// ignore
}
}
}
}
}
-123
View File
@@ -1,123 +0,0 @@
//
// $Id$
//
// Nenya library - tools for developing networked games
// Copyright (C) 2002-2007 Three Rings Design, Inc., All Rights Reserved
// http://www.threerings.net/code/nenya/
//
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation; either version 2.1 of the License, or
// (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
package com.threerings.flash {
/**
* Collection of math utility functions.
*/
public class MathUtil
{
/**
* Returns the value of n clamped to be within the range [min, max].
*/
public static function clamp (n :Number, min :Number, max :Number) :Number
{
return Math.min(Math.max(n, min), max);
}
/**
* Converts degrees to radians.
*/
public static function toRadians (degrees :Number) :Number
{
return degrees * Math.PI / 180;
}
/**
* Normalizes an angle in radians to occupy the [0, 2pi) range.
*/
public static function normalizeRadians (radians :Number) :Number
{
const twopi :Number = Math.PI * 2;
var norm :Number = radians % twopi;
return (norm >= 0) ? norm : (norm + twopi);
}
/**
* Converts radians to degrees.
*/
public static function toDegrees (radians :Number) :Number
{
return radians * 180 / Math.PI;
}
/**
* Normalizes an angle in degrees to occupy the [0, 360) range.
*/
public static function normalizeDegrees (degrees :Number) :Number
{
var norm :Number = degrees % 360;
return (norm >= 0) ? norm : (norm + 360);
}
/**
* Returns distance from point (x1, y1) to (x2, y2) in 2D.
*
* <p>Supports various distance metrics: the common Euclidean distance, taxicab distance,
* arbitrary Minkowski distances, and Chebyshev distance.
*
* <p>See the <a href="http://www.nist.gov/dads/HTML/lmdistance.html">NIST web page on
* distance definitions</a>.<p>
*
* @param x1 x value of the first point
* @param y1 y value of the first point
* @param x2 x value of the second point
* @param y2 y value of the second point
* @param p Optional: p value of the norm function. Common cases:
* <ul><li>p = 2 (default): standard Euclidean distance on a plane
* <li>p = 1: taxicab distance (aka Manhattan distance)
* <li>p = Infinity: Chebyshev distance
* </ul>
* <b>Note</b>: p < 1 or p = NaN are treated as equivalent to p = Infinity
*/
public static function distance (
x1 :Number, y1 :Number, x2 :Number, y2 :Number, p :int = 2) :Number
{
if (! isFinite(p) || p < 1) {
p = Infinity;
}
var dx :Number = x2 - x1;
var dy :Number = y2 - y1;
switch (p) {
case 1:
// optimized version for taxicab distance
return dx + dy;
case 2:
// optimized version for Euclidean
return Math.sqrt(dx * dx + dy * dy);
case Infinity:
// optimized version for Chebyshev
return Math.max(dx, dy);
default:
// generic version
var xx :Number = Math.pow(dx, p);
var yy :Number = Math.pow(dy, p);
return Math.pow(xx + yy, 1 / p);
}
}
}
}
@@ -1,781 +0,0 @@
//
// $Id$
//
// Nenya library - tools for developing networked games
// Copyright (C) 2002-2007 Three Rings Design, Inc., All Rights Reserved
// http://www.threerings.net/code/nenya/
//
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation; either version 2.1 of the License, or
// (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
package com.threerings.flash {
import flash.display.Bitmap;
import flash.display.DisplayObject;
import flash.display.DisplayObjectContainer;
import flash.display.Loader;
import flash.display.LoaderInfo;
import flash.display.Shape;
import flash.display.Sprite;
import flash.errors.IOError;
import flash.events.Event;
import flash.events.EventDispatcher;
import flash.events.ErrorEvent;
import flash.events.IOErrorEvent;
import flash.events.MouseEvent;
import flash.events.NetStatusEvent;
import flash.events.ProgressEvent;
import flash.events.SecurityErrorEvent;
import flash.events.StatusEvent;
import flash.events.TextEvent;
import flash.geom.Point;
import flash.geom.Rectangle;
import flash.net.URLRequest;
import flash.text.TextField;
import flash.text.TextFieldAutoSize;
import flash.system.ApplicationDomain;
import flash.system.LoaderContext;
import flash.system.Security;
import flash.system.SecurityDomain;
import flash.utils.ByteArray;
import com.threerings.util.Log;
import com.threerings.util.StringUtil;
import com.threerings.util.ValueEvent;
import com.threerings.util.Util;
import com.threerings.flash.media.FlvVideoPlayer;
// import com.threerings.flash.media.MediaPlayerCodes;
import com.threerings.flash.media.SimpleVideoDisplay;
import com.threerings.flash.media.VideoPlayer;
/**
* Dispatched when the size of the media being loaded is known.
*
* @eventType com.threerings.flash.MediaContainer.SIZE_KNOWN
*/
[Event(name="mediaSizeKnown", type="com.threerings.util.ValueEvent")]
/**
* Dispatched when we've initialized our content. This is merely a redispatch
* of the INIT event we get from the loader.
*
* @eventType flash.events.Event.INIT
*/
[Event(name="init", type="flash.events.Event")]
/**
* Dispatched when we've shown new media.
*
* @eventType flash.events.Event.COMPLETE
*/
[Event(name="complete", type="flash.events.Event")]
/**
* Dispatched when we've unloaded our content... always. The LoaderInfo's UNLOAD event
* is only dispatched if the INIT event has already been dispatched and not if you cancel a
* load before it INIT.
*/
[Event(name="unload", type="flash.events.Event")]
/**
* A wrapper class for all media that will be placed on the screen.
* Subject to change.
*/
public class MediaContainer extends Sprite
{
/** A ValueEvent we dispatch when our size is known.
* Value: [ width, height ].
*
* @eventType mediaSizeKnown
*/
public static const SIZE_KNOWN :String = "mediaSizeKnown";
/** A log instance that can be shared by sprites. */
protected static const log :Log = Log.getLog(MediaContainer);
/**
* Constructor.
*/
public function MediaContainer (url :String = null)
{
if (url != null) {
setMedia(url);
}
mouseEnabled = false;
mouseChildren = true;
}
/**
* Return true if the content has been initialized.
* For most content, this is true if the media is non-null, but
* for anything loaded with a Loader, it is only true after the INIT event is dispatched.
*/
public function isContentInitialized () :Boolean
{
return (_media != null) && (_initialized || !(_media is Loader));
}
/**
* Get the media. If the media was loaded using a URL, this will
* likely be the Loader object holding the real media.
*/
public function getMedia () :DisplayObject
{
return _media;
}
/**
* Configure the media to display.
*/
public function setMedia (url :String) :void
{
if (Util.equals(_url, url)) {
return; // no change
}
// shutdown any previous media
if (_media != null) {
shutdown(false);
}
_url = url;
// set up the new media
if (url != null) {
willShowNewMedia();
showNewMedia(url);
didShowNewMedia();
}
}
/**
* Set the media to display as a ByteArray.
*/
public function setMediaBytes (bytes :ByteArray) :void
{
if (_media != null) {
shutdown(false);
}
_url = null;
willShowNewMedia();
startedLoading();
initLoader().loadBytes(bytes, getContext(null));
didShowNewMedia();
}
/**
* Configure our media as an instance of the specified class.
*/
public function setMediaClass (clazz :Class) :void
{
setMediaObject(new clazz() as DisplayObject);
}
/**
* Configure an already-instantiated DisplayObject as our media.
*/
public function setMediaObject (disp :DisplayObject) :void
{
if (_media != null) {
shutdown(false);
}
_url = null;
willShowNewMedia();
addChildAt(disp, 0);
_media = disp;
updateContentDimensions(disp.width, disp.height);
didShowNewMedia();
}
/**
* Sets whether this MediaContainer automatically shuts down when removed
* from the stage. By default this is not enabled.
*/
public function setShutdownOnRemove (enable :Boolean = true) :void
{
var fn :Function = enable ? addEventListener : removeEventListener;
fn(Event.REMOVED_FROM_STAGE, handleShutdownOnRemove);
}
/**
* A place where subclasses can initialize things prior to showing new media.
*/
protected function willShowNewMedia () :void
{
_initialized = false;
_isImage = false;
}
protected function showNewMedia (url :String) :void
{
if (StringUtil.endsWith(url.toLowerCase(), ".flv")) {
setupVideo(url);
} else {
setupSwfOrImage(url);
}
}
/**
* A place where subclasses can configure things after we've setup new media.
*/
protected function didShowNewMedia () :void
{
// nada in the base class...
}
/**
* Configure this sprite to show a video.
*/
protected function setupVideo (url :String) :void
{
var player :FlvVideoPlayer = new FlvVideoPlayer();
_media = createVideoUI(player);
addChildAt(_media, 0);
player.load(url);
updateContentDimensions(_media.width, _media.height);
}
/**
* Create the actual display for the VideoPlayer.
*/
protected function createVideoUI (player :VideoPlayer) :DisplayObject
{
return new SimpleVideoDisplay(player);
}
/**
* Configure this sprite to show an image or flash movie.
*/
protected function setupSwfOrImage (url :String) :void
{
startedLoading();
var loader :Loader = initLoader();
loader.load(new URLRequest(url), getContext(url));
try {
var info :LoaderInfo = loader.contentLoaderInfo;
updateContentDimensions(info.width, info.height);
} catch (err :Error) {
// an error is thrown trying to access these props before they're
// ready
}
}
/**
* Initialize a Loader as our _media, and configure it however needed to prepare
* loading user content.
*/
protected function initLoader () :Loader
{
// create our loader and set up some event listeners
var loader :Loader = new Loader();
_media = loader;
addChildAt(loader, 0);
addListeners(loader.contentLoaderInfo);
return loader;
}
/**
* Display a 'broken image' to indicate there were troubles with
* loading the media.
*/
protected function setupBrokenImage (w :int = -1, h :int = -1) :void
{
if (w == -1) {
w = 100;
}
if (h == -1) {
h = 100;
}
setMediaObject(ImageUtil.createErrorImage(w, h));
}
/**
* Unload the media we're displaying, clean up any resources.
*
* @param completely if true, we're going away and should stop
* everything. Otherwise, we're just loading up new media.
*/
public function shutdown (completely :Boolean = true) :void
{
// remove the mask (but only if we added it)
if (_media != null && _media.mask != null) {
try {
removeChild(_media.mask);
_media.mask = null;
} catch (argErr :ArgumentError) {
// If we catch this error, it was thrown in removeChild
// and means that we did not add the media's mask,
// and so shouldn't remove it either. The action we
// take here is NOT setting _media.mask = null.
// Then, we continue happily...
}
}
shutdownMedia();
if (_media != null) {
removeChild(_media);
dispatchEvent(new Event(Event.UNLOAD));
}
// clean everything up
_w = 0;
_h = 0;
_media = null;
}
/**
* Get the width of the content, bounded by the maximum.
*/
public function getContentWidth () :int
{
return Math.min(Math.abs(_w * getMediaScaleX()), getMaxContentWidth());
}
/**
* Get the height of the content, bounded by the maximum.
*/
public function getContentHeight () :int
{
return Math.min(Math.abs(_h * getMediaScaleY()), getMaxContentHeight());
}
/**
* Get the maximum allowable width for our content.
*/
public function getMaxContentWidth () :int
{
return int.MAX_VALUE;
}
/**
* Get the maximum allowable height for our content.
*/
public function getMaxContentHeight () :int
{
return int.MAX_VALUE;
}
/**
* Get the X scaling factor to use on the actual media.
*/
public function getMediaScaleX () :Number
{
return 1;
}
/**
* Get the Y scaling factor to use on the actual media.
*/
public function getMediaScaleY () :Number
{
return 1;
}
/**
* Called by MediaWrapper as notification that its size has changed.
*/
public function containerDimensionsUpdated (newWidth :Number, newHeight :Number) :void
{
// do nothing in base MediaContainer
}
/**
* Note: This method is NOT used in normal mouseOver calculations.
* Normal mouseOver stuff seems to be completely broken for transparent
* images: the transparent portion is a 'hit'. I've (Ray) tried
* just about everything to fix this, more than once.
*
* But if someone *does* call this method (we do, in whirled), then
* attempt to do the right thing.
*/
override public function hitTestPoint (
x :Number, y :Number, shapeFlag :Boolean = false) :Boolean
{
// if we're holding a bitmap, do something smarter than the
// flash built-in and actually check for *GASP* transparent pixels!
try {
// We should be able to test if the media is a Loader (we can),
// then test if childAllowsParent, but even CHECKING that value causes a security
// exception. WTF?!? That's supposed to be the value to check to AVOID getting a
// security exception. So instead, we track whether or not we've
// loaded an image and use that value.
if (shapeFlag && _isImage) {
var b :Bitmap = Bitmap(Loader(_media).content);
var p :Point = b.globalToLocal(new Point(x, y));
// check that it's within the content bounds, and then check the bitmap directly
if (p.x >= 0 && p.x <= getMaxContentWidth() && p.y >= 0 &&
p.y <= getMaxContentHeight() &&
b.bitmapData.hitTest(new Point(0, 0), 0, p)) {
return true;
} else {
// the bitmap was not hit, see if other children were hit...
for (var ii :int = numChildren - 1; ii >= 0; ii--) {
var child :DisplayObject = getChildAt(ii);
if (child != _media && child.hitTestPoint(x, y, shapeFlag)) {
return true;
}
}
return false;
}
}
} catch (err :Error) {
// nada
}
// normal hit testing
return super.hitTestPoint(x, y, shapeFlag);
}
override public function toString () :String
{
return "MediaContainer[url=" + _url + "]";
}
/**
* Return the LoaderContext that should be used to load the media
* at the specified url.
*/
protected function getContext (url :String) :LoaderContext
{
if (isImage(url)) {
_isImage = true;
// load images into our domain so that we can view their pixels
return new LoaderContext(true,
new ApplicationDomain(ApplicationDomain.currentDomain),
getSecurityDomain(url));
} else {
// share nothing, trust nothing
return new LoaderContext(false, new ApplicationDomain(null), null);
}
}
/**
* Return the security domain to use for the specified image url.
*/
protected function getSecurityDomain (imageURL :String) :SecurityDomain
{
switch (Security.sandboxType) {
case Security.LOCAL_WITH_FILE:
case Security.LOCAL_WITH_NETWORK:
case Security.LOCAL_TRUSTED:
return null;
default:
return SecurityDomain.currentDomain;
}
}
/**
* Does the specified url represent an image?
*/
protected function isImage (url :String) :Boolean
{
if (url == null) {
return false;
}
// look at the last 4 characters in the lowercased url
switch (url.toLowerCase().slice(-4)) {
case ".png":
case ".jpg":
case ".gif":
return true;
default:
return false;
}
}
/**
* Add our listeners to the LoaderInfo object.
*/
protected function addListeners (info :LoaderInfo) :void
{
info.addEventListener(Event.COMPLETE, handleComplete);
info.addEventListener(Event.INIT, handleInit);
info.addEventListener(IOErrorEvent.IO_ERROR, handleError);
info.addEventListener(SecurityErrorEvent.SECURITY_ERROR, handleError);
info.addEventListener(ProgressEvent.PROGRESS, handleProgress);
}
/**
* Remove our listeners from the LoaderInfo object.
*/
protected function removeListeners (info :LoaderInfo) :void
{
info.removeEventListener(Event.COMPLETE, handleComplete);
info.removeEventListener(Event.INIT, handleInit);
info.removeEventListener(IOErrorEvent.IO_ERROR, handleError);
info.removeEventListener(SecurityErrorEvent.SECURITY_ERROR, handleError);
info.removeEventListener(ProgressEvent.PROGRESS, handleProgress);
}
/**
* A callback to receive IO_ERROR and SECURITY_ERROR events.
*/
protected function handleError (event :ErrorEvent) :void
{
stoppedLoading();
setupBrokenImage(-1, -1);
}
/**
* A callback to receive PROGRESS events.
*/
protected function handleProgress (event :ProgressEvent) :void
{
updateLoadingProgress(event.bytesLoaded, event.bytesTotal);
var info :LoaderInfo = (event.target as LoaderInfo);
try {
updateContentDimensions(info.width, info.height);
} catch (err :Error) {
// an error is thrown trying to access these props before they're
// ready
}
}
// /**
// * Handles video size known.
// */
// protected function handleVideoSizeKnown (event :ValueEvent) :void
// {
// const size :Point = Point(event.value);
// updateContentDimensions(size.x, size.y);
// }
// /**
// * Handles an error loading a video.
// */
// protected function handleVideoError (event :ValueEvent) :void
// {
// log.warning("Error loading video [cause=" + event.value + "].");
// stoppedLoading();
// setupBrokenImage(-1, -1);
// }
/**
* Handles the INIT event for content loaded with a Loader.
*/
protected function handleInit (event :Event) :void
{
_initialized = true;
// redispatch
dispatchEvent(event);
}
/**
* Callback function to receive COMPLETE events for swfs or images.
*/
protected function handleComplete (event :Event) :void
{
var info :LoaderInfo = (event.target as LoaderInfo);
removeListeners(info);
// attempt to make bitmaps look good
try {
if (info.loader.content is Bitmap) {
Bitmap(info.loader.content).smoothing = true;
}
} catch (err :Error) {
// cope
}
updateContentDimensions(info.width, info.height);
updateLoadingProgress(1, 1);
stoppedLoading();
// redispatch
dispatchEvent(event);
}
/**
* Handle shutting us down when we're removed. Only called if
* setShutdownOnRemove() was enabled.
*/
protected function handleShutdownOnRemove (event :Event) :void
{
shutdown();
}
/**
* Configure the mask for this object.
*/
protected function configureMask (rect :Rectangle) :void
{
var mask :Shape;
if (_media.mask != null) {
// see if the mask was added by us
try {
getChildIndex(_media.mask);
} catch (argErr :ArgumentError) {
// oy! We are not the controllers of this mask, it must
// have been added by someone else. This probably means
// that the _media is not a Loader, and so we should just
// leave it alone with its custom mask.
return;
}
// otherwise, it's a mask we previously configured
mask = (_media.mask as Shape);
} else {
mask = new Shape();
// the mask must be added to the display list (which is wacky)
addChildAt(mask, 0);
_media.mask = mask;
}
mask.graphics.clear();
mask.graphics.beginFill(0xFFFFFF);
mask.graphics.drawRect(rect.x, rect.y, rect.width, rect.height);
mask.graphics.endFill();
}
/**
* Called during loading as we figure out how big the content we're
* loading is.
*/
protected function updateContentDimensions (ww :int, hh :int) :void
{
// update our saved size, and possibly notify our container
if (_w != ww || _h != hh) {
_w = ww;
_h = hh;
contentDimensionsUpdated();
// dispatch an event to any listeners
dispatchEvent(new ValueEvent(SIZE_KNOWN, [ ww, hh ]));
}
}
/**
* Called when we know the true size of the content. Subclasses may override and use this
* opportunity to do their thing, too.
*/
protected function contentDimensionsUpdated () :void
{
// update the mask
var r :Rectangle = getMaskRectangle();
if (r != null) {
configureMask(r);
}
}
/**
* Get the mask area, or null if no mask is needed.
*/
protected function getMaskRectangle () :Rectangle
{
var maxW :int = getMaxContentWidth();
var maxH :int = getMaxContentHeight();
if ((maxW < int.MAX_VALUE) && (maxH < int.MAX_VALUE)) {
// if we should do masking, then mask to the lesser of maximum and actual size
return new Rectangle(0, 0, Math.min(maxW, _w), Math.min(maxH, _h));
}
return null;
}
/**
* Update the graphics to indicate how much is loaded.
*/
protected function updateLoadingProgress (soFar :Number, total :Number) :void
{
// nada, by default
}
/**
* Called when we've started loading new media. Will not be called
* for new media that does not require loading.
*/
protected function startedLoading () :void
{
// nada
}
/**
* Called when we've stopped loading, which may be as a result of
* completion, an error while loading, or early termination.
*/
protected function stoppedLoading () :void
{
// nada
}
/**
* Do whatever is necessary to shut down the media.
*/
protected function shutdownMedia () :void
{
if (_media is Loader) {
try {
var loader :Loader = (_media as Loader);
//var url :String = loader.contentLoaderInfo.url;
// remove any listeners
removeListeners(loader.contentLoaderInfo);
// dispose of media
LoaderUtil.unload(loader);
//var extra :String = (url == _url) ? "" : (", _url=" + _url);
//log.debug("Unloaded media [url=" + url + extra + "].");
} catch (ioe :IOError) {
log.warning("Error shutting down media", ioe);
}
} else if (_media is SimpleVideoDisplay) {
var vid :SimpleVideoDisplay = SimpleVideoDisplay(_media);
// vid.removeEventListener(MediaPlayerCodes.SIZE, handleVideoSizeKnown);
vid.unload();
}
}
/** The unaltered URL of the content we're displaying. */
protected var _url :String;
/** The unscaled width of our content. */
protected var _w :int;
/** The unscaled height of our content. */
protected var _h :int;
/** Either a Loader or a VideoDisplay. */
protected var _media :DisplayObject;
/** If we're using a Loader, true once the INIT event has been dispatched. */
protected var _initialized :Boolean;
/** Are we displaying an image? */
protected var _isImage :Boolean;
}
}
-49
View File
@@ -1,49 +0,0 @@
//
// $Id$
//
// Nenya library - tools for developing networked games
// Copyright (C) 2002-2007 Three Rings Design, Inc., All Rights Reserved
// http://www.threerings.net/code/nenya/
//
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation; either version 2.1 of the License, or
// (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
package com.threerings.flash {
import flash.ui.ContextMenuItem;
import flash.events.ContextMenuEvent;
import com.threerings.util.CommandEvent;
/**
*/
public class MenuUtil
{
/**
* Create a context menu item that will submit a command when selected.
*/
public static function createCommandContextMenuItem (
caption :String, cmdOrFn :Object, arg :Object = null,
separatorBefore :Boolean = false, enabled :Boolean = true) :ContextMenuItem
{
var item :ContextMenuItem = new ContextMenuItem(caption, separatorBefore, enabled);
item.addEventListener(ContextMenuEvent.MENU_ITEM_SELECT,
function (event :ContextMenuEvent) :void {
// mouseTarget may be null due to security reasons
CommandEvent.dispatch(event.mouseTarget || event.contextMenuOwner, cmdOrFn, arg);
});
return item;
}
}
}
@@ -1,49 +0,0 @@
//
// $Id$
package com.threerings.flash {
import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.display.SimpleButton;
import flash.geom.ColorTransform;
/**
* Takes a BitmapData and makes a button that brightens on hover and depresses when pushed.
*/
public class SimpleIconButton extends SimpleButton
{
/**
* Constructor. @see #setIcon()
*/
public function SimpleIconButton (icon :*)
{
setIcon(icon);
}
/**
* Update the icon for this button.
*
* @param icon a BitmapData, or Bitmap (from which the BitmapData will be extracted), or
* a Class that instantiates into either a BitmapData or Bitmap.
*/
public function setIcon (icon :*) :void
{
var bmp :BitmapData = ImageUtil.toBitmapData(icon);
if (bmp == null) {
throw new Error("Unknown icon spec: must be a Bitmap or BitmapData, or a Class " +
"that becomes one.");
}
const bright :ColorTransform = new ColorTransform(1.25, 1.25, 1.25);
upState = new Bitmap(bmp);
overState = new Bitmap(bmp);
overState.transform.colorTransform = bright;
downState = new Bitmap(bmp);
downState.y = 1;
downState.transform.colorTransform = bright;
hitTestState = upState;
}
}
}
@@ -1,76 +0,0 @@
//
// $Id$
package com.threerings.flash {
import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.display.Shape;
import flash.display.SimpleButton;
import flash.display.Sprite;
import flash.text.TextField;
import flash.text.TextFormat;
/**
* A simple skin that shifts the text in the down state.
*/
public class SimpleSkinButton extends SimpleButton
{
/**
* Create a SimpleSkinButton.
* @param skin a BitmapData, Bitmap, or class that will turn into either.
* @param text the text to place on the button
* @param textFieldProps initProps for the TextField
* @param textFormatProps initProps for the TextFormat
* @param outline if a uint, the color of the outline to put over the skin.
*/
public function SimpleSkinButton (
skin :*, text :String, textFieldProps :Object = null, textFormatProps :Object = null,
padding :int = 10, height :Number = NaN, xshift :int = 0, yshift :int = 1,
outline :Object = null)
{
var bmp :BitmapData = ImageUtil.toBitmapData(skin);
if (bmp == null) {
throw new Error("Skin must be a Bitmap, BitmapData, or Class");
}
upState = createState(bmp, text, textFieldProps, textFormatProps,
padding, height, 0, 0, outline);
overState = upState;
hitTestState = overState;
downState = createState(bmp, text, textFieldProps, textFormatProps,
padding, height, xshift, yshift, outline);
}
protected function createState (
bmp :BitmapData, text :String, textFieldProps :Object, textFormatProps :Object,
padding :int, height :Number, xshift :int, yshift :int, outline :Object) :Sprite
{
var state :Sprite = new Sprite();
var field :TextField = TextFieldUtil.createField(text, textFieldProps, textFormatProps);
if (isNaN(height)) {
height = field.height;
}
var skin :Bitmap = new Bitmap(bmp);
skin.width = field.width + (padding * 2);
skin.height = height;
state.addChild(skin);
field.x = padding + xshift;
field.y = (height - field.height) / 2 + yshift;
state.addChild(field);
if (outline != null) {
var border :Shape = new Shape();
border.graphics.lineStyle(1, uint(outline));
border.graphics.drawRect(0, 0, skin.width - 1, skin.height - 1);
state.addChild(border);
}
return state;
}
}
}
@@ -1,83 +0,0 @@
//
// $Id$
//
// Nenya library - tools for developing networked games
// Copyright (C) 2002-2007 Three Rings Design, Inc., All Rights Reserved
// http://www.threerings.net/code/nenya/
//
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation; either version 2.1 of the License, or
// (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
package com.threerings.flash {
import flash.display.Shape;
import flash.display.SimpleButton;
import flash.display.Sprite;
import flash.text.TextField;
import flash.text.TextFieldAutoSize;
import flash.text.TextFormat;
/**
* Displays a simple button with a rounded rectangle or rectangle for a face.
*/
public class SimpleTextButton extends SimpleButton
{
public function SimpleTextButton (
text :String, rounded :Boolean = true, foreground :uint = 0x003366,
background :uint = 0x6699CC, highlight :uint = 0x0066FF, padding :int = 5,
textFormat :TextFormat = null)
{
upState = makeFace(text, rounded, foreground, background, padding, textFormat);
overState = makeFace(text, rounded, highlight, background, padding, textFormat);
downState = makeFace(text, rounded, background, highlight, padding, textFormat);
hitTestState = upState;
}
protected function makeFace (
text :String, rounded :Boolean, foreground :uint, background :uint,
padding :int, textFormat :TextFormat) :Sprite
{
var face :Sprite = new Sprite();
// create the label so that we can measure its size
var label :TextField = new TextField();
if (textFormat) {
label.defaultTextFormat = textFormat;
}
label.text = text;
label.textColor = foreground;
label.autoSize = TextFieldAutoSize.LEFT;
face.addChild(label);
var w :Number = label.width + 2 * padding;
var h :Number = label.height + 2 * padding;
// draw our button background (and outline)
face.graphics.beginFill(background);
face.graphics.lineStyle(1, foreground);
if (rounded) {
face.graphics.drawRoundRect(0, 0, w, h, padding, padding);
} else {
face.graphics.drawRect(0, 0, w, h);
}
face.graphics.endFill();
label.x = padding;
label.y = padding;
return face;
}
}
}
-113
View File
@@ -1,113 +0,0 @@
//
// $Id$
//
// Nenya library - tools for developing networked games
// Copyright (C) 2002-2007 Three Rings Design, Inc., All Rights Reserved
// http://www.threerings.net/code/nenya/
//
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation; either version 2.1 of the License, or
// (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
package com.threerings.flash {
import flash.utils.getTimer;
/**
* Tracks multiple sine waves with different periods and amplitudes, and
* returns an instantaneous additive amplitude.
*/
public class Siner
{
/**
* @param args: amplitude1, period1, amplitude2, period2...
* Periods are specified in seconds.
*
* If constructed with more than one amplitude, the amplitudes are
* additive.
*
* The Siner will start in the reset() state.
*/
public function Siner (... args)
{
if (args.length == 0 || (args.length % 2 != 0)) {
throw new ArgumentError();
}
while (args.length > 0) {
if (!(args[0] is Number) || !(args[1] is Number)) {
throw new ArgumentError();
}
var amp :Number = args.shift();
var per :Number = args.shift();
_incs.push(TWO_PI / (per * 1000));
_amps.push(amp);
}
reset();
}
/**
* Reset to 0, with the amplitude about to increase.
*/
public function reset () :void
{
for (var ii :int = _amps.length - 1; ii >= 0; ii--) {
_values[ii] = 3 * Math.PI / 2;
}
_stamp = getTimer();
}
/**
* Randomize the value.
*/
public function randomize () :void
{
for (var ii :int = _amps.length - 1; ii >= 0; ii--) {
_values[ii] = Math.random() * TWO_PI;
}
}
/**
* Access the instantaneous value, which can range from
* [ -totalAmplitude, totalAmplitude ].
*
* Note: timestamps are only kept relative to the last access of the value,
* and floating point math is used, so things could get a little "off" after
* a while, and the frequency with which you sample the value will impact
* the error. You cope.
*/
public function get value () :Number
{
var now :Number = getTimer();
var elapsed :Number = now - _stamp;
_stamp = now;
var accum :Number = 0;
for (var ii :int = _values.length - 1; ii >= 0; ii--) {
var val :Number = (_values[ii] + (_incs[ii] * elapsed)) % TWO_PI;
_values[ii] = val;
accum += _amps[ii] * Math.sin(val);
}
return accum;
}
protected var _values :Array = [];
protected var _incs :Array = [];
protected var _amps :Array = [];
protected var _stamp :Number;
protected static const TWO_PI :Number = 2 * Math.PI;
}
}
@@ -1,38 +0,0 @@
//
// $Id$
//
// Nenya library - tools for developing networked games
// Copyright (C) 2002-2007 Three Rings Design, Inc., All Rights Reserved
// http://www.threerings.net/code/nenya/
//
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation; either version 2.1 of the License, or
// (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
package com.threerings.flash {
import flash.text.TextFormat;
public class SiningTextAnimation extends TextCharAnimation
{
public function SiningTextAnimation (text :String, textArgs :Object)
{
super(text, movementFn, textArgs);
}
protected function movementFn (elapsed :Number, index :Number) :Number
{
return 10 * Math.sin((elapsed / 500) + (index * Math.PI / 5));
}
}
}
@@ -1,76 +0,0 @@
//
// $Id$
//
// Nenya library - tools for developing networked games
// Copyright (C) 2002-2007 Three Rings Design, Inc., All Rights Reserved
// http://www.threerings.net/code/nenya/
//
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation; either version 2.1 of the License, or
// (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
package com.threerings.flash {
import flash.display.Sprite;
import flash.events.Event;
import flash.text.TextField;
import flash.text.TextFieldAutoSize;
import flash.text.TextFormat;
import flash.utils.getTimer; // function import
public class TextCharAnimation extends Sprite
implements Animation
{
public function TextCharAnimation (text :String, fn :Function, textArgs :Object)
{
_fn = fn;
var alignment :Sprite = new Sprite();
var w :Number = 0;
var h :Number = 0;
var tf :TextField;
for (var ii :int = 0; ii < text.length; ii++) {
tf = TextFieldUtil.createField(text.charAt(ii), textArgs);
tf.x = w;
w += tf.width;
h = Math.max(h, tf.height);
alignment.addChild(tf);
_texts.push(tf);
}
alignment.x = -(w/2);
alignment.y = -(h/2);
addChild(alignment);
AnimationManager.addDisplayAnimation(this);
}
// from Animation
public function updateAnimation (elapsed :Number) :void
{
for (var ii :int = 0; ii < _texts.length; ii++) {
_texts[ii].y = _fn(elapsed, ii);
}
}
protected var _texts :Array = [];
protected var _fn :Function;
}
}
@@ -1,296 +0,0 @@
//
// $Id$
//
// Nenya library - tools for developing networked games
// Copyright (C) 2002-2007 Three Rings Design, Inc., All Rights Reserved
// http://www.threerings.net/code/nenya/
//
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation; either version 2.1 of the License, or
// (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
package com.threerings.flash {
import com.threerings.util.StringUtil;
import com.threerings.util.Util;
import flash.display.Sprite;
import flash.events.Event;
import flash.events.MouseEvent;
import flash.filters.GlowFilter;
import flash.system.Capabilities;
import flash.system.System;
import flash.text.TextField;
import flash.text.TextFieldAutoSize;
import flash.text.TextFormat;
import flash.utils.Dictionary;
public class TextFieldUtil
{
/** A fudge factor that must be added to a TextField's textWidth when setting the width. */
public static const WIDTH_PAD :int = 5;
/** A fudge factor that must be added to a TextField's textHeight when setting the height. */
public static const HEIGHT_PAD :int = 4;
/**
* Ensures that a single-line TextField is not wider than the specified width, and
* truncates it with the truncation string if is. If the TextField is truncated,
* it will be resized to its new textWidth.
*
* @param width the maximum pixel width of the TextField. If tf.width > width,
* the text inside the TextField will be truncated, and will have the truncation string
* appended.
* @param truncationString the string to append to the end of the TextField if it exceeds
* the specified width.
* @return true if truncation took place
*/
public static function setMaximumTextWidth (
tf :TextField, width :Number, truncationString :String = "...") :Boolean
{
if (tf.numLines > 1) {
// We only operate on single-line TextFields
return false;
}
// TextField.textWidth doesn't account for scale, so account for it here
width /= tf.scaleX;
var truncated :Boolean;
var setText :String = tf.text;
while (tf.textWidth + WIDTH_PAD > width && setText.length > 0) {
// Drop characters from our string until we hit our target width.
// Flash doesn't appear to provide a nicer way to get text metrics than
// sticking stuff in a TextField and calling textWidth.
setText = setText.substr(0, setText.length - 1);
tf.text = setText + truncationString;
truncated = true;
}
if (truncated) {
// strip whitespace characters from the end of the truncated string
setText = StringUtil.trimEnd(setText);
tf.text = setText + truncationString;
// resize the TextField
tf.width = tf.textWidth + WIDTH_PAD;
tf.height = tf.textHeight + HEIGHT_PAD;
}
return truncated;
}
/**
* Create a TextField.
* The field will have setFocusable() called on it.
* Note that if the autoSize property is not none, then the field will be resized
* to the size of the text, overwriting any width/height properties specified.
*
* @param initProps contains properties with which to initialize the TextField.
* Additionally it may contain the following properties:
* outlineColor: uint
* @param formatProps contains properties with which to initialize the defaultTextFormat.
*/
public static function createField (
text :String, initProps :Object = null, formatProps :Object = null, clazz :Class = null)
:TextField
{
var tf :TextField = (clazz == null) ? new TextField() : TextField(new clazz());
if ((initProps != null) && ("outlineColor" in initProps)) {
tf.filters = [ new GlowFilter(uint(initProps["outlineColor"]), 1, 2, 2, 255) ];
}
Util.init(tf, initProps, null, MASK_FIELD_PROPS);
if (formatProps != null) {
tf.defaultTextFormat = createFormat(formatProps);
}
updateText(tf, text);
setFocusable(tf);
return tf;
}
/**
* Create a TextFormat using initProps.
* If unspecified, the following properties have default values:
* size: 18
* font: _sans
*/
public static function createFormat (initProps :Object) :TextFormat
{
var f :TextFormat = new TextFormat();
Util.init(f, initProps, DEFAULT_FORMAT_PROPS);
return f;
}
/**
* Update the defaultTextFormat for the specified field, as well as all text therein.
*/
public static function updateFormat (field :TextField, props :Object) :void
{
var f :TextFormat = field.defaultTextFormat; // this gets a clone of the default fmt
Util.init(f, props); // update the clone
field.defaultTextFormat = f; // set it as the new default
updateText(field, field.text); // jiggle the text to update it
}
/**
* Update the text in the field, automatically resizing it if appropriate.
*/
public static function updateText (field :TextField, text :String) :void
{
field.text = text;
if (field.autoSize != TextFieldAutoSize.NONE) {
field.width = field.textWidth + WIDTH_PAD;
field.height = field.textHeight + HEIGHT_PAD;
}
}
/**
* Add a special MouseEvent.CLICK listener so that the specified field is focusable
* even inside a security boundary.
*/
public static function setFocusable (field :TextField) :void
{
field.addEventListener(MouseEvent.CLICK, handleFieldFocus);
}
/**
* Include the specified TextField in a set of TextFields in which only
* one may have a selection at a time.
*/
public static function trackSingleSelectable (textField :TextField) :void
{
textField.addEventListener(MouseEvent.MOUSE_MOVE, handleTrackedSelection);
// immediately put the kibosh on any selection
textField.setSelection(0, 0);
}
/**
* Install listeners on the specified TextField such that the mouseEnabled property
* is only true when the mouse is over a link.
*/
public static function trackOnlyLinksMouseable (textField :TextField, on :Boolean = true) :void
{
if (on) {
_mouseables[textField] = true;
// always add the listener, seems easier than checking to see.
_frameDispatcher.addEventListener(Event.ENTER_FRAME, handleTrackMouseable);
} else {
delete _mouseables[textField];
}
}
/**
* Internal method related to tracking a single selectable TextField.
*/
protected static function handleTrackedSelection (event :MouseEvent) :void
{
if (event.buttonDown) {
var field :TextField = event.target as TextField;
if (field == _lastSelected) {
updateSelection(field);
} else if (field.selectionBeginIndex != field.selectionEndIndex) {
// clear the last one..
if (_lastSelected != null) {
handleLastSelectedRemoved();
}
_lastSelected = field;
_lastSelected.addEventListener(Event.REMOVED_FROM_STAGE, handleLastSelectedRemoved);
updateSelection(field);
}
}
}
/**
* Checks every damn frame to see if we should make a TextField mouse-enabled.
*/
protected static function handleTrackMouseable (event :Event) :void
{
var seen :Boolean = false;
for (var f :* in _mouseables) {
var field :TextField = f; // fucking hack of a language doesn't iterate non-string keys
seen = true;
if (field.stage != null) {
var charIndex :int = field.getCharIndexAtPoint(field.mouseX, field.mouseY);
field.mouseEnabled = (charIndex >= 0) && (charIndex < field.length) &&
!StringUtil.isBlank(field.getTextFormat(charIndex).url);
}
}
if (!seen) {
// try to remove the listener as soon as we can
_frameDispatcher.removeEventListener(Event.ENTER_FRAME, handleTrackMouseable);
}
}
/**
* Process the selection.
*/
protected static function updateSelection (field :TextField) :void
{
if (-1 != Capabilities.os.indexOf("Linux")) {
var str :String = field.text.substring(
field.selectionBeginIndex, field.selectionEndIndex);
System.setClipboard(str);
}
}
/**
* Internal method related to tracking a single selectable TextField.
*/
protected static function handleLastSelectedRemoved (... ignored) :void
{
_lastSelected.setSelection(0, 0);
_lastSelected.removeEventListener(Event.REMOVED_FROM_STAGE, handleLastSelectedRemoved);
_lastSelected = null;
}
/**
* Handle focusing the text field.
*/
protected static function handleFieldFocus (event :MouseEvent) :void
{
var tf :TextField = TextField(event.currentTarget);
if (tf.stage.focus == tf) {
return; // already has focus, bail.
}
// else, assign focus
tf.stage.focus = tf;
// try to be smart and move the cursor near the click
if (tf.selectable) {
var idx :int = tf.getCharIndexAtPoint(event.localX, event.localY);
if (idx != -1) {
tf.setSelection(idx, idx);
}
}
}
/** The last tracked TextField to be selected. */
protected static var _lastSelected :TextField;
protected static var _mouseables :Dictionary = new Dictionary(true); // weak keys
protected static const _frameDispatcher :Sprite = new Sprite();
protected static const MASK_FIELD_PROPS :Object = { outlineColor: true };
protected static const DEFAULT_FORMAT_PROPS :Object = { size: 18, font: "_sans" };
}
}
-347
View File
@@ -1,347 +0,0 @@
//
// $Id$
//
// Nenya library - tools for developing networked games
// Copyright (C) 2002-2007 Three Rings Design, Inc., All Rights Reserved
// http://www.threerings.net/code/nenya/
//
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation; either version 2.1 of the License, or
// (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
package com.threerings.flash {
import flash.geom.Point;
/**
* Basic 2D vector implementation.
*/
public class Vector2
{
public var x :Number = 0;
public var y :Number = 0;
/**
* Infinite vector - often the result of normalizing a zero vector.
*/
public static const INFINITE :Vector2 = new Vector2(Infinity, Infinity);
/**
* Converts Point p to a Vector2.
*/
public static function fromPoint (p :Point) :Vector2
{
return new Vector2(p.x, p.y);
}
/**
* Creates a new Vector2 pointing from s to t.
*/
public static function fromPoints (s :Point, t :Point) :Vector2
{
return new Vector2(t.x - s.x, t.y - s.y);
}
/**
* Creates a Vector2 of magnitude 'len' that has been rotated about the origin by 'radians'.
*/
public static function fromAngle (radians :Number, len :Number = 1) :Vector2
{
// we use the unit vector (1, 0)
return new Vector2(
Math.cos(radians) * len, // == len * (cos(theta)*x - sin(theta)*y)
Math.sin(radians) * len); // == len * (sin(theta)*x + cos(theta)*y)
}
/**
* Constructs a Vector2 from the given values.
*/
public function Vector2 (x :Number = 0, y :Number = 0)
{
this.x = x;
this.y = y;
}
/**
* Sets the vector's components to the given values.
*/
public function set (x :Number, y :Number) :void
{
this.x = x;
this.y = y;
}
/**
* Returns the dot product of this vector with vector v.
*/
public function dot (v :Vector2) :Number
{
return x * v.x + y * v.y;
}
/**
* Converts the Vector2 to a Point.
*/
public function toPoint () :Point
{
return new Point(x, y);
}
/**
* Returns a copy of this Vector2.
*/
public function clone () :Vector2
{
return new Vector2(x, y);
}
/**
* Returns the angle represented by this Vector2, in radians.
*/
public function get angle () :Number
{
var angle :Number = Math.atan2(y, x);
return (angle >= 0 ? angle : angle + (2 * Math.PI));
}
/**
* Returns this vector's length.
*/
public function get length () :Number
{
return Math.sqrt(x * x + y * y);
}
/**
* Sets this vector's length.
*/
public function set length (newLen :Number) :void
{
var scale :Number = newLen / this.length;
x *= scale;
y *= scale;
}
/**
* Returns the square of this vector's length.
*/
public function get lengthSquared () :Number
{
return (x * x + y * y);
}
/**
* Rotates the vector in place by 'radians'.
* Returns a reference to 'this', for chaining.
*/
public function rotateLocal (radians :Number) :Vector2
{
var cosTheta :Number = Math.cos(radians);
var sinTheta :Number = Math.sin(radians);
var oldX :Number = x;
x = (cosTheta * oldX) - (sinTheta * y);
y = (sinTheta * oldX) + (cosTheta * y);
return this;
}
/**
* Returns a rotated copy of this vector.
*/
public function rotate (radians :Number) :Vector2
{
return this.clone().rotateLocal(radians);
}
/**
* Normalizes the vector in place and returns its original length.
*/
public function normalizeLocalAndGetLength () :Number
{
var length :Number = this.length;
x /= length;
y /= length;
return length;
}
/**
* Normalizes this vector in place.
* Returns a reference to 'this', for chaining.
*/
public function normalizeLocal () :Vector2
{
var lengthInverse :Number = 1 / this.length;
x *= lengthInverse;
y *= lengthInverse;
return this;
}
/**
* Returns a normalized copy of the vector.
*/
public function normalize () :Vector2
{
return this.clone().normalizeLocal();
}
/**
* Adds another Vector2 to this, in place.
* Returns a reference to 'this', for chaining.
*/
public function addLocal (v :Vector2) :Vector2
{
x += v.x;
y += v.y;
return this;
}
/**
* Returns a copy of this vector added to 'v'.
*/
public function add (v :Vector2) :Vector2
{
return this.clone().addLocal(v);
}
/**
* Subtracts another vector from this one, in place.
* Returns a reference to 'this', for chaining.
*/
public function subtractLocal (v :Vector2) :Vector2
{
x -= v.x;
y -= v.y;
return this;
}
/**
* Returns (this - v).
*/
public function subtract (v :Vector2) :Vector2
{
return this.clone().subtractLocal(v);
}
/**
* Returns a vector that is perpendicular to this one.
* If ccw = true, the perpendicular vector is rotated 90 degrees counter-clockwise from this
* vector, otherwise it's rotated 90 degrees clockwise.
*/
public function getPerp (ccw :Boolean = true) :Vector2
{
if (ccw) {
return new Vector2(-y, x);
} else {
return new Vector2(y, -x);
}
}
/**
* Scales this vector by value.
*/
public function scaleLocal (value :Number) :Vector2
{
x *= value;
y *= value;
return this;
}
/** Returns (this * value). */
public function scale (value :Number) :Vector2
{
return this.clone().scaleLocal(value);
}
/**
* Inverts the vector.
*/
public function invertLocal () :Vector2
{
x = -x;
y = -y;
return this;
}
/**
* Returns a copy of the vector, inverted.
*/
public function invert () :Vector2
{
return this.clone().invertLocal();
}
/**
* Returns true if this vector is equal to v.
*/
public function equals (v :Vector2) :Boolean
{
return (x == v.x && y == v.y);
}
/**
* Returns true if the components of v are equal to the components of this Vector2,
* within the given epsilon.
*/
public function similar (v :Vector2, epsilon :Number) :Boolean
{
return ((Math.abs(x - v.x) <= epsilon) && (Math.abs(y - v.y) <= epsilon));
}
/**
* Returns a new vector that is the linear interpolation of vectors a and b
* at proportion p, where p is in [0, 1], p = 0 means the result is equal to a,
* and p = 1 means the result is equal to b.
*/
public static function interpolate (a :Vector2, b :Vector2, p :Number) :Vector2
{
// todo: maybe convert this into a non-static function, to fit the rest of the class?
var q :Number = 1 - p;
return new Vector2(q * a.x + p * b.x,
q * a.y + p * b.y);
}
/**
* Returns the smaller of the two angles between v1 and v2, in radians.
* Result will be in range [0, pi].
*/
public static function smallerAngleBetween (v1 :Vector2, v2 :Vector2) :Number
{
// v1 dot v2 == |v1||v2|cos(theta)
// theta = acos ((v1 dot v2) / (|v1||v2|))
var dot :Number = v1.dot(v2);
var len1 :Number = v1.length;
var len2 :Number = v2.length;
return Math.acos(dot / (len1 * len2));
}
/** Returns a string representation of the Vector2. */
public function toString () :String
{
return "[" + x + ", " + y + "]";
}
}
}
-215
View File
@@ -1,215 +0,0 @@
//
// $Id$
//
// Nenya library - tools for developing networked games
// Copyright (C) 2002-2007 Three Rings Design, Inc., All Rights Reserved
// http://www.threerings.net/code/nenya/
//
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation; either version 2.1 of the License, or
// (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
package com.threerings.flash {
/**
* Basic 3D vector implementation.
*/
public class Vector3
{
/**
* Infinite vector - often the result of normalizing a zero vector, or intersecting
* a vector with a parallel plane.
*/
public static const INFINITE :Vector3 = new Vector3(Infinity, Infinity, Infinity);
/** Vector components. */
public var x :Number = 0;
public var y :Number = 0;
public var z :Number = 0;
/** Creates a new vector. All three X, Y, Z parameters are optional. */
public function Vector3 (x :Number = 0, y :Number = 0, z :Number = 0)
{
set(x, y, z);
}
/** Assigns values to the three parameters of this vector. */
public function set (x :Number, y :Number, z :Number) :void
{
this.x = x;
this.y = y;
this.z = z;
}
/** Duplicates a vector. */
public function clone() :Vector3
{
return new Vector3(this.x, this.y, this.z);
}
/** Returns this vector's length. */
public function get length () :Number
{
return Math.sqrt(x * x + y * y + z * z);
}
/** Returns the dot product of this vector with vector v. */
public function dot (v :Vector3) :Number
{
return x * v.x + y * v.y + z * v.z;
}
/** Returns a new vector that is a normalized version of this vector. */
public function normalize () :Vector3
{
return this.clone().normalizeLocal();
}
/** Destructively normalizes this vector. Returns a reference to the modified self. */
public function normalizeLocal () :Vector3
{
var len :Number = this.length;
set(x / len, y / len, z / len);
return this;
}
/**
* Returns a new vector that is the cross product of this vector with vector v,
* such that <code>result = this &#8855; v</code>.
*/
public function cross (v :Vector3) :Vector3
{
return this.clone().crossLocal(v);
}
/**
* Sets this vector to the result of a cross product with vector v, such that
* <code>this = this &#8855; v</code>. Returns a reference to the modified self.
*/
public function crossLocal (v :Vector3) :Vector3
{
var xx :Number = y * v.z - z * v.y;
var yy :Number = z * v.x - x * v.z;
var zz :Number = x * v.y - y * v.x;
set(xx, yy, zz);
return this;
}
/** Returns a new vector that is the summation of this vector with vector v. */
public function add (v :Vector3) :Vector3
{
return this.clone().addLocal(v);
}
/**
* Sets this vector to the result of summation with v, such that <code>this = this + v</code>.
* Returns a reference to the modified self.
*/
public function addLocal (v :Vector3) :Vector3
{
set(x + v.x, y + v.y, z + v.z);
return this;
}
/** Returns a new vector that is the subtraction of vector v from this vector.*/
public function subtract (v :Vector3) :Vector3
{
return this.clone().subtractLocal(v);
}
/**
* Sets the vector to the result of subtraction of v, such that <code>this = this - v</code>.
* Returns a reference to the modified self.
*/
public function subtractLocal (v :Vector3) :Vector3
{
set(x - v.x, y - v.y, z - v.z);
return this;
}
/**
* Finds the intersection of a ray emitted from s along this vector,
* with a plane passing through point p with normal n. Returns the point
* of intersection, potentially infinite if the ray and plane are parallel.
*/
public function intersection (s :Vector3, p :Vector3, n :Vector3) :Vector3
{
// formula: given ray from /s/ along vector /this/, and a plane passing
// through /p/ with normal /n/, we find intersection parameter as:
// r = (n dot this) / (n dot (p - s))
// and the intersection point is:
// s' = s + r * this
var rray :Number = p.subtract(s).dot(n);
var rplane :Number = this.dot(n);
if (rplane == 0) {
return INFINITE; // the two shall never meet
} else {
var r :Number = rray / rplane;
return s.add(this.scale(r));
}
}
/**
* Returns a new vector that is the result of multiplying the current vector
* by the specified scalar.
*/
public function scale (value :Number) :Vector3
{
return this.clone().scaleLocal(value);
}
/**
* Destructively multiplies this vector by the specified scalar.
* Returns a reference to the modified self.
*/
public function scaleLocal (value :Number) :Vector3
{
set(x * value, y * value, z * value);
return this;
}
/**
* Returns a new vector that is a copy of this vector, with each coordinate clamped
* to within [0, 1]. Please note that this obviously does not preserve the
* vector's original direction in space.
*/
public function clampToUnitBox () :Vector3
{
return new Vector3(Math.min(Math.max(x, 0), 1),
Math.min(Math.max(y, 0), 1),
Math.min(Math.max(z, 0), 1));
}
/**
* Returns a new vector that is the linear interpolation of vectors a and b
* at proportion p, where p is in [0, 1], p = 0 means the result is equal to a,
* and p = 1 means the result is equal to b.
*/
public static function interpolate (a :Vector3, b :Vector3, p :Number) :Vector3
{
// todo: maybe convert this into a non-static function, to fit the rest of the class?
var q :Number = 1 - p;
return new Vector3(q * a.x + p * b.x,
q * a.y + p * b.y,
q * a.z + p * b.z);
}
public function toString () :String
{
return "[" + x + ", " + y + ", " + z + "]";
}
}
}
@@ -1,13 +0,0 @@
//
// $Id$
package com.threerings.flash.media {
/**
* Implemented by audio-playing backends.
*/
public interface AudioPlayer extends MediaPlayer
{
// surely we'll think of something unique to audio. :)
}
}
@@ -1,347 +0,0 @@
//
// $Id$
package com.threerings.flash.media {
import flash.display.DisplayObject;
import flash.events.AsyncErrorEvent;
import flash.events.IOErrorEvent;
import flash.events.ErrorEvent;
import flash.events.Event;
import flash.events.EventDispatcher;
import flash.events.NetStatusEvent;
import flash.events.SecurityErrorEvent;
import flash.events.TimerEvent;
import flash.geom.Point;
import flash.media.SoundTransform;
import flash.media.Video;
import flash.net.NetConnection;
import flash.net.NetStream;
import flash.utils.Timer;
import com.threerings.util.Log;
import com.threerings.util.MethodQueue;
import com.threerings.util.ValueEvent;
public class FlvVideoPlayer extends EventDispatcher
implements VideoPlayer
{
public function FlvVideoPlayer (autoPlay :Boolean = true)
{
_autoPlay = autoPlay;
_sizeChecker = new Timer(100);
_sizeChecker.addEventListener(TimerEvent.TIMER, handleSizeCheck);
_positionChecker = new Timer(250);
_positionChecker.addEventListener(TimerEvent.TIMER, handlePositionCheck);
}
/**
* Start playing a video!
*/
public function load (url :String) :void
{
_netCon = new NetConnection();
_netCon.addEventListener(NetStatusEvent.NET_STATUS, handleNetStatus);
// error handlers
_netCon.addEventListener(AsyncErrorEvent.ASYNC_ERROR, handleAsyncError);
_netCon.addEventListener(IOErrorEvent.IO_ERROR, handleIOError);
_netCon.addEventListener(SecurityErrorEvent.SECURITY_ERROR, handleSecurityError);
_netCon.connect(null);
_netStream = new NetStream(_netCon);
// pass in refs to some of our protected methods
_netStream.client = {
onMetaData: handleMetaData
};
_netStream.soundTransform = new SoundTransform(_volume);
_netStream.addEventListener(NetStatusEvent.NET_STATUS, handleStreamNetStatus);
_vid.attachNetStream(_netStream);
_sizeChecker.start();
_netStream.play(url);
if (_autoPlay) {
updateState(MediaPlayerCodes.STATE_PLAYING);
} else {
_netStream.pause();
updateState(MediaPlayerCodes.STATE_READY);
}
}
// from VideoPlayer
public function getDisplay () :DisplayObject
{
return _vid;
}
// from VideoPlayer
public function getState () :int
{
return _state;
}
// from VideoPlayer
public function getSize () :Point
{
return _size.clone();
}
// from VideoPlayer
public function play () :void
{
if (_netStream != null) {
_netStream.resume();
updateState(MediaPlayerCodes.STATE_PLAYING);
}
// TODO: throw an error if _netStream == null??
}
// from VideoPlayer
public function pause () :void
{
if (_netStream != null) {
_netStream.pause();
updateState(MediaPlayerCodes.STATE_PAUSED);
}
}
// from VideoPlayer
public function getDuration () :Number
{
return _duration;
}
// from VideoPlayer
public function getPosition () :Number
{
if (_netStream != null) {
return _netStream.time;
}
return NaN;
}
// from VideoPlayer
public function seek (position :Number) :void
{
if (_netStream != null) {
_netStream.seek(position);
}
}
// from VideoPlayer
public function getVolume () :Number
{
return _volume;
}
// from VideoPlayer
public function setVolume (volume :Number) :void
{
_volume = Math.max(0, Math.min(1, volume));
if (_netStream != null) {
_netStream.soundTransform = new SoundTransform(_volume);
}
}
// from VideoPlayer
public function getMetadata () :Object
{
return _metadata;
}
// from VideoPlayer
public function unload () :void
{
_metadata = null;
_duration = NaN;
_gotDurationFromMetadata = false;
_sizeChecker.reset();
_positionChecker.reset();
_vid.attachNetStream(null);
if (_netStream != null) {
_netStream.close();
_netStream.removeEventListener(NetStatusEvent.NET_STATUS, handleStreamNetStatus);
_netStream = null;
}
if (_netCon != null) {
_netCon.close();
_netCon.removeEventListener(NetStatusEvent.NET_STATUS, handleNetStatus);
_netCon.removeEventListener(AsyncErrorEvent.ASYNC_ERROR, handleAsyncError);
_netCon.removeEventListener(IOErrorEvent.IO_ERROR, handleIOError);
_netCon.removeEventListener(SecurityErrorEvent.SECURITY_ERROR, handleSecurityError);
_netCon = null;
}
}
/**
* Check to see if we now know the dimensions of the video.
*/
protected function handleSizeCheck (event :TimerEvent) :void
{
if (_vid.videoWidth == 0 || _vid.videoHeight == 0) {
return; // not known yet!
}
// stop the checker timer
_sizeChecker.stop();
_size = new Point(_vid.videoWidth, _vid.videoHeight);
// set up the width/height
_vid.width = _size.x;
_vid.height = _size.y;
dispatchEvent(new ValueEvent(MediaPlayerCodes.SIZE, _size.clone()));
}
protected function handlePositionCheck (event :TimerEvent = null) :void
{
const pos :Number = getPosition();
if (!_gotDurationFromMetadata && (isNaN(_duration) || (pos > _duration))) {
_duration = pos;
dispatchEvent(new ValueEvent(MediaPlayerCodes.DURATION, _duration));
}
dispatchEvent(new ValueEvent(MediaPlayerCodes.POSITION, pos));
}
protected function handleNetStatus (event :NetStatusEvent) :void
{
var info :Object = event.info;
if ("error" == info.level) {
log.warning("NetStatus error: " + info.code);
dispatchEvent(new ValueEvent(MediaPlayerCodes.ERROR, info.code));
return;
}
// else info.level == "status"
switch (info.code) {
case "NetConnection.Connect.Success":
case "NetConnection.Connect.Closed":
// these status events we ignore
break;
default:
log.info("NetStatus status: " + info.code);
break;
}
}
protected function handleStreamNetStatus (event :NetStatusEvent) :void
{
// log.debug("NetStatus", "level", event.info.level, "code", event.info.code);
switch (event.info.code) {
case "NetStream.Play.Stop":
if (_state == MediaPlayerCodes.STATE_PLAYING) {
handlePositionCheck(); // if we never got metadata, retrieve final position as dur
// rewind to the beginning
_netStream.seek(0);
_netStream.pause();
updateState(MediaPlayerCodes.STATE_STOPPED);
}
break;
case "NetStream.Seek.Notify":
MethodQueue.callLater(handlePositionCheck);
break;
}
}
protected function handleAsyncError (event :AsyncErrorEvent) :void
{
redispatchError(event);
}
protected function handleIOError (event :IOErrorEvent) :void
{
redispatchError(event);
}
protected function handleSecurityError (event :SecurityErrorEvent) :void
{
redispatchError(event);
}
/**
* Redispatch some error we received to our listeners.
*/
protected function redispatchError (event :ErrorEvent) :void
{
log.warning("got video error: " + event);
dispatchEvent(new ValueEvent(MediaPlayerCodes.ERROR, event.text));
}
/**
* Called when metadata (if any) is found in the video stream.
*/
protected function handleMetaData (info :Object) :void
{
if ("duration" in info) {
_duration = Number(info.duration);
_gotDurationFromMetadata = true;
if (!isNaN(_duration)) {
dispatchEvent(new ValueEvent(MediaPlayerCodes.DURATION, _duration));
}
}
_metadata = info;
dispatchEvent(new ValueEvent(MediaPlayerCodes.METADATA, info));
}
protected function updateState (newState :int) :void
{
const oldState :int = _state;
_state = newState;
if (_state == MediaPlayerCodes.STATE_PLAYING) {
_positionChecker.start();
} else {
_positionChecker.reset();
if (oldState == MediaPlayerCodes.STATE_PLAYING) {
handlePositionCheck();
}
}
dispatchEvent(new ValueEvent(MediaPlayerCodes.STATE, newState));
}
protected const log :Log = Log.getLog(this);
protected var _autoPlay :Boolean;
protected var _vid :Video = new Video();
protected var _netCon :NetConnection;
protected var _netStream :NetStream;
protected var _state :int = MediaPlayerCodes.STATE_UNREADY;
/** Our size, null until known. */
protected var _size :Point;
protected var _duration :Number = NaN;
protected var _gotDurationFromMetadata :Boolean;
protected var _volume :Number = 1;
protected var _metadata :Object;
/** Checks the video every 100ms to see if the dimensions are now know.
* Yes, this is how to do it. We could trigger on ENTER_FRAME, but then
* we may not know the dimensions unless we're added on the display list,
* and we want this to work in the general case. */
protected var _sizeChecker :Timer;
protected var _positionChecker :Timer;
}
}
@@ -1,107 +0,0 @@
//
// $Id$
package com.threerings.flash.media {
import flash.events.IEventDispatcher;
import com.threerings.util.ValueEvent;
/**
* Dispatched when the state of the mediaplayer changes, usually in response to commands
* such as play/pause, etc.
* <b>value</b>: an int state. @see MediaPlayerCodes
*
* @eventType com.threerings.flash.media.MediaPlayerCodes.STATE
*/
[Event(name="state", type="com.threerings.util.ValueEvent")]
/**
* Dispatched when the total duration of the media is known.
* <b>value</b>: a Number expressing the duration in seconds.
*
* @eventType com.threerings.flash.media.MediaPlayerCodes.DURATION
*/
[Event(name="duration", type="com.threerings.util.ValueEvent")]
/**
* Dispatched periodically as the position is updated, during playback.
* <b>value</b>: a Number expressing the position in seconds.
*
* @eventType com.threerings.flash.media.MediaPlayerCodes.POSITION
*/
[Event(name="position", type="com.threerings.util.ValueEvent")]
/**
* Dispatched when media metadata is available, if ever.
* <b>value</b>: metadata object. @see getMetadata() for more info.
*
* @eventType com.threerings.flash.media.MediaPlayerCodes.METADATA
*/
[Event(name="metadata", type="com.threerings.util.ValueEvent")]
/**
* Dispatched when there's a problem.
* <b>value</b>: a String error code/message.
*
* @eventType com.threerings.flash.media.MediaPlayerCodes.ERROR
*/
[Event(name="error", type="com.threerings.util.ValueEvent")]
/**
* Implemented by media-playing backends.
*/
public interface MediaPlayer extends IEventDispatcher
{
/**
* @return a MediaPlayerCodes state constant.
*/
function getState () :int;
/**
* Play the media, if not already.
*/
function play () :void;
/**
* Pause the media, if not already.
*/
function pause () :void;
/**
* Get the duration of the media, in seconds, or NaN if not yet known.
*/
function getDuration () :Number;
/**
* Get the position of the media, in seconds, or NaN if not yet ready.
*/
function getPosition () :Number;
/**
* Seek to the specified position.
*/
function seek (position :Number) :void;
/**
* Set the volume, between 0-1.
*/
function setVolume (volume :Number) :void;
/**
* Get the volume, from 0 to 1.
*/
function getVolume () :Number;
/**
* Get metadata, or null if none or not yet available. The exact format of this data
* object varies by MediaPlayer implementation, see the documentation for each.
*/
function getMetadata () :Object;
/**
* Unload the media.
*/
function unload () :void;
}
}
@@ -1,34 +0,0 @@
//
// $Id$
package com.threerings.flash.media {
public class MediaPlayerCodes
{
public static const STATE :String = "state";
public static const DURATION :String = "duration";
public static const POSITION :String = "position";
public static const METADATA :String = "metadata";
/** Only applicable for VideoPlayer. */
public static const SIZE :String = "size";
public static const ERROR :String = "error";
/** Indicates we're still loading or initializing. */
public static const STATE_UNREADY :int = 0;
/** Indicates we're ready to play. */
public static const STATE_READY :int = 1;
public static const STATE_PLAYING :int = 2;
public static const STATE_PAUSED :int = 3;
/** Indicates that the playhead reached the end and the media has stopped. */
public static const STATE_STOPPED :int = 4;
}
}
@@ -1,292 +0,0 @@
//
// $Id: SoundPlayer.as 10500 2008-08-07 19:14:38Z mdb $
package com.threerings.flash.media {
import flash.errors.IOError;
import flash.events.Event;
import flash.events.EventDispatcher;
import flash.events.IOErrorEvent;
import flash.events.ProgressEvent;
import flash.events.TimerEvent;
import flash.media.Sound;
import flash.media.SoundChannel;
import flash.media.SoundLoaderContext;
import flash.media.SoundTransform;
import flash.net.URLRequest;
import flash.utils.Timer;
import com.threerings.util.Log;
import com.threerings.util.ValueEvent;
public class Mp3AudioPlayer extends EventDispatcher
implements AudioPlayer
{
public function Mp3AudioPlayer (loop :Boolean = false)
{
_loop = loop;
_positionChecker = new Timer(250);
_positionChecker.addEventListener(TimerEvent.TIMER, handlePositionCheck);
}
/**
* Load and immediately start playing some audio!
*/
public function load (url :String, clientData :Object = null) :void
{
_isComplete = false;
_lastPosition = 0;
_sound = new Sound();
_sound.addEventListener(Event.ID3, handleId3);
_sound.addEventListener(IOErrorEvent.IO_ERROR, handleError);
_sound.addEventListener(Event.COMPLETE, handleLoadingComplete);
_sound.load(new URLRequest(url), new SoundLoaderContext(1000, true));
_cliData = clientData;
play();
}
/**
* Get any client data associated with the media currently playing.
*/
public function getClientData () :Object
{
return _cliData;
}
// from AudioPlayer
public function getState () :int
{
return _state;
}
// from AudioPlayer
public function play () :void
{
play0();
updateState(
(_chan != null) ? MediaPlayerCodes.STATE_PLAYING : MediaPlayerCodes.STATE_PAUSED);
}
// from AudioPlayer
public function pause () :void
{
handlePositionCheck(); // update _lastPosition and dispatch an event
pause0();
updateState(MediaPlayerCodes.STATE_PAUSED);
}
// from AudioPlayer
public function getDuration () :Number
{
if (_sound == null) {
return NaN;
}
const duration :Number = _sound.length / 1000;
if (_isComplete) {
return duration;
}
// extrapolate the total duration based on the current length and current bytesLoaded
return (duration / _sound.bytesLoaded) * _sound.bytesTotal;
}
// from AudioPlayer
public function getPosition () :Number
{
if (_chan == null) {
return _lastPosition;
}
// we mod by the duration because when we loop the position is ever-growing. hahah!
return (_chan.position / 1000);
}
// from AudioPlayer
public function seek (position :Number) :void
{
_lastPosition = position;
if (_state == MediaPlayerCodes.STATE_PLAYING) {
pause0();
play0();
} else {
handlePositionCheck();
}
}
// from AudioPlayer
public function setVolume (volume :Number) :void
{
_volume = Math.max(0, Math.min(1, volume));
if (_chan != null) {
_chan.soundTransform = new SoundTransform(_volume);
}
}
// from AudioPlayer
public function getVolume () :Number
{
return _volume;
}
// from AudioPlayer
public function getMetadata () :Object
{
if (_sound != null) {
try {
return _sound.id3;
} catch (err :SecurityError) {
Log.getLog(this).warning("Can't read id3 data", err);
}
}
return null;
}
// from AudioPlayer
public function unload () :void
{
pause0();
if (_sound != null) {
try {
_sound.close();
} catch (err :IOError) {
// ignore
}
}
_sound = null;
_isComplete = false;
_lastPosition = NaN;
_cliData = null;
updateState(MediaPlayerCodes.STATE_UNREADY);
handlePositionCheck();
}
override public function addEventListener (
type :String, listener :Function, useCapture :Boolean = false,
priority :int = 0, useWeakReference :Boolean = false) :void
{
super.addEventListener(type, listener, useCapture, priority, useWeakReference);
if (type == MediaPlayerCodes.POSITION) {
checkNeedTimer();
}
}
override public function removeEventListener (
type :String, listener :Function, useCapture :Boolean = false) :void
{
super.removeEventListener(type, listener, useCapture);
if (type == MediaPlayerCodes.POSITION) {
checkNeedTimer();
}
}
protected function checkNeedTimer () :void
{
const needTimer :Boolean = (_state == MediaPlayerCodes.STATE_PLAYING) &&
hasEventListener(MediaPlayerCodes.POSITION);
if (needTimer != _positionChecker.running) {
if (needTimer) {
_positionChecker.start();
} else {
_positionChecker.reset();
}
}
}
/**
* Play without updating the current state.
*/
protected function play0 () :void
{
pause0();
_chan = _sound.play(_lastPosition * 1000, 0, new SoundTransform(_volume));
if (_chan != null) {
_chan.addEventListener(Event.SOUND_COMPLETE, handlePlaybackComplete);
} else {
Log.getLog(this).warning("All sound channels are in use; " +
"unable to play sound.");
}
}
/**
* Pause without saving the position or updating the state.
*/
protected function pause0 () :void
{
if (_chan != null) {
_chan.stop();
_chan = null;
}
}
protected function handlePositionCheck (event :TimerEvent = null) :void
{
if (!_isComplete) {
dispatchEvent(new ValueEvent(MediaPlayerCodes.DURATION, getDuration()));
}
_lastPosition = getPosition();
dispatchEvent(new ValueEvent(MediaPlayerCodes.POSITION, _lastPosition));
}
protected function updateState (newState :int) :void
{
_state = newState;
checkNeedTimer();
dispatchEvent(new ValueEvent(MediaPlayerCodes.STATE, newState));
}
protected function handleId3 (event :Event) :void
{
dispatchEvent(new ValueEvent(MediaPlayerCodes.METADATA, getMetadata()));
}
/**
*/
protected function handleLoadingComplete (event :Event) :void
{
_isComplete = true;
dispatchEvent(new ValueEvent(MediaPlayerCodes.DURATION, getDuration()));
}
protected function handlePlaybackComplete (event :Event) :void
{
_lastPosition = 0;
pause0();
handlePositionCheck();
if (_loop) {
play0();
} else {
updateState(MediaPlayerCodes.STATE_STOPPED);
}
}
protected function handleError (event :IOErrorEvent) :void
{
dispatchEvent(new ValueEvent(MediaPlayerCodes.ERROR, event.text));
}
protected var _sound :Sound;
protected var _cliData :Object;
protected var _chan :SoundChannel;
protected var _loop :Boolean;
protected var _state :int = MediaPlayerCodes.STATE_UNREADY;
protected var _volume :Number = 1;
protected var _isComplete :Boolean;
protected var _lastPosition :Number = NaN;
protected var _positionChecker :Timer;
}
}
@@ -1,106 +0,0 @@
//
// $Id#
package com.threerings.flash.media {
import flash.display.DisplayObject;
import flash.display.Graphics;
import flash.display.Shape;
import flash.display.Sprite;
import flash.events.MouseEvent;
import flash.geom.Point;
import com.threerings.util.ValueEvent;
/**
* An extremely simple video display. Click to pause/play.
*/
public class SimpleVideoDisplay extends Sprite
{
public static const NATIVE_WIDTH :int = 320;
public static const NATIVE_HEIGHT :int = 240;
/**
* Create a video displayer.
*/
public function SimpleVideoDisplay (player :VideoPlayer)
{
_player = player;
_player.addEventListener(MediaPlayerCodes.SIZE, handlePlayerSize);
addChild(_player.getDisplay());
var masker :Shape = new Shape();
this.mask = masker;
addChild(masker);
// create the mask...
var g :Graphics = masker.graphics;
g.clear();
g.beginFill(0xFFFFFF);
g.drawRect(0, 0, NATIVE_WIDTH, NATIVE_HEIGHT);
g.endFill();
addEventListener(MouseEvent.CLICK, handleClick);
}
override public function get width () :Number
{
return NATIVE_WIDTH;
}
override public function get height () :Number
{
return NATIVE_HEIGHT;
}
/**
* Stop playing our video.
*/
public function unload () :void
{
_player.unload();
}
protected function handleClick (event :MouseEvent) :void
{
// the buck stops here!
event.stopImmediatePropagation();
switch (_player.getState()) {
case MediaPlayerCodes.STATE_READY:
case MediaPlayerCodes.STATE_PAUSED:
_player.play();
break;
case MediaPlayerCodes.STATE_PLAYING:
_player.pause();
break;
default:
trace("Click while player in unhandled state: " + _player.getState());
break;
}
}
protected function handlePlayerSize (event :ValueEvent) :void
{
const size :Point = Point(event.value);
const disp :DisplayObject = _player.getDisplay();
// TODO: siggggghhhhh
// disp.scaleX = NATIVE_WIDTH / size.x;
// disp.scaleY = NATIVE_HEIGHT / size.y;
disp.width = NATIVE_WIDTH;
disp.height = NATIVE_HEIGHT;
// and, we redispatch
dispatchEvent(event);
}
protected var _player :VideoPlayer;
}
}
@@ -1,35 +0,0 @@
//
// $Id$
package com.threerings.flash.media {
import flash.display.DisplayObject;
import flash.geom.Point;
import com.threerings.util.ValueEvent;
/**
* Disptached when the size of the video is known.
* <b>value</b>: a Point expressing the width/height.
*
* @eventType com.threerings.flash.media.MediaPlayerCodes.SIZE
*/
[Event(name="size", type="com.threerings.util.ValueEvent")]
/**
* Implemented by video-playing backends.
*/
public interface VideoPlayer extends MediaPlayer
{
/**
* Get the actual visualization of the video.
*/
function getDisplay () :DisplayObject;
/**
* Get the size of the video, or null if not yet known.
*/
function getSize () :Point;
}
}
+1 -1
View File
@@ -9,7 +9,7 @@ import flash.text.TextField;
import mx.controls.TextInput; import mx.controls.TextInput;
import com.threerings.flash.TextFieldUtil; import com.threerings.text.TextFieldUtil;
/** /**
* The class name of an image to use as the input prompt. * The class name of an image to use as the input prompt.
+1 -1
View File
@@ -8,7 +8,7 @@ import flash.geom.Rectangle;
import mx.core.UIComponent; import mx.core.UIComponent;
import com.threerings.flash.DisplayUtil; import com.threerings.display.DisplayUtil;
/** /**
* Flex popup utilities. * Flex popup utilities.