September 94 - Macintosh Q&A
Macintosh Q & A
Macintosh Developer Technical Support
Q What operations cause QuickDraw GX to invalidate its shape caches? We want to
maximize drawing performance in our application, and it would be nice to know ahead
of time when caches will need to be rebuilt.
A As you might imagine, this topic is complex, but we'll try to explain the basics and
give you some ideas about improving performance.
There are several caches associated with a shape, since each object associated with the
shape has a separate cache. (By the way, the caches are in the QuickDraw GX heap and
can be seen using GraphicsBug.) Every time you call GXDrawShape, QuickDraw GX
determines which caches need to be updated, updates them, and then draws the shape.
So the first thing to know is that if you want GXDrawShape to execute as fast as
possible (for instance, if you're drawing several shapes and want the time between
successive GXDrawShape calls to be minimal), you should call GXCacheShape ahead of
time to update the shape's caches, minimizing the work GXDrawShape needs to do.
In general, any time you change information within a shape, you cause QuickDraw GX
to invalidate at least one of the shape's caches. Layout shapes are exceptions to this
rule, however. The cache associated with a layout shape will not be invalidated if all of
the following conditions are met:
• You're adding characters to the end of the layout shape.
• There's a single run of text within the layout shape.
• The shape remains on the same device.
If you're drawing non-hairline geometric shapes and want to get them on the screen as
fast as possible, you can set the gxCacheShape attribute of the shape. With this
attribute set, QuickDraw GX will preprocess all the required parts of a shape into
compressed bitmaps. This means the shape is completely ready to be drawn when you
call GXDrawShape: the bits are just blasted to the screen.
Any time you change a view port's clip or mapping, QuickDraw GX will update all of
the caches for the shapes and child view ports associated with that view port. There
isn't any speed advantage to setting the clip or mapping of a shape rather than the clip
or mapping of the shape's view port; the same work has to happen in either case. Note,
though, that if the view port contains other shapes or has child view ports, their
caches will be invalidated, too. You should change the view port's mapping to move a
shape only when you want all shapes within the view port to move the same amount.
Q I'm looking into the possibility of writing a QuickDraw GX printer driver that will
only print to a file, and I've run into a couple of stumbling blocks I hope you can help
with. First, is there a way to create a desktop printer from the Chooser without having
an actual printer attached or selecting a port? Can I get to the Chooser list so that I can
display some kind of dummy or ghost printer? The second issue has to do with the user
interface of the Print dialogs/panels. I would like to set the "Destination: File" radio
button and disable the "Destination: Printer" one for QuickDraw GX application
interfaces.
A There should be no problem creating a printer driver that only prints to a file. And
yes, users will be able to create a desktop printer from such a driver. You can access
the Chooser list: for an example of this, look at the source code file Chooser.c from any
of the QuickDraw GX printer driver samples. You can do whatever you want within the
Chooser to handle and display lists of available printers for the currently selected
printer driver. You'll also need to modify the 'comm' resource to make sure that the
Chooser does the right thing. You should be able to put a "nops" 'comm' resource
reference into the 'look' resource.
You can disable any item in the Print dialog by overriding the GXJobPrintDialog
message and, in your override, getting the destination tag for the appropriate item and
locking it. This will make the item appear disabled in the dialog. PrintingMgr.h
contains all the tags you can lock. You should be sure to set up the item to be
gxVolatileOutputDriverCategory so that your settings go away if the user switches
drivers in the pop-up menu; you can simply OR this with collectionLockMask when
you set the item's attributes.
Q The documentation seems a bit thin on what resource type/ID is needed for ColorSync
profiles in QuickDraw GX printer drivers. Is the appropriate type 'prof' or 'cmat'?
Will QuickDraw GX automatically use one if I stick it in my driver, or do I also have to
override the various profile messages?
A All you need to do is include a 'cmat' resource with ID gxColorMatchingDataID and
specify it in your 'rdip' resource. However, you should also override
GXFindFormatProfile so that inquiring applications can ask you what the format's
profile will be. Additionally, you should override the GXImagePage message if you want
to provide different profiles on a page-by-page basis.
Q I'm working on QuickDraw GX printer drivers. Can you give me some information on
what must be done to support PrGeneral?
A PrGeneral support within your QuickDraw GX printer driver is automatic. The
printing system will automatically maintain the appropriate information within the
QuickDraw GX print record. The only exception to this automatic support is the SetRsl
opcode: if you don't want QuickDraw GX to use the default gxReslType resource when
the SetRsl opcode is used, you need to define a gxReslType resource of your own that
reflects the capabilities of your printer.
Q Our application generates its own custom PostScript TM code when printing to
PostScript printers. We'd like to support QuickDraw GX-style printing and still be
able to continue generating our own custom PostScript code to send directly to the
printer. What's the best way to handle this? I've tried generating empty shapes with
custom PostScript code attached as a tag (using synonyms), but the LaserWriter
QuickDraw GX driver emits its own "wrapper" code around our custom PostScript
code, which could alter the printer's graphics state. Are there any other methods to
achieve this without the possible side effects?
A You're actually very close to sending PostScript code correctly through the
QuickDraw GX printing system without the side effects. As you already know, after a
shape with 'post' tags has been sent to a PostScript printer, QuickDraw GX performs a
PostScript restore. There's no way to prevent this from happening. However, only
shape objects cause this behavior; QuickDraw GX will not perform a PostScript
restore for any other object besides a shape.
The fastest and best method for sending PostScript code is to attach the code with tags
(using synonyms) to only one empty shape. You can attach as many tags as are
required. We recommend that the tags contain 12K of PostScript data each, for optimal
performance. If you're sending PostScript code down to replace the clip or ink or some
other object besides a shape, just attach a 'post' tag to the object your PostScript code
describes; in this case a restore will not occur.
Q How should I download fonts to a PostScript printer under QuickDraw GX? I'm
sending a direct stream of custom PostScript code, but I don't expect the driver to be
able to deduce which fonts need to be included. I've read about the PostScript control
tag that can be attached to a shape, and I know the structure for the tag contains font
information, but the documentation about this tag is sketchy. Can you provide more
details?
Related to this question, what's the best way to cause fonts to be downloaded under the
current Printing Manager? We're using the "draw a single character off the page in
the proper font" trick. I understand this practice is frowned on. Is there an approved
way to do this short of intermixing QuickDraw and PostScript code in PicComments?
A Our thinking has changed regarding the use of the PostScript control tag for
downloading a font. If you want to download a font within your PostScript stream, you
should call GXFlattenFont and pass the font within your 'post' tag. The GX PostScript
engine will unflatten the font and download it when it finds it attached to your 'post'
tag.
The method you're using (drawing a character off the page) is completely supported
under QuickDraw GX. It was the recommended method for a long time. However, the
current recommendation is to use DrawText and pass in text that contains all the
glyphs you want to use. The reason this approach is better for QuickDraw GX is that
only the required information is downloaded, thereby saving memory on the printer.
Q Do I need to override the GXFreeBuffer message in my QuickDraw GX printer driver
if I perform a total override of the GXDumpBuffer message? If I do need to override
GXFreeBuffer, what do I do with the buffer? Should I dispose of it with DisposePtr?
A The documentation is a little confusing about the purpose of the GXFreeBuffer
message. GXFreeBuffer is sent to ensure that the indicated buffer has been processed
and is now available for more data; it doesn't actually dispose of a buffer. The only time
you need to override GXFreeBuffer is if you're doing custom I/O (the customIO flag is
set in your 'iobm' resource). The default implementation of GXFreeBuffer will work
correctly for QuickDraw GX's default buffering mechanism.
GXFreeBuffer allows you to start asynchronous I/O in GXDumpBuffer; then other
buffering routines can be sure that operations on a buffer are completed before they
reuse the buffer (or dispose of it). If you're overriding GXFreeBuffer, you should just
hang out in your override until I/O has completed for the buffer passed, and then
return. If you're doing synchronous I/O, just return immediately, since all I/O must
have completed.
Q When I control the start of a QuickTime movie from within my application, the
movie controller doesn't get updated properly. I'm calling StartMovie to begin the
movie as soon as it becomes visible, and I'm updating the movie controller like this:
theResult := MCDoAction(tmpMovie.fController, mcActionPlay,
@curRate);
However, this doesn't seem to work. What am I doing wrong?
A The MCDoAction call with mcActionPlay doesn't take a pointer to the data in the last
parameter; it takes the data itself. But since the prototype specifies type (void *), to
make the compiler happy it must be cast to a pointer. Many people have fallen into this
trap.
The recommended method to start a movie when you're using the standard movie
controller component is as follows:
// Play normal speed forward, taking into account the possibility
// of a movie with a nonstandard PreferredRate.
Fixed rate = GetMoviePreferredRate(MyMovie);
MCDoAction(MyMovieController, mcActionPlay, (void *)rate);
If you do need to use StartMovie, the correct way to cause the movie controller to
update is to call MCMovieChanged.
Q We're using the Communications Toolbox in our application and have noticed some
strange behavior. Specifically, when a tool is being used, the tool's resource fork is
placed not at the top of the resource chain, but behind the System and application
resource forks. As a result, we're having trouble with tools that use STR# resources
that conflict with those in our application. This results in bad configuration strings or
configuration dialogs with the wrong text. I can easily work around this by changing
the resource IDs in my application, but this doesn't solve the problem in the long run.
Any advice?
A What you're seeing is a part of the "pathology" of the Communications Toolbox and its
tools. Both do a less than perfect job of looking in the correct resource map for string
resources. The result is what you're currently seeing: application strings end up being
placed where tool strings are expected.
There are a couple of workarounds. The first you've cited, which is to make sure that
none of your application's resource ID numbers conflict with the CTB tool's ID
numbers. However, as you also noted, this can be a problem when you're using several
CTB tools, and may not work if the user happens to select a tool that has a conflicting
ID.
The better way to work around the problem is to save a reference to the current
resource file and then set the resource file to the System file. After completing a call
to CMChoose or CMGetConfig, you can reset the resource file to the one you started
with. Here's how to do this:
short oldResFile;
Point dlogBoxPt;
Ptr myConfigString;
/* First save the current resource file. */
oldResFile = CurResFile();
/* Now set the resource file to the System file. */
UseResFile(0);
/* Next call CMChoose to configure your connection tool. */
myErr = CMChoose(&myConnectionHdl, dlogBoxPt, nil);
/* Now call CMGetConfig to get the configuration string from the
connection tool. */
myConfigString = CMGetConfig(myConnectionHdl);
/* And finally, restore the old resource file. */
UseResFile(oldResFile);
Q What is the Human Interface suggestion for removing a digital signature from a
signed document? I see how to add and how to verify, but I can't find any suggestions
for removing signatures.
A Here's the relevant paragraph from the AOCE Human Interface Guidelines
document, which can be found on AppleLink (search the AOCE Talk folder):
Signatures may also be deleted by users. To accomplish this, the user should select the
signature by clicking on its icon and choosing Clear from the application's Edit menu.