ImageReader subclass:#FLIReader
instanceVariableNames:'frames nframes frameBuffer frameBufferSize imageBuffer redPalette
greenPalette bluePalette flags frameDelay'
classVariableNames:'FLI_FILE_MAGIC FLC_FILE_MAGIC FLI_FRAME_MAGIC FILE_HEAD_SIZE
FRAME_HEAD_SIZE CHUNK_HEAD_SIZE FLI_256_COLOR FLI_DELTA FLI_COLOR
FLI_LC FLI_BLACK FLI_BRUN FLI_COPY FLI_MINI MAXCOLORS'
poolDictionaries:''
category:'Graphics-Images-Support'
!
!FLIReader class methodsFor:'documentation'!
documentation
"
Read frames from a FLI/FLC file.
this is a very first attempt in reading FLI files;
its very experimental and may change.
(will introduce a new class hierarchy based upon
MovieReader ...).
When used like an imageReader, #fromFile: will return
the very first frame.
Warning: right now, the complete movie is read and huge
amounts of memoru are allocated. The interface will be changed
to allow stream operation ...
[author:]
Claus Gittinger
[see also:]
ImageReader
"
!
examples
"
|reader film view tNext|
reader := FLIReader readFile:'/usr/local/FLI/jeffmild.fli'.
reader isNil ifTrue:[^ nil].
film := reader images.
view := StandardSystemView extent:(film first extent).
view openAndWait.
tNext := Time millisecondClockValue + (reader frameDelay).
film do:[:frame |
frame displayOn:view x:0 y:0.
(Delay untilMilliseconds:tNext) wait.
tNext := tNext + (reader frameDelay).
].
"
! !
!FLIReader class methodsFor:'class initialization'!
initialize
FLI_FILE_MAGIC := 16rAF11.
FLC_FILE_MAGIC := 16rAF12.
FLI_FRAME_MAGIC := 16rF1FA.
FILE_HEAD_SIZE := 128.
FRAME_HEAD_SIZE := 16.
CHUNK_HEAD_SIZE := 6.
FLI_256_COLOR := 4.
FLI_DELTA := 7.
FLI_COLOR := 11.
FLI_LC := 12.
FLI_BLACK := 13.
FLI_BRUN := 15.
FLI_COPY := 16.
FLI_MINI := 18.
MAXCOLORS := 256.
"
FLIReader initialize
"
"Modified: 4.4.1997 / 22:19:45 / cg"
! !
!FLIReader class methodsFor:'testing'!
isValidImageFile:aFileName
"return true, if aFileName contains an FLI/FLC-movie"
|header n inStream len type|
inStream := self streamReadingFile:aFileName.
inStream isNil ifTrue:[^ false].
header := ByteArray new:FILE_HEAD_SIZE.
n := inStream nextBytes:FILE_HEAD_SIZE into:header.
inStream close.
n ~~ FILE_HEAD_SIZE ifTrue:[^ false].
len := header doubleWordAt:(1+0).
type := header wordAt:(1+4).
type ~~ FLI_FILE_MAGIC ifTrue:[
type ~~ FLC_FILE_MAGIC ifTrue:[
^ false
]
].
^ true
"
FLIReader isValidImageFile:'bitmaps/magtape.xpm'
FLIReader isValidImageFile:'/usr/local/FLI/jeffmild.fli'
"
"Modified: 4.4.1997 / 22:30:19 / cg"
! !
!FLIReader methodsFor:'accessing'!
frameDelay
"return the value of the instance variable 'frameDelay' (automatically generated)"
^ frameDelay
"Created: 4.4.1997 / 21:59:02 / cg"
!
hasMultipleImages
^ frames size > 1
"Created: 4.4.1997 / 21:39:25 / cg"
"Modified: 4.4.1997 / 21:42:59 / cg"
!
images
"return a collection of all images as represented by myself"
|images image depth|
depth := self bitsPerPixel.
images := OrderedCollection new.
frames do:[:aFrame |
image := (Image implementorForDepth:depth) new.
image
width:width
height:height
photometric:photometric
samplesPerPixel:samplesPerPixel
bitsPerSample:bitsPerSample
colorMap:colorMap
bits:aFrame
mask:mask.
images add:image.
].
^ images
"Modified: 20.6.1996 / 17:09:04 / cg"
"Created: 4.4.1997 / 21:44:44 / cg"
! !
!FLIReader methodsFor:'processing chunks'!
brunChunkAt:chunkOffs
|offs lineIdx nextLineIdx packets sz|
"/ 'brunChunkAt' infoPrintCR.
imageBuffer := ByteArray uninitializedNew:(width*height).
offs := chunkOffs.
lineIdx := 1.
1 to:height do:[:y |
nextLineIdx := lineIdx + width.
packets := frameBuffer byteAt:offs. offs := offs + 1.
1 to:packets do:[:p |
sz := frameBuffer signedByteAt:offs. offs := offs + 1.
sz > 0 ifTrue:[
data := frameBuffer at:offs. offs := offs + 1.
imageBuffer from:lineIdx to:(lineIdx+sz-1) put:data.
] ifFalse:[
sz == 0 ifTrue:[
self halt:'error in brun chunk'.
^ self.
].
sz := sz negated.
imageBuffer replaceFrom:lineIdx to:(lineIdx+sz-1)
with:frameBuffer startingAt:offs.
offs := offs + sz.
].
lineIdx := lineIdx + sz.
].
lineIdx := nextLineIdx
].
frames add:imageBuffer.
"
FLIReader imagesFromFile:'/usr/local/FLI/jeffmild.fli'
"
"Modified: 4.4.1997 / 22:45:37 / cg"
!
color256ChunkAt:chunkOffs
"/ 'color256Chunk' infoPrintCR.
self colorChunkAt:chunkOffs shift:0
"Modified: 4.4.1997 / 22:45:42 / cg"
!
color64ChunkAt:chunkOffs
"/ 'color64Chunk' infoPrintCR.
self colorChunkAt:chunkOffs shift:2
"Modified: 4.4.1997 / 22:45:45 / cg"
!
colorChunkAt:chunkOffs shift:colorShift
|b0 b1 packets offs ic skip change red green blue|
"/ ' colorChunk' infoPrintCR.
offs := chunkOffs.
packets := frameBuffer wordAt:offs. offs := offs + 2.
ic := 0.
1 to:packets do:[:i |
skip := frameBuffer byteAt:offs. offs := offs + 1.
ic := ic + skip.
ic := ic \\ MAXCOLORS.
change := frameBuffer byteAt:offs. offs := offs + 1.
change == 0 ifTrue:[change := 256].
1 to:change do:[:n |
red := frameBuffer at:offs. offs := offs + 1.
green := frameBuffer at:offs. offs := offs + 1.
blue := frameBuffer at:offs. offs := offs + 1.
red := red bitShift:colorShift.
green := green bitShift:colorShift.
blue := blue bitShift:colorShift.
"/ red print. ' ' print. green print. ' ' print. blue printCR.
redPalette at:(ic + 1) put:red.
greenPalette at:(ic + 1) put:green.
bluePalette at:(ic + 1) put:blue.
ic := ic + 1.
]
].
"
FLIReader fromFile:'/usr/local/FLI/jeffmild.fli'
"
"Modified: 4.4.1997 / 22:45:49 / cg"
!
deltaChunkAt:chunkOffs
"/ 'deltaChunkAt' infoPrintCR.
self halt:'not yet implemented'
"Modified: 4.4.1997 / 22:46:11 / cg"
!
lcChunkAt:chunkOffs
|jnext lines lineCnt offs lineIdx packets idx skip sz|
"/ 'lcChunkAt' infoPrintCR.
imageBuffer := imageBuffer copyFrom:1 to:(width*height).
offs := chunkOffs.
jnext := frameBuffer wordAt:offs. offs := offs + 2.
lines := frameBuffer wordAt:offs. offs := offs + 2.
lineCnt := 0.
lineIdx := 1.
0 to:height-1 do:[:row |
lineCnt >= lines ifTrue:[
frames add:imageBuffer.
^ self
].
row < jnext ifFalse:[
packets := frameBuffer byteAt:offs. offs := offs + 1.
lineCnt := lineCnt + 1.
idx := lineIdx.
1 to:packets do:[:p |
skip := frameBuffer byteAt:offs. offs := offs + 1.
idx := idx + skip.
sz := frameBuffer signedByteAt:offs. offs := offs + 1.
sz > 0 ifTrue:[
imageBuffer replaceFrom:idx to:(idx+sz-1)
with:frameBuffer startingAt:offs.
offs := offs + sz.
] ifFalse:[
sz < 0 ifTrue:[
sz := sz negated.
data := frameBuffer at:offs. offs := offs + 1.
imageBuffer from:idx to:(idx+sz-1) put:data.
]
].
idx := idx + sz.
].
].
lineIdx := lineIdx + width
].
frames add:imageBuffer.
"Modified: 4.4.1997 / 22:46:15 / cg"
!
processChunks:nchunks size:dataLen
"process chunks in a frame"
|len type offs chunkOffs|
offs := 1+0.
1 to:nchunks do:[:chunkIndex |
len := frameBuffer doubleWordAt:(offs+0).
type := frameBuffer wordAt:(offs+4).
chunkOffs := offs + CHUNK_HEAD_SIZE.
type == FLI_COLOR ifTrue:[
self color64ChunkAt:chunkOffs.
] ifFalse:[
type == FLI_256_COLOR ifTrue:[
self color256ChunkAt:chunkOffs.
] ifFalse:[
type == FLI_DELTA ifTrue:[
self deltaChunkAt:chunkOffs.
] ifFalse:[
type == FLI_LC ifTrue:[
self lcChunkAt:chunkOffs.
] ifFalse:[
type == FLI_BRUN ifTrue:[
self brunChunkAt:chunkOffs.
] ifFalse:[
'FLI [info]: unknown chunk type: ' infoPrint.
type hexPrintString infoPrintCR.
^ false.
]
]
]
].
].
offs := offs + len
].
"
FLIReader fromFile:'/usr/local/FLI/jeffmild.fli'
"
"Created: 3.4.1997 / 22:28:11 / cg"
"Modified: 3.4.1997 / 22:52:38 / cg"
! !
!FLIReader methodsFor:'reading from stream'!
fromStream:aStream
"read a FLI-movie from aStream."
inStream := aStream.
(self getHeader) ifFalse:[^ nil].
1 to:nframes do:[:frameIndex |
self getFrame
].
"/ return the first frame as image
colorMap := Colormap
redVector:redPalette
greenVector:greenPalette
blueVector:bluePalette.
photometric := #palette.
samplesPerPixel := 1.
bitsPerSample := #(8).
data := frames at:1.
"
FLIReader fromFile:'/usr/local/FLI/jeffmild.fli'
"
"Modified: 4.4.1997 / 22:45:32 / cg"
!
getFrame
"get a single frame"
|header n len type dataLen nchunks|
header := ByteArray new:FRAME_HEAD_SIZE.
n := inStream nextBytes:FRAME_HEAD_SIZE into:header.
n ~~ FRAME_HEAD_SIZE ifTrue:[^ false].
len := header doubleWordAt:(1+0).
type := header wordAt:(1+4).
type ~~ FLI_FRAME_MAGIC ifTrue:[^ false].
dataLen := len - FRAME_HEAD_SIZE.
dataLen > frameBufferSize ifTrue:[
frameBuffer := ByteArray uninitializedNew:dataLen.
frameBufferSize := dataLen.
].
n := inStream nextBytes:dataLen into:frameBuffer.
n ~~ dataLen ifTrue:[^ false].
nchunks := header wordAt:(1+6).
nchunks == 0 ifTrue:[
"/ mhmh - a timing frame; should add a dummy frame
^ self.
].
^ self processChunks:nchunks size:dataLen.
"
FLIReader fromFile:'bitmaps/magtape.xpm'
FLIReader fromFile:'/usr/local/FLI/jeffmild.fli'
"
"Created: 3.4.1997 / 22:15:19 / cg"
"Modified: 4.4.1997 / 22:18:21 / cg"
!
getHeader
"read the header; return true, if its valid"
|header n len type speed|
header := ByteArray new:FILE_HEAD_SIZE.
n := inStream nextBytes:FILE_HEAD_SIZE into:header.
n ~~ FILE_HEAD_SIZE ifTrue:[^ false].
len := header doubleWordAt:(1+0).
type := header wordAt:(1+4).
type ~~ FLI_FILE_MAGIC ifTrue:[
type ~~ FLC_FILE_MAGIC ifTrue:[
^ false
]
].
nframes := header wordAt:(1+6).
width := header wordAt:(1+8).
height := header wordAt:(1+10).
dimensionCallBack notNil ifTrue:[
dimensionCallBack value
].
flags := header wordAt:(1+14).
speed := header wordAt:(1+16).
speed <= 0 ifTrue:[
speed := 1
].
"/ FLI uses 1/70th of a second;
"/ FLC measures the frameDelay in milliseconds
type == FLI_FILE_MAGIC ifTrue:[
frameDelay := 1000 * speed // 70
] ifFalse:[
frameDelay := speed
].
frameBufferSize := width * height.
frameBuffer := ByteArray uninitializedNew:frameBufferSize.
redPalette := ByteArray new:256.
greenPalette := ByteArray new:256.
bluePalette := ByteArray new:256.
frames := OrderedCollection new:nframes.
^ true
"
FLIReader fromFile:'bitmaps/magtape.xpm'
FLIReader fromFile:'/usr/local/FLI/jeffmild.fli'
"
"Created: 3.4.1997 / 22:09:12 / cg"
"Modified: 4.4.1997 / 22:45:20 / cg"
! !
!FLIReader class methodsFor:'documentation'!
version
^ '$Header: /cvs/stx/stx/libview2/FLIReader.st,v 1.3 1997-04-04 20:46:41 cg Exp $'
! !
FLIReader initialize!