marți, 2 august 2011

A MonoTouch class for resize maintaining aspect ratio or crop your images

Hy guys,

I wrote a c# class for a personal project, and I'm sure it will be useful for you to. Very often we need to quickly resize an UIImage, using Thumb Resize maintaining aspect ratio or Crop Resize...

A quick example to crop and resize an image from 768x507 to 300x300:
//load a test image, with size: 768x507
UIImage img = UIImage.FromFile("test.jpg");
//initiate the resizer class
ImageResizer resizer = new ImageResizer(img);
//crop resize 
resizer.CropResize( 300,300 );
//and that's it! The image is resized & croped to exactly 300x300
UIImage croppedImage = resizer.ModifiedImage;

Or resize maintaining ratio to a maximum of 200x200:
//load a test image, with size: 768x507
UIImage img = UIImage.FromFile("test.jpg");
//initiate the resizer class
ImageResizer resizer = new ImageResizer(img);
//crop resize 
resizer.RatioResize( 200,200 ); //now it has 200x132
//and that's it! 
UIImage croppedImage = resizer.ModifiedImage;

Also you can scale it to 120% for example
//load a test image, with size: 768x507
UIImage img = UIImage.FromFile("test.jpg");
//initiate the resizer class
ImageResizer resizer = new ImageResizer(img);
//crop resize 
resizer.Scale( 120 ); //now it has 922x608
//and that's it! 
UIImage croppedImage = resizer.ModifiedImage;

So, here it is!

using System;
using MonoTouch.UIKit;
using System.Drawing;
using MonoTouch.CoreGraphics;
namespace Test
{
 public enum CropPosition
 {
  Start, Center, End
 }
 
 public class ImageResizer
 {
  UIImage initialImage  = null;
  UIImage modifiedImage = null;
  
  public ImageResizer ( UIImage image )
  {
   this.initialImage         = image;
   this.modifiedImage = image;
  }
  
  
  /// <summary>
  /// strech resize
  /// </summary>
  public void Resize( float width, float height )
  {
   UIGraphics.BeginImageContext( new SizeF( width, height ) );
   //
   modifiedImage.Draw( new RectangleF( 0,0, width, height ) );
   modifiedImage = UIGraphics.GetImageFromCurrentImageContext();
   //
   UIGraphics.EndImageContext();
  }
  
  public void RatioResizeToWidth( float width )
  {
   Console.WriteLine("resize to width: " + width  );
   var cur_width = modifiedImage.Size.Width;
   if( cur_width > width )
   {
    var ratio  = width / cur_width;
    var height = modifiedImage.Size.Height * ratio;
    //resize
    Resize( width, height );
   }
  }
  
  public void RatioResizeToHeight( float height )
  {
   Console.WriteLine("resize to height: " + height  );
   var cur_height = modifiedImage.Size.Height;
   if( cur_height > height )
   {
    var ratio = height / cur_height;
    var width = modifiedImage.Size.Width * ratio;
    //
    Resize( width, height );
   }
  }
  
  /// <summary>
  /// resize maintaining ratio
  /// </summary>
  public void RatioResize( float max_width, float max_height )
  {
   if( max_width > max_height )
   {
    RatioResizeToWidth ( max_width  );
    RatioResizeToHeight( max_height );
   }
   else
   {
    RatioResizeToHeight( max_height );
    RatioResizeToWidth ( max_width  );
   }
  }
  
  /// <summary>
  /// scaling the image with a given procent value
  /// </summary>
  public void Scale( float procent )
  {
   var width  = modifiedImage.Size.Width  * (procent / 100);
   var height = modifiedImage.Size.Height * (procent / 100);
   Resize( width, height );
  }
  
  /// <summary>
  /// default crop resize, set on center
  /// </summary>
  public void CropResize( float width, float height )
  {
   CropResize(width, height, CropPosition.Center );
  }
  
  /// <summary>
  /// resize and crop to a specific location
  /// </summary>
  public void CropResize( float width, float height, CropPosition position )
  {
   SizeF ImgSize = modifiedImage.Size;
   //
   if( ImgSize.Width  < width  ) width  = ImgSize.Width;
   if( ImgSize.Height < height ) height = ImgSize.Height;
   //
   float crop_x = 0;
   float crop_y = 0;
   if( ImgSize.Width / width < ImgSize.Height / height )
   {
    //scad din width
    RatioResizeToWidth( width );
    ImgSize = modifiedImage.Size;
    //compute crop_y
    if( position == CropPosition.Center ) crop_y = (ImgSize.Height / 2) - (height / 2);
    if( position == CropPosition.End    ) crop_y = ImgSize.Height - height;
   }
   else
   {
    //change height
    RatioResizeToHeight( height );
    ImgSize = modifiedImage.Size;
    //calculeaza crop_x
    if( position == CropPosition.Center ) crop_x = (ImgSize.Width / 2) - (width / 2);
    if( position == CropPosition.End    ) crop_x = ImgSize.Width - width;
   }
   //create new contect
   UIGraphics.BeginImageContext( new SizeF( width, height ) );
   CGContext context =  UIGraphics.GetCurrentContext();
   //crops the new context to the desired height and width
   RectangleF clippedRect = new RectangleF( 0, 0, width, height );
   context.ClipToRect(clippedRect);
   //draw my image on the context
   RectangleF drawRect = new RectangleF( -crop_x, -crop_y, ImgSize.Width, ImgSize.Height );
   modifiedImage.Draw( drawRect );
   //save the context in modifiedImage
   modifiedImage = UIGraphics.GetImageFromCurrentImageContext();
   //close the context
   UIGraphics.EndImageContext();
   
  }
 

  public UIImage InitialImage 
  {
   get 
   {
    return this.initialImage;
   }
  }

  public UIImage ModifiedImage 
  {
   get 
   {
    return this.modifiedImage;
   }
  }
 }
}
 

marți, 5 iulie 2011

A corona lua multi-touch pinch-zoom example





I found that the pinch zoom example in here was a little harder to understand, so I decided to make my own class to handle pinch-zoom events:

It's simple to use, just do something like:
--hide the statusbar
display.setStatusBar( display.HiddenStatusBar )

--include the mtouch library
local mtouch = require( "mtouch" )

--create the zoom object - an image
local image     = nil

local function OnZoomIn( event )
 --grow our image
 image.xScale = image.xScale * 1.01
 image.yScale = image.yScale * 1.01
 print("new width="..image.contentWidth)
end

local function OnZoomOut( event )
 --shrink our image
 image.xScale = image.xScale / 1.01
 image.yScale = image.yScale / 1.01
 print("new width="..image.contentWidth)
end

local function main()
 --add the image
 image = display.newImage("image.jpg" )
 --set the imaage to the center of the device
 image.x = display.contentWidth  * 0.5
 image.y = display.contentHeight * 0.5
 --create the pinch zoom over our image
 mtouch.setZoomObject( image )
 mtouch.setOnZoomIn  ( OnZoomIn  ) 
 mtouch.setOnZoomOut ( OnZoomOut  )
 --add the fictive touch for the simulator - comment this if you are deploying to device!
 mtouch.setFictiveSimulatorTouch( 100, 100 )
end

main()

Screenshot:


And the mtouch class:
module(..., package.seeall)
-- activate multitouch
system.activate( "multitouch" )

-- math helpers
local sqrt         = math.sqrt
local abs     = math.abs
-- current touches
local touches      = {}
-- last zoom distance - I use this to compare with current distance
-- if its smaller then zoom out, else  zoom in
local lastDistance = 0
local zoomObject   = nil      --the zoom object
local OnZoomIn     = nil      --the zoom in event
local OnZoomOut    = nil      --the zoom out event
--fictive touch
local fictiveTouch = {}
fictiveTouch.id = "QHHJGL10"

-----------------------------------------------
-- counts a dictionary
-----------------------------------------------
local function CountDictionary(dict)
 local ret = 0
 for k, v in pairs(dict) do
  ret = ret + 1
 end
 return ret
end

-----------------------------------------------
-- returns an element at some index in a dictionary
-----------------------------------------------
local function GetDictElAtIndex( dict, index )
 local temp = 0
 local ret  = nil
 for k, v in pairs(dict) do
  if( temp == index ) then
   ret   = v
   break
  end
  temp   = temp + 1
 end
 return ret
end

-----------------------------------------------
-- update x and y for a specific touch-event
-----------------------------------------------
local function UpdateTouch( event )
 local id  = event.id
 local obj = event.target
 local  x  = event.x
 local  y  = event.y
 if touches[id] then
  touches[id].x = x
  touches[id].y = y
 end
end

-----------------------------------------------
-- calculate the distance between two touches 
-----------------------------------------------
local function DistanceBetween2T( t1, t2 )
 local ret = 0
 local deltax = t2.x - t1.x
 local deltay = t2.y - t1.y
 ret          = sqrt( deltax * deltax + deltay * deltay )
 return ret
end
-----------------------------------------------
-- touch listener
-----------------------------------------------
function on_touch( event )
 local ret = false
 if     event.phase == "began" then
  --register this touch
  touches[ event.id ] = event
 elseif event.phase == "moved" then
  UpdateTouch(event)
  --verify if i have at least 2 touches
  if( CountDictionary(touches) >= 2 ) then
   zoomObject.touching = true
   --gets the first 2 touches and calculate the distance between them
   local touch1 = GetDictElAtIndex( touches, 0 )
   local touch2 = GetDictElAtIndex( touches, 1 )
   local dist   = DistanceBetween2T( touch1, touch2 )
   --
   local args        = {}
   args.distance     = dist
   args.lastdistance = lastDistance
   args.difference   = abs( lastDistance - dist )
   args.touch1       = touch1
   args.touch2       = touch2
   if lastDistance ~= -1 then
    if dist < lastDistance then 
     --zoom out
     if OnZoomOut ~= nil then OnZoomOut( args ) end
    else
     --zoom in
     if OnZoomIn  ~= nil then  OnZoomIn( args ) end
    end
    ret = true
   end
   --save the lastdistance
   lastDistance = dist
  end
 elseif event.phase == "ended" then
  --remove this touch from list
  touches[ event.id ]  = nil
  lastDistance    = -1
  zoomObject.touching = false
 end
 return ret
end

-----------------------------------------------
-- properties
-----------------------------------------------
function setZoomObject( obj )
 zoomObject          = obj
 zoomObject.touching = false
 -- register table listener
 zoomObject:addEventListener( "touch", on_touch )
end


-----------------------------------------------
-- returns current zoom object
-----------------------------------------------
function getZoomObject() 
 return zoomObject
end

-----------------------------------------------
-- returns the number of current touches
-----------------------------------------------
function getNrTouches() 
 return CountDictionary(touches)
end

-----------------------------------------------
-- sets the zoomin event function
-----------------------------------------------
function setOnZoomIn( event )
 OnZoomIn = event
end

-----------------------------------------------
-- sets the zoomout event function
-----------------------------------------------
function setOnZoomOut( event )
 OnZoomOut = event
end

-----------------------------------------------
-- for the simulator: create a fictive touch
-----------------------------------------------
function setFictiveSimulatorTouch( x, y)
 fictiveTouch.x = x; fictiveTouch.y = y
 touches[ fictiveTouch.id ] = fictiveTouch
 fictiveTouch.rect = display.newRect( fictiveTouch.x, fictiveTouch.y, 20, 20 )
end

-----------------------------------------------
-- for the simulator: removes a fictive touch
-----------------------------------------------
function removeFictiveSimulatorTouch()
 touches[ fictiveTouch.id ] = nil
 fictiveTouch.rect:removeSelf()
end

-----------------------------------------------
-- clean me
-----------------------------------------------
function clean()
 zoomObject:removeEventListener( "touch", on_touch )
end