[comp.sys.mac.programmer] Rolling your own 32-bit offscreen pixmaps -- magic incantat

jwhitnell@cup.portal.com (Jerry D Whitnell) (07/26/90)

Tim writes...
|So, I made changes as recommended, including such annoyances as creating
|an empty color table.  Problem is, after some intensive debugging effort,
|peeking at all the fields of the CGrafPort, GDevice, and PixMapHandle
|with Lightsbug, and going over my code with a fine-toothed comb, I still
|crash during the initial EraseRect.  Is there some magic wand I have to
|wave to get a 32-bit offscreen pixmap to work?

Funny, I could have written this messages a couple of weeks ago.  Can you
tell me (via email if necessary) what you are doing?  


A couple of things to check:  the pixelType, pixelSize, cmdCount and cmpSize
fields in the PixMap.  For 32-bit PixMaps, they should be chunky, 32, 3 and 8.
Also check the gdType field in the graphics device, it should be direct RGB,
not CLUT.  If you have a 24-bit color monitor, you might compare a 32-bit
color window with your offscreen PixMap, and comfirm the differences.

Hope this helps.

Jerry Whitnell
SuperMac Technology

tim@efi.com (Tim Maroney) (07/28/90)

>Tim writes...
>|So, I made changes as recommended, including such annoyances as creating
>|an empty color table.  Problem is, after some intensive debugging effort,
>|peeking at all the fields of the CGrafPort, GDevice, and PixMapHandle
>|with Lightsbug, and going over my code with a fine-toothed comb, I still
>|crash during the initial EraseRect.  Is there some magic wand I have to
>|wave to get a 32-bit offscreen pixmap to work?

In article <32082@cup.portal.com> jwhitnell@cup.portal.com (Jerry D Whitnell)
writes:
>A couple of things to check:  the pixelType, pixelSize, cmdCount and cmpSize
>fields in the PixMap.  For 32-bit PixMaps, they should be chunky, 32, 3 and 8.
>Also check the gdType field in the graphics device, it should be direct RGB,
>not CLUT.

Um, I agree with everything here except the pixelType.  According to the
32-bit quickdraw manual, page 3, pixelType should be RGBDirect = 16, not
chunky = 0.

>If you have a 24-bit color monitor, you might compare a 32-bit
>color window with your offscreen PixMap, and comfirm the differences.

Been there, done that, except not on a 32-bit color system.  I'm working
on a IIci with built-in video, and working with 32-bit offscreen pixmaps.
I compared it to a 32-bit offscreen GWorld built by 32-bit QD on the
same system.

By popular demand, here's the code.  TOffscreen is an abstract base
class which underlies TQDOffscreen (which is what I'm having problems
with -- roll-your-own offscreen graphics worlds).  Because only the
inherited IOffscreen method is ever called, this is the only part
of TOffscreen I am providing.  I am providing all the relevant parts
of TQDOffscreen, as well as the utility routine I use to create the
TQDOffscreen object.  Sorry for the weirdness imposed by the THINK
Pascal formatter and the large screen.  I have actually had to
remove some comments to make the code readable in this format.

          TQDOffscreen = object(TOffscreen)
                    fBuffer: Ptr;
                    fDevice: GDHandle;
                    fPort: CGrafPtr;

                    procedure TQDOffscreen.IOffscreen (depth: integer; r: Rect; tab: CTabHandle; dev: GDHandle; flags: GWorldFlags);
                    override;
                    { Create and initialize an offscreen drawing environment. }

                    procedure TQDOffscreen.Free;
                    override;

{ set up and restore drawing world methods }

                    procedure TQDOffscreen.PreDraw;
                    override;
                    { Save the current world and swap in the offscreen world. }

                    procedure TQDOffscreen.PostDraw;
                    override;
                    { Restores the saved port and gDevice. }

{ rarely used informational methods }

                    function TQDOffscreen.GetPixMap: PixMapHandle;
                    override;
                    { Return the PixMap. }

                    function TQDOffscreen.GetOffPort: CGrafPtr;
                    override;
                    { Return the port }

{ old-style-pixmap-only reset method }

                    procedure TQDOffscreen.SetPixBase (pixels: Ptr);
                    { Change the baseAddr of the pixel map. }

               end;

   { Utility routine for making offscreen pixmaps.}

{$S OffScreen}

     function NewOffWorlder (var aOffScreen: TOffscreen; frame: Rect; depth: integer; useOld: Boolean): Boolean;

          var
               fi: FailInfo;
               offFailed: Boolean;
               oldMap: TQDOffscreen;
               newMap: TQD32Offscreen;

          label
               100;

          procedure AllocFailed (error: OSErr; message: LONGINT);
          begin
               offFailed := true;
               goto 100;
          end;

     begin
          offFailed := false;
          CatchFailures(fi, AllocFailed);
          if useOld | not TrapExists(QD32bit) then
               begin
                    New(oldMap);
                    aOffScreen := oldMap;
               end
          else
               begin
                    New(newMap);
                    aOffScreen := newMap;
               end;
          FailNil(aOffScreen);
          aOffScreen.IOffscreen(depth, frame, nil, nil, []);
          Success(fi);
100:
          NewOffWorlder := not offFailed;
     end;

     procedure TOffscreen.IOffscreen (depth: integer; r: Rect; tab: CTabHandle; dev: GDHandle; flags: GWorldFlags);
                    { Create and initialize an offscreen drawing environment. }
     begin
          if (depth <> 0) & (depth <> 1) & (depth <> 2) & (depth <> 4) & (depth <> 8) & (depth <> 16) & (depth <> 32) then
               begin
                    Free;
                    Failure(minErr, 0);
               end;
          fOldPort := nil;
          fOldDevice := nil;
          IObject;
     end;

{ Here's the routine which crashes when the EraseRect is called.  Note }
{ that this does not always happen.  Sometimes the amount of space }
{ allocated to INITs has an effect, so that if some INITs are removed, }
{ (it doesn't matter which ones), a call will work.  Other parameters }
{ always crash wven with all INITs removed. }

     procedure TQDOffscreen.IOffscreen (depth: integer; r: Rect; tab: CTabHandle; dev: GDHandle; flags: GWorldFlags);
                    { Create and initialize an offscreen drawing environment. }

          var
               fi: FailInfo;
               have32bitQD: Boolean;
               saveDev: GDHandle;
               savePort: GrafPtr;
               docW, docH, pixBytes, theCmpSize, theCmpCount, theType, theRowBytes: integer;

          procedure DeathOffscreen (error: OSErr; message: longint);
          begin
               SetGDevice(saveDev);
               SetPort(savePort);
               Free;
          end;

     begin
          have32bitQD := TrapExists(QD32bit);

          inherited IOffscreen(depth, r, tab, dev, flags);
          fBuffer := nil;
          fDevice := nil;
          fPort := nil;
          GetPort(savePort);
          saveDev := GetGDevice;
          CatchFailures(fi, DeathOffscreen);

     { Let's set up the size of the rectangle we are using for the document. }
          docW := r.right - r.left;
          docH := r.bottom - r.top;

     { Figure out the color components involved. }
          case depth of
               0: 
                    ;  { XXX }
               1: 
                    ; { XXX }
               2: 
                    ; { XXX }
               4: 
                    ; { XXX }
               8: 
                    begin
                         pixBytes := 1;
                         theCmpSize := 8;
                         theCmpCount := 1;
                         theType := clutType;
                    end;
               16: 
                    begin
                         pixBytes := 2;
                         theCmpSize := 5;
                         theCmpCount := 3;
                         theType := directType;
                    end;
               32: 
                    begin
                         pixBytes := 4;
                         theCmpSize := 8;
                         theCmpCount := 3;
                         theType := directType;
                    end;
          end;

          theRowBytes := docW * pixBytes;
          if odd(theRowBytes) then
               theRowBytes := theRowBytes + 1;
          fBuffer := NewPtr(docH * theRowBytes);
          FailMemError;

     { Set up the graphics device. }
          if dev = nil then
               begin
                    fDevice := NewGDevice(0, -1);
                    FailNIL(fDevice);

                    HLock(Handle(fDevice));
                    HLock(Handle(fDevice^^.gdPMap));
                    with fDevice^^, fDevice^^.gdPMap^^ do
                         begin
                              if (pmVersion < 2) & have32bitQD then
                                   pmVersion := 2;
                              gdFlags := $4001;                                                     { color, device has no driver }
                              gdType := theType;                                                    { color table or direct type }
                              gdRect := r;                                                               { the bounding rectangle of device }

                              baseAddr := fBuffer;                                                       { The base address of the pixmap }
                              bounds := r;                                                               { bounding rectangle of our device }
                              rowBytes := theRowBytes + $8000;                            { set the color flag in rowBytes }
                              pixelSize := depth;
                              cmpCount := theCmpCount;
                              cmpSize := theCmpSize;
                              if theType = directType then
                                   pixelType := RGBDirect
                              else
                                   pixelType := 0;

               { Set up the color table. }
                              DisposCTable(pmTable);                                                { kill the existing color table }
                              pmTable := nil;
                              if tab = nil then
                                   begin
                                        if theType = clutType then
                                             begin
                                                  pmTable := GetCTable(depth);                           { install default color table }
                                                  FailNil(pmTable);
                                             end
                                        else
                                             begin                                                                 { install stub color table }
                                                  pmTable := CTabHandle(NewHandle(16));
                                                  FailNil(pmTable);
                                                  pmTable^^.ctSeed := GetCTSeed;
                                                  pmTable^^.ctSize := 0;
                                                  pmTable^^.ctFlags := $8000;
                                                  if have32bitQD then
                                                       CTabChanged(pmTable);
                                             end;
                                   end
                              else
                                   pmTable := tab;                                                            { install caller's color table }

               { build a new iTable for this device, based on color table }
                              gdResPref := 3;
                              MakeITable(pmTable, gdITable, gdResPref);
                              FailOSErr(QDError);
                              gdId := 0;
                              gdSearchProc := nil;
                              gdCompProc := nil;
                         end; { with }
                    HUnLock(Handle(fDevice^^.gdPMap));
                    HUnLock(Handle(fDevice));
                    if have32bitQD then
                         GDeviceChanged(fDevice);
               end
          else
               fDevice := dev;

          SetGDevice(fDevice);
          fPort := CGrafPtr(NewPtr(SizeOf(CGrafPort)));
          FailNil(fPort);
          OpenCPort(fPort);
          FailNoReserve;
          fPort^.portRect := r;
          RectRgn(fPort^.visRgn, r);
          if have32bitQD then
               PortChanged(GrafPtr(fPort));

          Success(fi);
          SetGDevice(saveDev);
          SetPort(savePort);

          PreDraw;
          EraseRect(r);
          PostDraw;
     end;

     procedure TQDOffscreen.Free;
          override;
     begin
          if fBuffer <> nil then
               DisposPtr(fBuffer);
          if fPort <> nil then
               begin
                    CloseCPort(fPort);
                    DisposPtr(Ptr(fPort));
               end;
          if fDevice <> nil then
               DisposGDevice(fDevice);
          inherited Free;
     end;

     procedure TQDOffscreen.PreDraw;
          { Save the current world and swap in the offscreen drawing world. }
          var
               savePort: GrafPtr;
     begin
{$IFC qDebug}
          if (fOldDevice <> nil) | (fOldPort <> nil) then
               ProgramBreak('PreDraw -- fOldDevice or fOldPort is not nil!');
{$ENDC}
          GetPort(savePort);                                                                   { save the current graphics port }
          fOldPort := CGrafPtr(savePort);
          fOldDevice := GetGDevice;                                                            { save the current graphics device }
          SetGDevice(fDevice);                                                                 { set up the offscreen graphics device }
          SetPort(GrafPtr(fPort));                                                        { set up the offscreen graphics port }
     end;

     procedure TQDOffscreen.PostDraw;
          { Reverses the effects of PreDraw by restoring the previously saved port and device. }
     begin
{$IFC qDebug}
          if (fOldDevice = nil) | (fOldPort = nil) then
               ProgramBreak('PostDraw -- fOldDevice or fOldPort is nil!');
{$ENDC}
          SetGDevice(fOldDevice);                                                              { restore the saved graphics device }
          SetPort(GrafPtr(fOldPort));                                                     { restore the saved graphics port }
          fOldDevice := nil;
          fOldPort := nil;
     end;

     procedure TQDOffscreen.SetPixBase (pixels: Ptr);
                    { Change the baseAddr of the pixel map. }
     begin
          if fBuffer <> nil then
               begin
                    DisposPtr(fBuffer);
                    fBuffer := nil;
               end;
          fDevice^^.gdPMap^^.baseAddr := pixels;
          if TrapExists(QD32bit) then
               GDeviceChanged(fDevice);
          fPort^.portPixMap^^.baseAddr := pixels;
          if TrapExists(QD32bit) then
               PortChanged(GrafPtr(fPort));
     end;