Now make-flomap* and effects functions accept (U (Vectorof Real) FlVector) for color instead of just FlVector

New vector-of-component functions: unsafe-flomap-ref*, flomap-ref*, flomap-bilinear-ref*, build-flomap*, inline-build-flomap*;
REPL interaction with -ref* is a bit weird - still need to make flvectors print nicely

Bilinear ref and scaling fix on top and left borders (bad/missing special cases);
Clarified flomap-bilinear-ref docs on extent of interpolated nonzero rectangle

Various doc fixes
This commit is contained in:
Neil Toronto 2012-05-30 15:04:19 -06:00
parent 4ad700021b
commit 91579f2a6e
6 changed files with 313 additions and 113 deletions

View File

@ -31,8 +31,7 @@
(match-define (flomap _ 4 w h) argb-fm)
(match-define (flomap _ 1 zw zh) z-fm)
(unless (and (= w zw) (= h zh))
(error 'deep-flomap
"expected flomaps of equal dimension; given dimensions ~e×~e and ~e×~e" w h zw zh))
(error 'deep-flomap "expected same-size flomaps; given sizes ~e×~e and ~e×~e" w h zw zh))
(values argb-fm z-fm)))
(: flomap->deep-flomap (flomap -> deep-flomap))

View File

@ -13,13 +13,13 @@
flomap-shadow flomap-shadowed
flomap-whirl-morph)
(: colorize-alpha (flomap FlVector -> flomap))
(: colorize-alpha (flomap (U (Vectorof Real) FlVector) -> flomap))
(define (colorize-alpha fm vs)
(match-define (flomap _ 1 w h) fm)
(flomap-append-components fm (fm* fm (make-flomap* w h vs))))
(: flomap-shadow (case-> (flomap Real -> flomap)
(flomap Real (Option FlVector) -> flomap)))
(flomap Real (Option (U (Vectorof Real) FlVector)) -> flomap)))
(define flomap-shadow
(case-lambda
[(fm σ) (flomap-shadow fm σ #f)]
@ -27,18 +27,18 @@
(match-define (flomap _ c w h) fm)
(cond [(c . = . 0) fm]
[else (define alpha-fm (flomap-ref-component fm 0))
(define color-vs (if (flvector? color) color (make-flvector (- c 1) 0.0)))
(define color-vs (if color color (make-flvector (- c 1) 0.0)))
(colorize-alpha (flomap-blur alpha-fm σ) color-vs)])]))
(: flomap-shadowed (case-> (flomap Real -> flomap)
(flomap Real (Option FlVector) -> flomap)))
(flomap Real (Option (U (Vectorof Real) FlVector)) -> flomap)))
(define flomap-shadowed
(case-lambda
[(fm σ) (flomap-shadowed fm σ #f)]
[(fm σ c) (flomap-cc-superimpose (flomap-shadow fm σ c) fm)]))
(: flomap-outline (case-> (flomap Real -> flomap)
(flomap Real (Option FlVector) -> flomap)))
(flomap Real (Option (U (Vectorof Real) FlVector)) -> flomap)))
(define flomap-outline
(case-lambda
[(fm amt) (flomap-outline fm amt #f)]
@ -57,11 +57,11 @@
(define alpha-fm (flomap-ref-component fm 0))
(define new-alpha-fm (fmmax 0.0 (fmmin 1.0 (fm/ (fm- (flomap-blur alpha-fm σ) v-min)
(- v-max v-min)))))
(define color-vs (if (flvector? color) color (make-flvector (- c 1) 0.0)))
(define color-vs (if color color (make-flvector (- c 1) 0.0)))
(colorize-alpha new-alpha-fm color-vs))]))
(: flomap-outlined (case-> (flomap Real -> flomap)
(flomap Real (Option FlVector) -> flomap)))
(flomap Real (Option (U (Vectorof Real) FlVector)) -> flomap)))
(define flomap-outlined
(case-lambda
[(fm amt) (flomap-outlined fm amt #f)]

View File

@ -164,9 +164,9 @@
[else (let ([height (abs height)])
(flomap-scale*-y fm (abs (exact->inexact (/ height h))) height))]))
;; standard deviation of an unscaled box filter (i.e. f([-1/2,1/2]) = {1}, zero elsewhere)
;; variance of an unscaled box filter (i.e. f([-1/2,1/2]) = {1}, zero elsewhere)
(define box-filter-variance (/ 1.0 12.0))
;; standard deviation of an unscaled triangle filter (simulates effect of linear interpolation)
;; variance of an unscaled triangle filter (simulates effect of linear interpolation)
(define triangle-filter-variance (/ 1.0 24.0))
;; calculates the standard deviation of downscaling blur, assuming linear interpolation will be
@ -175,7 +175,7 @@
(define (stddev-for-scale scale)
(define var (- (/ box-filter-variance (sqr scale))
triangle-filter-variance))
(abs (flsqrt (max 0.0 var))))
(flsqrt (max 0.0 var)))
(: flomap-scale*-x (flomap Flonum Exact-Nonnegative-Integer -> flomap))
(define (flomap-scale*-x fm scale width)
@ -196,17 +196,18 @@
(: flomap-scale*-x/linear (flomap Flonum Exact-Nonnegative-Integer -> flomap))
(define (flomap-scale*-x/linear fm s new-w)
(match-define (flomap vs c w h) fm)
(define w-1 (fx- w 1))
(define w-1 (unsafe-fx+ w -1))
(inline-build-flomap
c new-w h
(λ (k new-x y _i)
(define scaled-x (- (/ (+ (fx->fl new-x) 0.5) s) 0.5))
(define floor-scaled-x (floor scaled-x))
(define x0 (fl->fx floor-scaled-x))
(cond [(or (x0 . fx< . 0) (x0 . fx>= . w)) 0.0]
(cond [(or (x0 . fx< . -1) (x0 . fx>= . w)) 0.0]
[else
(define i0 (coords->index c w k x0 y))
(define v0 (flvector-ref vs i0))
(define v0 (cond [(x0 . fx= . -1) 0.0]
[else (flvector-ref vs i0)]))
(define v1 (cond [(x0 . fx= . w-1) 0.0]
[else (flvector-ref vs (unsafe-fx+ i0 c))]))
(fl-convex-combination v0 v1 (- scaled-x floor-scaled-x))]))))
@ -214,7 +215,7 @@
(: flomap-scale*-y/linear (flomap Flonum Exact-Nonnegative-Integer -> flomap))
(define (flomap-scale*-y/linear fm s new-h)
(match-define (flomap vs c w h) fm)
(define h-1 (fx- h 1))
(define h-1 (unsafe-fx+ h -1))
(define cw (* c w))
(inline-build-flomap
c w new-h
@ -222,10 +223,11 @@
(define scaled-y (- (/ (+ (fx->fl new-y) 0.5) s) 0.5))
(define floor-scaled-y (floor scaled-y))
(define y0 (fl->fx floor-scaled-y))
(cond [(or (y0 . fx< . 0) (y0 . fx>= . h)) 0.0]
(cond [(or (y0 . fx< . -1) (y0 . fx>= . h)) 0.0]
[else
(define i0 (coords->index c w k x y0))
(define v0 (flvector-ref vs i0))
(define v0 (cond [(y0 . fx= . -1) 0.0]
[else (flvector-ref vs i0)]))
(define v1 (cond [(y0 . fx= . h-1) 0.0]
[else (flvector-ref vs (unsafe-fx+ i0 cw))]))
(fl-convex-combination v0 v1 (- scaled-y floor-scaled-y))]))))

View File

@ -9,9 +9,12 @@
(provide flomap flomap? flomap-values flomap-components flomap-width flomap-height
;; Accessors
flomap-size unsafe-flomap-ref flomap-ref flomap-bilinear-ref coords->index
flomap-size coords->index
unsafe-flomap-ref flomap-ref flomap-bilinear-ref
unsafe-flomap-ref* flomap-ref* flomap-bilinear-ref*
;; Basic constructors
make-flomap make-flomap* build-flomap inline-build-flomap
make-flomap build-flomap inline-build-flomap
make-flomap* build-flomap* inline-build-flomap*
flomap-ref-component flomap-take-components flomap-drop-components flomap-append-components)
(struct: flomap ([values : FlVector] [components : Integer] [width : Integer] [height : Integer])
@ -43,6 +46,19 @@
(unsafe-flvector-ref vs (coords->index c w k x y))]
[else 0.0]))
(: unsafe-flomap-ref* (FlVector Integer Integer Integer Integer Integer -> FlVector))
(define (unsafe-flomap-ref* vs c w h x y)
(cond [(and (x . fx>= . 0) (x . fx< . w)
(y . fx>= . 0) (y . fx< . h))
(define i (coords->index c w 0 x y))
(define point-vs (make-flvector c))
(let: loop : Void ([k : Nonnegative-Fixnum 0])
(when (k . < . c)
(unsafe-flvector-set! point-vs k (unsafe-flvector-ref vs (unsafe-fx+ i k)))
(loop (unsafe-fx+ k 1))))
point-vs]
[else (make-flvector c 0.0)]))
(: flomap-ref (flomap Integer Integer Integer -> Flonum))
(define (flomap-ref fm k x y)
(match-define (flomap vs c w h) fm)
@ -50,6 +66,11 @@
(raise-type-error 'flomap-ref (format "nonnegative fixnum < ~e" c) k))
(unsafe-flomap-ref vs c w h k x y))
(: flomap-ref* (flomap Integer Integer -> FlVector))
(define (flomap-ref* fm x y)
(match-define (flomap vs c w h) fm)
(unsafe-flomap-ref* vs c w h x y))
) ; begin-encourage-inline
(: flomap-bilinear-ref (flomap Integer Real Real -> Flonum))
@ -58,14 +79,14 @@
(cond [(and (k . >= . 0) (k . < . c))
(let ([x (- (exact->inexact x) 0.5)]
[y (- (exact->inexact y) 0.5)])
(cond [(and (x . > . -0.5) (x . < . (+ 0.5 (->fl w)))
(y . > . -0.5) (y . < . (+ 0.5 (->fl h))))
(cond [(and (x . > . -1.0) (x . < . (->fl w))
(y . > . -1.0) (y . < . (->fl h)))
(define floor-x (floor x))
(define floor-y (floor y))
(define x0 (fl->fx floor-x))
(define y0 (fl->fx floor-y))
(define x1 (fx+ x0 1))
(define y1 (fx+ y0 1))
(define x1 (unsafe-fx+ x0 1))
(define y1 (unsafe-fx+ y0 1))
(define v00 (unsafe-flomap-ref vs c w h k x0 y0))
(define v10 (unsafe-flomap-ref vs c w h k x1 y0))
(define v01 (unsafe-flomap-ref vs c w h k x0 y1))
@ -78,6 +99,40 @@
[else
(raise-type-error 'flomap-bilinear-ref (format "nonnegative fixnum < ~e" c) 1 fm k x y)]))
(: flomap-bilinear-ref* (flomap Real Real -> FlVector))
(define (flomap-bilinear-ref* fm x y)
(match-define (flomap vs c w h) fm)
(let ([x (- (exact->inexact x) 0.5)]
[y (- (exact->inexact y) 0.5)])
(cond [(and (x . > . -1.0) (x . < . (->fl w))
(y . > . -1.0) (y . < . (->fl h)))
(define floor-x (floor x))
(define floor-y (floor y))
(define x0 (fl->fx floor-x))
(define y0 (fl->fx floor-y))
(define x1 (unsafe-fx+ x0 1))
(define y1 (unsafe-fx+ y0 1))
(define vs00 (unsafe-flomap-ref* vs c w h x0 y0))
(define vs10 (unsafe-flomap-ref* vs c w h x1 y0))
(define vs01 (unsafe-flomap-ref* vs c w h x0 y1))
(define vs11 (unsafe-flomap-ref* vs c w h x1 y1))
(define xα (- x floor-x))
(define yα (- y floor-y))
(define point-vs (make-flvector c))
(let: loop : FlVector ([k : Nonnegative-Fixnum 0])
(cond [(k . < . c)
(define v00 (unsafe-flvector-ref vs00 k))
(define v10 (unsafe-flvector-ref vs10 k))
(define v01 (unsafe-flvector-ref vs01 k))
(define v11 (unsafe-flvector-ref vs11 k))
(define v (fl-convex-combination (fl-convex-combination v00 v10 xα)
(fl-convex-combination v01 v11 xα)
yα))
(unsafe-flvector-set! point-vs k v)
(loop (unsafe-fx+ k 1))]
[else point-vs]))]
[else (make-flvector c 0.0)])))
;; ===================================================================================================
;; Construction and conversion
@ -98,7 +153,8 @@
[w : Integer width]
[h : Integer height])
(with-asserts ([c nonnegative-fixnum?] [w nonnegative-fixnum?] [h nonnegative-fixnum?])
(define vs (make-flvector (* c w h)))
(define fm (make-flomap c w h))
(define vs (flomap-values fm))
(let: y-loop : flomap ([y : Nonnegative-Fixnum 0] [i : Nonnegative-Fixnum 0])
(cond
[(y . fx< . h)
@ -111,19 +167,63 @@
(k-loop (unsafe-fx+ k 1) (unsafe-fx+ i 1))]
[else (x-loop (unsafe-fx+ x 1) i)]))]
[else (y-loop (unsafe-fx+ y 1) i)]))]
[else (flomap vs c w h)])))))
[else fm])))))
(: build-flomap (Integer Integer Integer
(Nonnegative-Fixnum Nonnegative-Fixnum Nonnegative-Fixnum
Nonnegative-Fixnum -> Real)
(Nonnegative-Fixnum Nonnegative-Fixnum Nonnegative-Fixnum -> Real)
-> flomap))
(define (build-flomap components width height fun)
(inline-build-flomap components width height (λ (k x y i) (exact->inexact (fun k x y i)))))
(define (build-flomap c w h f)
(inline-build-flomap c w h (λ (k x y i) (exact->inexact (f k x y)))))
(: make-flomap* (Integer Integer FlVector -> flomap))
#;
(: inline-build-flomap* (Integer Integer Integer
(Nonnegative-Fixnum Nonnegative-Fixnum Nonnegative-Fixnum
-> FlVector)
-> flomap))
(define-syntax-rule (inline-build-flomap* components width height f)
(let: ([c : Integer components]
[w : Integer width]
[h : Integer height])
(with-asserts ([c nonnegative-fixnum?] [w nonnegative-fixnum?] [h nonnegative-fixnum?])
(define fm (make-flomap c w h))
(define vs (flomap-values fm))
(let: y-loop : flomap ([y : Nonnegative-Fixnum 0] [i : Nonnegative-Fixnum 0])
(cond
[(y . fx< . h)
(let: x-loop : flomap ([x : Nonnegative-Fixnum 0] [i : Nonnegative-Fixnum i])
(cond
[(x . fx< . w)
(define: point-vs : FlVector (f x y i))
(cond
[(fx= (flvector-length point-vs) c)
(let: k-loop : flomap ([k : Nonnegative-Fixnum 0] [i : Nonnegative-Fixnum i])
(cond
[(k . fx< . c) (unsafe-flvector-set! vs i (unsafe-flvector-ref point-vs k))
(k-loop (unsafe-fx+ k 1) (unsafe-fx+ i 1))]
[else (x-loop (unsafe-fx+ x 1) i)]))]
[else (raise-type-error 'inline-build-flomap* (format "length-~e FlVector" c)
point-vs)])]
[else (y-loop (unsafe-fx+ y 1) i)]))]
[else fm])))))
(: make-flomap* (Integer Integer (U (Vectorof Real) FlVector) -> flomap))
(define (make-flomap* w h vs)
(define c (flvector-length vs))
(inline-build-flomap c w h (λ (k _x _y _i) (unsafe-flvector-ref vs k))))
(let ([vs (->flvector vs)])
(define c (flvector-length vs))
(inline-build-flomap* c w h (λ (_x _y _i) vs))))
(: build-flomap* (Integer Integer Integer
(Nonnegative-Fixnum Nonnegative-Fixnum -> (U (Vectorof Real) FlVector))
-> flomap))
(define (build-flomap* c w h f)
(inline-build-flomap*
c w h
(λ (x y i)
(define point-vs0 (f x y))
(define point-vs1 (->flvector point-vs0))
(cond [(fx= (flvector-length point-vs1) c) point-vs1]
[else (raise-type-error 'build-flomap* (format "length-~e Vector or FlVector" c)
point-vs0)]))))
(: flomap-ref-component (flomap Integer -> flomap))
(define (flomap-ref-component fm k)
@ -155,8 +255,7 @@
(match-define (flomap vs1 d1 w1 h1) fm1)
(match-define (flomap vs2 d2 w2 h2) fm2)
(unless (and (= w1 w2) (= h1 h2))
(error 'flomap-append-components
"expected flomaps with equal dimension; given dimensions ~e×~e and ~e×~e"
(error 'flomap-append-components "expected same-size flomaps; given sizes ~e×~e and ~e×~e"
w1 h1 w2 h2))
(inline-build-flomap (fx+ d1 d2) w1 h1
(λ (k x y _i)

View File

@ -6,7 +6,10 @@
[flvector-set! old:flvector-set!])
(except-in racket/fixnum fl->fx fx->fl) ; these two functions are untyped
racket/math
(only-in racket/unsafe/ops unsafe-flvector-set! unsafe-fx+)
(only-in racket/unsafe/ops
unsafe-flvector-set! unsafe-flvector-ref
unsafe-vector-set! unsafe-vector-ref
unsafe-fx+)
racket/performance-hint)
(provide (all-defined-out)
@ -35,8 +38,31 @@
(loop (unsafe-fx+ i 1))]
[else vs])))))
(: flvector->vector (FlVector -> (Vectorof Flonum)))
(define (flvector->vector vs)
(define n (flvector-length vs))
(define new-vs (make-vector n 0.0))
(let: loop : (Vectorof Flonum) ([k : Nonnegative-Fixnum 0])
(cond [(k . < . n) (unsafe-vector-set! new-vs k (unsafe-flvector-ref vs k))
(loop (unsafe-fx+ k 1))]
[else new-vs])))
(: real-vector->flvector ((Vectorof Real) -> FlVector))
(define (real-vector->flvector vs)
(define n (vector-length vs))
(define new-vs (make-flvector n 0.0))
(let: loop : FlVector ([k : Nonnegative-Fixnum 0])
(cond [(k . < . n) (unsafe-flvector-set! new-vs k (exact->inexact (unsafe-vector-ref vs k)))
(loop (unsafe-fx+ k 1))]
[else new-vs])))
(begin-encourage-inline
(: ->flvector ((U (Vectorof Real) FlVector) -> FlVector))
(define (->flvector vs)
(cond [(flvector? vs) vs]
[else (real-vector->flvector vs)]))
(: fx->fl (Fixnum -> Flonum))
(define fx->fl ->fl)

View File

@ -29,19 +29,19 @@ The following flomap @racket[fm] is used in various examples:
@interaction[#:eval flomap-eval
(define fm
(draw-flomap
(λ (bm-dc)
(send bm-dc set-alpha 0)
(send bm-dc set-background "black")
(send bm-dc clear)
(send bm-dc set-alpha 1/3)
(send bm-dc translate 2 2)
(send bm-dc set-pen "black" 4 'long-dash)
(send bm-dc set-brush "red" 'solid)
(send bm-dc draw-ellipse 0 0 192 192)
(send bm-dc set-brush "green" 'solid)
(send bm-dc draw-ellipse 64 0 192 192)
(send bm-dc set-brush "blue" 'solid)
(send bm-dc draw-ellipse 32 44 192 192))
(λ (fm-dc)
(send fm-dc set-alpha 0)
(send fm-dc set-background "black")
(send fm-dc clear)
(send fm-dc set-alpha 1/3)
(send fm-dc translate 2 2)
(send fm-dc set-pen "black" 4 'long-dash)
(send fm-dc set-brush "red" 'solid)
(send fm-dc draw-ellipse 0 0 192 192)
(send fm-dc set-brush "green" 'solid)
(send fm-dc draw-ellipse 64 0 192 192)
(send fm-dc set-brush "blue" 'solid)
(send fm-dc draw-ellipse 32 44 192 192))
260 240))
(flomap->bitmap fm)]
It is typical to use @racket[flomap->bitmap] to visualize a flomap at the REPL.
@ -68,7 +68,7 @@ There are three main reasons to use flomaps:
@item{@bold{Range.}
A floating-point value can also represent about 4.6 quintillion distinct intensities above saturation (@racket[1.0]).
If distinguishing oversaturated values is important, flomaps have the range for it.
Further, floating-point images are (approximately) closed under pointwise arithmetic.
Further, floating-point images are closed under pointwise arithmetic (up to floating-point error).
}
@item{@bold{Speed.}
The @racketmodname[images/flomap] module benefits greatly from Typed Racket's type-directed optimizations.
@ -76,7 +76,7 @@ There are three main reasons to use flomaps:
}
)
For these reasons, other parts of the @racket[images] library use flomaps internally, to represent and operate on
ARGB and RGB images, light maps, shadow maps, height maps, and normal maps.
RGB and ARGB images, light maps, shadow maps, height maps, and normal maps.
@subsection[#:tag "flomap:conceptual"]{Conceptual Model}
@ -88,23 +88,24 @@ The following example creates a 10×10 bitmap with RGB components, and indexes i
It also attempts to index component @racket[3], which doesn't exist.
Note that @racket[flomap-ref] accepts its coordinate arguments in a standard order: @racket[k] @racket[x] @racket[y] (with @racket[k] for @bold{k}omponent).
@interaction[#:eval flomap-eval
(define magenta-fm (make-flomap* 10 10 (flvector 1.0 0.0 1.0)))
(define magenta-fm (make-flomap* 10 10 #(0.5 0.0 1.0)))
(flomap->bitmap magenta-fm)
(flomap-ref magenta-fm 0 0 0)
(flomap-ref magenta-fm 0 -1 0)
(flomap-ref magenta-fm 0 0 1000)
(flomap-ref* magenta-fm 0 0)
(flomap-ref* magenta-fm -1 0)
(flomap-ref* magenta-fm 0 1000)
(flomap-ref magenta-fm 3 0 0)]
Many flomap functions, such as @racket[flomap-bilinear-ref], treat their arguments as if every @italic{real} @racket[x] @racket[y] coordinate has values.
In all such cases, known nonzero values are at half-integer coordinates and others are interpolated.
@examples[#:eval flomap-eval
(flomap-bilinear-ref magenta-fm 0 0.5 0.5)
(flomap-bilinear-ref magenta-fm 0 0.25 0.25)
(flomap-bilinear-ref magenta-fm 0 0.0 0.0)]
(flomap-bilinear-ref* magenta-fm 0.5 0.5)
(flomap-bilinear-ref* magenta-fm 0.25 0.25)
(flomap-bilinear-ref* magenta-fm 0.0 0.0)
(flomap-bilinear-ref* magenta-fm -0.25 -0.25)]
This conceptual model allows us to treat flomaps as if they were multi-valued functions on @racket[Real]×@racket[Real].
For example, we might plot the red component of an icon:
For example, we might plot the red component of an ARGB icon:
@interaction[#:eval flomap-eval
(require images/icons/misc plot)
(define icon-fm (bomb-flomap "azure" "orange" 48))
@ -112,7 +113,7 @@ For example, we might plot the red component of an icon:
(define-values (icon-width icon-height) (flomap-size icon-fm))
(plot3d-bitmap (contour-intervals3d
(λ (x y) (flomap-bilinear-ref icon-fm 1 x y))
0 icon-width 0 icon-height))]
-0.5 (+ 0.5 icon-width) -0.5 (+ 0.5 icon-height)))]
Notice that the plot's maximum height is above saturation (@racket[1.0]).
The tallest peak corresponds to the specular highlight (the shiny part) on the bomb.
Specular highlights are one case where it is important to operate on oversaturated values without truncating them---until it is time to display the image.
@ -143,10 +144,10 @@ As an example of the last point, consider blur.
The following example creates an alpha-multiplied flomap using @racket[draw-flomap].
It blurs the flomap using a general-purpose (i.e. non-alpha-aware) blur function, then converts the flomap to non-alpha-multiplied and does the same.
@interaction[#:eval flomap-eval
(define circle-fm (draw-flomap (λ (dc)
(send dc set-pen "black" 1 'transparent)
(send dc set-brush "green" 'solid)
(send dc draw-ellipse 10 10 30 30))
(define circle-fm (draw-flomap (λ (fm-dc)
(send fm-dc set-pen "black" 1 'transparent)
(send fm-dc set-brush "green" 'solid)
(send fm-dc draw-ellipse 10 10 30 30))
50 50))
(flomap->bitmap circle-fm)
(flomap->bitmap (flomap-blur circle-fm 4 4))
@ -239,10 +240,17 @@ Returns the width and height of @racket[fm] as nonnegative fixnums.
@defproc[(flomap-ref [fm flomap] [k Integer] [x Integer] [y Integer]) Float]{
Returns @racket[fm]'s value at @racket[k] @racket[x] @racket[y].
If @racket[x] or @racket[y] is out of bounds, this function returns @racket[0.0].
If @racket[k] is out of bounds, it raises an error.
See @secref{flomap:conceptual} to read about why.
The @secref{flomap:conceptual} section explains why @racket[k] is treated differently.
}
@defproc[(flomap-ref* [fm flomap] [x Integer] [y Integer]) FlVector]{
Returns @racket[fm]'s component values at @racket[x] @racket[y] as an flvector.
If @racket[x] or @racket[y] is out of bounds, this function returns an flvector filled with @racket[0.0].
It always returns an flvector of length @racket[(flomap-components fm)].
}
@defproc[(flomap-bilinear-ref [fm flomap] [k Integer] [x Real] [y Real]) Float]{
@ -252,9 +260,14 @@ Like all other @racket[flomap] functions that operate on real-valued coordinates
Mathematically, if @racket[x] = @racket[(+ i 0.5)] and @racket[y] = @racket[(+ j 0.5)] for any integers @racket[i] and @racket[j],
then @racket[(flomap-bilinear-ref fm k x y)] = @racket[(flomap-ref fm k i j)].
If @racket[x] or @racket[y] is out of bounds, this function returns @racket[0.0].
Suppose @racket[fm] is size @racket[w]×@racket[h].
If @racket[x] ≤ @racket[-0.5] or @racket[x] ≥ @racket[(+ w 0.5)], this function returns @racket[0.0]; similarly for @racket[y] and @racket[h].
If @racket[k] is out of bounds, it raises an error.
See @secref{flomap:conceptual} to read about why.
The @secref{flomap:conceptual} section explains why @racket[k] is treated differently.
}
@defproc[(flomap-bilinear-ref* [fm flomap] [x Real] [y Real]) FlVector]{
Like @racket[flomap-bilinear-ref], but returns an flvector containing estimates of all the components at @racket[x] @racket[y].
}
@defproc[(flomap-min-value [fm flomap]) Float]
@ -291,6 +304,12 @@ This function is used by some library functions, such as @racket[flomap-bilinear
From untyped code, applying this function is likely no faster than applying @racket[flomap-ref], because of extra contract checks.
}
@defproc[(unsafe-flomap-ref* [vs FlVector]
[c Integer] [w Integer] [h Integer]
[x Integer] [y Integer]) FlVector]{
Like @racket[unsafe-flomap-ref], but returns an flvector containing all the component values at @racket[x] @racket[y].
}
@; ===================================================================================================
@ -316,6 +335,8 @@ See @secref{flomap:opacity} for a discussion of opacity (alpha) representation.
A zero-size @racket[fm] is padded by one point in any zero direction before conversion.
For example, if @racket[fm] is size 0×1, the result of @racket[(flomap->bitmap fm)] is size 1×1.
Values are clamped to between @racket[0.0] and @racket[1.0] before conversion.
}
@defproc[(bitmap->flomap [bm Any]) flomap]{
@ -327,52 +348,68 @@ The argument type is imprecise because Typed Racket does not support the object
@defproc[(make-flomap [c Integer] [w Integer] [h Integer] [v Real 0.0]) flomap]{
Returns a @racket[w]×@racket[h] flomap with @racket[c] components, with every value initialized to @racket[v].
Analogous to @racket[make-vector].
To create flomaps filled with a solid color, use @racket[make-flomap*].
}
@defproc[(make-flomap* [w Integer] [h Integer] [vs FlVector]) flomap]{
Returns a @racket[w]×@racket[h] flomap with @racket[(flvector-length vs)] color components, with each known point initialized using the values in @racket[vs].
@defproc[(make-flomap* [w Integer] [h Integer] [vs (U (Vectorof Real) FlVector)]) flomap]{
Returns a @racket[w]×@racket[h] flomap with each point's components initialized using the values in @racket[vs].
Analogous to @racket[make-vector].
The following two examples create magenta bitmaps with an alpha channel:
The following two examples create an RGB and an ARGB flomap:
@interaction[#:eval flomap-eval
(flomap->bitmap (make-flomap* 100 100 (flvector 1.0 1.0 0.0 1.0)))
(flomap->bitmap (make-flomap* 100 100 (flvector 0.5 0.5 0.0 0.5)))]
(flomap->bitmap (make-flomap* 100 100 #(0.5 0.0 1.0)))
(flomap->bitmap (make-flomap* 100 100 #(0.5 0.25 0.0 0.5)))]
See @secref{flomap:opacity} for a discussion of opacity (alpha) representation.
}
@defproc[(build-flomap [c Integer] [w Integer] [h Integer]
[f (Nonnegative-Fixnum Nonnegative-Fixnum Nonnegative-Fixnum
Nonnegative-Fixnum -> Real)]) flomap]{
[f (Nonnegative-Fixnum Nonnegative-Fixnum Nonnegative-Fixnum -> Real)]) flomap]{
Returns a @racket[w]×@racket[h] flomap with @racket[c] color components, with values defined by @racket[f].
Analogous to @racket[build-vector].
The function @racket[f] receives three arguments @racket[k] @racket[x] @racket[y]: the color component and two positional coordinates.
The function @racket[f] receives four arguments @racket[k] @racket[x] @racket[y] @racket[i]: the color component, two positional coordinates, and a precalculated index into the flomap's @racketid[values] vector.
@examples[#:eval flomap-eval
(flomap->bitmap
(build-flomap 1 100 100
(λ (k x y i) (/ (+ x y) 200))))
(λ (k x y) (/ (+ x y) 200))))
(define sine-fm
(build-flomap
1 100 100
(λ (k x y i)
(λ (k x y)
(* 1/2 (+ 1 (sin (sqrt (+ (sqr (- x 50))
(sqr (- y 50))))))))))
(flomap->bitmap sine-fm)]
To build a flomap from a function that returns vectors, see @racket[build-flomap*].
}
@defform[(inline-build-flomap c w h f)]{
A macro version of @racket[build-flomap].
The function or macro @racket[f] must return a @racket[Float], not a @racket[Real] as the @racket[f] argument to @racket[build-flomap] can.
@defproc[(build-flomap* [c Integer] [w Integer] [h Integer]
[f (Nonnegative-Fixnum Nonnegative-Fixnum -> (U (Vectorof Real) FlVector))]) flomap]{
Returns a @racket[w]×@racket[h] flomap with @racket[c] color components.
Its values are defined by @racket[f], which returns vectors of point components.
The vectors returned by @racket[f] must be length @racket[c].
Using @racket[inline-build-flomap] instead of @racket[build-flomap] often ensures that @racket[f] is inlined, and therefore floats remain unboxed.
Many library functions use @racket[inline-build-flomap] internally for speed, notably @racket[fm+] and the other pointwise arithmetic operators.
Analogous to @racket[build-vector].
@bold{This is not available in untyped Racket.}
@examples[#:eval flomap-eval
(flomap->bitmap
(build-flomap* 4 100 100
(λ (x y)
(vector (/ (+ x y) 200)
(/ (+ (- 100 x) y) 200)
(/ (+ (- 100 x) (- 100 y)) 200)
(/ (+ x (- 100 y)) 200)))))
(build-flomap* 4 100 100
(λ (x y) (vector (/ (+ x y) 200))))]
}
@defproc[(draw-flomap [draw (Any -> Any)] [w Integer] [h Integer]) flomap]{
Returns a @racket[w]×@racket[h] bitmap drawn by @racket[draw].
Think of it as the flomap version of @racketmodname[slideshow]'s @racket[dc].
Analogous to @racketmodname[slideshow]'s @racket[dc].
The @racket[draw] function should accept a @racket[dc<%>] instance and use its drawing methods to draw on an underlying bitmap.
The bitmap is converted to a flomap using @racket[bitmap->flomap] and returned.
@ -395,6 +432,41 @@ You should not generally have to use these functions, because @racket[bitmap->fl
See @secref{flomap:opacity} for a discussion of opacity (alpha) representation.
}
@defform[(inline-build-flomap c w h f)
#:contracts ([c Integer]
[w Integer]
[h Integer]
[f (Nonnegative-Fixnum Nonnegative-Fixnum Nonnegative-Fixnum
Nonnegative-Fixnum -> Float)])]{
A macro version of @racket[build-flomap].
There are three differences between the function @racket[f] passed to @racket[build-flomap] and the @racket[f] passed to @racket[inline-build-flomap].
First, the @racket[f] passed to @racket[inline-build-flomap] can be a macro.
Second, it receives arguments @racket[k] @racket[x] @racket[y] @racket[i], where @racket[i] is a precalculated index into the result's @racketid[values].
Third, it must return a @racket[Float].
Using @racket[inline-build-flomap] instead of @racket[build-flomap] often ensures that @racket[f] is inlined, and therefore floats remain unboxed.
Many library functions use @racket[inline-build-flomap] internally for speed, notably @racket[fm+] and the other pointwise arithmetic operators.
@bold{This is not available in untyped Racket.}
}
@defform[(inline-build-flomap* c w h f)
#:contracts ([c Integer]
[w Integer]
[h Integer]
[f (Nonnegative-Fixnum Nonnegative-Fixnum
Nonnegative-Fixnum -> FlVector)])]{
A macro version of @racket[build-flomap*].
There are three differences between the function @racket[f] passed to @racket[build-flomap*] and the @racket[f] passed to @racket[inline-build-flomap*].
First, the @racket[f] passed to @racket[inline-build-flomap*] can be a macro.
Second, it receives arguments @racket[x] @racket[y] @racket[i], where @racket[i] is a precalculated index into the result's @racketid[values].
Third, it must return a @racket[FlVector].
@bold{This is not available in untyped Racket.}
}
@; ===================================================================================================
@ -466,23 +538,14 @@ For example, to estimate the local gradient magnitude at each point in a flomap:
Lifts a unary floating-point function to operate pointwise on flomaps.
}
@defform[(inline-flomap-lift f)]{
A macro version of @racket[flomap-lift].
The function or macro @racket[f] must return a @racket[Float], not a @racket[Real] as the @racket[f] argument to @racket[flomap-lift] can.
Using @racket[inline-flomap-lift] instead of @racket[flomap-lift] often ensures that @racket[f] is inlined, and therefore floats remain unboxed.
@bold{This is not available in untyped Racket.}
}
@defproc[(flomap-normalize [fm flomap]) flomap]{
Returns a flomap like @racket[fm], but with values linearly rescaled to be between @racket[0.0] and @racket[1.0] inclusive.
@examples[#:eval flomap-eval
(define gray-fm
(build-flomap 1 100 100 (λ (k x y i) (+ 0.375 (/ (+ x y) 800)))))
(build-flomap 1 100 100 (λ (k x y) (+ 0.375 (/ (+ x y) 800)))))
(flomap->bitmap gray-fm)
(flomap->bitmap (flomap-normalize gray-fm))]
Besides increasing contrast, you could use this function to debug a rendering pipeline that produces overbright intermediate flomaps.
Besides increasing contrast, you could use this function to visualize oversaturated flomaps, or visualize flomaps that don't correspond directly to displayed images, such as height maps and normal maps.
}
@defproc[(fm+ [fm1 (U Real flomap)] [fm2 (U Real flomap)]) flomap]
@ -504,7 +567,7 @@ Binary operations accept the following argument combinations, in either order:
Any other argument combination will raise a type error.
@examples[#:eval flomap-eval
(define fm1 (build-flomap 1 260 240 (λ (k x y i) (/ (+ x y) 500))))
(define fm1 (build-flomap 1 260 240 (λ (k x y) (/ (+ x y) 500))))
(define fm2 (fm- 1.0 fm1))
(flomap->bitmap fm1)
(flomap->bitmap fm2)
@ -526,7 +589,16 @@ Because @racket[fm] is an alpha-multiplied flomap (see @secref{flomap:opacity}),
Lifts a binary floating-point function to operate pointwise on flomaps, allowing the same argument combinations as @racket[fm+] and others.
}
@defform[(inline-flomap-lift2 f)]{
@defform[(inline-flomap-lift f) #:contracts ([f (Float -> Float)])]{
A macro version of @racket[flomap-lift].
The function or macro @racket[f] must return a @racket[Float], not a @racket[Real] as the @racket[f] argument to @racket[flomap-lift] can.
Using @racket[inline-flomap-lift] instead of @racket[flomap-lift] often ensures that @racket[f] is inlined, and therefore floats remain unboxed.
@bold{This is not available in untyped Racket.}
}
@defform[(inline-flomap-lift2 f) #:contracts ([f (Float Float -> Float)])]{
A macro version of @racket[flomap-lift2].
The function or macro @racket[f] must return a @racket[Float], not a @racket[Real] as the @racket[f] argument to @racket[flomap-lift2] can.
@ -550,10 +622,10 @@ These return, per-component, estimates of the local @italic{x}- and @italic{y}-d
Equivalent to @racket[(values (flomap-gradient-x fm) (flomap-gradient-y fm))].
@examples[#:eval flomap-eval
(let-values ([(dx-fm dy-fm) (flomap-gradient
(flomap-drop-components fm 1))])
(values (flomap->bitmap (fm* 0.5 (fm+ 1.0 dx-fm)))
(flomap->bitmap (fm* 0.5 (fm+ 1.0 dy-fm)))))]
(define-values (dx-fm dy-fm)
(flomap-gradient (flomap-drop-components fm 1)))
(values (flomap->bitmap (fm* 0.5 (fm+ 1.0 dx-fm)))
(flomap->bitmap (fm* 0.5 (fm+ 1.0 dy-fm))))]
}
@defproc[(flomap-gradient-normal [fm flomap]) flomap]{
@ -561,7 +633,8 @@ Given a one-component flomap, returns a @racket[3]-component flomap containing e
In other words, @racket[flomap-normal] converts height maps to normal maps.
@examples[#:eval flomap-eval
(flomap->bitmap sine-fm)
(flomap->bitmap (flomap-gradient-normal sine-fm))]
(flomap->bitmap (flomap-gradient-normal sine-fm))
(flomap-gradient-normal fm)]
}
@ -595,7 +668,7 @@ Like @racket[flomap-gaussian-blur-x], but per-column instead of per-row.
@defproc[(flomap-box-blur [fm flomap] [x-radius Real] [y-radius Real x-radius]) flomap]{
Returns @racket[fm] convolved, per-component, with a box kernel with radii @racket[x-radius] and @racket[y-radius].
The radii are of the largest ellipse that would fit in the box.
The radii are of the largest axis-aligned ellipse that would fit in the box.
@examples[#:eval flomap-eval
(flomap->bitmap (flomap-box-blur (flomap-inset fm 4) 4))
(flomap->bitmap (flomap-box-blur (flomap-inset fm 4 1) 4 1))]
@ -662,8 +735,8 @@ This function cannot return a larger flomap.
@examples[#:eval flomap-eval
(define small-circle-fm
(draw-flomap (λ (dc)
(send dc draw-ellipse 20 20 10 10))
(draw-flomap (λ (fm-dc)
(send fm-dc draw-ellipse 20 20 10 10))
100 100))
(flomap->bitmap small-circle-fm)
(flomap->bitmap (flomap-trim small-circle-fm))]
@ -741,11 +814,11 @@ The result is expanded as necessary.
@examples[#:eval flomap-eval
(flomap-pin fm -10 -10 sine-fm)
(define circle-fm
(draw-flomap (λ (the-dc)
(send the-dc set-pen "black" 4 'short-dash)
(send the-dc set-brush "yellow" 'solid)
(send the-dc set-alpha 1/2)
(send the-dc draw-ellipse 2 2 124 124))
(draw-flomap (λ (fm-dc)
(send fm-dc set-pen "black" 4 'short-dash)
(send fm-dc set-brush "yellow" 'solid)
(send fm-dc set-alpha 1/2)
(send fm-dc draw-ellipse 2 2 124 124))
128 128))
(flomap->bitmap (flomap-pin fm 0 0 circle-fm 64 64))
(flomap->bitmap (flomap-pin sine-fm 50 0 sine-fm))]
@ -795,9 +868,10 @@ See @racket[flomap-pin] and @racket[flomap-pin*] for implementation details.
@defproc[(flomap-hc-append [fm0 flomap] [fm flomap] ...) flomap]
@defproc[(flomap-hb-append [fm0 flomap] [fm flomap] ...) flomap]{
These create a new flomap by spatially appending the flomaps in the argument list.
The two-letter abbreviation determines direction (@racket[v] or @racket[h]) and alignment (@racket[l], @racket[c], @racket[r], or @racket[t], @racket[c], @racket[b]).
The two-letter abbreviation determines direction (@racketid[v] or @racketid[h]) and alignment (@racketid[l], @racketid[c], @racketid[r], or @racketid[t], @racketid[c], @racketid[b]).
@examples[#:eval flomap-eval
(flomap->bitmap (flomap-ht-append circle-fm fm circle-fm))]
(flomap->bitmap (flomap-ht-append circle-fm fm
(flomap-scale circle-fm 1/2)))]
See @racket[flomap-pin] and @racket[flomap-pin*] for implementation details.
}