用于读写BMP文件的golang版源码
源码基于源码分享-golang的二进制文件读写库 https://blog.csdn.net/zhyulo/article/details/128890546
BMP文件格式可参考位图文件解析-位图(bmp)、图标(ico)与光标(cur) https://blog.csdn.net/zhyulo/article/details/85934728
import ("binary""bufio""errors""fmt""os"
)var ofb = errors.New("out of bound")type BitmapFile struct {Header BitmapFileHeaderInfo BitmapInfoHeaderQuad BitmapQuad `info:"调色板"`BitLines BitmapLines `info:"点阵数据"`
}func ReadBitmapFile(path string) (*BitmapFile, error) {buf, err := os.ReadFile(path)if err != nil {return nil, err}var bmp BitmapFileerr = binary.Unmarshal(buf, false, &bmp)return &bmp, err
}func NewBitmapFile(width, height int32, bitCount uint16) *BitmapFile {bmp := &BitmapFile{Header: BitmapFileHeader{Type: MagicBmp{'B', 'M'}},Info: BitmapInfoHeader{Size: 40,Width: width,Height: height,Planes: 1,BitCount: bitCount,},}if height < 0 {height = -height}bmp.BitLines = make(BitmapLines, height, height)bytePerLine := (int32(bitCount)*width + 31) / 32 * 4for i := int32(0); i < height; i++ {bmp.BitLines[i] = make(BitmapLine, bytePerLine, bytePerLine)}bmp.Info.SizeImage = uint32(bytePerLine * height)if bitCount < 16 {bmp.Info.ClrUsed = 1 << bitCountbmp.Quad = make(BitmapQuad, bmp.Info.ClrUsed, bmp.Info.ClrUsed)}bmp.Header.OffBits = 14 + bmp.Info.Size + uint32(len(bmp.Quad))*4bmp.Header.Size = bmp.Header.OffBits + bmp.Info.SizeImagereturn bmp
}func (v *BitmapFile) SetQuad(f func(index int) RgbQuad) {for i := 0; i < len(v.Quad); i++ {v.Quad[i] = f(i)}
}func (v *BitmapFile) ReadLine(y int) ([]int, error) {height := v.Info.Heightif height < 0 {height = -height}if y < 0 || y >= int(height) {return nil, ofb}val := make([]int, v.Info.Width, v.Info.Height)err := binary.NewDecoder(v.BitLines[y], 0).Decode(val, true, int(v.Info.BitCount))return val, err
}func (v *BitmapFile) WriteLine(y int, val any) error {height := v.Info.Heightif height < 0 {height = -height}if y < 0 || y >= int(height) {return ofb}w := &lineWriter{buf: v.BitLines[y]}enc := binary.NewEncoder(w)defer enc.Write(nil)return enc.Encode(val, true, int(v.Info.BitCount))
}func (v *BitmapFile) Save(path string) error {f, err := os.Create(path)if err != nil {return err}defer f.Close()buf := bufio.NewWriter(f)defer buf.Flush()enc := binary.NewEncoder(buf)defer enc.Write(nil)return enc.Encode(v, false, 0)
}type BitmapFileHeader struct {Type MagicBmp `info:"文件标识"`Size uint32 `info:"文件大小"`Reserved1 uint16Reserved2 uint16OffBits uint32 `info:"点阵数据偏移"`
}type MagicBmp [2]bytefunc (v *MagicBmp) UnmarshalBinary(dec *binary.Decoder, isBig bool, bit int) error {err := dec.Decode(v[:], isBig, bit)if err != nil {return err}if string(v[:]) != "BM" {return errors.New("file isn't bitmap format")}return nil
}type BitmapInfoHeader BitmapInfo
type BitmapInfo struct {Size uint32 `info:"信息头大小"`Width int32 `info:"宽度(像素)"`Height int32 `info:"高度(像素)"`Planes uint16 `info:"颜色平面数"`BitCount uint16 `info:"比特/像素"`Compression uint32 `info:"压缩类型"`SizeImage uint32 `info:"点阵数据大小"`XPelsPerMeter int32 `info:"水平分辨率(像素/米)"`YPelsPerMeter int32 `info:"垂直分辨率(像素/米)"`ClrUsed uint32 `info:"调色板颜色数"`ClrImportant uint32 `info:"关键颜色数"`ClrMask ColorMask `info:"颜色掩码"`
}func (v *BitmapInfoHeader) UnmarshalBinary(dec *binary.Decoder, isBig bool, bit int) error {dec.SetArg(v)return dec.Decode((*BitmapInfo)(v), isBig, bit)
}func (v *BitmapInfoHeader) MarshalBinary(enc *binary.Encoder, isBig bool, bit int) error {enc.SetArg(v)return enc.Encode((*BitmapInfo)(v), isBig, bit)
}type ColorMask [3]uint32func (v *ColorMask) UnmarshalBinary(dec *binary.Decoder, isBig bool, bit int) error {bmp, ok := dec.Arg().(*BitmapInfoHeader)if !ok || bmp == nil {return errors.New("decode arg not set")}if bmp.Size == 40 {return nil} else if bmp.Size != 52 {return fmt.Errorf("unsupport BitmapInfoHeader size: %d", bmp.Size)}return dec.Decode(v[:], isBig, bit)
}func (v *ColorMask) MarshalBinary(enc *binary.Encoder, isBig bool, bit int) error {bmp, ok := enc.Arg().(*BitmapInfoHeader)if !ok || bmp == nil {return errors.New("encode arg not set")}if bmp.Size == 40 {return nil} else if bmp.Size != 52 {return fmt.Errorf("unsupport BitmapInfoHeader size: %d", bmp.Size)}return enc.Encode(v[:], isBig, bit)
}type BitmapQuad []RgbQuad
type RgbQuad struct {Blue byteGreen byteRed byteReserved byte
}func (v *BitmapQuad) UnmarshalBinary(dec *binary.Decoder, isBig bool, bit int) error {bmp, ok := dec.Arg().(*BitmapInfoHeader)if !ok || bmp == nil {return errors.New("decode arg not set")}if bmp.BitCount >= 16 {return nil}num := bmp.ClrUsedif num == 0 {num = 1 << bmp.BitCount}*v = make(BitmapQuad, num, num)return dec.Decode((*v)[:], isBig, bit)
}type BitmapLines []BitmapLine
type BitmapLine []bytefunc (v *BitmapLines) UnmarshalBinary(dec *binary.Decoder, isBig bool, bit int) error {bmp, ok := dec.Arg().(*BitmapInfoHeader)if !ok || bmp == nil {return errors.New("decode arg not set")}bytePerLine := int((int32(bmp.BitCount)*bmp.Width + 31) / 32 * 4)lineNum := int(bmp.Height)if lineNum < 0 {lineNum = -lineNum}*v = make([]BitmapLine, lineNum)for i := 0; i < lineNum; i++ {newDec := dec.SubDecoder(bytePerLine)err := newDec.Decode(&(*v)[i], isBig, bit)if err != nil {return err}err = dec.Seek(newDec.Pos())if err != nil {return err}}return nil
}type lineWriter struct {buf []byteoft int
}func (w *lineWriter) Write(p []byte) (n int, err error) {n = len(p)if w.oft+n > len(w.buf) {n = len(w.buf) - w.ofterr = ofb}copy(w.buf[w.oft:], p)w.oft += nreturn
}