-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
14 changed files
with
461 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
<!doctype html> | ||
<html> | ||
<head> | ||
<meta charset="UTF-8"> | ||
<title>きららファンタジアのガチャ画像をまとめるやつ</title> | ||
<meta name="viewport" content="width=device-width,initial-scale=1"> | ||
<style> | ||
body { | ||
margin: 0; | ||
font-family: sans-serif; | ||
} | ||
h1 { | ||
font-size: 1.4rem; | ||
} | ||
p { | ||
margin: 0; | ||
} | ||
input { | ||
box-sizing: border-box; | ||
padding: 0.3em; | ||
} | ||
</style> | ||
</head> | ||
<body> | ||
<h1>きららファンタジアのガチャ画像をまとめるやつ</h1> | ||
<div id="app"></div> | ||
<script src="../dist/bundle.js"></script> | ||
</body> | ||
</html> | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
import { | ||
Component, | ||
h, | ||
} from 'preact'; | ||
|
||
import { | ||
FileSelect, | ||
} from './file-select'; | ||
|
||
import { | ||
main, | ||
} from './logic/main'; | ||
|
||
export class App extends Component<{}, {}> { | ||
public render() { | ||
const fileHandler = (files: FileList)=>{ | ||
main(files); | ||
}; | ||
return <div> | ||
<p> | ||
<FileSelect | ||
label='ガチャ画像を選択' | ||
onSelect={fileHandler} | ||
/> | ||
</p> | ||
</div>; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
import { | ||
Component, | ||
h, | ||
} from 'preact'; | ||
|
||
export interface IPropFileSelect { | ||
label: string; | ||
onSelect(files: FileList): void; | ||
} | ||
|
||
/** | ||
* File select button. | ||
*/ | ||
export class FileSelect extends Component<IPropFileSelect, {}> { | ||
public render() { | ||
const { | ||
label, | ||
} = this.props; | ||
|
||
return <button onClick={this.handleClick.bind(this)}> | ||
{label} | ||
</button>; | ||
} | ||
protected handleClick(): void { | ||
// File select button is clicked. | ||
const { | ||
onSelect, | ||
} = this.props; | ||
|
||
const input = document.createElement('input'); | ||
input.type = 'file'; | ||
input.multiple = true; | ||
input.style.display = 'none'; | ||
|
||
input.addEventListener('change', ()=>{ | ||
if (input.files != null) { | ||
onSelect(input.files); | ||
} | ||
}, false); | ||
|
||
document.body.appendChild(input); | ||
|
||
setTimeout(()=>{ | ||
input.click(); | ||
setTimeout(()=>{ | ||
document.body.removeChild(input); | ||
}, 500); | ||
}, 0); | ||
|
||
} | ||
} |
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
import { | ||
h, | ||
render, | ||
} from 'preact'; | ||
|
||
import { | ||
App, | ||
} from './app'; | ||
|
||
document.addEventListener('DOMContentLoaded', ()=>{ | ||
const app = document.getElementById('app'); | ||
render(<App />, app); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,180 @@ | ||
import { | ||
IIcon, | ||
IWorkerResult, | ||
} from './find-icons'; | ||
|
||
// TypeScript workaround | ||
const ctx: Worker = self as any; | ||
|
||
// background color of the gacha board. | ||
const bg = { | ||
blue: 0xff, | ||
green: 0xd7, | ||
red: 0xaa, | ||
}; | ||
|
||
// state of scanner. | ||
const enum State { | ||
Start, | ||
UpBorder, | ||
Gacha10_1, | ||
CenterMargin, | ||
} | ||
|
||
ctx.onmessage = (e)=> { | ||
// Receive an ImageData. | ||
const { | ||
data, | ||
width, | ||
height, | ||
} = e.data as ImageData; | ||
|
||
// index of buffer. | ||
let idx = 0; | ||
// states. | ||
let s = State.Start; | ||
let y1: number | undefined; | ||
let xs: number[] | undefined; | ||
let y2: number | undefined; | ||
let sizex: number | undefined; | ||
let sizey: number | undefined; | ||
|
||
// Scan each line. | ||
wholeloop: for (let y = 0; y < height; y++) { | ||
let bgcnt = 0; | ||
for (let x = 0; x < width; x++) { | ||
const red = data[idx]; | ||
const green = data[idx+1]; | ||
const blue = data[idx+2]; | ||
|
||
if (red === bg.red && green === bg.green && blue === bg.blue) { | ||
// this is bg! | ||
bgcnt++; | ||
} | ||
idx += 4; | ||
} | ||
console.log(bgcnt); | ||
// State transition | ||
switch (s) { | ||
case State.Start: { | ||
if (bgcnt >= 650) { | ||
// Found a board. | ||
s = State.UpBorder; | ||
} | ||
break; | ||
} | ||
case State.UpBorder: { | ||
if (80 <= bgcnt && bgcnt <= 150) { | ||
// Found a 10 gacha. | ||
// Start of first line. | ||
y1 = y - 1; | ||
s = State.Gacha10_1; | ||
|
||
// Get start position of each boxes. | ||
const linestart = 4 * width * (y+10); | ||
[xs, sizex] = detectX(data.subarray(linestart, linestart + 4 * width), width, 5); | ||
} | ||
break; | ||
} | ||
case State.Gacha10_1: { | ||
if (bgcnt >= 650) { | ||
// End of gacha1. | ||
sizey = y - y1!; | ||
s = State.CenterMargin; | ||
} | ||
break; | ||
} | ||
case State.CenterMargin: { | ||
if (80 <= bgcnt && bgcnt <= 150) { | ||
// Start of second line. | ||
y2 = y - 1; | ||
// Job is done! | ||
break wholeloop; | ||
} | ||
break; | ||
} | ||
} | ||
} | ||
|
||
// End of scanning. | ||
const result: IIcon[] = []; | ||
if (sizex != null && sizey != null) { | ||
if (y1 != null && xs != null) { | ||
for (const x of xs) { | ||
result.push({ | ||
height: sizey, | ||
width: sizex, | ||
x, | ||
y: y1, | ||
}); | ||
} | ||
} | ||
if (y2 != null && xs != null) { | ||
for (const x of xs) { | ||
result.push({ | ||
height: sizey, | ||
width: sizex, | ||
x, | ||
y: y2, | ||
}); | ||
} | ||
} | ||
} | ||
|
||
const answer: IWorkerResult = { | ||
data: e.data, | ||
result, | ||
}; | ||
|
||
ctx.postMessage(answer, [data.buffer]); | ||
}; | ||
|
||
/** | ||
* Detect each box of gacha icon. | ||
*/ | ||
function detectX(data: Uint8ClampedArray, width: number, max: number): [number[], number] { | ||
const result = []; | ||
let curx: number | undefined; | ||
let sizex: number | undefined; | ||
|
||
let state = 0; | ||
let bgcont = 0; | ||
|
||
for (let x = 0; x < width; x++) { | ||
const idx = x * 4; | ||
const red = data[idx]; | ||
const green = data[idx+1]; | ||
const blue = data[idx+2]; | ||
const bgf = red === bg.red && green === bg.green && blue === bg.blue; | ||
|
||
console.log(bgf, bgcont); | ||
if (bgf) { | ||
// count continues background pixels. | ||
bgcont++; | ||
if (bgcont === 1 && state === 1 && curx != null) { | ||
// it's the end of the first box. | ||
sizex = x - curx; | ||
state = 2; | ||
} | ||
} else { | ||
if (bgcont >= 12) { | ||
// yes, it's after a true background region | ||
if (result.length < max) { | ||
console.log('found!', x, state); | ||
result.push(x); | ||
} | ||
if (state === 0) { | ||
curx = x; | ||
state = 1; | ||
} | ||
} | ||
bgcont = 0; | ||
|
||
} | ||
} | ||
if (sizex != null) { | ||
return [result, sizex]; | ||
} else { | ||
throw new Error('Something went wrong'); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
import FindIconWorker from 'worker-loader!./find-icons-worker'; | ||
|
||
export interface IIcon { | ||
x: number; | ||
y: number; | ||
width: number; | ||
height: number; | ||
} | ||
|
||
export interface IFindIconsResult { | ||
image: HTMLCanvasElement; | ||
result: IIcon[]; | ||
} | ||
|
||
export interface IWorkerResult { | ||
data: ImageData; | ||
result: IIcon[]; | ||
} | ||
|
||
/** | ||
* Find icons from an image. | ||
*/ | ||
export function findIcons(image: HTMLImageElement): Promise<IFindIconsResult> { | ||
// Invoke a worker. | ||
const worker = new FindIconWorker(); | ||
|
||
// Render image into a canvas. | ||
const canvas = document.createElement('canvas'); | ||
canvas.width = image.naturalWidth; | ||
canvas.height = image.naturalHeight; | ||
const ctx = canvas.getContext('2d'); | ||
if (ctx == null) { | ||
throw new Error('Could not get a context'); | ||
} | ||
ctx.drawImage(image, 0, 0); | ||
// Retrieve an ImageData. | ||
const data = ctx.getImageData(0, 0, canvas.width, canvas.height); | ||
|
||
// Send this data to the worker. | ||
worker.postMessage(data, [data.data.buffer]); | ||
|
||
return new Promise((resolve, reject)=>{ | ||
worker.onmessage = (e)=> { | ||
const answer: IWorkerResult = e.data; | ||
const { | ||
result, | ||
} = answer; | ||
console.log(result); | ||
resolve({ | ||
image: canvas, | ||
result, | ||
}); | ||
}; | ||
worker.onerror = reject; | ||
}); | ||
} |
Oops, something went wrong.