📜 ⬆️ ⬇️

We write the utility for cutting pictures

Recently, I needed a utility for cutting the image into small pieces of the same size, but all searches did not succeed. After that, a good old thought came to mind - “if you want to do something well, do it yourself” and it was decided to write a tiny utility.
So let's get started.


The first thing we need is a custom image view to display a pattern with cut lines.

@interface MyImageView : NSImageView { int vSize; int hSize; } @property int vSize; @property int hSize; @end * This source code was highlighted with Source Code Highlighter .
  1. @interface MyImageView : NSImageView { int vSize; int hSize; } @property int vSize; @property int hSize; @end * This source code was highlighted with Source Code Highlighter .
  2. @interface MyImageView : NSImageView { int vSize; int hSize; } @property int vSize; @property int hSize; @end * This source code was highlighted with Source Code Highlighter .
  3. @interface MyImageView : NSImageView { int vSize; int hSize; } @property int vSize; @property int hSize; @end * This source code was highlighted with Source Code Highlighter .
  4. @interface MyImageView : NSImageView { int vSize; int hSize; } @property int vSize; @property int hSize; @end * This source code was highlighted with Source Code Highlighter .
  5. @interface MyImageView : NSImageView { int vSize; int hSize; } @property int vSize; @property int hSize; @end * This source code was highlighted with Source Code Highlighter .
  6. @interface MyImageView : NSImageView { int vSize; int hSize; } @property int vSize; @property int hSize; @end * This source code was highlighted with Source Code Highlighter .
  7. @interface MyImageView : NSImageView { int vSize; int hSize; } @property int vSize; @property int hSize; @end * This source code was highlighted with Source Code Highlighter .
  8. @interface MyImageView : NSImageView { int vSize; int hSize; } @property int vSize; @property int hSize; @end * This source code was highlighted with Source Code Highlighter .
@interface MyImageView : NSImageView { int vSize; int hSize; } @property int vSize; @property int hSize; @end * This source code was highlighted with Source Code Highlighter .

')
Let us turn to the implementation of the methods of the class MyImageView. Override the method - (id) initWithCoder: (NSCoder *) coder to customize the creation of an instance of the class.

  1. if (self = [super initWithCoder: coder])
  2. {
  3. [self setEditable: YES];
  4. hSize = 1;
  5. vSize = 1;
  6. }
  7. return self;
* This source code was highlighted with Source Code Highlighter .


Next, override the - (void) drawRect: (NSRect) dirtyRect method to display cut lines.

  1. [super drawRect: dirtyRect];
  2. NSImage * img = [self image];
  3. CGContextRef context = (CGContextRef) [[NSGraphicsContext currentContext] graphicsPort];
  4. NSRect selfRect = [self bounds];
  5. if (! img)
  6. {
  7. static NSString * idleText = @ "Just drag image here!" ;
  8. NSFont * myFont = [NSFont fontWithName: @ "Arial" size: 16];
  9. NSMutableParagraphStyle * ps = [[NSParagraphStyle defaultParagraphStyle] mutableCopy];
  10. [ps setAlignment: NSCenterTextAlignment];
  11. NSDictionary * attsDict = [NSDictionary dictionaryWithObjectsAndKeys:
  12. [NSColor blackColor], NSForegroundColorAttributeName,
  13. myFont, NSFontAttributeName,
  14. nil];
  15. NSSize textSize = [idleText sizeWithAttributes: attsDict];
  16. [idleText drawAtPoint: NSMakePoint ((selfRect.size.width - textSize.width) / 2, (selfRect.size.height - textSize.height) / 2) withAttributes: attsDict];
  17. return ;
  18. }
  19. // calculate aspect ratios for correcting lines of slicing
  20. float aspectRatioImg = [img size] .width / [img size] .height;
  21. float aspectRatioView = selfRect.size.width / selfRect.size.height;
  22. NSRect scaledRect = selfRect;
  23. if (aspectRatioImg> aspectRatioView)
  24. {
  25. scaledRect.size.height = scaledRect.size.width / aspectRatioImg;
  26. scaledRect.origin.y = (selfRect.size.height - scaledRect.size.height) / 2;
  27. }
  28. else
  29. {
  30. scaledRect.size.width = scaledRect.size.height * aspectRatioImg;
  31. scaledRect.origin.x = (selfRect.size.width - scaledRect.size.width) / 2;
  32. }
  33. // set line color and width
  34. CGContextSetRGBStrokeColor (context, 1.f, 1.f, 0.f, 1.f);
  35. CGContextSetLineWidth (context, 1.f);
  36. // draw lines
  37. CGContextBeginPath (context);
  38. // vertical lines
  39. for ( int i = 1; i <hSize; i ++)
  40. {
  41. int x = scaledRect.origin.x + i * scaledRect.size.width / hSize;
  42. CGContextMoveToPoint (context, x, scaledRect.origin.y);
  43. CGContextAddLineToPoint (context, x, scaledRect.origin.y + scaledRect.size.height);
  44. }
  45. // horizontal lines
  46. for ( int j = 1; j <vSize; j ++)
  47. {
  48. int y = scaledRect.origin.y + j * scaledRect.size.height / vSize;
  49. CGContextMoveToPoint (context, scaledRect.origin.x, y);
  50. CGContextAddLineToPoint (context, scaledRect.origin.x + scaledRect.size.width, y);
  51. }
  52. CGContextClosePath (context);
  53. CGContextStrokePath (context);
* This source code was highlighted with Source Code Highlighter .


Do not forget to prescribe synthesize for all properties!

To quickly cut the big picture, we will make our applications multithreaded, especially since MacOS X provides quite a lot of tools for this. We will use the NSOperation class, which appeared in MacOS X, starting with version 10.5. NSOperation - multithreading is nowhere easier.

Create a descendant class for NSOperation and override the -main method. It is called when the operation starts. Class interface:

  1. @interface ImageSliceOperation: NSOperation
  2. {
  3. NSImage * image; // big image
  4. NSRect rect; // rect of slice
  5. NSURL * url; // URL to save slice
  6. }
  7. - (id) initWithImage: (NSImage *) anImage rect: (NSRect) aRect url: (NSURL *) anUrl;
  8. - ( void ) main;
  9. @end
* This source code was highlighted with Source Code Highlighter .


And the implementation class:
  1. @implementation ImageSliceOperation
  2. - (id) initWithImage: (NSImage *) anImage rect: (NSRect) aRect url: (NSURL *) anUrl
  3. {
  4. self = [super init];
  5. if (self)
  6. {
  7. image = [anImage retain];
  8. rect = aRect;
  9. url = [anUrl copy];
  10. }
  11. return self;
  12. }
  13. - ( void ) main
  14. {
  15. NSImage * target = [[NSImage alloc] initWithSize: NSMakeSize (rect.size.width, rect.size.height)];
  16. [target lockFocus];
  17. [image drawInRect: NSMakeRect (0,0, rect.size.width, rect.size.height)
  18. fromRect: rect
  19. operation: NSCompositeCopy
  20. fraction: 1.0];
  21. [target unlockFocus];
  22. NSData * imageData = [target TIFFRepresentation];
  23. NSBitmapImageRep * imageRep = [NSBitmapImageRep imageRepWithData: imageData];
  24. NSDictionary * imageProps = [NSDictionary dictionaryWithObject: [NSNumber numberWithFloat: 1.0] forKey: NSImageCompressionFactor];
  25. imageData = [imageRep representationUsingType: NSJPEGFileType properties: imageProps];
  26. // write the data to file
  27. [imageData writeToURL: url atomically: NO];
  28. [target release];
  29. }
  30. - ( void ) dealloc
  31. {
  32. [image release];
  33. [url release];
  34. [super dealloc];
  35. }
  36. @end
* This source code was highlighted with Source Code Highlighter .


Now it remains to create the MainView class.

  1. @interface MainView: NSView
  2. {
  3. IBOutlet MyImageView * imageView;
  4. IBOutlet NSTextField * hPartsLabel;
  5. IBOutlet NSTextField * vPartsLabel;
  6. IBOutlet NSButton * startButton;
  7. IBOutlet NSProgressIndicator * progressIndicator;
  8. NSOperationQueue * queue;
  9. }
  10. - (IBAction) startButtonPressed: (id) sender;
  11. // bind slider with label
  12. - (IBAction) hPartsSliderMoved: (id) sender;
  13. - (IBAction) vPartsSliderMoved: (id) sender;
  14. - ( void ) splitImage: (NSArray *) anArray;
  15. @end
* This source code was highlighted with Source Code Highlighter .


  1. @implementation MainView
  2. - (id) initWithCoder: (NSCoder *) coder
  3. {
  4. self = [super initWithCoder: coder];
  5. if (self)
  6. {
  7. queue = [[NSOperationQueue alloc] init];
  8. [queue setMaxConcurrentOperationCount: 4];
  9. }
  10. return self;
  11. }
  12. - (IBAction) startButtonPressed: (id) sender
  13. {
  14. if (! [imageView image])
  15. {
  16. NSAlert * alert = [[NSAlert alloc] init];
  17. [alert addButtonWithTitle: @ "OK" ];
  18. [alert setMessageText: @ "Error!" ];
  19. [alert setInformativeText: @ "You must open file previously." ];
  20. [alert setAlertStyle: NSCriticalAlertStyle];
  21. [alert runModal];
  22. [alert release];
  23. return ;
  24. }
  25. NSOpenPanel * saveDlg = [NSOpenPanel openPanel]; // select destination
  26. [saveDlg setCanCreateDirectories: YES];
  27. [saveDlg setCanChooseDirectories: YES];
  28. [saveDlg setCanChooseFiles: NO];
  29. int result = [saveDlg runModal];
  30. if (result == NSOKButton)
  31. {
  32. NSURL * dirURL = [saveDlg directoryURL];
  33. NSArray * argArray = [NSArray arrayWithObjects: dirURL,
  34. [imageView image],
  35. [NSNumber numberWithInt: [vPartsLabel intValue]],
  36. [NSNumber numberWithInt: [hPartsLabel intValue]],
  37. nil];
  38. [NSThread detachNewThreadSelector: @selector (splitImage :) toTarget: self withObject: argArray];
  39. }
  40. }
  41. - (IBAction) hPartsSliderMoved: (id) sender
  42. {
  43. int val = [sender intValue];
  44. [hPartsLabel setStringValue: [NSString stringWithFormat: @ "% i" , val]];
  45. imageView.hSize = val;
  46. [imageView setNeedsDisplay: YES];
  47. }
  48. - (IBAction) vPartsSliderMoved: (id) sender
  49. {
  50. int val = [sender intValue];
  51. [vPartsLabel setStringValue: [NSString stringWithFormat: @ "% i" , val]];
  52. imageView.vSize = val;
  53. [imageView setNeedsDisplay: YES];
  54. }
  55. - ( void ) splitImage: (NSArray *) anArray // 0 - destination URL; 1 - image; 2 - vParts; 3 - hParts
  56. {
  57. NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
  58. [startButton setEnabled: NO];
  59. [progressIndicator setUsesThreadedAnimation: YES];
  60. [progressIndicator startAnimation: self];
  61. NSURL * anUrl = [anArray objectAtIndex: 0];
  62. NSImage * anImage = [anArray objectAtIndex: 1];
  63. int vParts = [[anArray objectAtIndex: 2] intValue];
  64. int hParts = [[anArray objectAtIndex: 3] intValue];
  65. int imgW = [anImage size] .width;
  66. int imgH = [anImage size] .height;
  67. int partW = imgW / hParts;
  68. int partH = imgH / vParts;
  69. for ( int i = 0; i <hParts; i ++)
  70. {
  71. for ( int j = 0; j <vParts; j ++)
  72. {
  73. int currentX = partW * i;
  74. int currentY = imgH - partH * (j + 1);
  75. NSRect rect = NSMakeRect (currentX, currentY, partW, partH);
  76. NSString * fileName = [NSString stringWithFormat: @ "% i_% i.jpg" , i, j];
  77. NSURL * fileURL = [NSURL URLWithString: fileName relativeToURL: anUrl];
  78. ImageSliceOperation * op = [[ImageSliceOperation alloc] initWithImage: anImage rect: rect url: fileURL];
  79. [queue addOperation: op];
  80. [op release];
  81. }
  82. }
  83. [queue waitUntilAllOperationsAreFinished];
  84. [progressIndicator stopAnimation: self];
  85. [startButton setEnabled: YES];
  86. [pool release];
  87. }
  88. @end
* This source code was highlighted with Source Code Highlighter .


The splitImage: method is called in a separate thread to prevent the interface from hanging. The method creates an ImageSliceOperation and adds to the queue. This method also shows the progress indicator while the main action takes place.
The final step in writing code is to create an application delegate.

  1. @implementation iPictureSplitterAppDelegate
  2. - ( void ) applicationDidFinishLaunching: (NSNotification *) aNotification
  3. {
  4. // for custom init
  5. }
  6. - (IBAction) showAboutPanel: (id) sender
  7. {
  8. [aboutPanel orderFront: self];
  9. }
  10. @end
* This source code was highlighted with Source Code Highlighter .


The utility is almost ready. It remains only to create an Interface Builder document, create a window, place UI elements on it and link them to the outlet of our classes.

The result looks like this:
image

The source code of the project can be downloaded here .

Source: https://habr.com/ru/post/92282/


All Articles