Sunday sore-throat edition
This commit is contained in:
parent
9f6397806f
commit
d292dc423f
|
@ -0,0 +1 @@
|
|||
Oleg Kiselyov, RT Happe (porter)
|
|
@ -0,0 +1 @@
|
|||
Read entries from the (first) image directory of tiff files.
|
|
@ -0,0 +1,325 @@
|
|||
* Tiff -- A port of Oleg Kiselyov's tiff code
|
||||
|
||||
Both the code and this documentation is derived from Oleg's files, cf.
|
||||
http://okmij.org/ftp/Scheme/binary-io.html#tiff
|
||||
|
||||
This distribution comes with two family-friendly tiff files in sunterlib/
|
||||
scsh/tiff/.
|
||||
|
||||
** Reading TIFF files
|
||||
|
||||
[ Oleg's announcement. Main changes to the announced library: modularisation
|
||||
and fake srfi-4 uniform vectors. (The source files list the changes.) ]
|
||||
|
||||
From posting-system@google.com Wed Oct 8 01:24:13 2003
|
||||
Date: Tue, 7 Oct 2003 18:24:12 -0700
|
||||
From: oleg@pobox.com (oleg@pobox.com)
|
||||
Newsgroups: comp.lang.scheme
|
||||
Subject: [ANN] Reading TIFF files
|
||||
Message-ID: <7eb8ac3e.0310071724.59bffe62@posting.google.com>
|
||||
Status: OR
|
||||
|
||||
This is to announce a Scheme library to read and analyze TIFF image
|
||||
files. We can use the library to obtain the dimensions of a TIFF
|
||||
image; the image name and description; the resolution and other
|
||||
meta-data. We can then load a pixel matrix or a colormap table. An
|
||||
accompanying tiff-prober program prints out the TIFF dictionary in a
|
||||
raw and polished formats.
|
||||
|
||||
http://pobox.com/~oleg/ftp/Scheme/lib/tiff.scm
|
||||
dependencies: util.scm, char-encoding.scm, myenv.scm
|
||||
http://pobox.com/~oleg/ftp/Scheme/tests/vtiff.scm
|
||||
see also: gnu-head-sm.tif in the same directory
|
||||
http://pobox.com/~oleg/ftp/Scheme/tiff-prober.scm
|
||||
|
||||
Features:
|
||||
- The library handles TIFF files written in both endian formats
|
||||
- A TIFF directory is treated somewhat as a SRFI-44 immutable
|
||||
dictionary collection. Only the most basic SRFI-44 methods are
|
||||
implemented, including the left fold iterator and the get method.
|
||||
- An extensible tag dictionary translates between symbolic tag
|
||||
names and numeric ones. Ditto for tag values.
|
||||
- A tag dictionary for all TIFF 6 standard tags and values comes
|
||||
with the library. A user can add the definitions of
|
||||
his private tags.
|
||||
- The library handles TIFF directory values of types:
|
||||
(signed/unsigned) byte, short, long, rational; ASCII strings.
|
||||
- A particular care is taken to properly handle values whose
|
||||
total size is no more than 4 bytes.
|
||||
- Array values (including the image matrix) are returned as
|
||||
uniform vectors (SRFI-4)
|
||||
- Values are read lazily. If you are only interested in the
|
||||
dimensions of an image, the image matrix itself will not be loaded.
|
||||
|
||||
|
||||
Here's the result of running tiff-prober on the image of the GNU head
|
||||
(converted from JPEG to TIFF by xv). I hope I won't have any copyright
|
||||
problems with using and distributing that image.
|
||||
|
||||
Analyzing TIFF file tests/gnu-head-sm.tif...
|
||||
There are 15 entries in the TIFF directory
|
||||
they are
|
||||
TIFFTAG:IMAGEWIDTH, count 1, type short, value-offset 129 (0x81)
|
||||
TIFFTAG:IMAGELENGTH, count 1, type short, value-offset 122 (0x7A)
|
||||
TIFFTAG:BITSPERSAMPLE, count 1, type short, value-offset 8 (0x8)
|
||||
TIFFTAG:COMPRESSION, count 1, type short, value-offset 1 (0x1)
|
||||
TIFFTAG:PHOTOMETRIC, count 1, type short, value-offset 1 (0x1)
|
||||
TIFFTAG:IMAGEDESCRIPTION, count 29, type ascii str, value-offset 15932 (0x3E3C)
|
||||
TIFFTAG:STRIPOFFSETS, count 1, type long, value-offset 8 (0x8)
|
||||
TIFFTAG:ORIENTATION, count 1, type short, value-offset 1 (0x1)
|
||||
TIFFTAG:SAMPLESPERPIXEL, count 1, type short, value-offset 1 (0x1)
|
||||
TIFFTAG:ROWSPERSTRIP, count 1, type short, value-offset 122 (0x7A)
|
||||
TIFFTAG:STRIPBYTECOUNTS, count 1, type long, value-offset 15738 (0x3D7A)
|
||||
TIFFTAG:XRESOLUTION, count 1, type rational, value-offset 15962 (0x3E5A)
|
||||
TIFFTAG:YRESOLUTION, count 1, type rational, value-offset 15970 (0x3E62)
|
||||
TIFFTAG:PLANARCONFIG, count 1, type short, value-offset 1 (0x1)
|
||||
TIFFTAG:RESOLUTIONUNIT, count 1, type short, value-offset 2 (0x2)
|
||||
|
||||
image width: 129
|
||||
image height: 122
|
||||
image depth: 8
|
||||
document name: *NOT SPECIFIED*
|
||||
image description:
|
||||
JPEG:gnu-head-sm.jpg 129x122
|
||||
time stamp: *NOT SPECIFIED*
|
||||
compression: NONE
|
||||
|
||||
In particular, the dump of the tiff directory is produced by the
|
||||
following line of code
|
||||
(print-tiff-directory tiff-dict (current-output-port))
|
||||
To determine the width of the image, we do
|
||||
(tiff-directory-get tiff-dict 'TIFFTAG:IMAGEWIDTH not-spec)
|
||||
To determine the compression (as a symbol) we evaluate
|
||||
(tiff-directory-get-as-symbol tiff-dict 'TIFFTAG:COMPRESSION not-spec)
|
||||
|
||||
If an image directory contains private tags, they will be printed like
|
||||
the following:
|
||||
|
||||
private tag 33009, count 1, type signed long, value-offset 16500000 (0xFBC520)
|
||||
private tag 33010, count 1, type signed long, value-offset 4294467296
|
||||
(0xFFF85EE0)
|
||||
|
||||
A user may supply a dictionary of his private tags and enjoy
|
||||
the automatic translation from symbolic to numerical tag names.
|
||||
|
||||
The validation code vtiff.scm includes a function
|
||||
test-reading-pixel-matrix that demonstrates loading a pixel matrix of
|
||||
an image in an u8vector. The code can handle a single or multiple
|
||||
strips.
|
||||
|
||||
Portability: the library itself, tiff.scm, relies on the following
|
||||
extensions to R5RS: uniform vectors (SRFI-4); ascii->char function
|
||||
(which is on many systems just integer->char); trivial define-macro
|
||||
(which can be easily re-written into syntax-rules); let*-values
|
||||
(SRFI-11); records (SRFI-9). Actually, the code uses Gambit's native
|
||||
define-structures, which can be easily re-written into SRFI-9
|
||||
records. The Scheme system should be able to represent the full range
|
||||
of 32-bit integers and should support rationals.
|
||||
|
||||
The most problematic extension is an endian port. The TIFF library
|
||||
assumes the existence of a data structure with the following
|
||||
operations
|
||||
endian-port-set-bigendian!:: EPORT -> UNSPECIFIED
|
||||
endian-port-set-littlendian!:: EPORT -> UNSPECIFIED
|
||||
endian-port-read-int1:: EPORT -> UINTEGER (byte)
|
||||
endian-port-read-int2:: EPORT -> UINTEGER
|
||||
endian-port-read-int4:: EPORT -> UINTEGER
|
||||
endian-port-setpos:: EPORT INTEGER -> UNSPECIFIED
|
||||
|
||||
The library uses solely these methods to access the input port. The
|
||||
endian port can be implemented in a R5RS Scheme system if we assume
|
||||
that the composition of char->integer and read-char yields a byte and
|
||||
if we read the whole file into a string or a u8vector
|
||||
(SRFI-4). Obviously, there are times when such a solution is not
|
||||
satisfactory. Therefore, tiff-prober and the validation code
|
||||
vtiff.scm rely on a Gambit-specific code. All major Scheme systems can
|
||||
implement endian ports in a similar vein -- alas, each in its own
|
||||
particular way.
|
||||
|
||||
|
||||
|
||||
** Endian ports
|
||||
from structure endian.
|
||||
|
||||
We rely on an ENDIAN-PORT
|
||||
A port with the following operations
|
||||
endian-port-set-bigendian!:: EPORT -> UNSPECIFIED
|
||||
endian-port-set-littlendian!:: EPORT -> UNSPECIFIED
|
||||
endian-port-read-int1:: EPORT -> UINTEGER (byte)
|
||||
endian-port-read-int2:: EPORT -> UINTEGER
|
||||
endian-port-read-int4:: EPORT -> UINTEGER
|
||||
endian-port-setpos EPORT INTEGER -> UNSPECIFIED
|
||||
|
||||
close-endian-port:: EPORT -> UNSPECIFIED
|
||||
make-endian-port:: INPORT BOOLEAN -> EPORT
|
||||
The boolean argument sets the endianness of the resulting endian-port,
|
||||
boolean(most sigificant bit first). After having wrapped the INPORT
|
||||
in the EPORT, you should no longer manipulate the INPORT directly.
|
||||
|
||||
|
||||
|
||||
** Tiff
|
||||
in structures TIFF and TIFFLET. TIFFLET exports a survival package of
|
||||
bindings:
|
||||
read-tiff-file, print-tiff-directory, tiff-directory-get(-as-symbol).
|
||||
Refined needs will require TIFF.
|
||||
|
||||
*** TIFF tags: codes and values
|
||||
|
||||
A tag dictionary, tagdict, record helps translate between
|
||||
tag-symbols and their numerical values.
|
||||
|
||||
tagdict-get-by-name TAGDICT TAG-NAME => INT
|
||||
where TAG-NAME is a symbol.
|
||||
Translate a symbolic representation of a TIFF tag into a numeric
|
||||
representation.
|
||||
An error is raised if the lookup fails.
|
||||
|
||||
tagdict-get-by-num TAGDICT INT => TAG-NAME or #f
|
||||
Translate from a numeric tag value to a symbolic representation,
|
||||
if it exists. Return #f otherwise.
|
||||
|
||||
tagdict-tagval-get-by-name TAGDICT TAG-NAME VAL-NAME => INT
|
||||
where VAL-NAME is a symbol.
|
||||
Translate from the symbolic representation of a value associated
|
||||
with TAG-NAME in the TIFF directory, into the numeric representation.
|
||||
An error is raised if the lookup fails.
|
||||
|
||||
tagdict-tagval-get-by-num TAGDICT TAG-NAME INT => VAL-NAME or #f
|
||||
Translate from a numeric value associated with TAG-NAME in the TIFF
|
||||
directory to a symbolic representation, if it exists. Return #f
|
||||
otherwise.
|
||||
|
||||
make-tagdict ((TAG-NAME INT (VAL-NAME . INT) ...) ...)
|
||||
Build a tag dictionary
|
||||
|
||||
tagdict? TAGDICT -> BOOL
|
||||
|
||||
tagdict-add-all DEST-DICT SRC-DICT -> DEST-DICT
|
||||
Join two dictionaries
|
||||
|
||||
tiff-standard-tagdict : TAGDICT
|
||||
The variable tiff-standard-tagdict is initialized to the dictionary
|
||||
of standard TIFF tags (which you may look up in the first section above
|
||||
or in the source, tiff.scm).
|
||||
|
||||
Usage scenario:
|
||||
(tagdict-get-by-name tiff-standard-tagdict 'TIFFTAG:IMAGEWIDTH) => 256
|
||||
(tagdict-get-by-num tiff-standard-tagdict 256) => 'TIFFTAG:IMAGEWIDTH
|
||||
(tagdict-tagval-get-by-name tiff-standard-tagdict
|
||||
'TIFFTAG:COMPRESSION 'LZW) => 5
|
||||
(tagdict-tagval-get-by-num tiff-standard-tagdict
|
||||
'TIFFTAG:COMPRESSION 5) => 'LZW
|
||||
|
||||
(define extended-tagdict
|
||||
(tagdict-add-all tiff-standard-tagdict
|
||||
(make-tagdict
|
||||
'((WAupper_left_lat 33004)
|
||||
(WAhemisphere 33003 (North . 1) (South . 2))))))
|
||||
|
||||
|
||||
*** TIFF directory entry
|
||||
|
||||
a descriptor of a TIFF "item", which can be image data, document description,
|
||||
time stamp, etc, depending on the tag. Thus an entry has the following
|
||||
structure:
|
||||
unsigned short tag;
|
||||
unsigned short type; // data type: byte, short word, etc.
|
||||
unsigned long count; // number of items; length in spec
|
||||
unsigned long val_offset; // byte offset to field data
|
||||
|
||||
The values associated with each entry are disjoint and may appear anywhere
|
||||
in the file (so long as they are placed on a word boundary).
|
||||
|
||||
Note, If the value takes 4 bytes or less, then it is placed in the offset
|
||||
field to save space. If the value takes less than 4 bytes, it is
|
||||
*left*-justified in the offset field.
|
||||
Note, that it's always *left* justified (stored in the lower bytes)
|
||||
no matter what the byte order (big- or little- endian) is!
|
||||
Here's the precise quote from the TIFF 6.0 specification:
|
||||
"To save time and space the Value Offset contains the Value instead of
|
||||
pointing to the Value if and only if the Value fits into 4 bytes. If
|
||||
the Value is shorter than 4 bytes, it is left-justified within the
|
||||
4-byte Value Offset, i.e., stored in the lower- numbered
|
||||
bytes. Whether the Value fits within 4 bytes is determined by the Type
|
||||
and Count of the field."
|
||||
|
||||
tiff-dir-entry? TIFF-DIR-ENTRY => BOOLEAN
|
||||
tiff-dir-entry-tag TIFF-DIR-ENTRY => INTEGER
|
||||
tiff-dir-entry-type TIFF-DIR-ENTRY => INTEGER
|
||||
tiff-dir-entry-count TIFF-DIR-ENTRY => INTEGER
|
||||
tiff-dir-entry-val-offset TIFF-DIR-ENTRY => INTEGER
|
||||
tiff-dir-entry-value TIFF-DIR-ENTRY => VALUE
|
||||
|
||||
print-tiff-dir-entry TIFF-DIR-ENTRY TAGDICT OPORT -> UNSPECIFIED
|
||||
Print the contents of TIFF-DIR-ENTRY onto the output port OPORT
|
||||
using TAGDICT to convert tag identifiers to symbolic names
|
||||
|
||||
|
||||
*** TIFF Image File Directory
|
||||
|
||||
TIFF directory is a collection of TIFF directory entries. The entries
|
||||
are sorted in an ascending order by tag.
|
||||
Note, a TIFF file can contain more than one directory (chained together).
|
||||
We handle only the first one.
|
||||
|
||||
We treat a TIFF image directory somewhat as an ordered, immutable,
|
||||
dictionary collection, see SRFI-44.
|
||||
|
||||
tiff-directory? VALUE => BOOLEAN
|
||||
tiff-directory-size TIFF-DIRECTORY => INTEGER
|
||||
tiff-directory-empty? TIFF-DIRECTORY => BOOLEAN
|
||||
|
||||
tiff-directory-get TIFF-DIRECTORY KEY [ABSENCE-THUNK] => VALUE
|
||||
KEY can be either a symbol or an integer.
|
||||
If the lookup fails, ABSENCE-THUNK, if given, is evaluated and its value
|
||||
is returned. If ABSENCE-THUNK is omitted, the return value on failure
|
||||
is #f.
|
||||
|
||||
tiff-directory-get-as-symbol TIFF-DIRECTORY KEY [ABSENCE-THUNK] => VALUE
|
||||
KEY must be a symbol.
|
||||
If it is possible, the VALUE is returned as a symbol, as translated
|
||||
by the tagdict.
|
||||
|
||||
tiff-directory-fold-left TIFF-DIRECTORY FOLD-FUNCTION SEED-VALUE
|
||||
... => seed-value ...
|
||||
The fold function receives a tiff-directory-entry as a value
|
||||
|
||||
read-tiff-file:: EPORT [PRIVATE-TAGDICT] -> TIFF-DIRECTORY
|
||||
print-tiff-directory:: TIFF-DIRECTORY OPORT -> UNSPECIFIED
|
||||
|
||||
|
||||
|
||||
** Usage example: tiff prober
|
||||
|
||||
The scripts probe-tiff and equivalently tiff-prober.scm read a TIFF file
|
||||
and print out its directory (as well as values of a few "important" tags).
|
||||
The scripts (or script headers) assume that the executable scsh resides
|
||||
in /usr/local/bin, and that the environment variable SCSH_LIB_DIRS lists
|
||||
the sunterlib directory with the config file sunterlib.scm; cf. the reference
|
||||
manual about "Running Scsh\Scsh command-line switches\Switches".
|
||||
|
||||
Usage
|
||||
probe-tiff tiff-file1 ...
|
||||
or
|
||||
tiff-prober.scm tiff-file1 ...
|
||||
|
||||
Structure tiff-prober exports the entry point for the scripts:
|
||||
|
||||
tiff-prober ARGV => UNSPECIFIED
|
||||
Call, for instance, (tiff-prober '("foo" "bsp.tiff")).
|
||||
|
||||
|
||||
** Validating the library
|
||||
|
||||
The valdidating code is in sunterlib/scsh/tiff/vtiff.scm and assumes that
|
||||
the tiffed GNU logo sunterlib/tiff/gnu-head-sm.tif resides in the working
|
||||
directory. In that situation you may go
|
||||
,in tiff-testbed ; and
|
||||
,load vtiff.scm
|
||||
Alternatively make sure that the env variable SCSH_LIB_DIRS lists the
|
||||
directory with sunterlib.scm (just as for the tiff prober, see above)
|
||||
and run vtiff.scm as script.
|
||||
|
||||
oOo
|
||||
|
|
@ -0,0 +1,51 @@
|
|||
;
|
||||
; COUT, CERR, NL, ++ from Oleg Kiselyov's myenv.scm
|
||||
; changes:
|
||||
; ##stderr --> (error-output-port)
|
||||
; ++ via define-syntax and +
|
||||
|
||||
(define-syntax define-structure
|
||||
(lambda (form rename name=)
|
||||
(let ((name (cadr form)) ; Symbol | Generated-Name
|
||||
(fields (cddr form)) ; Proper-List(S. | G.N.)
|
||||
(concat (lambda (symbols) ; Proper-List(S.) -> S.
|
||||
(string->symbol
|
||||
(apply string-append
|
||||
(map symbol->string symbols))))))
|
||||
|
||||
`(,(rename 'define-record-type)
|
||||
,name
|
||||
(,(concat `(make- ,name)) ,@fields)
|
||||
,(concat `(,name ?))
|
||||
,@(map (lambda (field)
|
||||
`(,field
|
||||
,(concat `(,name - ,field))
|
||||
,(concat `(,name - ,field -set!))))
|
||||
fields))
|
||||
)))
|
||||
|
||||
|
||||
; like cout << arguments << args
|
||||
; where argument can be any Scheme object. If it's a procedure
|
||||
; (without args) it's executed rather than printed (like newline)
|
||||
|
||||
(define (cout . args)
|
||||
(for-each (lambda (x)
|
||||
(if (procedure? x) (x) (display x)))
|
||||
args))
|
||||
|
||||
(define (cerr . args)
|
||||
(for-each (lambda (x)
|
||||
(if (procedure? x)
|
||||
(x (error-output-port))
|
||||
(display x (error-output-port))))
|
||||
args))
|
||||
|
||||
(define nl (string #\newline))
|
||||
|
||||
|
||||
;; read-only increment
|
||||
(define-syntax ++
|
||||
(syntax-rules ()
|
||||
((++ x)
|
||||
(+ 1 x))))
|
Binary file not shown.
|
@ -0,0 +1,79 @@
|
|||
; taken and adapted from Oleg Kiselyov's tiff-prober.scm
|
||||
; Changes
|
||||
; ##fixnumm.logior --> bitwise-ior
|
||||
; ##fixnum.shl --> arithmetic-shift
|
||||
; make-endian-port : switches off buffering
|
||||
; To be evaluated in an environment binding
|
||||
; DEFINE-STRUCTURE to the required syntax (like CL's DEFSTRUCT)
|
||||
; CHAR->INTEGER to CHAR->ASCII
|
||||
|
||||
(define-structure endian-port port msb-first?)
|
||||
(set! make-endian-port
|
||||
(let ((really-make-endian-port make-endian-port))
|
||||
(lambda (port msb-first?)
|
||||
(set-port-buffering port bufpol/none) ; work around SEEK bug
|
||||
(really-make-endian-port port msb-first?))))
|
||||
|
||||
(define (close-endian-port eport)
|
||||
(close-input-port (endian-port-port eport)))
|
||||
|
||||
; endian-port-set-bigendian! EPORT -> UNSPECIFIED
|
||||
(define (endian-port-set-bigendian! eport)
|
||||
(endian-port-msb-first?-set! eport #t))
|
||||
|
||||
; endian-port-set-littlendian! EPORT -> UNSPECIFIED
|
||||
(define (endian-port-set-littlendian! eport)
|
||||
(endian-port-msb-first?-set! eport #f))
|
||||
|
||||
; endian-port-read-int1:: PORT -> UINTEGER (byte)
|
||||
(define (endian-port-read-int1 eport)
|
||||
(let ((c (read-char (endian-port-port eport))))
|
||||
(if (eof-object? c) (error "unexpected EOF")
|
||||
(char->integer c)))) ; Gambit-specific. Need read-byte
|
||||
; sunterlib: c->i bound to char->ascii
|
||||
|
||||
; endian-port-read-int2:: PORT -> UINTEGER
|
||||
(define (endian-port-read-int2 eport)
|
||||
(let* ((c1 (endian-port-read-int1 eport))
|
||||
(c2 (endian-port-read-int1 eport)))
|
||||
(if (endian-port-msb-first? eport)
|
||||
(bitwise-ior (arithmetic-shift c1 8) c2) ;(+ (* c1 256) c2)
|
||||
(bitwise-ior (arithmetic-shift c2 8) c1) ;(+ (* c2 256) c1)
|
||||
)))
|
||||
|
||||
; endian-port-read-int4:: PORT -> UINTEGER
|
||||
(define (endian-port-read-int4 eport)
|
||||
(let* ((c1 (endian-port-read-int1 eport))
|
||||
(c2 (endian-port-read-int1 eport))
|
||||
(c3 (endian-port-read-int1 eport))
|
||||
(c4 (endian-port-read-int1 eport)))
|
||||
(if (endian-port-msb-first? eport)
|
||||
;; (+ c4 (* 256 (+ c3 (* 256 (+ c2 (* 256 c1))))))
|
||||
(if (< c1 64) ; The int4 will fit into a fixnum
|
||||
(bitwise-ior
|
||||
(arithmetic-shift
|
||||
(bitwise-ior
|
||||
(arithmetic-shift
|
||||
(bitwise-ior (arithmetic-shift c1 8) c2) 8) c3) 8) c4)
|
||||
(+ (* 256 ; The multiplication will make a bignum
|
||||
(bitwise-ior
|
||||
(arithmetic-shift
|
||||
(bitwise-ior (arithmetic-shift c1 8) c2) 8) c3))
|
||||
c4))
|
||||
;; (+ c1 (* 256 (+ c2 (* 256 (+ c3 (* 256 c4))))))
|
||||
; c4 is the most-significant byte
|
||||
(if (< c4 64)
|
||||
(bitwise-ior
|
||||
(arithmetic-shift
|
||||
(bitwise-ior
|
||||
(arithmetic-shift
|
||||
(bitwise-ior (arithmetic-shift c4 8) c3) 8) c2) 8) c1)
|
||||
(+ (* 256
|
||||
(bitwise-ior
|
||||
(arithmetic-shift
|
||||
(bitwise-ior (arithmetic-shift c4 8) c3) 8) c2))
|
||||
c1)))))
|
||||
|
||||
; endian-port-setpos PORT INTEGER -> UNSPECIFIED
|
||||
(define (endian-port-setpos eport pos)
|
||||
(OS:fseek-abs (endian-port-port eport) pos))
|
Binary file not shown.
|
@ -0,0 +1,78 @@
|
|||
(define tifflet-face
|
||||
(export read-tiff-file
|
||||
print-tiff-directory
|
||||
tiff-directory-get
|
||||
tiff-directory-get-as-symbol
|
||||
))
|
||||
|
||||
(define tifftag-face
|
||||
(export tagdict-get-by-name
|
||||
tagdict-get-by-num
|
||||
tagdict-tagval-get-by-name
|
||||
tagdict-tagval-get-by-num
|
||||
make-tagdict
|
||||
tagdict?
|
||||
tagdict-add-all
|
||||
tiff-standard-tagdict
|
||||
))
|
||||
|
||||
(define tiffdir-face
|
||||
(export tiff-directory?
|
||||
tiff-directory-size
|
||||
tiff-directory-empty?
|
||||
tiff-directory-get
|
||||
tiff-directory-get-as-symbol
|
||||
read-tiff-file
|
||||
print-tiff-directory
|
||||
tiff-directory-fold-left
|
||||
tiff-dir-entry?
|
||||
tiff-dir-entry-tag
|
||||
tiff-dir-entry-type
|
||||
tiff-dir-entry-count
|
||||
tiff-dir-entry-val-offset
|
||||
tiff-dir-entry-value
|
||||
print-tiff-dir-entry
|
||||
))
|
||||
|
||||
(define tiff-prober-face
|
||||
(export tiff-prober))
|
||||
|
||||
(define-interface endian-face
|
||||
(export make-endian-port
|
||||
close-endian-port
|
||||
endian-port-set-bigendian!
|
||||
endian-port-set-littlendian!
|
||||
endian-port-read-int1
|
||||
endian-port-read-int2
|
||||
endian-port-read-int4
|
||||
endian-port-setpos))
|
||||
|
||||
;;;
|
||||
|
||||
(define-interface ersatz-srfi-4-face
|
||||
(export u8vector?
|
||||
u8vector make-u8vector
|
||||
u8vector-length
|
||||
u8vector-ref u8vector-set!
|
||||
u8vector->list list->u8vector
|
||||
|
||||
u16vector?
|
||||
u16vector make-u16vector
|
||||
u16vector-length
|
||||
u16vector-ref u16vector-set!
|
||||
u16vector->list list->u16vector
|
||||
|
||||
u32vector?
|
||||
u32vector make-u32vector
|
||||
u32vector-length
|
||||
u32vector-ref u32vector-set!
|
||||
u32vector->list list->u32vector
|
||||
))
|
||||
|
||||
|
||||
(define-interface tiff-helpers-face
|
||||
(export (define-structure :syntax)
|
||||
(++ :syntax)
|
||||
cerr cout nl))
|
||||
|
||||
|
|
@ -0,0 +1,79 @@
|
|||
(define-structures
|
||||
((tifflet tifflet-face)
|
||||
(tiff (compound-interface tifftag-face tiffdir-face)))
|
||||
(open tiff-helpers endian
|
||||
krims ; assert
|
||||
(modify sequence-lib (rename (sequence-any any?))) ; any?
|
||||
ascii ; ascii->char
|
||||
srfi-11 ; let*-values
|
||||
srfi-23 ; error
|
||||
ersatz-srfi-4
|
||||
scheme)
|
||||
(files tiff))
|
||||
|
||||
(define-structure endian endian-face
|
||||
(open tiff-helpers ; define-structure
|
||||
ascii ; char->ascii
|
||||
srfi-23 ; error
|
||||
(modify scheme-with-scsh (rename (seek OS:fseek-abs))))
|
||||
; seek, bit-ops, buffer policy
|
||||
(begin (define char->integer char->ascii))
|
||||
(files endian))
|
||||
|
||||
(define-structure tiff-helpers tiff-helpers-face
|
||||
(open srfi-9
|
||||
srfi-23 ; error
|
||||
scheme-with-scsh ; error-output-port
|
||||
)
|
||||
(for-syntax (open scheme))
|
||||
(files aux))
|
||||
|
||||
(define-structure ersatz-srfi-4 ersatz-srfi-4-face
|
||||
(open (modify scheme (alias (vector? u8vector?)
|
||||
(vector u8vector)
|
||||
(make-vector make-u8vector)
|
||||
(vector-length u8vector-length)
|
||||
(vector-ref u8vector-ref)
|
||||
(vector-set! u8vector-set!)
|
||||
(vector->list u8vector->list)
|
||||
(list->vector list->u8vector)
|
||||
|
||||
(vector? u16vector?)
|
||||
(vector u16vector)
|
||||
(make-vector make-u16vector)
|
||||
(vector-length u16vector-length)
|
||||
(vector-ref u16vector-ref)
|
||||
(vector-set! u16vector-set!)
|
||||
(vector->list u16vector->list)
|
||||
(list->vector list->u16vector)
|
||||
|
||||
(vector? u32vector?)
|
||||
(vector u32vector)
|
||||
(make-vector make-u32vector)
|
||||
(vector-length u32vector-length)
|
||||
(vector-ref u32vector-ref)
|
||||
(vector-set! u32vector-set!)
|
||||
(vector->list u32vector->list)
|
||||
(list->vector list->u32vector)
|
||||
))))
|
||||
|
||||
(define-structure tiff-prober tiff-prober-face
|
||||
(open tifflet
|
||||
endian ; make-endian-port
|
||||
tiff-helpers ; cout cerr nl
|
||||
scheme-with-scsh ; scsh for open-input-file
|
||||
)
|
||||
(begin (define (exit) #f)) ; good enough
|
||||
(files tiff-prober))
|
||||
|
||||
(define-structure tiff-testbed (export )
|
||||
(open tiff
|
||||
endian ; endian ports
|
||||
tiff-helpers ; cerr nl ++
|
||||
krims ; assert
|
||||
ersatz-srfi-4 ; fake uniform vectors
|
||||
srfi-11 ; let-values*
|
||||
scheme-with-scsh
|
||||
)
|
||||
(begin ))
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
#! /usr/local/bin/scsh \
|
||||
-e tiff-prober -ll sunterlib.scm -o tiff-prober -s
|
||||
!#
|
|
@ -0,0 +1,55 @@
|
|||
#! /usr/local/bin/scsh \
|
||||
-e tiff-prober -ll sunterlib.scm -o tiff -o tiff-helpers -o endian -s
|
||||
!#
|
||||
|
||||
;****************************************************************************
|
||||
; TIFF prober
|
||||
;
|
||||
; This code reads a TIFF file and prints out its directory (as well as values
|
||||
; of a few "important" tags)
|
||||
; Usage
|
||||
; tiff-prober tiff-file1...
|
||||
; Derived from Oleg Kiselyov's tiff-prober.scm 2.0 2003/10/04 02:35:30
|
||||
; Changes for the sunterlib
|
||||
; procedural wrapper TIFF-PROBER as entry point
|
||||
; argv as parameter
|
||||
; endian ports moved to endian.scm
|
||||
|
||||
(define (tiff-prober argv-s)
|
||||
(let
|
||||
((help
|
||||
(lambda ()
|
||||
(cerr nl nl "print information about TIFF file(s)" nl)
|
||||
(cerr nl "Usage")
|
||||
(cerr nl " tiff-prober tiff-file1...")
|
||||
(cerr nl nl "Example:")
|
||||
(cerr nl " tiff-prober im1.tiff im2.tiff" nl nl)
|
||||
(exit)))
|
||||
)
|
||||
; (car argv-s) is program's name, as usual
|
||||
(if (or (null? argv-s) (null? (cdr argv-s)))
|
||||
(help)) ; at least one argument, besides argv[0], is expected
|
||||
(for-each
|
||||
(lambda (file-name)
|
||||
(cout nl nl "Analyzing TIFF file " file-name "..." nl)
|
||||
(let* ((eport (make-endian-port (open-input-file file-name) #t))
|
||||
(tiff-dict (read-tiff-file eport))
|
||||
(not-spec (lambda () "*NOT SPECIFIED*")))
|
||||
(print-tiff-directory tiff-dict (current-output-port))
|
||||
(cout nl "image width: "
|
||||
(tiff-directory-get tiff-dict 'TIFFTAG:IMAGEWIDTH not-spec))
|
||||
(cout nl "image height: "
|
||||
(tiff-directory-get tiff-dict 'TIFFTAG:IMAGELENGTH not-spec))
|
||||
(cout nl "image depth: "
|
||||
(tiff-directory-get tiff-dict 'TIFFTAG:BITSPERSAMPLE not-spec))
|
||||
(cout nl "document name: "
|
||||
(tiff-directory-get tiff-dict 'TIFFTAG:DOCUMENTNAME not-spec))
|
||||
(cout nl "image description: " nl " "
|
||||
(tiff-directory-get tiff-dict 'TIFFTAG:IMAGEDESCRIPTION not-spec))
|
||||
(cout nl "time stamp: "
|
||||
(tiff-directory-get tiff-dict 'TIFFTAG:DATETIME not-spec))
|
||||
(cout nl "compression: "
|
||||
(tiff-directory-get-as-symbol tiff-dict
|
||||
'TIFFTAG:COMPRESSION not-spec))
|
||||
(cout nl nl)))
|
||||
(cdr argv-s))) )
|
|
@ -0,0 +1,774 @@
|
|||
;****************************************************************************
|
||||
;
|
||||
; Tag Image File Format (TIFF)
|
||||
;
|
||||
;
|
||||
; Tiff tag definitions were borrowed from:
|
||||
; > Copyright (c) 1988, 1990 by Sam Leffler.
|
||||
; > All rights reserved.
|
||||
; >
|
||||
; > This file is provided for unrestricted use provided that this
|
||||
; > legend is included on all tape media and as a part of the
|
||||
; > software program in whole or part. Users may copy, modify or
|
||||
; > distribute this file at will.
|
||||
; >
|
||||
; > Based on Rev 5.0 from:
|
||||
; > Developer's Desk Window Marketing Group
|
||||
; > Aldus Corporation Microsoft Corporation
|
||||
; > 411 First Ave. South 16011 NE 36th Way
|
||||
; > Suite 200 Box 97017
|
||||
; > Seattle, WA 98104 Redmond, WA 98073-9717
|
||||
; > 206-622-5500 206-882-8080
|
||||
;
|
||||
; Now updated for TIFF 6
|
||||
; http://www.wotsit.org/download.asp?f=tiff6
|
||||
;
|
||||
; We rely on an ENDIAN-PORT
|
||||
; A port with the following operations
|
||||
; endian-port-set-bigendian!:: PORT -> UNSPECIFIED
|
||||
; endian-port-set-littlendian!:: PORT -> UNSPECIFIED
|
||||
; endian-port-read-int1:: PORT -> UINTEGER (byte)
|
||||
; endian-port-read-int2:: PORT -> UINTEGER
|
||||
; endian-port-read-int4:: PORT -> UINTEGER
|
||||
; endian-port-setpos PORT INTEGER -> UNSPECIFIED
|
||||
;
|
||||
; Also needed SRFIs: SRFI-4 (uniform vectors), SRFI-9 (records)
|
||||
; Actually, we're using structures, which can be translated to SRFI-9
|
||||
; records.
|
||||
;
|
||||
; Derived from Oleg Kiselyov's tiff.scm 2.0 2003/09/29 20:05:12
|
||||
; Changes:
|
||||
; make-define-env, define-macro --> explicit-renaming
|
||||
|
||||
|
||||
;------------------------------------------------------------------------
|
||||
; TIFF file header
|
||||
; It is always written at the very beginning of the TIFF file
|
||||
|
||||
; unsigned short magic; // magic number (defines byte order)
|
||||
; unsigned short version; // TIFF version number
|
||||
; unsigned long diroffset; // byte offset to the first directory
|
||||
|
||||
(define TIFF:BIGENDIAN-magic #x4d4d) ; 'MM'
|
||||
(define TIFF:LITTLEENDIAN-magic #x4949) ; 'II'
|
||||
(define TIFF:VERSION 42)
|
||||
|
||||
; procedure: TIFF:read-header ENDIAN-PORT -> OFFSET
|
||||
;
|
||||
; Reads and checks the TIFF header, sets the reader to
|
||||
; the appropriate little/big endian mode and returns the byte
|
||||
; offset to the first TIFF directory
|
||||
(define (TIFF:read-header eport)
|
||||
(let ((magic-word (endian-port-read-int2 eport)))
|
||||
(cond
|
||||
((= magic-word TIFF:BIGENDIAN-magic)
|
||||
(endian-port-set-bigendian! eport))
|
||||
((= magic-word TIFF:LITTLEENDIAN-magic)
|
||||
(endian-port-set-littlendian! eport))
|
||||
(else (error "invalid magic word 0x" (number->string magic-word 16)
|
||||
"of the TIFF file"))))
|
||||
(let ((version (endian-port-read-int2 eport)))
|
||||
(if (not (= version TIFF:VERSION))
|
||||
(error "TIFF file version " version "differs from the standard "
|
||||
TIFF:VERSION)))
|
||||
(endian-port-read-int4 eport))
|
||||
|
||||
;------------------------------------------------------------------------
|
||||
; TIFF tags: codes and values
|
||||
;
|
||||
; A tag dictionary, tagdict, structure helps translate between
|
||||
; tag-symbols and their numerical values.
|
||||
;
|
||||
; procedure: tagdict-get-by-name TAGDICT TAG-NAME -> INT
|
||||
; where TAG-NAME is a symbol.
|
||||
; Translate a symbolic representation of a TIFF tag into a numeric
|
||||
; representation.
|
||||
; An error is raised if the lookup fails.
|
||||
;
|
||||
; procedure: tagdict-get-by-num TAGDICT INT -> TAG-NAME or #f
|
||||
; Translate from a numeric tag value to a symbolic representation,
|
||||
; if it exists. Return #f otherwise.
|
||||
;
|
||||
; procedure: tagdict-tagval-get-by-name TAGDICT TAG-NAME VAL-NAME -> INT
|
||||
; where VAL-NAME is a symbol.
|
||||
; Translate from the symbolic representation of a value associated
|
||||
; with TAG-NAME in the TIFF directory, into the numeric representation.
|
||||
; An error is raised if the lookup fails.
|
||||
;
|
||||
; procedure: tagdict-tagval-get-by-num TAGDICT TAG-NAME INT -> VAL-NAME or #f
|
||||
; Translate from a numeric value associated with TAG-NAME in the TIFF
|
||||
; directory to a symbolic representation, if it exists. Return #f
|
||||
; otherwise.
|
||||
;
|
||||
; procedure: make-tagdict ((TAG-NAME INT (VAL-NAME . INT) ...) ...)
|
||||
; Build a tag dictionary
|
||||
;
|
||||
; procedure: tagdict? TAGDICT -> BOOL
|
||||
;
|
||||
; procedure: tagdict-add-all DEST-DICT SRC-DICT -> DEST-DICT
|
||||
; Join two dictionaries
|
||||
;
|
||||
; The variable tiff-standard-tagdict is initialized to the dictionary
|
||||
; of standard TIFF tags.
|
||||
|
||||
; Usage scenario:
|
||||
; (tagdict-get-by-name tiff-standard-tagdict 'TIFFTAG:IMAGEWIDTH) => 256
|
||||
; (tagdict-get-by-num tiff-standard-tagdict 256) => 'TIFFTAG:IMAGEWIDTH
|
||||
; (tagdict-tagval-get-by-name tiff-standard-tagdict
|
||||
; 'TIFFTAG:COMPRESSION 'LZW) => 5
|
||||
; (tagdict-tagval-get-by-num tiff-standard-tagdict
|
||||
; 'TIFFTAG:COMPRESSION 5) => 'LZW
|
||||
;
|
||||
; (define extended-tagdict
|
||||
; (tagdict-add-all tiff-standard-tagdict
|
||||
; (make-tagdict
|
||||
; '((WAupper_left_lat 33004)
|
||||
; (WAhemisphere 33003 (North . 1) (South . 2))))))
|
||||
|
||||
(define-structure p-tiff-tag-dict table)
|
||||
|
||||
|
||||
(define tagdict? p-tiff-tag-dict?)
|
||||
|
||||
(define (make-tagdict args)
|
||||
(for-each ; error-check each dict association
|
||||
(lambda (arg)
|
||||
(or
|
||||
(and
|
||||
(pair? arg)
|
||||
(list? arg)
|
||||
(symbol? (car arg))
|
||||
(integer? (cadr arg)))
|
||||
(error "make-tagdict: bad association to add: " arg))
|
||||
(for-each
|
||||
(lambda (val-assoc)
|
||||
(or
|
||||
(and
|
||||
(pair? val-assoc)
|
||||
(symbol? (car val-assoc))
|
||||
(integer? (cdr val-assoc)))
|
||||
(error "make-tagdict: bad tag value association: " val-assoc)))
|
||||
(cddr arg)))
|
||||
args)
|
||||
(make-p-tiff-tag-dict args))
|
||||
|
||||
|
||||
; procedure: tagdict-add-all DEST-DICT SRC-DICT -> DEST-DICT
|
||||
; Join two dictionaries
|
||||
(define (tagdict-add-all dest-dict src-dict)
|
||||
(assert (tagdict? dest-dict) (tagdict? src-dict))
|
||||
(make-p-tiff-tag-dict
|
||||
(append (p-tiff-tag-dict-table dest-dict)
|
||||
(p-tiff-tag-dict-table src-dict))))
|
||||
|
||||
; procedure: tagdict-get-by-name TAGDICT TAG-NAME -> INT
|
||||
; An error is raised if the lookup fails.
|
||||
|
||||
(define (tagdict-get-by-name dict tag-name)
|
||||
(assert (tagdict? dict))
|
||||
(cond
|
||||
((assq tag-name (p-tiff-tag-dict-table dict))
|
||||
=> cadr)
|
||||
(else
|
||||
(error "tagdict-get-by-name: can't translate: " tag-name))))
|
||||
|
||||
|
||||
; procedure: tagdict-get-by-num TAGDICT INT -> TAG-NAME or #f
|
||||
(define (tagdict-get-by-num dict tag-int)
|
||||
(assert (tagdict? dict))
|
||||
(any?
|
||||
(lambda (table-row)
|
||||
(and (= (cadr table-row) tag-int) (car table-row)))
|
||||
(p-tiff-tag-dict-table dict)))
|
||||
|
||||
|
||||
; procedure: tagdict-tagval-get-by-name TAGDICT TAG-NAME VAL-NAME -> INT
|
||||
; An error is raised if the lookup fails.
|
||||
(define (tagdict-tagval-get-by-name dict tag-name val-name)
|
||||
(assert (tagdict? dict))
|
||||
(cond
|
||||
((assq tag-name (p-tiff-tag-dict-table dict))
|
||||
=> (lambda (table-row)
|
||||
(cond
|
||||
((assq val-name (cddr table-row)) => cdr)
|
||||
(else
|
||||
(error "tagdict-tagval-get-by-name: can't translate "
|
||||
tag-name val-name)))))
|
||||
(else
|
||||
(error "tagdict-tagval-get-by-name: unknown tag: " tag-name))))
|
||||
|
||||
|
||||
; procedure: tagdict-tagval-get-by-num TAGDICT TAG-NAME INT -> VAL-NAME or #f
|
||||
; Translate from a numeric value associated with TAG-NAME in the
|
||||
; TIFF directory.
|
||||
(define (tagdict-tagval-get-by-num dict tag-name val-int)
|
||||
(assert (tagdict? dict))
|
||||
(cond
|
||||
((assq tag-name (p-tiff-tag-dict-table dict))
|
||||
=> (lambda (table-row)
|
||||
(any?
|
||||
(lambda (assc)
|
||||
(and (= val-int (cdr assc)) (car assc)))
|
||||
(cddr table-row))))
|
||||
(else
|
||||
(error "tagdict-tagval-get-by-num: unknown tag: " tag-name))))
|
||||
|
||||
(define tiff-standard-tagdict
|
||||
(make-tagdict
|
||||
'(
|
||||
(TIFFTAG:SUBFILETYPE 254 ; subfile data descriptor
|
||||
(TIFFTAG:REDUCEDIMAGE . #x1) ; reduced resolution version
|
||||
(TIFFTAG:PAGE . #x2) ; one page of many
|
||||
(TIFFTAG:MASK . #x4))
|
||||
(TIFFTAG:OSUBFILETYPE 255 ; +kind of data in subfile
|
||||
(TIFFTAG:IMAGE . 1) ; full resolution image data
|
||||
(TIFFTAG:REDUCEDIMAGE . 2) ; reduced size image data
|
||||
(TIFFTAG:PAGE . 3)) ; one page of many
|
||||
(TIFFTAG:IMAGEWIDTH 256) ; image width in pixels
|
||||
(TIFFTAG:IMAGELENGTH 257) ; image height in pixels
|
||||
(TIFFTAG:BITSPERSAMPLE 258) ; bits per channel (sample)
|
||||
|
||||
(TIFFTAG:COMPRESSION 259 ; data compression technique
|
||||
(NONE . 1) ; dump mode
|
||||
(CCITTRLE . 2) ; CCITT modified Huffman RLE
|
||||
(CCITTFAX3 . 3) ; CCITT Group 3 fax encoding
|
||||
(CCITTFAX4 . 4) ; CCITT Group 4 fax encoding
|
||||
(LZW . 5) ; Lempel-Ziv & Welch
|
||||
(NEXT . 32766) ; NeXT 2-bit RLE
|
||||
(CCITTRLEW . 32771) ; #1 w/ word alignment
|
||||
(PACKBITS . 32773) ; Macintosh RLE
|
||||
(THUNDERSCAN . 32809) ; ThunderScan RLE
|
||||
(PICIO . 32900) ; old Pixar picio RLE
|
||||
(SGIRLE . 32901)) ; Silicon Graphics RLE
|
||||
|
||||
(TIFFTAG:PHOTOMETRIC 262 ; photometric interpretation
|
||||
(MINISWHITE . 0) ; min value is white
|
||||
(MINISBLACK . 1) ; min value is black
|
||||
(RGB . 2) ; RGB color model
|
||||
(PALETTE . 3) ; color map indexed
|
||||
(MASK . 4) ; holdout mask
|
||||
(DEPTH . 32768)) ; z-depth data
|
||||
|
||||
(TIFFTAG:THRESHOLDING 263 ; +thresholding used on data
|
||||
(BILEVEL . 1) ; b&w art scan
|
||||
(HALFTONE . 2) ; or dithered scan
|
||||
(ERRORDIFFUSE . 3)) ; usually floyd-steinberg
|
||||
|
||||
(TIFFTAG:CELLWIDTH 264) ; +dithering matrix width
|
||||
(TIFFTAG:CELLLENGTH 265) ; +dithering matrix height
|
||||
(TIFFTAG:FILLORDER 266 ; +data order within a byte
|
||||
(MSB2LSB . 1) ; most significant -> least
|
||||
(LSB2MSB . 2)) ; least significant -> most
|
||||
|
||||
(TIFFTAG:DOCUMENTNAME 269) ; name of doc. image is from
|
||||
(TIFFTAG:IMAGEDESCRIPTION 270) ; info about image
|
||||
(TIFFTAG:MAKE 271) ; scanner manufacturer name
|
||||
(TIFFTAG:MODEL 272) ; scanner model name/number
|
||||
(TIFFTAG:STRIPOFFSETS 273) ; offsets to data strips
|
||||
|
||||
(TIFFTAG:ORIENTATION 274 ; +image orientation
|
||||
(TOPLEFT . 1) ; row 0 top, col 0 lhs
|
||||
(TOPRIGHT . 2) ; row 0 top, col 0 rhs
|
||||
(BOTRIGHT . 3) ; row 0 bottom, col 0 rhs
|
||||
(BOTLEFT . 4) ; row 0 bottom, col 0 lhs
|
||||
(LEFTTOP . 5) ; row 0 lhs, col 0 top
|
||||
(RIGHTTOP . 6) ; row 0 rhs, col 0 top
|
||||
(RIGHTBOT . 7) ; row 0 rhs, col 0 bottom
|
||||
(LEFTBOT . 8)) ; row 0 lhs, col 0 bottom
|
||||
|
||||
(TIFFTAG:SAMPLESPERPIXEL 277) ; samples per pixel
|
||||
(TIFFTAG:ROWSPERSTRIP 278) ; rows per strip of data
|
||||
(TIFFTAG:STRIPBYTECOUNTS 279) ; bytes counts for strips
|
||||
(TIFFTAG:MINSAMPLEVALUE 280) ; +minimum sample value
|
||||
(TIFFTAG:MAXSAMPLEVALUE 281) ; maximum sample value
|
||||
(TIFFTAG:XRESOLUTION 282) ; pixels/resolution in x
|
||||
(TIFFTAG:YRESOLUTION 283) ; pixels/resolution in y
|
||||
|
||||
(TIFFTAG:PLANARCONFIG 284 ; storage organization
|
||||
(CONTIG . 1) ; single image plane
|
||||
(SEPARATE . 2)) ; separate planes of data
|
||||
|
||||
(TIFFTAG:PAGENAME 285) ; page name image is from
|
||||
(TIFFTAG:XPOSITION 286) ; x page offset of image lhs
|
||||
(TIFFTAG:YPOSITION 287) ; y page offset of image lhs
|
||||
(TIFFTAG:FREEOFFSETS 288) ; +byte offset to free block
|
||||
(TIFFTAG:FREEBYTECOUNTS 289) ; +sizes of free blocks
|
||||
|
||||
(TIFFTAG:GRAYRESPONSEUNIT 290 ; gray scale curve accuracy
|
||||
(S10 . 1) ; tenths of a unit
|
||||
(S100 . 2) ; hundredths of a unit
|
||||
(S1000 . 3) ; thousandths of a unit
|
||||
(S10000 . 4) ; ten-thousandths of a unit
|
||||
(S100000 . 5)) ; hundred-thousandths
|
||||
(TIFFTAG:GRAYRESPONSECURVE 291) ; gray scale response curve
|
||||
|
||||
(TIFFTAG:GROUP3OPTIONS 292 ; 32 flag bits
|
||||
(ENCODING2D . #x1) ; 2-dimensional coding
|
||||
(UNCOMPRESSED . #x2) ; data not compressed
|
||||
(FILLBITS . #x4)) ; fill to byte boundary
|
||||
(TIFFTAG:GROUP4OPTIONS 293 ; 32 flag bits
|
||||
(UNCOMPRESSED . #x2)) ; data not compressed
|
||||
|
||||
(TIFFTAG:RESOLUTIONUNIT 296 ; units of resolutions
|
||||
(NONE . 1) ; no meaningful units
|
||||
(INCH . 2) ; english
|
||||
(CENTIMETER . 3)) ; metric
|
||||
|
||||
(TIFFTAG:PAGENUMBER 297) ; page numbers of multi-page
|
||||
|
||||
(TIFFTAG:COLORRESPONSEUNIT 300 ; color scale curve accuracy
|
||||
(S10 . 1) ; tenths of a unit
|
||||
(S100 . 2) ; hundredths of a unit
|
||||
(S1000 . 3) ; thousandths of a unit
|
||||
(S10000 . 4) ; ten-thousandths of a unit
|
||||
(S100000 . 5)) ; hundred-thousandths
|
||||
|
||||
(TIFFTAG:COLORRESPONSECURVE 301); RGB response curve
|
||||
(TIFFTAG:SOFTWARE 305) ; name & release
|
||||
(TIFFTAG:DATETIME 306) ; creation date and time
|
||||
(TIFFTAG:ARTIST 315) ; creator of image
|
||||
(TIFFTAG:HOSTCOMPUTER 316) ; machine where created
|
||||
(TIFFTAG:PREDICTOR 317) ; prediction scheme w/ LZW
|
||||
(TIFFTAG:WHITEPOINT 318) ; image white point
|
||||
(TIFFTAG:PRIMARYCHROMATICITIES 319) ; primary chromaticities
|
||||
(TIFFTAG:COLORMAP 320) ; RGB map for pallette image
|
||||
(TIFFTAG:BADFAXLINES 326) ; lines w/ wrong pixel count
|
||||
|
||||
(TIFFTAG:CLEANFAXDATA 327 ; regenerated line info
|
||||
(CLEAN . 0) ; no errors detected
|
||||
(REGENERATED . 1) ; receiver regenerated lines
|
||||
(UNCLEAN . 2)) ; uncorrected errors exist
|
||||
|
||||
(TIFFTAG:CONSECUTIVEBADFAXLINES 328); max consecutive bad lines
|
||||
|
||||
(TIFFTAG:MATTEING 32995) ; alpha channel is present
|
||||
)))
|
||||
|
||||
;------------------------------------------------------------------------
|
||||
; TIFF directory entry
|
||||
; a descriptor of a TIFF "item", which can be image data, document description,
|
||||
; time stamp, etc, depending on the tag. Thus an entry has the following
|
||||
; structure:
|
||||
; unsigned short tag;
|
||||
; unsigned short type; // data type: byte, short word, etc.
|
||||
; unsigned long count; // number of items; length in spec
|
||||
; unsigned long val_offset; // byte offset to field data
|
||||
;
|
||||
; The values associated with each entry are disjoint and may appear anywhere
|
||||
; in the file (so long as they are placed on a word boundary).
|
||||
;
|
||||
; Note, If the value takes 4 bytes or less, then it is placed in the offset
|
||||
; field to save space. If the value takes less than 4 bytes, it is
|
||||
; *left*-justified in the offset field.
|
||||
; Note, that it's always *left* justified (stored in the lower bytes)
|
||||
; no matter what the byte order (big- or little- endian) is!
|
||||
; Here's the precise quote from the TIFF 6.0 specification:
|
||||
; "To save time and space the Value Offset contains the Value instead of
|
||||
; pointing to the Value if and only if the Value fits into 4 bytes. If
|
||||
; the Value is shorter than 4 bytes, it is left-justified within the
|
||||
; 4-byte Value Offset, i.e., stored in the lower- numbered
|
||||
; bytes. Whether the Value fits within 4 bytes is determined by the Type
|
||||
; and Count of the field."
|
||||
|
||||
|
||||
; Could be easily implemented as a syntax-rule
|
||||
(define-syntax make-define-env
|
||||
(lambda (form rename name=)
|
||||
(let ((env-name (cadr form))
|
||||
(associations (cddr form))
|
||||
(%begin (rename 'begin))
|
||||
(%define (rename 'define)))
|
||||
`(,%begin
|
||||
(,%define ,env-name ',associations)
|
||||
,@(map
|
||||
(lambda (assc) ; (name val . other fields)
|
||||
`(,%define ,(car assc) ,(cadr assc)))
|
||||
associations)))))
|
||||
|
||||
; values of the 'data type' field
|
||||
(make-define-env
|
||||
TIFF:types
|
||||
(TIFF:type-byte 1 "byte") ; 8-bit unsigned integer
|
||||
(TIFF:type-ascii 2 "ascii str") ; 8-bit bytes w/ last byte null
|
||||
(TIFF:type-short 3 "short") ; 16-bit unsigned integer
|
||||
(TIFF:type-long 4 "long") ; 32-bit unsigned integer
|
||||
(TIFF:type-rational 5 "rational") ; 64-bit fractional (numer+denominator)
|
||||
; The following was added in TIFF 6.0
|
||||
(TIFF:type-sbyte 6 "signed byte") ; 8-bit signed (2s-complement) integer
|
||||
(TIFF:type-undefined 7 "8-bit chunk") ; An 8-bit byte
|
||||
(TIFF:type-sshort 8 "signed short"); 16-bit signed (2s-complement) integer
|
||||
(TIFF:type-slong 9 "signed long") ; 32-bit signed (2s-complement) integer
|
||||
(TIFF:type-srational 10 "signed rational") ; two SLONGs (num+denominator)
|
||||
(TIFF:type-float 11 "IEEE 32-bit float") ; single precision (4-byte)
|
||||
(TIFF:type-double 12 "IEEE 64-bit double") ; double precision (8-byte)
|
||||
)
|
||||
|
||||
(define-structure tiff-dir-entry tag type count val-offset value)
|
||||
|
||||
; procedure: TIFF:read-dir-entry EPORT -> TIFF-DIR-ENTRY
|
||||
;
|
||||
; This procedure parses the current directory entry and
|
||||
; returns a tiff-dir-entry structure. EPORT must point to the beginning
|
||||
; of the entry in the TIFF directory. On exit, EPORT points to the
|
||||
; next entry or the end of the directory.
|
||||
; TIFF-DIR-ENTRY contains all the data of the entry, plus a promise
|
||||
; of entry's value.
|
||||
; The promise is forced only when the value is specifically requested
|
||||
; by an object's user. That is, we won't rush to read and make the
|
||||
; value (which may be pretty big: say the pixel matrix, etc).
|
||||
;
|
||||
; The promise closes over the EPORT!
|
||||
;
|
||||
; The value of an entry corresponds to its type: character, string,
|
||||
; exact integer, floating-point number, or a uniform
|
||||
; (u8, u16, or u32) vector. SRFI-4 is implied.
|
||||
|
||||
(define (TIFF:read-dir-entry eport)
|
||||
(let*
|
||||
((tag (endian-port-read-int2 eport))
|
||||
(type (endian-port-read-int2 eport))
|
||||
(count (endian-port-read-int4 eport))
|
||||
; we read the val-offset later. We need to check the size and the type
|
||||
; of the datum, because val-offset may contain the value itself,
|
||||
; in its lower-numbered bytes, regardless of the big/little endian
|
||||
; order!
|
||||
)
|
||||
; Some conversion procedures
|
||||
(define (u32->float x) ; unsigned 32-bit int -> IEEE float
|
||||
(error "u32->float is not yet implemented"))
|
||||
(define (u32->s32 x) ; unsigned 32-bit int -> signed 32 bit
|
||||
(if (>= x #x80000000)
|
||||
(- x #x100000000)
|
||||
x))
|
||||
; (= (u32->s32 #x7fffffff) #x7fffffff)
|
||||
; (= (u32->s32 #xffffffff) -1)
|
||||
|
||||
(define (u16->s16 x) ; unsigned 16-bit int -> signed 16 bit
|
||||
(if (>= x #x8000)
|
||||
(- x #x10000) x))
|
||||
; (= (u16->s16 32767) 32767)
|
||||
; (= (u16->s16 32768) -32768)
|
||||
; (= (u16->s16 65535) -1)
|
||||
|
||||
(define (u8->s8 x) ; unsigned 8-bit int -> signed 8-bit
|
||||
(if (>= x #x80)
|
||||
(- x #x100) x))
|
||||
; (= (u8->s8 127) 127)
|
||||
; (= (u8->s8 128) -128)
|
||||
; (= (u8->s8 255) -1)
|
||||
|
||||
(define (read-double val-offset)
|
||||
(error "read-double: not yet implemented"))
|
||||
|
||||
; read an ascii string. Note, the last byte of
|
||||
; an ascii string is always zero, which is
|
||||
; included in 'count'
|
||||
; but we don't need to read it
|
||||
(define (read-string val-offset)
|
||||
(assert (= TIFF:type-ascii type) (positive? count))
|
||||
(let ((str (make-string (- count 1))))
|
||||
(endian-port-setpos eport val-offset)
|
||||
(do ((i 0 (+ 1 i))) ((>= i (- count 1)) str)
|
||||
(string-set! str i
|
||||
(ascii->char (endian-port-read-int1 eport))))))
|
||||
|
||||
|
||||
; read an array of 'count' items
|
||||
; return a *uniform* vector of read data:
|
||||
; u8vector, u16vector or u32vector
|
||||
; We roll-out the code for efficiency
|
||||
(define (read-array val-offset)
|
||||
(endian-port-setpos eport val-offset)
|
||||
(cond
|
||||
((or (= type TIFF:type-byte) (= type TIFF:type-undefined))
|
||||
(let ((array (make-u8vector count)))
|
||||
(do ((i 0 (+ 1 i))) ((>= i count) array)
|
||||
(u8vector-set! array i (endian-port-read-int1 eport)))))
|
||||
((= type TIFF:type-short)
|
||||
(let ((array (make-u16vector count)))
|
||||
(do ((i 0 (+ 1 i))) ((>= i count) array)
|
||||
(u16vector-set! array i (endian-port-read-int2 eport)))))
|
||||
((= type TIFF:type-long)
|
||||
(let ((array (make-u32vector count)))
|
||||
(do ((i 0 (+ 1 i))) ((>= i count) array)
|
||||
(u32vector-set! array i (endian-port-read-int4 eport)))))
|
||||
(else (error "don't know how to read an array "
|
||||
"of type " type))))
|
||||
|
||||
; Now we need to figure out if val-offset contains the offset
|
||||
; or the value (or a part of the value). If val-offset contains the
|
||||
; value, we read it in val-offset and make the value a trivial promise
|
||||
; (delay val-offset).
|
||||
; If val-offset is an offset, then value is a non-trivial promise
|
||||
; (which closes over EPORT).
|
||||
(assert (positive? count))
|
||||
(let*-values
|
||||
(((val-offset value)
|
||||
(cond
|
||||
((> count 4) ; for sure, val-offset is an offset
|
||||
(let ((offset (endian-port-read-int4 eport)))
|
||||
(if (= type TIFF:type-ascii)
|
||||
(values offset (delay (read-string offset)))
|
||||
(values offset (delay (read-array offset))))))
|
||||
((> count 1) ; the iffy case
|
||||
(cond
|
||||
((and (= count 2) (= type TIFF:type-short))
|
||||
(let* ((v1 (endian-port-read-int2 eport))
|
||||
(v2 (endian-port-read-int2 eport))
|
||||
(v (u16vector v1 v2)))
|
||||
(values v (delay v))))
|
||||
((and (= count 2) (= type TIFF:type-ascii)) ; 1-char string
|
||||
(let ((v
|
||||
(string (ascii->char (endian-port-read-int1 eport)))))
|
||||
(endian-port-read-int1 eport) ; don't read '\0'
|
||||
(endian-port-read-int1 eport) ; skip two more zeros:
|
||||
(endian-port-read-int1 eport) ; padding
|
||||
(values v (delay v))))
|
||||
((and (= count 2) (or (= type TIFF:type-byte)
|
||||
(= type TIFF:type-undefined)))
|
||||
(let* ((v1 (endian-port-read-int1 eport))
|
||||
(v2 (endian-port-read-int1 eport))
|
||||
(v (u8vector v1 v2)))
|
||||
(endian-port-read-int1 eport) ; skip two more zeros:
|
||||
(endian-port-read-int1 eport) ; padding
|
||||
(values v (delay v))))
|
||||
((and (= count 3) (= type TIFF:type-ascii)) ; 2-char string
|
||||
(let* ((v1 (endian-port-read-int1 eport))
|
||||
(v2 (endian-port-read-int1 eport))
|
||||
(v (string (ascii->char v1) (ascii->char v2))))
|
||||
(endian-port-read-int1 eport) ; skip two more zeros:
|
||||
(endian-port-read-int1 eport) ; padding
|
||||
(values v (delay v))))
|
||||
((and (= count 3) (or (= type TIFF:type-byte)
|
||||
(= type TIFF:type-undefined)))
|
||||
(let* ((v1 (endian-port-read-int1 eport))
|
||||
(v2 (endian-port-read-int1 eport))
|
||||
(v3 (endian-port-read-int1 eport))
|
||||
(v (u8vector v1 v2 v3)))
|
||||
(endian-port-read-int1 eport) ; skip padding
|
||||
(values v (delay v))))
|
||||
((and (= count 4) (= type TIFF:type-ascii)) ; 3-char string
|
||||
(let* ((v1 (endian-port-read-int1 eport))
|
||||
(v2 (endian-port-read-int1 eport))
|
||||
(v3 (endian-port-read-int1 eport))
|
||||
(v (string (ascii->char v1) (ascii->char v2)
|
||||
(ascii->char v3))))
|
||||
(endian-port-read-int1 eport) ; skip padding
|
||||
(values v (delay v))))
|
||||
((and (= count 4) (or (= type TIFF:type-byte)
|
||||
(= type TIFF:type-undefined)))
|
||||
(let* ((v1 (endian-port-read-int1 eport))
|
||||
(v2 (endian-port-read-int1 eport))
|
||||
(v3 (endian-port-read-int1 eport))
|
||||
(v4 (endian-port-read-int1 eport))
|
||||
(v (u8vector v1 v2 v3 v4)))
|
||||
(values v (delay v))))
|
||||
(else
|
||||
(let ((offset (endian-port-read-int4 eport)))
|
||||
(if (= type TIFF:type-ascii)
|
||||
(values offset (delay (read-string offset)))
|
||||
(values offset (delay (read-array offset))))))))
|
||||
; Now count is 1
|
||||
((or (= type TIFF:type-byte) (= type TIFF:type-undefined)
|
||||
(= type TIFF:type-sbyte))
|
||||
(let ((v1 (endian-port-read-int1 eport)))
|
||||
(endian-port-read-int1 eport) ; skip the padding
|
||||
(endian-port-read-int1 eport)
|
||||
(endian-port-read-int1 eport)
|
||||
(values v1 (delay
|
||||
(if (= type TIFF:type-sbyte) (u8->s8 v1) v1)))))
|
||||
((= type TIFF:type-ascii) ; 0-byte string: count=1 for terminator
|
||||
(endian-port-read-int1 eport) ; skip the padding
|
||||
(endian-port-read-int1 eport)
|
||||
(endian-port-read-int1 eport)
|
||||
(endian-port-read-int1 eport)
|
||||
(values "" (delay "")))
|
||||
((or (= type TIFF:type-short) (= type TIFF:type-sshort))
|
||||
(let ((v1 (endian-port-read-int2 eport)))
|
||||
(endian-port-read-int1 eport) ; skip the padding
|
||||
(endian-port-read-int1 eport)
|
||||
(values v1 (delay
|
||||
(if (= type TIFF:type-sshort) (u16->s16 v1) v1)))))
|
||||
((= type TIFF:type-long)
|
||||
(let ((v1 (endian-port-read-int4 eport)))
|
||||
(values v1 (delay v1))))
|
||||
((= type TIFF:type-slong)
|
||||
(let ((v1 (endian-port-read-int4 eport)))
|
||||
(values v1 (delay (u32->s32 v1)))))
|
||||
((= type TIFF:type-float)
|
||||
(let ((v1 (endian-port-read-int4 eport)))
|
||||
(values v1 (delay (u32->float v1)))))
|
||||
((= type TIFF:type-double)
|
||||
(let ((offset (endian-port-read-int4 eport)))
|
||||
(values offset (delay (read-double offset)))))
|
||||
((or (= type TIFF:type-rational) (= type TIFF:type-srational))
|
||||
(let ((offset (endian-port-read-int4 eport)))
|
||||
(values offset
|
||||
(delay
|
||||
(let* ((_ (endian-port-setpos eport offset))
|
||||
(v1 (endian-port-read-int4 eport))
|
||||
(v2 (endian-port-read-int4 eport)))
|
||||
(if (= type TIFF:type-srational)
|
||||
(/ (u32->s32 v1) (u32->s32 v2))
|
||||
(/ v1 v2)))))))
|
||||
(else (delay (error "unknown data type: " type))))))
|
||||
(make-tiff-dir-entry tag type count val-offset value)
|
||||
)))
|
||||
|
||||
; procedure: print-tiff-dir-entry TIFF-DIR-ENTRY TAGDICT OPORT -> UNSPECIFIED
|
||||
;
|
||||
; Print the contents of TIFF-DIR-ENTRY onto the output port OPORT
|
||||
; using TAGDICT to convert tag identifiers to symbolic names
|
||||
(define (print-tiff-dir-entry tiff-dir-entry tagdict oport)
|
||||
(define (dspl . args) (for-each (lambda (item) (display item oport)) args))
|
||||
(let* ((tag-num (tiff-dir-entry-tag tiff-dir-entry))
|
||||
(tag-symbol (tagdict-get-by-num tagdict tag-num)))
|
||||
(dspl
|
||||
(or tag-symbol
|
||||
(string-append "private tag " (number->string tag-num))))
|
||||
(dspl ", count " (tiff-dir-entry-count tiff-dir-entry)
|
||||
", type ")
|
||||
(let ((type-str
|
||||
(any?
|
||||
(lambda (elem)
|
||||
(and (= (cadr elem) (tiff-dir-entry-type tiff-dir-entry))
|
||||
(caddr elem)))
|
||||
TIFF:types)))
|
||||
(if type-str
|
||||
(dspl type-str)
|
||||
(dspl "unknown (" (tiff-dir-entry-type tiff-dir-entry) ")")))
|
||||
(let ((val-offset (tiff-dir-entry-val-offset tiff-dir-entry)))
|
||||
(dspl ", value-offset " val-offset)
|
||||
(if (integer? val-offset)
|
||||
(dspl " (0x" (number->string val-offset 16) ")")))
|
||||
(dspl nl)))
|
||||
|
||||
|
||||
;------------------------------------------------------------------------
|
||||
; TIFF Image File Directory
|
||||
; TIFF directory is a collection of TIFF directory entries. The entries
|
||||
; are sorted in an ascending order by tag.
|
||||
; Note, a TIFF file can contain more than one directory (chained together).
|
||||
; We handle only the first one.
|
||||
;
|
||||
; We treat a TIFF image directory somewhat as an ordered, immutable,
|
||||
; dictionary collection, see SRFI-44.
|
||||
|
||||
; http://srfi.schemers.org/srfi-44/srfi-44.html
|
||||
|
||||
(define-structure tiff-directory entries tagdict)
|
||||
|
||||
; ; procedure: collection-name collection => symbol ('%)
|
||||
; (define (collection-name coll)
|
||||
; (and (tiff-directory? coll) 'tiff-directory))
|
||||
|
||||
; ; collection? value => value
|
||||
; (define collection? tiff-directory?)
|
||||
|
||||
; ; procedure: tiff-directory? value => bool
|
||||
; ; implied by the define-structure
|
||||
|
||||
; ; *-size collection => integer
|
||||
(define (tiff-directory-size coll)
|
||||
(vector-length (tiff-directory-entries coll)))
|
||||
|
||||
; (define (mutable-collection? coll) #f)
|
||||
; (define (dictionary? coll) #t)
|
||||
|
||||
(define (tiff-directory-empty? coll)
|
||||
(zero? (vector-length (tiff-directory-entries coll))))
|
||||
|
||||
|
||||
; tiff-directory-fold-left tiff-directory fold-function seed-value
|
||||
; ... => seed-value ...
|
||||
; The fold function receives a tiff-directory-entry as a value
|
||||
|
||||
(define (tiff-directory-fold-left coll fn . seeds)
|
||||
(let ((entries (tiff-directory-entries coll)))
|
||||
(let loop ((i 0) (seeds seeds))
|
||||
(if (>= i (vector-length entries))
|
||||
(apply values seeds)
|
||||
(let*-values
|
||||
(((proceed? . seeds) (apply fn (vector-ref entries i) seeds)))
|
||||
(loop (if proceed? (+ 1 i) (vector-length entries))
|
||||
seeds))))))
|
||||
|
||||
; procedure: collection-fold-keys-left collection fold-function
|
||||
; seed-value ... => seed-value ...
|
||||
; *-keys->list dictionary => list
|
||||
|
||||
|
||||
; read-tiff-file EPORT [PRIVATE-TAGDICT] => TIFF-DIRECTORY
|
||||
(define (read-tiff-file eport . tag-dict-opt)
|
||||
(endian-port-setpos eport (TIFF:read-header eport))
|
||||
(let ((entries (make-vector (endian-port-read-int2 eport)))
|
||||
(tagdict (if (null? tag-dict-opt)
|
||||
tiff-standard-tagdict
|
||||
(tagdict-add-all tiff-standard-tagdict
|
||||
(car tag-dict-opt)))))
|
||||
(do ((i 0 (+ 1 i))) ((>= i (vector-length entries)))
|
||||
(vector-set! entries i (TIFF:read-dir-entry eport)))
|
||||
(if (not (zero? (endian-port-read-int4 eport)))
|
||||
(cerr "The TIFF file contains several images, only the first one "
|
||||
"will be considered" nl))
|
||||
(make-tiff-directory entries tagdict)))
|
||||
|
||||
|
||||
; print-tiff-directory TIFF-DIRECTORY OPORT -> UNSPECIFIED
|
||||
(define (print-tiff-directory tiff-directory oport)
|
||||
(let*
|
||||
((entries (tiff-directory-entries tiff-directory))
|
||||
(nentries (vector-length entries))
|
||||
(tagdict (tiff-directory-tagdict tiff-directory)))
|
||||
(for-each (lambda (item) (display item oport))
|
||||
(list
|
||||
"There are " nentries " entries in the TIFF directory" nl
|
||||
"they are" nl))
|
||||
(do ((i 0 (+ 1 i))) ((>= i nentries))
|
||||
(print-tiff-dir-entry (vector-ref entries i) tagdict oport))))
|
||||
|
||||
|
||||
; *-get dictionary key [absence-thunk] => value
|
||||
; key can be either a symbol or an integer
|
||||
; tiff-directory-get TIFF-DIRECTORY KEY [ABSENCE-THUNK] -> VALUE
|
||||
; If the lookup fails, ABSENCE-THUNK, if given, is evaluated and its value
|
||||
; is returned. If ABSENCE-THUNK is omitted, the return value on failure
|
||||
; is #f.
|
||||
(define (tiff-directory-get coll key . default-val)
|
||||
(let*
|
||||
((key
|
||||
(cond
|
||||
((symbol? key)
|
||||
(tagdict-get-by-name (tiff-directory-tagdict coll) key))
|
||||
((integer? key) key)
|
||||
(else (error "tiff-directory-get: bad type of key: " key))))
|
||||
(entry
|
||||
; look up the entry in the directory of entries
|
||||
; We could have used a binary search... On the other hand,
|
||||
; the directory is usually not that big, so that binary
|
||||
; search is kind of overkill
|
||||
(any?
|
||||
(lambda (curr-elem)
|
||||
(and (= (tiff-dir-entry-tag curr-elem) key) curr-elem))
|
||||
(tiff-directory-entries coll)))
|
||||
)
|
||||
(if entry
|
||||
(force (tiff-dir-entry-value entry))
|
||||
(and (not (null? default-val)) ((car default-val))))))
|
||||
|
||||
|
||||
; tiff-directory-get-as-symbol TIFF-DIRECTORY KEY [ABSENCE-THUNK] -> VALUE
|
||||
; KEY must be a symbol
|
||||
; If it is possible, the VALUE is returned as a symbol, as translated
|
||||
; by the tagdict.
|
||||
(define (tiff-directory-get-as-symbol coll key . default-val)
|
||||
(let ((val (tiff-directory-get coll key)))
|
||||
(if val
|
||||
(if (integer? val)
|
||||
(let ((val-symbol
|
||||
(tagdict-tagval-get-by-num
|
||||
(tiff-directory-tagdict coll) key val)))
|
||||
(or val-symbol val))
|
||||
val) ; val is not an integer: don't translate
|
||||
(and (not (null? default-val)) ((car default-val)))
|
||||
)))
|
|
@ -0,0 +1,176 @@
|
|||
#! /usr/local/bin/scsh \
|
||||
-ll sunterlib.scm -m tiff-testbed -s
|
||||
!#
|
||||
|
||||
;****************************************************************************
|
||||
; Validate the TIFF reading package
|
||||
;
|
||||
; We test reading of a known TIFF file, print out its directory.
|
||||
; We also test an internal consistency of the package.
|
||||
;
|
||||
; Derived from vtiff.scm 1.1 2003/09/29 20:41:51 oleg
|
||||
|
||||
; Note: some tests below depend on the exact parameters
|
||||
; of the following sample file
|
||||
; The file is a GNU logo (from http://www.gnu.org)
|
||||
; converted from JPG to TIFF
|
||||
|
||||
(define sample-tiff-file "gnu-head-sm.tif")
|
||||
|
||||
(cerr nl "Verifying the TIFF library" nl)
|
||||
|
||||
(cerr nl "Verifying tagdict operations..." nl)
|
||||
(let ()
|
||||
(assert
|
||||
(= 256 (tagdict-get-by-name tiff-standard-tagdict 'TIFFTAG:IMAGEWIDTH)))
|
||||
(assert (eq? 'TIFFTAG:IMAGEWIDTH
|
||||
(tagdict-get-by-num tiff-standard-tagdict 256)))
|
||||
(assert (eq? #f
|
||||
(tagdict-get-by-num tiff-standard-tagdict 65500)))
|
||||
(assert (= 5
|
||||
(tagdict-tagval-get-by-name tiff-standard-tagdict
|
||||
'TIFFTAG:COMPRESSION 'LZW)))
|
||||
(assert (eq? 'LZW
|
||||
(tagdict-tagval-get-by-num tiff-standard-tagdict
|
||||
'TIFFTAG:COMPRESSION 5)))
|
||||
(assert (eq? #f
|
||||
(tagdict-tagval-get-by-num tiff-standard-tagdict
|
||||
'TIFFTAG:COMPRESSION 65500)))
|
||||
|
||||
(let ((ext-dict
|
||||
(tagdict-add-all tiff-standard-tagdict
|
||||
(make-tagdict
|
||||
'((WAupper_left_lat 33004)
|
||||
(WAhemisphere 33003 (North . 1) (South . 2)))))))
|
||||
(assert (= 33004 (tagdict-get-by-name ext-dict 'WAupper_left_lat)))
|
||||
(assert (eq? 'WAupper_left_lat
|
||||
(tagdict-get-by-num ext-dict 33004)))
|
||||
(assert (eq? 'TIFFTAG:PHOTOMETRIC (tagdict-get-by-num ext-dict 262)))
|
||||
(assert (eq? #f
|
||||
(tagdict-tagval-get-by-num ext-dict 'WAupper_left_lat 0)))
|
||||
(assert (= 1
|
||||
(tagdict-tagval-get-by-name ext-dict 'WAhemisphere 'North)))
|
||||
(assert (eq? 'South
|
||||
(tagdict-tagval-get-by-num ext-dict 'WAhemisphere 2))))
|
||||
)
|
||||
|
||||
(define (test-dictionary-consistency tiff-dict)
|
||||
(cerr nl "Verifying the consistency of dictionary operations ..." nl)
|
||||
(assert (tiff-directory? tiff-dict))
|
||||
(assert (positive? (tiff-directory-size tiff-dict))
|
||||
(not (tiff-directory-empty? tiff-dict)))
|
||||
(assert (=
|
||||
(tiff-directory-get tiff-dict 'TIFFTAG:IMAGEWIDTH)
|
||||
(tiff-directory-get tiff-dict 256)))
|
||||
(assert (eq? #f (tiff-directory-get tiff-dict 65500)))
|
||||
(let ((not-given (list 'not-given)))
|
||||
(assert (eq? not-given (tiff-directory-get tiff-dict 65500
|
||||
(lambda () not-given)))))
|
||||
(let ((size (tiff-directory-size tiff-dict)))
|
||||
(call-with-values
|
||||
(lambda ()
|
||||
(tiff-directory-fold-left tiff-dict
|
||||
(lambda (el count) (values #t (+ 1 count))) 0))
|
||||
(lambda (size-via-fold)
|
||||
(assert (= size size-via-fold)))))
|
||||
(let*-values
|
||||
(((len) (tiff-directory-get tiff-dict 'TIFFTAG:IMAGELENGTH))
|
||||
((len-via-fold prev-count)
|
||||
(tiff-directory-fold-left tiff-dict
|
||||
(lambda (dir-entry found prev-count)
|
||||
(if (= (tiff-dir-entry-tag dir-entry) 257)
|
||||
(values #f (force (tiff-dir-entry-value dir-entry))
|
||||
prev-count) ; and terminate now
|
||||
(values #t #f (+ 1 prev-count))))
|
||||
#f 0)))
|
||||
(assert (= len len-via-fold)
|
||||
(< 0 prev-count (tiff-directory-size tiff-dict))))
|
||||
)
|
||||
|
||||
(define (test-known-values-from-dict tiff-dict)
|
||||
(cerr nl "Getting sample data from the dictionary ")
|
||||
(let
|
||||
((known-values
|
||||
'((TIFFTAG:IMAGEWIDTH . 129)
|
||||
(TIFFTAG:IMAGELENGTH . 122)
|
||||
(TIFFTAG:BITSPERSAMPLE . 8)
|
||||
(TIFFTAG:IMAGEDESCRIPTION . "JPEG:gnu-head-sm.jpg 129x122")
|
||||
(TIFFTAG:COMPRESSION . 1)
|
||||
(TIFFTAG:SAMPLESPERPIXEL . 1)
|
||||
(TIFFTAG:STRIPBYTECOUNTS . 15738) ; the product of width and length
|
||||
(TIFFTAG:XRESOLUTION . 72)
|
||||
(TIFFTAG:CLEANFAXDATA . #f))))
|
||||
(for-each
|
||||
(lambda (tag-val)
|
||||
(cerr "Tag " (car tag-val) "...")
|
||||
(let ((real (tiff-directory-get tiff-dict (car tag-val))))
|
||||
(cerr real nl)
|
||||
(assert (equal? real (cdr tag-val)))))
|
||||
known-values
|
||||
))
|
||||
(assert (eq? 'NONE
|
||||
(tiff-directory-get-as-symbol tiff-dict
|
||||
'TIFFTAG:COMPRESSION)))
|
||||
)
|
||||
|
||||
|
||||
(define (test-reading-pixel-matrix tiff-dict eport)
|
||||
(cerr nl "Reading the pixel matrix and spot-checking it ...")
|
||||
; Make sure we can handle this particular TIFF image
|
||||
; No compression
|
||||
(assert (eq? 'NONE
|
||||
(tiff-directory-get-as-symbol tiff-dict
|
||||
'TIFFTAG:COMPRESSION)))
|
||||
(assert (= 1 (tiff-directory-get tiff-dict 'TIFFTAG:SAMPLESPERPIXEL)))
|
||||
(assert (= 8 (tiff-directory-get tiff-dict 'TIFFTAG:BITSPERSAMPLE)))
|
||||
|
||||
(let*
|
||||
((ncols (tiff-directory-get tiff-dict 'TIFFTAG:IMAGEWIDTH))
|
||||
(_ (assert (number? ncols) (positive? ncols)))
|
||||
(nrows (tiff-directory-get tiff-dict 'TIFFTAG:IMAGELENGTH))
|
||||
(_ (assert (number? nrows) (positive? nrows)))
|
||||
(rows-per-strip (tiff-directory-get tiff-dict 'TIFFTAG:ROWSPERSTRIP
|
||||
(lambda () nrows)))
|
||||
(_ (assert (positive? rows-per-strip)))
|
||||
(strip-offsets (tiff-directory-get tiff-dict 'TIFFTAG:STRIPOFFSETS
|
||||
(lambda () (error "STRIPOFFSETS must be present!"))))
|
||||
; make it a u32vector
|
||||
(strip-offsets
|
||||
(cond
|
||||
((u32vector? strip-offsets) strip-offsets)
|
||||
((u16vector? strip-offsets)
|
||||
(list->u32vector (u16vector->list strip-offsets)))
|
||||
(else (u32vector strip-offsets))))
|
||||
(image-size (* nrows ncols))
|
||||
(strip-size (* rows-per-strip ncols))
|
||||
(image (make-u8vector image-size 0))
|
||||
)
|
||||
(cerr nl "Loading the image matrix of the size " image-size
|
||||
" bytes...")
|
||||
(let outer ((strip 0) (i 0))
|
||||
(if (>= strip (u32vector-length strip-offsets)) #f
|
||||
(let ((i-end (min (+ i strip-size) image-size)))
|
||||
(endian-port-setpos eport (u32vector-ref strip-offsets strip))
|
||||
(let inner ((i i))
|
||||
(if (>= i i-end) (outer (++ strip) i)
|
||||
(begin
|
||||
(u8vector-set! image i (endian-port-read-int1 eport))
|
||||
(inner (++ i))))))))
|
||||
(assert (= 255 (u8vector-ref image 0))
|
||||
(= 248 (u8vector-ref image 17)))
|
||||
;(display image)
|
||||
))
|
||||
|
||||
|
||||
|
||||
(cerr nl "Reading the sample TIFF file " sample-tiff-file "..." nl)
|
||||
(let* ((eport (make-endian-port (open-input-file sample-tiff-file) #t))
|
||||
(tiff-dict (read-tiff-file eport)))
|
||||
(print-tiff-directory tiff-dict (current-output-port))
|
||||
(test-known-values-from-dict tiff-dict)
|
||||
(test-dictionary-consistency tiff-dict)
|
||||
(test-reading-pixel-matrix tiff-dict eport)
|
||||
)
|
||||
|
||||
|
||||
(cerr nl "All tests passed" nl)
|
Loading…
Reference in New Issue