用于读写BMP文件的golang版源码
源码基于源码分享-golang的二进制文件读写库 https://blog.csdn.net/zhyulo/article/details/128890546文章来源:https://www.toymoban.com/news/detail-814817.html
BMP文件格式可参考位图文件解析-位图(bmp)、图标(ico)与光标(cur) https://blog.csdn.net/zhyulo/article/details/85934728文章来源地址https://www.toymoban.com/news/detail-814817.html
import (
"binary"
"bufio"
"errors"
"fmt"
"os"
)
var ofb = errors.New("out of bound")
type BitmapFile struct {
Header BitmapFileHeader
Info BitmapInfoHeader
Quad BitmapQuad `info:"调色板"`
BitLines BitmapLines `info:"点阵数据"`
}
func ReadBitmapFile(path string) (*BitmapFile, error) {
buf, err := os.ReadFile(path)
if err != nil {
return nil, err
}
var bmp BitmapFile
err = 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 * 4
for 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 << bitCount
bmp.Quad = make(BitmapQuad, bmp.Info.ClrUsed, bmp.Info.ClrUsed)
}
bmp.Header.OffBits = 14 + bmp.Info.Size + uint32(len(bmp.Quad))*4
bmp.Header.Size = bmp.Header.OffBits + bmp.Info.SizeImage
return 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.Height
if 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.Height
if 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 uint16
Reserved2 uint16
OffBits uint32 `info:"点阵数据偏移"`
}
type MagicBmp [2]byte
func (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]uint32
func (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 byte
Green byte
Red byte
Reserved 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.ClrUsed
if num == 0 {
num = 1 << bmp.BitCount
}
*v = make(BitmapQuad, num, num)
return dec.Decode((*v)[:], isBig, bit)
}
type BitmapLines []BitmapLine
type BitmapLine []byte
func (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 []byte
oft 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.oft
err = ofb
}
copy(w.buf[w.oft:], p)
w.oft += n
return
}
到了这里,关于源码分享-golang的BMP文件读写库的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!