use game font for preview

This commit is contained in:
sup39 2022-10-07 23:02:40 +09:00
parent 5f81c7b9b1
commit 4589c52349
9 changed files with 2641 additions and 23 deletions

View file

@ -11,7 +11,8 @@
<span v-else>{{ getLabel('codeinfo.author') }} {{ translatedCode.author }}</span> <span v-else>{{ getLabel('codeinfo.author') }} {{ translatedCode.author }}</span>
</div> </div>
<p class="description" v-html="translatedCode.description"></p> <p class="description" v-html="translatedCode.description"></p>
<component v-if="configUI" :is="configUI" :version="version"></component> <component v-if="configUI" :is="configUI" :version="version"
:codeConfigs="codeConfigs" @config="$emit('config', {[code.id]: $event})" />
</div> </div>
</template> </template>
@ -24,6 +25,7 @@ export default {
anchor: { type: Boolean }, anchor: { type: Boolean },
code: { type: Object }, code: { type: Object },
version: { type: String }, version: { type: String },
codeConfigs: { type: Object },
}, },
computed: { computed: {
translatedCode: function () { translatedCode: function () {

View file

@ -42,7 +42,8 @@
<div v-if="codes && codes.length > 0" class="help"> <div v-if="codes && codes.length > 0" class="help">
<h3>{{ getLabel('headers.help') }}</h3> <h3>{{ getLabel('headers.help') }}</h3>
<CodeInfo v-if="!!inspectingCode" :code="inspectingCode" :version="selectedVersion" /> <CodeInfo v-if="!!inspectingCode" :code="inspectingCode" :version="selectedVersion"
:codeConfigs="codeConfigs" @config="onCodeConfigChanged" />
<div v-else-if="showStageLoaderHelp"> <div v-else-if="showStageLoaderHelp">
<h3>{{ getLabel('headers.stageloader') }}</h3> <h3>{{ getLabel('headers.stageloader') }}</h3>
<div> <div>
@ -109,6 +110,9 @@ import gameVersions from '../data/gameVersions.json';
// Util // Util
import { translate } from '../i18n/localeHelper'; import { translate } from '../i18n/localeHelper';
// Code Configs
import {getConfig as qftGetConfig} from './codes/qft/codegen';
export default { export default {
data() { data() {
return { return {
@ -122,6 +126,12 @@ export default {
stageLoaderCodes: [], stageLoaderCodes: [],
showStageLoaderHelp: false, showStageLoaderHelp: false,
generation: 0, generation: 0,
codeConfigs: {},
};
},
created() {
this.codeConfigs = {
qft: qftGetConfig(),
}; };
}, },
methods: { methods: {
@ -192,6 +202,10 @@ export default {
this.showStageLoaderHelp = false; this.showStageLoaderHelp = false;
this.inspectingCode = code; this.inspectingCode = code;
}, },
onCodeConfigChanged(e) {
this.codeConfigs = {...this.codeConfigs, ...e};
},
}, },
}; };
</script> </script>

View file

@ -0,0 +1,66 @@
<template>
<div class="preview-root">
<div class="preview-ctn">
<div :style="qft.bgStyle" />
<PreviewString v-if="qft" :x="qft.x" :y="qft.y" :size="qft.fontSize" :color="qft.color" text="0:00:00" />
<PreviewString v-if="mdp" :x="mdp.x" :y="mdp.y" :size="mdp.fontSize" :color="mdp.color" :text="mdp.text" />
<PreviewString :x="16" :y="320" :size="20" :color="'#fff'" text="Pattern #0 0 0" />
</div>
</div>
</template>
<script>
export default {
props: {
config: {type: Object},
},
computed: {
mdp() {
return {
x: 16,
y: 200,
fontSize: 20,
color: '#fff',
text: 'X Pos -4\nY Pos 27\nZ Pos -1515\nAngle 3117\nH Spd 4.26\nV Spd 4.28',
};
},
qft() {
const {config: {qft}} = this;
if (qft == null) return;
const {x, y, fontSize, fgRGB, fgA, fgRGB2, fgA2, bgRGB, bgA, width} = qft;
const i2s = (rgb, a) => '#'+rgb.toString(16).padStart('0', 6)+a.toString(16).padStart('0', 2);
const bg = i2s(bgRGB, bgA);
const fg1 = i2s(fgRGB, fgA);
return {
x, y, fontSize,
color: fgRGB2==null || fgA2==null ? fg1 : `linear-gradient(180deg, ${fg1}, ${i2s(fgRGB2, fgA2)})`,
bgStyle: {
left: x+'px',
top: (y-fontSize)+'px',
width: (width*fontSize/20)+'px',
height: fontSize+'px',
background: bg,
},
};
},
},
}
</script>
<style scoped>
div.preview-root {
position: relative;
width: 600px;
height: 448px;
background: url(/img/preview/background.png);
padding: 0;
overflow: hidden;
}
div.preview-ctn {
position: absolute;
top: -16px;
}
div.preview-ctn * {
position: absolute;
}
</style>

View file

@ -0,0 +1,97 @@
<template>
<div class="preview-str" :style="styles.root" >
<div v-for="style, i in styles.chars" :key="i" class="char-ctn" :style="style.ctn">
<div class="char-bg" :style="style.bg" />
<div class="char-mask" :style="style.mask" />
</div>
</div>
</template>
<script>
import charInfo from '../data/font-jp.json';
export default {
props: {
x: {type: Number},
y: {type: Number},
size: {type: Number},
color: {type: String},
text: {type: String},
},
computed: {
styles() {
const {x: x0, y: y0, size: fontSize, color, text} = this;
/** @type {{x: number, y: number, u: number, v: number}[]} */
const chars = [];
let x = 0;
let y = 0;
let useKerning = false;
text.split('').forEach(c => {
const {index, kerning, width} = charInfo[c] ?? charInfo[' '];
if (c === '\n') {
useKerning = false;
x = 0;
y += 20;
return;
}
if (useKerning) x -= kerning;
useKerning = true;
// uv
const [u, v] = [index%25*20, (index/25|0)*20];
chars.push({x, y, u, v});
// next
x += width + kerning;
});
return {
root: {
transform: `translate(${x0}px, ${y0-fontSize}px) scale(${fontSize/20})`,
},
chars: chars.map(({x, y, u, v}) => {
const offset = `${-u}px ${-v}px`;
return {
ctn: {
left: x+'px',
top: y+'px',
},
bg: {
'background-position': offset,
},
mask: {
'mask-position': offset,
'-webkit-mask-position': offset,
background: color,
},
};
}),
};
},
},
};
</script>
<style scoped>
.preview-str {
position: relative;
}
.preview-str * {
position: absolute;
}
div.char-ctn {
isolation: isolate;
}
div.char-ctn > div {
width: 20px;
height: 20px;
}
div.char-bg {
background: url(/img/preview/font-jp.png);
}
div.char-mask {
mask-image: url(/img/preview/font-jp.png);
-webkit-mask-image: url(/img/preview/font-jp.png);
mask-repeat: no-repeat;
-webkit-mask-repeat: no-repeat;
mix-blend-mode: multiply;
}
</style>

View file

@ -26,7 +26,8 @@ export const defaultConfig = {
}; };
export function getConfig() { export function getConfig() {
const config = parseJSON(localStorage.getItem(lskey)) ?? {}; const config =
(typeof localStorage !== 'undefined' && parseJSON(localStorage.getItem(lskey))) || {};
return { return {
...defaultConfig, ...defaultConfig,
...config, ...config,

View file

@ -22,21 +22,7 @@
<span>{{l.alpha}}</span><input type="number" min="0" max="255" v-model.number="bgA"><span>/255={{(bgA/2.55).toFixed(1)}}%</span> <span>{{l.alpha}}</span><input type="number" min="0" max="255" v-model.number="bgA"><span>/255={{(bgA/2.55).toFixed(1)}}%</span>
</div> </div>
<h4>{{l.preview}}</h4> <h4>{{l.preview}}</h4>
<svg viewBox="0 16 600 448"> <Preview :config="codeConfigs" />
<defs>
<linearGradient id="fgColor" x1="0%" y1="0%" x2="0%" y2="100%">
<stop offset="0%" :style="{stopColor: rgbI2S(fgRGB), stopOpacity: fgA/255}" />
<stop offset="100%" :style="{
stopColor: rgbI2S(fgRGB2 == null ? fgRGB : fgRGB2),
stopOpacity: (fgA2 == null ? fgA : fgA2)/255,
}" />
</linearGradient>
</defs>
<image href="/img/qft/preview-base.jpg" y="16" width="600" height="448" />
<rect :x="x" :y="y-fontSize" :width="width*fontSize/20" :height="fontSize" :fill="rgbaI2S(bgRGB, bgA)" />
<text :x="x+fontSize/10" :y="y-fontSize/10" fill="url(#fgColor)"
:style="{fontSize: fontSize+'px', fontFamily: 'auto'}">0:00.000</text>
</svg>
<div style="white-space: pre">{{l.previewNote}}</div> <div style="white-space: pre">{{l.previewNote}}</div>
</section> </section>
<section class="freeze"> <section class="freeze">
@ -58,23 +44,25 @@
</template> </template>
<script> <script>
import {getConfig, lskey, buttonValues, codes} from './codegen.js'; // import Preview from '../../PreviewString.vue';
import {getConfig, lskey, codes} from './codegen.js';
import labels from './labels.json'; import labels from './labels.json';
import {getLabels} from '../codegen.js';
function updateConfig() { function updateConfig() {
const {x, y, fontSize, width, fgRGB, fgA, fgRGB2, fgA2, bgRGB, bgA, freeze, freezeDuration} = this; const {x, y, fontSize, width, fgRGB, fgA, fgRGB2, fgA2, bgRGB, bgA, freeze, freezeDuration} = this;
localStorage.setItem(lskey, JSON.stringify({ const config = {
x, y, fontSize, width, fgRGB, fgA, fgRGB2, fgA2, bgRGB, bgA, freeze, freezeDuration x, y, fontSize, width, fgRGB, fgA, fgRGB2, fgA2, bgRGB, bgA, freeze, freezeDuration
})); };
localStorage.setItem(lskey, JSON.stringify(config));
this.$emit('config', config);
} }
export default { export default {
props: { props: {
version: {type: String}, version: {type: String},
codeConfigs: {type: Object},
}, },
methods: { methods: {
updateConfig,
onChangeFreeze($event, key) { onChangeFreeze($event, key) {
this.freeze[key] = $event.target.checked; this.freeze[key] = $event.target.checked;
this.updateConfig(); this.updateConfig();

File diff suppressed because it is too large Load diff

Binary file not shown.

After

Width:  |  Height:  |  Size: 505 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 74 KiB