チュートリアル - ビットマップ ペイント ツールの開発 - スムーズ ブラシ

チュートリアル > ビットマップ ペイント ツールの開発 - スムーズ ブラシ

このビットマップ ペイント ツール開発の手順では、ブラシにスムーズ フォールオフのオプションを追加します。

全体の流れ:

ブラシのリストボックスに新しい項目を追加します。

paintBrush 関数を拡張し、既存のカラーに基づいて新しいカラーと、ブラシの中心までのピクセルの距離に基づいてフォールオフを計算する case を追加します。

スクリプト:

macroScript MicroPaint category: "HowTo"
(
global MicroPaint_CanvasRollout
try (destroyDialog MicroPaint_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: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 =
  (
    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: (
        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) + (inkColor.color * (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
    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)
)

ステップごとの解説

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

「ツールボックス」に[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: ( 

[Circle Smooth]ブラシが選択された場合、

theFactor =(distance pos currentPos)/ (BrushSize.value/2.0)

描画されるピクセルからブラシの中心までの距離を計算し、それをブラシの半径で割ります。

if theFactor <= 1.0 do
(

結果が 1.0 より小さい場合、そのピクセルは円の中にあることになります。

theFactor = sin ( 90.0 * theFactor)

この場合、値に 90 をかけ、サイン関数を使用してきれいなフォールオフのカーブを計算します。この行をコメントアウトすると、サイン関数に基づいたフォールオフ カーブではなく線形のフォールオフ カーブになることに注意してください。

thePixels = getPixels theCanvasBitmap pos 1

次に、これから描画する位置から 1 ピクセル読み取ります。

if thePixels[1] != undefined do
(

配列に入っているピクセルが有効なカラーである場合、

thePixels[1] = (thePixels[1] * theFactor) + (inkColor.color * (1.0 - theFactor))

距離の係数に基づいてバックグラウンドのカラーをブラシのカラーとブレンドします。係数が 0.5 であれば、2 つのカラーは 50% ずつの割合でブレンドされます。係数が 1 の場合 (円の端) はバックグラウンド カラーが 100% 使用され、インクは 0% になります。

setPixels theCanvasBitmap pos thePixels

最後に、カラーをビットマップに書き出します。

) 
)
)  
)
)
 

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

結果:

前のチュートリアル:

チュートリアル - ビットマップ ペイント ツールの開発 - エアブラシとシェイプ

次のチュートリアル:

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

関連事項