mirror of
https://gitee.com/energye/energy.git
synced 2024-12-04 20:58:21 +08:00
A: GIF Play component
This commit is contained in:
parent
78a9a11839
commit
08fc3d01ca
32
cef/gifplay/header.go
Normal file
32
cef/gifplay/header.go
Normal file
@ -0,0 +1,32 @@
|
||||
//----------------------------------------
|
||||
//
|
||||
// Copyright © yanghy. All Rights Reserved.
|
||||
//
|
||||
// Licensed under Apache License Version 2.0, January 2004
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
//----------------------------------------
|
||||
|
||||
package gifplay
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
)
|
||||
|
||||
type TGIFHeader struct {
|
||||
Signature [3]byte // 页眉签名(始终为“GIF”)
|
||||
Version [3]byte // GIF格式版本(“87a”或“89a”)
|
||||
ScreenWidth uint16 // 以像素为单位的显示屏宽度
|
||||
ScreenHeight uint16 // 以像素为单位的显示屏高度
|
||||
Packedbit byte // 屏幕和彩色地图信息
|
||||
BackgroundColor byte // 背景色索引
|
||||
AspectRatio byte // 像素纵横比
|
||||
}
|
||||
|
||||
func (m *TGIFHeader) BytesToConvert(data []byte) {
|
||||
var buf bytes.Buffer
|
||||
buf.Write(data)
|
||||
binary.Read(&buf, binary.LittleEndian, m)
|
||||
}
|
53
cef/gifplay/image.go
Normal file
53
cef/gifplay/image.go
Normal file
@ -0,0 +1,53 @@
|
||||
//----------------------------------------
|
||||
//
|
||||
// Copyright © yanghy. All Rights Reserved.
|
||||
//
|
||||
// Licensed under Apache License Version 2.0, January 2004
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
//----------------------------------------
|
||||
|
||||
package gifplay
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"github.com/energye/energy/v2/pkgs/ext"
|
||||
)
|
||||
|
||||
type TGIFImage struct {
|
||||
Bitmap *ext.TBitmap
|
||||
PosX uint16
|
||||
PosY uint16
|
||||
Delay uint16
|
||||
Method byte
|
||||
}
|
||||
|
||||
type TGIFImageDescriptor struct {
|
||||
Left, // 显示器上图像的X位置
|
||||
Top, // 显示器上图像的Y位置
|
||||
Width, // 图像的宽度(像素)
|
||||
Height uint16 // 图像的高度(像素)
|
||||
Packedbit byte // 图像和颜色表数据信息
|
||||
}
|
||||
|
||||
func (m *TGIFImageDescriptor) BytesToConvert(data []byte) {
|
||||
var buf bytes.Buffer
|
||||
buf.Write(data)
|
||||
binary.Read(&buf, binary.LittleEndian, m)
|
||||
}
|
||||
|
||||
type TGIFGraphicsControlExtension struct {
|
||||
BlockSize byte // 剩余字段的大小(始终为04h)
|
||||
Packedbit byte // 要使用的图形处理方法
|
||||
DelayTime uint16 // 等待数十秒
|
||||
ColorIndex byte // 透明颜色索引
|
||||
Terminator byte // 块终止符(始终为0)
|
||||
}
|
||||
|
||||
func (m *TGIFGraphicsControlExtension) BytesToConvert(data []byte) {
|
||||
var buf bytes.Buffer
|
||||
buf.Write(data)
|
||||
binary.Read(&buf, binary.LittleEndian, m)
|
||||
}
|
61
cef/gifplay/list.go
Normal file
61
cef/gifplay/list.go
Normal file
@ -0,0 +1,61 @@
|
||||
//----------------------------------------
|
||||
//
|
||||
// Copyright © yanghy. All Rights Reserved.
|
||||
//
|
||||
// Licensed under Apache License Version 2.0, January 2004
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
//----------------------------------------
|
||||
|
||||
package gifplay
|
||||
|
||||
type TGIFList struct {
|
||||
items []*TGIFImage
|
||||
count int32
|
||||
}
|
||||
|
||||
func (m *TGIFList) Add(AGifImage *TGIFImage) int32 {
|
||||
m.items = append(m.items, AGifImage)
|
||||
m.count++
|
||||
return m.count
|
||||
}
|
||||
|
||||
//func (m *TGIFList) Extract(Item *TGIFImage) *TGIFImage {
|
||||
//
|
||||
//}
|
||||
//
|
||||
//func (m *TGIFList) Remove(AGifImage *TGIFImage) int32 {
|
||||
//
|
||||
//}
|
||||
//
|
||||
//func (m *TGIFList) IndexOf(AGifImage *TGIFImage) int32 {
|
||||
//
|
||||
//}
|
||||
|
||||
func (m *TGIFList) First() *TGIFImage {
|
||||
return m.items[0]
|
||||
}
|
||||
|
||||
func (m *TGIFList) Last() *TGIFImage {
|
||||
return m.items[len(m.items)-1]
|
||||
}
|
||||
|
||||
func (m *TGIFList) Count() int32 {
|
||||
return m.count
|
||||
}
|
||||
|
||||
func (m *TGIFList) Insert(index int32, AGifImage *TGIFImage) {
|
||||
m.items[index] = AGifImage
|
||||
}
|
||||
|
||||
func (m *TGIFList) GetItem(index int32) *TGIFImage {
|
||||
if index < m.count {
|
||||
return m.items[index]
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *TGIFList) SetItem(index int32, AGifImage *TGIFImage) {
|
||||
m.items[index] = AGifImage
|
||||
}
|
398
cef/gifplay/loader.go
Normal file
398
cef/gifplay/loader.go
Normal file
@ -0,0 +1,398 @@
|
||||
//----------------------------------------
|
||||
//
|
||||
// Copyright © yanghy. All Rights Reserved.
|
||||
//
|
||||
// Licensed under Apache License Version 2.0, January 2004
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
//----------------------------------------
|
||||
|
||||
package gifplay
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"github.com/energye/energy/v2/pkgs/ext"
|
||||
"github.com/energye/golcl/lcl"
|
||||
"github.com/energye/golcl/lcl/types"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
type TGIFLoader struct {
|
||||
gifHeader *TGIFHeader
|
||||
gifDescriptor *TGIFImageDescriptor
|
||||
gifGraphicsCtrlExt *TGIFGraphicsControlExtension
|
||||
gifUseGraphCtrlExt bool
|
||||
gifBackgroundColor byte
|
||||
interlaced bool
|
||||
scanLine []byte
|
||||
lineSize int32
|
||||
disposalMethod byte
|
||||
empty bool
|
||||
filePath string
|
||||
gifStream *lcl.TMemoryStream
|
||||
height int32
|
||||
isTransparent bool
|
||||
width int32
|
||||
palette *TPalette
|
||||
localHeight int32
|
||||
localWidth int32
|
||||
}
|
||||
|
||||
func (m *TGIFLoader) LoadAllBitmap(gifBitmaps *TGIFList) bool {
|
||||
if m.filePath == "" && m.gifStream == nil {
|
||||
return false
|
||||
}
|
||||
var gifStream *lcl.TMemoryStream
|
||||
if m.gifStream != nil {
|
||||
gifStream = m.gifStream
|
||||
} else {
|
||||
gifStream = lcl.NewMemoryStream()
|
||||
gifStream.LoadFromFile(m.filePath)
|
||||
gifStream.SetPosition(0)
|
||||
}
|
||||
m.ReadHeader(gifStream)
|
||||
if m.gifHeader.Version[0] != '8' && m.gifHeader.Version[1] != '9' && m.gifHeader.Version[2] != 'a' {
|
||||
return false
|
||||
}
|
||||
var introducer byte
|
||||
// 跳过第一个块扩展(如果存在)
|
||||
for {
|
||||
introducer = m.SkipBlock(gifStream)
|
||||
if introducer == ID_IMAGE_DESCRIPTOR || introducer == ID_TRAILER {
|
||||
break
|
||||
}
|
||||
}
|
||||
for {
|
||||
m.ReadGifBitmap(gifStream)
|
||||
// 解码扫描线缓冲区中的Gif位图
|
||||
m.ReadScanLine(gifStream)
|
||||
// 为放置扫描线像素创建临时Fp图像
|
||||
intfImage := ext.NewLazIntfImage(m.localWidth, m.localHeight)
|
||||
intfImage.DataDescription(ext.Init_BPP32_B8G8R8A8_M1_BIO_TTB, m.localWidth, m.localHeight)
|
||||
m.WriteScanLine(intfImage)
|
||||
|
||||
gifBitmap := &TGIFImage{
|
||||
Bitmap: ext.NewBitmap(),
|
||||
PosX: m.gifDescriptor.Left,
|
||||
PosY: m.gifDescriptor.Top,
|
||||
Method: m.disposalMethod,
|
||||
Delay: m.gifGraphicsCtrlExt.DelayTime,
|
||||
}
|
||||
gifBitmap.Bitmap.LoadFromIntfImage(intfImage)
|
||||
gifBitmaps.Add(gifBitmap)
|
||||
intfImage.Free()
|
||||
m.scanLine = nil
|
||||
m.gifUseGraphCtrlExt = false
|
||||
for {
|
||||
introducer = m.SkipBlock(gifStream)
|
||||
if introducer == ID_IMAGE_DESCRIPTOR || introducer == ID_TRAILER {
|
||||
break
|
||||
}
|
||||
}
|
||||
if introducer == ID_TRAILER {
|
||||
break
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (m *TGIFLoader) ReadPalette(stream *lcl.TMemoryStream, size int) {
|
||||
m.palette.Clear()
|
||||
m.palette.count = 0
|
||||
var entry TRGB
|
||||
sizeOf := int32(unsafe.Sizeof(entry)) //3
|
||||
for i := 0; i < size; i++ {
|
||||
_, d := stream.Read(sizeOf)
|
||||
entry.BytesToConvert(d)
|
||||
r := uint16(entry.Red)
|
||||
g := uint16(entry.Green)
|
||||
b := uint16(entry.Blue)
|
||||
color := &ext.TColor{
|
||||
Red: r | (r << 8),
|
||||
Green: g | (g << 8),
|
||||
Blue: b | (b << 8),
|
||||
Alpha: AlphaOpaque,
|
||||
}
|
||||
m.palette.Add(color)
|
||||
}
|
||||
}
|
||||
|
||||
func (m *TGIFLoader) ReadScanLine(stream *lcl.TMemoryStream) {
|
||||
var (
|
||||
unpackedSize, packedSize int64
|
||||
data, bits, code uint32
|
||||
sourcePtr PByte
|
||||
inCode uint32
|
||||
codeSize uint32
|
||||
codeMask uint32
|
||||
freeCode uint32
|
||||
oldCode uint32
|
||||
prefix [CODE_TABLE_SIZE]uint32
|
||||
suffix, stack = make([]byte, CODE_TABLE_SIZE), make([]byte, CODE_TABLE_SIZE)
|
||||
stackPointer PByte
|
||||
b, initialCodeSize, firstChar byte
|
||||
clearCode, eoiCode uint16
|
||||
)
|
||||
// 解压缩字典的初始化
|
||||
_, d := stream.Read(1)
|
||||
initialCodeSize = d[0]
|
||||
// 压缩尾部
|
||||
OldPos := stream.Position()
|
||||
packedSize = 0
|
||||
for {
|
||||
_, d = stream.Read(1)
|
||||
b = d[0]
|
||||
if b > 0 {
|
||||
packedSize += int64(b)
|
||||
stream.Seek(int64(b), types.SoCurrent)
|
||||
}
|
||||
if b == 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
stream.SetPosition(OldPos)
|
||||
var sourceBuf bytes.Buffer
|
||||
for {
|
||||
_, d = stream.Read(1)
|
||||
b = d[0]
|
||||
if b > 0 {
|
||||
_, d = stream.Read(int32(b))
|
||||
sourceBuf.Write(d)
|
||||
}
|
||||
if b == 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
sourceData := sourceBuf.Bytes()
|
||||
sourceBuf.Reset()
|
||||
sourcePtr = PByte(unsafe.Pointer(&sourceData[0]))
|
||||
target := uintptr(unsafe.Pointer(&m.scanLine[0]))
|
||||
codeSize = uint32(initialCodeSize + 1)
|
||||
clearCode = 1 << initialCodeSize
|
||||
eoiCode = clearCode + 1
|
||||
freeCode = uint32(clearCode + 2)
|
||||
oldCode = CODE_TABLE_SIZE
|
||||
codeMask = (1 << codeSize) - 1
|
||||
unpackedSize = int64(m.localWidth * m.localHeight)
|
||||
for I := 0; I < int(clearCode); I++ {
|
||||
prefix[I] = CODE_TABLE_SIZE
|
||||
suffix[I] = byte(I)
|
||||
}
|
||||
stackPointer = uintptr(unsafe.Pointer(&stack[0]))
|
||||
firstChar = 0
|
||||
data = 0
|
||||
bits = 0
|
||||
//解压缩LZW gif
|
||||
for unpackedSize > 0 && packedSize > 0 {
|
||||
source := uint32(*(*byte)(unsafe.Pointer(sourcePtr)))
|
||||
data += source << bits
|
||||
bits += 8
|
||||
for bits >= codeSize {
|
||||
code = data & codeMask
|
||||
data >>= codeSize
|
||||
bits -= codeSize
|
||||
if code == uint32(eoiCode) {
|
||||
break
|
||||
}
|
||||
if code == uint32(clearCode) {
|
||||
codeSize = uint32(initialCodeSize + 1)
|
||||
codeMask = 1<<codeSize - 1
|
||||
freeCode = uint32(clearCode + 2)
|
||||
oldCode = CODE_TABLE_SIZE
|
||||
continue
|
||||
}
|
||||
if code > freeCode {
|
||||
break
|
||||
}
|
||||
if oldCode == CODE_TABLE_SIZE {
|
||||
firstChar = suffix[code]
|
||||
*(*byte)(unsafe.Pointer(target)) = firstChar
|
||||
target++
|
||||
unpackedSize--
|
||||
oldCode = code
|
||||
continue
|
||||
}
|
||||
inCode = code
|
||||
if code == freeCode {
|
||||
*(*byte)(unsafe.Pointer(stackPointer)) = firstChar
|
||||
stackPointer++
|
||||
code = oldCode
|
||||
}
|
||||
for code > uint32(clearCode) {
|
||||
*(*byte)(unsafe.Pointer(stackPointer)) = suffix[code]
|
||||
stackPointer++
|
||||
code = prefix[code]
|
||||
}
|
||||
firstChar = suffix[code]
|
||||
*(*byte)(unsafe.Pointer(stackPointer)) = firstChar
|
||||
stackPointer++
|
||||
prefix[freeCode] = oldCode
|
||||
suffix[freeCode] = firstChar
|
||||
if freeCode == codeMask && codeSize < 12 {
|
||||
codeSize++
|
||||
codeMask = (1 << codeSize) - 1
|
||||
}
|
||||
if freeCode < CODE_TABLE_SIZE-1 {
|
||||
freeCode++
|
||||
}
|
||||
oldCode = inCode
|
||||
for {
|
||||
stackPointer--
|
||||
*(*byte)(unsafe.Pointer(target)) = *(*byte)(unsafe.Pointer(stackPointer))
|
||||
target++
|
||||
unpackedSize--
|
||||
if stackPointer == uintptr(unsafe.Pointer(&stack[0])) {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
sourcePtr++
|
||||
packedSize--
|
||||
}
|
||||
}
|
||||
func (m *TGIFLoader) ReadHeader(stream *lcl.TMemoryStream) {
|
||||
var gifHeader TGIFHeader
|
||||
sizeOf := int32(unsafe.Sizeof(gifHeader)) - 1 //13
|
||||
_, d := stream.Read(sizeOf)
|
||||
gifHeader.BytesToConvert(d)
|
||||
m.gifHeader = &gifHeader
|
||||
m.gifBackgroundColor = gifHeader.BackgroundColor
|
||||
m.width = int32(gifHeader.ScreenWidth)
|
||||
m.height = int32(gifHeader.ScreenHeight)
|
||||
m.localWidth = int32(gifHeader.ScreenWidth)
|
||||
m.localHeight = int32(gifHeader.ScreenHeight)
|
||||
m.isTransparent = false
|
||||
m.ReadGlobalPalette(stream)
|
||||
}
|
||||
|
||||
func (m *TGIFLoader) ReadGlobalPalette(stream *lcl.TMemoryStream) {
|
||||
if m.gifHeader.Packedbit&ID_COLOR_TABLE != 0 {
|
||||
colorTableSize := m.gifHeader.Packedbit&ID_COLOR_TABLE_SIZE + 1
|
||||
m.ReadPalette(stream, 1<<colorTableSize)
|
||||
}
|
||||
}
|
||||
|
||||
func (m *TGIFLoader) ReadGraphCtrlExt() {
|
||||
m.isTransparent = (m.gifGraphicsCtrlExt.Packedbit & ID_TRANSPARENT) != 0
|
||||
m.disposalMethod = (m.gifGraphicsCtrlExt.Packedbit & 0x1C) >> 2
|
||||
if m.isTransparent {
|
||||
// 如果透明位图更改alpha通道
|
||||
m.gifBackgroundColor = m.gifGraphicsCtrlExt.ColorIndex
|
||||
color := m.palette.Get(int(m.gifBackgroundColor))
|
||||
color.Alpha = AlphaTransparent
|
||||
m.palette.Set(int(m.gifBackgroundColor), color)
|
||||
}
|
||||
}
|
||||
|
||||
func (m *TGIFLoader) SkipBlock(stream *lcl.TMemoryStream) byte {
|
||||
var introducer, labels, skipByte byte
|
||||
_, d := stream.Read(1)
|
||||
introducer = d[0]
|
||||
if introducer == EXT_INTRODUCER {
|
||||
_, d = stream.Read(1)
|
||||
labels = d[0]
|
||||
switch labels {
|
||||
case EXT_COMMENT, EXT_APPLICATION:
|
||||
for {
|
||||
_, d = stream.Read(1)
|
||||
skipByte = d[0]
|
||||
if skipByte == 0 {
|
||||
break
|
||||
}
|
||||
stream.Seek(int64(skipByte), types.SoCurrent)
|
||||
}
|
||||
case EXT_GRAPHICS_CONTROL:
|
||||
var gifGraphicsCtrlExt TGIFGraphicsControlExtension
|
||||
_, d = stream.Read(int32(unsafe.Sizeof(gifGraphicsCtrlExt))) //6
|
||||
gifGraphicsCtrlExt.BytesToConvert(d)
|
||||
m.gifGraphicsCtrlExt = &gifGraphicsCtrlExt
|
||||
m.gifUseGraphCtrlExt = true
|
||||
case EXT_PLAIN_TEXT:
|
||||
_, d = stream.Read(1)
|
||||
skipByte = d[0]
|
||||
stream.Seek(int64(skipByte), types.SoCurrent)
|
||||
for {
|
||||
_, d = stream.Read(1)
|
||||
skipByte = d[0]
|
||||
if skipByte == 0 {
|
||||
break
|
||||
}
|
||||
stream.Seek(int64(skipByte), types.SoCurrent)
|
||||
}
|
||||
}
|
||||
}
|
||||
return introducer
|
||||
}
|
||||
|
||||
func (m *TGIFLoader) WriteScanLine(intfImage *ext.TLazIntfImage) {
|
||||
var (
|
||||
row, col int
|
||||
pass, every byte
|
||||
scanLineIdx int
|
||||
)
|
||||
if m.interlaced {
|
||||
for pass = 1; pass <= 4; pass++ {
|
||||
switch pass {
|
||||
case 1:
|
||||
row = 0
|
||||
every = 8
|
||||
case 2:
|
||||
row = 4
|
||||
every = 8
|
||||
case 3:
|
||||
row = 2
|
||||
every = 4
|
||||
case 4:
|
||||
row = 1
|
||||
every = 2
|
||||
}
|
||||
for {
|
||||
for col = 0; col < int(m.localWidth); col++ {
|
||||
color := *m.palette.Get(int(m.scanLine[scanLineIdx]))
|
||||
intfImage.Colors(int32(col), int32(row), color)
|
||||
scanLineIdx++
|
||||
}
|
||||
row += int(every)
|
||||
if row >= int(m.localHeight) {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for row = 0; row < int(m.localHeight); row++ {
|
||||
for col = 0; col < int(m.localWidth); col++ {
|
||||
color := *m.palette.Get(int(m.scanLine[scanLineIdx]))
|
||||
intfImage.Colors(int32(col), int32(row), color)
|
||||
scanLineIdx++
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (m *TGIFLoader) ReadGifBitmap(stream *lcl.TMemoryStream) {
|
||||
var gifDescriptor TGIFImageDescriptor
|
||||
sizeOf := int32(unsafe.Sizeof(gifDescriptor)) - 1 //9
|
||||
_, d := stream.Read(sizeOf)
|
||||
gifDescriptor.BytesToConvert(d)
|
||||
m.gifDescriptor = &gifDescriptor
|
||||
m.localWidth = int32(m.gifDescriptor.Width)
|
||||
m.localHeight = int32(m.gifDescriptor.Height)
|
||||
m.interlaced = m.gifDescriptor.Packedbit&ID_INTERLACED == ID_INTERLACED
|
||||
|
||||
m.lineSize = m.localWidth * (m.localHeight + 1)
|
||||
m.scanLine = make([]byte, m.lineSize)
|
||||
|
||||
if m.gifDescriptor.Packedbit&ID_COLOR_TABLE != 0 {
|
||||
colorTableSize := m.gifDescriptor.Packedbit&ID_COLOR_TABLE_SIZE + 1
|
||||
m.ReadPalette(stream, 1<<colorTableSize)
|
||||
}
|
||||
|
||||
if m.gifUseGraphCtrlExt {
|
||||
m.ReadGraphCtrlExt()
|
||||
}
|
||||
}
|
||||
|
||||
func (m *TGIFLoader) Free() {
|
||||
m.palette.Free()
|
||||
}
|
44
cef/gifplay/palette.go
Normal file
44
cef/gifplay/palette.go
Normal file
@ -0,0 +1,44 @@
|
||||
//----------------------------------------
|
||||
//
|
||||
// Copyright © yanghy. All Rights Reserved.
|
||||
//
|
||||
// Licensed under Apache License Version 2.0, January 2004
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
//----------------------------------------
|
||||
|
||||
package gifplay
|
||||
|
||||
import "github.com/energye/energy/v2/pkgs/ext"
|
||||
|
||||
type TPalette struct {
|
||||
colors []*ext.TColor
|
||||
count int
|
||||
}
|
||||
|
||||
func (m *TPalette) Clear() {
|
||||
m.colors = make([]*ext.TColor, 0)
|
||||
}
|
||||
|
||||
func (m *TPalette) Free() {
|
||||
m.colors = nil
|
||||
m.count = 0
|
||||
}
|
||||
|
||||
func (m *TPalette) Add(color *ext.TColor) {
|
||||
m.colors = append(m.colors, color)
|
||||
m.count++
|
||||
}
|
||||
|
||||
func (m *TPalette) Get(index int) *ext.TColor {
|
||||
return m.colors[index]
|
||||
}
|
||||
|
||||
func (m *TPalette) Set(index int, color *ext.TColor) {
|
||||
m.colors[index] = color
|
||||
}
|
||||
|
||||
func (m *TPalette) Count() int {
|
||||
return m.count
|
||||
}
|
237
cef/gifplay/play.go
Normal file
237
cef/gifplay/play.go
Normal file
@ -0,0 +1,237 @@
|
||||
//----------------------------------------
|
||||
//
|
||||
// Copyright © yanghy. All Rights Reserved.
|
||||
//
|
||||
// Licensed under Apache License Version 2.0, January 2004
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
//----------------------------------------
|
||||
|
||||
// GIF Play component
|
||||
|
||||
package gifplay
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"github.com/energye/energy/v2/pkgs/ext"
|
||||
"github.com/energye/golcl/lcl"
|
||||
"github.com/energye/golcl/lcl/types"
|
||||
)
|
||||
|
||||
const (
|
||||
EXT_INTRODUCER byte = 0x21
|
||||
EXT_GRAPHICS_CONTROL byte = 0xF9
|
||||
EXT_PLAIN_TEXT byte = 0x01
|
||||
EXT_APPLICATION byte = 0xFF
|
||||
EXT_COMMENT byte = 0xFE
|
||||
DSC_LOCAL_IMAGE byte = 0x2C
|
||||
ID_TRANSPARENT byte = 0x01
|
||||
ID_COLOR_TABLE_SIZE byte = 0x07
|
||||
ID_SORT byte = 0x20
|
||||
ID_INTERLACED byte = 0x40
|
||||
ID_COLOR_TABLE byte = 0x80
|
||||
ID_IMAGE_DESCRIPTOR byte = 0x2C
|
||||
ID_TRAILER byte = 0x3B
|
||||
CODE_TABLE_SIZE = 4096
|
||||
)
|
||||
|
||||
const (
|
||||
AlphaTransparent uint16 = 0x0000
|
||||
AlphaOpaque uint16 = 0xFFFF
|
||||
)
|
||||
|
||||
const (
|
||||
SoFromBeginning = iota
|
||||
SoFromCurrent
|
||||
SoFromEnd
|
||||
)
|
||||
|
||||
type PByte = uintptr
|
||||
|
||||
type TRGB struct {
|
||||
Red, Green, Blue byte
|
||||
}
|
||||
|
||||
//type buffer struct {
|
||||
// buf []byte
|
||||
//}
|
||||
//
|
||||
//func (b *buffer) Read(p []byte) (n int, err error) {
|
||||
// if b.buf == nil {
|
||||
// if len(p) == 0 {
|
||||
// return 0, nil
|
||||
// }
|
||||
// return 0, io.EOF
|
||||
// }
|
||||
// n = copy(p, b.buf)
|
||||
// return n, nil
|
||||
//}
|
||||
|
||||
func (m *TRGB) BytesToConvert(data []byte) {
|
||||
var buf bytes.Buffer
|
||||
buf.Write(data)
|
||||
binary.Read(&buf, binary.LittleEndian, m)
|
||||
}
|
||||
|
||||
type TGIFPlay struct {
|
||||
*lcl.TPanel
|
||||
animate bool
|
||||
empty bool
|
||||
filePath string
|
||||
gifBitmaps *TGIFList
|
||||
onFrameChanged lcl.TNotifyEvent
|
||||
onStart lcl.TNotifyEvent
|
||||
onStop lcl.TNotifyEvent
|
||||
wait *lcl.TTimer
|
||||
currentImage int32
|
||||
gifHeightidth int32
|
||||
gifWidth int32
|
||||
bufferImg *ext.TBitmap
|
||||
currentView *ext.TBitmap
|
||||
}
|
||||
|
||||
func NewGIFPlay(owner lcl.IComponent) (gifPlay *TGIFPlay) {
|
||||
control := lcl.NewPanel(owner)
|
||||
control.SetAutoSize(true)
|
||||
control.SetBounds(0, 0, 90, 90)
|
||||
gifPlay = &TGIFPlay{
|
||||
TPanel: control,
|
||||
}
|
||||
gifPlay.empty = true
|
||||
gifPlay.currentImage = 0
|
||||
gifPlay.currentView = ext.NewBitmap()
|
||||
gifPlay.bufferImg = ext.NewBitmap()
|
||||
gifPlay.wait = lcl.NewTimer(owner)
|
||||
gifPlay.wait.SetInterval(100)
|
||||
gifPlay.wait.SetEnabled(false)
|
||||
gifPlay.wait.SetOnTimer(gifPlay.OnTime)
|
||||
gifPlay.SetAnimate(true)
|
||||
gifPlay.SetOnPaint(gifPlay.OnPaint)
|
||||
return
|
||||
}
|
||||
|
||||
func (m *TGIFPlay) OnPaint(sender lcl.IObject) {
|
||||
if !m.empty && m.IsValid() {
|
||||
if m.currentImage < m.gifBitmaps.count {
|
||||
m.bufferImg.Canvas().Brush().SetColor(m.Color())
|
||||
currentImage := m.gifBitmaps.GetItem(m.currentImage)
|
||||
if m.currentImage == 0 {
|
||||
m.bufferImg.Canvas().FillRect(types.Rect(0, 0, m.Width(), m.Height()))
|
||||
}
|
||||
if currentImage.Delay != 0 {
|
||||
m.wait.SetInterval(uint32(currentImage.Delay * 10))
|
||||
}
|
||||
m.bufferImg.Canvas().Draw(int32(currentImage.PosX), int32(currentImage.PosY), currentImage.Bitmap)
|
||||
m.currentView.Assign(m.bufferImg)
|
||||
switch currentImage.Method {
|
||||
//case 0: 未指定...
|
||||
//case 1: 无更改背景
|
||||
case 2:
|
||||
m.bufferImg.Canvas().FillRect(types.Rect(int32(currentImage.PosX), int32(currentImage.PosY),
|
||||
currentImage.Bitmap.Width()+int32(currentImage.PosX), currentImage.Bitmap.Height()+int32(currentImage.PosY)))
|
||||
case 3:
|
||||
m.bufferImg.Canvas().FillRect(types.Rect(0, 0, m.Width(), m.Height()))
|
||||
}
|
||||
}
|
||||
m.Canvas().Draw(0, 0, m.currentView)
|
||||
}
|
||||
}
|
||||
|
||||
func (m *TGIFPlay) OnTime(sender lcl.IObject) {
|
||||
if !m.empty && m.IsValid() {
|
||||
if m.currentImage >= m.gifBitmaps.count {
|
||||
m.currentImage = 0
|
||||
} else {
|
||||
m.currentImage++
|
||||
}
|
||||
m.Repaint()
|
||||
}
|
||||
}
|
||||
|
||||
func (m *TGIFPlay) GifChanged() {
|
||||
canvas := m.bufferImg.Canvas()
|
||||
canvas.Brush().SetColor(m.Color())
|
||||
canvas.FillRect(types.Rect(0, 0, m.Width(), m.Height()))
|
||||
currentImage := m.gifBitmaps.GetItem(m.currentImage)
|
||||
canvas.Draw(int32(currentImage.PosX), int32(currentImage.PosY), currentImage.Bitmap)
|
||||
m.currentView.Assign(m.bufferImg)
|
||||
m.Invalidate()
|
||||
}
|
||||
|
||||
func (m *TGIFPlay) ResetImage() {
|
||||
if m.gifBitmaps != nil {
|
||||
m.gifBitmaps.items = make([]*TGIFImage, 0)
|
||||
m.gifBitmaps.count = 0
|
||||
}
|
||||
m.currentImage = 0
|
||||
canvas := m.currentView.Canvas()
|
||||
canvas.Brush().SetColor(m.Color())
|
||||
canvas.FillRect(types.TRect{Left: 0, Top: 0, Right: m.Width(), Bottom: m.Height()})
|
||||
}
|
||||
|
||||
func (m *TGIFPlay) LoadFromFile(filePath string) {
|
||||
if m.filePath == filePath {
|
||||
return
|
||||
}
|
||||
m.filePath = filePath
|
||||
m.ResetImage()
|
||||
if m.filePath == "" {
|
||||
return
|
||||
}
|
||||
if !m.empty {
|
||||
m.GifChanged()
|
||||
}
|
||||
m.empty = true
|
||||
gifLoader := &TGIFLoader{
|
||||
filePath: filePath,
|
||||
gifStream: nil,
|
||||
gifUseGraphCtrlExt: false,
|
||||
width: 20,
|
||||
height: 20,
|
||||
palette: &TPalette{},
|
||||
}
|
||||
if m.currentView == nil {
|
||||
m.currentView = ext.NewBitmap()
|
||||
}
|
||||
if m.gifBitmaps == nil {
|
||||
m.gifBitmaps = &TGIFList{}
|
||||
}
|
||||
m.empty = !gifLoader.LoadAllBitmap(m.gifBitmaps)
|
||||
m.DefineSize(gifLoader.width, gifLoader.height)
|
||||
gifLoader.Free()
|
||||
}
|
||||
|
||||
func (m *TGIFPlay) DefineSize(width, height int32) {
|
||||
if width == m.gifWidth && height == m.gifHeightidth {
|
||||
return
|
||||
}
|
||||
m.gifWidth = width
|
||||
m.gifHeightidth = height
|
||||
m.SetWidth(m.gifWidth)
|
||||
m.SetHeight(m.gifHeightidth)
|
||||
m.bufferImg.SetWidth(m.gifWidth)
|
||||
m.bufferImg.SetHeight(m.gifHeightidth)
|
||||
}
|
||||
|
||||
func (m *TGIFPlay) SetAnimate(v bool) {
|
||||
if m.animate == v {
|
||||
return
|
||||
}
|
||||
m.animate = v
|
||||
m.wait.SetEnabled(v)
|
||||
if m.animate {
|
||||
m.doStart()
|
||||
} else {
|
||||
m.doStop()
|
||||
}
|
||||
}
|
||||
|
||||
func (m *TGIFPlay) doStart() {
|
||||
|
||||
}
|
||||
|
||||
func (m *TGIFPlay) doStop() {
|
||||
|
||||
}
|
76
pkgs/ext/lazintfimage.go
Normal file
76
pkgs/ext/lazintfimage.go
Normal file
@ -0,0 +1,76 @@
|
||||
//----------------------------------------
|
||||
//
|
||||
// Copyright © yanghy. All Rights Reserved.
|
||||
//
|
||||
// Licensed under Apache License Version 2.0, January 2004
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
//----------------------------------------
|
||||
|
||||
package ext
|
||||
|
||||
import (
|
||||
"github.com/energye/energy/v2/common/imports"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
type DataDescription = int32
|
||||
|
||||
const (
|
||||
Init_BPP32_B8G8R8_BIO_TTB DataDescription = iota
|
||||
Init_BPP32_B8G8R8_M1_BIO_TTB
|
||||
Init_BPP32_B8G8R8A8_BIO_TTB
|
||||
Init_BPP32_B8G8R8A8_M1_BIO_TTB
|
||||
)
|
||||
|
||||
type TLazIntfImage struct {
|
||||
instance unsafe.Pointer
|
||||
}
|
||||
|
||||
type TColor struct {
|
||||
Red, Green, Blue, Alpha uint16
|
||||
}
|
||||
|
||||
func NewLazIntfImage(width, height int32) *TLazIntfImage {
|
||||
m := &TLazIntfImage{}
|
||||
r1, _, _ := imports.LibLCLExt().Proc(LazIntfImage_Create).Call(uintptr(width), uintptr(height))
|
||||
m.instance = unsafe.Pointer(r1)
|
||||
return m
|
||||
}
|
||||
|
||||
// Instance
|
||||
//
|
||||
// 返回对象实例指针。
|
||||
func (m *TLazIntfImage) Instance() uintptr {
|
||||
return uintptr(m.instance)
|
||||
}
|
||||
|
||||
// IsValid
|
||||
//
|
||||
// 检测地址是否为空。
|
||||
func (m *TLazIntfImage) IsValid() bool {
|
||||
return m.instance != nil
|
||||
}
|
||||
|
||||
func (m *TLazIntfImage) DataDescription(dataDescription DataDescription, width, height int32) {
|
||||
if !m.IsValid() {
|
||||
return
|
||||
}
|
||||
imports.LibLCLExt().Proc(LazIntfImage_DataDescription).Call(m.Instance(), uintptr(dataDescription), uintptr(width), uintptr(height))
|
||||
}
|
||||
|
||||
func (m *TLazIntfImage) Colors(x, y int32, color TColor) {
|
||||
if !m.IsValid() {
|
||||
return
|
||||
}
|
||||
imports.LibLCLExt().Proc(LazIntfImage_Colors).Call(m.Instance(), uintptr(x), uintptr(y), uintptr(unsafe.Pointer(&color)))
|
||||
}
|
||||
|
||||
func (m *TLazIntfImage) Free() {
|
||||
if !m.IsValid() {
|
||||
return
|
||||
}
|
||||
imports.LibLCLExt().Proc(LazIntfImage_Free).Call(m.Instance())
|
||||
m.instance = nil
|
||||
}
|
34
pkgs/ext/lcl_bitmap.go
Normal file
34
pkgs/ext/lcl_bitmap.go
Normal file
@ -0,0 +1,34 @@
|
||||
//----------------------------------------
|
||||
//
|
||||
// Copyright © yanghy. All Rights Reserved.
|
||||
//
|
||||
// Licensed under Apache License Version 2.0, January 2004
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
//----------------------------------------
|
||||
|
||||
package ext
|
||||
|
||||
import (
|
||||
"github.com/energye/energy/v2/common/imports"
|
||||
"github.com/energye/golcl/lcl"
|
||||
)
|
||||
|
||||
type TBitmap struct {
|
||||
*lcl.TBitmap
|
||||
}
|
||||
|
||||
func NewBitmap() *TBitmap {
|
||||
m := &TBitmap{
|
||||
TBitmap: lcl.NewBitmap(),
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
||||
func (m *TBitmap) LoadFromIntfImage(intfImage *TLazIntfImage) {
|
||||
if !m.IsValid() || !intfImage.IsValid() {
|
||||
return
|
||||
}
|
||||
imports.LibLCLExt().Proc(Bitmap_LoadFromIntfImage).Call(m.Instance(), intfImage.Instance())
|
||||
}
|
@ -45,6 +45,13 @@ func init() {
|
||||
dllimports.NewEnergyImport("Ext_Form_Activate", 0),
|
||||
dllimports.NewEnergyImport("Ext_Form_Deactivate", 0),
|
||||
dllimports.NewEnergyImport("Ext_Assigned", 0),
|
||||
// TBitmap
|
||||
dllimports.NewEnergyImport("Bitmap_LoadFromIntfImage", 0),
|
||||
// TLazIntfImage
|
||||
dllimports.NewEnergyImport("LazIntfImage_Create", 0),
|
||||
dllimports.NewEnergyImport("LazIntfImage_DataDescription", 0),
|
||||
dllimports.NewEnergyImport("LazIntfImage_Colors", 0),
|
||||
dllimports.NewEnergyImport("LazIntfImage_Free", 0),
|
||||
}
|
||||
imports.LibLCLExt().SetImportTable(lclExtImportDefs)
|
||||
}
|
||||
@ -56,6 +63,13 @@ const (
|
||||
Ext_Form_Activate
|
||||
Ext_Form_Deactivate
|
||||
Ext_Assigned
|
||||
// TBitmap
|
||||
Bitmap_LoadFromIntfImage
|
||||
// TLazIntfImage
|
||||
LazIntfImage_Create
|
||||
LazIntfImage_DataDescription
|
||||
LazIntfImage_Colors
|
||||
LazIntfImage_Free
|
||||
)
|
||||
|
||||
func PanelBevelColor(panel *lcl.TPanel) types.TColor {
|
||||
|
Loading…
Reference in New Issue
Block a user