ColorShortcuts.as 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471
  1. package caurina.transitions.properties {
  2. /**
  3. * properties.ColorShortcuts
  4. * List of default special color properties (normal and splitter properties) for the Tweener class
  5. * The function names are strange/inverted because it makes for easier debugging (alphabetic order). They're only for internal use (on this class) anyways.
  6. *
  7. * @author Zeh Fernando, Nate Chatellier, Arthur Debert
  8. * @version 1.0.0
  9. */
  10. import flash.geom.ColorTransform;
  11. import flash.filters.ColorMatrixFilter;
  12. import caurina.transitions.Tweener;
  13. import caurina.transitions.AuxFunctions;
  14. public class ColorShortcuts {
  15. // Sources:
  16. // http://www.graficaobscura.com/matrix/index.html
  17. // And mario Klingemann's ColorMatrix class as mentioned on the credits:
  18. // http://www.quasimondo.com/archives/000565.php
  19. // Defines luminance using sRGB luminance
  20. private static var LUMINANCE_R:Number = 0.212671;
  21. private static var LUMINANCE_G:Number = 0.715160;
  22. private static var LUMINANCE_B:Number = 0.072169;
  23. /**
  24. * There's no constructor.
  25. */
  26. public function ColorShortcuts () {
  27. trace ("This is an static class and should not be instantiated.")
  28. }
  29. /**
  30. * Registers all the special properties to the Tweener class, so the Tweener knows what to do with them.
  31. */
  32. public static function init(): void {
  33. // Normal properties
  34. Tweener.registerSpecialProperty("_color_ra", _oldColor_property_get, _oldColor_property_set, ["redMultiplier"]);
  35. Tweener.registerSpecialProperty("_color_rb", _color_property_get, _color_property_set, ["redOffset"]);
  36. Tweener.registerSpecialProperty("_color_ga", _oldColor_property_get, _oldColor_property_set, ["greenMultiplier"]);
  37. Tweener.registerSpecialProperty("_color_gb", _color_property_get, _color_property_set, ["greenOffset"]);
  38. Tweener.registerSpecialProperty("_color_ba", _oldColor_property_get, _oldColor_property_set, ["blueMultiplier"]);
  39. Tweener.registerSpecialProperty("_color_bb", _color_property_get, _color_property_set, ["blueOffset"]);
  40. Tweener.registerSpecialProperty("_color_aa", _oldColor_property_get, _oldColor_property_set, ["alphaMultiplier"]);
  41. Tweener.registerSpecialProperty("_color_ab", _color_property_get, _color_property_set, ["alphaOffset"]);
  42. Tweener.registerSpecialProperty("_color_redMultiplier", _color_property_get, _color_property_set, ["redMultiplier"]);
  43. Tweener.registerSpecialProperty("_color_redOffset", _color_property_get, _color_property_set, ["redOffset"]);
  44. Tweener.registerSpecialProperty("_color_greenMultiplier", _color_property_get, _color_property_set, ["greenMultiplier"]);
  45. Tweener.registerSpecialProperty("_color_greenOffset", _color_property_get, _color_property_set, ["greenOffset"]);
  46. Tweener.registerSpecialProperty("_color_blueMultiplier", _color_property_get, _color_property_set, ["blueMultiplier"]);
  47. Tweener.registerSpecialProperty("_color_blueOffset", _color_property_get, _color_property_set, ["blueOffset"]);
  48. Tweener.registerSpecialProperty("_color_alphaMultiplier", _color_property_get, _color_property_set, ["alphaMultiplier"]);
  49. Tweener.registerSpecialProperty("_color_alphaOffset", _color_property_get, _color_property_set, ["alphaOffset"]);
  50. // Normal splitter properties
  51. Tweener.registerSpecialPropertySplitter("_color", _color_splitter);
  52. Tweener.registerSpecialPropertySplitter("_colorTransform", _colorTransform_splitter);
  53. // Color changes that depend on the ColorMatrixFilter
  54. Tweener.registerSpecialProperty("_brightness", _brightness_get, _brightness_set, [false]);
  55. Tweener.registerSpecialProperty("_tintBrightness", _brightness_get, _brightness_set, [true]);
  56. Tweener.registerSpecialProperty("_contrast", _contrast_get, _contrast_set);
  57. Tweener.registerSpecialProperty("_hue", _hue_get, _hue_set);
  58. Tweener.registerSpecialProperty("_saturation", _saturation_get, _saturation_set, [false]);
  59. Tweener.registerSpecialProperty("_dumbSaturation", _saturation_get, _saturation_set, [true]);
  60. }
  61. // ==================================================================================================================================
  62. // PROPERTY GROUPING/SPLITTING functions --------------------------------------------------------------------------------------------
  63. // ----------------------------------------------------------------------------------------------------------------------------------
  64. // _color
  65. /**
  66. * Splits the _color parameter into specific color variables
  67. *
  68. * @param p_value Number The original _color value
  69. * @return Array An array containing the .name and .value of all new properties
  70. */
  71. public static function _color_splitter (p_value:*, p_parameters:Array):Array {
  72. var nArray:Array = new Array();
  73. if (p_value == null) {
  74. // No parameter passed, so just resets the color
  75. nArray.push({name:"_color_redMultiplier", value:1});
  76. nArray.push({name:"_color_redOffset", value:0});
  77. nArray.push({name:"_color_greenMultiplier", value:1});
  78. nArray.push({name:"_color_greenOffset", value:0});
  79. nArray.push({name:"_color_blueMultiplier", value:1});
  80. nArray.push({name:"_color_blueOffset", value:0});
  81. } else {
  82. // A color tinting is passed, so converts it to the object values
  83. nArray.push({name:"_color_redMultiplier", value:0});
  84. nArray.push({name:"_color_redOffset", value:AuxFunctions.numberToR(p_value)});
  85. nArray.push({name:"_color_greenMultiplier", value:0});
  86. nArray.push({name:"_color_greenOffset", value:AuxFunctions.numberToG(p_value)});
  87. nArray.push({name:"_color_blueMultiplier", value:0});
  88. nArray.push({name:"_color_blueOffset", value:AuxFunctions.numberToB(p_value)});
  89. }
  90. return nArray;
  91. }
  92. // ----------------------------------------------------------------------------------------------------------------------------------
  93. // _colorTransform
  94. /**
  95. * Splits the _colorTransform parameter into specific color variables
  96. *
  97. * @param p_value Number The original _colorTransform value
  98. * @return Array An array containing the .name and .value of all new properties
  99. */
  100. public static function _colorTransform_splitter (p_value:Object, p_parameters:Array):Array {
  101. var nArray:Array = new Array();
  102. if (p_value == null) {
  103. // No parameter passed, so just resets the color
  104. nArray.push({name:"_color_redMultiplier", value:1});
  105. nArray.push({name:"_color_redOffset", value:0});
  106. nArray.push({name:"_color_greenMultiplier", value:1});
  107. nArray.push({name:"_color_greenOffset", value:0});
  108. nArray.push({name:"_color_blueMultiplier", value:1});
  109. nArray.push({name:"_color_blueOffset", value:0});
  110. } else {
  111. // A color transformation object is passed, so converts it to the object values
  112. nArray.push({name:"_color_redMultiplier", value:p_value.redMultiplier});
  113. nArray.push({name:"_color_redOffset", value:p_value.redOffset});
  114. nArray.push({name:"_color_blueMultiplier", value:p_value.blueMultiplier});
  115. nArray.push({name:"_color_blueOffset", value:p_value.blueOffset});
  116. nArray.push({name:"_color_greenMultiplier", value:p_value.greenMultiplier});
  117. nArray.push({name:"_color_greenOffset", value:p_value.greenOffset});
  118. nArray.push({name:"_color_alphaMultiplier", value:p_value.alphaMultiplier});
  119. nArray.push({name:"_color_alphaOffset", value:p_value.alphaOffset});
  120. }
  121. return nArray;
  122. }
  123. // ==================================================================================================================================
  124. // NORMAL SPECIAL PROPERTY functions ------------------------------------------------------------------------------------------------
  125. // ----------------------------------------------------------------------------------------------------------------------------------
  126. // _color_*
  127. /**
  128. * _color_*
  129. * Generic function for the ra/rb/etc components of the deprecated colorTransform object
  130. */
  131. public static function _oldColor_property_get (p_obj:Object, p_parameters:Array, p_extra:Object = null):Number {
  132. return p_obj.transform.colorTransform[p_parameters[0]] * 100;
  133. }
  134. public static function _oldColor_property_set (p_obj:Object, p_value:Number, p_parameters:Array, p_extra:Object = null): void {
  135. var tf:ColorTransform = p_obj.transform.colorTransform;
  136. tf[p_parameters[0]] = p_value / 100;
  137. p_obj.transform.colorTransform = tf;
  138. }
  139. /**
  140. * _color_*
  141. * Generic function for the redMultiplier/redOffset/etc components of the new colorTransform
  142. */
  143. public static function _color_property_get (p_obj:Object, p_parameters:Array, p_extra:Object = null):Number {
  144. return p_obj.transform.colorTransform[p_parameters[0]];
  145. }
  146. public static function _color_property_set (p_obj:Object, p_value:Number, p_parameters:Array, p_extra:Object = null): void {
  147. var cfm:ColorTransform = p_obj.transform.colorTransform;
  148. cfm[p_parameters[0]] = p_value;
  149. p_obj.transform.colorTransform = cfm;
  150. }
  151. // ----------------------------------------------------------------------------------------------------------------------------------
  152. // Special coloring
  153. /**
  154. * _brightness
  155. * Brightness of an object: -1 -> [0] -> +1
  156. */
  157. public static function _brightness_get (p_obj:Object, p_parameters:Array, p_extra:Object = null):Number {
  158. var isTint:Boolean = p_parameters[0];
  159. /*
  160. // Using ColorMatrix:
  161. var mtx:Array = getObjectMatrix(p_obj);
  162. var mc:Number = 1 - ((mtx[0] + mtx[6] + mtx[12]) / 3); // Brightness as determined by the main channels
  163. var co:Number = (mtx[4] + mtx[9] + mtx[14]) / 3; // Brightness as determined by the offset channels
  164. */
  165. var cfm:ColorTransform = p_obj.transform.colorTransform;
  166. var mc:Number = 1 - ((cfm.redMultiplier + cfm.greenMultiplier + cfm.blueMultiplier) / 3); // Brightness as determined by the main channels
  167. var co:Number = (cfm.redOffset + cfm.greenOffset + cfm.blueOffset) / 3;
  168. if (isTint) {
  169. // Tint style
  170. //return (mc+(co/255))/2;
  171. return co > 0 ? co / 255 : -mc;
  172. } else {
  173. // Native, Flash "Adjust Color" and Photoshop style
  174. return co / 100;
  175. }
  176. }
  177. public static function _brightness_set (p_obj:Object, p_value:Number, p_parameters:Array, p_extra:Object = null): void {
  178. //var mtx:Array = getObjectMatrix(p_obj);
  179. var isTint:Boolean = p_parameters[0];
  180. var mc:Number; // Main channel
  181. var co:Number; // Channel offset
  182. if (isTint) {
  183. // Tint style
  184. mc = 1 - Math.abs(p_value);
  185. co = p_value > 0 ? Math.round(p_value*255) : 0;
  186. } else {
  187. // Native, Flash "Adjust Color" and Photoshop style
  188. mc = 1;
  189. co = Math.round(p_value*100);
  190. }
  191. /*
  192. // Using ColorMatrix:
  193. var mtx:Array = [
  194. mc, cc, cc, cc, co,
  195. cc, mc, cc, cc, co,
  196. cc, cc, mc, cc, co,
  197. 0, 0, 0, 1, 0
  198. ];
  199. setObjectMatrix(p_obj, mtx);
  200. */
  201. var cfm:ColorTransform = new ColorTransform(mc, mc, mc, 1, co, co, co, 0);
  202. p_obj.transform.colorTransform = cfm;
  203. }
  204. /**
  205. * _saturation
  206. * Saturation of an object: 0 -> [1] -> 2
  207. */
  208. public static function _saturation_get (p_obj:Object, p_parameters:Array, p_extra:Object = null):Number {
  209. var mtx:Array = getObjectMatrix(p_obj);
  210. var isDumb:Boolean = p_parameters[0];
  211. var rl:Number = isDumb ? 1/3 : LUMINANCE_R;
  212. var gl:Number = isDumb ? 1/3 : LUMINANCE_G;
  213. var bl:Number = isDumb ? 1/3 : LUMINANCE_B;
  214. var mc:Number = ((mtx[0]-rl)/(1-rl) + (mtx[6]-gl)/(1-gl) + (mtx[12]-bl)/(1-bl)) / 3; // Color saturation as determined by the main channels
  215. var cc:Number = 1 - ((mtx[1]/gl + mtx[2]/bl + mtx[5]/rl + mtx[7]/bl + mtx[10]/rl + mtx[11]/gl) / 6); // Color saturation as determined by the other channels
  216. return (mc + cc) / 2;
  217. }
  218. public static function _saturation_set (p_obj:Object, p_value:Number, p_parameters:Array, p_extra:Object = null): void {
  219. var isDumb:Boolean = p_parameters[0];
  220. var rl:Number = isDumb ? 1/3 : LUMINANCE_R;
  221. var gl:Number = isDumb ? 1/3 : LUMINANCE_G;
  222. var bl:Number = isDumb ? 1/3 : LUMINANCE_B;
  223. var sf:Number = p_value;
  224. var nf:Number = 1-sf;
  225. var nr:Number = rl * nf;
  226. var ng:Number = gl * nf;
  227. var nb:Number = bl * nf;
  228. var mtx:Array = [
  229. nr+sf, ng, nb, 0, 0,
  230. nr, ng+sf, nb, 0, 0,
  231. nr, ng, nb+sf, 0, 0,
  232. 0, 0, 0, 1, 0
  233. ];
  234. setObjectMatrix(p_obj, mtx);
  235. }
  236. /**
  237. * _contrast
  238. * Contrast of an object: -1 -> [0] -> +1
  239. */
  240. public static function _contrast_get (p_obj:Object, p_parameters:Array, p_extra:Object = null):Number {
  241. /*
  242. // Using ColorMatrix:
  243. var mtx:Array = getObjectMatrix(p_obj);
  244. var mc:Number = ((mtx[0] + mtx[6] + mtx[12]) / 3) - 1; // Contrast as determined by the main channels
  245. var co:Number = (mtx[4] + mtx[9] + mtx[14]) / 3 / -128; // Contrast as determined by the offset channel
  246. */
  247. var cfm:ColorTransform = p_obj.transform.colorTransform;
  248. var mc:Number; // Contrast as determined by the main channels
  249. var co:Number; // Contrast as determined by the offset channel
  250. mc = ((cfm.redMultiplier + cfm.greenMultiplier + cfm.blueMultiplier) / 3) - 1;
  251. co = (cfm.redOffset + cfm.greenOffset + cfm.blueOffset) / 3 / -128;
  252. /*
  253. if (cfm.ra < 100) {
  254. // Low contrast
  255. mc = ((cfm.ra + cfm.ga + cfm.ba) / 300) - 1;
  256. co = (cfm.rb + cfm.gb + cfm.bb) / 3 / -128;
  257. } else {
  258. // High contrast
  259. mc = (((cfm.ra + cfm.ga + cfm.ba) / 300) - 1) / 37;
  260. co = (cfm.rb + cfm.gb + cfm.bb) / 3 / -3840;
  261. }
  262. */
  263. return (mc+co)/2;
  264. }
  265. public static function _contrast_set (p_obj:Object, p_value:Number, p_parameters:Array, p_extra:Object = null): void {
  266. var mc:Number; // Main channel
  267. var co:Number; // Channel offset
  268. mc = p_value + 1;
  269. co = Math.round(p_value*-128);
  270. /*
  271. if (p_value < 0) {
  272. // Low contrast
  273. mc = p_value + 1;
  274. co = Math.round(p_value*-128);
  275. } else {
  276. // High contrast
  277. mc = (p_value * 37) + 1;
  278. co = Math.round(p_value*-3840);
  279. }
  280. */
  281. // Flash: * 8, * -512
  282. /*
  283. // Using ColorMatrix:
  284. var mtx:Array = [
  285. mc, 0, 0, 0, co,
  286. 0, mc, 0, 0, co,
  287. 0, 0, mc, 0, co,
  288. 0, 0, 0, 1, 0
  289. ];
  290. setObjectMatrix(p_obj, mtx);
  291. */
  292. var cfm:ColorTransform = new ColorTransform(mc, mc, mc, 1, co, co, co, 0);
  293. p_obj.transform.colorTransform = cfm;
  294. }
  295. /**
  296. * _hue
  297. * Hue of an object: -180 -> [0] -> 180
  298. */
  299. public static function _hue_get (p_obj:Object, p_parameters:Array, p_extra:Object = null):Number {
  300. var mtx:Array = getObjectMatrix(p_obj);
  301. // Find the current Hue based on a given matrix.
  302. // This is a kind of a brute force method by sucessive division until a close enough angle is found.
  303. // Reverse-engineering the hue equation would be is a better choice, but it's hard to find material
  304. // on the correct calculation employed by Flash.
  305. // This code has to run only once (before the tween starts), so it's good enough.
  306. var hues:Array = [];
  307. var i:Number;
  308. hues[0] = {angle:-179.9, matrix:getHueMatrix(-179.9)};
  309. hues[1] = {angle:180, matrix:getHueMatrix(180)};
  310. for (i = 0; i < hues.length; i++) {
  311. hues[i].distance = getHueDistance(mtx, hues[i].matrix);
  312. }
  313. var maxTries:Number = 15; // Number o maximum divisions until the hue is found
  314. var angleToSplit:Number;
  315. for (i = 0; i < maxTries; i++) {
  316. // Find the nearest angle
  317. if (hues[0].distance < hues[1].distance) {
  318. // First is closer
  319. angleToSplit = 1;
  320. } else {
  321. // Second is closer
  322. angleToSplit = 0;
  323. }
  324. hues[angleToSplit].angle = (hues[0].angle + hues[1].angle)/2;
  325. hues[angleToSplit].matrix = getHueMatrix(hues[angleToSplit].angle)
  326. hues[angleToSplit].distance = getHueDistance(mtx, hues[angleToSplit].matrix);
  327. }
  328. return hues[angleToSplit].angle;
  329. }
  330. public static function _hue_set (p_obj:Object, p_value:Number, p_parameters:Array, p_extra:Object = null): void {
  331. setObjectMatrix(p_obj, getHueMatrix(p_value));
  332. }
  333. public static function getHueDistance (mtx1:Array, mtx2:Array): Number {
  334. return (Math.abs(mtx1[0] - mtx2[0]) + Math.abs(mtx1[1] - mtx2[1]) + Math.abs(mtx1[2] - mtx2[2]));
  335. }
  336. public static function getHueMatrix (hue:Number): Array {
  337. var ha:Number = hue * Math.PI/180; // Hue angle, to radians
  338. var rl:Number = LUMINANCE_R;
  339. var gl:Number = LUMINANCE_G;
  340. var bl:Number = LUMINANCE_B;
  341. var c:Number = Math.cos(ha);
  342. var s:Number = Math.sin(ha);
  343. var mtx:Array = [
  344. (rl + (c * (1 - rl))) + (s * (-rl)),
  345. (gl + (c * (-gl))) + (s * (-gl)),
  346. (bl + (c * (-bl))) + (s * (1 - bl)),
  347. 0, 0,
  348. (rl + (c * (-rl))) + (s * 0.143),
  349. (gl + (c * (1 - gl))) + (s * 0.14),
  350. (bl + (c * (-bl))) + (s * -0.283),
  351. 0, 0,
  352. (rl + (c * (-rl))) + (s * (-(1 - rl))),
  353. (gl + (c * (-gl))) + (s * gl),
  354. (bl + (c * (1 - bl))) + (s * bl),
  355. 0, 0,
  356. 0, 0, 0, 1, 0
  357. ];
  358. return mtx;
  359. }
  360. // ==================================================================================================================================
  361. // AUXILIARY functions --------------------------------------------------------------------------------------------------------------
  362. private static function getObjectMatrix(p_obj:Object): Array {
  363. // Get the current color matrix of an object
  364. for (var i:Number = 0; i < p_obj.filters.length; i++) {
  365. if (p_obj.filters[i] is ColorMatrixFilter) {
  366. return p_obj.filters[i].matrix.concat();
  367. }
  368. }
  369. return [
  370. 1, 0, 0, 0, 0,
  371. 0, 1, 0, 0, 0,
  372. 0, 0, 1, 0, 0,
  373. 0, 0, 0, 1, 0
  374. ];
  375. }
  376. private static function setObjectMatrix(p_obj:Object, p_matrix:Array): void {
  377. // Set the current color matrix of an object
  378. var objFilters:Array = p_obj.filters.concat();
  379. var found:Boolean = false;
  380. for (var i:Number = 0; i < objFilters.length; i++) {
  381. if (objFilters[i] is ColorMatrixFilter) {
  382. objFilters[i].matrix = p_matrix.concat();
  383. found = true;
  384. }
  385. }
  386. if (!found) {
  387. // Has to create a new color matrix filter
  388. var cmtx:ColorMatrixFilter = new ColorMatrixFilter(p_matrix);
  389. objFilters[objFilters.length] = cmtx;
  390. }
  391. p_obj.filters = objFilters;
  392. }
  393. }
  394. }