spice-html5-rails 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/COPYING +674 -0
- data/COPYING.LESSER +165 -0
- data/LICENSE.txt +15 -0
- data/README.md +35 -0
- data/lib/spice-html5-rails/version.rb +7 -0
- data/lib/spice-html5-rails.rb +10 -0
- data/vendor/assets/javascripts/spice-html5.js +25 -0
- data/vendor/assets/javascripts/spiceHTML5/atKeynames.js +183 -0
- data/vendor/assets/javascripts/spiceHTML5/bitmap.js +51 -0
- data/vendor/assets/javascripts/spiceHTML5/cursor.js +92 -0
- data/vendor/assets/javascripts/spiceHTML5/display.js +806 -0
- data/vendor/assets/javascripts/spiceHTML5/enums.js +282 -0
- data/vendor/assets/javascripts/spiceHTML5/inputs.js +271 -0
- data/vendor/assets/javascripts/spiceHTML5/lz.js +166 -0
- data/vendor/assets/javascripts/spiceHTML5/main.js +177 -0
- data/vendor/assets/javascripts/spiceHTML5/png.js +256 -0
- data/vendor/assets/javascripts/spiceHTML5/quic.js +1335 -0
- data/vendor/assets/javascripts/spiceHTML5/spiceconn.js +455 -0
- data/vendor/assets/javascripts/spiceHTML5/spicedataview.js +96 -0
- data/vendor/assets/javascripts/spiceHTML5/spicemsg.js +883 -0
- data/vendor/assets/javascripts/spiceHTML5/spicetype.js +480 -0
- data/vendor/assets/javascripts/spiceHTML5/thirdparty/jsbn.js +589 -0
- data/vendor/assets/javascripts/spiceHTML5/thirdparty/prng4.js +79 -0
- data/vendor/assets/javascripts/spiceHTML5/thirdparty/rng.js +102 -0
- data/vendor/assets/javascripts/spiceHTML5/thirdparty/rsa.js +146 -0
- data/vendor/assets/javascripts/spiceHTML5/thirdparty/sha1.js +346 -0
- data/vendor/assets/javascripts/spiceHTML5/ticket.js +250 -0
- data/vendor/assets/javascripts/spiceHTML5/utils.js +261 -0
- data/vendor/assets/javascripts/spiceHTML5/wire.js +123 -0
- 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
|
+
}
|