December 94 - Macintosh Q & A
Macintosh Q & A
Q I'm having problems getting PICTs to display with the colors I want. I'm converting GIF files to PICTs by drawing the GIF into an offscreen GWorld. I'm using the Palette
Manager to set up the colors, but there's no way to associate a palette with an offscreen
PixMap. After I'm done drawing the GIF to a PixMap, I open the picture with the
offscreen PixMap as the current port and use CopyBits to copy the PixMap onto itself,
creating the picture. The problem is that if I use srcCopy, the colors are incorrect in
the PICT when opened with TeachText (and other applications). But if I use ditherCopy
the colors are saved correctly. I can use srcCopy if I do a CopyBits to/from a "color
window with the window's palette changed to my color palette. Is there a way to assign
a palette to use for OpenPicture and still use CopyBits from an offscreen bitmap with
srcCopy?
A You can associate a palette with a GWorld, but it won't solve your problem: since a GWorld never becomes "active," the associated device's colors are never changed to
match the palette. The solution is to use a custom color table with the GWorld. And you
can easily use Palette Manager routines to convert your palette to a color table.
Use the Palette2CTab routine to perform the conversion. Palette2CTab takes a
PaletteHandle and a CTabHandle and copies all the colors from the palette into the color
table, resizing the color table as necessary. If the palette handle is nil, no change takes
place.
Now you have a color table that you can associate with your GWorld. You can pass it to
NewGWorld when you create your GWorld initially; the fourth parameter is a handle to
a color table. You need to explicitly set the depth in this call for best results. (If you
pass nil for the depth, the color table parameter will be ignored and the depth of the
GWorld will be set to match the deepest device that intersects the GWorld's boundary
rectangle.) The other possibility is to associate the color table with an existing GWorld
using UpdateGWorld.
Q I'm having a bit of a problem with DiffRgn. I start out with a "wide open rectangular region (-32767, -32767, 32767, 32767) and then use DiffRgn to
subtract a group of smaller rectangles from it. When I'm done, the bounding box of the
region isn't what it should be. Any idea what's happening?
A What you need to do is create your clipping region so that it's not quite wide open (bottom and right coordinates of 32766 will work). If you do this, all your DiffRgn
calculations will work fine.
While this isn't explicitly documented anywhere, it does seem to be a quirk in the way
regions work. Due to the internal storage format of regions, the number 0x7FFF
(32767) causes problems if it appears as a point inside a region. 0x7FFF is used as a
flag in the internal region data structure to signify a "barrier." When this flag is used
as a data point in a nonrectangular region, region parsing becomes completely screwed
up.
QuickDraw tries to catch the creation of regions that will be poorly formed and turn
them into properly formed (but slightly incorrect) regions, but it isn't 100%
successful.
Q I'm erasing my windows with a color other than white, by setting the window's background color and calling EraseRect. But in cases where the Window Manager gets
there first (window ordering changes or a window's size gets larger) I still get
flicker, because the Window Manager erases with the wContent color from the
window's color table (white by default) and not the port's background color. Is there a
friendly, clean way to avoid that flicker? (I notice that in System 7.5, background
colors are implemented with EraseRect, just as I'm doing it. Are you simply assuming
the flash will be minimal?)
A One of the Window Manager's functions is to ensure that the content region of a window is opaque when it needs to be: that's why the Window Manager "pre-erases" the
window when the content region grows, before your application gets a chance to. As you
point out, if your application is then erasing large areas of the window to a different
color, you'll get a noticeable flicker in those parts of the content region that needed to
be opaque. This is an unfortunate side effect of a necessary maneuver by the Window
Manager. Any system dialogs that set a background color and use EraseRect will suffer
from the same flicker (although you won't spot it so often, since for the most part
they're modal, nonresizable, and relatively small).
There are two solutions: If you create your windows from 'WIND' resources, you can
create 'wctb' resources with the same ID and an appropriate wContent color and they'll
automatically be used when the window is created. Alternatively, you can use the
SetWinColor routine to apply a color table to a window after it has been created.
Q How can we write native PowerPC versions of our QuickDraw GX printer drivers? Native QuickDraw GX applications are easy, but I can't find any documentation for
writing native drivers. One problem I can see is the jump table at the top of the 'pdvr'
code resource, where each of the jump table entries is supposed to contain a 680x0
instruction to jump to the appropriate override procedure within the resource. How
should we proceed?
A First of all, in our experiments with native QuickDraw GX drivers we've found little or no performance increase, so we don't really recommend writing native printer
drivers. The bottlenecks in typical QuickDraw GX printer drivers are in the file and
network I/O, which aren't affected much by the driver's code (see the Print Hints
column in this issue of developfor more information). Unless you have some repetitive
and time-consuming operation in which you can expect a huge win, your code will most
likely just get bigger, and possibly even slower in some cases because of the context
switches.
That said, I'll tell you how to make any or all of your overrides into "fat" overrides.
Each of the fat overrides needs to consist of three parts:
• a "safe routine descriptor" (see below)
• the 680x0 code
• the PowerPC code
The safe routine descriptor allows a simple JMP instruction (such as those found in
the 'pdvr' jump table) to run either 680x0 code or PowerPC code, depending on what
type of Macintosh it's running on. The beginning of the safe routine descriptor contains
680x0 code that's executed the first time through and that determines whether the
Mixed Mode Manager is present. If so, the routine descriptor from the PowerPC chunk
of code is copied to the top of the resource. If not, a 680x0 branch instruction that
jumps to the 680x0 code is inserted at the top of the routine descriptor. This code
munging happens only the first time through -- after that, the code resource is set up
to run the 680x0 code or the PowerPC code immediately upon jumping to it. This is
described in more detail in Inside Macintosh: PowerPC System Software and in
MixedMode.r.
Here's how a fat override would be called from QuickDraw GX: First, execution jumps
to the desired entry in the printer driver's jump table. The 680x0 JMP instruction in
the table is executed and jumps to the safe routine descriptor in the override. The safe
routine descriptor then executes either the 680x0 code or the PowerPC code,
depending on the machine.
The real trick is building the makefile. First the makefile has to compile each of the
override functions into a 680x0 code resource using C and Link, and into a PowerPC
code resource using PPCC, PPCLink, makePEF, makesys, and Rez. Next, it needs to use
Rez to combine the results of the compiles into fat resources with safe descriptors.
Finally, it needs to use Rez to combine the jump table with all of the fat resources.
This last step is a doozy. You'll need to write an MPW tool that concatenates all the fat
resources while keeping track of the offsets of each one. These offsets will need to be
stuffed into the jump table at the top of the final 'pdvr' resource.
Q I'm trying to display from my QuickDraw GX printer driver a movable modal dialog box that contains a list. What would be the best way to do it?
A Since you're displaying the dialog from a printer driver, you can let QuickDraw GX do most of the work. Use GXAlertTheUser to put up a 'plrt' resource that specifies the
printingStatus type. That makes a movable modal dialog. Then override
GXInitializeStatusAlert to build your list information for the dialog, and
GXHandleAlertEvent so that you can update your dialog, handle clicks, dispose of the
dialog, and so forth. You'll probably also want to override GXHandleAlertFilter to help
out with that.
Q I noticed that the "Larger Print Area" checkbox has been taken out of QuickDraw GX's LaserWriter Page Setup Options. I assume this means one of three things: (a) it's now
on all the time, (b) it's now off all the time, or (c) it's lurking someplace else. Which
is it?
A The option has been removed, as you noted, but the functionality is still available, in the guise of papertypes. To decide where on the page to print, QuickDraw GX printing
uses the papertype that a page is formatted for. Some of these papertypes come
standard (built into QuickDraw GX or into specific drivers), but users can also create
their own papertypes using the papertype editor that accompanies QuickDraw GX.
For example, a user could create a papertype called "Company Letterhead" that's based
on US Letter but has an imageable area that excludes the part of the page at the top
where the address and logo might be, as well as the line or two of text at the bottom of
the page. When dropped into the Extensions folder, these custom papertypes become
available from QuickDraw GX applications, and users can format their documents with
them. In the case of this letterhead example, the result would be that you wouldn't have
to fiddle around with your text to avoid printing over the type and graphics on the
paper.
The "Larger Print Area" feature isn't compatible with the concept of papertypes
because in order for QuickDraw GX to honor the imageable area of a papertype, it can't
change the dimensions that the papertype is set up for. Again, using the letterhead
example, if we were to expand the imageable area it would probably result in text
printing over the company logo or address information.
We realized that some customers would still expect this functionality to be around, so
we've included some papertypes in the LaserWriter GX driver that mimic the old
behavior. When you format for the LaserWriter GX driver (or for a desktop printer
for that driver), you'll notice that two new papertypes become available in the
QuickDraw GX page setup dialogs: "A4 Letter (7.8 x 11.4)" and "US Letter (8.5 x
11)." These are "larger print area" versions of their counterparts. In applications
that aren't QuickDraw GX aware, these papertypes can be found in the Page Setup
dialog's Paper Type menu.
Q I was wondering whether QuickDraw GX, like LaserWriter 8, uses PostScript printer description (PPD) files. How do PPD files and the new printing architecture
integrate? What happens in QuickDraw printing compatibility mode?
A The short answer to this question is no, QuickDraw GX does not use PPD files. The longer answer is, well, longer.
The main use for PPD files was to extend the functionality of the LaserWriter 8
driver. As you know, the contents of many of LaserWriter 8's dialogs depend on the
contents of the PPD file. The user can associate a PPD file with a printer via the
Chooser from LaserWriter 8 onwards, by using the Setup button in the Chooser dialog.
As you're also aware, the mechanism for choosing printers has changed with
QuickDraw GX. This means that PPD files aren't needed, except for one case: using
QuickDraw GX Helper to print with LaserWriter 8 with QuickDraw GX installed. In
this situation, printing will occur in the same way as before -- in other words, the
print process will be the same as for plain old LaserWriter 8, including the use of
PPD files. In QuickDraw printing compatibility mode (that is, printing with a
pre-QuickDraw GX application on a system running QuickDraw GX), the emulation
will use the QuickDraw GX driver, so PPD files won't be used unless the driver
specifically supports them.
By the way, there's no reason a printer driver manufacturer can't incorporate PPD
files into a QuickDraw GX printer driver, if it's deemed appropriate.
Q When my application creates a new media (of text type in this case) for a new track in a movie created with NewMovieFromScrap, the dataRef and dataRefType should be
set to nil, according to the QuickTime documentation. The problem is that later I want
to edit that media (adding a text sample to it, for example), but BeginMediaEdits
returns -2007 (no data handler found). I assume I can get around that by first saving
the movie to a file, but this seems slimy since the movie won't end up on disk in the
end. Any suggestions for a better approach?
A You're correct -- BeginMediaEdits complains if the movie has been created with NewMovieFromScrap. Unfortunately, BeginMediaEdits doesn't think memory-based
movies are on a media that will support editing. The workaround is, as you thought, to
store the movie in a temporary file until you're finished editing it.
Fortunately, this is easy to do. When you call NewTrackMedia, pass an alias to a new
file in the dataRef parameter instead of nil. Passing nil (the usual approach) indicates
that the movie's default data reference should be used, but because your movie came
from the scrap and not a file, it has no data reference -- hence the error you're
getting. By the way, using the data handler in QuickTime 2.0, you can create a movie
entirely in memory.
Q I need to alter the pixel information of a QuickTime movie frame after it has been decompressed but before it's displayed on the screen. Is there any way that a
user-defined procedure can be called by QuickTime at this point? If not, how can I
accomplish this?
A As long as you use QuickTime 1.6 or later, you can do this easily. The mechanism is described in the Macintosh Technical Note "QuickTime 1.6.1 Features" (QT 4) under
the heading "SetTrackGWorld.
SetTrackGWorld lets you force a track to draw into a particular GWorld. This GWorld
may be different from that of the entire movie. After the track has drawn, it calls a
"transfer procedure" that you've written to copy the track to the actual movie GWorld.
You can also install a transfer procedure and set the GWorld to nil. This results in
your transfer procedure being called only as a notification that the track has drawn --
no transfer takes place.
You should do your image manipulation in your transfer procedure. Bear in mind, of
course, that calling resource-intensive or time-consuming routines in your transfer
procedure will have an adverse effect on the playback performance of your movie.