parent previous next question (Smalltalk Textbook 10)

Pixmap & Mask

Both 'Pixmap' and 'Mask' are a kind of 'UnmappableSurface' meaning it cannot be displayed on a video monitor. They can only be displayed through a 'graphicsContext'. Pixmap and Mask are created by sending the 'extent:' message with the size of a surface as shown here.

----------------------------------------------
aPixmap := Pixmap extent: 100 @ 100
aMask := Mask extent: 100 @ 100
----------------------------------------------

Program 10-1 displays a red hourglass on a white box. This is an example of the use of 'Pixmap' with 'GraphicsContext'.


Program-10-1: (Pixmap, ColorValue, OrderedCollection, Point; displayPolygon:, 
display:at:, display)
--------------------------------------------------------------------
| aPixmap pixmapContext pointCollection activeWindow windowContext |
aPixmap := Pixmap extent: 100 @ 100.
pixmapContext := aPixmap graphicsContext.
pointCollection := OrderedCollection new.
pointCollection add: aPixmap bounds topLeft.
pointCollection add: aPixmap bounds bottomRight.
pointCollection add: aPixmap bounds bottomLeft.
pointCollection add: aPixmap bounds topRight.
pixmapContext paint: ColorValue red.
pixmapContext displayPolygon: pointCollection.
activeWindow := ScheduledControllers activeController view.
windowContext := activeWindow graphicsContext.
windowContext display: aPixmap at: Point zero.
activeWindow sensor waitClickButton.
activeWindow display.
aPixmap close
--------------------------------------------------------------------

Program-10-2: (Mask, CoverageValue, OrderedCollection, Point; opaque, 
displayPolygon:, display:at:, display)
--------------------------------------------------------------------
| aMask maskContext pointCollection activeWindow windowContext |
aMask := Mask extent: 100 @ 100.
maskContext := aMask graphicsContext.
pointCollection := OrderedCollection new.
pointCollection add: aMask bounds topLeft.
pointCollection add: aMask bounds bottomRight.
pointCollection add: aMask bounds bottomLeft.
pointCollection add: aMask bounds topRight.
maskContext paint: CoverageValue opaque.
maskContext displayPolygon: pointCollection.
activeWindow := ScheduledControllers activeController view.
windowContext := activeWindow graphicsContext.
windowContext display: aMask at: Point zero.
activeWindow sensor waitClickButton.
activeWindow display.
aMask close
--------------------------------------------------------------------

Program 10-2 is similar to Program 10-1 but uses 'Mask' instead of 'Pixmap'. Program 10-1 displays a red hourglass on a white box, but Program 10-2 displays a black hourglass without a background.

Let's examine these programs more carefully. The 'paint:' argument is an instance of 'ColorValue' in Program 10-1. However, Program 10-2 uses 'CoverageValue' instead. You understand Program 10-1 because you have already studied the use of color. Now let me describe 'CoverageValue'. 'CoverageValue' works as a cover for an object you want draw on the display. There are only two possible arguments for 'CoverageValue', 'transparent' and 'opaque'.

Program 10-3 uses both 'Pixmap' and 'Mask'. Notice that an instance of 'Pixmap' is a red rectangle and an instance of 'Mask' is an opaque hourglass.


Program-10-3: (Pixmap, Mask, CoverageValue, Point; display, paint:, opaque,  
copyArea:from:sourceOffset:destinationOffset:)
---------------------------------------------------------------
| aPixmap pixmapContext aMask maskContext pointCollection
  activeWindow windowContext |
aPixmap := Pixmap extent: 100 @ 100.
pixmapContext := aPixmap graphicsContext.
pixmapContext paint: ColorValue red.
pixmapContext displayRectangle: aPixmap bounds.
aMask := Mask extent: 100 @ 100.
maskContext := aMask graphicsContext.
pointCollection := OrderedCollection new.
pointCollection add: aMask bounds topLeft.
pointCollection add: aMask bounds bottomRight.
pointCollection add: aMask bounds bottomLeft.
pointCollection add: aMask bounds topRight.
maskContext paint: CoverageValue opaque.
maskContext displayPolygon: pointCollection.
activeWindow := ScheduledControllers activeController view.
windowContext := activeWindow graphicsContext.
windowContext 
    copyArea: aMask
    from: pixmapContext
    sourceOffset: Point zero
    destinationOffset: Point zero.
activeWindow sensor waitClickButton.
activeWindow display.
aPixmap close.
aMask close
---------------------------------------------------------------

You see a red hourglass on the screen. We copy a red square to the graphics context of the window, but filter it through a mask.

Program 10-4 takes a part of the screen and covers it by an opaque hourglass mask. You see an hourglass filled by the image you took from the screen.


Program-10-4: (Image, Mask, Point; displayPolygon:, 
copyArea:fromImage:sourceOffset:destinationOffset:, opaque)
---------------------------------------------------------------
| anImage aMask maskContext pointCollection
  activeWindow windowContext |
anImage := Image fromUser.
aMask := Mask extent: anImage extent.
maskContext := aMask graphicsContext.
pointCollection := OrderedCollection new.
pointCollection add: aMask bounds topLeft.
pointCollection add: aMask bounds bottomRight.
pointCollection add: aMask bounds bottomLeft.
pointCollection add: aMask bounds topRight.
maskContext paint: CoverageValue opaque.
maskContext displayPolygon: pointCollection.
activeWindow := ScheduledControllers activeController view.
windowContext := activeWindow graphicsContext.
windowContext 
    copyArea: aMask
    fromImage: anImage
    sourceOffset: Point zero
    destinationOffset: Point zero.
activeWindow sensor waitClickButton.
activeWindow display.
aMask close
---------------------------------------------------------------

These two programs use messages which begin with 'copyArea:from...'. Program 10-3 uses this message to copy a part of a graphicsContext to a place in the same graphicsContext (using the same 'Point zero' offsets). Program 10-4 uses it to copy an image to a 'graphicsContext'.

Program 10-5 uses the 'copyArea:from...' message to clip an area surrounding the mouse, and show it on the screen. Take a close look at the two statements:

    aRectangle := InputSensor cursorPoint extent: Point zero.
    aRectangle := aRectangle expandedBy: aMask extent // 2.

The first assigns aRectangle what class of object? 'InputSensor cursorPoint' returns a point. And the extent: message sent to a point returns what? Inspect:

    Point zero extent: Point zero

and you will see that the first statement above creates a rectangle of zero extent located at the position of the mouse. The second statement then expands that rectangle by half the extent of the Mask. The net effect is to end up with a rectangle with the mouse point at its center.

How else could this have been achieved? Well, we could get the current mouse position and subtract half the height and half the width of the mask rectangle. This would leave us with the same rectangle as calculated above.


Program-10-5: (Mask, CoverageValue, Screen; 
displayWedgeBoundedBy:startAngle:sweepAngle:, 
copyArea:fromImage:sourceOffset:destinationOffset:, makeRectangleVisible:,
completeContentsOfArea:, opaque)
---------------------------------------------------------------
| aMask maskContext activeWindow windowContext aRectangle anImage |
aMask := Mask extent: 100 @ 50.
maskContext := aMask graphicsContext.
maskContext paint: CoverageValue opaque.
maskContext
    displayWedgeBoundedBy: aMask bounds
    startAngle: 0
    sweepAngle: 360.
activeWindow := ScheduledControllers activeController view.
windowContext := activeWindow graphicsContext.
[activeWindow sensor noButtonPressed] whileTrue:
    [aRectangle := InputSensor cursorPoint extent: Point zero.
    aRectangle := aRectangle expandedBy: aMask extent // 2.
    aRectangle := Screen default makeRectangleVisible: aRectangle.
    anImage := Screen default completeContentsOfArea: aRectangle.
    windowContext 
        copyArea: aMask
        fromImage: anImage
        sourceOffset: Point zero
        destinationOffset: Point zero].
activeWindow display.
aMask close
---------------------------------------------------------------

What happens when you move the mouse into the oval itself? Program 10-6 shows a doughnut instead of a circle and by now you should realize that any shape can serve as mask to filter what parts of an image are copied.


Program-10-6: (Mask, CoverageValue; transparent, 
displayWedgeBoundedBy:startAngle:sweepAngle:, 
copyArea:fromimage:sourceOffset:destinationOffset:)
---------------------------------------------------------------
| aMask maskContext activeWindow windowContext aRectangle anImage |
aMask := Mask extent: 100 @ 100.
maskContext := aMask graphicsContext.
maskContext paint: CoverageValue opaque.
maskContext
    displayWedgeBoundedBy: aMask bounds
    startAngle: 0
    sweepAngle: 360.
maskContext paint: CoverageValue transparent.
maskContext
    displayWedgeBoundedBy: (aMask bounds insetBy: aMask extent // 4)
    startAngle: 0
    sweepAngle: 360.
activeWindow := ScheduledControllers activeController view.
windowContext := activeWindow graphicsContext.
[activeWindow sensor noButtonPressed] whileTrue:
    [aRectangle := InputSensor cursorPoint extent: Point zero.
    aRectangle := aRectangle expandedBy: aMask extent // 2.
    aRectangle := Screen default makeRectangleVisible: aRectangle.
    anImage := Screen default completeContentsOfArea: aRectangle.
    windowContext 
        copyArea: aMask
        fromImage: anImage
        sourceOffset: Point zero
        destinationOffset: Point zero].
activeWindow display.
aMask close
---------------------------------------------------------------

'Pixmap' and 'Mask' are very useful classes. Take some time to study them thoroughly. For example, you know that several 'display-' messages can be used to draw on a graphicsContext of a Mask. Use a System Browser on the GraphicsContext class and the 'displaying' protocol to explore some of the other 'display-' messages. For example, what kind of mask would you expect the following to yield?

    . . .
    maskContext paint: CoverageValue opaque.
    maskContext displayString: 'A Textual Mask' at: 5 @ 20.
    . . .


parent previous next question
Copyright (C) 1994-1996 by Atsushi Aoki
Translated by Kaoru Rin Hayashi & Brent N. Reeves