root/src/blocks/jpeg.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. completeSWFJpegBitmap
  2. dumpJpegBlock
  3. skipJpegBlock
  4. methodWriteJpegFile
  5. writeSWFJpegBitmapToMethod
  6. writeSWFJpegWithAlphaToMethod
  7. destroySWFJpegBitmap
  8. scanJpegFile
  9. newSWFJpegBitmap_fromInput
  10. destroySWFJpegBitmap_andInputs
  11. newSWFJpegBitmap
  12. newSWFJpegWithAlpha_fromInput
  13. destroySWFJpegAlpha_andInputs
  14. newSWFJpegWithAlpha

/*
    Ming, an SWF output library
    Copyright (C) 2002  Opaque Industries - http://www.opaque.net/

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Lesser General Public
    License as published by the Free Software Foundation; either
    version 2.1 of the License, or (at your option) any later version.

    This library is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public
    License along with this library; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/

/* $Id: jpeg.c,v 1.19 2009/01/29 16:40:55 akleine Exp $ */

#include <stdlib.h>

#include "jpeg.h"
#include "character.h"
#include "input.h"
#include "error.h"
#include "method.h"
#include "libming.h"


struct SWFJpegBitmap_s
{
        struct SWFCharacter_s character;

        SWFInput input;
        int length;

#if TRACK_ALLOCS
        /* memory node for garbage collection */
        mem_node *gcnode;
#endif
};

struct SWFJpegWithAlpha_s
{
        struct SWFCharacter_s character;

        SWFInput input;  /* leave these here so that we */
        int length;                      /* can cast this to swfJpegBitmap */

#if TRACK_ALLOCS
        /* memory node for garbage collection */
        mem_node *gcnode;
#endif
        /* ---insert additions here (for casting to swfJpegBitmap) --- */
        SWFInput alpha;
        int jpegLength;
};

/* JPEG stream markers: */
#define JPEG_MARKER 0xFF

/* Start of Image, End of Image */
#define JPEG_SOI        0xD8
#define JPEG_EOI        0xD9

#define JPEG_JFIF 0xE0

/* encoding markers, quantization tables and Huffman tables */
#define JPEG_QUANT 0xDB
#define JPEG_HUFF        0xC4

/* image markers, start of frame and start of scan */
#define JPEG_SOF0 0xC0
#define JPEG_SOF1 0xC1
#define JPEG_SOF2 0xC2
#define JPEG_SOS        0xDA

#define JPEG_ED         0xED /* app13 */
#define JPEG_EE         0xEE /* app14 */
#define JPEG_DD         0xDD /* ??? */


static int
completeSWFJpegBitmap(SWFBlock block)
{
        return ((SWFJpegBitmap)block)->length;
}



/* clumsy utility function.. */
void
dumpJpegBlock(byte type,
                                                        SWFInput input, SWFByteOutputMethod method, void *data)
{
        int i, l0, l1, length;

        method(JPEG_MARKER, data);
        method(type, data);

        method((unsigned char)(l0 = SWFInput_getChar(input)), data);
        method((unsigned char)(l1 = SWFInput_getChar(input)), data);

        length = (l0<<8) + l1 - 2;

        for ( i=0; i<length; ++i )
                method((unsigned char)SWFInput_getChar(input), data);
}


int
skipJpegBlock(SWFInput input)
{
        int length = (SWFInput_getChar(input)<<8) + SWFInput_getChar(input);

        SWFInput_seek(input, length-2, SEEK_CUR);

        return length;
}


void
methodWriteJpegFile(SWFInput input, SWFByteOutputMethod method, void *data)
{
        int c;

        SWFInput_rewind(input);

        if ( (c = SWFInput_getChar(input)) != JPEG_MARKER )
                SWF_error("Initial Jpeg marker not found!");

        if ( (c = SWFInput_getChar(input)) != JPEG_SOI )
                SWF_error("Jpeg SOI not found!");

        /*
        method(JPEG_MARKER, data);
        method(JPEG_SOI, data);
        method(JPEG_MARKER, data);
        method(JPEG_EOI, data);
        */

        method(JPEG_MARKER, data);
        method(JPEG_SOI, data);

        for ( ;; )
        {
                if ( SWFInput_getChar(input) != JPEG_MARKER )
                        SWF_error("Jpeg marker not found where expected!");

                switch ( c = SWFInput_getChar(input) )
                {
                        case JPEG_EOI:
                                SWF_error("Unexpected end of Jpeg file (EOI found)!");

        /*      case JPEG_JFIF: */
                        case JPEG_QUANT:
                        case JPEG_HUFF:
                        case JPEG_DD:
                                /* if(!finishedEncoding) */
                                dumpJpegBlock((unsigned char)c, input, method, data);
                                /* else
                                         SWF_error("Encoding tables found in Jpeg image section!"); */
                                break;

                        case JPEG_SOF0:
                        case JPEG_SOF1:
                        case JPEG_SOF2:
                                /*
                                if(!finishedEncoding)
                                {
                                        finishedEncoding = TRUE;
                                        method(JPEG_MARKER, data);
                                        method(JPEG_EOI, data);
                                        method(JPEG_MARKER, data);
                                        method(JPEG_SOI, data);
                                        method(JPEG_MARKER, data);
                                        method(c, data);
                                }
                                */
                                dumpJpegBlock((unsigned char)c, input, method, data);
                                break;

                        case JPEG_SOS:
                                /*
                                        if(!finishedEncoding)
                                        SWF_error("Found SOS before SOF in Jpeg file!");
                                */
                                break;

                        default:
                                /* dumpJpegBlock(c, input, method, data); */
                                skipJpegBlock(input);
                }

                if ( c == JPEG_SOS )
                        break;

                if ( SWFInput_eof(input) )
                        SWF_error("Unexpected end of Jpeg file (EOF found)!");
        }

        if ( c != JPEG_SOS )
                SWF_error("SOS block not found in Jpeg file!");

        /* rest is SOS, dump to end of file */
        method(JPEG_MARKER, data);
        method((unsigned char)c, data);

        while ( (c = SWFInput_getChar(input)) != EOF )
                method((unsigned char)c, data);
}


void
writeSWFJpegBitmapToMethod(SWFBlock block, SWFByteOutputMethod method, void *data)
{
        SWFJpegBitmap jpeg = (SWFJpegBitmap)block;

        methodWriteUInt16(CHARACTERID(jpeg), method, data);
        methodWriteJpegFile(jpeg->input, method, data);
}


void
writeSWFJpegWithAlphaToMethod(SWFBlock block, SWFByteOutputMethod method, void *data)
{
        SWFJpegWithAlpha jpeg = (SWFJpegWithAlpha)block;
        int c;

        methodWriteUInt16(CHARACTERID(jpeg), method, data);
        methodWriteUInt32(jpeg->jpegLength, method, data);
        methodWriteJpegFile(jpeg->input, method, data);

        /* now write alpha file.. */

        SWFInput_rewind(jpeg->alpha);

        while ( (c = SWFInput_getChar(jpeg->alpha)) != EOF )
                method((unsigned char)c, data);
}


void
destroySWFJpegBitmap(SWFJpegBitmap jpegBitmap)
{
        free(CHARACTER(jpegBitmap)->bounds);
#if TRACK_ALLOCS
        ming_gc_remove_node(jpegBitmap->gcnode);
#endif
        free(jpegBitmap);
}


struct jpegInfo
{
        int width;
        int height;
        int length;
};

static struct jpegInfo*
scanJpegFile(SWFInput input)
{
        int length = 0, l, c;
        long pos, end;

        struct jpegInfo* info = (struct jpegInfo*)malloc(sizeof(struct jpegInfo));

        /* If malloc failed, return NULL to signify this */
        if (NULL == info)
                return NULL;

        /* scan file, get height and width, make sure it looks valid,
                 also figure length of block.. */

        if ( SWFInput_getChar(input) != JPEG_MARKER )
                SWF_error("Initial Jpeg marker not found!");

        if ( SWFInput_getChar(input) != JPEG_SOI )
                SWF_error("Jpeg SOI not found!");

        for ( ;; )
        {
                if ( SWFInput_getChar(input) != JPEG_MARKER )
                        SWF_error("Jpeg marker not found where expected!");

                switch ( c = SWFInput_getChar(input) )
                {
                        case JPEG_EOI:
                                SWF_error("Unexpected end of Jpeg file (EOI found)!");

        /*      case JPEG_JFIF: */
                        case JPEG_QUANT:
                        case JPEG_HUFF:
                        case JPEG_DD:
                                /*
                                if(finishedEncoding)
                                        SWF_error("Encoding tables found in Jpeg image section!");
                                */
                                length += skipJpegBlock(input) + 2;
                                break;

                        case JPEG_SOF2:
                                SWF_error("Only baseline (frame 0) jpegs are supported!");

                        case JPEG_SOF0:
                        case JPEG_SOF1:
                                /*
                                if ( finishedEncoding )
                                        SWF_error("Found second SOF in Jpeg file!");
                                else
                                {
                                        finishedEncoding = TRUE;
                                        length += 4; // end image, start image
                                }
                                */

                                l = SWFInput_getUInt16_BE(input);
                                SWFInput_getChar(input); /* precision */
                                info->height = SWFInput_getUInt16_BE(input);
                                info->width = SWFInput_getUInt16_BE(input);

                                length += l + 2;
                                l -= 7;
                                
                                SWFInput_seek(input, l, SEEK_CUR);

                                break;

                        case JPEG_SOS:
                                /*
                                        if(!finishedEncoding)
                                        SWF_error("Found SOS before SOF in Jpeg file!");
                                */
                                break;

                        default:
                                /* length += */
                                skipJpegBlock(input); /* + 2 */
                }

                if ( c == JPEG_SOS )
                        break;

                if ( SWFInput_eof(input) )
                        SWF_error("Unexpected end of Jpeg file (EOF found)!");
        }

        if ( c != JPEG_SOS )
                SWF_error("SOS block not found in Jpeg file!");

        /* rest is SOS, dump to end of file */

        length += 2; /* SOS tag */

        /* figure out how long the rest of the file is */
        pos = SWFInput_tell(input);
        SWFInput_seek(input, 0, SEEK_END);
        end = SWFInput_tell(input);

        length += end - pos;

        info->length = length;

        return info;
}


SWFJpegBitmap
newSWFJpegBitmap_fromInput(SWFInput input)
{
        SWFJpegBitmap jpeg;
        struct jpegInfo *info;
        SWFRect temp_rect;

        jpeg = (SWFJpegBitmap) malloc(sizeof(struct SWFJpegBitmap_s));

        /* If malloc failed, return NULL to signify this */
        if (NULL == jpeg)
                return NULL;

        SWFCharacterInit((SWFCharacter)jpeg);

        CHARACTERID(jpeg) = ++SWF_gNumCharacters;

        BLOCK(jpeg)->writeBlock = writeSWFJpegBitmapToMethod;
        BLOCK(jpeg)->complete = completeSWFJpegBitmap;
        BLOCK(jpeg)->dtor = (destroySWFBlockMethod) destroySWFJpegBitmap;
        BLOCK(jpeg)->type = SWF_DEFINEBITSJPEG2;

        jpeg->input = input;

        info = scanJpegFile(input);

        /* If scanJpegFile() failed, return NULL to signify this */
        if (NULL == info)
        {
                free (jpeg);
                return NULL;
        }

        temp_rect = newSWFRect(0, info->width, 0, info->height);

        /* If newSWFRect() failed, return NULL to signify this */
        if (NULL == temp_rect)
        {
                free(info);
                free(jpeg);
                return NULL;
        }

        CHARACTER(jpeg)->bounds = temp_rect;
        jpeg->length = info->length + 4;

        free(info);

#if TRACK_ALLOCS
        jpeg->gcnode = ming_gc_add_node(jpeg, (dtorfunctype) destroySWFBitmap);
#endif

        return jpeg;
}


static void
destroySWFJpegBitmap_andInputs(SWFJpegBitmap jpegBitmap)
{
        destroySWFInput(jpegBitmap->input);
        destroySWFJpegBitmap(jpegBitmap);
}


SWFJpegBitmap
newSWFJpegBitmap(FILE *f)
{
        SWFJpegBitmap jpeg = newSWFJpegBitmap_fromInput(newSWFInput_file(f));

        /* If newSWFJpegBitmap_fromInput() failed, return NULL to signify this */
        if (NULL == jpeg)
                return NULL;

        BLOCK(jpeg)->dtor = (destroySWFBlockMethod) destroySWFJpegBitmap_andInputs;
        return jpeg;
}


/* f is a jpeg file, alpha is zlib-compressed data */

SWFJpegWithAlpha
newSWFJpegWithAlpha_fromInput(SWFInput input, SWFInput alpha)
{
        SWFRect temp_rect;
        SWFJpegWithAlpha jpeg;
        struct jpegInfo *info;
        int alen;

        jpeg = (SWFJpegWithAlpha) malloc(sizeof(struct SWFJpegWithAlpha_s));

        /* If malloc failed, return NULL to signify this */
        if (NULL == jpeg)
                return NULL;

        SWFCharacterInit((SWFCharacter)jpeg);

        CHARACTERID(jpeg) = ++SWF_gNumCharacters;

        BLOCK(jpeg)->writeBlock = writeSWFJpegWithAlphaToMethod;
        BLOCK(jpeg)->complete = completeSWFJpegBitmap; // can use same complete
        BLOCK(jpeg)->dtor = (destroySWFBlockMethod) destroySWFJpegBitmap;                        // ditto here
        BLOCK(jpeg)->type = SWF_DEFINEBITSJPEG3;

        jpeg->input = input;
        jpeg->alpha = alpha;

        info = scanJpegFile(input);

        /* If scanJpegFile() failed, return NULL to signify this */
        if (NULL == info)
        {
                free (jpeg);
                return NULL;
        }

        temp_rect = newSWFRect(0, info->width, 0, info->height);

        /* If newSWFRect() failed, return NULL to signify this */
        if (NULL == temp_rect)
        {
                free(info);
                free(jpeg);
                return NULL;
        }

        CHARACTER(jpeg)->bounds = temp_rect;
        jpeg->jpegLength = info->length + 2; /* ?? */

        free(info);

        if ( (alen = SWFInput_length(alpha)) == -1 )
                SWF_error("couldn't get alpha file length!");

        jpeg->length = jpeg->jpegLength + alen + 6;
#if TRACK_ALLOCS
        jpeg->gcnode = ming_gc_add_node(jpeg, (dtorfunctype) destroySWFBitmap);
#endif

        return jpeg;
}


void
destroySWFJpegAlpha_andInputs(SWFJpegWithAlpha jpegWithAlpha)
{
        destroySWFInput(jpegWithAlpha->input);
        destroySWFInput(jpegWithAlpha->alpha);
        destroySWFJpegBitmap((SWFJpegBitmap) jpegWithAlpha);
}


SWFJpegWithAlpha
newSWFJpegWithAlpha(FILE *f, FILE *alpha)
{
        SWFJpegWithAlpha jpeg =
                newSWFJpegWithAlpha_fromInput(newSWFInput_file(f), newSWFInput_file(alpha));

        /* If newSWFJpegBitmap_fromInput() failed, return NULL to signify this */
        if (NULL == jpeg)
                return NULL;

        BLOCK(jpeg)->dtor = (destroySWFBlockMethod) destroySWFJpegAlpha_andInputs;
        return jpeg;
}


/*
 * Local variables:
 * tab-width: 2
 * c-basic-offset: 2
 * End:
 */

/* [<][>][^][v][top][bottom][index][help] */