Archive for the 'Quartz' Category

WWDC 2007 session videos are out

Monday, July 30th, 2007

If you attended WWDC, you can head over to ADC on iTunes and see what you missed.

Why Mac programmers should learn PostScript

Saturday, April 7th, 2007

I'll follow this up with a tutorial called “PostScript for Cocoa programmers”, but today brings my list of reasons why you should care in the first place.

(more...)

What’s the resolution of your screen?

Sunday, February 4th, 2007

A few weeks ago, I installed Adobe Reader to view a particular PDF, and noticed something interesting in its Preferences:

Its Resolution setting is set by default to “System setting: 98 dpi”.

“Wow”, I thought, “I wonder how it knows that.” So I went looking through the Quartz Display Services documentation, and found it.

The function is CGDisplayScreenSize. It returns a struct CGSize containing the number of millimeters in each dimension of the physical size of the screen. Convert to inches and divide the number of pixels by it, and you've got DPI.

Not all displays support EDID (which is what the docs for CGDisplayScreenSize say it uses); if yours doesn't, CGDisplayScreenSize will return CGSizeZero. Watch for this; failure to account for this possibility will lead to division-by-zero errors.

Here's an app to demonstrate this technique:

ShowAllResolutions' main window: “Resolution from Quartz Display Services: 98.52×96.33 dpi. Resolution from NSScreen: 72 dpi.”

ShowAllResolutions will show one of these windows on each display on your computer, and it should update if your display configuration changes (e.g. you change resolution or plug/unplug a display). If CGDisplayScreenSize comes back with CGZeroSize, ShowAllResolutions will state its resolution as 0 dpi both ways.

The practical usage of this is for things like Adobe Reader and Preview (note: Preview doesn't do this), and their photographic equivalents. If you're writing an image editor of any kind, you should consider using the screen resolution to correct the magnification factor so that a 8.5×11″ image takes up exactly 8.5″ across (and 11″ down, if possible).

“Ah,”, you say, “but what about Resolution Independence?”.

The theory of Resolution Independence is that in some future version of Mac OS X (possibly Leopard), the OS will automatically set the UI scale factor so that the interface objects will be some fixed number of (meters|inches) in size, rather than some absolute number of pixels. So in my case, it would set the UI scale factor to roughly 98/72, or about 1+⅓.

This is a great idea, but it screws up the Adobe Reader theory of automatic magnification. With its setting that asks you what resolution your display is, it inherently assumes that your virtual display is 72 dpi—that is, that your UI is not scaled. Multiplying by 98/72 is not appropriate when the entire UI has already been multiplied by this same factor; you would essentially be doing the multiplication twice (the OS does it once, and then you do it again).

The solution to that is in the bottom half of that window. While I was working on ShowAllResolutions, I noticed that NSScreen also has a means to ascertain the screen's resolution: [[[myScreen deviceDescription] objectForKey:NSDeviceResolution] sizeValue]. It's not the same as the Quartz Display Services function, as you can see; it seemingly returns { 72, 72 } constantly.

Except it doesn't.

In fact, the size that it returns is premultiplied by the UI scale factor; if you set your scale factor to 2 in Quartz Debug and launch ShowAllResolutions, you'll see that NSScreen now returns { 144, 144 }.

The Resolution-Independent version of Mac OS X will probably use CGDisplayScreenSize to set the scale factor automatically, so that on that version of Mac OS X, NSScreen will probably return { 98.52, 98.52 }, { 96.33, 96.33 }, or { 98.52, 96.33 } for me. At that point, dividing the resolution you derived from CGDisplayScreenSize by the resolution you got from NSScreen will be a no-op, and the PDF view will not be doubly-magnified after all. It will be magnified by 133+⅓% by the UI scale factor, and then magnified again by 100% (CGDisplayScreenSize divided by NSDeviceResolution) by the app.

Obviously, that's assuming that the app actually uses NSScreen to get the virtual resolution, or corrects for HIGetScaleFactor() itself. Adobe Reader doesn't do that, unfortunately, so it suffers the double-multiplication problem.

So, the summary:

  • To scale your drawing so that its size matches up to real-world measurements, scale by NSDeviceResolution divided by { 72.0f, 72.0f }. For example, in my case, you would scale by { 98.52, 96.33 } / { 72.0, 72.0 } (that is, the x-axis by 98.52/72 and the y-axis by 96.33/72). The correct screen to ask for its resolution is generally [[self window] screen] (where self is a kind of NSView).
  • You do not need to worry about HIGetScaleFactor most of the time. It is only useful for things like -[NSStatusBar thickness], which return a number of pixels rather than points (which is inconvenient in, say, your status item's content view).

A Core-Image-less Image Unit

Wednesday, January 17th, 2007

Can you imagine an Image Unit that didn't actually use Core Image?

I just wrote one.

Well, OK, so I did use CIFilter and CIImage — you can't get away without those. But I did not use a CIKernel. That's right: This simple filter does its work without a kernel.

For the uninitiated, a kernel is what QuartzCore compiles to either a pixel shader or a series of vector (AltiVec or SSE) instructions. All Image Units (as far as I know) use one — not only because it's faster than any other way, but because that's all you see in the documentation.

But I was curious. Could an Image Unit be written that didn't use a kernel? I saw nothing to prevent it, and indeed, it does work just fine.

The image unit that I wrote simply scales the image by a multiplier, using AppKit. I call it the AppKit-scaling Image Unit. Feel free to try it out or peek at the source code; my usual BSD license applies.

Obviously, this Image Unit shouldn't require a Core Image-capable GPU.

New Apple documentation

Wednesday, March 8th, 2006

I don't ordinarily write about changes to Apple's documentation, since you can get that info straight from ADC RSS, but today's round of changes is unusually significant.

Apple replaced several of the documents relating to Cocoa with new ones, and added some things to the new ones as well. the full run-down:

Was Now
  • Basic Drawing
  • Drawing and Images
  • The Drawing Environment
  • OpenGL
Cocoa Drawing Guide
  • What Is Cocoa?
  • Cocoa Objects
  • Adding Behavior to a Cocoa Program
  • Cocoa Design Patterns
  • Communicating With Objects
Cocoa Fundamentals Guide Two new chapters have been added: "The Core Application Architecture" and "Other Cocoa Architectures". In addition, the discussion of the Model-View-Controller design pattern in "Cocoa Design Patterns" has been greatly expanded. — revision history
  • Sdef Scriptability Guide for Cocoa
  • Scriptable Application Programming Guide for Cocoa
Cocoa Scripting Guide
Document-Based Applications Document-Based Applications Overview New articles added are "Message Flow in the Document Architecture", "Creating Multiple-Document-Type Applications", "Autosaving in the Document Architecture", and "Error Handling in the Document Architecture". — revision history
Drawing and Views Scroll View Programming Guide for Cocoa
Drawing and Views View Programming Guide for Cocoa

I think the new set of documents may be much more suitable for passing to people who are learning for the first time. I'll show The_Tick, who's learning Cocoa, and see what he thinks.

while I'm at it, I'll point out that they clarified the docs on CGImageSourceUpdateData:

Each time you call the function CGImageSourceUpdateData, the data parameter must contain all of the image file data accumulated so far.

so you should probably use a CFMutableData (or its NS equivalent) when using this function.

On writing Image Units

Wednesday, January 11th, 2006

Apple's Core Image documentation doesn't clearly state how to make a CPU-executable Image Unit, as opposed to a non-executable (GPU-only) one.

The answer is simple: Don't make a .cikernel file. You should start with one when you're running the validation tool over your Image Unit, but if you want to make a CPU-executable Image Unit (and please do, so I can use it on my Cube), move the kernel code into your Obj-C code once it compiles, and then delete the .cikernel file.

(BTW, yes, I am testing MarsEdit. Hence the two posts today. ;)

UPDATE 2008-02-18: I've since found out that, in fact, the definition of “non-executable” is more strict than that. The Core Image Programming Guide's chapter on Executable vs. Non-executable Filters provides a more exact definition:

  • This type of filter is a pure kernel, meaning that it is fully contained in a .cikernel file. As such, it doesn’t have a filter class and is restricted in the types of processing it can provide.

  • Sampling instructions of the following form are the only types of sampling instructions that are valid for a nonexecutable filter:

    color = sample (someSrc, samplerCoord(someSrc));
  • CPU nonexecutable filters must be packaged as part of an image unit.

  • Core Image assumes that the ROI coincides with the domain of definition. This means that nonexecutable filters are not suited for such effects as blur or distortion.

Testing Image Units

Sunday, January 8th, 2006

Some of you may know that I've been studying Apple's Core Image API — specifically, how to write an Image Unit. I just found this, buried in Apple's website: Software Licensing & Trademark Agreements: Image Units.

What's special about that? Read the page closely:

3. Download the Image Units Validation Tool (DMG). Use of this application is subject to the terms of the Validation Tool License (RTF) presented upon launch.

It's actually a command-line tool, and the agreement is displayed when the image is mounted rather than when the tool is run, but nonetheless — it's a tool that examines your Image Unit and attempts to compile its .cikernel file, and tells you if it finds anything wrong. Highly useful.