#lang racket
(require mrlib/image-core)
(require 2htdp/private/image-more)
(require scheme/gui)
(require lang/prim)
(provide real->int
maybe-color?
name->color
)
(provide-higher-order-primitive map-image (f _))
(provide-higher-order-primitive map-masked-image (f _))
(provide-primitive get-pixel-color)
(provide-primitive pixel-visible?)
(provide-higher-order-primitive build-image (_ _ f))
(provide-higher-order-primitive build-masked-image (_ _ f))
(define (maybe-color? thing)
(or (color? thing)
(eqv? thing #f)))
(define (name->color name)
(let [[result (send the-color-database find-color name)]]
(if result
(color%->color result)
#f)))
(define (color->color% c)
(if (string? c)
c
(make-object color%
(color-red c)
(color-green c)
(color-blue c))))
(define (color%->color c)
(make-color (send c red)
(send c green)
(send c blue)))
(define (natural? it)
(and (integer? it)
(>= it 0)))
(define (build-image-internal width height f mask-bm)
(unless (and (natural? width) (natural? height))
(error 'build-image "Expected natural numbers as first two arguments"))
(unless (procedure-arity-includes? f 2)
(error 'build-image "Expected function with contract number number -> color as third argument"))
(let* [[bm (make-object bitmap% width height)]
[bmdc (make-object bitmap-dc% bm)]
]
(for ((y (in-range height)))
(for ((x (in-range width)))
(send bmdc set-pixel x y (color->color% (f x y)))
))
(send bmdc set-bitmap #f)
(make-image
(make-translate (quotient width 2) (quotient height 2)
(make-bitmap bm mask-bm 0 1 1 #f #f))
(make-bb width height height)
#f )
))
(define (build-image width height f)
(unless (and (natural? width) (natural? height))
(error 'build-image "Expected natural numbers as first two arguments"))
(unless (procedure-arity-includes? f 2)
(error 'build-image "Expected function with contract number number -> color as third argument"))
(let* [[mask-bm (make-object bitmap% width height #t)] [mask-bmdc (make-object bitmap-dc% mask-bm)]
[black (make-object color% 0 0 0)]]
(send mask-bmdc set-background black)
(send mask-bmdc clear)
(send mask-bmdc set-bitmap #f)
(build-image-internal width height f mask-bm)
)
)
(define (build-masked-image width height f)
(unless (and (natural? width) (natural? height))
(error 'build-masked-image "Expected natural numbers as first two arguments"))
(unless (procedure-arity-includes? f 2)
(error 'build-masked-image "Expected function with contract number number -> maybe-color as third argument"))
(let* [[bm (make-object bitmap% width height)]
[bmdc (make-object bitmap-dc% bm)]
[mask-bm (make-object bitmap% width height #t)] [mask-bmdc (make-object bitmap-dc% mask-bm)]
[visible (make-object color% 0 0 0)]
[transparent (make-object color% 255 255 255)]]
(for ((y (in-range height)))
(for ((x (in-range width)))
(let* [[mc (f x y)]
[color (if mc (color->color% mc) transparent)]
[mask (if mc visible transparent)]]
(send bmdc set-pixel x y color)
(send mask-bmdc set-pixel x y mask)
)))
(send bmdc set-bitmap #f)
(send mask-bmdc set-bitmap #f)
(make-image
(make-translate (quotient width 2) (quotient height 2)
(make-bitmap bm mask-bm 0 1 1 #f #f))
(make-bb width height height)
#f )
))
(define (real->int num)
(inexact->exact (round num)))
(define (get-px x y bmdc)
(let ((col (make-object color%)))
(send bmdc get-pixel x y col)
(color%->color col)))
(define get-pixel-color
(let [[last-image #f]
[last-bm #f]
[last-bmdc #f]]
(lambda (x y pic)
(unless (eqv? pic last-image)
(set! last-image pic)
(set! last-bm (make-object bitmap% (image-width pic) (image-height pic)))
(set! last-bmdc (make-object bitmap-dc% last-bm))
(render-image pic last-bmdc 0 0))
(if (and (<= 0 x (image-width pic))
(<= 0 y (image-height pic)))
(get-px x y last-bmdc)
(make-color 0 0 0)))))
(define pixel-visible?
(let [[last-image #f]
[last-bm #f]
[last-bmdc #f]]
(lambda (x y pic)
(unless (eqv? pic last-image)
(set! last-image pic)
(set! last-bm (get-mask pic))
(set! last-bmdc (make-object bitmap-dc% last-bm)))
(let [[mask-pix (get-px x y last-bmdc)]] (and (equal? mask-pix (make-color 0 0 0)) (>= x 0)
(>= y 0)
(< x (image-width pic))
(< y (image-height pic))
)))))
(define (get-mask pic)
(let* ((w (image-width pic))
(h (image-height pic))
(bm1 (make-object bitmap% w h))
(bm2 (make-object bitmap% w h))
(mask-bm (make-object bitmap% w h #t))
(dc1 (make-object bitmap-dc% bm1))
(dc2 (make-object bitmap-dc% bm2))
(mask-dc (make-object bitmap-dc% mask-bm))
(white (make-object color% "white"))
(black (make-object color% "black")))
(render-image (rectangle w h "solid" "green") dc1 0 0)
(render-image (rectangle w h "solid" "purple") dc2 0 0)
(render-image pic dc1 0 0)
(render-image pic dc2 0 0)
(for ((x (in-range w)))
(for ((y (in-range h)))
(send mask-dc set-pixel x y
(if (equal? (get-px x y dc1) (get-px x y dc2))
black
white))))
(send mask-dc set-bitmap #f)
mask-bm))
(define (map-image f pic)
(unless (procedure-arity-includes? f 3)
(error 'map-image "Expected function with contract number number color -> color as first argument"))
(unless (image? pic)
(error 'map-image "Expected image as second argument"))
(let* [[width (image-width pic)]
[height (image-height pic)]
[bm (make-object bitmap% width height)]
[bmdc (make-object bitmap-dc% bm)]
[mask (get-mask pic)]
]
(render-image pic bmdc 0 0) (build-image-internal (image-width pic) (image-height pic)
(lambda (x y)
(f x y (get-px x y bmdc)))
mask)))
(define (map-masked-image f pic)
(unless (procedure-arity-includes? f 3)
(error 'map-masked-image "Expected function with contract number number maybe-color -> maybe-color as first argument"))
(unless (image? pic)
(error 'map-masked-image "Expected image as second argument"))
(let* [[width (image-width pic)]
[height (image-height pic)]
[bm (make-object bitmap% width height)]
[bmdc (make-object bitmap-dc% bm)]
[mask (get-mask pic)]
]
(render-image pic bmdc 0 0)
(build-masked-image
width height
(lambda (x y)
(f x y
(if (pixel-visible? x y pic)
(get-pixel-color x y pic)
#f))))))