[racket-dev] Bug report and complaint: Bitmap drawing and masks

From: John Boyle (johnthescavenger at gmail.com)
Date: Tue Dec 14 23:46:27 EST 2010

Problem: The dc% method 'draw-bitmap-section disregards the boundaries of
the drawing section when the 'color argument is the color black.

Not only does the documentation clearly state that the 'color argument
should be ignored for a color bitmap, but it's quite bizarre that, upon
seeing black, it decides to draw the whole bitmap, whereas if black is
replaced by (say) green, it correctly draws only the specified section.  I
have observed this on nightly builds from the last several days on both Mac
OS X and Linux.

To reproduce:

#lang racket/gui
;(current-directory "/Users/john/Dropbox/rkt")
;Just make sure you can access the PNGs.
(define u (make-object bitmap% "valkyrie.png"))
(send u set-loaded-mask (make-object bitmap% "valkyrie-umask.png"))

(define frame (new frame% [label "Example"]
                   (x 900) (y 100)
                   (width 600) (height 600)))

(define cv (new canvas% [parent frame]))
(define dc (send cv get-dc))
(send frame show #t)

And now, at the REPL:

;Works properly
(let ((nonce-color (make-object color% "green")))
  (send dc clear)
  (send dc draw-bitmap-section u 0 0 0 0 48 48
       'solid nonce-color (send u get-loaded-mask)))

;Screws up
(let ((nonce-color (make-object color%))) ;defaults to "black"
  (send dc clear)
  (send dc draw-bitmap-section u 0 0 0 0 48 48
       'solid nonce-color (send u get-loaded-mask)))

For convenience, I have attached one of the bitmaps and masks I've used.

Next, I complain about some other weird behavior of bitmaps (this behavior
has, I think, existed for a long time).  In the following example (code at
the bottom of this email), I load a black-and-white bitmap (which seems to
load as color), then create two bitmaps inside bitmap-dc%s (one monochrome
and the other color), draw the original black-and-white bitmap to each of
these bitmap-dc%s, and extract the bitmaps from the bitmap-dc%s.  Now I have
three black-and-white bitmaps that all look identical: the originally loaded
one, the monochrome copy, and the color copy.

Now I try to use them as masks to draw the "valkyrie" bitmaps.  What
happens?  The first one works, the second and third don't.  Why is this?  I
investigated and found that, while the pixels of the copies are identical to
those of the original (at least, the color copy is identical to the
original), the "alpha" channel is different: the alpha bytes are all 0 on
the original, but they're all 255 on both of the copies.

Questions/complaints:
1. Why does the alpha channel of a bitmap matter when it's being used as a
mask?
2. Why doesn't drawing a bitmap to another one of the same size produce an
exact copy of the first?  (Or, if, say, the second one is monochrome while
the first is color, why doesn't it produce the monochrome equivalent of the
first?  And vice versa.)

Code for REPL (the PNGs are needed again):

;Constructs three black-and-white bitmaps as described above.
;For each, draws the bitmap, then draws the "valkyrie" thing using the
bitmap
; as a mask, then prints out whether it's color and the value of the 0th
alpha byte.
(let* ((u (make-object bitmap% "valkyrie.png"))
         (um (make-object bitmap% "valkyrie-umask.png"))
       (umm (make-object bitmap-dc%
              (make-monochrome-bitmap (send um get-width) (send um
get-height))))
       (umc (make-object bitmap-dc%
              (make-bitmap (send um get-width) (send um get-height)))))
  (send umm draw-bitmap um 0 0)
  (send umc draw-bitmap um 0 0)
  (let ((mu (send umm get-bitmap))
        (cu (send umc get-bitmap)))
      (for-each (lambda (x)
                  (send dc clear)
                  (send dc draw-bitmap x 0 0)
                  (sleep 1)
                  (send dc clear)
                  (send dc draw-bitmap u 0 0 'solid (make-object color%
"green")
                        x)
                  (for-each display (list "color? " (send x is-color?)
                                          " alpha-byte " (let ((c
(make-bytes 9000)))
                                                           (send x
get-argb-pixels 0 0 10 10 c 't)
                                                           (bytes-ref c
0))))
                  (newline)
                  (sleep 1))
                (list um mu cu))))
--John Boyle
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.racket-lang.org/dev/archive/attachments/20101214/3dc7b195/attachment.html>
-------------- next part --------------
A non-text attachment was scrubbed...
Name: valkyrie.png
Type: image/png
Size: 122038 bytes
Desc: not available
URL: <http://lists.racket-lang.org/dev/archive/attachments/20101214/3dc7b195/attachment.png>
-------------- next part --------------
A non-text attachment was scrubbed...
Name: valkyrie-umask.png
Type: image/png
Size: 31539 bytes
Desc: not available
URL: <http://lists.racket-lang.org/dev/archive/attachments/20101214/3dc7b195/attachment-0001.png>

Posted on the dev mailing list.