diff --git a/collects/images/compile-time.rkt b/collects/images/compile-time.rkt index 819f480ce7..d9857f3903 100644 --- a/collects/images/compile-time.rkt +++ b/collects/images/compile-time.rkt @@ -15,7 +15,14 @@ (define-syntax (compiled-bitmap stx) (syntax-case stx () [(_ expr) (syntax/loc stx - (let-syntax ([maker (λ (inner-stx) (make-3d-bitmap inner-stx expr))]) + (let-syntax ([maker (λ (inner-stx) + (define bm expr) + (unless (is-a? bm bitmap%) + (raise-syntax-error + 'compiled-bitmap + (format "expected argument of type ; given ~e" bm) + #'expr)) + (make-3d-bitmap inner-stx bm))]) (maker)))])) (define-syntax (compiled-bitmap-list stx) @@ -23,7 +30,13 @@ [(_ expr) (syntax/loc stx (let-syntax ([maker (λ (inner-stx) + (define bms expr) + (unless (and (list? bms) (andmap (λ (bm) (is-a? bm bitmap%)) bms)) + (raise-syntax-error + 'compiled-bitmap-list + (format "expected argument of type ; given ~e" bms) + #'expr)) (with-syntax ([(bm (... ...)) - (map (λ (e) (make-3d-bitmap inner-stx e)) expr)]) + (map (λ (e) (make-3d-bitmap inner-stx e)) bms)]) #'(list bm (... ...))))]) (maker)))])) diff --git a/collects/images/scribblings/compile-time.scrbl b/collects/images/scribblings/compile-time.scrbl index cc3c4dabb9..aa9f6dfac2 100644 --- a/collects/images/scribblings/compile-time.scrbl +++ b/collects/images/scribblings/compile-time.scrbl @@ -2,8 +2,13 @@ @(require scribble/eval (for-label images/compile-time - racket) - images/compile-time) + images/icons/control + images/icons/style + images/logos + racket racket/draw)) + +@(define ctime-eval (make-base-eval)) +@interaction-eval[#:eval ctime-eval (require (for-syntax racket/base))] @(define (author-email) "neil.toronto@gmail.com") @@ -11,3 +16,66 @@ @author{@(author+email "Neil Toronto" (author-email))} @defmodule[images/compile-time] + +Producing computed bitmaps can take time, especially those computed by the functions in the @racketmodname[images/icons] and @racketmodname[images/logos] collections. +To reduce the startup time of programs that use computed bitmaps, use the macros exported by @racketmodname[images/compile-time] to @italic{compile} them: to embed the computed bitmaps in fully expanded, compiled modules. + +@margin-note*{This is a form of constant folding, or equivalently a form of @italic{safe} ``3D'' values.} +The macros defined here compute bitmaps at expansion time, and expand to the bitmap's @racket[bytes] and a simple wrapper that converts @racket[bytes] to a @racket[bitmap%]. +Thus, fully expanded, compiled modules contain (more or less) literal bitmap values, which do not need to be computed again when the module is @racket[require]d by another. + +The literal bitmap values are encoded in @link["http://en.wikipedia.org/wiki/Portable_Network_Graphics"]{PNG} format, so they are compressed in the compiled module. + +To get the most from compiled bitmaps during development, it is best to put them in files that are changed infrequently. +For example, for games, we suggest having a separate module called something like @tt{images.rkt} or @tt{resources.rkt} that @racket[provide]s all the game's images. + +@defform[(compiled-bitmap expr)]{ +Evaluates @racket[expr] at expansion time, which must return a @racket[bitmap%], and returns to the bitmap at run time. +Keep in mind that @racket[expr] has access only to expansion-time values, not run-time values. + +Generally, to use this macro, wrap a @racket[bitmap%]-producing expression with it and move any identifiers it depends on into the expansion phase. +For example, suppose we are computing a large PLT logo at run time: +@codeblock|{#lang racket}| +@racketblock+eval[#:eval ctime-eval +(require images/logos) + +(define the-logo (plt-logo 384)) +] +Running this takes several seconds. It produces +@interaction[#:eval ctime-eval the-logo] + +To move the cost to expansion time, we change the program to +@codeblock|{ +#lang racket + +(require images/compile-time + (for-syntax images/logos)) + +(define the-logo (compiled-bitmap (plt-logo 384))) +}| +The logo is unchanged, but now @italic{expanding} (and thus compiling) the program takes several seconds, and running it takes a few milliseconds. +Note that @racketmodname[images/logos] is now required @racket[for-syntax], so that the expansion-phase expression @racket[(plt-logo 384)] +has access to the identifier @racket[plt-logo]. +} + +@defform[(compiled-bitmap-list expr)]{ +Like @racket[compiled-bitmap], but it expects @racket[expr] to return a @racket[list] of @racket[bitmap%]s, and it returns the list at run time. + +Use this for animations. For example, +@codeblock|{#lang racket}| +@racketblock+eval[#:eval ctime-eval +(require images/compile-time + (for-syntax images/icons/stickman)) + +(begin-for-syntax + (define num-stickman-frames 12)) + +(define running-stickman-frames + (compiled-bitmap-list + (for/list ([t (in-range 0 1 (/ 1 num-stickman-frames))]) + (running-stickman-icon t "red" "white" "red" 32)))) +] +This computes +@interaction[#:eval ctime-eval running-stickman-frames] +at expansion time. +} diff --git a/collects/images/scribblings/images.scrbl b/collects/images/scribblings/images.scrbl index ec8dfd4cb9..e6d7d89cd2 100644 --- a/collects/images/scribblings/images.scrbl +++ b/collects/images/scribblings/images.scrbl @@ -5,6 +5,13 @@ @title{Images} @author{@(author+email "Neil Toronto" (author-email))} +This library contains convenient functions for constructing icons and logos, and will eventually offer the same for other @racket[bitmap%]s. +The idea is to make it easy to include such things in your own programs. + +Generally, the images in this library are computed when requested, not loaded from disk. +Most of them are drawn on a @racket[dc<%>] and then @link["http://en.wikipedia.org/wiki/Ray_tracing_%28graphics%29"]{ray traced}. +This can become computationally expensive, so this library also includes @racketmodname[images/compile-time], which makes it easy to compute images at compile time and access them at run time. + @table-of-contents[] @include-section["icons.scrbl"]