MacOS: Programmatically add some text to an image? - image

I'm converting some code from linux to mac.
How can I programatically overlay an image with text, similar to the ImageMagick convert command? For various reasons I can't depend on having ImageMagick installed.
convert -draw 'text 50,800 "hello world"' f1.png f2.png

You can use the NSImage and NSString drawing APIs to create the edited image, and then use NSBitmapImageRep to get PNG data for the image. Example:
NSString *text = #"hello world";
NSImage *f1 = [[NSImage alloc] initWithContentsOfFile:#"/path/to/f1.png"];
NSImage *f2 = [NSImage imageWithSize:f1.size flipped:YES drawingHandler:^BOOL(NSRect dstRect) {
[f1 drawInRect:dstRect];
// Attributes can be customized to change font, text color, etc.
NSDictionary *attributes = #{NSFontAttributeName: [NSFont systemFontOfSize:17]};
[text drawAtPoint:NSMakePoint(50, 800) withAttributes:attributes];
return YES;
}];
NSBitmapImageRep *rep = [[NSBitmapImageRep alloc] initWithCGImage:[f2 CGImageForProposedRect:NULL context:nil hints:nil]];
NSData *data = [rep representationUsingType:NSPNGFileType properties:nil];
[data writeToFile:#"/path/to/f2.png" atomically:YES];

Related

How to draw EPS data on NSView

I'm struggling with the problem to draw an eps file on a NSView.
When I first load the eps file from a file and draw it with drawInRect: the image is displayed correctly. However, the image will not be drawn when I load it from an archive file.
I've prepared a dirty small example that you can copy/paste and try out. Create a new Cocoa App project and add this to the delegate method.
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
// Just a sample eps file
NSURL *url = [NSURL URLWithString: #"http://embedded.eecs.berkeley.edu/concurrency/latex/figure.eps"];
NSImage *epsImage = [[[NSImage alloc] initWithContentsOfURL: url] autorelease];
// Encode data
NSMutableData *mutableData = [[[NSMutableData alloc] init] autorelease];
NSKeyedArchiver *coder = [[[NSKeyedArchiver alloc] initForWritingWithMutableData: mutableData] autorelease];
[coder encodeObject: epsImage forKey: #"image"];
[coder finishEncoding];
NSString *dataFile = [#"~/Desktop/image.data" stringByExpandingTildeInPath];
[mutableData writeToFile: dataFile atomically: YES];
// Decode data
NSData *data = [NSData dataWithContentsOfFile: dataFile];
NSKeyedUnarchiver *decoder = [[NSKeyedUnarchiver alloc] initForReadingWithData: data];
NSImage *loadedImage = [decoder decodeObjectForKey: #"image"];
// Draw image
NSRect rect;
rect.origin = NSZeroPoint;
rect.size = loadedImage.size;
NSView *view = [[NSApp mainWindow] contentView];
[view lockFocus];
[loadedImage drawInRect: rect fromRect: rect operation: NSCompositeSourceOver fraction: 1.0];
[view unlockFocus];
}
To prove that the first loaded image draws correctly just change the line [loadedImage drawInRect:...] to [epsImage drawInRect:...].
I'm using NSKeyedArchiver and NSKeyedUnarchiver here for simulating encodeWithCoder: and initWithCoder:. So please focus on the fact that NSImage with NSEPSImageRep representation, which does not contain a preview (from a resource fork?) and loaded purely as eps commands, is not drawn on a NSView correctly.
Any help is appreciated.
Due to the way that cacheing works on NSImage, I've often found it more effective to actually grab the NSImageRep if I know what the type is.
In our code, we found that the most reliable way to save off images is in their original format, but that requires either saving off the data in its original format somewhere else, or requesting the data from the NSImageRep. Unfortunately, there's not a generic -(NSData*)data method of NSImageRep, so we ended up specifically checking for various types of NSImageRep and saving them off depending on what we knew them to be.
Fortunately, loading is simple, as NSImage::initWithData: will figure out the type based on the data.
Here's our long-standing code for doing this. Basically, it prefers PDF then EPS then it makes a TIFF of anything it doesn't understand.
+ (NSData*) dataWithImage:(NSImage*)image kindString:( NSString**)kindStringPtr
{
if (!image)
return nil;
NSData *pdfData=nil, *newData=nil, *epsData=nil, *imageData=nil;;
NSString *kindString=nil;
NSArray *reps = [image representations];
for (NSImageRep *rep in reps) {
if ([rep isKindOfClass: [NSPDFImageRep class]]) {
// we have a PDF, so save that
pdfData = [(NSPDFImageRep*)rep PDFRepresentation];
PDFDocument *doc = [[PDFDocument alloc] initWithData:pdfData];
newData = [doc dataRepresentation];
if (newData && ([newData length]<[pdfData length])) {
pdfData = newData;
}
break;
}
if ([rep isKindOfClass: [NSEPSImageRep class]]) {
epsData = [(NSEPSImageRep*)rep EPSRepresentation];
break;
}
}
if (pdfData) {
imageData=pdfData;
kindString= #"pdfImage";
} else if (epsData) {
imageData=epsData;
kindString=#"epsImage";
} else {
// make a big copy
NSBitmapImageRep *rep0 = [reps objectAtIndex:0];
if ([rep0 isKindOfClass: [NSBitmapImageRep class]]) {
[image setSize: NSMakeSize( [rep0 pixelsWide], [rep0 pixelsHigh])];
}
imageData = [image TIFFRepresentation];
kindString=#"tiffImage";
}
if (kindStringPtr)
*kindStringPtr=kindString;
return imageData;
}
Once we have the NSData*, it can be saved in a keyed archive, written to disk or whatever.
On the way back in, load in the NSData* and then
NSImage *image = [[NSImage alloc] initWithData: savedData];
and you should be all set.

cocoa nsimage does not get saved

I'm new to cocoa and programming, sorry if this is something basic.
I'm using ARC. I have an NSImageView component, which is controlled by DropZone class. I drag & drop an image into it, but once I try to call in the scalling method I got, it tells me that the ": ImageIO: CGImageSourceCreateWithData data parameter is nil" I assume I'm doing something wrong, just don't know what yet.
Here's my method in DropZone.m
- (void)scaleIcons:(NSString *)outputPath{
NSImage *anImage;
NSSize imageSize;
NSString *finalPath;
anImage = [[NSImage alloc]init];
anImage = image;
imageSize = [anImage size];
imageSize.width = 512;
imageSize.height = 512;
[anImage setSize:imageSize];
finalPath = [outputPath stringByAppendingString:#"/icon_512x512.png"];
NSData *imageData = [anImage TIFFRepresentation];
NSBitmapImageRep *rep = [NSBitmapImageRep imageRepWithData:imageData];
NSData *dataToWrite = [rep representationUsingType:NSPNGFileType properties:nil];
[dataToWrite writeToFile:finalPath atomically:NO];
}
The DropZone.h
#interface DropZone : NSImageView <NSDraggingDestination>{
NSImage *image;
}
- (void)scaleIcons:(NSString *)outputPath;
#end
And here's how I call it it my AppDelegate.m
- (IBAction)createIconButtonClicked:(id)sender {
NSFileManager *filemgr;
NSString *tempDir;
filemgr = [NSFileManager defaultManager];
tempDir = [pathString stringByAppendingString:#"/icon.iconset"];
NSURL *newDir = [NSURL fileURLWithPath:tempDir];
[filemgr createDirectoryAtURL: newDir withIntermediateDirectories:YES attributes: nil error:nil];
DropZone *myZone = [[DropZone alloc] init];
[myZone scaleIcons:tempDir];
NSAlert *alert = [[NSAlert alloc] init];
[alert setMessageText:#"Done!"];
[alert runModal];
}
the image get's pulled from the pastebaord:
- (BOOL)performDragOperation:(id<NSDraggingInfo>)sender{
if ([NSImage canInitWithPasteboard:[sender draggingPasteboard]]) {
image = [[NSImage alloc]initWithPasteboard:[sender draggingPasteboard]];
[self setImage:image];
[self setImageAlignment: NSImageAlignCenter];
}
return YES;
}
For some reason my "image" gets lost. Could somebody help?
The problem is that you're creating a new instance of DropZone in your app delegate, but I'm assuming that you created the image view in IB and changed its class to DropZone. Is that correct? If so, you need to have an IBOutlet in the app delegate connected to the image view in IB, and have this:
[self.iv scaleIcons:tempDir];
where iv is my IBOutlet.
Without knowing where "image" comes from, I would say your problem is in these lines...
anImage = [[NSImage alloc]init];
anImage = image;
You should just grab the image from the imageView...
anImage = [myImageView image];

Xcode save and load image in Xcode iOS

Check my app if you don't really understand ( Quick Notes!) But here it goes. My app is a notes app so it allows the user to select from few different kinds of note colors and designs below. When the user selects one, it changes the note above to what ever they set it to. So i need a button that will save the picture they selected, and when the leave the view and come back they can click the load button and the same image they selected will appear. I am using Xcode 4.3. Thanks so much!
Here is my save code:
-(IBAction)save123456 {
NSBitmapImageRep *rep;
NSData *data;
NSImage *noteview;
[self lockFocus];
rep = [[NSBitmapImageRep alloc] initWithFocusedViewRect:[self frame]];
[self unlockFocus];
image = [[[NSImage alloc] initWithSize:[rep size]] autorelease];
[image addRepresentation:rep];
data = [rep representationUsingType: NSPNGFileType properties: nil];
//save as png but failed
[data writeToFile: #"asd.png" atomically: NO];
//save as pdf, succeeded but with flaw
data = [self dataWithPDFInsideRect:[self frame]];
[data writeToFile:#"asd.pdf" atomically:YES];
}
And my load:
-(IBAction)load123456 {
NSImage loadedImage = [[NSImage alloc] initWithContentsOfFile: NSString* filePath]
}
I get so many error codes in my save. My image is called noteview. Thanks!!
Try use instead
[data writeToFile: #"asd.png" atomically: NO];
a code line
[data writeToFile: #"asd.png" atomically: YES];
Some open source examples:
AGSimpleImageEditorView
image editors in iOS
projects with work with Pictures

NSString *text to NSString *icon?

I am making an app that is a standalone menu item and the basis for the code is sample code I found on a website. The sample code uses a number as the menu icon, but I want to change it to an image.
I want it to be like other apps where it shows icon.png when not clicked and icon-active.png when clicked.
The current code is this:
- (void)drawRect:(NSRect)rect {
// Draw background if appropriate.
if (clicked) {
[[NSColor selectedMenuItemColor] set];
NSRectFill(rect);
}
// Draw some text, just to show how it's done.
NSString *text = #"3"; // whatever you want
NSColor *textColor = [NSColor controlTextColor];
if (clicked) {
textColor = [NSColor selectedMenuItemTextColor];
}
NSFont *msgFont = [NSFont menuBarFontOfSize:15.0];
NSMutableParagraphStyle *paraStyle = [[NSMutableParagraphStyle alloc] init];
[paraStyle setParagraphStyle:[NSParagraphStyle defaultParagraphStyle]];
[paraStyle setAlignment:NSCenterTextAlignment];
[paraStyle setLineBreakMode:NSLineBreakByTruncatingTail];
NSMutableDictionary *msgAttrs = [NSMutableDictionary dictionaryWithObjectsAndKeys:
msgFont, NSFontAttributeName,
textColor, NSForegroundColorAttributeName,
paraStyle, NSParagraphStyleAttributeName,
nil];
[paraStyle release];
NSSize msgSize = [text sizeWithAttributes:msgAttrs];
NSRect msgRect = NSMakeRect(0, 0, msgSize.width, msgSize.height);
msgRect.origin.x = ([self frame].size.width - msgSize.width) / 2.0;
msgRect.origin.y = ([self frame].size.height - msgSize.height) / 2.0;
[text drawInRect:msgRect withAttributes:msgAttrs];
}
Also, I found a post describing a method on how to do this, but it did not work for me. The url to that is this: http://mattgemmell.com/2008/03/04/using-maattachedwindow-with-an-nsstatusitem/comment-page-1#comment-46501.
Thanks!
Use an NSImage and draw it where desired. For example:
NSString *name = clicked? #"icon-active" : #"icon";
NSImage *image = [NSImage imageNamed:name];
NSPoint p = [self bounds].origin;
[image drawAtPoint:p fromRect:NSZeroRect
operation:NSCompositeSourceOver fraction:1.0];
If this is for a status item and you just want an icon with no programmatic drawing, drop the view and set the status item's image and alternateImage. The former is what the status item uses normally; the status item switches to the alternate image (if it has one) when the user opens its menu.

Strange artifacts after CILanczosScaleTransform

I'm trying to do image scaling using Core Image, using Lanczos Scale Transform filter.
It is fine when I'm doing scaleup. But on scaledown and saving to JPEG I found a class of images which produces strange noise artifacts and behavior.
For inputScale multiples to 0.5: 0.5, 0.25, 0.125 etc it is always fine.
For others inputScale values,it's broken.
When I'm saving to TIFF,PNG,JPEG2000 or draw on screen - it's fine.
When I'm saving to JPG or BMP - it's broken.
I uploaded sample images:
(original, 4272x2848, 3.4Mb)
[http://www.zoomfoot.com/get/package/517e2423-7795-4239-a166-03d507ec51d8]
(scaled with noise, 1920x1280, 2.2Mb)
[http://www.zoomfoot.com/get/package/6eb64d33-3a30-4e8d-9953-67ce1e7d7ef9]
It also, pretty good reproducible on 'sunset' images.
I've tried couple more ways to scale images. Using CIAffineTransform and drawing using NSImage itself. Both provides results without any noise.
Here is the code I was using for scaling & saving:
================= Lanczos ==============
- (NSImage *) scaleLanczosImage:(NSString *)path
Width:(int) desiredWidth
Height:(int) desiredHeight
{
CIImage *image=[CIImage imageWithContentsOfURL:
[NSURL fileURLWithPath:path]];
CIFilter *scaleFilter = [CIFilter filterWithName:#"CILanczosScaleTransform"];
int originalHeight=[image extent].size.height;
int originalWidth=[image extent].size.width;
float yScale=(float)desiredHeight / (float)originalHeight;
float xScale=(float)desiredWidth / (float)originalWidth;
float scale=fminf(yScale, xScale);
[scaleFilter setValue:[NSNumber numberWithFloat:scale]
forKey:#"inputScale"];
[scaleFilter setValue:[NSNumber numberWithFloat:1.0]
forKey:#"inputAspectRatio"];
[scaleFilter setValue: image
forKey:#"inputImage"];
image = [scaleFilter valueForKey:#"outputImage"];
NSBitmapImageRep* rep = [[NSBitmapImageRep alloc] initWithCIImage:image];
NSMutableDictionary *options=[NSMutableDictionary dictionaryWithObject:[NSDecimalNumber numberWithFloat:1.0]
forKey:NSImageCompressionFactor];
[options setValue:[NSNumber numberWithBool:YES] forKey:NSImageProgressive];
NSData* jpegData = [rep representationUsingType:NSJPEGFileType
properties:options];
[jpegData writeToFile:#"/Users/dmitry/tmp/img_4389-800x600.jpg" atomically:YES];
}
================= NSImage ==============
- (void) scaleNSImage:(NSString *)path
Width:(int) desiredWidth
Height:(int) desiredHeight
{
NSImage *sourceImage = [[NSImage alloc] initWithContentsOfFile:path];
NSImage *resizedImage = [[NSImage alloc] initWithSize:
NSMakeSize(desiredWidth, desiredHeight)];
NSSize originalSize = [sourceImage size];
[resizedImage lockFocus];
[sourceImage drawInRect: NSMakeRect(0, 0, desiredWidth, desiredHeight)
fromRect: NSMakeRect(0, 0, originalSize.width, originalSize.height)
operation: NSCompositeSourceOver fraction: 1.0];
[resizedImage unlockFocus];
NSMutableDictionary *options=[NSMutableDictionary dictionaryWithObject:[NSDecimalNumber numberWithFloat:1.0]
forKey:NSImageCompressionFactor];
[options setValue:[NSNumber numberWithBool:YES] forKey:NSImageProgressive];
NSBitmapImageRep *rep = [[NSBitmapImageRep alloc]
initWithData:[resizedImage TIFFRepresentation]];
NSData* jpegData = [rep representationUsingType:NSJPEGFileType
properties:options];
[jpegData writeToFile:#"~/nsimg_4389-800x600.jpg" atomically:YES];
}
If anyone can explain or suggest something here, i'm really appreciate this.
Thanks.
There are some quirks with scaling with CIImage. Will this post by Dan Wood help?
My understanding is that due to its nature, the hardware Core Image renderer has some odd quirks when rendering for anything but on-screen display. So, you are recommended to use the software renderer for other situations.
http://developer.apple.com/mac/library/qa/qa2005/qa1416.html
That's definitely a CoreImage bug. File it at bugreport.apple.com

Resources