www.xbdev.net xbdev - software development
Friday March 22, 2019
home | about | contact | Donations

     
 

Image Formats

A single picture is worth a thousand words...

 

Flash (.swf) File Format - (Jpg Tag)

by bkenwright@xbdev.net

 

I just had to put this as its own little chapter, as the jpg tag isn't exactly complicated, but getting the jpg information and displaying the image on the screen or dumping it to a file is a little tricky,....so if you've not used the jpg format, it can be a bit worrying.....god I've wrote my own jpg parser and I'm still not keen on the jpg format.  Its great for compression, but for people new to c/c++ or compression principles and image formats, its like getting hit with a 14ton truck.

 

Have a coffee and stick with me....first think is just get the jpg tag...and then look at its contents...theres a couple of different versions of the jpg tag...think the easiest is the jpg2 tag...which is like a totally self contained jpg :)

 

Each time we come across a jpg data set, we dump it out a file and name it a jpg file.

 

Source - Download Source Code (56kb)
////////////////////////////////////////////////////////////////////////////////////////
//                                                                                    //
// File:   main.cpp                                                                   //
// Date:   13-11-07 (winter)                                                          //
// Author: bkenwright@xbdev.net                                                       //
// URL:    www.xbdev.net                                                              //
//                                                                                    //
////////////////////////////////////////////////////////////////////////////////////////
/*
   Introduction to the flash swf file format - dumping tag information

   Jpg Data

   Just for testing, this code creates a simple window and uses the directx api so we
   can try and covert the jpg image data to a texture, also we dump out the jpg
   encoding and image data to a jpg file
*/
////////////////////////////////////////////////////////////////////////////////////////

#include <windows.h>
#include <stdio.h>        // sprintf(..), fopen(..)
#include <stdarg.h>     // So we can use ... (in dprintf)

#include "loadjpg.h"    // DecodeJpgFileData(...)
////////////////////////////////////////////////////////////////////////////////////////
//                                                                                    //
//  Debug printf that writes to a txt file :)                                         //
//                                                                                    //
////////////////////////////////////////////////////////////////////////////////////////

//Saving debug information to a log file
void dprintf(const char *fmt, ...) 
{
    va_list parms;
    char buf[256];

    // Try to print in the allocated space.
    va_start(parms, fmt);
    vsprintf (buf, fmt, parms);
    va_end(parms);

    // Write the information out to a txt file
    FILE *fp = fopen("output.txt", "a+");
    fprintf(fp, "%s", buf);
    fclose(fp);
}// End dprintf(..)

////////////////////////////////////////////////////////////////////////////////////////
//                                                                                    //
//  Various Flash SWF Container Structure Defines                                     //
//                                                                                    //
////////////////////////////////////////////////////////////////////////////////////////

#pragma pack(1)
struct stSWFHeader
{
    unsigned char sig[3];
    unsigned char version;
    unsigned int  uncompSize;

    enum rectSize
    {
        xmin = 0,        // X minimum position for rectangle in twips
        xmax,            // X maximum position for rectangle in twips
        ymin,            // Y minimum position for rectangle in twips
        ymax            // Y maximum position for rectangle in twips
    };

    int    rectBits;        // Bits used for each subsequent field
    int rectSizes[4];    // rectSize indexes

    unsigned short int frameRate;
    unsigned short int frameCount;
};
#pragma pack()

////////////////////////////////////////////////////////////////////////////////////////

struct stTagType
{
    int        m_id;
    char    m_name[32];
};

stTagType Tags [] =
{
    { 0,  "END"                    },                    
    { 1,  "SHOWFRAME"            },              
    { 2,  "DEFINESHAPE"            },            
    { 3,  "FREECHARACTER"        },          
    { 4,  "PLACEOBJECT"            },            
    { 5,  "REMOVEOBJECT"        },           
    { 6,  "DEFINEBITS"            },             
    { 7,  "DEFINEBUTTON"        },           
    { 8,  "JPEGTABLES"            },             
    { 9,  "SETBACKGROUNDCOLOR"    },     
    { 10, "DEFINEFONT"            },             
    { 11, "DEFINETEXT"            },             
    { 12,  "DOACTION"            },              
    { 13,  "DEFINEFONTINFO"        },        
    { 14,  "DEFINESOUND"        },           
    { 15,  "STARTSOUND"            },            
    { 17,  "DEFINEBUTTONSOUND"    },     
    { 18,  "SOUNDSTREAMHEAD"    },       
    { 19,  "SOUNDSTREAMBLOCK"    },      
    { 20,  "DEFINELOSSLESS"        },        
    { 21,  "DEFINEBITSJPEG2"    },       
    { 22,  "DEFINESHAPE2"        },          
    { 23,  "DEFINEBUTTONCXFORM" },     
    { 24,  "PROTECT"            },               
    { 25,  "PATHSAREPOSTSCRIPT" },     
    { 26,  "PLACEOBJECT2"        },          
    { 28,  "REMOVEOBJECT2"        },         
    { 29,  "SYNCFRAME"            },             
    { 31,  "FREEALL"            },               
    { 32,  "DEFINESHAPE3"        },          
    { 33,  "DEFINETEXT2"        },           
    { 34,  "DEFINEBUTTON2"        },         
    { 35,  "DEFINEBITSJPEG3"    },       
    { 36,  "DEFINELOSSLESS2"    },       
    { 37,  "DEFINEEDITTEXT"        },        
    { 38,  "DEFINEVIDEO"        },           
    { 39,  "DEFINESPRITE"        },          
    { 40,  "NAMECHARACTER"        },         
    { 41,  "SERIALNUMBER"        },          
    { 42,  "DEFINETEXTFORMAT"    },      
    { 43,  "FRAMELABEL"            },            
    { 45,  "SOUNDSTREAMHEAD2"    },      
    { 46,  "DEFINEMORPHSHAPE"    },      
    { 47,  "FRAMETAG"            },              
    { 48,  "DEFINEFONT2"        },           
    { 49,  "GENCOMMAND"            },            
    { 50,  "DEFINECOMMANDOBJ"    },      
    { 51,  "CHARACTERSET"        },          
    { 52,  "FONTREF"            },               
    { 56,  "EXPORTASSETS"        },          
    { 57,  "IMPORTASSETS"        },          
    { 58,  "ENABLEDEBUGGER"        },        
    { 59,  "INITACTION"            },            
    { 60,  "DEFINEVIDEOSTREAM"    },     
    { 61,  "VIDEOFRAME"            },            
    { 62,  "DEFINEFONTINFO2"    },        
    { 1023,"DEFINEBITSPTR"        },         
};


struct stSmallTag
{
    unsigned short Tag    : 10;    // UB[10] Tag id 
    unsigned short Length : 6;    // UB[6] Length of tag 
};

struct stLargeTag
{
    unsigned short int Tag        : 10;    // UB[10] Tag id 
    unsigned short int LengthID    : 6;    // Long Header Flag UB[6] Always 0x3F  
    int                   Length;            // UI32 Length of tag 
};

////////////////////////////////////////////////////////////////////////////////////////

void DebugRawDataFile(char* filename, char * data, int size)
{
    FILE *fp = fopen(filename, "wb+");
    fwrite(data, size, 1, fp);
    fclose(fp);
}

////////////////////////////////////////////////////////////////////////////////////////


int ReadDefineBitsJPEG2(char * uncompData, int tagLen)
{
    int characterID = *((unsigned short*)&uncompData[0]);
    uncompData += 2;
    tagLen -= 2;


    dprintf("\tstDefineBitsJPEG2 : CharacterID: %d\n", characterID);

    
    static int count = 0;
    count++;
    char buf[32];
    sprintf(buf, "imageJPEG2_%d.jpg", count);


    // ??? Not sure what this is about!  Should start with the SOI jpg tag, but
    // instead seems to start with 4 misc bytes...looks like EOI and SOI tags,
    // back to front!  But why would that be?
    uncompData+=4;
    tagLen -= 4;

    dprintf("Dumping To File %s\n", buf);
    DebugRawDataFile(buf, &uncompData[0], tagLen);


    // Decompressing the jpg image data and write it to a bmp
    const unsigned char* jpgRawData = (const unsigned char*)&uncompData[0];
    unsigned char* rgbpix = NULL;
    unsigned int width = 0;
    unsigned int height = 0;
    DecodeJpgFileData(jpgRawData, tagLen, &rgbpix, &width, &height);

    // Dumping it to a bmp file
    sprintf(buf, "imageJPEG2_to_BMP%d.bmp", count);
    WriteBMP24(buf, width, height, rgbpix); 
    delete[] rgbpix;

    return 0;
}

////////////////////////////////////////////////////////////////////////////////////////
//--------------------------------------------------------------------------------------

int ReadDefineBitsJPEG3(char * uncompData, int tagLen)
{
    int characterID = *((unsigned short*)&uncompData[0]);
    uncompData += 2;
    tagLen -= 2;

    // Offset to the alpha component as well :)
    int sizeJpgData = *((unsigned int*)&uncompData[0]);
    uncompData += 4;
    tagLen -= 4;


    dprintf("\tstDefineBitsJPEG3 : CharacterID: %d\n", characterID);

    static int count = 0;
    count++;
    char buf[32];
    sprintf(buf, "imageJPEG3_%d.jpg", count);

    dprintf("Dumping To File %s\n", buf);
    DebugRawDataFile(buf, &uncompData[0], sizeJpgData);

    // Decompressing the jpg image data and write it to a bmp
    const unsigned char* jpgRawData = (const unsigned char*)&uncompData[0];
    unsigned char* rgbpix = NULL;
    unsigned int width = 0;
    unsigned int height = 0;
    DecodeJpgFileData(jpgRawData, sizeJpgData, &rgbpix, &width, &height);

    // Dumping it to a bmp file
    sprintf(buf, "imageJPEG3_to_BMP%d.bmp", count);
    WriteBMP24(buf, width, height, rgbpix); 
    delete[] rgbpix;

    // Lets do the alpha part on the end....

    // Increment past the jpg data to the alpha part
    int alphaSize = sizeJpgData;
    char* alphaData = uncompData + sizeJpgData;

    // -- Alpha part of grabbing the data ----

    // The jpg3 alpha component is zlib compressed, so we have to use the zlib
    // decompression algorithm again
    int (*uncompress) (char *dest, unsigned int *destLen, const char *source, unsigned int sourceLen);
    HMODULE dll = LoadLibrary("zlib1.dll");
    uncompress = (int (*) (char *dest, unsigned int *destLen, const char *source, unsigned int sourceLen))GetProcAddress(dll, "uncompress");


    // Just allocate a big junk of memory, read it all in, and uncompress it!
    int outSize = 500000;
    // This is where our uncompressed data will go
    char * tmpOut = new char [outSize];
    memset(tmpOut, 0, outSize);
    // We have to set tmpOutLen to a val, as it modifies it to the actual size of
    // the decompressed data
    unsigned int tmpOutLen = outSize;
    // go go go...do the uncompression
    int error = uncompress(tmpOut, &tmpOutLen, alphaData, alphaSize);

    /*
    // Just for info, the zlib error codes
    #define Z_OK 0
    #define Z_STREAM_END 1
    #define Z_NEED_DICT 2
    #define Z_ERRNO (-1)
    #define Z_STREAM_ERROR (-2)
    #define Z_DATA_ERROR (-3)
    #define Z_MEM_ERROR (-4)
    #define Z_BUF_ERROR (-5)
    #define Z_VERSION_ERROR (-6)
    */

    if (error!=0)
    {
        dprintf("Error decompressing JPEG3 Alpha - zlib error: %d", error);
    }

    return 0;
}

////////////////////////////////////////////////////////////////////////////////////////


void ReadDataTag(int tagID, int tagLength, char * data)
{
    char * tagName = "**Unknown** - Wierd";
    for (int i=0; i<sizeof(Tags)/sizeof(stTagType); i++)
    {
        if (tagID == Tags[i].m_id)
        {
            tagName = Tags[i].m_name;
            break;
        }
    }

    dprintf("\tTag: %.2d",        tagID);
    dprintf(", Name: %21s",        tagName);
    dprintf(", Length: %4d",    tagLength);

    // Lets dump the first 2 raw data bytes for curiosity as well
    dprintf(", Raw-Data:0x%.2X, 0x%.2X\n", data[0], data[1]);


    // Parse any tags that we want
    switch (tagID)
    {
        case 21: // DEFINEBITSJPEG2
        {
            ReadDefineBitsJPEG2(&data[0], tagLength);
        }
        break;

        case 35: // DEFINEBITSJPEG3
        {
            ReadDefineBitsJPEG3(&data[0], tagLength);
        }
        break;
    }

}// End ReadDataTag(..)

//--------------------------------------------------------------------------------------

void ReadDataChunks(char * data, int lenData)
{
    char * tmpData = data;
    int    tmpLen  = lenData;

    while (tmpLen > 0)
    {
        unsigned short int tmpx = tmpData[1] | (tmpData[0]<<8);
        unsigned short int *tmp0 = (unsigned short int*)&tmpData[0];
        tmpData+=2;
        tmpLen-=2;

        int Tag = (*tmp0 >> 6);
        int Length = (*tmp0 & 0x3F);

        if (Length != 0x3F)    // Small Tag Data
        {
            ReadDataTag(Tag, Length, tmpData);

            tmpData+=Length;
            tmpLen-=Length;
        }
        else //------------ // Large Tag Data
        {
            Length =  *((unsigned int*)&tmpData[0]);
            tmpData+=4;
            tmpLen-=4;

            ReadDataTag(Tag, Length, tmpData);

            tmpData+=Length;
            tmpLen-=Length;
        }
    }

    dprintf("\nEnd Reading Tags\n");

}// End ReadDataChunks(..)



////////////////////////////////////////////////////////////////////////////////////////
//                                                                                    //
// Program Entry Point                                                                //
//                                                                                    //
////////////////////////////////////////////////////////////////////////////////////////

void main()
{
    // For testing, we'll just read in a test flash swf file called dialog.swf
    FILE * fp = fopen("dialog.swf", "rb");

    // Basic error checking
    if (fp==NULL)
    {
        dprintf("** Error - unable to open swf file, ejecting..exiting..goodbye\n");
    }

    stSWFHeader head;

    dprintf("---\n");

    fread (    &head.sig,                    // void * buffer,
            3 * sizeof(char),            // size
            1,                            // count,
            fp );                        // FILE * stream

    fread (    &head.version,                // void * buffer,
            sizeof(head.version),        // size
            1,                            // count,
            fp );                        // FILE * stream

    fread (    &head.uncompSize,            // void * buffer,
            sizeof(head.uncompSize),    // size
            1,                            // count,
            fp );                        // FILE * stream

    dprintf("sig: %c %c %c\n",    head.sig[0], head.sig[1], head.sig[2]);
    dprintf("version: %d\n",    head.version);
    dprintf("uncom size: %d\n", head.uncompSize);


    // Just for this simple example we'll just allocate a big chunk of memory
    // to read in all our data
    int outSize = 500000;
    char * tmpIn  = new char [20000];
    char * tmpOut = new char [outSize];


    memset(tmpIn,  0, 4000);
    memset(tmpOut, 0, outSize);

    // Okay read in all the file data into our temp buffer!
    int done = 1;
    int size = 0;
    while (done)
    {
        done = (int)fread(tmpIn + size, 1, 1, fp);
        if (done)
        {
            size += 1;
        }
    }
    // Finished with the file now :)
    fclose(fp);
    

    // Now at this point we have all our data from the file in memory, so we 
    // have to determine if to just use as is, or uncompress it first, when
    // we've made this decidion, we will set them to variables
    char * uncompData        = NULL;    // Pointer to our swf data
    int    uncompDataSize    = 0;    // How many bytes or data is


    // Do we have a compressed swf?  
    if (head.sig[0]=='C' &&
        head.sig[1]=='W' &&
        head.sig[2]=='S')
    {

        int (*uncompress) (char *dest, unsigned int *destLen, const char *source, unsigned int sourceLen);
        HMODULE dll = LoadLibrary("zlib1.dll");
        uncompress = (int (*) (char *dest, unsigned int *destLen, const char *source, unsigned int sourceLen))GetProcAddress(dll, "uncompress");

        unsigned int tmpOutLen = outSize;
        int error = uncompress(tmpOut, &tmpOutLen, tmpIn, size);

        uncompDataSize = tmpOutLen;

        delete[] tmpIn;
        tmpIn = NULL;


        // Lets point to our uncompressed data
        uncompData = tmpOut;
    }
    // Not compressed
    else if (head.sig[0]=='F' &&
             head.sig[1]=='W' &&
             head.sig[2]=='S')
    {
        delete[] tmpOut;
        tmpOut = NULL;

        
        uncompData        = tmpIn;
        uncompDataSize    = size;
    }
    // Its a mutant unknown file!...bale out
    else
    {
        dprintf("*** Error, unknown file format! Ejecting, exiting..goodbye\n");
        return;
    }


    // We made it!...we made it!...we actually have the data now, so we progress
    // by now reading in the rest of the swf file format header...I think this is
    // bad to mix the compression with part of the header..maybe divided the header
    // into two parts...oh well...just the way it is.


    // Seems messy, but the swf rect is bit packed, so we do all this messy bit
    // fiddly stuff to get the data

    // Header Rect
    dprintf("---\n");
    unsigned char rectFirstByte = *uncompData;
    uncompData++;
    
    int nbits = rectFirstByte >> 3;
    head.rectBits = nbits;

    dprintf("Rect No: nbits: %d\n", nbits);

    int buf = 0;
    int numBits = 3;
    int data = rectFirstByte & 0x7;

    int i=0;
    while (i<4)
    {
        buf = 0;
        for (int j=0; j<nbits; j++)
        {
            if (data & 0x80)
            {
                buf = buf | 1;        // We have a 1 at this bit
            }
            buf = buf << 1;            // Defaults to 0, so we have 0 at this bit
            data = data<<1;

            numBits--;
            if (numBits==0)
            {
                data = *uncompData;
                uncompData++;
                numBits=8;

                uncompDataSize--;    // Total bytes left in our data
            }
        }
        buf = buf>>1;                // Took me a while to track this down, but we need
                                    // to go back one as we have found our value
        dprintf("\tbuf: %d\n", buf);                                                                            

        head.rectSizes[i] = buf;

        i++;
    }

    // Header FrameRate
    unsigned short int *tmpInt = (unsigned short int*)&uncompData[0];
    head.frameRate = *tmpInt;
    uncompData+=2;
    uncompDataSize-=2;

    // Header FrameCount
    tmpInt = (unsigned short int*)&uncompData[0];
    head.frameCount = *tmpInt;
    uncompData+=2;
    uncompDataSize-=2;

    dprintf("Head FrameRate: %d\n", head.frameRate);
    dprintf("Head FrameCount: %d\n", head.frameCount);

    ////////////////////////////////////////////////////////////////////////////////////
    // ** Dump Tags ** Dump Tags **  Dump Tags ** Dump Tags ** Dump Tags ** Dump Tags //
    // ** Dump Tags ** Dump Tags **  Dump Tags ** Dump Tags ** Dump Tags ** Dump Tags //
    // ** Dump Tags ** Dump Tags **  Dump Tags ** Dump Tags ** Dump Tags ** Dump Tags //
    ////////////////////////////////////////////////////////////////////////////////////

    // We are now at the start of the list of tags, so we go into this function, which
    // basically iterates through all the tags and dumps its name and size, one by
    // one to the debug text file..

    ReadDataChunks(&uncompData[0], uncompDataSize);


    ////////////////////////////////////////////////////////////////////////////////////

    // Well your mum always told you to tidy up before you leave!...
    if (tmpOut)
    {
        delete[] tmpOut;
        tmpOut = NULL;
    }

    if (tmpIn)
    {
        delete[] tmpIn;
        tmpIn = NULL;
    }


    dprintf("Done Parsing SWF File...thank you, goodbye :)\n");

}// End main(..)



/* output.txt

---
sig: C W S
version: 6
uncom size: 62352
---
Rect No: nbits: 15
    buf: 0
    buf: 12800
    buf: 0
    buf: 9600
Head FrameRate: 15360
Head FrameCount: 722
    Tag: 09, Name:    SETBACKGROUNDCOLOR, Length:    3, Raw-Data:0xFFFFFFFF, 0x0A
    Tag: 43, Name:            FRAMELABEL, Length:   11, Raw-Data:0x7F, 0x05
    Tag: 21, Name:       DEFINEBITSJPEG2, Length:  722, Raw-Data:0xFFFFFFBF, 0x00
    Tag: 02, Name:           DEFINESHAPE, Length:   36, Raw-Data:0xFFFFFFFF, 0x09
    Tag: 39, Name:          DEFINESPRITE, Length:   16, Raw-Data:0xFFFFFFFF, 0x09
    Tag: 39, Name:          DEFINESPRITE, Length:   34, Raw-Data:0xFFFFFF93, 0x06
    Tag: 26, Name:          PLACEOBJECT2, Length:   19, Raw-Data:0xFFFFFFFF, 0x08
    Tag: 35, Name:       DEFINEBITSJPEG3, Length: 1550, Raw-Data:0xFFFFFFBF, 0x00
    Tag: 02, Name:           DEFINESHAPE, Length:   37, Raw-Data:0xFFFFFFFF, 0x09
    Tag: 39, Name:          DEFINESPRITE, Length:   16, Raw-Data:0xFFFFFFFF, 0x09
    Tag: 39, Name:          DEFINESPRITE, Length:   35, Raw-Data:0xFFFFFF93, 0x06
    Tag: 26, Name:          PLACEOBJECT2, Length:   19, Raw-Data:0xFFFFFFFF, 0x08
    Tag: 35, Name:       DEFINEBITSJPEG3, Length:  755, Raw-Data:0xFFFFFFBF, 0x00
    Tag: 02, Name:           DEFINESHAPE, Length:   33, Raw-Data:0xFFFFFFFF, 0x09
    Tag: 39, Name:          DEFINESPRITE, Length:   16, Raw-Data:0xFFFFFFFF, 0x09
    Tag: 39, Name:          DEFINESPRITE, Length:   35, Raw-Data:0xFFFFFF93, 0x06
    Tag: 26, Name:          PLACEOBJECT2, Length:   19, Raw-Data:0xFFFFFFFF, 0x08
    Tag: 35, Name:       DEFINEBITSJPEG3, Length: 1506, Raw-Data:0xFFFFFFBF, 0x00
    Tag: 02, Name:           DEFINESHAPE, Length:   34, Raw-Data:0xFFFFFFFF, 0x09
    Tag: 39, Name:          DEFINESPRITE, Length:   16, Raw-Data:0xFFFFFFFF, 0x09
    Tag: 39, Name:          DEFINESPRITE, Length:   34, Raw-Data:0xFFFFFF93, 0x06
    Tag: 26, Name:          PLACEOBJECT2, Length:   19, Raw-Data:0x7F, 0x05
    Tag: 21, Name:       DEFINEBITSJPEG2, Length:  698, Raw-Data:0xFFFFFFBF, 0x00
    Tag: 02, Name:           DEFINESHAPE, Length:   33, Raw-Data:0xFFFFFFFF, 0x09

.. other same things...

    Tag: 01, Name:             SHOWFRAME, Length:    0, Raw-Data:0x00, 0x00
    Tag: 00, Name:                   END, Length:    0, Raw-Data:0x00, 0x00
    Tag: 00, Name:                   END, Length:    0, Raw-Data:0x00, 0x00

End Reading Tags
Done Parsing SWF File...thank you, goodbye :)

*/


 

Don't you just love coding!... :D

 

 

 

 

 

 

 

 

 

 

 

 

 

 
 Visitor: 9534626  { 209.237.238.175 } Copyright (c) 2002-2017 xbdev.net - All rights reserved.
Designated tutorial and software are the property of their respective owners.