PictPack
Volume Number: 9
Issue Number: 5
Column Tag: 4th Dimension
Related Info: Picture Utilities
PictPack - A Package of 4D Externals
Managing picture variables in 4D Externals
By Kent Miller, Arlington, Texas
Note: Source code files accompanying article are located on MacTech CD-ROM or
source code disks.
About the author
Kent Miller is the author of 4D Balloon Help and 4D Chooser, both published by
Business Network of Oklahoma City. His Email addresses are KPMiller@aol.com or
KPMILLER on America Online.
In this article, I will develop a small 4D package that reads PICT files into
picture variables, writes picture variables to PICT files, and returns information
about 4D picture variables. I will also attempt to illustrate three things:
• How to read and write a PICT file
• How to free up extra memory for your 4D external
• Using a 4D EntryPoint to put a PicHandle into a picture variable
PicHandles and PICT files contain the exact same information except a PICT file
has 512 bytes of information at the beginning (to hold information such as the name of
the creating program and copyright information). So, if you want to read a PICT, you
simply skip the first 512 bytes of the file and then read the rest into a PicHandle. To
write one, you just write 512 bytes of something and then write a PicHandle.
The ReadPict Procedure
The ReadPict external takes two parameters, the name of the picture variable to
put the picture in and an integer that returns an error. The procedure prompts the
user to select a file. When a file is chosen, it reads the picture if it can free up enough
memory.
After a 4D database is used for a while, memory gets pretty full and you will
probably need to free up some space before reading the picture. I was under the
impression that a call to NewHandle made every effort to free up a block of memory,
but it seems like I can get a bigger handle more reliably (at least in a 4D external) by
calling CompactMem with the amount of memory I want before making the NewHandle
call.
A picture field in 4D is made up of two things, a PicHandle and a 6-byte PicEnd
record that 4D uses to determine where and how to draw the picture. The PicEnd
record is defined like this:
{1}
PicEnd = record
origin : Point;
transferMode : integer;
end;
The PicEnd record goes (strangely enough) at the end of the picture. Since we had
the foresight when we allocated memory to allocate enough for the PicHandle plus the
PicEnd, we can just use BlockMove and some fancy pointer arithmetic to put the record
at the end of the PicHandle.
{2}
with myPicEnd do
begin
origin.h := 0;
origin.v := 0;
transfer := srcCopy;
end;
BlockMove(@myPicEnd, Ptr(ord(thePic^) +
GetHandleSize(thePic) - 6), 6);
We use a 4D EntryPoint routine to put the picture back into 4D. 4D has some
really convoluted variant record structures it uses to pass information to and from
externals. We need two of these structures to put the picture into 4D-the VarRec
contains the information we want to send to 4D and the ParmBlock is used whenever
you need to make a 4D EntryPoint call.
We fill the VarRec like this:
{3}
myVarRec.varKind := PICT;
myVarRec.picSize := GetHandleSize(thePic);
myVarRec.PP := PicHandle(thePic);
and we fill the ParmBlock like this:
{4}
Blk4D.Name := PtrList^[1].S^;
Blk4D.HH := @myVarRec;
Blk4D.ClearOldVariable := true;
and call the EntryPoint:
{5}
Call4D(EX_PUT_VARIABLE, Blk4D);
There are 2 reasons I call 4D to put the information into the variable instead of
just directly passing a picture variable. In a database that is compiled with 4D
Compiler, 4D doesn’t pre-initalize variables it passes you. So, there really isn’t
anyway to tell if a variable is valid or not unless you leave the responsibility of
initialization to the 4D developer. This isn’t a big deal if it is an integer or longint,
but if you think it is a valid PicHandle and try to dispose it, bad things can happen. If
you don’t dispose it, you take the chance of the memory becoming lost in the heap. The
second reason is assuming the handle is valid, I never can decide what to do with the
handle 4D passes. Should I dispose it? What if 4D has another copy of it somewhere?
This method eliminates that confusion and I recommend that anytime you need to pass a
picture (or text) variable back to 4D you use the EntryPoint routines.
The only other thing of note in this procedure is that once you give 4D the
PicHandle, don’t dispose it. After you make the EntryPoint call, 4D becomes
responsible for managing the PicHandle. Sample 4D code to call ReadPict is shown in
Figure 1.
Figure 1 - Sample 4D Code calling ReadPict
The SavePict Procedure
There are three parameters to the SavePict procedure, the picture variable to
save, the creator type for the file, and a place to return an error. I can pass a picture
variable this time since I am just going to use it, not replace it. First, I call
GetHandleSize to see if the PicHandle from 4D looks valid. If it does, the procedure
uses StandardPutFile to get a path for the file. Next, it opens the file and writes 512
bytes of zeros. Then it determines how many bytes of the PicHandle to write by calling
GetHandleSize and subtracting 6 bytes for the PicEnd record. Finally, it writes those
bytes to the file.
{6}
count := GetHandleSize(Handle(pic)) - 6;
err := FSWrite(newFileRefNum, count, Ptr(pic^));