![]() |
<!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> |