EI_GraphicsImport/EI_GraphicsImport.c

/*
    File:       EI_GraphicsImport.c
    
    Description: QuickTime graphics importer component
 
    Author:     QuickTime Engineering
    
    Copyright:  © Copyright 1999-2003 Apple Computer, Inc. All rights reserved.
    
    Disclaimer: IMPORTANT:  This Apple software is supplied to you by Apple Computer, Inc.
                ("Apple") in consideration of your agreement to the following terms, and your
                use, installation, modification or redistribution of this Apple software
                constitutes acceptance of these terms.  If you do not agree with these terms,
                please do not use, install, modify or redistribute this Apple software.
 
                In consideration of your agreement to abide by the following terms, and subject
                to these terms, Apple grants you a personal, non-exclusive license, under AppleÕs
                copyrights in this original Apple software (the "Apple Software"), to use,
                reproduce, modify and redistribute the Apple Software, with or without
                modifications, in source and/or binary forms; provided that if you redistribute
                the Apple Software in its entirety and without modifications, you must retain
                this notice and the following text and disclaimers in all such redistributions of
                the Apple Software.  Neither the name, trademarks, service marks or logos of
                Apple Computer, Inc. may be used to endorse or promote products derived from the
                Apple Software without specific prior written permission from Apple.  Except as
                expressly stated in this notice, no other rights or licenses, express or implied,
                are granted by Apple herein, including but not limited to any patent rights that
                may be infringed by your derivative works or by other works in which the Apple
                Software may be incorporated.
 
                The Apple Software is provided by Apple on an "AS IS" basis.  APPLE MAKES NO
                WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED
                WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR
                PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN
                COMBINATION WITH YOUR PRODUCTS.
 
                IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR
                CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
                GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
                ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION
                OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, TORT
                (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN
                ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
                
    Change History (most recent first):
       <2>      12/09/00    dts         updated for X
       <1>      11/28/99    QTE         first file
*/
 
#if TARGET_OS_WIN32
    #include "EIComponentWindowsPrefix.h"
#endif
 
#if __MACH__
    #include <Carbon/Carbon.h>
    #include <QuickTime/QuickTime.h>
#else
    #include <Endian.h>
    #include <ImageCompression.h>
    #include <Movies.h>
    #include <Script.h>
    #include <TextUtils.h>
#endif
 
#include "EI_Image.h"
#include "EI_MakeImageDescription.h"
#include "EI_GraphicsImportVersion.h"
 
// Component globals
typedef struct {
    ComponentInstance       self;
    ComponentInstance       parent;
    ComponentInstance       delegateComponent;
} EI_GraphicsImportGlobalsRecord, *EI_GraphicsImportGlobals;
 
// Setup required for ComponentDispatchHelper.c
#define GRAPHICSIMPORT_BASENAME()   EI_GraphicsImport
#define GRAPHICSIMPORT_GLOBALS()    EI_GraphicsImportGlobals storage
 
#define CALLCOMPONENT_BASENAME()    GRAPHICSIMPORT_BASENAME()
#define CALLCOMPONENT_GLOBALS()     GRAPHICSIMPORT_GLOBALS()
 
#define COMPONENT_UPP_PREFIX()      uppGraphicsImport
#define COMPONENT_DISPATCH_FILE     "EI_GraphicsImportDispatch.h"
#define COMPONENT_SELECT_PREFIX()   kGraphicsImport
 
#define GET_DELEGATE_COMPONENT()    (storage->delegateComponent)
 
#if __MACH__
    #include <CoreServices/Components.k.h>
    #include <QuickTime/ImageCompression.k.h>
    #include <QuickTime/ComponentDispatchHelper.c>
#else
    #include <Components.k.h>
    #include <ImageCompression.k.h>
    #include <ComponentDispatchHelper.c>
#endif
 
// Component Open Request - Required
pascal ComponentResult EI_GraphicsImportOpen(EI_GraphicsImportGlobals store, ComponentInstance self)
{
    ComponentResult err = noErr;
 
    // Allocate memory for our globals, set them up and inform the component manager that we've done so
    store = (EI_GraphicsImportGlobals)NewPtrClear(sizeof(EI_GraphicsImportGlobalsRecord));
    if (err = MemError()) goto bail;
    
    store->self = self;
    store->parent = self;
 
    SetComponentInstanceStorage(self, (Handle)store);
 
    // Open and target an instance of the base graphic importer as we delegate
    // most of our calls to the base importer instance
    err = OpenADefaultComponent(GraphicsImporterComponentType, GraphicsImporterComponentType, &store->delegateComponent);
    if (err) goto bail;
    
    err = ComponentSetTarget(store->delegateComponent, self);
    if (err) goto bail;
 
bail:
    return err;
}
 
// Component Close Request - Required
pascal ComponentResult EI_GraphicsImportClose(EI_GraphicsImportGlobals store, ComponentInstance self)
{
#pragma unused(self)
    
    // Make sure to close the base component and dealocate our storage
    if (store) {
        if (store->delegateComponent)
            CloseComponent(store->delegateComponent);
 
        DisposePtr((Ptr)store);
    }
 
    return noErr;
}
 
// Component Version Request - Required
pascal ComponentResult EI_GraphicsImportVersion(EI_GraphicsImportGlobals store)
{
#pragma unused(store)
 
    return kEI_GraphicsImportVersion;
}
 
// Component Target Request
//      Allows another component to "target" you i.e., you call another component whenever
// you would call yourself (as a result of your component being used by another component)
pascal ComponentResult EI_GraphicsImportTarget(EI_GraphicsImportGlobals store, ComponentInstance target)
{
    store->parent = target;
    
    return noErr;
}
 
#pragma mark-
 
// GraphicsImportGetImageDescription
//      Examine the image file and construct an image description for it.
// The base importer uses this image description to respond to other calls such as GraphicsImportGetNaturalBounds and
// GraphicsImportDraw. In the case of GraphicsImportDraw, the image description is passed to the Image Compression
// Manager, so the cType field must identify a codec that will be able to draw the image. If a graphics importer needs to pass
// extra information that the codec will need at PreDecompress time, it can pass it in an image description extension.
pascal ComponentResult EI_GraphicsImportGetImageDescription(EI_GraphicsImportGlobals store, ImageDescriptionHandle *descOut)
{
    ComponentResult err = noErr;
    ImageFrame frame;
    unsigned long offset, size;
    short colorCount;
    Ptr colors = NULL;
 
    if (!descOut)
        return paramErr;
 
    // Get the offset and size of the image data
    err = GraphicsImportGetDataOffsetAndSize(store->parent, &offset, &size);
    if (err) goto bail;
 
    // Read some data, in this case an ImageFrame (this is specific to the Electric Image Format)
    err = GraphicsImportReadData(store->parent, &frame, offset, sizeof(frame));
    if (err) goto bail;
 
    // framePalettes contains the number of entries in the color table
    colorCount = EndianU16_BtoN(frame.framePalettes);
 
    // Alocate some storage for the color table and get it
    colors = NewPtr(3 * colorCount);
    if (err = MemError()) goto bail;
 
    err = GraphicsImportReadData(store->parent, colors, offset + sizeof(frame), 3 * colorCount);
    if (err) goto bail;
 
    // Create the Image Description
    err = EI_MakeImageDescription(&frame, colorCount, (UInt8 *)colors, descOut);
    if (err) goto bail;
 
bail:
    if (colors)
        DisposePtr(colors);
 
    return err; 
}
 
// GraphicsImportGetDataOffsetAndSize
//      Returns the offset and size of the compressed image data within a file.
// Sometimes, the data to be passed to the image decompressor is only a portion of the file. In these cases, implement
// the GraphicsImportGetDataOffsetAndSize function to indicate the byte range to send to the image decompressor.
// It is often useful for this call to first call the base importer's implementation to find out the size of the input data stream.
pascal ComponentResult EI_GraphicsImportGetDataOffsetAndSize(EI_GraphicsImportGlobals store, unsigned long *offsetOut, unsigned long *sizeOut)
{
    ComponentResult err = noErr;
    ImageHeader header;
    ImageFrame frame;
    unsigned long offset, size;
 
    // Call the base importer first
    err = GraphicsImportGetDataOffsetAndSize(store->delegateComponent, &offset, &size);
    if (err) goto bail;
 
    // Read some data
    err = GraphicsImportReadData(store->parent, &header, offset, sizeof(header));
    if (err) goto bail;
 
    // If we don't like what we see bail!
    if ((EndianU16_BtoN(header.imageVersion) != 5) || (EndianU32_BtoN(header.imageFrames) < 1)) {
        err = codecBadDataErr;
        goto bail;
    }
 
    // Read some more data
    err = GraphicsImportReadData(store->parent, &frame, offset + sizeof(header), sizeof(frame));
    if (err) goto bail;
 
    // Set the correct offset into the image and adjust the size appropriately
    if (offsetOut) *offsetOut = offset + sizeof(ImageHeader);
    if (sizeOut) *sizeOut = sizeof(ImageFrame) + (EndianU16_BtoN(frame.framePalettes) * 3) + EndianU32_BtoN(frame.frameSize);
 
bail:
    return err;
}
 
// GraphicsImportValidate
//      Attempt to ascertain quickly whether a file matches the importer's format.
// This is especially useful for formats which start with identifying codes or "magic numbers" (such as PNG and TIFF)
// in situations where image files do not have correct file types or suffixes. In situations like this, the Image Compression
// Manager may ask many graphics importers in turn to validate until it finds one that accepts the file, so it is important that
// GraphicsImportValidate calls not be too slow. Importers that implement the GraphicsImportValidate call
// should have the canMovieImportValidateFile bit set in their component flags.
pascal ComponentResult EI_GraphicsImportValidate(EI_GraphicsImportGlobals store, Boolean *valid)
{
    ComponentResult err = noErr;
    unsigned char header[2];
 
    *valid = false;
 
    err = GraphicsImportReadData(store->parent, &header[0], 0, 2);
    if (err) goto bail;
    
    // If your image format has a "magic number" at the start, this is where you should look for it.
    
    // Electric Image files don't have a magic number, but they do have a version code, which should
    // be a big-endian two-byte integer "5".  So we'll check that.  
    if( ( 0 == header[0] )
     && ( 5 == header[1] ) )
        *valid = true;
        
bail:
    return err;
}
 
// GraphicsImportGetMetaData
//      Extract metadata from an image file and add it to a user data structure.
// Note that userData must already be allocated. If the user data passed to GraphicsImportGetMetaData
// belongs to a movie, track or media, then whatever metadata is extracted will be added to that movie, track or media.
pascal ComponentResult EI_GraphicsImportGetMetaData(EI_GraphicsImportGlobals store, void *userData)
{
#pragma unused(store)
 
    ComponentResult err = noErr;
    Handle  dataHandle = NULL;
    UserData userDataOut = (UserData)userData;
 
    // For the sake of example we'll return a fixed string as metadata.
    char *theCString = { "File created with Electric Image" };
    
    #if !TARGET_API_MAC_CARBON
        StringPtr thePString = c2pstr(theCString);
    #else
        Str255 thePString;
        CopyCStringToPascal(theCString, thePString);
    #endif // TARGET_API_MAC_CARBON
 
    if (NULL == userData) {
        err = paramErr;
        goto bail;
    }
 
    dataHandle = NewHandle(0);
    if (NULL == dataHandle) {
        err = memFullErr;
        goto bail;
    }
    
    err = PtrAndHand(&thePString[1], dataHandle, thePString[0]);
    if (err) goto bail;
    
    err = AddUserDataText(userDataOut, dataHandle, kUserDataTextInformation, 1, langEnglish);
 
bail:
    if (dataHandle)
        DisposeHandle(dataHandle);
 
    return err;
}
 
// GraphicsImportGetMIMETypeList
//      Returns a list of MIME types supported by the graphics import component.
// To indicate that your graphics import component supports this function, set the hasMovieImportMIMEList flag in the
// componentFlags field of the component description record.
pascal ComponentResult EI_GraphicsImportGetMIMETypeList(EI_GraphicsImportGlobals store, void *qtAtomContainerPtr)
{
    // Note that GetComponentResource is only available in QuickTime 3.0 or later.
    // However, it's safe to call it here because GetMIMETypeList is only defined in QuickTime 3.0 or later.
    return GetComponentResource((Component)store->self, FOUR_CHAR_CODE('mime'), 128, (Handle *)qtAtomContainerPtr);
}
 
#pragma mark-
 
// When building the *Application Version Only* make our component available for use by applications (or other clients).
// Once the Component Manager has registered a component, applications can find and open the component using standard
// Component Manager routines.
#if !STAND_ALONE && !TARGET_OS_WIN32
void EI_GraphicsImporterRegister(void);
void EI_GraphicsImporterRegister(void)
{
    ComponentDescription foo;   
    #if !TARGET_API_MAC_CARBON
        ComponentRoutineUPP componentEntryPoint = NewComponentRoutineProc(EI_GraphicsImportComponentDispatch);
    #else
        ComponentRoutineUPP componentEntryPoint = NewComponentRoutineUPP((ComponentRoutineProcPtr)EI_GraphicsImportComponentDispatch);
    #endif
 
    foo.componentType = GraphicsImporterComponentType;
    foo.componentSubType = FOUR_CHAR_CODE('EIDI');
    foo.componentManufacturer = kAppleManufacturer;
    foo.componentFlags = 0;
    foo.componentFlagsMask = 0;
 
    RegisterComponent(&foo, componentEntryPoint, registerComponentGlobal, NULL, NULL, NULL);
}
#endif // !STAND_ALONE && !TARGET_OS_WIN32