📄 bitmap.java
字号:
public class Bitmap {
public static final String BITMAP_FLAG = "BM";
public static final String[] BITMAP_FLAGS = new String[]{"BM","BA","CI", "CP", "IC", "PT"};
public static final int[] BITSPERPIXELS = new int[] {1, 4, 8, 16, 24, 32};
public static final int[] COMPRESSIONS = new int[] {0,1,2,3};
/**
* 文件头
*/
private Header header;
/**
* 图象数据 根据BMP版本及调色板尺寸的不同而不同 Bitmap Data xxx bytes 该域的大小取决于压缩方法及图像的尺寸和图像的位深度,它包含所有的位图数据字节,这些数据可能是彩色调色板的索引号,也可能是实际的RGB值,这将根据图像信息头中的位深度值来决定。
*/
private byte[] data;
public boolean valid() {
if( this.header == null || !this.header.valid() ) return false;
if( this.data == null && this.header.dataSize > 0 || this.data.length != this.header.dataSize ) return false;
return true;
}
public byte[] getByte() {
if( !this.valid() ) return null;
byte[] buffer = new byte[this.header.headerSize + this.header.dataSize];
byte[] headerBuffer = this.header.getByte();
this.fill(buffer, 0, headerBuffer, 0, headerBuffer.length);
if( this.data != null ) this.fill(buffer, 0, this.data, 0, this.data.length);
return buffer;
}
public boolean read(byte[] buffer) {
Header h = new Header();
if( !h.read(buffer, 0) ) return false;
if( !h.valid() ) return false;
if( buffer.length < h.offset + h.dataSize ) return false;
this.data = new byte[h.dataSize];
this.header = h;
this.fill(this.data, 0, buffer, h.offset, h.dataSize);
return true;
}
public Header getHeader() {
return header;
}
public byte[] getData() {
return data;
}
public void setPixel(int x, int y, int color) {
Pixel p = new Pixel(x, y);
p.set(color);
}
public int getPixel(int x, int y) {
Pixel p = new Pixel(x, y);
return p.get();
}
public class Pixel {
private int x;
private int y;
private int colorIndex;
private int byteIndex;
private int byteLength;
private int bitOffset;
public Pixel(int x, int y) {
this.x = x;
this.y = y;
int bitWidth = header.width * header.bitsPerPixel;
int byteWidth = bitWidth / 8;
if( bitWidth % 8 > 0 ) byteWidth ++;
if( byteWidth % 4 > 0 ) byteWidth += 4 - byteWidth % 4;
int bitX = x * header.bitsPerPixel;
int byteX = bitX / 8;
this.bitOffset = bitX % 8;
this.byteIndex = y * byteWidth + byteX;
this.byteLength = header.bitsPerPixel / 8 + (header.bitsPerPixel % 8 > 0 ? 1 : 0);
}
public int get() {
byte[] pixel = getbytes(data, byteIndex, byteLength );
String s = "";
for(int i=0; i<pixel.length; i++) {
String ss = "00000000" + Integer.toBinaryString(pixel[i]);
s = s + ss.substring(ss.length()-8);
}
s = s.substring(bitOffset, header.bitsPerPixel);
int colorIndex = Integer.valueOf(s, 2).intValue();
int color = this.getColor(colorIndex);
return color;
}
public void set(int color) {
String str0 = Long.toBinaryString(0x100000000l).substring(1);
String str1 = Long.toBinaryString(0xFFFFFFFFl);
String mask = str1.substring(0,this.bitOffset) + str0.substring(0,header.bitsPerPixel) + str1.substring(0,this.byteLength*8-header.bitsPerPixel-this.bitOffset);
int value = this.getColorIndex(color);
String vbits = Long.toBinaryString(0x100000000l+value);
vbits = vbits.substring(vbits.length() - header.bitsPerPixel);
vbits = mask.substring(0,this.bitOffset) + vbits + mask.substring(this.bitOffset+header.bitsPerPixel);
String bits = this.getBinaryString(data, this.byteIndex, this.byteLength);
long lmask = Long.valueOf(mask, 2);
long lvmask = Long.valueOf(vbits, 2);
long lbytes = Long.valueOf(bits, 2);
lbytes &= lmask;
lbytes |= lvmask;
bits = Long.toBinaryString(0x100000000l + lbytes);
bits = bits.substring(bits.length() - this.byteLength * 8);
for(int i=0; i<this.byteLength; i++) {
data[this.byteIndex+i] = (byte)Integer.valueOf(bits.substring(i*8,(i+1)*8), 2).intValue();
}
}
private String getBinaryString(byte[] buffer, int index, int length) {
String s = "";
for(int i=0; i<length; i++) {
String ss = "00000000" + Integer.toBinaryString(buffer[index+i]);
s = s + ss.substring(ss.length()-8);
}
return s;
}
private int getColor(int index) {
int color = 0;
if( header.getPalette() != null && header.palette.length != 0 ){
if( header.palette.length > index ) color = header.palette[index].getColor();
}
else {
int cmax = Integer.valueOf(Integer.toBinaryString(0xFFFFFF).substring(0,header.bitsPerPixel),2);
int Cmax = 0xFFFFFF;
int Cindex = Cmax / cmax * index;
color = Cindex;
}
return color;
}
private int getColorIndex(int color) {
int index = 0;
if( header.getPalette() != null && header.palette.length != 0 ) {
for(int i=0; i<header.palette.length; i++)
if( header.palette[i].getColor() == color ) return i;
}
else {
int cmax = Integer.valueOf(Integer.toBinaryString(0xFFFFFF).substring(0,header.bitsPerPixel),2);
int Cmax = 0xFFFFFF;
index = (cmax + 1) / (Cmax + 1) * (color + 1) - 1;
}
return index;
}
}
/**
* BITMAP图像头
*
* @author Katherine
*
*/
public class Header {
/**
* 0000h
* 文件标识 2 bytes 两字节的内容用来识别位图的类型:
* ‘BM’ : Windows 3.1x, 95, NT, …
* ‘BA’ :OS/2 Bitmap Array
* ‘CI’ :OS/2 Color Icon
* ‘CP’ :OS/2 Color Pointer
* ‘IC’ : OS/2 Icon
* ‘PT’ :OS/2 Pointer
* 注:因为OS/2系统并没有被普及开,所以在编程时,你只需判断第一个标识“BM”就行。
*/
private String flag;
/**
* 0002h
* File Size 1 dword 用字节表示的整个文件的大小
*/
private int fileSize;
/**
* 0006h
* Reserved 1 dword 保留,必须设置为0
*/
private int reserved = 0;
/**
* 000Ah
* Bitmap Data Offset 1 dword 从文件开始到位图数据开始之间的数据(bitmap data)之间的偏移量
*/
private int offset;
/**
* 000Eh
* Bitmap Header Size 1 dword 位图信息头(Bitmap Info Header)的长度,用来描述位图的颜色、压缩方法等。下面的长度表示:
* 28h - Windows 3.1x, 95, NT, …
* 0Ch - OS/2 1.x
* F0h - OS/2 2.x
* 注:在Windows95、98、2000等操作系统中,位图信息头的长度并不一定是28h,因为微软已经制定出了新的BMP文件格式,其中的信息头结构变化比较大,长度加长。所以最好不要直接使用常数28h,而是应该从具体的文件中读取这个值。这样才能确保程序的兼容性。
*/
private int headerSize;
/**
* 0012h
* Width 1 dword 位图的宽度,以象素为单位
*/
private int width;
/**
* 0016h
* Height 1 dword 位图的高度,以象素为单位
*/
private int height;
/**
* 001Ah
* Planes 1 word 位图的位面数(注:该值将总是1)
*/
private short planes = 1;
/**
* 001Ch
* Bits Per Pixel 1 word 每个象素的位数
* 1 - 单色位图(实际上可有两种颜色,缺省情况下是黑色和白色。你可以自己定义这两种颜色)
* 4 - 16 色位图
* 8 - 256 色位图
* 16 - 16bit 高彩色位图
* 24 - 24bit 真彩色位图
* 32 - 32bit 增强型真彩色位图
*/
private short bitsPerPixel = 1;
/**
* 001Eh
* Compression 1 dword 压缩说明:
* 0 - 不压缩 (使用BI_RGB表示)
* 1 - RLE 8-使用8位RLE压缩方式(用BI_RLE8表示)
* 2 - RLE 4-使用4位RLE压缩方式(用BI_RLE4表示)
* 3 - Bitfields-位域存放方式(用BI_BITFIELDS表示)
*/
private int compression;
/**
* 0022h
* Bitmap Data Size 1 dword 用字节数表示的位图数据的大小。该数必须是4的倍数
*/
private int dataSize;
/**
* 0026h
* HResolution 1 dword 用象素/米表示的水平分辨率
*/
private int hResolution;
/**
* 002Ah
* VResolution 1 dword 用象素/米表示的垂直分辨率
*/
private int vResolution;
/**
* 002Eh
* Colors 1 dword 位图使用的颜色数。如8-比特/象素表示为100h或者 256.
*/
private int colors;
/**
* 0032h
* Important Colors 1 dword 指定重要的颜色数。
* 当该域的值等于颜色数时(或者等于0时),表示所有颜色都一样重要
*/
private int importantColors;
/**
* 0036h
* 调色板数据
* 根据BMP版本的不同而不同
* Palette N * 4 byte 调色板规范。
*/
private Palette[] palette;
public Header() {
this.flag = BITMAP_FLAGS[0];
this.fileSize = 0;
this.offset = 0;
this.headerSize = 0x0036;
this.width = 0;
this.height = 0;
this.bitsPerPixel = 1;
this.compression = 0;
this.dataSize = 0;
this.hResolution = 0;
this.vResolution = 0;
this.colors = 0x0010;
this.importantColors = 0;
this.palette = null;
}
public boolean valid() {
if( this.flag == null ) return false;
boolean isvalid = false;
for(int i=0; i<BITMAP_FLAGS.length; i++)
if( isequal(this.flag.getBytes(), BITMAP_FLAGS[i].getBytes()) ) {
isvalid = true;
break;
}
if( !isvalid ) return false;
if( this.fileSize != this.offset + this.dataSize ) return false;
if( this.reserved != 0 ) return false;
if( this.offset < (0x0036 + (this.palette == null ? 0 : this.palette.length)*Palette.SIZE) ) return false;
if( this.width < 0 ) return false;
if( this.height < 0 ) return false;
if( this.planes != 1 ) return false;
isvalid = false;
for(int i=0; i<BITSPERPIXELS.length; i++)
if( BITSPERPIXELS[i] == this.bitsPerPixel ) {
isvalid = true;
break;
}
if( !isvalid ) return false;
isvalid = false;
for(int i=0; i<BITSPERPIXELS.length; i++)
if( BITSPERPIXELS[i] == this.bitsPerPixel ) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -