1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157
|
// SPDX-FileCopyrightText: 2022 UnionTech Software Technology Co., Ltd.
//
// SPDX-License-Identifier: GPL-3.0-or-later
package cursor
import (
"bufio"
"encoding/binary"
"errors"
"io"
"os"
)
const (
typeImage = 0xfffd0002
)
type decoder struct {
f *os.File
br *bufio.Reader
version uint32
tocs []tocEntry
tmp [36]byte
}
func newDecoder(f *os.File) (*decoder, error) {
var d decoder
d.f = f
d.br = bufio.NewReader(f)
err := d.readHeader()
if err != nil {
return nil, err
}
return &d, nil
}
func (d *decoder) close() error {
return d.f.Close()
}
// table of contents entry
type tocEntry struct {
Type uint32 // entry type
Subtype uint32 //
Position uint32 // absolute byte position of table in file
}
func readFull(r io.Reader, b []byte) error {
_, err := io.ReadFull(r, b)
if err == io.EOF {
err = io.ErrUnexpectedEOF
}
return err
}
func (d *decoder) readHeader() error {
err := readFull(d.br, d.tmp[:16])
if err != nil {
return err
}
magic := string(d.tmp[:4])
if magic != "Xcur" {
return errors.New("magic not match")
}
d.version = binary.LittleEndian.Uint32(d.tmp[8:12])
nToc := binary.LittleEndian.Uint32(d.tmp[12:16])
d.tocs = make([]tocEntry, nToc)
for i := range d.tocs {
d.tocs[i], err = d.readTocEntry()
if err != nil {
return err
}
}
return nil
}
func (d *decoder) readTocEntry() (tocEntry, error) {
err := readFull(d.br, d.tmp[:12])
if err != nil {
return tocEntry{}, err
}
var e tocEntry
e.Type = binary.LittleEndian.Uint32(d.tmp[:4])
e.Subtype = binary.LittleEndian.Uint32(d.tmp[4:8])
e.Position = binary.LittleEndian.Uint32(d.tmp[8:12])
return e, nil
}
func (d *decoder) setPos(pos uint32) error {
_, err := d.f.Seek(int64(pos), io.SeekStart)
if err != nil {
return err
}
d.br.Reset(d.f)
return nil
}
func (d *decoder) readImage(toc tocEntry) (*Image, error) {
err := d.setPos(toc.Position)
if err != nil {
return nil, err
}
err = readFull(d.br, d.tmp[:36])
if err != nil {
return nil, err
}
// chuck header
headerLen := binary.LittleEndian.Uint32(d.tmp[:4])
if headerLen != 36 {
return nil, errors.New("image type chunk head len is not 36")
}
type0 := binary.LittleEndian.Uint32(d.tmp[4:8])
if type0 != typeImage {
return nil, errors.New("chunk type not match")
}
subtype := binary.LittleEndian.Uint32(d.tmp[8:12])
version := binary.LittleEndian.Uint32(d.tmp[12:16])
if version != 1 {
return nil, errors.New("version not supported")
}
var img Image
img.Size = subtype
img.Width = binary.LittleEndian.Uint32(d.tmp[16:20])
img.Height = binary.LittleEndian.Uint32(d.tmp[20:24])
img.XHot = binary.LittleEndian.Uint32(d.tmp[24:28])
if img.XHot > img.Width {
img.XHot = img.Width
}
img.YHot = binary.LittleEndian.Uint32(d.tmp[28:32])
if img.YHot > img.Height {
img.YHot = img.Height
}
img.Delay = binary.LittleEndian.Uint32(d.tmp[32:36])
// read pixels
img.Pixels = make([]byte, 4*int(img.Width)*int(img.Height))
err = readFull(d.br, img.Pixels)
if err != nil {
return nil, err
}
return &img, nil
}
|