How To ... Develop a Bitmap Painting Tool - Airbrush and Shapes

new.gif NEW Tutorial:

In this step of the Bitmap Painting tool development, we will add an airbrush option to the tool and a brush shape option to draw both rectanglular and circular brushes.

Natural Language

 

MAXScript

macroScript MicroPaint category:"HowTo"

(

global MicroPaint_CanvasRollout

try(destroyDialog CanvasRollout)catch()

local isDrawing = false

local bitmapX = bitmapY = 512

local theCanvasBitmap = bitmap bitmapX bitmapY color:white

local currentPos = lastPos = [0,0]

 

rollout MicroPaint_CanvasRollout "MicroPaint"

(

bitmap theCanvas pos:[0,0] width:bitmapX height:bitmapY bitmap:theCanvasBitmap

colorpicker inkColor height:16 modal:false color:black across:4

checkbutton airBrush "AirBrush" width:50

spinner AirBrushSpeed "Speed" range:[0.1,50,10] fieldwidth:40

spinner BrushSize "Size" range:[1,50,10] type:#integer fieldwidth:40

listbox BrushShape items:#("Circle", "Box") pos:[bitmapX+5,0] width:90

 

fn paintBrush pos =

(

case BrushShape.selection of

(

1: (

if distance pos currentPos <= BrushSize.value/2 do

setPixels theCanvasBitmap pos #(inkColor.color)

)

2:  setPixels theCanvasBitmap pos #(inkColor.color)

)

)

 

fn drawStroke lastPos pos =

(

currentPos = lastPos

deltaX = pos.x - lastPos.x

deltaY = pos.y - lastPos.y

maxSteps = amax #(abs(deltaX),abs(deltaY))

deltaStepX = deltaX / maxSteps

deltaStepY = deltaY / maxSteps

for i = 0 to maxSteps do

(

if airBrush.checked then

(

for b = 1 to (BrushSize.value / AirBrushSpeed.value) do

paintBrush (currentPos + (random [-BrushSize.value/2,-BrushSize.value/2] [BrushSize.value/2,BrushSize.value/2] ))

)

else

for b = -BrushSize.value/2 to BrushSize.value/2 do

for c = -BrushSize.value/2 to BrushSize.value/2 do

paintBrush (currentPos + [c,b])

currentPos += [deltaStepX, deltaStepY]

theCanvas.bitmap = theCanvasBitmap

)

 

on MicroPaint_CanvasRollout lbuttondown pos do

(

lastPos = pos

isDrawing = true

drawStroke lastPos pos

)

on MicroPaint_CanvasRollout lbuttonup pos do isDrawing = false

on MicroPaint_CanvasRollout mousemove pos do

(

if isDrawing do drawStroke lastPos pos

lastPos = pos

)

)

createDialog MicroPaint_CanvasRollout (bitmapx+100) (bitmapy+30)

)

 

Step-By-Step

--Code in green has not changes since the previous version!

 

macroScript MicroPaint category:"HowTo"

(

global MicroPaint_CanvasRollout

try(destroyDialog CanvasRollout)catch()

local isDrawing = false

local bitmapX = bitmapY = 512

local theCanvasBitmap = bitmap bitmapX bitmapY color:white

 

rollout MicroPaint_CanvasRollout "MicroPaint"

(

bitmap theCanvas pos:[0,0] width:bitmapX height:bitmapY bitmap:theCanvasBitmap

 

colorpicker inkColor height:16 modal:false color:black across:4

To accommodate automatically all new controls in the bottom row, we will have to increase the across: parameter to 4.

checkbutton airBrush "AirBrush" width:50

When checked, this checkbutton will enable the Airbrush feature.

spinner AirBrushSpeed "Speed" range:[0.1,50,10] fieldwidth:40

This value will control the strength of the Airbrush feature.

 

spinner BrushSize "Size" range:[1,50,10] type:#integer fieldwidth:40

 

listbox BrushShape items:#("Circle", "Box") width:90 pos:[bitmapx+5,0]

This listbox controls the shape of the brush. In the future versions, we will add additional tools to it.

Listbox

 

fn paintBrush pos =

(

case BrushShape.selection of

(

Depending on the setting of the Brush Shape dropdown list, the pixels will be limited inside a circle, or will be drawn as before.

Case Expression

 

1: (

if distance pos currentPos <= BrushSize.value/2 do

setPixels theCanvasBitmap pos #(inkColor.color)

)

If the Circle shape is selected, the distance from the position to be drawn to the center of the brush will be compared to the radius value (the size divided by 2). Only if the point is at a distance less than the radius, it will be drawn. This defines a circular brush.

2: setPixels theCanvasBitmap pos #(inkColor.color)

This is the original function used in the previous step. It will be used when the dropdown list selection is 2 - "Box".

)

)

 

fn drawStroke lastPos pos =

(

currentPos = lastPos

deltaX = pos.x - lastPos.x

deltaY = pos.y - lastPos.y

maxSteps = amax #(abs(deltaX),abs(deltaY))

deltaStepX = deltaX / maxSteps

deltaStepY = deltaY / maxSteps

for i = 0 to maxSteps do

(

if airBrush.checked then

(

When the Airbrush option is checked, the following code will be executed:

for b = 1 to (BrushSize.value / AirBrushSpeed.value) do

We will repeat a number of times, the actual number will be dependent on the size of the brush and the Airbrush speed value. The higher the Speed value, the lower the number of samples in the brush.

paintBrush (currentPos + (random [-BrushSize.value/2,-BrushSize.value/2] [BrushSize.value/2,BrushSize.value/2] ))

For every iteration, we will draw a random point between the lower left and upper right corners of the box brush. If circular brush is selected, some of the points will be filtered out by the new code in the paintbrush function above.

)

else

Otherwise, the original brush code below will be execured.

for b = -BrushSize.value/2 to BrushSize.value/2 do

for c = -BrushSize.value/2 to BrushSize.value/2 do

paintBrush (currentPos + [c,b])

currentPos += [deltaStepX, deltaStepY]

) 

theCanvas.bitmap = theCanvasBitmap

)

on MicroPaint_CanvasRollout lbuttondown pos do

(

lastPos = pos

isDrawing = true

drawAPoint lastPos pos

)

on MicroPaint_CanvasRollout lbuttonup pos do isDrawing = false

 

on MicroPaint_CanvasRollout mousemove pos do

(

if isDrawing do drawAPoint lastPos pos

lastPos = pos

)

createDialog MicroPaint_CanvasRollout (bitmapX+100) (bitmapY+30)

In order to accommodate the new User Interface controls for the color and brush size, we have to increase the horizontal size of the dialog by 100 pixels.

)

 

Result

 

MicroPaint_Step4.gif

 

Previous Tutorial:

How To ... Develop a Bitmap Painting Tool - Brush Size and Color

Next Tutorial:

How To ... Develop a Bitmap Painting Tool - Smooth Brushes

 

See also

"How To" Tutorials Index Page