<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<HTML LANG="ja">
<HEAD>
<META HTTP-EQUIV="Content-Type" CONTENT="text/html;charset=Shift_JIS">
<TITLE>フルカラー GIF 作成</TITLE>
</HEAD>
<BODY BGCOLOR="#CCFFFF">
<CENTER>
<BR>
<B>フルカラー GIF 作成</B>
<BR><BR>
<FORM>
<TABLE><TR><TD>
入力ファイル(PNG):<BR>
<INPUT TYPE=FILE ID="file" SIZE=60 onChange="in_sel()">
</TD></TR></TABLE>
<DIV STYLE="position:relative; width:100px; height:100px; background-color:white">
<IMG ID="in_img" WIDTH=1 HEIGHT=1 STYLE="position:absolute; visibility:hidden">
</DIV>
<SPAN ID="size"></SPAN>
<BR><BR>
<INPUT TYPE=BUTTON ID="cre" VALUE="作成" DISABLED onClick="create()">
<BR><BR>
<SPAN ID="info"></SPAN>
<BR>
<DIV STYLE="position:relative; width:100px; height:100px; background-color:white">
<IMG ID="out_img" WIDTH=1 HEIGHT=1 STYLE="position:absolute; visibility:hidden">
</DIV>
<TABLE><TR><TD>
<INPUT TYPE=BUTTON ID="view" VALUE="表示" DISABLED onClick="view_gif()">
<INPUT TYPE=BUTTON ID="save" VALUE="保存" DISABLED onClick="save_gif()">
</TD></TR></TABLE>
<BR><BR>
<SPAN ID="msg1"></SPAN>
<BR>
<SPAN ID="msg2"></SPAN>
<BR>
<INPUT TYPE=BUTTON ID="can" VALUE="中止" onClick="cancel = true" STYLE="visibility:hidden">
</FORM>
</CENTER>
<SCRIPT LANGUAGE="JavaScript1.5" TYPE="text/javascript">
<!--
function in_sel() {
elem_in_img.style.visibility = "hidden";
elem_size.textContent = "";
elem_cre.disabled = true;
elem_info.textContent = "";
elem_out_img.style.visibility = "hidden";
elem_view.disabled = true;
elem_save.disabled = true;
// Firefox 3 では FileUpload オブジェクトの value プロパティにパスが付かなくなった.
// そのためファイルのパスを指定して内容を読み込むことができなくなった.
// 代わりに,FileUpload オブジェクトに新設された files プロパティ(nsIDOMFileList)を使って
// ファイルの内容を読み込むことができるようになったので,files プロパティが存在すれば files
// を使い,存在しなければ以前のままの処理とするように修正した.
// Firefox 7 では nsIDOMFile オブジェクトの getAsBinary(),getAsDataURL() 等が削除された.
// 代わりに,Firefox 3.6 で新設された FileReader オブジェクトを使って同様のことができるので,
// FileReader オブジェクトが使えればそれを使い,使えなければ以前のままの処理とするように修正
// した.
// 結果的に,現在のバージョンでは次のような処理になっている.
// FileUpload オブジェクトの files プロパティ
// + 存在しない
// | ↓
// | FileUpload オブジェクトの value プロパティを使う
// |
// + 存在する
// ↓
// FileReader オブジェクト
// + 使える
// | ↓
// | FileReader オブジェクトを使う
// |
// + 使えない
// ↓
// getAsBinary(),getAsDataURL() を使う
if(elem_file.files == undefined) {
file = "file:///" + elem_file.value;
var req = new XMLHttpRequest();
req.open("GET", file, false);
//req.overrideMimeType("text/plain;charset=UNKNOWN-8BIT");
req.overrideMimeType("text/plain;charset=x-user-defined");
try {
req.send(null);
}
catch(e) {
alert(e.message);
return;
}
text = req.responseText;
}
else {
if(typeof FileReader == "function") {
file_reader = new FileReader();
file_reader.onload = in_sel_fr1;
file_reader.readAsDataURL(elem_file.files.item(0));
return;
}
else {
file = elem_file.files.item(0).getAsDataURL();
text = elem_file.files.item(0).getAsBinary();
}
}
in_sel2();
}
function in_sel_fr1() {
file = file_reader.result;
file_reader.onload = in_sel_fr2;
file_reader.readAsBinaryString(elem_file.files.item(0));
}
function in_sel_fr2() {
text = file_reader.result;
file_reader = undefined;
in_sel2();
}
function in_sel2() {
if(text.length < 8) {
alert("入力ファイルは PNG 形式ではありません。");
return;
}
in_data = new Array();
in_data.length = text.length;
for(var i = 0; i < text.length; i++)
in_data[i] = text.charCodeAt(i) & 0xff;
// signature
if(data_str(in_data, 0, 8) != "\x89PNG\x0d\x0a\x1a\x0a") {
alert("入力ファイルは PNG 形式ではありません。");
return;
}
// ヘッダ取得
var err = true;
if(in_data.length >= 20 /* 8+4+4+4 */) {
if(data_str(in_data, 12, 4) == "IHDR") { // Chunk Type
var len = data_int4(in_data, 8);
if(20 + len <= in_data.length) {
if(len >= 13) {
// CRC チェック
if(chunk_crc(in_data, 12, 4 + len) == data_int4(in_data, 16 + len)) {
// Width,Height の最大は (2**31)-1
img_width = data_int4(in_data, 16); // Width
img_height = data_int4(in_data, 20); // Height
img_depth = in_data[24]; // Bit depth
img_color = in_data[25]; // Color type
img_comp = in_data[26] // Compression method
img_filter = in_data[27]; // Filter method
img_inter = in_data[28]; // Interlace method
if(img_width > 0 && img_height > 0) {
switch(img_color) {
case 2: // RGB
break;
case 6: // RGB + アルファ
alert("アルファ チャネルは無視されます。");
break;
default:
alert("RGB カラー以外は使用できません。");
return;
}
switch(img_depth) {
case 8:
case 16:
if(img_comp != 0) {
alert("Deflate 以外の圧縮方法は使用できません。");
return;
}
if(img_inter) {
alert("インタレースは使用できません。");
return;
}
err = false;
break;
}
}
}
}
}
}
}
if(err) {
alert("入力ファイルの内容に誤りがあります。");
return;
}
// PNG サムネイル表示
with(elem_in_img) {
if(img_width > 100 || img_height > 100) {
if(img_width > img_height) {
width = 100;
height = Math.floor(100 * img_height / img_width);
}
else {
height = 100;
width = Math.floor(100 * img_width / img_height);
}
}
else {
width = img_width;
height = img_height;
}
style.left = String((100 - width) >> 1) + "px";
style.top = String((100 - height) >> 1) + "px";
style.visibility = "visible";
src = file;
}
elem_size.textContent = String(img_width) + "x" + String(img_height);
elem_cre.disabled = false;
}
// 作成開始
function create() {
elem_cre.disabled = true;
elem_view.disabled = true;
elem_save.disabled = true;
elem_msg1.textContent = "読み込み中...";
elem_can.style.visibility = "visible";
cancel = false;
setTimeout(read_png, 0);
}
// 表示
function view_gif() {
open("data:image/gif;base64," + gif_data, "view");
}
// 保存
function save_gif() {
location.href = "data:application/octet-stream;base64," + gif_data;
}
//----------------------------------------------------------
// PNG データ読み込み
function read_png() {
var err;
def_data = [];
transp = "";
var p;
for(p = 8; p < in_data.length; ) {
// 次のチャンク
var len;
err = true;
if(p + 12 <= in_data.length) {
len = data_int4(in_data, p);
if(p + 12 + len <= in_data.length) {
// CRC チェック
if(chunk_crc(in_data, p + 4, 4 + len) == data_int4(in_data, p + 8 + len))
err = false;
}
}
if(err)
break;
switch(data_str(in_data, p + 4, 4)) { // Chunk Type
case "IEND":
p = Infinity;
continue;
case "IDAT":
def_data = def_data.concat(in_data.slice(p + 8, p + 8 + len));
break;
case "tRNS":
if(len != 6) {
err = true;
break;
}
var p2 = p + 8;
if(img_depth == 16) {
transp_r2 = in_data[p2 + 1];
transp_g2 = in_data[p2 + 3];
transp_b2 = in_data[p2 + 5];
}
else {
p2++;
}
transp = String.fromCharCode(((in_data[p2] & 0x0f) << 8) | in_data[p2 + 2])
+ String.fromCharCode(((in_data[p2] & 0xf0) << 4) | in_data[p2 + 4] | 0x1000);
break;
case "gAMA":
alert("ガンマ補正は無視されます。");
break;
}
if(err)
break;
p += 12 + len;
}
err = true;
if(!isFinite(p)) { // エラーなし(IEND まで到達)
i_data = 0;
var cmf, flg;
if((cmf = get_def_byte()) != null) { // CMF
if((flg = get_def_byte()) != null) { // FLG
if(!(((cmf << 8) | flg) % 31)) { // FCHECK チェック
if(cmf == 0x78) { // Deflate & ウィンドウ サイズ 32K
if(!(flg & 0x20)) // プリセット辞書なし
err = false;
}
}
}
}
}
if(err) {
alert("入力ファイルの内容に誤りがあります。");
end_proc();
return;
}
setTimeout(read_png2, 0);
}
function read_png2() {
if(cancel) {
end_proc();
return;
}
var i;
// Inflate
inf_data = [];
n_bits = 0;
var sts;
for(; ; ) {
sts = false;
var bfinal;
if((bfinal = get_bits(1)) == null)
break;
var btype;
if((btype = get_bits(2)) == null)
break;
switch(btype) {
case 0x0: // no compression
n_bits = 0;
var buff;
if((buff = get_def_bytes(4)) != null) { // LEN, NLEN
if((buff = get_def_bytes(buff[0] | (buff[1] << 8))) != null) {
sts = true;
inf_data = inf_data.concat(buff);
}
}
break;
case 0x1: // compressed with fixed Huffman codes
sts = fixed();
break;
case 0x2: // compressed with dynamic Huffman codes
sts = dynamic();
break;
}
if(!sts)
break;
if(bfinal)
break;
}
if(sts) {
// Adler-32 チェック
var adler32;
if((adler32 = get_def_bytes(4)) == null) { // ADLER32
sts = false;
}
else {
var s1 = 1;
var s2 = 0;
for(i = 0; i < inf_data.length; i++) {
s1 = (s1 + inf_data[i]) % 65521;
s2 = (s2 + s1) % 65521;
}
if((s1 | (s2 << 16)) != data_int4(adler32, 0))
sts = false;
}
}
def_data = undefined;
if(!sts) {
alert("入力ファイルの内容に誤りがあります。");
end_proc();
return;
}
png_bpc = (img_depth == 16) ? 2 : 1; // バイト数/1 原色
png_bpp = ((img_color == 2) ? 3 : 4) * png_bpc; // バイト数/ピクセル
png_bpl = img_width * png_bpp; // バイト数/ライン
png_stride = png_bpl + 1; // ライン間バイト数
i_data = 0;
// 画像 インデックス配列
pixels = new Array();
pixels.length = img_height;
for(i = 0; i < img_height; i++) {
pixels[i] = new Array();
pixels[i].length = img_width;
}
colors = "";
frames = [];
elem_msg1.textContent = "色情報収集中...";
setTimeout(color_index, 0, 0);
}
// チャンクの CRC を計算する.
// X32+X26+X23+X22+X16+X12+X11+X10+X8+X7+X5+X4+X2+X+1
// 結果の MSB が 1 の場合,Integer の値としては負になる.
function chunk_crc(data, index, len) {
var rem = ~0;
for(; len; len--) {
var data2 = rem & 0xff ^ data[index++];
data2 ^= (data2 << 6) & 0xff;
rem = ((rem >>> 8) | (data2 << 24))
^ (data2 >> 2) ^ (data2 << 1) ^ (data2 << 2) ^ (data2 << 8) ^ (data2 << 12)
^ (data2 << 13) ^ (data2 << 14) ^ (data2 << 16) ^ (data2 << 17) ^ (data2 << 19)
^ (data2 << 20) ^ (data2 << 22) ^ (data2 << 23);
}
return ~rem;
}
// バイト配列から文字列を取り出す.
function data_str(data, index, len) {
var str = "";
for(var i = 0; i < len; i++)
str += String.fromCharCode(data[index++]);
return str;
}
// バイト配列から 4 バイト Integer を取り出す.
// 結果の MSB が 1 の場合,Integer の値としては負になる.
function data_int4(data, index) {
return (data[index] << 24) | (data[index + 1] << 16) | (data[index + 2] << 8) | data[index + 3];
}
//----------------------------------------------------------
// fixed Huffman codes
function fixed() {
for(; ; ) {
var bits;
if(!get_huff(7))
return false;
if(huff == 0x00) // 256 = end-of-block
return true;
var i_len;
if (huff < 0x18) { // 257 - 279
i_len = huff - 1;
}
else if(huff < 0x60) { // 0 - 143
if(!append_huff(1))
return false;
inf_data.push(huff - 0x30);
continue;
}
else if(huff < 0x64) { // 280 - 287
if(!append_huff(1))
return false;
i_len = huff - 0xc0 + (280 - 257);
}
else { // 144 - 255
if(!append_huff(2))
return false;
inf_data.push(huff - 0x190 + 144);
continue;
}
if((bits = get_bits(len_def[i_len][1]/*extra bits*/)) == null)
return false;
var length = len_def[i_len][0]/*length*/ + bits;
if(!get_huff(5))
return false;
if((bits = get_bits(dist_def[huff][1]/*extra bits*/)) == null)
return false;
copy_def_bytes(dist_def[huff][0]/*distance*/ + bits, length);
}
}
// dynamic Huffman codes
function dynamic() {
var hlit;
if((hlit = get_bits(5)) == null) // HLIT
return false;
var hdist;
if((hdist = get_bits(5)) == null) // HDIST
return false;
var hclen;
if((hclen = get_bits(4)) == null) // HCLEN
return false;
var code;
var repeat;
var bit_len;
var bit_len2;
var prev_len;
var bits;
var i;
// code length codes
var len_code = [];
for(i = 0; i < hclen + 4; i++) {
if((bits = get_bits(3)) == null)
return false;
if(bits)
len_code.push({bit_len:bits, code:len_code_def[i]});
}
var len_dec = pre_decode(len_code);
var lit_code = [];
repeat = 0;
for(code = 0; code < hlit + 257; ) {
bit_len = 0;
huff = 0;
for(i = 0; i < len_dec.length; i++) {
if(!append_huff(len_code[len_dec[i].i_code].bit_len - bit_len))
return false;
if(huff < len_dec[i].from + len_dec[i].count) {
switch(bit_len2 = len_code[len_dec[i].i_code + (huff - len_dec[i].from)].code) {
case 0:
code++;
break;
case 16:
if((bits = get_bits(2)) == null)
return false;
for(repeat = bits + 3; repeat; repeat--) {
if(code > hlit + 257)
break;
lit_code.push({bit_len:prev_len, code:code++});
}
break;
case 17:
if((bits = get_bits(3)) == null)
return false;
if((code += bits + 3) > hlit + 257) {
repeat = code - (hlit + 257);
prev_len = 0;
}
break;
case 18:
if((bits = get_bits(7)) == null)
return false;
if((code += bits + 11) > hlit + 257) {
repeat = code - (hlit + 257);
prev_len = 0;
}
break;
default:
lit_code.push({bit_len:prev_len = bit_len2, code:code++});
break;
}
break;
}
bit_len = len_code[len_dec[i].i_code].bit_len;
}
}
var dist_code = [];
code = 0;
if(repeat) {
if(prev_len) {
for(; repeat; repeat--)
dist_code.push({bit_len:prev_len, code:code++});
}
else {
code = repeat;
}
}
for(; code < hdist + 1; ) {
bit_len = 0;
huff = 0;
for(i = 0; i < len_dec.length; i++) {
if(!append_huff(len_code[len_dec[i].i_code].bit_len - bit_len))
return false;
if(huff < len_dec[i].from + len_dec[i].count) {
switch(bit_len2 = len_code[len_dec[i].i_code + (huff - len_dec[i].from)].code) {
case 0:
code++;
break;
case 16:
if((bits = get_bits(2)) == null)
return false;
for(repeat = bits + 3; repeat; repeat--)
dist_code.push({bit_len:prev_len, code:code++});
break;
case 17:
if((bits = get_bits(3)) == null)
return false;
code += bits + 3;
break;
case 18:
if((bits = get_bits(7)) == null)
return false;
code += bits + 11;
break;
default:
dist_code.push({bit_len:prev_len = bit_len2, code:code++});
break;
}
break;
}
bit_len = len_code[len_dec[i].i_code].bit_len;
}
}
var lit_dec = pre_decode(lit_code);
var dist_dec = pre_decode(dist_code);
for(; ; ) {
// length
bit_len = 0;
huff = 0;
for(i = 0; i < lit_dec.length; i++) {
if(!append_huff(lit_code[lit_dec[i].i_code].bit_len - bit_len))
return false;
if(huff < lit_dec[i].from + lit_dec[i].count) {
if((code = lit_code[lit_dec[i].i_code + (huff - lit_dec[i].from)].code) == 256) // end-of-block
return true;
if(code < 256) {
inf_data.push(code);
}
else {
var i_len = code - 257;
if((bits = get_bits(len_def[i_len][1]/*extra bits*/)) == null)
return false;
var length = len_def[i_len][0]/*length*/ + bits;
// distance
bit_len = 0;
huff = 0;
for(i = 0; i < dist_dec.length; i++) {
if(!append_huff(dist_code[dist_dec[i].i_code].bit_len - bit_len))
return false;
if(huff < dist_dec[i].from + dist_dec[i].count) {
var i_dist = dist_code[dist_dec[i].i_code + (huff - dist_dec[i].from)].code;
if((bits = get_bits(dist_def[i_dist][1]/*extra bits*/)) == null)
return false;
copy_def_bytes(dist_def[i_dist][0]/*distance*/ + bits, length);
break;
}
bit_len = dist_code[dist_dec[i].i_code].bit_len;
}
}
break;
}
bit_len = lit_code[lit_dec[i].i_code].bit_len;
}
}
}
// デコード用ワーク作成
function pre_decode(code) {
code.sort(pre_decode_comp);
// i_code:コード テーブル インデックス, from:先頭コード, count:コード数
var dec = [];
var bit_len = 0;
var count = 0;
var huff = 0;
for(var i = 0; i < code.length; i++) {
if(code[i].bit_len > bit_len) {
if(i) {
dec[dec.length - 1].count = count;
huff = (huff + count) << 1;
huff <<= code[i].bit_len - bit_len - 1;
count = 0;
}
dec.push({i_code:i, from:huff, count:0});
bit_len = code[i].bit_len;
}
count++;
}
dec[dec.length - 1].count = count;
return dec;
}
function pre_decode_comp(elm1, elm2) {
if(elm1.bit_len < elm2.bit_len
|| elm1.bit_len == elm2.bit_len && elm1.code < elm2.code)
return -1;
return 1;
}
function get_bits(n) {
var bits = 0;
var bit = 0;
for(; n; n--) {
if(!n_bits) {
if((def_byte = get_def_byte()) == null)
return null;
n_bits = 8;
}
bits |= (def_byte & 0x01) << bit;
def_byte >>= 1;
n_bits--;
bit++;
}
return bits;
}
function get_huff(n) {
huff = 0;
return append_huff(n);
}
function append_huff(n) {
for(; n; n--) {
if(!n_bits) {
if((def_byte = get_def_byte()) == null)
return false;
n_bits = 8;
}
huff = (huff << 1) | def_byte & 0x01;
def_byte >>= 1;
n_bits--;
}
return true;
}
function get_def_bytes(len) {
if(i_data + len > def_data.length)
return null;
var i = i_data;
i_data += len;
return def_data.slice(i, i_data);
}
function get_def_byte() {
if(i_data == def_data.length)
return null;
return def_data[i_data++];
}
function copy_def_bytes(dist, length) {
var i = inf_data.length - dist;
for(; length; length--)
inf_data.push(inf_data[i++]);
}
//----------------------------------------------------------
// カラー テーブル作成 & インデックス化
function color_index(y) {
if(cancel) {
end_proc();
return;
}
var cnt;
var x;
var i;
// フィルタ
switch(inf_data[i_data++]) { // filter type
case 1: // Sub
i = i_data + png_bpp;
for(cnt = png_bpl - png_bpp; cnt; cnt--, i++)
inf_data[i] = (inf_data[i - png_bpp] + inf_data[i]) & 0xff;
break;
case 2: // Up
if(!y)
break;
i = i_data;
for(cnt = png_bpl; cnt; cnt--, i++)
inf_data[i] = (inf_data[i - png_stride] + inf_data[i]) & 0xff;
break;
case 3: // Average
i = i_data;
for(x = 0; x < png_bpl; x++, i++)
inf_data[i] = (((((x >= png_bpp) ? inf_data[i - png_bpp] : 0)
+ ((y) ? inf_data[i - png_stride] : 0)) >> 1) + inf_data[i]) & 0xff;
break;
case 4: // Paeth
i = i_data;
for(x = 0; x < png_bpl; x++, i++) {
var a = (x >= png_bpp) ? inf_data[i - png_bpp] : 0;
var b = (y) ? inf_data[i - png_stride] : 0;
var c = (y && x >= png_bpp) ? inf_data[i - png_stride - png_bpp] : 0;
var p = a + b - c;
var pa = Math.abs(p - a);
var pb = Math.abs(p - b);
var pc = Math.abs(p - c);
var d;
if(pa <= pb && pa <= pc)
d = a;
else if(pb <= pc)
d = b;
else
d = c;
inf_data[i] = (d + inf_data[i]) & 0xff;
}
break;
}
// 色集積 & インデックス化
for(x = 0; x < img_width; x++) {
var color = String.fromCharCode(((inf_data[i_data] & 0x0f) << 8) | inf_data[i_data + png_bpc])
+ String.fromCharCode(((inf_data[i_data] & 0xf0) << 4) | inf_data[i_data + (png_bpc << 1)] | 0x1000);
if((color == transp) ? ((img_depth == 16) ? (inf_data[i_data + 1] == transp_r2
&& inf_data[i_data + 3] == transp_g2
&& inf_data[i_data + 5] == transp_b2) : true) : false) { // 透過色
pixels[y][x] = -1; // すべてのフレームで透過になる
}
else {
i = colors.lastIndexOf(color);
if(i == -1) {
i = colors.length;
if(i == 131072 /* 65536*2 */) {
alert("使用色数が多すぎます。");
end_proc();
return;
}
colors += color;
if(!(i % 510 /* 255*2 */)) // フレーム数増加
//frames.push({x_min:Infinity, y_min:y, x_max:0, y_max:0});
// IE 対策
// IE では,フレームの幅が画像全体の幅と一致していないと表示が一部欠ける
frames.push({x_min:0, y_min:y, x_max:img_width - 1, y_max:0});
}
pixels[y][x] = i >> 1;
with(frames[Math.floor(i / 510 /* 255*2 */)]) {
y_max = y;
if(x < x_min)
x_min = x;
if(x > x_max)
x_max = x;
}
}
i_data += png_bpp;
}
if(++y == img_height) {
inf_data = undefined;
create_gif();
}
else {
setTimeout(color_index, 0, y);
}
}
//----------------------------------------------------------
// GIF 作成
function create_gif() {
if(!colors.length) {
alert("使用色数が 0 です。");
end_proc();
return;
}
if(colors.length <= 512) {
if(!confirm("使用色数が 256 以下です。GIF 作成を続けますか?")) {
end_proc();
return;
}
}
elem_info.textContent = String(colors.length >> 1) + "色 " + String(frames.length) + "フレーム";
elem_msg1.textContent = "GIF 画像作成中...";
setTimeout(create_gif2, 0);
}
function create_gif2() {
gif_data = "";
b64_buff = "";
gif_header[I_SCREEN_WIDTH ] = img_width & 0xff;
gif_header[I_SCREEN_WIDTH + 1] = img_width >> 8;
gif_header[I_SCREEN_HEIGHT ] = img_height & 0xff;
gif_header[I_SCREEN_HEIGHT + 1] = img_height >> 8;
// Header,Logical Screen Descriptor
for(var i = 0; i < gif_header.length; i++)
putc(gif_header[i]);
i_color = 0;
i_frame = 0;
create_gif_frame();
}
// GIF 1 フレーム出力
function create_gif_frame() {
if(cancel) {
end_proc();
return;
}
with(frames[i_frame]) {
gif_frame[I_IMAGE_LEFT ] = x_min & 0xff;
gif_frame[I_IMAGE_LEFT + 1] = x_min >> 8;
gif_frame[I_IMAGE_TOP ] = y_min & 0xff;
gif_frame[I_IMAGE_TOP + 1] = y_min >> 8;
var w = x_max - x_min + 1;
gif_frame[I_IMAGE_WIDTH ] = w & 0xff;
gif_frame[I_IMAGE_WIDTH + 1] = w >> 8;
w = y_max - y_min + 1;
gif_frame[I_IMAGE_HEIGHT ] = w & 0xff;
gif_frame[I_IMAGE_HEIGHT + 1] = w >> 8;
}
elem_msg2.textContent = "フレーム " + String(i_frame + 1) + "/" + String(frames.length);
setTimeout(create_gif_frame2, 0);
}
function create_gif_frame2() {
var i;
// Graphic Control Extension,Image Descriptor
gif_frame[I_DELAY_TIME] = gif_frame[I_DELAY_TIME + 1] = (i_frame + 1 < frames.length) ? 0x00 : 0xff;
// 最終フレームのみ 65535,他は 0
for(i = 0; i < gif_frame.length; i++)
putc(gif_frame[i]);
// Local Color Table
i = i_color << 1;
for(var cnt = 255; cnt; cnt--) {
if(i == colors.length) { // 色テーブル終了
// ダミー
for(cnt *= 3; cnt; cnt--)
putc(0);
break;
}
var c1 = colors.charCodeAt(i++);
var c2 = colors.charCodeAt(i++);
putc((c1 >> 8) | ((c2 & 0xf00) >> 4)); // R
putc(c1 & 0xff); // G
putc(c2 & 0xff); // B
}
putc(0);
putc(0);
putc(0);
// LZW Minimum Code Size
putc(8); // 8 ビット
// 辞書ルート初期化
for(i = 0; i < 256; i++) {
root_prop[i].chara = undefined;
root_prop[i].prop = undefined;
}
block_cnt = 0;
pack_buff = 0;
pack_bits = 0;
n_code = 258;
code_bits = 9;
max_n = 512;
put_code(256); // Clear
with(frames[i_frame]) {
dict_code = pixels[y_min][x_min] - i_color;
}
// 0 〜 254 以外は透過(255 はそのままでよい)
if(dict_code & ~0xff)
dict_code = 255;
dict_prop = root_prop[dict_code];
setTimeout(create_gif_frame3, 0, frames[i_frame].y_min);
}
function create_gif_frame3(y) {
if(cancel) {
end_proc();
return;
}
var frame = frames[i_frame];
var x = frame.x_min;
if(y == frame.y_min)
x++;
while(x <= frame.x_max) {
// 次の 1 ピクセル
var c = pixels[y][x++] - i_color;
// 0 〜 254 以外は透過(255 はそのままでよい)
if(c & ~0xff)
c = 255;
var cs = String.fromCharCode(c);
// 辞書サーチ
var i = -1;
if(dict_prop.chara != undefined)
i = dict_prop.chara.indexOf(cs);
if(i == -1) { // 未登録
put_code(dict_code);
if(n_code == 4095) { // 辞書が一杯(のひとつ前)
// 辞書クリア
put_code(256); // Clear
for(i = 0; i < 256; i++) {
root_prop[i].chara = undefined;
root_prop[i].prop = undefined;
}
n_code = 258;
code_bits = 9;
max_n = 512;
}
else {
// 辞書に登録
if(dict_prop.chara == undefined) {
dict_prop.chara = cs;
dict_prop.prop = [{code:n_code, chara:undefined, prop:undefined}];
}
else {
dict_prop.chara += cs;
dict_prop.prop.push({code:n_code, chara:undefined, prop:undefined});
}
if(n_code++ == max_n) {
code_bits++;
max_n <<= 1;
}
}
dict_code = c;
dict_prop = root_prop[c];
}
else { // 登録済み
dict_code = dict_prop.prop[i].code;
dict_prop = dict_prop.prop[i];
}
}
if(y == frame.y_max)
create_gif_frame4();
else
setTimeout(create_gif_frame3, 0, y + 1);
}
function create_gif_frame4() {
put_code(dict_code); // 最後のコード
put_code(257); // End of Information
// コード パック用バッファ フラッシュ
if(pack_bits)
put_code(0);
// ブロック バッファ フラッシュ
if(block_cnt) {
putc(block_cnt);
for(var i = 0; i < block_cnt; i++)
putc(block_buff[i]);
}
putc(0x00); // Block Terminator
if(++i_frame < frames.length) {
i_color += 255;
create_gif_frame();
return;
}
putc(0x3b); // Trailer
// BASE64 バッファ フラッシュ
if(b64_buff.length)
gif_data += btoa(b64_buff);
// GIF サムネイル表示
elem_out_img.width = elem_in_img.width;
elem_out_img.height = elem_in_img.height;
elem_out_img.style.left = elem_in_img.style.left;
elem_out_img.style.top = elem_in_img.style.top;
elem_out_img.src = "data:image/gif;base64," + gif_data;
elem_out_img.style.visibility = "visible";
end_proc();
elem_view.disabled = false;
elem_save.disabled = false;
}
// LZW コード出力
function put_code(code) {
pack_buff |= code << pack_bits;
pack_bits += code_bits;
while(pack_bits >= 8) {
block_buff[block_cnt++] = pack_buff & 0xff;
if(block_cnt == 255) {
putc(255);
for(var i = 0; i < 255; i++)
putc(block_buff[i]);
block_cnt = 0;
}
pack_buff >>= 8;
pack_bits -= 8;
}
}
// イメージ データに 1 バイト出力
function putc(c) {
b64_buff += String.fromCharCode(c);
if(b64_buff.length == 3) {
gif_data += btoa(b64_buff);
b64_buff = "";
}
}
//----------------------------------------------------------
// 作成終了
function end_proc() {
def_data = undefined;
inf_data = undefined;
colors = undefined;
frames = undefined;
pixels = undefined;
for(var i = 0; i < 256; i++) {
root_prop[i].chara = undefined;
root_prop[i].prop = undefined;
}
elem_msg1.textContent = "";
elem_msg2.textContent = "";
elem_can.style.visibility = "hidden";
elem_cre.disabled = false;
}
//----------------------------------------------------------
var in_data; // 入力ファイル データ
var img_width; // Width
var img_height; // Height
var img_depth; // Bit depth
var img_color; // Color type
var img_comp; // Compression method
var img_filter; // Filter method
var img_inter; // Interlace method
var cancel; // 中止フラグ
var def_data; // 圧縮データ
var inf_data; // 展開データ
var i_data; // データ インデックス
var transp; // transparent color
var transp_r2; // transparent color 16 ビットのときの LSB R
var transp_g2; // G
var transp_b2; // B
var png_bpc; // バイト数/1 原色
var png_bpp; // バイト数/ピクセル
var png_bpl; // バイト数/ライン
var png_stride; // ライン間バイト数
var def_byte; // 圧縮データ バッファ
var n_bits; // 圧縮データ バッファ格納ビット数
var huff; // ハフマン コード
// length code → length
// [length, extra bits]
var len_def = [
[ 3, 0], [ 4, 0], [ 5, 0], [ 6, 0], [ 7, 0], [ 8, 0], [ 9, 0], [ 10, 0],
[ 11, 1], [ 13, 1], [ 15, 1], [ 17, 1], [ 19, 2], [ 23, 2], [ 27, 2], [ 31, 2],
[ 35, 3], [ 43, 3], [ 51, 3], [ 59, 3], [ 67, 4], [ 83, 4], [ 99, 4], [115, 4],
[131, 5], [163, 5], [195, 5], [227, 5], [258, 0]
];
// distance code → distance
// [distance, extra bits]
var dist_def = [
[ 1, 0], [ 2, 0], [ 3, 0], [ 4, 0],
[ 5, 1], [ 7, 1], [ 9, 2], [ 13, 2],
[ 17, 3], [ 25, 3], [ 33, 4], [ 49, 4],
[ 65, 5], [ 97, 5], [ 129, 6], [ 193, 6],
[ 257, 7], [ 385, 7], [ 513, 8], [ 769, 8],
[ 1025, 9], [ 1537, 9], [ 2049, 10], [ 3073, 10],
[ 4097, 11], [ 6145, 11], [ 8193, 12], [12289, 12],
[16385, 13], [24577, 13]
];
// code length code
var len_code_def = [
16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15
];
var colors; // 色テーブル
var frames; // フレーム領域
var pixels; // 画像 インデックス配列
var i_color; // 色テーブル インデックス
var i_frame; // フレーム インデックス
// Header,Logical Screen Descriptor
gif_header = [
0x47, 0x49, 0x46, 0x38, 0x39, 0x61, // GIF89a
0x00, 0x00, 0x00, 0x00,
0x77, // 8 ビット,256色
0x00, // Background Color Index
0x00 // Pixel Aspect Ratio
];
I_SCREEN_WIDTH = 6;
I_SCREEN_HEIGHT = 8;
// Graphic Control Extension,Image Descriptor
gif_frame = [
0x21, // Extension Introducer
0xf9, // Graphic Control Label
4, // Block Size
0x05, // Do not dispose,Transparent Color
0x00, 0x00,
255, // Transparent Color Index
0, // Block Terminator
0x2c, // Image Separator
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x87, // Local Color Table,256色
];
I_DELAY_TIME = 4;
I_IMAGE_LEFT = 9;
I_IMAGE_TOP = 11;
I_IMAGE_WIDTH = 13;
I_IMAGE_HEIGHT = 15;
// 辞書ルート
root_prop = new Array();
root_prop.length = 256;
for(i = 0; i < 256; i++)
root_prop[i] = {chara:undefined, prop:undefined};
var n_code; // コード登録数
var code_bits; // コード ビット数
var max_n; // 最大コード登録数
var dict_code; // コード
var dict_prop; // 辞書情報
var pack_buff; // コード パック用バッファ
var pack_bits; // コード パック用バッファ格納ビット数
block_buff = new Array(); // ブロック バッファ
block_buff.length = 255;
var block_cnt; // ブロック バッファ格納バイト数
var gif_data; // GIF イメージ データ
var b64_buff; // BASE64 バッファ
elem_file = document.getElementById("file");
elem_in_img = document.getElementById("in_img");
elem_size = document.getElementById("size");
elem_cre = document.getElementById("cre");
elem_out_img = document.getElementById("out_img");
elem_info = document.getElementById("info");
elem_view = document.getElementById("view");
elem_save = document.getElementById("save");
elem_msg1 = document.getElementById("msg1");
elem_msg2 = document.getElementById("msg2");
elem_can = document.getElementById("can");
document.forms[0].reset(); // ページを再ロードしたとき,「入力ファイル」項目の内容が残ってしまうため
if(elem_file.files == undefined) {
if(location.protocol != "file:")
alert("お使いのブラウザでは、このプログラムをオンラインで実行することはできません。\n"
+ "プログラムをダウンロードして、ローカルのストレージから起動してください。");
}
//-->
</SCRIPT>
</BODY>
</HTML>
|