|
64 | 64 | left: 0;
|
65 | 65 | top: 288px;
|
66 | 66 | min-width: 288px;
|
67 |
| - min-height: 200px; |
| 67 | + min-height: 96px; |
68 | 68 | background: #ccc;
|
69 | 69 | padding: 16px;
|
70 | 70 | box-sizing: border-box;
|
|
88 | 88 | background: #f7f7f7;
|
89 | 89 | margin: 16px;
|
90 | 90 | width: calc(100% - 32px);
|
91 |
| - height: 500px; |
92 | 91 | }
|
93 | 92 |
|
94 | 93 | @media screen and (max-width: 1000px) {
|
|
131 | 130 | height: 32px;
|
132 | 131 | }
|
133 | 132 |
|
| 133 | +.input > label { |
| 134 | + flex: 1; |
| 135 | + display: flex; |
| 136 | + align-items: center; |
| 137 | +} |
| 138 | + |
134 | 139 | .input > input {
|
135 | 140 | border: none;
|
136 | 141 | background: #bbb;
|
|
196 | 201 | <div class="header">Minecraft UNICODE Font Texture Genterator Online</div>
|
197 | 202 | <div class="content">
|
198 | 203 | <div class="preview">
|
199 |
| - <div class="preview-info"> |
200 |
| - Loading... |
201 |
| - </div> |
| 204 | + <code class="preview-info">Loading...</code> |
202 | 205 | <canvas id="preview-canvas"></canvas>
|
203 | 206 | </div>
|
204 | 207 | <div class="panel">
|
|
228 | 231 | <input name="gridsize" type="number" min="8" step="1" value="16"/>
|
229 | 232 | </div>
|
230 | 233 | </div>
|
| 234 | + <div class="yoffset"> |
| 235 | + <div class="label"> |
| 236 | + Y-axis Offset: |
| 237 | + </div> |
| 238 | + <div class="input"> |
| 239 | + <input name="yoffset" type="number" value="0"/> |
| 240 | + </div> |
| 241 | + </div> |
231 | 242 | <div class="formatstring">
|
232 | 243 | <div class="label">
|
233 |
| - Format Template: |
| 244 | + Output Textures(expr): |
| 245 | + </div> |
| 246 | + <div class="input"> |
| 247 | + <input name="formatstring" value="assets/minecraft/textures/font/unicode_page_%02x.png"/> |
| 248 | + </div> |
| 249 | + </div> |
| 250 | + <div class="glyph_sizes_bin"> |
| 251 | + <div class="label"> |
| 252 | + glyph_sizes.bin(expr): |
234 | 253 | </div>
|
235 | 254 | <div class="input">
|
236 |
| - <input name="formatstring" value="unicode_page_%02x.png"/> |
| 255 | + <input name="glyph_sizes_bin" value="assets/minecraft/font/glyph_sizes.bin"/> |
237 | 256 | </div>
|
238 | 257 | </div>
|
239 |
| - <div class="exportfilename"> |
| 258 | + <div class="metamode"> |
240 | 259 | <div class="label">
|
241 |
| - Export File Name: |
| 260 | + Meta format |
242 | 261 | </div>
|
243 | 262 | <div class="input">
|
244 |
| - <input name="exportfilename" value="export.zip"/> |
| 263 | + <label> |
| 264 | + <input name="metamode" type="radio" value="none" />NONE |
| 265 | + </label> |
| 266 | + <label> |
| 267 | + <input name="metamode" type="radio" value="pc" checked/>PC |
| 268 | + </label> |
| 269 | + <label> |
| 270 | + <input name="metamode" type="radio" value="pe" disabled/>PE |
| 271 | + </label> |
| 272 | + </div> |
| 273 | + </div> |
| 274 | + <div class="pack_format"> |
| 275 | + <div class="label"> |
| 276 | + pack_format: |
| 277 | + </div> |
| 278 | + <div class="input"> |
| 279 | + <input name="pack_format" type="number" value="1" min="1"/> |
| 280 | + </div> |
| 281 | + </div> |
| 282 | + <div class="description_format"> |
| 283 | + <div class="label"> |
| 284 | + description format: |
| 285 | + </div> |
| 286 | + <div class="input"> |
| 287 | + <input name="description_format" value="FONT %s %dx(%d)"/> |
245 | 288 | </div>
|
246 | 289 | </div>
|
247 | 290 | </form>
|
|
250 | 293 | <button id="gen_btn">Generate!</button>
|
251 | 294 | <button id="reset_btn">Reset!</button>
|
252 | 295 | </div>
|
253 |
| - <hr /> |
254 | 296 | </div>
|
255 | 297 | </div>
|
256 | 298 | </div>
|
|
269 | 311 | const gen_btn = document.querySelector("#gen_btn");
|
270 | 312 | const reset_btn = document.querySelector("#reset_btn");
|
271 | 313 | const form = document.forms[0];
|
272 |
| - const drawCharAt = (ctx, size, ch, x, y) => { |
273 |
| - ctx.fillText(ch, x, y); |
274 |
| - return ctx.measureText(ch) / size * 0xF; |
275 |
| - }; |
276 |
| - const drawPage = (ctx, glyph, size, start, grid) => { |
277 |
| - for (let i = 0; i < 16; i++) |
278 |
| - for (let j = 0; j < 16; j++) |
279 |
| - glyph[(start << 8) + i * 16 + j] = drawCharAt(ctx, size, String.fromCharCode((start << 8) + i * 16 + j), j * grid, i * grid); |
280 |
| - }; |
281 | 314 | const stripDataurl = dataurl => dataurl.substr(dataurl.indexOf(',') + 1);
|
282 | 315 | const updateProgress = ch => preview_info.innerText = sprintf("Process %02x (%5.2f%%)", ch, ch / 0xFF * 100);
|
283 | 316 |
|
| 317 | + const lock = (() => { |
| 318 | + let startStatus = false; |
| 319 | + let stopStatus = false; |
| 320 | + return { |
| 321 | + init() { |
| 322 | + if (startStatus) { |
| 323 | + stopStatus = true; |
| 324 | + return true; |
| 325 | + } |
| 326 | + startStatus = true; |
| 327 | + stopStatus = false; |
| 328 | + preview_info.innerText = "Starting..."; |
| 329 | + gen_btn.innerText = "Stop!"; |
| 330 | + return false; |
| 331 | + }, |
| 332 | + stop() { |
| 333 | + startStatus = false; |
| 334 | + preview_info.innerText = "Ready."; |
| 335 | + gen_btn.innerText = "Generate!"; |
| 336 | + }, |
| 337 | + sholdAbort() { |
| 338 | + if (stopStatus) { |
| 339 | + lock.stop(); |
| 340 | + return true; |
| 341 | + } |
| 342 | + return false; |
| 343 | + } |
| 344 | + } |
| 345 | + })(); |
| 346 | + |
284 | 347 | gen_btn.addEventListener("click", () => {
|
| 348 | + if (lock.init()) return; |
285 | 349 | const zip = new JSZip();
|
286 |
| - const textures = zip.folder("textures"); |
287 | 350 | const ctx = preview_canvas.getContext('2d');
|
288 | 351 | const glyph_sizes = new Uint8Array(64 * 1024);
|
289 |
| - preview_canvas.width = form.elements.gridsize.value * 16; |
290 |
| - preview_canvas.height = form.elements.gridsize.value * 16; |
| 352 | + const yoffset = parseInt(form.elements.yoffset.value); |
| 353 | + const fontsize = form.elements.fontsize.value; |
| 354 | + const fontname = form.elements.fontname.value; |
| 355 | + const formatstring = form.elements.formatstring.value; |
| 356 | + const gridsize = form.elements.gridsize.value; |
| 357 | + const glyph_sizes_bin = form.elements.glyph_sizes_bin.value; |
| 358 | + const metamode = form.elements.metamode.value; |
| 359 | + const pack_format = parseInt(form.elements.pack_format.value); |
| 360 | + const description_format = form.elements.description_format.value; |
| 361 | + preview_canvas.width = gridsize * 16; |
| 362 | + preview_canvas.height = gridsize * 16; |
291 | 363 | ctx.fillStyle = "white";
|
292 |
| - ctx.font = sprintf("%dpx %s", form.elements.fontsize.value, form.elements.fontname.value); |
| 364 | + ctx.font = sprintf("%dpx %s", fontsize, fontname); |
293 | 365 | ctx.textBaseline = "top";
|
| 366 | + |
| 367 | + const drawCharAt = (size, ch, x, y) => { |
| 368 | + ctx.fillText(ch, x, y + yoffset); |
| 369 | + return (n => n > 0xF ? 0xF : n)(ctx.measureText(ch).width / size * 0xF); |
| 370 | + }; |
| 371 | + const drawPage = (start, grid) => { |
| 372 | + for (let i = 0; i < 16; i++) |
| 373 | + for (let j = 0; j < 16; j++) |
| 374 | + glyph_sizes[(start << 8) + i * 16 + j] = drawCharAt(grid, String.fromCharCode((start << 8) + i * 16 + j), j * grid, i * grid); |
| 375 | + }; |
294 | 376 | const draw = current => {
|
| 377 | + if (lock.sholdAbort()) return ctx.clearRect(0, 0, preview_canvas.width, preview_canvas.height);; |
295 | 378 | updateProgress(current);
|
296 | 379 | ctx.clearRect(0, 0, preview_canvas.width, preview_canvas.height);
|
297 |
| - drawPage(ctx, glyph_sizes, form.elements.gridsize.value, current, form.elements.gridsize.value); |
298 |
| - textures.file(sprintf(form.elements.formatstring.value, current), stripDataurl(preview_canvas.toDataURL("image/png")), {base64: true}); |
| 380 | + drawPage(current, gridsize); |
| 381 | + zip.file(sprintf(formatstring, current), stripDataurl(preview_canvas.toDataURL("image/png")), {createFolders: true, base64: true}); |
299 | 382 | if (current == 0xff) return finish();
|
300 | 383 | setTimeout(() => draw(current + 1), 10);
|
301 | 384 | };
|
302 | 385 | draw(0);
|
303 | 386 | const finish = () => {
|
304 | 387 | preview_info.innerText = "storing glyph_sizes.bin";
|
305 |
| - zip.file("glyph_sizes.bin", glyph_sizes, {binary: true}); |
| 388 | + zip.file(glyph_sizes_bin, glyph_sizes, {binary: true, createFolders: true}); |
| 389 | + switch (metamode) { |
| 390 | + case 'pc': |
| 391 | + preview_info.innerText = "creating metafile"; |
| 392 | + zip.file("pack.mcmeta", JSON.stringify({pack: {pack_format, description: sprintf(description_format, fontname, gridsize, fontsize)}}, null, '\t')); |
| 393 | + break; |
| 394 | + default: |
| 395 | + preview_info.innerText = "skipping creating meta file."; |
| 396 | + } |
306 | 397 | preview_info.innerText = "finish...generating zip file...."
|
307 | 398 | zip.generateAsync({type:"blob"}).then(content => {
|
308 |
| - saveAs(content, form.elements.exportfilename.value); |
309 |
| - preview_info.innerText = "Ready."; |
| 399 | + saveAs(content, sprintf("%s x%d(%d).zip", fontname, gridsize, fontsize)); |
| 400 | + lock.stop(); |
310 | 401 | });
|
311 | 402 | };
|
312 | 403 | });
|
|
0 commit comments