updated/corrected documentation
[swftools.git] / src / jpeg2swf.c
index 2a70cae..aa01fbc 100644 (file)
-/* jpeg2swf.c\r
-\r
-   JPEG to SWF converter tool\r
-\r
-   Part of the swftools package.\r
-\r
-   Copyright (c) 2001 Rainer Böhme <rfxswf@reflex-studio.de>\r
\r
-   This file is distributed under the GPL, see file COPYING for details \r
-\r
-*/\r
-\r
-#include <stdio.h>\r
-#include <math.h>\r
-#include <fcntl.h>\r
-#include <jpeglib.h>\r
-#include "../lib/rfxswf.h"\r
-#include "../lib/args.h" // not really a header ;-)\r
-\r
-#define MAX_INPUT_FILES 1024\r
-#define VERBOSE(x) (global.verbose>=x)\r
-\r
-struct\r
-{ int quality;\r
-  int framerate;\r
-  int max_image_width;\r
-  int max_image_height;\r
-  int force_width;\r
-  int force_height;\r
-  int nfiles;\r
-  int verbose;\r
-  char * files[MAX_INPUT_FILES];\r
-  char * outfile;\r
-} global;\r
-\r
-TAG * MovieStart(SWF * swf,int framerate,int dx,int dy)\r
-{ TAG * t;\r
-  RGBA rgb;\r
-\r
-  memset(swf,0x00,sizeof(SWF));\r
-\r
-  swf->FileVersion       = 4;\r
-  swf->FrameRate         = (25600/framerate);\r
-  swf->MovieSize.xmax    = dx*20;\r
-  swf->MovieSize.ymax    = dy*20;\r
-\r
-  t = swf->FirstTag = InsertTag(NULL,ST_SETBACKGROUNDCOLOR);\r
-\r
-  rgb.r = rgb.g = rgb.b = rgb.a  = 0x00;\r
-  SetRGB(t,&rgb);\r
-\r
-  return t;\r
-}\r
-\r
-int MovieFinish(SWF * swf,TAG * t,char * sname)\r
-{  int handle, so = fileno(stdout);\r
-   t = InsertTag(t,ST_END);\r
-\r
-   if ((!isatty(so))&&(!sname)) handle = so;\r
-   else\r
-   { if (!sname) sname = "out.swf";\r
-     handle = open(sname,O_RDWR|O_CREAT|O_TRUNC,0666);\r
-   }\r
-   if FAILED(WriteSWF(handle,swf)) if (VERBOSE(1)) fprintf(stderr,"Unable to write output file: %s\n",sname);\r
-   if (handle!=so) close(handle);\r
-   \r
-   FreeTags(swf);\r
-   return 0;\r
-}\r
-\r
-TAG * MovieAddFrame(SWF * swf,TAG * t,char * sname,int quality,int id)\r
-{ SHAPE * s;\r
-  SRECT r;\r
-  MATRIX m;\r
-  int fs;\r
-  \r
-  struct jpeg_decompress_struct cinfo;\r
-  struct jpeg_error_mgr jerr;\r
-  LPJPEGBITS out;\r
-  FILE * f;\r
-  U8 * scanline;\r
-\r
-  if ((f=fopen(sname,"rb"))==NULL)\r
-  { if (VERBOSE(1)) fprintf(stderr,"Read access failed: %s\n",sname);\r
-    return t;\r
-  }\r
-  \r
-  cinfo.err = jpeg_std_error(&jerr);\r
-  jpeg_create_decompress(&cinfo); \r
-  jpeg_stdio_src(&cinfo,f);\r
-  jpeg_read_header(&cinfo, TRUE);\r
-  jpeg_start_decompress(&cinfo);\r
-\r
-  t = InsertTag(t,ST_DEFINEBITSJPEG2);\r
-\r
-        SetU16(t,id);  // id\r
-  \r
-        out = SetJPEGBitsStart(t,cinfo.output_width,cinfo.output_height,quality);\r
-        scanline = (U8*)malloc(4*cinfo.output_width);\r
-  \r
-        if (scanline)\r
-        { int y;\r
-          U8 * js = scanline;\r
-          for (y=0;y<cinfo.output_height;y++)\r
-          { jpeg_read_scanlines(&cinfo,&js,1);\r
-            SetJPEGBitsLines(out,(U8**)&js,1);\r
-          }\r
-          free(scanline);\r
-        }\r
-        \r
-        SetJPEGBitsFinish(out);\r
-    \r
-  t = InsertTag(t,ST_DEFINESHAPE);\r
-\r
-        NewShape(&s);\r
-        GetMatrix(NULL,&m);\r
-        m.sx = 20*0x10000;\r
-        m.sy = 20*0x10000;\r
-        fs = ShapeAddBitmapFillStyle(s,&m,id,0);\r
-        \r
-        SetU16(t,id+1); // id\r
-\r
-\r
-        r.xmin = r.ymin = 0;\r
-        r.xmax = cinfo.output_width*20;\r
-        r.ymax = cinfo.output_height*20;\r
-        SetRect(t,&r);\r
-        \r
-        SetShapeHeader(t,s);\r
-\r
-        ShapeSetAll(t,s,0,0,0,fs,0);\r
-        ShapeSetLine(t,s,r.xmax,0);\r
-        ShapeSetLine(t,s,0,r.ymax);\r
-        ShapeSetLine(t,s,-r.xmax,0);\r
-        ShapeSetLine(t,s,0,-r.ymax);\r
-        \r
-        ShapeSetEnd(t);\r
-        \r
-  t = InsertTag(t,ST_REMOVEOBJECT2);\r
-        SetU16(t,1); // depth\r
-\r
-  t = InsertTag(t,ST_PLACEOBJECT2);\r
-\r
-        GetMatrix(NULL,&m);\r
-        m.tx = (swf->MovieSize.xmax-(int)cinfo.output_width*20)/2;\r
-        m.ty = (swf->MovieSize.ymax-(int)cinfo.output_height*20)/2;\r
-        ObjectPlace(t,id+1,1,&m,NULL,NULL);\r
-\r
-  t = InsertTag(t,ST_SHOWFRAME);\r
-\r
-  jpeg_finish_decompress(&cinfo);\r
-  fclose(f);\r
-\r
-  return t;\r
-}\r
-\r
-int CheckInputFile(char * fname,char ** realname)\r
-{ struct jpeg_decompress_struct cinfo;\r
-  struct jpeg_error_mgr jerr;\r
-  FILE * f;\r
-  char * s = malloc(strlen(fname)+5);\r
-  \r
-  if (!s) exit(2);\r
-  (*realname) = s;\r
-  strcpy(s,fname);\r
-\r
-  // Check whether file exists (with typical extensions)\r
-\r
-  if ((f=fopen(s,"rb"))==NULL)\r
-  { sprintf(s,"%s.jpg",fname);\r
-    if ((f=fopen(s,"rb"))==NULL)\r
-    { sprintf(s,"%s.jpeg",fname);\r
-      if ((f=fopen(s,"rb"))==NULL)\r
-      { sprintf(s,"%s.JPG",fname);\r
-        if ((f=fopen(s,"rb"))==NULL)\r
-        { sprintf(s,"%s.JPEG",fname);\r
-          if ((f=fopen(s,"rb"))==NULL)\r
-            return 0;\r
-        }\r
-      }\r
-    }\r
-  }\r
-  \r
-  cinfo.err = jpeg_std_error(&jerr);\r
-  jpeg_create_decompress(&cinfo); \r
-  jpeg_stdio_src(&cinfo,f);\r
-  jpeg_read_header(&cinfo, TRUE);\r
-\r
-  // Get image dimensions\r
-\r
-  if (global.max_image_width<cinfo.image_width) global.max_image_width = cinfo.image_width;\r
-  if (global.max_image_height<cinfo.image_height) global.max_image_height = cinfo.image_height;\r
-  \r
-  jpeg_destroy_decompress(&cinfo);\r
-  fclose(f);\r
-\r
-  return 0;\r
-}\r
-\r
-int args_callback_option(char*arg,char*val)\r
-{ int res = 0;\r
-  if (arg[1]) res = -1;\r
-  else switch (arg[0])\r
-  { case 'q':\r
-      if (val) global.quality = atoi(val);\r
-      if ((global.quality<1)||(global.quality>100))\r
-      { if (VERBOSE(1)) fprintf(stderr,"Error: You must specify a valid quality between 1 and 100.\n");\r
-        exit(1);\r
-      }\r
-      res = 1;\r
-      break;\r
-\r
-    case 'r':\r
-      if (val) global.framerate = atoi(val);\r
-      if ((global.framerate<1)||(global.framerate>5000))\r
-      { if (VERBOSE(1)) fprintf(stderr,"Error: You must specify a valid framerate between 1 and 10000.\n");\r
-        exit(1);\r
-      }\r
-      res = 1;\r
-      break;\r
-\r
-    case 'o':\r
-      if (val) global.outfile = val; res = 1; break;\r
-\r
-    case 'v':\r
-      if (val) global.verbose = atoi(val); res = 1; break;\r
-\r
-    case 'X':\r
-      if (val) global.force_width = atoi(val); res = 1; break;\r
-\r
-    case 'Y':\r
-      if (val) global.force_height = atoi(val); res = 1; break;\r
-\r
-    case 'V':\r
-      printf("jpeg2swf - part of %s %s\n", PACKAGE, VERSION);exit(0);\r
-      \r
-    default:\r
-      res = -1;\r
-      break;\r
-  }\r
-  \r
-  if (res<0)\r
-  { if (VERBOSE(1)) fprintf(stderr,"Unknown option: -v%s\n",arg);\r
-    return 0;\r
-  }\r
-  return res;\r
-}\r
-\r
-struct options_t options[] =\r
-{{"q","quality"},\r
- {"o","output"},\r
- {"r","rate"},\r
- {"v","verbose"},\r
- {"X","width"},\r
- {"Y","height"},\r
- {"V","version"}\r
-};\r
-\r
-int args_callback_longoption(char*name,char*val) {\r
-    return args_long2shortoption(options, name, val);\r
-}\r
-\r
-int args_callback_command(char*arg,char*next)  // actually used as filename\r
-{ char * s;\r
-  if (CheckInputFile(arg,&s)<0)\r
-  { if (VERBOSE(1)) fprintf(stderr, "Unable to open input file: %s\n",arg);\r
-    free(s);\r
-  }\r
-  else\r
-  { global.files[global.nfiles] = s;\r
-    global.nfiles++;\r
-    if (global.nfiles>=MAX_INPUT_FILES)\r
-    { if (VERBOSE(1)) fprintf(stderr, "Error: Too many input files.\n");\r
-      exit(1);\r
-    }\r
-  }\r
-  return 0;\r
-}\r
-\r
-void args_callback_usage(char*name)\r
-{ fprintf(stderr,"Usage: %s imagefiles[.jpg]|[.jpeg] [...] [-options [value]]\n",name);\r
-  fprintf(stderr,"-q quality            (quality) Set JPEG compression quality (1-100)\n");\r
-  fprintf(stderr,"-r framerate          (rate) Set movie framerate (100/sec)\n");\r
-  fprintf(stderr,"-o outputfile         (output) Set name for SWF output file\n");\r
-  fprintf(stderr,"-v level              (verbose) Set verbose level (0=quiet, 1=default, 2=debug)\n");\r
-  fprintf(stderr,"-X pixel              (width) Force movie width to scale (default: autodetect)\n");\r
-  fprintf(stderr,"-Y pixel              (height) Force movie height to scale (default: autodetect)\n");\r
-  fprintf(stderr,"-V                    (version) Print version information and exit\n");\r
-}\r
-\r
-\r
-int main(int argc, char ** argv)\r
-{ SWF swf;\r
-  TAG * t;\r
-\r
-  memset(&global,0x00,sizeof(global));\r
-    \r
-  global.quality                = 60;\r
-  global.framerate              = 100;\r
-  global.verbose                = 1;\r
-  \r
-  processargs(argc, argv);\r
-\r
-  if (VERBOSE(2)) fprintf(stderr,"Processing %i file(s)...\n",global.nfiles);\r
-\r
-  t = MovieStart(&swf,global.framerate,\r
-                      global.force_width?global.force_width:global.max_image_width,\r
-                      global.force_height?global.force_height:global.max_image_height);\r
-\r
-  { int i;\r
-    for (i=0;i<global.nfiles;i++)\r
-    { if (VERBOSE(3)) fprintf(stderr,"[%03i] %s\n",i,global.files[i]);\r
-      t = MovieAddFrame(&swf,t,global.files[i],global.quality,(i*2)+1);\r
-      free(global.files[i]);\r
-    }\r
-  }\r
-\r
-  MovieFinish(&swf,t,global.outfile);\r
-\r
-  return 0;\r
-}\r
-\r
-\r
-// Old main routine\r
-\r
-/*\r
-int ConvertJPEG2SWF(char * sname,char * dname,int quality)\r
-{ RGBA rgb;\r
-  SWF swf;\r
-  TAG * t;\r
-  \r
-  SHAPE * s;\r
-  SRECT r;\r
-  MATRIX m;\r
-  int fs;\r
-  \r
-  struct jpeg_decompress_struct cinfo;\r
-  struct jpeg_error_mgr jerr;\r
-  LPJPEGBITS out;\r
-  FILE * f;\r
-  U8 * scanline;\r
-\r
-  int handle;\r
-\r
-  cinfo.err = jpeg_std_error(&jerr);\r
-  jpeg_create_decompress(&cinfo); \r
-\r
-  if ((f=fopen(sname,"rb"))==NULL)\r
-  { fprintf(stderr,"Read access failed: %s\n",sname);\r
-    return -1;\r
-  }\r
-  \r
-  jpeg_stdio_src(&cinfo,f);\r
-  jpeg_read_header(&cinfo, TRUE);\r
-  jpeg_start_decompress(&cinfo);\r
-  \r
-  memset(&swf,0x00,sizeof(SWF));\r
-\r
-  swf.FileVersion       = 4;\r
-  swf.FrameRate         = 0x1000;\r
-  swf.MovieSize.xmax    = cinfo.output_width*20;\r
-  swf.MovieSize.ymax    = cinfo.output_height*20;\r
-\r
-  printf("dx = %i, dy = %i\n",cinfo.output_width,cinfo.output_height);\r
-\r
-  t = swf.FirstTag = InsertTag(NULL,ST_SETBACKGROUNDCOLOR);\r
-\r
-        rgb.r = rgb.g = rgb.b = rgb.a  = 0x00;\r
-        SetRGB(t,&rgb);\r
-\r
-  t = InsertTag(t,ST_DEFINEBITSJPEG2);\r
-\r
-        SetU16(t,1);  // id\r
-  \r
-        out = SetJPEGBitsStart(t,cinfo.output_width,cinfo.output_height,quality);\r
-        scanline = (U8*)malloc(4*cinfo.output_width);\r
-  \r
-        if (scanline)\r
-        { int y;\r
-          U8 * js = scanline;\r
-          for (y=0;y<cinfo.output_height;y++)\r
-          { jpeg_read_scanlines(&cinfo,&js,1);\r
-            SetJPEGBitsLines(out,(U8**)&js,1);\r
-          }\r
-          free(scanline);\r
-        }\r
-        \r
-        SetJPEGBitsFinish(out);\r
-\r
-        printf("JPEG Tag-Length: %06x\n",GetDataSize(t));\r
-\r
-  t = InsertTag(t,ST_DEFINESHAPE);\r
-\r
-        NewShape(&s);\r
-        GetMatrix(NULL,&m);\r
-        m.sx = 20*0x10000;\r
-        m.sy = 20*0x10000;\r
-        rgb.r = 0xff;\r
-        fs = ShapeAddBitmapFillStyle(s,&m,1,0);\r
-//        fs = ShapeAddSolidFillStyle(s,&rgb);\r
-        \r
-        SetU16(t,2); // id\r
-        SetRect(t,&swf.MovieSize);\r
-        SetShapeHeader(t,s);\r
-\r
-        ShapeSetAll(t,s,0,0,0,fs,0);\r
-        ShapeSetLine(t,s,swf.MovieSize.xmax,0);\r
-        ShapeSetLine(t,s,0,swf.MovieSize.ymax);\r
-        ShapeSetLine(t,s,-swf.MovieSize.xmax,0);\r
-        ShapeSetLine(t,s,0,-swf.MovieSize.ymax);\r
-        \r
-        ShapeSetEnd(t);\r
-\r
-  t = InsertTag(t,ST_PLACEOBJECT2);\r
-\r
-        ObjectPlace(t,2,1,NULL,NULL,NULL);\r
-\r
-  t = InsertTag(t,ST_SHOWFRAME);\r
-  \r
-  t = InsertTag(t,ST_END);\r
-\r
-  jpeg_finish_decompress(&cinfo);\r
-  fclose(f);\r
-  \r
-  handle = open(dname,O_RDWR|O_CREAT|O_TRUNC,0666);\r
-  if FAILED(WriteSWF(handle,&swf)) fprintf(stderr,"WriteSWF() failed.\n");\r
-  close(handle);\r
-\r
-  return 0;\r
-}\r
-*/\r
-\r
+/* jpeg2swf.c
+
+   JPEG to SWF converter tool
+
+   Part of the swftools package.
+
+   Copyright (c) 2001 Rainer Böhme <rfxswf@reflex-studio.de>
+   Copyright (c) 2002,2003 Matthias Kramm <kramm@quiss.org>
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program 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 General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */
+#include <stdio.h>
+#include <math.h>
+#include <fcntl.h>
+#include <jpeglib.h>
+#include "../lib/rfxswf.h"
+#include "../lib/args.h"       // not really a header ;-)
+
+#define MAX_INPUT_FILES 1024
+#define VERBOSE(x) (global.verbose>=x)
+
+struct {
+    int quality;
+    float framerate;
+    int max_image_width;
+    int max_image_height;
+    int force_width;
+    int force_height;
+    int nfiles;
+    int verbose;
+    char *outfile;
+    int mx;
+} global;
+
+typedef struct _image {
+    char *filename;
+    int quality;
+    int width;
+    int height;
+} image_t;
+image_t image[MAX_INPUT_FILES];
+
+VIDEOSTREAM stream;
+
+TAG *MovieStart(SWF * swf, float framerate, int dx, int dy)
+{
+    TAG *t;
+    RGBA rgb;
+
+    memset(swf, 0x00, sizeof(SWF));
+
+    swf->fileVersion = 4;
+    swf->frameRate = (int)(256.0 * framerate);
+    swf->movieSize.xmax = dx * 20;
+    swf->movieSize.ymax = dy * 20;
+
+    t = swf->firstTag = swf_InsertTag(NULL, ST_SETBACKGROUNDCOLOR);
+
+    rgb.r = rgb.g = rgb.b = rgb.a = 0x00;
+    swf_SetRGB(t, &rgb);
+
+    if (global.mx) {
+       t = swf_InsertTag(t, ST_DEFINEVIDEOSTREAM);
+       swf_SetU16(t, 0xf00d);
+       swf_SetVideoStreamDefine(t, &stream, 65535, dx, dy);
+    }
+
+    return t;
+}
+
+int MovieFinish(SWF * swf, TAG * t, char *sname)
+{
+    int handle, so = fileno(stdout);
+    t = swf_InsertTag(t, ST_END);
+
+    if ((!isatty(so)) && (!sname))
+       handle = so;
+    else {
+       if (!sname)
+           sname = "output.swf";
+       handle = open(sname, O_BINARY | O_RDWR | O_CREAT | O_TRUNC, 0666);
+    }
+    if (swf_WriteSWF(handle, swf)<0) 
+           fprintf(stderr, "Unable to write output file: %s\n", sname);
+    if (handle != so)
+       close(handle);
+
+    swf_FreeTags(swf);
+    return 0;
+}
+
+int getJPEG(char*filename, int* width, int* height, RGBA**pic2)
+{
+    struct jpeg_decompress_struct cinfo;
+    struct jpeg_error_mgr jerr;
+    struct jpeg_source_mgr mgr;
+    int x,y;
+    FILE*f;
+    RGBA*pic,*js;
+    U8*buf;
+
+    if ((f=fopen(filename,"rb"))==NULL) {
+       fprintf(stderr, "rfxswf: file open error\n");
+       return 0;
+    }
+
+    cinfo.err = jpeg_std_error(&jerr);
+    jpeg_create_decompress(&cinfo); 
+    jpeg_stdio_src(&cinfo, f);
+    jpeg_read_header(&cinfo, TRUE);
+    jpeg_start_decompress(&cinfo);
+
+    pic = malloc(cinfo.output_width*cinfo.output_height*sizeof(RGBA));
+    buf = malloc(cinfo.output_width*4);
+    memset(pic, 255, cinfo.output_width*cinfo.output_height*sizeof(RGBA));
+    js = pic;
+
+    *width = cinfo.output_width;
+    *height = cinfo.output_height;
+    
+    for (y=0;y<cinfo.output_height;y++) {
+       int x;
+       jpeg_read_scanlines(&cinfo,&buf,1);
+
+       if(cinfo.out_color_space == JCS_GRAYSCALE) {
+           for(x=0;x<cinfo.output_width;x++) {
+               js[x].r = js[x].g = js[x].b = buf[x];
+           }
+       } else if(cinfo.out_color_space == JCS_RGB) {
+           for (x=0;x<cinfo.output_width;x++)
+           { 
+               js[x].r = buf[x*3+0];
+               js[x].g = buf[x*3+1];
+               js[x].b = buf[x*3+2];
+           }
+       } else if(cinfo.out_color_space == JCS_YCCK) {
+           //FIXME
+           fprintf(stderr, "Error: Can't convert YCCK to RGB.\n");
+           return -1;
+       } else if(cinfo.out_color_space == JCS_YCbCr) {
+           for(x=0;x<cinfo.output_width;x++) {
+               int y = buf[x*3+0];
+               int u = buf[x*3+1];
+               int v = buf[x*3+1];
+               js[x].r = y + ((360*(v-128))>>8);
+               js[x].g = y - ((88*(u-128)+183*(v-128))>>8);
+               js[x].b = y + ((455 * (u-128))>>8);
+           }
+       }
+       else if(cinfo.out_color_space == JCS_CMYK) 
+       { 
+           for(x=0;x<cinfo.output_width;x++) {
+                 int white = 255 - buf[x*4+3];
+                 js[x].r = white - ((buf[x*4]*white)>>8);
+                 js[x].g = white - ((buf[x*4+1]*white)>>8);
+                 js[x].b = white - ((buf[x*4+2]*white)>>8);
+           }
+       }
+       js += cinfo.output_width;
+    }
+
+    jpeg_finish_decompress(&cinfo);
+    jpeg_destroy_decompress(&cinfo);
+    
+    free(buf);
+    *pic2 = pic;
+    return 1;
+}
+
+
+int frame = 0;
+TAG *MovieAddFrame(SWF * swf, TAG * t, char *sname, int quality, 
+                  int id, int width, int height)
+{
+    SHAPE *s;
+    SRECT r;
+    MATRIX m;
+    int fs;
+
+
+    if(global.mx) {
+       int sizex, sizey;
+       RGBA * pic2;
+       SWFPLACEOBJECT obj;
+       int quant=0;
+       getJPEG(sname, &sizex, &sizey, &pic2);
+       if(sizex != stream.owidth || sizey != stream.oheight) {
+           fprintf(stderr, "All images must have the same dimensions if using -m!");
+           exit(1);
+       }
+
+       t = swf_InsertTag(t, ST_VIDEOFRAME);
+       swf_SetU16(t, 0xf00d);
+       quant = 1+(30-(30*quality)/100);
+       if(!(frame&127)) {
+           swf_SetVideoStreamIFrame(t, &stream, pic2, quant);
+       } else {
+           swf_SetVideoStreamPFrame(t, &stream, pic2, quant);
+       }
+
+       t = swf_InsertTag(t, ST_PLACEOBJECT2);
+       swf_GetPlaceObject(0, &obj);
+       if(frame==0) {
+           obj.depth = 1;
+           obj.id = 0xf00d;
+       } else {
+           obj.depth = 1;
+           obj.move = 1;
+           obj.ratio = frame;
+       }
+       swf_SetPlaceObject(t,&obj);
+
+       t = swf_InsertTag(t, ST_SHOWFRAME);
+    } else {
+       t = swf_InsertTag(t, ST_DEFINEBITSJPEG2);
+       swf_SetU16(t, id);              // id
+       swf_SetJPEGBits(t,sname,quality);
+
+       t = swf_InsertTag(t, ST_DEFINESHAPE);
+       swf_ShapeNew(&s);
+       swf_GetMatrix(NULL, &m);
+       m.sx = 20 * 0x10000;
+       m.sy = 20 * 0x10000;
+       fs = swf_ShapeAddBitmapFillStyle(s, &m, id, 0);
+       swf_SetU16(t, id + 1);  // id
+       r.xmin = r.ymin = 0;
+       r.xmax = width * 20;
+       r.ymax = height * 20;
+       swf_SetRect(t, &r);
+       swf_SetShapeHeader(t, s);
+       swf_ShapeSetAll(t, s, 0, 0, 0, fs, 0);
+       swf_ShapeSetLine(t, s, r.xmax, 0);
+       swf_ShapeSetLine(t, s, 0, r.ymax);
+       swf_ShapeSetLine(t, s, -r.xmax, 0);
+       swf_ShapeSetLine(t, s, 0, -r.ymax);
+       swf_ShapeSetEnd(t);
+
+       t = swf_InsertTag(t, ST_REMOVEOBJECT2);
+       swf_SetU16(t, 1);               // depth
+
+       t = swf_InsertTag(t, ST_PLACEOBJECT2);
+       swf_GetMatrix(NULL, &m);
+       m.tx = (swf->movieSize.xmax - (int) width * 20) / 2;
+       m.ty = (swf->movieSize.ymax - (int) height * 20) / 2;
+       swf_ObjectPlace(t, id + 1, 1, &m, NULL, NULL);
+
+       t = swf_InsertTag(t, ST_SHOWFRAME);
+    }
+    frame++;
+
+    return t;
+}
+
+int CheckInputFile(image_t* i, char *fname, char **realname)
+{
+    struct jpeg_decompress_struct cinfo;
+    struct jpeg_error_mgr jerr;
+    FILE *f;
+    char *s = malloc(strlen(fname) + 5);
+    int width, height;
+
+    if (!s)
+       exit(2);
+    (*realname) = s;
+    strcpy(s, fname);
+
+    // Check whether file exists (with typical extensions)
+
+    if ((f = fopen(s, "rb")) == NULL) {
+       sprintf(s, "%s.jpg", fname);
+       if ((f = fopen(s, "rb")) == NULL) {
+           sprintf(s, "%s.jpeg", fname);
+           if ((f = fopen(s, "rb")) == NULL) {
+               sprintf(s, "%s.JPG", fname);
+               if ((f = fopen(s, "rb")) == NULL) {
+                   sprintf(s, "%s.JPEG", fname);
+                   if ((f = fopen(s, "rb")) == NULL)
+                       return -1;
+               }
+           }
+       }
+    }
+
+    cinfo.err = jpeg_std_error(&jerr);
+    jpeg_create_decompress(&cinfo);
+    jpeg_stdio_src(&cinfo, f);
+    jpeg_read_header(&cinfo, TRUE);
+
+    width = cinfo.image_width;
+    height = cinfo.image_height;
+
+    i->width = width;
+    i->height = height;
+
+    // Get image dimensions
+
+    if (global.max_image_width < width)
+       global.max_image_width = width;
+    if (global.max_image_height < height)
+       global.max_image_height = height;
+
+    jpeg_destroy_decompress(&cinfo);
+    fclose(f);
+
+    return 0;
+}
+
+int args_callback_option(char *arg, char *val)
+{
+    int res = 0;
+    if (arg[1])
+       res = -1;
+    else
+       switch (arg[0]) {
+       case 'q':
+           if (val)
+               global.quality = atoi(val);
+           if ((global.quality < 1) ||(global.quality > 100)) {
+               if (VERBOSE(1))
+                   fprintf(stderr,
+                           "Error: You must specify a valid quality between 1 and 100.\n");
+               exit(1);
+           }
+           res = 1;
+           break;
+
+       case 'r':
+           if (val)
+               global.framerate = atof(val);
+           if ((global.framerate < 1.0/256) || (global.framerate >= 256.0)) {
+               if (VERBOSE(1))
+                   fprintf(stderr,
+                           "Error: You must specify a valid framerate between 1 and 10000.\n");
+               exit(1);
+           }
+           res = 1;
+           break;
+
+       case 'o':
+           if (val)
+               global.outfile = val;
+           res = 1;
+           break;
+
+       case 'v':
+           if (val)
+               global.verbose = atoi(val);
+           res = 1;
+           break;
+
+       case 'X':
+           if (val)
+               global.force_width = atoi(val);
+           res = 1;
+           break;
+
+       case 'm':
+           global.mx = 1;
+           return 0;
+
+       case 'Y':
+           if (val)
+               global.force_height = atoi(val);
+           res = 1;
+           break;
+
+       case 'V':
+           printf("jpeg2swf - part of %s %s\n", PACKAGE, VERSION);
+           exit(0);
+
+       default:
+           res = -1;
+           break;
+       }
+
+    if (res < 0) {
+       if (VERBOSE(1))
+           fprintf(stderr, "Unknown option: -%s\n", arg);
+       exit(1);
+       return 0;
+    }
+    return res;
+}
+
+static struct options_t options[] = {
+{"o", "output"},
+{"m", "mx"},
+{"q", "quality"},
+{"r", "rate"},
+{"X", "width"},
+{"Y", "height"},
+{"v", "verbose"},
+{"V", "version"},
+{0,0}
+};
+
+int args_callback_longoption(char *name, char *val)
+{
+    return args_long2shortoption(options, name, val);
+}
+
+int args_callback_command(char *arg, char *next)       // actually used as filename
+{
+    char *s;
+    image_t* i = &image[global.nfiles];
+    if (CheckInputFile(i, arg, &s) < 0) {
+       if (VERBOSE(1))
+           fprintf(stderr, "Unable to open input file: %s\n", arg);
+       free(s);
+    } else {
+       i->filename = s;
+       i->quality = global.quality;
+       global.nfiles++;
+       if (global.nfiles >= MAX_INPUT_FILES) {
+           if (VERBOSE(1))
+               fprintf(stderr, "Error: Too many input files.\n");
+           exit(1);
+       }
+    }
+    return 0;
+}
+
+void args_callback_usage(char *name)
+{
+    printf("\n");
+    printf("Usage: %s [-options [value]] imagefiles[.jpg]|[.jpeg] [...]\n", name);
+    printf("\n");
+    printf("-o , --output <outputfile>     Explicitly specify output file. (otherwise, output.swf will be used)\n");
+    printf("-m , --mx                      Use Flash MX H.263 compression (use for correlated images)\n");
+    printf("-q , --quality <quality>       Set compression quality (1-100, 1=worst, 100=best)\n");
+    printf("-r , --rate <framerate>         Set movie framerate (frames per second)\n");
+    printf("-X , --width <width>           Force movie width to <width> (default: autodetect)\n");
+    printf("-Y , --height <height>         Force movie height to <height> (default: autodetect)\n");
+    printf("-v , --verbose <level>         Set verbose level to <level> (0=quiet, 1=default, 2=debug)\n");
+    printf("-V , --version                 Print version information and exit\n");
+    printf("\n");
+}
+
+
+int main(int argc, char **argv)
+{
+    SWF swf;
+    TAG *t;
+
+    memset(&global, 0x00, sizeof(global));
+
+    global.quality = 60;
+    global.framerate = 1.0;
+    global.verbose = 1;
+
+    processargs(argc, argv);
+
+    if (VERBOSE(2))
+       fprintf(stderr, "Processing %i file(s)...\n", global.nfiles);
+
+    t = MovieStart(&swf, global.framerate,
+                  global.force_width ? global.force_width : global.
+                  max_image_width,
+                  global.force_height ? global.force_height : global.
+                  max_image_height);
+
+    {
+       int i;
+       for (i = 0; i < global.nfiles; i++) {
+           if (VERBOSE(3))
+               fprintf(stderr, "[%03i] %s (%i%%, 1/%i)\n", i,
+                       image[i].filename, image[i].quality);
+           t = MovieAddFrame(&swf, t, image[i].filename, image[i].quality,
+                             (i * 2) + 1, 
+                             image[i].width, image[i].height);
+           free(image[i].filename);
+       }
+    }
+
+    MovieFinish(&swf, t, global.outfile);
+
+    return 0;
+}