Ignore icon DPI
Ignore icon DPI
I'm writing a program that uses Growl, and I ran into a problem where the icons I send in notifications were drawn being very small. Even though the image files were 128x128 pixels, the image in the notification was about 10x10. I eventually realized that the image files (PNGs) had very high dots per inch. Removing the DPI information solved my problem.
I assume that some Cocoa method is being too smart and is comparing the DPI information in the image to the DPI setting of the screen when asked to draw the image. Since growl knows the exact size in pixels that it wants to draw, this isn't the desired behavior. Maybe there's another method that will copy the pixels, ignoring the DPI, that can be used instead.
I assume that some Cocoa method is being too smart and is comparing the DPI information in the image to the DPI setting of the screen when asked to draw the image. Since growl knows the exact size in pixels that it wants to draw, this isn't the desired behavior. Maybe there's another method that will copy the pixels, ignoring the DPI, that can be used instead.
-
Wooden Brain Concepts
- Latté
- Posts: 50
- Joined: Wed Dec 27, 2006 2:37 am
Re: Ignore icon DPI
I'm having this problem at the moment with a JPEG and not sure how to handle it.
I am trying to cache online images to a local file, get the file path, and pass the path to Growl.
This is happening both with applescript, using standard "image from location" parameter, and using cocoa, passing as follows:
The file itself opens in Graphic Converter at 24% size and shows the resolution as 300 x 300 ppi. When shown in Graphic Converter at 100% size, it looks normal and is only 64 x 69 pixels.
This is what I expect Growl to show.
Here is the original image URL I am caching and passing to Growl:

I think this needs to be HANDLED by growl because this image is just an example. I want to be able to pass any image path to it.
I am trying to cache online images to a local file, get the file path, and pass the path to Growl.
This is happening both with applescript, using standard "image from location" parameter, and using cocoa, passing as follows:
Code: Select all
iconData:[[[[NSImage alloc] initWithContentsOfFile:aPath] autorelease] TIFFRepresentation]
This is what I expect Growl to show.
Here is the original image URL I am caching and passing to Growl:

I think this needs to be HANDLED by growl because this image is just an example. I want to be able to pass any image path to it.
Re: Ignore icon DPI
Yes it is. It's called “resolution independence”, and means that images will show at their real-world size. Growl will scale the image down, if necessary, but not up—it obeys the real-world size, and I consider that correct.rwbarton wrote:I assume that some Cocoa method is being too smart and is comparing the DPI information in the image to the DPI setting of the screen when asked to draw the image. Since growl knows the exact size in pixels that it wants to draw, this isn't the desired behavior.
Re: Ignore icon DPI
If it's really 64 by 69 *pixels*, then that would be 15 by 16 pixels—which, I'm pretty sure, is not the real size of the cover of “Weeds”.Wooden Brain Concepts wrote:The file itself opens in Graphic Converter at 24% size and shows the resolution as 300 x 300 ppi. When shown in Graphic Converter at 100% size, it looks normal and is only 64 x 69 pixels.
If possible, you should correct whatever code is generating the images. If you're scaling them, adjust or throw out the resolution. If you're getting them already-scaled from somewhere else, throw out the resolution using -[NSImage setSize:] (set it to the pixel size of the image's bitmap representation).
Growl's behavior (obeying the resolution) is correct. If the resolution information is wrong, then that's what you (or somebody upstream) need(s) to fix.I think this needs to be HANDLED by growl because this image is just an example.
-
Wooden Brain Concepts
- Latté
- Posts: 50
- Joined: Wed Dec 27, 2006 2:37 am
Re: Ignore icon DPI
Well this is driving me quite mad -- I've spent hours on this with no resolution -- so to speak.
I've tried several methods that I could find online (with slight modifications) which were similar to what PH suggested. The ones that didn't actually produce errors follow. However none of them seem to do a thing in terms of changing the size to normal.
I think the problem is there is only one "representation" and that is not a TIFF representation but whatever bastard DPI thingy it is.
Preview, Safari, Path Finder, Finder, etc, all show the image properly in what appears to me to be its "actual" 64 x 90 pixel size.
If Growl can't fix this, I do need a working method to handle any image. Any other ideas? Thanks!
I've tried several methods that I could find online (with slight modifications) which were similar to what PH suggested. The ones that didn't actually produce errors follow. However none of them seem to do a thing in terms of changing the size to normal.
I think the problem is there is only one "representation" and that is not a TIFF representation but whatever bastard DPI thingy it is.
Preview, Safari, Path Finder, Finder, etc, all show the image properly in what appears to me to be its "actual" 64 x 90 pixel size.
If Growl can't fix this, I do need a working method to handle any image. Any other ideas? Thanks!
Code: Select all
NSImage *myImage = [NSImage alloc];
myImage = [myImage initWithContentsOfFile:aPath];
// http://lists.apple.com/archives/cocoa-dev/2004/May/msg00201.html
NSBitmapImageRep *rep = [[myImage representations] objectAtIndex:0];
int renderedWidth = [rep pixelsWide];
int renderedHeight = [rep pixelsHigh];
[rep setSize:NSMakeSize(renderedWidth, renderedHeight)];
[myImage setSize:NSMakeSize(renderedWidth, renderedHeight)];
[myImage addRepresentation:rep];
[myImage removeRepresentation:[[myImage representations] objectAtIndex:0]];
/*/ #2
NSBitmapImageRep *rep = [[myImage representations] objectAtIndex: 0];
// If you think you might get something other than a bitmap image representation,
// check for it here.
NSSize size = NSMakeSize ([rep pixelsWide], [rep pixelsHigh]);
[myImage setSize: size];
/*/
/*
* http://code.google.com/p/cocoaslideshow/source/browse/tags/release-0.2/CocoaSlideShow.m
* Get the size of the original image in its raw bitmap format.
* The bestRepresentationForDevice: nil tells the NSImage to just
* give us the raw image instead of it's wacky DPI-translated version.
*/
/*/ #3
NSSize existingSize;
existingSize.width = [[myImage bestRepresentationForDevice: nil] pixelsWide];
existingSize.height = [[myImage bestRepresentationForDevice: nil] pixelsHigh];
NSSize newSize = NSMakeSize(existingSize.height, existingSize.width);
[myImage setSize: newSize];
/*/
Re: Ignore icon DPI
You hope that there is only one representation. If the input file is a TIFF or .icns file, there could be many. That's why using [[image representations] objectAtIndex:0U] is bad: who's to say that the first rep is the one you want?Wooden Brain Concepts wrote:I think the problem is there is only one "representation" …
-TIFFRepresentation is a factory, not a property. It creates a TIFF representation for any image. No image carries a TIFF representation around with it everywhere it goes.and that is not a TIFF representation but whatever bastard DPI thingy it is.
Preview uses the DPI if a checkbox is checked. I don't remember whether it's checked by default.Preview, Safari, Path Finder, Finder, etc, all show the image properly in what appears to me to be its "actual" 64 x 90 pixel size.
This seems correct, although you shouldn't exchange the height and width as this NSMakeSize call does (its arguments are width, height).Code: Select all
NSSize existingSize; existingSize.width = [[myImage bestRepresentationForDevice: nil] pixelsWide]; existingSize.height = [[myImage bestRepresentationForDevice: nil] pixelsHigh]; NSSize newSize = NSMakeSize(existingSize.height, existingSize.width); [myImage setSize: newSize];
Make sure you actually send the modified image (myImage) to Growl, and not the original image.
-
Wooden Brain Concepts
- Latté
- Posts: 50
- Joined: Wed Dec 27, 2006 2:37 am
Re: Ignore icon DPI
Unfortunately it doesn't work either. I switched the witdth and height around as you say. I think I am sending the modified image to Growl. When I invoke [myImage setSize: new size] i am modifiying "myImage", right?
I think the problem is that "bestRepresentationForDevice" like "[myImage representations] objectAtIndex:0]" do exactly the same thing -- there is only one representation and it is that messed up DPI one.
See code below.
edit:
I added NS Log for existing width & height and this is what it outputs:
Jun 19 14:32:00 G5-Dual WBC ASS Growler[7955]: Width: 64.00
Jun 19 14:32:00 G5-Dual WBC ASS Growler[7955]: height: 90.00
I think the problem is that "bestRepresentationForDevice" like "[myImage representations] objectAtIndex:0]" do exactly the same thing -- there is only one representation and it is that messed up DPI one.
See code below.
Code: Select all
NSImage *myImage = [NSImage alloc];
myImage = [myImage initWithContentsOfFile:aPath];
NSSize existingSize;
existingSize.width = [[myImage bestRepresentationForDevice: nil] pixelsWide];
existingSize.height = [[myImage bestRepresentationForDevice: nil] pixelsHigh];
NSSize newSize = NSMakeSize(existingSize.width, existingSize.height);
[myImage setSize: newSize];
[GrowlApplicationBridge notifyWithTitle:atitle
description:amsg
notificationName:anotif
iconData:[myImage TIFFRepresentation]
priority:aPrior
isSticky:aStick
clickContext:aClick];
[myImage release];
I added NS Log for existing width & height and this is what it outputs:
Jun 19 14:32:00 G5-Dual WBC ASS Growler[7955]: Width: 64.00
Jun 19 14:32:00 G5-Dual WBC ASS Growler[7955]: height: 90.00
Last edited by Wooden Brain Concepts on Thu Jun 19, 2008 6:32 pm, edited 1 time in total.
Re: Ignore icon DPI
Right.Wooden Brain Concepts wrote:When I invoke [myImage setSize: new size] i am modifiying "myImage", right?
That is not the problem. That is not even a problem.I think the problem is that "bestRepresentationForDevice" like "[myImage representations] objectAtIndex:0]" do exactly the same thing …
Why make it pick the best representation twice? Instead, get it once and use it twice.Code: Select all
existingSize.width = [[myImage bestRepresentationForDevice: nil] pixelsWide]; existingSize.height = [[myImage bestRepresentationForDevice: nil] pixelsHigh];
Or just:Code: Select all
NSSize newSize = NSMakeSize(existingSize.width, existingSize.height);
Code: Select all
NSSize newSize = existingSize;Code: Select all
NSSize newSize = { [myRepresentation pixelsWide], [myRepresentation pixelsHigh] };AFAIK, this should work. Could you save the TIFF to a file and send it to me?Code: Select all
[myImage setSize: newSize]; [GrowlApplicationBridge notifyWithTitle:atitle description:amsg notificationName:anotif iconData:[myImage TIFFRepresentation] priority:aPrior isSticky:aStick clickContext:aClick]; [myImage release];
The code to do that is:
Code: Select all
NSString *TIFFPath = [NSTemporaryDirectory() stringByAppendingPathComponent:@"SavedGrowlIcon.tiff"];
[[myImage TIFFRepresentation] writeToFile:TIFFPath atomically:NO];
[[NSWorkspace sharedWorkspace] selectFile:TIFFPath inFileViewerRootedAtPath:@""];-
Wooden Brain Concepts
- Latté
- Posts: 50
- Joined: Wed Dec 27, 2006 2:37 am
Re: Ignore icon DPI
well... i added the savedgrowlicon code and it saved... i got a 17Kb tiff file as opposed to the 2 kb original. so clearly it did something. however Growl still displays it as TINY. opening that TIFF in Graphic Converter, as before, opens it at 24% with 300 ppi.
original as downloaded / cached:

SavedGrowlIcon.tiff

Preview of Path Finder display vs Growl notificaiton:

I'll PM you with a link to the source.
original as downloaded / cached:

SavedGrowlIcon.tiff
Preview of Path Finder display vs Growl notificaiton:
I'll PM you with a link to the source.
Re: Ignore icon DPI
JPEG is compressed. TIFF usually isn't. Compressed files are smaller than uncompressed files.Wooden Brain Concepts wrote:i got a 17Kb tiff file as opposed to the 2 kb original.
Re: Ignore icon DPI
#4 is, in fact, the correct solution (and I'm sorry that I forgot about the +representationOfImageRepsInArray:usingType:properties: method again). It didn't work because you implemented it incorrectly:Wooden Brain Concepts wrote:I'll PM you with a link to the source.
- That method returns NSData, not NSBitmapImageRep. (Just like TIFFRepresentation.) Remember, an NSBitmapImageRep is just a raster image—it's not in any specific file format. So, if a method promises to give you a representation in some file format, then it's going to give you NSData. Such is the case here.
- NSDecimalNumber is only for very specific circumstances (e.g., banking). This is not one of them. You can just use plain old NSNumber.
You don't have to use NSJPEGFileType, of course. You could also use NSPNGFileType, which is lossless.
One other thing: Please don't make your notifications sticky by default. ☹
-
Wooden Brain Concepts
- Latté
- Posts: 50
- Joined: Wed Dec 27, 2006 2:37 am
Re: Ignore icon DPI
Well, sorry to say I still have no luck here. As you know, i'm only just learning Obj-C, and specifically for the purpose of creating an AS Studio bridge to Growl clickable notifications (and a couple other methods limited in scope).boredzo wrote:#4 is, in fact, the correct solution (and I'm sorry that I forgot about the +representationOfImageRepsInArray:usingType:properties: method again). It didn't work because you implemented it incorrectly.
I tried this, thinking that since "rep" is already NS Data I don't have to pass myImage to GAB bur rather rep:
Code: Select all
NSImage *myImage = [NSImage alloc];
myImage = [myImage initWithContentsOfFile:aPath];
NSData *rep = [NSData representationOfImageRepsInArray:[myImage representations] usingType:NSJPEGFileType properties:[NSDictionary dictionaryWithObject:[NSNumber numberWithFloat:1.0] forKey:NSImageCompressionFactor]];
[GrowlApplicationBridge notifyWithTitle:atitle
description:amsg
notificationName:anotif
iconData:rep
priority:aPrior
isSticky:aStick
clickContext:aClick];
[myImage release];
Code: Select all
NSImage *myImage = [NSImage alloc];
myImage = [myImage initWithContentsOfFile:aPath];
NSData *rep = [NSData representationOfImageRepsInArray:[myImage representations] usingType:NSJPEGFileType properties:[NSDictionary dictionaryWithObject:[NSNumber numberWithFloat:1.0] forKey:NSImageCompressionFactor]];
[myImage addRepresentation:rep];
[myImage removeRepresentation:[[myImage representations] objectAtIndex:0]];
[GrowlApplicationBridge notifyWithTitle:atitle
description:amsg
notificationName:anotif
iconData:[myImage TIFFRepresentation]
priority:aPrior
isSticky:aStick
clickContext:aClick];
[myImage release];
The source I sent you is a demonstration of a method. I think more of these are needed so people can actually learn from working examples. It's not meant to be distributed other than as such, so it hardly matters what the default settings are -- they are set only to best look at the results.One other thing: Please don't make your notifications sticky by default. ☹
Re: Ignore icon DPI
Wooden Brain Concepts wrote:The source I sent you is a demonstration of a method. I think more of these are needed so people can actually learn from working examples. It's not meant to be distributed other than as such, so it hardly matters what the default settings are -- they are set only to best look at the results.boredzo wrote:One other thing: Please don't make your notifications sticky by default. ☹
Since it's an example, I'd just be hesitant to have sticky by default in there, since that can be really annoying behavior for people learning to emulate.
I'm glad someone is working on what you guys are working on.
Re: Ignore icon DPI
That's correct. In this case, the framework accepts an NSImage if you pass one, but the method is typed as expecting an NSData.Wooden Brain Concepts wrote:I tried this, thinking that since "rep" is already NS Data I don't have to pass myImage to GAB bur rather rep:
Code: Select all
NSImage *myImage = [NSImage alloc];
myImage = [myImage initWithContentsOfFile:aPath];
NSData *rep = [NSData representationOfImageRepsInArray:[myImage representations] usingType:NSJPEGFileType properties:[NSDictionary dictionaryWithObject:[NSNumber numberWithFloat:1.0] forKey:NSImageCompressionFactor]];
[GrowlApplicationBridge notifyWithTitle:atitle
description:amsg
notificationName:anotif
iconData:rep
priority:aPrior
isSticky:aStick
clickContext:aClick];
[myImage release];
Again, look at methods' type information before you go blindly passing objects to them. addRepresentation: expects an NSImageRep. rep is an NSData. An NSData is not an NSImageRep.and then I tried this, thinking that I would add the representation to the image and then remove the original representation (this is closer to the code I received).
Code: Select all
NSData *rep = [NSData representationOfImageRepsInArray:[myImage representations] usingType:NSJPEGFileType properties:…]; [myImage addRepresentation:rep];
I left it on my RAM disk—it's gone now. You're on the right track with passing rep, though.If you can post the working code it would be much appreciated.
-
Wooden Brain Concepts
- Latté
- Posts: 50
- Joined: Wed Dec 27, 2006 2:37 am
Re: Ignore icon DPI
Sorry, it's not.Looks right to me.
If I use this:
Code: Select all
NSData *rep = [NSData representationOfImageRepsInArray:When I substitute that with this:
Code: Select all
NSData *rep = [NSBitmapImageRep representationOfImageRepsInArray: Exactly like before, using precisely that first code:

This is the resulting "rep" saved to disk with the code you provided earlier (which is jus the NSDATA written to disk with a .tiff extension not, presumably, an actual .tiff):
Re: Ignore icon DPI
Er, right. Sorry. Didn't notice the class name.Wooden Brain Concepts wrote:If I use this:Then I get a build warning about NSData may not respond... It compiles and when run I get an unrecognized selector error in the log and an event failed message.Code: Select all
NSData *rep = [NSData representationOfImageRepsInArray:
It does.When I substitute that with this:It compiles and runs fine. However it doesn't produce the expected results.Code: Select all
NSData *rep = [NSBitmapImageRep representationOfImageRepsInArray:
The code I provided earlier still asks for the TIFFRepresentation of the NSImage, which hasn't changed. Notice that the file is still a TIFF file, while you're using NSJPEGFileType.This is the resulting "rep" saved to disk with the code you provided earlier (which is jus the NSDATA written to disk with a .tiff extension not, presumably, an actual .tiff):
You need to change it to send the writeData: message to rep, and change the filename extension from .tiff to .jpg.
-
Wooden Brain Concepts
- Latté
- Posts: 50
- Joined: Wed Dec 27, 2006 2:37 am
Re: Ignore icon DPI
I very much appreciate your responsiveness and patience, please don't get me wrong. But you're not seeing the forest for the trees here.
Please don't complain that I need to change the variable TIFFPath to JPEGPath or something!
Here's another screenshot of the code as executed:

And here again is the resulting file saved:

(edit: FYI, I have tried to use usingType:NSJPEGFileType, usingType:NSPNGFileType, usingType:NSTIFFFileType all with identical results)
You are complaining about how the data in RAM is saved to disk. But that's just included for demonstration purposes. What is passed to Growl has nothing to do with what's saved. Furthermore, I can't see how the actual file extension matters. Still, I changed the code to this (which actually just changed the file extension because it was ALREADY sending the writeToFile msg to "rep" and not "myImage"):boredzo wrote: ...Notice that the file is still a TIFF file, while you're using NSJPEGFileType. You need to change it to send the writeData: message to rep, and change the filename extension from .tiff to .jpg.
Code: Select all
NSImage *myImage = [NSImage alloc];
myImage = [myImage initWithContentsOfFile:aPath];
NSData *rep = [NSBitmapImageRep representationOfImageRepsInArray: [myImage representations] usingType:NSJPEGFileType properties:[NSDictionary dictionaryWithObject:[NSNumber numberWithFloat:1.0] forKey:NSImageCompressionFactor]];
[GrowlApplicationBridge notifyWithTitle:atitle
description:amsg
notificationName:anotif
iconData:rep
priority:aPrior
isSticky:aStick
clickContext:aClick];
NSString *TIFFPath = [NSTemporaryDirectory() stringByAppendingPathComponent:@"SavedGrowlIcon2.jpg"];
[rep writeToFile:TIFFPath atomically:NO];
[[NSWorkspace sharedWorkspace] selectFile:TIFFPath inFileViewerRootedAtPath:@""];
Here's another screenshot of the code as executed:

And here again is the resulting file saved:
No this confuses the heck out of me. You said that I was "on the right track" by passing "rep" (which is now NSData) rather than some modified version of NSImage. Please she some light on that! Thanks again.The code I provided earlier still asks for the TIFFRepresentation of the NSImage, which hasn't changed.
(edit: FYI, I have tried to use usingType:NSJPEGFileType, usingType:NSPNGFileType, usingType:NSTIFFFileType all with identical results)
Re: Ignore icon DPI
That too.Wooden Brain Concepts wrote:Please don't complain that I need to change the variable TIFFPath to JPEGPath or something!
No this confuses the heck out of me. You said that I was "on the right track" by passing "rep" (which is now NSData) rather than some modified version of NSImage.[/quote]The code I provided earlier still asks for the TIFFRepresentation of the NSImage, which hasn't changed.
That's correct. The TIFFRepresentation of the NSImage is the wrong thing to pass. I'm not sure why setting the size of the image doesn't work, but you're correct: it doesn't. So you need to set the size of the rep, and get your data from that.
In summary, here's what you need to do:
- Change the size of the rep to its pixel size using the rep's own setSize: method.
- Get the PNG or JPEG or TIFF representation (data) of the rep whose size you changed. Get this by passing an array of that one rep (arrayWithObject:).
- Pass the PNG or JPEG or TIFF data to GAB.
- Attachments
-
- Result of stripping resolution (DPI) information from an image used as the icon of a Growl notification.
- ResolutionAgnostic-proof.png (21.48 KiB) Viewed 11983 times
-
Wooden Brain Concepts
- Latté
- Posts: 50
- Joined: Wed Dec 27, 2006 2:37 am
Re: Ignore icon DPI
Holy Chr*st...
I got it. (PS: I got it without seeing the following message, too! ;0)
I got it. (PS: I got it without seeing the following message, too! ;0)
Code: Select all
NSImage *myImage = [NSImage alloc];
myImage = [myImage initWithContentsOfFile:aPath];
NSImageRep *rep = [myImage bestRepresentationForDevice: nil];
NSSize newSize = { [rep pixelsWide], [rep pixelsHigh] };
[rep setSize: newSize];
NSData *thedata = [NSBitmapImageRep representationOfImageRepsInArray: [NSArray arrayWithObject:rep] usingType:NSPNGFileType properties:[NSDictionary dictionaryWithObject:[NSNumber numberWithFloat:1.0] forKey:NSImageCompressionFactor]];
[GrowlApplicationBridge notifyWithTitle:atitle
description:amsg
notificationName:anotif
iconData:thedata
priority:aPrior
isSticky:aStick
clickContext:aClick];
[myImage release];
Last edited by Wooden Brain Concepts on Fri Jun 20, 2008 9:10 pm, edited 2 times in total.
Re: Ignore icon DPI
Step 2 is the creation of the NSData using representationOfImageRepsInArray:. “rep” exclusively means NSImageReps (in this case, the one from bestRepresentationForDevice:). For the array, pass an array containing only rep ([NSArray arrayWithObject:rep]).Wooden Brain Concepts wrote:Which rep? the one created by representationOfImageRepsInArray is NSData.
This is still method #4.Are you suggesting going back to an earlier method I was trying?
I don't just claim to have the proof; I showed it. Nonetheless, I'd prefer that you learn the technique rather than simply put in the code and be done. This way, if you ever have to fix it, you'll know how it works and therefore be closer to knowing how to fix it.It would be much easier if you posted some working code since you claim to have the proof.