gui/gui-lib/mrlib/cache-image-snip.rkt
2014-12-02 02:33:07 -05:00

850 lines
29 KiB
Racket

#lang racket/base
(require racket/draw
racket/snip
racket/class
racket/contract
racket/promise
(for-syntax racket/base)
mzlib/string)
(provide cache-image-snip%
cache-image-snip-class%
snip-class
coerce-to-cache-image-snip
snip-size
bitmaps->cache-image-snip)
;; type argb = (make-argb (vectorof rational[between 0 & 255]) int int)
(define-struct argb (vector width height))
#|
The true meaning of an image is a vector of rationals,
between 0 & 255, representing color and alpha channel
information. The vector's contents are analogous to
the last argument to the get-argb-pixels method. That is,
there are (* 4 w h) entries in the vector for an image
of width w and height h, and the entries represent the
alpha, red, green, & blue channels, resp.
When drawn to the screen, the rationals are rounded to
their nearest integer, but the true meaning is kept inside
the image.
note to self:
mask of zero means this image dominates
mask of 255 means this image contributes nothing
black is 0
white is 255
a cleared out bitmap is full of 255s (white)
an alpha of 1 means the pixel value is 0
an alpha of 0 means the pixel value is 255
|#
(define cache-image-snip%
(class image-snip%
;; dc-proc : (union #f ((is-a?/c dc<%>) int[dx] int[dy] -> void))
;; used for direct drawing
(init-field dc-proc)
(define/public (get-dc-proc) dc-proc)
;; argb-proc : ((vectorof rational[0 <= x <= 255]) int[dx] int[dy] -> void)
;; used for drawing into a bitmap
(init-field argb-proc)
(define/public (get-argb-proc) argb-proc)
;; the pinhole's coordinates
(init-field px py)
(when (inexact? px)
(set! px (floor (inexact->exact px))))
(when (inexact? py)
(set! py (floor (inexact->exact py))))
(define/public (get-pinhole) (values px py))
(init-field (width #f)
(height #f))
(define/public (get-size)
(values width height))
;; argb : (union #f argb)
(init-field [argb #f])
;; bitmap : (union #f (is-a?/c bitmap%))
;; the way that this image is be drawn, on its own
(define bitmap #f)
(define/override (copy)
(new cache-image-snip%
(dc-proc dc-proc)
(argb-proc argb-proc)
(width width)
(height height)
(argb argb)
(px px)
(py py)))
;; get-bitmap : -> bitmap or false
;; returns a bitmap showing what the image would look like,
;; if it were drawn
(define/override (get-bitmap)
(cond
[(or (zero? width) (zero? height))
#f]
[else
(unless bitmap
(set! bitmap (argb->bitmap (get-argb))))
bitmap]))
;; get-argb : -> argb
(define/public (get-argb)
(unless argb
(set! argb (make-argb (make-vector (* 4 width height) 255) width height))
(argb-proc argb 0 0))
argb)
;; get-argb/no-compute : -> (union #f argb)
(define/public (get-argb/no-compute)
argb)
(define/override (get-extent dc x y w h descent space lspace rspace)
(set-box/f! w width)
(set-box/f! h height)
(set-box/f! descent 0)
(set-box/f! space 0)
(set-box/f! lspace 0)
(set-box/f! rspace 0))
(define/override (draw dc x y left top right bottom dx dy draw-caret)
(cond
[argb
(let ([bitmap (get-bitmap)])
(when bitmap
(send dc draw-bitmap bitmap x y 'solid
(send the-color-database find-color "black")
(send bitmap get-loaded-mask))))]
[dc-proc
(let ([smoothing (send dc get-smoothing)])
(send dc set-smoothing 'aligned)
(dc-proc dc x y)
(send dc set-smoothing smoothing))]
[else (void)]))
(define/override (write f)
(let ([str (string->bytes/utf-8
(format "~s"
(list (argb-vector (get-argb))
width
height
px
py)))])
(send f put str)))
(define/override (get-num-scroll-steps) (inexact->exact (+ (floor (/ height 20)) 1)))
(define/override (find-scroll-step y) (inexact->exact (floor (/ y 20))))
(define/override (get-scroll-step-offset offset) (* offset 20))
(define/override (equal-to? snip recur)
(if (snip . is-a? . cache-image-snip%)
;; Support extensions of cache-image-snip%:
(send snip other-equal-to? this recur)
;; Use ths object's extension:
(other-equal-to? snip recur)))
(define/override (other-equal-to? snip recur)
(image=? this snip))
(super-new)
(inherit set-snipclass)
(set-snipclass snip-class)))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
;; snip-class
;;
(define cache-image-snip-class%
(class snip-class%
(define/override (read f)
(data->snip (read-from-string (send f get-bytes) (lambda () #f))))
(define/public (data->snip data)
(cond
[(not (list? data)) (make-null-cache-image-snip)]
[(= (length data) 4)
;; this is the case for old save files
;; if the width is zero, the height
;; will automatically also be zero
(let ([argb-vec (list-ref data 0)]
[width (list-ref data 1)]
[px (list-ref data 2)]
[py (list-ref data 3)])
(argb->cache-image-snip (make-argb argb-vec
width
(if (zero? width)
0
(/ (vector-length argb-vec) width 4)))
px
py))]
[(= (length data) 5)
;; this is the new saved data and it has the width and the height separately.
(let ([argb-vec (list-ref data 0)]
[width (list-ref data 1)]
[height (list-ref data 2)]
[px (list-ref data 3)]
[py (list-ref data 4)])
(argb->cache-image-snip (make-argb argb-vec width height) px py))]))
(super-new)))
(define snip-class (new cache-image-snip-class%))
(send snip-class set-version 1)
(send snip-class set-classname (format "~s" `(lib "cache-image-snip.ss" "mrlib")))
;; ***** WARNING: illegal activities **** -- MF
(define the-drscheme-snip-class (get-the-snip-class-list))
(send the-drscheme-snip-class add snip-class)
(provide the-drscheme-snip-class)
;; ***** WARNING: illegal activities ****
(define (make-null-cache-image-snip)
(define size 10)
(define (draw dc dx dy)
(with-pen/brush
dc
"black" 'solid
"black" 'transparent
(send dc draw-ellipse dx dy size size)
(send dc draw-line dx (+ dy size -1) (+ dx size -1) dy)))
(define bm (build-bitmap (lambda (dc) (draw dc 0 0))
size
size))
(new cache-image-snip%
(width size)
(height size)
(draw-proc draw)
(px (/ size 2))
(py (/ size 2))
(argb-proc
(lambda (argb dx dy)
(overlay-bitmap argb size size dx dy bm bm)))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
;; image equality
;;
(define size-dc (delay (make-object bitmap-dc% (make-object bitmap% 1 1))))
(define (snip-size a)
(cond
[(is-a? a cache-image-snip%)
(send a get-size)]
[else
(let* ([dc (force size-dc)]
[wb (box 0)]
[hb (box 0)])
(send a get-extent dc 0 0 wb hb #f #f #f #f)
(values (unbox wb)
(unbox hb)))]))
(define (image=? a-raw b-raw)
(let ([a (coerce-to-cache-image-snip a-raw)]
[b (coerce-to-cache-image-snip b-raw)])
(let-values ([(aw ah) (snip-size a)]
[(bw bh) (snip-size b)]
[(apx apy) (send a get-pinhole)]
[(bpx bpy) (send b get-pinhole)])
(and (= aw bw)
(= ah bh)
(= apx bpx)
(= apy bpy)
(same/alpha? (argb-vector (send a get-argb))
(argb-vector (send b get-argb)))))))
(define (same/alpha? v1 v2)
(let loop ([i (vector-length v1)])
(or (zero? i)
(let ([a1 (vector-ref v1 (- i 4))]
[a2 (vector-ref v2 (- i 4))])
(and (or (= a1 a2 255)
(and (= a1 a2)
(= (vector-ref v1 (- i 3)) (vector-ref v2 (- i 3)))
(= (vector-ref v1 (- i 2)) (vector-ref v2 (- i 2)))
(= (vector-ref v1 (- i 1)) (vector-ref v2 (- i 1)))))
(loop (- i 4)))))))
(define image-snip-cache (make-weak-hasheq))
;; coerce-to-cache-image-snip : image -> (is-a?/c cache-image-snip%)
(define (coerce-to-cache-image-snip snp)
(cond
[(is-a? snp cache-image-snip%) snp]
[(hash-ref image-snip-cache snp (λ () #f)) => values]
[(is-a? snp image-snip%)
(let* ([bmp (send snp get-bitmap)]
[cis
(if bmp
(let ([bmp-mask (or (send bmp get-loaded-mask)
(send snp get-bitmap-mask)
(bitmap->mask bmp))])
(bitmaps->cache-image-snip (copy-bitmap bmp)
(copy-bitmap bmp-mask)
(floor (/ (send bmp get-width) 2))
(floor (/ (send bmp get-height) 2))))
(let-values ([(w h) (snip-size snp)])
(let* ([bmp (make-object bitmap%
(inexact->exact (floor w))
(inexact->exact (floor h)))]
[bdc (make-object bitmap-dc% bmp)])
(send bdc clear)
(send snp draw bdc 0 0 0 0 w h 0 0 'no-caret)
(send bdc set-bitmap #f)
(bitmaps->cache-image-snip bmp
(bitmap->mask bmp)
(floor (/ w 2))
(floor (/ h 2))))))])
(hash-set! image-snip-cache snp cis)
cis)]
[else snp]))
;; copy-bitmap : bitmap -> bitmap
;; does not copy the mask.
(define (copy-bitmap bitmap)
(let* ([w (send bitmap get-width)]
[h (send bitmap get-height)]
[copy (make-object bitmap% w h)]
[a-dc (make-object bitmap-dc% copy)])
(send a-dc clear)
(send a-dc draw-bitmap bitmap 0 0)
(send a-dc set-bitmap #f)
copy))
;; bitmap->mask : bitmap -> bitmap
(define (bitmap->mask bitmap)
(let* ([w (send bitmap get-width)]
[h (send bitmap get-height)]
[s (make-bytes (* 4 w h))]
[new-bitmap (make-object bitmap% w h)]
[dc (make-object bitmap-dc% new-bitmap)])
(send dc clear)
(send dc draw-bitmap bitmap 0 0)
(send dc get-argb-pixels 0 0 w h s)
(let loop ([i (* 4 w h)])
(unless (zero? i)
(let ([r (- i 3)]
[g (- i 2)]
[b (- i 1)])
(unless (and (eq? 255 (bytes-ref s r))
(eq? 255 (bytes-ref s g))
(eq? 255 (bytes-ref s b)))
(bytes-set! s r 0)
(bytes-set! s g 0)
(bytes-set! s b 0))
(loop (- i 4)))))
(send dc set-argb-pixels 0 0 w h s)
(begin0
(send dc get-bitmap)
(send dc set-bitmap #f))))
(define (bitmaps->cache-image-snip color mask px py)
(let ([w (send color get-width)]
[h (send color get-height)])
(new cache-image-snip%
[width w]
[height h]
[dc-proc
(lambda (dc dx dy)
(send dc draw-bitmap color dx dy 'solid
(send the-color-database find-color "black")
mask))]
[argb-proc
(lambda (argb-vector dx dy)
(overlay-bitmap argb-vector dx dy color mask))]
[px px]
[py py])))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
;; misc. utilities
;;
;; takes a bitmap with a mask and flattens the colors and the mask
;; drawing them as they would appear on the screen.
(define (flatten-bitmap bm)
(let* ([w (send bm get-width)]
[h (send bm get-height)]
[new-bm (make-object bitmap% w h)]
[bdc (make-object bitmap-dc% new-bm)])
(send bdc clear)
(send bdc draw-bitmap bm 0 0 'solid
(send the-color-database find-color "black")
(send bm get-loaded-mask))
(send bdc set-bitmap #f)
new-bm))
;; build-bitmap : (dc -> void) number number -> bitmap
(define (build-bitmap draw w h)
(let* ([bm (make-object bitmap% w h)]
[bdc (make-object bitmap-dc% bm)])
(send bdc clear)
; (send bdc set-smoothing 'aligned) ; causes image-inside? to fail in test suite.
(draw bdc)
(send bdc set-bitmap #f)
bm))
(define-syntax (with-pen/brush stx)
(syntax-case stx ()
[(_ dc pen-color pen-style brush-color brush-style code ...)
(syntax
(let ([old-pen (send dc get-pen)]
[old-brush (send dc get-brush)])
(send dc set-pen (send the-pen-list find-or-create-pen pen-color 1 pen-style))
(send dc set-brush (send the-brush-list find-or-create-brush brush-color brush-style))
code ...
(send dc set-pen old-pen)
(send dc set-brush old-brush)))]))
(define (set-box/f! b v) (when (box? b) (set-box! b v)))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
;; argb vector utilties
;;
;; argb->cache-image-snip : argb number number -> cache-image-snip
(define (argb->cache-image-snip argb px py)
(let* ([width (argb-width argb)]
[height (argb-height argb)]
[argb-vector (argb-vector argb)]
[bitmap (argb->bitmap argb)]
[mask (and bitmap (send bitmap get-loaded-mask))])
(new cache-image-snip%
(width width)
(height height)
(argb argb)
(px px)
(py py)
(argb-proc (if (or (zero? width) (zero? height))
void
(lambda (argb dx dy) (overlay-bitmap argb dx dy bitmap mask))))
(dc-proc (if (or (zero? width) (zero? height))
void
(lambda (dc dx dy)
(send dc draw-bitmap bitmap dx dy 'solid
(send the-color-database find-color "black")
mask)))))))
;; argb-vector->bitmap : argb -> bitmap or false
;; flattens the argb vector into a bitmap
(define (argb->bitmap argb)
(let* ([argb-vector (argb-vector argb)]
[w (argb-width argb)]
[h (argb-height argb)])
(cond
[(or (zero? w) (zero? h)) #f]
[else
(let* ([bm (make-object bitmap% w h)]
[mask-bm (make-object bitmap% w h)]
[bdc (new bitmap-dc% (bitmap bm))]
[bytes (make-bytes (vector-length argb-vector) 255)]
[mask-bytes (make-bytes (vector-length argb-vector) 255)])
(let loop ([i (- (vector-length argb-vector) 1)])
(cond
[(zero? (modulo i 4))
(let ([av (round (vector-ref argb-vector i))])
(bytes-set! mask-bytes (+ i 1) av)
(bytes-set! mask-bytes (+ i 2) av)
(bytes-set! mask-bytes (+ i 3) av))]
[else
(bytes-set! bytes i (round (vector-ref argb-vector i)))])
(unless (zero? i)
(loop (- i 1))))
(send bdc set-argb-pixels 0 0 w h bytes)
(send bdc set-bitmap mask-bm)
(send bdc set-argb-pixels 0 0 w h mask-bytes)
(send bdc set-bitmap #f)
(send bm set-loaded-mask mask-bm)
bm)])))
;; overlay-bitmap : argb int int bitmap bitmap -> void
;; assumes that the mask bitmap only has greyscale in it
;; (ie, that looking at the red component of the mask is enough)
(define (overlay-bitmap argb dx dy color mask)
(let* ([argb-vector (argb-vector argb)]
[argb-w (argb-width argb)]
[w (send color get-width)]
[h (send color get-height)]
[color-bytes (make-bytes (* w h 4) 0)]
[mask-bytes (make-bytes (* w h 4) 0)]
[dc (make-object bitmap-dc%)])
(send dc set-bitmap color)
(send dc get-argb-pixels 0 0 w h color-bytes)
(send dc set-bitmap #f) ;; in case mask and color are the same bitmap....
(send dc set-bitmap mask)
(send dc get-argb-pixels 0 0 w h mask-bytes)
(send dc set-bitmap #f)
(let yloop ([y 0]
[str-i 0])
(unless (= y h)
(let xloop ([x 0]
[str-i str-i])
(if (= x w)
(yloop (add1 y) str-i)
(begin
(when (and (<= 0 (+ x dx))
(< (+ x dx) argb-w))
(let ([argb-i (* 4 (+ (+ dx x) (* (+ dy y) argb-w)))])
(when (and (<= 0 argb-i)
(< argb-i (vector-length argb-vector)))
(let* ([m1 (vector-ref argb-vector argb-i)]
[m2 (bytes-ref mask-bytes (+ str-i 1))] ;; get red coordinate
[m3 (build-m3 m1 m2)]
[bang (lambda (i v) (vector-set! argb-vector i (floor v)))]
[do-b
(lambda (off)
(bang (+ argb-i off)
(build-b3 m1
(vector-ref argb-vector (+ argb-i off))
m2
(bytes-ref color-bytes (+ str-i off))
m3)))])
(bang argb-i m3)
(do-b 1)
(do-b 2)
(do-b 3)))))
(xloop (+ x 1) (+ str-i 4)))))))))
#|
From Matthew's computation in PR 6930:
> m3 is (m1+m2-m1*m2) and
> b3 is (m1*b1*(1-m2) + m2*b2)/m3
but that's for values between 0 and 1 and we
need values between 0 and 255. Worse, the values
sense are reversed. That is,
1 above corresponds to 0 in pixel values and
0 above corresponds to 255.
;; the spec
(define (build-m3-0 big-m1 big-m2)
(let ([m1 (- 1 (/ big-m1 255))]
[m2 (- 1 (/ big-m2 255))])
(let ([m3 (+ m1 m2 (- (* m1 m2)))])
(* 255 (- 1 m3)))))
; = substitute in lets
(define (build-m3-1 m1 m2)
(* 255 (- 1 (+ (- 1 (/ m1 255))
(- 1 (/ m2 255))
(- (* (- 1 (/ m1 255))
(- 1 (/ m2 255))))))))
;= multiply out last product
(define (build-m3-2 m1 m2)
(* 255 (- 1 (+ (- 1 (/ m1 255))
(- 1 (/ m2 255))
(- (+ 1
(- (/ m1 255))
(- (/ m2 255))
(* (- (/ m1 255)) (- (/ m2 255)))))))))
; = lift out the neagtives into topmost sum
(define (build-m3-3 m1 m2)
(* 255 (- 1 (+ (- (/ m1 255))
1
(- (/ m2 255))
1
-1
(/ m1 255)
(/ m2 255)
(- (* (/ m1 255) (/ m2 255)))))))
; = push in topmost subtraction
(define (build-m3-4 m1 m2)
(* 255 (+ 1
(/ m1 255)
-1
(/ m2 255)
-1
1
(- (/ m1 255))
(- (/ m2 255))
(* (/ m1 255) (/ m2 255)))))
; = simplify sum:
(define (build-m3-5 m1 m2)
(* 255 (* (/ m1 255) (/ m2 255))))
; = distribute 255
(define (build-m3-6 m1 m2) (* m1 m2 1/255))
(define (test-m3 m1 m2)
(values (build-m3-0 m1 m2)
(build-m3-1 m1 m2)
(build-m3-2 m1 m2)
(build-m3-3 m1 m2)
(build-m3-4 m1 m2)
(build-m3-5 m1 m2)
(build-m3-6 m1 m2)))
(test-m3 0 0)
(test-m3 255 255)
(test-m3 100 200)
for b3, we have:
(define (build-m3-6 m1 m2) (* m1 m2 1/255))
;; the spec
(define (build-b3-0 big-m1 big-b1 big-m2 big-b2 big-m3)
(let ([m1 (- 1 (/ big-m1 255))]
[b1 (- 1 (/ big-b1 255))]
[m2 (- 1 (/ big-m2 255))]
[b2 (- 1 (/ big-b2 255))]
[m3 (- 1 (/ big-m3 255))])
(let ([ans (/ (+ (* m1 b1 (- 1 m2)) (* m2 b2)) m3)])
(* 255 (- 1 ans)))))
;; = substitute in for let.
(define (build-b3-1 m1 b1 m2 b2 m3)
(* 255 (- 1 (/ (+ (* (- 1 (/ m1 255)) (- 1 (/ b1 255)) (- 1 (- 1 (/ m2 255))))
(* (- 1 (/ m2 255)) (- 1 (/ b2 255))))
(- 1 (/ m3 255))))))
;; = simple substitution
(define (build-b3-2 m1 b1 m2 b2 m3)
(* 255 (- 1 (/ (+ (* (- 1 (/ m1 255)) (- 1 (/ b1 255)) (/ m2 255))
(* (- 1 (/ m2 255)) (- 1 (/ b2 255))))
(- 1 (/ m3 255))))))
;; = multiply out first part of first *
(define (build-b3-3 m1 b1 m2 b2 m3)
(* 255 (- 1 (/ (+ (* (+ 1
(- (/ m1 255))
(- (/ b1 255))
(* (/ m1 255) (/ b1 255)))
(/ m2 255))
(* (- 1 (/ m2 255)) (- 1 (/ b2 255))))
(- 1 (/ m3 255))))))
;; = distribute out newly created product
(define (build-b3-4 m1 b1 m2 b2 m3)
(* 255 (- 1 (/ (+ (/ m2 255)
(* (- (/ m1 255)) (/ m2 255))
(* (- (/ b1 255)) (/ m2 255))
(* (/ m1 255) (/ b1 255) (/ m2 255))
(* (- 1 (/ m2 255)) (- 1 (/ b2 255))))
(- 1 (/ m3 255))))))
;; = multiply out product of sum
(define (build-b3-5 m1 b1 m2 b2 m3)
(* 255 (- 1 (/ (+ (/ m2 255)
(* (- (/ m1 255)) (/ m2 255))
(* (- (/ b1 255)) (/ m2 255))
(* (/ m1 255) (/ b1 255) (/ m2 255))
(+ 1
(- (/ m2 255))
(- (/ b2 255))
(* (/ m2 255) (/ b2 255))))
(- 1 (/ m3 255))))))
;; = flatten out sum of sum & simplify
(define (build-b3-6 m1 b1 m2 b2 m3)
(* 255 (- 1 (/ (+ (* (- (/ m1 255)) (/ m2 255))
(* (- (/ b1 255)) (/ m2 255))
(* (/ m1 255) (/ b1 255) (/ m2 255))
1
(- (/ b2 255))
(* (/ m2 255) (/ b2 255)))
(- 1 (/ m3 255))))))
;; = rearrange denom
(define (build-b3-7 m1 b1 m2 b2 m3)
(* 255 (- 1 (/ (+ (* (- (/ m1 255)) (/ m2 255))
(* (- (/ b1 255)) (/ m2 255))
(* (/ m1 255) (/ b1 255) (/ m2 255))
1
(- (/ b2 255))
(* (/ m2 255) (/ b2 255)))
(/ (- 255 m3) 255)))))
;; = move 255 to numerator
(define (build-b3-8 m1 b1 m2 b2 m3)
(* 255 (- 1 (/ (* 255 (+ (* (- (/ m1 255)) (/ m2 255))
(* (- (/ b1 255)) (/ m2 255))
(* (/ m1 255) (/ b1 255) (/ m2 255))
1
(- (/ b2 255))
(* (/ m2 255) (/ b2 255))))
(- 255 m3)))))
;; cancel out 255s in numerator
(define (build-b3-9 m1 b1 m2 b2 m3)
(* 255 (- 1 (/ (+ (* (- m1) (/ m2 255))
(* (- b1) (/ m2 255))
(* m1 (/ b1 255) (/ m2 255))
255
(- b2)
(* m2 (/ b2 255)))
(- 255 m3)))))
;; rearrange numerator
(define (build-b3-10 m1 b1 m2 b2 m3)
(* 255 (- 1 (/ (+ (/ (* (- m1) m2) 255)
(/ (* (- b1) m2) 255)
(/ (* m1 b1 (/ m2 255)) 255)
(/ (* 255 255) 255)
(/ (* 255 (- b2)) 255)
(/ (* m2 b2) 255))
(- 255 m3)))))
;; pull out 255 in num
(define (build-b3-11 m1 b1 m2 b2 m3)
(* 255 (- 1 (/ (/ (+ (* (- m1) m2)
(* (- b1) m2)
(* m1 b1 (/ m2 255))
(* 255 255)
(* 255 (- b2))
(* m2 b2))
255)
(- 255 m3)))))
;; push 255 into denom
(define (build-b3-12 m1 b1 m2 b2 m3)
(* 255 (- 1 (/ (+ (* (- m1) m2)
(* (- b1) m2)
(* m1 b1 (/ m2 255))
(* 255 255)
(* 255 (- b2))
(* m2 b2))
(* 255 (- 255 m3))))))
;; turn 1 into (/ (* 255 (- 255 m3)) (* 255 (- 255 m3)))
;; and add into numerator
(define (build-b3-13 m1 b1 m2 b2 m3)
(* 255 (/ (- (* 255 (- 255 m3))
(+ (* (- m1) m2)
(* (- b1) m2)
(* m1 b1 (/ m2 255))
(* 255 255)
(* 255 (- b2))
(* m2 b2)))
(* 255 (- 255 m3)))))
;; cancel out outer 255
(define (build-b3-14 m1 b1 m2 b2 m3)
(/ (- (* 255 (- 255 m3))
(+ (* (- m1) m2)
(* (- b1) m2)
(* m1 b1 (/ m2 255))
(* 255 255)
(* 255 (- b2))
(* m2 b2)))
(- 255 m3)))
;; push negative thru to make big sum in numerator
(define (build-b3-15 m1 b1 m2 b2 m3)
(/ (+ (* 255 (- 255 m3))
(* m1 m2)
(* b1 m2)
(- (* m1 b1 (/ m2 255)))
(- (* 255 255))
(* 255 b2)
(- (* m2 b2)))
(- 255 m3)))
;; distribute 255 in first num term
(define (build-b3-16 m1 b1 m2 b2 m3)
(/ (+ (* 255 255)
(- (* 255 m3))
(* m1 m2)
(* b1 m2)
(- (* m1 b1 (/ m2 255)))
(- (* 255 255))
(* 255 b2)
(- (* m2 b2)))
(- 255 m3)))
;; simplify num
(define (build-b3-17 m1 b1 m2 b2 m3)
(/ (+ (* m1 m2)
(* b1 m2)
(- (* m2 b2))
(- (* m1 b1 m2 1/255))
(* 255 b2)
(- (* 255 m3)))
(- 255 m3)))
;; simplify num, some more
(define (build-b3-18 m1 b1 m2 b2 m3)
(/ (+ (* (+ m1 b1 (- b2)) m2)
(* m1 b1 m2 -1/255)
(* 255 b2)
(* -255 m3))
(- 255 m3)))
(define (test-b3 m1 b1 m2 b2)
(let ([m3 (build-m3-6 m1 m2)])
(values (build-b3-0 m1 b1 m2 b2 m3)
(build-b3-1 m1 b1 m2 b2 m3)
(build-b3-2 m1 b1 m2 b2 m3)
(build-b3-3 m1 b1 m2 b2 m3)
(build-b3-4 m1 b1 m2 b2 m3)
(build-b3-5 m1 b1 m2 b2 m3)
(build-b3-6 m1 b1 m2 b2 m3)
(build-b3-7 m1 b1 m2 b2 m3)
(build-b3-8 m1 b1 m2 b2 m3)
(build-b3-9 m1 b1 m2 b2 m3)
(build-b3-10 m1 b1 m2 b2 m3)
(build-b3-11 m1 b1 m2 b2 m3)
(build-b3-12 m1 b1 m2 b2 m3)
(build-b3-13 m1 b1 m2 b2 m3)
(build-b3-14 m1 b1 m2 b2 m3)
(build-b3-15 m1 b1 m2 b2 m3)
(build-b3-16 m1 b1 m2 b2 m3)
(build-b3-17 m1 b1 m2 b2 m3)
(build-b3-18 m1 b1 m2 b2 m3)
)))
(test-b3 255 100 0 250)
(test-b3 0 150 255 100)
(test-b3 100 200 75 150)
|#
(define (build-m3 m1 m2) (* m1 m2 1/255))
(define (build-b3 m1 b1 m2 b2 m3)
(if (= m3 255)
0
(/ (+ (* (+ m1 b1 (- b2)) m2)
(* m1 b1 m2 -1/255)
(* 255 b2)
(* -255 m3))
(- 255 m3))))
(define bitmap-size/c (and/c integer? exact? (between/c 1 10000)))
(provide/contract
[overlay-bitmap (argb? exact-integer?
exact-integer?
(is-a?/c bitmap%)
(is-a?/c bitmap%)
. -> .
any)]
[build-bitmap (((is-a?/c dc<%>) . -> . any) bitmap-size/c bitmap-size/c . -> . (is-a?/c bitmap%))]
[flatten-bitmap ((is-a?/c bitmap%) . -> . (is-a?/c bitmap%))]
[argb->cache-image-snip (argb? number? number? . -> . (is-a?/c cache-image-snip%))]
[argb->bitmap (argb? . -> . (or/c #f (is-a?/c bitmap%)))]
[argb? (any/c . -> . boolean?)]
[make-argb ((vectorof byte?) exact-nonnegative-integer? exact-nonnegative-integer? . -> . argb?)]
[argb-vector (argb? . -> . (vectorof byte?))]
[argb-width (argb? . -> . exact-nonnegative-integer?)]
[argb-height (argb? . -> . exact-nonnegative-integer?)])