How To > Develop a Bitmap Painting Tool- Erase Changes |
In this step of the Bitmap Painting tool development, we will add an eraser option to the existing brushes. When the user is drawing with the right mouse button pressed, the background image will be revealed.This mode will support all available brushes including the Airbrush mode, allowing the user to erase smoothly.
--Code in green has not changes since the previous version! macroScript MicroPaint category:"HowTo" ( global MicroPaint_CanvasRollout try(destroyDialog MicroPaint_CanvasRollout)catch() local isErasing = isDrawing = false
We add a new variable isErasing which will be set to true when the right mouse button is pressed.
local bitmapX = bitmapY = 512 local theCanvasBitmap = bitmap bitmapX bitmapY color:white local theBackgroundBitmap = bitmap bitmapX bitmapY color:white local currentPos = lastPos = [0,0] rcMenu CanvasMenu ( subMenu "File" ( menuItem new_menu "New" menuItem open_menu "Open..." menuItem save_as "Save As..." separator file_menu_1 menuItem quit_tool "Quit" ) subMenu "Edit" ( menuItem commit_menu "Commit Changes"
We add a new menu item to the Edit dialog. It will be used to commit the editing changes and make them "unerasable".
This is the event handler for the commit changes option. When selected, the foreground image we are painting on will be copied into the background image, thus "baking" all the changed pixels into it. Using the eraser from this point on will reveal the new pixels.
subMenu "Help" ( menuItem about_tool "About MicroPaint..." ) on new_menu picked do ( theBackgroundBitmap = theCanvasBitmap = bitmap bitmapX bitmapY color:MicroPaint_CanvasRollout.paperColor.color MicroPaint_CanvasRollout.theCanvas.bitmap = theCanvasBitmap ) on open_menu picked do ( theOpenBitmap= selectBitmap() if theOpenBitmap != undefined do ( copy theOpenBitmap theCanvasBitmap copy theOpenBitmap theBackgroundBitmap close theOpenBitmap MicroPaint_CanvasRollout.theCanvas.bitmap = theCanvasBitmap ) ) on save_as picked do ( theSaveName = getSaveFileName types:"BMP (*.bmp)|*.bmp|Targa (*.tga)|*.tga|JPEG (*.jpg)|*.jpg" if theSaveName != undefined do ( theCanvasBitmap.filename = theSaveName save theCanvasBitmap ) ) on about_tool picked do messagebox "MicroPaint\nMAXScript Tutorial" title:"About..." on quit_tool picked do destroyDialog MicroPaint_CanvasRollout ) rollout MicroPaint_CanvasRollout "MicroPaint" ( bitmap theCanvas pos:[0,0] width:bitmapX height:bitmapY bitmap:theCanvasBitmap colorpicker inkColor height:16modal:false color:black across:5 colorpicker paperColor height:16 modal:false color:white checkbutton airBrush "AirBrush" width:50 spinner AirBrushSpeed "Speed" range:[0.1,50,10] fieldwidth:30 spinner BrushSize "Size" range:[1,50,10] type:#integer fieldwidth:40 listbox BrushShape items:#("Circle", "Box", "Circle Smooth") pos:[bitmapX+5,0] width:90 fn paintBrush pos = ( if isErasing then
If the eraser mode is active (that means, the user is holding down the right mouse button), then
we get the color of the pixel from the background as the ink color.
otherwise we use the ink color defined by the User Interface.
If the pixel was outside the bitmap, it might contain undefined. To avoid errors, we reset it back to white.
case BrushShape.selection of ( 1: ( if distance pos currentPos <= BrushSize.value/2 do setPixels theCanvasBitmap pos #(thePaintColor) ) 2: setPixels theCanvasBitmap pos #(thePaintColor)
In both cases, we replace the explicit use of the InkColor.color with thepaintColor which contains the correct color to paint with.
3: ( theFactor = (distance pos currentPos) / (BrushSize.value/2.0) if theFactor <= 1.0 do ( theFactor = sin ( 90.0 * theFactor) thePixels = getPixels theCanvasBitmap pos 1 if thePixels[1] != undefined do ( thePixels[1] = (thePixels[1] * theFactor) + (thePaintColor * (1.0 - theFactor))
Again, we replace the explicit use of the InkColor.color with thePaintColor which contains the correct color to paint with.
setPixels theCanvasBitmap pos thePixels ) ) )--end case 3 )--end case )--end fn 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 isErasing = false
The isErasing flag is set to false whenever the user presses the left mouse button.
drawStroke lastPos pos ) on MicroPaint_CanvasRollout rbuttondown pos do ( lastPos = pos isErasing = isDrawing = true drawStroke lastPos pos )
This new handler is called whenever the right mouse button is pressed. It is identical to the left mouse button handler except for the isErasing flag which is set to true this time.
on MicroPaint_CanvasRollout lbuttonup pos do isErasing = isDrawing = false on MicroPaint_CanvasRollout rbuttonup pos do isErasing = isDrawing = false
Both the lbuttonup and the new rbuttonup handlers will stop both drawing and erasing by setting both flags to false.
on MicroPaint_CanvasRollout mousemove pos do ( if isDrawing do drawStroke lastPos pos lastPos = pos ) ) createDialog MicroPaint_CanvasRollout (bitmapx+100) (bitmapy+30) menu:CanvasMenu MicroPaint_CanvasRollout.theCanvas.bitmap = theBackgroundBitmap )
How To ... Develop a Bitmap Painting Tool - Load and Save
How To ... Develop a Bitmap Painting Tool - Unwrap UV Coordinates