Bitmap for SuperPaint
Volume Number: | | 6
|
Issue Number: | | 2
|
Column Tag: | | Programmer's Workshop
|
Related Info: Color Quickdraw Dialog Manager Menu Manager
BitMapper Utility For SuperPaint
By Michael Ogawa, Oceanside, CA
Note: Source code files accompanying article are located on MacTech CD-ROM or source code disks.
[Michael Ogawa is a Software Designer for Palomar Software where he has been involved in several projects including the Print Preview and PrintQ Monitor features of Palomars printer and plotter drivers. He has done design and development on Apple ][ computers since 1978, and has worked on business productivity tools, such as database and word processing applications, for the Macintosh since 1985.]
This article covers two interesting and relatively new topics: SuperPaint plug-ins, and a new data structure/resource format for bit images. The BTMP resource type is a handy way of encapsulating a bit image and its dimension specifying bit map data into one package. It is easy to retrieve from disk, and to draw with or manipulate. SuperPaint is a wonderful application for creating bit image graphics. And using a plug-in tool that well create in this article, its a snap grabbing an image from a SuperPaint document and creating a BTMP resource.
Writing this article should be a snap because Ill be able to leverage off of two other articles in this issue of MacTutor--meaning that I wont have to write as much! This article is a how-to on writing a transformation menu command. For an overview of SuperPaint plug-ins, and for specifics on interactive palette tools, see Linda McLennans article. Mike Scanlins article covers the BTMP resource type. It includes routines to retrieve these resources from disk and to draw them. He also shows you how to write a custom ResEdit editor to create and edit these resources directly within ResEdit.
Whats a Butmump?
The familiar ICON and SICN resources are nothing more than bit image data in pre-defined dimension formats. An ICON is a 32-bit by 32-bit bit image which is easily retrieved from disk using the GetICON() Toolbox routine, and drawn using PlotICON(). A SICN is a array of 16-bit by 16-bit image. The Macintosh Toolbox doesnt have any routines specific to this resource type, but most Macintosh developers have their own library routines in their bag of tricks for dealing with them.
So great, weve got 32 by 32, and 16 by 16 covered. What if we need to draw a bit image thats 36-bits wide and 24-bits tall? The common answer is to create a standard PICT resource and use GetPicture() and DrawPicture(). One drawback to this is the lack of flexibility in drawing modes. Many developers have implemented their small icon drawing routines with a copy mode parameter allowing them to opaquely (blast-all-them-bits) or transparently (overlay) draw a small icon, or to punch holes using a masking small icon. DrawPicture() has no such flexibility.
Another advantage with bit images has to do with the recent availability of routines to create regions directly from bit map images. Now available from Apple Computer as part of 32-Bit Color QuickDraw, or available separately in MPW object form through Apple Software Licensing, is a routine called BitMapRgn(). Ted Cohns article Covert PICTs to Regions, MacTutor, June 1989, provides sources to a routine called MakeRgn(). Both of these routines take a bit map and create a region from it. Very handy when you want to drag around a gray region based on some bit image graphic.
What we have to deal with are bit images of non-standard dimensions, and bit map records explaining just what we have in the way of our bit images. The bounds field of the bit map record readily tells us the exact bitwise dimensions of the bit image. The rowBytes field tells us the row width of the bit image, which must be an even number of bytes. (Therefore, the bit image data may have unused bits off to the right of each row of the actual image.)
The bit map record is a fixed size data structure, and contains an even number of bytes. We can conviently tack on the actual bytes of the bit image to the bit map record, creating the variable size BTMP data structure and resource type:
/* 1 */
typedef struct BTMP {
BitMap map;
short image[];
} BTMP, *BTMPPeek, **BTMPPkHndl;
typedef BitMap *BTMPPtr, **BTMPHndl;
The only field that is a little fuzzy is the baseAddr field of the bit map record. On disk this field has no meaning and is merely a place holder. Once in memory and locked down, this field is set to contain the actual beginning address of the following bit image.
Mike Scanlins article contains routines to get and draw these puppies. And because the first field is an actual BitMap record, no type coercion is necessary when passing a BTMP to a routine expecting a BitMap.
Writing SuperPaint Externals
Silicon Beach Software has come up with a neat method of making their products extendable. Both SuperPaint 2.0 and Digital Darkroom employ this technology which allows developers to add features to these products by writing some code and wrapping it in a file with a specified file type and a standard version identifying resource. The file is then placed in the same folder as the preferences file and voilá, the application suddenly has a new feature!
SuperPaint supports two types of plug-in modules, the term they use for their externals. One type adds custom painting tools to their painting tools palette. This type of tool is referred to as an interactive painting tool. The other type adds a command to the Paint menu and operates on the selected area in the current document. This type of tool is referred to as a menu command or transformation command tool. When the user puts a menu command plug-in in the folder that contains their preferences file, the command appears at the bottom of the Paint menu.
Figure 1. Plug-in menu commands appears at the bottom of the Paint menu in SuperPaint. The resource name of the plug-in menus code resource appears as the command name in the menu.
To keep things simple this article only deals with SuperPaint menu command tools. For an overview on plug-in modules and details on other types of tools see Linda McLennans article.
The Bare Essentials
You can create a plug-in tool in any development environment that allows you to create a stand-alone code resource (e.g. a definition proc such as a WDEF, CDEF, or LDEF, or a HyperCard or 4th Dimension external). A menu command tool is a file whose file type is BWtc. That stands for Black and White transformation command. Its creator type is up to you. If you use SPNT, SuperPaints creator type, it will show up on the Finder desktop with a standard plug-in icon provided by SuperPaint. You can give it your own creator type, and provide the other necessary resources (creator, BNDL, FREF, and ICN#) to give it your own unique desktop icon.
The plug-in must contain a plug-in module version information resource, and the code resource. The plug-in module version information resource is used for compatibility checking. It is a resource of type PiMI. Currently it is two bytes long and must contain the current version value of $0001 (or just plain ol decimal one). The code resource must be of type BWtc, the same as the file type. The resource name of the BWtc code resource will show up at the bottom of SuperPaints Paint menu, and in SuperPaints plug-in info list in the About Box.
The file may contain any other resources the tool requires, and all resources should be marked purgeable. When SuperPaint executes the plug-in code resource the plug-in file is set as the current resource file. The plug-in should do one-deep resource calls (e.g Get1Resource()) unless it is trying to get a resource from the System file.
Ive Got Your Number
What resource IDs should your resources have? Practically any that you want, as long a you follow the rules. Of course.
You should start numbering resources of each type within your plug-in from 16000, and you should try to have all resources of a type numbered consecutively. The range of 16000 to 32000, a subset of the range reserved for applications, is reserved by Silicon Beach for plug-ins. By starting at a base ID of 16000 you avoid conflicting with resources in SuperPaint and the System. Silicon Beach has eased up their original consecutive numbering limitation, but you should still try very hard to follow it as a guideline, and never have numbering gaps larger than you can survive with to preserve you coding sanity.
The reason for the original insistence on consecutive numbering had to do with potential renumbering of your plug-ins resources should they be merged into a file with other plug-ins, or moved into the SuperPaint application itself. Just as with desk accessories, a merging utility would have to renumber a plug-ins resources if their IDs collided with another plug-ins. (The implications of resource ID renumbering to your coding is discussed in the next section.)
The drawback to developers is this: say your plug-in has a dialog and an alert. You give the DLOG and its DITL resources IDs of 16000. You can give the ALRT resource an ID of 16000, but you cant give its DITL a resource of 16000 because that is already taken. While the Dialog Manager will have no trouble associating DITL 16001 with ALRT 16000 if you create the resources correctly, this type of odd-ball correspondence definitely flies in the face of creating easily readable source code and product maintenance.
After discussions with Silicon Beach about this annoying restriction, theyve decided that it can be eased up. Your PiMI and BWtc resources should still be numbered 16000. And as much as possible all other resource types should be numbered consecutively from a base ID 16000. You dont have to. But you should try as much as possible, keeping ID gaps as small as possible, so that if renumbering is done on your plug-ins resource they will take up as small an ID range as possible.
There is an exception that was not considered by Silicon Beach, and that is the Finders Get Info version resources that you may want to include in your plug-in. The vers resources, as explained in Macintosh Technical Note #189: Version Territory, must be ID number 1 and/or 2. As things are now this works fine for un-merged plug-ins. But what happens when plug-ins are merged?
I recommend that developers that provide vers resources do not have vers resources numbered with their base ID, or their base ID plus one. I recommend to Silicon Beach or others creating plug-in merge utilities that if they encounter vers 1 or 2 resources they be renumbered as if they had been originally numbered at the base ID or base ID plus one. When un-merging a plug-in so that it is in its own file, any vers resource at the base ID or base ID plus one be renumbered to ID 1 or 2.
Whats a BWtc Code Resource Look Like?
Very simply, a BWtc code resource looks to the caller (i.e. SuperPaint) like a function whose prototype definition is:
/* 2 */
pascal void main(short selector,
MenuDataPtr toolInfo, long *refCon,
short *returnCode);
The executable code must begin at the first byte of the BWtc resource which SuperPaint will load and lock down before it begins executing it. The resource ID of the BWtc resource is considered the plug-ins base ID. The resource name is the name that SuperPaint will display in the Paint menu.
Selector can be one of three commands: 0 == provide About Box information, 10 == set options, and 11 == command has been selected from menu (do your thing). The file SPMenu.h, supplied by Silicon Beach as part of their plug-in developers kit, contains the following symbolic definitions, as well as other equates and data structure definitions:
/* 3 */
#define menuAbout 0
#define menuOptions 10
#define menuSelected11
Before each call of selector == menuAbout or menuSelected, your plug-in will be called with the menuOptions selector. Currently the only use of this call is to allow the menu command to request a scratch buffer from SuperPaint. The toolInfo parameter will point to a menu options record:
/* 4 */
typedef struct {
BooleanusesScratch;
} MenuOptionsRec, *MenuOptionsPtr;
Set the usesScratch field to FALSE if you dont need SuperPaint to provide a scratch buffer for you tool.
When the user has requested information about your plug-in from SuperPaints About Box, it calls your plug-in with selector set to menuAbout. You can provide your own informative About Box using modal dialogs or alerts. If you do, you let SuperPaint know that youve handled the request by setting the VAR parameter *returnCode to 0. The toolInfo parameter is a pointer to an abbreviated menu command information record. The only currently used field is the very important toolID field. This field tells you what your current runtime base ID is.
If you have a TEXT resource for SuperPaint to display in its plug-in About Box set the VAR parameter *returnCode to textAbout (2). In this case, your informative TEXT resource must have the same resource ID as your BWtc code resource. If you have nothing to say, set *returnCode to noAbout (1).
Selector menuSelected is where we have our fun. ToolInfo is a pointer to a menu command information record:
!codeexamples
tart/* 5 */
typedef struct {
short toolID; /* tool rsrc ID */
short spare1; /* reserved */
short spare2; /* reserved */
short spare3; /* reserved */
short spare4; /* reserved */
short spare5; /* reserved */
short spare6; /* reserved */
short spare7; /* reserved */
BooleanshiftIsDown; /*shift key down*/
BooleancmdIsDown; /*command key down*/
BooleanoptIsDown; /*option key down*/
BooleanspareFlag; /* spare */
BooleanfillPatNone; /* flag to
indicate if current fill
pattern is none */
BooleanlinePatNone; /* flag to
indicate if current line
pattern is none */
MenuHandle subMenu; /* handle to
commands submenu */
short menuItem;/* number of
submenu item selected */
BitMap scratchBits; /* scratch buffer
bitmap */
BitMap lassoMaskBits; /* lasso mask
bitmap */
BitMap undoBits;/* undo buffer
bitmap */
Rect selectRect;/* selection
rectangle in document
coords */
PatterncurFillPat;/* current fill
pattern */
Handle curPatList;/* handle to
currently selected pattern
list */
} MenuDatarec, *MenuDataPtr;
Again, there is the all-important toolID field which contains our runtime base ID. The state of the users current drawing tools and palettes is given by several fields: fillPatNone, linePatNone, curFillPattern, curPatList.
When a plug-in is called the current grafPort is set up so that the current selections top left corner is at local coordinate (0,0). Also, the pnSize, pnMode, pnPat, txFont, txFace, and txSize fields of the grafPort reflect the users current tool selections.
And if you need to know just where in the document the selection is, the selectRect field of the menu command information record contains the location of the selection in document coordinates.
Inside BitMapper
BitMappers call chain looks like:
BitMapper (MPW only)
main
DoIt
OpenOutput
PutPICT
DoOutput
PutBTMP
CopyBits2Buffer
DoOutput
Main() is a dispatcher routine, and entirely handles the About Box information and set options calls.
The routine AlertErr() (not displayed in the call chain shown above) is very instructional. It contains an example of referencing one of our resources using the runtime base ID. Note how it calls NoteAlert() with a resource ID of: baseID + kALRT_Err, where baseID was passed in as a parameter, and was originally retrieved from the toolID field of the menu command information record. The constant kALRT_Err is defined in a header file as the resources offset from the base ID. And the Rez source file defines the ALRT resources ID as the compile time base ID plus the offset: kOrigBaseID + kALRT_Err.
Another point of interest is the usage of a local variable rather than attempting to use a global variable. Very important rule: NO GLOBALS! This means that you cannot access QuickDraw globals such as thePort, or Black (in ThinkC, in MPW C you cant use: qd.thePort or qd.Black). It also means you cant use any variables declared with the storage specifier static or external, nor can you declare any variable outside of a code block. You also cannot use strings embedded within your code, such as: DrawString(\pHello, world); because the string data is normally stored as global data. As the code in AlertErr() illustrates, even empty strings such as cant be used!
/* 6 */
unsigned char str0 = \0;
ParamText(s, &str0, &str0, &str0);
/* The following line would be illegal!!
ParamText(s, , , ); */
As a general development system point of interest that is not specific to creating plug-in tools, AlertErr() calls CenterWindow(), a routine found in the examples in the developers kit. This routine has been modified to be ambidextrous--it is compilable from either ThinkC or MPW C 3.0. The changes required had to do with globals and names of include files.
A call to GetMBarHeight() was substituted for the use of the low-memory global MBarHeight, and the usage of the low-memory global CurrentA5 requires conditional compiling because of the difference in how the two compilers treat globals. In ThinkC, usage of a low-memory global implies using the value at the low-memory location. In MPW C you must dereference after coercing the symbol to be a pointer to the appropriate data type. This difference was also the reason for using the function call to get the height of the menu bar. (Note that usage of the menu bar height global is System dependent. Dont try this at home (or anywhere for that matter) on 128K Macs.)
Also, sizeof(thePort) was changed to sizeof(GrafPtr) so we dont have to conditionally code it as sizeof(thePort) or sizeof(qd.thePort).
Outside of that, there probably isnt all that much to elaborate on in the first example that isnt spelled out in the examples inline documentation. The stuff that does the grunt-level plug-in environment-specific work is in PutBTMP(), and is summarized by its documentation:
/* 7 */
short PutBTMP(TBitMapperPtr pMyInfo)
Makes a BTMP resource from the selection area our command is working with, then outputs the resource. PMyInfo is a pointer to a BitMapper record containing information about the currently executing command. This includes a grafPtr to the document port (which is also the current port).
Before calling us, SuperPaint has already set the ports portRect to be equivalent to the selection bounding rectangle in a coordinate system local to the selection. That is, portRect.topLeft is (0,0), portRect.bottom is equal to the height of the selection area, and portRect.right is equal to the width of the selection area.
The code itself simply does a bit copy from the selection area, which has been set up by SuperPaint as the current grafPorts portRect, to a private buffer which we send to the clipboard. What is really beautiful is that clipping is handled automatically if the selection is not rectangular! So, for many operations you dont need to mask to the selection area. If you do need the selection area, the lassoBits field of the menu command record contains a BitMap which specifies the actual selection area. (An oversight is that this field only applies to non-rectangular selections made using the lasso tool. It does not contain a valid BitMap if the selection was made with any other selection tool.)
The transformation command plug-in tools were designed primarily to allow programmers a way to access the bits of the selection area, and to modify them. A really simple example would be to create your own invert command. Simply calling InvertRect(&curGptr->portRect), where curGptr is the current grafPtr, will invert the selected area in the document. It can also be used to import images. And, as this example has shown, it can be used to export images as.
Menu Commands with Sub-Menus
It is also possible for menu commands to contain their own hierarchical sub-menus. A second example, based on the first, illustrates this. Mike Scanlin in his article discusses BTM# resources, which, consists of a list of BTMP resources. This second example creates a menu command that allows us to create either a BTMP or a BTM# resource from the SuperPaint selection.
Figure 2. The BitMapper menu command with a hierarchical sub-menu. The name of the BWtc resource is still used as the command name in the Paint menu. The sub-menu is as specified in BitMappers MENU resource, whose resource ID is the same as the BWtc resource.
There are only two additional things that you need to know to create a menu command with a sub-menu. The first involves creating the MENU resource, the second involves knowing which menu item was chosen by the user at execution time.
The resource ID of the MENU resource must be the same as the BWtc resource. And, as with other resources in the plug-in, it should be marked purgeable. Purgeable? What about the warning in Inside Macintosh, Volume IV, The Menu Manager: Menu resources should never be marked purgeable? Well, because the resource file of a plug-in isnt open unless it is actually executing, SuperPaint must load and detach a sub-menus resource, so at the same time it marks the detached menu resource as non-purgeable.
As for the menu ID of the sub-menu, Apple has specified that sub-menus from applications should use a menu ID in the range of 0-235. SuperPaint restricts sub-menus of plug-in to the range of 200-235, but it also will dynamically change the menu ID as necessary to avoid conflicts with other plug-ins.
At execution time, if your plug-in has a sub-menu, you merely need to check the menuItem field of the menu command data record. In the second example, this is done in the routine DoIt(), which branches accordingly.
A Little Bonus
The BitMapper examples were culled from my SuperPaint plug-in Resourceror, which is available on the MacTutor sources disk in executable (only) form. Resourceror creates ICON, ICN#, SICN, and CURS resources from SuperPaint, as well as BTMP and BTM# resources.
Figure 3. Resourcerors sub-menu. The user can can select any combination of resources to create from the middle section of the sub-menu. The user can also elect to send the resources to either the Clipboard or directly to a file by toggling the choices in the bottom section of the file. The Create command at the top executes the resource creation.
Resourceror shows an example of remembering user preference settings from the menu commands sub-menu. The user can toggle any combination of resources to create in the sub-menu. The resources arent actually created until the Create command is chosen, but the settings are saved to disk--that is, they are sticky.
With Resourceror, the user can also choose to write the resources directly to a resource file rather than to the clipboard by toggling the To File item at the bottom of the sub-menu. Doing this also adds ellipsis to the Create command, indicating, as per Apple Computers human interface guidelines, that a dialog will be brought up to complete the command.
Within the Standard GetFile dialog, there are more instructive changes. A prompt (sensitive to whether one or more resource types are being created) has been added to the Standard GetFile dialog, the Open button has been changed to a Save button, a default item hilite border has been added to the Save button, and this hilite is activated or deactivated depending on whether the selection is a file that can be saved to. Files whose resource fork is already open cannot be saved to, and a warning at the bottom of the dialog box indicates this when appropriate. Finally, a New button was added to the dialog that takes the user to a Standard PutFile dialog.
When the resources are saved to the specified file, they are all given the same unique resource ID. This ID is not user selectable, but it is unique, and all resources created with the same command are given the same ID.
Bibliography
Inside Macintosh, Addison-Wesley, 1985.
Macintosh Technical Note #189: Version Territory, Apple Computer, Inc. 1989.
Macintosh Technical Notes #193: So many BitMaps, so little time, Apple Computer, Inc. 1989.
Convert PICTs to Regions, Ted Cohn, MacTutor June 1989.
Plug-in Module Developers Toolkit, Silicon Beach Software, 1989.
Figure 4. Setting the Project Type information in ThinkC. The project must be single segment and shouldnt use QuickDraw globals.
#===========================================#
#file: Makefile
#date: 11.13.89
#===========================================#
#Makefile for the BitMapper SuperPaint
#plug-in tool. All objects are built
#into an ohs folder within the
#BitMapper source folder.
#===========================================#
#Copyright © 1989, Michael Ogawa and
#MacTutor -- All Rights Reserved.
#===========================================#
# Directories
OHSF = :ohs: # objects folders
COptions= -r -mbg ch8
# Plug-in creator signature, filetype,
# and base resource ID.
CREATOR = SPNT
FILETYPE= BWtc
ORIG_BASE_ID= 16000
# All object files
OBJS = {OHSF}BitMapper_main.c.o
{OHSF}CenterWindow.c.o
# Default dependency rule:
# map source directory to objects folder
{OHSF} :
# Default dependency rule:
# map C sources to objects
.c.o .c
C {Default}.c {COptions}
-o {OHSF}{Default}.c.o
# BitMapper file object/source dependencies
BitMapper_main.c.includes =
BitMapper.h
BitMapper_goodies.h
BitMapperRsrc.h
CenterWindowIntf.h
SPMenu.h
{OHSF}BitMapper_main.c.o
{BitMapper_main.c.includes}
CenterWindow.c.includes =
CenterWindowIntf.h
qdGlobals.h
{OHSF}CenterWindow.c.o
{CenterWindow.c.includes}
# Plug-in tool executable/objects
# dependencies
BitMapper {OBJS}
Link -c {CREATOR} -t {FILETYPE}
# code rsrcs type & ID
-rt {FILETYPE}={ORIG_BASE_ID}
# name of entry point routine
-m BITMAPPER
# code rsrc name as we want it
# to appear in the Paint menu
-sn Main=BitMapper
# progress info, just to be sure
-p
{OBJS}
{CLibraries}CRuntime.o
{Libraries}Interface.o
-o BitMapper
# Plug-in tool executable/resources
# dependencies
BitMapper.r.includes = BitMapperRsrc.h
BitMapper BitMapper.r
{BitMapper.r.includes}
Rez BitMapper.r {ROptions} -a
-o BitMapper
# check resources, just to be sure
RezDet -l BitMapper
# Duplicate BitMapper {SP_POUCH}
/* ======================================= *
file: BitMapper.h
date: 10.01.89
* -------------------------------------- *
Main header file for the BitMapper
SuperPaint plug-in menu command.
* -------------------------------------- *
Copyright © 1989, Michael Ogawa and
MacTutor -- All Rights Reserved.
* ======================================= */
#ifndef _H_BitMapper
#define _H_BitMapper
#ifdef applec
#include <Types.h>
#include <Menus.h>
#include <Dialogs.h>
#endif
#include BitMapper_goodies.h
#include BitMapperRsrc.h
#include SPMenu.h
#include CenterWindowIntf.h
/* data types ============================ */
typedef struct {
MenuDataPtrpToolInfo;
long *pRefCon;
GrafPtrdrawGptr;
} TBitMapperRec, *TBitMapperPtr;
/* The BitMapper record is used to pass all
pertinent information for our command in one
data structure. PToolInfo is a pointer to
the menu data structure that was passed in by
SuperPaint when we were called. PRefCon is
a pointer to the refCon word passed in, and
that we can modify and use as we desire.
(BitMapper does not use the refCon.)
DrawGptr is the current grafPort set up by
SuperPaint that contains the selection our
command works on. */
/* m_o 09.17.89 */
/* function prototypes =================== */
#ifdef applec
extern pascal void BitMapper(short selector,
MenuDataPtr toolInfo, long *refCon,
short *returnCode);
#endif applec
extern pascal void main(short selector,
MenuDataPtr toolInfo, long *refCon,
short *returnCode);
#endif _H_BitMapper
/* ======================================= *
file: BitMapperRsrc.h
date: 11.17.89
* -------------------------------------- *
Header file for C and Rez for the
BitMapper SuperPaint plug-in tool.
Contains symbolic constants, data type
and resource type definitions that are
common to both the C and Rez components
of the project. Utilizes the Rez
compiler symbol, REZ, to conditionally
include/exclude definition lines as
necessary.
* -------------------------------------- *
Copyright © 1989, Michael Ogawa and
MacTutor -- All Rights Reserved.
* ======================================= */
#ifndef _H_BitMapperRsrc
#define _H_BitMapperRsrc
#ifdef REZ
#ifndef bold
#include Types.r
#include SysTypes.r
#endif bold
#endif REZ
/* constants ============================= */
#define kOrigBaseID16000
/* All resources of plug-in tools should be
numbered consecutively from a base resource
ID. This is the ID# we are using as our base
ID. */
/* m_o 09.13.89 */
#define kType_BitMap BTMP
/* Resource type of our custom bitmap and
image resource that we create with
BitMapper. */
/* m_o 09.13.89 */
#define kALRT_Err0 /* + baseID */
#define kDITL_ErrkALRT_Err/* + baseID */
#define kactb_ErrkALRT_Err/* + baseID */
/* ID offsets for the error alert
resources, based off of the base resource ID.
The resources are Rez compiled with these
resource IDs plus kOrigBaseID. They are
calculated at runtime based on the value in
the toolID field of the menu command data
record that is passed in to our tool. */
/* m_o 09.17.89 */
/* data types ============================ */
#ifndef REZ
typedef struct {
BitMap map;
short image[];
} TBTMP, *TBTMPPeek, **TBTMPPkHndl;
typedef BitMap *TBTMPPtr, **TBTMPHndl;
#else
type kType_BitMap {
unsigned longint = 0; /* map.baseAddr */
integer; /* map.rowBytes */
rect; /* map.bounds */
hex string;/* image */
};
#endif REZ
/* Data structure for our custom bitmap and
image resource type. */
/* mps 06.22.89/m_o 06.25.89 */
#endif _H_BitMapperRsrc
/* ======================================= *
file: BitMapper_goodies.h
date: 11.17.89
* -------------------------------------- *
Copyright © 1989, Michael Ogawa -- All
Rights Reserved.
* ======================================= */
#ifndef _H_BitMapper_goodies
#define _H_BitMapper_goodies
#ifdef applec
#include <Types.h>
#include <Memory.h>
#include <Errors.h>
#endif applec
#ifdef THINK_C
/* #define qDEBUG */
#endif THINK_C
/* constants ============================= */
#ifndef NULL/* also defined in stdio.h */
#define NULL0L
#endif
#define kBitsPerByte 8
#define kBytesPerWord2
#define kBitsPerWord \
(kBitsPerByte * kBytesPerWord)
#define kWordsPerLWord 2
#define kBytesPerLWord \
(kBytesPerWord * kWordsPerLWord)
#define kBitsPerLWord\
(kBitsPerByte * kBytesLPerWord)
/* Generally useful equates of basic data
element sizes. One byte consists of eight
bits (kBitsPerByte), one word consists of two
bytes (kBytesPerWord), and one long word
consists of two words (kWordsPerLWord) or
four bytes (kBytesPerLWord). There are 16
bits in a word (kBitsPerWord), and 32 bits in
a long word (kBitsPerLWord). */
/* m_o 09.17.89 */
/* data types ============================ */
#ifdef THINK_C
#define const /* not yet implemented */
#endif THINK_C
/* macro-functions ===================== */
#define WIDTH(box) \
(((box).right) - ((box).left))
#define HEIGHT(box)\
(((box).bottom) - ((box).top))
/* These macros return the width or height
of the specified rectangle. */
/* m_o 07.17.89 */
#define BITS2ROWBYTES(nBits)\
((((nBits) - 1) / kBitsPerWord + 1) \
* kBytesPerWord)
/* Converts the number of bits nBits to its
equivalent number of rowBytes, making sure to
account for the fact that rowBytes must be even. */
/* mps 07.17.89/m_o 09.17.89 */
#endif _H_BitMapper_goodies
/* ======================================= *
file: BitMapper_main.c
date: 11.13.89
* -------------------------------------- *
Copyright © 1989, Michael Ogawa and
MacTutor -- All Rights Reserved.
* ======================================= */
/* #include <MacHeaders> */
#include BitMapper.h
#ifdef applec
#include <Desk.h>
#include <Scrap.h>
#include <Packages.h>
typedef char SignedByte;
#endif applec
/* Code resource entry point (MPW) ======= */
#ifdef applec
pascal void BitMapper(short selector,
MenuDataPtr toolInfo, long *refCon,
short *returnCode)
/* Code resource entry point for a
SuperPaint transformation command plug-in
tool built in MPW C. This avoids having to
forward reference all the internal
routines. */
/* m_o 06.23.89 */
{
main(selector, toolInfo, refCon,
returnCode);
}
#endif applec
/* private functions ===================== */
static short OpenOutput(TBitMapperPtr pMyInfo)
/* Prepares the appropriate output method.
PMyInfo is a pointer to a BitMapper record
containing information about the currently
executing command.
This version of OpenOutput() simply
prepares output to the desk scrap. The
clipboard is cleared, and as a workaround for
MultiFinders selective updating of other
partitions ScrapCount low-memory variable,
SystemEdit() is called with the copy message.
(See Macintosh Technical Note #180:
MultiFinder Miscellanea for details.)
If no error occured then the result code
noErr is returned as the function return
value; otherwise an appropriate Operating
System result code is returned as the
function return value. */
/* m_o 11.13.89 */
{
#pragma unused(pMyInfo)
/* MultiFinder work-around: notify
System of copy operation */
(void)SystemEdit(3);
/* clear the desk scrap */
return(ZeroScrap());
}
static short DoOutput(Handle hData,
ResType rType, TBitMapperPtr pMyInfo)
/* Outputs the data in the relocatable
block specified by the handle hData. The
data output format is specified by rType.
PMyInfo is a pointer to a BitMapper record
containing information about currently
executing command.
This version of DoOutput() simply sends
the data to the clipboard using the Toolbox
routine PutScrap(), specifying rType as the
Desk scrap data type. Note that it does not
call ZeroScrap(), so the output is added to
the current scrap content as an additional
type. This is not good if the scrap already
contains a resource of the specified type.
Therefore, ZeroScrap() should be called once
before outputting any data to the scrap.
If no error occured then the result code
noErr is returned as the function return
value; otherwise an appropriate Operating
System result code is returned as the
function return value. */
/* m_o 11.13.89 */
{
#pragma unused(pMyInfo)
SignedByte savedMPFlags;
short result;
/* get master pointer flags and lock down */
savedMPFlags = HGetState(hData);
MoveHHi(hData);
HLock(hData);
result = PutScrap(GetHandleSize(hData), rType, *hData);
/* restore master pointer state */
HSetState(hData, savedMPFlags);
return(result);
}
static void CopyBits2Buffer(
const BitMap *pSrcBMap,
const Rect *pSrcRect, Ptr pBuffer,
short bufferRowBytes, short mode)
/* Transfers a bit image specified by the
bit map *pSrcMap to the image buffer
pBuffer[]. The image buffer must begin on an
even address. The width, in bytes, of the
destination buffer is specified by
bufferRowBytes, which must also be even.
The bits enclosed by the source
rectangle *pSrcRect are transferred to a
similar sized rectangle in the destination
image. The destination rectangle is the same
size as the source rectangle and is aligned
at the top left corner of the destination bit
image. The destination image buffer must be
big enough to accomodate the entire image
being transferred. The transfer mode is
specified by the mode parameter. */
/* m_o 06.22.89 */
{
BitMap dstBMap;
dstBMap.baseAddr = pBuffer;
dstBMap.rowBytes = bufferRowBytes;
dstBMap.bounds = *pSrcRect;
CopyBits(pSrcBMap, &dstBMap, pSrcRect,
pSrcRect, mode, NULL);
}
static short PutPICT(TBitMapperPtr pMyInfo)
/* Makes a picture that is a copy of the
selection area our command is working with,
then outputs the picture. PMyInfo is a
pointer to a BitMapper record containing
information about currently executing
command. This includes a grafPtr to the
document port (which is also the current
port).
Before calling us, SuperPaint has
already set the ports portRect to be
equivalent to the selection bounding
rectangle in a coordinate system local to the
selection. That is, portRect.topLeft is
(0,0), portRect.bottom is equal to the height
of the selection area, and portRect.right is
equal to the width of the selection area.
If no error occured then the result code
noErr is returned as the function return
value; otherwise an appropriate Operating
System result code is returned as the
function return value. */
/* m_o 09.13.89 */
{
short result;
PicHandle hMyPic;
if (hMyPic = OpenPicture(
&pMyInfo->drawGptr->portRect)) {
/* copy selection */
CopyBits(
&pMyInfo->drawGptr->portBits,
&pMyInfo->drawGptr->portBits,
&pMyInfo->drawGptr->portRect,
&pMyInfo->drawGptr->portRect,
srcCopy, NULL);
ClosePicture();
result = DoOutput((Handle)hMyPic,
PICT, pMyInfo);
KillPicture(hMyPic);
} else
result = MemError();
return(result);
}
static short PutBTMP(TBitMapperPtr pMyInfo)
/* Makes a BTMP resource from the
selection area our command is working with,
then outputs the resource. PMyInfo is a
pointer to a BitMapper record containing
information about the currently executing
command. This includes a grafPtr to the
document port (which is also the current
port).
Before calling us, SuperPaint has
already set the ports portRect to be
equivalent to the selection bounding
rectangle in a coordinate system local to the
selection. That is, portRect.topLeft is
(0,0), portRect.bottom is equal to the height
of the selection area, and portRect.right is
equal to the width of the selection area.
If no error occured then the result code
noErr is returned as the function return
value; otherwise an appropriate Operating
System result code is returned as the
function return value. */
/* m_o 09.17.89 */
{
long sz;
short result;
BitMap myMap;
TBTMPPkHndl hMyBTMP;
/* set up BitMap: init baseAddr */
myMap.baseAddr = NULL;
/* get bounds rectangle */
myMap.bounds = pMyInfo->drawGptr->portRect;
/* calc rowBytes */
myMap.rowBytes =
BITS2ROWBYTES(WIDTH(myMap.bounds));
/* calc size of BTMP resource */
sz = sizeof(TBTMP) +
myMap.bounds.bottom *
myMap.rowBytes;
/* get clean memory for new BTMP resource */
if (!(hMyBTMP =
(TBTMPPkHndl)NewHandleClear(sz)))
return(MemError());
/* copy BitMap */
(**hMyBTMP).map = myMap;
/* copy bit image */
MoveHHi((Handle)hMyBTMP);
HLock((Handle)hMyBTMP);
CopyBits2Buffer(
&pMyInfo->drawGptr->portBits,
&myMap.bounds,
(Ptr)&(**hMyBTMP).image,
myMap.rowBytes, srcCopy);
HUnlock((Handle)hMyBTMP);
/* ship it! */
result = DoOutput((Handle)hMyBTMP, BTMP, pMyInfo);
/* clean up and leave */
DisposHandle((Handle)hMyBTMP);
return(result);
}
static short DoIt(TBitMapperPtr pMyInfo)
/* Makes a BTMP resource and a picture
from the selection area our command is
working with, then outputs the resources.
PMyInfo is a pointer to a BitMapper record
containing information about currently
executing command. All its fields should be
filled in on entry, except the drawGptr field
which this routine fills in. (The drawGptr
field is a grafPtr to the document port,
which is also the current port.)
If no error occured then the result code
noErr is returned as the function return
value; otherwise an appropriate Operating
System result code is returned as the
function return value. */
/* m_o 08.29.89 */
{
short result;
GetPort(&pMyInfo->drawGptr);
if ((result = OpenOutput(pMyInfo)) == noErr) {
short result2;
result = PutBTMP(pMyInfo);
if ((result2 = PutPICT(pMyInfo)) !=
noErr && result == noErr)
result = result2;
}
return(result);
}
static void AlertErr(short err, short baseID)
/* Puts up an alert telling the user an
error occured. Err is the result code of
the error. This is converted to a string
and substituted for param text ^0 in the
alert.
BaseID is the resource ID of the first
(numerically lowest) resource in our plug-in
tool. Because resource IDs may be
reassigned, we calculate our alerts current
resource ID based on this. */
/* m_o 09.30.89 */
{
Str255 s;
unsigned char str0 = \0;
/* Convert error ID to string format,
put into param text ^0. Note usage
of str0 because we cant have any
globals. Not even empty strings! */
NumToString(err, s);
ParamText(s, &str0, &str0, &str0);
/* The following line would be illegal!!
ParamText(s, , , ); */
CenterWindow(ALRT, baseID + kALRT_Err);
/* make sure cursor is arrow */
InitCursor();
NoteAlert(baseID + kALRT_Err, NULL);
}
/* entry points ========================== */
pascal void main(short selector,
MenuDataPtr toolInfo, long *refCon,
short *returnCode)
/* Entry point and command dispatcher for
the BitMapper plug-in command tool. Selector
is a call type code as defined by Silicon
Beach Software in the Menu Command Interface
section of their plug-in documentation
Developing Plug-In Modules for SuperPaint.
*/
/* m_o 08.29.89 */
{
TBitMapperRec myInfo;
switch (selector) {
case menuAbout:
/* tell SuperPaint we have TEXT
resource for About Box info */
*returnCode = textAbout;
break;
case menuOptions:
/* tell SuperPaint we dont need
a scratch buffer */
((MenuOptionsPtr)toolInfo)->usesScratch = false;
*returnCode = noErr;
break;
case menuSelected:
/* setup private data record and do it! */
myInfo.pToolInfo = toolInfo;
myInfo.pRefCon = refCon;
*returnCode = DoIt(&myInfo);
break;
}
/* alert user on errors */
if (*returnCode != noErr && selector != menuAbout)
AlertErr(*returnCode, toolInfo->toolID);
return;
}
/* ======================================= *
file: BitMapper.r
date: 11.17.89
* -------------------------------------- *
Resources for the BitMapper SuperPaint
plug-in menu command.
* -------------------------------------- *
Copyright © 1989, Michael Ogawa and
MacTutor -- All Rights Reserved.
* ======================================= */
#include BitMapperRsrc.h
data PiMI (kOrigBaseID, purgeable)
/* Plug-in Module Information specifying
version 1 interface. */
/* m_o 09.13.89 */
{
$00 01"
};
resource ALRT (kOrigBaseID + kALRT_Err,
errAlrt, purgeable)
/* Alert template for the error alert. The
window height and width are based on the
actual items in the dialogs. The top
coordinate is arbitrary. The left coordinate
is calculated by: (512 - width) / 2. The
actual desktop position should be calculated
at runtime depending on the actual desktop
size/configuration. */
/* m_o 09.30.89 */
{/*148*/
{40, 0, 40+92+8, 0+208+8},
kOrigBaseID + kDITL_Err,
{ /* array: 4 elements */
OK, visible, sound1,/* [1] */
OK, visible, sound1,/* [2] */
OK, visible, sound1,/* [3] */
OK, visible, sound1 /* [4] */
}
};
resource DITL (kOrigBaseID + kDITL_Err,
errAlrt, purgeable)
/* Dialog item list for the error alert.
The param text ^0 in the prompt text should
be filled in with the error code or some
other message at runtime. */
/* m_o 09.30.89 */
{
{ /* array DITLarray: 2 elements */
/* [1] */
{72, 8, 92, 78}, Button
{enabled, Darn! },
/* [2] */
{8, 64, 56, 208}, StaticText
{disabled,
Oops! Something went
wrong. Sorry about that.
(^0)
}
}
};
resource actb (kOrigBaseID + kactb_Err,
errAlrt, purgeable)
/* This is a default color table for the
error alert */
/* m_o 09.30.89 */
{
0x0,
0,
{}/* array ColorSpec: 0 elements */
};
data TEXT (kOrigBaseID, about, purgeable)
/* Provides information about BitMapper to
the user from within SuperPaints About Box.
Utilizes the constants kCopyright,
kVersionStr, and kAnnotate. */
/* m_o 09.30.89 */
{
BitMapper takes snapshots of your
active SuperPaint document window and
turns them into BTMP resources
which it places on the clipboard.
It also places a PICT copy of the
selection on the clipboard so that
you can see what the BTMP looks
like.\n
The BTMP resource type consists
of a BitMap data structure followed
by a bit image.
The baseAddr field of the saved
resource is set to Nil.
The bounds field is set to a
rectangle the size of the selection
rectangle with its top left corner
set to (0,0).
RowBytes is the actual width of the
selection (in bytes), rounded up
to an even width if necessary.
The actual bit image then follows.\n
For details on using BTMP
resources, see Mike Scanlins article
in MacTutor magazine.\n
Please send comments to: Michael
Ogawa, 619-224-3058, 619-721-7000
(Palomar Software), or AppleLink
M.O.\n\n
BitMapper by \n
Michael Ogawa\n
Special thanks to \n
Dana Gregory\n
Linda McClennan\n
Mike Scanlin\n\n
Copyright © 1989 Michael Ogawa -- All
Rights Reserved.\n
May not be redistributed for
commercial purposes. May be freely
distributed on electronic bulletin
boards provided no additional charges
above the boards standard connect
charges are imposed. May be freely
distributed by non-profit
organizations on disk provided that
any charges for the distribution disk
does not exceed actual disk,
labelling materials, reproduction,
and shipping charges incurred by the
organization.\n
This software is provided as is,
with no warranties, either express or
implied, being made regarding its
fitness for any particular purpose.\n
v1.00
};
resource vers (1, purgeable)
/* Finder get info resource providing
version information about our plug-in menu
command to the user. */
/* m_o 11.13.89 */
{
0x01,
0x00,
release,
0x00,
verUs,
1.00,
1.00 (US), © 1989 Michael Ogawa
};
/* ======================================= *
To create the second example with a hierarchical sub-menu, make the following changes:
Add the following definitions to the end of the constants section in BitMapperRsrc.h:
#define kMENU_BitMapper 200
#ifndef REZ
enum {
kMITEM_BTMP= 1, /* BTMP */
kMITEM_BTMN/* BTM# */
};
#endif REZ
/* Menu ID and item numbers for our
sub-menu. Menu IDs in the range of 0-235
are reserved for applications to use for
sub-menus, SuperPaint reserves the sub-range
of 200-235 for plug-ins. */
/* m_o 11.13.89 */
Add the following definitions to the end of the data types section in BitMapperRsrc.h:
#ifndef REZ
typedef struct {
short szEntry;
TBTMP theBTMP;
} TBTMNEntry, *TBTMNEntryPtr;
#endif REZ
/* An entry in the BTM# resource data
type consists of a field containing the size
of the entry in bytes, followed by the BTMP
resource data.
Note that you may want to change the
szEntry field to type Size to deal with very
large bit images. */
/* mps 06.22.89/m_o 11.17.89 */
#ifndef REZ
typedef struct {
short count;
TBTMNEntry theBTMNEntries[];
} *TBMPN, *TBTMNPtr, **TBTMNHndl;
#endif REZ
/* The BTM# resource data type consists
of a field indicating the number of entries
in the resource, followed by the entries. */
/* mps 06.22.89/m_o 11.13.89 */
Change the function DoIt() in BitMapper_main.c to:
static short DoIt(TBitMapperPtr pMyInfo)
/* Makes a BTMP or BTM# resource and a
picture from the selection area our command
is working with, then outputs the resources.
PMyInfo is a pointer to a BitMapper record
containing information about currently
executing command. All its fields should be
filled in on entry, except the drawGptr field
which this routine fills in. (The drawGptr
field is a grafPtr to the document port,
which is also the current port.)
The menuItem field of the menu command
data record specified by the BitMapper record
specifies whether to create a BTMP or BTM#
resource.
If no error occured then the result code
noErr is returned as the function return
value; otherwise an appropriate Operating
System result code is returned as the
function return value. */
/* m_o 11.17.89 */
{
short result;
GetPort(&pMyInfo->drawGptr);
if ((result = OpenOutput(pMyInfo)) == noErr) {
short result2;
result =
(pMyInfo->pToolInfo->menuItem
== kMITEM_BTMP) ?
PutBTMP(pMyInfo) :
PutBTMN(pMyInfo);
if ((result2 = PutPICT(pMyInfo)) !=
noErr && result == noErr)
result = result2;
}
return(result);
}
Add the function PutBTMN() between PutBTMP() and DoIt() in BitMapper_main.c:
static short PutBTMN(TBitMapperPtr pMyInfo)
/* Makes a BTM# resource from the
selection area our command is working with,
then outputs the resource. PMyInfo is a
pointer to a BitMapper record containing
information about the currently executing
command. This includes a grafPtr to the
document port (which is also the current
port).
Before calling us, SuperPaint has
already set the ports portRect to be
equivalent to the selection bounding
rectangle in a coordinate system local to the
selection. That is, portRect.topLeft is
(0,0), portRect.bottom is equal to the height
of the selection area, and portRect.right is
equal to the width of the selection area.
If no error occured then the result code
noErr is returned as the function return
value; otherwise an appropriate Operating
System result code is returned as the
function return value. */
/* m_o 11.17.89 */
{
long sz;
short result;
BitMap myMap;
TBTMNHndl hMyBTMN;
TBTMNEntryPtr pMyBTMNEntry;
/* set up BitMap: init baseAddr */
myMap.baseAddr = NULL;
/* get bounds rectangle */
myMap.bounds =
pMyInfo->drawGptr->portRect;
/* calc rowBytes */
myMap.rowBytes =
BITS2ROWBYTES(WIDTH(myMap.bounds));
/* calc size of BTMP resource */
sz = sizeof(TBTMP) +
myMap.bounds.bottom *
myMap.rowBytes;
/* get clean memory for new
BTM# resource */
if (!(hMyBTMN =
(TBTMNHndl)NewHandleClear(sz +
sizeof((**hMyBTMN).count) +
sizeof((**hMyBTMN).
theBTMNEntries[0].szEntry))))
return(MemError());
/* number of BTMP entries */
(**hMyBTMN).count = 1;
/* lock, get pointer to entry */
MoveHHi((Handle)hMyBTMN);
HLock((Handle)hMyBTMN);
pMyBTMNEntry =
(**hMyBTMN).theBTMNEntries;
/* size of the BTM# entry */
pMyBTMNEntry->szEntry =
sizeof(pMyBTMNEntry->szEntry) + sz;
/* copy BitMap */
pMyBTMNEntry->theBTMP.map = myMap;
/* copy bit image */
CopyBits2Buffer(
&pMyInfo->drawGptr->portBits,
&myMap.bounds,
(Ptr)pMyBTMNEntry->theBTMP.image,
myMap.rowBytes, srcCopy);
HUnlock((Handle)hMyBTMN);
/* ship it! */
result = DoOutput((Handle)hMyBTMN,
BTM#, pMyInfo);
/* clean up and leave */
DisposHandle((Handle)hMyBTMN);
return(result);
}
Add the following resource to BitMapper.r:
resource MENU (16000, purgeable)
/* Hierarchical sub-menu. */
/* m_o 11.17.89 */
{
kMENU_BitMapper,
textMenuProc,
allEnabled,
enabled,
BitMapper,
{ /* array: 2 elements */
/* [1] kMITEM_BTMP */
BTMP, noIcon, noKey, noMark,
plain,
/* [2] kMITEM_BTMN */
BTM#, noIcon, noKey, noMark,
plain
}
};
Change the TEXT and vers resources in BitMapper.r to:
data TEXT (kOrigBaseID, about, purgeable)
/* Provides information about BitMapper to
the user from within SuperPaints About Box.
Utilizes the constants kCopyright,
kVersionStr, and kAnnotate. */
/* m_o 09.30.89 */
{
BitMapper takes snapshots of your
active SuperPaint document window and
turns them into BTMP or BTM#
resources which it places on the
clipboard. It also places a PICT
copy of the selection on the
clipboard so that you can see what
the BTMP or BTM# looks like.\n
The BTMP resource type consists
of a BitMap data structure followed
by a bit image.
The baseAddr field of the saved
resource is set to Nil.
The bounds field is set to a
rectangle the size of the selection
rectangle with its top left corner
set to (0,0).
RowBytes is the actual width of the
selection (in bytes), rounded up
to an even width if necessary.
The actual bit image then follows.\n
The BTM# resource type consists
of a list of BTMP resources.
It begins with an INTEGER count field
indicating how many BTMP entries
are in the resource.
Then, each entry consists of an
INTEGER szEntry field and a BTMP.
The szEntry field gives the size, in
bytes, of the entry.
That is, the size of the BTMP
resource for the entry plus two (the
size of the INTEGER szEntry field).
This makes it easy to quickly walk
through the BTM# to get to the
indexed BTMP entry that you are
interested in.\n
For details on using BTMP and
BTM# resources, see Mike Scanlins
article in MacTutor magazine.\n
Please send comments to: Michael
Ogawa, 619-224-3058, 619-721-7000
(Palomar Software), or AppleLink
M.O.\n\n
BitMapper by \n
Michael Ogawa\n
Special thanks to \n
Dana Gregory\n
Linda McClennan\n
Mike Scanlin\n\n
Copyright © 1989 Michael Ogawa -- All
Rights Reserved.\n
May not be redistributed for
commercial purposes. May be freely
distributed on electronic bulletin
boards provided no additional charges
above the boards standard connect
charges are imposed. May be freely
distributed by non-profit
organizations on disk provided that
any charges for the distribution disk
does not exceed actual disk,
labelling materials, reproduction,
and shipping charges incurred by the
organization.\n
This software is provided as is,
with no warranties, either express or
implied, being made regarding its
fitness for any particular purpose.\n
v2.00
};
resource vers (1, purgeable)
/* Finder get info resource providing
version information about our plug-in menu
command to the user. */
/* m_o 11.13.89 */
{
0x02,
0x00,
release,
0x00,
verUs,
2.00,
2.00 (US), © 1989 Michael Ogawa
};