Page 1 of 2

Ignore icon DPI

Posted: Sat Dec 22, 2007 5:46 am
by rwbarton
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.

Re: Ignore icon DPI

Posted: Tue Jun 17, 2008 5:35 pm
by Wooden Brain Concepts
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:

Code: Select all

iconData:[[[[NSImage alloc] initWithContentsOfFile:aPath] autorelease] TIFFRepresentation]
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:

Image

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

Posted: Tue Jun 17, 2008 5:47 pm
by boredzo
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.
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.

Re: Ignore icon DPI

Posted: Tue Jun 17, 2008 5:52 pm
by boredzo
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 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”.

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).
I think this needs to be HANDLED by growl because this image is just an example.
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.

Re: Ignore icon DPI

Posted: Wed Jun 18, 2008 3:54 am
by Wooden Brain Concepts
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!

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

Posted: Wed Jun 18, 2008 6:46 pm
by boredzo
Wooden Brain Concepts wrote:I think the problem is there is only one "representation" …
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?
and that is not a TIFF representation but whatever bastard DPI thingy it is.
-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.
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.
Preview uses the DPI if a checkbox is checked. I don't remember whether it's checked by default.

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];
This seems correct, although you shouldn't exchange the height and width as this NSMakeSize call does (its arguments are width, height).

Make sure you actually send the modified image (myImage) to Growl, and not the original image.

Re: Ignore icon DPI

Posted: Thu Jun 19, 2008 6:16 pm
by Wooden Brain Concepts
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.

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];
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

Re: Ignore icon DPI

Posted: Thu Jun 19, 2008 6:23 pm
by boredzo
Wooden Brain Concepts wrote:When I invoke [myImage setSize: new size] i am modifiying "myImage", right?
Right.
I think the problem is that "bestRepresentationForDevice" like "[myImage representations] objectAtIndex:0]" do exactly the same thing …
That is not the problem. That is not even a problem.

Code: Select all

existingSize.width = [[myImage bestRepresentationForDevice: nil] pixelsWide];
existingSize.height = [[myImage bestRepresentationForDevice: nil] pixelsHigh];
Why make it pick the best representation twice? Instead, get it once and use it twice.

Code: Select all

NSSize newSize = NSMakeSize(existingSize.width, existingSize.height);
Or just:

Code: Select all

NSSize newSize = existingSize;
Or:

Code: Select all

NSSize newSize = { [myRepresentation pixelsWide], [myRepresentation pixelsHigh] };
(where myRepresentation is the rep you got from your message to bestRepresentationForDevice:).

Code: Select all

[myImage setSize: newSize];

	[GrowlApplicationBridge notifyWithTitle:atitle
	description:amsg
	notificationName:anotif
	iconData:[myImage TIFFRepresentation]
	priority:aPrior
	isSticky:aStick
	clickContext:aClick];

[myImage release];
AFAIK, this should work. Could you save the TIFF to a file and send it to me?

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:@""];

Re: Ignore icon DPI

Posted: Thu Jun 19, 2008 7:06 pm
by Wooden Brain Concepts
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:

Image

SavedGrowlIcon.tiff
Image

Preview of Path Finder display vs Growl notificaiton:
Image

I'll PM you with a link to the source.

Re: Ignore icon DPI

Posted: Thu Jun 19, 2008 7:28 pm
by boredzo
Wooden Brain Concepts wrote:i got a 17Kb tiff file as opposed to the 2 kb original.
JPEG is compressed. TIFF usually isn't. Compressed files are smaller than uncompressed files.

Re: Ignore icon DPI

Posted: Thu Jun 19, 2008 7:53 pm
by boredzo
Wooden Brain Concepts wrote:I'll PM you with a link to the source.
#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:
  • 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.
Upon opening the resulting JFIF file in Preview, the Info window says it is 72 DPI, which is what you want (1 pt = 1 px).

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. ☹

Re: Ignore icon DPI

Posted: Fri Jun 20, 2008 1:08 am
by Wooden Brain Concepts
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.
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).

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];
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

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];

If you can post the working code it would be much appreciated.
One other thing: Please don't make your notifications sticky by default. ☹
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.

Re: Ignore icon DPI

Posted: Fri Jun 20, 2008 2:10 am
by The_Tick
Wooden Brain Concepts wrote:
boredzo wrote:One other thing: Please don't make your notifications sticky by default. ☹
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.

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

Posted: Fri Jun 20, 2008 3:11 am
by boredzo
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:
That's correct. In this case, the framework accepts an NSImage if you pass one, but the method is typed as expecting an NSData.

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];
Looks right to me.
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];
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.
If you can post the working code it would be much appreciated.
I left it on my RAM disk—it's gone now. You're on the right track with passing rep, though.

Re: Ignore icon DPI

Posted: Fri Jun 20, 2008 4:54 am
by Wooden Brain Concepts
Looks right to me.
Sorry, it's not.

If I use this:

Code: Select all

NSData *rep = [NSData representationOfImageRepsInArray:
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.

When I substitute that with this:

Code: Select all

NSData *rep = [NSBitmapImageRep representationOfImageRepsInArray: 
It compiles and runs fine. However it doesn't produce the expected results.

Exactly like before, using precisely that first code:

Image

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):

Image

Re: Ignore icon DPI

Posted: Fri Jun 20, 2008 6:45 am
by boredzo
Wooden Brain Concepts wrote:If I use this:

Code: Select all

NSData *rep = [NSData representationOfImageRepsInArray:
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.
Er, right. Sorry. Didn't notice the class name.
When I substitute that with this:

Code: Select all

NSData *rep = [NSBitmapImageRep representationOfImageRepsInArray: 
It compiles and runs fine. However it doesn't produce the expected results.
It does.
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):

Image
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.

You need to change it to send the writeData: message to rep, and change the filename extension from .tiff to .jpg.

Re: Ignore icon DPI

Posted: Fri Jun 20, 2008 12:14 pm
by Wooden Brain Concepts
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.
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.
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"):

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:@""];
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:

Image

And here again is the resulting file saved:
Image
The code I provided earlier still asks for the TIFFRepresentation of the NSImage, which hasn't changed.
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.

(edit: FYI, I have tried to use usingType:NSJPEGFileType, usingType:NSPNGFileType, usingType:NSTIFFFileType all with identical results)

Re: Ignore icon DPI

Posted: Fri Jun 20, 2008 7:49 pm
by boredzo
Wooden Brain Concepts wrote:Please don't complain that I need to change the variable TIFFPath to JPEGPath or something!
That too. ;)
The code I provided earlier still asks for the TIFFRepresentation of the NSImage, which hasn't changed.
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]

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:
  1. Change the size of the rep to its pixel size using the rep's own setSize: method.
  2. 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:).
  3. Pass the PNG or JPEG or TIFF data to GAB.
Here's photographic evidence that it works:

Re: Ignore icon DPI

Posted: Fri Jun 20, 2008 8:55 pm
by Wooden Brain Concepts
Holy Chr*st...

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];


Re: Ignore icon DPI

Posted: Fri Jun 20, 2008 8:59 pm
by boredzo
Wooden Brain Concepts wrote:Which rep? the one created by representationOfImageRepsInArray is NSData.
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]).
Are you suggesting going back to an earlier method I was trying?
This is still method #4.
It would be much easier if you posted some working code since you claim to have the proof.
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.