Skip to content

Commit

Permalink
手动设置贴图大小;错误提示
Browse files Browse the repository at this point in the history
  • Loading branch information
elisaday committed Oct 1, 2018
1 parent a7e0db9 commit 9cddef0
Show file tree
Hide file tree
Showing 6 changed files with 199 additions and 35 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"version": "1.0.0",
"author": "zeb <[email protected]>",
"description": "bitmap font generator",
"license": null,
"license": "see LICENSE file",
"main": "./dist/electron/main.js",
"scripts": {
"build": "node .electron-vue/build.js && electron-builder",
Expand Down
86 changes: 86 additions & 0 deletions src/renderer/assets/bin-packing/packer.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
/******************************************************************************
This is a very simple binary tree based bin packing algorithm that is initialized
with a fixed width and height and will fit each block into the first node where
it fits and then split that node into 2 parts (down and right) to track the
remaining whitespace.
Best results occur when the input blocks are sorted by height, or even better
when sorted by max(width,height).
Inputs:
------
w: width of target rectangle
h: height of target rectangle
blocks: array of any objects that have .w and .h attributes
Outputs:
-------
marks each block that fits with a .fit attribute pointing to a
node with .x and .y coordinates
Example:
-------
var blocks = [
{ w: 100, h: 100 },
{ w: 100, h: 100 },
{ w: 80, h: 80 },
{ w: 80, h: 80 },
etc
etc
];
var packer = new Packer(500, 500);
packer.fit(blocks);
for(var n = 0 ; n < blocks.length ; n++) {
var block = blocks[n];
if (block.fit) {
Draw(block.fit.x, block.fit.y, block.w, block.h);
}
}
******************************************************************************/

Packer = function(w, h) {
this.init(w, h);
};

Packer.prototype = {

init: function(w, h) {
this.root = { x: 0, y: 0, w: w, h: h };
},

fit: function(blocks) {
var n, node, block;
for (n = 0; n < blocks.length; n++) {
block = blocks[n];
if (node = this.findNode(this.root, block.w, block.h))
block.fit = this.splitNode(node, block.w, block.h);
}
},

findNode: function(root, w, h) {
if (root.used)
return this.findNode(root.right, w, h) || this.findNode(root.down, w, h);
else if ((w <= root.w) && (h <= root.h))
return root;
else
return null;
},

splitNode: function(node, w, h) {
node.used = true;
node.down = { x: node.x, y: node.y + h, w: node.w, h: node.h - h };
node.right = { x: node.x + w, y: node.y, w: node.w - w, h: h };
return node;
}

}

module.exports = Packer
52 changes: 47 additions & 5 deletions src/renderer/components/Setting.vue
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,21 @@
<div class="control-row">
<label class="control-item">输出文件路径:</label>
<input class="form-control control-item-stretch" type="text" v-model="outputPath">
<button class="btn btn-default control-item" style="margin-left: 4px" @click="onClickBrowseOutputPath">
<span></span>
<button class="btn btn-default control-item" @click="onClickBrowseOutputPath">
<span class="icon icon-dot-3"></span>
</button>
</div>
<div class="control-row checkbox">
<label><input type="checkbox" v-model="autoSize">自动处理合并后贴图大小</label>
</div>
<div class="control-row" :style="{display: sizeHide}">
<label class="control-item">宽:</label>
<input class="form-control control-item" type="number" v-model="textureWidth" style="width: 80px">
<span></span>
<label class="control-item">高:</label>
<input class="form-control control-item" type="number" v-model="textureHeight" style="width: 80px">
</div>
<div class="control-row checkbox">
<label><input type="checkbox" v-model="npower2">合并后贴图大小强制2的N次幂</label>
</div>
Expand All @@ -15,7 +26,7 @@
</div>
<div class="control-row">
<label class="control-item">Padding:</label>
<input class="form-control control-item" type="text" v-model="padding">
<input class="form-control control-item" type="number" v-model="padding" style="width: 60px">
</div>
<div class="control-row">
<label class="control-item">合并算法:</label>
Expand All @@ -35,6 +46,31 @@
mounted () {
},
computed: {
sizeHide () {
return this.autoSize ? 'none' : ''
},
textureWidth: {
get () { return this.$store.state.Setting.textureWidth },
set (value) {
if (Number(value) < 1) value = 1
this.SET_TEXTURE_WIDTH(Number(value))
this.ON_PROJ_MODIFIED()
}
},
textureHeight: {
get () { return this.$store.state.Setting.textureHeight },
set (value) {
this.SET_TEXTURE_HEIGHT(Number(value))
this.ON_PROJ_MODIFIED()
}
},
autoSize: {
get () { return this.$store.state.Setting.autoSize },
set (value) {
this.ENABLE_AUTO_SIZE(value)
this.ON_PROJ_MODIFIED()
}
},
npower2: {
get () { return this.$store.state.Setting.NPower2 },
set (value) {
Expand All @@ -52,7 +88,7 @@
padding: {
get () { return this.$store.state.Setting.padding },
set (value) {
this.SET_PADDING(value)
this.SET_PADDING(Number(value))
this.ON_PROJ_MODIFIED()
}
},
Expand All @@ -73,7 +109,8 @@
},
methods: {
...mapMutations([
'ENABLE_NPOWER2', 'ENABLE_SAME_WH', 'SET_PADDING', 'SET_OUTPUT_PATH', 'SET_PACK_ALGO', 'SET_OUTPUT_PATH', 'ON_PROJ_MODIFIED'
'ENABLE_NPOWER2', 'ENABLE_SAME_WH', 'SET_PADDING', 'SET_OUTPUT_PATH', 'SET_PACK_ALGO',
'SET_OUTPUT_PATH', 'ON_PROJ_MODIFIED', 'ENABLE_AUTO_SIZE', 'SET_TEXTURE_WIDTH', 'SET_TEXTURE_HEIGHT'
]),
onClickBrowseOutputPath () {
Expand Down Expand Up @@ -123,7 +160,12 @@
.control-item-stretch {
flex-grow: 1;
width: auto;
}
}
span {
margin-left: 6px;
margin-right: 6px;
}
}
.form-control {
Expand Down
5 changes: 4 additions & 1 deletion src/renderer/store/modules/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,10 @@ async function openProj (commit) {
{ name: 'BMFont Project File', extensions: ['bfp'] },
{ name: 'All Files', extensions: ['*'] }
]
})[0]
})
if (projPath === undefined) return

projPath = projPath[0]

try {
let data = await readFile(projPath, { encoding: 'utf8' })
Expand Down
70 changes: 43 additions & 27 deletions src/renderer/store/modules/BMPList.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import { remote } from 'electron'
import GrowingPacker from '../../assets/bin-packing/packer.growing.js'
import Packer from '../../assets/bin-packing/packer.js'
import Jimp from 'jimp/es'
import * as path from 'path'
import * as xmlbuilder from 'xmlbuilder'
Expand All @@ -11,39 +13,51 @@ const state = {
bmpList: []
}

function packingImages (imgList, padding) {
let packer = new GrowingPacker()
function calculateSize (w, h, setting) {
if (setting.NPower2) {
[w, h] = expandSize(w, h)
}
if (setting.sameWH) {
let s = Math.max(w, h)
w = s
h = s
}
return [w, h]
}

function packingImages (imgList, setting) {
let packer
let w, h
if (setting.autoSize) {
packer = new GrowingPacker()
} else {
[w, h] = calculateSize(setting.textureWidth, setting.textureHeight, setting)
packer = new Packer(w, h)
}
let blocks = []
for (let img of imgList) {
blocks.push({ w: img.bitmap.width + padding, h: img.bitmap.height + padding })
blocks.push({ w: img.bitmap.width + setting.padding, h: img.bitmap.height + setting.padding })
}
packer.fit(blocks)
return blocks
if (setting.autoSize) {
[w, h] = calculateSize(packer.root.w, packer.root.h, setting)
}
return [blocks, w, h]
}

function expandSize (size) {
let w = 2 ** Math.ceil(Math.log(size.w) / Math.log(2))
let h = 2 ** Math.ceil(Math.log(size.h) / Math.log(2))
return { w: w, h: h }
function expandSize (w, h) {
w = 2 ** Math.ceil(Math.log(w) / Math.log(2))
h = 2 ** Math.ceil(Math.log(h) / Math.log(2))
return [w, h]
}

function validateBlocks (blocks) {
for (let b of blocks) {
if (!b.fit || !b.fit.used) {
throw b
return false
}
}
}

function calculateSize (blocks) {
let maxWidth = 0
let maxHeight = 0
for (let idx in blocks) {
let block = blocks[idx]
if (block.fit.x + block.fit.w > maxWidth) maxWidth = block.fit.x + block.fit.w
if (block.fit.y + block.fit.h > maxHeight) maxHeight = block.fit.y + block.fit.h
}
return { w: maxWidth, h: maxHeight }
return true
}

async function loadAllImages (bmpList) {
Expand Down Expand Up @@ -116,14 +130,15 @@ const actions = {
let setting = rootState.Setting
try {
let imgList = await loadAllImages(state.bmpList)
let blocks = packingImages(imgList, setting.padding)
validateBlocks(blocks)
let [blocks, w, h] = packingImages(imgList, setting)
if (!validateBlocks(blocks)) {
remote.dialog.showErrorBox('', '贴图太小了,不能容纳所有的字符')
return
}

let size = calculateSize(blocks)
if (setting.NPower2) size = expandSize(size)
console.log(size)
console.log(w, h)

let resultImg = await new Jimp(size.w, size.h)
let resultImg = await new Jimp(w, h)
for (let idx in blocks) {
let block = blocks[idx]
let img = imgList[idx]
Expand All @@ -134,7 +149,8 @@ const actions = {
let fntPath = setting.outputPath.substring(0, setting.outputPath.length - ext.length) + '.xml'
saveFNT(blocks, state.bmpList, imgList, fntPath)
} catch (e) {
console.log(e)
console.dir(e)
remote.dialog.showErrorBox('', '请检查字符图片文件是否正确\n' + e.message)
}
}
}
Expand Down
19 changes: 18 additions & 1 deletion src/renderer/store/modules/Setting.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@ const state = {
padding: 1,
NPower2: true,
sameWH: true,
autoSize: true,
textureWidth: 1024,
textureHeight: 1024,
outputPath: 'untitiled.png',
packAlgo: 'Bin-Packing'
}
Expand All @@ -13,6 +16,9 @@ const mutations = {
if (save.padding !== undefined) state.padding = Number(save.padding)
if (save.NPower2 !== undefined) state.NPower2 = save.NPower2
if (save.sameWH !== undefined) state.sameWH = save.sameWH
if (save.autoSize !== undefined) state.autoSize = save.autoSize
if (save.textureWidth !== undefined) state.textureWidth = save.textureWidth
if (save.textureHeight !== undefined) state.textureHeight = save.textureHeight
if (save.outputPath !== undefined) state.outputPath = save.outputPath
if (save.packAlgo !== undefined) state.packAlgo = save.packAlgo
}
Expand All @@ -21,17 +27,28 @@ const mutations = {
state.padding = 1
state.NPower2 = true
state.sameWH = true
state.autoSize = true
state.textureWidth = 1024
state.textureHeight = 1024
state.outputPath = 'untitiled.png'
state.packAlgo = 'Bin-Packing'
},
ENABLE_AUTO_SIZE (state, autoSize) {
state.autoSize = autoSize
},
SET_TEXTURE_WIDTH (state, width) {
state.textureWidth = Number(width)
},
SET_TEXTURE_HEIGHT (state, height) {
state.textureHeight = Number(height)
},
SET_PADDING (state, padding) {
state.padding = Number(padding)
},
ENABLE_NPOWER2 (state, enabled) {
state.NPower2 = enabled
},
ENABLE_SAME_WH (state, enabled) {
console.log(state.sameWH, enabled)
state.sameWH = enabled
},
SET_OUTPUT_PATH (state, outputPath) {
Expand Down

0 comments on commit 9cddef0

Please sign in to comment.