/*
HueChange.hx
Jan Thor, 2009-09-24
A simple applet that can change the hue of an image (or part of it) constantly,
permanently or randomly.
The code is trivial enough that you can consider it to be in the public domain.
No warranty blahblahblah. Sue me, I dare you.
Parameters can be provided like this:
<object classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000"
width="256"
height="256"
id="haxe"
align="middle">
<param name="movie" value="HueChange.swf"/>
<param name="allowScriptAccess" value="always" />
<param name="quality" value="high" />
<param name="scale" value="noscale" />
<param name="salign" value="lt" />
<param name="bgcolor" value="#00ffff"/>
<param name="flashvars" value="click=yes&hue=120&step=1&delay=1&image=hueimage.png&overlay=hueoverlay.png&underlay=hueunderlay.png"/>
<embed src="HueChange.swf"
flashvars="click=yes&hue=120&step=1&delay=1&image=hueimage.png&overlay=hueoverlay.png&underlay=hueunderlay.png"
bgcolor="#00ffff"
width="256"
height="256"
name="haxe"
quality="high"
align="middle"
allowScriptAccess="always"
type="application/x-shockwave-flash"
pluginspage="http://www.macromedia.com/go/getflashplayer"
/>
</object>
Possible parameters are:
image = <pathname>
URL of an image ressource. This is the only parameter that must be
provided. If the image can't be loaded, bad things will happen.
underlay = <pathname>
If present, loads an image to be displayed _under_ the main image (the main
image should contain transparent areas).
overlay = <pathname>
If present, loads an image to be displayed _over_ the main image (this
image should contain transparent areas).
hue = <int>
default: 0
Integer between 0 and 360, describes the initial hue value. A value of
hue=0 means that nothing will be changed. For a value of hue=120, red
becomes green, green becomes blue and blue becomes red.
click = yes | no | <link>
default: no
yes: Mouseclick changes hue to a random value
no: Mouseclick does nothing
<link>: Mouseclick opens <link> in the current window.
delay = <int>
default: 0
Delay in 1/30 second between animation steps. If 0 (the default), no
animation takes place. A value of delay=1 means the animation takes place
with a frame rate of 30 FPS. A value of delay=30 means the animation takes
place with one frame per second.
step = <int>
default: 1
Change in hue for animation. If delay=0, this parameter has no effect.
Sensible values would be in the range -180 to 180. The value step=0 will
prevent the animation from being noticable. Values greater than +1 will
lead to jumps in the color space. For example, for step=60, the hue will
alternate between six values (0, 60, 120, 180, 240, 270). If step is
relative prime to 360, all possible hue values will be reached, but not
necessairily in order (try, for example, step=157 or step=179). For large
values of step, delay should also have a rather large value (like
delay=30), otherwise, the animation might show seizure inducing color
flashes (delay=1&step=180 would be a bad idea).
*/
class HueChange extends flash.display.Sprite {
var underlay:flash.display.DisplayObject;
var hueimage:flash.display.DisplayObject;
var overlay:flash.display.DisplayObject;
var myloader:flash.display.Loader;
var loadpos:Int;
var myHue:Int;
var delay:Int;
var step:Int;
var parm_click:String;
var parm_underlay:String;
var parm_image:String;
var parm_overlay:String;
var tick:Int;
static function main() {
/* The flow of command is rather linear and easy to follow:
* main -> preload -> bmploaded -> init
*/
var app:HueChange = new HueChange();
flash.Lib.current.addChild(app);
app.preload();
}
/* Helper function for parameters with defaults */
inline function dparm(param, defparam) {
if (param == null) {return defparam;} else {return param;}
}
function preload() {
/* Preparations before loading the images */
stage.align = flash.display.StageAlign.TOP_LEFT;
stage.scaleMode = flash.display.StageScaleMode.NO_SCALE;
parm_click = dparm(flash.Lib.current.loaderInfo.parameters.click, "no");
myHue = Std.parseInt(dparm(flash.Lib.current.loaderInfo.parameters.hue, "0"));
delay = Std.parseInt(dparm(flash.Lib.current.loaderInfo.parameters.delay, "0"));
step = Std.parseInt(dparm(flash.Lib.current.loaderInfo.parameters.step, "1"));
parm_underlay = flash.Lib.current.loaderInfo.parameters.underlay;
parm_overlay = flash.Lib.current.loaderInfo.parameters.overlay;
parm_image = flash.Lib.current.loaderInfo.parameters.image;
myloader = new flash.display.Loader();
myloader.contentLoaderInfo.addEventListener(flash.events.Event.COMPLETE, bmploaded);
loadpos = 0;
bmploaded(null);
}
function bmploaded(event:flash.events.Event) {
/* I guess it could be possible to solve this in a less roundabout way
* I use one Loader to load up to three images, but two of them are
* optional.
*/
if (loadpos == 0) {
loadpos = 1;
if (parm_underlay != null) {
myloader.load(new flash.net.URLRequest(parm_underlay));
return;
}
}
if (loadpos == 1) {
loadpos = 2;
if (parm_underlay != null) {
underlay = myloader.content;
addChild(underlay);
}
myloader.load(new flash.net.URLRequest(parm_image));
return;
}
if (loadpos == 2) {
loadpos = 3;
hueimage = myloader.content;
addChild(hueimage);
if (parm_overlay != null) {
myloader.load(new flash.net.URLRequest(parm_overlay));
return;
}
}
if (parm_overlay != null) {
overlay = myloader.content;
addChild(overlay);
}
init();
}
function init() {
setHue(myHue);
if (parm_click != "no")
addEventListener(flash.events.MouseEvent.MOUSE_DOWN, mouseDown);
if (delay > 0) {
tick = 0;
addEventListener(flash.events.Event.ENTER_FRAME, update);
}
}
function update(e:flash.events.Event):Void {
tick += 1;
if (tick == delay) {
tick = 0;
setHue(myHue + step);
}
}
function mouseDown(event:flash.events.MouseEvent) {
if (parm_click == "yes")
setHue(Std.int(Math.random()*360));
else {
flash.Lib.getURL(new flash.net.URLRequest(parm_click), "_top");
}
}
function setHue(hue:Int) {
/*
* This function sets the hue of hueimage, using a ColorMatrixFilter.
*
* Possible values for hue are between 0 and 360. We define six
* prototypes for the ColorMatrixFilter (I'm only considering the 3x3
* submatrix for RGB values) for multiplies of 60. For multiplies of
* 120, the values of this matrix are quite obvious (shift the primary
* colors around). For the other three prototypes, we must map primary
* colors to secondary colors and vice versa, which means that we need
* negative values in our matrices, like this:
*
*
* 0 60 120 180 240 300
*
* 1 0 0 1 -1 1 0 0 1 -1 1 1 0 1 0 1 1 -1
* 0 1 0 1 1 -1 1 0 0 1 -1 1 0 0 1 -1 1 1
* 0 0 1 -1 1 1 0 1 0 1 1 -1 1 0 0 1 -1 1
*
*
* For a hue not a multiple of 60, we interpolate these matrices. The
* following computation might be a bit nonobvious, and easier to
* understand if you read it backwards.
*/
var huex:Float;
var monster:Array<Int>;
var mata:Float; var matb:Float; var matc:Float;
hue = (hue + 360) % 360;
myHue = hue;
monster = [[1, 0, 0, -1, 0, 1], [1, -1, -1, 1, 1, 0],
[0, -1, 0, 1, 1, 0], [-1, 1, 1, 0, 1, -1],
[0, 1, 1, 0, 0, -1], [1, 0, 1, -1, -1, 1]][Std.int(hue / 60)];
huex = (hue % 60) / 60;
mata = monster[0] + huex * monster[1];
matb = monster[2] + huex * monster[3];
matc = monster[4] + huex * monster[5];
hueimage.filters = [new flash.filters.ColorMatrixFilter(
[mata, matb, matc, 0, 0,
matc, mata, matb, 0, 0,
matb, matc, mata, 0, 0,
0, 0, 0, 1, 0])];
}
}