flesh out page

This commit is contained in:
Joshua Bell 2018-10-27 22:42:31 -07:00
parent 899a1a4722
commit 45302cc3a0
2 changed files with 259 additions and 207 deletions

View File

@ -1,12 +1,59 @@
<!doctype html>
<title>Screen Capture Demo</title>
<title>vnIIc</title>
<link href="https://fonts.googleapis.com/css?family=Audiowide" rel="stylesheet">
<style>
video, canvas { border: 2px dotted black; }
h1, h2, h3 { font-family: Audiowide; }
h1 { font-size: 64px; margin: 0;}
#title { background-color: #d0d0d0; height: 128px; margin: 20px 0; padding: 10px; }
body { font-family: sans-serif; margin: 0 20px;}
</style>
<div id=title>
<h1>vn//c
<img style="float: left; margin-right: 24px;" src="res/icon128.png">
</h1>
<div>
Desktop streaming to an Apple II with Super Serial Card
</div>
</div>
<canvas id=quant width=140 height=192 style="width: 280px; height: 192px;"></canvas>
<br>
<div>
<button id=start>Start Capturing</button>
<button id=save>Save</button>
<button id=bootstrap>Bootstrap</button>
</div>
<script src="server.js"></script>
<h2>About vnIIc</h2>
<p>
The name "vnIIc" is a play on <a target=_blank
href="https://en.wikipedia.org/wiki/Virtual_Network_Computing">VNC
("Virtual Network Computing")</a> and the <a target=_blank
href="https://en.wikipedia.org/wiki/Apple_IIc">Apple IIc</a> for humor
purposes, but the application does not use the VNC or <a target=_blank
href="https://en.wikipedia.org/wiki/RFB_protocol">RFB</a> protocols.
Apple IIc is a trademark of Apple Computer, Inc. VNC and RFB are
registered trademarks of RealVNC Ltd.
</p>
<p>
The first version was a Windows application, written in 2008. See a <a
target=_blank href="https://www.youtube.com/watch?v=vAZHJa91JHk">video
demonstration on YouTube</a>.
</p>
<p>
Thanks to: Michael J. Mahon, Nick Westgate, David Wilson, David
Schmenk, David Schmidt and the rest of the <a target=_blank
href="https://groups.google.com/forum/#!forum/comp.sys.apple2.programmer">comp.sys.apple2.programmer</a>
gang!
</p>
</div>

View File

@ -1,7 +1,6 @@
(async function() {
const $ = document.querySelector.bind(document);
const $ = document.querySelector.bind(document);
const palette = [
const palette = [
/* Black1 */ [0x00, 0x00, 0x00],
/* Green */ [0x2f, 0xbc, 0x1a],
/* Violet */ [0xd0, 0x43, 0xe5],
@ -10,11 +9,12 @@
/* Orange */ [0xd0, 0x6a, 0x1a],
/* Blue */ [0x2f, 0x95, 0xe5],
/* White2 */ [0xff, 0xff, 0xff]
];
];
let hires_buffer = new Uint8Array(8192);
let hires_buffer = new Uint8Array(8192);
$('#save').addEventListener('click', e => {
// Save the last captured frame as a hires image file.
$('#save').addEventListener('click', e => {
const blob = new Blob([hires_buffer], {type: 'application/octet-stream'});
const anchor = document.createElement('a');
anchor.download = 'image.bin';
@ -23,8 +23,10 @@
anchor.click();
anchor.remove();
URL.revokeObjectURL(anchor.href);
});
});
// Start capturing the desktop.
$('#start').addEventListener('click', async e => {
try {
const mediaStream = await navigator.getDisplayMedia({video:true});
const vid = document.createElement('video');
@ -57,17 +59,17 @@
} catch (e) {
console.warn(`Error: ${e.name} - ${e.message}`);
}
});
// Distance in 3-space
function distance(r1,g1,b1,r2,g2,b2) {
// Distance in 3-space
function distance(r1,g1,b1,r2,g2,b2) {
const dr = r1 - r2;
const dg = g1 - g2;
const db = b1 - b2;
return Math.sqrt(dr*dr + dg*dg + db*db);
}
}
function quantize(imagedata, indexes) {
function quantize(imagedata, indexes) {
const hash = {};
for (let i = 0; i < palette.length; ++i) {
const entry = palette[i];
@ -135,14 +137,14 @@
err(x + 1, y + 1, err_r * 1/16, err_g * 1/16, err_b * 1/16);
}
}
}
}
// Scan line mapping table for Apple II Hi-Res screen.
// Index into the array is the y-coordinate. The value
// in the array is the offset (in bytes) from the
// start of the hi-res screen buffer to the start of the
// scan line. The scan line itself is 40 bytes wide.
const OFFSETS = [
// Scan line mapping table for Apple II Hi-Res screen.
// Index into the array is the y-coordinate. The value
// in the array is the offset (in bytes) from the
// start of the hi-res screen buffer to the start of the
// scan line. The scan line itself is 40 bytes wide.
const OFFSETS = [
0x0000,0x0400,0x0800,0x0c00,0x1000,0x1400,0x1800,0x1c00,
0x0080,0x0480,0x0880,0x0c80,0x1080,0x1480,0x1880,0x1c80,
0x0100,0x0500,0x0900,0x0d00,0x1100,0x1500,0x1900,0x1d00,
@ -167,14 +169,14 @@
0x02d0,0x06d0,0x0ad0,0x0ed0,0x12d0,0x16d0,0x1ad0,0x1ed0,
0x0350,0x0750,0x0b50,0x0f50,0x1350,0x1750,0x1b50,0x1f50,
0x03d0,0x07d0,0x0bd0,0x0fd0,0x13d0,0x17d0,0x1bd0,0x1fd0
];
];
const SCREEN_WIDTH = 280;
const SCREEN_WIDTH_COLOR = SCREEN_WIDTH/2;
const SCREEN_HEIGHT = 192;
const PIXEL_BITS_PER_BYTE = 7;
const SCREEN_WIDTH = 280;
const SCREEN_WIDTH_COLOR = SCREEN_WIDTH/2;
const SCREEN_HEIGHT = 192;
const PIXEL_BITS_PER_BYTE = 7;
function convert_to_hires(indexes, buffer) {
function convert_to_hires(indexes, buffer) {
for (let y = 0; y < SCREEN_HEIGHT; ++y) {
let hbas = OFFSETS[y];
@ -236,9 +238,14 @@
}
}
}
}
}
$('#bootstrap').addEventListener('click', async e => {
$('#bootstrap').addEventListener('click', async e => {
alert('On the Apple II, type:\n\n' +
' IN#2 (then press Return)\n' +
' Ctrl+A 14B (then press Return)\n\n' +
'Then click OK');
const CLIENT_ADDR = 0x6000;
const CLIENT_FILE = 'client/client.bin';
@ -266,6 +273,4 @@
send('\x03'); // Ctrl+C - Exit Monitor
send(`CALL ${CLIENT_ADDR}`); // Execute client
});
})();
});