|
That title bar is not in vector format - it's three bitmaps! Your reason for thinking that it is in vector form is superficially plausible, but it turns out to be fallacious:
"the gradients used in the title bar are all smoothly scalable."
I'm assuming that you used the OS X Preview application to look at it - that applies some kind of interpolation (it looks like bilinear interpolation) to any bitmaps it finds in a PDF. Try saving out a whole screenshot using the Shift-Command-3 key press. That generates a bitmap of the screen stored in a PDF. Zoom in on that, and you'll see exactly the same effect - the gradient remains perfectly smooth as you zoom in, despite the fact that these screenshot PDFs just contain one big bitmap.
This gradient smoothness, which you have mistaken for evidence of vector imagery, is just the result of the interpolating scaling algorithm being used to scale the bitmap - linear gradient fills in bitmaps happen to come out looking just great if you use either bilinear or bicubic interpolation to expand them.
If you open your PDF up in Adobe Illustrator, you'll be able to see clearly that the title bar is just three bitmaps - the main area and the two end caps. (And the traffic lights are another partially transparent bitmap drawn on top of the main title bar bitmap.)
Alternatively, if you don't have Illustrator, open it up in a PDF viewer that uses nearest neighbour scaling rather than a smoothing interpolation. The current PC version of Adobe Acrobat Reader does this. When you zoom in with such a view, it becomes perfectly clear that the title bar is just three composed bitmaps.
So this remains unconvincing - you have produced a PDF containing a bunch of bitmaps. This simply appears to add further support to my two hypotheses (1: that OS X cannot produce scalable window content, and 2: that this is because once you get outside of a Quartz 2D surface, Quartz works by composing bitmaps).
I tried a variation on this experiment. I wrote a Cocoa application with an NSImageView on it. I drew a scalable image in OmniGraffle and made that image the NSImageView's image, and also set the NSImageView's autosizing to make it resize with the application.
When resizing the window, the contents of the NSImageView get redrawn every time. This of course gets Quartz 2D to redraw the contents from scratch every time. So on each redraw Quartz 2D rerasterizes the image to the approprate size and then hands it over to the compositor. So the imagery appears to be scalable. But look what happens when I print to PDF:
ScalingFailure.pdf
(I apologise for the exceptionally poor drawing skills, but Dammit Jim, I'm a progammer, not a graphic designer!)
Even though you mistook the bitmaps in your image for vector imagery, surely you cannot fail to see that this PDF is clearly made up of bitmaps - the image view that I gave scalable image data to is quite clearly a bitmap in this PDF. (And I used exactly the same technique to generate this bitmap as you described by the way. I just added the NSImageView. And a button, to see what that would look like. I notice that scales poorly too, although that's presumably because it's done as a bitmap just like the traffic lights.)
This leaves just the text in vector form. But even Win32 can do that. In fact it can do slightly better (even if you use the old GDI32 API) - if you set up an Enhanced Metafile device context and then pass it using a WM_PRINT message to a window, it will write a scalable representation of the window into the enhanced metafile. (This is the closest equivalent I could get to of printing a window using Save as PDF... under Windows, because Windows doesn't have built-in PDF support.)
If generate a scalable EMF file of a window in this way on Windows XP, all the window decorations suffer from the same problem as your OS X example exhibits - the decorations all come out as bitmaps. Any text will come out in text form rather than bitmaps, as with your example. But unlike your example, in Win32, the resulting enhanced metafile will also put any vector drawing primitives (like ellipses, bezier curves etc) in vector format too. (Contrast this with what happened in my NSImageView example on the Mac.)
So it's possible to do a slightly better job with Windows than you have done here. I therefore remain unconvinced. (But I'm still retaining an open mind. If anyone can show me a PDF of a UI that really is scalable, rather than simply being a bunch of bitmaps, then please do!)
|
This turns out not to be the case. While you are correct that I did look at it in Preview, I also went to the trouble of actually dumping out the PDF drawing stream.
While there are some bitmaps used, the main part of the title bar is a rectangle (vectors) that is filled with a pattern. It is *not* a bitmap.
This is the dump of the actual page content stream:
"q Q q 181 466.5 12 22 re W n 0 sc /Gs1 gs 181 466.5 12 22 re f /Perceptual
ri /Gs2 gs q 24 0 0 22 181 466.5 cm /Im1 Do Q Q q 649 466.5 12 22 re
W n 0 sc /Gs1 gs 649 466.5 12 22 re f /Perceptual ri /Gs2 gs q 24 0
0 22 637 466.5 cm /Im2 Do Q Q q 193 466.5 456 22 re W n /Pattern cs
/P1 scn 193 466.5 456 22 re f Q q 394 469.5 54 17 re W n 1 1 1 0 k
q 1 0 0 -1 394 486.5 cm BT 13 0 0 -13 2 14 Tm /F1.0 1 Tf (Window) Tj
ET Q Q q 181 106.5 480 382 re W n /Pattern cs /P2 scn 181 106.5 480
360 re f Q q 189 468.5 14 16 re W n /Perceptual ri q 14 0 0 16 189
468.5 cm /Im3 Do Q Q q 231 468.5 14 16 re W n /Perceptual ri q 14 0
0 16 231 468.5 cm /Im4 Do Q Q q 210 468.5 14 16 re W n /Perceptual
ri q 14 0 0 16 210 468.5 cm /Im5 Do Q Q q 181 106.5 480 382 re W n
/Perceptual ri q 15 0 0 15 646 106.5 cm /Im6 Do Q Q"
The crucial part is the following bit:
/Pattern cs /P1 scn 193 466.5 456 22 re f
This sets up a pattern color-space, then draws a rectangle that is filled with this pattern.
The pattern that is used is defined as follows:
q Q q /Perceptual ri /Im7 Do Q
So here we get the "bitmap" that Illustrator seems to pick up. But this is not a bitmap that is drawn, it is a bitmap pattern that is used as a fill-color for a rectangle. This bitmap is a 24 x 22 vertical gradient from 0xfe to 0x9d.
So why are we seeing a shape filled with a pattern that is a bitmap? Simple: a "bitmap" of gradually changing values is actually the recommended method for drawing a linear gradient in PDF! Or more precisely, was until version 1.3 of the spec. introduced shading dictionaries.
Because we are using a well defined and device-independent imaging model, we know that drawing an image with the 256 gray values 0-255 is exactly the same as drawing 256 rectangles with gray values ranging from 0.0 - 1.0 (if we set up the color-spaces correctly). This can then be scaled to the required dimensions.
So what you are seeing is a gradient implemented in PDF, at 24 step "resolution". Which, incidentally, is completely equivalent to drawing 24 (vector) rectangles, not just practically but also theoretically! Now they could have used more steps to define the gradient, but that would have been pretty pointless given the fact that this is a window title bar. They could also have used a shading dictionary, but that is only available in newer PDF versions, and my guess is that it just wasn't needed.
2. "Scaling failure" with NSImageView
You have made a well-known beginner's mistake in using NSImage that dates back to NextStep days. If you look up the NSImge documentation, you will find that it creates and caches a device-specific rendered version of any resource it uses. And it will then throw away the original, unless it is told otherwise. This behavior is appropriate for the NSImage's intended use: drawing icons and other screen ornaments.
I am guessing you set up your NSImageView completely within Interface Builder, including referencing the pdf "image" by name from within the nib. That is how you get the behavior you describe, and I have duplicated this behavior in the following PDF example:
NonScalable.pdf
As you have noted, even that example will "scale" with the window as you change size, because the NSImage notices that its cached representation is not at the appropriate resolution for the current scale and will thus generate a new representation that does have the appropriate resolution for that particular scale.
If you don't want that behavior, just use - (void)setDataRetained:(BOOL)flag to tell it to retain the original, which it will automatically do if initialized with a file path (instead of an image name, for exmaple). So, to get perfectly scalable output, just make the NSImageView "Editable" (there's a check box in Interface Builder) and drop a pdf in at runtime. I actually took the exact same PDF that I was referencing from within the nib and dropped it in the image-view. Voilá, perfectly scalable output, because the original scalable representation is retained:
Scalable.pdf
In fact, you can use the same pdf both time. Incidentally, the reason that the output did appear scalable while you were resizing is that NSImage will automagically notice that the scale it is drawing at to screen has changed and go back to the original source to re-render a cached representation for that scale. So as you can see, all the machinery is already in place, and WORKING, for automatic adjustment to different resolutions.
Of course, invoking -setDataRetained: is timing-sensitive, there is no use doing this after the NSImage has already discarded the original.
So to sum up: both of your "proofs" have evaporated, as usual, and just show that you didn't know what you're doing on Mac OS X. Of course, not knowing what you're doing on the Mac is OK, you're a Windows programmer after all. However, it is usually better to avoid making bold assertions by drawing wrong conclusions from an incomplete and flawed understanding of the facts.