前置知识:图片组成原理,android打印图片的图片数据格式
需求提前:打印小票需要在热熔纸上打印二维码图片
原理:利用canvas把所需打印的图片描绘出来,通过canvas api取去图像的数据,经过位图算法的出来得到位图数据,最后转化成android符合的数据格式
第一步:
<canvas style="width: 1000px;height: 1300px;" canvas-id="firstCanvas"></canvas>
新增canvas画布
第二部:
export default {
data (){
return{
canvasWidth:'',
canvasHeight:''
}
},
methods:{
renderCanvas() {
let filePath = '../../static/img/code.jpeg'
const firstCanvas = uni.createCanvasContext('firstCanvas', this);
let scla = 0.2
uni.getImageInfo({
src: filePath,
success:res=> {
this.canvasWidth = res.width * scla
this.canvasHeight = res.height * scla
firstCanvas.drawImage(filePath, 0, 0, this.canvasWidth, this.canvasHeight);
firstCanvas.draw(false, () => {
this.$nextTick(() => { //获取画布像素数据
uni.canvasGetImageData({
canvasId: 'firstCanvas',
x: 0,
y: 0,
width: this.canvasWidth,
height: this.canvasHeight,
success: (res) => {
this.byteData = setBitmap(res)
},
fail: function(res) {
console.log(res)
}
})
})
});
},
fail(res) {
console.log(res)
}
})
}
}
}
利用canvas画出二维码,获取图片信息。
第三步:引入print.js 文件
const _queue = [],
command = []
function intToByte(i) {
// 此处关键 -- android是java平台 byte数值范围是 [-128, 127]
// 因为java平台的byte类型是有符号的 最高位表示符号,所以数值范围固定
// 而图片计算出来的是数值是 0 -255 属于int类型
// 所以把int 转换成byte类型
var b = i & 0xFF;
var c = 0;
if (b >= 128) {
c = b % 128;
c = -1 * (128 - c);
} else {
c = b;
}
return c
}
function _enqueue(cmd) {
_queue.push(...cmd)
}
//生成位图数据
export function setBitmap(res) {
const w = res.width
const h = res.height
const bitw = parseInt(String((w + 7) / 8)) * 8
const bith = h
const pitch = parseInt(String(bitw / 8))
const bits = new Uint8Array(bith * pitch)
//设置打印图片居右
_enqueue([27])
_enqueue([97])
_enqueue([2])
//设置打印图片
_enqueue([29]) // 0x1D
_enqueue([118]) // 0x76
_enqueue([48]) // 0x30
_enqueue([0]) // 0x00
_enqueue([parseInt(String(pitch % 256))]) // 宽度的低8位
_enqueue([parseInt(String(pitch / 256))]) // 宽度的高8位
_enqueue([parseInt(String(bith % 256))]) // 高度的低8位
_enqueue([parseInt(String(bith / 256))]) // 高度的高8位
for (let y = 0; y < h; y++) {
for (let x = 0; x < w; x++) {
const color = res.data[(y * w + x) * 4]
if (color < 128) {
bits[parseInt(String(y * pitch + x / 8))] |= 0x80 >> x % 8
}
}
}
for (let i = 0; i < bits.length; i++) {
_enqueue([bits[i]])
}
for (let i = 0; i < _queue.length; ++i) {
command.push(intToByte(_queue[i]))
}
return command
}
生成位图原理:
1位:(黑白图像,每个像素使用1位存储,0代表黑,1代表白)
8位(灰度图像,每个像素使用8位存储,代表256种灰度)
24位(RGB彩色图像,每个像素使用24位存储,8位表示红、绿、蓝三种颜色)
在打印机上打印的是黑白图片,所以只需要取一个通道值可以了,跟取灰度的原理相似。
又因为取出来的图片数据是包含了三通道和透明度的,所以取值的时候需要每隔4个取值
灰度图像灰白两色的判断条件是获取的红色通道值(0-255)与 128 进行比较,判断该像素是接近黑色还是接近白色
如果 color < 128,表示颜色较暗,被视为黑色。
如果 color >= 128,表示颜色较亮,被视为白色。
const color = res.data[(y * w + x) * 4] 中 y * w + x 是一个常见的二维数组到一维数组的坐标转换公式,主要用于从一维数组中定位到二维图像中的某个像素
1. 二维图像的表示
假设图像的宽度为 w,高度为 h,则这个图像可以被看作一个 h 行 w 列的二维网格,其中每个像素位于一个 (x, y) 坐标位置上:
x 表示横向(列)的索引,范围是 0 <= x < w。
y 表示纵向(行)的索引,范围是 0 <= y < h。
2. 一维数组表示像素数据
通常图像的像素数据会存储在一个一维数组中,而不是直接存储在二维数组中。假设这个数组的长度是 w * h,每个元素存储一个像素值。
为了从一维数组中访问某个特定位置的像素,我们需要将二维坐标 (x, y) 转换为一维数组的索引。
设置位图数据:bits[parseInt(String(y * pitch + x / 8))] |= 0x80 >> (x % 8)
1、bits 是一个 Uint8Array 类型的数组,用于存储最终的位图数据,每个字节表示 8 个像素的黑白状态。
2、y * pitch + x / 8:计算出当前行的偏移量以及当前像素对应的字节位置(每 8 个像素存储为 1 个字节)。
- pitch 是一行的字节数,也就是每行能存放多少个 8 像素组。
- x / 8 表示当前像素在哪个字节内。
3、0x80 >> (x % 8):通过位移操作将当前像素位置转换成对应的位,0x80 表示一个字节的最高位,通过 >> 右移操作,将最高位移到当前像素的正确位置。
4、bits[...] |=:通过按位或运算,将这一位设置为 1,表示该像素是黑色。最终每个字节(bits[...])中的 8 位分别对应 8 个像素的黑白状态。
最后把生成的位图数据转化成android端符合的数据格式,条用intToByte方法就可以在打印机上打印了