/*
File: DecompressShape.h
Contains: graphics libraries - shape decompression
Written by: Mike Reed
Copyright: (c) 1995 by Apple Computer, Inc., all rights reserved.
Writers:
(jtd) John Daggett
Change History (most recent first):
<1> 9/14/95 jtd First checked in.
*/
#pragma once
#ifndef decompressShapeIncludes
#define decompressShapeIncludes
#include <QuickDraw.h>
#include <GXTypes.h>
#ifdef __cplusplus
extern "C" {
#endif
Handle CreateQDGXStream(gxShape source, PicHandle proxie,
Boolean forPrintingOnly, Boolean eraseBackground);
PicHandle DecompressShape(gxShape theShape, PicHandle proxie,
Boolean forPrintingOnly, Boolean eraseBackground);
PicHandle ShapeToPICT(gxShape source);
void ShapeToScrap(gxShape source, Boolean addProxie,
Boolean forPrintingOnly, Boolean eraseBackground);
void DragAndDropShape(EventRecord* event, gxShape shape);
#ifdef __cplusplus
};
#endif
#endif
/*
File: DecompressShape.c
Contains: graphics libraries - shape decompression
Written by: Mike Reed
Copyright: (c) 1995 by Apple Computer, Inc., all rights reserved.
Writers:
(jtd) John Daggett
Change History (most recent first):
<2> 9/14/95 jtd replaced boolean with Boolean
<1> 9/14/95 jtd First checked in.
*/
#include <Drag.h>
#include <Gestalt.h>
#include <ImageCompression.h>
#include <Memory.h>
#include <Scrap.h>
#include <GXTypes.h>
#include <GXMath.h>
#include <GXGraphics.h>
#include <GXEnvironment.h>
#include "StorageLibrary.h"
#include "DecompressShape.h"
#define LONGALIGN(n) (((n) + 3) & ~3L)
#define kAtomHeaderSize (sizeof(Size) + sizeof(OSType))
static void RectangleToRect(const gxRectangle* gxr, Rect* qdr)
{
qdr->left = FixedRound(gxr->left);
qdr->top = FixedRound(gxr->top);
qdr->right = FixedRound(gxr->right);
qdr->bottom = FixedRound(gxr->bottom);
}
static long* AppendAtom(long stream[], Size size, OSType tag, const void* data)
{
#ifdef debugging
if (size & 3)
DebugStr("\patom size needs to be long aligned");
#endif
*stream++ = size + kAtomHeaderSize;
*stream++ = tag;
BlockMove(data, (Ptr)stream, size);
return (long*)((char*)stream + size);
}
/*
* See the comment on DecompressShape for an explanation of the parameters.
* This routine is used by both DecompressShape for embedding shapes in PICTs,
* and AddQDGXRecorderFrame for making gx movies.
*/
Handle CreateQDGXStream(gxShape source, PicHandle proxie,
Boolean forPrintingOnly, Boolean eraseBackground)
{
#define gxForPrintingOnlyAtom 'fpto'
#define gxEraseBackgroundAtom 'erbg'
long atomCount, shapeSize, proxieSize;
long dataSize, fontListSize, eraseSize;
Handle dataHdl, shapeHdl;
gxFlatFontList* fontList;
gxTag fontListTag;
#ifdef debugging
GXIgnoreGraphicsWarning(tags_of_type_flst_removed);
#endif
shapeHdl = ShapeToHandleWithFlags(source, gxFontListFlatten
| gxFontGlyphsFlatten | gxFontVariationsFlatten);
#ifdef debugging
GXPopGraphicsWarning();
#endif
if (shapeHdl == nil)
return nil;
if (proxie)
{ atomCount = 2;
proxieSize = LONGALIGN(GetHandleSize((Handle)proxie));
}
else
{ atomCount = 1;
proxieSize = 0;
}
shapeSize = LONGALIGN(GetHandleSize(shapeHdl));
if (forPrintingOnly)
++atomCount;
if (eraseBackground)
++atomCount;
fontListSize = 0;
fontList = nil;
GXIgnoreGraphicsWarning(count_out_of_range);
if (GXGetShapeTags(source, gxFlatFontListItemTag, 1,
1, &fontListTag) > 0)
{ fontListSize = GXGetTag(fontListTag, nil, nil);
if (fontListSize > 0)
{ fontList = (gxFlatFontList*)NewPtr(fontListSize);
if (fontList != nil)
{ GXGetTag(fontListTag, nil, fontList);
fontListSize = LONGALIGN(fontListSize);
++atomCount;
}
else
fontListSize = 0;
}
}
GXPopGraphicsWarning(); // count_out_of_range
dataSize = atomCount * kAtomHeaderSize + shapeSize
+ proxieSize + fontListSize + sizeof(long);
dataHdl = NewHandle(dataSize);
if (dataHdl == nil)
{ DisposHandle(shapeHdl);
if (fontList)
DisposPtr((Ptr)fontList);
return nil;
}
{ long* p = (long*)*dataHdl;
if (forPrintingOnly)
p = AppendAtom(p, 0, gxForPrintingOnlyAtom, nil);
if (eraseBackground)
p = AppendAtom(p, 0, gxEraseBackgroundAtom, nil);
if (proxie)
p = AppendAtom(p, proxieSize, 'PICT', *proxie);
if (fontList)
p = AppendAtom(p, fontListSize,
gxFlatFontListItemTag, fontList);
p = AppendAtom(p, shapeSize, 'qdgx', *shapeHdl);
*p++ = 0; // end of the atom-list
DisposHandle(shapeHdl);
if (fontList)
DisposPtr((Ptr)fontList);
}
return dataHdl;
}
static void GetRidOfAnyQDShapeTags(gxShape shape)
{
gxShapeType shapeType = GXGetShapeType(shape);
if (shapeType == gxPictureType)
{ long index, count;
gxShape* subShapes;
count = GXGetPicture(shape, nil, nil, nil, nil);
if (count > 0)
{ subShapes = (gxShape*)NewPtr(count * sizeof(gxShape));
if (subShapes != nil)
{ GXGetPicture(shape, subShapes, nil, nil, nil);
for (index = 0; index < count; index++)
GetRidOfAnyQDShapeTags(subShapes[index]);
DisposPtr((Ptr)subShapes);
}
}
}
else if (shapeType == gxRectangleType &&
GXGetShapeTags(shape, gxQuickDrawPictTag,
1, gxSelectToEnd, nil) > 0)
GXSetShapeType(shape, gxPictureType);
}
/*
* This guy returns a Quickdraw picture containing an embedded shape,
and a proxie of the shape, if proxie is not nil. This is called by
ShapeToScrap and DragAndDropShape.
*
* theShape * the shape you want to embedd in a PICT
* proxie * a PICT to be drawn if theShape cannot be
drawn (optional but recommended)
* forPrintingOnly * if TRUE, then the decompressor will
always look for the proxie and theShape will only be used
when printing. Use this setting if theShape might be too
large or too slow when drawn from other apps. If FALSE,
then the decompressor will draw theShape unless it
gets an error, in which case it will look for a proxie.
* eraseBackground * if TRUE, the decompressor will always erase
the background to WHITE before drawing the shape. This
is slower, but needed if the shape does not fill its
bounding rectangle. if FALSE, the decompressor will
just draw the shape. Use this setting if the shape
entirely fills its bounding rectangle.
The shape [and proxie] is embedded by constructing a stream of
atoms. Each atom begins with a size (long) and a type (OSType)
and then the data for that type. After the last atom, there
is a trailing zero (long) to mark the end of the stream. For
embedded shapes, the type is 'qdgx', and for the proxie the
type is 'PICT'. Note that the size fields are rounded up to
a multiple of 4. Finally, to alert QuickTime that the data
is in this parsable form with a possible PICT proxie, we add
a 'prxy' extension to the ImageDescriptionHandle.
Picture of this form will draw the embedded shape when an
application calls DrawPicture if GX is around, and if not,
the proxie will be drawn. When printed, the shape or the
proxie will be printed. This is meant to replace the
PicComment described in GX 1.0 for embedding shapes in
pictures.
If you want to include a flatFontList tag, be sure that
theShape is a picture, otherwise GX will not return the
tag after GXFlattenShape. The flatFontList tag makes
certain printing conditions more efficient (i.e. font
downloading to postscript printers).
Your shape must not contain a gxQuickDrawPictTag, meaning
it contains embedded QD data, becuase this will potentially
crash when it tries to print. To fix that, DecompressShape
looks for occurrances of the tag, and converts them to real
gx data by calling GXSetShapeType(shape, gxPictureType).
*/
PicHandle DecompressShape(gxShape theShape, PicHandle proxie,
Boolean forPrintingOnly, Boolean eraseBackground)
{
#define kQuickTimeGestalt 'qtim'
PicHandle thePicture;
ImageDescriptionHandle descHdl;
ImageDescriptionPtr descPtr;
Handle dataHdl;
long version;
if (Gestalt(kQuickTimeGestalt, &version) != noErr)
return nil;
GetRidOfAnyQDShapeTags(theShape);
/*
* Move the shape's topLeft to 0,0 so that it draws neatly inside the picture frame.
* Note that the qdgx movie library does not move the shape, since the shape may not
* take up the whole frame.
*/
{ gxRectangle bounds;
GXGetShapeLocalBounds(theShape, &bounds);
if (bounds.left || bounds.top)
GXMoveShape(theShape, -bounds.left, -bounds.top);
dataHdl = CreateQDGXStream(theShape, proxie, forPrintingOnly, eraseBackground);
if (bounds.left || bounds.top)
GXMoveShape(theShape, bounds.left, bounds.top);
}
if (dataHdl == nil)
return nil;
descHdl = (ImageDescriptionHandle)NewHandleClear(sizeof(ImageDescription));
if (descHdl)
{ Rect shortBounds;
gxRectangle bounds;
GXGetShapeLocalBounds(theShape, &bounds);
RectangleToRect(&bounds, &shortBounds);
OffsetRect(&shortBounds, -shortBounds.left,
-shortBounds.top); // set the topLeft of the src to 0,0
thePicture = OpenPicture(&shortBounds);
descPtr = *descHdl;
descPtr->idSize = sizeof(ImageDescription);
descPtr->cType = 'qdgx';
descPtr->vendor = 'appl';
descPtr->temporalQuality = codecLosslessQuality;
descPtr->width = shortBounds.right;
descPtr->height = shortBounds.bottom;
descPtr->hRes = descPtr->vRes = ff(72);
descPtr->dataSize = GetHandleSize(dataHdl);
descPtr->frameCount = 1;
descPtr->depth = 32;
descPtr->clutID = -1;
// If there is a PICT proxie, add an image extension to
// tell QuickTime, in case GX is not around.
if (proxie)
{ Handle prxyVersionHdl = NewHandle(sizeof(long));
if (prxyVersionHdl != nil)
{ *(long*)*prxyVersionHdl = 0; // version number for 'prxy' extension
SetImageDescriptionExtension(descHdl, prxyVersionHdl, 'prxy');
}
}
HLock(dataHdl);
DecompressImage(*dataHdl, descHdl,
((CGrafPtr)qd.thePort)->portPixMap, &shortBounds,
&shortBounds, srcCopy, nil);
DisposeHandle((Handle)descHdl);
ClosePicture();
}
else
thePicture = nil;
DisposHandle(dataHdl);
return thePicture;
}
/*
* This guy returns a Quickdraw picture containing a 1-bit bitmap of the shape.
* This is used by ShapeToScrap to create a proxie when calling DecompressShape.
* If you want to make the proxie prettier (and larger), change the bitmap to 8-bit.
* However, if you're using this in conjunction with DecompressShape to place a
* gxShape on the clipboard, 1-bit should be enough, since the actual shape will be
* drawn, rather than the proxie (unless forPrintingOnly is true).
*/
PicHandle ShapeToPICT(gxShape source)
{
gxRectangle bounds;
gxShape bitShape;
gxBitmap bitmap;
PicHandle thePicture;
Rect shortBounds;
/*
* GetShapeLocalBounds doesn't accurately report the bounds of a gxQuickDrawPictTag.
* One option is to convert the tags to real GX pictures.
*/
GetRidOfAnyQDShapeTags(source);
GXGetShapeLocalBounds(source, &bounds);
RectangleToRect(&bounds, &shortBounds);
OffsetRect(&shortBounds, -shortBounds.left, -shortBounds.top);
bitmap.width = shortBounds.right;
bitmap.height = shortBounds.bottom;
bitmap.rowBytes = bitmap.width + 31 >> 5 << 2;
bitmap.pixelSize = 1;
bitmap.space = gxIndexedSpace;
bitmap.set = nil;
bitmap.profile = nil;
bitmap.image = NewPtrClear(bitmap.rowBytes * bitmap.height);
if (bitmap.image == nil)
return nil;
bitShape = GXNewBitmap(&bitmap, nil);
if (bitShape != nil)
{ gxViewGroup group = GXNewViewGroup();
gxViewDevice device = GXNewViewDevice(group, bitShape);
gxViewPort port = GXNewViewPort(group);
gxTransform trans = GXCloneTransform(GXGetShapeTransform(source));
GXSetShapeAttributes(source, GXGetShapeAttributes(source) | gxMapTransformShape);
GXMoveShape(source, -bounds.left, -bounds.top);
GXSetViewPortDither(port, 4);
GXSetShapeViewPorts(source, 1, &port);
GXDrawShape(source);
GXSetShapeTransform(source, trans);
GXDisposeTransform(trans);
GXDisposeViewGroup(group); /* this disposes the gxViewPort and gxViewDevice */
GXDisposeShape(bitShape);
}
{ GrafPtr thePort;
BitMap srcBits;
GetPort(&thePort);
srcBits.baseAddr = bitmap.image;
srcBits.rowBytes = bitmap.rowBytes;
srcBits.bounds = shortBounds;
thePicture = OpenPicture(&shortBounds);
CopyBits(&srcBits, &thePort->portBits,
&shortBounds, &shortBounds, srcOr, nil);
ClosePicture();
}
DisposPtr((Ptr)bitmap.image);
return thePicture;
}
/*
* This guy puts a Quickdraw picture on the clipboard containing an embedded shape
* and, if addProxie is true, a 1-bit bitmap of the shape. Call this in response to
* the user choosing "Copy" or "Cut" from the Edit menu. See comment for DecompressShape
* to explain forPrintingOnly.
*/
void ShapeToScrap(gxShape source, Boolean addProxie,
Boolean forPrintingOnly, Boolean eraseBackground)
{
PicHandle picture, proxie;
proxie = addProxie ? ShapeToPICT(source) : nil;
picture = DecompressShape(source, proxie, forPrintingOnly, eraseBackground);
if (proxie)
KillPicture(proxie);
if (picture)
{ HLock((Handle)picture);
ZeroScrap();
PutScrap(GetHandleSize((Handle)picture), 'PICT', (Ptr)*picture);
KillPicture(picture);
}
}
/*
* The ItemReference is the gxShape to be sent. The dragSendRefCon is ignored.
*/
static pascal OSErr LibrarySendDataProc(FlavorType theType, void *dragSendRefCon,
ItemReference theItem, DragReference theDrag)
{
OSErr result = noErr;
gxShape shape = (gxShape)theItem;
switch (theType) {
case 'qdgx':
{ Handle flat = ShapeToHandle(shape);
if (flat)
{ HLock(flat);
result = SetDragItemFlavorData(theDrag, theItem,
'qdgx', *flat, GetHandleSize(flat), 0);
DisposHandle(flat);
}
break;
}
case 'PICT':
{ PicHandle proxie = ShapeToPICT(shape);
PicHandle pict = DecompressShape(shape, proxie, false, true);
if (proxie)
KillPicture(proxie);
if (pict)
{ HLock((Handle)pict);
result = SetDragItemFlavorData(theDrag, theItem,
'PICT', (Ptr)*pict, GetHandleSize((Handle)pict), 0);
KillPicture(pict);
}
break;
}
default:
result = badDragFlavorErr;
|