Expected behavior for patterns and image draw modes

I'm curious regarding the expected behavior of any of the image draw modes when used in conjunction with a pattern set via setPattern. For context, let me first enumerate the different ways to set a pattern for drawing:

  1. setDitherPattern(alpha, [ditherType])
  2. setPattern(pattern)
  3. setPattern(image, [x, y])

setDitherPattern may be used in conjunction with setColor to indicate which color the dither pattern should be drawn in. We can largely ignore this option for the purposes of this discussion. setPattern, by contrast, bakes the color information into the encoded pattern or pattern image, and as such the currently set color has no effect. (In fact, calling setColor clears the current pattern.) Thus, for most purposes, the pattern behaves just like an image when it comes to drawing. This is reasonable, especially given that option 3 literally sets the pattern using an image.

Fonts are also essentially represented as images. Somewhere along the way I learned the trick of using the image draw modes (e.g. kDrawModeFillWhite or kDrawModeInverted) to turn would-be-black text into white text which would appear against a dark background. Because the fonts are images under the hood, calling setImageDrawMode on the current graphics context impacts their display.

This brings me to the question: would one expect the current image draw mode to affect the rendering of patterns? Given the similarity with fonts, I assumed this would be the case ā€” I was mistaken. I can pre-process the images passed to setPattern, of course. However, I'm working on a system that pre-renders various pattern images at initialization and then draws using them later on, and I was surprised to discover the need to create separate patterns for light-on-dark vs. dark-on-light applications given that neither setColor nor setImageDrawMode will affect the displayed pattern.

Do others share my expectation? Is this a bug; a quirk of the API where there was no strongly defined expectation in its initial design; or are there good reasons that patterns behave this way?

1 Like

This is clearly something we need to explain clearer in the docs. Thank you for pointing it out! Under the hood, patterns act like colors not images. When you do setColor() you set the color variable to black/white/clear/XOR, and when you call setPattern() you set the same variable to a pointer to an 8x8 (x2 for mask) buffer defining the pattern. If you use an image as a source it copies the bits from the image to that buffer, it doesn't turn it into an image draw, which is why the image mode has no effect.

Should it? Yeah I think so, it makes the API more flexible. Using the current image draw mode in setPattern(image) isn't really tenable because affects existing code, and it's kind of clumsy. I like adding an optional image draw mode to setPattern(image) because it keeps the parameter local to the call, also because we can distinguish between setPattern(image, x, y) and setPattern(image, mode) and setPattern(image, x, y, mode) by the number of parameters.

What do you think? Does that solve the problem?

1 Like

Fixing the bug I filed regarding inverted images with patterns actually suffices for my current use case. That said, the approach you propose does sound reasonable. However, Iā€™m not sure about the benefit regarding the number of parameters in the function signature assuming that x, y and mode would all be be optional.

What I meant is having a singleton after a pair means we can do

(image) -> (image)
(image, number) -> (image, mode)
(image, number, number) -> (image, x, y)
(image, number, number, number) -> (image, x, y, mode)

There are other places where we have separate options (scale) and (xscale, yscale) where you can't disambiguate like this.

Ah, that makes sense! Thanks for clarifying.