サンプルスクリプト

いきなりAPIを羅列するよりも、サンプルスクリプトを見た方が「あぁなるほど」と納得して頂けると思いますので :D

(1) 適当な画像を作り、コピーし、ドキュメントの作成

local w = 512
local h = 300
local idx = mdi_img_offscreen( 0 ) -- offscreenバッファの取得
mdi_img32_resize( idx, w,h ) -- ↑のリサイズ

for j = 0,h do
  for i = 0,w do
    local r = i
    local g = j
    local b = r * g / 255
    mdi_img32_pixelset( idx, i,j, r,g,b,255 )
  end
end

local idx2 = mdi_img_offscreen( 1 ) -- 意味も無く新規バッファを作り、
mdi_img32_copy( idx2, idx ) -- そこにコピーして、

mdi_new_img32( idx2 ) -- 画像から新規作成

(2) 32bppレイヤーのネガポジ反転 (重いので小さな画像限定で…)

mdi_undo_layer(); -- レイヤを操作するので、Undoに保存

local w = mdi_width();
local h = mdi_height();

local li = mdi_layer_active();
local bpp = mdi_layer_type( li );
local idx = mdi_img_layer( li );

for j = 0,h do
  for i = 0,w do
    -- 32bppの場合
    if bpp == 32 then
      local r,g,b,a = mdi_img32_pixelget( idx, i,j );
      mdi_img32_pixelset( idx, i,j, 255-r,255-g,255-b,a );
    end
  end
end

(3) アクティブな32bppレイヤに、矩形と楕円を描画する

mdi_undo_layer();

local w = mdi_width();
local h = mdi_height();

local bw = w / 100;
local bh = h / 100;
local li = mdi_layer_active();
local bpp = mdi_layer_type( li );
local idx = mdi_img_layer( li );

for j = 0,bh do
  for i = 0,bw do

    -- こういう判定は、本来for,forの外で行うのがいいんですが :D
    -- 1bppレイヤの場合
    if bpp == 1 then
      mdi_img1_rect( idx, i*100,j*100, 80,80, 1 );
      mdi_img1_ellipse( idx, i*100+10,j*100+10, 60,60, 0 );
    end

    -- 8bppレイヤの場合
    if bpp == 8 then
      mdi_img8_rect( idx, i*100,j*100, 80,80, 255,255 );
      mdi_img8_ellipse( idx, i*100+10,j*100+10, 60,60, 0,128 );
    end

    -- 32bppレイヤの場合
    if bpp == 32 then
      local r = math.random() * 255;
      local g = math.random() * 255;
      local b = math.random() * 255;

      mdi_img32_rect( idx, i*100,j*100, 80,80, r,g,b, 255 );
      mdi_img32_ellipse( idx, i*100+10,j*100+10, 60,60, 255-r,255-g,255-b, 255 );
    end
  end
end

(4) かんたん絵画風フィルタ (id:XELFさんのスクリプトが元ネタ)

mdi_undo_all();

local act = mdi_layer_active() -- アクティブなレイヤ
local sourceImage = mdi_img_layer( act ) -- 参照元レイヤとする
local newLayer = mdi_layer_add( 32 ) -- レイヤを追加し、
local destImage = mdi_img_layer( newLayer ); -- 追加した新規レイヤに対して描画する

math.randomseed( mdi_ms() ) -- 乱数シードの初期化

local w, h = mdi_width(), mdi_height();
local n = w * h / 20

for i = 0,n do
  local x = math.random() * w
  local y = math.random() * h
  local r,g,b,a = mdi_img32_pixelget( sourceImage, x, y )

  local r2 = 5 + math.random()*4
  mdi_img32_ellipse( destImage, x,y, r2,r2, r,g,b,a );
end

(5) 8bpp画像を使って、32bpp画像から不透明度を抜く

mdi_undo_layer()

local idx0 = mdi_img_offscreen( 0 )
local idx1 = mdi_img_layer( 0 )
local w, h = mdi_width(), mdi_height();
mdi_img8_resize( idx0, w, h )

-- 8bpp画像に、円を描画する
for i=0,200 do
  local x = math.random() * w
  local y = math.random() * h
  local r2 = 10 + math.random()*40
  mdi_img8_ellipse( idx0, x,y, r2,r2, 255,255 );
end

-- 32bppレイヤ画像から、8bpp画像を不透明度として引く
mdi_img8_sub32( idx1, idx0 )
||<

** (6) 選択範囲内だけ、ネガポジ反転をする

>||
mdi_undo_layer(); -- レイヤを操作するので、Undoに保存

local w = mdi_width();
local h = mdi_height();

local li = mdi_layer_active(); -- アクティブなIndex
local bpp = mdi_layer_type( li ); -- そのレイヤのbpp取得
local idx = mdi_img_layer( li ); -- そのレイヤの画像バッファのIndex取得

local sx,sy,sw,sh = mdi_select_range() -- 選択範囲の有効範囲を取得

-- 有効範囲のみ処理 (選択範囲がない場合は、全体になる)
for j = sy,sy+sh do
  for i = sx,sx+sw do
    -- 32bppの場合
    if bpp == 32 then
      local v = mdi_select_pixelget( i,j ) -- 選択範囲情報の取得
      local r,g,b,a = mdi_img32_pixelget( idx, i,j ); -- レイヤ色の取得
      mdi_img32_pixelset( idx, i,j, 255-r,255-g,255-b, v ); -- 選択範囲の濃度を元に色反転
    end
  end
end

(7) 警告そして終了、ガウスぼかしで華やかな画に

-- モード警告
local num = mdi_layer_num()
if num == 0 then
  mdi_halt( "This script needs 32bpp layer" )
end

mdi_undo_all()

-- レイヤを新規作成して、加算モードに
local idx0 = mdi_img_layer( 0 )
local li = mdi_layer_add( 32 )
mdi_layer_setmode( li, "add" )

local idx1 = mdi_img_layer( li )
local idx2 = mdi_img_offscreen( 1 )

-- コピーを作り、レベル補正で高輝度抽出
mdi_img32_copy( idx2, idx0 )
mdi_img32_level( idx2, 128,255, 0,255, 1.0 )

-- それをガウスぼかしして、新規レイヤへ
mdi_img32_gaussblur( idx1, idx2, 5 )

(8) aobench (数十秒以上掛かります)

http://twitter.com/aobench


--------------------------------------------
-- vec.pde
--------------------------------------------

function vec_zero()
  local res = {}
  res.x = 0
  res.y = 0
  res.z = 0
  return res
end

function vec_new( x, y, z )
  local res = {}
  res.x = x
  res.y = y
  res.z = z
  return res
end

function vec_copy( vec )
  local res = {}
  res.x = vec.x
  res.y = vec.y
  res.z = vec.z
  return res
end

function vec_sub( vec, vecSub )
  local res = {}
  res.x = vec.x - vecSub.x
  res.y = vec.y - vecSub.y
  res.z = vec.z - vecSub.z
  return res
end

function vec_cross( vec1, vec2 )
  local res = {}
  res.x = vec1.y * vec2.z - vec2.y * vec1.z
  res.y = vec1.z * vec2.x - vec2.z * vec1.x
  res.z = vec1.x * vec2.y - vec2.x * vec1.y
  return res
end

function vec_len( vec )
  local res = vec.x*vec.x + vec.y*vec.y + vec.z*vec.z
  if res ~= 0 then
    res = math.sqrt( res )
  end
  return res
end

function vec_dot( vec1, vec2 )
  local res = vec1.x*vec2.x + vec1.y*vec2.y + vec1.z*vec2.z
  return res
end

function vec_normalize( vec )
  local res = vec_copy( vec )
  local d = vec_len( res )

  if math.abs( d ) > 1.0e-6 then
    local invlen = 1.0 / d
    res.x = res.x * invlen
    res.y = res.y * invlen
    res.z = res.z * invlen
    return res
  end

  return res
end

--------------------------------------------
-- geometry.pde
--------------------------------------------

function ray_new( org, dir )
  local res = {}
  res.org = vec_copy( org )
  res.dir = vec_copy( dir )
  return res
end

function ray_copy( ray )
  local res = {}
  res.org = vec_copy( ray.org )
  res.dir = vec_copy( ray.dir )
  return res
end

function intersection_new()
  local res = {}
  res.hit = false
  res.t = 1.0e+30
  res.n = vec_zero()
  res.p = vec_zero()
  return res
end

function intersection_copy( isec )
  local res = {}
  res.hit = isec.hit
  res.t = isec.t
  res.n = vec_copy( isec.n )
  res.p = vec_copy( isec.p )
  return res
end

function sphere_new( center, radius )
  local res = {}
  res.center = vec_copy( center )
  res.radius = radius
  return res
end

function sphere_copy( sphere )
  local res = {}
  res.center = vec_copy( sphere.center )
  res.radius = sphere.radius
  return res
end

function sphere_intersect( isect, sphere, ray )
  local rs = vec_sub( ray.org, sphere.center )
  local B = vec_dot( rs, ray.dir )
  local C = vec_dot( rs, rs ) - ( sphere.radius * sphere.radius )
  local D = B * B - C

  if D > 0.0 then
    local t = -B - math.sqrt( D )
    if (t > 0.0) and (t < isect.t) then
      isect.t = t
      isect.hit = true

      local p = vec_new( ray.org.x + ray.dir.x * t, ray.org.y + ray.dir.y * t, ray.org.z + ray.dir.z * t )
      local n = vec_sub( p, sphere.center )
      n = vec_normalize( n )
      isect.n = vec_copy( n )
      isect.p = vec_copy( p )
    end
  end 
end

function plane_new( p, n )
  local res = {}
  res.p = vec_copy( p )
  res.n = vec_copy( n )
  return res
end

function plane_intersect( isect, plane, ray )
  local d = -vec_dot( plane.p, plane.n )
  local v = vec_dot( ray.dir, plane.n )

  if math.abs(v) < 1.0e-6 then 
    return
  end

  local t = -(vec_dot( ray.org, plane.n ) + d) / v
  if (t > 0) and (t < isect.t) then
    isect.hit = true
    isect.t = t
    isect.n = vec_copy( plane.n )
    isect.p = vec_new( ray.org.x + t * ray.dir.x, ray.org.y + t * ray.dir.y, ray.org.z + t * ray.dir.z )
  end
end

--------------------------------------------
-- ao.pde
--------------------------------------------

local NAO_SAMPLES = 8

local sphere = {}
sphere[0] = sphere_new( vec_new( -2.0, 0.0, -3.5 ), 0.5 )
sphere[1] = sphere_new( vec_new( -0.5, 0.0, -3.0 ), 0.5 )
sphere[2] = sphere_new( vec_new( 1.0, 0.0, -2.2 ), 0.5 )

local plane = plane_new( vec_new( 0.0, -0.5, 0.0 ), vec_new( 0.0, 1.0, 0.0 ) )

function clamp( f )
  local i = f * 255.5
  if i < 0 then 
    i = 0 
  end
  if i > 255 then 
    i = 255 
  end
  return i  
end

function orthoBasis( basis, n )

  basis[2] = vec_copy( n )
  basis[1] = vec_zero()
  basis[0] = vec_zero()

  if (n.x < 0.6) and (n.x > -0.6) then
    basis[1].x = 1.0
  elseif (n.y < 0.6) and (n.y > -0.6) then
    basis[1].y = 1.0
  elseif (n.z < 0.6) and (n.z > -0.6) then
    basis[1].z = 1.0
  else
    basis[1].x = 1.0
  end

  basis[0] = vec_cross( basis[1], basis[2] )
  basis[0] = vec_normalize( basis[0] )

  basis[1] = vec_cross( basis[2], basis[0] )
  basis[1] = vec_normalize( basis[1] )
end

function ambientOcclusion( isect )
  local i,j
  local ntheta = NAO_SAMPLES
  local nphi = NAO_SAMPLES
  local eps = 0.0001

  local p = vec_new( isect.p.x + eps * isect.n.x, isect.p.y + eps * isect.n.y, isect.p.z + eps * isect.n.z )

  local basis = {}
  orthoBasis( basis, isect.n )

  local occlusion = 0.0

  for j=0,ntheta-1 do
    for i=0,nphi-1 do
      local r = math.random()
      local phi = 2.0 * math.pi * math.random()

      local x = math.cos(phi) * math.sqrt(1.0 - r)
      local y = math.sin(phi) * math.sqrt(1.0 - r)
      local z = math.sqrt(r)

      local rx = x * basis[0].x + y * basis[1].x + z * basis[2].x
      local ry = x * basis[0].y + y * basis[1].y + z * basis[2].y
      local rz = x * basis[0].z + y * basis[1].z + z * basis[2].z

      local raydir = vec_new( rx, ry, rz )
      local ray = ray_new( p, raydir )

      local occIsect = intersection_new()
      sphere_intersect( occIsect, sphere[0], ray )
      sphere_intersect( occIsect, sphere[1], ray )
      sphere_intersect( occIsect, sphere[2], ray )
      plane_intersect( occIsect, plane, ray )

      if occIsect.hit then
        occlusion = occlusion + 1.0
      end
    end
  end

  occlusion = (ntheta * nphi - occlusion) / (ntheta * nphi)
  return vec_new( occlusion, occlusion, occlusion )
end

function render( width, height, y, nsubsamples )
  local fimg = {}

  local i,j, u,v
  for i=0,width do
    
    fimg[3 * i + 0] = 0
    fimg[3 * i + 1] = 0
    fimg[3 * i + 2] = 0

    for v=0,nsubsamples-1 do
      for u=0,nsubsamples-1 do
        local px = (i + (u / nsubsamples) - (width / 2.0)) / (width / 2.0)
        local py = (y + (v / nsubsamples) - (height / 2.0)) / (height / 2.0)
        py = -py

        local t = 10000.0
        local eye = vec_new( px, py, -1.0 )
        eye = vec_normalize( eye )

        local ray = ray_new( vec_zero(), eye )

        local isect = intersection_new()
        sphere_intersect( isect, sphere[0], ray )
        sphere_intersect( isect, sphere[1], ray )
        sphere_intersect( isect, sphere[2], ray )
        plane_intersect( isect, plane, ray )

        if isect.hit then
          t = isect.t
          local col = ambientOcclusion( isect )
          fimg[3 * i + 0] = fimg[3 * i + 0] + col.x
          fimg[3 * i + 1] = fimg[3 * i + 1] + col.y
          fimg[3 * i + 2] = fimg[3 * i + 2] + col.z
        end
      end
    end

    fimg[3 * i + 0] = fimg[3 * i + 0] / (nsubsamples * nsubsamples)
    fimg[3 * i + 1] = fimg[3 * i + 1] / (nsubsamples * nsubsamples)
    fimg[3 * i + 2] = fimg[3 * i + 2] / (nsubsamples * nsubsamples)
  end

  return fimg
end

--------------------------------------------
-- main
--------------------------------------------
local IMAGE_WIDTH = 64
local IMAGE_HEIGHT = 64
local NSUBSAMPLES = 2

local idx = mdi_img_offscreen( 0 )
mdi_img32_resize( idx, IMAGE_WIDTH, IMAGE_HEIGHT )

for y=0,IMAGE_HEIGHT-1 do
  local fimg = render( IMAGE_WIDTH, IMAGE_HEIGHT, y, NSUBSAMPLES )
  local i
  for i=0,IMAGE_WIDTH-1 do
    local r = clamp( fimg[3*i + 0] )
    local g = clamp( fimg[3*i + 1] )
    local b = clamp( fimg[3*i + 2] )
    mdi_img32_pixelset( idx, i,y, r,g,b,255 )
  end
end

mdi_new_img32( idx )