spice-html5-rails 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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
+ }