
normalize the internal representation of bitmaps so it always uses an alpha bitmap instead of sometimes using a mask bitmap and sometimes using alpha. This also fixes a bug where the library would get consfused when it saved a bitmap to a file, since it didn't record if it was an alpha bitmap or not. This improves the save files that contain images, cutting the size for bitmaps in half (bringing the drracket save file down to a mere 25x larger than the png file format for the example I was using...) original commit: 1f02106318edba9c011afaf24d6ef34a3081c9ff
1273 lines
48 KiB
Racket
1273 lines
48 KiB
Racket
#lang racket/base
|
|
|
|
#|
|
|
|
|
This library is the part of the 2htdp/image
|
|
teachpack that has to be shared between drracket
|
|
and the user's program to make copy and paste
|
|
work right.
|
|
|
|
Most of the exports are just for use in 2htdp/image
|
|
(technically, 2htdp/private/image-more). The main
|
|
use of this library is the snip class addition it
|
|
does (and any code that does not depend on
|
|
has been moved out).
|
|
|
|
|
|
-- in the middle of text:
|
|
|
|
- bounding boxes
|
|
- rotating (and bounding boxes)
|
|
- hbl append(?)
|
|
- this doesn't work (how to test?)
|
|
(beside/places "baseline"
|
|
(text "ijy" 12 'black)
|
|
(text "ijy" 24 'black))
|
|
- /places => /align
|
|
|
|
|#
|
|
|
|
(require racket/class
|
|
racket/draw
|
|
(for-syntax racket/base)
|
|
file/convertible
|
|
racket/math
|
|
racket/contract
|
|
"private/image-core-bitmap.rkt"
|
|
"image-core-wxme.rkt"
|
|
"private/image-core-snipclass.rkt"
|
|
"private/regmk.rkt"
|
|
racket/snip
|
|
(prefix-in cis: "cache-image-snip.rkt"))
|
|
|
|
|
|
|
|
|
|
;
|
|
;
|
|
;
|
|
;
|
|
;
|
|
;
|
|
; ;; ;; ;; ;;;
|
|
; ;; ;;; ;; ;;;
|
|
; ;;;;; ;;;; ;;;;; ;;;; ;;;;; ;;;; ;;;;; ;; ;;;
|
|
; ;;;;;; ;; ;; ;;;; ;; ;; ;;;;;; ;; ;; ;;;; ;;;;;;
|
|
; ;;; ;; ;;;; ;;; ;;;; ;;; ;; ;;;;;;;; ;; ;; ;;
|
|
; ;;; ;; ;;; ;; ;;; ;;; ;; ;;; ;; ;;; ;; ;; ;;
|
|
; ;;;;;; ;;; ;; ;;;;;;; ;; ;;;;;; ;;; ;; ;; ;; ;;
|
|
; ;;;;; ;;;;;; ;;; ;;;;;; ;;;;; ;;;; ;; ;; ;;
|
|
;
|
|
;
|
|
;
|
|
;
|
|
|
|
|
|
;; a image is
|
|
;; (make-image shape bb boolean (or/c point #f))
|
|
;; NOTE: the shape field is mutated when normalized, as
|
|
;; is the normalized? field.
|
|
(define (make-image shape bb normalized? [pinhole #f])
|
|
(new image% [shape shape] [bb bb] [normalized? normalized?] [pinhole pinhole]))
|
|
(define (image-shape p) (send p get-shape))
|
|
(define (image-bb p) (send p get-bb))
|
|
(define (image-normalized? p) (send p get-normalized?))
|
|
(define (set-image-shape! p s) (send p set-shape s))
|
|
(define (set-image-normalized?! p n?) (send p set-normalized? n?))
|
|
(define (image? p)
|
|
(or (is-a? p image<%>)
|
|
(is-a? p image-snip%)
|
|
(is-a? p bitmap%)))
|
|
|
|
(define (un/cache-image img bitmap-cache?)
|
|
(unless (image? img)
|
|
(error 'un/cache-image "expected an image as the first argument, got ~e" img))
|
|
(define res (send img copy))
|
|
(send res set-use-bitmap-cache?! (and bitmap-cache? #t))
|
|
res)
|
|
|
|
(define (compute-image-cache img)
|
|
(unless (image? img)
|
|
(error 'compute-cached-bitmap "expected an image as the first argument, got ~e" img))
|
|
(when (is-a? img image<%>)
|
|
(send img compute-cached-bitmap))
|
|
(void))
|
|
|
|
;; a shape is either:
|
|
;;
|
|
;; - (make-overlay shape shape)
|
|
;; the shapes are in the order passed to the overlay or beside,
|
|
;; which means the bottom one should be drawn first so as to appear
|
|
;; underneath the top one.
|
|
(define-struct/reg-mk overlay (top bottom) #:transparent #:omit-define-syntaxes)
|
|
;;
|
|
;; - (make-translate dx dy shape)
|
|
(define-struct/reg-mk translate (dx dy shape) #:transparent #:omit-define-syntaxes)
|
|
;;
|
|
;; - (make-scale x-factor y-factor shape)
|
|
(define-struct/reg-mk scale (x y shape) #:transparent #:omit-define-syntaxes)
|
|
;;
|
|
;; - (make-crop (listof vector) shape)
|
|
(define-struct/reg-mk crop (points shape) #:transparent #:omit-define-syntaxes)
|
|
;;
|
|
;; - atomic-shape
|
|
|
|
;; an atomic-shape is either:
|
|
;; - polygon
|
|
;; - line-segment
|
|
;; - curve-segment
|
|
;; - bitmap
|
|
;; - np-atomic-shape
|
|
|
|
;; a np-atomic-shape is:
|
|
;;
|
|
;; - (make-ellipse width height angle mode color)
|
|
(define-struct/reg-mk ellipse (width height angle mode color) #:transparent #:omit-define-syntaxes)
|
|
;;
|
|
;; - (make-text string angle number color
|
|
;; number (or/c #f string) family (or/c 'normal 'italic) (or/c 'normal 'light 'bold) boolean)
|
|
;; NOTE: font can't be the raw mred font or else copy & paste won't work
|
|
(define-struct/reg-mk text (string angle y-scale color size face family style weight underline)
|
|
#:omit-define-syntaxes #:transparent)
|
|
;;
|
|
;; - flip
|
|
|
|
;; a bitmap is:
|
|
;; - (make-ibitmap (and/c (is-a?/c bitmap%) (lambda (x) (send x has-alpha-channel?)))
|
|
;; angle positive-real
|
|
;; hash[(list boolean[flip] number[x-scale] number[y-scale] number[angle]) -o> (is-a?/c bitmap%)])
|
|
;; NOTE: bitmap copying needs to happen in 'write' and 'read' methods
|
|
(define-struct/reg-mk ibitmap #:reflect-id bitmap (raw-bitmap angle x-scale y-scale cache)
|
|
#:omit-define-syntaxes #:transparent
|
|
#:property prop:custom-write (λ (x y z) (bitmap-write x y z)))
|
|
|
|
;; a flip is:
|
|
;; - (make-flip boolean bitmap)
|
|
;; * the boolean is #t if the bitmap should be flipped vertically (after applying whatever rotation is in there)
|
|
;; * this struct is here to avoid adding a field to bitmaps, so that old save files
|
|
;; from when the library did not support flipping still load
|
|
;; (since normalization will add a flip structure if necessary)
|
|
(define-struct/reg-mk flip (flipped? shape) #:transparent)
|
|
|
|
;; a polygon is:
|
|
;;
|
|
;; - (make-polygon (listof vector) mode color)
|
|
(define-struct/reg-mk polygon (points mode color) #:transparent #:omit-define-syntaxes)
|
|
|
|
;; a line-segment is
|
|
;;
|
|
;; - (make-line-segment point point color)
|
|
(define-struct/reg-mk line-segment (start end color) #:transparent #:omit-define-syntaxes)
|
|
|
|
;; a curve-segment is
|
|
;;
|
|
;; - (make-curve-segment point real real point real real color)
|
|
(define-struct/reg-mk curve-segment (start s-angle s-pull end e-angle e-pull color) #:transparent #:omit-define-syntaxes)
|
|
|
|
;; a normalized-shape (subtype of shape) is either
|
|
;; - (make-overlay normalized-shape cn-or-simple-shape)
|
|
;; - cn-or-simple-shape
|
|
|
|
;; an cn-or-simple-shape is either:
|
|
;; - simple-shape
|
|
;; - (make-crop (listof points) normalized-shape)
|
|
|
|
;; a simple-shape (subtype of shape) is
|
|
;; - (make-translate dx dy np-atomic-shape)
|
|
;; - polygon
|
|
;; - line-segment
|
|
;; - curve-segment
|
|
|
|
;; an angle is a number between 0 and 360 (degrees)
|
|
|
|
;; a mode is either 'solid or 'outline (indicating a pen width for outline mode)
|
|
|
|
;; a pen is
|
|
;; - (make-pen color? ;; <- the struct, not a string
|
|
;; (<=/c 0 255)
|
|
;; (or/c 'solid 'dot 'long-dash 'short-dash 'dot-dash)
|
|
;; (or/c 'round 'projecting 'butt)
|
|
;; (or/c 'round 'bevel 'miter))
|
|
(define-struct/reg-mk pen (color width style cap join) #:transparent)
|
|
|
|
;; an color is
|
|
;; - (make-color (<=/c 0 255) (<=/c 0 255) (<=/c 0 255))
|
|
;; - string
|
|
(define-struct/reg-mk color (red green blue alpha) #:transparent)
|
|
(define -make-color
|
|
;; this let is here just for the name
|
|
(let ([make-color
|
|
(λ (r g b [a 255])
|
|
(make-color r g b a))])
|
|
make-color))
|
|
;
|
|
;
|
|
;
|
|
; ;; ;; ;;
|
|
; ;; ;;;; ;
|
|
; ; ; ;; ;
|
|
; ;; ;;;;;;;;; ;;;;; ;;;;;; ;;;; ; ;; ;
|
|
; ;; ;; ;;; ;;;; ;; ;; ;; ;;; ;; ;; ;
|
|
; ;; ;; ;;; ;;; ;;;; ;;; ;; ;;;;;; ; ;;;
|
|
; ;; ;; ;;; ;;;;; ;; ;;; ;; ;;; ;;
|
|
; ;; ;; ;;; ;;;;; ;; ;;;;; ;;; ; ; ;; ;;
|
|
; ;; ;; ;;; ;;;;;;;;;; ;;;;;; ;;;; ;; ;;;
|
|
; ;; ;;
|
|
; ;; ;
|
|
; ;;;;
|
|
|
|
(define skip-image-equality-fast-path (make-parameter #f))
|
|
(define render-normalized (make-parameter #f))
|
|
|
|
(define png-convertible<%>
|
|
(interface* ()
|
|
([prop:convertible
|
|
(lambda (img format default)
|
|
(case format
|
|
[(png-bytes)
|
|
(let ([s (open-output-bytes)])
|
|
(send (to-bitmap (to-img img)) save-file s 'png)
|
|
(get-output-bytes s))]
|
|
[else default]))])))
|
|
|
|
(define (to-bitmap img)
|
|
(let* ([bb (send img get-bb)]
|
|
[bm (make-bitmap
|
|
(inexact->exact (ceiling (bb-right bb)))
|
|
(inexact->exact (ceiling (bb-bottom bb))))]
|
|
[bdc (new bitmap-dc% [bitmap bm])])
|
|
(send bdc erase)
|
|
(render-image img bdc 0 0)
|
|
(begin0
|
|
(send bdc get-bitmap)
|
|
(send bdc set-bitmap #f))))
|
|
|
|
(define-local-member-name
|
|
set-use-bitmap-cache?!
|
|
set-cached-bitmap
|
|
compute-cached-bitmap)
|
|
|
|
(define image%
|
|
(class* snip% (png-convertible<%> image<%>)
|
|
(init-field shape bb normalized? pinhole)
|
|
|
|
(define/override (equal-to? that eq-recur) (compare-em that eq-recur))
|
|
(define/override (other-equal-to? that eq-recur) (compare-em that eq-recur))
|
|
|
|
(define/private (compare-em that eq-recur)
|
|
(or (eq? this that)
|
|
(let ([that
|
|
(cond
|
|
[(is-a? that image-snip%) (image-snip->image that)]
|
|
[(is-a? that bitmap%) (bitmap->image that)]
|
|
[else that])])
|
|
(and (is-a? that image%)
|
|
(same-bb? bb (send that get-bb))
|
|
(equal? pinhole (send that get-pinhole))
|
|
(or (and (not (skip-image-equality-fast-path)) ;; this is here to make testing more effective
|
|
(equal? (get-normalized-shape) (send that get-normalized-shape)))
|
|
|
|
;; some shapes (ie, rectangles) draw 1 outside the bounding box
|
|
;; so we make the bitmap slightly bigger to accommodate that.
|
|
(let ([w (+ 1 (round (inexact->exact (bb-right bb))))]
|
|
[h (+ 1 (round (inexact->exact (bb-bottom bb))))])
|
|
(or ;(zero? w)
|
|
;(zero? h)
|
|
(let ([bm1 (make-bitmap w h #t)]
|
|
[bm2 (make-bitmap w h #t)]
|
|
[bytes1 (make-bytes (* w h 4) 0)]
|
|
[bytes2 (make-bytes (* w h 4) 0)]
|
|
[bdc (make-object bitmap-dc%)])
|
|
(draw-into bm1 bdc bytes1 this)
|
|
(draw-into bm2 bdc bytes2 that)
|
|
(equal? bytes1 bytes2)))))))))
|
|
|
|
(define/private (draw-into bm bdc bytes obj)
|
|
(send bdc set-bitmap bm)
|
|
(send bdc erase)
|
|
(render-image obj bdc 0 0)
|
|
(send bdc get-argb-pixels 0 0 (send bm get-width) (send bm get-height) bytes #f #t))
|
|
|
|
;; this could render the image into a bitmap and then get the hash code of the bytes
|
|
;; cannot render the tree into a string and then get the hash code of that string
|
|
;; b/c that might make equal things have the same code.
|
|
(define/override (equal-hash-code-of y) 42)
|
|
(define/override (equal-secondary-hash-code-of y) 3)
|
|
|
|
(define/public (get-shape) shape)
|
|
(define/public (set-shape s) (set! shape s))
|
|
(define/public (get-bb) bb)
|
|
(define/public (get-pinhole) pinhole)
|
|
(define/public (get-normalized?) normalized?)
|
|
(define/public (set-normalized? n?) (set! normalized? n?))
|
|
|
|
(define/public (get-normalized-shape)
|
|
(unless normalized?
|
|
(set! shape (normalize-shape shape))
|
|
(set! normalized? #t))
|
|
shape)
|
|
|
|
(inherit get-admin)
|
|
(define scroll-step #f)
|
|
(define/private (calc-scroll-step)
|
|
(unless scroll-step
|
|
;; try to set scroll step by font size of the standard style
|
|
(let ([admin (get-admin)])
|
|
(when admin
|
|
(let* ([ed (send admin get-editor)]
|
|
[sl (send ed get-style-list)]
|
|
[standard (send sl find-named-style "Standard")])
|
|
(when standard
|
|
(let ([dc (make-object bitmap-dc% (make-object bitmap% 1 1))])
|
|
(let-values ([(w h d a) (send dc get-text-extent "X" (send standard get-font))])
|
|
(set! scroll-step (+ h (send admin get-line-spacing)))))))))
|
|
;; if that didn't happen, set it to 12.
|
|
(unless scroll-step (set! scroll-step 12))))
|
|
|
|
(define/override (get-num-scroll-steps)
|
|
(calc-scroll-step)
|
|
(inexact->exact (ceiling (/ (bb-bottom bb) scroll-step))))
|
|
(define/override (get-scroll-step-offset offset)
|
|
(calc-scroll-step)
|
|
(min (inexact->exact (ceiling (* offset scroll-step)))
|
|
(bb-bottom bb)))
|
|
(define/override (find-scroll-step y)
|
|
(calc-scroll-step)
|
|
(inexact->exact (ceiling (/ y scroll-step))))
|
|
|
|
(define/override (copy)
|
|
(define res (make-image shape bb normalized? pinhole))
|
|
(when cached-bitmap
|
|
(send res set-cached-bitmap cached-bitmap))
|
|
res)
|
|
|
|
(define cached-bitmap #f)
|
|
(define use-cached-bitmap? #t)
|
|
|
|
;; this method is only used by the 'copy' method
|
|
(define/public (set-cached-bitmap bm) (set! cached-bitmap bm))
|
|
|
|
(define/public (compute-cached-bitmap)
|
|
(when use-cached-bitmap?
|
|
(unless cached-bitmap
|
|
(set! cached-bitmap (make-bitmap (+ (inexact->exact (round (bb-right bb))) 1)
|
|
(+ (inexact->exact (round (bb-bottom bb))) 1)))
|
|
(define bdc (make-object bitmap-dc% cached-bitmap))
|
|
(send bdc erase)
|
|
(render-image this bdc 0 0)
|
|
(send bdc set-bitmap #f))))
|
|
|
|
(define/public (set-use-bitmap-cache?! u-b-c?)
|
|
(set! use-cached-bitmap? u-b-c?)
|
|
(unless use-cached-bitmap?
|
|
(set! cached-bitmap #f)))
|
|
|
|
(define/override (draw dc x y left top right bottom dx dy draw-caret)
|
|
(compute-cached-bitmap)
|
|
|
|
(let ([alpha (send dc get-alpha)])
|
|
(when (pair? draw-caret)
|
|
(send dc set-alpha (* alpha .5)))
|
|
(if use-cached-bitmap?
|
|
(send dc draw-bitmap cached-bitmap x y)
|
|
(render-image this dc x y))
|
|
(send dc set-alpha alpha)))
|
|
|
|
(define/override (get-extent dc x y [w #f] [h #f] [descent #f] [space #f] [lspace #f] [rspace #f])
|
|
(send (get-the-snip-class-list) add snip-class)
|
|
(let ([bottom (round (bb-bottom bb))]
|
|
[right (round (bb-right bb))])
|
|
(set-box/f! w right)
|
|
(set-box/f! h bottom)
|
|
(set-box/f! descent (- bottom (round (bb-baseline bb))))
|
|
(set-box/f! space 0)
|
|
(set-box/f! lspace 0)
|
|
(set-box/f! rspace 0)))
|
|
|
|
(define/override (write f)
|
|
(let ([bytes (string->bytes/utf-8 (format "~s" (list shape bb pinhole)))])
|
|
(send f put (bytes-length bytes) bytes)))
|
|
|
|
(super-new)
|
|
|
|
(inherit set-snipclass)
|
|
(set-snipclass snip-class)))
|
|
|
|
(define (same-bb? bb1 bb2)
|
|
(and (= (round (bb-right bb1)) (round (bb-right bb2)))
|
|
(= (round (bb-bottom bb1)) (round (bb-bottom bb2)))
|
|
(= (round (bb-baseline bb1)) (round (bb-baseline bb2)))))
|
|
|
|
(define racket/base:read read)
|
|
(define image-snipclass%
|
|
(class snip-class%
|
|
(define/override (read f)
|
|
(let ([lst (parse (fetch (send f get-unterminated-bytes)))])
|
|
(cond
|
|
[(not lst)
|
|
(make-image (make-translate 50 50 (make-ellipse 100 100 0 'solid "black"))
|
|
(make-bb 100 100 100)
|
|
#f
|
|
#f)]
|
|
[else
|
|
(make-image (list-ref lst 0)
|
|
(list-ref lst 1)
|
|
#f
|
|
(list-ref lst 2))])))
|
|
(super-new)))
|
|
|
|
(provide snip-class)
|
|
(define snip-class (new image-snipclass%))
|
|
(send snip-class set-classname (format "~s" (list '(lib "image-core.ss" "mrlib")
|
|
'(lib "image-core-wxme.rkt" "mrlib"))))
|
|
(send snip-class set-version 1)
|
|
(send (get-the-snip-class-list) add snip-class)
|
|
|
|
(define (set-box/f! b v) (when (box? b) (set-box! b v)))
|
|
|
|
(define (parse sexp)
|
|
(let/ec k
|
|
(let loop ([sexp sexp])
|
|
(cond
|
|
[(pair? sexp) (cons (loop (car sexp)) (loop (cdr sexp)))]
|
|
[(and (immutable? sexp) (hash? sexp))
|
|
(hash-copy sexp)]
|
|
[(vector? sexp)
|
|
(if (= (vector-length sexp) 0)
|
|
(k #f)
|
|
(cond
|
|
[(bytes? (vector-ref sexp 0))
|
|
;; bitmaps are vectors with a bytes in the first field
|
|
(apply bytes->bitmap (vector->list sexp))]
|
|
[else
|
|
(let* ([tag (vector-ref sexp 0)]
|
|
[args (cdr (vector->list sexp))]
|
|
[constructor (id->constructor tag)]
|
|
[arg-count (length args)]
|
|
[parsed-args (map loop args)])
|
|
(cond
|
|
[(and constructor (procedure-arity-includes? constructor arg-count))
|
|
(apply constructor parsed-args)]
|
|
[(and (eq? tag 'struct:bitmap)
|
|
(= arg-count 7))
|
|
;; we changed the arity of the bitmap constructor from old versions,
|
|
;; so fix it up here.
|
|
(make-bitmap (list-ref parsed-args 0)
|
|
(list-ref parsed-args 1)
|
|
(list-ref parsed-args 2)
|
|
(list-ref parsed-args 3)
|
|
(list-ref parsed-args 4)
|
|
(make-hash))]
|
|
[(and (eq? tag 'struct:bitmap)
|
|
(= arg-count 6))
|
|
;; we changed the arity of the bitmap constructor from old versions,
|
|
;; so fix it up here.
|
|
;; it used to have these fields: (raw-bitmap raw-mask angle x-scale y-scale cache)
|
|
;; and the mask field was dropped in favor of always having an alpha bitmap in the
|
|
;; raw-bitmap field. The bytes that were written out always had the mask
|
|
;; factored in, tho (which led to a bug) so we can just ignore the mask here
|
|
(make-bitmap (list-ref parsed-args 0)
|
|
(list-ref parsed-args 2)
|
|
(list-ref parsed-args 3)
|
|
(list-ref parsed-args 4)
|
|
(make-hash))]
|
|
[(and (eq? tag 'struct:color)
|
|
(= arg-count 3))
|
|
;; we changed the arity of the color constructor from old versions,
|
|
;; so fix it up here.
|
|
(make-color (list-ref parsed-args 0)
|
|
(list-ref parsed-args 1)
|
|
(list-ref parsed-args 2)
|
|
255)]
|
|
[else
|
|
(k #f)]))]))]
|
|
[else sexp]))))
|
|
|
|
(define (normalized-shape? s)
|
|
(cond
|
|
[(overlay? s)
|
|
(and (normalized-shape? (overlay-top s))
|
|
(cn-or-simple-shape? (overlay-bottom s)))]
|
|
[else
|
|
(cn-or-simple-shape? s)]))
|
|
|
|
(define (cn-or-simple-shape? s)
|
|
(cond
|
|
[(crop? s)
|
|
(normalized-shape? (crop-shape s))]
|
|
[else
|
|
(simple-shape? s)]))
|
|
|
|
(define (simple-shape? shape)
|
|
(or (and (translate? shape)
|
|
(np-atomic-shape? (translate-shape shape)))
|
|
(polygon? shape)
|
|
(line-segment? shape)
|
|
(curve-segment? shape)))
|
|
|
|
(define (atomic-shape? shape)
|
|
(or (polygon? shape)
|
|
(line-segment? shape)
|
|
(curve-segment? shape)
|
|
(ibitmap? shape)
|
|
(np-atomic-shape? shape)))
|
|
|
|
(define (np-atomic-shape? shape)
|
|
(or (ellipse? shape)
|
|
(text? shape)
|
|
(and (flip? shape)
|
|
(boolean? (flip-flipped? shape))
|
|
(ibitmap? (flip-shape shape)))))
|
|
|
|
;; normalize-shape : shape -> normalized-shape
|
|
;; normalizes 'shape', calling 'f' on each atomic shape in the normalized shape.
|
|
(define/contract (normalize-shape shape)
|
|
(-> any/c ;; should be shape?
|
|
normalized-shape?)
|
|
(let loop ([shape shape]
|
|
[dx 0]
|
|
[dy 0]
|
|
[x-scale 1]
|
|
[y-scale 1]
|
|
[bottom #f])
|
|
(define (scale-point p)
|
|
(make-point (+ dx (* x-scale (point-x p)))
|
|
(+ dy (* y-scale (point-y p)))))
|
|
(cond
|
|
[(translate? shape)
|
|
(loop (translate-shape shape)
|
|
(+ dx (* x-scale (translate-dx shape)))
|
|
(+ dy (* y-scale (translate-dy shape)))
|
|
x-scale
|
|
y-scale
|
|
bottom)]
|
|
[(scale? shape)
|
|
(loop (scale-shape shape)
|
|
dx
|
|
dy
|
|
(* x-scale (scale-x shape))
|
|
(* y-scale (scale-y shape))
|
|
bottom)]
|
|
[(overlay? shape)
|
|
(loop (overlay-bottom shape)
|
|
dx dy x-scale y-scale
|
|
(loop (overlay-top shape)
|
|
dx dy x-scale y-scale
|
|
bottom))]
|
|
[(crop? shape)
|
|
(let* ([inside (loop (crop-shape shape)
|
|
dx dy x-scale y-scale
|
|
#f)]
|
|
[this-one
|
|
(make-crop (map scale-point (crop-points shape))
|
|
inside)])
|
|
(if bottom
|
|
(make-overlay bottom this-one)
|
|
this-one))]
|
|
[(polygon? shape)
|
|
(let* ([this-one
|
|
(make-polygon (map scale-point (polygon-points shape))
|
|
(polygon-mode shape)
|
|
(scale-color (polygon-color shape) x-scale y-scale))])
|
|
(if bottom
|
|
(make-overlay bottom this-one)
|
|
this-one))]
|
|
[(line-segment? shape)
|
|
(let ([this-one
|
|
(make-line-segment (scale-point (line-segment-start shape))
|
|
(scale-point (line-segment-end shape))
|
|
(scale-color (line-segment-color shape) x-scale y-scale))])
|
|
(if bottom
|
|
(make-overlay bottom this-one)
|
|
this-one))]
|
|
[(curve-segment? shape)
|
|
;; the pull is multiplied by the distance
|
|
;; between the two points when it is drawn,
|
|
;; so we don't need to scale it here
|
|
(let ([this-one
|
|
(make-curve-segment (scale-point (curve-segment-start shape))
|
|
(curve-segment-s-angle shape)
|
|
(curve-segment-s-pull shape)
|
|
(scale-point (curve-segment-end shape))
|
|
(curve-segment-e-angle shape)
|
|
(curve-segment-e-pull shape)
|
|
(scale-color (curve-segment-color shape) x-scale y-scale))])
|
|
(if bottom
|
|
(make-overlay bottom this-one)
|
|
this-one))]
|
|
[(or (ibitmap? shape) (np-atomic-shape? shape))
|
|
(let ([shape (if (ibitmap? shape)
|
|
(make-flip #f shape)
|
|
shape)])
|
|
(let ([this-one
|
|
(make-translate dx dy (scale-np-atomic x-scale y-scale shape))])
|
|
(if bottom
|
|
(make-overlay bottom this-one)
|
|
this-one)))]
|
|
[else
|
|
(error 'normalize-shape "unknown shape ~s\n" shape)])))
|
|
|
|
(define/contract (scale-np-atomic x-scale y-scale shape)
|
|
(-> number? number? np-atomic-shape? np-atomic-shape?)
|
|
(cond
|
|
[(ellipse? shape)
|
|
(make-ellipse (* x-scale (ellipse-width shape))
|
|
(* y-scale (ellipse-height shape))
|
|
(ellipse-angle shape)
|
|
(ellipse-mode shape)
|
|
(scale-color (ellipse-color shape) x-scale y-scale))]
|
|
[(text? shape)
|
|
;; should probably do something different here so that
|
|
;; the y-scale is always greater than 1
|
|
;; (or else always smaller than 1)
|
|
(make-text (text-string shape)
|
|
(text-angle shape)
|
|
(* (text-y-scale shape) (/ y-scale x-scale))
|
|
(text-color shape)
|
|
(* (text-size shape) x-scale)
|
|
(text-face shape)
|
|
(text-family shape)
|
|
(text-style shape)
|
|
(text-weight shape)
|
|
(text-underline shape))]
|
|
[(flip? shape)
|
|
(cond
|
|
[(and (= 1 x-scale) (= 1 y-scale))
|
|
shape]
|
|
[else
|
|
(let ([bitmap (flip-shape shape)])
|
|
(make-flip (flip-flipped? shape)
|
|
(make-ibitmap (ibitmap-raw-bitmap bitmap)
|
|
(ibitmap-angle bitmap)
|
|
(* x-scale (ibitmap-x-scale bitmap))
|
|
(* y-scale (ibitmap-y-scale bitmap))
|
|
(ibitmap-cache bitmap))))])]))
|
|
|
|
(define (scale-color color x-scale y-scale)
|
|
(cond
|
|
[(pen? color)
|
|
(make-pen (pen-color color)
|
|
(* (pen-width color) (/ (+ x-scale y-scale) 2))
|
|
(pen-style color)
|
|
(pen-cap color)
|
|
(pen-join color))]
|
|
[else color]))
|
|
|
|
;
|
|
;
|
|
;
|
|
;
|
|
;
|
|
;
|
|
; ;; ;;
|
|
; ;; ;;
|
|
; ;;;; ;;;; ;; ;;; ;;;;; ;;;; ;;;;;;; ;; ;;; ;;;;;;
|
|
; ;;;; ;; ;; ;;;;;; ;;;;;; ;; ;; ;;;; ;; ;;;;;; ;;;;;;
|
|
; ;; ;;;;;;;; ;; ;; ;;; ;; ;;;;;;;; ;; ;; ;; ;; ;;; ;;
|
|
; ;; ;;; ;; ;; ;;; ;; ;;; ;; ;; ;; ;; ;;; ;;
|
|
; ;; ;;; ;; ;; ;; ;;;;;; ;;; ;; ;; ;; ;; ;; ;;;;;;
|
|
; ;; ;;;; ;; ;; ;;;;; ;;;; ;; ;; ;; ;; ;;;;;
|
|
; ;; ;;;
|
|
; ;;;;;
|
|
;
|
|
;
|
|
|
|
;; render-image : image dc dx dy -> void
|
|
(define (render-image image dc dx dy)
|
|
(let ([pen (send dc get-pen)]
|
|
[brush (send dc get-brush)]
|
|
[font (send dc get-font)]
|
|
[fg (send dc get-text-foreground)]
|
|
[smoothing (send dc get-smoothing)]
|
|
[alpha (send dc get-alpha)])
|
|
(cond
|
|
[(is-a? image bitmap%)
|
|
(send dc draw-bitmap image dx dy)]
|
|
[(is-a? image image-snip%)
|
|
(send dc draw-bitmap (send image get-bitmap) dx dy)]
|
|
[else
|
|
(if (render-normalized)
|
|
(render-normalized-shape (send image get-normalized-shape) dc dx dy)
|
|
(render-arbitrary-shape (send image get-shape) dc dx dy))
|
|
(let ([ph (send image get-pinhole)])
|
|
(when ph
|
|
(let* ([px (point-x ph)]
|
|
[py (point-y ph)]
|
|
[bb (image-bb image)]
|
|
[w (bb-right bb)]
|
|
[h (bb-bottom bb)])
|
|
(send dc set-alpha (* alpha .5))
|
|
(send dc set-smoothing 'smoothed)
|
|
|
|
(send dc set-pen "white" 1 'solid)
|
|
(send dc draw-line (+ dx px .5) (+ dy .5) (+ dx px .5) (+ dy h -.5))
|
|
(send dc draw-line (+ dx .5) (+ dy py .5) (+ dx w -.5) (+ dy py .5))
|
|
|
|
(send dc set-pen "black" 1 'solid)
|
|
(send dc draw-line (+ dx px -.5) (+ dy .5) (+ dx px -.5) (+ dy h -.5))
|
|
(send dc draw-line (+ dx .5) (+ dy py -.5) (+ dx w -.5) (+ dy py -.5)))))])
|
|
(send dc set-pen pen)
|
|
(send dc set-brush brush)
|
|
(send dc set-font font)
|
|
(send dc set-text-foreground fg)
|
|
(send dc set-smoothing smoothing)
|
|
(send dc set-alpha alpha)))
|
|
|
|
(define (save-image-as-bitmap image filename kind)
|
|
(let* ([bb (send image get-bb)]
|
|
[bm (make-object bitmap%
|
|
(+ 1 (ceiling (inexact->exact (bb-right bb))))
|
|
(+ 1 (ceiling (inexact->exact (bb-bottom bb)))))]
|
|
[bdc (make-object bitmap-dc% bm)])
|
|
(send bdc erase)
|
|
(render-image image bdc 0 0)
|
|
(send bdc set-bitmap #f)
|
|
(send bm save-file filename kind)))
|
|
|
|
(define (render-normalized-shape shape dc dx dy)
|
|
(cond
|
|
[(overlay? shape)
|
|
(render-cn-or-simple-shape (overlay-bottom shape) dc dx dy)
|
|
(render-normalized-shape (overlay-top shape) dc dx dy)]
|
|
[else
|
|
(render-cn-or-simple-shape shape dc dx dy)]))
|
|
|
|
(define last-cropped-points (make-parameter #f))
|
|
|
|
(define (render-cn-or-simple-shape shape dc dx dy)
|
|
(cond
|
|
[(crop? shape)
|
|
(render-cropped-shape (crop-points shape)
|
|
(crop-shape shape)
|
|
(λ (s) (render-normalized-shape s dc dx dy))
|
|
dc dx dy)]
|
|
[else
|
|
(render-simple-shape shape dc dx dy)]))
|
|
|
|
(define (render-cropped-shape points inner-shape continue dc dx dy)
|
|
(cond
|
|
[(equal? points (last-cropped-points))
|
|
(continue inner-shape)]
|
|
[else
|
|
(let ([old-region (send dc get-clipping-region)]
|
|
[new-region (new region% [dc dc])]
|
|
[path (polygon-points->path points)])
|
|
(send new-region set-path path dx dy)
|
|
(when old-region (send new-region intersect old-region))
|
|
(send dc set-clipping-region new-region)
|
|
(parameterize ([last-cropped-points points])
|
|
(continue inner-shape))
|
|
(send dc set-clipping-region old-region))]))
|
|
|
|
(define (render-simple-shape simple-shape dc dx dy)
|
|
(cond
|
|
[(translate? simple-shape)
|
|
(let ([dx (+ dx (translate-dx simple-shape))]
|
|
[dy (+ dy (translate-dy simple-shape))]
|
|
[np-atomic-shape (translate-shape simple-shape)])
|
|
(render-np-atomic-shape np-atomic-shape
|
|
dc
|
|
dx dy))]
|
|
[else
|
|
(render-poly/line-segment/curve-segment simple-shape dc dx dy)]))
|
|
|
|
(define (render-arbitrary-shape shape dc dx dy)
|
|
(let loop ([shape shape]
|
|
[dx dx]
|
|
[dy dy]
|
|
[x-scale 1]
|
|
[y-scale 1])
|
|
(define (scale-point p)
|
|
(make-point (* x-scale (point-x p))
|
|
(* y-scale (point-y p))))
|
|
(cond
|
|
[(translate? shape)
|
|
(loop (translate-shape shape)
|
|
(+ dx (* x-scale (translate-dx shape)))
|
|
(+ dy (* y-scale (translate-dy shape)))
|
|
x-scale
|
|
y-scale)]
|
|
[(scale? shape)
|
|
(loop (scale-shape shape)
|
|
dx
|
|
dy
|
|
(* x-scale (scale-x shape))
|
|
(* y-scale (scale-y shape)))]
|
|
[(overlay? shape)
|
|
(loop (overlay-bottom shape) dx dy x-scale y-scale)
|
|
(loop (overlay-top shape) dx dy x-scale y-scale)]
|
|
[(crop? shape)
|
|
(render-cropped-shape
|
|
(map scale-point (crop-points shape))
|
|
(crop-shape shape)
|
|
(λ (s) (loop s dx dy x-scale y-scale)) dc dx dy)]
|
|
[(polygon? shape)
|
|
(let* ([this-one
|
|
(make-polygon (map scale-point (polygon-points shape))
|
|
(polygon-mode shape)
|
|
(scale-color (polygon-color shape) x-scale y-scale))])
|
|
(render-poly/line-segment/curve-segment this-one dc dx dy))]
|
|
[(line-segment? shape)
|
|
(let ([this-one
|
|
(make-line-segment (scale-point (line-segment-start shape))
|
|
(scale-point (line-segment-end shape))
|
|
(scale-color (line-segment-color shape) x-scale y-scale))])
|
|
(render-poly/line-segment/curve-segment this-one dc dx dy))]
|
|
[(curve-segment? shape)
|
|
;; the pull is multiplied by the distance
|
|
;; between the two points when it is drawn,
|
|
;; so we don't need to scale it here
|
|
(let ([this-one
|
|
(make-curve-segment (scale-point (curve-segment-start shape))
|
|
(curve-segment-s-angle shape)
|
|
(curve-segment-s-pull shape)
|
|
(scale-point (curve-segment-end shape))
|
|
(curve-segment-e-angle shape)
|
|
(curve-segment-e-pull shape)
|
|
(scale-color (curve-segment-color shape) x-scale y-scale))])
|
|
(render-poly/line-segment/curve-segment this-one dc dx dy))]
|
|
[(or (ibitmap? shape) (np-atomic-shape? shape))
|
|
(let* ([shape (if (ibitmap? shape)
|
|
(make-flip #f shape)
|
|
shape)]
|
|
[this-one (scale-np-atomic x-scale y-scale shape)])
|
|
(render-np-atomic-shape this-one dc dx dy))]
|
|
[else
|
|
(error 'normalize-shape "unknown shape ~s\n" shape)])))
|
|
|
|
(define/contract (render-poly/line-segment/curve-segment simple-shape dc dx dy)
|
|
(-> (or/c polygon? line-segment? curve-segment?) any/c any/c any/c void?)
|
|
(cond
|
|
[(polygon? simple-shape)
|
|
(let ([mode (polygon-mode simple-shape)]
|
|
[color (polygon-color simple-shape)]
|
|
[path (polygon-points->path (polygon-points simple-shape))])
|
|
(send dc set-pen (mode-color->pen mode color))
|
|
(send dc set-brush (mode-color->brush mode color))
|
|
(send dc set-smoothing (mode-color->smoothing mode color))
|
|
(send dc draw-path path dx dy 'winding))]
|
|
[(line-segment? simple-shape)
|
|
(let* ([start (line-segment-start simple-shape)]
|
|
[end (line-segment-end simple-shape)]
|
|
[path (new dc-path%)]
|
|
[sx (point-x start)]
|
|
[sy (point-y start)]
|
|
[ex (point-x end)]
|
|
[ey (point-y end)])
|
|
(send path move-to sx sy)
|
|
(send path line-to ex ey)
|
|
(send dc set-pen (mode-color->pen 'outline (line-segment-color simple-shape)))
|
|
(send dc set-brush "black" 'transparent)
|
|
(send dc set-smoothing 'smoothed)
|
|
(send dc draw-path path dx dy))]
|
|
[(curve-segment? simple-shape)
|
|
(let* ([path (new dc-path%)]
|
|
[start (curve-segment-start simple-shape)]
|
|
[end (curve-segment-end simple-shape)]
|
|
[sx (point-x start)]
|
|
[sy (point-y start)]
|
|
[ex (point-x end)]
|
|
[ey (point-y end)]
|
|
[sa (degrees->radians (curve-segment-s-angle simple-shape))]
|
|
[ea (degrees->radians (curve-segment-e-angle simple-shape))]
|
|
[d (sqrt (+ (sqr (- ey sy)) (sqr (- ex sx))))]
|
|
[sp (* (curve-segment-s-pull simple-shape) d)]
|
|
[ep (* (curve-segment-e-pull simple-shape) d)])
|
|
(send path move-to sx sy)
|
|
(send path curve-to
|
|
(+ sx (* sp (cos sa)))
|
|
(- sy (* sp (sin sa)))
|
|
(- ex (* ep (cos ea)))
|
|
(+ ey (* ep (sin ea)))
|
|
ex
|
|
ey)
|
|
(send dc set-pen (mode-color->pen 'outline (curve-segment-color simple-shape)))
|
|
(send dc set-brush "black" 'transparent)
|
|
(send dc set-smoothing 'smoothed)
|
|
(send dc draw-path path dx dy))]))
|
|
|
|
(define (render-np-atomic-shape np-atomic-shape dc dx dy)
|
|
(cond
|
|
[(ellipse? np-atomic-shape)
|
|
(let* ([path (new dc-path%)]
|
|
[ew (ellipse-width np-atomic-shape)]
|
|
[eh (ellipse-height np-atomic-shape)]
|
|
[θ (degrees->radians (ellipse-angle np-atomic-shape))]
|
|
[color (ellipse-color np-atomic-shape)]
|
|
[mode (ellipse-mode np-atomic-shape)])
|
|
(let-values ([(rotated-width rotated-height) (ellipse-rotated-size ew eh θ)])
|
|
(send path ellipse 0 0 ew eh)
|
|
(send path translate (- (/ ew 2)) (- (/ eh 2)))
|
|
(send path rotate θ)
|
|
(send dc set-pen (mode-color->pen mode color))
|
|
(send dc set-brush (mode-color->brush mode color))
|
|
(send dc set-smoothing (mode-color->smoothing mode color))
|
|
(send dc draw-path path dx dy)))]
|
|
[(flip? np-atomic-shape)
|
|
(let ([bm (get-rendered-bitmap np-atomic-shape)])
|
|
(send dc set-smoothing 'smoothed)
|
|
(send dc draw-bitmap
|
|
bm
|
|
(- dx (/ (send bm get-width) 2))
|
|
(- dy (/ (send bm get-height) 2))))]
|
|
[(text? np-atomic-shape)
|
|
(let ([θ (degrees->radians (text-angle np-atomic-shape))]
|
|
[font (send dc get-font)])
|
|
(send dc set-font (text->font np-atomic-shape))
|
|
(send dc set-smoothing 'aligned) ;; should this be smoothed?
|
|
(let ([color (get-color-arg (text-color np-atomic-shape))])
|
|
(send dc set-text-foreground
|
|
(cond
|
|
[(string? color)
|
|
(or (send the-color-database find-color color)
|
|
(send the-color-database find-color "black"))]
|
|
[else color])))
|
|
(let-values ([(w h _1 _2) (send dc get-text-extent (text-string np-atomic-shape))])
|
|
(let ([p (- (make-rectangular dx dy)
|
|
(* (make-polar 1 (- θ)) (make-rectangular (/ w 2) (/ h 2))))])
|
|
(send dc draw-text (text-string np-atomic-shape)
|
|
(real-part p)
|
|
(imag-part p)
|
|
#f 0 θ))))]))
|
|
|
|
(define (polygon-points->path points)
|
|
(let ([path (new dc-path%)])
|
|
(send path move-to (point-x (car points)) (point-y (car points)))
|
|
(let loop ([points (cdr points)])
|
|
(unless (null? points)
|
|
(send path line-to
|
|
(point-x (car points))
|
|
(point-y (car points)))
|
|
(loop (cdr points))))
|
|
(send path close)
|
|
;(send path line-to (round (point-x (car points))) (round (point-y (car points))))
|
|
path))
|
|
|
|
(define (points->bb-path points)
|
|
(let ([path (new dc-path%)])
|
|
(let-values ([(left top right bottom) (points->ltrb-values points)])
|
|
(send path move-to left top)
|
|
(send path line-to right top)
|
|
(send path line-to right bottom)
|
|
(send path line-to left bottom)
|
|
(send path line-to left top)
|
|
path)))
|
|
|
|
;; points->ltrb-values : (cons point (listof points)) -> (values number number number number)
|
|
(define (points->ltrb-values points)
|
|
(let* ([fx (point-x (car points))]
|
|
[fy (point-y (car points))]
|
|
[left fx]
|
|
[top fy]
|
|
[right fx]
|
|
[bottom fy])
|
|
(for-each (λ (point)
|
|
(let ([new-x (point-x point)]
|
|
[new-y (point-y point)])
|
|
(set! left (min new-x left))
|
|
(set! top (min new-y top))
|
|
(set! right (max new-x right))
|
|
(set! bottom (max new-y bottom))))
|
|
(cdr points))
|
|
(values left top right bottom)))
|
|
|
|
#|
|
|
|
|
the mask bitmap and the original bitmap are all together in a single bytes!
|
|
|
|
|#
|
|
|
|
|
|
(define (get-rendered-bitmap flip-bitmap)
|
|
(let ([key (get-bitmap-cache-key flip-bitmap)])
|
|
(lookup/calc-rendered-bitmap flip-bitmap key)))
|
|
|
|
(define (get-bitmap-cache-key flip-bitmap)
|
|
(let ([bm (flip-shape flip-bitmap)])
|
|
(list (flip-flipped? flip-bitmap)
|
|
(ibitmap-x-scale bm)
|
|
(ibitmap-y-scale bm)
|
|
(ibitmap-angle bm))))
|
|
|
|
(define (lookup/calc-rendered-bitmap flip-bitmap key)
|
|
(let ([bitmap (flip-shape flip-bitmap)])
|
|
(cond
|
|
[(hash-ref (ibitmap-cache bitmap) key #f) => values]
|
|
[else
|
|
(let ([flipped? (flip-flipped? flip-bitmap)])
|
|
(define orig-bitmap-obj (ibitmap-raw-bitmap bitmap))
|
|
(define bitmap-obj
|
|
(cond
|
|
[(<= (* (ibitmap-x-scale bitmap)
|
|
(ibitmap-y-scale bitmap))
|
|
1)
|
|
;; since we prefer to rotate big things, we rotate first
|
|
(do-scale bitmap (do-rotate bitmap orig-bitmap-obj flipped?))]
|
|
[else
|
|
;; since we prefer to rotate big things, we scale first
|
|
(do-rotate bitmap (do-scale bitmap orig-bitmap-obj) flipped?)]))
|
|
(hash-set! (ibitmap-cache bitmap) key bitmap-obj)
|
|
bitmap-obj)])))
|
|
|
|
(define (do-rotate bitmap bitmap-obj flip?)
|
|
(cond
|
|
[(and (not flip?) (zero? (ibitmap-angle bitmap)))
|
|
;; don't rotate anything in this case.
|
|
bitmap-obj]
|
|
[else
|
|
(let ([θ (degrees->radians (ibitmap-angle bitmap))])
|
|
(let-values ([(bytes w h) (bitmap->bytes bitmap-obj #f)])
|
|
(let-values ([(rotated-bytes rotated-w rotated-h)
|
|
(rotate-bytes bytes w h θ)])
|
|
(let* ([flipped-bytes (if flip?
|
|
(flip-bytes rotated-bytes rotated-w rotated-h)
|
|
rotated-bytes)]
|
|
[bm (bytes->bitmap flipped-bytes rotated-w rotated-h)])
|
|
bm))))]))
|
|
|
|
(define (do-scale bitmap orig-bm)
|
|
(define x-scale (ibitmap-x-scale bitmap))
|
|
(define y-scale (ibitmap-y-scale bitmap))
|
|
(cond
|
|
[(and (= 1 x-scale) (= 1 y-scale))
|
|
;; no need to scale in this case
|
|
orig-bm]
|
|
[else
|
|
(define bdc (make-object bitmap-dc%))
|
|
(define orig-w (send orig-bm get-width))
|
|
(define orig-h (send orig-bm get-height))
|
|
(define scale-w (ceiling (inexact->exact (* x-scale (send orig-bm get-width)))))
|
|
(define scale-h (ceiling (inexact->exact (* y-scale (send orig-bm get-height)))))
|
|
(define new-bm (make-bitmap scale-w scale-h))
|
|
|
|
(send bdc set-bitmap new-bm)
|
|
(send bdc set-scale x-scale y-scale)
|
|
(send bdc erase)
|
|
(send bdc draw-bitmap orig-bm 0 0)
|
|
|
|
(send bdc set-bitmap #f)
|
|
|
|
new-bm]))
|
|
|
|
(define (text->font text)
|
|
(define adjusted-size (min (max (inexact->exact (round (text-size text))) 1) 255))
|
|
(cond
|
|
[(text-face text)
|
|
(send the-font-list find-or-create-font
|
|
adjusted-size
|
|
(text-face text)
|
|
(text-family text)
|
|
(text-style text)
|
|
(text-weight text)
|
|
(text-underline text))]
|
|
[else
|
|
(send the-font-list find-or-create-font
|
|
adjusted-size
|
|
(text-family text)
|
|
(text-style text)
|
|
(text-weight text)
|
|
(text-underline text))]))
|
|
|
|
(define (ellipse-rotated-size ew eh θ)
|
|
(cond
|
|
[(and (zero? ew) (zero? eh))
|
|
(values 0 0)]
|
|
[(zero? eh)
|
|
(values (* (cos θ) ew)
|
|
(* (sin θ) ew))]
|
|
[(zero? ew)
|
|
(values (* (sin θ) eh)
|
|
(* (cos θ) eh))]
|
|
[else
|
|
(let* ([t1 (atan (/ eh ew (exact->inexact (tan θ))))]
|
|
; a*cos(t1),b*sin(t1) is the point on *original* ellipse which gets rotated to top.
|
|
[t2 (atan (/ (* (- eh) (tan θ)) ew))] ; the original point rotated to right side.
|
|
[rotated-height (+ (* ew (sin θ) (cos t1)) (* eh (cos θ) (sin t1)))]
|
|
[rotated-width (- (* ew (cos θ) (cos t2)) (* eh (sin θ) (sin t2)))])
|
|
(values (abs rotated-width)
|
|
(abs rotated-height)))]))
|
|
|
|
(define (degrees->radians θ)
|
|
(* θ 2 pi (/ 360)))
|
|
|
|
(define (mode-color->smoothing mode color)
|
|
(cond
|
|
[(and (eq? mode 'outline)
|
|
(not (pen? color)))
|
|
'aligned]
|
|
[else 'smoothed]))
|
|
|
|
(define (mode-color->pen mode color)
|
|
(cond
|
|
[(eq? mode 'outline)
|
|
(cond
|
|
[(pen? color)
|
|
(pen->pen-obj/cache color)]
|
|
[else
|
|
(send the-pen-list find-or-create-pen (get-color-arg color) 0 'solid 'round 'miter)])]
|
|
[else
|
|
(send the-pen-list find-or-create-pen "black" 1 'transparent)]))
|
|
|
|
(define (mode-color->brush mode color)
|
|
(cond
|
|
[(eq? mode 'outline)
|
|
(send the-brush-list find-or-create-brush "black" 'transparent)]
|
|
[else
|
|
;; this should only be 'solid if we have an old image from a save file somewhere
|
|
(define extra-alpha (if (eq? mode 'solid)
|
|
255
|
|
mode))
|
|
(send the-brush-list find-or-create-brush (get-color-arg color extra-alpha) 'solid)]))
|
|
|
|
(define (get-color-arg color [extra-alpha 255])
|
|
(cond
|
|
[(string? color)
|
|
(define color-obj (or (send the-color-database find-color color)
|
|
(send the-color-database find-color "black")))
|
|
(make-object color%
|
|
(send color-obj red)
|
|
(send color-obj green)
|
|
(send color-obj blue)
|
|
(/ extra-alpha 255))]
|
|
[else
|
|
(make-object color%
|
|
(color-red color)
|
|
(color-green color)
|
|
(color-blue color)
|
|
(* (/ (color-alpha color) 255)
|
|
(/ extra-alpha 255)))]))
|
|
|
|
|
|
(define (pen->pen-obj/cache pen)
|
|
(send the-pen-list find-or-create-pen
|
|
(get-color-arg (pen-color pen))
|
|
(pen-width pen)
|
|
(pen-style pen)
|
|
(pen-cap pen)
|
|
(pen-join pen)))
|
|
|
|
(define (to-img arg)
|
|
(cond
|
|
[(is-a? arg image-snip%) (image-snip->image arg)]
|
|
[(is-a? arg bitmap%) (bitmap->image arg)]
|
|
[else arg]))
|
|
|
|
(define (image-snip->image is)
|
|
(let ([bm (send is get-bitmap)])
|
|
(cond
|
|
[(not bm)
|
|
;; this might mean we have a cache-image-snip%
|
|
;; or it might mean we have a useless snip.
|
|
(let-values ([(w h) (if (is-a? is cis:cache-image-snip%)
|
|
(send is get-size)
|
|
(values 0 0))])
|
|
(make-image (make-polygon
|
|
(list (make-point 0 0)
|
|
(make-point w 0)
|
|
(make-point w h)
|
|
(make-point 0 h))
|
|
'solid "black")
|
|
(make-bb w h h)
|
|
#f))]
|
|
[else
|
|
(bitmap->image bm
|
|
(or (send is get-bitmap-mask)
|
|
(send bm get-loaded-mask)))])))
|
|
|
|
(define (bitmap->image bm [mask-bm (send bm get-loaded-mask)])
|
|
(define w (send bm get-width))
|
|
(define h (send bm get-height))
|
|
(define alpha-bm
|
|
(cond
|
|
[(and (is-a? bm bitmap%)
|
|
(send bm has-alpha-channel?))
|
|
bm]
|
|
[else
|
|
(define bm (make-bitmap w h))
|
|
(define bdc (make-object bitmap-dc% bm))
|
|
(send bdc draw-bitmap bm 0 0 'solid
|
|
(send the-color-database find-color "black")
|
|
mask-bm)
|
|
(send bdc set-bitmap #f)
|
|
bm]))
|
|
(make-image (make-translate (/ w 2)
|
|
(/ h 2)
|
|
(make-ibitmap alpha-bm 0 1 1 (make-hash)))
|
|
(make-bb w h h)
|
|
#f))
|
|
|
|
(define (bitmap-write bitmap port mode)
|
|
(let* ([v (struct->vector bitmap)]
|
|
[recur (case mode
|
|
[(#t) write]
|
|
[(#f) display]
|
|
[else (lambda (p port) (print p port mode))])]
|
|
[update
|
|
(λ (i)
|
|
(let ([o (vector-ref v i)])
|
|
(let ([nv (and o
|
|
(call-with-values (λ () (bitmap->bytes o #f)) vector))])
|
|
(vector-set! v i nv))))])
|
|
(update 1)
|
|
;; don't save the cache
|
|
(vector-set! v 5 (make-hash))
|
|
(recur v port)))
|
|
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
|
|
|
|
(provide make-image image-shape image-bb image-normalized? image%
|
|
|
|
un/cache-image compute-image-cache
|
|
|
|
(struct-out bb)
|
|
(struct-out point)
|
|
make-overlay overlay? overlay-top overlay-bottom
|
|
make-translate translate? translate-dx translate-dy translate-shape
|
|
make-scale scale? scale-x scale-y scale-shape
|
|
make-crop crop? crop-points crop-shape
|
|
make-ellipse ellipse? ellipse-width ellipse-height ellipse-angle ellipse-mode ellipse-color
|
|
make-text text? text-string text-angle text-y-scale text-color
|
|
text-angle text-size text-face text-family text-style text-weight text-underline
|
|
make-polygon polygon? polygon-points polygon-mode polygon-color
|
|
make-line-segment line-segment? line-segment-start line-segment-end line-segment-color
|
|
make-curve-segment curve-segment?
|
|
curve-segment-start curve-segment-s-angle curve-segment-s-pull
|
|
curve-segment-end curve-segment-e-angle curve-segment-e-pull
|
|
curve-segment-color
|
|
make-pen pen? pen-color pen-width pen-style pen-cap pen-join pen
|
|
|
|
make-ibitmap ibitmap? ibitmap-raw-bitmap ibitmap-angle ibitmap-x-scale ibitmap-y-scale
|
|
ibitmap-cache
|
|
|
|
make-flip flip? flip-flipped? flip-shape
|
|
|
|
(except-out (struct-out color) make-color)
|
|
(rename-out [-make-color make-color])
|
|
|
|
degrees->radians
|
|
normalize-shape
|
|
ellipse-rotated-size
|
|
points->ltrb-values
|
|
|
|
image?
|
|
|
|
text->font
|
|
render-image
|
|
save-image-as-bitmap
|
|
|
|
skip-image-equality-fast-path
|
|
render-normalized
|
|
|
|
scale-np-atomic
|
|
|
|
to-img
|
|
bitmap->image
|
|
image-snip->image
|
|
image-snip%)
|
|
|
|
;; method names
|
|
(provide get-shape get-bb get-pinhole get-normalized? get-normalized-shape)
|
|
|
|
(provide np-atomic-shape? atomic-shape? simple-shape? cn-or-simple-shape? normalized-shape?)
|