Compare commits
847 Commits
Author | SHA1 | Date | |
---|---|---|---|
c96dde9972 | |||
ba98256a08 | |||
327df539a7 | |||
99b00bc57f | |||
add97baf54 | |||
76a29bf51a | |||
7bf2662b66 | |||
d2dc42e7cf | |||
186b5a305a | |||
a75e78e366 | |||
6546b520b3 | |||
7660119b50 | |||
6a9c9467a5 | |||
a712764401 | |||
da105e6237 | |||
e2b6f0776b | |||
c7381b771f | |||
776cf1ec87 | |||
5f49a68b62 | |||
fc75334e48 | |||
fced9fa914 | |||
661c6fba7d | |||
539d7c7870 | |||
14fde4f8a0 | |||
7088d9aa33 | |||
c907f47f5d | |||
1aa6b61bee | |||
3043bac82c | |||
fc1d23b3f5 | |||
f9b8a78396 | |||
a965bb0908 | |||
ba43c888be | |||
00ee251868 | |||
bcb6c3aca9 | |||
e79183812b | |||
2058630768 | |||
be8a87110d | |||
54342c16d1 | |||
dd2e55f65e | |||
e7c53dac38 | |||
ebe37a612f | |||
ef7108a5e0 | |||
fc9cb07be1 | |||
ae1f880473 | |||
eb02e713e2 | |||
ecc1444b76 | |||
4985dfacf3 | |||
365503d9b5 | |||
9bd3bf4adf | |||
fb9c600b9f | |||
20ecfd431b | |||
5eb936d8c2 | |||
f72da81fa4 | |||
30b0e5e607 | |||
c55a81f29b | |||
0712e04c12 | |||
d2ac600d7a | |||
049faba83e | |||
cb5ece914e | |||
b90887b98c | |||
cc62c5b3cb | |||
01eb0cdb43 | |||
857524dee4 | |||
73b165ef0c | |||
cd8d5c0b52 | |||
01a0cb14fd | |||
4394e29db0 | |||
4361992014 | |||
b46d882c18 | |||
00a162a22e | |||
af382401be | |||
aaaf824ad7 | |||
97690ce486 | |||
f58a54e2ea | |||
a2fce811dd | |||
42aefa6851 | |||
31edd71c41 | |||
34bfe0668c | |||
936991cefa | |||
f7112bb005 | |||
5369b31dbc | |||
323ab36a1b | |||
016079bdc8 | |||
b394a69904 | |||
e8eafb8004 | |||
777cbb5b0a | |||
4cb78b283c | |||
dd6920f641 | |||
152e6c9c0d | |||
b0e6e31b88 | |||
0d4c8cd5df | |||
f756266d6c | |||
ae56e9422f | |||
771e3d0981 | |||
eff4ac3056 | |||
22e876c844 | |||
21d4857b74 | |||
8c29afbca6 | |||
8f558fb798 | |||
c56161ee91 | |||
09969253d3 | |||
3c31746785 | |||
a928a2819f | |||
172da85f15 | |||
f5a33dc39a | |||
00f0debf12 | |||
20b7eb2a3c | |||
c70e339296 | |||
6583d8f8b9 | |||
e1592a8ace | |||
5b567683a7 | |||
4d8b093941 | |||
f828471f0d | |||
92d5a4a2fe | |||
f0ed4927e8 | |||
67b66e4a10 | |||
f767d24280 | |||
27061291a3 | |||
12ac85f0f6 | |||
3525b318a6 | |||
fce9bb5727 | |||
3cc3204939 | |||
222c65a8a5 | |||
4196576c19 | |||
06e864ca0a | |||
3d1a24d5cf | |||
070003a414 | |||
7d5259acd7 | |||
2b28577813 | |||
7e1451fa8d | |||
dd1d2bf441 | |||
8aa6eff715 | |||
6f5e2f130e | |||
26f4448175 | |||
82510314a5 | |||
7fea616f0f | |||
8d26d693ff | |||
9329a5fd03 | |||
1c1f6c11ab | |||
cfe3545eec | |||
b27e6b6f66 | |||
30c3cdbcc5 | |||
392204e5c5 | |||
0dff1f7a9a | |||
6d309419d3 | |||
460688e2d5 | |||
947306a80c | |||
0b439e1b00 | |||
676cbd17ea | |||
2e3558ef08 | |||
b5234089cd | |||
5cda3e57b4 | |||
ca3bbf1c57 | |||
8081d5e232 | |||
2c75daecb1 | |||
c11e0d5d8d | |||
107751b7eb | |||
8ec47506bf | |||
6311049210 | |||
4cf6088441 | |||
00a05ee839 | |||
73badf06b0 | |||
ea679913f9 | |||
ba33533178 | |||
c32b327f23 | |||
573d7ca051 | |||
23b82b4cf0 | |||
f61682bb8d | |||
a9e04a4abd | |||
0021de35b4 | |||
1e693e4e36 | |||
a3a75b6096 | |||
2448e65ffa | |||
814db90e54 | |||
3585c2debd | |||
8b983414a6 | |||
4d53f5e3b7 | |||
9811a3a604 | |||
28912fc58f | |||
7554b3355c | |||
f7592f864b | |||
66fa71affd | |||
a0c9f2923e | |||
c9529dc65c | |||
a8f727fdcd | |||
48d1214a6d | |||
b859857b2d | |||
669d7a21cb | |||
a286d5926a | |||
92cc986fb6 | |||
da1f26291c | |||
a92e198519 | |||
91ffce0bfe | |||
acb6fd2172 | |||
055bcdb001 | |||
fe9875841a | |||
e5be581e19 | |||
6620f7e5a9 | |||
758cc6202a | |||
d96c1a9c06 | |||
551e15e67b | |||
d576c56068 | |||
fddec5c95c | |||
1abd6113a3 | |||
ef05cc4fd1 | |||
87341b049e | |||
9f31b2c7e4 | |||
3f992cbb4a | |||
0bdcf38d2f | |||
d254a9b72e | |||
d8d7f1adea | |||
d6a85aaf6f | |||
1bc85bfbca | |||
90c2ed3470 | |||
5a469202e9 | |||
8faa6db4c0 | |||
8d618fc31c | |||
e9c99a241f | |||
3209c50304 | |||
5d38804523 | |||
d17f235aee | |||
a0350ff2e8 | |||
d6351fccb3 | |||
a58b643a26 | |||
5367c75972 | |||
7d964c7fde | |||
d0acb625cf | |||
e6950e5c1a | |||
63449b2694 | |||
ed32ddc747 | |||
cbb97c60d0 | |||
ece3105893 | |||
069bfb9a90 | |||
96ab2dd781 | |||
1fe327495c | |||
089b4ea14d | |||
3853a78019 | |||
ee0a4c16aa | |||
84e26b28da | |||
cffb68c88c | |||
489298e87a | |||
dffe23746c | |||
8c629bd842 | |||
251ceae318 | |||
c7a80ebdec | |||
2280740421 | |||
c6287653f9 | |||
bd8eaa4307 | |||
c0fda032e5 | |||
744709b15b | |||
6a2f7fb58d | |||
479df8ced7 | |||
72edf47734 | |||
6b6674a04d | |||
754bc9b830 | |||
ff98670055 | |||
4e1f6bee3f | |||
6a4d3cb106 | |||
7048e1fd42 | |||
cd36c07a45 | |||
9f0aaceb5f | |||
99da69553c | |||
fdb5483e87 | |||
1208324d4d | |||
5437ad8651 | |||
c074217047 | |||
e0c9a46ed3 | |||
d962217f90 | |||
9800d85cb7 | |||
011b07c735 | |||
2fdc85556b | |||
7a8efc56b0 | |||
0d81865f3b | |||
12cfe16cb4 | |||
e773f9ae6d | |||
5c46cfe20a | |||
2d9001db6e | |||
8ff15fd0e1 | |||
8e4ea8437f | |||
48f24c0cf3 | |||
8d85093874 | |||
1beeb8d6e4 | |||
f9b07b29a9 | |||
9bc330e5e8 | |||
a51e20b370 | |||
ef6ef6256e | |||
4edbc29e72 | |||
b72c775a04 | |||
0c9f04bc71 | |||
034057dcd2 | |||
016316518d | |||
ac9ccd04e2 | |||
ce8d71f47e | |||
29cd0d80f3 | |||
d3f5a41c0d | |||
3f181c6248 | |||
8ae14281cc | |||
9de77c9f21 | |||
d31865a9ef | |||
cf62f2ac0b | |||
16362e1b13 | |||
aa79919496 | |||
6df145455e | |||
daceb326c5 | |||
5fbadc0d80 | |||
473bd4705c | |||
c570d8fd75 | |||
7909d4d94b | |||
138d5d02d2 | |||
0683583da1 | |||
1decd64a30 | |||
9e1cfef924 | |||
e384f7d2e2 | |||
be3d2cf20d | |||
54ae52e117 | |||
b480acc6a0 | |||
007e4d4e11 | |||
6070ebead5 | |||
5b081c8859 | |||
29e205d441 | |||
1f5272415c | |||
d1b12a07ac | |||
f2f8158efb | |||
77877c118f | |||
e0c16486d3 | |||
ba491736c1 | |||
0e817a88a7 | |||
5d4b8b87a3 | |||
94c29c2ff5 | |||
e260b36585 | |||
3d42ab25b2 | |||
0a6250141a | |||
3d58bcae6c | |||
281103c46e | |||
e6ed0c28a3 | |||
522006f67a | |||
e11355193b | |||
30cdb6d335 | |||
c40e27bc17 | |||
3d6cd3e576 | |||
4b7b18ca6f | |||
9e6e39e3d8 | |||
c600d62bd2 | |||
f114676db7 | |||
0ec3787fc4 | |||
40cced7be0 | |||
81a9e7a678 | |||
6328fe760f | |||
932974d744 | |||
eb4941417c | |||
93deb1c2c5 | |||
c9b581f6db | |||
6a6f75b3ce | |||
04a1633a90 | |||
b168e8ca76 | |||
fa6f2e5db6 | |||
1c66282b01 | |||
5cbb9b8664 | |||
6254490a23 | |||
43e60e300c | |||
fe95abca0f | |||
bcecd3058b | |||
68a807ccb5 | |||
7a355b39a0 | |||
0f1489727c | |||
bab3d6677e | |||
bcb709300c | |||
4f2f01ea36 | |||
8592cd2e53 | |||
b4c1a4c714 | |||
294af67099 | |||
3585a6f96e | |||
0914e2816e | |||
0b41c0f648 | |||
beb2fa6ba9 | |||
61fb6c5e6f | |||
d310a77893 | |||
84f366e7e4 | |||
561d016a45 | |||
334d6ad21f | |||
0382b3858b | |||
2aa87b5fac | |||
d7fef0f88b | |||
c7131678f8 | |||
5224c9ddd8 | |||
3265a96fe6 | |||
5831447f75 | |||
043f077408 | |||
25bb46d097 | |||
5cb1d0cd03 | |||
123ea31191 | |||
1df5d0da38 | |||
50f159c982 | |||
df5aef363b | |||
0642e17aa8 | |||
1402394d07 | |||
e1ba57c92f | |||
2db04fe7d6 | |||
dc61be27f0 | |||
7fd49aaccb | |||
cffaec09b2 | |||
62a7755407 | |||
2ab1e29365 | |||
fe110a3d8e | |||
5afec16258 | |||
fad483ce7a | |||
b10e87d2b7 | |||
61ee1d9b32 | |||
ce1a5c4918 | |||
796cd4466e | |||
0f49c884f2 | |||
c8dae1cb79 | |||
54837d0e21 | |||
001e35cf7b | |||
ac5083633b | |||
c32af500dc | |||
dc4de32162 | |||
243990a90f | |||
96ef362cf8 | |||
e1e029a849 | |||
fe5e8966a5 | |||
37aa6c3d72 | |||
d805e13d57 | |||
a7ef57b6ee | |||
ac08775406 | |||
6583d3d560 | |||
b5465ca066 | |||
258d13371d | |||
e3e6730b45 | |||
6ef99bba15 | |||
afe790e5e3 | |||
8989e984cb | |||
00dd660571 | |||
8a29b78af8 | |||
9ef46d5ec5 | |||
6445b44d02 | |||
9afe69cb87 | |||
508fb79c32 | |||
8ebdc4cd41 | |||
fab9c6e836 | |||
f7f9587520 | |||
12dcacea5a | |||
9325abb924 | |||
4ed7338f25 | |||
90845b3a62 | |||
125e332b7c | |||
e457209c8f | |||
8643f4402a | |||
92d7109ef7 | |||
6b32239fa1 | |||
8441f28ac1 | |||
e8db80a0ec | |||
7d9f8a8ccf | |||
073f46b0d7 | |||
32e528525c | |||
913ef272cf | |||
1a8f10c63d | |||
b2ab504422 | |||
35d4be5d3e | |||
efccfdc0c6 | |||
895a15524d | |||
006d72a195 | |||
13001bd7bd | |||
caebce5ec8 | |||
978319af20 | |||
a8788c83f1 | |||
56b1f421bc | |||
1955d3f8f5 | |||
17824ae1c4 | |||
34dbcedec3 | |||
e711050f75 | |||
844437dfc9 | |||
715c148908 | |||
b0ec276aac | |||
8d8c40e6a6 | |||
6d6e80c762 | |||
05e8f9adac | |||
1b99a22c1d | |||
280dd1a809 | |||
c1900a07f0 | |||
a43b20e182 | |||
b556143b66 | |||
19c99d1aa7 | |||
dc78c3cecd | |||
829bcb8ad1 | |||
56f008bda6 | |||
5350255eee | |||
d4884a2de5 | |||
581bd46dcd | |||
1e8315f32c | |||
cc8460cc26 | |||
aa375315a2 | |||
e5cb0717e2 | |||
6af04bb599 | |||
dfc3bfd181 | |||
ad3dd935e0 | |||
18ff6f88a7 | |||
b2fbe269d3 | |||
a8ce829e6c | |||
2fabf68282 | |||
b66d5ee93b | |||
982a5ab048 | |||
a6d70920e2 | |||
b311312260 | |||
665d2bd9bc | |||
4aac65fb9e | |||
4102e929f4 | |||
b3bb2472f1 | |||
a6d939cc9a | |||
304a5c06da | |||
707a69182f | |||
8f5ead43d9 | |||
41a4ee3f3d | |||
f8b6d7b0d3 | |||
84e757768e | |||
264e236473 | |||
96fc5f2418 | |||
26a463c4b4 | |||
8e425c64d4 | |||
bf9be5de2d | |||
2c235e659b | |||
a77168986a | |||
24186d5aec | |||
7b3f5ee858 | |||
939042a645 | |||
fa4e96e7e5 | |||
fa626532ba | |||
a2e2459169 | |||
8a70943b09 | |||
d126023c4a | |||
f2281d7125 | |||
3efa94dce5 | |||
bd7ebc5f7d | |||
89466d582a | |||
23fd3c464c | |||
5e5ec1a358 | |||
42c799d217 | |||
98f59fecf1 | |||
fbb5ccc7e2 | |||
d0739e811d | |||
25089c1772 | |||
762bd42790 | |||
4f4cb1cf12 | |||
2809a551d7 | |||
d501129e8e | |||
a45407571e | |||
da9cd78b56 | |||
93f3526f4b | |||
cdef28c60f | |||
aafc74bab8 | |||
f1b6ea4ae3 | |||
0fec4eff4a | |||
6dc3a2bdf7 | |||
ac2fe87558 | |||
5b4074fc38 | |||
c7c536dc80 | |||
2aa8bf578b | |||
7cbf2b2794 | |||
1401c30d87 | |||
d8772bdd8d | |||
b0318c8b11 | |||
0c441c214b | |||
7569a4343c | |||
21a759d8eb | |||
a3c409ff94 | |||
a3108225f6 | |||
6c882928cc | |||
cf560fce0f | |||
cc45ede931 | |||
43957b4a13 | |||
384f8f71b1 | |||
2827bbe94a | |||
2ec71da0e8 | |||
a8e0fef416 | |||
6fcbfe3873 | |||
0d599ae4a7 | |||
8eb0374351 | |||
cfb846b6de | |||
94ce3907da | |||
453fbcf88e | |||
3969867dfa | |||
9af23baa88 | |||
59195c9fb6 | |||
6ad5bde5d1 | |||
98135d01f8 | |||
aa4c94e3af | |||
c98d25ab66 | |||
637fa05109 | |||
db6dff0564 | |||
f4108c7dbf | |||
7ad836f9f9 | |||
8a85e953f1 | |||
c565111947 | |||
fa3b44baf9 | |||
f16b810ffb | |||
adca1bbd08 | |||
bc23e9cea6 | |||
b7362c7082 | |||
0b1977b47c | |||
1db937ae01 | |||
7fb5fe93fa | |||
b2bdb252b7 | |||
38f18360b8 | |||
d803e88c93 | |||
a060e32b15 | |||
cf2c0e7045 | |||
98aad13f1e | |||
af52d9a96a | |||
5541d030a5 | |||
c2a3ccc8d0 | |||
8335c07519 | |||
b8ef570077 | |||
d45ea00ca5 | |||
b7e4deae00 | |||
0cecdc74eb | |||
b712a497e2 | |||
9e78086ea8 | |||
a59bfcab11 | |||
29fa604d74 | |||
c5accf978e | |||
76044fa6d4 | |||
23f1e8908e | |||
b217f8f005 | |||
09319ecc1b | |||
3abf0897d5 | |||
ee3285089a | |||
4804477498 | |||
dc729ee80b | |||
0d3a078145 | |||
abd5ac5959 | |||
d42064d2fe | |||
dd4a544d7a | |||
39d7d4d8c8 | |||
78974a9a93 | |||
644b72cc6e | |||
1ce633bd06 | |||
c9251229fc | |||
7357614d9a | |||
090443c318 | |||
3be4c78883 | |||
3765ce3d5e | |||
4410aa5420 | |||
6d62d11872 | |||
b734db28dc | |||
5b7e07e11e | |||
7f17e17cff | |||
231ae9e165 | |||
4947cc4820 | |||
0e94606f41 | |||
b0ed5e4a7f | |||
85b64a9f04 | |||
d2ec797496 | |||
41e52a7a39 | |||
f2da622edb | |||
f2aceedefd | |||
173936d74f | |||
d4f315e0c1 | |||
1f022fd4a7 | |||
8581e4ec65 | |||
903f6817cf | |||
4b608e98a9 | |||
4b0b1a4bad | |||
9ae01cb074 | |||
c38300392e | |||
3258f0a383 | |||
87574a2b30 | |||
50e7d05764 | |||
b32d0ddcfe | |||
f139a9c130 | |||
087b8c57c5 | |||
0072a2c8b0 | |||
ab401d3013 | |||
a430d72415 | |||
47c6261289 | |||
cac1fa8ed5 | |||
b51620634e | |||
2f1e13ca20 | |||
0f817ddf58 | |||
5d4ba0b79f | |||
aae994b3d5 | |||
bd0adda73f | |||
85084b8279 | |||
676bf1c7fd | |||
c105619605 | |||
0dc5b2bc31 | |||
a31e3570f3 | |||
e8d96bf73f | |||
9fc971f40b | |||
0d2337221c | |||
fc19695a4a | |||
fbdf1aaf9a | |||
f98c2d2cf5 | |||
4056142b97 | |||
e4cf2ac40b | |||
b77f7057d7 | |||
8b6958bf0b | |||
b11b16b427 | |||
cecadf54e1 | |||
e4c14e234e | |||
0109eb81dd | |||
167adaceb0 | |||
8cf588b26d | |||
75dc1c5944 | |||
42862e188d | |||
c04a645991 | |||
5bbbdf64d9 | |||
569f42b7e2 | |||
46b267c2a4 | |||
bb9bafd67c | |||
11a3155e38 | |||
e852fb7c57 | |||
3de3c3542e | |||
ce111be5c8 | |||
e88d8fae07 | |||
f26b1d4ebc | |||
2d3bbc73f1 | |||
0a05374af5 | |||
6eabf01ffc | |||
9d0f41362b | |||
7d296f3dc3 | |||
a0273edb3e | |||
e0b76f5329 | |||
c033d65cde | |||
f0ef016309 | |||
25e6470499 | |||
5d83a39cf0 | |||
e68ae7f31d | |||
3af64d3f45 | |||
86cd1cdeaa | |||
4a1a7b6c2b | |||
e9a2ccfd1d | |||
df4978f6af | |||
781abb735b | |||
4f54715f70 | |||
c8a8d470a7 | |||
bd030fdf1f | |||
6e1ce724cb | |||
2248f41e68 | |||
44722ab88e | |||
cd4952cc7b | |||
eb559eee0c | |||
e0d63bf295 | |||
8a380b6c78 | |||
5693f34fb9 | |||
2ba451d42c | |||
bd99027852 | |||
68edbe62c7 | |||
472906957a | |||
b7e8310b61 | |||
51f86afe6e | |||
3ce9aaa843 | |||
7490651f83 | |||
2f6f0d14e1 | |||
a69b83e9bd | |||
c332aa2dea | |||
b2258a668d | |||
a0a1fa7bdf | |||
3dde3504d1 | |||
6c0f54032d | |||
61419f0bba | |||
2509ba80a4 | |||
07cb37f2bf | |||
1156008213 | |||
a8b42a35da | |||
721783ce6c | |||
f549174424 | |||
4527846ad6 | |||
55e3607ecc | |||
564f74265a | |||
913a50cb28 | |||
93ba8c70a0 | |||
17a147c880 | |||
1e99a051e6 | |||
894486fba6 | |||
5502d75ca5 | |||
a87b09908d | |||
09da75af92 | |||
a075a1c8b3 | |||
2686a2e944 | |||
f514b6cd10 | |||
7aa407970f | |||
64daa05140 | |||
90f2fac2d3 | |||
b3d1cc5ea6 | |||
4eebff804b | |||
76511058d1 | |||
4f754c6af2 | |||
17ba93cc9f | |||
bcf34e6e55 | |||
82dda463a8 | |||
b261884d2b | |||
87ba28372c | |||
be9238c9b1 | |||
ca427e0853 | |||
b254c582b9 | |||
80b336aab5 | |||
948457a3dc | |||
38dc1dc9fe | |||
39d9491aff | |||
f3453918c1 | |||
2f9b75fe5e | |||
2df811b647 | |||
1bc73125dc | |||
50ca22d91a | |||
3a8d96f840 | |||
9395be3034 | |||
ff5f9273a8 | |||
80a9fe3396 | |||
6528c7724b | |||
4f6863eb8a | |||
069ccb0735 | |||
e91f1fc74b | |||
811524f31a | |||
d909cec19d | |||
628fd1625b | |||
73b98850db | |||
b4d52cfca8 | |||
879358fae4 | |||
5be8f7e7fb | |||
d8d6f27462 | |||
03711869cd | |||
6a5b3b6849 | |||
b3953ea9db | |||
fd2907cfde | |||
2e3ddd6ed1 | |||
750f2fb7b5 | |||
9ef22f646e | |||
2299f35977 | |||
36c247cb04 | |||
e6080c781b | |||
39287e3400 | |||
c23de31e07 | |||
509571314a | |||
f468790baa | |||
0c2cdc02ae | |||
87a68bfe21 | |||
dc557bdba8 | |||
2deaf00911 | |||
21172249a3 | |||
1977141076 | |||
b42f896584 | |||
02e261880a | |||
f89cb8146d | |||
54ea427dce | |||
a16e1bab09 | |||
8acd91b4d9 | |||
91bacd1dd9 |
27
.gitignore
vendored
@ -3,10 +3,35 @@
|
||||
|
||||
# nodejs local installs
|
||||
node_modules
|
||||
npm-debug.log
|
||||
|
||||
# node webkit cache
|
||||
cache
|
||||
|
||||
# sublime text stuff (the -project should actually be shared, but then we'd have to share the same disk location)
|
||||
*.sublime-project
|
||||
*.sublime-workspace
|
||||
|
||||
# netbeans project folder
|
||||
nbproject
|
||||
|
||||
# git stackdumps
|
||||
*.stackdump
|
||||
*.stackdump
|
||||
|
||||
|
||||
# diffs
|
||||
diff.txt
|
||||
|
||||
# build destination
|
||||
dest
|
||||
build/closure/closure_compiled_binary.js
|
||||
|
||||
# spriting artifacts
|
||||
src/img/icons.png
|
||||
src/css/icons.css
|
||||
|
||||
# plato report directory
|
||||
report
|
||||
|
||||
# marked as private
|
||||
*.private.*
|
75
.jscsrc
Normal file
@ -0,0 +1,75 @@
|
||||
{
|
||||
"requireCurlyBraces": [
|
||||
"if",
|
||||
"else",
|
||||
"for",
|
||||
"while",
|
||||
"do",
|
||||
"try",
|
||||
"catch"
|
||||
],
|
||||
"requireOperatorBeforeLineBreak": true,
|
||||
"requireCamelCaseOrUpperCaseIdentifiers": true,
|
||||
"maximumLineLength": {
|
||||
"value": 80,
|
||||
"allExcept": ["comments", "regex"]
|
||||
},
|
||||
"validateIndentation": 2,
|
||||
"validateQuoteMarks": "'",
|
||||
|
||||
"disallowMultipleLineStrings": true,
|
||||
"disallowMixedSpacesAndTabs": true,
|
||||
"disallowTrailingWhitespace": true,
|
||||
"disallowSpaceAfterPrefixUnaryOperators": true,
|
||||
"disallowMultipleVarDecl": true,
|
||||
"disallowKeywordsOnNewLine": ["else"],
|
||||
|
||||
"requireSpaceAfterKeywords": [
|
||||
"if",
|
||||
"else",
|
||||
"for",
|
||||
"while",
|
||||
"do",
|
||||
"switch",
|
||||
"return",
|
||||
"try",
|
||||
"catch"
|
||||
],
|
||||
"requireSpaceBeforeBinaryOperators": [
|
||||
"=", "+=", "-=", "*=", "/=", "%=", "<<=", ">>=", ">>>=",
|
||||
"&=", "|=", "^=", "+=",
|
||||
|
||||
"+", "-", "*", "/", "%", "<<", ">>", ">>>", "&",
|
||||
"|", "^", "&&", "||", "===", "==", ">=",
|
||||
"<=", "<", ">", "!=", "!=="
|
||||
],
|
||||
"requireSpaceAfterBinaryOperators": true,
|
||||
"requireSpacesInConditionalExpression": true,
|
||||
"requireSpaceBeforeBlockStatements": true,
|
||||
"requireSpacesInForStatement": true,
|
||||
"requireLineFeedAtFileEnd": true,
|
||||
"requireSpacesInFunctionExpression": {
|
||||
"beforeOpeningCurlyBrace": true
|
||||
},
|
||||
"disallowSpacesInAnonymousFunctionExpression": {
|
||||
"beforeOpeningRoundBrace": false
|
||||
},
|
||||
"disallowSpacesInsideObjectBrackets": "all",
|
||||
"disallowSpacesInsideArrayBrackets": "all",
|
||||
"disallowSpacesInsideParentheses": true,
|
||||
|
||||
"disallowMultipleLineBreaks": true,
|
||||
"disallowNewlineBeforeBlockStatements": true,
|
||||
"disallowKeywords": ["with"],
|
||||
"disallowSpacesInFunctionExpression": null,
|
||||
"disallowSpacesInFunctionDeclaration": null,
|
||||
"disallowSpacesInCallExpression": true,
|
||||
"disallowSpaceAfterObjectKeys": false,
|
||||
"requireSpaceBeforeObjectValues": true,
|
||||
"requireCapitalizedConstructors": true,
|
||||
"requireDotNotation": true,
|
||||
"requireSemicolons": true,
|
||||
"validateParameterSeparator": ", ",
|
||||
|
||||
"jsDoc": null
|
||||
}
|
@ -1,7 +1,8 @@
|
||||
language: node_js
|
||||
node_js:
|
||||
- 0.8
|
||||
- "4.1"
|
||||
before_install:
|
||||
- npm update -g npm
|
||||
- npm install -g grunt-cli
|
||||
- git clone git://github.com/n1k0/casperjs.git ~/casperjs
|
||||
- cd ~/casperjs
|
||||
@ -11,3 +12,4 @@ before_install:
|
||||
before_script:
|
||||
- phantomjs --version
|
||||
- casperjs --version
|
||||
sudo: false
|
||||
|
386
Gruntfile.js
@ -1,60 +1,334 @@
|
||||
/**
|
||||
* How to run grunt tasks:
|
||||
* - At project root, run 'npm install' - It will install nodedependencies declared in package,json in <root>/.node_modules
|
||||
* - install grunt CLI tools globally, run 'npm install -g grunt-cli'
|
||||
* - run a grunt target defined in Gruntfiles.js, ex: 'grunt lint'
|
||||
*
|
||||
* Note: The 'ghost' grunt task have special deps on CasperJS and phantomjs.
|
||||
* For now, It's configured to run only on TravisCI where these deps are
|
||||
* correctly defined.
|
||||
* If you run this task locally, it may require some env set up first.
|
||||
*/
|
||||
|
||||
module.exports = function(grunt) {
|
||||
grunt.initConfig({
|
||||
jshint: {
|
||||
/*options: {
|
||||
"evil": true,
|
||||
"asi": true,
|
||||
"smarttabs": true,
|
||||
"eqnull": true
|
||||
},*/
|
||||
files: [
|
||||
'Gruntfile.js',
|
||||
'package.json',
|
||||
'js/**/*.js',
|
||||
'!js/lib/**/*.js' // Exclude lib folder (note the leading !)
|
||||
]
|
||||
},
|
||||
connect: {
|
||||
www: {
|
||||
options: {
|
||||
base: '.',
|
||||
port: 4545
|
||||
}
|
||||
}
|
||||
},
|
||||
ghost: {
|
||||
dist: {
|
||||
filesSrc: ['tests/integration/casperjs/*_test.js'],
|
||||
options: {
|
||||
args: {
|
||||
baseUrl: 'http://localhost:' +
|
||||
'<%= connect.www.options.port %>/'
|
||||
},
|
||||
direct: false,
|
||||
logLevel: 'error',
|
||||
printCommand: false,
|
||||
printFilePaths: true
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
grunt.loadNpmTasks('grunt-contrib-connect');
|
||||
grunt.loadNpmTasks('grunt-contrib-jshint');
|
||||
grunt.loadNpmTasks('grunt-ghost');
|
||||
// Update this variable if you don't want or can't serve on localhost
|
||||
var hostname = 'localhost';
|
||||
|
||||
grunt.registerTask('lint', ['jshint']);
|
||||
grunt.registerTask('test', ['jshint', 'connect', 'ghost']);
|
||||
var PORT = {
|
||||
PROD : 9001,
|
||||
DEV : 9901,
|
||||
TEST : 9991
|
||||
};
|
||||
|
||||
var DEV_MODE = '?debug';
|
||||
|
||||
// create a version based on the build timestamp
|
||||
var dateFormat = require('dateformat');
|
||||
var version = '-' + dateFormat(new Date(), "yyyy-mm-dd-hh-MM");
|
||||
|
||||
/**
|
||||
* Helper to prefix all strings in provided array with the provided path
|
||||
*/
|
||||
var prefixPaths = function (paths, prefix) {
|
||||
return paths.map(function (path) {
|
||||
return prefix + path;
|
||||
});
|
||||
};
|
||||
|
||||
// get the list of scripts paths to include
|
||||
var scriptPaths = require('./src/piskel-script-list.js').scripts;
|
||||
var piskelScripts = prefixPaths(scriptPaths, "src/").filter(function (path) {
|
||||
return path.indexOf('devtools') === -1;
|
||||
});
|
||||
|
||||
// get the list of styles paths to include
|
||||
var stylePaths = require('./src/piskel-style-list.js').styles;
|
||||
var piskelStyles = prefixPaths(stylePaths, "src/");
|
||||
|
||||
var getCasperConfig = function (suiteName, delay, host) {
|
||||
var testPaths = require('./test/casperjs/' + suiteName).tests;
|
||||
var tests = prefixPaths(testPaths, "test/casperjs/");
|
||||
|
||||
return {
|
||||
filesSrc : tests,
|
||||
options : {
|
||||
args : {
|
||||
baseUrl : 'http://' + host + ':' + PORT.TEST,
|
||||
mode : DEV_MODE,
|
||||
delay : delay
|
||||
},
|
||||
async : false,
|
||||
direct : false,
|
||||
logLevel : 'info',
|
||||
printCommand : false,
|
||||
printFilePaths : true
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
var getConnectConfig = function (base, port, host) {
|
||||
if (typeof base === 'string') {
|
||||
base = [base];
|
||||
}
|
||||
|
||||
return {
|
||||
options: {
|
||||
port: port,
|
||||
hostname : host,
|
||||
base: base
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
// load all grunt tasks
|
||||
require('load-grunt-tasks')(grunt);
|
||||
|
||||
grunt.initConfig({
|
||||
clean: {
|
||||
all: ['dest', 'src/img/icons.png', 'src/css/icons.css'],
|
||||
prod: ['dest/prod', 'dest/tmp'],
|
||||
desktop: ['dest/desktop', 'dest/tmp'],
|
||||
dev: ['dest/dev', 'dest/tmp']
|
||||
},
|
||||
|
||||
/**
|
||||
* STYLE CHECKS
|
||||
*/
|
||||
|
||||
leadingIndent : {
|
||||
options: {
|
||||
indentation : "spaces"
|
||||
},
|
||||
css : ['src/css/**/*.css']
|
||||
},
|
||||
|
||||
jscs : {
|
||||
options : {
|
||||
"config": ".jscsrc",
|
||||
"maximumLineLength": 120,
|
||||
"requireCamelCaseOrUpperCaseIdentifiers": "ignoreProperties",
|
||||
"validateQuoteMarks": { "mark": "'", "escape": true },
|
||||
"disallowMultipleVarDecl": "exceptUndefined",
|
||||
"disallowSpacesInAnonymousFunctionExpression": null
|
||||
},
|
||||
js : [ 'src/js/**/*.js' , '!src/js/**/lib/**/*.js' ]
|
||||
},
|
||||
|
||||
jshint: {
|
||||
options: {
|
||||
undef : true,
|
||||
latedef : true,
|
||||
browser : true,
|
||||
trailing : true,
|
||||
curly : true,
|
||||
globals : {'$':true, 'jQuery' : true, 'pskl':true, 'Events':true, 'Constants':true, 'console' : true, 'module':true, 'require':true, 'Q':true}
|
||||
},
|
||||
files: [
|
||||
'Gruntfile.js',
|
||||
'package.json',
|
||||
'src/js/**/*.js',
|
||||
'!src/js/**/lib/**/*.js' // Exclude lib folder (note the leading !)
|
||||
]
|
||||
},
|
||||
|
||||
/**
|
||||
* SERVERS, BROWSER LAUNCHERS
|
||||
*/
|
||||
|
||||
connect: {
|
||||
prod: getConnectConfig('dest/prod', PORT.PROD, hostname),
|
||||
test: getConnectConfig(['dest/dev', 'test'], PORT.TEST, hostname),
|
||||
dev: getConnectConfig(['dest/dev', 'test'], PORT.DEV, hostname)
|
||||
},
|
||||
|
||||
open : {
|
||||
prod : {
|
||||
path : 'http://' + hostname + ':' + PORT.PROD + '/'
|
||||
},
|
||||
dev : {
|
||||
path : 'http://' + hostname + ':' + PORT.DEV + '/' + DEV_MODE
|
||||
}
|
||||
},
|
||||
|
||||
watch: {
|
||||
prod: {
|
||||
files: ['src/**/*.*'],
|
||||
tasks: ['build'],
|
||||
options: {
|
||||
spawn: false
|
||||
}
|
||||
},
|
||||
dev: {
|
||||
files: ['src/**/*.*'],
|
||||
tasks: ['build-dev'],
|
||||
options: {
|
||||
spawn: false
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* BUILD STEPS
|
||||
*/
|
||||
|
||||
sprite:{
|
||||
all : {
|
||||
src: 'src/img/icons/**/*.png',
|
||||
dest: 'src/img/icons.png',
|
||||
destCss: 'src/css/icons.css'
|
||||
}
|
||||
},
|
||||
|
||||
concat : {
|
||||
js : {
|
||||
options : {
|
||||
separator : ';'
|
||||
},
|
||||
src : piskelScripts,
|
||||
dest : 'dest/prod/js/piskel-packaged' + version + '.js'
|
||||
},
|
||||
css : {
|
||||
src : piskelStyles,
|
||||
dest : 'dest/prod/css/piskel-style-packaged' + version + '.css'
|
||||
}
|
||||
},
|
||||
|
||||
uglify : {
|
||||
options : {
|
||||
mangle : true
|
||||
},
|
||||
js : {
|
||||
files : {
|
||||
'dest/tmp/js/piskel-packaged-min.js' : ['dest/prod/js/piskel-packaged' + version + '.js']
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
includereplace: {
|
||||
all: {
|
||||
src: 'src/index.html',
|
||||
dest: 'dest/tmp/index.html',
|
||||
options : {
|
||||
globals : {
|
||||
'version' : version
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
replace: {
|
||||
// main-partial.html is used when embedded in piskelapp.com
|
||||
mainPartial: {
|
||||
options: {
|
||||
patterns: [{
|
||||
match: /^(.|[\r\n])*<!--body-main-start-->/,
|
||||
replacement: "{% raw %}",
|
||||
description : "Remove everything before body-main-start comment"
|
||||
},{
|
||||
match: /<!--body-main-end-->(.|[\r\n])*$/,
|
||||
replacement: "{% endraw %}",
|
||||
description : "Remove everything after body-main-end comment"
|
||||
},{
|
||||
match: /([\r\n]) /g,
|
||||
replacement: "$1",
|
||||
description : "Decrease indentation by one"
|
||||
}
|
||||
]
|
||||
},
|
||||
files: [
|
||||
// src/index.html should already have been moved by the includereplace task
|
||||
{src: ['dest/tmp/index.html'], dest: 'dest/prod/piskelapp-partials/main-partial.html'}
|
||||
]
|
||||
}
|
||||
},
|
||||
|
||||
copy: {
|
||||
prod: {
|
||||
files: [
|
||||
// dest/js/piskel-packaged-min.js should have been created by the uglify task
|
||||
{src: ['dest/tmp/js/piskel-packaged-min.js'], dest: 'dest/prod/js/piskel-packaged-min' + version + '.js'},
|
||||
{src: ['dest/tmp/index.html'], dest: 'dest/prod/index.html'},
|
||||
{src: ['src/logo.png'], dest: 'dest/prod/logo.png'},
|
||||
{src: ['src/js/lib/gif/gif.ie.worker.js'], dest: 'dest/prod/js/lib/gif/gif.ie.worker.js'},
|
||||
{expand: true, src: ['img/**'], cwd: 'src/', dest: 'dest/prod/', filter: 'isFile'},
|
||||
{expand: true, src: ['css/fonts/**'], cwd: 'src/', dest: 'dest/prod/', filter: 'isFile'}
|
||||
]
|
||||
},
|
||||
dev: {
|
||||
files: [
|
||||
// in dev copy everything to dest/dev
|
||||
{src: ['dest/tmp/index.html'], dest: 'dest/dev/index.html'},
|
||||
{src: ['src/piskel-script-list.js'], dest: 'dest/dev/piskel-script-list.js'},
|
||||
{src: ['src/piskel-style-list.js'], dest: 'dest/dev/piskel-style-list.js'},
|
||||
{expand: true, src: ['js/**'], cwd: 'src/', dest: 'dest/dev/', filter: 'isFile'},
|
||||
{expand: true, src: ['css/**'], cwd: 'src/', dest: 'dest/dev/', filter: 'isFile'},
|
||||
{expand: true, src: ['img/**'], cwd: 'src/', dest: 'dest/dev/', filter: 'isFile'},
|
||||
]
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* TESTING
|
||||
*/
|
||||
|
||||
karma: {
|
||||
unit: {
|
||||
configFile: 'karma.conf.js'
|
||||
}
|
||||
},
|
||||
|
||||
ghost : {
|
||||
'travis' : getCasperConfig('TravisTestSuite.js', 10000, hostname),
|
||||
'local' : getCasperConfig('LocalTestSuite.js', 50, hostname)
|
||||
},
|
||||
|
||||
/**
|
||||
* DESKTOP BUILDS
|
||||
*/
|
||||
|
||||
nwjs: {
|
||||
windows : {
|
||||
options: {
|
||||
version : "0.12.3",
|
||||
build_dir: './dest/desktop/', // destination folder of releases.
|
||||
win: true,
|
||||
linux32: true,
|
||||
linux64: true
|
||||
},
|
||||
src: ['./dest/prod/**/*', "./package.json", "!./dest/desktop/"]
|
||||
},
|
||||
macos : {
|
||||
options: {
|
||||
osx64: true,
|
||||
version : "0.12.3",
|
||||
build_dir: './dest/desktop/'
|
||||
},
|
||||
src: ['./dest/prod/**/*', "./package.json", "!./dest/desktop/"]
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Validate
|
||||
grunt.registerTask('lint', ['jscs:js', 'leadingIndent:css', 'jshint']);
|
||||
|
||||
// karma/unit-tests task
|
||||
grunt.registerTask('unit-test', ['karma']);
|
||||
|
||||
// Validate & Test
|
||||
grunt.registerTask('test-travis', ['lint', 'unit-test', 'build-dev', 'connect:test', 'ghost:travis']);
|
||||
// Validate & Test (faster version) will NOT work on travis !!
|
||||
grunt.registerTask('test-local', ['lint', 'unit-test', 'build-dev', 'connect:test', 'ghost:local']);
|
||||
grunt.registerTask('test-local-nolint', ['unit-test', 'build-dev', 'connect:test', 'ghost:local']);
|
||||
|
||||
grunt.registerTask('test', ['test-travis']);
|
||||
grunt.registerTask('precommit', ['test-local']);
|
||||
|
||||
grunt.registerTask('build-index.html', ['includereplace']);
|
||||
grunt.registerTask('merge-statics', ['concat:js', 'concat:css', 'uglify']);
|
||||
grunt.registerTask('build', ['clean:prod', 'sprite', 'merge-statics', 'build-index.html', 'replace', 'copy:prod']);
|
||||
grunt.registerTask('build-dev', ['clean:dev', 'sprite', 'build-index.html', 'copy:dev']);
|
||||
|
||||
// Validate & Build
|
||||
grunt.registerTask('default', ['lint', 'build']);
|
||||
|
||||
// Build stand alone app with nodewebkit
|
||||
grunt.registerTask('desktop', ['clean:desktop', 'default', 'nwjs:windows']);
|
||||
grunt.registerTask('desktop-mac', ['clean:desktop', 'default', 'nwjs:macos']);
|
||||
|
||||
// Start webserver and watch for changes
|
||||
grunt.registerTask('serve', ['build', 'connect:prod', 'open:prod', 'watch:prod']);
|
||||
// Start webserver on src folder, in debug mode
|
||||
grunt.registerTask('serve-dev', ['build-dev', 'connect:dev', 'open:dev', 'watch:dev']);
|
||||
|
||||
grunt.registerTask('serve-debug', ['serve-dev']);
|
||||
grunt.registerTask('play', ['serve-dev']);
|
||||
};
|
||||
|
202
LICENSE
Normal file
@ -0,0 +1,202 @@
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
119
README.md
@ -1,70 +1,89 @@
|
||||
Piskel
|
||||
======
|
||||
|
||||
The goal is to create an easy-to-use/in-the-cloud/web-based 2d animation editor.
|
||||
[](https://travis-ci.org/juliandescottes/piskel) [](http://gruntjs.com/)
|
||||
|
||||
Try it at : http://juliandescottes.github.com/piskel/
|
||||
A simple web-based tool for Spriting and Pixel art.
|
||||
|
||||
29 May 2013
|
||||
-----------------------------------
|
||||
Trying to wake up after a big 8 months nap.
|
||||

|
||||
|
||||

|
||||
You can try the standalone editor at **http://juliandescottes.github.io/piskel** or see it integrated in **http://piskelapp.com**.
|
||||
|
||||
Also, new features :
|
||||
* color picker : 
|
||||
* create an animated GIF from your piskel (uses jsgif, results may vary ...)
|
||||
Piskel is mainly developped by :
|
||||
|
||||
16 Sep 2012
|
||||
------------------------------------
|
||||
Just a quick update to post a new screenshot. @grosbouddha is delivering features so fast, it's hard to keep up !
|
||||
* **[@juliandescottes](https://github.com/juliandescottes)**
|
||||
* **[@grosbouddha](https://github.com/grosbouddha)**
|
||||
|
||||

|
||||
## What's the point ?
|
||||
|
||||
15 Sep 2012
|
||||
------------------------------------
|
||||
2 weeks already since the last README.md update, and so many changes ! There has been a continuous stream of features added to piskel by @grosboudda, @captainbrosset (thanks guys) and myself.
|
||||
I can't list everything here but quickly
|
||||
* __Tools__ : in addition to the regular Pen, you can now draw Rectangles, Circles. You can move stuff, copy, paste !
|
||||
* __Undo/redo__ : you can now cancel your actions using ctrl-z/ctrl-y
|
||||
* __Drag and drop__ : move frames around in your framesheet, using drag and drop
|
||||
You can use Piskel to do two things :
|
||||
* **spriting** : create retro-style sprites for games
|
||||
|
||||
And a screenshot, for the record :
|
||||

|
||||
|
||||

|
||||
* **pixelart** : create crazy/pretty pixelart animations for fun !
|
||||
|
||||
30 Aug 2012
|
||||
------------------------------------
|
||||
Many new features in 2 days :
|
||||
* __save animations__, they are persisted in the cloud, and can be retrieved via a __unique URL__
|
||||
* __color picker__, no longer limited to black and white
|
||||
* __local storage__, your work is automatically backed up locally
|
||||
* __color palette__, listing all the colors already used in the animation
|
||||
* __slider__ for choosing the speed of the preview
|
||||

|
||||
|
||||
UI was slightly updated :
|
||||
Integrated in **[piskelapp.com](http://piskelapp.com)**, you can share everything you work on with others as easily as you share a link.
|
||||
|
||||

|
||||
## Requirements
|
||||
|
||||
28 Aug 2012
|
||||
------------------------------------
|
||||
Thanks to grosbouddha, new features added to Piskel :
|
||||
* modify preview speed !
|
||||
* remove frames
|
||||
* transparent background
|
||||
Piskel supports the following browsers :
|
||||
* **Chrome** (latest)
|
||||
* **Firefox** (latest)
|
||||
* **Internet Explorer** 11+
|
||||
|
||||
24 Aug 2012 (aka the thing I did last night)
|
||||
------------------------------------
|
||||
* create small animations in __black__ (left click) and __white__ (right click)
|
||||
* and actually animations are always in __32x32__ zoomed 10 times
|
||||
* you can __not even save them__ !
|
||||
* add new frames for your animation
|
||||
* do small __ridiculous__ characters
|
||||
... and a fairly recent computer.
|
||||
|
||||
Looks like this :
|
||||

|
||||
We don't plan/want/could be forced into supporting older IEs. For Opera and Safari, we've never tested them but the gap shouldn't be huge.
|
||||
|
||||
**On the left**, the list of frames for the animation.
|
||||
**In the 'middle'**, the editable canvas.
|
||||
**On the right**, the LIVE-ANIMATED-PREVIEW (rocket science stuff going on here).
|
||||
## Offline version
|
||||
|
||||
Offline builds are available. More details in the [dedicated wiki page](https://github.com/juliandescottes/piskel/wiki/Desktop-applications).
|
||||
|
||||
## Built with
|
||||
|
||||
The Piskel editor is purely built in **JavaScript, HTML and CSS**. It uses Canvas extensively for displaying all them pretty sprites.
|
||||
|
||||
We also use the following **libraries** :
|
||||
* [spectrum](https://github.com/bgrins/spectrum) : awesome standalone colorpicker
|
||||
* [gifjs](http://jnordberg.github.io/gif.js/) : generate animated GIFs in javascript, using webworkers
|
||||
* [supergif](https://github.com/buzzfeed/libgif-js) : modified version of SuperGif to parse and import GIFs
|
||||
* [jszip](https://github.com/Stuk/jszip) : create, read and edit .zip files with Javascript
|
||||
* [canvas-toBlob](https://github.com/eligrey/canvas-toBlob.js/) : shim for canvas toBlob
|
||||
* [jquery](http://jquery.com/) : used sporadically in the application
|
||||
* [bootstrap-tooltip](http://getbootstrap.com/javascript/#tooltips) : nice tooltips
|
||||
|
||||
As well as some **icons** from the [Noun Project](http://thenounproject.com/) :
|
||||
* Folder by Simple Icons from The Noun Project
|
||||
* (and probably one or two others)
|
||||
|
||||
|
||||
## Contributing ?
|
||||
|
||||
Help is always welcome !
|
||||
|
||||
* **Issues** : Found a problem when using the application, want to request a feature, [open an issue](https://github.com/juliandescottes/piskel/issues).
|
||||
* **Participate** : Have a look at the [wiki](https://github.com/juliandescottes/piskel/wiki) to set up the development environment
|
||||
|
||||
## License
|
||||
|
||||
Copyright 2016 Julian Descottes
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
## Mobile/Tablets
|
||||
|
||||
There is no support for mobile for now.
|
||||
|
||||
|
39
all.html
@ -1,39 +0,0 @@
|
||||
<!doctype html5>
|
||||
<html>
|
||||
<head>
|
||||
<title>All piskels</title>
|
||||
</head>
|
||||
<body>
|
||||
<script type="text/javascript">
|
||||
(function () {
|
||||
var loadAllPiskelIds = function (frameId) {
|
||||
var xhr = new XMLHttpRequest();
|
||||
// TODO: Change frameId to framesheetId on the backend
|
||||
xhr.open('GET', 'http://2.piskel-app.appspot.com/all?l=' + frameId, true);
|
||||
xhr.responseType = 'text';
|
||||
|
||||
xhr.onload = function(e) {
|
||||
var ul = document.createElement("UL");
|
||||
var innerHTML = "";
|
||||
eval("var responseObject = " + this.responseText);
|
||||
var keys = responseObject.keys;
|
||||
var baseUrl = window.location.origin + "/piskel/?frameId=";
|
||||
if (keys) {
|
||||
for (var i = 0 ; i < keys.length ; i++) {
|
||||
var key = keys[i];
|
||||
|
||||
innerHTML += "<li><a target='_blank' href='"+baseUrl+key+"'>"+key+"</a></li>"
|
||||
}
|
||||
}
|
||||
ul.innerHTML = innerHTML;
|
||||
document.body.appendChild(ul);
|
||||
};
|
||||
|
||||
xhr.send();
|
||||
};
|
||||
|
||||
loadAllPiskelIds();
|
||||
})();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
309
css/style.css
@ -1,309 +0,0 @@
|
||||
|
||||
|
||||
body {
|
||||
background: radial-gradient(circle, #000, #373737);
|
||||
/* 16/06/2013 : -webkit still needed for
|
||||
safari, safari mobile and android browser and chrome for android
|
||||
cf http://caniuse.com/css-gradients */
|
||||
background: -webkit-radial-gradient(circle, #000, #373737);
|
||||
}
|
||||
|
||||
/**
|
||||
* Application layout
|
||||
*/
|
||||
|
||||
.main-wrapper {
|
||||
position: absolute;
|
||||
top: 5px;
|
||||
right: 0;
|
||||
bottom: 5px;
|
||||
left: 0;
|
||||
}
|
||||
|
||||
.column-wrapper {
|
||||
text-align: center;
|
||||
font-size: 0;
|
||||
position: absolute;
|
||||
left: 100px; /* Reserve room for tools on the left edge of the screen. */
|
||||
top: 0;
|
||||
right: 50px; /* Reserve room for actions on the right edge of the screen. */
|
||||
bottom: 0;
|
||||
}
|
||||
|
||||
.piskel-name-container {
|
||||
overflow:hidden;
|
||||
position:fixed;
|
||||
top:10px;
|
||||
left:10px;
|
||||
color:white;
|
||||
font-family:Tahoma;
|
||||
z-index: 10000;
|
||||
}
|
||||
|
||||
.piskel-name-container #piskel-name {
|
||||
border :none;
|
||||
color : lightgrey;
|
||||
background: transparent;
|
||||
font-size:16pt;
|
||||
|
||||
}
|
||||
|
||||
.column {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.left-column {
|
||||
vertical-align: top;
|
||||
height: 100%;
|
||||
margin-right: 7px;
|
||||
}
|
||||
|
||||
.main-column {
|
||||
height: 100%;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.right-column {
|
||||
vertical-align: top;
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
.drawing-canvas-container {
|
||||
font-size: 0;
|
||||
}
|
||||
|
||||
.sticky-section {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
z-index: 1000;
|
||||
}
|
||||
|
||||
.sticky-section .sticky-section-wrap {
|
||||
display: table;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.sticky-section .vertical-centerer {
|
||||
display: table-cell;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.left-sticky-section.sticky-section {
|
||||
left: 0;
|
||||
max-width: 100px;
|
||||
}
|
||||
|
||||
.left-sticky-section .tool-icon {
|
||||
float: left;
|
||||
}
|
||||
|
||||
.right-sticky-section.sticky-section {
|
||||
right: 0;
|
||||
width: 47px;
|
||||
|
||||
-webkit-transition: all 200ms ease-out;
|
||||
-moz-transition: all 200ms ease-out;
|
||||
-ms-transition: all 200ms ease-out;
|
||||
transition: all 200ms ease-out;
|
||||
}
|
||||
|
||||
.right-sticky-section .tool-icon {
|
||||
float: right;
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
.drawer {
|
||||
|
||||
}
|
||||
|
||||
.drawer-content {
|
||||
overflow: hidden;
|
||||
background-color: #444;
|
||||
height: 550px;
|
||||
max-height: 100%;
|
||||
width: 280px;
|
||||
border-top-left-radius: 4px;
|
||||
border-bottom-left-radius: 4px;
|
||||
}
|
||||
|
||||
/** Righty sticky drawer expanded state. */
|
||||
|
||||
.right-sticky-section.expanded {
|
||||
right: 280px;
|
||||
}
|
||||
|
||||
.right-sticky-section.expanded .tool-icon {
|
||||
margin-right: 1px;
|
||||
}
|
||||
|
||||
.right-sticky-section .tool-icon.has-expanded-drawer {
|
||||
position: relative;
|
||||
background-color: #444;
|
||||
margin-right: 0;
|
||||
padding-right: 1px;
|
||||
}
|
||||
|
||||
.settings-section {
|
||||
margin: 10px 20px;
|
||||
font-size: 12px;
|
||||
font-weight: bold;
|
||||
color: #ccc;
|
||||
text-shadow: 1px 1px #000;
|
||||
}
|
||||
|
||||
.settings-title {
|
||||
margin-top: 20px;
|
||||
margin-bottom: 10px;
|
||||
text-transform: uppercase;
|
||||
border-bottom: 1px #aaa solid;
|
||||
padding-bottom: 5px;
|
||||
}
|
||||
|
||||
.settings-item {}
|
||||
|
||||
.background-picker-wrapper {
|
||||
overflow: hidden;
|
||||
padding: 10px 5px 20px 5px;
|
||||
}
|
||||
|
||||
.background-picker {
|
||||
cursor: pointer;
|
||||
float: left;
|
||||
height: 35px;
|
||||
width: 35px;
|
||||
background-color: transparent;
|
||||
margin-right: 15px;
|
||||
padding: 1px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.background-picker:after {
|
||||
content: " ";
|
||||
position: absolute;
|
||||
top: -2px;
|
||||
right: -2px;
|
||||
bottom: -2px;
|
||||
left: -2px;
|
||||
}
|
||||
|
||||
.background-picker:hover:after {
|
||||
border: #eee 1px solid;
|
||||
}
|
||||
|
||||
.background-picker.selected:after {
|
||||
border: gold 1px solid;
|
||||
}
|
||||
|
||||
/**
|
||||
* Canvases layout
|
||||
*/
|
||||
|
||||
.canvas {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.canvas-container {
|
||||
position: relative;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.canvas-container .canvas-background {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
}
|
||||
|
||||
.light-picker-background,
|
||||
.light-canvas-background .canvas-background {
|
||||
background: url(../img/canvas_background/light_canvas_background.png) repeat;
|
||||
}
|
||||
|
||||
.medium-picker-background,
|
||||
.medium-canvas-background .canvas-background {
|
||||
background: url(../img/canvas_background/medium_canvas_background.png) repeat;
|
||||
}
|
||||
|
||||
.lowcont-medium-picker-background,
|
||||
.lowcont-medium-canvas-background .canvas-background {
|
||||
background: url(../img/canvas_background/lowcont_medium_canvas_background.png) repeat;
|
||||
}
|
||||
|
||||
.lowcont-dark-picker-background,
|
||||
.lowcont-dark-canvas-background .canvas-background {
|
||||
background: url(../img/canvas_background/lowcont_dark_canvas_background.png) repeat;
|
||||
}
|
||||
|
||||
|
||||
.canvas.canvas-overlay {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Animated preview styles.
|
||||
*/
|
||||
|
||||
.preview-container {
|
||||
border : 0px Solid black;
|
||||
border-radius:5px 0px 0px 5px;
|
||||
box-shadow : 0px 0px 2px rgba(0,0,0,0.2);
|
||||
font-size: 0;
|
||||
}
|
||||
|
||||
.preview-container canvas {
|
||||
border : 0px Solid transparent;
|
||||
}
|
||||
|
||||
.display-fps {
|
||||
float: left;
|
||||
color: #aaa;
|
||||
font-size: 12px;
|
||||
min-width: 55px;
|
||||
vertical-align: bottom;
|
||||
line-height: 24px;
|
||||
}
|
||||
|
||||
.range-fps {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/**
|
||||
* User messages
|
||||
*/
|
||||
.user-message {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background-color: #F9EDBE;
|
||||
padding: 10px 47px;
|
||||
border-top-left-radius: 7px;
|
||||
color: #222;
|
||||
border: #F0C36D 1px solid;
|
||||
border-right: 0;
|
||||
border-bottom: 0;
|
||||
font-weight: bold;
|
||||
font-size: 13px;
|
||||
z-index: 10000;
|
||||
max-width: 300px;
|
||||
}
|
||||
|
||||
.user-message .close {
|
||||
position: absolute;
|
||||
top: 6px;
|
||||
right: 17px;
|
||||
color: gray;
|
||||
font-weight: bold;
|
||||
cursor: pointer;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.user-message .close:hover {
|
||||
color: black;
|
||||
}
|
223
css/tools.css
@ -1,223 +0,0 @@
|
||||
|
||||
.tools-wrapper,
|
||||
.options-wrapper,
|
||||
.palette-wrapper {
|
||||
float: left;
|
||||
}
|
||||
|
||||
.tool-icon {
|
||||
cursor : pointer;
|
||||
width: 46px;
|
||||
height: 46px;
|
||||
margin: 1px;
|
||||
background-color: #3a3a3a;
|
||||
background-repeat: no-repeat;
|
||||
background-position: 12px 12px;
|
||||
background-size: 24px 24px;
|
||||
}
|
||||
|
||||
.tool-icon.selected {
|
||||
cursor: default;
|
||||
background-color: #444;
|
||||
cursor: normal;
|
||||
border: 1px gold solid;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.tool-icon:hover {
|
||||
background-color: #444;
|
||||
}
|
||||
|
||||
/*
|
||||
* Tool icons:
|
||||
*/
|
||||
.tool-icon.tool-pen {
|
||||
background-image: url(../img/tools/pen.png);
|
||||
}
|
||||
|
||||
.tool-icon.tool-vertical-mirror-pen {
|
||||
background-image: url(../img/tools/mirror.png);
|
||||
background-position: 0px 10px;
|
||||
background-size: 38px 27px;
|
||||
}
|
||||
|
||||
.tool-icon.tool-paint-bucket {
|
||||
background-image: url(../img/tools/paintbucket.png);
|
||||
}
|
||||
|
||||
.tool-icon.tool-eraser {
|
||||
background-image: url(../img/tools/eraser.png);
|
||||
}
|
||||
|
||||
.tool-icon.tool-stroke {
|
||||
background-image: url(../img/tools/stroke.png);
|
||||
}
|
||||
|
||||
.tool-icon.tool-rectangle {
|
||||
background-image: url(../img/tools/rectangle.png);
|
||||
background-position: 12px 14px;
|
||||
background-size: 24px 20px;
|
||||
}
|
||||
|
||||
.tool-icon.tool-circle {
|
||||
background-image: url(../img/tools/circle.png);
|
||||
}
|
||||
|
||||
.tool-icon.tool-move {
|
||||
background-image: url(../img/tools/hand.png);
|
||||
background-position: 12px 12px;
|
||||
background-size: 24px 24px;
|
||||
}
|
||||
|
||||
.tool-icon.tool-rectangle-select {
|
||||
background-image: url(../img/tools/rectangle_selection.png);
|
||||
background-position: 12px 14px;
|
||||
background-size: 24px 20px;
|
||||
}
|
||||
|
||||
.tool-icon.tool-shape-select {
|
||||
background-image: url(../img/tools/magicwand.png);
|
||||
}
|
||||
|
||||
.tool-icon.tool-colorpicker {
|
||||
background-image: url(../img/tools/eyedropper.png);
|
||||
background-position: 12px 12px;
|
||||
background-size: 23px 23px;
|
||||
}
|
||||
|
||||
/*
|
||||
* Tool cursors:
|
||||
*/
|
||||
|
||||
.tool-paint-bucket .drawing-canvas-container:hover {
|
||||
cursor: url(../img/icons/paint-bucket.png) 12 12, pointer;
|
||||
}
|
||||
|
||||
.tool-vertical-mirror-pen .drawing-canvas-container:hover {
|
||||
cursor: url(../img/icons/vertical-mirror-pen.png) 5 15, pointer;
|
||||
}
|
||||
|
||||
.tool-pen .drawing-canvas-container:hover {
|
||||
cursor: url(../img/icons/pen.png) 0 15, pointer;
|
||||
}
|
||||
|
||||
.tool-eraser .drawing-canvas-container:hover {
|
||||
cursor: url(../img/icons/eraser.png) 0 15, pointer;
|
||||
}
|
||||
|
||||
.tool-stroke .drawing-canvas-container:hover {
|
||||
cursor: url(../img/icons/pen.png) 0 15, pointer;
|
||||
}
|
||||
|
||||
.tool-rectangle .drawing-canvas-container:hover {
|
||||
cursor: url(../img/icons/rectangle.png) 0 15, pointer;
|
||||
}
|
||||
|
||||
.tool-circle .drawing-canvas-container:hover {
|
||||
cursor: url(../img/icons/circle.png) 2 15, pointer;
|
||||
}
|
||||
|
||||
.tool-move .drawing-canvas-container:hover {
|
||||
cursor: url(../img/icons/hand.png) 15 15, pointer;
|
||||
}
|
||||
|
||||
.tool-rectangle-select .drawing-canvas-container:hover {
|
||||
cursor: url(../img/icons/select.png) 15 15, pointer;
|
||||
}
|
||||
|
||||
.tool-shape-select .drawing-canvas-container:hover {
|
||||
cursor: url(../img/icons/wand.png) 15 15, pointer;
|
||||
}
|
||||
|
||||
.tool-colorpicker .drawing-canvas-container:hover {
|
||||
cursor: url(../img/icons/dropper.png) 0 15, pointer;
|
||||
}
|
||||
|
||||
.tool-color-picker input {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
text-indent: -10000px;
|
||||
border: 1px solid black;
|
||||
background: white;
|
||||
cursor: pointer;
|
||||
position : relative;
|
||||
top: 10px;
|
||||
margin-left: 2px;
|
||||
}
|
||||
|
||||
.tool-color-picker .secondary-color-picker {
|
||||
top : 18px;
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
|
||||
.palette .palette-color.transparent-color {
|
||||
position: relative;
|
||||
top: 10px;
|
||||
left: 10px;
|
||||
background-color: white;
|
||||
height : 16px;
|
||||
width : 16px;
|
||||
border: 2px solid #000;
|
||||
background-size: 16px 16px;
|
||||
background-position: 0 0;
|
||||
background-image: -webkit-gradient(
|
||||
linear,
|
||||
left top,
|
||||
right bottom,
|
||||
color-stop(0, #fff),
|
||||
color-stop(0.45, #fff),
|
||||
color-stop(0.5, #ff0000),
|
||||
color-stop(0.55, #fff),
|
||||
color-stop(1, #fff)
|
||||
);
|
||||
background-image: -moz-linear-gradient(
|
||||
left top,
|
||||
#fff 0%,
|
||||
#fff 45%,
|
||||
#f00 50%,
|
||||
#fff 55%,
|
||||
#fff 100%
|
||||
);
|
||||
}
|
||||
|
||||
/*
|
||||
* Framesheet level actions:
|
||||
*/
|
||||
|
||||
.tool-icon.gallery-icon {
|
||||
background-image: url(../img/gallery.png);
|
||||
background-position: 3px 3px;
|
||||
background-size: 39px 39px;
|
||||
}
|
||||
|
||||
.tool-icon.save-icon {
|
||||
background-image: url(../img/save.png);
|
||||
background-position: 6px 6px;
|
||||
background-size: 36px 36px;
|
||||
}
|
||||
|
||||
.tool-icon.gear-icon {
|
||||
background-image: url(../img/gear.png);
|
||||
background-position: 6px 7px;
|
||||
background-size: 32px 32px;
|
||||
}
|
||||
|
||||
.tool-icon.upload-cloud-icon {
|
||||
background-image: url(../img/cloud_export.png);
|
||||
background-position: 4px 0px;
|
||||
background-size: 36px 36px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.upload-cloud-icon .label {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
bottom: 4px;
|
||||
right: 0;
|
||||
font-size: 11px;
|
||||
text-transform: uppercase;
|
||||
color: #fff;
|
||||
text-align: center;
|
||||
}
|
||||
|
Before Width: | Height: | Size: 223 B |
Before Width: | Height: | Size: 431 B |
Before Width: | Height: | Size: 278 B |
Before Width: | Height: | Size: 418 B |
Before Width: | Height: | Size: 634 B |
BIN
img/gear.png
Before Width: | Height: | Size: 789 B |
BIN
img/plus.png
Before Width: | Height: | Size: 271 B |
BIN
img/save.png
Before Width: | Height: | Size: 395 B |
Before Width: | Height: | Size: 3.1 KiB |
Before Width: | Height: | Size: 1010 B |
Before Width: | Height: | Size: 3.8 KiB |
Before Width: | Height: | Size: 1.0 KiB |
Before Width: | Height: | Size: 1.9 KiB |
Before Width: | Height: | Size: 1.6 KiB |
Before Width: | Height: | Size: 1.2 KiB |
Before Width: | Height: | Size: 1.6 KiB |
Before Width: | Height: | Size: 245 B |
Before Width: | Height: | Size: 372 B |
Before Width: | Height: | Size: 1.2 KiB |
188
index.html
@ -1,188 +0,0 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
|
||||
|
||||
<title>Piskel</title>
|
||||
<meta name="description" content="">
|
||||
<meta name="author" content="Julian Descottes">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<link rel="stylesheet" type="text/css" href="css/reset.css">
|
||||
<link rel="stylesheet" type="text/css" href="css/style.css">
|
||||
<link rel="stylesheet" type="text/css" href="css/tools.css">
|
||||
<link rel="stylesheet" type="text/css" href="css/bootstrap/bootstrap.css">
|
||||
<link rel="stylesheet" type="text/css" href="css/bootstrap/bootstrap-tooltip-custom.css">
|
||||
<link rel="stylesheet" type="text/css" href="css/preview-film-section.css">
|
||||
</head>
|
||||
<body>
|
||||
<div class="piskel-name-container">
|
||||
<input readonly id="piskel-name" type="text" value=""/>
|
||||
</div>
|
||||
<div id="main-wrapper" class="main-wrapper">
|
||||
<div class="sticky-section left-sticky-section" id="tool-section">
|
||||
<div class="sticky-section-wrap">
|
||||
<div class="vertical-centerer">
|
||||
<ul id="tools-container" class="tools-wrapper"></ul>
|
||||
<div class="palette-wrapper">
|
||||
<div class="tool-icon tool-color-picker">
|
||||
<input id="color-picker" class="color {hash:true}" type="text" value="" />
|
||||
<input id="secondary-color-picker" class="secondary-color-picker color {hash:true}" type="text" value="" />
|
||||
</div>
|
||||
<div class="tool-icon tool-palette">
|
||||
<div id="palette" class="palette">
|
||||
<span class="tool-icon palette-color transparent-color" data-color="TRANSPARENT" title="Transparent"></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="application-action-section" class="sticky-section right-sticky-section">
|
||||
<div class="sticky-section-wrap">
|
||||
<div class="vertical-centerer">
|
||||
<div id="settings" class="tool-icon gear-icon" title="Preferences" rel="tooltip" data-placement="left"></div>
|
||||
<a class="tool-icon gallery-icon" title="Visit gallery" href="http://juliandescottes.github.io/piskel-website/" rel="tooltip" data-placement="left" target="_blank"></a>
|
||||
<div class="tool-icon save-icon" title="Save to gallery" onclick="pskl.app.storeSheet()" rel="tooltip" data-placement="left" ></div>
|
||||
<div class="tool-icon upload-cloud-icon" title="Upload as an animated GIF" onclick="pskl.app.uploadAsAnimatedGIF()" rel="tooltip" data-placement="left">
|
||||
<span class="label">GIF</span>
|
||||
</div>
|
||||
<div class="tool-icon upload-cloud-icon" title="Upload as a spritesheet PNG" onclick="pskl.app.uploadAsSpritesheetPNG()" rel="tooltip" data-placement="left">
|
||||
<span class="label">PNG</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="drawer vertical-centerer">
|
||||
<div class="drawer-content">
|
||||
<div class="settings-section">
|
||||
<div class="settings-title">
|
||||
Canvas settings:
|
||||
</div>
|
||||
<div class="settings-item">
|
||||
<label>Background:</label>
|
||||
<div id="background-picker-wrapper" class="background-picker-wrapper">
|
||||
<div class="background-picker light-picker-background" data-background-class="light-canvas-background"
|
||||
rel="tooltip" data-placement="bottom" title="light / high contrast">
|
||||
</div>
|
||||
<div class="background-picker medium-picker-background" data-background-class="medium-canvas-background"
|
||||
rel="tooltip" data-placement="bottom" title="medium / high contrast">
|
||||
</div>
|
||||
<div class="background-picker lowcont-medium-picker-background" data-background-class="lowcont-medium-canvas-background"
|
||||
rel="tooltip" data-placement="bottom" title="medium / low contrast">
|
||||
</div>
|
||||
<div class="background-picker lowcont-dark-picker-background" data-background-class="lowcont-dark-canvas-background"
|
||||
rel="tooltip" data-placement="bottom" title="dark / low contrast">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="settings-item">
|
||||
<label for="show-grid">Show grid:</label> <input id="show-grid" type="checkbox"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="column-wrapper" class="column-wrapper">
|
||||
<div class='column left-column'>
|
||||
|
||||
<!-- List of frames: -->
|
||||
<div id="preview-list-wrapper" class="preview-list-wrapper">
|
||||
<div id="preview-list-scroller" class="preview-list-scroller">
|
||||
<ul class="preview-list" id="preview-list"></ul>
|
||||
</div>
|
||||
<div class="top-overflow"></div>
|
||||
<div class="bottom-overflow"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class='column main-column'>
|
||||
<!-- Drawing area: -->
|
||||
<div id="drawing-canvas-container" class="drawing-canvas-container canvas-container">
|
||||
<div class="canvas-background"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="column right-column">
|
||||
<!-- Animation preview: -->
|
||||
<div class='preview-container'>
|
||||
<div id='preview-canvas-container' class="canvas-container">
|
||||
<div class="canvas-background"></div>
|
||||
</div>
|
||||
<div>
|
||||
<span id="display-fps" class="display-fps">12 FPS</span>
|
||||
<input id="preview-fps" class="range-fps" type="range" min="1" max="24" value="12"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Core libraries: -->
|
||||
<script src="js/lib/jquery-1.8.0.js"></script>
|
||||
<script src="js/lib/jquery-ui-1.10.3.custom.js"></script>
|
||||
<script src="js/lib/pubsub.js"></script>
|
||||
<script src="js/lib/bootstrap/bootstrap.js"></script>
|
||||
|
||||
<!-- GIF Encoding libraries -->
|
||||
<script src="js/lib/gif/GIFEncoder.js"></script>
|
||||
<script src="js/lib/gif/b64.js"></script>
|
||||
<script src="js/lib/gif/NeuQuant.js"></script>
|
||||
<script src="js/lib/gif/LZWEncoder.js"></script>
|
||||
|
||||
<!-- Application wide configuration -->
|
||||
<script src="js/Constants.js"></script>
|
||||
<script src="js/Events.js"></script>
|
||||
|
||||
<!-- Libraries -->
|
||||
<script src="js/utils/core.js"></script>
|
||||
<script src="js/utils/PixelUtils.js"></script>
|
||||
<script src="js/utils/CanvasUtils.js"></script>
|
||||
<script src="js/utils/UserSettings.js"></script>
|
||||
<script src="js/lib/jsColor_1_4_0/jscolor.js"></script>
|
||||
|
||||
<!-- Application libraries-->
|
||||
<script src="js/rendering/DrawingLoop.js"></script>
|
||||
<!-- Models -->
|
||||
<script src="js/model/Frame.js"></script>
|
||||
<script src="js/model/FrameSheet.js"></script>
|
||||
<script src="js/selection/SelectionManager.js"></script>
|
||||
<script src="js/selection/BaseSelection.js"></script>
|
||||
<script src="js/selection/RectangularSelection.js"></script>
|
||||
<script src="js/selection/ShapeSelection.js"></script>
|
||||
<!-- Rendering -->
|
||||
<script src="js/rendering/CanvasRenderer.js"></script>
|
||||
<script src="js/rendering/FrameRenderer.js"></script>
|
||||
<script src="js/rendering/SpritesheetRenderer.js"></script>
|
||||
<!-- Controllers -->
|
||||
<script src="js/controller/DrawingController.js"></script>
|
||||
<script src="js/controller/PreviewFilmController.js"></script>
|
||||
<script src="js/controller/AnimatedPreviewController.js"></script>
|
||||
<script src="js/controller/ToolController.js"></script>
|
||||
<script src="js/controller/PaletteController.js"></script>
|
||||
<script src="js/controller/NotificationController.js"></script>
|
||||
<script src="js/controller/SettingsController.js"></script>
|
||||
<!-- Services -->
|
||||
<script src="js/service/LocalStorageService.js"></script>
|
||||
<script src="js/service/HistoryService.js"></script>
|
||||
<script src="js/service/KeyboardEventService.js"></script>
|
||||
<!-- Tools-->
|
||||
<script src="js/drawingtools/BaseTool.js"></script>
|
||||
<script src="js/drawingtools/SimplePen.js"></script>
|
||||
<script src="js/drawingtools/VerticalMirrorPen.js"></script>
|
||||
<script src="js/drawingtools/Eraser.js"></script>
|
||||
<script src="js/drawingtools/Stroke.js"></script>
|
||||
<script src="js/drawingtools/PaintBucket.js"></script>
|
||||
<script src="js/drawingtools/Rectangle.js"></script>
|
||||
<script src="js/drawingtools/Circle.js"></script>
|
||||
<script src="js/drawingtools/Move.js"></script>
|
||||
<script src="js/drawingtools/selectiontools/BaseSelect.js"></script>
|
||||
<script src="js/drawingtools/selectiontools/RectangleSelect.js"></script>
|
||||
<script src="js/drawingtools/selectiontools/ShapeSelect.js"></script>
|
||||
<script src="js/drawingtools/ColorPicker.js"></script>
|
||||
|
||||
<!-- Application controller and initialization -->
|
||||
<script src="js/piskel.js"></script>
|
||||
</body>
|
||||
</html>
|
@ -1,36 +0,0 @@
|
||||
// TODO(grosbouddha): put under pskl namespace.
|
||||
var Constants = {
|
||||
DEFAULT_SIZE : {
|
||||
height : 32,
|
||||
width : 32
|
||||
},
|
||||
|
||||
MAX_HEIGHT : 128,
|
||||
MAX_WIDTH : 128,
|
||||
|
||||
DEFAULT_PEN_COLOR : '#000000',
|
||||
TRANSPARENT_COLOR : 'TRANSPARENT',
|
||||
|
||||
/*
|
||||
* Fake semi-transparent color used to highlight transparent
|
||||
* strokes and rectangles:
|
||||
*/
|
||||
SELECTION_TRANSPARENT_COLOR: 'rgba(255, 255, 255, 0.6)',
|
||||
|
||||
/*
|
||||
* When a tool is hovering the drawing canvas, we highlight the eventual
|
||||
* pixel target with this color:
|
||||
*/
|
||||
TOOL_TARGET_HIGHLIGHT_COLOR: 'rgba(255, 255, 255, 0.2)',
|
||||
|
||||
/*
|
||||
* Default entry point for piskel web service:
|
||||
*/
|
||||
PISKEL_SERVICE_URL: 'http://3.piskel-app.appspot.com',
|
||||
|
||||
GRID_STROKE_WIDTH: 1,
|
||||
GRID_STROKE_COLOR: "lightgray",
|
||||
|
||||
LEFT_BUTTON : "left_button_1",
|
||||
RIGHT_BUTTON : "right_button_2"
|
||||
};
|
64
js/Events.js
@ -1,64 +0,0 @@
|
||||
// TODO(grosbouddha): put under pskl namespace.
|
||||
Events = {
|
||||
|
||||
TOOL_SELECTED : "TOOL_SELECTED",
|
||||
TOOL_RELEASED : "TOOL_RELEASED",
|
||||
PRIMARY_COLOR_SELECTED: "PRIMARY_COLOR_SELECTED",
|
||||
PRIMARY_COLOR_UPDATED: "PRIMARY_COLOR_UPDATED",
|
||||
SECONDARY_COLOR_SELECTED: "SECONDARY_COLOR_SELECTED",
|
||||
SECONDARY_COLOR_UPDATED: "SECONDARY_COLOR_UPDATED",
|
||||
|
||||
/**
|
||||
* When this event is emitted, a request is sent to the localstorage
|
||||
* Service to save the current framesheet. The storage service
|
||||
* may not immediately store data (internal throttling of requests).
|
||||
*/
|
||||
LOCALSTORAGE_REQUEST: "LOCALSTORAGE_REQUEST",
|
||||
|
||||
CANVAS_RIGHT_CLICKED: "CANVAS_RIGHT_CLICKED",
|
||||
|
||||
/**
|
||||
* Event to request a refresh of the display.
|
||||
* A bit overkill but, it's just workaround in our current drawing system.
|
||||
* TODO: Remove or rework when redraw system is refactored.
|
||||
*/
|
||||
REFRESH: "REFRESH",
|
||||
|
||||
/**
|
||||
* Temporary event to bind the redraw of right preview film to the canvas.
|
||||
* This redraw should be driven by model updates.
|
||||
* TODO(vincz): Remove.
|
||||
*/
|
||||
REDRAW_PREVIEWFILM: "REDRAW_PREVIEWFILM",
|
||||
|
||||
/**
|
||||
* Fired each time a user setting change.
|
||||
* The payload will be:
|
||||
* 1st argument: Name of the settings
|
||||
* 2nd argument: New value
|
||||
*/
|
||||
USER_SETTINGS_CHANGED: "USER_SETTINGS_CHANGED",
|
||||
|
||||
/**
|
||||
* The framesheet was reseted and is now probably drastically different.
|
||||
* Number of frames, content of frames, color used for the palette may have changed.
|
||||
*/
|
||||
FRAMESHEET_RESET: "FRAMESHEET_RESET",
|
||||
|
||||
FRAME_SIZE_CHANGED : "FRAME_SIZE_CHANGED",
|
||||
|
||||
CURRENT_FRAME_SET: "CURRENT_FRAME_SET",
|
||||
|
||||
SELECTION_CREATED: "SELECTION_CREATED",
|
||||
SELECTION_MOVE_REQUEST: "SELECTION_MOVE_REQUEST",
|
||||
SELECTION_DISMISSED: "SELECTION_DISMISSED",
|
||||
|
||||
SHOW_NOTIFICATION: "SHOW_NOTIFICATION",
|
||||
HIDE_NOTIFICATION: "HIDE_NOTIFICATION",
|
||||
|
||||
UNDO: "UNDO",
|
||||
REDO: "REDO",
|
||||
CUT: "CUT",
|
||||
COPY: "COPY",
|
||||
PASTE: "PASTE"
|
||||
};
|
@ -1,67 +0,0 @@
|
||||
(function () {
|
||||
var ns = $.namespace("pskl.controller");
|
||||
ns.AnimatedPreviewController = function (framesheet, container, dpi) {
|
||||
this.framesheet = framesheet;
|
||||
this.container = container;
|
||||
|
||||
this.elapsedTime = 0;
|
||||
this.currentIndex = 0;
|
||||
|
||||
this.fps = parseInt($("#preview-fps")[0].value, 10);
|
||||
|
||||
var renderingOptions = {
|
||||
"dpi": this.calculateDPI_()
|
||||
};
|
||||
this.renderer = new pskl.rendering.FrameRenderer(this.container, renderingOptions);
|
||||
|
||||
$.subscribe(Events.FRAME_SIZE_CHANGED, this.updateDPI_.bind(this));
|
||||
};
|
||||
|
||||
ns.AnimatedPreviewController.prototype.init = function () {
|
||||
// the oninput event won't work on IE10 unfortunately, but at least will provide a
|
||||
// consistent behavior across all other browsers that support the input type range
|
||||
// see https://bugzilla.mozilla.org/show_bug.cgi?id=853670
|
||||
$("#preview-fps")[0].addEventListener('change', this.onFPSSliderChange.bind(this));
|
||||
};
|
||||
|
||||
ns.AnimatedPreviewController.prototype.onFPSSliderChange = function (evt) {
|
||||
this.setFPS(parseInt($("#preview-fps")[0].value, 10));
|
||||
};
|
||||
|
||||
ns.AnimatedPreviewController.prototype.setFPS = function (fps) {
|
||||
this.fps = fps;
|
||||
$("#preview-fps").val(this.fps);
|
||||
$("#display-fps").html(this.fps + " FPS");
|
||||
};
|
||||
|
||||
ns.AnimatedPreviewController.prototype.render = function (delta) {
|
||||
this.elapsedTime += delta;
|
||||
var index = Math.floor(this.elapsedTime / (1000/this.fps));
|
||||
if (index != this.currentIndex) {
|
||||
this.currentIndex = index;
|
||||
if (!this.framesheet.hasFrameAtIndex(this.currentIndex)) {
|
||||
this.currentIndex = 0;
|
||||
this.elapsedTime = 0;
|
||||
}
|
||||
this.renderer.render(this.framesheet.getFrameByIndex(this.currentIndex));
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Calculate the preview DPI depending on the framesheet size
|
||||
*/
|
||||
ns.AnimatedPreviewController.prototype.calculateDPI_ = function () {
|
||||
var previewSize = 200,
|
||||
framePixelHeight = this.framesheet.getCurrentFrame().getHeight(),
|
||||
framePixelWidth = this.framesheet.getCurrentFrame().getWidth();
|
||||
// TODO (julz) : should have a utility to get a Size from framesheet easily (what about empty framesheets though ?)
|
||||
|
||||
//return pskl.PixelUtils.calculateDPIForContainer($(".preview-container"), framePixelHeight, framePixelWidth);
|
||||
return pskl.PixelUtils.calculateDPI(previewSize, previewSize, framePixelHeight, framePixelWidth);
|
||||
};
|
||||
|
||||
ns.AnimatedPreviewController.prototype.updateDPI_ = function () {
|
||||
this.dpi = this.calculateDPI_();
|
||||
this.renderer.updateDPI(this.dpi);
|
||||
};
|
||||
})();
|
@ -1,313 +0,0 @@
|
||||
(function () {
|
||||
var ns = $.namespace("pskl.controller");
|
||||
ns.DrawingController = function (framesheet, container) {
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
this.framesheet = framesheet;
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
this.overlayFrame = pskl.model.Frame.createEmptyFromFrame(framesheet.getCurrentFrame());
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
this.container = container;
|
||||
|
||||
// TODO(vincz): Store user prefs in a localstorage string ?
|
||||
var renderingOptions = {
|
||||
"dpi": this.calculateDPI_(),
|
||||
"supportGridRendering" : true
|
||||
};
|
||||
|
||||
this.renderer = new pskl.rendering.FrameRenderer(this.container, renderingOptions, "drawing-canvas");
|
||||
this.overlayRenderer = new pskl.rendering.FrameRenderer(this.container, renderingOptions, "canvas-overlay");
|
||||
|
||||
this.renderer.init(framesheet.getCurrentFrame());
|
||||
this.overlayRenderer.init(this.overlayFrame);
|
||||
|
||||
// State of drawing controller:
|
||||
this.isClicked = false;
|
||||
this.isRightClicked = false;
|
||||
this.previousMousemoveTime = 0;
|
||||
this.currentToolBehavior = null;
|
||||
this.primaryColor = Constants.DEFAULT_PEN_COLOR;
|
||||
this.secondaryColor = Constants.TRANSPARENT_COLOR;
|
||||
|
||||
this.initMouseBehavior();
|
||||
|
||||
$.subscribe(Events.TOOL_SELECTED, $.proxy(function(evt, toolBehavior) {
|
||||
console.log("Tool selected: ", toolBehavior);
|
||||
this.currentToolBehavior = toolBehavior;
|
||||
}, this));
|
||||
|
||||
/**
|
||||
* TODO(grosbouddha): Primary/secondary color state are kept in this general controller.
|
||||
* Find a better place to store that. Perhaps PaletteController?
|
||||
*/
|
||||
$.subscribe(Events.PRIMARY_COLOR_SELECTED, $.proxy(function(evt, color) {
|
||||
console.log("Primary color selected: ", color);
|
||||
this.primaryColor = color;
|
||||
$.publish(Events.PRIMARY_COLOR_UPDATED, [color]);
|
||||
}, this));
|
||||
$.subscribe(Events.SECONDARY_COLOR_SELECTED, $.proxy(function(evt, color) {
|
||||
console.log("Secondary color selected: ", color);
|
||||
this.secondaryColor = color;
|
||||
$.publish(Events.SECONDARY_COLOR_UPDATED, [color]);
|
||||
}, this));
|
||||
|
||||
$(window).resize($.proxy(this.startDPIUpdateTimer_, this));
|
||||
|
||||
$.subscribe(Events.USER_SETTINGS_CHANGED, $.proxy(this.onUserSettingsChange_, this));
|
||||
$.subscribe(Events.FRAME_SIZE_CHANGED, $.proxy(this.updateDPI_, this));
|
||||
|
||||
this.updateDPI_();
|
||||
};
|
||||
|
||||
ns.DrawingController.prototype.initMouseBehavior = function() {
|
||||
var body = $('body');
|
||||
this.container.mousedown($.proxy(this.onMousedown_, this));
|
||||
this.container.mousemove($.proxy(this.onMousemove_, this));
|
||||
body.mouseup($.proxy(this.onMouseup_, this));
|
||||
|
||||
// Deactivate right click:
|
||||
body.contextmenu(this.onCanvasContextMenu_);
|
||||
};
|
||||
|
||||
|
||||
|
||||
ns.DrawingController.prototype.startDPIUpdateTimer_ = function () {
|
||||
if (this.dpiUpdateTimer) {
|
||||
window.clearInterval(this.dpiUpdateTimer);
|
||||
}
|
||||
this.dpiUpdateTimer = window.setTimeout($.proxy(this.updateDPI_, this), 200);
|
||||
},
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
ns.DrawingController.prototype.onUserSettingsChange_ = function (evt, settingsName, settingsValue) {
|
||||
if(settingsName == pskl.UserSettings.SHOW_GRID) {
|
||||
this.updateDPI_();
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
ns.DrawingController.prototype.onMousedown_ = function (event) {
|
||||
this.isClicked = true;
|
||||
|
||||
if(event.button == 2) { // right click
|
||||
this.isRightClicked = true;
|
||||
$.publish(Events.CANVAS_RIGHT_CLICKED);
|
||||
}
|
||||
|
||||
var coords = this.getSpriteCoordinates(event);
|
||||
|
||||
this.currentToolBehavior.applyToolAt(
|
||||
coords.col, coords.row,
|
||||
this.getCurrentColor_(),
|
||||
this.framesheet.getCurrentFrame(),
|
||||
this.overlayFrame,
|
||||
this.wrapEvtInfo_(event)
|
||||
);
|
||||
|
||||
$.publish(Events.LOCALSTORAGE_REQUEST);
|
||||
};
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
ns.DrawingController.prototype.onMousemove_ = function (event) {
|
||||
var currentTime = new Date().getTime();
|
||||
// Throttling of the mousemove event:
|
||||
if ((currentTime - this.previousMousemoveTime) > 40 ) {
|
||||
var coords = this.getSpriteCoordinates(event);
|
||||
if (this.isClicked) {
|
||||
|
||||
this.currentToolBehavior.moveToolAt(
|
||||
coords.col, coords.row,
|
||||
this.getCurrentColor_(),
|
||||
this.framesheet.getCurrentFrame(),
|
||||
this.overlayFrame,
|
||||
this.wrapEvtInfo_(event)
|
||||
);
|
||||
|
||||
// TODO(vincz): Find a way to move that to the model instead of being at the interaction level.
|
||||
// Eg when drawing, it may make sense to have it here. However for a non drawing tool,
|
||||
// you don't need to draw anything when mousemoving and you request useless localStorage.
|
||||
$.publish(Events.LOCALSTORAGE_REQUEST);
|
||||
} else {
|
||||
|
||||
this.currentToolBehavior.moveUnactiveToolAt(
|
||||
coords.col, coords.row,
|
||||
this.getCurrentColor_(),
|
||||
this.framesheet.getCurrentFrame(),
|
||||
this.overlayFrame,
|
||||
this.wrapEvtInfo_(event)
|
||||
);
|
||||
}
|
||||
this.previousMousemoveTime = currentTime;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
ns.DrawingController.prototype.onMouseup_ = function (event) {
|
||||
if(this.isClicked || this.isRightClicked) {
|
||||
// A mouse button was clicked on the drawing canvas before this mouseup event,
|
||||
// the user was probably drawing on the canvas.
|
||||
// Note: The mousemove movement (and the mouseup) may end up outside
|
||||
// of the drawing canvas.
|
||||
|
||||
this.isClicked = false;
|
||||
this.isRightClicked = false;
|
||||
|
||||
var coords = this.getSpriteCoordinates(event);
|
||||
//console.log("mousemove: col: " + spriteCoordinate.col + " - row: " + spriteCoordinate.row);
|
||||
this.currentToolBehavior.releaseToolAt(
|
||||
coords.col, coords.row,
|
||||
this.getCurrentColor_(),
|
||||
this.framesheet.getCurrentFrame(),
|
||||
this.overlayFrame,
|
||||
this.wrapEvtInfo_(event)
|
||||
);
|
||||
|
||||
$.publish(Events.TOOL_RELEASED);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
ns.DrawingController.prototype.wrapEvtInfo_ = function (event) {
|
||||
var evtInfo = {};
|
||||
if (event.button === 0) {
|
||||
evtInfo.button = Constants.LEFT_BUTTON;
|
||||
} else if (event.button == 2) {
|
||||
evtInfo.button = Constants.RIGHT_BUTTON;
|
||||
}
|
||||
return evtInfo;
|
||||
},
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
ns.DrawingController.prototype.getRelativeCoordinates = function (clientX, clientY) {
|
||||
var canvasPageOffset = this.container.offset();
|
||||
return {
|
||||
x : clientX - canvasPageOffset.left,
|
||||
y : clientY - canvasPageOffset.top
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
ns.DrawingController.prototype.getSpriteCoordinates = function(event) {
|
||||
var coords = this.getRelativeCoordinates(event.clientX, event.clientY);
|
||||
return this.renderer.convertPixelCoordinatesIntoSpriteCoordinate(coords);
|
||||
};
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
ns.DrawingController.prototype.getCurrentColor_ = function () {
|
||||
if(this.isRightClicked) {
|
||||
return this.secondaryColor;
|
||||
} else {
|
||||
return this.primaryColor;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
ns.DrawingController.prototype.onCanvasContextMenu_ = function (event) {
|
||||
if ($(event.target).closest('#drawing-canvas-container').length) {
|
||||
// Deactivate right click on drawing canvas only.
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
event.cancelBubble = true;
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
ns.DrawingController.prototype.render = function () {
|
||||
this.renderFrame();
|
||||
this.renderOverlay();
|
||||
};
|
||||
|
||||
ns.DrawingController.prototype.renderFrame = function () {
|
||||
var frame = this.framesheet.getCurrentFrame();
|
||||
var serializedFrame = frame.serialize();
|
||||
if (this.serializedFrame != serializedFrame) {
|
||||
if (!frame.isSameSize(this.overlayFrame)) {
|
||||
this.overlayFrame = pskl.model.Frame.createEmptyFromFrame(frame);
|
||||
}
|
||||
this.serializedFrame = serializedFrame;
|
||||
this.renderer.render(frame);
|
||||
}
|
||||
};
|
||||
|
||||
ns.DrawingController.prototype.renderOverlay = function () {
|
||||
var serializedOverlay = this.overlayFrame.serialize();
|
||||
if (this.serializedOverlay != serializedOverlay) {
|
||||
this.serializedOverlay = serializedOverlay;
|
||||
this.overlayRenderer.render(this.overlayFrame);
|
||||
}
|
||||
};
|
||||
|
||||
ns.DrawingController.prototype.forceRendering_ = function () {
|
||||
this.serializedFrame = this.serializedOverlay = null;
|
||||
};
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
ns.DrawingController.prototype.calculateDPI_ = function() {
|
||||
var availableViewportHeight = $('#main-wrapper').height(),
|
||||
leftSectionWidth = $('.left-column').outerWidth(true),
|
||||
rightSectionWidth = $('.right-column').outerWidth(true),
|
||||
availableViewportWidth = $('#main-wrapper').width() - leftSectionWidth - rightSectionWidth,
|
||||
framePixelHeight = this.framesheet.getCurrentFrame().getHeight(),
|
||||
framePixelWidth = this.framesheet.getCurrentFrame().getWidth();
|
||||
|
||||
if (pskl.UserSettings.get(pskl.UserSettings.SHOW_GRID)) {
|
||||
availableViewportWidth = availableViewportWidth - (framePixelWidth * Constants.GRID_STROKE_WIDTH);
|
||||
availableViewportHeight = availableViewportHeight - (framePixelHeight * Constants.GRID_STROKE_WIDTH);
|
||||
}
|
||||
|
||||
var dpi = pskl.PixelUtils.calculateDPI(
|
||||
availableViewportHeight, availableViewportWidth, framePixelHeight, framePixelWidth);
|
||||
|
||||
return dpi;
|
||||
};
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
ns.DrawingController.prototype.updateDPI_ = function() {
|
||||
var dpi = this.calculateDPI_();
|
||||
this.renderer.updateDPI(dpi);
|
||||
this.overlayRenderer.updateDPI(dpi);
|
||||
|
||||
var currentFrameHeight = this.framesheet.getCurrentFrame().getHeight();
|
||||
var canvasHeight = currentFrameHeight * dpi;
|
||||
if (pskl.UserSettings.get(pskl.UserSettings.SHOW_GRID)) {
|
||||
canvasHeight += Constants.GRID_STROKE_WIDTH * currentFrameHeight;
|
||||
}
|
||||
|
||||
var verticalGapInPixel = Math.floor(($('#main-wrapper').height() - canvasHeight) / 2);
|
||||
$('#column-wrapper').css({
|
||||
'top': verticalGapInPixel + 'px',
|
||||
'height': canvasHeight + 'px'
|
||||
});
|
||||
|
||||
this.forceRendering_();
|
||||
};
|
||||
})();
|
@ -1,116 +0,0 @@
|
||||
(function () {
|
||||
var ns = $.namespace("pskl.controller");
|
||||
|
||||
ns.PaletteController = function () {
|
||||
this.paletteRoot = null;
|
||||
this.paletteColors = [];
|
||||
};
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
ns.PaletteController.prototype.onPickerChange_ = function(evt, isPrimary) {
|
||||
var inputPicker = $(evt.target);
|
||||
if(evt.data.isPrimary) {
|
||||
$.publish(Events.PRIMARY_COLOR_SELECTED, [inputPicker.val()]);
|
||||
}
|
||||
else {
|
||||
$.publish(Events.SECONDARY_COLOR_SELECTED, [inputPicker.val()]);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
ns.PaletteController.prototype.addColorToPalette_ = function (color) {
|
||||
if (this.paletteColors.indexOf(color) == -1 && color != Constants.TRANSPARENT_COLOR) {
|
||||
this.paletteColors.push(color);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
ns.PaletteController.prototype.addColorsToPalette_ = function (colors) {
|
||||
for(var color in colors) {
|
||||
this.addColorToPalette_(color);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
ns.PaletteController.prototype.onPaletteColorClick_ = function (event) {
|
||||
var selectedColor = $(event.target).data("color");
|
||||
var isLeftClick = (event.which == 1);
|
||||
var isRightClick = (event.which == 3);
|
||||
if (isLeftClick) {
|
||||
$.publish(Events.PRIMARY_COLOR_SELECTED, [selectedColor]);
|
||||
} else if (isRightClick) {
|
||||
$.publish(Events.SECONDARY_COLOR_SELECTED, [selectedColor]);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
ns.PaletteController.prototype.updateColorPicker_ = function (color, colorPicker) {
|
||||
if (color == Constants.TRANSPARENT_COLOR) {
|
||||
// We can set the current palette color to transparent.
|
||||
// You can then combine this transparent color with an advanced
|
||||
// tool for customized deletions.
|
||||
// Eg: bucket + transparent: Delete a colored area
|
||||
// Stroke + transparent: hollow out the equivalent of a stroke
|
||||
|
||||
// The colorpicker can't be set to a transparent state.
|
||||
// We set its background to white and insert the
|
||||
// string "TRANSPARENT" to mimic this state:
|
||||
colorPicker[0].color.fromString("#fff");
|
||||
colorPicker.val(Constants.TRANSPARENT_COLOR);
|
||||
} else {
|
||||
colorPicker[0].color.fromString(color);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
ns.PaletteController.prototype.init = function(framesheet) {
|
||||
|
||||
this.paletteRoot = $("#palette");
|
||||
this.framesheet = framesheet;
|
||||
|
||||
// Initialize palette:
|
||||
this.addColorsToPalette_(this.framesheet.getUsedColors());
|
||||
|
||||
$.subscribe(Events.FRAMESHEET_RESET, $.proxy(function(evt) {
|
||||
this.addColorsToPalette_(this.framesheet.getUsedColors());
|
||||
}, this));
|
||||
|
||||
this.paletteRoot.mouseup($.proxy(this.onPaletteColorClick_, this));
|
||||
|
||||
$.subscribe(Events.PRIMARY_COLOR_UPDATED, $.proxy(function(evt, color) {
|
||||
this.updateColorPicker_(color, $('#color-picker'));
|
||||
this.addColorToPalette_(color);
|
||||
}, this));
|
||||
|
||||
$.subscribe(Events.SECONDARY_COLOR_UPDATED, $.proxy(function(evt, color) {
|
||||
this.updateColorPicker_(color, $('#secondary-color-picker'));
|
||||
this.addColorToPalette_(color);
|
||||
}, this));
|
||||
|
||||
// Initialize colorpickers:
|
||||
var colorPicker = $('#color-picker');
|
||||
colorPicker.val(Constants.DEFAULT_PEN_COLOR);
|
||||
colorPicker.change({isPrimary : true}, $.proxy(this.onPickerChange_, this));
|
||||
|
||||
|
||||
var secondaryColorPicker = $('#secondary-color-picker');
|
||||
secondaryColorPicker.val(Constants.TRANSPARENT_COLOR);
|
||||
secondaryColorPicker.change({isPrimary : false}, $.proxy(this.onPickerChange_, this));
|
||||
|
||||
};
|
||||
})();
|
||||
|
||||
|
||||
|
@ -1,217 +0,0 @@
|
||||
(function () {
|
||||
var ns = $.namespace("pskl.controller");
|
||||
ns.PreviewFilmController = function (framesheet, container, dpi) {
|
||||
|
||||
this.framesheet = framesheet;
|
||||
this.container = container;
|
||||
this.dpi = this.calculateDPI_();
|
||||
|
||||
this.redrawFlag = true;
|
||||
|
||||
$.subscribe(Events.TOOL_RELEASED, this.flagForRedraw_.bind(this));
|
||||
$.subscribe(Events.FRAMESHEET_RESET, this.flagForRedraw_.bind(this));
|
||||
$.subscribe(Events.FRAMESHEET_RESET, this.refreshDPI_.bind(this));
|
||||
|
||||
$('#preview-list-scroller').scroll(this.updateScrollerOverflows.bind(this));
|
||||
this.updateScrollerOverflows();
|
||||
};
|
||||
|
||||
ns.PreviewFilmController.prototype.init = function() {};
|
||||
|
||||
ns.PreviewFilmController.prototype.addFrame = function () {
|
||||
this.framesheet.addEmptyFrame();
|
||||
this.framesheet.setCurrentFrameIndex(this.framesheet.getFrameCount() - 1);
|
||||
this.updateScrollerOverflows();
|
||||
};
|
||||
|
||||
ns.PreviewFilmController.prototype.flagForRedraw_ = function () {
|
||||
this.redrawFlag = true;
|
||||
};
|
||||
|
||||
ns.PreviewFilmController.prototype.refreshDPI_ = function () {
|
||||
this.dpi = this.calculateDPI_();
|
||||
};
|
||||
|
||||
ns.PreviewFilmController.prototype.render = function () {
|
||||
if (this.redrawFlag) {
|
||||
// TODO(vincz): Full redraw on any drawing modification, optimize.
|
||||
this.createPreviews_();
|
||||
this.redrawFlag = false;
|
||||
}
|
||||
};
|
||||
|
||||
ns.PreviewFilmController.prototype.updateScrollerOverflows = function () {
|
||||
var scroller = $('#preview-list-scroller');
|
||||
var scrollerHeight = scroller.height();
|
||||
var scrollTop = scroller.scrollTop();
|
||||
var scrollerContentHeight = $('#preview-list').height();
|
||||
var treshold = $('.top-overflow').height();
|
||||
var overflowTop = false,
|
||||
overflowBottom = false;
|
||||
if (scrollerHeight < scrollerContentHeight) {
|
||||
if (scrollTop > treshold) {
|
||||
overflowTop = true;
|
||||
}
|
||||
var scrollBottom = (scrollerContentHeight - scrollTop) - scrollerHeight;
|
||||
if (scrollBottom > treshold) {
|
||||
overflowBottom = true;
|
||||
}
|
||||
}
|
||||
var wrapper = $('#preview-list-wrapper');
|
||||
wrapper.toggleClass('top-overflow-visible', overflowTop);
|
||||
wrapper.toggleClass('bottom-overflow-visible', overflowBottom);
|
||||
};
|
||||
|
||||
ns.PreviewFilmController.prototype.createPreviews_ = function () {
|
||||
|
||||
this.container.html("");
|
||||
// Manually remove tooltips since mouseout events were shortcut by the DOM refresh:
|
||||
$(".tooltip").remove();
|
||||
|
||||
var frameCount = this.framesheet.getFrameCount();
|
||||
|
||||
for (var i = 0, l = frameCount; i < l ; i++) {
|
||||
this.container.append(this.createPreviewTile_(i));
|
||||
}
|
||||
// Append 'new empty frame' button
|
||||
var newFrameButton = document.createElement("div");
|
||||
newFrameButton.id = "add-frame-action";
|
||||
newFrameButton.className = "add-frame-action";
|
||||
newFrameButton.innerHTML = "<p class='label'>Add new frame</p>";
|
||||
this.container.append(newFrameButton);
|
||||
|
||||
$(newFrameButton).click(this.addFrame.bind(this));
|
||||
|
||||
var needDragndropBehavior = (frameCount > 1);
|
||||
if(needDragndropBehavior) {
|
||||
this.initDragndropBehavior_();
|
||||
}
|
||||
this.updateScrollerOverflows();
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
ns.PreviewFilmController.prototype.initDragndropBehavior_ = function () {
|
||||
|
||||
$("#preview-list").sortable({
|
||||
placeholder: "preview-tile-drop-proxy",
|
||||
update: $.proxy(this.onUpdate_, this),
|
||||
items: ".preview-tile"
|
||||
});
|
||||
$("#preview-list").disableSelection();
|
||||
};
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
ns.PreviewFilmController.prototype.onUpdate_ = function( event, ui ) {
|
||||
var originFrameId = parseInt(ui.item.data("tile-number"), 10);
|
||||
var targetInsertionId = $('.preview-tile').index(ui.item);
|
||||
|
||||
this.framesheet.moveFrame(originFrameId, targetInsertionId);
|
||||
this.framesheet.setCurrentFrameIndex(targetInsertionId);
|
||||
|
||||
// TODO(grosbouddha): move localstorage request to the model layer?
|
||||
$.publish(Events.LOCALSTORAGE_REQUEST);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @private
|
||||
* TODO(vincz): clean this giant rendering function & remove listeners.
|
||||
*/
|
||||
ns.PreviewFilmController.prototype.createPreviewTile_ = function(tileNumber) {
|
||||
var currentFrame = this.framesheet.getFrameByIndex(tileNumber);
|
||||
|
||||
var previewTileRoot = document.createElement("li");
|
||||
var classname = "preview-tile";
|
||||
previewTileRoot.setAttribute("data-tile-number", tileNumber);
|
||||
|
||||
if (this.framesheet.getCurrentFrame() == currentFrame) {
|
||||
classname += " selected";
|
||||
}
|
||||
previewTileRoot.className = classname;
|
||||
|
||||
var canvasContainer = document.createElement("div");
|
||||
canvasContainer.className = "canvas-container";
|
||||
|
||||
var canvasBackground = document.createElement("div");
|
||||
canvasBackground.className = "canvas-background";
|
||||
canvasContainer.appendChild(canvasBackground);
|
||||
|
||||
previewTileRoot.addEventListener('click', this.onPreviewClick_.bind(this, tileNumber));
|
||||
|
||||
var cloneFrameButton = document.createElement("button");
|
||||
cloneFrameButton.setAttribute('rel', 'tooltip');
|
||||
cloneFrameButton.setAttribute('data-placement', 'right');
|
||||
cloneFrameButton.setAttribute('title', 'Duplicate this frame');
|
||||
cloneFrameButton.className = "tile-overlay duplicate-frame-action";
|
||||
previewTileRoot.appendChild(cloneFrameButton);
|
||||
cloneFrameButton.addEventListener('click', this.onAddButtonClick_.bind(this, tileNumber));
|
||||
|
||||
// TODO(vincz): Eventually optimize this part by not recreating a FrameRenderer. Note that the real optim
|
||||
// is to make this update function (#createPreviewTile) less aggressive.
|
||||
var renderingOptions = {"dpi": this.dpi };
|
||||
var currentFrameRenderer = new pskl.rendering.FrameRenderer($(canvasContainer), renderingOptions, "tile-view");
|
||||
currentFrameRenderer.init(currentFrame);
|
||||
|
||||
previewTileRoot.appendChild(canvasContainer);
|
||||
|
||||
if(tileNumber > 0 || this.framesheet.getFrameCount() > 1) {
|
||||
// Add 'remove frame' button.
|
||||
var deleteButton = document.createElement("button");
|
||||
deleteButton.setAttribute('rel', 'tooltip');
|
||||
deleteButton.setAttribute('data-placement', 'right');
|
||||
deleteButton.setAttribute('title', 'Delete this frame');
|
||||
deleteButton.className = "tile-overlay delete-frame-action";
|
||||
deleteButton.addEventListener('click', this.onDeleteButtonClick_.bind(this, tileNumber));
|
||||
previewTileRoot.appendChild(deleteButton);
|
||||
|
||||
// Add 'dragndrop handle'.
|
||||
var dndHandle = document.createElement("div");
|
||||
dndHandle.className = "tile-overlay dnd-action";
|
||||
previewTileRoot.appendChild(dndHandle);
|
||||
}
|
||||
var tileCount = document.createElement("div");
|
||||
tileCount.className = "tile-overlay tile-count";
|
||||
tileCount.innerHTML = tileNumber;
|
||||
previewTileRoot.appendChild(tileCount);
|
||||
|
||||
|
||||
return previewTileRoot;
|
||||
};
|
||||
|
||||
ns.PreviewFilmController.prototype.onPreviewClick_ = function (index, evt) {
|
||||
// has not class tile-action:
|
||||
if(!evt.target.classList.contains('tile-overlay')) {
|
||||
this.framesheet.setCurrentFrameIndex(index);
|
||||
}
|
||||
};
|
||||
|
||||
ns.PreviewFilmController.prototype.onDeleteButtonClick_ = function (index, evt) {
|
||||
this.framesheet.removeFrameByIndex(index);
|
||||
$.publish(Events.LOCALSTORAGE_REQUEST); // Should come from model
|
||||
this.updateScrollerOverflows();
|
||||
};
|
||||
|
||||
ns.PreviewFilmController.prototype.onAddButtonClick_ = function (index, evt) {
|
||||
this.framesheet.duplicateFrameByIndex(index);
|
||||
$.publish(Events.LOCALSTORAGE_REQUEST); // Should come from model
|
||||
this.framesheet.setCurrentFrameIndex(index + 1);
|
||||
this.updateScrollerOverflows();
|
||||
};
|
||||
|
||||
/**
|
||||
* Calculate the preview DPI depending on the framesheet size
|
||||
*/
|
||||
ns.PreviewFilmController.prototype.calculateDPI_ = function () {
|
||||
var previewSize = 120,
|
||||
framePixelHeight = this.framesheet.getCurrentFrame().getHeight(),
|
||||
framePixelWidth = this.framesheet.getCurrentFrame().getWidth();
|
||||
// TODO (julz) : should have a utility to get a Size from framesheet easily (what about empty framesheets though ?)
|
||||
|
||||
return pskl.PixelUtils.calculateDPI(previewSize, previewSize, framePixelHeight, framePixelWidth);
|
||||
};
|
||||
})();
|
@ -1,45 +0,0 @@
|
||||
(function () {
|
||||
var ns = $.namespace("pskl.controller");
|
||||
|
||||
ns.SettingsController = function () {};
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
ns.SettingsController.prototype.init = function() {
|
||||
|
||||
// Highlight selected background picker:
|
||||
var backgroundClass = pskl.UserSettings.get(pskl.UserSettings.CANVAS_BACKGROUND);
|
||||
$('#background-picker-wrapper')
|
||||
.find('.background-picker[data-background-class=' + backgroundClass + ']')
|
||||
.addClass('selected');
|
||||
|
||||
// Initial state for grid display:
|
||||
var show_grid = pskl.UserSettings.get(pskl.UserSettings.SHOW_GRID);
|
||||
$('#show-grid').prop('checked', show_grid);
|
||||
|
||||
// Expand drawer when clicking 'Settings' tab.
|
||||
$('#settings').click(function(evt) {
|
||||
$('.right-sticky-section').toggleClass('expanded');
|
||||
$('#settings').toggleClass('has-expanded-drawer');
|
||||
});
|
||||
|
||||
// Handle grid display changes:
|
||||
$('#show-grid').change($.proxy(function(evt) {
|
||||
var checked = $('#show-grid').prop('checked');
|
||||
pskl.UserSettings.set(pskl.UserSettings.SHOW_GRID, checked);
|
||||
}, this));
|
||||
|
||||
// Handle canvas background changes:
|
||||
$('#background-picker-wrapper').click(function(evt) {
|
||||
var target = $(evt.target).closest('.background-picker');
|
||||
if (target.length) {
|
||||
var backgroundClass = target.data('background-class');
|
||||
pskl.UserSettings.set(pskl.UserSettings.CANVAS_BACKGROUND, backgroundClass);
|
||||
|
||||
$('.background-picker').removeClass('selected');
|
||||
target.addClass('selected');
|
||||
}
|
||||
});
|
||||
};
|
||||
})();
|
@ -1,99 +0,0 @@
|
||||
(function () {
|
||||
var ns = $.namespace("pskl.controller");
|
||||
|
||||
|
||||
ns.ToolController = function () {
|
||||
|
||||
this.toolInstances = {
|
||||
"simplePen" : new pskl.drawingtools.SimplePen(),
|
||||
"verticalMirrorPen" : new pskl.drawingtools.VerticalMirrorPen(),
|
||||
"eraser" : new pskl.drawingtools.Eraser(),
|
||||
"paintBucket" : new pskl.drawingtools.PaintBucket(),
|
||||
"stroke" : new pskl.drawingtools.Stroke(),
|
||||
"rectangle" : new pskl.drawingtools.Rectangle(),
|
||||
"circle" : new pskl.drawingtools.Circle(),
|
||||
"move" : new pskl.drawingtools.Move(),
|
||||
"rectangleSelect" : new pskl.drawingtools.RectangleSelect(),
|
||||
"shapeSelect" : new pskl.drawingtools.ShapeSelect(),
|
||||
"colorPicker" : new pskl.drawingtools.ColorPicker()
|
||||
};
|
||||
|
||||
this.currentSelectedTool = this.toolInstances.simplePen;
|
||||
this.previousSelectedTool = this.toolInstances.simplePen;
|
||||
};
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
ns.ToolController.prototype.activateToolOnStage_ = function(tool) {
|
||||
var stage = $("body");
|
||||
var previousSelectedToolClass = stage.data("selected-tool-class");
|
||||
if(previousSelectedToolClass) {
|
||||
stage.removeClass(previousSelectedToolClass);
|
||||
}
|
||||
stage.addClass(tool.toolId);
|
||||
stage.data("selected-tool-class", tool.toolId);
|
||||
};
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
ns.ToolController.prototype.selectTool_ = function(tool) {
|
||||
console.log("Selecting Tool:" , this.currentSelectedTool);
|
||||
this.currentSelectedTool = tool;
|
||||
this.activateToolOnStage_(this.currentSelectedTool);
|
||||
$.publish(Events.TOOL_SELECTED, [tool]);
|
||||
};
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
ns.ToolController.prototype.onToolIconClicked_ = function(evt) {
|
||||
var target = $(evt.target);
|
||||
var clickedTool = target.closest(".tool-icon");
|
||||
|
||||
if(clickedTool.length) {
|
||||
for(var tool in this.toolInstances) {
|
||||
if (this.toolInstances[tool].toolId == clickedTool.data().toolId) {
|
||||
this.selectTool_(this.toolInstances[tool]);
|
||||
|
||||
// Show tool as selected:
|
||||
$('#tool-section .tool-icon.selected').removeClass('selected');
|
||||
clickedTool.addClass('selected');
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
ns.ToolController.prototype.createToolMarkup_ = function() {
|
||||
var currentTool, toolMarkup = '', extraClass;
|
||||
// TODO(vincz): Tools rendering order is not enforced by the data stucture (this.toolInstances), fix that.
|
||||
for (var toolKey in this.toolInstances) {
|
||||
currentTool = this.toolInstances[toolKey];
|
||||
extraClass = currentTool.toolId;
|
||||
if (this.currentSelectedTool == currentTool) {
|
||||
extraClass = extraClass + " selected";
|
||||
}
|
||||
toolMarkup += '<li rel="tooltip" data-placement="right" class="tool-icon ' + extraClass + '" data-tool-id="' + currentTool.toolId +
|
||||
'" title="' + currentTool.helpText + '"></li>';
|
||||
}
|
||||
$('#tools-container').html(toolMarkup);
|
||||
};
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
ns.ToolController.prototype.init = function() {
|
||||
|
||||
this.createToolMarkup_();
|
||||
|
||||
// Initialize tool:
|
||||
// Set SimplePen as default selected tool:
|
||||
this.selectTool_(this.toolInstances.simplePen);
|
||||
// Activate listener on tool panel:
|
||||
$("#tool-section").click($.proxy(this.onToolIconClicked_, this));
|
||||
};
|
||||
})();
|
@ -1,72 +0,0 @@
|
||||
/*
|
||||
* @provide pskl.drawingtools.BaseTool
|
||||
*
|
||||
* @require pskl.utils
|
||||
*/
|
||||
(function() {
|
||||
var ns = $.namespace("pskl.drawingtools");
|
||||
|
||||
ns.BaseTool = function() {};
|
||||
|
||||
ns.BaseTool.prototype.applyToolAt = function(col, row, color, frame, overlay) {};
|
||||
|
||||
ns.BaseTool.prototype.moveToolAt = function(col, row, color, frame, overlay) {};
|
||||
|
||||
ns.BaseTool.prototype.moveUnactiveToolAt = function(col, row, color, frame, overlay) {
|
||||
if (overlay.containsPixel(col, row)) {
|
||||
if (!isNaN(this.highlightedPixelCol) &&
|
||||
!isNaN(this.highlightedPixelRow) &&
|
||||
(this.highlightedPixelRow != row ||
|
||||
this.highlightedPixelCol != col)) {
|
||||
|
||||
// Clean the previously highlighted pixel:
|
||||
overlay.clear();
|
||||
}
|
||||
|
||||
// Show the current pixel targeted by the tool:
|
||||
overlay.setPixel(col, row, Constants.TOOL_TARGET_HIGHLIGHT_COLOR);
|
||||
|
||||
this.highlightedPixelCol = col;
|
||||
this.highlightedPixelRow = row;
|
||||
}
|
||||
};
|
||||
|
||||
ns.BaseTool.prototype.releaseToolAt = function(col, row, color, frame, overlay) {};
|
||||
|
||||
/**
|
||||
* Bresenham line algorihtm: Get an array of pixels from
|
||||
* start and end coordinates.
|
||||
*
|
||||
* http://en.wikipedia.org/wiki/Bresenham's_line_algorithm
|
||||
* http://stackoverflow.com/questions/4672279/bresenham-algorithm-in-javascript
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
ns.BaseTool.prototype.getLinePixels_ = function(x0, x1, y0, y1) {
|
||||
|
||||
var pixels = [];
|
||||
var dx = Math.abs(x1-x0);
|
||||
var dy = Math.abs(y1-y0);
|
||||
var sx = (x0 < x1) ? 1 : -1;
|
||||
var sy = (y0 < y1) ? 1 : -1;
|
||||
var err = dx-dy;
|
||||
|
||||
while(true){
|
||||
|
||||
// Do what you need to for this
|
||||
pixels.push({"col": x0, "row": y0});
|
||||
|
||||
if ((x0==x1) && (y0==y1)) break;
|
||||
var e2 = 2*err;
|
||||
if (e2>-dy){
|
||||
err -= dy;
|
||||
x0 += sx;
|
||||
}
|
||||
if (e2 < dx) {
|
||||
err += dx;
|
||||
y0 += sy;
|
||||
}
|
||||
}
|
||||
return pixels;
|
||||
};
|
||||
})();
|
@ -1,85 +0,0 @@
|
||||
/*
|
||||
* @provide pskl.drawingtools.Circle
|
||||
*
|
||||
* @require pskl.utils
|
||||
*/
|
||||
(function() {
|
||||
var ns = $.namespace("pskl.drawingtools");
|
||||
|
||||
ns.Circle = function() {
|
||||
this.toolId = "tool-circle";
|
||||
this.helpText = "Circle tool";
|
||||
|
||||
// Circle's first point coordinates (set in applyToolAt)
|
||||
this.startCol = null;
|
||||
this.startRow = null;
|
||||
};
|
||||
|
||||
pskl.utils.inherit(ns.Circle, ns.BaseTool);
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
ns.Circle.prototype.applyToolAt = function(col, row, color, frame, overlay) {
|
||||
this.startCol = col;
|
||||
this.startRow = row;
|
||||
|
||||
// Drawing the first point of the rectangle in the fake overlay canvas:
|
||||
overlay.setPixel(col, row, color);
|
||||
};
|
||||
|
||||
ns.Circle.prototype.moveToolAt = function(col, row, color, frame, overlay) {
|
||||
overlay.clear();
|
||||
if(color == Constants.TRANSPARENT_COLOR) {
|
||||
color = Constants.SELECTION_TRANSPARENT_COLOR;
|
||||
}
|
||||
|
||||
// draw in overlay
|
||||
this.drawCircle_(col, row, color, overlay);
|
||||
};
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
ns.Circle.prototype.releaseToolAt = function(col, row, color, frame, overlay) {
|
||||
overlay.clear();
|
||||
if(frame.containsPixel(col, row)) { // cancel if outside of canvas
|
||||
// draw in frame to finalize
|
||||
this.drawCircle_(col, row, color, frame);
|
||||
}
|
||||
};
|
||||
|
||||
ns.Circle.prototype.drawCircle_ = function (col, row, color, targetFrame) {
|
||||
var circlePoints = this.getCirclePixels_(this.startCol, this.startRow, col, row);
|
||||
for(var i = 0; i< circlePoints.length; i++) {
|
||||
// Change model:
|
||||
targetFrame.setPixel(circlePoints[i].col, circlePoints[i].row, color);
|
||||
}
|
||||
};
|
||||
|
||||
ns.Circle.prototype.getCirclePixels_ = function (x0, y0, x1, y1) {
|
||||
var coords = pskl.PixelUtils.getOrderedRectangleCoordinates(x0, y0, x1, y1);
|
||||
var xC = (coords.x0 + coords.x1)/2;
|
||||
var yC = (coords.y0 + coords.y1)/2;
|
||||
|
||||
var rX = coords.x1 - xC;
|
||||
var rY = coords.y1 - yC;
|
||||
|
||||
var pixels = [];
|
||||
var x, y, angle;
|
||||
for (x = coords.x0 ; x < coords.x1 ; x++) {
|
||||
angle = Math.acos((x - xC)/rX);
|
||||
y = Math.round(rY * Math.sin(angle) + yC);
|
||||
pixels.push({"col": x, "row": y});
|
||||
pixels.push({"col": 2*xC - x, "row": 2*yC - y});
|
||||
}
|
||||
|
||||
for (y = coords.y0 ; y < coords.y1 ; y++) {
|
||||
angle = Math.asin((y - yC)/rY);
|
||||
x = Math.round(rX * Math.cos(angle) + xC);
|
||||
pixels.push({"col": x, "row": y});
|
||||
pixels.push({"col": 2*xC - x, "row": 2*yC - y});
|
||||
}
|
||||
return pixels;
|
||||
};
|
||||
})();
|
@ -1,29 +0,0 @@
|
||||
/*
|
||||
* @provide pskl.drawingtools.ColorPicker
|
||||
*
|
||||
* @require pskl.utils
|
||||
*/
|
||||
(function() {
|
||||
var ns = $.namespace("pskl.drawingtools");
|
||||
|
||||
ns.ColorPicker = function() {
|
||||
this.toolId = "tool-colorpicker";
|
||||
this.helpText = "Color picker";
|
||||
};
|
||||
|
||||
pskl.utils.inherit(ns.ColorPicker, ns.BaseTool);
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
ns.ColorPicker.prototype.applyToolAt = function(col, row, color, frame, overlay, context) {
|
||||
if (frame.containsPixel(col, row)) {
|
||||
var sampledColor = frame.getPixel(col, row);
|
||||
if (context.button == Constants.LEFT_BUTTON) {
|
||||
$.publish(Events.PRIMARY_COLOR_SELECTED, [sampledColor]);
|
||||
} else if (context.button == Constants.RIGHT_BUTTON) {
|
||||
$.publish(Events.SECONDARY_COLOR_SELECTED, [sampledColor]);
|
||||
}
|
||||
}
|
||||
};
|
||||
})();
|
@ -1,23 +0,0 @@
|
||||
/*
|
||||
* @provide pskl.drawingtools.Eraser
|
||||
*
|
||||
* @require Constants
|
||||
* @require pskl.utils
|
||||
*/
|
||||
(function() {
|
||||
var ns = $.namespace("pskl.drawingtools");
|
||||
|
||||
ns.Eraser = function() {
|
||||
this.toolId = "tool-eraser";
|
||||
this.helpText = "Eraser tool";
|
||||
};
|
||||
|
||||
pskl.utils.inherit(ns.Eraser, ns.SimplePen);
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
ns.Eraser.prototype.applyToolAt = function(col, row, color, frame, overlay) {
|
||||
this.superclass.applyToolAt.call(this, col, row, Constants.TRANSPARENT_COLOR, frame, overlay);
|
||||
};
|
||||
})();
|
@ -1,54 +0,0 @@
|
||||
/*
|
||||
* @provide pskl.drawingtools.Move
|
||||
*
|
||||
* @require pskl.utils
|
||||
*/
|
||||
(function() {
|
||||
var ns = $.namespace("pskl.drawingtools");
|
||||
|
||||
ns.Move = function() {
|
||||
this.toolId = "tool-move";
|
||||
this.helpText = "Move tool";
|
||||
|
||||
// Stroke's first point coordinates (set in applyToolAt)
|
||||
this.startCol = null;
|
||||
this.startRow = null;
|
||||
};
|
||||
|
||||
pskl.utils.inherit(ns.Move, ns.BaseTool);
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
ns.Move.prototype.applyToolAt = function(col, row, color, frame, overlay) {
|
||||
this.startCol = col;
|
||||
this.startRow = row;
|
||||
this.frameClone = frame.clone();
|
||||
};
|
||||
|
||||
ns.Move.prototype.moveToolAt = function(col, row, color, frame, overlay) {
|
||||
var colDiff = col - this.startCol, rowDiff = row - this.startRow;
|
||||
this.shiftFrame(colDiff, rowDiff, frame, this.frameClone);
|
||||
};
|
||||
|
||||
ns.Move.prototype.shiftFrame = function (colDiff, rowDiff, frame, reference) {
|
||||
var color;
|
||||
for (var col = 0 ; col < frame.getWidth() ; col++) {
|
||||
for (var row = 0 ; row < frame.getHeight() ; row++) {
|
||||
if (reference.containsPixel(col - colDiff, row - rowDiff)) {
|
||||
color = reference.getPixel(col - colDiff, row - rowDiff);
|
||||
} else {
|
||||
color = Constants.TRANSPARENT_COLOR;
|
||||
}
|
||||
frame.setPixel(col, row, color);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
ns.Move.prototype.releaseToolAt = function(col, row, color, frame, overlay) {
|
||||
this.moveToolAt(col, row, color, frame, overlay);
|
||||
};
|
||||
})();
|
@ -1,36 +0,0 @@
|
||||
/*
|
||||
* @provide pskl.drawingtools.PaintBucket
|
||||
*
|
||||
* @require pskl.utils
|
||||
*/
|
||||
(function() {
|
||||
var ns = $.namespace("pskl.drawingtools");
|
||||
|
||||
ns.PaintBucket = function() {
|
||||
this.toolId = "tool-paint-bucket";
|
||||
this.helpText = "Paint bucket tool";
|
||||
};
|
||||
|
||||
pskl.utils.inherit(ns.PaintBucket, ns.BaseTool);
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
ns.PaintBucket.prototype.applyToolAt = function(col, row, color, frame, overlay) {
|
||||
|
||||
pskl.PixelUtils.paintSimilarConnectedPixelsFromFrame(frame, col, row, color);
|
||||
};
|
||||
})();
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -1,59 +0,0 @@
|
||||
/*
|
||||
* @provide pskl.drawingtools.Rectangle
|
||||
*
|
||||
* @require pskl.utils
|
||||
*/
|
||||
(function() {
|
||||
var ns = $.namespace("pskl.drawingtools");
|
||||
|
||||
ns.Rectangle = function() {
|
||||
this.toolId = "tool-rectangle";
|
||||
this.helpText = "Rectangle tool";
|
||||
|
||||
// Rectangle's first point coordinates (set in applyToolAt)
|
||||
this.startCol = null;
|
||||
this.startRow = null;
|
||||
};
|
||||
|
||||
pskl.utils.inherit(ns.Rectangle, ns.BaseTool);
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
ns.Rectangle.prototype.applyToolAt = function(col, row, color, frame, overlay) {
|
||||
this.startCol = col;
|
||||
this.startRow = row;
|
||||
|
||||
// Drawing the first point of the rectangle in the fake overlay canvas:
|
||||
overlay.setPixel(col, row, color);
|
||||
};
|
||||
|
||||
ns.Rectangle.prototype.moveToolAt = function(col, row, color, frame, overlay) {
|
||||
overlay.clear();
|
||||
if(color == Constants.TRANSPARENT_COLOR) {
|
||||
color = Constants.SELECTION_TRANSPARENT_COLOR;
|
||||
}
|
||||
|
||||
// draw in overlay
|
||||
this.drawRectangle_(col, row, color, overlay);
|
||||
};
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
ns.Rectangle.prototype.releaseToolAt = function(col, row, color, frame, overlay) {
|
||||
overlay.clear();
|
||||
if(frame.containsPixel(col, row)) { // cancel if outside of canvas
|
||||
// draw in frame to finalize
|
||||
this.drawRectangle_(col, row, color, frame);
|
||||
}
|
||||
};
|
||||
|
||||
ns.Rectangle.prototype.drawRectangle_ = function (col, row, color, targetFrame) {
|
||||
var strokePoints = pskl.PixelUtils.getBoundRectanglePixels(this.startCol, this.startRow, col, row);
|
||||
for(var i = 0; i< strokePoints.length; i++) {
|
||||
// Change model:
|
||||
targetFrame.setPixel(strokePoints[i].col, strokePoints[i].row, color);
|
||||
}
|
||||
};
|
||||
})();
|
@ -1,49 +0,0 @@
|
||||
/*
|
||||
* @provide pskl.drawingtools.SimplePen
|
||||
*
|
||||
* @require pskl.utils
|
||||
*/
|
||||
(function() {
|
||||
var ns = $.namespace("pskl.drawingtools");
|
||||
|
||||
ns.SimplePen = function() {
|
||||
this.toolId = "tool-pen";
|
||||
this.helpText = "Pen tool";
|
||||
|
||||
this.previousCol = null;
|
||||
this.previousRow = null;
|
||||
|
||||
};
|
||||
|
||||
pskl.utils.inherit(ns.SimplePen, ns.BaseTool);
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
ns.SimplePen.prototype.applyToolAt = function(col, row, color, frame, overlay) {
|
||||
if (frame.containsPixel(col, row)) {
|
||||
frame.setPixel(col, row, color);
|
||||
}
|
||||
this.previousCol = col;
|
||||
this.previousRow = row;
|
||||
};
|
||||
|
||||
ns.SimplePen.prototype.moveToolAt = function(col, row, color, frame, overlay) {
|
||||
if((Math.abs(col - this.previousCol) > 1) || (Math.abs(row - this.previousRow) > 1)) {
|
||||
// The pen movement is too fast for the mousemove frequency, there is a gap between the
|
||||
// current point and the previously drawn one.
|
||||
// We fill the gap by calculating missing dots (simple linear interpolation) and draw them.
|
||||
var interpolatedPixels = this.getLinePixels_(col, this.previousCol, row, this.previousRow);
|
||||
for(var i=0, l=interpolatedPixels.length; i<l; i++) {
|
||||
var coords = interpolatedPixels[i];
|
||||
this.applyToolAt(coords.col, coords.row, color, frame, overlay);
|
||||
}
|
||||
}
|
||||
else {
|
||||
this.applyToolAt(col, row, color, frame, overlay);
|
||||
}
|
||||
|
||||
this.previousCol = col;
|
||||
this.previousRow = row;
|
||||
};
|
||||
})();
|
@ -1,80 +0,0 @@
|
||||
/*
|
||||
* @provide pskl.drawingtools.Stroke
|
||||
*
|
||||
* @require pskl.utils
|
||||
*/
|
||||
(function() {
|
||||
var ns = $.namespace("pskl.drawingtools");
|
||||
|
||||
ns.Stroke = function() {
|
||||
this.toolId = "tool-stroke";
|
||||
this.helpText = "Stroke tool";
|
||||
|
||||
// Stroke's first point coordinates (set in applyToolAt)
|
||||
this.startCol = null;
|
||||
this.startRow = null;
|
||||
};
|
||||
|
||||
pskl.utils.inherit(ns.Stroke, ns.BaseTool);
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
ns.Stroke.prototype.applyToolAt = function(col, row, color, frame, overlay) {
|
||||
this.startCol = col;
|
||||
this.startRow = row;
|
||||
|
||||
// When drawing a stroke we don't change the model instantly, since the
|
||||
// user can move his cursor to change the stroke direction and length
|
||||
// dynamically. Instead we draw the (preview) stroke in a fake canvas that
|
||||
// overlay the drawing canvas.
|
||||
// We wait for the releaseToolAt callback to impact both the
|
||||
// frame model and canvas rendering.
|
||||
|
||||
// The fake canvas where we will draw the preview of the stroke:
|
||||
// Drawing the first point of the stroke in the fake overlay canvas:
|
||||
overlay.setPixel(col, row, color);
|
||||
};
|
||||
|
||||
ns.Stroke.prototype.moveToolAt = function(col, row, color, frame, overlay) {
|
||||
overlay.clear();
|
||||
|
||||
// When the user moussemove (before releasing), we dynamically compute the
|
||||
// pixel to draw the line and draw this line in the overlay canvas:
|
||||
var strokePoints = this.getLinePixels_(this.startCol, col, this.startRow, row);
|
||||
|
||||
// Drawing current stroke:
|
||||
for(var i = 0; i< strokePoints.length; i++) {
|
||||
|
||||
if(color == Constants.TRANSPARENT_COLOR) {
|
||||
// When mousemoving the stroke tool, we draw in the canvas overlay above the drawing canvas.
|
||||
// If the stroke color is transparent, we won't be
|
||||
// able to see it during the movement.
|
||||
// We set it to a semi-opaque white during the tool mousemove allowing to see colors below the stroke.
|
||||
// When the stroke tool will be released, It will draw a transparent stroke,
|
||||
// eg deleting the equivalent of a stroke.
|
||||
color = Constants.SELECTION_TRANSPARENT_COLOR;
|
||||
}
|
||||
overlay.setPixel(strokePoints[i].col, strokePoints[i].row, color);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
ns.Stroke.prototype.releaseToolAt = function(col, row, color, frame, overlay) {
|
||||
// If the stroke tool is released outside of the canvas, we cancel the stroke:
|
||||
// TODO: Mutualize this check in common method
|
||||
if(frame.containsPixel(col, row)) {
|
||||
// The user released the tool to draw a line. We will compute the pixel coordinate, impact
|
||||
// the model and draw them in the drawing canvas (not the fake overlay anymore)
|
||||
var strokePoints = this.getLinePixels_(this.startCol, col, this.startRow, row);
|
||||
for(var i = 0; i< strokePoints.length; i++) {
|
||||
// Change model:
|
||||
frame.setPixel(strokePoints[i].col, strokePoints[i].row, color);
|
||||
}
|
||||
}
|
||||
// For now, we are done with the stroke tool and don't need an overlay anymore:
|
||||
overlay.clear();
|
||||
};
|
||||
})();
|
@ -1,46 +0,0 @@
|
||||
(function() {
|
||||
var ns = $.namespace("pskl.drawingtools");
|
||||
|
||||
ns.VerticalMirrorPen = function() {
|
||||
this.toolId = "tool-vertical-mirror-pen";
|
||||
this.helpText = "vertical mirror pen tool";
|
||||
|
||||
this.swap = null;
|
||||
this.mirroredPreviousCol = null;
|
||||
this.mirroredPreviousRow = null;
|
||||
};
|
||||
|
||||
pskl.utils.inherit(ns.VerticalMirrorPen, ns.SimplePen);
|
||||
|
||||
|
||||
ns.VerticalMirrorPen.prototype.setMirrorContext = function() {
|
||||
this.swap = this.previousCol;
|
||||
this.previousCol = this.mirroredPreviousCol;
|
||||
};
|
||||
|
||||
ns.VerticalMirrorPen.prototype.unsetMirrorContext = function() {
|
||||
this.mirroredPreviousCol = this.previousCol;
|
||||
this.previousCol = this.swap;
|
||||
};
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
ns.VerticalMirrorPen.prototype.applyToolAt = function(col, row, color, frame, overlay) {
|
||||
this.superclass.applyToolAt.call(this, col, row, color, frame, overlay);
|
||||
|
||||
var mirroredCol = this.getSymmetricCol_(col, frame);
|
||||
this.mirroredPreviousCol = mirroredCol;
|
||||
|
||||
this.setMirrorContext();
|
||||
this.superclass.applyToolAt.call(this, mirroredCol, row, color, frame, overlay);
|
||||
this.unsetMirrorContext();
|
||||
};
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
ns.VerticalMirrorPen.prototype.getSymmetricCol_ = function(col, frame) {
|
||||
return frame.getWidth() - col - 1;
|
||||
};
|
||||
})();
|
@ -1,150 +0,0 @@
|
||||
/*
|
||||
* @provide pskl.drawingtools.BaseSelect
|
||||
*
|
||||
* @require pskl.utils
|
||||
*/
|
||||
(function() {
|
||||
var ns = $.namespace("pskl.drawingtools");
|
||||
|
||||
ns.BaseSelect = function() {
|
||||
this.secondaryToolId = "tool-move";
|
||||
this.BodyRoot = $('body');
|
||||
|
||||
// Select's first point coordinates (set in applyToolAt)
|
||||
this.startCol = null;
|
||||
this.startRow = null;
|
||||
};
|
||||
|
||||
pskl.utils.inherit(ns.BaseSelect, ns.BaseTool);
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
ns.BaseSelect.prototype.applyToolAt = function(col, row, color, frame, overlay) {
|
||||
this.startCol = col;
|
||||
this.startRow = row;
|
||||
|
||||
this.lastCol = col;
|
||||
this.lastRow = row;
|
||||
|
||||
// The select tool can be in two different state.
|
||||
// If the inital click of the tool is not on a selection, we go in "select"
|
||||
// mode to create a selection.
|
||||
// If the initial click is on a previous selection, we go in "moveSelection"
|
||||
// mode to allow to move the selection by drag'n dropping it.
|
||||
if(overlay.getPixel(col, row) != Constants.SELECTION_TRANSPARENT_COLOR) {
|
||||
|
||||
this.mode = "select";
|
||||
this.onSelectStart_(col, row, color, frame, overlay);
|
||||
}
|
||||
else {
|
||||
|
||||
this.mode = "moveSelection";
|
||||
this.onSelectionDragStart_(col, row, color, frame, overlay);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
ns.BaseSelect.prototype.moveToolAt = function(col, row, color, frame, overlay) {
|
||||
if(this.mode == "select") {
|
||||
|
||||
this.onSelect_(col, row, color, frame, overlay);
|
||||
}
|
||||
else if(this.mode == "moveSelection") {
|
||||
|
||||
this.onSelectionDrag_(col, row, color, frame, overlay);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
ns.BaseSelect.prototype.releaseToolAt = function(col, row, color, frame, overlay) {
|
||||
if(this.mode == "select") {
|
||||
|
||||
this.onSelectEnd_(col, row, color, frame, overlay);
|
||||
} else if(this.mode == "moveSelection") {
|
||||
|
||||
this.onSelectionDragEnd_(col, row, color, frame, overlay);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* If we mouseover the selection draw inside the overlay frame, show the 'move' cursor
|
||||
* instead of the 'select' one. It indicates that we can move the selection by dragndroping it.
|
||||
* @override
|
||||
*/
|
||||
ns.BaseSelect.prototype.moveUnactiveToolAt = function(col, row, color, frame, overlay) {
|
||||
|
||||
if(overlay.getPixel(col, row) != Constants.SELECTION_TRANSPARENT_COLOR) {
|
||||
// We're hovering the selection, show the move tool:
|
||||
this.BodyRoot.addClass(this.toolId);
|
||||
this.BodyRoot.removeClass(this.secondaryToolId);
|
||||
} else {
|
||||
// We're not hovering the selection, show create selection tool:
|
||||
this.BodyRoot.addClass(this.secondaryToolId);
|
||||
this.BodyRoot.removeClass(this.toolId);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Move the overlay frame filled with semi-transparent pixels that represent the selection.
|
||||
* @private
|
||||
*/
|
||||
ns.BaseSelect.prototype.shiftOverlayFrame_ = function (colDiff, rowDiff, overlayFrame, reference) {
|
||||
var color;
|
||||
for (var col = 0 ; col < overlayFrame.getWidth() ; col++) {
|
||||
for (var row = 0 ; row < overlayFrame.getHeight() ; row++) {
|
||||
if (reference.containsPixel(col - colDiff, row - rowDiff)) {
|
||||
color = reference.getPixel(col - colDiff, row - rowDiff);
|
||||
} else {
|
||||
color = Constants.TRANSPARENT_COLOR;
|
||||
}
|
||||
overlayFrame.setPixel(col, row, color);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// The list of callbacks to implement by specialized tools to implement the selection creation behavior.
|
||||
/** @protected */
|
||||
ns.BaseSelect.prototype.onSelectStart_ = function (col, row, color, frame, overlay) {};
|
||||
/** @protected */
|
||||
ns.BaseSelect.prototype.onSelect_ = function (col, row, color, frame, overlay) {};
|
||||
/** @protected */
|
||||
ns.BaseSelect.prototype.onSelectEnd_ = function (col, row, color, frame, overlay) {};
|
||||
|
||||
|
||||
// The list of callbacks that define the drag'n drop behavior of the selection.
|
||||
/** @private */
|
||||
ns.BaseSelect.prototype.onSelectionDragStart_ = function (col, row, color, frame, overlay) {
|
||||
// Since we will move the overlayFrame in which the current selection is rendered,
|
||||
// we clone it to have a reference for the later shifting process.
|
||||
this.overlayFrameReference = overlay.clone();
|
||||
};
|
||||
/** @private */
|
||||
ns.BaseSelect.prototype.onSelectionDrag_ = function (col, row, color, frame, overlay) {
|
||||
var deltaCol = col - this.lastCol;
|
||||
var deltaRow = row - this.lastRow;
|
||||
|
||||
var colDiff = col - this.startCol, rowDiff = row - this.startRow;
|
||||
|
||||
// Shifting selection on overlay frame:
|
||||
this.shiftOverlayFrame_(colDiff, rowDiff, overlay, this.overlayFrameReference);
|
||||
|
||||
// Update selection model:
|
||||
$.publish(Events.SELECTION_MOVE_REQUEST, [deltaCol, deltaRow]);
|
||||
|
||||
this.lastCol = col;
|
||||
this.lastRow = row;
|
||||
};
|
||||
/** @private */
|
||||
ns.BaseSelect.prototype.onSelectionDragEnd_ = function (col, row, color, frame, overlay) {
|
||||
this.onSelectionDrag_(col, row, color, frame, overlay);
|
||||
};
|
||||
|
||||
|
||||
})();
|
@ -1,51 +0,0 @@
|
||||
/*
|
||||
* @provide pskl.drawingtools.RectangleSelect
|
||||
*
|
||||
* @require pskl.utils
|
||||
*/
|
||||
(function() {
|
||||
var ns = $.namespace("pskl.drawingtools");
|
||||
|
||||
ns.RectangleSelect = function() {
|
||||
this.toolId = "tool-rectangle-select";
|
||||
this.helpText = "Rectangle selection tool";
|
||||
|
||||
ns.BaseSelect.call(this);
|
||||
};
|
||||
|
||||
pskl.utils.inherit(ns.RectangleSelect, ns.BaseSelect);
|
||||
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
ns.RectangleSelect.prototype.onSelectStart_ = function (col, row, color, frame, overlay) {
|
||||
// Drawing the first point of the rectangle in the fake overlay canvas:
|
||||
overlay.setPixel(col, row, color);
|
||||
};
|
||||
|
||||
/**
|
||||
* When creating the rectangle selection, we clear the current overlayFrame and
|
||||
* redraw the current rectangle based on the orgin coordinate and
|
||||
* the current mouse coordiinate in sprite.
|
||||
* @override
|
||||
*/
|
||||
ns.RectangleSelect.prototype.onSelect_ = function (col, row, color, frame, overlay) {
|
||||
overlay.clear();
|
||||
if(this.startCol == col &&this.startRow == row) {
|
||||
$.publish(Events.SELECTION_DISMISSED);
|
||||
} else {
|
||||
var selection = new pskl.selection.RectangularSelection(
|
||||
this.startCol, this.startRow, col, row);
|
||||
$.publish(Events.SELECTION_CREATED, [selection]);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
ns.RectangleSelect.prototype.onSelectEnd_ = function (col, row, color, frame, overlay) {
|
||||
this.onSelect_(col, row, color, frame, overlay);
|
||||
};
|
||||
|
||||
})();
|
@ -1,34 +0,0 @@
|
||||
/*
|
||||
* @provide pskl.drawingtools.ShapeSelect
|
||||
*
|
||||
* @require pskl.utils
|
||||
*/
|
||||
(function() {
|
||||
var ns = $.namespace("pskl.drawingtools");
|
||||
|
||||
ns.ShapeSelect = function() {
|
||||
this.toolId = "tool-shape-select";
|
||||
this.helpText = "Shape selection tool";
|
||||
|
||||
ns.BaseSelect.call(this);
|
||||
};
|
||||
|
||||
pskl.utils.inherit(ns.ShapeSelect, ns.BaseSelect);
|
||||
|
||||
|
||||
/**
|
||||
* For the shape select tool, you just need to click one time to create a selection.
|
||||
* So we jsut need to implement onSelectStart_ (no need for onSelect_ & onSelectEnd_)
|
||||
* @override
|
||||
*/
|
||||
ns.ShapeSelect.prototype.onSelectStart_ = function (col, row, color, frame, overlay) {
|
||||
// Clean previous selection:
|
||||
$.publish(Events.SELECTION_DISMISSED);
|
||||
|
||||
// From the pixel cliked, get shape using an algorithm similar to the paintbucket one:
|
||||
var pixels = pskl.PixelUtils.getSimilarConnectedPixelsFromFrame(frame, col, row);
|
||||
var selection = new pskl.selection.ShapeSelection(pixels);
|
||||
$.publish(Events.SELECTION_CREATED, [selection]);
|
||||
};
|
||||
|
||||
})();
|
@ -1,571 +0,0 @@
|
||||
/**
|
||||
* This class lets you encode animated GIF files
|
||||
* Base class : http://www.java2s.com/Code/Java/2D-Graphics-GUI/AnimatedGifEncoder.htm
|
||||
* @author Kevin Weiner (original Java version - kweiner@fmsware.com)
|
||||
* @author Thibault Imbert (AS3 version - bytearray.org)
|
||||
* @version 0.1 AS3 implementation
|
||||
*/
|
||||
|
||||
|
||||
//import flash.utils.ByteArray;
|
||||
//import flash.display.BitmapData;
|
||||
//import flash.display.Bitmap;
|
||||
//import org.bytearray.gif.encoder.NeuQuant
|
||||
//import flash.net.URLRequestHeader;
|
||||
//import flash.net.URLRequestMethod;
|
||||
//import flash.net.URLRequest;
|
||||
//import flash.net.navigateToURL;
|
||||
|
||||
GIFEncoder = function()
|
||||
{
|
||||
for(var i = 0, chr = {}; i < 256; i++)
|
||||
chr[i] = String.fromCharCode(i);
|
||||
|
||||
function ByteArray(){
|
||||
this.bin = [];
|
||||
}
|
||||
|
||||
ByteArray.prototype.getData = function(){
|
||||
|
||||
for(var v = '', l = this.bin.length, i = 0; i < l; i++)
|
||||
v += chr[this.bin[i]];
|
||||
return v;
|
||||
}
|
||||
ByteArray.prototype.writeByte = function(val){
|
||||
this.bin.push(val);
|
||||
}
|
||||
ByteArray.prototype.writeUTFBytes = function(string){
|
||||
for(var l = string.length, i = 0; i < l; i++)
|
||||
this.writeByte(string.charCodeAt(i));
|
||||
}
|
||||
ByteArray.prototype.writeBytes = function(array, offset, length){
|
||||
for(var l = length || array.length, i = offset||0; i < l; i++)
|
||||
this.writeByte(array[i]);
|
||||
}
|
||||
|
||||
var exports = {};
|
||||
/*private*/ var width/*int*/ // image size
|
||||
/*private*/ var height/*int*/;
|
||||
/*private*/ var transparent/***/ = null; // transparent color if given
|
||||
/*private*/ var transIndex/*int*/; // transparent index in color table
|
||||
/*private*/ var repeat/*int*/ = -1; // no repeat
|
||||
/*private*/ var delay/*int*/ = 0; // frame delay (hundredths)
|
||||
/*private*/ var started/*Boolean*/ = false; // ready to output frames
|
||||
/*private*/ var out/*ByteArray*/;
|
||||
/*private*/ var image/*Bitmap*/; // current frame
|
||||
/*private*/ var pixels/*ByteArray*/; // BGR byte array from frame
|
||||
/*private*/ var indexedPixels/*ByteArray*/ // converted frame indexed to palette
|
||||
/*private*/ var colorDepth/*int*/; // number of bit planes
|
||||
/*private*/ var colorTab/*ByteArray*/; // RGB palette
|
||||
/*private*/ var usedEntry/*Array*/ = new Array; // active palette entries
|
||||
/*private*/ var palSize/*int*/ = 7; // color table size (bits-1)
|
||||
/*private*/ var dispose/*int*/ = -1; // disposal code (-1 = use default)
|
||||
/*private*/ var closeStream/*Boolean*/ = false; // close stream when finished
|
||||
/*private*/ var firstFrame/*Boolean*/ = true;
|
||||
/*private*/ var sizeSet/*Boolean*/ = false; // if false, get size from first frame
|
||||
/*private*/ var sample/*int*/ = 10; // default sample interval for quantizer
|
||||
|
||||
/**
|
||||
* Sets the delay time between each frame, or changes it for subsequent frames
|
||||
* (applies to last frame added)
|
||||
* int delay time in milliseconds
|
||||
* @param ms
|
||||
*/
|
||||
|
||||
var setDelay = exports.setDelay = function setDelay(ms/*int*/)/*void*/
|
||||
{
|
||||
|
||||
delay = Math.round(ms / 10);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the GIF frame disposal code for the last added frame and any
|
||||
*
|
||||
* subsequent frames. Default is 0 if no transparent color has been set,
|
||||
* otherwise 2.
|
||||
* @param code
|
||||
* int disposal code.
|
||||
*/
|
||||
|
||||
var setDispose = exports.setDispose = function setDispose(code/*int*/)/*void*/
|
||||
{
|
||||
|
||||
if (code >= 0) dispose = code;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the number of times the set of GIF frames should be played. Default is
|
||||
* 1; 0 means play indefinitely. Must be invoked before the first image is
|
||||
* added.
|
||||
*
|
||||
* @param iter
|
||||
* int number of iterations.
|
||||
* @return
|
||||
*/
|
||||
|
||||
var setRepeat = exports.setRepeat = function setRepeat(iter/*int*/)/*void*/
|
||||
{
|
||||
|
||||
if (iter >= 0) repeat = iter;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the transparent color for the last added frame and any subsequent
|
||||
* frames. Since all colors are subject to modification in the quantization
|
||||
* process, the color in the final palette for each frame closest to the given
|
||||
* color becomes the transparent color for that frame. May be set to null to
|
||||
* indicate no transparent color.
|
||||
* @param
|
||||
* Color to be treated as transparent on display.
|
||||
*/
|
||||
|
||||
var setTransparent = exports.setTransparent = function setTransparent(c/*Number*/)/*void*/
|
||||
{
|
||||
|
||||
transparent = c;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* The addFrame method takes an incoming BitmapData object to create each frames
|
||||
* @param
|
||||
* BitmapData object to be treated as a GIF's frame
|
||||
*/
|
||||
|
||||
var addFrame = exports.addFrame = function addFrame(im/*BitmapData*/, is_imageData)/*Boolean*/
|
||||
{
|
||||
|
||||
if ((im == null) || !started || out == null)
|
||||
{
|
||||
throw new Error ("Please call start method before calling addFrame");
|
||||
return false;
|
||||
}
|
||||
|
||||
var ok/*Boolean*/ = true;
|
||||
|
||||
try {
|
||||
if(!is_imageData){
|
||||
image = im.getImageData(0,0, im.canvas.width, im.canvas.height).data;
|
||||
if (!sizeSet) setSize(im.canvas.width, im.canvas.height);
|
||||
}else{
|
||||
image = im;
|
||||
}
|
||||
getImagePixels(); // convert to correct format if necessary
|
||||
analyzePixels(); // build color table & map pixels
|
||||
|
||||
if (firstFrame)
|
||||
{
|
||||
writeLSD(); // logical screen descriptior
|
||||
writePalette(); // global color table
|
||||
if (repeat >= 0)
|
||||
{
|
||||
// use NS app extension to indicate reps
|
||||
writeNetscapeExt();
|
||||
}
|
||||
}
|
||||
|
||||
writeGraphicCtrlExt(); // write graphic control extension
|
||||
writeImageDesc(); // image descriptor
|
||||
if (!firstFrame) writePalette(); // local color table
|
||||
writePixels(); // encode and write pixel data
|
||||
firstFrame = false;
|
||||
} catch (e/*Error*/) {
|
||||
ok = false;
|
||||
}
|
||||
|
||||
return ok;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds final trailer to the GIF stream, if you don't call the finish method
|
||||
* the GIF stream will not be valid.
|
||||
*/
|
||||
|
||||
var finish = exports.finish = function finish()/*Boolean*/
|
||||
{
|
||||
if (!started) return false;
|
||||
var ok/*Boolean*/ = true;
|
||||
started = false;
|
||||
try {
|
||||
out.writeByte(0x3b); // gif trailer
|
||||
} catch (e/*Error*/) {
|
||||
ok = false;
|
||||
}
|
||||
|
||||
return ok;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets some members so that a new stream can be started.
|
||||
* This method is actually called by the start method
|
||||
*/
|
||||
|
||||
var reset = function reset ( )/*void*/
|
||||
{
|
||||
|
||||
// reset for subsequent use
|
||||
transIndex = 0;
|
||||
image = null;
|
||||
pixels = null;
|
||||
indexedPixels = null;
|
||||
colorTab = null;
|
||||
closeStream = false;
|
||||
firstFrame = true;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* * Sets frame rate in frames per second. Equivalent to
|
||||
* <code>setDelay(1000/fps)</code>.
|
||||
* @param fps
|
||||
* float frame rate (frames per second)
|
||||
*/
|
||||
|
||||
var setFrameRate = exports.setFrameRate = function setFrameRate(fps/*Number*/)/*void*/
|
||||
{
|
||||
|
||||
if (fps != 0xf) delay = Math.round(100/fps);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets quality of color quantization (conversion of images to the maximum 256
|
||||
* colors allowed by the GIF specification). Lower values (minimum = 1)
|
||||
* produce better colors, but slow processing significantly. 10 is the
|
||||
* default, and produces good color mapping at reasonable speeds. Values
|
||||
* greater than 20 do not yield significant improvements in speed.
|
||||
* @param quality
|
||||
* int greater than 0.
|
||||
* @return
|
||||
*/
|
||||
|
||||
var setQuality = exports.setQuality = function setQuality(quality/*int*/)/*void*/
|
||||
{
|
||||
|
||||
if (quality < 1) quality = 1;
|
||||
sample = quality;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the GIF frame size. The default size is the size of the first frame
|
||||
* added if this method is not invoked.
|
||||
* @param w
|
||||
* int frame width.
|
||||
* @param h
|
||||
* int frame width.
|
||||
*/
|
||||
|
||||
var setSize = exports.setSize = function setSize(w/*int*/, h/*int*/)/*void*/
|
||||
{
|
||||
|
||||
if (started && !firstFrame) return;
|
||||
width = w;
|
||||
height = h;
|
||||
if (width < 1)width = 320;
|
||||
if (height < 1)height = 240;
|
||||
sizeSet = true
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Initiates GIF file creation on the given stream.
|
||||
* @param os
|
||||
* OutputStream on which GIF images are written.
|
||||
* @return false if initial write failed.
|
||||
*
|
||||
*/
|
||||
|
||||
var start = exports.start = function start()/*Boolean*/
|
||||
{
|
||||
|
||||
reset();
|
||||
var ok/*Boolean*/ = true;
|
||||
closeStream = false;
|
||||
out = new ByteArray;
|
||||
try {
|
||||
out.writeUTFBytes("GIF89a"); // header
|
||||
} catch (e/*Error*/) {
|
||||
ok = false;
|
||||
}
|
||||
|
||||
return started = ok;
|
||||
|
||||
}
|
||||
|
||||
var cont = exports.cont = function cont()/*Boolean*/
|
||||
{
|
||||
|
||||
reset();
|
||||
var ok/*Boolean*/ = true;
|
||||
closeStream = false;
|
||||
out = new ByteArray;
|
||||
|
||||
return started = ok;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Analyzes image colors and creates color map.
|
||||
*/
|
||||
|
||||
var analyzePixels = function analyzePixels()/*void*/
|
||||
{
|
||||
|
||||
var len/*int*/ = pixels.length;
|
||||
var nPix/*int*/ = len / 3;
|
||||
indexedPixels = [];
|
||||
var nq/*NeuQuant*/ = new NeuQuant(pixels, len, sample);
|
||||
// initialize quantizer
|
||||
colorTab = nq.process(); // create reduced palette
|
||||
// map image pixels to new palette
|
||||
var k/*int*/ = 0;
|
||||
for (var j/*int*/ = 0; j < nPix; j++) {
|
||||
var index/*int*/ = nq.map(pixels[k++] & 0xff, pixels[k++] & 0xff, pixels[k++] & 0xff);
|
||||
usedEntry[index] = true;
|
||||
indexedPixels[j] = index;
|
||||
}
|
||||
pixels = null;
|
||||
colorDepth = 8;
|
||||
palSize = 7;
|
||||
// get closest match to transparent color if specified
|
||||
if (transparent != null) {
|
||||
transIndex = findClosest(transparent);
|
||||
|
||||
var r = colorTab[transIndex*3];
|
||||
var g = colorTab[transIndex*3+1];
|
||||
var b = colorTab[transIndex*3+2];
|
||||
var trans_indices = [];
|
||||
for (var i=0; i<colorTab.length; i+=3)
|
||||
{
|
||||
var index = i / 3;
|
||||
if (!usedEntry[index]) continue;
|
||||
if (colorTab[i] == r && colorTab[i+1] == g && colorTab[i+2] == b)
|
||||
trans_indices.push(index);
|
||||
}
|
||||
for (var i=0; i<indexedPixels.length; i++) {
|
||||
if (trans_indices.indexOf(indexedPixels[i]) >= 0) {
|
||||
indexedPixels[i] = transIndex;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns index of palette color closest to c
|
||||
*
|
||||
*/
|
||||
|
||||
var findClosest = function findClosest(c/*Number*/)/*int*/
|
||||
{
|
||||
|
||||
if (colorTab == null) return -1;
|
||||
var r/*int*/ = (c & 0xFF0000) >> 16;
|
||||
var g/*int*/ = (c & 0x00FF00) >> 8;
|
||||
var b/*int*/ = (c & 0x0000FF);
|
||||
var minpos/*int*/ = 0;
|
||||
var dmin/*int*/ = 256 * 256 * 256;
|
||||
var len/*int*/ = colorTab.length;
|
||||
|
||||
for (var i/*int*/ = 0; i < len;) {
|
||||
var dr/*int*/ = r - (colorTab[i++] & 0xff);
|
||||
var dg/*int*/ = g - (colorTab[i++] & 0xff);
|
||||
var db/*int*/ = b - (colorTab[i] & 0xff);
|
||||
var d/*int*/ = dr * dr + dg * dg + db * db;
|
||||
var index/*int*/ = i / 3;
|
||||
if (usedEntry[index] && (d < dmin)) {
|
||||
dmin = d;
|
||||
minpos = index;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
return minpos;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts image pixels into byte array "pixels
|
||||
*/
|
||||
|
||||
var getImagePixels = function getImagePixels()/*void*/
|
||||
{
|
||||
|
||||
var w/*int*/ = width;
|
||||
var h/*int*/ = height;
|
||||
pixels = [];
|
||||
var data = image;
|
||||
var count/*int*/ = 0;
|
||||
|
||||
for ( var i/*int*/ = 0; i < h; i++ )
|
||||
{
|
||||
|
||||
for (var j/*int*/ = 0; j < w; j++ )
|
||||
{
|
||||
|
||||
var b = (i*w*4)+j*4;
|
||||
pixels[count++] = data[b];
|
||||
pixels[count++] = data[b+1];
|
||||
pixels[count++] = data[b+2];
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes Graphic Control Extension
|
||||
*/
|
||||
|
||||
var writeGraphicCtrlExt = function writeGraphicCtrlExt()/*void*/
|
||||
{
|
||||
out.writeByte(0x21); // extension introducer
|
||||
out.writeByte(0xf9); // GCE label
|
||||
out.writeByte(4); // data block size
|
||||
var transp/*int*/
|
||||
var disp/*int*/;
|
||||
if (transparent == null) {
|
||||
transp = 0;
|
||||
disp = 0; // dispose = no action
|
||||
} else {
|
||||
transp = 1;
|
||||
disp = 2; // force clear if using transparent color
|
||||
}
|
||||
if (dispose >= 0) {
|
||||
disp = dispose & 7; // user override
|
||||
}
|
||||
disp <<= 2;
|
||||
// packed fields
|
||||
out.writeByte(0 | // 1:3 reserved
|
||||
disp | // 4:6 disposal
|
||||
0 | // 7 user input - 0 = none
|
||||
transp); // 8 transparency flag
|
||||
|
||||
WriteShort(delay); // delay x 1/100 sec
|
||||
out.writeByte(transIndex); // transparent color index
|
||||
out.writeByte(0); // block terminator
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes Image Descriptor
|
||||
*/
|
||||
|
||||
var writeImageDesc = function writeImageDesc()/*void*/
|
||||
{
|
||||
|
||||
out.writeByte(0x2c); // image separator
|
||||
WriteShort(0); // image position x,y = 0,0
|
||||
WriteShort(0);
|
||||
WriteShort(width); // image size
|
||||
WriteShort(height);
|
||||
|
||||
// packed fields
|
||||
if (firstFrame) {
|
||||
// no LCT - GCT is used for first (or only) frame
|
||||
out.writeByte(0);
|
||||
} else {
|
||||
// specify normal LCT
|
||||
out.writeByte(0x80 | // 1 local color table 1=yes
|
||||
0 | // 2 interlace - 0=no
|
||||
0 | // 3 sorted - 0=no
|
||||
0 | // 4-5 reserved
|
||||
palSize); // 6-8 size of color table
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes Logical Screen Descriptor
|
||||
*/
|
||||
|
||||
var writeLSD = function writeLSD()/*void*/
|
||||
{
|
||||
|
||||
// logical screen size
|
||||
WriteShort(width);
|
||||
WriteShort(height);
|
||||
// packed fields
|
||||
out.writeByte((0x80 | // 1 : global color table flag = 1 (gct used)
|
||||
0x70 | // 2-4 : color resolution = 7
|
||||
0x00 | // 5 : gct sort flag = 0
|
||||
palSize)); // 6-8 : gct size
|
||||
|
||||
out.writeByte(0); // background color index
|
||||
out.writeByte(0); // pixel aspect ratio - assume 1:1
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes Netscape application extension to define repeat count.
|
||||
*/
|
||||
|
||||
var writeNetscapeExt = function writeNetscapeExt()/*void*/
|
||||
{
|
||||
|
||||
out.writeByte(0x21); // extension introducer
|
||||
out.writeByte(0xff); // app extension label
|
||||
out.writeByte(11); // block size
|
||||
out.writeUTFBytes("NETSCAPE" + "2.0"); // app id + auth code
|
||||
out.writeByte(3); // sub-block size
|
||||
out.writeByte(1); // loop sub-block id
|
||||
WriteShort(repeat); // loop count (extra iterations, 0=repeat forever)
|
||||
out.writeByte(0); // block terminator
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes color table
|
||||
*/
|
||||
|
||||
var writePalette = function writePalette()/*void*/
|
||||
{
|
||||
out.writeBytes(colorTab);
|
||||
var n/*int*/ = (3 * 256) - colorTab.length;
|
||||
for (var i/*int*/ = 0; i < n; i++) out.writeByte(0);
|
||||
|
||||
}
|
||||
|
||||
var WriteShort = function WriteShort (pValue/*int*/)/*void*/
|
||||
{
|
||||
|
||||
out.writeByte( pValue & 0xFF );
|
||||
out.writeByte( (pValue >> 8) & 0xFF);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Encodes and writes pixel data
|
||||
*/
|
||||
|
||||
var writePixels = function writePixels()/*void*/
|
||||
{
|
||||
|
||||
var myencoder/*LZWEncoder*/ = new LZWEncoder(width, height, indexedPixels, colorDepth);
|
||||
myencoder.encode(out);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* retrieves the GIF stream
|
||||
*/
|
||||
var stream = exports.stream = function stream ( )/*ByteArray*/
|
||||
{
|
||||
|
||||
return out;
|
||||
|
||||
}
|
||||
|
||||
var setProperties = exports.setProperties = function setProperties(has_start, is_first){
|
||||
started = has_start;
|
||||
firstFrame = is_first;
|
||||
//out = new ByteArray; //??
|
||||
}
|
||||
|
||||
return exports
|
||||
|
||||
}
|
@ -1,328 +0,0 @@
|
||||
/**
|
||||
* This class handles LZW encoding
|
||||
* Adapted from Jef Poskanzer's Java port by way of J. M. G. Elliott.
|
||||
* @author Kevin Weiner (original Java version - kweiner@fmsware.com)
|
||||
* @author Thibault Imbert (AS3 version - bytearray.org)
|
||||
* @version 0.1 AS3 implementation
|
||||
*/
|
||||
|
||||
//import flash.utils.ByteArray;
|
||||
|
||||
LZWEncoder = function()
|
||||
{
|
||||
var exports = {};
|
||||
/*private_static*/ var EOF/*int*/ = -1;
|
||||
/*private*/ var imgW/*int*/;
|
||||
/*private*/ var imgH/*int*/
|
||||
/*private*/ var pixAry/*ByteArray*/;
|
||||
/*private*/ var initCodeSize/*int*/;
|
||||
/*private*/ var remaining/*int*/;
|
||||
/*private*/ var curPixel/*int*/;
|
||||
|
||||
// GIFCOMPR.C - GIF Image compression routines
|
||||
// Lempel-Ziv compression based on 'compress'. GIF modifications by
|
||||
// David Rowley (mgardi@watdcsu.waterloo.edu)
|
||||
// General DEFINEs
|
||||
|
||||
/*private_static*/ var BITS/*int*/ = 12;
|
||||
/*private_static*/ var HSIZE/*int*/ = 5003; // 80% occupancy
|
||||
|
||||
// GIF Image compression - modified 'compress'
|
||||
// Based on: compress.c - File compression ala IEEE Computer, June 1984.
|
||||
// By Authors: Spencer W. Thomas (decvax!harpo!utah-cs!utah-gr!thomas)
|
||||
// Jim McKie (decvax!mcvax!jim)
|
||||
// Steve Davies (decvax!vax135!petsd!peora!srd)
|
||||
// Ken Turkowski (decvax!decwrl!turtlevax!ken)
|
||||
// James A. Woods (decvax!ihnp4!ames!jaw)
|
||||
// Joe Orost (decvax!vax135!petsd!joe)
|
||||
|
||||
/*private*/ var n_bits/*int*/ // number of bits/code
|
||||
/*private*/ var maxbits/*int*/ = BITS; // user settable max # bits/code
|
||||
/*private*/ var maxcode/*int*/ // maximum code, given n_bits
|
||||
/*private*/ var maxmaxcode/*int*/ = 1 << BITS; // should NEVER generate this code
|
||||
/*private*/ var htab/*Array*/ = new Array;
|
||||
/*private*/ var codetab/*Array*/ = new Array;
|
||||
/*private*/ var hsize/*int*/ = HSIZE; // for dynamic table sizing
|
||||
/*private*/ var free_ent/*int*/ = 0; // first unused entry
|
||||
|
||||
// block compression parameters -- after all codes are used up,
|
||||
// and compression rate changes, start over.
|
||||
|
||||
/*private*/ var clear_flg/*Boolean*/ = false;
|
||||
|
||||
// Algorithm: use open addressing double hashing (no chaining) on the
|
||||
// prefix code / next character combination. We do a variant of Knuth's
|
||||
// algorithm D (vol. 3, sec. 6.4) along with G. Knott's relatively-prime
|
||||
// secondary probe. Here, the modular division first probe is gives way
|
||||
// to a faster exclusive-or manipulation. Also do block compression with
|
||||
// an adaptive reset, whereby the code table is cleared when the compression
|
||||
// ratio decreases, but after the table fills. The variable-length output
|
||||
// codes are re-sized at this point, and a special CLEAR code is generated
|
||||
// for the decompressor. Late addition: construct the table according to
|
||||
// file size for noticeable speed improvement on small files. Please direct
|
||||
// questions about this implementation to ames!jaw.
|
||||
|
||||
/*private*/ var g_init_bits/*int*/;
|
||||
/*private*/ var ClearCode/*int*/;
|
||||
/*private*/ var EOFCode/*int*/;
|
||||
|
||||
// output
|
||||
// Output the given code.
|
||||
// Inputs:
|
||||
// code: A n_bits-bit integer. If == -1, then EOF. This assumes
|
||||
// that n_bits =< wordsize - 1.
|
||||
// Outputs:
|
||||
// Outputs code to the file.
|
||||
// Assumptions:
|
||||
// Chars are 8 bits long.
|
||||
// Algorithm:
|
||||
// Maintain a BITS character long buffer (so that 8 codes will
|
||||
// fit in it exactly). Use the VAX insv instruction to insert each
|
||||
// code in turn. When the buffer fills up empty it and start over.
|
||||
|
||||
/*private*/ var cur_accum/*int*/ = 0;
|
||||
/*private*/ var cur_bits/*int*/ = 0;
|
||||
/*private*/ var masks/*Array*/ = [ 0x0000, 0x0001, 0x0003, 0x0007, 0x000F, 0x001F, 0x003F, 0x007F, 0x00FF, 0x01FF, 0x03FF, 0x07FF, 0x0FFF, 0x1FFF, 0x3FFF, 0x7FFF, 0xFFFF ];
|
||||
|
||||
// Number of characters so far in this 'packet'
|
||||
/*private*/ var a_count/*int*/;
|
||||
|
||||
// Define the storage for the packet accumulator
|
||||
/*private*/ var accum/*ByteArray*/ = [];
|
||||
|
||||
var LZWEncoder = exports.LZWEncoder = function LZWEncoder (width/*int*/, height/*int*/, pixels/*ByteArray*/, color_depth/*int*/)
|
||||
{
|
||||
|
||||
imgW = width;
|
||||
imgH = height;
|
||||
pixAry = pixels;
|
||||
initCodeSize = Math.max(2, color_depth);
|
||||
|
||||
}
|
||||
|
||||
// Add a character to the end of the current packet, and if it is 254
|
||||
// characters, flush the packet to disk.
|
||||
var char_out = function char_out(c/*Number*/, outs/*ByteArray*/)/*void*/
|
||||
{
|
||||
accum[a_count++] = c;
|
||||
if (a_count >= 254) flush_char(outs);
|
||||
|
||||
}
|
||||
|
||||
// Clear out the hash table
|
||||
// table clear for block compress
|
||||
|
||||
var cl_block = function cl_block(outs/*ByteArray*/)/*void*/
|
||||
{
|
||||
|
||||
cl_hash(hsize);
|
||||
free_ent = ClearCode + 2;
|
||||
clear_flg = true;
|
||||
output(ClearCode, outs);
|
||||
|
||||
}
|
||||
|
||||
// reset code table
|
||||
var cl_hash = function cl_hash(hsize/*int*/)/*void*/
|
||||
{
|
||||
|
||||
for (var i/*int*/ = 0; i < hsize; ++i) htab[i] = -1;
|
||||
|
||||
}
|
||||
|
||||
var compress = exports.compress = function compress(init_bits/*int*/, outs/*ByteArray*/)/*void*/
|
||||
|
||||
{
|
||||
var fcode/*int*/;
|
||||
var i/*int*/ /* = 0 */;
|
||||
var c/*int*/;
|
||||
var ent/*int*/;
|
||||
var disp/*int*/;
|
||||
var hsize_reg/*int*/;
|
||||
var hshift/*int*/;
|
||||
|
||||
// Set up the globals: g_init_bits - initial number of bits
|
||||
g_init_bits = init_bits;
|
||||
|
||||
// Set up the necessary values
|
||||
clear_flg = false;
|
||||
n_bits = g_init_bits;
|
||||
maxcode = MAXCODE(n_bits);
|
||||
|
||||
ClearCode = 1 << (init_bits - 1);
|
||||
EOFCode = ClearCode + 1;
|
||||
free_ent = ClearCode + 2;
|
||||
|
||||
a_count = 0; // clear packet
|
||||
|
||||
ent = nextPixel();
|
||||
|
||||
hshift = 0;
|
||||
for (fcode = hsize; fcode < 65536; fcode *= 2)
|
||||
++hshift;
|
||||
hshift = 8 - hshift; // set hash code range bound
|
||||
|
||||
hsize_reg = hsize;
|
||||
cl_hash(hsize_reg); // clear hash table
|
||||
|
||||
output(ClearCode, outs);
|
||||
|
||||
outer_loop: while ((c = nextPixel()) != EOF)
|
||||
|
||||
{
|
||||
|
||||
fcode = (c << maxbits) + ent;
|
||||
i = (c << hshift) ^ ent; // xor hashing
|
||||
|
||||
if (htab[i] == fcode)
|
||||
{
|
||||
ent = codetab[i];
|
||||
continue;
|
||||
} else if (htab[i] >= 0) // non-empty slot
|
||||
{
|
||||
disp = hsize_reg - i; // secondary hash (after G. Knott)
|
||||
if (i == 0)
|
||||
disp = 1;
|
||||
do
|
||||
{
|
||||
|
||||
if ((i -= disp) < 0) i += hsize_reg;
|
||||
|
||||
if (htab[i] == fcode)
|
||||
{
|
||||
ent = codetab[i];
|
||||
continue outer_loop;
|
||||
}
|
||||
} while (htab[i] >= 0);
|
||||
}
|
||||
|
||||
output(ent, outs);
|
||||
ent = c;
|
||||
if (free_ent < maxmaxcode)
|
||||
{
|
||||
codetab[i] = free_ent++; // code -> hashtable
|
||||
htab[i] = fcode;
|
||||
} else cl_block(outs);
|
||||
}
|
||||
|
||||
// Put out the final code.
|
||||
output(ent, outs);
|
||||
output(EOFCode, outs);
|
||||
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
var encode = exports.encode = function encode(os/*ByteArray*/)/*void*/
|
||||
{
|
||||
os.writeByte(initCodeSize); // write "initial code size" byte
|
||||
remaining = imgW * imgH; // reset navigation variables
|
||||
curPixel = 0;
|
||||
compress(initCodeSize + 1, os); // compress and write the pixel data
|
||||
os.writeByte(0); // write block terminator
|
||||
|
||||
}
|
||||
|
||||
// Flush the packet to disk, and reset the accumulator
|
||||
var flush_char = function flush_char(outs/*ByteArray*/)/*void*/
|
||||
{
|
||||
|
||||
if (a_count > 0)
|
||||
{
|
||||
outs.writeByte(a_count);
|
||||
outs.writeBytes(accum, 0, a_count);
|
||||
a_count = 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
var MAXCODE = function MAXCODE(n_bits/*int*/)/*int*/
|
||||
{
|
||||
|
||||
return (1 << n_bits) - 1;
|
||||
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Return the next pixel from the image
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
var nextPixel = function nextPixel()/*int*/
|
||||
{
|
||||
|
||||
if (remaining == 0) return EOF;
|
||||
|
||||
--remaining;
|
||||
|
||||
var pix/*Number*/ = pixAry[curPixel++];
|
||||
|
||||
return pix & 0xff;
|
||||
|
||||
}
|
||||
|
||||
var output = function output(code/*int*/, outs/*ByteArray*/)/*void*/
|
||||
|
||||
{
|
||||
cur_accum &= masks[cur_bits];
|
||||
|
||||
if (cur_bits > 0) cur_accum |= (code << cur_bits);
|
||||
else cur_accum = code;
|
||||
|
||||
cur_bits += n_bits;
|
||||
|
||||
while (cur_bits >= 8)
|
||||
|
||||
{
|
||||
|
||||
char_out((cur_accum & 0xff), outs);
|
||||
cur_accum >>= 8;
|
||||
cur_bits -= 8;
|
||||
|
||||
}
|
||||
|
||||
// If the next entry is going to be too big for the code size,
|
||||
// then increase it, if possible.
|
||||
|
||||
if (free_ent > maxcode || clear_flg)
|
||||
{
|
||||
|
||||
if (clear_flg)
|
||||
{
|
||||
|
||||
maxcode = MAXCODE(n_bits = g_init_bits);
|
||||
clear_flg = false;
|
||||
|
||||
} else
|
||||
{
|
||||
|
||||
++n_bits;
|
||||
|
||||
if (n_bits == maxbits) maxcode = maxmaxcode;
|
||||
|
||||
else maxcode = MAXCODE(n_bits);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (code == EOFCode)
|
||||
{
|
||||
|
||||
// At EOF, write the rest of the buffer.
|
||||
while (cur_bits > 0)
|
||||
{
|
||||
|
||||
char_out((cur_accum & 0xff), outs);
|
||||
cur_accum >>= 8;
|
||||
cur_bits -= 8;
|
||||
}
|
||||
|
||||
|
||||
flush_char(outs);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
LZWEncoder.apply(this, arguments);
|
||||
return exports;
|
||||
}
|
||||
|
@ -1,668 +0,0 @@
|
||||
/*
|
||||
* NeuQuant Neural-Net Quantization Algorithm
|
||||
* ------------------------------------------
|
||||
*
|
||||
* Copyright (c) 1994 Anthony Dekker
|
||||
*
|
||||
* NEUQUANT Neural-Net quantization algorithm by Anthony Dekker, 1994. See
|
||||
* "Kohonen neural networks for optimal colour quantization" in "Network:
|
||||
* Computation in Neural Systems" Vol. 5 (1994) pp 351-367. for a discussion of
|
||||
* the algorithm.
|
||||
*
|
||||
* Any party obtaining a copy of these files from the author, directly or
|
||||
* indirectly, is granted, free of charge, a full and unrestricted irrevocable,
|
||||
* world-wide, paid up, royalty-free, nonexclusive right and license to deal in
|
||||
* this software and documentation files (the "Software"), including without
|
||||
* limitation the rights to use, copy, modify, merge, publish, distribute,
|
||||
* sublicense, and/or sell copies of the Software, and to permit persons who
|
||||
* receive copies from any such party to do so, with the only requirement being
|
||||
* that this copyright notice remain intact.
|
||||
*/
|
||||
|
||||
/*
|
||||
* This class handles Neural-Net quantization algorithm
|
||||
* @author Kevin Weiner (original Java version - kweiner@fmsware.com)
|
||||
* @author Thibault Imbert (AS3 version - bytearray.org)
|
||||
* @version 0.1 AS3 implementation
|
||||
*/
|
||||
|
||||
//import flash.utils.ByteArray;
|
||||
|
||||
NeuQuant = function()
|
||||
{
|
||||
var exports = {};
|
||||
/*private_static*/ var netsize/*int*/ = 256; /* number of colours used */
|
||||
|
||||
/* four primes near 500 - assume no image has a length so large */
|
||||
/* that it is divisible by all four primes */
|
||||
|
||||
/*private_static*/ var prime1/*int*/ = 499;
|
||||
/*private_static*/ var prime2/*int*/ = 491;
|
||||
/*private_static*/ var prime3/*int*/ = 487;
|
||||
/*private_static*/ var prime4/*int*/ = 503;
|
||||
/*private_static*/ var minpicturebytes/*int*/ = (3 * prime4);
|
||||
|
||||
/* minimum size for input image */
|
||||
/*
|
||||
* Program Skeleton ---------------- [select samplefac in range 1..30] [read
|
||||
* image from input file] pic = (unsigned char*) malloc(3*width*height);
|
||||
* initnet(pic,3*width*height,samplefac); learn(); unbiasnet(); [write output
|
||||
* image header, using writecolourmap(f)] inxbuild(); write output image using
|
||||
* inxsearch(b,g,r)
|
||||
*/
|
||||
|
||||
/*
|
||||
* Network Definitions -------------------
|
||||
*/
|
||||
|
||||
/*private_static*/ var maxnetpos/*int*/ = (netsize - 1);
|
||||
/*private_static*/ var netbiasshift/*int*/ = 4; /* bias for colour values */
|
||||
/*private_static*/ var ncycles/*int*/ = 100; /* no. of learning cycles */
|
||||
|
||||
/* defs for freq and bias */
|
||||
/*private_static*/ var intbiasshift/*int*/ = 16; /* bias for fractions */
|
||||
/*private_static*/ var intbias/*int*/ = (1 << intbiasshift);
|
||||
/*private_static*/ var gammashift/*int*/ = 10; /* gamma = 1024 */
|
||||
/*private_static*/ var gamma/*int*/ = (1 << gammashift);
|
||||
/*private_static*/ var betashift/*int*/ = 10;
|
||||
/*private_static*/ var beta/*int*/ = (intbias >> betashift); /* beta = 1/1024 */
|
||||
/*private_static*/ var betagamma/*int*/ = (intbias << (gammashift - betashift));
|
||||
|
||||
/* defs for decreasing radius factor */
|
||||
/*private_static*/ var initrad/*int*/ = (netsize >> 3); /*
|
||||
* for 256 cols, radius
|
||||
* starts
|
||||
*/
|
||||
|
||||
/*private_static*/ var radiusbiasshift/*int*/ = 6; /* at 32.0 biased by 6 bits */
|
||||
/*private_static*/ var radiusbias/*int*/ = (1 << radiusbiasshift);
|
||||
/*private_static*/ var initradius/*int*/ = (initrad * radiusbias); /*
|
||||
* and
|
||||
* decreases
|
||||
* by a
|
||||
*/
|
||||
|
||||
/*private_static*/ var radiusdec/*int*/ = 30; /* factor of 1/30 each cycle */
|
||||
|
||||
/* defs for decreasing alpha factor */
|
||||
/*private_static*/ var alphabiasshift/*int*/ = 10; /* alpha starts at 1.0 */
|
||||
/*private_static*/ var initalpha/*int*/ = (1 << alphabiasshift);
|
||||
/*private*/ var alphadec/*int*/ /* biased by 10 bits */
|
||||
|
||||
/* radbias and alpharadbias used for radpower calculation */
|
||||
/*private_static*/ var radbiasshift/*int*/ = 8;
|
||||
/*private_static*/ var radbias/*int*/ = (1 << radbiasshift);
|
||||
/*private_static*/ var alpharadbshift/*int*/ = (alphabiasshift + radbiasshift);
|
||||
|
||||
/*private_static*/ var alpharadbias/*int*/ = (1 << alpharadbshift);
|
||||
|
||||
/*
|
||||
* Types and Global Variables --------------------------
|
||||
*/
|
||||
|
||||
/*private*/ var thepicture/*ByteArray*//* the input image itself */
|
||||
/*private*/ var lengthcount/*int*/; /* lengthcount = H*W*3 */
|
||||
/*private*/ var samplefac/*int*/; /* sampling factor 1..30 */
|
||||
|
||||
// typedef int pixel[4]; /* BGRc */
|
||||
/*private*/ var network/*Array*/; /* the network itself - [netsize][4] */
|
||||
/*protected*/ var netindex/*Array*/ = new Array();
|
||||
|
||||
/* for network lookup - really 256 */
|
||||
/*private*/ var bias/*Array*/ = new Array();
|
||||
|
||||
/* bias and freq arrays for learning */
|
||||
/*private*/ var freq/*Array*/ = new Array();
|
||||
/*private*/ var radpower/*Array*/ = new Array();
|
||||
|
||||
var NeuQuant = exports.NeuQuant = function NeuQuant(thepic/*ByteArray*/, len/*int*/, sample/*int*/)
|
||||
{
|
||||
|
||||
var i/*int*/;
|
||||
var p/*Array*/;
|
||||
|
||||
thepicture = thepic;
|
||||
lengthcount = len;
|
||||
samplefac = sample;
|
||||
|
||||
network = new Array(netsize);
|
||||
|
||||
for (i = 0; i < netsize; i++)
|
||||
{
|
||||
|
||||
network[i] = new Array(4);
|
||||
p = network[i];
|
||||
p[0] = p[1] = p[2] = (i << (netbiasshift + 8)) / netsize;
|
||||
freq[i] = intbias / netsize; /* 1/netsize */
|
||||
bias[i] = 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
var colorMap = function colorMap()/*ByteArray*/
|
||||
{
|
||||
|
||||
var map/*ByteArray*/ = [];
|
||||
var index/*Array*/ = new Array(netsize);
|
||||
for (var i/*int*/ = 0; i < netsize; i++)
|
||||
index[network[i][3]] = i;
|
||||
var k/*int*/ = 0;
|
||||
for (var l/*int*/ = 0; l < netsize; l++) {
|
||||
var j/*int*/ = index[l];
|
||||
map[k++] = (network[j][0]);
|
||||
map[k++] = (network[j][1]);
|
||||
map[k++] = (network[j][2]);
|
||||
}
|
||||
return map;
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* Insertion sort of network and building of netindex[0..255] (to do after
|
||||
* unbias)
|
||||
* -------------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
var inxbuild = function inxbuild()/*void*/
|
||||
{
|
||||
|
||||
var i/*int*/;
|
||||
var j/*int*/;
|
||||
var smallpos/*int*/;
|
||||
var smallval/*int*/;
|
||||
var p/*Array*/;
|
||||
var q/*Array*/;
|
||||
var previouscol/*int*/
|
||||
var startpos/*int*/
|
||||
|
||||
previouscol = 0;
|
||||
startpos = 0;
|
||||
for (i = 0; i < netsize; i++)
|
||||
{
|
||||
|
||||
p = network[i];
|
||||
smallpos = i;
|
||||
smallval = p[1]; /* index on g */
|
||||
/* find smallest in i..netsize-1 */
|
||||
for (j = i + 1; j < netsize; j++)
|
||||
{
|
||||
q = network[j];
|
||||
if (q[1] < smallval)
|
||||
{ /* index on g */
|
||||
|
||||
smallpos = j;
|
||||
smallval = q[1]; /* index on g */
|
||||
}
|
||||
}
|
||||
|
||||
q = network[smallpos];
|
||||
/* swap p (i) and q (smallpos) entries */
|
||||
|
||||
if (i != smallpos)
|
||||
{
|
||||
|
||||
j = q[0];
|
||||
q[0] = p[0];
|
||||
p[0] = j;
|
||||
j = q[1];
|
||||
q[1] = p[1];
|
||||
p[1] = j;
|
||||
j = q[2];
|
||||
q[2] = p[2];
|
||||
p[2] = j;
|
||||
j = q[3];
|
||||
q[3] = p[3];
|
||||
p[3] = j;
|
||||
|
||||
}
|
||||
|
||||
/* smallval entry is now in position i */
|
||||
|
||||
if (smallval != previouscol)
|
||||
|
||||
{
|
||||
|
||||
netindex[previouscol] = (startpos + i) >> 1;
|
||||
|
||||
for (j = previouscol + 1; j < smallval; j++) netindex[j] = i;
|
||||
|
||||
previouscol = smallval;
|
||||
startpos = i;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
netindex[previouscol] = (startpos + maxnetpos) >> 1;
|
||||
for (j = previouscol + 1; j < 256; j++) netindex[j] = maxnetpos; /* really 256 */
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* Main Learning Loop ------------------
|
||||
*/
|
||||
|
||||
var learn = function learn()/*void*/
|
||||
|
||||
{
|
||||
|
||||
var i/*int*/;
|
||||
var j/*int*/;
|
||||
var b/*int*/;
|
||||
var g/*int*/
|
||||
var r/*int*/;
|
||||
var radius/*int*/;
|
||||
var rad/*int*/;
|
||||
var alpha/*int*/;
|
||||
var step/*int*/;
|
||||
var delta/*int*/;
|
||||
var samplepixels/*int*/;
|
||||
var p/*ByteArray*/;
|
||||
var pix/*int*/;
|
||||
var lim/*int*/;
|
||||
|
||||
if (lengthcount < minpicturebytes) samplefac = 1;
|
||||
|
||||
alphadec = 30 + ((samplefac - 1) / 3);
|
||||
p = thepicture;
|
||||
pix = 0;
|
||||
lim = lengthcount;
|
||||
samplepixels = lengthcount / (3 * samplefac);
|
||||
delta = samplepixels / ncycles;
|
||||
alpha = initalpha;
|
||||
radius = initradius;
|
||||
|
||||
rad = radius >> radiusbiasshift;
|
||||
if (rad <= 1) rad = 0;
|
||||
|
||||
for (i = 0; i < rad; i++) radpower[i] = alpha * (((rad * rad - i * i) * radbias) / (rad * rad));
|
||||
|
||||
|
||||
if (lengthcount < minpicturebytes) step = 3;
|
||||
|
||||
else if ((lengthcount % prime1) != 0) step = 3 * prime1;
|
||||
|
||||
else
|
||||
|
||||
{
|
||||
|
||||
if ((lengthcount % prime2) != 0) step = 3 * prime2;
|
||||
|
||||
else
|
||||
|
||||
{
|
||||
|
||||
if ((lengthcount % prime3) != 0) step = 3 * prime3;
|
||||
|
||||
else step = 3 * prime4;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
i = 0;
|
||||
|
||||
while (i < samplepixels)
|
||||
|
||||
{
|
||||
|
||||
b = (p[pix + 0] & 0xff) << netbiasshift;
|
||||
g = (p[pix + 1] & 0xff) << netbiasshift;
|
||||
r = (p[pix + 2] & 0xff) << netbiasshift;
|
||||
j = contest(b, g, r);
|
||||
|
||||
altersingle(alpha, j, b, g, r);
|
||||
|
||||
if (rad != 0) alterneigh(rad, j, b, g, r); /* alter neighbours */
|
||||
|
||||
pix += step;
|
||||
|
||||
if (pix >= lim) pix -= lengthcount;
|
||||
|
||||
i++;
|
||||
|
||||
if (delta == 0) delta = 1;
|
||||
|
||||
if (i % delta == 0)
|
||||
|
||||
{
|
||||
|
||||
alpha -= alpha / alphadec;
|
||||
radius -= radius / radiusdec;
|
||||
rad = radius >> radiusbiasshift;
|
||||
|
||||
if (rad <= 1) rad = 0;
|
||||
|
||||
for (j = 0; j < rad; j++) radpower[j] = alpha * (((rad * rad - j * j) * radbias) / (rad * rad));
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
** Search for BGR values 0..255 (after net is unbiased) and return colour
|
||||
* index
|
||||
* ----------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
var map = exports.map = function map(b/*int*/, g/*int*/, r/*int*/)/*int*/
|
||||
|
||||
{
|
||||
|
||||
var i/*int*/;
|
||||
var j/*int*/;
|
||||
var dist/*int*/
|
||||
var a/*int*/;
|
||||
var bestd/*int*/;
|
||||
var p/*Array*/;
|
||||
var best/*int*/;
|
||||
|
||||
bestd = 1000; /* biggest possible dist is 256*3 */
|
||||
best = -1;
|
||||
i = netindex[g]; /* index on g */
|
||||
j = i - 1; /* start at netindex[g] and work outwards */
|
||||
|
||||
while ((i < netsize) || (j >= 0))
|
||||
|
||||
{
|
||||
|
||||
if (i < netsize)
|
||||
|
||||
{
|
||||
|
||||
p = network[i];
|
||||
|
||||
dist = p[1] - g; /* inx key */
|
||||
|
||||
if (dist >= bestd) i = netsize; /* stop iter */
|
||||
|
||||
else
|
||||
|
||||
{
|
||||
|
||||
i++;
|
||||
|
||||
if (dist < 0) dist = -dist;
|
||||
|
||||
a = p[0] - b;
|
||||
|
||||
if (a < 0) a = -a;
|
||||
|
||||
dist += a;
|
||||
|
||||
if (dist < bestd)
|
||||
|
||||
{
|
||||
|
||||
a = p[2] - r;
|
||||
|
||||
if (a < 0) a = -a;
|
||||
|
||||
dist += a;
|
||||
|
||||
if (dist < bestd)
|
||||
|
||||
{
|
||||
|
||||
bestd = dist;
|
||||
best = p[3];
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (j >= 0)
|
||||
{
|
||||
|
||||
p = network[j];
|
||||
|
||||
dist = g - p[1]; /* inx key - reverse dif */
|
||||
|
||||
if (dist >= bestd) j = -1; /* stop iter */
|
||||
|
||||
else
|
||||
{
|
||||
|
||||
j--;
|
||||
if (dist < 0) dist = -dist;
|
||||
a = p[0] - b;
|
||||
if (a < 0) a = -a;
|
||||
dist += a;
|
||||
|
||||
if (dist < bestd)
|
||||
|
||||
{
|
||||
|
||||
a = p[2] - r;
|
||||
if (a < 0)a = -a;
|
||||
dist += a;
|
||||
if (dist < bestd)
|
||||
{
|
||||
bestd = dist;
|
||||
best = p[3];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return (best);
|
||||
|
||||
}
|
||||
|
||||
var process = exports.process = function process()/*ByteArray*/
|
||||
{
|
||||
|
||||
learn();
|
||||
unbiasnet();
|
||||
inxbuild();
|
||||
return colorMap();
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* Unbias network to give byte values 0..255 and record position i to prepare
|
||||
* for sort
|
||||
* -----------------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
var unbiasnet = function unbiasnet()/*void*/
|
||||
|
||||
{
|
||||
|
||||
var i/*int*/;
|
||||
var j/*int*/;
|
||||
|
||||
for (i = 0; i < netsize; i++)
|
||||
{
|
||||
network[i][0] >>= netbiasshift;
|
||||
network[i][1] >>= netbiasshift;
|
||||
network[i][2] >>= netbiasshift;
|
||||
network[i][3] = i; /* record colour no */
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* Move adjacent neurons by precomputed alpha*(1-((i-j)^2/[r]^2)) in
|
||||
* radpower[|i-j|]
|
||||
* ---------------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
var alterneigh = function alterneigh(rad/*int*/, i/*int*/, b/*int*/, g/*int*/, r/*int*/)/*void*/
|
||||
|
||||
{
|
||||
|
||||
var j/*int*/;
|
||||
var k/*int*/;
|
||||
var lo/*int*/;
|
||||
var hi/*int*/;
|
||||
var a/*int*/;
|
||||
var m/*int*/;
|
||||
|
||||
var p/*Array*/;
|
||||
|
||||
lo = i - rad;
|
||||
if (lo < -1) lo = -1;
|
||||
|
||||
hi = i + rad;
|
||||
|
||||
if (hi > netsize) hi = netsize;
|
||||
|
||||
j = i + 1;
|
||||
k = i - 1;
|
||||
m = 1;
|
||||
|
||||
while ((j < hi) || (k > lo))
|
||||
|
||||
{
|
||||
|
||||
a = radpower[m++];
|
||||
|
||||
if (j < hi)
|
||||
|
||||
{
|
||||
|
||||
p = network[j++];
|
||||
|
||||
try {
|
||||
|
||||
p[0] -= (a * (p[0] - b)) / alpharadbias;
|
||||
p[1] -= (a * (p[1] - g)) / alpharadbias;
|
||||
p[2] -= (a * (p[2] - r)) / alpharadbias;
|
||||
|
||||
} catch (e/*Error*/) {} // prevents 1.3 miscompilation
|
||||
|
||||
}
|
||||
|
||||
if (k > lo)
|
||||
|
||||
{
|
||||
|
||||
p = network[k--];
|
||||
|
||||
try
|
||||
{
|
||||
|
||||
p[0] -= (a * (p[0] - b)) / alpharadbias;
|
||||
p[1] -= (a * (p[1] - g)) / alpharadbias;
|
||||
p[2] -= (a * (p[2] - r)) / alpharadbias;
|
||||
|
||||
} catch (e/*Error*/) {}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* Move neuron i towards biased (b,g,r) by factor alpha
|
||||
* ----------------------------------------------------
|
||||
*/
|
||||
|
||||
var altersingle = function altersingle(alpha/*int*/, i/*int*/, b/*int*/, g/*int*/, r/*int*/)/*void*/
|
||||
{
|
||||
|
||||
/* alter hit neuron */
|
||||
var n/*Array*/ = network[i];
|
||||
n[0] -= (alpha * (n[0] - b)) / initalpha;
|
||||
n[1] -= (alpha * (n[1] - g)) / initalpha;
|
||||
n[2] -= (alpha * (n[2] - r)) / initalpha;
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* Search for biased BGR values ----------------------------
|
||||
*/
|
||||
|
||||
var contest = function contest(b/*int*/, g/*int*/, r/*int*/)/*int*/
|
||||
{
|
||||
|
||||
/* finds closest neuron (min dist) and updates freq */
|
||||
/* finds best neuron (min dist-bias) and returns position */
|
||||
/* for frequently chosen neurons, freq[i] is high and bias[i] is negative */
|
||||
/* bias[i] = gamma*((1/netsize)-freq[i]) */
|
||||
|
||||
var i/*int*/;
|
||||
var dist/*int*/;
|
||||
var a/*int*/;
|
||||
var biasdist/*int*/;
|
||||
var betafreq/*int*/;
|
||||
var bestpos/*int*/;
|
||||
var bestbiaspos/*int*/;
|
||||
var bestd/*int*/;
|
||||
var bestbiasd/*int*/;
|
||||
var n/*Array*/;
|
||||
|
||||
bestd = ~(1 << 31);
|
||||
bestbiasd = bestd;
|
||||
bestpos = -1;
|
||||
bestbiaspos = bestpos;
|
||||
|
||||
for (i = 0; i < netsize; i++)
|
||||
|
||||
{
|
||||
|
||||
n = network[i];
|
||||
dist = n[0] - b;
|
||||
|
||||
if (dist < 0) dist = -dist;
|
||||
|
||||
a = n[1] - g;
|
||||
|
||||
if (a < 0) a = -a;
|
||||
|
||||
dist += a;
|
||||
|
||||
a = n[2] - r;
|
||||
|
||||
if (a < 0) a = -a;
|
||||
|
||||
dist += a;
|
||||
|
||||
if (dist < bestd)
|
||||
|
||||
{
|
||||
|
||||
bestd = dist;
|
||||
bestpos = i;
|
||||
|
||||
}
|
||||
|
||||
biasdist = dist - ((bias[i]) >> (intbiasshift - netbiasshift));
|
||||
|
||||
if (biasdist < bestbiasd)
|
||||
|
||||
{
|
||||
|
||||
bestbiasd = biasdist;
|
||||
bestbiaspos = i;
|
||||
|
||||
}
|
||||
|
||||
betafreq = (freq[i] >> betashift);
|
||||
freq[i] -= betafreq;
|
||||
bias[i] += (betafreq << gammashift);
|
||||
|
||||
}
|
||||
|
||||
freq[bestpos] += beta;
|
||||
bias[bestpos] -= betagamma;
|
||||
return (bestbiaspos);
|
||||
|
||||
}
|
||||
|
||||
NeuQuant.apply(this, arguments);
|
||||
return exports;
|
||||
}
|
@ -1,18 +0,0 @@
|
||||
function encode64(input) {
|
||||
var output = "", i = 0, l = input.length,
|
||||
key = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=",
|
||||
chr1, chr2, chr3, enc1, enc2, enc3, enc4;
|
||||
while (i < l) {
|
||||
chr1 = input.charCodeAt(i++);
|
||||
chr2 = input.charCodeAt(i++);
|
||||
chr3 = input.charCodeAt(i++);
|
||||
enc1 = chr1 >> 2;
|
||||
enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
|
||||
enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
|
||||
enc4 = chr3 & 63;
|
||||
if (isNaN(chr2)) enc3 = enc4 = 64;
|
||||
else if (isNaN(chr3)) enc4 = 64;
|
||||
output = output + key.charAt(enc1) + key.charAt(enc2) + key.charAt(enc3) + key.charAt(enc4);
|
||||
}
|
||||
return output;
|
||||
}
|
BIN
js/lib/jsColor_1_4_0/.DS_Store
vendored
Before Width: | Height: | Size: 66 B |
Before Width: | Height: | Size: 83 B |
@ -1,12 +0,0 @@
|
||||
<html>
|
||||
<head>
|
||||
<title>jscolor demo</title>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<script type="text/javascript" src="jscolor.js"></script>
|
||||
|
||||
Click here: <input class="color" value="66ff00">
|
||||
|
||||
</body>
|
||||
</html>
|
Before Width: | Height: | Size: 2.6 KiB |
Before Width: | Height: | Size: 2.8 KiB |
@ -1,953 +0,0 @@
|
||||
/**
|
||||
* jscolor, JavaScript Color Picker
|
||||
*
|
||||
* @version 1.4.0
|
||||
* @license GNU Lesser General Public License, http://www.gnu.org/copyleft/lesser.html
|
||||
* @author Jan Odvarko, http://odvarko.cz
|
||||
* @created 2008-06-15
|
||||
* @updated 2012-07-06
|
||||
* @link http://jscolor.com
|
||||
*/
|
||||
|
||||
|
||||
var jscolor = {
|
||||
|
||||
|
||||
dir : '', // location of jscolor directory (leave empty to autodetect)
|
||||
bindClass : 'color', // class name
|
||||
binding : true, // automatic binding via <input class="...">
|
||||
preloading : true, // use image preloading?
|
||||
|
||||
|
||||
install : function() {
|
||||
jscolor.addEvent(window, 'load', jscolor.init);
|
||||
},
|
||||
|
||||
|
||||
init : function() {
|
||||
if(jscolor.binding) {
|
||||
jscolor.bind();
|
||||
}
|
||||
if(jscolor.preloading) {
|
||||
jscolor.preload();
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
getDir : function() {
|
||||
if(!jscolor.dir) {
|
||||
var detected = jscolor.detectDir();
|
||||
jscolor.dir = detected!==false ? detected : 'jscolor/';
|
||||
}
|
||||
return jscolor.dir;
|
||||
},
|
||||
|
||||
|
||||
detectDir : function() {
|
||||
var base = location.href;
|
||||
|
||||
var e = document.getElementsByTagName('base');
|
||||
for(var i=0; i<e.length; i+=1) {
|
||||
if(e[i].href) { base = e[i].href; }
|
||||
}
|
||||
|
||||
var e = document.getElementsByTagName('script');
|
||||
for(var i=0; i<e.length; i+=1) {
|
||||
if(e[i].src && /(^|\/)jscolor\.js([?#].*)?$/i.test(e[i].src)) {
|
||||
var src = new jscolor.URI(e[i].src);
|
||||
var srcAbs = src.toAbsolute(base);
|
||||
srcAbs.path = srcAbs.path.replace(/[^\/]+$/, ''); // remove filename
|
||||
srcAbs.query = null;
|
||||
srcAbs.fragment = null;
|
||||
return srcAbs.toString();
|
||||
}
|
||||
}
|
||||
return false;
|
||||
},
|
||||
|
||||
|
||||
bind : function() {
|
||||
var matchClass = new RegExp('(^|\\s)('+jscolor.bindClass+')\\s*(\\{[^}]*\\})?', 'i');
|
||||
var e = document.getElementsByTagName('input');
|
||||
for(var i=0; i<e.length; i+=1) {
|
||||
var m;
|
||||
if(!e[i].color && e[i].className && (m = e[i].className.match(matchClass))) {
|
||||
var prop = {};
|
||||
if(m[3]) {
|
||||
try {
|
||||
prop = (new Function ('return (' + m[3] + ')'))();
|
||||
} catch(eInvalidProp) {}
|
||||
}
|
||||
e[i].color = new jscolor.color(e[i], prop);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
preload : function() {
|
||||
for(var fn in jscolor.imgRequire) {
|
||||
if(jscolor.imgRequire.hasOwnProperty(fn)) {
|
||||
jscolor.loadImage(fn);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
images : {
|
||||
pad : [ 181, 101 ],
|
||||
sld : [ 16, 101 ],
|
||||
cross : [ 15, 15 ],
|
||||
arrow : [ 7, 11 ]
|
||||
},
|
||||
|
||||
|
||||
imgRequire : {},
|
||||
imgLoaded : {},
|
||||
|
||||
|
||||
requireImage : function(filename) {
|
||||
jscolor.imgRequire[filename] = true;
|
||||
},
|
||||
|
||||
|
||||
loadImage : function(filename) {
|
||||
if(!jscolor.imgLoaded[filename]) {
|
||||
jscolor.imgLoaded[filename] = new Image();
|
||||
jscolor.imgLoaded[filename].src = jscolor.getDir()+filename;
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
fetchElement : function(mixed) {
|
||||
return typeof mixed === 'string' ? document.getElementById(mixed) : mixed;
|
||||
},
|
||||
|
||||
|
||||
addEvent : function(el, evnt, func) {
|
||||
if(el.addEventListener) {
|
||||
el.addEventListener(evnt, func, false);
|
||||
} else if(el.attachEvent) {
|
||||
el.attachEvent('on'+evnt, func);
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
fireEvent : function(el, evnt) {
|
||||
if(!el) {
|
||||
return;
|
||||
}
|
||||
if(document.createEvent) {
|
||||
var ev = document.createEvent('HTMLEvents');
|
||||
ev.initEvent(evnt, true, true);
|
||||
el.dispatchEvent(ev);
|
||||
} else if(document.createEventObject) {
|
||||
var ev = document.createEventObject();
|
||||
el.fireEvent('on'+evnt, ev);
|
||||
} else if(el['on'+evnt]) { // alternatively use the traditional event model (IE5)
|
||||
el['on'+evnt]();
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
getElementPos : function(e) {
|
||||
var e1=e, e2=e;
|
||||
var x=0, y=0;
|
||||
if(e1.offsetParent) {
|
||||
do {
|
||||
x += e1.offsetLeft;
|
||||
y += e1.offsetTop;
|
||||
} while(e1 = e1.offsetParent);
|
||||
}
|
||||
while((e2 = e2.parentNode) && e2.nodeName.toUpperCase() !== 'BODY') {
|
||||
x -= e2.scrollLeft;
|
||||
y -= e2.scrollTop;
|
||||
}
|
||||
return [x, y];
|
||||
},
|
||||
|
||||
|
||||
getElementSize : function(e) {
|
||||
return [e.offsetWidth, e.offsetHeight];
|
||||
},
|
||||
|
||||
|
||||
getRelMousePos : function(e) {
|
||||
var x = 0, y = 0;
|
||||
if (!e) { e = window.event; }
|
||||
if (typeof e.offsetX === 'number') {
|
||||
x = e.offsetX;
|
||||
y = e.offsetY;
|
||||
} else if (typeof e.layerX === 'number') {
|
||||
x = e.layerX;
|
||||
y = e.layerY;
|
||||
}
|
||||
return { x: x, y: y };
|
||||
},
|
||||
|
||||
|
||||
getViewPos : function() {
|
||||
if(typeof window.pageYOffset === 'number') {
|
||||
return [window.pageXOffset, window.pageYOffset];
|
||||
} else if(document.body && (document.body.scrollLeft || document.body.scrollTop)) {
|
||||
return [document.body.scrollLeft, document.body.scrollTop];
|
||||
} else if(document.documentElement && (document.documentElement.scrollLeft || document.documentElement.scrollTop)) {
|
||||
return [document.documentElement.scrollLeft, document.documentElement.scrollTop];
|
||||
} else {
|
||||
return [0, 0];
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
getViewSize : function() {
|
||||
if(typeof window.innerWidth === 'number') {
|
||||
return [window.innerWidth, window.innerHeight];
|
||||
} else if(document.body && (document.body.clientWidth || document.body.clientHeight)) {
|
||||
return [document.body.clientWidth, document.body.clientHeight];
|
||||
} else if(document.documentElement && (document.documentElement.clientWidth || document.documentElement.clientHeight)) {
|
||||
return [document.documentElement.clientWidth, document.documentElement.clientHeight];
|
||||
} else {
|
||||
return [0, 0];
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
URI : function(uri) { // See RFC3986
|
||||
|
||||
this.scheme = null;
|
||||
this.authority = null;
|
||||
this.path = '';
|
||||
this.query = null;
|
||||
this.fragment = null;
|
||||
|
||||
this.parse = function(uri) {
|
||||
var m = uri.match(/^(([A-Za-z][0-9A-Za-z+.-]*)(:))?((\/\/)([^\/?#]*))?([^?#]*)((\?)([^#]*))?((#)(.*))?/);
|
||||
this.scheme = m[3] ? m[2] : null;
|
||||
this.authority = m[5] ? m[6] : null;
|
||||
this.path = m[7];
|
||||
this.query = m[9] ? m[10] : null;
|
||||
this.fragment = m[12] ? m[13] : null;
|
||||
return this;
|
||||
};
|
||||
|
||||
this.toString = function() {
|
||||
var result = '';
|
||||
if(this.scheme !== null) { result = result + this.scheme + ':'; }
|
||||
if(this.authority !== null) { result = result + '//' + this.authority; }
|
||||
if(this.path !== null) { result = result + this.path; }
|
||||
if(this.query !== null) { result = result + '?' + this.query; }
|
||||
if(this.fragment !== null) { result = result + '#' + this.fragment; }
|
||||
return result;
|
||||
};
|
||||
|
||||
this.toAbsolute = function(base) {
|
||||
var base = new jscolor.URI(base);
|
||||
var r = this;
|
||||
var t = new jscolor.URI;
|
||||
|
||||
if(base.scheme === null) { return false; }
|
||||
|
||||
if(r.scheme !== null && r.scheme.toLowerCase() === base.scheme.toLowerCase()) {
|
||||
r.scheme = null;
|
||||
}
|
||||
|
||||
if(r.scheme !== null) {
|
||||
t.scheme = r.scheme;
|
||||
t.authority = r.authority;
|
||||
t.path = removeDotSegments(r.path);
|
||||
t.query = r.query;
|
||||
} else {
|
||||
if(r.authority !== null) {
|
||||
t.authority = r.authority;
|
||||
t.path = removeDotSegments(r.path);
|
||||
t.query = r.query;
|
||||
} else {
|
||||
if(r.path === '') {
|
||||
t.path = base.path;
|
||||
if(r.query !== null) {
|
||||
t.query = r.query;
|
||||
} else {
|
||||
t.query = base.query;
|
||||
}
|
||||
} else {
|
||||
if(r.path.substr(0,1) === '/') {
|
||||
t.path = removeDotSegments(r.path);
|
||||
} else {
|
||||
if(base.authority !== null && base.path === '') {
|
||||
t.path = '/'+r.path;
|
||||
} else {
|
||||
t.path = base.path.replace(/[^\/]+$/,'')+r.path;
|
||||
}
|
||||
t.path = removeDotSegments(t.path);
|
||||
}
|
||||
t.query = r.query;
|
||||
}
|
||||
t.authority = base.authority;
|
||||
}
|
||||
t.scheme = base.scheme;
|
||||
}
|
||||
t.fragment = r.fragment;
|
||||
|
||||
return t;
|
||||
};
|
||||
|
||||
function removeDotSegments(path) {
|
||||
var out = '';
|
||||
while(path) {
|
||||
if(path.substr(0,3)==='../' || path.substr(0,2)==='./') {
|
||||
path = path.replace(/^\.+/,'').substr(1);
|
||||
} else if(path.substr(0,3)==='/./' || path==='/.') {
|
||||
path = '/'+path.substr(3);
|
||||
} else if(path.substr(0,4)==='/../' || path==='/..') {
|
||||
path = '/'+path.substr(4);
|
||||
out = out.replace(/\/?[^\/]*$/, '');
|
||||
} else if(path==='.' || path==='..') {
|
||||
path = '';
|
||||
} else {
|
||||
var rm = path.match(/^\/?[^\/]*/)[0];
|
||||
path = path.substr(rm.length);
|
||||
out = out + rm;
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
if(uri) {
|
||||
this.parse(uri);
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
|
||||
/*
|
||||
* Usage example:
|
||||
* var myColor = new jscolor.color(myInputElement)
|
||||
*/
|
||||
|
||||
color : function(target, prop) {
|
||||
|
||||
|
||||
this.required = true; // refuse empty values?
|
||||
this.adjust = true; // adjust value to uniform notation?
|
||||
this.hash = false; // prefix color with # symbol?
|
||||
this.caps = true; // uppercase?
|
||||
this.slider = true; // show the value/saturation slider?
|
||||
this.valueElement = target; // value holder
|
||||
this.styleElement = target; // where to reflect current color
|
||||
this.onImmediateChange = null; // onchange callback (can be either string or function)
|
||||
this.hsv = [0, 0, 1]; // read-only 0-6, 0-1, 0-1
|
||||
this.rgb = [1, 1, 1]; // read-only 0-1, 0-1, 0-1
|
||||
this.minH = 0; // read-only 0-6
|
||||
this.maxH = 6; // read-only 0-6
|
||||
this.minS = 0; // read-only 0-1
|
||||
this.maxS = 1; // read-only 0-1
|
||||
this.minV = 0; // read-only 0-1
|
||||
this.maxV = 1; // read-only 0-1
|
||||
|
||||
this.pickerOnfocus = true; // display picker on focus?
|
||||
this.pickerMode = 'HSV'; // HSV | HVS
|
||||
this.pickerPosition = 'bottom'; // left | right | top | bottom
|
||||
this.pickerSmartPosition = true; // automatically adjust picker position when necessary
|
||||
this.pickerButtonHeight = 20; // px
|
||||
this.pickerClosable = false;
|
||||
this.pickerCloseText = 'Close';
|
||||
this.pickerButtonColor = 'ButtonText'; // px
|
||||
this.pickerFace = 10; // px
|
||||
this.pickerFaceColor = 'ThreeDFace'; // CSS color
|
||||
this.pickerBorder = 1; // px
|
||||
this.pickerBorderColor = 'ThreeDHighlight ThreeDShadow ThreeDShadow ThreeDHighlight'; // CSS color
|
||||
this.pickerInset = 1; // px
|
||||
this.pickerInsetColor = 'ThreeDShadow ThreeDHighlight ThreeDHighlight ThreeDShadow'; // CSS color
|
||||
this.pickerZIndex = 10000;
|
||||
|
||||
|
||||
for(var p in prop) {
|
||||
if(prop.hasOwnProperty(p)) {
|
||||
this[p] = prop[p];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
this.hidePicker = function() {
|
||||
if(isPickerOwner()) {
|
||||
removePicker();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
this.showPicker = function() {
|
||||
if(!isPickerOwner()) {
|
||||
var tp = jscolor.getElementPos(target); // target pos
|
||||
var ts = jscolor.getElementSize(target); // target size
|
||||
var vp = jscolor.getViewPos(); // view pos
|
||||
var vs = jscolor.getViewSize(); // view size
|
||||
var ps = getPickerDims(this); // picker size
|
||||
var a, b, c;
|
||||
switch(this.pickerPosition.toLowerCase()) {
|
||||
case 'left': a=1; b=0; c=-1; break;
|
||||
case 'right':a=1; b=0; c=1; break;
|
||||
case 'top': a=0; b=1; c=-1; break;
|
||||
default: a=0; b=1; c=1; break;
|
||||
}
|
||||
var l = (ts[b]+ps[b])/2;
|
||||
|
||||
// picker pos
|
||||
if (!this.pickerSmartPosition) {
|
||||
var pp = [
|
||||
tp[a],
|
||||
tp[b]+ts[b]-l+l*c
|
||||
];
|
||||
} else {
|
||||
var pp = [
|
||||
-vp[a]+tp[a]+ps[a] > vs[a] ?
|
||||
(-vp[a]+tp[a]+ts[a]/2 > vs[a]/2 && tp[a]+ts[a]-ps[a] >= 0 ? tp[a]+ts[a]-ps[a] : tp[a]) :
|
||||
tp[a],
|
||||
-vp[b]+tp[b]+ts[b]+ps[b]-l+l*c > vs[b] ?
|
||||
(-vp[b]+tp[b]+ts[b]/2 > vs[b]/2 && tp[b]+ts[b]-l-l*c >= 0 ? tp[b]+ts[b]-l-l*c : tp[b]+ts[b]-l+l*c) :
|
||||
(tp[b]+ts[b]-l+l*c >= 0 ? tp[b]+ts[b]-l+l*c : tp[b]+ts[b]-l-l*c)
|
||||
];
|
||||
}
|
||||
drawPicker(pp[a], pp[b]);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
this.importColor = function() {
|
||||
if(!valueElement) {
|
||||
this.exportColor();
|
||||
} else {
|
||||
if(!this.adjust) {
|
||||
if(!this.fromString(valueElement.value, leaveValue)) {
|
||||
styleElement.style.backgroundImage = styleElement.jscStyle.backgroundImage;
|
||||
styleElement.style.backgroundColor = styleElement.jscStyle.backgroundColor;
|
||||
styleElement.style.color = styleElement.jscStyle.color;
|
||||
this.exportColor(leaveValue | leaveStyle);
|
||||
}
|
||||
} else if(!this.required && /^\s*$/.test(valueElement.value)) {
|
||||
valueElement.value = '';
|
||||
styleElement.style.backgroundImage = styleElement.jscStyle.backgroundImage;
|
||||
styleElement.style.backgroundColor = styleElement.jscStyle.backgroundColor;
|
||||
styleElement.style.color = styleElement.jscStyle.color;
|
||||
this.exportColor(leaveValue | leaveStyle);
|
||||
|
||||
} else if(this.fromString(valueElement.value)) {
|
||||
// OK
|
||||
} else {
|
||||
this.exportColor();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
this.exportColor = function(flags) {
|
||||
if(!(flags & leaveValue) && valueElement) {
|
||||
var value = this.toString();
|
||||
if(this.caps) { value = value.toUpperCase(); }
|
||||
if(this.hash) { value = '#'+value; }
|
||||
valueElement.value = value;
|
||||
}
|
||||
if(!(flags & leaveStyle) && styleElement) {
|
||||
styleElement.style.backgroundImage = "none";
|
||||
styleElement.style.backgroundColor =
|
||||
'#'+this.toString();
|
||||
styleElement.style.color =
|
||||
0.213 * this.rgb[0] +
|
||||
0.715 * this.rgb[1] +
|
||||
0.072 * this.rgb[2]
|
||||
< 0.5 ? '#FFF' : '#000';
|
||||
}
|
||||
if(!(flags & leavePad) && isPickerOwner()) {
|
||||
redrawPad();
|
||||
}
|
||||
if(!(flags & leaveSld) && isPickerOwner()) {
|
||||
redrawSld();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
this.fromHSV = function(h, s, v, flags) { // null = don't change
|
||||
if(h !== null) { h = Math.max(0.0, this.minH, Math.min(6.0, this.maxH, h)); }
|
||||
if(s !== null) { s = Math.max(0.0, this.minS, Math.min(1.0, this.maxS, s)); }
|
||||
if(v !== null) { v = Math.max(0.0, this.minV, Math.min(1.0, this.maxV, v)); }
|
||||
|
||||
this.rgb = HSV_RGB(
|
||||
h===null ? this.hsv[0] : (this.hsv[0]=h),
|
||||
s===null ? this.hsv[1] : (this.hsv[1]=s),
|
||||
v===null ? this.hsv[2] : (this.hsv[2]=v)
|
||||
);
|
||||
|
||||
this.exportColor(flags);
|
||||
};
|
||||
|
||||
|
||||
this.fromRGB = function(r, g, b, flags) { // null = don't change
|
||||
if(r !== null) { r = Math.max(0.0, Math.min(1.0, r)); }
|
||||
if(g !== null) { g = Math.max(0.0, Math.min(1.0, g)); }
|
||||
if(b !== null) { b = Math.max(0.0, Math.min(1.0, b)); }
|
||||
|
||||
var hsv = RGB_HSV(
|
||||
r===null ? this.rgb[0] : r,
|
||||
g===null ? this.rgb[1] : g,
|
||||
b===null ? this.rgb[2] : b
|
||||
);
|
||||
if(hsv[0] !== null) {
|
||||
this.hsv[0] = Math.max(0.0, this.minH, Math.min(6.0, this.maxH, hsv[0]));
|
||||
}
|
||||
if(hsv[2] !== 0) {
|
||||
this.hsv[1] = hsv[1]===null ? null : Math.max(0.0, this.minS, Math.min(1.0, this.maxS, hsv[1]));
|
||||
}
|
||||
this.hsv[2] = hsv[2]===null ? null : Math.max(0.0, this.minV, Math.min(1.0, this.maxV, hsv[2]));
|
||||
|
||||
// update RGB according to final HSV, as some values might be trimmed
|
||||
var rgb = HSV_RGB(this.hsv[0], this.hsv[1], this.hsv[2]);
|
||||
this.rgb[0] = rgb[0];
|
||||
this.rgb[1] = rgb[1];
|
||||
this.rgb[2] = rgb[2];
|
||||
|
||||
this.exportColor(flags);
|
||||
};
|
||||
|
||||
|
||||
this.fromString = function(hex, flags) {
|
||||
var m = hex.match(/^\W*([0-9A-F]{3}([0-9A-F]{3})?)\W*$/i);
|
||||
if(!m) {
|
||||
return false;
|
||||
} else {
|
||||
if(m[1].length === 6) { // 6-char notation
|
||||
this.fromRGB(
|
||||
parseInt(m[1].substr(0,2),16) / 255,
|
||||
parseInt(m[1].substr(2,2),16) / 255,
|
||||
parseInt(m[1].substr(4,2),16) / 255,
|
||||
flags
|
||||
);
|
||||
} else { // 3-char notation
|
||||
this.fromRGB(
|
||||
parseInt(m[1].charAt(0)+m[1].charAt(0),16) / 255,
|
||||
parseInt(m[1].charAt(1)+m[1].charAt(1),16) / 255,
|
||||
parseInt(m[1].charAt(2)+m[1].charAt(2),16) / 255,
|
||||
flags
|
||||
);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
this.toString = function() {
|
||||
return (
|
||||
(0x100 | Math.round(255*this.rgb[0])).toString(16).substr(1) +
|
||||
(0x100 | Math.round(255*this.rgb[1])).toString(16).substr(1) +
|
||||
(0x100 | Math.round(255*this.rgb[2])).toString(16).substr(1)
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
function RGB_HSV(r, g, b) {
|
||||
var n = Math.min(Math.min(r,g),b);
|
||||
var v = Math.max(Math.max(r,g),b);
|
||||
var m = v - n;
|
||||
if(m === 0) { return [ null, 0, v ]; }
|
||||
var h = r===n ? 3+(b-g)/m : (g===n ? 5+(r-b)/m : 1+(g-r)/m);
|
||||
return [ h===6?0:h, m/v, v ];
|
||||
}
|
||||
|
||||
|
||||
function HSV_RGB(h, s, v) {
|
||||
if(h === null) { return [ v, v, v ]; }
|
||||
var i = Math.floor(h);
|
||||
var f = i%2 ? h-i : 1-(h-i);
|
||||
var m = v * (1 - s);
|
||||
var n = v * (1 - s*f);
|
||||
switch(i) {
|
||||
case 6:
|
||||
case 0: return [v,n,m];
|
||||
case 1: return [n,v,m];
|
||||
case 2: return [m,v,n];
|
||||
case 3: return [m,n,v];
|
||||
case 4: return [n,m,v];
|
||||
case 5: return [v,m,n];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function removePicker() {
|
||||
delete jscolor.picker.owner;
|
||||
document.getElementsByTagName('body')[0].removeChild(jscolor.picker.boxB);
|
||||
}
|
||||
|
||||
|
||||
function drawPicker(x, y) {
|
||||
if(!jscolor.picker) {
|
||||
jscolor.picker = {
|
||||
box : document.createElement('div'),
|
||||
boxB : document.createElement('div'),
|
||||
pad : document.createElement('div'),
|
||||
padB : document.createElement('div'),
|
||||
padM : document.createElement('div'),
|
||||
sld : document.createElement('div'),
|
||||
sldB : document.createElement('div'),
|
||||
sldM : document.createElement('div'),
|
||||
btn : document.createElement('div'),
|
||||
btnS : document.createElement('span'),
|
||||
btnT : document.createTextNode(THIS.pickerCloseText)
|
||||
};
|
||||
for(var i=0,segSize=4; i<jscolor.images.sld[1]; i+=segSize) {
|
||||
var seg = document.createElement('div');
|
||||
seg.style.height = segSize+'px';
|
||||
seg.style.fontSize = '1px';
|
||||
seg.style.lineHeight = '0';
|
||||
jscolor.picker.sld.appendChild(seg);
|
||||
}
|
||||
jscolor.picker.sldB.appendChild(jscolor.picker.sld);
|
||||
jscolor.picker.box.appendChild(jscolor.picker.sldB);
|
||||
jscolor.picker.box.appendChild(jscolor.picker.sldM);
|
||||
jscolor.picker.padB.appendChild(jscolor.picker.pad);
|
||||
jscolor.picker.box.appendChild(jscolor.picker.padB);
|
||||
jscolor.picker.box.appendChild(jscolor.picker.padM);
|
||||
jscolor.picker.btnS.appendChild(jscolor.picker.btnT);
|
||||
jscolor.picker.btn.appendChild(jscolor.picker.btnS);
|
||||
jscolor.picker.box.appendChild(jscolor.picker.btn);
|
||||
jscolor.picker.boxB.appendChild(jscolor.picker.box);
|
||||
}
|
||||
|
||||
var p = jscolor.picker;
|
||||
|
||||
// controls interaction
|
||||
p.box.onmouseup =
|
||||
p.box.onmouseout = function() { target.focus(); };
|
||||
p.box.onmousedown = function() { abortBlur=true; };
|
||||
p.box.onmousemove = function(e) {
|
||||
if (holdPad || holdSld) {
|
||||
holdPad && setPad(e);
|
||||
holdSld && setSld(e);
|
||||
if (document.selection) {
|
||||
document.selection.empty();
|
||||
} else if (window.getSelection) {
|
||||
window.getSelection().removeAllRanges();
|
||||
}
|
||||
dispatchImmediateChange();
|
||||
}
|
||||
};
|
||||
p.padM.onmouseup =
|
||||
p.padM.onmouseout = function() { if(holdPad) { holdPad=false; jscolor.fireEvent(valueElement,'change'); } };
|
||||
p.padM.onmousedown = function(e) {
|
||||
// if the slider is at the bottom, move it up
|
||||
switch(modeID) {
|
||||
case 0: if (THIS.hsv[2] === 0) { THIS.fromHSV(null, null, 1.0); }; break;
|
||||
case 1: if (THIS.hsv[1] === 0) { THIS.fromHSV(null, 1.0, null); }; break;
|
||||
}
|
||||
holdPad=true;
|
||||
setPad(e);
|
||||
dispatchImmediateChange();
|
||||
};
|
||||
p.sldM.onmouseup =
|
||||
p.sldM.onmouseout = function() { if(holdSld) { holdSld=false; jscolor.fireEvent(valueElement,'change'); } };
|
||||
p.sldM.onmousedown = function(e) {
|
||||
holdSld=true;
|
||||
setSld(e);
|
||||
dispatchImmediateChange();
|
||||
};
|
||||
|
||||
// picker
|
||||
var dims = getPickerDims(THIS);
|
||||
p.box.style.width = dims[0] + 'px';
|
||||
p.box.style.height = dims[1] + 'px';
|
||||
|
||||
// picker border
|
||||
p.boxB.style.position = 'absolute';
|
||||
p.boxB.style.clear = 'both';
|
||||
p.boxB.style.left = x+'px';
|
||||
p.boxB.style.top = y+'px';
|
||||
p.boxB.style.zIndex = THIS.pickerZIndex;
|
||||
p.boxB.style.border = THIS.pickerBorder+'px solid';
|
||||
p.boxB.style.borderColor = THIS.pickerBorderColor;
|
||||
p.boxB.style.background = THIS.pickerFaceColor;
|
||||
|
||||
// pad image
|
||||
p.pad.style.width = jscolor.images.pad[0]+'px';
|
||||
p.pad.style.height = jscolor.images.pad[1]+'px';
|
||||
|
||||
// pad border
|
||||
p.padB.style.position = 'absolute';
|
||||
p.padB.style.left = THIS.pickerFace+'px';
|
||||
p.padB.style.top = THIS.pickerFace+'px';
|
||||
p.padB.style.border = THIS.pickerInset+'px solid';
|
||||
p.padB.style.borderColor = THIS.pickerInsetColor;
|
||||
|
||||
// pad mouse area
|
||||
p.padM.style.position = 'absolute';
|
||||
p.padM.style.left = '0';
|
||||
p.padM.style.top = '0';
|
||||
p.padM.style.width = THIS.pickerFace + 2*THIS.pickerInset + jscolor.images.pad[0] + jscolor.images.arrow[0] + 'px';
|
||||
p.padM.style.height = p.box.style.height;
|
||||
p.padM.style.cursor = 'crosshair';
|
||||
|
||||
// slider image
|
||||
p.sld.style.overflow = 'hidden';
|
||||
p.sld.style.width = jscolor.images.sld[0]+'px';
|
||||
p.sld.style.height = jscolor.images.sld[1]+'px';
|
||||
|
||||
// slider border
|
||||
p.sldB.style.display = THIS.slider ? 'block' : 'none';
|
||||
p.sldB.style.position = 'absolute';
|
||||
p.sldB.style.right = THIS.pickerFace+'px';
|
||||
p.sldB.style.top = THIS.pickerFace+'px';
|
||||
p.sldB.style.border = THIS.pickerInset+'px solid';
|
||||
p.sldB.style.borderColor = THIS.pickerInsetColor;
|
||||
|
||||
// slider mouse area
|
||||
p.sldM.style.display = THIS.slider ? 'block' : 'none';
|
||||
p.sldM.style.position = 'absolute';
|
||||
p.sldM.style.right = '0';
|
||||
p.sldM.style.top = '0';
|
||||
p.sldM.style.width = jscolor.images.sld[0] + jscolor.images.arrow[0] + THIS.pickerFace + 2*THIS.pickerInset + 'px';
|
||||
p.sldM.style.height = p.box.style.height;
|
||||
try {
|
||||
p.sldM.style.cursor = 'pointer';
|
||||
} catch(eOldIE) {
|
||||
p.sldM.style.cursor = 'hand';
|
||||
}
|
||||
|
||||
// "close" button
|
||||
function setBtnBorder() {
|
||||
var insetColors = THIS.pickerInsetColor.split(/\s+/);
|
||||
var pickerOutsetColor = insetColors.length < 2 ? insetColors[0] : insetColors[1] + ' ' + insetColors[0] + ' ' + insetColors[0] + ' ' + insetColors[1];
|
||||
p.btn.style.borderColor = pickerOutsetColor;
|
||||
}
|
||||
p.btn.style.display = THIS.pickerClosable ? 'block' : 'none';
|
||||
p.btn.style.position = 'absolute';
|
||||
p.btn.style.left = THIS.pickerFace + 'px';
|
||||
p.btn.style.bottom = THIS.pickerFace + 'px';
|
||||
p.btn.style.padding = '0 15px';
|
||||
p.btn.style.height = '18px';
|
||||
p.btn.style.border = THIS.pickerInset + 'px solid';
|
||||
setBtnBorder();
|
||||
p.btn.style.color = THIS.pickerButtonColor;
|
||||
p.btn.style.font = '12px sans-serif';
|
||||
p.btn.style.textAlign = 'center';
|
||||
try {
|
||||
p.btn.style.cursor = 'pointer';
|
||||
} catch(eOldIE) {
|
||||
p.btn.style.cursor = 'hand';
|
||||
}
|
||||
p.btn.onmousedown = function () {
|
||||
THIS.hidePicker();
|
||||
};
|
||||
p.btnS.style.lineHeight = p.btn.style.height;
|
||||
|
||||
// load images in optimal order
|
||||
switch(modeID) {
|
||||
case 0: var padImg = 'hs.png'; break;
|
||||
case 1: var padImg = 'hv.png'; break;
|
||||
}
|
||||
p.padM.style.backgroundImage = "url('"+jscolor.getDir()+"cross.gif')";
|
||||
p.padM.style.backgroundRepeat = "no-repeat";
|
||||
p.sldM.style.backgroundImage = "url('"+jscolor.getDir()+"arrow.gif')";
|
||||
p.sldM.style.backgroundRepeat = "no-repeat";
|
||||
p.pad.style.backgroundImage = "url('"+jscolor.getDir()+padImg+"')";
|
||||
p.pad.style.backgroundRepeat = "no-repeat";
|
||||
p.pad.style.backgroundPosition = "0 0";
|
||||
|
||||
// place pointers
|
||||
redrawPad();
|
||||
redrawSld();
|
||||
|
||||
jscolor.picker.owner = THIS;
|
||||
document.getElementsByTagName('body')[0].appendChild(p.boxB);
|
||||
}
|
||||
|
||||
|
||||
function getPickerDims(o) {
|
||||
var dims = [
|
||||
2*o.pickerInset + 2*o.pickerFace + jscolor.images.pad[0] +
|
||||
(o.slider ? 2*o.pickerInset + 2*jscolor.images.arrow[0] + jscolor.images.sld[0] : 0),
|
||||
o.pickerClosable ?
|
||||
4*o.pickerInset + 3*o.pickerFace + jscolor.images.pad[1] + o.pickerButtonHeight :
|
||||
2*o.pickerInset + 2*o.pickerFace + jscolor.images.pad[1]
|
||||
];
|
||||
return dims;
|
||||
}
|
||||
|
||||
|
||||
function redrawPad() {
|
||||
// redraw the pad pointer
|
||||
switch(modeID) {
|
||||
case 0: var yComponent = 1; break;
|
||||
case 1: var yComponent = 2; break;
|
||||
}
|
||||
var x = Math.round((THIS.hsv[0]/6) * (jscolor.images.pad[0]-1));
|
||||
var y = Math.round((1-THIS.hsv[yComponent]) * (jscolor.images.pad[1]-1));
|
||||
jscolor.picker.padM.style.backgroundPosition =
|
||||
(THIS.pickerFace+THIS.pickerInset+x - Math.floor(jscolor.images.cross[0]/2)) + 'px ' +
|
||||
(THIS.pickerFace+THIS.pickerInset+y - Math.floor(jscolor.images.cross[1]/2)) + 'px';
|
||||
|
||||
// redraw the slider image
|
||||
var seg = jscolor.picker.sld.childNodes;
|
||||
|
||||
switch(modeID) {
|
||||
case 0:
|
||||
var rgb = HSV_RGB(THIS.hsv[0], THIS.hsv[1], 1);
|
||||
for(var i=0; i<seg.length; i+=1) {
|
||||
seg[i].style.backgroundColor = 'rgb('+
|
||||
(rgb[0]*(1-i/seg.length)*100)+'%,'+
|
||||
(rgb[1]*(1-i/seg.length)*100)+'%,'+
|
||||
(rgb[2]*(1-i/seg.length)*100)+'%)';
|
||||
}
|
||||
break;
|
||||
case 1:
|
||||
var rgb, s, c = [ THIS.hsv[2], 0, 0 ];
|
||||
var i = Math.floor(THIS.hsv[0]);
|
||||
var f = i%2 ? THIS.hsv[0]-i : 1-(THIS.hsv[0]-i);
|
||||
switch(i) {
|
||||
case 6:
|
||||
case 0: rgb=[0,1,2]; break;
|
||||
case 1: rgb=[1,0,2]; break;
|
||||
case 2: rgb=[2,0,1]; break;
|
||||
case 3: rgb=[2,1,0]; break;
|
||||
case 4: rgb=[1,2,0]; break;
|
||||
case 5: rgb=[0,2,1]; break;
|
||||
}
|
||||
for(var i=0; i<seg.length; i+=1) {
|
||||
s = 1 - 1/(seg.length-1)*i;
|
||||
c[1] = c[0] * (1 - s*f);
|
||||
c[2] = c[0] * (1 - s);
|
||||
seg[i].style.backgroundColor = 'rgb('+
|
||||
(c[rgb[0]]*100)+'%,'+
|
||||
(c[rgb[1]]*100)+'%,'+
|
||||
(c[rgb[2]]*100)+'%)';
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function redrawSld() {
|
||||
// redraw the slider pointer
|
||||
switch(modeID) {
|
||||
case 0: var yComponent = 2; break;
|
||||
case 1: var yComponent = 1; break;
|
||||
}
|
||||
var y = Math.round((1-THIS.hsv[yComponent]) * (jscolor.images.sld[1]-1));
|
||||
jscolor.picker.sldM.style.backgroundPosition =
|
||||
'0 ' + (THIS.pickerFace+THIS.pickerInset+y - Math.floor(jscolor.images.arrow[1]/2)) + 'px';
|
||||
}
|
||||
|
||||
|
||||
function isPickerOwner() {
|
||||
return jscolor.picker && jscolor.picker.owner === THIS;
|
||||
}
|
||||
|
||||
|
||||
function blurTarget() {
|
||||
if(valueElement === target) {
|
||||
THIS.importColor();
|
||||
}
|
||||
if(THIS.pickerOnfocus) {
|
||||
THIS.hidePicker();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function blurValue() {
|
||||
if(valueElement !== target) {
|
||||
THIS.importColor();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function setPad(e) {
|
||||
var mpos = jscolor.getRelMousePos(e);
|
||||
var x = mpos.x - THIS.pickerFace - THIS.pickerInset;
|
||||
var y = mpos.y - THIS.pickerFace - THIS.pickerInset;
|
||||
switch(modeID) {
|
||||
case 0: THIS.fromHSV(x*(6/(jscolor.images.pad[0]-1)), 1 - y/(jscolor.images.pad[1]-1), null, leaveSld); break;
|
||||
case 1: THIS.fromHSV(x*(6/(jscolor.images.pad[0]-1)), null, 1 - y/(jscolor.images.pad[1]-1), leaveSld); break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function setSld(e) {
|
||||
var mpos = jscolor.getRelMousePos(e);
|
||||
var y = mpos.y - THIS.pickerFace - THIS.pickerInset;
|
||||
switch(modeID) {
|
||||
case 0: THIS.fromHSV(null, null, 1 - y/(jscolor.images.sld[1]-1), leavePad); break;
|
||||
case 1: THIS.fromHSV(null, 1 - y/(jscolor.images.sld[1]-1), null, leavePad); break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function dispatchImmediateChange() {
|
||||
if (THIS.onImmediateChange) {
|
||||
var callback;
|
||||
if (typeof THIS.onImmediateChange === 'string') {
|
||||
callback = new Function (THIS.onImmediateChange);
|
||||
} else {
|
||||
callback = THIS.onImmediateChange;
|
||||
}
|
||||
callback.call(THIS);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
var THIS = this;
|
||||
var modeID = this.pickerMode.toLowerCase()==='hvs' ? 1 : 0;
|
||||
var abortBlur = false;
|
||||
var
|
||||
valueElement = jscolor.fetchElement(this.valueElement),
|
||||
styleElement = jscolor.fetchElement(this.styleElement);
|
||||
var
|
||||
holdPad = false,
|
||||
holdSld = false;
|
||||
var
|
||||
leaveValue = 1<<0,
|
||||
leaveStyle = 1<<1,
|
||||
leavePad = 1<<2,
|
||||
leaveSld = 1<<3;
|
||||
|
||||
// target
|
||||
jscolor.addEvent(target, 'focus', function() {
|
||||
if(THIS.pickerOnfocus) { THIS.showPicker(); }
|
||||
});
|
||||
jscolor.addEvent(target, 'blur', function() {
|
||||
if(!abortBlur) {
|
||||
window.setTimeout(function(){ abortBlur || blurTarget(); abortBlur=false; }, 0);
|
||||
} else {
|
||||
abortBlur = false;
|
||||
}
|
||||
});
|
||||
|
||||
// valueElement
|
||||
if(valueElement) {
|
||||
var updateField = function() {
|
||||
THIS.fromString(valueElement.value, leaveValue);
|
||||
dispatchImmediateChange();
|
||||
};
|
||||
jscolor.addEvent(valueElement, 'keyup', updateField);
|
||||
jscolor.addEvent(valueElement, 'input', updateField);
|
||||
jscolor.addEvent(valueElement, 'blur', blurValue);
|
||||
valueElement.setAttribute('autocomplete', 'off');
|
||||
}
|
||||
|
||||
// styleElement
|
||||
if(styleElement) {
|
||||
styleElement.jscStyle = {
|
||||
backgroundImage : styleElement.style.backgroundImage,
|
||||
backgroundColor : styleElement.style.backgroundColor,
|
||||
color : styleElement.style.color
|
||||
};
|
||||
}
|
||||
|
||||
// require images
|
||||
switch(modeID) {
|
||||
case 0: jscolor.requireImage('hs.png'); break;
|
||||
case 1: jscolor.requireImage('hv.png'); break;
|
||||
}
|
||||
jscolor.requireImage('cross.gif');
|
||||
jscolor.requireImage('arrow.gif');
|
||||
|
||||
this.importColor();
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
jscolor.install();
|
@ -1,118 +0,0 @@
|
||||
(function () {
|
||||
var ns = $.namespace("pskl.model");
|
||||
|
||||
ns.Frame = function (pixels) {
|
||||
this.pixels = pixels;
|
||||
this.previousStates = [this.getPixels()];
|
||||
this.stateIndex = 0;
|
||||
};
|
||||
|
||||
ns.Frame.createEmpty = function (width, height) {
|
||||
var pixels = ns.Frame.createEmptyPixelGrid_(width, height);
|
||||
return new ns.Frame(pixels);
|
||||
};
|
||||
|
||||
ns.Frame.createEmptyPixelGrid_ = function (width, height) {
|
||||
var pixels = []; //new Array(width);
|
||||
for (var columnIndex=0; columnIndex < width; columnIndex++) {
|
||||
var columnArray = [];
|
||||
for(var heightIndex = 0; heightIndex < height; heightIndex++) {
|
||||
columnArray.push(Constants.TRANSPARENT_COLOR);
|
||||
}
|
||||
pixels[columnIndex] = columnArray;
|
||||
}
|
||||
return pixels;
|
||||
};
|
||||
|
||||
ns.Frame.createEmptyFromFrame = function (frame) {
|
||||
return ns.Frame.createEmpty(frame.getWidth(), frame.getHeight());
|
||||
};
|
||||
|
||||
ns.Frame.prototype.clone = function () {
|
||||
return new ns.Frame(this.getPixels());
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns a copy of the pixels used by the frame
|
||||
*/
|
||||
ns.Frame.prototype.getPixels = function () {
|
||||
return this.clonePixels_(this.pixels);
|
||||
};
|
||||
|
||||
/**
|
||||
* Copies the passed pixels into the frame.
|
||||
*/
|
||||
ns.Frame.prototype.setPixels = function (pixels) {
|
||||
this.pixels = this.clonePixels_(pixels);
|
||||
};
|
||||
|
||||
|
||||
|
||||
ns.Frame.prototype.clear = function () {
|
||||
var pixels = ns.Frame.createEmptyPixelGrid_(this.getWidth(), this.getHeight());
|
||||
this.setPixels(pixels);
|
||||
};
|
||||
|
||||
/**
|
||||
* Clone a set of pixels. Should be static utility method
|
||||
* @private
|
||||
*/
|
||||
ns.Frame.prototype.clonePixels_ = function (pixels) {
|
||||
var clonedPixels = [];
|
||||
for (var col = 0 ; col < pixels.length ; col++) {
|
||||
clonedPixels[col] = pixels[col].slice(0 , pixels[col].length);
|
||||
}
|
||||
return clonedPixels;
|
||||
};
|
||||
|
||||
ns.Frame.prototype.serialize = function () {
|
||||
return JSON.stringify(this.pixels);
|
||||
};
|
||||
|
||||
ns.Frame.prototype.setPixel = function (col, row, color) {
|
||||
this.pixels[col][row] = color;
|
||||
};
|
||||
|
||||
ns.Frame.prototype.getPixel = function (col, row) {
|
||||
return this.pixels[col][row];
|
||||
};
|
||||
|
||||
ns.Frame.prototype.getWidth = function () {
|
||||
return this.pixels.length;
|
||||
};
|
||||
|
||||
ns.Frame.prototype.getHeight = function () {
|
||||
return this.pixels[0].length;
|
||||
};
|
||||
|
||||
ns.Frame.prototype.containsPixel = function (col, row) {
|
||||
return col >= 0 && row >= 0 && col < this.pixels.length && row < this.pixels[0].length;
|
||||
};
|
||||
|
||||
ns.Frame.prototype.saveState = function () {
|
||||
// remove all states past current state
|
||||
this.previousStates.length = this.stateIndex + 1;
|
||||
// push new state
|
||||
this.previousStates.push(this.getPixels());
|
||||
// set the stateIndex to latest saved state
|
||||
this.stateIndex = this.previousStates.length - 1;
|
||||
};
|
||||
|
||||
ns.Frame.prototype.loadPreviousState = function () {
|
||||
if (this.stateIndex > 0) {
|
||||
this.stateIndex--;
|
||||
this.setPixels(this.previousStates[this.stateIndex]);
|
||||
}
|
||||
};
|
||||
|
||||
ns.Frame.prototype.loadNextState = function () {
|
||||
if (this.stateIndex < this.previousStates.length - 1) {
|
||||
this.stateIndex++;
|
||||
this.setPixels(this.previousStates[this.stateIndex]);
|
||||
}
|
||||
};
|
||||
|
||||
ns.Frame.prototype.isSameSize = function (otherFrame) {
|
||||
return this.getHeight() == otherFrame.getHeight() && this.getWidth() == otherFrame.getWidth();
|
||||
};
|
||||
})();
|
@ -1,157 +0,0 @@
|
||||
(function () {
|
||||
var ns = $.namespace("pskl.model");
|
||||
ns.FrameSheet = function (height, width) {
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
this.frames = [];
|
||||
this.currentFrameIndex = 0;
|
||||
};
|
||||
|
||||
ns.FrameSheet.prototype.getHeight = function () {
|
||||
return this.height;
|
||||
};
|
||||
|
||||
ns.FrameSheet.prototype.getWidth = function () {
|
||||
return this.width;
|
||||
};
|
||||
|
||||
ns.FrameSheet.prototype.addEmptyFrame = function () {
|
||||
this.addFrame(ns.Frame.createEmpty(this.width, this.height));
|
||||
};
|
||||
|
||||
ns.FrameSheet.prototype.addFrame = function (frame) {
|
||||
this.frames.push(frame);
|
||||
};
|
||||
|
||||
ns.FrameSheet.prototype.getFrameCount = function () {
|
||||
return this.frames.length;
|
||||
};
|
||||
|
||||
ns.FrameSheet.prototype.getCurrentFrame = function () {
|
||||
return this.frames[this.currentFrameIndex];
|
||||
};
|
||||
|
||||
ns.FrameSheet.prototype.setCurrentFrameIndex = function (index) {
|
||||
this.currentFrameIndex = index;
|
||||
$.publish(Events.CURRENT_FRAME_SET, [this.getCurrentFrame()]);
|
||||
$.publish(Events.FRAMESHEET_RESET); // Is it no to overkill to have this here ?
|
||||
};
|
||||
|
||||
ns.FrameSheet.prototype.getUsedColors = function() {
|
||||
var colors = {};
|
||||
for (var frameIndex=0; frameIndex < this.frames.length; frameIndex++) {
|
||||
var frame = this.frames[frameIndex];
|
||||
for (var i = 0, width = frame.getWidth(); i < width ; i++) {
|
||||
var line = frame[i];
|
||||
for (var j = 0, height = frame.getHeight() ; j < height ; j++) {
|
||||
var pixel = frame.getPixel(i, j);
|
||||
colors[pixel] = pixel;
|
||||
}
|
||||
}
|
||||
}
|
||||
return colors;
|
||||
};
|
||||
|
||||
// Could be used to pass around model using long GET param (good enough for simple models) and
|
||||
// do some temporary locastorage
|
||||
ns.FrameSheet.prototype.serialize = function() {
|
||||
var serializedFrames = [];
|
||||
for (var i = 0 ; i < this.frames.length ; i++) {
|
||||
serializedFrames.push(this.frames[i].serialize());
|
||||
}
|
||||
return '[' + serializedFrames.join(",") + ']';
|
||||
//return JSON.stringify(frames);
|
||||
};
|
||||
|
||||
/**
|
||||
* Load a framesheet from a model that might have been persisted in db / localstorage
|
||||
* Overrides existing frames.
|
||||
* @param {String} serialized
|
||||
*/
|
||||
ns.FrameSheet.prototype.deserialize = function (serialized) {
|
||||
try {
|
||||
this.load(JSON.parse(serialized));
|
||||
} catch (e) {
|
||||
throw "Could not load serialized framesheet : " + e.message;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Load a framesheet from a model that might have been persisted in db / localstorage
|
||||
* Overrides existing frames.
|
||||
* @param {String} serialized
|
||||
*/
|
||||
ns.FrameSheet.prototype.load = function (framesheet) {
|
||||
this.frames = [];
|
||||
for (var i = 0 ; i < framesheet.length ; i++) {
|
||||
var frameCfg = framesheet[i];
|
||||
this.addFrame(new ns.Frame(frameCfg));
|
||||
}
|
||||
|
||||
if (this.hasFrameAtIndex(0)) {
|
||||
this.height = this.getFrameByIndex(0).getHeight();
|
||||
this.width = this.getFrameByIndex(0).getWidth();
|
||||
this.setCurrentFrameIndex(0);
|
||||
$.publish(Events.FRAME_SIZE_CHANGED);
|
||||
}
|
||||
|
||||
$.publish(Events.FRAMESHEET_RESET);
|
||||
};
|
||||
|
||||
|
||||
ns.FrameSheet.prototype.hasFrameAtIndex = function(index) {
|
||||
return (index >= 0 && index < this.getFrameCount());
|
||||
};
|
||||
|
||||
ns.FrameSheet.prototype.getFrameByIndex = function(index) {
|
||||
if (isNaN(index)) {
|
||||
throw "Bad argument value for getFrameByIndex method: <" + index + ">";
|
||||
}
|
||||
|
||||
if (!this.hasFrameAtIndex(index)) {
|
||||
throw "Out of bound index for frameSheet object.";
|
||||
}
|
||||
|
||||
return this.frames[index];
|
||||
};
|
||||
|
||||
ns.FrameSheet.prototype.removeFrameByIndex = function(index) {
|
||||
if(!this.hasFrameAtIndex(index)) {
|
||||
throw "Out of bound index for frameSheet object.";
|
||||
}
|
||||
this.frames.splice(index, 1);
|
||||
|
||||
// Current frame index might not be valid anymore
|
||||
if (!this.hasFrameAtIndex(this.currentFrameIndex)) {
|
||||
// if not select last frame available
|
||||
this.setCurrentFrameIndex(this.getFrameCount() - 1);
|
||||
}
|
||||
|
||||
$.publish(Events.FRAMESHEET_RESET);
|
||||
};
|
||||
|
||||
ns.FrameSheet.prototype.duplicateFrameByIndex = function(index) {
|
||||
var frame = this.getFrameByIndex(index);
|
||||
this.frames.splice(index + 1, 0, frame.clone());
|
||||
};
|
||||
|
||||
ns.FrameSheet.prototype.moveFrame = function(originIndex, destinationIndex) {
|
||||
this.frames.splice(destinationIndex, 0, this.frames.splice(originIndex, 1)[0]);
|
||||
};
|
||||
|
||||
ns.FrameSheet.prototype.swapFrames = function(indexFrame1, indexFrame2) {
|
||||
if(isNaN(indexFrame1) || isNaN(indexFrame1) ||
|
||||
(!this.hasFrameAtIndex(indexFrame1) && !this.hasFrameAtIndex(indexFrame2))) {
|
||||
throw "Bad indexes for swapFrames Framesheet function.";
|
||||
}
|
||||
if(indexFrame1 == indexFrame2) {
|
||||
return;
|
||||
}
|
||||
else {
|
||||
var swapFrame = this.frames[indexFrame1];
|
||||
this.frames[indexFrame1] = this.frames[indexFrame2];
|
||||
this.frames[indexFrame2] = swapFrame;
|
||||
}
|
||||
};
|
||||
})();
|
248
js/piskel.js
@ -1,248 +0,0 @@
|
||||
/**
|
||||
* @require Constants
|
||||
* @require Events
|
||||
*/
|
||||
(function () {
|
||||
var ns = $.namespace("pskl");
|
||||
/**
|
||||
* FrameSheetModel instance.
|
||||
*/
|
||||
var frameSheet;
|
||||
/**
|
||||
* Main application controller
|
||||
*/
|
||||
ns.app = {
|
||||
|
||||
init : function () {
|
||||
var frameSize = this.readSizeFromURL_();
|
||||
frameSheet = new pskl.model.FrameSheet(frameSize.height, frameSize.width);
|
||||
frameSheet.addEmptyFrame();
|
||||
|
||||
/**
|
||||
* True when piskel is running in static mode (no back end needed).
|
||||
* When started from APP Engine, appEngineToken_ (Boolean) should be set on window.pskl
|
||||
*/
|
||||
this.isStaticVersion = !pskl.appEngineToken_;
|
||||
|
||||
this.drawingController = new pskl.controller.DrawingController(frameSheet, $('#drawing-canvas-container'));
|
||||
this.animationController = new pskl.controller.AnimatedPreviewController(frameSheet, $('#preview-canvas-container'));
|
||||
this.previewsController = new pskl.controller.PreviewFilmController(frameSheet, $('#preview-list'));
|
||||
this.settingsController = new pskl.controller.SettingsController();
|
||||
|
||||
// To catch the current active frame, the selection manager have to be initialized before
|
||||
// the 'frameSheet.setCurrentFrameIndex(0);' line below.
|
||||
// TODO(vincz): Slice each constructor to have:
|
||||
// - an event(s) listening init
|
||||
// - an event(s) triggering init
|
||||
// All listeners will be hook in a first step, then all event triggering inits will be called
|
||||
// in a second batch.
|
||||
this.selectionManager = new pskl.selection.SelectionManager(frameSheet, this.drawingController.overlayFrame);
|
||||
|
||||
// DO NOT MOVE THIS LINE (see comment above)
|
||||
frameSheet.setCurrentFrameIndex(0);
|
||||
|
||||
this.animationController.init();
|
||||
this.previewsController.init();
|
||||
this.settingsController.init();
|
||||
|
||||
this.historyService = new pskl.service.HistoryService(frameSheet);
|
||||
this.historyService.init();
|
||||
|
||||
this.keyboardEventService = new pskl.service.KeyboardEventService();
|
||||
this.keyboardEventService.init();
|
||||
|
||||
this.notificationController = new pskl.controller.NotificationController();
|
||||
this.notificationController.init();
|
||||
|
||||
this.localStorageService = new pskl.service.LocalStorageService(frameSheet);
|
||||
this.localStorageService.init();
|
||||
|
||||
if (this.isStaticVersion) {
|
||||
var framesheetId = this.readFramesheetIdFromURL_();
|
||||
if (framesheetId) {
|
||||
$.publish(Events.SHOW_NOTIFICATION, [{"content": "Loading animation with id : [" + framesheetId + "]"}]);
|
||||
this.loadFramesheetFromService(framesheetId);
|
||||
} else {
|
||||
this.finishInit();
|
||||
this.localStorageService.displayRestoreNotification();
|
||||
}
|
||||
} else {
|
||||
if (pskl.framesheetData_) {
|
||||
frameSheet.load(pskl.framesheetData_);
|
||||
}
|
||||
this.finishInit();
|
||||
}
|
||||
|
||||
var drawingLoop = new pskl.rendering.DrawingLoop();
|
||||
drawingLoop.addCallback(this.render, this);
|
||||
drawingLoop.start();
|
||||
|
||||
// Init (event-delegated) bootstrap tooltips:
|
||||
$('body').tooltip({
|
||||
selector: '[rel=tooltip]'
|
||||
});
|
||||
},
|
||||
|
||||
render : function (delta) {
|
||||
this.drawingController.render(delta);
|
||||
this.animationController.render(delta);
|
||||
this.previewsController.render(delta);
|
||||
},
|
||||
|
||||
finishInit : function () {
|
||||
var toolController = new pskl.controller.ToolController();
|
||||
toolController.init();
|
||||
|
||||
var paletteController = new pskl.controller.PaletteController();
|
||||
paletteController.init(frameSheet);
|
||||
},
|
||||
|
||||
readSizeFromURL_ : function () {
|
||||
var sizeParam = this.readUrlParameter_("size"), size;
|
||||
// parameter expected as size=64x48 => size=widthxheight
|
||||
var parts = sizeParam.split("x");
|
||||
if (parts && parts.length == 2 && !isNaN(parts[0]) && !isNaN(parts[1])) {
|
||||
var width = parseInt(parts[0], 10),
|
||||
height = parseInt(parts[1], 10);
|
||||
|
||||
size = {
|
||||
height : Math.min(height, Constants.MAX_HEIGHT),
|
||||
width : Math.min(width, Constants.MAX_WIDTH)
|
||||
};
|
||||
} else {
|
||||
size = Constants.DEFAULT_SIZE;
|
||||
}
|
||||
return size;
|
||||
},
|
||||
|
||||
readFramesheetIdFromURL_ : function() {
|
||||
return this.readUrlParameter_("frameId");
|
||||
},
|
||||
|
||||
readUrlParameter_ : function (paramName) {
|
||||
var searchString = window.location.search.substring(1),
|
||||
i, val, params = searchString.split("&");
|
||||
|
||||
for (i=0;i<params.length;i++) {
|
||||
val = params[i].split("=");
|
||||
if (val[0] == paramName) {
|
||||
return unescape(val[1]);
|
||||
}
|
||||
}
|
||||
return "";
|
||||
},
|
||||
|
||||
loadFramesheetFromService : function (frameId) {
|
||||
var xhr = new XMLHttpRequest();
|
||||
xhr.open('GET', Constants.PISKEL_SERVICE_URL + '/get?l=' + frameId, true);
|
||||
xhr.responseType = 'text';
|
||||
|
||||
xhr.onload = function(e) {
|
||||
var res = JSON.parse(this.responseText);
|
||||
frameSheet.load(res.framesheet);
|
||||
pskl.app.animationController.setFPS(res.fps);
|
||||
$.publish(Events.HIDE_NOTIFICATION);
|
||||
pskl.app.finishInit();
|
||||
};
|
||||
|
||||
xhr.onerror = function () {
|
||||
$.publish(Events.HIDE_NOTIFICATION);
|
||||
pskl.app.finishInit();
|
||||
};
|
||||
|
||||
xhr.send();
|
||||
},
|
||||
|
||||
getFirstFrameAsPNGData_ : function () {
|
||||
var tmpSheet = new pskl.model.FrameSheet(frameSheet.getWidth(), frameSheet.getHeight());
|
||||
tmpSheet.addFrame(frameSheet.getFrameByIndex(0));
|
||||
return (new pskl.rendering.SpritesheetRenderer(tmpSheet)).renderAsImageDataSpritesheetPNG();
|
||||
},
|
||||
|
||||
// TODO(julz): Create package ?
|
||||
storeSheet : function (event) {
|
||||
var xhr = new XMLHttpRequest();
|
||||
var formData = new FormData();
|
||||
formData.append('framesheet_content', frameSheet.serialize());
|
||||
formData.append('fps_speed', $('#preview-fps').val());
|
||||
|
||||
if (this.isStaticVersion) {
|
||||
// anonymous save on old app-engine backend
|
||||
xhr.open('POST', Constants.PISKEL_SERVICE_URL + "/store", true);
|
||||
} else {
|
||||
// additional values only used with latest app-engine backend
|
||||
formData.append('name', $('#piskel-name').val());
|
||||
formData.append('frames', frameSheet.getFrameCount());
|
||||
// Get image/png data for first frame
|
||||
|
||||
formData.append('preview', this.getFirstFrameAsPNGData_());
|
||||
|
||||
xhr.open('POST', "save", true);
|
||||
}
|
||||
|
||||
xhr.onload = function(e) {
|
||||
if (this.status == 200) {
|
||||
if (pskl.app.isStaticVersion) {
|
||||
var baseUrl = window.location.href.replace(window.location.search, "");
|
||||
window.location.href = baseUrl + "?frameId=" + this.responseText;
|
||||
} else {
|
||||
$.publish(Events.SHOW_NOTIFICATION, [{"content": "Successfully saved !"}]);
|
||||
}
|
||||
} else {
|
||||
this.onerror(e);
|
||||
}
|
||||
};
|
||||
xhr.onerror = function(e) {
|
||||
$.publish(Events.SHOW_NOTIFICATION, [{"content": "Saving failed ("+this.status+")"}]);
|
||||
};
|
||||
xhr.send(formData);
|
||||
|
||||
if(event) {
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
}
|
||||
return false;
|
||||
},
|
||||
|
||||
uploadToScreenletstore : function(imageData) {
|
||||
var xhr = new XMLHttpRequest();
|
||||
var formData = new FormData();
|
||||
formData.append('data', imageData);
|
||||
xhr.open('POST', "http://screenletstore.appspot.com/__/upload", true);
|
||||
var cloudURL;
|
||||
var that = this;
|
||||
xhr.onload = function(e) {
|
||||
if (this.status == 200) {
|
||||
cloudURL = "http://screenletstore.appspot.com/img/" + this.responseText;
|
||||
that.openWindow(cloudURL);
|
||||
}
|
||||
};
|
||||
|
||||
xhr.send(formData);
|
||||
},
|
||||
|
||||
uploadAsAnimatedGIF : function () {
|
||||
var fps = pskl.app.animationController.fps;
|
||||
var imageData = (new pskl.rendering.SpritesheetRenderer(frameSheet)).renderAsImageDataAnimatedGIF(fps);
|
||||
this.uploadToScreenletstore(imageData);
|
||||
},
|
||||
|
||||
uploadAsSpritesheetPNG : function () {
|
||||
var imageData = (new pskl.rendering.SpritesheetRenderer(frameSheet)).renderAsImageDataSpritesheetPNG();
|
||||
this.uploadToScreenletstore(imageData);
|
||||
},
|
||||
|
||||
openWindow: function(url) {
|
||||
var options = [
|
||||
"dialog=yes", "scrollbars=no", "status=no",
|
||||
"width=" + frameSheet.getWidth() * frameSheet.getFrameCount(),
|
||||
"height=" + frameSheet.getHeight()
|
||||
].join(",");
|
||||
|
||||
window.open(url, "piskel-export", options);
|
||||
}
|
||||
};
|
||||
|
||||
pskl.app.init();
|
||||
|
||||
})();
|
@ -1,36 +0,0 @@
|
||||
(function () {
|
||||
|
||||
var ns = $.namespace("pskl.rendering");
|
||||
ns.CanvasRenderer = function (frame, dpi) {
|
||||
this.frame = frame;
|
||||
this.dpi = dpi;
|
||||
};
|
||||
|
||||
ns.CanvasRenderer.prototype.render = function (frame, dpi) {
|
||||
var canvas = this.createCanvas_();
|
||||
var context = canvas.getContext('2d');
|
||||
for(var col = 0, width = this.frame.getWidth(); col < width; col++) {
|
||||
for(var row = 0, height = this.frame.getHeight(); row < height; row++) {
|
||||
var color = this.frame.getPixel(col, row);
|
||||
this.renderPixel_(color, col, row, context);
|
||||
}
|
||||
}
|
||||
|
||||
return context;
|
||||
};
|
||||
|
||||
ns.CanvasRenderer.prototype.renderPixel_ = function (color, col, row, context) {
|
||||
if(color == Constants.TRANSPARENT_COLOR) {
|
||||
color = "#FFF";
|
||||
}
|
||||
|
||||
context.fillStyle = color;
|
||||
context.fillRect(col * this.dpi, row * this.dpi, this.dpi, this.dpi);
|
||||
};
|
||||
|
||||
ns.CanvasRenderer.prototype.createCanvas_ = function () {
|
||||
var width = this.frame.getWidth() * this.dpi;
|
||||
var height = this.frame.getHeight() * this.dpi;
|
||||
return pskl.CanvasUtils.createCanvas(width, height);
|
||||
};
|
||||
})();
|
@ -1,61 +0,0 @@
|
||||
(function () {
|
||||
var ns = $.namespace("pskl.rendering");
|
||||
|
||||
ns.DrawingLoop = function () {
|
||||
this.requestAnimationFrame = this.getRequestAnimationFrameShim_();
|
||||
this.isRunning = false;
|
||||
this.previousTime = 0;
|
||||
this.callbacks = [];
|
||||
};
|
||||
|
||||
ns.DrawingLoop.prototype.addCallback = function (callback, scope, args) {
|
||||
var callbackObj = {
|
||||
fn : callback,
|
||||
scope : scope,
|
||||
args : args
|
||||
};
|
||||
this.callbacks.push(callbackObj);
|
||||
return callbackObj;
|
||||
};
|
||||
|
||||
ns.DrawingLoop.prototype.removeCallback = function (callbackObj) {
|
||||
var index = this.callbacks.indexOf(callbackObj);
|
||||
if (index != -1) {
|
||||
this.callbacks.splice(index, 1);
|
||||
}
|
||||
};
|
||||
|
||||
ns.DrawingLoop.prototype.start = function () {
|
||||
this.isRunning = true;
|
||||
this.loop_();
|
||||
};
|
||||
|
||||
ns.DrawingLoop.prototype.loop_ = function () {
|
||||
var currentTime = Date.now();
|
||||
var delta = currentTime - this.previousTime;
|
||||
this.executeCallbacks_(delta);
|
||||
this.previousTime = currentTime;
|
||||
this.requestAnimationFrame.call(window, this.loop_.bind(this));
|
||||
};
|
||||
|
||||
ns.DrawingLoop.prototype.executeCallbacks_ = function (deltaTime) {
|
||||
for (var i = 0 ; i < this.callbacks.length ; i++) {
|
||||
var cb = this.callbacks[i];
|
||||
cb.fn.call(cb.scope, deltaTime, cb.args);
|
||||
}
|
||||
};
|
||||
|
||||
ns.DrawingLoop.prototype.stop = function () {
|
||||
this.isRunning = false;
|
||||
};
|
||||
|
||||
ns.DrawingLoop.prototype.getRequestAnimationFrameShim_ = function () {
|
||||
var requestAnimationFrame = window.requestAnimationFrame ||
|
||||
window.mozRequestAnimationFrame ||
|
||||
window.webkitRequestAnimationFrame ||
|
||||
window.msRequestAnimationFrame ||
|
||||
function (callback) { window.setTimeout(callback, 1000/60); };
|
||||
|
||||
return requestAnimationFrame;
|
||||
};
|
||||
})();
|
@ -1,165 +0,0 @@
|
||||
(function () {
|
||||
var ns = $.namespace("pskl.rendering");
|
||||
|
||||
ns.FrameRenderer = function (container, renderingOptions, className) {
|
||||
this.defaultRenderingOptions = {
|
||||
'supportGridRendering' : false
|
||||
};
|
||||
renderingOptions = $.extend(true, {}, this.defaultRenderingOptions, renderingOptions);
|
||||
|
||||
if(container === undefined) {
|
||||
throw 'Bad FrameRenderer initialization. <container> undefined.';
|
||||
}
|
||||
|
||||
if(isNaN(renderingOptions.dpi)) {
|
||||
throw 'Bad FrameRenderer initialization. <dpi> not well defined.';
|
||||
}
|
||||
|
||||
this.container = container;
|
||||
this.dpi = renderingOptions.dpi;
|
||||
this.className = className;
|
||||
this.canvas = null;
|
||||
this.supportGridRendering = renderingOptions.supportGridRendering;
|
||||
|
||||
this.enableGrid(pskl.UserSettings.get(pskl.UserSettings.SHOW_GRID));
|
||||
|
||||
// Flag to know if the config was altered
|
||||
this.canvasConfigDirty = true;
|
||||
this.updateBackgroundClass_(pskl.UserSettings.get(pskl.UserSettings.CANVAS_BACKGROUND));
|
||||
$.subscribe(Events.USER_SETTINGS_CHANGED, $.proxy(this.onUserSettingsChange_, this));
|
||||
};
|
||||
|
||||
ns.FrameRenderer.prototype.init = function (frame) {
|
||||
this.render(frame);
|
||||
this.lastRenderedFrame = frame;
|
||||
};
|
||||
|
||||
ns.FrameRenderer.prototype.updateDPI = function (newDPI) {
|
||||
this.dpi = newDPI;
|
||||
this.canvasConfigDirty = true;
|
||||
};
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
ns.FrameRenderer.prototype.onUserSettingsChange_ = function (evt, settingName, settingValue) {
|
||||
|
||||
if(settingName == pskl.UserSettings.SHOW_GRID) {
|
||||
this.enableGrid(settingValue);
|
||||
}
|
||||
else if (settingName == pskl.UserSettings.CANVAS_BACKGROUND) {
|
||||
this.updateBackgroundClass_(settingValue);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
ns.FrameRenderer.prototype.updateBackgroundClass_ = function (newClass) {
|
||||
var currentClass = this.container.data('current-background-class');
|
||||
if (currentClass) {
|
||||
this.container.removeClass(currentClass);
|
||||
}
|
||||
this.container.addClass(newClass);
|
||||
this.container.data('current-background-class', newClass);
|
||||
};
|
||||
|
||||
ns.FrameRenderer.prototype.enableGrid = function (flag) {
|
||||
this.gridStrokeWidth = (flag && this.supportGridRendering) ? Constants.GRID_STROKE_WIDTH : 0;
|
||||
this.canvasConfigDirty = true;
|
||||
};
|
||||
|
||||
ns.FrameRenderer.prototype.render = function (frame) {
|
||||
this.clear(frame);
|
||||
var context = this.getCanvas_(frame).getContext('2d');
|
||||
for(var col = 0, width = frame.getWidth(); col < width; col++) {
|
||||
for(var row = 0, height = frame.getHeight(); row < height; row++) {
|
||||
var color = frame.getPixel(col, row);
|
||||
this.renderPixel_(color, col, row, context);
|
||||
}
|
||||
}
|
||||
this.lastRenderedFrame = frame;
|
||||
};
|
||||
|
||||
ns.FrameRenderer.prototype.renderPixel_ = function (color, col, row, context) {
|
||||
if(color != Constants.TRANSPARENT_COLOR) {
|
||||
context.fillStyle = color;
|
||||
context.fillRect(this.getFramePos_(col), this.getFramePos_(row), this.dpi, this.dpi);
|
||||
}
|
||||
};
|
||||
|
||||
ns.FrameRenderer.prototype.clear = function (frame) {
|
||||
var canvas = this.getCanvas_(frame);
|
||||
canvas.getContext("2d").clearRect(0, 0, canvas.width, canvas.height);
|
||||
};
|
||||
|
||||
/**
|
||||
* Transform a screen pixel-based coordinate (relative to the top-left corner of the rendered
|
||||
* frame) into a sprite coordinate in column and row.
|
||||
* @public
|
||||
*/
|
||||
ns.FrameRenderer.prototype.convertPixelCoordinatesIntoSpriteCoordinate = function(coords) {
|
||||
var cellSize = this.dpi + this.gridStrokeWidth;
|
||||
return {
|
||||
"col" : (coords.x - coords.x % cellSize) / cellSize,
|
||||
"row" : (coords.y - coords.y % cellSize) / cellSize
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
ns.FrameRenderer.prototype.getFramePos_ = function(index) {
|
||||
return index * this.dpi + ((index - 1) * this.gridStrokeWidth);
|
||||
};
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
ns.FrameRenderer.prototype.drawGrid_ = function(canvas, width, height, col, row) {
|
||||
var ctx = canvas.getContext("2d");
|
||||
ctx.lineWidth = Constants.GRID_STROKE_WIDTH;
|
||||
ctx.strokeStyle = Constants.GRID_STROKE_COLOR;
|
||||
for(var c=1; c < col; c++) {
|
||||
ctx.moveTo(this.getFramePos_(c), 0);
|
||||
ctx.lineTo(this.getFramePos_(c), height);
|
||||
ctx.stroke();
|
||||
}
|
||||
|
||||
for(var r=1; r < row; r++) {
|
||||
ctx.moveTo(0, this.getFramePos_(r));
|
||||
ctx.lineTo(width, this.getFramePos_(r));
|
||||
ctx.stroke();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
ns.FrameRenderer.prototype.getCanvas_ = function (frame) {
|
||||
if(this.canvasConfigDirty) {
|
||||
$(this.canvas).remove();
|
||||
|
||||
var col = frame.getWidth(),
|
||||
row = frame.getHeight();
|
||||
|
||||
var pixelWidth = col * this.dpi + this.gridStrokeWidth * (col - 1);
|
||||
var pixelHeight = row * this.dpi + this.gridStrokeWidth * (row - 1);
|
||||
var classes = ['canvas'];
|
||||
if (this.className) {
|
||||
classes.push(this.className);
|
||||
}
|
||||
var canvas = pskl.CanvasUtils.createCanvas(pixelWidth, pixelHeight, classes);
|
||||
|
||||
this.container.append(canvas);
|
||||
|
||||
if(this.gridStrokeWidth > 0) {
|
||||
this.drawGrid_(canvas, pixelWidth, pixelHeight, col, row);
|
||||
}
|
||||
|
||||
this.canvas = canvas;
|
||||
this.canvasConfigDirty = false;
|
||||
}
|
||||
return this.canvas;
|
||||
};
|
||||
})();
|
@ -1,63 +0,0 @@
|
||||
(function () {
|
||||
|
||||
var ns = $.namespace("pskl.rendering");
|
||||
|
||||
ns.SpritesheetRenderer = function (framesheet) {
|
||||
this.framesheet = framesheet;
|
||||
};
|
||||
|
||||
ns.SpritesheetRenderer.prototype.renderAsImageDataSpritesheetPNG = function () {
|
||||
var canvas = this.createCanvas_();
|
||||
for (var i = 0 ; i < this.framesheet.getFrameCount() ; i++) {
|
||||
var frame = this.framesheet.getFrameByIndex(i);
|
||||
this.drawFrameInCanvas_(frame, canvas, i * this.framesheet.getWidth(), 0);
|
||||
}
|
||||
return canvas.toDataURL("image/png");
|
||||
};
|
||||
|
||||
ns.SpritesheetRenderer.prototype.renderAsImageDataAnimatedGIF = function (fps) {
|
||||
var encoder = new GIFEncoder(), dpi = 10;
|
||||
encoder.setRepeat(0);
|
||||
encoder.setDelay(1000/fps);
|
||||
|
||||
encoder.start();
|
||||
encoder.setSize(this.framesheet.getWidth() * dpi, this.framesheet.getHeight() * dpi);
|
||||
for (var i = 0 ; i < this.framesheet.frames.length ; i++) {
|
||||
var frame = this.framesheet.frames[i];
|
||||
var renderer = new pskl.rendering.CanvasRenderer(frame, dpi);
|
||||
encoder.addFrame(renderer.render());
|
||||
}
|
||||
encoder.finish();
|
||||
|
||||
var imageData = 'data:image/gif;base64,' + encode64(encoder.stream().getData());
|
||||
return imageData;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* TODO(juliandescottes): Mutualize with code already present in FrameRenderer
|
||||
*/
|
||||
ns.SpritesheetRenderer.prototype.drawFrameInCanvas_ = function (frame, canvas, offsetWidth, offsetHeight) {
|
||||
var context = canvas.getContext('2d');
|
||||
for(var col = 0, width = frame.getWidth(); col < width; col++) {
|
||||
for(var row = 0, height = frame.getHeight(); row < height; row++) {
|
||||
var color = frame.getPixel(col, row);
|
||||
if(color != Constants.TRANSPARENT_COLOR) {
|
||||
context.fillStyle = color;
|
||||
context.fillRect(col + offsetWidth, row + offsetHeight, 1, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
ns.SpritesheetRenderer.prototype.createCanvas_ = function () {
|
||||
var frameCount = this.framesheet.getFrameCount();
|
||||
if (frameCount > 0){
|
||||
var width = frameCount * this.framesheet.getWidth();
|
||||
var height = this.framesheet.getHeight();
|
||||
return pskl.CanvasUtils.createCanvas(width, height);
|
||||
} else {
|
||||
throw "Cannot render empty Spritesheet";
|
||||
}
|
||||
};
|
||||
})();
|
@ -1,34 +0,0 @@
|
||||
(function () {
|
||||
var ns = $.namespace("pskl.selection");
|
||||
|
||||
ns.BaseSelection = function () {
|
||||
this.reset();
|
||||
};
|
||||
|
||||
ns.BaseSelection.prototype.reset = function () {
|
||||
this.pixels = [];
|
||||
this.hasPastedContent = false;
|
||||
};
|
||||
|
||||
ns.BaseSelection.prototype.move = function (colDiff, rowDiff) {
|
||||
var movedPixel, movedPixels = [];
|
||||
|
||||
for(var i=0, l=this.pixels.length; i<l; i++) {
|
||||
movedPixel = this.pixels[i];
|
||||
movedPixel.col += colDiff;
|
||||
movedPixel.row += rowDiff;
|
||||
movedPixels.push(movedPixel);
|
||||
}
|
||||
this.pixels = movedPixels;
|
||||
};
|
||||
|
||||
ns.BaseSelection.prototype.fillSelectionFromFrame = function (targetFrame) {
|
||||
var pixelWithCopiedColor;
|
||||
for(var i=0, l=this.pixels.length; i<l; i++) {
|
||||
pixelWithCopiedColor = this.pixels[i];
|
||||
pixelWithCopiedColor.copiedColor =
|
||||
targetFrame.getPixel(pixelWithCopiedColor.col, pixelWithCopiedColor.row);
|
||||
}
|
||||
this.hasPastedContent = true;
|
||||
};
|
||||
})();
|
@ -1,9 +0,0 @@
|
||||
(function () {
|
||||
var ns = $.namespace("pskl.selection");
|
||||
|
||||
ns.RectangularSelection = function (x0, y0, x1, y1) {
|
||||
this.pixels = pskl.PixelUtils.getRectanglePixels(x0, y0, x1, y1);
|
||||
};
|
||||
|
||||
pskl.utils.inherit(ns.RectangularSelection, ns.BaseSelection);
|
||||
})();
|
@ -1,130 +0,0 @@
|
||||
(function () {
|
||||
var ns = $.namespace("pskl.selection");
|
||||
|
||||
|
||||
ns.SelectionManager = function (framesheet, overlayFrame) {
|
||||
|
||||
this.framesheet = framesheet;
|
||||
this.overlayFrame = overlayFrame;
|
||||
|
||||
this.currentSelection = null;
|
||||
|
||||
$.subscribe(Events.SELECTION_CREATED, $.proxy(this.onSelectionCreated_, this));
|
||||
$.subscribe(Events.SELECTION_DISMISSED, $.proxy(this.onSelectionDismissed_, this));
|
||||
$.subscribe(Events.SELECTION_MOVE_REQUEST, $.proxy(this.onSelectionMoved_, this));
|
||||
|
||||
$.subscribe(Events.PASTE, $.proxy(this.onPaste_, this));
|
||||
$.subscribe(Events.COPY, $.proxy(this.onCopy_, this));
|
||||
$.subscribe(Events.CUT, $.proxy(this.onCut_, this));
|
||||
|
||||
$.subscribe(Events.TOOL_SELECTED, $.proxy(this.onToolSelected_, this));
|
||||
};
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
ns.SelectionManager.prototype.cleanSelection_ = function(selection) {
|
||||
if(this.currentSelection) {
|
||||
this.currentSelection.reset();
|
||||
}
|
||||
this.overlayFrame.clear();
|
||||
};
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
ns.SelectionManager.prototype.onToolSelected_ = function(evt, tool) {
|
||||
var isSelectionTool = tool instanceof pskl.drawingtools.BaseSelect;
|
||||
if(!isSelectionTool) {
|
||||
this.cleanSelection_();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
ns.SelectionManager.prototype.onSelectionDismissed_ = function(evt) {
|
||||
this.cleanSelection_();
|
||||
};
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
ns.SelectionManager.prototype.onCut_ = function(evt) {
|
||||
if(this.currentSelection) {
|
||||
// Put cut target into the selection:
|
||||
this.currentSelection.fillSelectionFromFrame(this.framesheet.getCurrentFrame());
|
||||
|
||||
var pixels = this.currentSelection.pixels;
|
||||
var currentFrame = this.framesheet.getCurrentFrame();
|
||||
for(var i=0, l=pixels.length; i<l; i++) {
|
||||
try {
|
||||
currentFrame.setPixel(pixels[i].col, pixels[i].row, Constants.TRANSPARENT_COLOR);
|
||||
}
|
||||
catch(e) {
|
||||
// Catchng out of frame's bound pixels without testing
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
throw "Bad state for CUT callback in SelectionManager";
|
||||
}
|
||||
};
|
||||
|
||||
ns.SelectionManager.prototype.onPaste_ = function(evt) {
|
||||
if(this.currentSelection && this.currentSelection.hasPastedContent) {
|
||||
var pixels = this.currentSelection.pixels;
|
||||
var currentFrame = this.framesheet.getCurrentFrame();
|
||||
for(var i=0, l=pixels.length; i<l; i++) {
|
||||
try {
|
||||
currentFrame.setPixel(
|
||||
pixels[i].col, pixels[i].row,
|
||||
pixels[i].copiedColor);
|
||||
}
|
||||
catch(e) {
|
||||
// Catchng out of frame's bound pixels without testing
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
ns.SelectionManager.prototype.onCopy_ = function(evt) {
|
||||
if(this.currentSelection && this.framesheet.getCurrentFrame()) {
|
||||
this.currentSelection.fillSelectionFromFrame(this.framesheet.getCurrentFrame());
|
||||
}
|
||||
else {
|
||||
throw "Bad state for CUT callback in SelectionManager";
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
ns.SelectionManager.prototype.onSelectionCreated_ = function(evt, selection) {
|
||||
if(selection) {
|
||||
this.currentSelection = selection;
|
||||
var pixels = selection.pixels;
|
||||
for(var i=0, l=pixels.length; i<l; i++) {
|
||||
this.overlayFrame.setPixel(pixels[i].col, pixels[i].row, Constants.SELECTION_TRANSPARENT_COLOR);
|
||||
}
|
||||
}
|
||||
else {
|
||||
throw "No selection set in SelectionManager";
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
ns.SelectionManager.prototype.onSelectionMoved_ = function(evt, colDiff, rowDiff) {
|
||||
if(this.currentSelection) {
|
||||
this.currentSelection.move(colDiff, rowDiff);
|
||||
}
|
||||
else {
|
||||
throw "Bad state: No currentSelection set when trying to move it in SelectionManager";
|
||||
}
|
||||
};
|
||||
})();
|
@ -1,9 +0,0 @@
|
||||
(function () {
|
||||
var ns = $.namespace("pskl.selection");
|
||||
|
||||
ns.ShapeSelection = function (pixels) {
|
||||
this.pixels = pixels;
|
||||
};
|
||||
|
||||
pskl.utils.inherit(ns.ShapeSelection, ns.BaseSelection);
|
||||
})();
|
@ -1,28 +0,0 @@
|
||||
(function () {
|
||||
var ns = $.namespace("pskl.service");
|
||||
ns.HistoryService = function (framesheet) {
|
||||
this.framesheet = framesheet;
|
||||
};
|
||||
|
||||
ns.HistoryService.prototype.init = function () {
|
||||
|
||||
$.subscribe(Events.TOOL_RELEASED, this.saveState.bind(this));
|
||||
$.subscribe(Events.UNDO, this.undo.bind(this));
|
||||
$.subscribe(Events.REDO, this.redo.bind(this));
|
||||
};
|
||||
|
||||
ns.HistoryService.prototype.saveState = function () {
|
||||
this.framesheet.getCurrentFrame().saveState();
|
||||
};
|
||||
|
||||
ns.HistoryService.prototype.undo = function () {
|
||||
this.framesheet.getCurrentFrame().loadPreviousState();
|
||||
$.publish(Events.FRAMESHEET_RESET);
|
||||
};
|
||||
|
||||
ns.HistoryService.prototype.redo = function () {
|
||||
this.framesheet.getCurrentFrame().loadNextState();
|
||||
$.publish(Events.FRAMESHEET_RESET);
|
||||
};
|
||||
|
||||
})();
|
@ -1,64 +0,0 @@
|
||||
(function () {
|
||||
var ns = $.namespace("pskl.service");
|
||||
|
||||
ns.KeyboardEventService = function () {};
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
ns.KeyboardEventService.prototype.KeyboardActions_ = {
|
||||
|
||||
"ctrl" : {
|
||||
"z" : Events.UNDO,
|
||||
"y" : Events.REDO,
|
||||
"x" : Events.CUT,
|
||||
"c" : Events.COPY,
|
||||
"v" : Events.PASTE
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
ns.KeyboardEventService.prototype.CharCodeToKeyCodeMap_ = {
|
||||
|
||||
90 : "z",
|
||||
89 : "y",
|
||||
88 : "x",
|
||||
67 : "c",
|
||||
86 : "v"
|
||||
};
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
ns.KeyboardEventService.prototype.onKeyUp_ = function(evt) {
|
||||
var isMac = false;
|
||||
if (navigator.appVersion.indexOf("Mac")!=-1) {
|
||||
// Welcome in mac world where vowels are consons and meta used instead of ctrl:
|
||||
isMac = true;
|
||||
}
|
||||
|
||||
if (isMac ? evt.metaKey : evt.ctrlKey) {
|
||||
// Get key pressed:
|
||||
var letter = this.CharCodeToKeyCodeMap_[evt.which];
|
||||
if(letter) {
|
||||
var eventToTrigger = this.KeyboardActions_.ctrl[letter];
|
||||
if(eventToTrigger) {
|
||||
$.publish(eventToTrigger);
|
||||
|
||||
evt.preventDefault();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
ns.KeyboardEventService.prototype.init = function() {
|
||||
$(document.body).keydown($.proxy(this.onKeyUp_, this));
|
||||
};
|
||||
|
||||
})();
|
@ -1,88 +0,0 @@
|
||||
(function () {
|
||||
var ns = $.namespace("pskl.service");
|
||||
|
||||
ns.LocalStorageService = function (framesheet_) {
|
||||
|
||||
if(framesheet_ === undefined) {
|
||||
throw "Bad LocalStorageService initialization: <undefined frameSheet>";
|
||||
}
|
||||
this.framesheet = framesheet_;
|
||||
this.localStorageThrottler_ = null;
|
||||
};
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
ns.LocalStorageService.prototype.persistToLocalStorageRequest_ = function () {
|
||||
// Persist to localStorage when drawing. We throttle localStorage accesses
|
||||
// for high frequency drawing (eg mousemove).
|
||||
if(this.localStorageThrottler_ !== null) {
|
||||
window.clearTimeout(this.localStorageThrottler_);
|
||||
}
|
||||
this.localStorageThrottler_ = window.setTimeout($.proxy(function() {
|
||||
this.persistToLocalStorage_();
|
||||
this.localStorageThrottler_ = null;
|
||||
}, this), 1000);
|
||||
};
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
ns.LocalStorageService.prototype.persistToLocalStorage_ = function() {
|
||||
|
||||
console.log('[LocalStorage service]: Snapshot stored');
|
||||
window.localStorage.snapShot = this.framesheet.serialize();
|
||||
};
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
ns.LocalStorageService.prototype.restoreFromLocalStorage_ = function() {
|
||||
|
||||
this.framesheet.deserialize(window.localStorage.snapShot);
|
||||
this.framesheet.setCurrentFrameIndex(0);
|
||||
};
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
ns.LocalStorageService.prototype.cleanLocalStorage_ = function() {
|
||||
console.log('[LocalStorage service]: Snapshot removed');
|
||||
delete window.localStorage.snapShot;
|
||||
};
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
ns.LocalStorageService.prototype.init = function(framesheet_) {
|
||||
$.subscribe(Events.LOCALSTORAGE_REQUEST, $.proxy(this.persistToLocalStorageRequest_, this));
|
||||
};
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
ns.LocalStorageService.prototype.displayRestoreNotification = function() {
|
||||
if(window.localStorage && window.localStorage.snapShot) {
|
||||
var reloadLink = "<a href='#' class='localstorage-restore onclick='pskl.app.restoreFromLocalStorage()'>reload</a>";
|
||||
var discardLink = "<a href='#' class='localstorage-discard' onclick='pskl.app.cleanLocalStorage()'>discard</a>";
|
||||
var content = "Non saved version found. " + reloadLink + " or " + discardLink;
|
||||
|
||||
$.publish(Events.SHOW_NOTIFICATION, [{
|
||||
"content": content,
|
||||
"behavior": $.proxy(function(rootNode) {
|
||||
rootNode = $(rootNode);
|
||||
rootNode.click($.proxy(function(evt) {
|
||||
var target = $(evt.target);
|
||||
if(target.hasClass("localstorage-restore")) {
|
||||
this.restoreFromLocalStorage_();
|
||||
}
|
||||
else if (target.hasClass("localstorage-discard")) {
|
||||
this.cleanLocalStorage_();
|
||||
}
|
||||
$.publish(Events.HIDE_NOTIFICATION);
|
||||
}, this));
|
||||
}, this)
|
||||
}]);
|
||||
}
|
||||
};
|
||||
})();
|
@ -1,22 +0,0 @@
|
||||
(function () {
|
||||
var ns = $.namespace("pskl");
|
||||
|
||||
ns.CanvasUtils = {
|
||||
createCanvas : function (width, height, classList) {
|
||||
var canvas = document.createElement("canvas");
|
||||
canvas.setAttribute("width", width);
|
||||
canvas.setAttribute("height", height);
|
||||
|
||||
if (typeof classList == "string") {
|
||||
classList = [classList];
|
||||
}
|
||||
if (Array.isArray(classList)) {
|
||||
for (var i = 0 ; i < classList.length ; i++) {
|
||||
canvas.classList.add(classList[i]);
|
||||
}
|
||||
}
|
||||
|
||||
return canvas;
|
||||
}
|
||||
};
|
||||
})();
|
@ -1,175 +0,0 @@
|
||||
(function () {
|
||||
var ns = $.namespace("pskl");
|
||||
|
||||
ns.PixelUtils = {
|
||||
|
||||
getRectanglePixels : function (x0, y0, x1, y1) {
|
||||
var rectangle = this.getOrderedRectangleCoordinates(x0, y0, x1, y1);
|
||||
var pixels = [];
|
||||
|
||||
for(var x = rectangle.x0; x <= rectangle.x1; x++) {
|
||||
for(var y = rectangle.y0; y <= rectangle.y1; y++) {
|
||||
pixels.push({"col": x, "row": y});
|
||||
}
|
||||
}
|
||||
|
||||
return pixels;
|
||||
},
|
||||
|
||||
getBoundRectanglePixels : function (x0, y0, x1, y1) {
|
||||
var rectangle = this.getOrderedRectangleCoordinates(x0, y0, x1, y1);
|
||||
var pixels = [];
|
||||
// Creating horizontal sides of the rectangle:
|
||||
for(var x = rectangle.x0; x <= rectangle.x1; x++) {
|
||||
pixels.push({"col": x, "row": rectangle.y0});
|
||||
pixels.push({"col": x, "row": rectangle.y1});
|
||||
}
|
||||
|
||||
// Creating vertical sides of the rectangle:
|
||||
for(var y = rectangle.y0; y <= rectangle.y1; y++) {
|
||||
pixels.push({"col": rectangle.x0, "row": y});
|
||||
pixels.push({"col": rectangle.x1, "row": y});
|
||||
}
|
||||
|
||||
return pixels;
|
||||
},
|
||||
|
||||
/**
|
||||
* Return an object of ordered rectangle coordinate.
|
||||
* In returned object {x0, y0} => top left corner - {x1, y1} => bottom right corner
|
||||
* @private
|
||||
*/
|
||||
getOrderedRectangleCoordinates : function (x0, y0, x1, y1) {
|
||||
return {
|
||||
x0 : Math.min(x0, x1), y0 : Math.min(y0, y1),
|
||||
x1 : Math.max(x0, x1), y1 : Math.max(y0, y1),
|
||||
};
|
||||
},
|
||||
|
||||
/**
|
||||
* Return the list of pixels that would have been filled by a paintbucket tool applied
|
||||
* on pixel at coordinate (x,y).
|
||||
* This function is not altering the Frame object argument.
|
||||
*
|
||||
* @param frame pskl.model.Frame The frame target in which we want to paintbucket
|
||||
* @param col number Column coordinate in the frame
|
||||
* @param row number Row coordinate in the frame
|
||||
*
|
||||
* @return an array of the pixel coordinates paint with the replacement color
|
||||
*/
|
||||
getSimilarConnectedPixelsFromFrame: function(frame, col, row) {
|
||||
// To get the list of connected (eg the same color) pixels, we will use the paintbucket algorithm
|
||||
// in a fake cloned frame. The returned pixels by the paintbucket algo are the painted pixels
|
||||
// and are as well connected.
|
||||
var fakeFrame = frame.clone(); // We just want to
|
||||
var fakeFillColor = "sdfsdfsdf"; // A fake color that will never match a real color.
|
||||
var paintedPixels = this.paintSimilarConnectedPixelsFromFrame(fakeFrame, col, row, fakeFillColor);
|
||||
|
||||
return paintedPixels;
|
||||
},
|
||||
|
||||
/**
|
||||
* Apply the paintbucket tool in a frame at the (col, row) initial position
|
||||
* with the replacement color.
|
||||
*
|
||||
* @param frame pskl.model.Frame The frame target in which we want to paintbucket
|
||||
* @param col number Column coordinate in the frame
|
||||
* @param row number Row coordinate in the frame
|
||||
* @param replacementColor string Hexadecimal color used to fill the area
|
||||
*
|
||||
* @return an array of the pixel coordinates paint with the replacement color
|
||||
*/
|
||||
paintSimilarConnectedPixelsFromFrame: function(frame, col, row, replacementColor) {
|
||||
/**
|
||||
* Queue linear Flood-fill (node, target-color, replacement-color):
|
||||
* 1. Set Q to the empty queue.
|
||||
* 2. If the color of node is not equal to target-color, return.
|
||||
* 3. Add node to Q.
|
||||
* 4. For each element n of Q:
|
||||
* 5. If the color of n is equal to target-color:
|
||||
* 6. Set w and e equal to n.
|
||||
* 7. Move w to the west until the color of the node to the west of w no longer matches target-color.
|
||||
* 8. Move e to the east until the color of the node to the east of e no longer matches target-color.
|
||||
* 9. Set the color of nodes between w and e to replacement-color.
|
||||
* 10. For each node n between w and e:
|
||||
* 11. If the color of the node to the north of n is target-color, add that node to Q.
|
||||
* 12. If the color of the node to the south of n is target-color, add that node to Q.
|
||||
* 13. Continue looping until Q is exhausted.
|
||||
* 14. Return.
|
||||
*/
|
||||
var paintedPixels = [];
|
||||
var queue = [];
|
||||
var dy = [-1, 0, 1, 0];
|
||||
var dx = [0, 1, 0, -1];
|
||||
var targetColor;
|
||||
try {
|
||||
targetColor = frame.getPixel(col, row);
|
||||
} catch(e) {
|
||||
// Frame out of bound exception.
|
||||
}
|
||||
|
||||
if(targetColor == replacementColor) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
queue.push({"col": col, "row": row});
|
||||
var loopCount = 0;
|
||||
var cellCount = frame.getWidth() * frame.getHeight();
|
||||
while(queue.length > 0) {
|
||||
loopCount ++;
|
||||
|
||||
var currentItem = queue.pop();
|
||||
frame.setPixel(currentItem.col, currentItem.row, replacementColor);
|
||||
paintedPixels.push({"col": currentItem.col, "row": currentItem.row });
|
||||
|
||||
for (var i = 0; i < 4; i++) {
|
||||
var nextCol = currentItem.col + dx[i];
|
||||
var nextRow = currentItem.row + dy[i];
|
||||
try {
|
||||
if (frame.containsPixel(nextCol, nextRow) && frame.getPixel(nextCol, nextRow) == targetColor) {
|
||||
queue.push({"col": nextCol, "row": nextRow });
|
||||
}
|
||||
} catch(e) {
|
||||
// Frame out of bound exception.
|
||||
}
|
||||
}
|
||||
|
||||
// Security loop breaker:
|
||||
if(loopCount > 10 * cellCount) {
|
||||
console.log("loop breaker called");
|
||||
break;
|
||||
}
|
||||
}
|
||||
return paintedPixels;
|
||||
},
|
||||
|
||||
/**
|
||||
* Calculate and return the maximal DPI to display a picture in a given container.
|
||||
*
|
||||
* @param container jQueryObject Container where the picture should be displayed
|
||||
* @param number pictureHeight height in pixels of the picture to display
|
||||
* @param number pictureWidth width in pixels of the picture to display
|
||||
* @return number maximal dpi
|
||||
*/
|
||||
calculateDPIForContainer : function (container, pictureHeight, pictureWidth) {
|
||||
return this.calculateDPI(container.height(), container.width(), pictureHeight, pictureWidth);
|
||||
},
|
||||
|
||||
/**
|
||||
* Calculate and return the maximal DPI to display a picture for a given height and width.
|
||||
*
|
||||
* @param height number Height available to display the picture
|
||||
* @param width number Width available to display the picture
|
||||
* @param number pictureHeight height in pixels of the picture to display
|
||||
* @param number pictureWidth width in pixels of the picture to display
|
||||
* @return number maximal dpi
|
||||
*/
|
||||
calculateDPI : function (height, width, pictureHeight, pictureWidth) {
|
||||
var heightBoundDpi = Math.floor(height / pictureHeight),
|
||||
widthBoundDpi = Math.floor(width / pictureWidth);
|
||||
|
||||
return Math.min(heightBoundDpi, widthBoundDpi);
|
||||
},
|
||||
};
|
||||
})();
|
@ -1,76 +0,0 @@
|
||||
(function () {
|
||||
var ns = $.namespace("pskl");
|
||||
|
||||
ns.UserSettings = {
|
||||
|
||||
SHOW_GRID : 'SHOW_GRID',
|
||||
CANVAS_BACKGROUND : 'CANVAS_BACKGROUND',
|
||||
|
||||
KEY_TO_DEFAULT_VALUE_MAP_ : {
|
||||
'SHOW_GRID' : false,
|
||||
'CANVAS_BACKGROUND' : 'medium-canvas-background'
|
||||
},
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
cache_ : {},
|
||||
|
||||
/**
|
||||
* Static method to access a user defined settings value ot its default
|
||||
* value if not defined yet.
|
||||
*/
|
||||
get : function (key) {
|
||||
this.checkKeyValidity_(key);
|
||||
if (!(key in this.cache_)) {
|
||||
this.cache_[key] =
|
||||
this.readFromLocalStorage_(key) || this.readFromDefaults_(key);
|
||||
}
|
||||
return this.cache_[key];
|
||||
},
|
||||
|
||||
set : function (key, value) {
|
||||
this.checkKeyValidity_(key);
|
||||
this.cache_[key] = value;
|
||||
this.writeToLocalStorage_(key, value);
|
||||
|
||||
$.publish(Events.USER_SETTINGS_CHANGED, [key, value]);
|
||||
},
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
readFromLocalStorage_ : function(key) {
|
||||
var value = window.localStorage[key];
|
||||
if (typeof value != "undefined") {
|
||||
value = JSON.parse(value);
|
||||
}
|
||||
return value;
|
||||
},
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
writeToLocalStorage_ : function(key, value) {
|
||||
// TODO(grosbouddha): Catch storage exception here.
|
||||
window.localStorage[key] = JSON.stringify(value);
|
||||
},
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
readFromDefaults_ : function (key) {
|
||||
return this.KEY_TO_DEFAULT_VALUE_MAP_[key];
|
||||
},
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
checkKeyValidity_ : function(key) {
|
||||
if(!(key in this.KEY_TO_DEFAULT_VALUE_MAP_)) {
|
||||
// TODO(grosbouddha): Define error catching strategy and throw exception from here.
|
||||
console.log("UserSettings key <"+ key +"> not find in supported keys.");
|
||||
}
|
||||
}
|
||||
};
|
||||
})();
|
@ -1,36 +0,0 @@
|
||||
jQuery.namespace = function() {
|
||||
var a=arguments, o=null, i, j, d;
|
||||
for (i=0; i<a.length; i=i+1) {
|
||||
d=a[i].split(".");
|
||||
o=window;
|
||||
for (j=0; j<d.length; j=j+1) {
|
||||
o[d[j]]=o[d[j]] || {};
|
||||
o=o[d[j]];
|
||||
}
|
||||
}
|
||||
return o;
|
||||
};
|
||||
|
||||
/*
|
||||
* @provide pskl.utils
|
||||
*
|
||||
* @require Constants
|
||||
*/
|
||||
(function() { // namespace: pskl.utils
|
||||
|
||||
var ns = $.namespace("pskl.utils");
|
||||
|
||||
ns.rgbToHex = function(r, g, b) {
|
||||
if (r > 255 || g > 255 || b > 255)
|
||||
throw "Invalid color component";
|
||||
return ((r << 16) | (g << 8) | b).toString(16);
|
||||
};
|
||||
|
||||
ns.inherit = function(extendedObject, inheritFrom) {
|
||||
extendedObject.prototype = Object.create(inheritFrom.prototype);
|
||||
extendedObject.prototype.constructor = extendedObject;
|
||||
extendedObject.prototype.superclass = inheritFrom.prototype;
|
||||
};
|
||||
|
||||
})();
|
||||
|
69
karma.conf.js
Normal file
@ -0,0 +1,69 @@
|
||||
// Karma configuration
|
||||
// Generated on Tue Jul 22 2014 23:49:26 GMT+0200 (Romance Daylight Time)
|
||||
|
||||
module.exports = function(config) {
|
||||
|
||||
var mapToSrcFolder = function (path) {return ['src', path].join('/');};
|
||||
|
||||
var piskelScripts = require('./src/piskel-script-list.js').scripts.map(mapToSrcFolder);
|
||||
piskelScripts.push('test/js/testutils/**/*.js');
|
||||
piskelScripts.push('test/js/**/*.js');
|
||||
|
||||
config.set({
|
||||
|
||||
// base path that will be used to resolve all patterns (eg. files, exclude)
|
||||
basePath: '',
|
||||
|
||||
|
||||
// frameworks to use
|
||||
// available frameworks: https://npmjs.org/browse/keyword/karma-adapter
|
||||
frameworks: ['jasmine'],
|
||||
|
||||
|
||||
// list of files / patterns to load in the browser
|
||||
files: piskelScripts,
|
||||
|
||||
|
||||
// list of files to exclude
|
||||
exclude: [],
|
||||
|
||||
|
||||
// preprocess matching files before serving them to the browser
|
||||
// available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor
|
||||
preprocessors: {
|
||||
},
|
||||
|
||||
|
||||
// test results reporter to use
|
||||
// possible values: 'dots', 'progress'
|
||||
// available reporters: https://npmjs.org/browse/keyword/karma-reporter
|
||||
reporters: ['progress'],
|
||||
|
||||
|
||||
// web server port
|
||||
port: 9876,
|
||||
|
||||
|
||||
// enable / disable colors in the output (reporters and logs)
|
||||
colors: true,
|
||||
|
||||
|
||||
// level of logging
|
||||
// possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
|
||||
logLevel: config.LOG_INFO,
|
||||
|
||||
|
||||
// enable / disable watching file and executing tests whenever any file changes
|
||||
autoWatch: true,
|
||||
|
||||
|
||||
// start these browsers
|
||||
// available browser launchers: https://npmjs.org/browse/keyword/karma-launcher
|
||||
browsers: ['PhantomJS'],
|
||||
|
||||
|
||||
// Continuous Integration mode
|
||||
// if true, Karma captures browsers, runs the tests and exits
|
||||
singleRun: true
|
||||
});
|
||||
};
|
101
misc/desktop/Info.plist
Normal file
@ -0,0 +1,101 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>BuildMachineOSBuild</key>
|
||||
<string>12C3006</string>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>en</string>
|
||||
<key>CFBundleDisplayName</key>
|
||||
<string>Piskel</string>
|
||||
<key>CFBundleDocumentTypes</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>CFBundleTypeIconFile</key>
|
||||
<string>nw.icns</string>
|
||||
<key>CFBundleTypeName</key>
|
||||
<string>node-webkit App</string>
|
||||
<key>CFBundleTypeRole</key>
|
||||
<string>Viewer</string>
|
||||
<key>LSHandlerRank</key>
|
||||
<string>Owner</string>
|
||||
<key>LSItemContentTypes</key>
|
||||
<array>
|
||||
<string>com.intel.nw.app</string>
|
||||
</array>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>CFBundleTypeName</key>
|
||||
<string>Folder</string>
|
||||
<key>CFBundleTypeOSTypes</key>
|
||||
<array>
|
||||
<string>fold</string>
|
||||
</array>
|
||||
<key>CFBundleTypeRole</key>
|
||||
<string>Viewer</string>
|
||||
<key>LSHandlerRank</key>
|
||||
<string>None</string>
|
||||
</dict>
|
||||
</array>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>node-webkit</string>
|
||||
<key>CFBundleIconFile</key>
|
||||
<string>nw.icns</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>com.intel.nw</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>Piskel</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>0.1.0</string>
|
||||
<key>DTSDKBuild</key>
|
||||
<string>11E52</string>
|
||||
<key>DTSDKName</key>
|
||||
<string>macosx10.7</string>
|
||||
<key>DTXcode</key>
|
||||
<string>0452</string>
|
||||
<key>DTXcodeBuild</key>
|
||||
<string>4G2008a</string>
|
||||
<key>LSFileQuarantineEnabled</key>
|
||||
<true/>
|
||||
<key>LSMinimumSystemVersion</key>
|
||||
<string>10.6.0</string>
|
||||
<key>NSPrincipalClass</key>
|
||||
<string>NSApplication</string>
|
||||
<key>NSSupportsAutomaticGraphicsSwitching</key>
|
||||
<true/>
|
||||
<key>SCMRevision</key>
|
||||
<string>239963</string>
|
||||
<key>UTExportedTypeDeclarations</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>UTTypeConformsTo</key>
|
||||
<array>
|
||||
<string>com.pkware.zip-archive</string>
|
||||
</array>
|
||||
<key>UTTypeDescription</key>
|
||||
<string>node-webkit App</string>
|
||||
<key>UTTypeIconFile</key>
|
||||
<string>nw.icns</string>
|
||||
<key>UTTypeIdentifier</key>
|
||||
<string>com.intel.nw.app</string>
|
||||
<key>UTTypeReferenceURL</key>
|
||||
<string>https://github.com/rogerwang/node-webkit/wiki/How-to-package-and-distribute-your-apps</string>
|
||||
<key>UTTypeTagSpecification</key>
|
||||
<dict>
|
||||
<key>com.apple.ostype</key>
|
||||
<string>node-webkit</string>
|
||||
<key>public.filename-extension</key>
|
||||
<array>
|
||||
<string>nw</string>
|
||||
</array>
|
||||
<key>public.mime-type</key>
|
||||
<string>application/x-node-webkit-app</string>
|
||||
</dict>
|
||||
</dict>
|
||||
</array>
|
||||
</dict>
|
||||
</plist>
|
BIN
misc/desktop/logo.ico
Normal file
After Width: | Height: | Size: 31 KiB |
BIN
misc/desktop/nw.icns
Normal file
BIN
misc/desktop/package-piskel.evb
Normal file
1
misc/gif-tests/low-colors-no-transparency.piskel
Normal file
@ -0,0 +1 @@
|
||||
{"modelVersion":2,"piskel":{"name":"low-colors-no-transparency","description":"","fps":12,"height":60,"width":60,"layers":["{\"name\":\"Layer 1\",\"frameCount\":2,\"base64PNG\":\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAHgAAAA8CAYAAACtrX6oAAABZUlEQVR4nO3cwRHCMAxE0RSUGlIF1TBDMTRAi1BCsCVZK/kf/n0n7xr5+FzXd6fe57lVR/YHBxhggAEGGGCA9yj7gwMMMMAAAwwwwHsEcPOWAR/Px22dgM/367bywP+grsbORl2NHQJsgY2GVoONhnYH9sSNQFbGjUB2A46AjYBWho2AdgFegeuFXAXXC9kMvBLXA7kSrgeyCTgD14pcDdeKDDDAergW5Iq4FuQp4GxYC3JV3FlkgAHWxZ1Brow7gwwwwACrFQacDemBXB13FBlggAFWDGCAAQYYYNkABhhggAGWLQRYFXlk/wiwKvLIfoABBlitUGA15NHto8BqyKPbAQZYF3lm9wywCvLMbv6qLII8uxlggPWQLXstwFnIlr3cJokjW7dyXSiM7LGT+2BBaM99XPiLIXtv440OEeioTbyyk4i9YgfvZDUP4OYB3DyAmwdw8wBuHsDNA7h5ADcP4Ob9AHU/4CXfXtXyAAAAAElFTkSuQmCC\"}"],"expanded":false}}
|
1
misc/gif-tests/low-colors-with-transparency.piskel
Normal file
@ -0,0 +1 @@
|
||||
{"modelVersion":2,"piskel":{"name":"low-colors-with-transparency","description":"","fps":12,"height":60,"width":60,"layers":["{\"name\":\"Layer 1\",\"frameCount\":2,\"base64PNG\":\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAHgAAAA8CAYAAACtrX6oAAABOUlEQVR4nO3bQWoDQQxEUR3Ix8lpAj5MTmovQsNgEmwYdZVG+h9q3+KtO4KIiIiIiIiIiIhoUN9fj7dr1O3n/ng39xvP9wlqI+xPUHtgn4G9IPQZ2OtBZ+JeADkTtzbyDtjC0Dtg60IrcAshK3DrICtxCyArcf3IDlwjsgPXiwxwY2AnrgHZiatHdsOKkd2wemQ3KsAbc4OKkd2gemQ3JsAAZ+bG1AK7IcXIbkg9shsRYIAzcyMCDHBybkSAAc7MjQgwwMm5EQEGODM3oh44oibyxtyQWtwIgAts570AF9jOe39zg4pwV25QLW4EwO2BI2ogC3PDanFXQ3BXs3AjAG4PHMHPhta4qyG4q1m4qyG4q1m4x5rDvjYH9tgQ3NUs3GPNYV+bA/tXTVH/awYqERERERERERFRVk8BxgukicHldgAAAABJRU5ErkJggg==\"}"],"expanded":false}}
|
1
misc/gif-tests/too-many-colors-no-transparency.piskel
Normal file
1
misc/gif-tests/too-many-colors-with-transparency.piskel
Normal file
68
misc/icons/SVG/eye.svg
Normal file
@ -0,0 +1,68 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="300"
|
||||
height="140.00085"
|
||||
id="svg2"
|
||||
version="1.1"
|
||||
inkscape:version="0.48.4 r9939"
|
||||
sodipodi:docname="eye.svg">
|
||||
<defs
|
||||
id="defs4" />
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="0.98994949"
|
||||
inkscape:cx="-6.0052729"
|
||||
inkscape:cy="93.159467"
|
||||
inkscape:document-units="px"
|
||||
inkscape:current-layer="layer1"
|
||||
showgrid="false"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1148"
|
||||
inkscape:window-x="-8"
|
||||
inkscape:window-y="-8"
|
||||
inkscape:window-maximized="1"
|
||||
fit-margin-top="0"
|
||||
fit-margin-left="0"
|
||||
fit-margin-right="0"
|
||||
fit-margin-bottom="0" />
|
||||
<metadata
|
||||
id="metadata7">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title></dc:title>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
transform="translate(-223.21875,-469.24915)">
|
||||
<path
|
||||
style="fill:#ffffff;fill-opacity:1;stroke:none"
|
||||
d="m 373.21875,469.25 c -63.15236,-0.28299 -150,70 -150,70 0,0 78.75568,70 150,70 62.82091,0 150,-70 150,-70 0,0 -87.47199,-69.71981 -150,-70 z m -1.5,22.3125 c 27.44855,0 49.6875,22.23895 49.6875,49.6875 0,27.44855 -22.23895,49.6875 -49.6875,49.6875 C 344.2702,590.9375 322,568.69855 322,541.25 c 0,-27.44855 22.2702,-49.6875 49.71875,-49.6875 z m 0.0312,18.34375 c -17.85254,0 -32.34375,14.49121 -32.34375,32.34375 0,17.85254 14.49121,32.3125 32.34375,32.3125 17.85254,0 32.3125,-14.45996 32.3125,-32.3125 0,-17.85254 -14.45996,-32.34375 -32.3125,-32.34375 z"
|
||||
id="path2987"
|
||||
inkscape:export-filename="C:\Development\git\piskel\misc\icons\eye.png"
|
||||
inkscape:export-xdpi="28.799999"
|
||||
inkscape:export-ydpi="28.799999"
|
||||
inkscape:connector-curvature="0" />
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 2.5 KiB |
88
misc/icons/SVG/flip.svg
Normal file
@ -0,0 +1,88 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="770.71875"
|
||||
height="581.4375"
|
||||
id="svg2"
|
||||
version="1.1"
|
||||
inkscape:version="0.48.4 r9939"
|
||||
sodipodi:docname="mirror.svg"
|
||||
inkscape:export-filename="C:\Development\git\piskel\src\img\tools\flip.png"
|
||||
inkscape:export-xdpi="35.446629"
|
||||
inkscape:export-ydpi="35.446629">
|
||||
<defs
|
||||
id="defs4" />
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="0.7"
|
||||
inkscape:cx="612.40785"
|
||||
inkscape:cy="222.08964"
|
||||
inkscape:document-units="px"
|
||||
inkscape:current-layer="layer1"
|
||||
showgrid="false"
|
||||
fit-margin-top="0"
|
||||
fit-margin-left="0"
|
||||
fit-margin-right="0"
|
||||
fit-margin-bottom="0"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1148"
|
||||
inkscape:window-x="-8"
|
||||
inkscape:window-y="-8"
|
||||
inkscape:window-maximized="1" />
|
||||
<metadata
|
||||
id="metadata7">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title></dc:title>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
transform="translate(-63.5625,-233.7818)">
|
||||
<path
|
||||
style="fill:#ffffff;fill-opacity:1;stroke:none"
|
||||
d="m 359.2768,233.7907 -295.7143,581.4286 295.7143,0 z"
|
||||
id="rect2987"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="cccc"
|
||||
inkscape:export-filename="C:\Development\git\piskel\src\img\tools\flip.png"
|
||||
inkscape:export-xdpi="35.446629"
|
||||
inkscape:export-ydpi="35.446629" />
|
||||
<path
|
||||
style="fill:#ffffff;fill-opacity:1;stroke:none"
|
||||
d="m 538.5625,233.7818 295.71875,581.4375 -295.71875,0 0,-581.4375 z m 29.125,117.4375 0,434.28125 218.59375,0 L 567.6875,351.2193 z"
|
||||
id="rect2987-1"
|
||||
inkscape:connector-curvature="0"
|
||||
inkscape:export-filename="C:\Development\git\piskel\src\img\tools\flip.png"
|
||||
inkscape:export-xdpi="35.446629"
|
||||
inkscape:export-ydpi="35.446629" />
|
||||
<path
|
||||
style="fill:#ffffff;fill-opacity:1;stroke:none"
|
||||
d="m 435.5625,233.93805 0,41.28125 25.71875,0 0,-41.28125 -25.71875,0 z m 0,91.28125 0,40 25.71875,0 0,-40 -25.71875,0 z m 0,90 0,40 25.71875,0 0,-40 -25.71875,0 z m 0,90 0,40 25.71875,0 0,-40 -25.71875,0 z m 0,90 0,40 25.71875,0 0,-40 -25.71875,0 z m 0,90 0,40 25.71875,0 0,-40 -25.71875,0 z m 0,90 0,40 25.71875,0 0,-40 -25.71875,0 z"
|
||||
id="rect3796"
|
||||
inkscape:export-filename="C:\Development\git\piskel\src\img\tools\flip.png"
|
||||
inkscape:export-xdpi="35.446629"
|
||||
inkscape:export-ydpi="35.446629"
|
||||
inkscape:connector-curvature="0" />
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 3.2 KiB |
105
misc/icons/SVG/import-icon.svg
Normal file
@ -0,0 +1,105 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:osb="http://www.openswatchbook.org/uri/2009/osb"
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="744.09448819"
|
||||
height="1052.3622047"
|
||||
id="svg2"
|
||||
version="1.1"
|
||||
inkscape:version="0.48.4 r9939"
|
||||
inkscape:export-filename="C:\Users\Julian\Desktop\import-icon.png"
|
||||
inkscape:export-xdpi="90"
|
||||
inkscape:export-ydpi="90"
|
||||
sodipodi:docname="New document 1">
|
||||
<defs
|
||||
id="defs4">
|
||||
<linearGradient
|
||||
id="linearGradient3776"
|
||||
osb:paint="solid">
|
||||
<stop
|
||||
style="stop-color:#000000;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop3778" />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="3.959798"
|
||||
inkscape:cx="211.98241"
|
||||
inkscape:cy="421.19661"
|
||||
inkscape:document-units="px"
|
||||
inkscape:current-layer="g3937"
|
||||
showgrid="false"
|
||||
inkscape:snap-bbox="true"
|
||||
inkscape:object-nodes="false"
|
||||
inkscape:object-paths="true"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1148"
|
||||
inkscape:window-x="-8"
|
||||
inkscape:window-y="-8"
|
||||
inkscape:window-maximized="1" />
|
||||
<metadata
|
||||
id="metadata7">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title></dc:title>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1">
|
||||
<g
|
||||
id="g3937"
|
||||
transform="matrix(0.09331433,0,0,0.09331433,132.76469,588.24921)"
|
||||
style="stroke:#ffffff;stroke-opacity:1">
|
||||
<g
|
||||
id="g3959"
|
||||
inkscape:export-filename="C:\Users\Julian\Desktop\import-icon.png"
|
||||
inkscape:export-xdpi="90"
|
||||
inkscape:export-ydpi="90">
|
||||
<rect
|
||||
style="fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:30;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
|
||||
id="rect2985"
|
||||
width="384.28558"
|
||||
height="384.28564"
|
||||
x="161.42857"
|
||||
y="249.50504"
|
||||
ry="45.714287" />
|
||||
<g
|
||||
transform="matrix(1.1961461,0,0,2.4281875,-3.7905251,90.097124)"
|
||||
id="g3765"
|
||||
style="fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-opacity:1">
|
||||
<g
|
||||
id="g3769"
|
||||
style="fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-opacity:1">
|
||||
<path
|
||||
sodipodi:nodetypes="cccczscscc"
|
||||
inkscape:connector-curvature="0"
|
||||
d="m 400,142.55868 -100,56.94635 -100,-57.33935 60,0.1965 c 0,0 0,-84.96745 0,-107.61226 0,-22.64481 55.79903,-25.2405808 80.01042,-22.177879 38.98433,4.931455 46.17248,30.649944 46.17248,30.649944 0,0 -46.97367,-31.662831 -46.18302,0.888182 0.65678,27.039499 0,98.252013 0,98.252013 z"
|
||||
style="fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-width:6;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
|
||||
id="rect3758" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 3.6 KiB |
72
misc/icons/SVG/lasso.svg
Normal file
@ -0,0 +1,72 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="224.38731"
|
||||
height="177.96065"
|
||||
id="svg2"
|
||||
version="1.1"
|
||||
inkscape:version="0.48.4 r9939"
|
||||
sodipodi:docname="New document 1">
|
||||
<defs
|
||||
id="defs4" />
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="0.175"
|
||||
inkscape:cx="-1542.2107"
|
||||
inkscape:cy="-1434.6156"
|
||||
inkscape:document-units="px"
|
||||
inkscape:current-layer="layer1"
|
||||
showgrid="false"
|
||||
fit-margin-top="0"
|
||||
fit-margin-left="0"
|
||||
fit-margin-right="0"
|
||||
fit-margin-bottom="0"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1148"
|
||||
inkscape:window-x="-8"
|
||||
inkscape:window-y="-8"
|
||||
inkscape:window-maximized="1" />
|
||||
<metadata
|
||||
id="metadata7">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title></dc:title>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
transform="translate(-212.79148,-238.81242)">
|
||||
<path
|
||||
style="fill:none;stroke:#000000;stroke-width:15;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:30, 30;stroke-dashoffset:0"
|
||||
d="m 429.0234,294.50503 c 0,26.68038 -49.13123,48.3091 -109.73768,48.3091 -60.60644,0 -110.61221,-39.48861 -96.16624,-73.3091 19.3392,-45.27639 60.80353,-4.92189 116.16624,-16.88052 101.16819,-21.85285 89.73768,15.20015 89.73768,41.88052 z"
|
||||
id="path3761"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="sssss" />
|
||||
<path
|
||||
style="fill:none;stroke:#000000;stroke-width:10;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
|
||||
d="m 309.32357,338.9218 c 0,0 8.97064,20.56224 -30.34229,45.31736 -39.31294,24.75512 -40.99309,30.13484 -40.99309,30.13484"
|
||||
id="path3764"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="czc" />
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 2.6 KiB |
62
misc/icons/SVG/layers.svg
Normal file
@ -0,0 +1,62 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
version="1.1"
|
||||
width="90"
|
||||
height="87.886002"
|
||||
id="svg3024">
|
||||
<defs
|
||||
id="defs3026" />
|
||||
<metadata
|
||||
id="metadata3029">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title></dc:title>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
transform="translate(-266.63206,-597.79217)"
|
||||
id="layer1">
|
||||
<g
|
||||
transform="translate(262.14286,465.21932)"
|
||||
id="Captions" />
|
||||
<g
|
||||
id="g3825" />
|
||||
<g
|
||||
transform="translate(-0.51079959,126.51585)"
|
||||
id="layer1-9"
|
||||
style="fill:#ffffff;fill-opacity:1">
|
||||
<g
|
||||
transform="translate(262.14286,465.21932)"
|
||||
id="Captions-4"
|
||||
style="fill:#ffffff;fill-opacity:1" />
|
||||
<g
|
||||
id="g3825-8"
|
||||
style="fill:#ffffff;fill-opacity:1">
|
||||
<path
|
||||
d="m 357.14286,556.72132 c 0,1.346 -1.097,2.441 -2.442,2.441 h -60.239 c -1.347,0 -2.441,-1.096 -2.441,-2.441 v -4.555 h 50.241 c 4.346,0 7.884,-3.539 7.884,-7.885 v -48.128 h 4.555 c 1.346,0 2.442,1.096 2.442,2.441 v 58.127 z"
|
||||
id="path2989-8"
|
||||
style="fill:#ffffff;fill-opacity:1" />
|
||||
<path
|
||||
d="m 279.58086,544.28132 v -4.555 h 6.997 2.722 2.721 37.803 c 4.346,0 7.884,-3.537 7.884,-7.883 v -35.69 -2.721 -2.721 -6.998 h 4.555 c 1.346,0 2.442,1.096 2.442,2.443 v 4.555 2.721 2.721 48.129 c 0,1.346 -1.097,2.443 -2.442,2.443 h -50.242 -2.721 -2.722 -4.555 c -1.347,0 -2.442,-1.098 -2.442,-2.444 z"
|
||||
id="path2991-2"
|
||||
style="fill:#ffffff;fill-opacity:1" />
|
||||
<path
|
||||
d="m 329.82386,471.27632 h -60.239 c -1.347,0 -2.442,1.093 -2.442,2.441 v 58.127 c 0,1.344 1.095,2.441 2.442,2.441 h 4.555 2.721 2.721 6.997 2.722 2.721 37.803 c 1.345,0 2.441,-1.098 2.441,-2.441 v -35.691 -2.721 -2.721 -6.998 -2.721 -2.721 -4.554 c -10e-4,-1.348 -1.098,-2.441 -2.442,-2.441 z"
|
||||
id="path2993-4"
|
||||
style="fill:#ffffff;fill-opacity:1" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 2.4 KiB |
99
misc/icons/SVG/onion.svg
Normal file
@ -0,0 +1,99 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="484.83377"
|
||||
height="430.80322"
|
||||
id="svg3775"
|
||||
version="1.1"
|
||||
inkscape:version="0.48.4 r9939"
|
||||
sodipodi:docname="New document 3">
|
||||
<defs
|
||||
id="defs3777" />
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="0.04375"
|
||||
inkscape:cx="-429.81362"
|
||||
inkscape:cy="-202.97901"
|
||||
inkscape:document-units="px"
|
||||
inkscape:current-layer="layer1"
|
||||
showgrid="false"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1148"
|
||||
inkscape:window-x="-8"
|
||||
inkscape:window-y="-8"
|
||||
inkscape:window-maximized="1"
|
||||
fit-margin-top="0"
|
||||
fit-margin-left="0"
|
||||
fit-margin-right="0"
|
||||
fit-margin-bottom="0" />
|
||||
<metadata
|
||||
id="metadata3780">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title></dc:title>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
transform="translate(-108.38875,-334.08796)">
|
||||
<g
|
||||
id="g4409">
|
||||
<g
|
||||
id="g4402" />
|
||||
<g
|
||||
transform="translate(0,0.82903262)"
|
||||
id="g4395">
|
||||
<path
|
||||
style="font-size:medium;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-indent:0;text-align:start;text-decoration:none;line-height:normal;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;text-anchor:start;baseline-shift:baseline;color:#000000;fill:#000000;fill-opacity:1;stroke:none;stroke-width:25;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate;font-family:Sans;-inkscape-font-specification:Sans"
|
||||
d="m 395.27679,333.25893 -27.75,24.625 -1.65625,-9 -15.87054,4.59375 0,30.6875 1.02679,21.75 16.5625,-14.6875 13.6875,-12.15625 c -0.38933,5.52995 -0.57204,11.36571 -0.1875,17.84375 0.60884,10.25666 3.42535,19.47486 9.03125,26.6875 5.6059,7.21264 13.15778,11.83264 21.53125,15.65625 16.74693,7.64722 37.6168,20.19024 63.19196,41.4465 48.42535,40.24771 54.41585,60.33492 63.14228,95.72811 8.30827,46.0064 -4.89609,79.1393 -23.94924,107.24539 -13.61329,20.08154 -30.46397,37.90172 -67.50554,45.41482 -37.04157,7.5131 -71.72672,9.9375 -96.53125,9.9375 l 0,25 c 26.40206,0 62.17269,-2.46078 101.5,-10.4375 39.32731,-7.97672 78.11592,-20.77325 101.8125,-45.5625 25.65093,-26.83371 50.16969,-75.46693 35.5,-151.5 l -0.0312,-0.125 -0.0312,-0.125 c -6.53497,-30.32511 -26.37349,-75.86993 -103.9375,-109.59375 -32.08342,-13.94946 -49.48883,-22.95257 -62.57589,-28.92857 -6.54353,-2.988 -10.31288,-6.85833 -12.36607,-9.5 -2.05319,-2.64167 -3.40095,-5.87935 -3.8125,-12.8125 -0.92607,-15.60085 1.34241,-27.46751 3.71875,-35.3125 2.37634,-7.84499 4.375,-10.875 4.375,-10.875 l -18.875,-16 z"
|
||||
id="path3783-2"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="ccccccccsssscssccsscccsssssccc" />
|
||||
<path
|
||||
style="font-size:medium;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-indent:0;text-align:start;text-decoration:none;line-height:normal;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;text-anchor:start;baseline-shift:baseline;color:#000000;fill:#000000;fill-opacity:1;stroke:none;stroke-width:25;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate;font-family:Sans;-inkscape-font-specification:Sans"
|
||||
d="m 368.02679,382.44643 c 0,0 -18.02679,-4.71234 -18.02679,1.3125 0,33.38646 -0.45698,39.28362 10.99554,62.875 11.45252,23.59138 26.22845,47.86253 48.53571,68.45982 42.18558,38.95183 38.76978,44.87254 45.77232,64.91071 2.57793,7.37689 3.66466,24.51253 1.875,37.96875 -1.78965,13.45623 -6.8081,31.21802 -13.40625,41.3125 -10.36811,15.86211 -18.71831,32.07642 -38.55357,43.09375 C 384.4983,713.88847 366.62548,716.375 350,716.375 l 0,25 c 20.82486,0 44.52967,-5.01706 69.5625,-15.125 25.03283,-10.10794 49.12742,-25.27054 63.71875,-47.59375 9.77369,-14.95272 20.06105,-37.72017 22.33202,-55.91561 2.27097,-18.19544 6.78209,-33.89274 -5.08281,-58.40387 -18.26325,-37.72918 -42.54947,-49.06002 -72.19845,-74.15863 -18.35479,-15.5378 -34.85805,-33.83125 -44.86772,-54.45046 -10.00967,-20.61921 -14.81706,-41.55822 -15.4375,-53.28125 z"
|
||||
id="path3783-9-4-2"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="csssszssccssssssc" />
|
||||
<path
|
||||
style="font-size:medium;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-indent:0;text-align:start;text-decoration:none;line-height:normal;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;text-anchor:start;baseline-shift:baseline;color:#000000;fill:#000000;fill-opacity:1;stroke:none;stroke-width:25;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate;font-family:Sans;-inkscape-font-specification:Sans"
|
||||
d="M 364.71429,398.50893 350,399.85268 c 0.74677,14.10988 0,43.14662 0,70.71875 0,13.09078 2.17726,28.99404 4.22321,39.97321 3.9423,21.15552 10.51352,34.8833 14.63393,48.16965 3.27485,10.5598 6.94665,25.34012 9.3884,31.02232 6.61244,15.3878 7.52886,34.1921 0.75446,55.84375 -6.86056,21.92704 -17.55684,42.94479 -35.42857,49.84375 L 350,716.625 c 25.86119,-9.9831 62.03992,-38.1333 71.93512,-69.75936 8.9188,-28.50537 3.28613,-54.26844 -4.84375,-73.1875 -4.92941,-11.47123 -9.61575,-19.37277 -14.86607,-28.9375 -6.46007,-11.76857 -20.1885,-26.52209 -24.11895,-38.53817 -3.57466,-10.92835 -7.82736,-24.14439 -8.92331,-37.66229 -2.1919,-27.03581 -3.63369,-54.77948 -4.46875,-70.03125 z"
|
||||
id="path3783-9-4-4-8-8"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="ccsssssccsssssc" />
|
||||
</g>
|
||||
</g>
|
||||
<path
|
||||
style="font-size:medium;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-indent:0;text-align:start;text-decoration:none;line-height:normal;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;text-anchor:start;baseline-shift:baseline;color:#000000;fill:#000000;fill-opacity:1;stroke:none;stroke-width:25;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate;font-family:Sans;-inkscape-font-specification:Sans"
|
||||
d="m 306.33447,334.11887 27.75,24.625 1.65625,-9 15.87054,4.59375 0,30.6875 -1.02679,21.75 -16.5625,-14.6875 -13.6875,-12.15625 c 0.38933,5.52995 0.57204,11.36571 0.1875,17.84375 -0.60884,10.25666 0.61526,20.48501 -4.99064,27.69765 -5.6059,7.21264 -12.14763,16.88341 -20.5211,20.70702 -16.74693,7.64722 -36.60664,20.19024 -62.1818,41.4465 -48.42535,40.24771 -47.34479,51.24354 -56.07122,86.63673 -3.25751,47.01655 -5.92183,46.3581 4.75635,78.96112 6.45302,19.70261 22.38275,38.91187 59.42432,56.52649 34.13302,16.23151 85.86885,30.14056 110.67338,30.14056 l 0,25 c -26.40206,0 -62.17269,-2.46078 -101.5,-10.4375 -39.32731,-7.97672 -78.11592,-20.77325 -101.8125,-45.5625 -25.65093,-26.83371 -50.169693,-75.46693 -35.5,-151.5 l 0.0312,-0.125 0.0312,-0.125 c 6.53497,-30.32511 26.37349,-75.86993 103.9375,-109.59375 32.08342,-13.94946 49.48883,-22.95257 62.57589,-28.92857 6.54353,-2.988 10.31288,-6.85833 12.36607,-9.5 2.05319,-2.64167 3.40095,-5.87935 3.8125,-12.8125 0.92607,-15.60085 -1.34241,-27.46751 -3.71875,-35.3125 -2.37634,-7.84499 -4.375,-10.875 -4.375,-10.875 l 18.875,-16 z"
|
||||
id="path3783-2-4"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="ccccccccsssscssccsscccsssssccc" />
|
||||
<path
|
||||
style="font-size:medium;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-indent:0;text-align:start;text-decoration:none;line-height:normal;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;text-anchor:start;baseline-shift:baseline;color:#000000;fill:#000000;fill-opacity:1;stroke:none;stroke-width:25;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate;font-family:Sans;-inkscape-font-specification:Sans"
|
||||
d="m 343.97613,380.44701 9.00001,12.77232 c -0.74677,14.10988 0,43.14662 0,70.71875 0,13.09078 3.53703,30.42261 1.49108,41.40178 -3.9423,21.15552 -3.37067,28.45473 -7.49108,41.74108 -3.27485,10.5598 -6.94665,28.91155 -9.3884,34.59375 -6.61244,15.3878 -8.24315,50.62067 -1.46875,72.27232 6.86056,21.92704 11.12828,50.08764 21.85715,66.27232 l 3.98985,41.15013 C 338.24766,729.2435 314.3133,692.32754 304.4181,660.70148 c -8.9188,-28.50537 -8.28613,-78.55416 -0.15625,-97.47322 4.92941,-11.47123 9.61575,-22.9442 14.86607,-32.50893 6.46007,-11.76857 10.13774,-23.49163 13.05804,-36.51786 2.51528,-11.21964 6.93976,-29.49102 8.03571,-43.00892 2.1919,-27.03581 2.9194,-55.49377 3.75446,-70.74554 z"
|
||||
id="path3783-9-4-4-8-8-7"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="ccsssssccsssssc" />
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 9.5 KiB |
80
misc/icons/SVG/swap-arrow-square.svg
Normal file
@ -0,0 +1,80 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="744.09448819"
|
||||
height="1052.3622047"
|
||||
id="svg2"
|
||||
version="1.1"
|
||||
inkscape:version="0.48.4 r9939"
|
||||
sodipodi:docname="swap-arrow-square.svg">
|
||||
<defs
|
||||
id="defs4" />
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="0.98994949"
|
||||
inkscape:cx="394.04689"
|
||||
inkscape:cy="437.49995"
|
||||
inkscape:document-units="px"
|
||||
inkscape:current-layer="g3903"
|
||||
showgrid="false"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1148"
|
||||
inkscape:window-x="-8"
|
||||
inkscape:window-y="-8"
|
||||
inkscape:window-maximized="1" />
|
||||
<metadata
|
||||
id="metadata7">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title></dc:title>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1">
|
||||
<g
|
||||
id="g3863"
|
||||
transform="matrix(1.0584227,0.8783336,-0.88927511,1.0454,500.51129,-281.01924)" />
|
||||
<g
|
||||
id="g3903"
|
||||
transform="matrix(1.0752324,0,0,1.1512202,-10.080657,-112.64735)">
|
||||
<path
|
||||
sodipodi:nodetypes="cccc"
|
||||
inkscape:connector-curvature="0"
|
||||
id="path3015"
|
||||
d="m 134.94957,482.97409 77.33565,-84.65176 90.19445,84.65176 c -55.58473,1.43563 -111.94607,1.12702 -167.5301,0 z"
|
||||
style="opacity:0.98999999;fill:#000000;fill-opacity:1;stroke:#000000;stroke-width:1.91215169px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
|
||||
<path
|
||||
style="fill:#000000;fill-opacity:1;stroke:#000000;stroke-width:1.02133131px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="m 454.32064,637.60642 c 0,0 -209.21865,11.0537 -216.48659,-180.84972 l -55.19052,0.25672 c 0.0919,230.63552 273.06531,236.67753 273.06531,236.67753 z"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="ccccc"
|
||||
id="path2995-0" />
|
||||
<path
|
||||
sodipodi:nodetypes="cccc"
|
||||
inkscape:connector-curvature="0"
|
||||
id="path3015-5"
|
||||
d="m 420.44925,576.40133 84.53491,77.33565 -84.53491,90.19445 c -1.43564,-55.58473 -1.12702,-111.94607 0,-167.5301 z"
|
||||
style="opacity:0.98999999;fill:#000000;fill-opacity:1;stroke:#000000;stroke-width:1.91215169px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 3.0 KiB |
80
misc/icons/SVG/swap-arrow.svg
Normal file
@ -0,0 +1,80 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="744.09448819"
|
||||
height="1052.3622047"
|
||||
id="svg2"
|
||||
version="1.1"
|
||||
inkscape:version="0.48.4 r9939"
|
||||
sodipodi:docname="New document 1">
|
||||
<defs
|
||||
id="defs4" />
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="1.4"
|
||||
inkscape:cx="205.60891"
|
||||
inkscape:cy="475.62016"
|
||||
inkscape:document-units="px"
|
||||
inkscape:current-layer="layer1"
|
||||
showgrid="false"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1148"
|
||||
inkscape:window-x="-8"
|
||||
inkscape:window-y="-8"
|
||||
inkscape:window-maximized="1" />
|
||||
<metadata
|
||||
id="metadata7">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title></dc:title>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1">
|
||||
<g
|
||||
id="g3866"
|
||||
transform="matrix(0.76563359,0.64327693,-0.64327693,0.76563359,437.69481,-47.0057)">
|
||||
<path
|
||||
sodipodi:nodetypes="cccc"
|
||||
inkscape:connector-curvature="0"
|
||||
id="path3015"
|
||||
d="m 130,570.36218 0,-60 63.85714,0 c -20.96635,20.14932 -41.98769,40.84088 -63.85714,60 z"
|
||||
style="opacity:0.98999999;fill:#000000;fill-opacity:1;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
|
||||
<path
|
||||
sodipodi:nodetypes="cccc"
|
||||
inkscape:connector-curvature="0"
|
||||
id="path3015-7"
|
||||
d="m 469.84542,570.36218 0,-60 -63.85714,0 c 20.96635,20.14932 41.98769,40.84088 63.85714,60 z"
|
||||
style="opacity:0.98999999;fill:#000000;fill-opacity:1;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
|
||||
<g
|
||||
id="g3863">
|
||||
<path
|
||||
style="fill:#000000;fill-opacity:1;stroke:#000000;stroke-width:0.73463827px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="m 449.48174,512.15368 c 0,0 -145.41018,128.54808 -299.94302,0 L 150,552.36218 c 154.5622,109.35713 300,0 300,0 z"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="ccccc"
|
||||
id="path2995-0" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 2.8 KiB |
72
misc/icons/SVG/swap-colors-sq.svg
Normal file
@ -0,0 +1,72 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="435"
|
||||
height="409.28125"
|
||||
id="svg3768"
|
||||
version="1.1"
|
||||
inkscape:version="0.48.4 r9939"
|
||||
sodipodi:docname="New document 3">
|
||||
<defs
|
||||
id="defs3770" />
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="0.98994949"
|
||||
inkscape:cx="87.95854"
|
||||
inkscape:cy="195.23297"
|
||||
inkscape:document-units="px"
|
||||
inkscape:current-layer="layer1"
|
||||
showgrid="false"
|
||||
fit-margin-top="0"
|
||||
fit-margin-left="0"
|
||||
fit-margin-right="0"
|
||||
fit-margin-bottom="0"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1148"
|
||||
inkscape:window-x="-8"
|
||||
inkscape:window-y="-8"
|
||||
inkscape:window-maximized="1" />
|
||||
<metadata
|
||||
id="metadata3773">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title></dc:title>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
transform="translate(-151.0625,-197.71875)">
|
||||
<rect
|
||||
style="fill:none;stroke:#ffffff;stroke-width:35;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
|
||||
id="rect3784"
|
||||
width="400"
|
||||
height="374.28571"
|
||||
x="168.57143"
|
||||
y="215.21933" />
|
||||
<path
|
||||
style="fill:#ffffff;fill-opacity:1;stroke:none"
|
||||
d="m 368.5625,209.5 0,97.0625 c -18.16488,0.20304 -36.33513,0.39071 -54.5,0.59375 l 0,-51.1875 c -36.22093,27.13868 -72.43533,54.2988 -108.65625,81.4375 l 107.375,91.46875 1.28125,-53.5 54.5,0.96875 0,62.25 c 20.86255,0.2332 41.73121,0.45431 62.59375,0.6875 l 0,-51.1875 C 467.37718,415.23243 503.59158,442.3613 539.8125,469.5 l -107.40625,91.5 -1.25,-53.5 -62.59375,1.09375 0,75.1875 200,0 0,-365.71875 -200,-8.5625 z"
|
||||
id="path4296"
|
||||
inkscape:connector-curvature="0" />
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 2.5 KiB |
97
misc/icons/SVG/swap-colors.svg
Normal file
@ -0,0 +1,97 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="305.0015"
|
||||
height="340.10172"
|
||||
id="svg2"
|
||||
version="1.1"
|
||||
inkscape:version="0.48.4 r9939"
|
||||
inkscape:export-filename="C:\Development\git\piskel\src\img\tools\swap.png"
|
||||
inkscape:export-xdpi="28.799999"
|
||||
inkscape:export-ydpi="28.799999"
|
||||
sodipodi:docname="swap-colors.svg">
|
||||
<defs
|
||||
id="defs2994" />
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="1.979899"
|
||||
inkscape:cx="8.3838947"
|
||||
inkscape:cy="209.5365"
|
||||
inkscape:document-units="px"
|
||||
inkscape:current-layer="layer1"
|
||||
showgrid="false"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1148"
|
||||
inkscape:window-x="-8"
|
||||
inkscape:window-y="-8"
|
||||
inkscape:window-maximized="1"
|
||||
fit-margin-top="0"
|
||||
fit-margin-left="0"
|
||||
fit-margin-right="0"
|
||||
fit-margin-bottom="0" />
|
||||
<metadata
|
||||
id="metadata7">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title></dc:title>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
transform="translate(-253.81175,62.601668)">
|
||||
<path
|
||||
style="fill:none;stroke:#ffffff;stroke-width:25;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
|
||||
d="m 281.31983,48.818994 c 3.33333,70.714286 6.66667,141.428566 10,212.142856 61.21871,3.33294 122.57845,5.69263 183.84823,2.1217 15.38711,-0.63513 30.77146,-1.33859 46.15177,-2.1217 3.33333,-70.71429 6.66667,-141.42857 10,-212.142856 -62.39939,12.482847 -126.73342,18.836303 -190.05871,9.668003 -20.08786,-2.492087 -40.09989,-5.6543 -59.94129,-9.668003 z"
|
||||
id="path3907"
|
||||
inkscape:connector-curvature="0"
|
||||
inkscape:export-filename="C:\Development\git\piskel\src\img\tools\swap.png"
|
||||
inkscape:export-xdpi="28.799999"
|
||||
inkscape:export-ydpi="28.799999" />
|
||||
<path
|
||||
style="fill:none;stroke:#ffffff;stroke-width:15;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
|
||||
d="m 261.31983,63.818994 c 0,-10 0,-20 0,-30 17.1189,-42.7715577 62.54009,-66.510405 106.22509,-72.841714 51.17865,-7.022275 108.04464,-2.652241 150.47229,29.6746917 14.38134,11.798678 28.46011,26.7242963 33.30262,44.9943353 0,9.390896 0,18.781791 0,28.172687"
|
||||
id="path3909"
|
||||
inkscape:connector-curvature="0"
|
||||
inkscape:export-filename="C:\Development\git\piskel\src\img\tools\swap.png"
|
||||
inkscape:export-xdpi="28.799999"
|
||||
inkscape:export-ydpi="28.799999" />
|
||||
<path
|
||||
style="fill:none;stroke:#ffffff;stroke-width:35;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
|
||||
d="m 364.53412,-41.538146 c 25.60486,-3.886191 51.66473,-4.881507 77.20839,0.0707 l 3.09159,0.466979 1.12859,0.176599"
|
||||
id="path3915"
|
||||
inkscape:connector-curvature="0"
|
||||
inkscape:export-filename="C:\Development\git\piskel\src\img\tools\swap.png"
|
||||
inkscape:export-xdpi="28.799999"
|
||||
inkscape:export-ydpi="28.799999" />
|
||||
<path
|
||||
style="fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="m 154.55334,129.87795 0.68071,15.85947 c 0,0 -57.346456,21.06263 -57.436416,36.51009 -0.09,15.44746 36.662436,29.96772 55.746256,42.34379 19.08382,12.37606 46.83462,28.70031 47.51123,48.36912 C 200.90809,289.78491 152.86,310.7174 152.86,310.7174 l 0.68319,14.23203 111.11677,-2.02031 11.11168,-206.07112 z"
|
||||
id="path3764"
|
||||
inkscape:connector-curvature="0"
|
||||
transform="translate(253.81175,-62.601668)"
|
||||
sodipodi:nodetypes="cczzcccccc"
|
||||
inkscape:export-filename="C:\Development\git\piskel\src\img\tools\swap.png"
|
||||
inkscape:export-xdpi="28.799999"
|
||||
inkscape:export-ydpi="28.799999" />
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 4.5 KiB |
Before Width: | Height: | Size: 594 B After Width: | Height: | Size: 594 B |
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 11 KiB |