|
|
|
#lang debug racket/base
|
|
|
|
(require
|
|
|
|
racket/match
|
|
|
|
"reference.rkt"
|
|
|
|
"core.rkt"
|
|
|
|
racket/dict
|
|
|
|
sugar/unstable/dict)
|
|
|
|
|
|
|
|
#|
|
|
|
|
https://github.com/mbutterick/pdfkit/blob/master/lib/image/jpeg.coffee
|
|
|
|
|#
|
|
|
|
|
|
|
|
(provide make-jpeg (struct-out $jpeg))
|
|
|
|
|
|
|
|
(define MARKERS '(#xffc0 #xffc1 #xffc2 #xffc3
|
|
|
|
#xffc5 #xffc6 #xffc7
|
|
|
|
#xffc8 #xffc9 #xffca #xffcb
|
|
|
|
#xffcc #xffcd #xffce #xffcf))
|
|
|
|
|
|
|
|
(struct $jpeg $img (bits channels colorSpace) #:transparent #:mutable)
|
|
|
|
|
|
|
|
(define (make-jpeg data [label #f])
|
|
|
|
|
|
|
|
(define jpeg-ip (if (input-port? data) data (open-input-bytes data)))
|
|
|
|
(unless (= (read-16bit-integer jpeg-ip) #xffd8)
|
|
|
|
(error 'JPEG "Start of input marker byte not found"))
|
|
|
|
(define marker (let loop ([skip 0])
|
|
|
|
(read-bytes skip jpeg-ip)
|
|
|
|
(define m (read-16bit-integer jpeg-ip))
|
|
|
|
(if (memv m MARKERS)
|
|
|
|
m
|
|
|
|
(loop (read-16bit-integer (peek-bytes 2 0 jpeg-ip))))))
|
|
|
|
(read-16bit-integer jpeg-ip)
|
|
|
|
(define bits (read-byte jpeg-ip))
|
|
|
|
(define height (read-16bit-integer jpeg-ip))
|
|
|
|
(define width (read-16bit-integer jpeg-ip))
|
|
|
|
(define channels (read-byte jpeg-ip))
|
|
|
|
(define colorSpace (case channels
|
|
|
|
[(1) 'DeviceGray]
|
|
|
|
[(3) 'DeviceRGB]
|
|
|
|
[(4) 'DeviceCMYK]))
|
|
|
|
(define obj #f)
|
|
|
|
($jpeg data label width height obj jpeg-embed bits channels colorSpace))
|
|
|
|
|
|
|
|
(define (jpeg-embed jpeg)
|
|
|
|
(unless ($img-ref jpeg)
|
|
|
|
(set-$img-ref! jpeg
|
|
|
|
(make-ref
|
|
|
|
(mhash
|
|
|
|
'Type 'XObject
|
|
|
|
'Subtype 'Image
|
|
|
|
'BitsPerComponent ($jpeg-bits jpeg)
|
|
|
|
'Width ($img-width jpeg)
|
|
|
|
'Height ($img-height jpeg)
|
|
|
|
'ColorSpace ($jpeg-colorSpace jpeg)
|
|
|
|
'Filter 'DCTDecode)))
|
|
|
|
|
|
|
|
;; add extra decode params for CMYK images. By swapping the
|
|
|
|
;; min and max values from the default, we invert the colors. See
|
|
|
|
;; section 4.8.4 of the spec.
|
|
|
|
(when (eq? ($jpeg-colorSpace jpeg) 'DeviceCMYK)
|
|
|
|
(dict-set! ($img-ref jpeg) 'Decode '(1.0 0.0 1.0 0.0 1.0 0.0 1.0 0.0)))
|
|
|
|
(file-position ($img-data jpeg) 0)
|
|
|
|
(ref-write ($img-ref jpeg) ($img-data jpeg))
|
|
|
|
(ref-end ($img-ref jpeg))))
|
|
|
|
|
|
|
|
(define (read-16bit-integer ip-or-bytes)
|
|
|
|
(define signed #f) (define big-endian #t)
|
|
|
|
(integer-bytes->integer (read-bytes 2 (match ip-or-bytes
|
|
|
|
[(? bytes? bs) (open-input-bytes bs)]
|
|
|
|
[ip ip])) signed big-endian))
|
|
|
|
|
|
|
|
|
|
|
|
(module+ test
|
|
|
|
(require rackunit)
|
|
|
|
(check-equal? (number->string (read-16bit-integer (bytes #x12 #x34 #x56)) 16) "1234")
|
|
|
|
(define my-jpeg (make-jpeg (open-input-file "../ptest/assets/test.jpeg")))
|
|
|
|
(check-equal? ($img-height my-jpeg) 533)
|
|
|
|
(check-equal? ($img-width my-jpeg) 400)
|
|
|
|
(check-equal? ($jpeg-channels my-jpeg) 3)
|
|
|
|
(check-equal? ($jpeg-colorSpace my-jpeg) 'DeviceRGB))
|