add GIF parser

This commit is contained in:
Matthew Flatt 2010-06-02 14:55:31 -06:00
parent 5bef90e197
commit 41ce8ece58
3 changed files with 330 additions and 2 deletions

View File

@ -8,6 +8,7 @@
"png.ss" "png.ss"
"jpeg.ss" "jpeg.ss"
"xbm.ss" "xbm.ss"
"gif.rkt"
"local.ss" "local.ss"
"color.ss") "color.ss")
@ -295,8 +296,15 @@
(values s #f))))) (values s #f)))))
(lambda () (lambda ()
(destroy-decompress d))))] (destroy-decompress d))))]
[(gif gif/mask gif/alpha [(gif gif/mask gif/alpha)
bmp bmp/mask bmp/alpha) (let-values ([(w h rows) (gif->rgba-rows in)])
(let* ([s (cairo_image_surface_create CAIRO_FORMAT_ARGB32 w h)]
[alpha? #t]
[pre? #f]
[b&w? #f])
(install-from-png-arrays s w h rows b&w? alpha? pre? #f)
(values s b&w?)))]
[(bmp bmp/mask bmp/alpha)
(let* ([s (cairo_image_surface_create CAIRO_FORMAT_ARGB32 10 10)]) (let* ([s (cairo_image_surface_create CAIRO_FORMAT_ARGB32 10 10)])
(values s #f))] (values s #f))]
[(xbm xbm/alpha) [(xbm xbm/alpha)

View File

@ -0,0 +1,176 @@
#lang scheme
(require "lzw.rkt")
(provide gif->rgba-rows)
(define-syntax-rule (when-debugging expr ...)
#;(begin expr ...)
(void))
(define-struct lsd (width height gt? res sort? gt-size bg-idx ratio))
(define-struct img-desc (left top width height lct? interlace? sort? lct-size))
(define-struct image (desc data ct))
(define current-color-table
(make-parameter #f))
(define (read-header p)
(unless (bytes=? (read-bytes 3 p) #"GIF")
(error "missing the GIF header"))
(let ([version (read-bytes 3 p)])
(cond
[(bytes=? version #"87a") 87]
[(bytes=? version #"89a") 89]
[else (error "unknown GIF version: ~a" version)])))
(define (print-lsd l)
(printf "dimensions: ~a x ~a~n"
(lsd-width l) (lsd-height l))
(if (lsd-gt? l)
(printf "global color table of size ~a~n" (lsd-gt-size l))
(printf "no global color table~n"))
(printf "color res:~a~nsort?: ~a~n"
(lsd-res l) (lsd-sort? l))
(printf "bg-idx: ~a~naspect ratio:~a~n"
(lsd-bg-idx l) (lsd-ratio l)))
(define (read-lsd p)
(define block (read-bytes 7 p))
(unless (and (bytes? block)
(= (bytes-length block) 7))
(error "malformed Logical Screen Descriptor"))
(let ([packed-fields (bytes-ref block 4)])
(make-lsd (integer-bytes->integer block #f #f 0 2)
(integer-bytes->integer block #f #f 2 4)
(bitwise-bit-set? packed-fields 7)
(add1 (bitwise-bit-field packed-fields 4 7))
(bitwise-bit-set? packed-fields 3)
(expt 2 (add1 (bitwise-bit-field packed-fields 0 3)))
(bytes-ref block 5)
(bytes-ref block 6))))
(define (read-ct size p)
(define res (make-bytes (* 3 size)))
(let ([n (read-bytes! res p)])
(unless (= n (* 3 size))
(error "Color Table unexpectedly ended")))
res)
(define (print-argbs cs)
(define size (/ (bytes-length cs) 4))
(printf "size: ~a~n" size)
(for ([n (in-range size)])
(printf "color ~a: (~a, ~a, ~a, ~a)~n"
n
(bytes-ref cs (* 4 n))
(bytes-ref cs (+ 1 (* 4 n)))
(bytes-ref cs (+ 2 (* 4 n)))
(bytes-ref cs (+ 3 (* 4 n))))))
(define (print-img-desc id)
(printf "pos: (~a, ~a)~n"
(img-desc-left id) (img-desc-top id))
(printf "dimensions: ~a x ~a~n"
(img-desc-width id) (img-desc-height id))
(if (img-desc-lct? id)
(printf "local color table of size ~a~n" (img-desc-lct-size id))
(printf "no local color table~n"))
(printf "interlaced?: ~a~nsort?: ~a~n"
(img-desc-interlace? id)
(img-desc-sort? id)))
(define (read-img-desc p)
(define block (read-bytes 9 p))
(let ([packed-fields (bytes-ref block 8)])
(make-img-desc
(integer-bytes->integer block #f #f 0 2)
(integer-bytes->integer block #f #f 2 4)
(integer-bytes->integer block #f #f 4 6)
(integer-bytes->integer block #f #f 6 8)
(bitwise-bit-set? packed-fields 7)
(bitwise-bit-set? packed-fields 6)
(bitwise-bit-set? packed-fields 5)
(expt 2 (add1 (bitwise-bit-field packed-fields 0 3))))))
(define (read-data-subblocks p)
(apply
bytes-append
(let loop ([blocks null])
(let ([size (read-byte p)])
(cond
[(eof-object? size)
(error "Unexpected EOF")]
[(= size 0) (reverse blocks)]
[else
(loop (cons (read-bytes size p) blocks))])))))
(define (read-image-table image-data p)
(define coding-bits (read-byte p))
(define lzw-data (read-data-subblocks p))
(lzw-decompress image-data coding-bits lzw-data))
(define (read-image p)
(define id (read-img-desc p))
(define data (make-bytes (* (img-desc-width id) (img-desc-height id))))
(when-debugging
(print-img-desc id))
(parameterize ([current-color-table
(if (img-desc-lct? id)
(read-ct (img-desc-lct-size id) p)
(current-color-table))])
(when-debugging
(when (img-desc-lct? id)
(print-argbs (current-color-table))))
(read-image-table data p)
(make-image id data (current-color-table))))
(define (read-gif p just-one?)
(define version (read-header p))
(define lsd (read-lsd p))
(define global-table
(and (lsd-gt? lsd)
(read-ct (lsd-gt-size lsd) p)))
(when-debugging
(printf "version: ~a~n" version)
(print-lsd lsd)
(when global-table
(print-argbs global-table)))
(parameterize ([current-color-table global-table])
(define parsed-blocks
(let loop ([parsed-blocks null])
(let ([id (read-byte p)])
(cond
[(eof-object? id)
(error "Unexpected end of file")]
[(= id #x3b)
(reverse parsed-blocks)]
[(= id #x2c)
(let ([i (read-image p)])
(if just-one?
i
(loop (cons i parsed-blocks))))]
[else
(log-warning (format "gif: unhandled block type 0x~x~n" id))
(loop parsed-blocks)]))))
parsed-blocks))
(define (gif->rgba-rows in)
(let ([i (read-gif in #t)])
(let* ([data (image-data i)]
[len (bytes-length data)]
[ct (image-ct i)]
[w (img-desc-width (image-desc i))]
[h (img-desc-height (image-desc i))])
(values
w
h
(list->vector
(for/list ([j (in-range h)])
(let ([bstr (make-bytes (* 4 w) 255)])
(let ([yp (* w j)])
(for ([i (in-range w)])
(let ([pos (* 3 (bytes-ref data (+ yp i)))])
(bytes-set! bstr (* i 4) (bytes-ref ct pos))
(bytes-set! bstr (+ 1 (* i 4)) (bytes-ref ct (+ 1 pos)))
(bytes-set! bstr (+ 2 (* i 4)) (bytes-ref ct (+ 2 pos))))))
bstr)))))))

View File

@ -0,0 +1,144 @@
#lang racket/base
;;; Translated from Skippy for Common Lisp:
;;;
;;; Copyright (c) 2006 Zachary Beane, All Rights Reserved
;;;
;;; Redistribution and use in source and binary forms, with or without
;;; modification, are permitted provided that the following conditions
;;; are met:
;;;
;;; * Redistributions of source code must retain the above copyright
;;; notice, this list of conditions and the following disclaimer.
;;;
;;; * Redistributions in binary form must reproduce the above
;;; copyright notice, this list of conditions and the following
;;; disclaimer in the documentation and/or other materials
;;; provided with the distribution.
;;;
;;; THIS SOFTWARE IS PROVIDED BY THE AUTHOR 'AS IS' AND ANY EXPRESSED
;;; OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
;;; WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
;;; ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
;;; DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
;;; DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
;;; GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
;;; INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
;;; WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
;;; NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
;;; SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
;;;
;;;; Id: lzw.lisp,v 1.11 2007/01/03 22:01:10 xach Exp
(provide lzw-decompress)
(define (make-input-bitstream bstr)
(let ([pos 0]
[val 0]
[bits 0]
[limit (bytes-length bstr)])
(lambda (n)
(let loop ()
(cond
[(n . <= . bits)
(begin0
(bitwise-and val (sub1 (arithmetic-shift 1 n)))
(set! val (arithmetic-shift val (- n)))
(set! bits (- bits n)))]
[(= pos limit)
(add1 (arithmetic-shift 1 n))]
[else
(set! val (bitwise-ior (arithmetic-shift (bytes-ref bstr pos)
bits)
val))
(set! pos (add1 pos))
(set! bits (+ 8 bits))
(loop)])))))
(define (read-bits n bstream)
(bstream n))
(define (lzw-decompress result-bstr code-size bstr)
(let* ((entries (make-vector 4096 -1))
(preds (make-vector 4096 -1))
(clear-code (expt 2 code-size))
(end-of-input (+ clear-code 1))
(next-entry-index (+ clear-code 2))
(compression-size (add1 code-size))
(compression-threshold (* clear-code 2))
(pos 0)
(bitstream (make-input-bitstream bstr)))
(for ([i (in-range clear-code)])
(vector-set! entries i i))
(letrec ([reset-table
(lambda ()
(vector-fill! preds -1)
(for ([i (in-range clear-code 4096)])
(vector-set! entries i -1))
(set! next-entry-index (+ clear-code 2))
(set! compression-size (add1 code-size))
(set! compression-threshold (* clear-code 2)))]
[root-value
(lambda (code)
(let loop ([code code])
(let ([pred (vector-ref preds code)])
(if (negative? pred)
(vector-ref entries code)
(loop pred)))))]
[increase-compression-size!
(lambda ()
(set! compression-size (min 12 (add1 compression-size)))
(set! compression-threshold (* compression-threshold 2)))]
[add-entry
(lambda (entry pred)
(when (>= pred next-entry-index)
(error "Corrupt data in LZW stream"))
(vector-set! preds next-entry-index pred)
(vector-set! entries next-entry-index entry)
(let ([result next-entry-index])
(set! next-entry-index (add1 next-entry-index))
(when (>= next-entry-index compression-threshold)
(increase-compression-size!))
result))]
[code-depth
(lambda (code)
(let loop ([depth 0][code code])
(let ([pred (vector-ref preds code)])
(if (negative? pred)
depth
(loop (add1 depth) pred)))))]
[output-code-string
(lambda (code)
(let ([j pos])
(let ([i (+ pos (code-depth code))])
(set! pos (add1 i))
(if (>= i (bytes-length result-bstr))
(log-warning "Too much input data for image, ignoring extra")
(let loop ([code code]
[i i])
;; (printf "set ~a\n" (vector-ref entries code))
(bytes-set! result-bstr i (vector-ref entries code))
(when (i . > . j)
(loop (vector-ref preds code)
(sub1 i))))))))])
(let loop ([last-code -1])
(let ([code (read-bits compression-size bitstream)])
;; (printf "~s: ~s ~s ~s\n" compression-size code clear-code end-of-input)
(cond
[(= code clear-code)
(reset-table)
(loop -1)]
[(= code end-of-input)
(void)]
[(= last-code -1)
(output-code-string code)
(loop code)]
[else
(let ([entry (vector-ref entries code)])
(if (negative? entry)
(let ([root (root-value last-code)])
(output-code-string (add-entry root last-code)))
(let ([root (root-value code)])
(add-entry root last-code)
(output-code-string code))))
(loop code)]))))))