チュートリアル - ビットマップ ペイント ツールの開発 - 変更の消去

チュートリアル > ビットマップ ペイント ツールの開発 - 変更の消去

このビットマップ ペイント ツール開発の手順では、既存のブラシに新しい消去オプションを追加します。ユーザがマウスの右ボタンを押しながら描画するとき、バックグラウンド イメージが表に出るようにします。このモードはエアブラシ モードを含むすべてのブラシで使用でき、ユーザがスムーズに消去を行えるようにします。

全体の流れ:

消去モードをコントロールするフラグ変数を新しく追加します。

paintBrush 関数を変更し、バックグラウンド イメージのピクセルを読み込めるようにします。

マウスの右ボタンのハンドラを実装し、バックグラウンドの元のピクセルで既存のピクセルを消去できるようにします。

[編集](Edit)メニューの先頭に変更を確定するオプションを追加し、フォアグラウンドのペイント キャンバスをバックグラウンドにコピーして、すべての変更を焼き固めます(ベイク処理)。

スクリプト:

macroScript MicroPaint category: "HowTo"
(
global MicroPaint_CanvasRollout
try (destroyDialog MicroPaint_CanvasRollout) catch()
local isErasing =isDrawing = false
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"
  )
  on commit_menu picked do copy theCanvasBitmap theBackgroundBitmap
 
  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:16 modal: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
      thePaintColor = (getPixels theBackgroundBitmap pos 1)[1]
    else
      thePaintColor = inkColor.color
    if thePaintColor == undefined do thePaintColor = white
 
    case BrushShape.selection of
    (
      1: (
        if distance pos currentPos <= BrushSize.value/2 do
          setPixels theCanvasBitmap pos #(thePaintColor)
      )
      2: setPixels theCanvasBitmap pos #(thePaintColor)
      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))
              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
    drawStroke lastPos pos
  )
  on MicroPaint_CanvasRollout rbuttondown pos do
  (
    lastPos = pos
    isErasing = isDrawing = true
    drawStroke lastPos pos
  )
  on MicroPaint_CanvasRollout lbuttonup pos do isErasing =isDrawing = false
  on MicroPaint_CanvasRollout rbuttonup pos do isErasing =isDrawing = 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
)

ステップごとの解説

--Code in green has no changes since the previous version. macroScript MicroPaint category:"HowTo"
(
global MicroPaint_CanvasRollout
try(destroyDialog MicroPaint_CanvasRollout)catch() local isErasing = isDrawing = false

新しい変数 isErasing を追加します。この変数はマウスの右ボタンが押された場合に true に設定されます。

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"

[編集](Edit)ダイアログ ボックスに新しいメニュー項目を追加します。このメニュー項目は、編集した変更を確定して「消去不可能」にします。

) on commit_menu picked do copy theCanvasBitmap theBackgroundBitmap

変更を確定するオプションのイベント ハンドラです。選択されると、ペイント中のフォアグラウンドのイメージはバックグラウンド イメージにコピーされます。 こうして、変更されたピクセルのすべてがその中に焼き固められます(ベイク処理)。この時点以降、消去オプションを使っても、新しいピクセルが表示されます。

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

消去モードがアクティブである (つまり、ユーザがマウスの右ボタンを押している) 場合、

thePaintColor = (getPixels theBackgroundBitmap pos 1)[1]

バックグラウンドのピクセルのカラーを取得し、それをインク カラーとします。

else
thePaintColor = inkColor.color

それ以外の場合は、ユーザ インタフェースで定義されたインク カラーを使用します。

if thePaintColor == undefined do thePaintColor = white

ピクセルがビットマップの外にある場合、未定義のものが含まれる可能性があります。エラーを避けるため、これはリセットして白に戻します。

case BrushShape.selection of
(
1: (
if distance pos currentPos <= BrushSize.value/2 do setPixels theCanvasBitmap pos #(thePaintColor) ) 2: setPixels theCanvasBitmap pos #(thePaintColor)

どちらの場合も、InkColor.color の明示的な使用を正しいペイントのカラーが含まれる thepaintColor と置き換えます。

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)) 

ここでも、InkColor.color の明示的な使用を正しいペイントのカラーが含まれる thePaintColor と置き換えます。

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

ユーザがマウスの左ボタンを押すと、isErasing フラグが false に設定されます。

drawStroke lastPos pos
) on MicroPaint_CanvasRollout rbuttondown pos do
(
lastPos = pos
isErasing = isDrawing = true
drawStroke lastPos pos
)

この新しいハンドラは、マウスの右ボタンを押すと呼び出されます。isErasing フラグが true に設定される以外は、マウスの左ボタンのハンドラとまったく同じです。

on MicroPaint_CanvasRollout lbuttonup pos do isErasing = isDrawing = false
on MicroPaint_CanvasRollout rbuttonup pos do isErasing = isDrawing = false

lbuttonup と新しい rbuttonup のどちらのハンドラも、両方のフラグを 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
) 

前のチュートリアル:

チュートリアル - ビットマップ ペイント ツールの開発 - ロードと保存

次のチュートリアル:

チュートリアル - ビットマップ ペイント ツールの開発 - UV 座標のアンラップ

関連事項