Merge branch 'gif' of git://github.com/klazuka/factor

db4
Slava Pestov 2009-09-30 05:15:21 -05:00
commit 17b52314dc
24 changed files with 345 additions and 77 deletions

View File

@ -1 +1,2 @@
Doug Coleman
Doug Coleman
Keith Lazuka

View File

@ -0,0 +1,83 @@
! Copyright (C) 2009 Keith Lazuka
! See http://factorcode.org/license.txt for BSD license.
USING: bitstreams byte-arrays classes help.markup help.syntax
kernel math quotations sequences ;
IN: compression.lzw
HELP: gif-lzw-uncompress
{ $values
{ "seq" sequence } { "code-size" integer }
{ "byte-array" byte-array }
}
{ $description "Decompresses a sequence of LZW-compressed bytes obtained from a GIF file." } ;
HELP: tiff-lzw-uncompress
{ $values
{ "seq" sequence }
{ "byte-array" byte-array }
}
{ $description "Decompresses a sequence of LZW-compressed bytes obtained from a TIFF file." } ;
HELP: lzw-read
{ $values
{ "lzw" lzw }
{ "lzw" lzw } { "n" integer }
}
{ $description "Read the next LZW code." } ;
HELP: lzw-process-next-code
{ $values
{ "lzw" lzw } { "quot" quotation }
}
{ $description "Read the next LZW code and, assuming that the code is neither the Clear Code nor the End of Information Code, conditionally processes it by calling " { $snippet "quot" } " with the lzw object and the LZW code. If it does read a Clear Code, this combinator will take care of handling the Clear Code for you." } ;
HELP: <lzw-uncompress>
{ $values
{ "input" bit-reader } { "code-size" "number of bits" } { "class" class }
{ "obj" object }
}
{ $description "Instantiate a new LZW decompressor." } ;
HELP: code-space-full?
{ $values
{ "lzw" lzw }
{ "?" boolean }
}
{ $description "Determines when to increment the variable length code's bit-width." } ;
HELP: reset-lzw-uncompress
{ $values
{ "lzw" lzw }
{ "lzw" lzw }
}
{ $description "Reset the LZW uncompressor state (either at initialization time or immediately after receiving a Clear Code). " } ;
ARTICLE: "compression.lzw.differences" "LZW Differences between TIFF and GIF"
{ $vocab-link "compression.lzw" }
$nl
"There are some subtle differences between the LZW algorithm used by TIFF and GIF images."
{ $heading "Variable Length Codes" }
"Both TIFF and GIF use a variation of the LZW algorithm that uses variable length codes. In both cases, the maximum code size is 12 bits. The initial code size, however, is different between the two formats. TIFF's initial code size is always 9 bits. GIF's initial code size is specified on a per-file basis at the beginning of the image descriptor block, with a minimum of 3 bits."
$nl
"TIFF and GIF each switch to the next code size using slightly different algorithms. GIF increments the code size as soon as the LZW string table's length is equal to 2**code-size, while TIFF increments the code size when the table's length is equal to 2**code-size - 1."
{ $heading "Packing Bits into Bytes" }
"TIFF and GIF LZW algorithms differ in how they pack the code bits into the byte stream. The least significant bit in a TIFF code is stored in the most significant bit of the bytestream, while the least significant bit in a GIF code is stored in the least significant bit of the bytestream."
{ $heading "Special Codes" }
"TIFF and GIF both add the concept of a 'Clear Code' and a 'End of Information Code' to the LZW algorithm. In both cases, the 'Clear Code' is equal to 2**(code-size - 1) and the 'End of Information Code' is equal to the Clear Code + 1. These 2 codes are reserved in the string table. So in both cases, the LZW string table is initialized to have a length equal to the End of Information Code + 1."
;
ARTICLE: "compression.lzw" "LZW Compression"
{ $vocab-link "compression.lzw" }
$nl
"Implements both the TIFF and GIF variations of the LZW algorithm."
{ $heading "Decompression" }
{ $subsection tiff-lzw-uncompress }
{ $subsection gif-lzw-uncompress }
{ $heading "Compression" }
"Compression has not yet been implemented."
$nl
"Implementation details:"
{ $subsection "compression.lzw.differences" }
;
ABOUT: "compression.lzw"

View File

@ -1,39 +1,37 @@
! Copyright (C) 2009 Doug Coleman.
! See http://factorcode.org/license.txt for BSD license.
USING: accessors alien.accessors assocs byte-arrays combinators
io.encodings.binary io.streams.byte-array kernel math sequences
vectors ;
USING: accessors combinators io kernel math namespaces
prettyprint sequences vectors ;
QUALIFIED-WITH: bitstreams bs
IN: compression.lzw
QUALIFIED-WITH: bitstreams bs
TUPLE: lzw
input
output
table
code
old-code
initial-code-size
code-size
clear-code
end-of-information-code ;
CONSTANT: clear-code 256
CONSTANT: end-of-information 257
TUPLE: tiff-lzw < lzw ;
TUPLE: gif-lzw < lzw ;
TUPLE: lzw input output table code old-code ;
SYMBOL: table-full
: lzw-bit-width ( n -- n' )
{
{ [ dup 510 <= ] [ drop 9 ] }
{ [ dup 1022 <= ] [ drop 10 ] }
{ [ dup 2046 <= ] [ drop 11 ] }
{ [ dup 4094 <= ] [ drop 12 ] }
[ drop table-full ]
} cond ;
: lzw-bit-width-uncompress ( lzw -- n )
table>> length lzw-bit-width ;
: initial-uncompress-table ( -- seq )
258 iota [ 1vector ] V{ } map-as ;
: initial-uncompress-table ( size -- seq )
iota [ 1vector ] V{ } map-as ;
: reset-lzw-uncompress ( lzw -- lzw )
initial-uncompress-table >>table ;
dup end-of-information-code>> 1 + initial-uncompress-table >>table
dup initial-code-size>> >>code-size ;
: <lzw-uncompress> ( input -- obj )
lzw new
: <lzw-uncompress> ( input code-size class -- obj )
new
swap >>code-size
dup code-size>> >>initial-code-size
dup code-size>> 1 - 2^ >>clear-code
dup clear-code>> 1 + >>end-of-information-code
swap >>input
BV{ } clone >>output
reset-lzw-uncompress ;
@ -55,22 +53,43 @@ ERROR: not-in-table value ;
: write-code ( lzw -- )
[ lookup-code ] [ output>> ] bi push-all ;
: add-to-table ( seq lzw -- ) table>> push ;
GENERIC: code-space-full? ( lzw -- ? )
: size-and-limit ( lzw -- m n ) [ table>> length ] [ code-size>> 2^ ] bi ;
M: tiff-lzw code-space-full? size-and-limit 1 - = ;
M: gif-lzw code-space-full? size-and-limit = ;
: maybe-increment-code-size ( lzw -- lzw )
dup code-space-full? [ [ 1 + ] change-code-size ] when ;
: add-to-table ( seq lzw -- )
[ table>> push ]
[ maybe-increment-code-size 2drop ] 2bi ;
: lzw-read ( lzw -- lzw n )
[ ] [ lzw-bit-width-uncompress ] [ input>> ] tri bs:read ;
[ ] [ code-size>> ] [ input>> ] tri bs:read ;
: end-of-information? ( lzw code -- ? ) swap end-of-information-code>> = ;
: clear-code? ( lzw code -- ? ) swap clear-code>> = ;
DEFER: handle-clear-code
: lzw-process-next-code ( lzw quot: ( lzw code -- ) -- )
[ lzw-read ] dip {
{ [ 3dup drop end-of-information? ] [ 3drop ] }
{ [ 3dup drop clear-code? ] [ 2drop handle-clear-code ] }
[ call( lzw code -- ) ]
} cond ; inline
DEFER: lzw-uncompress-char
: handle-clear-code ( lzw -- )
reset-lzw-uncompress
lzw-read dup end-of-information = [
2drop
] [
[
>>code
[ write-code ]
[ code>old-code ] bi
lzw-uncompress-char
] if ;
] lzw-process-next-code ;
: handle-uncompress-code ( lzw -- lzw )
dup code-in-table? [
@ -89,23 +108,15 @@ DEFER: lzw-uncompress-char
] if ;
: lzw-uncompress-char ( lzw -- )
lzw-read [
>>code
dup code>> end-of-information = [
drop
] [
dup code>> clear-code = [
handle-clear-code
] [
handle-uncompress-code
lzw-uncompress-char
] if
] if
] [
drop
] if* ;
[ >>code handle-uncompress-code lzw-uncompress-char ]
lzw-process-next-code ;
: lzw-uncompress ( seq -- byte-array )
bs:<msb0-bit-reader>
: lzw-uncompress ( bitstream code-size class -- byte-array )
<lzw-uncompress>
[ lzw-uncompress-char ] [ output>> ] bi ;
: tiff-lzw-uncompress ( seq -- byte-array )
bs:<msb0-bit-reader> 9 tiff-lzw lzw-uncompress ;
: gif-lzw-uncompress ( seq code-size -- byte-array )
[ bs:<lsb0-bit-reader> ] dip gif-lzw lzw-uncompress ;

View File

@ -1,10 +1,44 @@
! Copyright (C) 2009 Doug Coleman.
! See http://factorcode.org/license.txt for BSD license.
USING: tools.test images.tiff ;
USING: accessors images.tiff images.viewer io
io.encodings.binary io.files namespaces sequences tools.test ;
IN: images.tiff.tests
: tiff-test-path ( -- path )
"resource:extra/images/test-images/rgb.tiff" ;
: path>tiff ( path -- tiff )
binary [ input-stream get load-tiff ] with-file-reader ;
: tiff-example1 ( -- tiff )
"resource:extra/images/testing/square.tiff" path>tiff ;
: tiff-example2 ( -- tiff )
"resource:extra/images/testing/cube.tiff" path>tiff ;
: tiff-example3 ( -- tiff )
"resource:extra/images/testing/bi.tiff" path>tiff ;
: tiff-example4 ( -- tiff )
"resource:extra/images/testing/noise.tiff" path>tiff ;
: tiff-example5 ( -- tiff )
"resource:extra/images/testing/alpha.tiff" path>tiff ;
: tiff-example6 ( -- tiff )
"resource:extra/images/testing/color_spectrum.tiff" path>tiff ;
: tiff-example7 ( -- tiff )
"resource:extra/images/testing/small.tiff" path>tiff ;
: tiff-all. ( -- )
{
tiff-example1 tiff-example2 tiff-example3 tiff-example4 tiff-example5
tiff-example6
}
[ execute( -- gif ) tiff>image image. ] each ;
[ 1024 ] [ tiff-example1 ifds>> first bitmap>> length ] unit-test
[ 1024 ] [ tiff-example2 ifds>> first bitmap>> length ] unit-test
[ 131744 ] [ tiff-example3 ifds>> first bitmap>> length ] unit-test
[ 49152 ] [ tiff-example4 ifds>> first bitmap>> length ] unit-test
[ 16 ] [ tiff-example5 ifds>> first bitmap>> length ] unit-test
[ 117504 ] [ tiff-example6 ifds>> first bitmap>> length ] unit-test
: tiff-test-path2 ( -- path )
"resource:extra/images/test-images/octagon.tiff" ;

View File

@ -438,7 +438,7 @@ ERROR: unhandled-compression compression ;
: (uncompress-strips) ( strips compression -- uncompressed-strips )
{
{ compression-none [ ] }
{ compression-lzw [ [ lzw-uncompress ] map ] }
{ compression-lzw [ [ tiff-lzw-uncompress ] map ] }
[ unhandled-compression ]
} case ;

View File

@ -0,0 +1,2 @@
Doug Coleman
Keith Lazuka

View File

@ -0,0 +1,12 @@
! Copyright (C) 2009 Keith Lazuka.
! See http://factorcode.org/license.txt for BSD license.
USING: help.markup help.syntax kernel sequences ;
IN: images.gif
ARTICLE: "images.gif" "GIF Image Loader"
{ $vocab-link "images.gif" }
$nl
{ $notes "Currently multi-frame GIF images are not supported." }
;
ABOUT: "images.gif"

View File

@ -0,0 +1,95 @@
! Copyright (C) 2009 Keith Lazuka.
! See http://factorcode.org/license.txt for BSD license.
USING: accessors bitstreams compression.lzw images.gif io
io.encodings.binary io.files kernel math math.bitwise
math.parser namespaces prettyprint sequences tools.test images.viewer ;
QUALIFIED-WITH: bitstreams bs
IN: images.gif.tests
: path>gif ( path -- loading-gif )
binary [ input-stream get load-gif ] with-file-reader ;
: gif-example1 ( -- loading-gif )
"resource:extra/images/testing/circle.gif" path>gif ;
: gif-example2 ( -- loading-gif )
"resource:extra/images/testing/checkmark.gif" path>gif ;
: gif-example3 ( -- loading-gif )
"resource:extra/images/testing/monochrome.gif" path>gif ;
: gif-example4 ( -- loading-gif )
"resource:extra/images/testing/noise.gif" path>gif ;
: gif-example5 ( -- loading-gif )
"resource:extra/images/testing/alpha.gif" path>gif ;
: gif-example6 ( -- loading-gif )
"resource:extra/images/testing/astronaut_animation.gif" path>gif ;
: gif-all. ( -- )
{
gif-example1 gif-example2 gif-example3 gif-example4 gif-example5
gif-example6
}
[ execute( -- gif ) gif>image image. ] each ;
: declared-num-colors ( gif -- n ) flags>> 3 bits 1 + 2^ ;
: actual-num-colors ( gif -- n ) global-color-table>> length ;
[ 16 ] [ gif-example1 actual-num-colors ] unit-test
[ 16 ] [ gif-example1 declared-num-colors ] unit-test
[ 256 ] [ gif-example2 actual-num-colors ] unit-test
[ 256 ] [ gif-example2 declared-num-colors ] unit-test
[ 2 ] [ gif-example3 actual-num-colors ] unit-test
[ 2 ] [ gif-example3 declared-num-colors ] unit-test
: >index-stream ( gif -- seq )
[ compressed-bytes>> ]
[ image-descriptor>> first-code-size>> ] bi
gif-lzw-uncompress ;
[
BV{
0 0 0 0 0 0
1 0 0 0 0 1
1 1 0 0 1 1
1 1 1 1 1 1
1 0 1 1 0 1
1 0 0 0 0 1
}
] [ gif-example3 >index-stream ] unit-test
[
B{
255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255
0 0 0 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 0 0 0 255
0 0 0 255 0 0 0 255 255 255 255 255 255 255 255 255 0 0 0 255 0 0 0 255
0 0 0 255 0 0 0 255 0 0 0 255 0 0 0 255 0 0 0 255 0 0 0 255
0 0 0 255 255 255 255 255 0 0 0 255 0 0 0 255 255 255 255 255 0 0 0 255
0 0 0 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 0 0 0 255
}
] [ gif-example3 gif>image bitmap>> ] unit-test
[
BV{
0 1
1 0
}
] [ gif-example5 >index-stream ] unit-test
[
B{
255 000 000 255 000 000 000 000
000 000 000 000 255 000 000 255
}
] [ gif-example5 gif>image bitmap>> ] unit-test
[ 100 ] [ gif-example1 >index-stream length ] unit-test
[ 870 ] [ gif-example2 >index-stream length ] unit-test
[ 16384 ] [ gif-example4 >index-stream length ] unit-test
! example6 is a GIF animation and the first frame contains 1768 pixels
[ 1768 ] [ gif-example6 >index-stream length ] unit-test

View File

@ -1,11 +1,11 @@
! Copyrigt (C) 2009 Doug Coleman.
! Copyrigt (C) 2009 Doug Coleman, Keith Lazuka
! See http://factorcode.org/license.txt for BSD license.
USING: accessors arrays combinators constructors destructors
images images.loader io io.binary io.buffers
io.encodings.binary io.encodings.string io.encodings.utf8
io.files io.files.info io.ports io.streams.limited kernel make
math math.bitwise math.functions multiline namespaces
prettyprint sequences ;
USING: accessors arrays assocs combinators compression.lzw
constructors destructors grouping images images.loader io
io.binary io.buffers io.encodings.binary io.encodings.string
io.encodings.utf8 io.files io.files.info io.ports
io.streams.limited kernel make math math.bitwise math.functions
multiline namespaces prettyprint sequences ;
IN: images.gif
SINGLETON: gif-image
@ -37,12 +37,10 @@ ERROR: unknown-extension n ;
ERROR: gif-unexpected-eof ;
TUPLE: graphics-control-extension
label block-size raw-data
packed delay-time color-index
block-terminator ;
flags delay-time transparent-color-index ;
TUPLE: image-descriptor
separator left top width height flags ;
left top width height flags first-code-size ;
TUPLE: plain-text-extension
introducer label block-size text-grid-left text-grid-top text-grid-width
@ -67,6 +65,8 @@ CONSTANT: graphic-control-extension HEX: f9
CONSTANT: comment-extension HEX: fe
CONSTANT: application-extension HEX: ff
CONSTANT: trailer HEX: 3b
CONSTANT: graphic-control-extension-block-size HEX: 04
CONSTANT: block-terminator HEX: 00
: <loading-gif> ( -- loading-gif )
\ loading-gif new
@ -92,18 +92,20 @@ M: input-port stream-peek1
: read-image-descriptor ( -- image-descriptor )
\ image-descriptor new
1 read le> >>separator
2 read le> >>left
2 read le> >>top
2 read le> >>width
2 read le> >>height
1 read le> >>flags ;
1 read le> >>flags
1 read le> 1 + >>first-code-size ;
: read-graphic-control-extension ( -- graphic-control-extension )
\ graphics-control-extension new
1 read le> [ >>block-size ] [ read ] bi
>>raw-data
1 read le> >>block-terminator ;
1 read le> graphic-control-extension-block-size assert=
1 read le> >>flags
2 read le> >>delay-time
1 read le> >>transparent-color-index
1 read le> block-terminator assert= ;
: read-plain-text-extension ( -- plain-text-extension )
\ plain-text-extension new
@ -147,12 +149,14 @@ ERROR: unimplemented message ;
: interlaced? ( image -- ? ) flags>> 6 bit? ; inline
: sort? ( image -- ? ) flags>> 5 bit? ; inline
: color-table-size ( image -- ? ) flags>> 3 bits 1 + 2^ 3 * ; inline
: transparency? ( image -- ? )
graphic-control-extensions>> first flags>> 0 bit? ; inline
: color-resolution ( image -- ? ) flags>> -4 shift 3 bits ; inline
: read-global-color-table ( loading-gif -- loading-gif )
dup color-table? [
dup color-table-size read >>global-color-table
dup color-table-size read 3 group >>global-color-table
] when ;
: maybe-read-local-color-table ( loading-gif -- loading-gif )
@ -220,8 +224,33 @@ ERROR: unhandled-data byte ;
} case
] with-input-stream ;
: loading-gif>image ( loading-gif -- image )
;
: decompress ( loading-gif -- indexes )
[ compressed-bytes>> ]
[ image-descriptor>> first-code-size>> ] bi
gif-lzw-uncompress ;
: colorize ( index palette transparent-index/f -- seq )
pick = [ 2drop B{ 0 0 0 0 } ] [ nth 255 suffix ] if ;
: apply-palette ( indexes palette transparent-index/f -- bitmap )
[ colorize ] 2curry V{ } map-as concat ;
: dimensions ( loading-gif -- dim )
[ image-descriptor>> width>> ] [ image-descriptor>> height>> ] bi 2array ;
: ?transparent-color-index ( loading-gif -- index/f )
dup transparency?
[ graphic-control-extensions>> first transparent-color-index>> ]
[ drop f ] if ;
: gif>image ( loading-gif -- image )
[ <image> ] dip
[ dimensions >>dim ]
[ drop RGBA >>component-order ubyte-components >>component-type ]
[
[ decompress ] [ global-color-table>> ] [ ?transparent-color-index ] tri
apply-palette >>bitmap
] tri ;
ERROR: loading-gif-error gif-image ;
@ -229,4 +258,4 @@ ERROR: loading-gif-error gif-image ;
dup loading?>> [ loading-gif-error ] when ;
M: gif-image stream>image ( path gif-image -- image )
drop load-gif ensure-loaded loading-gif>image ;
drop load-gif ensure-loaded gif>image ;

View File

@ -0,0 +1 @@
GIF image file format

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 B

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 129 B

Binary file not shown.

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 51 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

Binary file not shown.

Binary file not shown.