背景
项目中使用手写板,用户在上面签名后,将电子签名传到后台,同时将签名连同别的信息打印出来。
手写板已经有很多优秀的开源库,也可以动手撸一个。
手写板签名后可以得到签名的Bitmap,而所用的硬件是有打印接口的,可以接收Bitmap为参数打印图片,这样看似需求就能实现了。
问题
但打印接口接收的Bitmap必须是单色位图,类似于二维码,每个像素点非黑即白,非1即0。
其实我们在使用zxing
生成二维码时,也利用zxing
的api对每个像素点进行了判断,赋予其黑或白。
而在带有签名的Bitmap
中,很明显有笔迹的像素点应该是黑色,其余应该是白色的,这样打印出来就没问题了。
那怎样判断某个像素点是否有笔迹呢?
突破口
这就要靠下面这个神奇的方法:Bitmap.getPixel(x, y)
:
/**
* Returns the {@link Color} at the specified location. Throws an exception
* if x or y are out of bounds (negative or >= to the width or height
* respectively). The returned color is a non-premultiplied ARGB value.
*
* @param x The x coordinate (0...width-1) of the pixel to return
* @param y The y coordinate (0...height-1) of the pixel to return
* @return The argb {@link Color} at the specified coordinate
* @throws IllegalArgumentException if x, y exceed the bitmap's bounds
*/
@ColorInt
public int getPixel(int x, int y) {
checkRecycled("Can't call getPixel() on a recycled bitmap");
checkPixelAccess(x, y);
return nativeGetPixel(mNativePtr, x, y);
}
该方法返回坐标x, y处的颜色,当返回值为背景色时,即表示空白;否则表示有笔迹。
注意:不同Bitmap的背景色可能不同!一般为白色或透明。
Bitmap
还有一个setPixels
方法,能重新设置每个像素点的颜色。
/**
* <p>Replace pixels in the bitmap with the colors in the array. Each element
* in the array is a packed int prepresenting a non-premultiplied ARGB
* {@link Color}.</p>
*
* @param pixels The colors to write to the bitmap
* @param offset The index of the first color to read from pixels[]
* @param stride The number of colors in pixels[] to skip between rows.
* Normally this value will be the same as the width of
* the bitmap, but it can be larger (or negative).
* @param x The x coordinate of the first pixel to write to in
* the bitmap.
* @param y The y coordinate of the first pixel to write to in
* the bitmap.
* @param width The number of colors to copy from pixels[] per row
* @param height The number of rows to write to the bitmap
*
* @throws IllegalStateException if the bitmap is not mutable
* @throws IllegalArgumentException if x, y, width, height are outside of
* the bitmap's bounds.
* @throws ArrayIndexOutOfBoundsException if the pixels array is too small
* to receive the specified number of pixels.
*/
public void setPixels(@ColorInt int[] pixels, int offset, int stride,
int x, int y, int width, int height) {
checkRecycled("Can't call setPixels() on a recycled bitmap");
if (!isMutable()) {
throw new IllegalStateException();
}
if (width == 0 || height == 0) {
return; // nothing to do
}
checkPixelsAccess(x, y, width, height, offset, stride, pixels);
nativeSetPixels(mFinalizer.mNativeBitmap, pixels, offset, stride,
x, y, width, height);
}
解决方案
结合这两个方法,将Bitmap
转为单色位图的思路就是:
- 遍历
Bitmap
上的每个像素点 - 每次遍历时,先将像素点赋值白色。
- 利用
getPixel
方法判断是否有笔迹,有的话再赋值黑色。 - 遍历结束后,利用
setPixels
方法使颜色设置作用于Bitmap
上。
按照这个思路,可以写出下面的转换方法:
private Bitmap getSingleColorBitmap(Bitmap bitmap, @ColorInt int bgColor) {
int[] pix = new int[bitmap.getWidth() * bitmap.getHeight()];
boolean isBitmapEmpty = true;
for (int y = 0; y < bitmap.getHeight(); y++) {
for (int x = 0; x < bitmap.getWidth(); x++) {
int index = y * bitmap.getWidth() + x;
pix[index] = WHITE;
if (bitmap.getPixel(x, y) != bgColor) {
isBitmapEmpty = false;
pix[index] = BLACK;
}
}
}
if (isBitmapEmpty) {
return null;
}
bitmap.setPixels(pix, 0, bitmap.getWidth(), 0, 0, bitmap.getWidth(), bitmap.getHeight());
return bitmap;
}
同时该方法使用标志位isBitmapEmpty
判断图片是否空白。