spice-html5-rails 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (30) hide show
  1. data/COPYING +674 -0
  2. data/COPYING.LESSER +165 -0
  3. data/LICENSE.txt +15 -0
  4. data/README.md +35 -0
  5. data/lib/spice-html5-rails/version.rb +7 -0
  6. data/lib/spice-html5-rails.rb +10 -0
  7. data/vendor/assets/javascripts/spice-html5.js +25 -0
  8. data/vendor/assets/javascripts/spiceHTML5/atKeynames.js +183 -0
  9. data/vendor/assets/javascripts/spiceHTML5/bitmap.js +51 -0
  10. data/vendor/assets/javascripts/spiceHTML5/cursor.js +92 -0
  11. data/vendor/assets/javascripts/spiceHTML5/display.js +806 -0
  12. data/vendor/assets/javascripts/spiceHTML5/enums.js +282 -0
  13. data/vendor/assets/javascripts/spiceHTML5/inputs.js +271 -0
  14. data/vendor/assets/javascripts/spiceHTML5/lz.js +166 -0
  15. data/vendor/assets/javascripts/spiceHTML5/main.js +177 -0
  16. data/vendor/assets/javascripts/spiceHTML5/png.js +256 -0
  17. data/vendor/assets/javascripts/spiceHTML5/quic.js +1335 -0
  18. data/vendor/assets/javascripts/spiceHTML5/spiceconn.js +455 -0
  19. data/vendor/assets/javascripts/spiceHTML5/spicedataview.js +96 -0
  20. data/vendor/assets/javascripts/spiceHTML5/spicemsg.js +883 -0
  21. data/vendor/assets/javascripts/spiceHTML5/spicetype.js +480 -0
  22. data/vendor/assets/javascripts/spiceHTML5/thirdparty/jsbn.js +589 -0
  23. data/vendor/assets/javascripts/spiceHTML5/thirdparty/prng4.js +79 -0
  24. data/vendor/assets/javascripts/spiceHTML5/thirdparty/rng.js +102 -0
  25. data/vendor/assets/javascripts/spiceHTML5/thirdparty/rsa.js +146 -0
  26. data/vendor/assets/javascripts/spiceHTML5/thirdparty/sha1.js +346 -0
  27. data/vendor/assets/javascripts/spiceHTML5/ticket.js +250 -0
  28. data/vendor/assets/javascripts/spiceHTML5/utils.js +261 -0
  29. data/vendor/assets/javascripts/spiceHTML5/wire.js +123 -0
  30. metadata +108 -0
@@ -0,0 +1,166 @@
1
+ "use strict";
2
+ /*
3
+ Copyright (C) 2012 by Jeremy P. White <jwhite@codeweavers.com>
4
+
5
+ This file is part of spice-html5.
6
+
7
+ spice-html5 is free software: you can redistribute it and/or modify
8
+ it under the terms of the GNU Lesser General Public License as published by
9
+ the Free Software Foundation, either version 3 of the License, or
10
+ (at your option) any later version.
11
+
12
+ spice-html5 is distributed in the hope that it will be useful,
13
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
14
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
+ GNU Lesser General Public License for more details.
16
+
17
+ You should have received a copy of the GNU Lesser General Public License
18
+ along with spice-html5. If not, see <http://www.gnu.org/licenses/>.
19
+ */
20
+
21
+
22
+ /*----------------------------------------------------------------------------
23
+ ** lz.js
24
+ ** Functions for handling SPICE_IMAGE_TYPE_LZ_RGB
25
+ ** Adapted from lz.c .
26
+ **--------------------------------------------------------------------------*/
27
+ function lz_rgb32_decompress(in_buf, at, out_buf, type, default_alpha)
28
+ {
29
+ var encoder = at;
30
+ var op = 0;
31
+ var ctrl;
32
+ var ctr = 0;
33
+
34
+ for (ctrl = in_buf[encoder++]; (op * 4) < out_buf.length; ctrl = in_buf[encoder++])
35
+ {
36
+ var ref = op;
37
+ var len = ctrl >> 5;
38
+ var ofs = (ctrl & 31) << 8;
39
+
40
+ //if (type == LZ_IMAGE_TYPE_RGBA)
41
+ //console.log(ctr++ + ": from " + (encoder + 28) + ", ctrl " + ctrl + ", len " + len + ", ofs " + ofs + ", op " + op);
42
+ if (ctrl >= 32) {
43
+
44
+ var code;
45
+ len--;
46
+
47
+ if (len == 7 - 1) {
48
+ do {
49
+ code = in_buf[encoder++];
50
+ len += code;
51
+ } while (code == 255);
52
+ }
53
+ code = in_buf[encoder++];
54
+ ofs += code;
55
+
56
+
57
+ if (code == 255) {
58
+ if ((ofs - code) == (31 << 8)) {
59
+ ofs = in_buf[encoder++] << 8;
60
+ ofs += in_buf[encoder++];
61
+ ofs += 8191;
62
+ }
63
+ }
64
+ len += 1;
65
+ if (type == LZ_IMAGE_TYPE_RGBA)
66
+ len += 2;
67
+
68
+ ofs += 1;
69
+
70
+ ref -= ofs;
71
+ if (ref == (op - 1)) {
72
+ var b = ref;
73
+ //if (type == LZ_IMAGE_TYPE_RGBA) console.log("alpha " + out_buf[(b*4)+3] + " dupped into pixel " + op + " through pixel " + (op + len));
74
+ for (; len; --len) {
75
+ if (type == LZ_IMAGE_TYPE_RGBA)
76
+ {
77
+ out_buf[(op*4) + 3] = out_buf[(b*4)+3];
78
+ }
79
+ else
80
+ {
81
+ for (i = 0; i < 4; i++)
82
+ out_buf[(op*4) + i] = out_buf[(b*4)+i];
83
+ }
84
+ op++;
85
+ }
86
+ } else {
87
+ //if (type == LZ_IMAGE_TYPE_RGBA) console.log("alpha copied to pixel " + op + " through " + (op + len) + " from " + ref);
88
+ for (; len; --len) {
89
+ if (type == LZ_IMAGE_TYPE_RGBA)
90
+ {
91
+ out_buf[(op*4) + 3] = out_buf[(ref*4)+3];
92
+ }
93
+ else
94
+ {
95
+ for (i = 0; i < 4; i++)
96
+ out_buf[(op*4) + i] = out_buf[(ref*4)+i];
97
+ }
98
+ op++; ref++;
99
+ }
100
+ }
101
+ } else {
102
+ ctrl++;
103
+
104
+ if (type == LZ_IMAGE_TYPE_RGBA)
105
+ {
106
+ //console.log("alpha " + in_buf[encoder] + " set into pixel " + op);
107
+ out_buf[(op*4) + 3] = in_buf[encoder++];
108
+ }
109
+ else
110
+ {
111
+ out_buf[(op*4) + 0] = in_buf[encoder + 2];
112
+ out_buf[(op*4) + 1] = in_buf[encoder + 1];
113
+ out_buf[(op*4) + 2] = in_buf[encoder + 0];
114
+ if (default_alpha)
115
+ out_buf[(op*4) + 3] = 255;
116
+ encoder += 3;
117
+ }
118
+ op++;
119
+
120
+
121
+ for (--ctrl; ctrl; ctrl--) {
122
+ if (type == LZ_IMAGE_TYPE_RGBA)
123
+ {
124
+ //console.log("alpha " + in_buf[encoder] + " set into pixel " + op);
125
+ out_buf[(op*4) + 3] = in_buf[encoder++];
126
+ }
127
+ else
128
+ {
129
+ out_buf[(op*4) + 0] = in_buf[encoder + 2];
130
+ out_buf[(op*4) + 1] = in_buf[encoder + 1];
131
+ out_buf[(op*4) + 2] = in_buf[encoder + 0];
132
+ if (default_alpha)
133
+ out_buf[(op*4) + 3] = 255;
134
+ encoder += 3;
135
+ }
136
+ op++;
137
+ }
138
+ }
139
+
140
+ }
141
+ return encoder - 1;
142
+ }
143
+
144
+ function convert_spice_lz_to_web(context, lz_image)
145
+ {
146
+ var at;
147
+ if (lz_image.type === LZ_IMAGE_TYPE_RGB32 || lz_image.type === LZ_IMAGE_TYPE_RGBA)
148
+ {
149
+ var u8 = new Uint8Array(lz_image.data);
150
+ var ret = context.createImageData(lz_image.width, lz_image.height);
151
+
152
+ at = lz_rgb32_decompress(u8, 0, ret.data, LZ_IMAGE_TYPE_RGB32, lz_image.type != LZ_IMAGE_TYPE_RGBA);
153
+ if (lz_image.type == LZ_IMAGE_TYPE_RGBA)
154
+ lz_rgb32_decompress(u8, at, ret.data, LZ_IMAGE_TYPE_RGBA, false);
155
+ }
156
+ else if (lz_image.type === LZ_IMAGE_TYPE_XXXA)
157
+ {
158
+ var u8 = new Uint8Array(lz_image.data);
159
+ var ret = context.createImageData(lz_image.width, lz_image.height);
160
+ lz_rgb32_decompress(u8, 0, ret.data, LZ_IMAGE_TYPE_RGBA, false);
161
+ }
162
+ else
163
+ return undefined;
164
+
165
+ return ret;
166
+ }
@@ -0,0 +1,177 @@
1
+ "use strict";
2
+ /*
3
+ Copyright (C) 2012 by Jeremy P. White <jwhite@codeweavers.com>
4
+
5
+ This file is part of spice-html5.
6
+
7
+ spice-html5 is free software: you can redistribute it and/or modify
8
+ it under the terms of the GNU Lesser General Public License as published by
9
+ the Free Software Foundation, either version 3 of the License, or
10
+ (at your option) any later version.
11
+
12
+ spice-html5 is distributed in the hope that it will be useful,
13
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
14
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
+ GNU Lesser General Public License for more details.
16
+
17
+ You should have received a copy of the GNU Lesser General Public License
18
+ along with spice-html5. If not, see <http://www.gnu.org/licenses/>.
19
+ */
20
+
21
+ /*----------------------------------------------------------------------------
22
+ ** SpiceMainConn
23
+ ** This is the master Javascript class for establishing and
24
+ ** managing a connection to a Spice Server.
25
+ **
26
+ ** Invocation: You must pass an object with properties as follows:
27
+ ** uri (required) Uri of a WebSocket listener that is
28
+ ** connected to a spice server.
29
+ ** password (required) Password to send to the spice server
30
+ ** message_id (optional) Identifier of an element in the DOM
31
+ ** where SpiceConn will write messages.
32
+ ** It will use classes spice-messages-x,
33
+ ** where x is one of info, warning, or error.
34
+ ** screen_id (optional) Identifier of an element in the DOM
35
+ ** where SpiceConn will create any new
36
+ ** client screens. This is the main UI.
37
+ ** dump_id (optional) If given, an element to use for
38
+ ** dumping every single image + canvas drawn.
39
+ ** Sometimes useful for debugging.
40
+ ** onerror (optional) If given, a function to receive async
41
+ ** errors. Note that you should also catch
42
+ ** errors for ones that occur inline
43
+ **
44
+ ** Throws error if there are troubles. Requires a modern (by 2012 standards)
45
+ ** browser, including WebSocket and WebSocket.binaryType == arraybuffer
46
+ **
47
+ **--------------------------------------------------------------------------*/
48
+ function SpiceMainConn()
49
+ {
50
+ if (typeof WebSocket === "undefined")
51
+ throw new Error("WebSocket unavailable. You need to use a different browser.");
52
+
53
+ SpiceConn.apply(this, arguments);
54
+
55
+ }
56
+
57
+ SpiceMainConn.prototype = Object.create(SpiceConn.prototype);
58
+ SpiceMainConn.prototype.process_channel_message = function(msg)
59
+ {
60
+ if (msg.type == SPICE_MSG_MAIN_INIT)
61
+ {
62
+ this.log_info("Connected to " + this.ws.url);
63
+ this.report_success("Connected")
64
+ this.main_init = new SpiceMsgMainInit(msg.data);
65
+ this.connection_id = this.main_init.session_id;
66
+
67
+ if (DEBUG > 0)
68
+ {
69
+ // FIXME - there is a lot here we don't handle; mouse modes, agent,
70
+ // ram_hint, multi_media_time
71
+ this.log_info("session id " + this.main_init.session_id +
72
+ " ; display_channels_hint " + this.main_init.display_channels_hint +
73
+ " ; supported_mouse_modes " + this.main_init.supported_mouse_modes +
74
+ " ; current_mouse_mode " + this.main_init.current_mouse_mode +
75
+ " ; agent_connected " + this.main_init.agent_connected +
76
+ " ; agent_tokens " + this.main_init.agent_tokens +
77
+ " ; multi_media_time " + this.main_init.multi_media_time +
78
+ " ; ram_hint " + this.main_init.ram_hint);
79
+ }
80
+
81
+ this.mouse_mode = this.main_init.current_mouse_mode;
82
+ if (this.main_init.current_mouse_mode != SPICE_MOUSE_MODE_CLIENT &&
83
+ (this.main_init.supported_mouse_modes & SPICE_MOUSE_MODE_CLIENT))
84
+ {
85
+ var mode_request = new SpiceMsgcMainMouseModeRequest(SPICE_MOUSE_MODE_CLIENT);
86
+ var mr = new SpiceMiniData();
87
+ mr.build_msg(SPICE_MSGC_MAIN_MOUSE_MODE_REQUEST, mode_request);
88
+ this.send_msg(mr);
89
+ }
90
+
91
+ var attach = new SpiceMiniData;
92
+ attach.type = SPICE_MSGC_MAIN_ATTACH_CHANNELS;
93
+ attach.size = attach.buffer_size();
94
+ this.send_msg(attach);
95
+ return true;
96
+ }
97
+
98
+ if (msg.type == SPICE_MSG_MAIN_MOUSE_MODE)
99
+ {
100
+ var mode = new SpiceMsgMainMouseMode(msg.data);
101
+ DEBUG > 0 && this.log_info("Mouse supported modes " + mode.supported_modes + "; current " + mode.current_mode);
102
+ this.mouse_mode = mode.current_mode;
103
+ if (this.inputs)
104
+ this.inputs.mouse_mode = mode.current_mode;
105
+ return true;
106
+ }
107
+
108
+ if (msg.type == SPICE_MSG_MAIN_CHANNELS_LIST)
109
+ {
110
+ var i;
111
+ var chans;
112
+ DEBUG > 0 && console.log("channels");
113
+ chans = new SpiceMsgChannels(msg.data);
114
+ for (i = 0; i < chans.channels.length; i++)
115
+ {
116
+ var conn = {
117
+ uri: this.ws.url,
118
+ parent: this,
119
+ connection_id : this.connection_id,
120
+ type : chans.channels[i].type,
121
+ chan_id : chans.channels[i].id
122
+ };
123
+ if (chans.channels[i].type == SPICE_CHANNEL_DISPLAY)
124
+ this.display = new SpiceDisplayConn(conn);
125
+ else if (chans.channels[i].type == SPICE_CHANNEL_INPUTS)
126
+ {
127
+ this.inputs = new SpiceInputsConn(conn);
128
+ this.inputs.mouse_mode = this.mouse_mode;
129
+ }
130
+ else if (chans.channels[i].type == SPICE_CHANNEL_CURSOR)
131
+ this.cursor = new SpiceCursorConn(conn);
132
+ else
133
+ {
134
+ this.log_err("Channel type " + chans.channels[i].type + " unknown.");
135
+ if (! ("extra_channels" in this))
136
+ this.extra_channels = [];
137
+ this.extra_channels[i] = new SpiceConn(conn);
138
+ }
139
+
140
+ }
141
+
142
+ return true;
143
+ }
144
+
145
+ return false;
146
+ }
147
+
148
+ SpiceMainConn.prototype.stop = function(msg)
149
+ {
150
+ this.state = "closing";
151
+
152
+ if (this.inputs)
153
+ {
154
+ this.inputs.cleanup();
155
+ this.inputs = undefined;
156
+ }
157
+
158
+ if (this.cursor)
159
+ {
160
+ this.cursor.cleanup();
161
+ this.cursor = undefined;
162
+ }
163
+
164
+ if (this.display)
165
+ {
166
+ this.display.cleanup();
167
+ this.display.destroy_surfaces();
168
+ this.display = undefined;
169
+ }
170
+
171
+ this.cleanup();
172
+
173
+ if ("extra_channels" in this)
174
+ for (var e in this.extra_channels)
175
+ this.extra_channels[e].cleanup();
176
+ this.extra_channels = undefined;
177
+ }
@@ -0,0 +1,256 @@
1
+ "use strict";
2
+ /*
3
+ Copyright (C) 2012 by Jeremy P. White <jwhite@codeweavers.com>
4
+
5
+ This file is part of spice-html5.
6
+
7
+ spice-html5 is free software: you can redistribute it and/or modify
8
+ it under the terms of the GNU Lesser General Public License as published by
9
+ the Free Software Foundation, either version 3 of the License, or
10
+ (at your option) any later version.
11
+
12
+ spice-html5 is distributed in the hope that it will be useful,
13
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
14
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
+ GNU Lesser General Public License for more details.
16
+
17
+ You should have received a copy of the GNU Lesser General Public License
18
+ along with spice-html5. If not, see <http://www.gnu.org/licenses/>.
19
+ */
20
+
21
+ /*----------------------------------------------------------------------------
22
+ ** crc logic from rfc2083 ported to Javascript
23
+ **--------------------------------------------------------------------------*/
24
+
25
+ var rfc2083_crc_table = Array(256);
26
+ var rfc2083_crc_table_computed = 0;
27
+ /* Make the table for a fast CRC. */
28
+ function rfc2083_make_crc_table()
29
+ {
30
+ var c;
31
+ var n, k;
32
+ for (n = 0; n < 256; n++)
33
+ {
34
+ c = n;
35
+ for (k = 0; k < 8; k++)
36
+ {
37
+ if (c & 1)
38
+ c = ((0xedb88320 ^ (c >>> 1)) >>> 0) & 0xffffffff;
39
+ else
40
+ c = c >>> 1;
41
+ }
42
+ rfc2083_crc_table[n] = c;
43
+ }
44
+
45
+ rfc2083_crc_table_computed = 1;
46
+ }
47
+
48
+ /* Update a running CRC with the bytes buf[0..len-1]--the CRC
49
+ should be initialized to all 1's, and the transmitted value
50
+ is the 1's complement of the final running CRC (see the
51
+ crc() routine below)). */
52
+
53
+ function rfc2083_update_crc(crc, u8buf, at, len)
54
+ {
55
+ var c = crc;
56
+ var n;
57
+
58
+ if (!rfc2083_crc_table_computed)
59
+ rfc2083_make_crc_table();
60
+
61
+ for (n = 0; n < len; n++)
62
+ {
63
+ c = rfc2083_crc_table[(c ^ u8buf[at + n]) & 0xff] ^ (c >>> 8);
64
+ }
65
+
66
+ return c;
67
+ }
68
+
69
+ function rfc2083_crc(u8buf, at, len)
70
+ {
71
+ return rfc2083_update_crc(0xffffffff, u8buf, at, len) ^ 0xffffffff;
72
+ }
73
+
74
+ function crc32(mb, at, len)
75
+ {
76
+ var u8 = new Uint8Array(mb);
77
+ return rfc2083_crc(u8, at, len);
78
+ }
79
+
80
+ function PngIHDR(width, height)
81
+ {
82
+ this.width = width;
83
+ this.height = height;
84
+ this.depth = 8;
85
+ this.type = 6;
86
+ this.compression = 0;
87
+ this.filter = 0;
88
+ this.interlace = 0;
89
+ }
90
+
91
+ PngIHDR.prototype =
92
+ {
93
+ to_buffer: function(a, at)
94
+ {
95
+ at = at || 0;
96
+ var orig = at;
97
+ var dv = new SpiceDataView(a);
98
+ dv.setUint32(at, this.buffer_size() - 12); at += 4;
99
+ dv.setUint8(at, 'I'.charCodeAt(0)); at++;
100
+ dv.setUint8(at, 'H'.charCodeAt(0)); at++;
101
+ dv.setUint8(at, 'D'.charCodeAt(0)); at++;
102
+ dv.setUint8(at, 'R'.charCodeAt(0)); at++;
103
+ dv.setUint32(at, this.width); at += 4;
104
+ dv.setUint32(at, this.height); at += 4;
105
+ dv.setUint8(at, this.depth); at++;
106
+ dv.setUint8(at, this.type); at++;
107
+ dv.setUint8(at, this.compression); at++;
108
+ dv.setUint8(at, this.filter); at++;
109
+ dv.setUint8(at, this.interlace); at++;
110
+ dv.setUint32(at, crc32(a, orig + 4, this.buffer_size() - 8)); at += 4;
111
+ return at;
112
+ },
113
+ buffer_size: function()
114
+ {
115
+ return 12 + 13;
116
+ }
117
+ }
118
+
119
+
120
+ function adler()
121
+ {
122
+ this.s1 = 1;
123
+ this.s2 = 0;
124
+ }
125
+
126
+ adler.prototype.update = function(b)
127
+ {
128
+ this.s1 += b;
129
+ this.s1 %= 65521;
130
+ this.s2 += this.s1;
131
+ this.s2 %= 65521;
132
+ }
133
+
134
+ function PngIDAT(width, height, bytes)
135
+ {
136
+ if (bytes.byteLength > 65535)
137
+ {
138
+ throw new Error("Cannot handle more than 64K");
139
+ }
140
+ this.data = bytes;
141
+ this.width = width;
142
+ this.height = height;
143
+ }
144
+
145
+ PngIDAT.prototype =
146
+ {
147
+ to_buffer: function(a, at)
148
+ {
149
+ at = at || 0;
150
+ var orig = at;
151
+ var x, y, i, j;
152
+ var dv = new SpiceDataView(a);
153
+ var zsum = new adler();
154
+ dv.setUint32(at, this.buffer_size() - 12); at += 4;
155
+ dv.setUint8(at, 'I'.charCodeAt(0)); at++;
156
+ dv.setUint8(at, 'D'.charCodeAt(0)); at++;
157
+ dv.setUint8(at, 'A'.charCodeAt(0)); at++;
158
+ dv.setUint8(at, 'T'.charCodeAt(0)); at++;
159
+
160
+ /* zlib header. */
161
+ dv.setUint8(at, 0x78); at++;
162
+ dv.setUint8(at, 0x01); at++;
163
+
164
+ /* Deflate header. Specifies uncompressed, final bit */
165
+ dv.setUint8(at, 0x80); at++;
166
+ dv.setUint16(at, this.data.byteLength + this.height); at += 2;
167
+ dv.setUint16(at, ~(this.data.byteLength + this.height)); at += 2;
168
+ var u8 = new Uint8Array(this.data);
169
+ for (i = 0, y = 0; y < this.height; y++)
170
+ {
171
+ /* Filter type 0 - uncompressed */
172
+ dv.setUint8(at, 0); at++;
173
+ zsum.update(0);
174
+ for (x = 0; x < this.width && i < this.data.byteLength; x++)
175
+ {
176
+ zsum.update(u8[i]);
177
+ dv.setUint8(at, u8[i++]); at++;
178
+ zsum.update(u8[i]);
179
+ dv.setUint8(at, u8[i++]); at++;
180
+ zsum.update(u8[i]);
181
+ dv.setUint8(at, u8[i++]); at++;
182
+ zsum.update(u8[i]);
183
+ dv.setUint8(at, u8[i++]); at++;
184
+ }
185
+ }
186
+
187
+ /* zlib checksum. */
188
+ dv.setUint16(at, zsum.s2); at+=2;
189
+ dv.setUint16(at, zsum.s1); at+=2;
190
+
191
+ /* FIXME - something is not quite right with the zlib code;
192
+ you get an error from libpng if you open the image in
193
+ gimp. But it works, so it's good enough for now... */
194
+
195
+ dv.setUint32(at, crc32(a, orig + 4, this.buffer_size() - 8)); at += 4;
196
+ return at;
197
+ },
198
+ buffer_size: function()
199
+ {
200
+ return 12 + this.data.byteLength + this.height + 4 + 2 + 1 + 2 + 2;
201
+ }
202
+ }
203
+
204
+
205
+ function PngIEND()
206
+ {
207
+ }
208
+
209
+ PngIEND.prototype =
210
+ {
211
+ to_buffer: function(a, at)
212
+ {
213
+ at = at || 0;
214
+ var orig = at;
215
+ var i;
216
+ var dv = new SpiceDataView(a);
217
+ dv.setUint32(at, this.buffer_size() - 12); at += 4;
218
+ dv.setUint8(at, 'I'.charCodeAt(0)); at++;
219
+ dv.setUint8(at, 'E'.charCodeAt(0)); at++;
220
+ dv.setUint8(at, 'N'.charCodeAt(0)); at++;
221
+ dv.setUint8(at, 'D'.charCodeAt(0)); at++;
222
+ dv.setUint32(at, crc32(a, orig + 4, this.buffer_size() - 8)); at += 4;
223
+ return at;
224
+ },
225
+ buffer_size: function()
226
+ {
227
+ return 12;
228
+ }
229
+ }
230
+
231
+
232
+ function create_rgba_png(width, height, bytes)
233
+ {
234
+ var i;
235
+ var ihdr = new PngIHDR(width, height);
236
+ var idat = new PngIDAT(width, height, bytes);
237
+ var iend = new PngIEND;
238
+
239
+ var mb = new ArrayBuffer(ihdr.buffer_size() + idat.buffer_size() + iend.buffer_size());
240
+ var at = ihdr.to_buffer(mb);
241
+ at = idat.to_buffer(mb, at);
242
+ at = iend.to_buffer(mb, at);
243
+
244
+ var u8 = new Uint8Array(mb);
245
+ var str = "";
246
+ for (i = 0; i < at; i++)
247
+ {
248
+ str += "%";
249
+ if (u8[i] < 16)
250
+ str += "0";
251
+ str += u8[i].toString(16);
252
+ }
253
+
254
+
255
+ return "%89PNG%0D%0A%1A%0A" + str;
256
+ }