From c443d6d8159bce11eef509d806d564d6ef32b41e Mon Sep 17 00:00:00 2001
From: Doug Coleman <erg@jobim.local>
Date: Thu, 14 May 2009 15:46:10 -0500
Subject: [PATCH] check in marc's jpeg loader, png decoder, huffman, inflate,
 and image-processing vocabularies

---
 basis/compression/huffman/huffman.factor  |  88 +++++++
 basis/compression/inflate/inflate.factor  | 209 +++++++++++++++
 basis/images/jpeg/jpeg.factor             | 304 ++++++++++++++++++++++
 basis/images/loader/loader.factor         |   6 +-
 basis/images/png/png.factor               |  21 +-
 basis/images/processing/processing.factor |  40 +++
 6 files changed, 665 insertions(+), 3 deletions(-)
 create mode 100755 basis/compression/huffman/huffman.factor
 create mode 100755 basis/compression/inflate/inflate.factor
 create mode 100755 basis/images/jpeg/jpeg.factor
 create mode 100755 basis/images/processing/processing.factor

diff --git a/basis/compression/huffman/huffman.factor b/basis/compression/huffman/huffman.factor
new file mode 100755
index 0000000000..60b3a1d5a1
--- /dev/null
+++ b/basis/compression/huffman/huffman.factor
@@ -0,0 +1,88 @@
+! Copyright (C) 2009 Marc Fauconneau.
+! See http://factorcode.org/license.txt for BSD license.
+USING: accessors alt.bitstreams arrays assocs constructors fry
+hashtables io kernel locals math math.order math.parser
+math.ranges multiline sequences ;
+IN: compression.huffman
+
+QUALIFIED-WITH: alt.bitstreams bs
+
+<PRIVATE
+
+! huffman codes
+
+TUPLE: huffman-code
+    { value }
+    { size }
+    { code } ;
+
+: <huffman-code> ( -- code ) 0 0 0 huffman-code boa ;
+: next-size ( code -- ) [ 1+ ] change-size [ 2 * ] change-code drop ;
+: next-code ( code -- ) [ 1+ ] change-code drop ;
+
+:: all-patterns ( huff n -- seq )
+    n log2 huff size>> - :> free-bits
+    free-bits 0 >
+    [ free-bits 2^ [0,b) [ huff code>> free-bits 2^ * + ] map ]
+    [ huff code>> free-bits neg 2^ /i 1array ] if ;
+
+:: huffman-each ( tdesc quot: ( huff -- ) -- )
+    <huffman-code> :> code
+    tdesc
+    [
+        code next-size
+        [ code (>>value) code clone quot call code next-code ] each
+    ] each ; inline
+
+: update-reverse-table ( huff n table -- )
+    [ drop all-patterns ]
+    [ nip '[ _ swap _ set-at ] each ] 3bi ;
+
+:: reverse-table ( tdesc n -- rtable )
+   n f <array> <enum> :> table
+   tdesc [ n table update-reverse-table ] huffman-each
+   table seq>> ;
+
+:: huffman-table ( tdesc max -- table )
+   max f <array> :> table
+   tdesc [ [ ] [ value>> ] bi table set-nth ] huffman-each
+   table ;
+
+PRIVATE>
+
+! decoder
+
+TUPLE: huffman-decoder
+    { bs }
+    { tdesc }
+    { rtable }
+    { bits/level } ;
+
+CONSTRUCTOR: huffman-decoder ( bs tdesc -- decoder )
+    16 >>bits/level
+    [ ] [ tdesc>> ] [ bits/level>> 2^ ] tri reverse-table >>rtable ;
+
+: read1-huff ( decoder -- elt )
+    16 over [ bs>> bs:peek ] [ rtable>> nth ] bi ! first/last
+    [ size>> swap bs>> bs:seek ] [ value>> ] bi ;
+
+! %remove
+: reverse-bits ( value bits -- value' )
+    [ >bin ] [ CHAR: 0 pad-head <reversed> bin> ] bi* ;
+
+: read1-huff2 ( decoder -- elt )
+    16 over [ bs>> bs:peek 16 reverse-bits ] [ rtable>> nth ] bi ! first/last
+    [ size>> swap bs>> bs:seek ] [ value>> ] bi ;
+
+/*
+: huff>string ( code -- str )
+    [ value>> number>string ]
+    [ [ code>> ] [ size>> bits>string ] bi ] bi
+    " = " glue ;
+
+: huff. ( code -- ) huff>string print ;
+
+:: rtable. ( rtable -- )
+    rtable length>> log2 :> n
+    rtable <enum> [ swap n bits. [ huff. ] each ] assoc-each ;
+*/
diff --git a/basis/compression/inflate/inflate.factor b/basis/compression/inflate/inflate.factor
new file mode 100755
index 0000000000..a828718f75
--- /dev/null
+++ b/basis/compression/inflate/inflate.factor
@@ -0,0 +1,209 @@
+! Copyright (C) 2009 Marc Fauconneau.
+! See http://factorcode.org/license.txt for BSD license.
+USING: accessors arrays assocs byte-arrays
+byte-vectors combinators constructors fry grouping hashtables
+compression.huffman images io.binary kernel locals
+math math.bitwise math.order math.ranges multiline sequences
+sorting ;
+IN: compression.inflate
+
+QUALIFIED-WITH: alt.bitstreams bs
+
+<PRIVATE
+
+: enum>seq ( assoc -- seq )
+    dup keys [ ] [ max ] map-reduce 1 + f <array>
+    [ '[ swap _ set-nth ] assoc-each ] keep ;
+
+ERROR: zlib-unimplemented ;
+ERROR: bad-zlib-data ;
+ERROR: bad-zlib-header ;
+    
+:: check-zlib-header ( data -- )
+    16 data bs:peek 2 >le be> 31 mod    ! checksum
+    0 assert=                           
+    4 data bs:read 8 assert=            ! compression method: deflate
+    4 data bs:read                      ! log2(max length)-8, 32K max
+    7 <= [ bad-zlib-header ] unless     
+    5 data bs:seek                      ! drop check bits 
+    1 data bs:read 0 assert=            ! dictionnary - not allowed in png
+    2 data bs:seek                      ! compression level; ignore
+    ;
+
+:: default-table ( -- table )
+    0 <hashtable> :> table
+    0 143 [a,b] 280 287 [a,b] append 8 table set-at
+    144 255 [a,b] >array 9 table set-at
+    256 279 [a,b] >array 7 table set-at 
+    table enum>seq 1 tail ;
+
+CONSTANT: clen-shuffle { 16 17 18 0 8 7 9 6 10 5 11 4 12 3 13 2 14 1 15 }
+
+: get-table ( values size -- table ) 
+    16 f <array> clone <enum> 
+    [ '[ _ push-at ] 2each ] keep seq>> 1 tail [ natural-sort ] map ;
+
+:: decode-huffman-tables ( bitstream -- tables )
+    5 bitstream bs:read 257 +
+    5 bitstream bs:read 1 +
+    4 bitstream bs:read 4 +
+    clen-shuffle swap head
+    dup [ drop 3 bitstream bs:read ] map
+    get-table
+    bitstream swap <huffman-decoder> 
+    [ 2dup + ] dip swap :> k!
+    '[
+        _ read1-huff2
+        {
+            { [ dup 16 = ] [ 2 bitstream bs:read 3 + 2array ] }
+            { [ dup 17 = ] [ 3 bitstream bs:read 3 + 2array ] }
+            { [ dup 18 = ] [ 7 bitstream bs:read 11 + 2array ] }
+            [ ]
+        } cond
+        dup array? [ dup second ] [ 1 ] if
+        k swap - dup k! 0 >
+    ] 
+    [ ] produce swap suffix
+    { } [ dup array? [ dup first 16 = ] [ f ] if [ [ unclip-last ] [ second 1+ swap <repetition> append ] bi* ] [ suffix ] if ] reduce
+    [ dup array? [ second 0 <repetition> ] [ 1array ] if ] map concat
+    nip swap cut 2array [ [ length>> [0,b) ] [ ] bi get-table ] map ;
+    
+CONSTANT: length-table
+    {
+        3 4 5 6 7 8 9 10
+        11 13 15 17
+        19 23 27 31
+        35 43 51 59
+        67 83 99 115
+        131 163 195 227
+    }
+
+CONSTANT: dist-table
+    { 1 2 3 4 
+      5 7 9 13 
+      17 25 33 49
+      65 97 129 193
+      257 385 513 769
+      1025 1537 2049 3073
+      4097 6145 8193 12289
+      16385 24577 }
+
+: nth* ( n seq -- elt )
+    [ length 1- swap - ] [ nth ] bi ;
+
+:: inflate-lz77 ( seq -- bytes )
+    1000 <byte-vector> :> bytes
+    seq
+    [
+        dup array?
+        [ first2 '[ _ 1- bytes nth* bytes push ] times ]
+        [ bytes push ] if
+    ] each 
+    bytes ;
+
+:: inflate-dynamic ( bitstream -- bytes )
+    bitstream decode-huffman-tables
+    bitstream '[ _ swap <huffman-decoder> ] map :> tables
+    [
+        tables first read1-huff2
+        dup 256 >
+        [
+            dup 285 = 
+            [ ]
+            [ 
+                dup 264 > 
+                [ 
+                    dup 261 - 4 /i dup 5 > 
+                    [ bad-zlib-data ] when 
+                    bitstream bs:read 2array 
+                ]
+                when 
+            ] if
+            ! 5 bitstream read-bits ! distance
+            tables second read1-huff2
+            dup 3 > 
+            [ 
+                dup 2 - 2 /i dup 13 >
+                [ bad-zlib-data ] when
+                bitstream bs:read 2array
+            ] 
+            when
+            2array
+        ]
+        when
+        dup 256 = not
+    ]
+    [ ] produce nip
+    [
+        dup array? [
+            first2
+            [  
+                dup array? [ first2 ] [ 0 ] if
+                [ 257 - length-table nth ] [ + ] bi*
+            ] 
+            [
+                dup array? [ first2 ] [ 0 ] if
+                [ dist-table nth ] [ + ] bi*
+            ] bi*
+            2array
+        ] when
+    ] map ;
+    
+: inflate-raw ( bitstream -- bytes ) zlib-unimplemented ;
+: inflate-static ( bitstream -- bytes ) zlib-unimplemented ;
+
+:: inflate-loop ( bitstream -- bytes )
+    [ 1 bitstream bs:read 0 = ]
+    [
+        bitstream
+        2 bitstream bs:read ! B
+        { 
+            { 0 [ inflate-raw ] }
+            { 1 [ inflate-static ] }
+            { 2 [ inflate-dynamic ] }
+            { 3 [ bad-zlib-data f ] }
+        }
+        case
+    ]
+    [ produce ] keep call suffix concat ;
+    
+  !  [ produce ] keep dip swap suffix
+
+:: paeth ( a b c -- p ) 
+    a b + c - { a b c } [ [ - abs ] keep 2array ] with map 
+    sort-keys first second ;
+    
+:: png-unfilter-line ( prev curr filter -- curr' )
+    prev :> c
+    prev 3 tail-slice :> b
+    curr :> a
+    curr 3 tail-slice :> x
+    x length [0,b)
+    filter
+    {
+        { 0 [ drop ] }
+        { 1 [ [| n | n x nth n a nth + 256 wrap n x set-nth ] each ] }
+        { 2 [ [| n | n x nth n b nth + 256 wrap n x set-nth ] each ] }
+        { 3 [ [| n | n x nth n a nth n b nth + 2/ + 256 wrap n x set-nth ] each ] }
+        { 4 [ [| n | n x nth n a nth n b nth n c nth paeth + 256 wrap n x set-nth ] each ] }
+        
+    } case 
+    curr 3 tail ;
+
+PRIVATE>
+
+! for debug -- shows residual values
+: reverse-png-filter' ( lines -- filtered )
+    [ first ] [ 1 tail ] [ map ] bi-curry@ bi nip
+    concat [ 128 + 256 wrap ] map ;
+    
+: reverse-png-filter ( lines -- filtered )
+    dup first [ 0 ] replicate prefix
+    [ { 0 0 } prepend  ] map
+    2 clump [ first2 dup [ third ] [ 0 2 rot set-nth ] bi png-unfilter-line ] map concat ;
+
+: zlib-inflate ( bytes -- bytes )
+    bs:<lsb0-bitstream>
+    [ check-zlib-header ]
+    [ inflate-loop ] bi
+    inflate-lz77 ;
diff --git a/basis/images/jpeg/jpeg.factor b/basis/images/jpeg/jpeg.factor
new file mode 100755
index 0000000000..0588e5c365
--- /dev/null
+++ b/basis/images/jpeg/jpeg.factor
@@ -0,0 +1,304 @@
+! Copyright (C) 2009 Marc Fauconneau.
+! See http://factorcode.org/license.txt for BSD license.
+USING: accessors arrays byte-arrays combinators
+constructors grouping compression.huffman images
+images.processing io io.binary io.encodings.binary io.files
+io.streams.byte-array kernel locals math math.bitwise
+math.constants math.functions math.matrices math.order
+math.ranges math.vectors memoize multiline namespaces
+sequences sequences.deep ;
+IN: images.jpeg
+
+QUALIFIED-WITH: alt.bitstreams bs
+
+TUPLE: jpeg-image < image
+    { headers }
+    { bitstream }
+    { color-info initial: { f f f f } }
+    { quant-tables initial: { f f } }
+    { huff-tables initial: { f f f f } }
+    { components } ;
+
+<PRIVATE
+
+CONSTRUCTOR: jpeg-image ( headers bitstream -- image ) ;
+
+SINGLETONS: SOF DHT DAC RST SOI EOI SOS DQT DNL DRI DHP EXP
+APP JPG COM TEM RES ;
+
+! ISO/IEC 10918-1 Table B.1
+:: >marker ( byte -- marker )
+    byte
+    {
+      { [ dup HEX: CC = ] [ { DAC } ] }
+      { [ dup HEX: C4 = ] [ { DHT } ] }
+      { [ dup HEX: C9 = ] [ { JPG } ] }
+      { [ dup -4 shift HEX: C = ] [ SOF byte 4 bits 2array ] }
+
+      { [ dup HEX: D8 = ] [ { SOI } ] }
+      { [ dup HEX: D9 = ] [ { EOI } ] }
+      { [ dup HEX: DA = ] [ { SOS } ] }
+      { [ dup HEX: DB = ] [ { DQT } ] }
+      { [ dup HEX: DC = ] [ { DNL } ] }
+      { [ dup HEX: DD = ] [ { DRI } ] }
+      { [ dup HEX: DE = ] [ { DHP } ] }
+      { [ dup HEX: DF = ] [ { EXP } ] }
+      { [ dup -4 shift HEX: D = ] [ RST byte 4 bits 2array ] }
+
+      { [ dup -4 shift HEX: E = ] [ APP byte 4 bits 2array ] }
+      { [ dup HEX: FE = ] [ { COM } ] }
+      { [ dup -4 shift HEX: F = ] [ JPG byte 4 bits 2array ] }
+
+      { [ dup HEX: 01 = ] [ { TEM } ] }
+      [ { RES } ]
+    }
+    cond nip ;
+
+TUPLE: jpeg-chunk length type data ;
+
+CONSTRUCTOR: jpeg-chunk ( type length data -- jpeg-chunk ) ;
+
+TUPLE: jpeg-color-info
+    h v quant-table dc-huff-table ac-huff-table { diff initial: 0 } id ;
+
+CONSTRUCTOR: jpeg-color-info ( h v quant-table -- jpeg-color-info ) ;
+
+: jpeg> ( -- jpeg-image ) jpeg-image get ;
+
+: apply-diff ( dc color -- dc' )
+    [ diff>> + dup ] [ (>>diff) ] bi ;
+
+: fetch-tables ( component -- )
+    [ [ jpeg> quant-tables>> nth ] change-quant-table drop ]
+    [ [ jpeg> huff-tables>> nth ] change-dc-huff-table drop ]
+    [ [ 2 + jpeg> huff-tables>> nth ] change-ac-huff-table drop ] tri ;
+
+: read4/4 ( -- a b ) read1 16 /mod ;
+
+
+! headers
+
+: decode-frame ( header -- )
+    data>>
+    binary
+    [
+        read1 8 assert=
+        2 read be>
+        2 read be>
+        swap 2array jpeg> (>>dim)
+        read1
+        [
+            read1 read4/4 read1 <jpeg-color-info>
+            swap [ >>id ] keep jpeg> color-info>> set-nth
+        ] times
+    ] with-byte-reader ;
+
+: decode-quant-table ( chunk -- )
+    dup data>>
+    binary
+    [
+        length>>
+        2 - 65 /
+        [
+            read4/4 [ 0 assert= ] dip
+            64 read
+            swap jpeg> quant-tables>> set-nth
+        ] times
+    ] with-byte-reader ;
+
+: decode-huff-table ( chunk -- )
+    data>>
+    binary
+    [
+        1 ! %fixme: Should handle multiple tables at once
+        [
+            read4/4 swap 2 * +
+            16 read
+            dup [ ] [ + ] map-reduce read
+            binary [ [ read [ B{ } ] unless* ] { } map-as ] with-byte-reader
+            swap jpeg> huff-tables>> set-nth
+        ] times
+    ] with-byte-reader ;
+
+: decode-scan ( chunk -- )
+    data>>
+    binary
+    [
+        read1 [0,b)
+        [   drop
+            read1 jpeg> color-info>> nth clone
+            read1 16 /mod [ >>dc-huff-table ] [ >>ac-huff-table ] bi*
+        ] map jpeg> (>>components)
+        read1 0 assert=
+        read1 63 assert=
+        read1 16 /mod [ 0 assert= ] bi@
+    ] with-byte-reader ;
+
+: singleton-first ( seq -- elt )
+    [ length 1 assert= ] [ first ] bi ;
+
+: baseline-parse ( -- )
+    jpeg> headers>>
+    {
+        [ [ type>> { SOF 0 } = ] filter singleton-first decode-frame ]
+        [ [ type>> { DQT } = ] filter [ decode-quant-table ] each ]
+        [ [ type>> { DHT } = ] filter [ decode-huff-table ] each ]
+        [ [ type>> { SOS } = ] filter singleton-first decode-scan ]
+    } cleave ;
+
+: parse-marker ( -- marker )
+    read1 HEX: FF assert=
+    read1 >marker ;
+
+: parse-headers ( -- chunks )
+    [ parse-marker dup { SOS } = not ]
+    [
+        2 read be>
+        dup 2 - read <jpeg-chunk>
+    ] [ produce ] keep dip swap suffix ;
+
+MEMO: zig-zag ( -- zz )
+    {
+        {  0  1  5  6 14 15 27 28 }
+        {  2  4  7 13 16 26 29 42 }
+        {  3  8 12 17 25 30 41 43 }
+        {  9 11 18 24 31 40 44 53 }
+        { 10 19 23 32 39 45 52 54 }
+        { 20 22 33 38 46 51 55 60 }
+        { 21 34 37 47 50 56 59 61 }
+        { 35 36 48 49 57 58 62 63 }
+    } flatten ;
+
+MEMO: yuv>bgr-matrix ( -- m )
+    {
+        { 1  2.03211  0       }
+        { 1 -0.39465 -0.58060 }
+        { 1  0        1.13983 }
+    } ;
+
+: wave ( x u -- n ) swap 2 * 1 + * pi * 16 / cos ;
+
+:: dct-vect ( u v -- basis )
+    { 8 8 } coord-matrix [ { u v } [ wave ] 2map product ] map^2
+    1 u v [ 0 = [ 2 sqrt / ] when ] bi@ 4 / m*n ;
+
+MEMO: dct-matrix ( -- m ) 64 [0,b) [ 8 /mod dct-vect flatten ] map ;
+
+: mb-dim ( component -- dim )  [ h>> ] [ v>> ] bi 2array ;
+
+: all-macroblocks ( quot: ( mb -- ) -- )
+    [
+        jpeg>
+        [ dim>> 8 v/n ]
+        [ color-info>> sift { 0 0 } [ mb-dim vmax ] reduce v/ ] bi
+        [ ceiling ] map
+        coord-matrix flip concat
+    ]
+    [ each ] bi* ; inline
+
+: reverse-zigzag ( b -- b' ) zig-zag swap [ nth ] curry map ;
+
+: idct-factor ( b -- b' ) dct-matrix v.m ;
+
+USE: math.blas.vectors
+USE: math.blas.matrices
+
+MEMO: dct-matrix-blas ( -- m ) dct-matrix >float-blas-matrix ;
+: V.M ( x A -- x.A ) Mtranspose swap M.V ;
+: idct-blas ( b -- b' ) >float-blas-vector dct-matrix-blas V.M ;
+
+: idct ( b -- b' ) idct-blas ;
+
+:: draw-block ( block x,y color jpeg-image -- )
+    block dup length>> sqrt >fixnum group flip
+    dup matrix-dim coord-matrix flip
+    [
+        [ first2 spin nth nth ]
+        [ x,y v+ color id>> 1- jpeg-image draw-color ] bi
+    ] with each^2 ;
+
+: sign-extend ( bits v -- v' )
+    swap [ ] [ 1- 2^ < ] 2bi
+    [ -1 swap shift 1+ + ] [ drop ] if ;
+
+: read1-jpeg-dc ( decoder -- dc )
+    [ read1-huff dup ] [ bs>> bs:read ] bi sign-extend ;
+
+: read1-jpeg-ac ( decoder -- run/ac )
+    [ read1-huff 16 /mod dup ] [ bs>> bs:read ] bi sign-extend 2array ;
+
+:: decode-block ( pos color -- )
+    color dc-huff-table>> read1-jpeg-dc color apply-diff
+    64 0 <array> :> coefs
+    0 coefs set-nth
+    0 :> k!
+    [
+        color ac-huff-table>> read1-jpeg-ac
+        [ first 1+ k + k! ] [ second k coefs set-nth ] [ ] tri
+        { 0 0 } = not
+        k 63 < and
+    ] loop
+    coefs color quant-table>> v*
+    reverse-zigzag idct
+    ! %fixme: color hack
+    ! this eat 50% cpu time
+    color h>> 2 =
+    [ 8 group 2 matrix-zoom concat ] unless
+    pos { 8 8 } v* color jpeg> draw-block ;
+
+: decode-macroblock ( mb -- )
+    jpeg> components>>
+    [
+        [ mb-dim coord-matrix flip concat [ [ { 2 2 } v* ] [ v+ ] bi* ] with map ]
+        [ [ decode-block ] curry each ] bi
+    ] with each ;
+
+: cleanup-bitstream ( bytes -- bytes' )
+    binary [
+        [
+            { HEX: FF } read-until
+            read1 tuck HEX: 00 = and
+        ]
+        [ drop ] produce
+        swap >marker {  EOI } assert=
+        swap suffix
+        { HEX: FF } join
+    ] with-byte-reader ;
+
+: setup-bitmap ( image -- )
+    dup dim>> 16 v/n [ ceiling ] map 16 v*n >>dim
+    BGR >>component-order
+    f >>upside-down?
+    dup dim>> first2 * 3 * 0 <array> >>bitmap
+    drop ;
+
+: baseline-decompress ( -- )
+    jpeg> bitstream>> cleanup-bitstream { 255 255 255 255 } append
+    >byte-array bs:<msb0-bitstream> jpeg> (>>bitstream)
+    jpeg> [ bitstream>> ] [ [ [ <huffman-decoder> ] with map ] change-huff-tables drop ] bi
+    jpeg> components>> [ fetch-tables ] each
+    jpeg> setup-bitmap
+    [ decode-macroblock ] all-macroblocks ;
+
+! this eats ~25% cpu time
+: color-transform ( yuv -- rgb )
+    { 128 0 0 } v+ yuv>bgr-matrix swap m.v
+    [ 0 max 255 min >fixnum ] map ;
+
+PRIVATE>
+
+: load-jpeg ( path -- image )
+    binary [
+        parse-marker { SOI } assert=
+        parse-headers
+        contents <jpeg-image>
+    ] with-file-reader
+    dup jpeg-image [
+        baseline-parse
+        baseline-decompress
+        jpeg> bitmap>> 3 <groups> [ color-transform ] change-each
+        jpeg> [ >byte-array ] change-bitmap drop
+    ] with-variable ;
+
+M: jpeg-image load-image* ( path jpeg-image -- bitmap )
+    drop load-jpeg ;
diff --git a/basis/images/loader/loader.factor b/basis/images/loader/loader.factor
index fe33cc8f00..27b726f3c0 100644
--- a/basis/images/loader/loader.factor
+++ b/basis/images/loader/loader.factor
@@ -1,7 +1,8 @@
 ! Copyright (C) 2009 Doug Coleman.
 ! See http://factorcode.org/license.txt for BSD license.
 USING: constructors kernel splitting unicode.case combinators
-accessors images.bitmap images.tiff images io.pathnames ;
+accessors images.bitmap images.tiff images io.pathnames
+images.jpeg images.png ;
 IN: images.loader
 
 ERROR: unknown-image-extension extension ;
@@ -11,6 +12,9 @@ ERROR: unknown-image-extension extension ;
         { "bmp" [ bitmap-image ] }
         { "tif" [ tiff-image ] }
         { "tiff" [ tiff-image ] }
+        { "jpg" [ jpeg-image ] }
+        { "jpeg" [ jpeg-image ] }
+        { "png" [ png-image ] }
         [ unknown-image-extension ]
     } case ;
 
diff --git a/basis/images/png/png.factor b/basis/images/png/png.factor
index b027362977..bf13c43546 100755
--- a/basis/images/png/png.factor
+++ b/basis/images/png/png.factor
@@ -3,7 +3,7 @@
 USING: accessors constructors images io io.binary io.encodings.ascii
 io.encodings.binary io.encodings.string io.files io.files.info kernel
 sequences io.streams.limited fry combinators arrays math
-checksums checksums.crc32 ;
+checksums checksums.crc32 compression.inflate grouping byte-arrays ;
 IN: images.png
 
 TUPLE: png-image < image chunks
@@ -17,7 +17,8 @@ TUPLE: png-chunk length type data ;
 
 CONSTRUCTOR: png-chunk ( -- png-chunk ) ;
 
-CONSTANT: png-header B{ HEX: 89 HEX: 50 HEX: 4e HEX: 47 HEX: 0d HEX: 0a HEX: 1a HEX: 0a }
+CONSTANT: png-header
+    B{ HEX: 89 HEX: 50 HEX: 4e HEX: 47 HEX: 0d HEX: 0a HEX: 1a HEX: 0a }
 
 ERROR: bad-png-header header ;
 
@@ -61,6 +62,18 @@ ERROR: bad-checksum ;
 : fill-image-data ( image -- image )
     dup [ width>> ] [ height>> ] bi 2array >>dim ;
 
+: zlib-data ( png-image -- bytes ) 
+    chunks>> [ type>> "IDAT" = ] find nip data>> ;
+
+: decode-png ( image -- image ) 
+    {
+        [ zlib-data zlib-inflate ] 
+        [ dim>> first 3 * 1 + group reverse-png-filter ]
+        [ swap >byte-array >>bitmap drop ]
+        [ RGB >>component-order drop ]
+        [ ]
+    } cleave ;
+
 : load-png ( path -- image )
     [ binary <file-reader> ] [ file-info size>> ] bi
     stream-throws <limited-stream> [
@@ -69,4 +82,8 @@ ERROR: bad-checksum ;
         read-png-chunks
         parse-ihdr-chunk
         fill-image-data
+        decode-png
     ] with-input-stream ;
+
+M: png-image load-image*
+    drop load-png ;
diff --git a/basis/images/processing/processing.factor b/basis/images/processing/processing.factor
new file mode 100755
index 0000000000..2304c56171
--- /dev/null
+++ b/basis/images/processing/processing.factor
@@ -0,0 +1,40 @@
+! Copyright (C) 2009 Marc Fauconneau.
+! See http://factorcode.org/license.txt for BSD license.
+USING: accessors arrays byte-arrays combinators grouping images
+images.loader images.viewer kernel locals math math.order
+math.ranges math.vectors sequences sequences.deep fry ;
+IN: images.processing
+
+: coord-matrix ( dim -- m )
+    [ [0,b) ] map first2 [ [ 2array ] with map ] curry map ;
+
+: map^2 ( m quot -- m' ) '[ _ map ] map ; inline
+: each^2 ( m quot -- m' ) '[ _ each ] each ; inline
+
+: matrix-dim ( m -- dim ) [ length ] [ first length ] bi 2array ;
+    
+: matrix>image ( m -- image )
+    <image> over matrix-dim >>dim
+    swap flip flatten
+    [ 128 * 128 + 0 max 255 min  >fixnum ] map
+    >byte-array >>bitmap L >>component-order ;
+
+:: matrix-zoom ( m f -- m' )
+    m matrix-dim f v*n coord-matrix
+    [ [ f /i ] map first2 swap m nth nth ] map^2 ;
+
+:: image-offset ( x,y image -- xy )
+    image dim>> first
+    x,y second * x,y first + ;
+        
+:: draw-grey ( value x,y image -- )
+    x,y image image-offset 3 * { 0 1 2 }
+    [
+        + value 128 + >fixnum 0 max 255 min swap image bitmap>> set-nth
+    ] with each ;
+
+:: draw-color ( value x,y color-id image -- )
+    x,y image image-offset 3 * color-id + value >fixnum
+    swap image bitmap>> set-nth ;
+
+! : matrix. ( m -- ) 10 matrix-zoom matrix>image image. ;