[BACK]Return to libmil.c CVS log [TXT][DIR] Up to [jp.NetBSD.org] / othersrc / mgl / mgl2 / ext

File: [jp.NetBSD.org] / othersrc / mgl / mgl2 / ext / libmil.c (download)

Revision 1.3, Sat Jul 28 16:55:16 2001 UTC (16 years, 7 months ago) by suz
Branch: MAIN
CVS Tags: HEAD
Changes since 1.2: +3 -3 lines



o mil/mil.c (ext/libmil.c)

  -- read_gif_file の clese し忘れの 修正 (有野さん)
  -- png で 1bpp 等のファイルが正しく読めない問題の対応
  -- gif ファイルで 知らないレコードタイプが来たら それまでのデータを
     使うようにして、エラーにしないようにした( 透過GIF?)

/*
 * MIL -- MobileGear Image Loader -
 * Copyright (C) 1998, 1999
 *      Yukihiko Sano (yukihiko@yk.rim.or.jp)
 *      Koji Suzuki (suz@at.sakura.ne.jp)
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY YUKIHIKO SANO ``AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE TERRENCE R. LAMBERT BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 */

//#define USE_JPEG
//#define USE_GIF
//#define USE_PNG

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <string.h>
#include <fcntl.h>
#include <errno.h>
#include <unistd.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/param.h>
#ifdef USE_JPEG
#  include "jpeglib.h"
#endif /* USE_JPEG */
#ifdef USE_GIF
#  include "gif_lib.h"
#endif /* USE_GIF */
#ifdef USE_PNG
#  include "png.h"
#endif /* USE_PNG */
#if defined (USE_JPEG) || defined (USE_PNG)
#  include <setjmp.h>
#endif /* USE_JPEG || USE_PNG */
#include "mgl2.h"
#include "mglcol.h"

#define  MIL_VER     "0.4"
#define  DEBUG       0

struct screen *mgl_read_screen_file(char *f, int *w, int *h);
struct screen *mgl_conv_screen_from_data(char *f, int len, int *w, int *h);
int mgl_bitblt_scale(struct screen *dst,int dx,int dy,int dxs,int dys
		,struct screen *src,int sx,int sy,int sxs,int sys);

static int cont_jpeg(int type,void *fd, int len, int *w, int *h, struct screen **image);
static int read_file(char *f, int *w, int *h, struct screen **image, int len);


#ifndef STK_MIL
#define STK_MIL STK_GENERIC_FULLCOLOR
#endif
/* 拡大縮小をすると、FULLCOROR でないと汚くなる。
 * メモリ優先なら、STK_NATIVE とかにしても良い。
 */
#ifndef MAKE_BITMAP_LIMIT
#define MAKE_BITMAP_LIMIT  (800*600)
#endif
/* 拡大縮小用の screen を作るかどうか。
 * メモリ優先なら、0 にしてもよい。
 */


#define MAX_MAGNITUDE   9
#define INIT_MAG	4
int magnitude = INIT_MAG;
int magnitude_tab[] = {250, 353,  500,  707,  1000
			,  1414, 2000,  2828, 4000, 5657};
#define MAG(x) ((x) * magnitude_tab[magnitude]/1000)
#define RMAG(x) ((x) * 1000/magnitude_tab[magnitude])
/* 拡大縮小の率を magnitude_tab で決めている。
   適当に変更しても、たぶん動く。
*/

#define  SUP_MGR     "MGR"
#define  SUP_JPEG    "JPEG"
#define  SUP_GIF     "GIF"
#define  SUP_PNG     "PNG"

#define  MAX_SUP     (4 + 1)

#ifdef USE_GIF
extern int _GifError;

/* The way Interlaced image should. */
static int InterlacedOffset[] = { 0, 4, 2, 1 };
/* be read - offsets and jumps... */
static int InterlacedJumps[] = { 8, 8, 4, 2 };
static int ColorMapSize = 0;
static int BackGround = 0;
static ColorMapObject *ColorMap;

gif_put_pixstream(int x,int y,GifRowType buf,int length) {
	GifColorType *ColorMapEntry;
	int i;
	int r,g,b;

	if ((length < 0) || ( length > 4096)) {
		return;
	}
    {
	int mgl_image[length];
	for (i=0; i< length; i++) {
        	ColorMapEntry = &ColorMap->Colors[*buf++];
		r = (int)ColorMapEntry->Red >> 4;
		g = (int)ColorMapEntry->Green >> 4;
		b = (int)ColorMapEntry->Blue >> 4;
		mgl_image[i] = mc_from_rgb(packRGB(r,g,b)) | COLOR_DITHER;
	}
	put_pixstream(x,y,mgl_image,length,DIR_NORTH);
    }
}

struct gif_buffer {
	char *buf;
	int len;
};

static int read_gif_buffer(GifFileType *gf, GifByteType *buf, int len) {
	struct gif_buffer *gb;
	int rlen;

	gb = (struct gif_buffer *)(gf->UserData);
	rlen = len;
	if (gb->len < len) rlen = gb->len;
	memcpy(buf,gb->buf,rlen);
	gb->buf += rlen;
	gb->len -= rlen;
	return rlen;
}

static int read_gif_file(char *f, int *w, int *h, struct screen **image, int len)
{
    int Width,Height,Col,Row,ExtCode;
    GifFileType *GifFile;
    GifRowType  gif_image;
    GifRecordType RecordType;
    GifByteType *Extension;
    struct gif_buffer gif_buffer;
    char *p;
    int i, j;
    int width,height;

    /* Gif File Check */
    if (len > 0) {
	if (len < 3 || strncmp(f,"GIF",3)) {
		return -1;
	}
	gif_buffer.buf = f;
	gif_buffer.len = len;
        GifFile = DGifOpen(&gif_buffer, read_gif_buffer);
//fprintf(stderr,"read_gif_file -- data %d\n",len);
    } else {
        GifFile = DGifOpenFileName(f);
//fprintf(stderr,"read_gif_file -- file %s -> %x\n",f,GifFile);
    }

    if(!GifFile){
        return -1;
    }

    *w = width =  GifFile->SWidth;
    *h = height = GifFile->SHeight;

//fprintf(stderr,"read_gif_file w h = %d %d\n",*w,*h);

    gif_image = (GifRowType)malloc(width * sizeof(GifPixelType));
    *image = create_memscreen(width,height,NULL,STK_MIL,0);
    if (!image) {
    	*image = create_memscreen(width,height,NULL,STK_NATIVE,0);
    }

//fprintf(stderr,"read_gif_file create_memscreen = %x\n",*image);

    if (*image) push_screen(*image);

    if (!(*image) || !gif_image) {
err:
	if (*image) {
		pop_screen();
		free_screen(*image);
	}
	if (gif_image) free(gif_image);
        if(DGifCloseFile(GifFile) == GIF_ERROR){
                PrintGifError();
        }
	return -1;
    }

    /* Scan the content of the GIF file and load the image(s) in: */
    do {
        if(DGifGetRecordType(GifFile, &RecordType) == GIF_ERROR) {
		RecordType = TERMINATE_RECORD_TYPE;
        }
        switch(RecordType){
        case IMAGE_DESC_RECORD_TYPE:
            if(DGifGetImageDesc(GifFile) == GIF_ERROR){
		goto err;
            }
            /* Image Position relative to Screen.  */

            Row = GifFile->Image.Top;
            Col = GifFile->Image.Left;
            Width = GifFile->Image.Width;
            Height = GifFile->Image.Height;
	    BackGround = GifFile->SBackGroundColor;
    	    ColorMap = GifFile->Image.ColorMap ? GifFile->Image.ColorMap
                			: GifFile->SColorMap;
    			ColorMapSize = ColorMap->ColorCount;
            if((Col + Width> width) || (Row + Height > height)) {
                fprintf(stderr,
                       "Image is not confined to screen dimension, aborted.\n");
		goto err;
            }
            if(GifFile->Image.Interlace){
                /* Need to perform 4 passes on the images: */
                for(i = 0; i < 4; i++){
                    for(j = Row + InterlacedOffset[i];
                        j < Row + Height; j += InterlacedJumps[i]){
                        if(DGifGetLine(GifFile, gif_image,Width) == GIF_ERROR){
			    goto err;
                        }
			gif_put_pixstream(Col,j,gif_image,Width);
                    }
                }
            } else{
                for(i = 0; i < Height; i++){
                    if(DGifGetLine(GifFile, gif_image,Width) == GIF_ERROR) {
			goto err;
                    }
		    gif_put_pixstream(Col,Row++,gif_image,Width);
                }
            }
            break;
        case EXTENSION_RECORD_TYPE:
            /* Skip any extension blocks in file: */
            if(DGifGetExtension(GifFile, &ExtCode, &Extension) == GIF_ERROR){
		goto err;
            }
            while(Extension != NULL) {
                if(DGifGetExtensionNext(GifFile, &Extension) == GIF_ERROR){
			goto err;
                }
            }
            break;
        case TERMINATE_RECORD_TYPE:
            break;
        default:    /* Should be traps by DGifGetRecordType. */
            break;
        }
    } while(RecordType != TERMINATE_RECORD_TYPE);



    pop_screen();
    free(gif_image);
    DGifCloseFile(GifFile);
    return 0;
}
#endif /* USE_GIF */

#ifdef USE_JPEG

#define JPEG_FILE_INPUT		0
#define JPEG_BUFFER_INPUT	1

static void init_source (j_decompress_ptr cinfo) {
//fprintf(stderr,"init_source\n");
}

static int fill_input_buffer (j_decompress_ptr cinfo) {
//fprintf(stderr,"fill_input_buffer\n");
  return FALSE;
}

static void skip_input_data (j_decompress_ptr cinfo, long num_bytes) {
  struct mil_source *src = (struct mil_source *)cinfo->src;
//fprintf(stderr,"skip_input_data %d\n",num_bytes);
}

static void term_source (j_decompress_ptr cinfo) {
  struct jpeg_source_mgr *src = cinfo->src;
//fprintf(stderr,"jpeg_buffer_src .. term_source \n");
  src->next_input_byte = NULL;
  src->bytes_in_buffer = 0;
}

static void jpeg_buffer_src (j_decompress_ptr cinfo, char *buf, int len) {
  struct jpeg_source_mgr *src;

  if (cinfo->src == NULL) {	/* first time for this JPEG object? */
    cinfo->src = (struct jpeg_source_mgr *)
      (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT,
				  sizeof(struct jpeg_source_mgr));
  }

//fprintf(stderr,"jpeg_buffer_src\n");
  src = cinfo->src;
  src->init_source = init_source;
  src->fill_input_buffer = fill_input_buffer;
  src->skip_input_data = skip_input_data;
  src->resync_to_restart = jpeg_resync_to_restart; /* use default method */
  src->term_source = term_source;
  src->next_input_byte = buf;
  src->bytes_in_buffer = len;
}

static int read_jpeg_file(char *f, int *w, int *h, struct screen **image,int len)
{
    FILE *fd = NULL;
    unsigned char sig[3];
    int ret;

//fprintf(stderr,"start read jpeg %d \n",len);
    /* File Open */
    if (len > 0) {
	strncpy(sig,f,3);
	if (sig[0] != 0xff || sig[1] != 0xd8) {
//fprintf(stderr,"signature error %02x %02x\n",f[0],f[1]);
		return -1;
	}
        ret = cont_jpeg(JPEG_BUFFER_INPUT, f, len , w, h, image);
    } else {
    	fd = fopen(f, "rb");
    	if(fd == (FILE *)NULL){
            fprintf(stderr, "open %s failed.\n", f);
            return -1;
    	}
	if (fread(sig,1,3,fd) != 3) {
        	fclose(fd);
		return -1;
	}
	fseek(fd,0,0);
	if (sig[0] != 0xff || sig[1] != 0xd8) {
        	fclose(fd);
		return -1;
	}
        /* File Read */
        ret = cont_jpeg(JPEG_FILE_INPUT, fd, 0, w, h, image);
        fclose(fd);
    }
    if(!ret) {
        return -1;
    }
    return 0;
}
#endif /* USE_JPEG */

#ifdef USE_PNG

struct png_source {
	char *buf;
	int len;
};

static void png_read_source(png_structp png_ptr, png_bytep buf, png_size_t len) {
	struct png_source *src = png_get_io_ptr(png_ptr);
	if (len > src->len) {
		memcpy(buf,src->buf,src->len);
		png_error(png_ptr, "input over run");
		src->buf += src->len;
		src->len = 0;
	} else {
		memcpy(buf,src->buf,len);
		src->buf += len;
		src->len -= len;
	}
}


#define MAGIC_OFFSET 4

static int read_png_file(char *f, int *w, int *h, struct screen **image,int len)
{
    FILE *fp;
    char buf[MAGIC_OFFSET];
    int ret;
    int i, y;
    int r, g, b, mc;
    png_structp png_ptr;
    png_infop info_ptr, end_info;
    png_bytep *row_pointers = (png_bytep *)0;
    png_bytep png_image, p;
    int *mgl_image;
    struct png_source png_source;

    if (len > 0) {
 	if (len < MAGIC_OFFSET) {
		return -1;
	}
        if(png_sig_cmp(f, 0, MAGIC_OFFSET)) {
	     return -1;
        }
	png_source.buf = f + MAGIC_OFFSET;
	png_source.len = len - MAGIC_OFFSET;

    } else {
    	/* File Check */
    	fp = fopen(f, "rb");
    	if(fp == (FILE *)NULL) {
            fprintf(stderr, "open %s failed.\n", f);
            return -1;
        }

        if(fread(buf, 1, MAGIC_OFFSET, fp) != MAGIC_OFFSET){
            fprintf(stderr, "read %s failed.\n", f);
	    goto err1;
        }

        if(png_sig_cmp(buf, 0, MAGIC_OFFSET)){
	     goto err1;
        }
    }

    /* memory allocate */
    png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING,
                                     (png_voidp)NULL, (png_error_ptr)NULL,
                                     (png_error_ptr)NULL);
    if(!png_ptr){
	goto err1;
    }

    info_ptr = png_create_info_struct(png_ptr);
    if(!info_ptr){
	goto err2;
    }

    end_info = png_create_info_struct(png_ptr);
    if(!end_info){
	goto err2;
    }

    /* error handling */
    if(setjmp(png_ptr->jmpbuf)){
	goto err2;
    }

    if (len > 0) {
	png_set_read_fn(png_ptr, &png_source, png_read_source);
    } else {
    	/* File Pointer Set */
    	png_init_io(png_ptr, fp);
    }

    /* File Pointer Offset */
    png_set_sig_bytes(png_ptr, MAGIC_OFFSET);

    /* read the file information */
    png_read_info(png_ptr, info_ptr);

    /* 1, 2, 4bit -> 8bit */
    if(info_ptr->bit_depth < 8){
        png_set_packing(png_ptr);
    }

    /* 8bit with colormap -> 24bit */
    if(info_ptr->color_type == PNG_COLOR_TYPE_PALETTE &&
       info_ptr->bit_depth <= 8){
        /* png_set_palette_to_rgb(png_ptr); */
        png_set_expand(png_ptr);
    }

#if 1
    /* gray -> 8bit */
    if(info_ptr->color_type == PNG_COLOR_TYPE_GRAY &&
       info_ptr->bit_depth < 8){
        /* png_set_gray_1_2_4_to_8(png_ptr); */
        png_set_expand(png_ptr);
    }
#endif

    /* gray -> 24bit */
    if(info_ptr->color_type == PNG_COLOR_TYPE_GRAY ||
       info_ptr->color_type == PNG_COLOR_TYPE_GRAY_ALPHA){
          png_set_gray_to_rgb(png_ptr);
    }

#if 0
    /* ??? */
    if(png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)){
        /* png_set_tRNS_to_alpha(png_ptr); */
        png_set_expand(png_ptr);
    }

    /* Cut alpha channel */
    if(info_ptr->color_type & PNG_COLOR_MASK_ALPHA){
        /* png_set_invert_alpha(png_ptr); */
        png_set_strip_alpha(png_ptr);
    }
#endif

    /* 16bit -> 8bit/color */
    if(info_ptr->bit_depth == 16){
        png_set_strip_16(png_ptr);
    }

    /* RGB -> RGBA */
#if 0
    if((info_ptr->color_type == PNG_COLOR_TYPE_PALETTE ||
       info_ptr->color_type == PNG_COLOR_TYPE_RGB) &&
       info_ptr->bit_depth <= 8){
        png_set_filler(png_ptr, 0xff, PNG_FILLER_AFTER);
    }
#endif
    if(info_ptr->pixel_depth != 32){
        png_set_filler(png_ptr, 0xff, PNG_FILLER_AFTER);
    }

    /* Update info structure */
    png_read_update_info(png_ptr, info_ptr);

    *w  =  (int)info_ptr->width;
    *h  = (int)info_ptr->height;

    png_image = (png_bytep)malloc(info_ptr->rowbytes * info_ptr->height);
    if(!png_image){
	goto err2;
    }

    /* File Read */
    row_pointers = (png_bytep *)malloc(info_ptr->height * sizeof(png_bytep *));
    if(!row_pointers){
	goto err3;
    }
    for(i = 0; i < info_ptr->height; ++i){
        row_pointers[i] = png_image + i * info_ptr->rowbytes;
    }

    png_read_image(png_ptr, row_pointers);

    /* Read End */
    png_read_end(png_ptr, end_info);

#if 0
printf("width            [%d]\n", info_ptr->width);
printf("height           [%d]\n", info_ptr->height);
printf("rowbytes         [%d]\n", info_ptr->rowbytes);
printf("bit_depth        [%d]\n", info_ptr->bit_depth);
printf("num_palette      [%d]\n", info_ptr->num_palette);
printf("color_type       [%d]\n", info_ptr->color_type);
printf("compression_type [%d]\n", info_ptr->compression_type);
printf("filter_type      [%d]\n", info_ptr->filter_type);
printf("interlace_type   [%d]\n", info_ptr->interlace_type);
printf("pixel_depth      [%d]\n", info_ptr->pixel_depth);
#endif

    /* File Close */
    if (len == 0)
        fclose(fp);

    /* Clear */
    png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
    free(row_pointers);

    /* mgl2 image */
    *image = create_memscreen(*w, *h, NULL,STK_MIL,0);
    if(!(*image)){
        *image = create_memscreen(*w, *h, NULL,STK_NATIVE,0);
    }
    if(!(*image)){
        free(png_image);
        return -1;
    }

    mgl_image = (int *)malloc(*w * sizeof(int));
    if(!mgl_image){
        free(png_image);
	free_screen(*image);
        return -1;
    }

    push_screen(*image);

    p = png_image;
    for(y = 0 ; y < *h ; y++){
        for(i = 0 ; i < *w ; i++) {
            r = (*p++) & 0xff;
            g = (*p++) & 0xff;
            b = (*p++) & 0xff;
            r >>= 4;
            g >>= 4;
            b >>= 4;
            mgl_image[i] = mc_from_rgb(packRGB(r, g, b)) | COLOR_DITHER;
            *p++;
        }
        put_pixstream(0, y, mgl_image, *w, DIR_NORTH);
    }

    pop_screen();

    /* free the structures */
    free(png_image);
    free(mgl_image);

    return 0;

err3:
        free(png_image);
err2:
        png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
err1:
	if (len == 0) fclose(fp);
        return -1;
}
#endif /* USE_PNG */

/* image  : 8bit Gray */
/* bitmap : 2bit Gray */
static int read_file(char *f, int *w, int *h, struct screen **image, int len)
{
    int ret = -1;

#if defined (USE_GIF)
    ret = read_gif_file(f, w, h, image, len);
    if(!ret){
        return(ret);
    }
#endif /* USE_GIF */

#if defined (USE_PNG)
    ret = read_png_file(f, w, h, image, len);
    if(!ret){
        return(ret);
    }
#endif /* USE_PNG */

#if defined (USE_JPEG)
    ret = read_jpeg_file(f, w, h, image, len);
    if(!ret){
        return(ret);
    }
#endif /* USE_JPEG */

    return(ret);
}

struct screen *mgl_read_screen_file(char *f, int *w, int *h) {
	struct screen *s;
	int ret;
	ret = read_file(f, w, h, &s, 0);
	if (ret == 0) {
		return s;
	}
	return NULL;
}

struct screen *mgl_conv_screen_from_data(char *f, int len, int *w, int *h) {
	struct screen *s;
	int ret;
	if (len <= 0) {
		return NULL;
	}
	ret = read_file(f, w, h, &s, len);
	if (ret == 0) {
		return s;
	}
	return NULL;
}

#ifdef USE_JPEG
#include <setjmp.h>

struct return_error_mgr{
  struct jpeg_error_mgr pub;
  jmp_buf setjmp_buffer;
};

typedef struct return_error_mgr * return_error_ptr;

void error_return (j_common_ptr cinfo)
{
  /* cinfo->err really points to a return_error_mgr struct, so coerce pointer */
  return_error_ptr returnerr = (return_error_ptr) cinfo->err;

  /* Always display the message. */
  /* We could postpone this until after returning, if we chose. */
  (*cinfo->err->output_message) (cinfo);

  /* Return control to the setjmp point */
  longjmp(returnerr->setjmp_buffer, 1);
}

static int cont_jpeg(int type, void *fd, int len, int *w, int *h, struct screen **image)
{
    JSAMPROW buffer[1];
    int row_stride;
    struct jpeg_decompress_struct cinfo;
    struct return_error_mgr jerr;
    JSAMPLE *jpeg_image,*p;
    int *mgl_image;
    int  pl,width,height;
    int i,y;

    cinfo.err = jpeg_std_error(&jerr.pub);
#if 1
    cinfo.err->error_exit = error_return;

    if(setjmp(jerr.setjmp_buffer)){
       jpeg_destroy_decompress(&cinfo);
       return 0;
    }
#endif

    jpeg_create_decompress(&cinfo);

    if (type == JPEG_BUFFER_INPUT) {
//fprintf(stderr,"jpeg_buffer_src\n");
    	jpeg_buffer_src(&cinfo, fd, len);
    } else {
//fprintf(stderr,"jpeg_stdio_src\n");
    	jpeg_stdio_src(&cinfo, fd);
    }

    /* header read */
    jpeg_read_header(&cinfo, TRUE);

#if DEBUG
    /* Width and height of image */
    printf("image_width     [%d]\n",cinfo.image_width);
    printf("image_height    [%d]\n",cinfo.image_height);
    /* Number of color components */
    printf("num_components  [%d]\n",cinfo.num_components);
    /* Colorspace of image */
    printf("jpeg_color_space[%d]\n",cinfo.jpeg_color_space);
#endif

    cinfo.out_color_space = JCS_RGB;
    cinfo.dither_mode = JDITHER_NONE;
    cinfo.quantize_colors = FALSE;

    jpeg_calc_output_dimensions(&cinfo);

#if DEBUG
    /* Actual dimensions of output image. */
    printf("output_width        [%d]\n",cinfo.output_width);
    printf("output_height       [%d]\n",cinfo.output_height);
    /* Number of color components in out_color_space. */
    printf("out_color_components[%d]\n",cinfo.out_color_components);
    /* Number of color components returned. */
    printf("output_components   [%d]\n",cinfo.output_components);
    /* Recommended height of scanline buffer. */
    printf("rec_outbuf_height   [%d]\n",cinfo.rec_outbuf_height);
#endif

    row_stride = cinfo.output_width * cinfo.output_components;

    *w  = width =  (int)cinfo.output_width;
    *h  = height = (int)cinfo.output_height;
    pl  = (int)cinfo.output_components;
    *image = create_memscreen(width, height, NULL,STK_MIL,0);
    if (!image) {
        *image = create_memscreen(width, height, NULL,STK_NATIVE,0);
    }

    mgl_image = (int *)malloc(width * sizeof(int));
    jpeg_image = (JSAMPLE *)malloc(width * pl);

    if(!jpeg_image || !(*image) || !mgl_image) {
//printf("%x %x %x\n",jpeg_image,mgl_image,*image);
        perror("malloc");
        /* End */
        jpeg_finish_decompress(&cinfo);
        jpeg_destroy_decompress(&cinfo);

	if (*image) {
		free_screen(*image);
	}
	if (mgl_image) {
		free(mgl_image);
	}
        return 0;
    }

    /* Start decompressor */
    jpeg_start_decompress(&cinfo);

    buffer[0] = (JSAMPROW) jpeg_image;
    push_screen(*image);
    y = 0;
    while(cinfo.output_scanline < cinfo.output_height) {
	jpeg_read_scanlines(&cinfo, buffer, (JDIMENSION)1);
	{
		int r,g,b,mc;
		p = jpeg_image;
		for (i=0; i< width; i++) {
			r = (*p++) & 0xff;
			g = (*p++) & 0xff;
			b = (*p++) & 0xff;
			r >>= 4;
			g >>= 4;
			b >>= 4;
			mgl_image[i] = mc_from_rgb(packRGB(r,g,b)) | COLOR_DITHER;
		}
	}
	put_pixstream(0,y++,mgl_image,width,DIR_NORTH);
    }
    pop_screen();

    /* End */
    jpeg_finish_decompress(&cinfo);
    jpeg_destroy_decompress(&cinfo);

    free(jpeg_image);
    return 1;
}
#endif /* USE_JPEG */

/*
拡大縮小

dst の領域を計算し、おもみを付けて元値を足す。

   +----------+----------+----------+
   |          |          |          |
   |      +-------------------+     |
   |      | 1 |    4     | 7  |     |
   +------|---+----------+----|-----+
   |      |   |          |    |     |
   |      | 2 |    5     | 8  |     |
   |      |   |          |    |     |
   +------|---+----------+----|-----+
   |      | 3 |    6     | 9  |     |
   |      +-------------------+     |
   |          |          |          |
   +----------+----------+----------+

たぶん、微妙な拡大縮小でもうまく動くと思う。
ディザ付きの画像は、ディザも拡大してしまうので、
内部 screen を FULLCOLOR にした方がよい。

   +----------+----------+----------+
   |  +--+    |          |          |
   |  |10|    |          |          |
   |  +--+    |  +-----+ |          |
   +----------+--|-----|-+----------+
   |          |  |     | |          |
   |          |  +-----+ |          |
   |          |          |          |
   +----------+----------+----------+
   |      +-----+        |          |
   |      |   | |        |          |
   |      +-----+        |          |
   +----------+----------+----------+

このケースは、うまく動かないので、( 10 の処理がある)

ちなみに、hue は、微妙な計算をしていない。


------------

オリジナルは、次のような構造をしていて、
すなおに作ると非常に遅い。すこしでも使えるようにチューニングしてみた。

   each (y) {
     each (x) {
	 get_pixel() を沢山 (上下左右に重なりがある)

         push_screen()
         put_pixel(x,y)
         pop_screen()
     }
   }
       
縮小 convert (570,398) -> (402,281) -   9.06/(dot)
拡大 convert (570,398) -> (805,562) -   7.61/(dot)


OPT1

まず、x と x+1 での重なりの分を最適化
(7)(8)(9) の計算のときに次の (1)(2)(3) もついでにやる方法。


### convert (570,398) -> (402,281) -   7.51/(dot)
### convert (570,398) -> (805,562) -   6.35/(dot)

残念ながら少ししか早くならない。

OPT2

1 dot 書く毎に push_screen/pop_screen した上で、put_pixel しているので、
put_pixstream で一気に書くことにした。

### convert (570,398) -> (402,281) -   5.06/(dot)
### convert (570,398) -> (805,562) -   3.91/(dot)

ずいぶん早くなったが、オリジナルの 倍にはなっていない。
もうすこし早くしたいところ。

OPT3

さらに、get_pixel を最適化することにした。
入力は、1 line では済まないので、2 次元のキャッシュを作ることにした。
(y 方向に 3dot ぐらいは必要)

### convert (570,398) -> (402,281) -   2.33/(dot)
### convert (570,398) -> (805,562) -   2.00/(dot)

これで相当加速された。3倍 以上は早くなっている。

*/

#define LINE_CACHE_OUT_SIZE   1024
#define LINE_CACHE_IN_SIZE    4096
#define LINE_CACHE_IN_MAXYS   16

#define OPT1
#define OPT2
#define OPT3


#define SCALE 16
#define MASK 0xf
#define SHFT 4

extern struct screen *current_screen;
extern long long millitime();

#ifdef OPT3
#define c_get_pixel(x,y)	((in_lc_miny <= y) && (y <= in_lc_maxy))\
					?in_lc[y - in_lc_miny][x]\
					:get_pixel(x,y,0)
#else
#define c_get_pixel(x,y)	get_pixel(x,y,0)
#endif
int mgl_bitblt_scale(struct screen *dst,int dx,int dy,int dxs,int dys
		,struct screen *src,int sx,int sy,int sxs,int sys) {
	struct screen *new,*org;
	int h,w;
	int x,y;
	int c,i,j;
	int xx0,yy0;
	int xx1,yy1;
	int x0,y0;
	int x1,y1;
	int dx0,dy0;
	int dx1,dy1;
	int hue,sat,bri;
	int o_hue,o_sat,o_bri;
	int area;
	int t_bri,t_sat;
	int t_area;
	int xc,yc;
	long long s_time,e_time;
#ifdef OPT1
	int mod_area = 0;
	int m_area;
	int mod_bri;
	int mod_sat;
#endif
#ifdef OPT2
	int out_lc[LINE_CACHE_OUT_SIZE];
	int out_lc_on = 0;
#endif
#ifdef OPT3
	int in_lc_buf[LINE_CACHE_IN_SIZE];
	int *in_lc[LINE_CACHE_IN_MAXYS];
	int *tmp,n;
	int in_lc_ys = 0;
	int in_lc_miny = -1;
	int in_lc_maxy = -1;
#endif

	if (dst == NULL) dst= current_screen;
	if (src == NULL) src= current_screen;

	new = create_subscreen(dst,dx,dy,dxs,dys);
	org = create_subscreen(src,sx,sy,sxs,sys);

	if (!org || !new) {
		if (new) free_screen(new);
		if (org) free_screen(org);
		return;
	}
	w = new->width;
	h = new->height;
#ifdef OPT2
	if (w < LINE_CACHE_OUT_SIZE) {
		out_lc_on = 1;
	}
#endif
#ifdef OPT3
	in_lc_ys = LINE_CACHE_IN_SIZE / org->width;
	if (in_lc_ys > LINE_CACHE_IN_MAXYS)
		in_lc_ys = LINE_CACHE_IN_MAXYS;
	for (i=0; i< in_lc_ys; i++) {
		in_lc[i] = &in_lc_buf[i * org->width];
	}
#endif
	push_screen(org);

	s_time = millitime();
	for (y = 0; y < h; y++) {
	    yy0 = (y * org->height * SCALE)/h;
	    yy1 = ((y+1) * org->height * SCALE)/h;
	    y0 = yy0 >> SHFT;
	    y1 = yy1 >> SHFT;
	    dy0 = yy0 & MASK;
	    dy1 = yy1 & MASK;

#ifdef OPT3
	    if (in_lc_ys) {
		n = (y1 - y0 + 1);
		if (n > in_lc_ys)
			n = in_lc_ys;
		
		for (i=0; i<n; i++) {
		    if ((in_lc_miny <= (y0+i)) && ((y0+i) <= in_lc_maxy)) {
			j = (y0+i) - in_lc_miny;
			tmp = in_lc[i];
			in_lc[i] = in_lc[j];
			in_lc[j] = tmp;
		    } else if (y0+i < org->height) {
			get_pixstream(0,y0+i,in_lc[i],org->width,DIR_NORTH,0);
		    }
		}
		in_lc_miny = y0;
		in_lc_maxy = y0 + n - 1;
	    }
#endif
	    for (x=0; x < w; x++) {
		xx0 = (x * org->width * SCALE)/w;
		xx1 = ((x+1) * org->width * SCALE)/w;
		x0 = xx0 >> SHFT;
		x1 = xx1 >> SHFT;
		dx0 = xx0 & MASK;
		dx1 = xx1 & MASK;
		t_bri = t_area = t_sat = 0;

		if (dx0) {
#ifdef OPT1
		    if ((x != 0) && mod_area) {
			t_bri += mod_bri;
			t_sat += mod_sat;
			t_area += mod_area;
		    } else
#endif
		    {
			/* 1 */
			if (dy0) {
				c = c_get_pixel(x0,y0);
				unpackMC(c,hue,sat,bri);
				area = (SCALE-dx0)*(SCALE-dy0);
				t_bri += bri * area;
				t_sat += sat * area;
				t_area += area;
			   }
			/* 2 */
			for (i=y0+1; i < y1; i++) {
				c = c_get_pixel(x0,i);
				unpackMC(c,hue,sat,bri);
				area = (SCALE-dx0)*(SCALE);
				t_bri += bri * area;
				t_sat += sat * area;
				t_area += area;
			}
			/* 3 */
			if (dy1) {
				c = c_get_pixel(x0,y1);
				unpackMC(c,hue,sat,bri);
				area = (SCALE-dx0)*(dy1);
				t_bri += bri * area;
				t_sat += sat * area;
				t_area += area;
			}
		    }
		}
		if (dy0) {
			/* 4 */
			for (i=x0+1; i < x1; i++) {
				c = c_get_pixel(i,y0);
				unpackMC(c,hue,sat,bri);
				area = (SCALE)*(SCALE-dy0);
				t_bri += bri * area;
				t_sat += sat * area;
				t_area += area;
			}
		}
		/* 5 */
		for (i=y0+1; i < y1; i++) {
			for (j=x0+1; j < x1; j++) {
				c = c_get_pixel(j,i);
				unpackMC(c,hue,sat,bri);
				area = (SCALE)*(SCALE);
				t_bri += bri * area;
				t_sat += sat * area;
				t_area += area;
			}
		}
		if (dy1) {
			/* 6 */
			for (i=x0+1; i < x1; i++) {
				c = c_get_pixel(i,y1);
				unpackMC(c,hue,sat,bri);
				area = (SCALE)*(dy1);
				t_bri += bri * area;
				t_sat += sat * area;
				t_area += area;
			}
		}
#ifdef OPT1
		mod_area = mod_bri = mod_sat = 0;
#endif
		if (dx1) {
			/* 7 */
			if (dy0) {
				c = c_get_pixel(x1,y0);
				unpackMC(c,hue,sat,bri);
				area = (dx1)*(SCALE-dy0);
				t_bri += bri * area;
				t_sat += sat * area;
				t_area += area;
#ifdef OPT1
				m_area = (SCALE-dx1)*(SCALE-dy0);
				mod_bri += bri * m_area;
				mod_sat += sat * m_area;
				mod_area += m_area;
#endif
			}
			/* 8 */
			for (i=y0+1; i < y1; i++) {
				c = c_get_pixel(x1,i);
				unpackMC(c,hue,sat,bri);
				area = (dx1)*(SCALE);
				t_bri += bri * area;
				t_sat += sat * area;
				t_area += area;
				m_area = (SCALE-dx1)*(SCALE);
				mod_bri += bri * m_area;
				mod_sat += sat * m_area;
				mod_area += m_area;
			}
			/* 9 */
			if (dy1) {
				c = c_get_pixel(x1,y1);
				unpackMC(c,hue,sat,bri);
				area = (dx1)*(dy1);
				t_bri += bri * area;
				t_sat += sat * area;
				t_area += area;
				m_area = (SCALE-dx1)*(dy1);
				mod_bri += bri * m_area;
				mod_sat += sat * m_area;
				mod_area += m_area;
			}
		}
		/* 10 */
		if (t_area == 0) {
			c = c_get_pixel(x0,y0);
			unpackMC(c,hue,sat,bri);
			area = (SCALE)*(SCALE);
			t_bri += bri * area;
			t_sat += sat * area;
			t_area += area;
		}
		o_bri = (t_bri + SCALE/2)/ t_area;
		o_sat = (t_sat + SCALE/2)/ t_area;
		xc = (xx0+xx1)>>(SHFT+1);
		yc = (yy0+yy1)>>(SHFT+1);
		c = c_get_pixel(xc,yc);
		unpackMC(c,hue,sat,bri);
		o_hue = hue;

#ifdef OPT2
		if (out_lc_on) {
			out_lc[x] = c;
		} else 
#endif
		{
		    push_screen(new);
		    put_pixel(x,y,c);
		    pop_screen();
		}
	    }
#ifdef OPT2
	    if (out_lc_on) {
		push_screen(new);
		put_pixstream(0,y,out_lc,w,DIR_NORTH);
		pop_screen();
	    }
#endif
	}
	e_time = millitime();
#if 0
	printf("### convert (%d,%d) -> (%d,%d) - %d dots/sec\n"
		,org->width,org->height
		,w,h
		,(int)((double)(w*h)*1000.0/(e_time - s_time))   );
#endif

	pop_screen();

	free_screen(new);
	free_screen(org);
}