Dailycode.info

Short solution for short problems

iOS: Picture editor (zooming, drawing, ...) complete code

I needed a view controller that could edit an image. But not just edit, also zoom in and out on the image and moving the image in the canvas.

So I created a separate XIB file with view and a view controller. It's working perfect. The first version was without zoom, so it resized the image to the screen size. In this case we always lost the resolution of the image.

So first a little demo with some pictures:

After taking the picture with the camera, a thumbnail appears in the picture list. By clicking on it, we are redirected to the picture edit screen. This is done on the 'didSelectItemAtIndexPath' method of the collection view (code see below following picture):

 

The code to accomplish this is reasonably simple. I get the asset based on the picture URL that is saved in the collection view item:

-(void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath

{

    if(showZones==NO)

    {

        ImageThumbView * cell = (ImageThumbView *)[collectionView cellForItemAtIndexPath:indexPath];

        typedef void (^ALAssetsLibraryAssetForURLResultBlock)(ALAsset *asset);

        typedef void (^ALAssetsLibraryAccessFailureBlock)(NSError *error);

 

        Picture * p = [picturesForProject objectAtIndex:indexPath.row];

        

        ALAssetsLibraryAssetForURLResultBlock resultblock = ^(ALAsset *myasset)

        {

            ALAssetRepresentation *rep = [myasset defaultRepresentation];

            CGImageRef iref = [rep fullResolutionImage];

 

            if (iref)

            {

                @try {

                    self.img = [UIImage imageWithCGImage:iref scale:[rep scale] orientation:(UIImageOrientation)[rep orientation]];

                    [StoryBoardNavigation NavigateToChangePictureStoryboard:selfAndPicture:self.imgAndPictureObject:cell.pic];

                }

                @catch (NSException *exception) {

                    NSLog(@"Exception:%@",exception);

                    [cell.imgImage setImage:[UIImage imageNamed:@"error.png"]];

                }

            }

            else

            {

                [cell.imgImage setImage:[UIImage imageNamed:@"error.png"]];

            }

        };

        

        ALAssetsLibraryAccessFailureBlock failureblock  = ^(NSError *myerror)

        {

            NSLog(@"can't get image");

        };

        NSURL *asseturl = [NSURL URLWithString:p.picture_url];

        [assetslibrary assetForURL:asseturl

                       resultBlock:resultblock

                      failureBlock:failureblock];

    }

 

}

 

Here the code to call the picture edit, so here you actually provide the PictureEdit view controller with a image object:

+(void) NavigateToChangePictureStoryboard:(UIViewController *) vc AndPicture:(UIImage *) image AndPictureObject:(Picture *) pic

{

    PictureEditVC * vcTo = [[PictureEditVCallocinitWithNibName:@"PictureEditVC"bundle:nil];

    vcTo.imageForMainView = image;

    vcTo.pic = pic;

    [UIViewtransitionWithView:vc.viewduration:0.8options:UIViewAnimationOptionTransitionCrossDissolve

                    animations:^{

                        [vc.navigationController pushViewController:vcTo animated:NO];

                    }

                    completion:NULL];

 

 

}

Now we end up on the picture edit screen. On top you see 3 buttons, the first on the left saves the images if there were changes made. The second on the left cancels the changes. The button on the right switches between zooming and editing. Below the zooming mode is set, you can zoom by using the pinch gesture and you can move the picture with the pan gesture.

Before we edit, we click on the edit button, and you'll see the different color buttons appear. 

We click on the red button and draw the little red heart on the image.

Now lets zoom in. Click on the edit button to go to zoom mode and zoom in:

Now we click again on the edit button to edit and draw some yellow hearts:

Zoom out again and save:

If you look closely the thumbnail reflects the changes. A copy of the changed image is also saved in the library.

Before we look into the code of the view controller that does all the work, first thing is to create a XIB.

Create the XIB file with the view controller. The xib file is very simple and could look like this:

 

I provide 2 properties (Basically you only will need the first). The first is the image to edit, the second is the picture object with the url of the picture (I use this to store the picture reference in the local DB), if we change the picture, we will change the picture objects url.

The .h file of the picture edit view controller is very simple:

//

//  PictureEditVC.h

//  LocationReport

//

//  Created by Mark Deraeve on 03/06/14.

//  Copyright (c) 2014 AssistU. All rights reserved.

//

 

#import <UIKit/UIKit.h>

#import "Button.h"

#import "Picture.h"

 

@interface PictureEditVC : HomeVC <UIScrollViewDelegate>

 

@property (weak, nonatomic) IBOutlet Button *btnEdit;

@property (weak, nonatomic) IBOutlet Button *btnUndo;

@property (weak, nonatomic) IBOutlet Button *btnSave;

 

@property (weak, nonatomic) IBOutlet UIButton *btnYellow;

@property (weak, nonatomic) IBOutlet UIButton *btnGreen;

@property (weak, nonatomic) IBOutlet UIButton *btnRed;

@property (weak, nonatomic) IBOutlet UIButton *btnBlack;

 

@property (weak, nonatomic) UIImage * imageForMainView;

 

@property (weak, nonatomic) Picture * pic;

 

 

@end

It just has the outlets for the buttons and the image property and the picture object property. (You can easely change the type Button to UIButton, it's only for layout purpose.

Now all you need is the implementation file:

//

//  PictureEditVC.m

//  LocationReport

//

//  Created by Mark Deraeve on 03/06/14.

//  Copyright (c) 2014 AssistU. All rights reserved.

//

 

#import "PictureEditVC.h"

#import <AssetsLibrary/AssetsLibrary.h>

 

@interfacePictureEditVC ()

 

@end

 

@implementation PictureEditVC

{

    bool drawActive;

    CGPoint cumTranslation;

    CGFloat red;

    CGFloat green;

    CGFloat blue;

    CGFloat brush;

    CGFloat opacity;

    CGRect pageRect;

    UIImage * initImage;

    UIImageView * tempDrawImage;

    UIImageView * mainImageView;

    UIScrollView * scrollView;

    bool mouseSwiped;

    CGPoint lastPoint;

    CGPoint scaleLastPoint;

}

 

- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil

{

    self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];

    if (self) {

        // Custom initialization

    }

    returnself;

}

 

- (void)viewDidLoad

{

    [superviewDidLoad];

     [GeneralFunctionsMakeSelectedButton:self.btnBlack];

}

 

-(void) viewDidAppear:(BOOL)animated

{

    // Do any additional setup after loading the view from its nib.

    tempDrawImage.userInteractionEnabled =YES;

    cumTranslation = CGPointMake(0,0);

    

    drawActive = NO;

    red = 0.0/255.0;

    green = 0.0/255.0;

    blue = 0.0/255.0;

    

    brush = 3.0;

    opacity = 1.0;

    

    mainImageView.contentMode = UIViewContentModeScaleAspectFit;

    if (self.imageForMainView)

    {

        mainImageView = [[UIImageViewalloc] initWithImage:self.imageForMainView];

        pageRect = CGRectMake(0, 0, self.imageForMainView.size.width, self.imageForMainView.size.height);

        //mainImageView.image = self.imageForMainView;

    }

    else

    {

        pageRect = self.view.frame;

        pageRect.origin = CGPointMake(0,0);

        mainImageView = [[UIImageViewalloc] initWithFrame:pageRect];

    }

    

    tempDrawImage =[[UIImageViewalloc] initWithFrame:pageRect];

    

    scrollView = [[UIScrollViewalloc] initWithFrame:pageRect];

    scrollView.delegate = self;

    scrollView.minimumZoomScale = 0.3;

    scrollView.maximumZoomScale = 3.0;

    

    [scrollViewsetBackgroundColor:[UIColorredColor]];

    

    scrollView.contentSize= pageRect.size;

    

    [scrollViewaddSubview:mainImageView];

    [scrollViewaddSubview:tempDrawImage];

    [scrollViewsendSubviewToBack:mainImageView];

    

    [self.view addSubview:scrollView];

    [self.viewsendSubviewToBack: scrollView];

    

    UIPanGestureRecognizer *Scrolling  = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(onScroll:)];

    UIPinchGestureRecognizer *Zooming   = [[UIPinchGestureRecognizer alloc] initWithTarget:self action:@selector(onZoom:)];

    UITapGestureRecognizer *Tap   = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(onTap:)];

    

    [scrollView addGestureRecognizer:Scrolling];

    [scrollView addGestureRecognizer:Zooming];

    [scrollView addGestureRecognizer:Tap];

}

 

#pragma mark Zoom / Draw

 

- (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView

{

    return tempDrawImage;

}

 

- (void)onTap:(UIPinchGestureRecognizer*)sender

{

    if (drawActive)

    {

        UIGraphicsBeginImageContext(tempDrawImage.frame.size);

        [tempDrawImage.image drawInRect:CGRectMake(0, 0, tempDrawImage.frame.size.width, tempDrawImage.frame.size.height)];

        

        CGPoint currentPoint = [sender locationInView:tempDrawImage];

        

        CGPoint scalePoint;

        scalePoint.x = currentPoint.x*scrollView.zoomScale;

        scalePoint.y = currentPoint.y*scrollView.zoomScale;

        

        CGRect borderRect = CGRectMake(scalePoint.x, scalePoint.y, brush, brush);

        CGContextRef context = UIGraphicsGetCurrentContext();

        CGContextSetRGBStrokeColor(context, 1.0, 1.0, 1.0, 1.0);

        CGContextSetRGBStrokeColor(UIGraphicsGetCurrentContext(), red, green, blue, 1.0);

        CGContextSetLineWidth(context, brush);

        CGContextFillEllipseInRect (context, borderRect);

        CGContextStrokeEllipseInRect(context, borderRect);

        CGContextFillPath(context);

        

        CGContextFlush(UIGraphicsGetCurrentContext());

        tempDrawImage.image = UIGraphicsGetImageFromCurrentImageContext();

        UIGraphicsEndImageContext();

        

        //Draw dot.

        

    }

}

 

- (void)onZoom:(UIPinchGestureRecognizer*)sender

{

    if (!drawActive)

    {

        sender.view.transform = CGAffineTransformScale(sender.view.transform, sender.scale, sender.scale);

        sender.scale = 1;

    }

}

 

- (void)onScroll:(UIPanGestureRecognizer*)sender

{

    if (drawActive==NO)

    {

        CGPoint translation = [sender translationInView:self.view];

        sender.view.center = CGPointMake(sender.view.center.x + translation.x,

                                         sender.view.center.y + translation.y);

        [sender setTranslation:CGPointMake(0, 0) inView:self.view];

        cumTranslation = CGPointMake(cumTranslation.x + translation.x,

                                     cumTranslation.y + translation.y);

    }

    else

    {// Processing the drawing by using comparing:

        if (sender.state == UIGestureRecognizerStateBegan)

        { /* drawing began */

            

            mouseSwiped = NO;

            

            lastPoint = [sender locationInView:tempDrawImage];

            scaleLastPoint.x = lastPoint.x*scrollView.zoomScale;

            scaleLastPoint.y = lastPoint.y*scrollView.zoomScale;

            

        }

        else if (sender.state == UIGestureRecognizerStateChanged)

        { /* drawing occured */

            mouseSwiped = YES;

            

            CGPoint currentPoint = [sender locationInView:tempDrawImage];

            

            CGPoint scalePoint;

            scalePoint.x = currentPoint.x*scrollView.zoomScale;

            scalePoint.y = currentPoint.y*scrollView.zoomScale;

            

            UIGraphicsBeginImageContext(tempDrawImage.frame.size);

            [tempDrawImage.image drawInRect:CGRectMake(0, 0, tempDrawImage.frame.size.width, tempDrawImage.frame.size.height)];

            CGContextMoveToPoint(UIGraphicsGetCurrentContext(), scaleLastPoint.x, scaleLastPoint.y);

            CGContextAddLineToPoint(UIGraphicsGetCurrentContext(), scalePoint.x, scalePoint.y);

            CGContextSetLineCap(UIGraphicsGetCurrentContext(), kCGLineCapRound);

            CGContextSetLineWidth(UIGraphicsGetCurrentContext(), brush );

            CGContextSetRGBStrokeColor(UIGraphicsGetCurrentContext(), red, green, blue, 1.0);

            CGContextSetBlendMode(UIGraphicsGetCurrentContext(),kCGBlendModeNormal);

            

            CGContextStrokePath(UIGraphicsGetCurrentContext());

            tempDrawImage.image = UIGraphicsGetImageFromCurrentImageContext();

            [tempDrawImage setAlpha:opacity];

            UIGraphicsEndImageContext();

            

            scaleLastPoint = scalePoint;

        }

        else if (sender.state == UIGestureRecognizerStateEnded)

        { /* drawing ended */

            if(!mouseSwiped) {

                UIGraphicsBeginImageContext(tempDrawImage.frame.size);

                [tempDrawImage.image drawInRect:CGRectMake(0, 0, tempDrawImage.frame.size.width, tempDrawImage.frame.size.height)];

                CGContextSetLineCap(UIGraphicsGetCurrentContext(), kCGLineCapRound);

                CGContextSetLineWidth(UIGraphicsGetCurrentContext(), brush);

                CGContextSetRGBStrokeColor(UIGraphicsGetCurrentContext(), red, green, blue, opacity);

                CGContextMoveToPoint(UIGraphicsGetCurrentContext(), scaleLastPoint.x, scaleLastPoint.y);

                CGContextAddLineToPoint(UIGraphicsGetCurrentContext(), scaleLastPoint.x, scaleLastPoint.y);

                CGContextStrokePath(UIGraphicsGetCurrentContext());

                CGContextFlush(UIGraphicsGetCurrentContext());

                tempDrawImage.image = UIGraphicsGetImageFromCurrentImageContext();

                UIGraphicsEndImageContext();

            }

        }

    }

}

 

#pragma mark Button events

 

- (IBAction)btnEditClicked:(id)sender

{

    drawActive = !drawActive;

    self.btnBlack.hidden = !drawActive;

    self.btnGreen.hidden = !drawActive;

    self.btnRed.hidden = !drawActive;

    self.btnYellow.hidden = !drawActive;

    if (drawActive==YES)

    {

        [self.btnEdit setTitle:[[VariableStore sharedInstance] Translate:@"$PO$Zoom"] forState:UIControlStateNormal];

    }

    else

    {

        [self.btnEdit setTitle:[[VariableStore sharedInstance] Translate:@"$PO$Draw"] forState:UIControlStateNormal];

    }

}

- (IBAction)btnYellowClicked:(id)sender

{

    red = 255/255.0;

    green = 255.0/255.0;

    blue = 0.0/255.0;

    [self SetClicked:self.btnYellow];

}

- (IBAction)btnGreenClicked:(id)sender

{

    red = 0.0/255.0;

    green = 255.0/255.0;

    blue = 0.0/255.0;

    [self SetClicked:self.btnGreen];

}

- (IBAction)btnRedClicked:(id)sender

{

    red = 255.0/255.0;

    green = 0.0/255.0;

    blue = 0.0/255.0;

    [self SetClicked:self.btnRed];

}

- (IBAction)btnBlackClicked:(id)sender

{

    red = 0.0/255.0;

    green = 0.0/255.0;

    blue = 0.0/255.0;

    [self SetClicked:self.btnBlack];

}

 

-(void) SetClicked:(UIButton *) btn

{

    [GeneralFunctions MakeSimpleButton:self.btnBlack];

    [GeneralFunctions MakeSimpleButton:self.btnRed];

    [GeneralFunctions MakeSimpleButton:self.btnYellow];

    [GeneralFunctions MakeSimpleButton:self.btnGreen];

    [GeneralFunctions MakeSelectedButton:btn];

}

 

- (IBAction)btnUndoClicked:(id)sender

{

    tempDrawImage.image = nil;

}

- (IBAction)btnSaveClicked:(id)sender

{

    if (tempDrawImage!=nil)

    {

        UIGraphicsBeginImageContext(mainImageView.frame.size);

        

        [mainImageView.image drawInRect:CGRectMake(0, 0, mainImageView.frame.size.width, mainImageView.frame.size.height) blendMode:kCGBlendModeNormal alpha:1.0];

        [tempDrawImage.image drawInRect:CGRectMake(0, 0, tempDrawImage.frame.size.width, tempDrawImage.frame.size.height) blendMode:kCGBlendModeNormal alpha:opacity];

        mainImageView.image = UIGraphicsGetImageFromCurrentImageContext();

        tempDrawImage.image = nil;

        UIGraphicsEndImageContext();

 

        //overwrite the picture in the picture library

        ALAssetsLibrary *library = [[ALAssetsLibrary alloc] init];

        [library writeImageToSavedPhotosAlbum:mainImageView.image.CGImage orientation:(ALAssetOrientation)mainImageView.image.imageOrientation completionBlock:^(NSURL *assetURL, NSError *error )

         {

             if (self.pic)

             {

                 self.pic.picture_url = [NSString stringWithFormat:@"%@", assetURL];

                 [DBStore SaveContext];

             }

         }];

        [self.navigationController popViewControllerAnimated:YES];

    }

}

 

- (void)didReceiveMemoryWarning

{

    [super didReceiveMemoryWarning];

    // Dispose of any resources that can be recreated.

}

 

@end

 

Want to try this yourself, you could lose the Picture stuff, just provide the image to the view controller. Copy paste the code, with some easy fixes it should run perfectly. If needed, I could provide a demo project.


Using WIA 2.0 to take picture

I have a webcam connected via USB to my pc. I needed to take a picture and do some calculations to it.

As I started to use the WIA library I noticed that it supported VB.Net better then c#. Some things where not possible in c#. I create an interface, ICamera and a class Camera that implemented this interface. The Camera is written in VB.Net because of limitations in c#.

For now the class exposes 1 function: TakePicture. This funtion takes a picture, returns an System.Drawing.Image and cleans up reasources. Here is the class that does the work:

Imports System.Drawing
Imports NSP.Win.Interfaces.IO
Imports WIA
Imports System.IO
...
 
Public Function TakePicture() As System.Drawing.Image Implements ICamera.TakePicture
 
        Dim returnImg As Image = Nothing
        Dim DefaultDevice As Device
        Dim Dialog1 As New WIA.CommonDialog
        DefaultDevice = Dialog1.ShowSelectDevice 'Does NOT show dialog. Merely retrieves device. 
        Dim total As Integer
        total = DefaultDevice.Items.Count
 
        Dim Item As WIA.Item = DefaultDevice.ExecuteCommand(WIA.CommandID.wiaCommandTakePicture)
        Dim Img As WIA.ImageFile = Item.Transfer 'because this item is an image. 
 
        'Dim Img As WIA.ImageFile = DefaultDevice.Items(total).Transfer() 'Whereas ShowTransfer() shows the progress bar. 
        If File.Exists("MyImage.Tif") Then
            File.Delete("MyImage.Tif")
        End If
        Img.SaveFile("MyImage.Tif")
        returnImg = Image.FromFile("MyImage.Tif")
 
 
        Dim x As Short
        ' Remove all items from the current device
        For Each WIAItm In DefaultDevice.Items
            ' Run through each item and remove
            For x = 1 To DefaultDevice.Items.Count
                Try
                    ' Remove the item
                    DefaultDevice.Items.Remove(x)
                    ' Exit the loop
                    Exit For
                Catch exp As Exception
                End Try
            Next
        Next
        Return returnImg
    End Function
End Class

 

I'm starting to investigate the properties of the WIA device, so I can set the brithgness, resolution etc and expose them via an interface. Maybe I will post this later on.

If you are wondering what different in c#, for example, less or practically no information, also, the dialog.showselectdevice cannot be addressed in c# without parameters, and it will always show the GUI.