effects.htm

戻る

<!DOCTYPE HTML>
<HTML LANG="ja">

<HEAD>
<META CHARSET="Shift_JIS">
<TITLE>特殊効果ビデオ</TITLE>
</HEAD>

<BODY onLoad="init()" STYLE="background-color:#CCFFFF">

<DIV STYLE="text-align:center">
<BR>
<B>特殊効果ビデオ</B>
<BR><BR>

<FORM>
<SELECT ID="sel" DISABLED onChange="sel_chg(this.selectedIndex)">
<OPTION SELECTED>ソラリゼーション
<OPTION>レリーフ
<OPTION>ラプラシアン 1
<OPTION>ラプラシアン 2
<OPTION>ポスタリゼーション 1
<OPTION>ポスタリゼーション 2
<OPTION>ポスタリゼーション 3
<OPTION>ガラス
<OPTION>スケッチ
<OPTION>ディザ 1
<OPTION>ディザ 2
<OPTION>ディザ 3
<OPTION>網点
<OPTION>波紋
<OPTION>回転
</SELECT>
</FORM>
<BR>

<CANVAS ID="view" WIDTH=1 HEIGHT=1></CANVAS>
</DIV>

<SCRIPT TYPE="text/javascript">
<!--

function init() {
  if(navigator.mozGetUserMedia == undefined)
    navigator.getUserMedia({video:true, audio:false}, success, error);
  else
    navigator.mozGetUserMedia({video:true, audio:false}, success, error);
}

function success(stream) {
  elem_video = document.createElement("VIDEO");
  elem_video.autoplay = true;
  elem_video.addEventListener("canplay", wait, false);
  elem_video.src = (window.URL == undefined || window.URL.createObjectURL == undefined)
                     ? stream : window.URL.createObjectURL(stream);
  elem_video.play();
}

function error(err) {
  alert("カメラが使用できません");
}

function wait() {
  if(elem_video.videoWidth) {
    video_width_h = (video_width = elem_video.videoWidth) >> 1;
    video_height_h = (video_height = elem_video.videoHeight) >> 1;
    with(elem_view = document.getElementById("view")) {
      width  = video_width;
      height = video_height;
      style.width  = String(video_width)  + "px";
      style.height = String(video_height) + "px";
      ctx_view = getContext("2d");
    }
    with(work_imgdat = ctx_view.getImageData(0, 0, video_width, video_height)) {
      dev_width_h = (dev_width = width) >> 1;
      dev_height_h = (dev_height = height) >> 1;
    }

    data_len = (dev_size = dev_width * dev_height) << 2;

    // ガラス,波紋 用
    glass_rnd = new Uint32Array(dev_size);
    ripple_pha = new Uint8Array(dev_size);
    ripple_att = new Float32Array(dev_size);
    var i = 0;
    for(var y = 0; y < dev_height; y++) {
      var dist_y = y - dev_height_h;
      dist_y *= dist_y;

      for(var x = 0; x < dev_width; x++) {
        var rnd_x = x + (Math.floor(Math.random() * 41) - 20);
        if(rnd_x < 0)
          rnd_x = 0;
        else if(rnd_x >= dev_width)
          rnd_x = dev_width - 1;
        var rnd_y = y + (Math.floor(Math.random() * 41) - 20);
        if(rnd_y < 0)
          rnd_y = 0;
        else if(rnd_y >= dev_height)
          rnd_y = dev_height - 1;
        glass_rnd[i] = (rnd_y * (dev_width + 6) + rnd_x) << 2;

        var dist = x - dev_width_h;
        dist = Math.sqrt(dist * dist + dist_y);
        ripple_pha[i] = Math.round(dist * 8) & 0xff;
        ripple_att[i] = (dist < 32) ? dist : dist * Math.sqrt(dist / 32);

        i++;
      }
    }

    // 回転 用
    ctx_view.beginPath();
    ctx_view.arc(video_width_h, video_height_h,
                 ((video_width < video_height) ? video_width_h : video_height_h) - 10, 0, PI2, false);
    ctx_view.closePath();

    work_data = new Uint8Array(work_imgdat.data.buffer, 0, data_len);
    work_data32 = new Uint32Array(work_imgdat.data.buffer, 0, dev_size);

    work1 = new Uint8Array(work_imgdat.data.buffer, 0, dev_size);
    work2 = new Uint8Array(work_imgdat.data.buffer, dev_size, dev_size);
    work3 = new Uint8Array(work_imgdat.data.buffer, dev_size << 1, dev_size);

    var size = dev_width_h * dev_height_h;
    work1d = new Int16Array(work_imgdat.data.buffer, 0, size);
    work2d = new Int16Array(work_imgdat.data.buffer, size << 1, size);
    work3d = new Int16Array(work_imgdat.data.buffer, size << 2, size);

    with(elem_work = document.createElement("CANVAS")) {
      width  = video_width + 6;
      height = video_height + 6;
      ctx_work = getContext("2d");
    }

    document.getElementById("sel").disabled = false;

    frame();
    return;
  }

  setTimeout(wait, 50);
}

function sel_chg(index) {
  proc = proc_tbl[index];

  interval = (proc == rotate/* 回転 */) ? 50 : 200;
  if(proc == glass) {  // ガラス
    // αを非透過に初期化する
    for(var i = 3; i < data_len; i += 4)
      work_data[i] = 255;
  }
}

function frame() {
  setTimeout(frame, interval);

  if(proc == glass) {  // ガラス
    glass();
  }
  else {
    ctx_view.drawImage(elem_video, 0, 0);

    if(proc == rotate) {  // 回転
      rotate();
    }
    else {
      var imgdat = ctx_view.getImageData(0, 0, video_width, video_height);

      if(proc == ripple) {  // 波紋
        ripple(imgdat);
      }
      else {
        // クランプされないように(高速化のため),ただの配列にする
        proc(new Uint8Array(imgdat.data.buffer));
        ctx_view.putImageData(imgdat, 0, 0);
      }
    }
  }
}

// ソラリゼーション
function solar(data) {
  for(var i_data = 0; i_data < data_len; i_data += 4)
    data[i_data] = data[i_data + 1] = data[i_data + 2]
      = solar_map[(360 * data[i_data] + 707 * data[i_data + 1] + 137 * data[i_data + 2]) >> 8];
}

// レリーフ
function relief(data) {
  var i_data = 0;
  var off = (dev_width << 3) + 8;
  for(var y = dev_height - 2; y; y--) {
    for(var x = dev_width - 2; x; x--) {
      var i_data2 = i_data + off;
      var val = (((306 * data[i_data] + 601 * data[i_data + 1] + 117 * data[i_data + 2])
                    + 0x3fc00
                    - (306 * data[i_data2] + 601 * data[i_data2 + 1] + 117 * data[i_data2 + 2])) >> 10)
                  - 128;
      data[i_data] = data[i_data + 1] = data[i_data + 2] = (val > 255) ? 255 : ((val < 0) ? 0 : val);

      i_data += 4;
    }

    data[i_data] = data[i_data + 1] = data[i_data + 2]
      = data[i_data + 4] = data[i_data + 5] = data[i_data + 6] = 128;
    i_data += 8;
  }
  for(; i_data < data_len; i_data += 4)
    data[i_data] = data[i_data + 1] = data[i_data + 2] = 128;
}

// ラプラシアン 1
function laplacian_1(data) {
  var stride = dev_width << 2;

  var i_data = stride + 4;
  var i_work = 0;
  for(var y = dev_height - 2; y; y--) {
    for(var x = dev_width - 2; x; x--) {
      var i_data2 = i_data - stride;
      var i_data3 = i_data + stride;
      var val = ((data[i_data] << 3)
                   - data[i_data2 - 4] - data[i_data2] - data[i_data2 + 4]
                   - data[i_data - 4] - data[i_data + 4]
                   - data[i_data3 - 4] - data[i_data3] - data[i_data3 + 4])
              + ((data[i_data + 1] << 3)
                   - data[i_data2 - 3] - data[i_data2 + 1] - data[i_data2 + 5]
                   - data[i_data - 3] - data[i_data + 5]
                   - data[i_data3 - 3] - data[i_data3 + 1] - data[i_data3 + 5])
              + ((data[i_data + 2] << 3)
                   - data[i_data2 - 2] - data[i_data2 + 2] - data[i_data2 + 6]
                   - data[i_data - 2] - data[i_data + 6]
                   - data[i_data3 - 2] - data[i_data3 + 2] - data[i_data3 + 6]);
      work1[i_work++] = (val > 255) ? 0 : ((val < 0) ? 255 : 255 - val)

      i_data += 4;
    }

    i_data += 8;
  }

  var off = (dev_height - 1) * stride;
  for(i_data = 0; i_data < stride; i_data += 4) {
    var i_data2 = i_data + off;
    data[i_data] = data[i_data + 1] = data[i_data + 2]
      = data[i_data2] = data[i_data2 + 1] = data[i_data2 + 2] = 255;
  }
  i_work = 0;
  for(var y = dev_height - 2; y; y--) {
    data[i_data] = data[i_data + 1] = data[i_data + 2] = 255;
    i_data += 4;
    for(var x = dev_width - 2; x; x--) {
      data[i_data] = data[i_data + 1] = data[i_data + 2] = work1[i_work++];
      i_data += 4;
    }
    data[i_data] = data[i_data + 1] = data[i_data + 2] = 255;
    i_data += 4;
  }
}

// ラプラシアン 2
function laplacian_2(data) {
  var stride = dev_width << 2;

  var i_data = stride + 4;
  var i_work = 0;
  for(var y = dev_height - 2; y; y--) {
    for(var x = dev_width - 2; x; x--) {
      var i_data2 = i_data - stride;
      var i_data3 = i_data + stride;
      var r = (data[i_data] << 3)
                - data[i_data2 - 4] - data[i_data2] - data[i_data2 + 4]
                - data[i_data - 4] - data[i_data + 4]
                - data[i_data3 - 4] - data[i_data3] - data[i_data3 + 4];
      var g = (data[i_data + 1] << 3)
                - data[i_data2 - 3] - data[i_data2 + 1] - data[i_data2 + 5]
                - data[i_data - 3] - data[i_data + 5]
                - data[i_data3 - 3] - data[i_data3 + 1] - data[i_data3 + 5];
      var b = (data[i_data + 2] << 3)
                - data[i_data2 - 2] - data[i_data2 + 2] - data[i_data2 + 6]
                - data[i_data - 2] - data[i_data + 6]
                - data[i_data3 - 2] - data[i_data3 + 2] - data[i_data3 + 6];
      work1[i_work++] = (r > 50 || g > 50 || b > 50) ? 0 : 255;

      i_data += 4;
    }

    i_data += 8;
  }

  var off = (dev_height - 1) * stride;
  for(i_data = 0; i_data < stride; i_data += 4) {
    var i_data2 = i_data + off;
    data[i_data] = data[i_data + 1] = data[i_data + 2]
      = data[i_data2] = data[i_data2 + 1] = data[i_data2 + 2] = 255;
  }
  i_work = 0;
  for(var y = dev_height - 2; y; y--) {
    data[i_data] = data[i_data + 1] = data[i_data + 2] = 255;
    i_data += 4;
    for(var x = dev_width - 2; x; x--) {
      data[i_data] = data[i_data + 1] = data[i_data + 2] = work1[i_work++];
      i_data += 4;
    }
    data[i_data] = data[i_data + 1] = data[i_data + 2] = 255;
    i_data += 4;
  }
}

// ポスタリゼーション 1
function poster_1(data) {
  for(var i_data = 0; i_data < data_len; i_data += 4) {
    var val;
    val = data[i_data]     & 0xc0;
    data[i_data]     = val | (val >> 2) | (val >> 4) | (val >> 6);
    val = data[i_data + 1] & 0xc0;
    data[i_data + 1] = val | (val >> 2) | (val >> 4) | (val >> 6);
    val = data[i_data + 2] & 0xc0;
    data[i_data + 2] = val | (val >> 2) | (val >> 4) | (val >> 6);
  }
}

// ポスタリゼーション 2
function poster_2(data) {
  for(var i_data = 0; i_data < data_len; i_data += 4) {
    data[i_data]     = (data[i_data]     & 0x80) ? 0xd0 : 0x30;
    data[i_data + 1] = (data[i_data + 1] & 0x80) ? 0xd0 : 0x30;
    data[i_data + 2] = (data[i_data + 2] & 0x80) ? 0xd0 : 0x30;
  }
}

// ポスタリゼーション 3
function poster_3(data) {
  // 16色に減色
  var i_data;
  var i;

  poster_leaves = 0;
  poster_pixels[0] = 0;
  for(i = 1; i < 4681; i++)
    poster_pixels[i] = 0x80000000;
  poster_internal_cnt[1] = poster_internal_cnt[2] = poster_internal_cnt[3] = 0;

  for(i_data = 0; i_data < data_len; i_data += 4) {
    poster_add_color(data[i_data], data[i_data + 1], data[i_data + 2], 0, 0);

    while(poster_leaves > 16) {  // 葉の数が 16 より多い
      // 葉を減らす
      var level;
      for(level = 3; ; level--) {
        if(poster_internal_cnt[level])
          break;
      }
      var i_store = poster_internal[(level - 1) * 17 + --poster_internal_cnt[level]];

      var index = (i_store - poster_off[level]) << 3;
      level++;
      var pixels = 0;
      var r_sum = 0;
      var g_sum = 0;
      var b_sum = 0;
      for(i = 0; i < 8; i++) {
        var i_store_child = poster_off[level] + (index | i);
        if(!(poster_pixels[i_store_child] & 0x80000000)) {  // ノードあり
          pixels += poster_pixels[i_store_child];
          r_sum += poster_r_sum[i_store_child];
          g_sum += poster_g_sum[i_store_child];
          b_sum += poster_b_sum[i_store_child];

          poster_leaves--;
        }
      }
      poster_leaves++;

      poster_pixels[i_store] = pixels;
      poster_r_sum[i_store] = r_sum;
      poster_g_sum[i_store] = g_sum;
      poster_b_sum[i_store] = b_sum;
    }
  }

  poster_i_color = 0;
  poster_get_colors(0, 0);

  for(i_data = 0; i_data < data_len; i_data += 4) {
    var r = data[i_data];
    var g = data[i_data + 1];
    var b = data[i_data + 2];
    var min = 0x7fffffff;
    var i_min;
    for(i = 0; i < poster_leaves; i++) {
      var dr = r - poster_r[i];
      var dg = g - poster_g[i];
      var db = b - poster_b[i];
      var d2 = dr * dr + dg * dg + db * db;
      if(d2 < min) {
        min = d2;
        i_min = i;
      }
    }
    data[i_data]     = poster_r[i_min];
    data[i_data + 1] = poster_g[i_min];
    data[i_data + 2] = poster_b[i_min];
  }
}

function poster_add_color(r, g, b, level, index) {
  var i_store = poster_off[level] + index;
  if(poster_pixels[i_store] & 0x80000000) {  // ノードなし
    // ノード追加
    poster_pixels[i_store] = 0;
    if(level == 4) {  // 葉
      poster_r_sum[i_store] = 0;
      poster_g_sum[i_store] = 0;
      poster_b_sum[i_store] = 0;
      poster_leaves++;
    }
    else {
      poster_internal[(level - 1) * 17 + poster_internal_cnt[level]++] = i_store;
    }
  }

  if(poster_pixels[i_store] || level == 4) {  // 葉
    poster_pixels[i_store]++;
    poster_r_sum[i_store] += r;
    poster_g_sum[i_store] += g;
    poster_b_sum[i_store] += b;
  }
  else {
    poster_add_color(r, g, b, level + 1, (index << 3) | ((r >> (5 - level)) & 0x4)
                                                      | ((g >> (6 - level)) & 0x2)
                                                      | ((b >> (7 - level)) & 0x1));
  }
}

function poster_get_colors(level, index) {
  var i_store = poster_off[level] + index;
  var pixels = poster_pixels[i_store];
  if(pixels) {  // 葉
    poster_r[poster_i_color] = Math.floor(poster_r_sum[i_store] / pixels);
    poster_g[poster_i_color] = Math.floor(poster_g_sum[i_store] / pixels);
    poster_b[poster_i_color] = Math.floor(poster_b_sum[i_store] / pixels);
    poster_i_color++;
  }
  else {
    index <<= 3;
    level++;
    for(var i = 0; i < 8; i++) {
      if(!(poster_pixels[poster_off[level] + (index | i)] & 0x80000000)) {  // ノードあり
        poster_get_colors(level, index | i);
        if(poster_i_color == poster_leaves)
          break;
      }
    }
  }
}

// ガラス
function glass() {
  ctx_work.drawImage(elem_video, 3, 3);
  ctx_work.drawImage(elem_work, 3, 3, 1, video_height, 0, 3, 3, video_height);
  ctx_work.drawImage(elem_work, 2 + video_width, 3, 1, video_height, 3 + video_width, 3, 3, video_height);
  ctx_work.drawImage(elem_work, 0, 3, video_width + 6, 1, 0, 0, video_width + 6, 3);
  ctx_work.drawImage(elem_work, 0, 2 + video_height, video_width + 6, 1, 0, 3 + video_height, video_width + 6, 3);
  var data = ctx_work.getImageData(0, 0, video_width + 6, video_height + 6).data;

  var y_l6 = (dev_width + 6) * 24;
  var off = dev_width << 2;
  for(var i_rnd = dev_size; i_rnd; ) {
    var i_data;
    var sum_r, sum_g, sum_b;
    sum_r = sum_g = sum_b = 0;
    i_data = glass_rnd[--i_rnd];
    var i_kernel = 0;
    for(var y_end = i_data + y_l6; ; ) {
      for(var x_end = i_data + 24; ; ) {
        var k = blur_kernel[i_kernel++];
        sum_r += data[i_data]     * k;
        sum_g += data[i_data + 1] * k;
        sum_b += data[i_data + 2] * k;

        if(i_data == x_end)
          break;
        i_data += 4;
      }

      if(i_data > y_end)
        break;
      i_data += off;
    }
    i_data = i_rnd << 2;
    work_data[i_data]     = sum_r >> 12;
    work_data[i_data + 1] = sum_g >> 12;
    work_data[i_data + 2] = sum_b >> 12;
  }

  ctx_view.putImageData(work_imgdat, 0, 0);
}

// スケッチ
function sketch(data) {
  var i_data;
  for(i_data = 0; i_data < data_len; i_data += 4)
    data[i_data] = (306 * data[i_data] + 601 * data[i_data + 1] + 117 * data[i_data + 2]) >> 10;

  var off1 = (dev_width + 1) * 12;
  var off2 = (dev_width - 6) << 2;
  i_data = 0;
  for(var y = 0; y < dev_height; y++) {
    for(var x = 0; x < dev_width; x++) {
      var sum = 0;
      var i_data2 = i_data - off1;
      var i_kernel = 0;
      for(var dy = -3; ; ) {
        var y_ob = (y + dy < 0 || y + dy >= dev_height);
        for(var dx = -3; ; ) {
          sum += data[(y_ob || x + dx < 0 || x + dx >= dev_width) ? i_data : i_data2] * blur_kernel[i_kernel++];

          if(dx == 3)
            break;
          dx++;
          i_data2 += 4;
        }

        if(dy == 3)
          break;
        dy++;
        i_data2 += off2;
      }
      data[i_data + 1] = sum >> 12;

      i_data += 4;
    }
  }

  for(i_data = 0; i_data < data_len; i_data += 4) {
    var val = Math.floor((data[i_data] << 9) / (data[i_data + 1] + 1)) - 256;
    data[i_data] = data[i_data + 1] = data[i_data + 2] = (val > 255) ? 255 : ((val < 0) ? 0 : val);
  }
}

// ディザ 1
function dither_1(data) {
  var i_data = 0;
  var i, i_end;
  for(i = 0, i_end = dev_width << 1; i < i_end; i++) {
    work1d[i] = data[i_data]
    work2d[i] = data[i_data + 1]
    work3d[i] = data[i_data + 2]
    i_data += 4;
  }

  i_data = 0;
  var i_work1 = 0;
  var i_work2 = dev_width;
  for(var y = dev_height; ; ) {
    for(var x = dev_width; x; x--) {
      // R
      var r = work1d[i_work1];
      data[i_data]     = (r >= 128) ? (r -= 255, 255) : 0;
      // G
      var g = work2d[i_work1];
      data[i_data + 1] = (g >= 128) ? (g -= 255, 255) : 0;
      // B
      var b = work3d[i_work1];
      data[i_data + 2] = (b >= 128) ? (b -= 255, 255) : 0;

      if(x > 1) {
        work1d[i_work1 + 1] += (r * 7) >> 4;
        work2d[i_work1 + 1] += (g * 7) >> 4;
        work3d[i_work1 + 1] += (b * 7) >> 4;
      }
      if(y > 1) {
        if(x < dev_width) {
          work1d[i_work2 - 1] += (r * 3) >> 4;
          work2d[i_work2 - 1] += (g * 3) >> 4;
          work3d[i_work2 - 1] += (b * 3) >> 4;
        }
        work1d[i_work2] += (r * 5) >> 4;
        work2d[i_work2] += (g * 5) >> 4;
        work3d[i_work2] += (b * 5) >> 4;
        if(x > 1) {
          work1d[i_work2 + 1] += r >> 4;
          work2d[i_work2 + 1] += g >> 4;
          work3d[i_work2 + 1] += b >> 4;
        }
      }

      i_data += 4;
      i_work1++;
      i_work2++;
    }

    if(y == 1)
      break;

    if(i_work1 == dev_width)
      i_work2 = 0;
    else
      i_work1 = 0;
    var i_data2 = i_data + (dev_width << 2);
    for(i = i_work2, i_end = i_work2 + dev_width; i < i_end; i++) {
      work1d[i] = data[i_data2]
      work2d[i] = data[i_data2 + 1]
      work3d[i] = data[i_data2 + 2]
      i_data2 += 4;
    }

    y--;
  }
}

// ディザ 2
function dither_2(data) {
  var stride = dev_width << 2;

  var i_data = 0;
  var i_work = 0;
  var x, y;
  for(y = dev_height_h; y; y--) {
    for(x = dev_width_h; x; x--) {
      var i_data2 = i_data + stride;
      work1d[i_work] = (data[i_data]     + data[i_data + 4] + data[i_data2]     + data[i_data2 + 4]) >> 2;
      work2d[i_work] = (data[i_data + 1] + data[i_data + 5] + data[i_data2 + 1] + data[i_data2 + 5]) >> 2;
      work3d[i_work] = (data[i_data + 2] + data[i_data + 6] + data[i_data2 + 2] + data[i_data2 + 6]) >> 2;

      i_data += 8;
      i_work++;
    }
    if(dev_width & 0x1) {
      data[i_data] = data[i_data + 1] = data[i_data + 2]
        = data[i_data + stride] = data[i_data + stride + 1] = data[i_data + stride + 2] = 0;
      i_data += 4;
    }

    i_data += stride;
  }
  if(dev_height & 0x1) {
    for(x = dev_width; x; x--) {
      data[i_data] = data[i_data + 1] = data[i_data + 2] = 0;
      i_data += 4;
    }
  }

  i_data = 0;
  i_work = 0;
  for(y = dev_height_h; y; y--) {
    for(x = dev_width_h; x; x--) {
      var i_data2 = i_data + stride;

      // R
      var r = work1d[i_work];
      data[i_data] = data[i_data + 4] = data[i_data2] = data[i_data2 + 4]
        = (r >= 128) ? (r -= 255, 255) : 0;
      // G
      var g = work2d[i_work];
      data[i_data + 1] = data[i_data + 5] = data[i_data2 + 1] = data[i_data2 + 5]
        = (g >= 128) ? (g -= 255, 255) : 0;
      // B
      var b = work3d[i_work];
      data[i_data + 2] = data[i_data + 6] = data[i_data2 + 2] = data[i_data2 + 6]
        = (b >= 128) ? (b -= 255, 255) : 0;

      if(x > 1) {
        work1d[i_work + 1] += (r * 7) >> 4;
        work2d[i_work + 1] += (g * 7) >> 4;
        work3d[i_work + 1] += (b * 7) >> 4;
      }
      if(y > 1) {
        var i_work2 = i_work + dev_width_h;

        if(x < dev_width_h) {
          work1d[i_work2 - 1] += (r * 3) >> 4;
          work2d[i_work2 - 1] += (g * 3) >> 4;
          work3d[i_work2 - 1] += (b * 3) >> 4;
        }
        work1d[i_work2] += (r * 5) >> 4;
        work2d[i_work2] += (g * 5) >> 4;
        work3d[i_work2] += (b * 5) >> 4;
        if(x > 1) {
          work1d[i_work2 + 1] += r >> 4;
          work2d[i_work2 + 1] += g >> 4;
          work3d[i_work2 + 1] += b >> 4;
        }
      }

      i_data += 8;
      i_work++;
    }
    if(dev_width & 0x1)
      i_data += 4;

    i_data += stride;
  }
}

// ディザ 3
function dither_3(data) {
  var i_data = 0;
  for(var y = 0; y < dev_height; y++) {
    var y2 = (y & 0x3) << 2;
    for(var x = 0; x < dev_width; x++) {
      var thresh = dither_thresh[y2 | (x & 0x3)];
      data[i_data]     = (data[i_data]     >= thresh) ? 255 : 0;
      data[i_data + 1] = (data[i_data + 1] >= thresh) ? 255 : 0;
      data[i_data + 2] = (data[i_data + 2] >= thresh) ? 255 : 0;
      i_data += 4;
    }
  }
}

// 網点
function halftone(data) {
  var x_adj;
  var y_rem;
  var i;

  // 3 ピクセル単位の余りの部分をクリア
  var data32 = new Uint32Array(data.buffer);
  switch(x_adj = dev_width % 3) {
  case 1:
    for(i = dev_size - 1; i > 0; i -= dev_width)
      data32[i] = 0xffffffff;
    break;
  case 2:
    for(i = dev_size - 1; i > 0; i -= dev_width)
      data32[i - 1] = data32[i] = 0xffffffff;
    break;
  }
  x_adj <<= 2;
  if(y_rem = dev_height % 3) {
    for(i = dev_width * (dev_height - y_rem); i < dev_size; i++)
      data32[i] = 0xffffffff;
  }

  var stride = dev_width << 2;
  var wt = Math.floor(dev_width / 3);

  var i_data1 = data_len - x_adj;
  for(var y = Math.floor(dev_height / 3); y; y--) {
    for(var x = wt; x; x--) {
      i_data1 -= 12;

      var i_data2, i_data3;
      i_data3 = (i_data2 = i_data1 + stride) + stride;
      var g_b2, g_b3, b_b2, b_b3;

      // R(C)
      i = Math.floor((data[i_data1] + data[i_data1 + 4] + data[i_data1 + 8]
                    + data[i_data2] + data[i_data2 + 4] + data[i_data2 + 8]
                    + data[i_data3] + data[i_data3 + 4] + data[i_data3 + 8]) / 9) >> 4;
      data[i_data2 + 4] = halftone_b1[i];
      data[i_data1 + 4] = data[i_data2] = data[i_data2 + 8] = data[i_data3 + 4] = halftone_b2[i];
      data[i_data1] = data[i_data1 + 8] = data[i_data3] = data[i_data3 + 8] = halftone_b3[i];

      // G(M)
      // 1 ピクセル右にずらす
      i = Math.floor((data[i_data1 + 1] + data[i_data1 + 5] + data[i_data1 + 9]
                    + data[i_data2 + 1] + data[i_data2 + 5] + data[i_data2 + 9]
                    + data[i_data3 + 1] + data[i_data3 + 5] + data[i_data3 + 9]) / 9) >> 4;
      data[i_data2 + 9] = halftone_b1[i];
      data[i_data1 + 9] = data[i_data2 + 5] = data[i_data3 + 9] = g_b2 = halftone_b2[i];
      data[i_data1 + 5] = data[i_data3 + 5] = g_b3 = halftone_b3[i];

      // B(Y)
      // 1 ピクセル下にずらす
      i = Math.floor((data[i_data1 + 2] + data[i_data1 + 6] + data[i_data1 + 10]
                    + data[i_data2 + 2] + data[i_data2 + 6] + data[i_data2 + 10]
                    + data[i_data3 + 2] + data[i_data3 + 6] + data[i_data3 + 10]) / 9) >> 4;
      data[i_data3 + 6] = halftone_b1[i];
      data[i_data2 + 6] = data[i_data3 + 2] = data[i_data3 + 10] = b_b2 = halftone_b2[i];
      data[i_data2 + 2] = data[i_data2 + 10] = b_b3 = halftone_b3[i];

      if(x > 1 || x_adj) {
        // G(M)
        data[i_data2 + 13] = g_b2;
        data[i_data1 + 13] = data[i_data3 + 13] = g_b3;
      }

      if(y > 1 || y_rem) {
        // B(Y)
        i = i_data3 + stride;
        data[i + 6] = b_b2;
        data[i + 2] = data[i + 10] = b_b3;
      }
    }

    i_data1 -= x_adj + (stride << 1);
  }
  for(i = data_len - y_rem * stride + 1; ; ) {
    if((i -= stride) < 0)
      break;
    data[i] = 255;
  }
  for(i = stride - x_adj + 2; ; ) {
    if((i -= 4) < 0)
      break;
    data[i] = 255;
  }
}

// 波紋
function ripple(imgdat) {
  var data = new Uint32Array(imgdat.data.buffer);

  var x_end = (dev_width & 0x1) ? dev_width_h + 1 : dev_width_h;
  var y_end = (dev_height & 0x1) ? dev_height_h + 1 : dev_height_h;
  var i = 0;
  for(var y = - dev_height_h; y < y_end; y++) {
    for(var x = - dev_width_h; x < x_end; x++) {
      if(x | y) {
        var disp = ripple_sin[(ripple_pha[i] + ripple_time) & 0xff] / ripple_att[i];
        var x2 = x + Math.round(x * disp);
        var y2 = y + Math.round(y * disp);
        if(x >= 0) {
          if     (x2 < 0)
            x2 = 0;
          else if(x2 >= x_end)
            x2 = x_end - 1;
        }
        else {
          if     (x2 >= 0)
            x2 = -1;
          else if(x2 < - dev_width_h)
            x2 = - dev_width_h;
        }
        if(y >= 0) {
          if     (y2 < 0)
            y2 = 0;
          else if(y2 >= y_end)
            y2 = y_end - 1;
        }
        else {
          if     (y2 >= 0)
            y2 = -1;
          else if(y2 < - dev_height_h)
            y2 = - dev_height_h;
        }
        work_data32[i] = data[(y2 + dev_height_h) * dev_width + (x2 + dev_width_h)];
      }
      else {
        work_data32[i] = data[i];
      }

      i++;
    }
  }

  ctx_view.putImageData(work_imgdat, 0, 0);

  ripple_time = (ripple_time - 40) & 0xff;
}

// 回転
function rotate() {
  ctx_work.drawImage(elem_view, 0, 0);
  ctx_view.save();
  ctx_view.clip();
  ctx_view.translate(video_width_h, video_height_h);
  ctx_view.rotate(rotate_angle);
  ctx_view.translate(- video_width_h, - video_height_h);
  ctx_view.drawImage(elem_work, 0, 0, video_width, video_height);
  ctx_view.restore();

  if((rotate_angle += 0.05) >= PI2)
    rotate_angle -= PI2;
}

  proc_tbl = [solar, relief, laplacian_1, laplacian_2, poster_1, poster_2, poster_3, glass, sketch,
              dither_1, dither_2, dither_3, halftone, ripple, rotate];
  proc = solar;
  interval = 200;

  // ソラリゼーション用変換マップ
  solar_map = new Uint8Array(1200);
  for(i = 0; i < 200; i++)
    solar_map[i + 1000] = (solar_map[i] = Math.floor(i * 128 / 200)) + 128;
  for(; i < 600; i++) {
    var sin = Math.sin((i - 200) * Math.PI / 400) * 256 / Math.PI;
    solar_map[i]       = Math.floor(128 + sin);
    solar_map[i + 400] = Math.floor(128 - sin);
  }

  // ポスタリゼーション用減色処理ワーク
  // 1 + 8 + 8*8 + 8*8*8 + 8*8*8*8 = 4681
  poster_pixels = new Uint32Array(4681);
  poster_r_sum = new Uint32Array(4681);
  poster_g_sum = new Uint32Array(4681);
  poster_b_sum = new Uint32Array(4681);
  poster_off = new Uint16Array([0, 1, 9, 73, 585]);
  poster_internal = new Uint16Array(51);
  poster_internal_cnt = new Uint8Array(4);
  poster_r = new Uint8Array(16);
  poster_g = new Uint8Array(16);
  poster_b = new Uint8Array(16);

  // ぼかしフィルター
  blur_kernel = new Uint16Array([  0,   1,   4,   7,   4,   1,   0,
                                   1,  12,  54,  88,  54,  12,   1,
                                   4,  54, 240, 396, 240,  54,   4,
                                   7,  88, 396, 652, 396,  88,   7,
                                   4,  54, 240, 396, 240,  54,   4,
                                   1,  12,  54,  88,  54,  12,   1,
                                   0,   1,   4,   7,   4,   1,   0]);

  // ディザ用しきい値
  dither_thresh = new Uint8Array([ 15, 136,  45, 166,
                                  196,  75, 226, 105,
                                   60, 181,  30, 151,
                                  241, 120, 211,  90]);

  // 網点用明度
  halftone_b1 = new Uint8Array([  0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   1,  29, 135, 255]);
  halftone_b2 = new Uint8Array([  7,  20,  34,  48,  63,  79,  96, 113, 132, 152, 175, 200, 228, 251, 255, 255]);
  halftone_b3 = new Uint8Array([116, 133, 149, 165, 180, 194, 208, 220, 231, 241, 249, 254, 255, 255, 255, 255]);

  // 波紋用 sin テーブル
  ripple_sin = new Float32Array(256);
  for(i = 0; i < 256; i++)
    ripple_sin[i] = Math.sin(i * Math.PI / 128) * 40;
  ripple_time = 0;

  rotate_angle = 0;
  PI2 = 2 * Math.PI;

  // ページを再ロードしたときのため
  document.forms[0].reset();
  document.getElementById("sel").disabled = true;

//-->
</SCRIPT>

</BODY>

</HTML>