//
// Copyright (C) 1999-2002 Toshikaz Hirabayashi
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
// TOSHIKAZ HIRABAYASHI BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
// OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
// Except as contained in this notice, the name of Toshikaz Hirabayashi shall
// not be used in advertising or otherwise to promote the sale, use or other
// dealings in this Software without prior written authorization from
// Toshikaz Hirabayashi.

#include <WScom.h>
#include <win/WSDwinImage.h>
#include <WSCimageSet.h>
#include <WSClocaleSet.h>
#include <WSDfileSystem.h>
#include <win/WSDwinAppDev.h>
#include <win/WSDwinColor.h>

extern "C" {
#include <jpeglib.h>
#include <png.h>
};
extern HBITMAP JpegReadFileToBitmap(char* filename,WSCulong* w,WSCulong* h);
extern HBITMAP PngReadFileToBitmap(char* filename,WSCulong* w,WSCulong* h,HBITMAP*);
extern long WSGFxBmpReadFpToPixmap(char* fname,FILE* ifile,HBITMAP* p,HBITMAP* p2,long* w,long* h);
extern long WSGFxBmpReadFileToPixmap(char* filename,HBITMAP* p,HBITMAP* p2,long* w,long* h);

WSMFclassInit(WSDwinImage,WSDimage);

WSDimage* _win_image_create_handler(){
  WSDimage* image = new WSDwinImage();
  return image;
};


void _win_image_init(){
    WSGIappImageSet()->setCreateHandler(_win_image_create_handler);
};

WSDwinImage::WSDwinImage(){
  hBitmap = NULL;
  nColors = 0;
}

WSDwinImage::~WSDwinImage(){
  destroy();
}

long WSDwinImage::initialize(){
  char* iname = getSrc();
  if (iname == NULL || !strcmp(iname,"") ){
    return WS_NO_ERR;
  }
  setValue1(-1);
  setValue2(-1);

  WSCstring in;
  in.setString(WSGIappFileSystem()->adjustFileName( iname ));
  while( in.replaceString("/","\\",0) );

  in.replaceString(".xpm", ".png", 0);
  in.replaceString(".XPM", ".png", 0);
  if (in.isExist(".jpg") != -1 || in.isExist(".JPG") != -1){
    WSCulong w,h;
    hBitmap = JpegReadFileToBitmap((char*)in,&w,&h);
    if (hBitmap != 0){
      setImageWidth(w);
      setImageHeight(h);
      setValue1((long)hBitmap);
      setValue2((long)0);
      setValue3((long)0);
      return WS_NO_ERR;
    }
  }
  if (in.isExist(".png") != -1 || in.isExist(".PNG") != -1){
    WSCulong w,h;
    HBITMAP mask=0;
    hBitmap = PngReadFileToBitmap((char*)in,&w,&h,&mask);
    if (hBitmap != 0){
      setImageWidth(w);
      setImageHeight(h);
      setValue1((long)hBitmap);
      setValue2((long)0);
      setValue3((long)mask);
      return WS_NO_ERR;
    }
  }


  in.replaceString(".xpm", ".bmp", 0);
  in.replaceString(".XPM", ".bmp", 0);
  in.replaceString(".jpg", ".bmp", 0);
  in.replaceString(".JPG", ".bmp", 0);
  in.replaceString(".png", ".bmp", 0);
  in.replaceString(".PNG", ".bmp", 0);
  char* load_file_name = in.getString(WSGIappLocaleSet()->
                                      getSystemLocaleEncoding());
 // hBitmap = (HBITMAP) ::LoadImage( WSGIwinAppDev()->getInstanceHandle(), 
  long iw=0,ih=0;
  HBITMAP mask =(HBITMAP)0;
  hBitmap = (HBITMAP)0;
  WSGFxBmpReadFileToPixmap(load_file_name,&hBitmap,&mask,&iw,&ih);

//  hBitmap = (HBITMAP)LoadImage( NULL,
//       (LPCTSTR)load_file_name,
//       IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE | LR_CREATEDIBSECTION | LR_LOADREALSIZE );
WSMFtrace("XXZZim1 WSDwinImage::init fname=#%s# 0x%x \n",(char*)in,hBitmap);
  if (hBitmap == 0){
    long words = in.getWords("\\");
    if (words > 1){
      WSCstring in2 = in.getWord(words-1,"\\");
      load_file_name = (char*)in2;
      hBitmap = (HBITMAP)LoadImage( NULL,
        (LPCTSTR)load_file_name,
        IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE | LR_CREATEDIBSECTION | LR_LOADREALSIZE );
    }
  }
  if (hBitmap == 0){
    WSCstring in2(iname);
    in2.replaceString(".xpm", ".bmp", 0);
    in2.replaceString(".XPM", ".bmp", 0);
    in2.replaceString(".jpg", ".bmp", 0);
    in2.replaceString(".JPG", ".bmp", 0);
    long val = (long)WSGIappImageSet()->getDefaultImageHandle((char*)in2);
    if (val != 0){
      hBitmap = (HBITMAP)LoadBitmap(WSGIwinAppDev()->getInstanceHandle(), (char*)val);
    }
  }
  if (hBitmap == 0){
    in.replaceString(".bmp", ".ico", 0);
    in.replaceString(".BMP", ".ico", 0);
    load_file_name = (char*)in;
    hBitmap = (HBITMAP)LoadImage( NULL,
        (LPCTSTR)load_file_name,
        IMAGE_ICON, 0, 0, LR_LOADFROMFILE | LR_CREATEDIBSECTION | LR_LOADREALSIZE );
    if (hBitmap == 0){
      long words = in.getWords("\\");
      if (words > 1){
        WSCstring in2 = in.getWord(words-1,"\\");
        load_file_name = (char*)in2;
        hBitmap = (HBITMAP)LoadImage( NULL,
          (LPCTSTR)load_file_name,
          IMAGE_ICON, 0, 0, LR_LOADFROMFILE | LR_CREATEDIBSECTION | LR_LOADREALSIZE );
      }
    }
    if (hBitmap == 0){
      WSCstring in2(iname);
      in2.replaceString(".xpm", ".ico", 0);
      in2.replaceString(".XPM", ".ico", 0);
      in2.replaceString(".jpg", ".ico", 0);
      in2.replaceString(".JPG", ".ico", 0);
      long val = (long)WSGIappImageSet()->getDefaultImageHandle((char*)in2);
      if (val != 0){
        hBitmap = (HBITMAP)LoadIcon(WSGIwinAppDev()->getInstanceHandle(), (char*)val);
      }
WSMFtrace("XXZZim1 WSDwinImage::load val=%d #%s# hBitmap=0x%x hInst=0x%x done.\n",val,(char*)in2,hBitmap,WSGIwinAppDev()->getInstanceHandle());
    }
    if (hBitmap ==0) {
      return WS_ERR;
    }
    setImageWidth(32);
    setImageHeight(32);
    setValue1((long)hBitmap);
    setValue2((long)1);
    setValue3((long)0);
    return WS_NO_ERR;
  }



  if (hBitmap !=0) {
    DIBSECTION ds;
//    SIZE size1;
    GetObject(hBitmap,sizeof(DIBSECTION),&ds);
    setImageWidth(ds.dsBm.bmWidth);
    setImageHeight(ds.dsBm.bmHeight);
    setValue1((long)hBitmap);
    setValue2((long)0);
    setValue3((long)mask);
WSMFtrace("WSDwinImage::init fname=#%s# 0x%x   %d,%d\n",(char*)in,hBitmap, ds.dsBm.bmWidth, ds.dsBm.bmHeight);

  }

   return WS_NO_ERR;
}

long WSDwinImage::destroy(){
  if (hBitmap != 0){
    DeleteObject((void*)hBitmap);
    hBitmap = 0;
  }
  setValue1(-1);
  setValue2(-1);
  return WS_NO_ERR;
}


static void ehandler(j_common_ptr jinf,int level){
}

static void exhandler(j_common_ptr jinf){
}
static int chandler(j_decompress_ptr jinf){
return TRUE;
}

HBITMAP JpegReadFileToBitmap(char* filename,WSCulong* w,WSCulong* h){

    int red_mask, green_mask, blue_mask;
    int red_shift, green_shift, blue_shift;
//    int start_shift, msb_flag;
//    unsigned int start_mask;
    BITMAPINFO bmi;
    HDC mdc;
    long bytes_per_line;

    red_mask = green_mask = blue_mask = 0;
    red_shift = green_shift = blue_shift = 0;
    WSCstring fname(filename);
    
    FILE* ifile = fopen(fname.getString(WSGIappLocaleSet()->getSystemLocaleEncoding()),
                        "rb");
    if (ifile == NULL){
      return NULL;
    }
    struct jpeg_decompress_struct    jinf;
    struct jpeg_error_mgr            jerr;

    jinf.err = jpeg_std_error(&jerr);
    jinf.err->emit_message = ehandler;
    jinf.err->error_exit = exhandler;

    jpeg_create_decompress(&jinf);
    jpeg_set_marker_processor(&jinf,JPEG_COM,chandler);

    jpeg_stdio_src(&jinf,ifile);

WSMFtrace("1 cinf->global_state=%d\n",jinf.global_state);
    jpeg_read_header(&jinf,TRUE);
    jpeg_calc_output_dimensions(&jinf);
WSMFtrace("2 cinf->global_state=%d\n",jinf.global_state);
    jpeg_start_decompress(&jinf);

    long width = jinf.output_width;
    long height = jinf.output_height;
    *w = width;
    *h = height;

    long components = jinf.output_components;

    unsigned char* buf = new unsigned char[components * height * width*sizeof(JSAMPLE)];

    JSAMPROW rowptr[1];
    long i;
    for(i=0; i< height; i++){
      rowptr[0] = (JSAMPROW)&(buf[i * width * components]);
      jpeg_read_scanlines(&jinf, rowptr,1);
    }

    bmi.bmiHeader.biSize=sizeof(BITMAPINFOHEADER);
    bmi.bmiHeader.biWidth=width;
    bmi.bmiHeader.biHeight=height;
    bmi.bmiHeader.biPlanes=1;
    bmi.bmiHeader.biBitCount=24;
    bmi.bmiHeader.biCompression=BI_RGB;
    bmi.bmiHeader.biSizeImage=0;
    bmi.bmiHeader.biXPelsPerMeter=0;
    bmi.bmiHeader.biYPelsPerMeter=0;
    bmi.bmiHeader.biClrUsed=0;
    bmi.bmiHeader.biClrImportant=0;

//    long gap = width*3 + bytes_per_line;
    bytes_per_line = (width+1)*3 & ~3;

    WSCuchar* data = new WSCuchar[bytes_per_line * height];

#if 0
    short tmp1 = 0xff00;
    char* tmp2 = (char*)&tmp1; 
    if (tmp2[0] == 0xff){
      msb_flag = 1;
    }else{
      msb_flag = 0;
    }
#endif

    unsigned char r,g,b;
    long ptr = 0;
    long ptr2 = 0;
    long j;

    if (components == 3){
WSMFtrace("XXZZim1 here3\n");
//        long end = height*width-1;
        for(i=0; i<height; i++){
          for(j=0; j<width; j++){
            long base = (height - 1 -i)*bytes_per_line + j*3;
            ((unsigned char*)data)[base +2] = buf[ptr++];
            ((unsigned char*)data)[base +1] = buf[ptr++];
            ((unsigned char*)data)[base] = buf[ptr++];
            ptr2 +=3;
          }
        }
      }else{
WSMFtrace("XXZZim1 here1\n");
        for(i=0; i<height; i++){
          for(j=0; j<width; j++){
            r = buf[ptr]>>3;
            g = buf[ptr]>>2;
            b = buf[ptr++]>>3;
            ((short*)data)[ptr2++] =  r <<11 | g<<5 | b;
          }
        }
    }
    HDC hdc = GetDC((HWND)WSGIwinAppDev()->getWindowResource());
    HBITMAP hBitmap = CreateCompatibleBitmap(hdc,width,height);
    ReleaseDC((HWND)WSGIwinAppDev()->getWindowResource(),hdc);

    mdc = CreateCompatibleDC(NULL);
    if (!SetDIBits(mdc,hBitmap,0,height,data,&bmi,DIB_RGB_COLORS)){
WSMFtrace("XXZZim1 setDIBits error %s\n",filename);
      DeleteObject((void*)hBitmap);
      hBitmap = 0;
    }
    GdiFlush();
    DeleteDC(mdc);

    jpeg_finish_decompress(&jinf);
    jpeg_destroy_decompress(&jinf);

    if (data != NULL){
      delete data;
      data = NULL;
    }
    if (buf != NULL){
      delete buf;
    }

    fclose(ifile);
    return hBitmap;
}

void png_cexcept_error(png_structp png_ptr, png_const_charp msg){
   if(png_ptr){
     fprintf(stderr, "png file read error: %s\n", msg);
   }
}

HBITMAP PngReadFileToBitmap(char* filename,WSCulong* w,WSCulong* h,HBITMAP* mask){
    *mask = (HBITMAP)0;
    int red_mask, green_mask, blue_mask;
    int red_shift, green_shift, blue_shift;
//    int start_shift, msb_flag;
//    unsigned int start_mask, udat;
    BITMAPINFO bmi;
    HDC mdc;
    long bytes_per_line;

    red_mask = green_mask = blue_mask = 0;
    red_shift = green_shift = blue_shift = 0;

    WSCstring fname(filename);
    
    FILE* ifile = fopen(fname.getString(WSGIappLocaleSet()->getSystemLocaleEncoding()),
                        "rb");
    if (ifile == NULL){
//printf("XXZZ Png err1 #%s#\n",(char*)fname);
      return NULL;
    }

    png_byte            sig[8];
    fread(sig, 1, 8, ifile);
    if (!png_check_sig(sig, 8)){
//printf("XXZZ Png err2\n");
      return NULL;
    }
    png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL,
      (png_error_ptr)png_cexcept_error, (png_error_ptr)NULL);
    if (png_ptr == NULL){
//printf("XXZZ Png err3\n");
      return NULL;
    } 
    png_infop info_ptr = png_create_info_struct(png_ptr);
    if (info_ptr == NULL){
//printf("XXZZ Png err4\n");
      png_destroy_read_struct(&png_ptr, NULL, NULL);
      return NULL;
    }

    png_init_io(png_ptr, ifile);
    png_set_sig_bytes(png_ptr, 8);
    png_read_info(png_ptr, info_ptr);
    png_uint_32 png_width;
    png_uint_32 png_height;
    int png_depth;
    int png_color_type;
    png_get_IHDR(png_ptr, info_ptr, &png_width, &png_height, &png_depth,
            &png_color_type, NULL, NULL, NULL);

    if (png_depth == 16){
      png_set_strip_16(png_ptr);
    }
    png_uint_32 png_row_bytes = png_get_rowbytes(png_ptr, info_ptr);
    png_uint_32 png_channels = png_get_channels(png_ptr, info_ptr);
//printf("load png row_bytes=%d channels=%d depth=%d\n",png_row_bytes,png_channels,png_depth);

    if (png_depth < 8){
      if (png_color_type == PNG_COLOR_TYPE_GRAY ){
        png_set_gray_1_2_4_to_8(png_ptr);
        png_row_bytes = png_width * 3;
        png_channels = 3;
      }else if (png_color_type == PNG_COLOR_TYPE_GRAY_ALPHA){
        png_set_gray_1_2_4_to_8(png_ptr);
        png_row_bytes = png_width * 4;
        png_channels = 4;
      }else{
        png_set_expand(png_ptr);
        png_row_bytes = png_width * 3;
        png_channels = 3;
      }
    }
    if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)){
      png_set_expand(png_ptr);
      png_row_bytes = png_width;
      if (png_channels == 1){
        png_row_bytes = png_width * 4;
        png_channels = 4;
      }
      if (png_channels == 3){
        png_row_bytes = png_width * 4;
        png_channels = 4;
      }
    }
    if (png_color_type == PNG_COLOR_TYPE_GRAY ||
        png_color_type == PNG_COLOR_TYPE_GRAY_ALPHA){
      png_set_gray_to_rgb(png_ptr);
      if (png_channels < 2){
        png_row_bytes = png_width;
      }
    }
    if (png_color_type == PNG_COLOR_TYPE_PALETTE){
      png_set_expand(png_ptr);
      png_row_bytes = png_width * 3;
      png_channels = 3;
    }
    if (png_channels == 2){
      png_row_bytes = png_width * 4;
      png_channels = 4;
    }
    if (png_channels == 1){
      png_row_bytes = png_width * 3;
      png_channels = 3;
    }

    long rwidth = png_width;
    long rheight = png_height;
    long components = png_channels;

    unsigned char* buf = NULL;
//    if (components == 2){
//      buf = new unsigned char[png_row_bytes * png_height * sizeof(png_byte)*2];
//    }else{
      buf = new unsigned char[png_row_bytes * png_height * sizeof(png_byte)];
//    }

    if (buf == NULL){
fprintf(stderr,"png read error: out of memory..\n");
      png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
//printf("XXZZ Png err5\n");
      fclose(ifile);
      return NULL;
    }
    png_byte** png_row_ptrs = new png_bytep[png_height];
    if (png_row_ptrs == NULL){
fprintf(stderr,"png read error: out of memory..\n");
      png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
//printf("XXZZ Png err6\n");
      fclose(ifile);
      return NULL;
    }
    int i;
    for(i = 0; i < (int)png_height; i++){
//      if (components == 2){
//        png_row_ptrs[i] = (png_byte*)(buf + i * png_row_bytes*2);
//      }else{
        png_row_ptrs[i] = (png_byte*)(buf + i * png_row_bytes);
//      }
    }
    png_read_image(png_ptr, png_row_ptrs);
    png_read_end(png_ptr,NULL);
    delete png_row_ptrs;

    *w = rwidth;
    *h = rheight;

    long width = (long)rwidth;
    long height = (long)rheight;

    bmi.bmiHeader.biSize=sizeof(BITMAPINFOHEADER);
    bmi.bmiHeader.biWidth=width;
    bmi.bmiHeader.biHeight=height;
    bmi.bmiHeader.biPlanes=1;
    bmi.bmiHeader.biBitCount=24;
    bmi.bmiHeader.biCompression=BI_RGB;
    bmi.bmiHeader.biSizeImage=0;
    bmi.bmiHeader.biXPelsPerMeter=0;
    bmi.bmiHeader.biYPelsPerMeter=0;
    bmi.bmiHeader.biClrUsed=0;
    bmi.bmiHeader.biClrImportant=0;

    bytes_per_line = (width+1)*3 & ~3;
    WSCuchar* data = NULL;
    if (components < 3){
      data = new WSCuchar[bytes_per_line * height*3];
    }else{
      data = new WSCuchar[bytes_per_line * height];
    }
    WSCuchar* data2 = NULL;
    if (png_channels == 4){
      data2 = new WSCuchar[bytes_per_line * height];
    }

    unsigned char r,g,b;
    long ptr = 0;
    long ptr2 = 0;
    long j;
//    if (components == 3 || components == 4){
      for(i=0; i<height; i++){
        for(j=0; j<width; j++){
          long base = (height - 1 -i)*bytes_per_line + j*3;
          ((unsigned char*)data)[base +2] = buf[ptr++];
          ((unsigned char*)data)[base +1] = buf[ptr++];
          ((unsigned char*)data)[base] = buf[ptr++];
          if (components == 4){
            unsigned char a = ~(buf[ptr++]);
            ((unsigned char*)data2)[base +2] = a;
            ((unsigned char*)data2)[base +1] = a;
            ((unsigned char*)data2)[base] = a;
          }
        }
        ptr = i * png_row_bytes;
      }
//    }else{
//      for(i=0; i<height; i++){
//        for(j=0; j<width; j++){
//          long base = (height - 1 -i)*bytes_per_line + j*3;
//          r = buf[ptr]>>3;
//          g = buf[ptr]>>2;
//          b = buf[ptr++]>>3;
//          ((short*)data)[base] =  r <<11 | g<<5 | b;
//        }
//      }
//    }
    HDC hdc = GetDC((HWND)WSGIwinAppDev()->getWindowResource());
    HBITMAP hBitmap = CreateCompatibleBitmap(hdc,width,height);
    HBITMAP hBitmap2 = (HBITMAP)0;
    if (components == 4){
      hBitmap2 = CreateCompatibleBitmap(hdc,width,height);
    }
    ReleaseDC((HWND)WSGIwinAppDev()->getWindowResource(),hdc);

    mdc = CreateCompatibleDC(NULL);
    if (!SetDIBits(mdc,hBitmap,0,height,data,&bmi,DIB_RGB_COLORS)){
      DeleteObject((void*)hBitmap);
      hBitmap = 0;
    }
    if (components == 4){
      if (!SetDIBits(mdc,hBitmap2,0,height,data2,&bmi,DIB_RGB_COLORS)){
        DeleteObject((void*)hBitmap2);
        hBitmap2 = 0;
      }
    }
    GdiFlush();
    DeleteDC(mdc);


    if (data != NULL){
      delete data;
      data = NULL;
    }
    if (data2 != NULL){
      delete data2;
      data2 = NULL;
    }
    png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
    if (buf != NULL){
      delete buf;
    }

//printf("XXZZ Png return OK\n");

    fclose(ifile);
    *mask = hBitmap2;
    return hBitmap;
}



int WSGFxgetbyte(FILE *fp) {
  int c;
  if ((c = fgetc(fp)) == EOF){
    return -1;
  }
  return c;
}

int WSGFxreadLittleShort(FILE *fp, short *d) {
  int c;
  if ((c = fgetc(fp)) == EOF){
    return -1;
  }
  *d = c & 0xff;
  if ((c = fgetc(fp)) == EOF){
    return -1;
  }
  *d |= (c & 0xff) << 8;
  return 0;
}

int WSGFxreadLittleLong(FILE *fp, long *d) {
  int c;

  if ((c = fgetc(fp)) == EOF){
    return -1;
  }
  *d = c & 0xff;
  if ((c = fgetc(fp)) == EOF){
    return -1;
  }
  *d |= ((c & 0xff) << 8);
  if ((c = fgetc(fp)) == EOF){
    return -1;
  }
  *d |= ((c & 0xff) << 16);
  if ((c = fgetc(fp)) == EOF){
    return -1;
  }
  *d |= ((c & 0xff) << 24);
  return 0;
}

long WSGFxBmpReadFpToPixmap(char* fname,FILE* ifile,HBITMAP* p,HBITMAP* p2,long* w,long* h){

  // bmp (Windows 3.x) header
  //       implemented
  //            1. uncompressed 1bit black & white
  //            2. uncompressed 4bit  16 color
  //            3. uncompressed 8bit 256 color
  //            4. uncompressed 24bit true color
  //            5. RLE 4bit  16 color
  //            6. RLE 8bit 256 color
  *p = (HBITMAP)0;
  *p2 = (HBITMAP)0;
  unsigned char red[256], green[256], blue[256];
  if ((WSGFxgetbyte(ifile) != 'B') || (WSGFxgetbyte(ifile) != 'M')){  //  0
    // not a .bmp file
    return WS_ERR;
  }
  long width, height, long_tmp, fsize, foffs, cmapsize, cmptype;
  short bit_cnt, short_tmp;
  if (WSGFxreadLittleLong(ifile, &fsize) == -1){                //  2
    return WS_ERR;
  }
  if (WSGFxreadLittleLong(ifile, &long_tmp) == -1){             //  6
    return WS_ERR;
  }
  if (WSGFxreadLittleLong(ifile, &foffs) == -1){                 // 10
    return WS_ERR;
  }
  if (WSGFxreadLittleLong(ifile, &long_tmp) == -1){             // 14
    return WS_ERR;
  }
  if (long_tmp != 40){
    // not a Windows 3.x bmp file
    if (long_tmp != 0x6c){
      return WS_ERR;
    }
  }
  if (WSGFxreadLittleLong(ifile, &width) == -1){                 // 18
    return WS_ERR;
  }
  if (WSGFxreadLittleLong(ifile, &height) == -1){                // 22
    return WS_ERR;
  }
  if (WSGFxreadLittleShort(ifile, &short_tmp) == -1){            // 26
    return WS_ERR;
  }
  if (short_tmp != 1){
    // plane != 1 is not supported
    return WS_ERR;
  }
  if (WSGFxreadLittleShort(ifile, &bit_cnt) == -1){              // 28
    return WS_ERR;
  }
  if ((bit_cnt != 1) && (bit_cnt != 4) && (bit_cnt != 8) &&
      (bit_cnt != 24) && (bit_cnt != 32)){
    // bit_cnt=16 is not supported
    return WS_ERR;
  }
  if (WSGFxreadLittleLong(ifile, &cmptype) == -1){              // 30
    return WS_ERR;
  }
  if (cmptype > 2){
    if (cmptype == 3 && bit_cnt == 32){
      // 24bit + mask.
    }else{
      // compressed file is not supported
      return WS_ERR;
    }
  }

  if (cmptype == 3 && bit_cnt == 32){
  }else{
    *p = (HBITMAP)LoadImage( NULL,
                 (LPCTSTR)fname,
                 IMAGE_BITMAP, 0, 0,
                 LR_LOADFROMFILE | LR_CREATEDIBSECTION | LR_LOADREALSIZE );
    return WS_NO_ERR;
  }
  if (WSGFxreadLittleLong(ifile, &long_tmp) == -1){              // 34
    return WS_ERR;
  }
  if (WSGFxreadLittleLong(ifile, &long_tmp) == -1){              // 38
    return WS_ERR;
  }
  if (WSGFxreadLittleLong(ifile, &long_tmp) == -1){              // 42
    return WS_ERR;
  }
  if (WSGFxreadLittleLong(ifile, &cmapsize) == -1){              // 46
    return WS_ERR;
  }
  if (WSGFxreadLittleLong(ifile, &long_tmp) == -1){              // 50
    return WS_ERR;
  }
  if ((cmapsize == 0) && (bit_cnt <=8)){
    cmapsize = 1 << bit_cnt;
  }
  int n, c;
  for (n = 0; n < cmapsize; n++) {
    if ((c = WSGFxgetbyte(ifile)) == -1){
      return WS_ERR;
    }
    blue[n] = (unsigned char)c;
    if ((c = WSGFxgetbyte(ifile)) == -1){
      return WS_ERR;
    }
    green[n] = (unsigned char)c;
    if ((c = WSGFxgetbyte(ifile)) == -1){
      return WS_ERR;
    }
    red[n] = (unsigned char)c;
    if (WSGFxgetbyte(ifile) == -1){
      return WS_ERR;
    }
  }
  if (fseek(ifile, foffs, SEEK_SET) != 0){
    return WS_ERR;
  }
  unsigned char* fbuf = new unsigned char[fsize - foffs];
  if (fread(fbuf, fsize - foffs, 1, ifile) != 1) {
    delete fbuf;
    return WS_ERR;
  }
  unsigned char* buf = new unsigned char[width * height * 3];
  unsigned char* buf_mask = NULL;
  if (cmptype == 3){
    buf_mask = new unsigned char[width * height * 3];
  }
  unsigned char *tmp, *ftmp;
  unsigned char *tmp_mask;

  int line, dat, x, y;
//printf("cmptype=%d w,h=%d,%d bit=%d\n",cmptype,width,height,bit_cnt);
  if (cmptype) {
    for (y = 0; y < height; y++) {
//      tmp = buf + (height - 1 - y) * width * 3;
      tmp = buf + y * width * 3;
      for (x = 0; x < width; x++) {
        *(tmp++) = red[0];        // red
        *(tmp++) = green[0];      // green
        *(tmp++) = blue[0];       // blue
      }
    }
  }
  if (cmptype == 1) {
    // BI_RLE8
    x = 0;
    y = 0;
    ftmp = fbuf;
    while (1) {
      if (*ftmp) {
        // copy N times
//        tmp = buf + (((height - 1 - y) * width) + x) * 3;
        tmp = buf + ((y * width) + x) * 3;
        for (n = 0; n < *ftmp; n++, x++) {
          if (x<width){
            *(tmp++) = red[*(ftmp + 1)];        // red
            *(tmp++) = green[*(ftmp + 1)];      // green
            *(tmp++) = blue[*(ftmp + 1)];       // blue
          }
        }
        ftmp += 2;
      } else {
        if (*(ftmp + 1) == 0) {
          // newline
          x = 0;
          y++;
          ftmp += 2;
        } else if (*(ftmp + 1) == 1) {
          // end
          break;
        } else if (*(ftmp + 1) == 2) {
          // jump
          x += *(ftmp + 2);
          y += *(ftmp + 3);
          ftmp += 4;
        } else {
          // copy
//          tmp = buf + (((height - 1 - y) * width) + x) * 3;
          tmp = buf + ((y * width) + x) * 3;
          for (n = 0; n < *(ftmp + 1); n++, x++) {
            if (x<width){
              *(tmp++) = red[*(ftmp + n + 2)];        // red
              *(tmp++) = green[*(ftmp + n + 2)];      // green
              *(tmp++) = blue[*(ftmp + n + 2)];       // blue
            }
          }
          ftmp += (*(ftmp + 1) + 2 + (*(ftmp + 1) % 2)?1:0);
        }
      }
    }
  } else if (cmptype == 2) {
    // BI_RLE4
    int m;
    x = 0;
    y = 0;
    ftmp = fbuf;
    while (1) {
      if (*ftmp) {
        // copy N times
//        tmp = buf + (((height - 1 - y) * width) + x) * 3;
        tmp = buf + ((y * width) + x) * 3;
        for (n = 0; n < *ftmp; n++, x++) {
          if (x % 2)
            dat = *(ftmp + 1) & 0x0f;         // left
          else
            dat = (*(ftmp + 1) & 0xf0) >> 4;  // right
          *(tmp++) = red[dat];        // red
          *(tmp++) = green[dat];      // green
          *(tmp++) = blue[dat];       // blue
        }
        ftmp += 2;
      } else {
        if (*(ftmp + 1) == 0) {
          // newline
          x = 0;
          y++;
          ftmp += 2;
        } else if (*(ftmp + 1) == 1) {
          // end
          break;
        } else if (*(ftmp + 1) == 2) {
          // jump
          x += *(ftmp + 2);
          y += *(ftmp + 3);
          ftmp += 4;
        } else {
          // copy
          m = 0;
//          tmp = buf + (((height - 1 - y) * width) + x) * 3;
          tmp = buf + ((y * width) + x) * 3;
          for (n = 0; n < *(ftmp + 1); n++, x++) {
            if (x % 2) {
              dat = *(ftmp + m + 2) & 0x0f;         // left
              m++;
            } else
              dat = (*(ftmp + m + 2) & 0xf0) >> 4;  // right
            *(tmp++) = red[dat];        // red
            *(tmp++) = green[dat];      // green
            *(tmp++) = blue[dat];       // blue
          }
          m = (*(ftmp + 1) + 1) / 2;
          ftmp += (m + 2 + ((m % 2)?1:0));
        }
      }
    }
  } else {
    // uncompress
    switch(bit_cnt) {
      case 1:
        line = ((width + 31) / 32) * 4;
        tmp = buf;
        for (y = 0; y < height; y++){
//          ftmp = fbuf + ((height - 1 - y) * line);
          ftmp = fbuf + (y * line);
          for (x = 0; x < width; x++){
            dat = (*ftmp >> (7 - (x % 8))) & 0x01;
            if ((x % 8) == 7){
              ftmp++;
            }
            *(tmp++) = red[dat];          // red
            *(tmp++) = green[dat];        // green
            *(tmp++) = blue[dat];         // blue
          }
        }
        break;
      case 4:
        line = ((width + 7) / 8) * 4;
        tmp = buf;
        for (y = 0; y < height; y++){
//          ftmp = fbuf + ((height - 1 - y) * line);
          ftmp = fbuf + (y * line);
          for (x = 0; x < width; x++){
            if (x % 2) {
              dat = *ftmp & 0x0f;         // left
              ftmp++;
            }else{
              dat = (*ftmp & 0xf0) >> 4;  // right
            }
            *(tmp++) = red[dat];          // red
            *(tmp++) = green[dat];        // green
            *(tmp++) = blue[dat];         // blue
          }
        }
        break;
      case 8:
        line = ((width + 3) / 4) * 4;
        tmp = buf;
        for (y = 0; y < height; y++){
//          ftmp = fbuf + ((height - 1 - y) * line);
          ftmp = fbuf + (y * line);
          for (x = 0; x < width; x++){
            *(tmp++) = red[*ftmp];        // red
            *(tmp++) = green[*ftmp];      // green
            *(tmp++) = blue[*ftmp];       // blue
            ftmp++;
          }
        }
        break;
      case 24:
        tmp = buf;
        tmp_mask = buf_mask;
        for (y = 0; y < height; y++){
//          ftmp = fbuf + ((height - 1 - y) * width * 3);
          ftmp = fbuf + (y * width * 3);
          for (x = 0; x < width; x++){
            *(tmp++) = *(ftmp + 0);       // red
            *(tmp++) = *(ftmp + 1);       // green
            *(tmp++) = *(ftmp + 2);       // blue
            if (cmptype != 3){
              ftmp += 3;
            }else{
              *(tmp_mask++) = *(ftmp + 3);       // mask
              *(tmp_mask++) = *(ftmp + 3);       // mask
              *(tmp_mask++) = *(ftmp + 3);       // mask
              ftmp += 4;
            }
          }
        }
        break;
      case 32:
        tmp = buf;
        tmp_mask = buf_mask;
        for (y = 0; y < height; y++){
//          ftmp = fbuf + ((height - 1 - y) * width * 4);
          ftmp = fbuf + (y * width * 4);
          for (x = 0; x < width; x++){
            *(tmp++) = *(ftmp + 0);       // red
            *(tmp++) = *(ftmp + 1);       // green
            *(tmp++) = *(ftmp + 2);       // blue
           
            if (cmptype == 3){
              *(tmp_mask++) = *(ftmp + 3);       // mask
              *(tmp_mask++) = *(ftmp + 3);       // mask
              *(tmp_mask++) = *(ftmp + 3);       // mask
//printf("i=%d j=%d pt=%d mask=0x%x\n",y,x,tmp_mask-1 - buf_mask,*(tmp_mask -1));
            }
            ftmp += 4;
          }
        }
        break;
     }
  }
  delete fbuf;

  BITMAPINFO bmi;
  HDC mdc;
  long bytes_per_line;

  bmi.bmiHeader.biSize=sizeof(BITMAPINFOHEADER);
  bmi.bmiHeader.biWidth=width;
  bmi.bmiHeader.biHeight=height;
  bmi.bmiHeader.biPlanes=1;
  bmi.bmiHeader.biBitCount=24;
  bmi.bmiHeader.biCompression=BI_RGB;
  bmi.bmiHeader.biSizeImage=0;
  bmi.bmiHeader.biXPelsPerMeter=0;
  bmi.bmiHeader.biYPelsPerMeter=0;
  bmi.bmiHeader.biClrUsed=0;
  bmi.bmiHeader.biClrImportant=0;

  bytes_per_line = (width+1)*3 & ~3;

  HDC hdc = GetDC((HWND)WSGIwinAppDev()->getWindowResource());
  HBITMAP hBitmap = CreateCompatibleBitmap(hdc,width,height);
  HBITMAP hBitmap2 = (HBITMAP)0;
  if (cmptype == 3){
    hBitmap2 = CreateCompatibleBitmap(hdc,width,height);
  }
  ReleaseDC((HWND)WSGIwinAppDev()->getWindowResource(),hdc);

  mdc = CreateCompatibleDC(NULL);
  if (!SetDIBits(mdc,hBitmap,0,height,buf,&bmi,DIB_RGB_COLORS)){
    DeleteObject((void*)hBitmap);
    hBitmap = 0;
  }
  if (cmptype == 3){
    if (!SetDIBits(mdc,hBitmap2,0,height,buf_mask,&bmi,DIB_RGB_COLORS)){
      DeleteObject((void*)hBitmap2);
      hBitmap2 = 0;
    }
  }
  GdiFlush();
  DeleteDC(mdc);

  if (buf != NULL){
    delete buf;
    buf = NULL;
  }
  if (buf_mask != NULL){
    delete buf_mask;
    buf_mask = NULL;
  }
  *p = hBitmap;
  if (cmptype == 3){
    *p2 = hBitmap2;
  }
  return WS_NO_ERR;
}

long WSGFxBmpReadFileToPixmap(char* filename,HBITMAP* pixmap,HBITMAP* pixmap2,long* w,long* h){

  FILE* ifile = fopen(filename,"r");
  if (ifile == NULL){
    return -1;
  }

  long reti = WSGFxBmpReadFpToPixmap(filename,ifile, pixmap,pixmap2, w, h);
  fclose(ifile);
  return reti;
}

