/*********************************************************/ /* This source code copyright (c) 1991-2001, Aaron Giles */ /* See the Read Me file for licensing information. */ /* Contact email: mac@aarongiles.com */ /*********************************************************/ #define SystemSevenOrLater 1 #include #include #include #include #include #include #include #include /* * Macros and typedefs * */ #define JFIFPreviewVersion 0x00010001 #define kBufferSize 20 #define Height(r) ((r)->bottom - (r)->top) #define Width(r) ((r)->right - (r)->left) #define pnotDisplayPreview 1 typedef unsigned char uchar; typedef unsigned short ushort; typedef unsigned long ulong; enum { uppPreviewOpenProcInfo = kPascalStackBased | RESULT_SIZE(SIZE_CODE(sizeof(ComponentResult))) | STACK_ROUTINE_PARAMETER(1, SIZE_CODE(sizeof(ComponentInstance))), uppPreviewCloseProcInfo = kPascalStackBased | RESULT_SIZE(SIZE_CODE(sizeof(ComponentResult))) | STACK_ROUTINE_PARAMETER(1, SIZE_CODE(sizeof(Handle))) | STACK_ROUTINE_PARAMETER(2, SIZE_CODE(sizeof(ComponentInstance))), uppPreviewCanDoProcInfo = kPascalStackBased | RESULT_SIZE(SIZE_CODE(sizeof(ComponentResult))) | STACK_ROUTINE_PARAMETER(1, SIZE_CODE(sizeof(short))), uppPreviewDisplayProcInfo = kPascalStackBased | RESULT_SIZE(SIZE_CODE(sizeof(ComponentResult))) | STACK_ROUTINE_PARAMETER(1, SIZE_CODE(sizeof(Handle))) | STACK_ROUTINE_PARAMETER(2, SIZE_CODE(sizeof(OSType))) | STACK_ROUTINE_PARAMETER(3, SIZE_CODE(sizeof(Handle))) | STACK_ROUTINE_PARAMETER(4, SIZE_CODE(sizeof(const Rect *))) }; /* * Function prototypes * */ extern pascal ComponentResult EntryPoint(ComponentParameters *params, Handle storage); pascal ComponentResult JFIFPreviewOpen(ComponentInstance self); pascal ComponentResult JFIFPreviewClose(Handle theBuffer, ComponentInstance self); pascal ComponentResult JFIFPreviewCanDo(short selector); pascal ComponentResult JFIFPreviewShow(Handle theBuffer, OSType dataType, Handle data, const Rect *inHere); OSErr LoadPreview(Handle *theData, short theFile, uchar *buffer, short width, short height); OSErr LoadPreviewExtension(Handle *theData, short theFile, long theSize); OSErr MakePreviewGWorld(GWorldPtr *theGWorld, const Rect *inHere, short width, short height); OSErr DrawPreview(Handle previewData, const Rect *inHere, short width, short height); uchar *ParseJPEG(uchar *theAdr, long theLen, uchar theCode); OSErr MakeImageDescription(ImageDescriptionHandle *theDesc, Handle previewData); OSErr DrawJPEGPreview(Handle previewData, const Rect *inHere); OSErr Draw1CompPreview(Handle previewData, const Rect *inHere); OSErr Draw3CompPreview(Handle previewData, const Rect *inHere); #if defined(powerc) || defined(__powerc) enum { uppMainProcInfo = kPascalStackBased | RESULT_SIZE(SIZE_CODE(sizeof(ComponentResult))) | STACK_ROUTINE_PARAMETER(1, SIZE_CODE(sizeof(ComponentParameters *))) | STACK_ROUTINE_PARAMETER(2, SIZE_CODE(sizeof(Handle))) }; RoutineDescriptor EntryPointRD = BUILD_ROUTINE_DESCRIPTOR(uppMainProcInfo, EntryPoint); #endif /* * This is the entry point where we dispatch the request to the appropriate function * */ extern pascal ComponentResult EntryPoint(ComponentParameters *params, Handle storage) { ComponentFunctionUPP theRoutine = nil; ComponentResult theResult = noErr; switch (params->what) { case kComponentOpenSelect: if (theRoutine = NewRoutineDescriptor((ProcPtr)JFIFPreviewOpen, uppPreviewOpenProcInfo, GetCurrentISA())) theResult = CallComponentFunction(params, theRoutine); break; case kComponentCloseSelect: if (theRoutine = NewRoutineDescriptor((ProcPtr)JFIFPreviewClose, uppPreviewCloseProcInfo, GetCurrentISA())) theResult = CallComponentFunctionWithStorage(storage, params, theRoutine); break; case kComponentCanDoSelect: if (theRoutine = NewRoutineDescriptor((ProcPtr)JFIFPreviewCanDo, uppPreviewCanDoProcInfo, GetCurrentISA())) theResult = CallComponentFunction(params, theRoutine); break; case kComponentVersionSelect: return JFIFPreviewVersion; case pnotDisplayPreview: if (theRoutine = NewRoutineDescriptor((ProcPtr)JFIFPreviewShow, uppPreviewDisplayProcInfo, GetCurrentISA())) theResult = CallComponentFunctionWithStorage(storage, params, theRoutine); break; default: theResult = badComponentSelector; break; } if (theRoutine) DisposeRoutineDescriptor(theRoutine); return theResult; } /* * Open Component function: Allocate our global storage * */ pascal ComponentResult JFIFPreviewOpen(ComponentInstance self) { Handle theBuffer; if (!(theBuffer = NewHandle(kBufferSize))) return MemError(); SetComponentInstanceStorage(self, theBuffer); return noErr; } /* * Close Component function: Dispose of our global storage * */ pascal ComponentResult JFIFPreviewClose(Handle theBuffer, ComponentInstance self) { #if applec #pragma unused(self) #endif if (theBuffer) DisposeHandle(theBuffer); return noErr; } /* * Can Do function: return 1 for any selector we can handle * */ pascal ComponentResult JFIFPreviewCanDo(short selector) { switch (selector) { case kComponentOpenSelect: case kComponentCloseSelect: case kComponentCanDoSelect: case kComponentVersionSelect: case pnotDisplayPreview: return 1; default: return 0; } } /* * Show preview function: accept an alias and extract a preview from it * * INTERESTING FACT: * * If we return an error, QuickTime will go ahead and use any custom icon as the preview, * so we return an error if there is no JFIF preview, in order to preserve this * functionality * */ pascal ComponentResult JFIFPreviewShow(Handle theBuffer, OSType dataType, Handle data, const Rect *inHere) { long theLength = kBufferSize; short fileRef, width, height; Handle previewData; Boolean isFolder; FSSpec theFile; OSErr theErr; uchar *buf; if (dataType != rAliasType) return paramErr; HLock(theBuffer); buf = (uchar *)*theBuffer; theErr = ResolveAlias(nil, (AliasHandle)data, &theFile, &isFolder); if (theErr == noErr) { theErr = FSpOpenDF(&theFile, fsRdPerm, &fileRef); if (theErr == noErr) { theErr = FSRead(fileRef, &theLength, buf); if (theErr == noErr && buf[0] == 0xff && buf[1] == 0xd8 && buf[2] == 0xff && buf[3] == 0xe0 && buf[6] == 'J' && buf[7] == 'F' && buf[8] == 'I' && buf[9] == 'F' && buf[10] == 0) { width = buf[18]; height = buf[19]; if (width && height) { theErr = LoadPreview(&previewData, fileRef, buf, width, height); if (theErr == noErr) { theErr = DrawPreview(previewData, inHere, width, height); DisposeHandle(previewData); } } else { SetFPos(fileRef, fsFromStart, 4 + (buf[4] << 8) + buf[5]); theLength = 10; theErr = FSRead(fileRef, &theLength, buf); if (theErr == noErr && buf[0] == 0xff && buf[1] == 0xe0 && buf[4] == 'J' && buf[5] == 'F' && buf[6] == 'X' && buf[7] == 'X' && buf[8] == 0) { theErr = LoadPreviewExtension(&previewData, fileRef, (buf[2] << 8) + buf[3] - 8); if (theErr == noErr) { switch (buf[9]) { case 0x10: theErr = DrawJPEGPreview(previewData, inHere); break; case 0x11: theErr = Draw1CompPreview(previewData, inHere); break; case 0x13: theErr = Draw3CompPreview(previewData, inHere); break; } DisposeHandle(previewData); } } else theErr = memFullErr; // fake an error if no preview around } } FSClose(fileRef); } } HUnlock(theBuffer); return theErr; } /* * LoadPreview: Allocate a new handle and read the preview data into it * */ OSErr LoadPreview(Handle *theData, short theFile, uchar *buffer, short width, short height) { #if applec #pragma unused(buffer) #endif long theSize = (long)width * (long)height * 3L; OSErr theErr = noErr; if (!(*theData = NewHandle(theSize))) { *theData = TempNewHandle(theSize, &theErr); if (theErr != noErr) return theErr; } HLock(*theData); theErr = FSRead(theFile, &theSize, **theData); HUnlock(*theData); return theErr; } /* * LoadPreviewExtension: Allocate a new handle and read the preview extension data into it * */ OSErr LoadPreviewExtension(Handle *theData, short theFile, long theSize) { OSErr theErr = noErr; if (!(*theData = NewHandle(theSize))) { *theData = TempNewHandle(theSize, &theErr); if (theErr != noErr) return theErr; } HLock(*theData); theErr = FSRead(theFile, &theSize, **theData); HUnlock(*theData); return theErr; } /* * MakePreviewGWorld: Create a GWorld the proper size for the given preview width and * height * */ OSErr MakePreviewGWorld(GWorldPtr *theGWorld, const Rect *inHere, short width, short height) { Rect GWBounds; OSErr theErr; GWBounds.left = GWBounds.top = 0; GWBounds.bottom = (height > Height(inHere)) ? Height(inHere) : height; GWBounds.right = (width > Width(inHere)) ? Width(inHere) : width; theErr = NewGWorld(theGWorld, 32, &GWBounds, nil, nil, 0); if (theErr != noErr) theErr = NewGWorld(theGWorld, 32, &GWBounds, nil, nil, useTempMem); return theErr; } /* * DrawPreview: Make a GWorld and copy the preview data into it; then do a CopyBits into * the requested area * */ OSErr DrawPreview(Handle previewData, const Rect *inHere, short width, short height) { PixMapHandle thePixMap, dstPixMap; uchar *bufPtr, *pixPtr, *pixBase; Rect GWBounds, dstRect = *inHere; short rowBytes, r, c, oldWidth; GWorldPtr theGWorld, curGWorld; char mmuMode = true32b; OSErr theErr = noErr; GDHandle curDevice; if (!(oldWidth = width)) { width = *(uchar *)*previewData; height = *((uchar *)*previewData + 1); } theErr = MakePreviewGWorld(&theGWorld, inHere, width, height); if (theErr == noErr) { GWBounds = theGWorld->portRect; LockPixels(thePixMap = GetGWorldPixMap(theGWorld)); pixBase = (uchar *)GetPixBaseAddr(thePixMap); rowBytes = (*thePixMap)->rowBytes & 0x3fff; bufPtr = (uchar *)StripAddress(*previewData); if (!oldWidth) bufPtr++; SwapMMUMode(&mmuMode); for (r = 0; r < GWBounds.bottom; r++) { pixPtr = pixBase; for (c = 0; c < GWBounds.right; c++) { pixPtr++; *pixPtr++ = *bufPtr++; *pixPtr++ = *bufPtr++; *pixPtr++ = *bufPtr++; } bufPtr += 3L * (long)(width - GWBounds.right); pixBase += rowBytes; } SwapMMUMode(&mmuMode); dstRect.left += (Width(inHere) - GWBounds.right) >> 1; dstRect.top += (Height(inHere) - GWBounds.bottom) >> 1; dstRect.right = dstRect.left + GWBounds.right; dstRect.bottom = dstRect.top + GWBounds.bottom; GetGWorld(&curGWorld, &curDevice); dstPixMap = GetGWorldPixMap(curGWorld); CopyBits((BitMap *)*thePixMap, (BitMap *)*dstPixMap, &GWBounds, &dstRect, srcCopy + ditherCopy, nil); DisposeGWorld(theGWorld); } return theErr; } /* * ParseJPEG: returns a pointer to the first instance of the specified code in the * given JPEG stream * */ uchar *ParseJPEG(uchar *theAdr, long theLen, uchar theCode) { uchar *theEnd = theAdr + theLen; long theSize; while (true) { while ((*theAdr++ != 0xff) && (theAdr < theEnd)); if (theAdr >= theEnd) return nil; while (*theAdr == 0xff) theAdr++; if (theAdr >= theEnd) return nil; if (*theAdr == theCode) break; else if (*theAdr == 0xd9) return nil; else if ((*theAdr <= 0x01) || ((*theAdr >= 0xd0) && (*theAdr <= 0xd8))) { theAdr++; continue; } theAdr++; if ((theSize = (*theAdr << 8) + *(theAdr + 1)) < 0) return nil; if ((theAdr + theSize) >= theEnd) { *(theAdr - 1) = 0xd9; return nil; } else theAdr += theSize; } theAdr += 3; return theAdr; } /* * MakeImageDescription: create an image description record for the given JPEG image * */ OSErr MakeImageDescription(ImageDescriptionHandle *theDesc, Handle previewData) { long theLen = GetHandleSize(previewData); CodecInfo theInfo; uchar *theData; short theCode; OSErr theErr; theErr = GetCodecInfo(&theInfo, 'jpeg', 0); if (theErr == noErr) { *theDesc = (ImageDescriptionHandle)NewHandle(sizeof(ImageDescription)); if (!*theDesc) { *theDesc = (ImageDescriptionHandle)TempNewHandle(sizeof(ImageDescription), &theErr); if (theErr != noErr) return theErr; } for (theCode = 0xc0; theCode < 0xd0; theCode++) if (theData = ParseJPEG((uchar *)*previewData, theLen, theCode)) break; if (theCode != 0xd0) { (**theDesc)->idSize = sizeof(ImageDescription); (**theDesc)->cType = 'jpeg'; (**theDesc)->resvd1 = (**theDesc)->resvd2 = (**theDesc)->dataRefIndex = 0; BlockMove(theInfo.typeName, (**theDesc)->name, 32); (**theDesc)->version = theInfo.version; (**theDesc)->revisionLevel = theInfo.revisionLevel; (**theDesc)->vendor = theInfo.vendor; (**theDesc)->temporalQuality = 0; (**theDesc)->spatialQuality = 0x200L; (**theDesc)->width = (theData[3] << 8) + theData[4]; (**theDesc)->height = (theData[1] << 8) + theData[2]; (**theDesc)->hRes = (**theDesc)->vRes = 0x480000L; (**theDesc)->dataSize = theLen; (**theDesc)->frameCount = 1; (**theDesc)->depth = 32; (**theDesc)->clutID = -1; } else theErr = memFullErr; } return theErr; } /* * DrawJPEGPreview: draws a preview image in JPEG format * */ OSErr DrawJPEGPreview(Handle previewData, const Rect *inHere) { Rect srcRect, dstRect = *inHere; ImageDescriptionHandle theDesc; PixMapHandle thePixMap; MatrixRecord theMatrix; OSErr theErr = noErr; GWorldPtr curGWorld; short width, height; GDHandle curDevice; theErr = MakeImageDescription(&theDesc, previewData); if (theErr == noErr) { width = (*theDesc)->width; height = (*theDesc)->height; srcRect.left = srcRect.top = 0; srcRect.bottom = (height > Height(inHere)) ? Height(inHere) : height; srcRect.right = (width > Width(inHere)) ? Width(inHere) : width; dstRect.left += (Width(inHere) - srcRect.right) >> 1; dstRect.top += (Height(inHere) - srcRect.bottom) >> 1; dstRect.right = dstRect.left + srcRect.right; dstRect.bottom = dstRect.top + srcRect.bottom; GetGWorld(&curGWorld, &curDevice); thePixMap = GetGWorldPixMap(curGWorld); RectMatrix(&theMatrix, &srcRect, &dstRect); HLock(previewData); theErr = FDecompressImage(StripAddress(*previewData), theDesc, thePixMap, &srcRect, &theMatrix, srcCopy + ditherCopy, nil, nil, nil, codecHighQuality, anyCodec, GetHandleSize(previewData), nil, nil); HUnlock(previewData); DisposeHandle((Handle)theDesc); } return theErr; } /* * Draw1CompPreview: draws a preview image 256-color palette format * */ OSErr Draw1CompPreview(Handle previewData, const Rect *inHere) { short rowBytes, r, c, width, height; ulong *pixPtr, *pixBase, *theColor; PixMapHandle thePixMap, dstPixMap; Rect GWBounds, dstRect = *inHere; GWorldPtr theGWorld, curGWorld; uchar *bufPtr, *colPtr; char mmuMode = true32b; OSErr theErr = noErr; Handle colorsHandle; GDHandle curDevice; colorsHandle = NewHandle(256 * sizeof(long)); if (!colorsHandle) { colorsHandle = TempNewHandle(256 * sizeof(long), &theErr); if (theErr != noErr) return theErr; } width = *(uchar *)*previewData; height = *((uchar *)*previewData + 1); theErr = MakePreviewGWorld(&theGWorld, inHere, width, height); if (theErr == noErr) { GWBounds = theGWorld->portRect; LockPixels(thePixMap = GetGWorldPixMap(theGWorld)); pixBase = (ulong *)GetPixBaseAddr(thePixMap); rowBytes = (*thePixMap)->rowBytes & 0x3fff; bufPtr = (uchar *)StripAddress(*previewData) + 2; theColor = (ulong *)(colPtr = (uchar *)StripAddress(*colorsHandle)); for (c = 0; c < 256; c++) { *colPtr++ = 0; *colPtr++ = *bufPtr++; *colPtr++ = *bufPtr++; *colPtr++ = *bufPtr++; } SwapMMUMode(&mmuMode); for (r = 0; r < GWBounds.bottom; r++) { pixPtr = pixBase; for (c = 0; c < GWBounds.right; c++) *pixPtr++ = theColor[*bufPtr++]; bufPtr += 3L * (long)(width - GWBounds.right); pixBase += rowBytes >> 2; } SwapMMUMode(&mmuMode); dstRect.left += (Width(inHere) - GWBounds.right) >> 1; dstRect.top += (Height(inHere) - GWBounds.bottom) >> 1; dstRect.right = dstRect.left + GWBounds.right; dstRect.bottom = dstRect.top + GWBounds.bottom; GetGWorld(&curGWorld, &curDevice); dstPixMap = GetGWorldPixMap(curGWorld); CopyBits((BitMap *)*thePixMap, (BitMap *)*dstPixMap, &GWBounds, &dstRect, srcCopy + ditherCopy, nil); DisposeGWorld(theGWorld); } return theErr; } /* * Draw3CompPreview: draws a preview image 24-bit color format * */ OSErr Draw3CompPreview(Handle previewData, const Rect *inHere) { return DrawPreview(previewData, inHere, 0, 0); }