Bilinear Resampling and 21 Other Filters

From Directorforum Collaboration Wiki

Revision as of 21:06, 27 May 2009 by Martin Schaefer (Talk | contribs)
(diff) ← Older revision | Current revision (diff) | Newer revision → (diff)
Jump to: navigation, search
-- Bilinear Image Resampling and many other filters
-- by Josh Chunick
-- inspired by VB code written by Tanner Helland who,  
-- in turn, was inspired by code on vbAccelerator.com
-- other code 
 
-- bilinear-quality code worth considering
-- by fazstp DOUG Post: http://director-online.com/forums/read.php?2,30701
on ScaleIncremental var_image, var_scale
  final_width = integer( var_image.width * var_scale )
  final_height = integer( var_image.height * var_scale )
 
  final_image = image( final_width, final_height, 32 )
 
  temp_image = var_image.duplicate()
 
  repeat while ( temp_image.width < final_image.width )
    temp_image = ScaleImage( temp_image, 2 )
  end repeat
 
  final_image.copyPixels( temp_image, final_image.rect, temp_image.rect )
 
  return final_image
 
end ScaleIncremental
 
on ScaleImage var_image, var_scale
  final_width = integer( var_image.width * var_scale )
  final_height = integer( var_image.height * var_scale )
 
  final_image = image( final_width, final_height, 32 )
 
  final_image.copyPixels( var_image, final_image.rect, var_image.rect )
  final_image.copyPixels( var_image, final_image.rect + [1, 0, 1, 0], var_image.rect, [#blendLevel: 55] )
  final_image.copyPixels( var_image, final_image.rect + [-1, 0, -1, 0], var_image.rect, [#blendLevel: 55] )
  final_image.copyPixels( var_image, final_image.rect + [0, 1, 0, 1], var_image.rect, [#blendLevel: 55] )
  final_image.copyPixels( var_image, final_image.rect + [0, -1, 0, -1], var_image.rect, [#blendLevel: 55] )
  final_image.copyPixels( var_image, final_image.rect + [1, 1, 1, 1], var_image.rect, [#blendLevel: 22] )
  final_image.copyPixels( var_image, final_image.rect + [-1, 1, -1, 1], var_image.rect, [#blendLevel: 22] )
  final_image.copyPixels( var_image, final_image.rect + [1, -1, 1, -1], var_image.rect, [#blendLevel: 22] )
  final_image.copyPixels( var_image, final_image.rect + [-1, -1, -1, -1], var_image.rect, [#blendLevel: 22] )
 
  return final_image
 
end ScaleImage 
 
on InvertImage (theImage)
  theImage = theImage.duplicate()
  newImage = image(theImage.width, theImage.height, theImage.depth)
  newImage.floodfill(point(0,0), color(0,0,0))
  theRect = theImage.rect
  newImage.useAlpha = theImage.useAlpha
  newImage.copyPixels(theImage, theRect, theRect, [#ink: 2, #maskImage: theImage.extractAlpha()])
  return newImage
end
 
on Noise (theImage, colorize, theBlend, theInk)
 
  theWidth = theImage.width - 1
  theHeight = theImage.height - 1
  anImage = theImage.duplicate()
  if colorize then
    newImage = image(anImage.width, anImage.height, 8)
  else
    newImage = image(anImage.width, anImage.height, 8, #grayscale)
  end if
 
  repeat with y1 = 0 to theHeight   
    repeat with x1 = 0 to theWidth
      rnd = random(255)
      newImage.setPixel(point(x1,y1), color(rnd))
    end repeat
  end repeat
 
  anImage.copyPixels(newImage, anImage.rect, anImage.rect, [#ink: theInk, #blendLevel: theBlend])
  return anImage
 
end
 
on diffuseImage (theImage, amount)
 
  theWidth = theImage.width - 1
  theHeight = theImage.height - 1
  newImage = theImage.duplicate()
 
  -- the look-up table for the original image (done for speed purposes)
  listX = []
  listY = [] 
 
  repeat with x = 0 to theWidth
    repeat with y = 0 to theHeight
      listY.add(theImage.getPixel(x,y))
    end repeat
    listX.add(listY)
    listY = []
  end repeat
 
  -- draw each pixel in the new image
  repeat with y1 = 0 to theHeight 
 
    repeat with x1 = 0 to theWidth
      rndX = random(amount) - 2 
      rndY = random(amount) - 2 
 
      xMod = max(min(theWidth, x1 + rndX), 1)
      yMod = max(min(theHeight, y1 + rndY), 1)
 
      theColour = listX[xMod][yMod] 
 
      R = theColour.red
      G = theColour.green
      B = theColour.blue
 
      newImage.setPixel(point(x1,y1), color(R, G, B))
    end repeat
  end repeat
 
  return newImage
 
end
 
on embossImage (theImage, offsetX, offsetY)
 
  offsetX = integer(offsetX)
  offsetY = integer(offsetY)
 
  theWidth = theImage.width - 1
  theHeight = theImage.height - 1
  newImage = theImage.duplicate()
  -- the look-up table for the original image (done for speed purposes)
  listX = []
  listY = [] 
  repeat with x = 0 to theWidth
    repeat with y = 0 to theHeight
      listY.add(theImage.getPixel(x,y))
    end repeat
    listX.add(listY)
    listY = []
  end repeat
 
  -- draw each pixel in the new image
  repeat with y1 = 0 to theHeight
    yMod = max(min(theHeight, y1), 1)
    yMod1 = max(min(theHeight, y1 + offsetY), 1)
 
    repeat with x1 = 0 to theWidth
 
      xMod = max(min(theWidth, x1), 1)
      xMod1 = max(min(theWidth, x1 + offsetX), 1)
      theColour1 = listX[xMod][yMod] 
      theColour2 = listX[xMod1][yMod1] 
      R = abs(theColour1.red - theColour2.red + 128)
      G = abs(theColour1.green - theColour2.green + 128)
      B = abs(theColour1.blue - theColour2.blue + 128)
      newImage.setPixel(point(x1,y1), color(R, G, B))
    end repeat
  end repeat
 
  return newImage
end
 
on blurImage2 (theImage)
  theWidth = theImage.width - 1
  theHeight = theImage.height - 1
  newImage = theImage.duplicate()
  -- the look-up table for the original image (done for speed purposes)
 
  listX = []
  listY = [] 
 
  repeat with x = 0 to theWidth
    repeat with y = 0 to theHeight
      listY.add(theImage.getPixel(x,y))
    end repeat
    listX.add(listY)
    listY = []
  end repeat
 
  repeat with y1 = 0 to theHeight
    yMod = max(min(theHeight, y1), 1)
    yMod1 = max(min(theHeight, y1 - 1), 1)
    yMod2 = max(min(theHeight, y1 + 1), 1)
 
    repeat with x1 = 0 to theWidth
 
      -- get the 9 pixels around the interpolated one
      xMod = max(min(theWidth, x1), 1)
      xMod1 = max(min(theWidth, x1 - 1), 1)
      xMod2 = max(min(theWidth, x1 + 1), 1)
 
      theColour1 = listX[xMod1][yMod1]
      theColour2 = listX[xMod1][yMod]
      theColour3 = listX[xMod][yMod1]
      theColour4 = listX[xMod2][yMod1]
      theColour5 = listX[xMod1][yMod2]
      theColour6 = listX[xMod][yMod]
      theColour7 = listX[xMod2][yMod] 
      theColour8 = listX[xMod][yMod2]
      theColour9 = listX[xMod2][yMod2]
 
      R = (theColour1.red + theColour2.red + theColour3.red + theColour4.red + theColour5.red + theColour6.red + theColour7.red + theColour8.red + theColour9.red) / 9
      G = (theColour1.green + theColour2.green + theColour3.green + theColour4.green + theColour5.green + theColour6.green + theColour7.green + theColour8.green + theColour9.green) / 9
      B = (theColour1.blue + theColour2.blue + theColour3.blue + theColour4.blue + theColour5.blue + theColour6.blue + theColour7.blue + theColour8.blue + theColour9.blue) / 9
 
      --Set this pixel into the new image
      newImage.setPixel(point(x1,y1), color(R, G, B))
 
    end repeat
  end repeat
 
  return newImage
 
end
 
on horizontalBlur (theImage)
 
  -- ********************************************************************
  -- This is a crappy algorithm
  -- change it with one based off of a convolution matrix and copyPixels
  -- ********************************************************************
  theWidth = theImage.width - 1
  theHeight = theImage.height - 1
  newImage = theImage.duplicate()
  -- the look-up table for the original image (done for speed purposes)
 
  listX = []
  listY = [] 
 
  repeat with x = 0 to theWidth
    repeat with y = 0 to theHeight
      listY.add(theImage.getPixel(x,y))
    end repeat
    listX.add(listY)
    listY = []
  end repeat
 
  repeat with y1 = 0 to theHeight
    yMod = max(min(theHeight, y1 + 1), 1)
    -- yMod1 = max(min(theHeight, y1 - 1), 1)
    --yMod2 = max(min(theHeight, y1 + 1), 1)
 
    repeat with x1 = 0 to theWidth
 
      -- get the 9 pixels around the interpolated one
      xMod1 = max(min(theWidth, x1 - 4), 1)
      xMod2 = max(min(theWidth, x1 - 3), 1)
      xMod3 = max(min(theWidth, x1 - 2), 1)
      xMod4 = max(min(theWidth, x1), 1)
      xMod5 = max(min(theWidth, x1 + 1), 1)
      xMod6 = max(min(theWidth, x1 + 1), 1)
      xMod7 = max(min(theWidth, x1 + 2), 1)
      xMod8 = max(min(theWidth, x1 + 3), 1)
      xMod9 = max(min(theWidth, x1 + 4), 1)
 
 
      clr1 = listX[xMod1][yMod]
      clr2 = listX[xMod2][yMod]
      clr3 = listX[xMod3][yMod]
      clr4 = listX[xMod4][yMod]
      clr5 = listX[xMod5][yMod]
      clr6 = listX[xMod6][yMod]
      clr7 = listX[xMod7][yMod] 
      clr8 = listX[xMod8][yMod]
      clr9 = listX[xMod9][yMod]
 
      R = (clr1.red + 2 * (clr2.red) + 3 * (clr3.red) + 4 * (clr4.red) + 5 * (clr5.red) + 4 * (clr6.red) + 3 * (clr7.red) + 2 * (clr8.red) + clr9.red) / 25
      G = (clr1.green + 2 * (clr2.green) + 3 * (clr3.green) + 4 * (clr4.green) + 5 * (clr5.green) + 4 * (clr6.green) + 3 * (clr7.green) + 2 * (clr8.green) + clr9.green) / 25
      B = (clr1.blue + 2 * (clr2.blue) + 3 * (clr3.blue) + 4 * (clr4.blue) + 5 * (clr5.blue) + 4 * (clr6.blue) + 3 * (clr7.blue) + 2 * (clr8.blue) + clr9.blue) / 25
 
      --Set this pixel into the new image
      newImage.setPixel(point(x1,y1), color(R, G, B))
 
    end repeat
  end repeat
 
  return newImage
 
end
 
on sharpenImage (theImage, theAmount)
 
  theWidth = theImage.width - 1
  theHeight = theImage.height - 1
  newImage = theImage.duplicate()
  -- the look-up table for the original image (done for speed purposes)
  listX = []
  listY = [] 
  repeat with x = 0 to theWidth
    repeat with y = 0 to theHeight
      listY.add(theImage.getPixel(x,y))
    end repeat
    listX.add(listY)
    listY = []
  end repeat
  -- draw each pixel in the new image
  repeat with y1 = 2 to theHeight
    repeat with x1 = 2 to theWidth
 
      -- get the 2 pixels we need for sharpening
      theColour1 = listX[x1][y1]
      theColour2 = listX[x1 - 1][y1 - 1]
 
      newColour = theColour1 + theAmount * (theColour1 - theColour2)
 
      --      if y1 = 2 and x1 = 2 then put newColour
 
      --Set this pixel into the new image
      newImage.setPixel(point(x1 - 1,y1 - 1), newColour)
 
    end repeat
  end repeat
 
  return newImage
 
end
 
 
-- Bilinear Image Resampling Code
-- ©2005 by Josh Chunick (josh@chunick.com)
-- code optimizations by Thomas Mavrofides
-- This code is free to use in commercial applications
-- or however you want.If you use this code you
-- must keep the comments, including this message,
-- intact. Feel free to add any changes or make improvements.
-- 
-- inspired by VB code from vbAccelerator.com
 
on bilinearResample2 (theImage, newWidth, newHeight, sharpen, amount) 
  --theImage = anImage.duplicate()
  newImage = image(newWidth, newHeight, 32)
  oldWidth = theImage.width - 1
  oldHeight = theImage.height - 1
 
  -- return the original image if the scale factor is 1 
  if newImage.rect = theImage.rect then return theImage
 
  -- the look-up table for the original image (done for speed purposes)
  -- this shaves off 600 milliseconds from my 320x240 test image
  listX = []
  listY = []
  repeat with x = 0 to oldWidth
    repeat with y = 0 to oldHeight
      listY.add(theImage.getPixel(x,y))
    end repeat
    listX.add(listY)
    listY = []
  end repeat
 
  -- get the ratio between the old image and the new one
  xScale = (oldWidth) / float(newWidth)
  yScale = (oldHeight) / float(newHeight)
 
  -- draw each pixel in the new image 
  repeat with y = 1 to newHeight
 
    -- generate the y calculation variables
    dstY = (y - 1) * yScale
    interplY = bitXor(dstY, 0)
    calcY = dstY - interplY
 
    repeat with x = 1 to newWidth
      -- generate the x calculation variables
      dstX = (x - 1) * xScale
      interplX = bitXor(dstX, 0)
      calcX = dstX - interplX
 
      -- get the 4 pixels around the interpolated one
      theColour1 = listX[interplX + 1][interplY + 1]
      theColour2 = listX[interplX + 2][interplY + 1]
      theColour3 = listX[interplX + 1][interplY + 2]
      theColour4 = listX[interplX + 2][interplY + 2]
 
      -- calculate the new colour
      newColor1 = theColour1 * (1 - calcY) + theColour3 * calcY
      newColor2 = theColour2 * (1 - calcY) + theColour4 * calcY
      newColor= newColor1 * (1 - calcX) + newColor2 * calcX
 
      --Set this pixel into the new image
 
      newImage.setPixel(point(x - 1,y - 1), newColor)
 
    end repeat
  end repeat
 
  if sharpen then
    if voidP(amount) or amount = 0 then amount = 0.5
    newImage = sharpenImage (newImage, amount)
  end if
 
  return newImage
 
end
 
on fastBlur (anImage, hBlur, vBlur) 
  newImage = anImage.duplicate()
  theWidth = newImage.width - 1
  theHeight = newImage.height - 1
 
  -- draw each pixel in the new image 
  repeat with y1 = 0 to theHeight
 
    repeat with x1 = 0 to theWidth
 
      interplY1 = max(min(y1 - vBlur, y1 - 1), 1)
      interplX1 = max(min(x1 - hBlur, x1 - 1), 1)
      interplY2 = min(max(y1 + vBlur, y1 + 1), theHeight)
      interplX2 = min(max(x1 + hBlur, x1 + 1), theWidth)
      newImage.copyPixels(anImage, rect(x1,y1,x1 + 1,y1 + 1), rect(interplX1,interplY1,interplX2,interplY2))
 
    end repeat
  end repeat
 
  return newImage
 
end
 
on rescale (membRef, maxW, maxH)
  wide=membRef.width
  high=membRef.height
  k=new(#bitmap)
  repeat while wide>maxW or high>maxH
    if wide>maxW then
      scaler=maxW/float(wide)
      wide=wide*scaler
      high=high*scaler
      nI=image(wide,high,32)
      nI.copyPixels(membRef,nI.rect,membRef.rect)
      k.erase()
      k=new(#bitmap)
      k.image=nI
    end if  
    wide=membRef.width
    high=membRef.height
    if high>maxH then
      scaler=maxH/float(high)
      wide=wide*scaler
      high=high*scaler
      nI=image(wide,high,32)
      nI.copyPixels(membRef,nI.rect,membRef.rect)
      k.erase()
      k=new(#bitmap)
      k.image=nI
    end if
    wide=membRef.width
    high=membRef.height
  end repeat
  return k.image
  k.erase()
end
 
on bilinearResample1 (sourceImg, newWidth, newHeight, antiAlias)
  ----------------------------------------------------------------
  -- original code written created by "johnAQ" from the MM forums
  -- modified by Josh Chunick
  -- modification includes the addition of a blurring on the image
  -- to smooth out any jaggies which may appear on diagonal lines
  -- in the image when scaling by fractional factors.
  ----------------------------------------------------------------
  theStart = the milliseconds
  oldImgObj = sourceImg.duplicate()
  sourceDepth = oldImgObj.depth
 
  oldWidth = oldImgObj.width
  oldHeight = oldImgObj.height
 
  --first do the scale along the x axis
  newImgObj1 = image(newWidth, oldHeight, 32)
  if newWidth <> oldWidth then
 
    theWidth = newWidth - 1
    theHeight = newHeight - 1
 
    repeat with i = 0 to newWidth
      ratio = 1.0 * i / (newWidth - 1) * (oldWidth - 1)
      a = bitOr(ratio, 0)
      t = ratio - 1
      destRect = rect(i, 0, i + 1, oldHeight)
      sourceRect = rect(a, 0, a + 1, oldHeight)
      newImgObj1.copyPixels(oldImgObj, destRect, sourceRect)
      if t > 0 then
        sourceRect = rect(a + 1, 0, a + 2, oldHeight)
        newImgObj1.copyPixels(oldImgObj, destRect, sourceRect, [#blendLevel: 255 * t])
      end if
    end repeat
  else
    newImgObj1.copyPixels(oldImgObj, oldImgObj.rect, oldImgObj.rect)
  end if
 
  --next do the scale along the y axis
  newImgObj2 = image(newWidth, newheight, 32)
  if newHeight <> oldHeight then
    repeat with i = 0 to newHeight
      ratio = 1.0 * i / (newHeight - 1) * (oldHeight - 1)
      a = bitOr(ratio, 0)
      t = ratio - 1
      destRect = rect(0, i, newWidth, i + 1)
      sourceRect = rect(0, a, newWidth, a + 1)
      newImgObj2.copyPixels(newImgObj1, destRect, sourceRect)
      if t > 0 then
        sourceRect = rect(0, a + 1, newWidth, a + 2)
        newImgObj2.copyPixels(newImgObj1, destRect, sourceRect, [#blendLevel: 255 * t])
      end if
    end repeat
  else
    newImgObj2.copyPixels(newImgObj1, newImgObj1.rect, newImgObj1.rect)
  end if
  if antiAlias then
    theOffset = 3
    newImgObj3 = blurImage(newImgObj2, theOffset)
    newImgObj2.copyPixels(newImgObj3, newImgObj2.rect,  newImgObj3.rect + rect(theOffset, theOffset, - theOffset, - theOffset), [#Ink: 39])
  end if
  return newImgObj2
end
 
on nearestNeighbour (sourceImage, newWidth, newHeight, antiAlias)
  newImage = image(newWidth, newHeight, 32)
  newImage.copyPixels(sourceImage, newImage.rect, sourceImage.rect)
  if antiAlias then
    theOffset = 3
    newImage2 = blurImage(newImage, theOffset)
    newImage.copyPixels(newImage2, newImage.rect,  newImage2.rect + rect(theOffset, theOffset, - theOffset, - theOffset), [#Ink: 39])
  end if
  return newImage
end
 
on blurImage (anImage, blurLevel)
  -- blurring the image
  myImg = anImage.duplicate()
  imgW = myImg.width
  imgH = myImg.height
  buffer1 = myImg.duplicate() 
 
  -- NW SE NE SW W E N S center
  offsetL = [[0,0], [2, 2], [0, 2], [2, 0], [0, 1], [2, 1], [1, 0], [1, 2], [1,1]]
 
  myBlend = 5*255/offsetL.count --1.8*255/offsetL.count 
  -- 1.8 = luminosity correction, from 1.5 to 5
  -- optional : start a loop for multiple pass
  repeat with i = 1 to blurLevel 
    -- blurring the image
    buffer2 = image( imgW+2*i, imgH+2*i, 32 )
    myRect = rect( 0, 0, buffer1.width, buffer1.height )
    repeat with j = 1 to 9 -- = offsetL.count
      destRect = myRect.offset(offsetL[j][1], offsetL[j][2])
      buffer2.copyPixels(buffer1, destRect, myRect, [#blendLevel : myBlend])
    end repeat 
 
    buffer1 = buffer2.duplicate()
  end repeat 
 
  --   apply the result
  return buffer1
end
 
on grayScaleImg (theImage, theMethod, sepia, sepiaColor, sepiaAmount)
  grayImage = theImage.duplicate()
  -- Luminance Coefficients
  -- http://en.wikipedia.org/wiki/Luminance_(video)
  -- CCIR 601 (most digital standards) Y' = 0.299 R' + 0.587 G' + 0.114 B'
  -- http://en.wikipedia.org/wiki/Luminance_%28relative%29
  -- ITU-R BT.709 (sRGB) Y = 0.2126 R + 0.7152 G + 0.0722 B
  if grayImage.paletteRef <> #grayscale then
    Case theMethod of
      1:
        newImage = image(grayImage.width, grayImage.height, 8, #grayscale)
        newImage.copyPixels(grayImage, grayImage.rect, grayImage.rect, [#dither: 1215]) -- 1969, 1215
        grayImage = newImage
 
      2: 
        newImage = image(grayImage.width, grayImage.height, 8, #grayscale)
        newImage.copyPixels(grayImage, grayImage.rect, grayImage.rect, [#dither: 1969])
        grayImage = newImage
 
      3:
        theWidth = grayImage.width - 1
        theHeight = grayImage.height - 1
        newImage = image(grayImage.width, grayImage.height, 8, #grayscale)
        repeat with x = 0 to theWidth
          repeat with y = 0 to theHeight
            theColour = grayImage.getPixel(point(x,y))
            -- commented out one is not as rich, but brings out more detail, potentially
            -- grayComp = (0.2980 * theColour.red) + (0.5882 * theColour.green) + (0.1138 * theColour.blue)
            -- the below uses these fractions: 54/255 R, 182.5/255 G, 18.5/255 B
            -- correction - using (sRGB) coefficients now.
            grayComp = (0.2126 * theColour.red) + (0.7152 * theColour.green) + (0.0722 * theColour.blue)
            newImage.setPixel(point(x,y), color(255 - grayComp)) 
          end repeat
        end repeat
        grayImage = newImage
 
      4:
        theWidth = grayImage.width - 1
        theHeight = grayImage.height - 1
        newImage = image(grayImage.width, grayImage.height, 8, #grayscale)
        repeat with x = 0 to theWidth
          repeat with y = 0 to theHeight
            theColour = grayImage.getPixel(point(x,y))
            -- not as rich, but brings out more detail, potentially
            -- uses these fractions: 76/255 R, 150/255 G, 29/255 B
            -- grayComp = (0.1 * theColour.red) + (.8 * theColour.green) + (.1 * theColour.blue)
            grayComp = (0.2980 * theColour.red) + (0.5882 * theColour.green) + (0.1138 * theColour.blue)
            newImage.setPixel(point(x,y), color(255 - grayComp))
          end repeat
        end repeat
        grayImage = newImage
 
    end Case
  end if
 
  if the paramCount >= 3 and sepia then 
    sepiaImg = image(grayImage.width, grayImage.height, 32)
    colour = color(112,  66,   20)
    if ilk(sepiaColor) = #color then colour = sepiaColor
    sepiaImg.fill(point(0,0), colour)
    tmpGrayImg = image(grayImage.width, grayImage.height, 32)
    tmpGrayImg.copyPixels(grayImage, grayImage.rect, grayImage.rect)
    grayImage = tmpGrayImg.duplicate()
    tmpGrayImg.copyPixels(sepiaImg, tmpGrayImg.rect, sepiaImg.rect, [#ink: 33])
    blendLvl = 100
    if integerP(sepiaAmount) then blendLvl = sepiaAmount
    grayImage.copyPixels(tmpGrayImg, grayImage.rect, tmpGrayImg.rect, [#ink: 3, #blendLevel: blendLvl])
  end if
  return grayImage
end
 
on posterize (imageRef, bitDepth, thePalette, theDither)
  posterizedImage = image (imageRef.width, imageRef.height, bitDepth, thePalette)
  posterizedImage.copyPixels (imageRef, imageRef.rect, imageRef.rect, [#dither: theDither])
  return posterizedImage
end 
 
on blackWhite1 (imageRef)
  b_wImage = image (imageRef.width, imageRef.height, 1)
  b_wImage.copyPixels (imageRef, imageRef.rect, imageRef.rect)
  return b_wImage
end
 
on blackWhite2 (imageRef, thePalette, theDither)
  posterizedImage = image (imageRef.width, imageRef.height, 8, thePalette)
  posterizedImage.copyPixels (imageRef, imageRef.rect, imageRef.rect, [#dither: theDither])
  b_wImage = image (imageRef.width, imageRef.height, 1)
  b_wImage.copyPixels (posterizedImage, imageRef.rect, imageRef.rect)
  return b_wImage
end
 
on flipImage (anImage, aFlip)
  --------------------------------------------------------------------
  -- original code by Luke Wigley www.lingoworkshop.com
  -- I added the flip parameter.
  -- aFlip can be these values: #flipV, #flipH, #flipVH
  --------------------------------------------------------------------
  aRect = anImage.rect
  aQuad = [point(0,0), point(aRect.width,0), point(aRect.width,aRect.height), point(0,aRect.height)]
  Case aFlip of
    #flipV:
      newQuad = [aQuad[4], aQuad[3], aQuad[2], aQuad[1]]
    #flipH:
      newQuad = [aQuad[2], aQuad[1], aQuad[4], aQuad[3]]
    #flipVH:
      newQuad = [aQuad[3], aQuad[4], aQuad[1], aQuad[2]]
  end Case
  newImage = image(anImage.width, anImage.height, anImage.depth)
  newImage.copyPixels(anImage, newQuad, aRect)
  return newImage
end 
 
 
on rotateImage (anImage, anAngle, anOffSet, aColour, smoothing)
  --------------------------------------------------------------------
  -- rotates the supplied quad. 
  -- Angle is specified in degrees.
  -- I Added:
  -- a) anImage parameter
  -- b) aColour parameter
  -- c) the smoothing parameter
  -- the offset parameter is the pivot point of the rotation.
  -- pass along the member.regPoint for rotation around the center point
  -- the colour parameter is the background colour of the empty image
  -- which you get when you rotate an image.
  -- original code by Luke Wigley www.lingoworkshop.com
  --------------------------------------------------------------------
  aWidth = anImage.width
  aHeight = anImage.height
  if smoothing then 
    anImage = bilinearResample2(anImage, aWidth * 2, aHeight * 2, True)
    anOffSet = anOffSet * 2
  end if
  aRect = anImage.rect
  aQuad = [point(0,0), point(aRect.width,0), point(aRect.width,aRect.height), point(0,aRect.height)]
  newQuad = []
  anAngle = anAngle * pi/180.0
  repeat with j = 1 to 4
    thisPoint = aQuad[j]
    x = thisPoint[1] - anOffSet[1]
    y = thisPoint[2] - anOffSet[2]
    thisPoint[1] = x * cos(anAngle) - y * sin(anAngle)
    thisPoint[2] = x * sin(anAngle) + y * cos(anAngle)
    newQuad[j] = thisPoint + anOffSet
  end repeat
  -- wMax = max(newQuad[1][1], newQuad[2][1], newQuad[3][1], newQuad[4][1])
  -- wMin = min(newQuad[1][1], newQuad[2][1], newQuad[3][1], newQuad[4][1])
  -- hMax = max(newQuad[1][2], newQuad[2][2], newQuad[3][2], newQuad[4][2])
  -- hMin = min(newQuad[1][2], newQuad[2][2], newQuad[3][2], newQuad[4][2])
  -- newWidth = bitOr((wMax - wMin),0)+((wMax - wMin)>0)
  -- newHeight = bitOr((hMax - hMin),0)+((hMax - hMin)>0)
  -- newRect = rect(0,0,newWidth, newHeight) 
  --  put newRect
  newImage = image(anImage.width, anImage.height, anImage.depth)
  newImage.floodFill(point(0,0), aColour)
  newImage.copyPixels(anImage, newQuad, aRect, [#useFastQuads: 0])
  if smoothing then newImage = bilinearResample2 (newImage, aWidth, aHeight)
  return newImage
end
 
-- emulates find edges in paint window:
-- findEdges(member(4).image, 2, 1, 1.3)
 
on findEdges (anImage, blurLevel, offsetMultiplier, gamma)
  myImg = anImage.duplicate()
  imgW = myImg.width
  imgH = myImg.height
  buffer1 = myImg.duplicate()
  -- NW, SE, NE, SW, W, E, N, S, center
  -- Sobel filter (kernel)
  -- offsetL = [[1,-1], [-1, 1], [-1, -1], [1, 1], [2, 0], [-2, 0], [0, -2], [0, 2], [0,0]]
  -- Prewitt filter (kernel)
  offsetL = [[1,-1], [-1, 1], [-1, -1], [1, 1], [1, 0], [-1, 0], [0, -1], [0, 1], [0,0]]
  offsetL = offsetL * offsetMultiplier
  offsetCount = offsetL.count
  --myBlend = 1.8*255/offsetCount
  myBlend = gamma*255/offsetCount
  -- 1.8 = luminosity correction, from 1.5 to 5
  repeat with i = 1 to blurLevel 
    -- blurring the image
    buffer2 = image( imgW+2*i, imgH+2*i, 32 )
    myRect = rect( 0, 0, buffer1.width, buffer1.height )
    repeat with j = 1 to offsetCount
      destRect = myRect.offset(offsetL[j][1], offsetL[j][2])
      buffer2.copyPixels(buffer1, destRect, myRect, [#blendLevel : myBlend])
    end repeat 
    buffer1 = buffer2.duplicate()
  end repeat 
  myRect = myImg.rect
  myImg.copyPixels(buffer1, myRect, myRect, [#ink: 2])
  myImg2 = image(imgW, imgH, 1)
  myImg2.copyPixels(myImg, myRect, myRect)
  myImg.copyPixels(myImg2, myRect, myRect)
  return myImg  
end