源码分享-golang的BMP文件读写库

这篇具有很好参考价值的文章主要介绍了源码分享-golang的BMP文件读写库。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

用于读写BMP文件的golang版源码

源码基于源码分享-golang的二进制文件读写库 https://blog.csdn.net/zhyulo/article/details/128890546

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模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 组件分享之后端组件——基于Golang实现的全局的、版本化的、点对点的文件系统go-ipfs...

    近期正在探索前端、后端、系统端各类常用组件与工具,对其一些常见的组件进行再次整理一下,形成标准化组件专题,后续该专题将包含各类语言中的一些常用组件。欢迎大家进行持续关注。 组件: go-ipfs 开源协议:The go-ipfs project is dual-licensed under Apache 2.0 and MIT terms: Ap

    2024年02月01日
    浏览(28)
  • Golang中互斥锁和读写互斥锁

    目录 互斥锁 使用互斥锁的示例代码 读写互斥锁 读写互斥锁的示例代码         在Golang中,互斥锁(Mutex)是一种基本的同步原语,用于实现对共享资源的互斥访问。互斥锁通过在代码中标记临界区来控制对共享资源的访问,从而保证同一时间只有一个 goroutine 可以访问共

    2024年02月06日
    浏览(24)
  • 【Golang】Golang进阶系列教程--Golang中文件目录操作的实现

    Golang中,文件是指计算机中存储数据的实体,文件可以是文本文件、二进制文件、配置文件等。在Go语言中,通过操作文件,我们可以读取文件的内容,写入数据到文件,以及获取文件的属性等。 Golang中的文件可以分为两种类型:文本文件和二进制文件。文本文件是指只包含

    2024年02月15日
    浏览(26)
  • 从源码角度看 Golang 的调度

    G:代表一个 goroutine,每个 goroutine 都有自己独立的栈存放当前的运行内存及状态。可以把一个 G 当做一个任务。 M: 代表内核线程(Pthread),它本身就与一个内核线程进行绑定,goroutine 运行在 M 上。 P:代表一个处理器,可以认为一个“有运行任务”的 P 占了一个 CPU 线程的资源

    2024年02月13日
    浏览(30)
  • golang http transport源码分析

    Golang http库在日常开发中使用会很多。这里通过一个demo例子出发,从源码角度梳理golang http库底层的数据结构以及大致的调用流程 先看看http.Client结构体,如下: Transport:http client实际发送请求结构体 Timeout:http client请求超时设置(后续在详细分析) 在看Transport结构体(net/http

    2024年02月11日
    浏览(24)
  • 【golang】http.ListenAndServe源码解析

    ListenAndServe监听TCP地址addr,并且会使用handler参数调用Serve函数处理接收到的连接。handler参数一般会设为nil,此时会使用DefaultServeMux。 接下来我们看一下这个函数的主要源码流程。 Server 定义运行HTTP服务器的参数。Server的零值是一个有效的配置。 第一层相当于封装了一下创建

    2024年04月08日
    浏览(45)
  • 【分享】golang windows 运行报错 undefined: syscall.SIGUSR1

    在跟着煎鱼大佬学习 Golang-gin的时候,\\\"在优雅的重启服务篇\\\" ,为了gin服务的热更新,采用了 endless+fresh的方案,安装endless后无法在windows本地调试,然后报错。  (优雅的重启服务-地鼠文档 优雅的重启服务-我不怎么喜欢左写写,右写写,因此总是在不知不觉中写了不少的系列教

    2024年02月09日
    浏览(24)
  • 【GoLang】【工具分享】1、根据 JsonTag 对 slice 进行稳定排序(不支持嵌套)

    后端返回 List 列表中的 Json 序列化结构到前端,其中包含许多字段,业务需求可以按照某个字段对列表数据做 升/降序排序。 前端将后端返回的 Json 及排序方向作为参数传递至后端即可,后端根据 Json 排序找到对应的 JsonTag 后,采用反射找到字段,再写自定义排

    2024年02月12日
    浏览(31)
  • Golang-语言源码级调试器 Delve

    Go 目前的调试器有如下几种: GDB 最早期的调试工具,现在用的很少。 LLDB macOS 系统推荐的标准调试工具,单 Go 的一些专有特性支持的比较少。 Delve 专门为 Go 语言打造的调试工具,使用最为广泛。 本篇简单说明如何使用 Delve 工具来调试 Go 程序,使用的 delve 版本为 1.20.1。

    2024年02月13日
    浏览(29)
  • 从源码角度剖析 golang 如何fork一个进程

    创建一个新进程分为两个步骤,一个是fork系统调用,一个是execve 系统调用,fork调用会复用父进程的堆栈,而execve直接覆盖当前进程的堆栈,并且将下一条执行指令指向新的可执行文件。 在分析源码之前,我们先来看看golang fork一个子进程该如何写。(👉严格的讲是先fork再

    2024年02月07日
    浏览(33)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包