How To > Develop a Bitmap Painting Tool- Smooth Brushes |
In this step of the Bitmap Painting tool development, we will add optional smooth falloff to the brush.
--Code in italic has no 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 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", "Circle Smooth") pos:[bitmapX+5,0] width:90
We add a new brush to our "toolbox" - "Circle Smooth".
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) 3: (
When the "Circle Smooth" brush is selected,
we calculate the distance from the pixel to be drawn to the center of the brush and divide by the radius of the brush.
If the result is less than 1.0, the pixel lies inside the circle.
In that case, we multiply the value by 90 and use the Sine function to calculate a nice falloff curve. Note that remarking this line gives you a linear falloff curve instead of the sine-based falloff curve.
Next, we read a single pixel from the position we are about to write to.
If the single pixel in the array is a valid color,
we blend the color of the background with the brush color based on the distance factor. If the factor is 0.5, the two colors will be blended 50-50%. If the factor is 1 (at the edge of the circle, the background color will be used 100% and the ink will be 0%.
Finally, we write the color back into the bitmap.
) ) ) ) ) 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) )
How To ... Develop a Bitmap Painting Tool - Airbrush and Shapes