ファミコン互換機の眠い映像から、くっきりした画面を取得
キャプボ(カメラ)から映像を取得してCanvasのImageDataを加工します
AVerMedia.js
import {View} from './js/View.js'
class Pixelater extends View{
constructor(from,to){
super('Pixelater')
this.delta = [10,0]//カーソルキーで微調整
this.source = new AVerMedia(from,{loaded:e=>{
console.log(e)
this.in = new Canvas(this.source.size,{willReadFrequently:true})
this.out = new Canvas(to,{willReadFrequently:true}).pixelate()
this.add(this.source,this.out).update()
}})
document.addEventListener('keyup',e=>e.preventDefault())
document.addEventListener('keydown',e=>{
switch(e.key){
case 'ArrowRight' :this.delta[0]++ ;break
case 'ArrowLeft' :this.delta[0]-- ;break
case 'ArrowDown' :this.delta[1]++ ;break
case 'ArrowUp' :this.delta[1]-- ;break
default :return
}
console.log(this.delta)
e.preventDefault()
})
}
update(){
var scale = [this.in.width/this.out.width,this.in.height/this.out.height]
var delta = this.delta.map(n=>n/10)
var a = this.in.drawImage(this.source).getImageData()
var b = this.out.createImageData()
var p = 0
for(var y=0;y<this.out.height;y++){
for(var x=0;x<this.out.width;x++){
var sx = x*scale[0]+delta[0]
var sy = y*scale[1]+delta[1]
var sp = (Math.round(sx)+Math.round(sy)*this.in.width)*4//RGBA
for(var i=0;i<4;i++){b.data[p++]=a.data[sp++]}
}
}
this.out.putImageData(b)
requestAnimationFrame(this.update.bind(this))
}
}
class Canvas extends View{
constructor(size,option){
super('CANVAS').assign(size)
Object.assign(this,size,{size})
this.context = this.node.getContext('2d',option)
}
pixelate(){
this.context.imageSmoothingEnabled = false
return this.style`image-rendering:pixelated`
}
createImageData(){return this.context.createImageData(this.width,this.height)}
getImageData(){return this.context.getImageData(0,0,this.width,this.height)}
putImageData(v){this.context.putImageData(v,0,0);return this}
drawImage(v){this.context.drawImage(v.node,0,0);return this}
}
class AVerMedia extends View.Dispatcher{
constructor(size,...a){
super('VIDEO AVerMedia').assign({autoplay:true,controls:true}).add(...a)
this.size = size
navigator.mediaDevices.getUserMedia({video:size,audio:true}).then(stream=>{
this.node.srcObject = stream
this.node.onloadedmetadata = e=>{
this.size = {width:this.width,height:this.height}
this.on('loaded',this)
}
}).catch(e=>console.log(e.toString()))
}
get width(){return this.node.videoWidth}
get height(){return this.node.videoHeight}
}
document.body.append(
new Pixelater(
{width:1280,height:720},//240x3 = 720p
{width:270,height:240}//8BIT COMPACT HDMI - VRAMイメージ
).node
)
入力キャプチャボード:Live Gamer 4K(GC573)
https://www.avermedia.co.jp/product-detail/GC573
ファミコンHDMI出力:8BIT COMPACT HDMI
https://www.columbuscircle.co.jp/products/?id=1528173155-049425
カセット:キングオブキングス(ナムコ)
https://www.amazon.co.jp/dp/B000068H3C