//
// 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 <windows.h>
#include <WSCdevice.h>
#include <WSCcolorSet.h>
#include <WSCimageSet.h>
#include <WSCbase.h>
#include <WSDmouse.h>
#include <WSCbaseList.h>
#include <win/WSDwinmwinDev.h>
#include <win/WSDwinAppDev.h>
#include <win/WSDwindraw.h>
#include <win/WSwincom.h>
#include <win/WSDwinformDev.h>

extern "C" {
#include <jpeglib.h>
#include <png.h>
};
extern long PngWriteBufToFp(FILE* fp, char* buf,long width,long height);
extern long JpegWriteBufToFp(FILE* fp, char* buf,long width,long height);

WSMFclassInit(WSDwinmwinDev,WSDmwindowDev);

WSDdev* _wscdwinmwindev_init_(){
  return new WSDwinmwinDev();
}

class _WSDwinmwinDev_init_ {
  public:_WSDwinmwinDev_init_(){
     WSGIappDevice()->setCreateHandler("mwindowDev",_wscdwinmwindev_init_);
  };
};
static _WSDwinmwinDev_init_ _init_to_run_WSDwinmwinDev_;

//static WSDwindraw* draw_of_winmwin = NULL;

int red_mask, green_mask, blue_mask;
int red_shift, green_shift, blue_shift;
int start_shift;
unsigned int start_mask;
char mask_initialized = 0;
long msb_flag = 0;

WSDwinmwinDev::WSDwinmwinDev(){
  _hBitmap  = 0;
  _mdc  = 0;
  _bmi = NULL;
//  if (draw_of_winmwin == NULL){
//      draw_of_winmwin = new WSDwindraw();
//  }
//  setDraw(draw_of_winmwin);
}

WSDwinmwinDev::~WSDwinmwinDev(){
  if (_bmi != NULL){
    delete _bmi;
    _bmi = NULL;
  }
  destroyPixmap();
}
long WSDwinmwinDev::copyToWindow(WSDdev* dest,short x,short y,WSCushort w,WSCushort h,short dx,short dy){
  if (_hBitmap == 0 || _mdc == 0){
    return WS_ERR;
  }
  HDC hdc = GetDC((HWND)dest->getWindowResource());
  HPALETTE palette = WSGIwinAppDev()->getPalette();
  HPALETTE palette_bk = 0;
  if (palette != 0 ){
     palette_bk = SelectPalette(hdc,palette,False);
     RealizePalette(hdc);
  }

  if (dest->cast("WSDnwDev") != NULL){
    int vx = 0;
    int vy = 0;
    if (dest->getAttachedClient() != NULL){
      vx = dest->getAttachedClient()->getProperty(WSNx);
      vy = dest->getAttachedClient()->getProperty(WSNy);
    }
    BitBlt(hdc,vx+dx,vy+dy,w,h,_mdc,x,y,SRCCOPY);
    WSDwinformDev* wdev = (WSDwinformDev*)dest->getParentDev()->cast("WSDwinformDev");
    if (wdev != NULL && wdev->getPixmapStyle() != WS_DIRECT_WINDOW){
      BitBlt((HDC)wdev->getContextResource(),vx+dx,vy+dy,w,h,_mdc,x,y,SRCCOPY);
    }
  }else{
    BitBlt(hdc,dx,dy,w,h,_mdc,x,y,SRCCOPY);
    WSDwinformDev* wdev = (WSDwinformDev*)dest->cast("WSDwinformDev");
    if (wdev != NULL && wdev->getPixmapStyle() != WS_DIRECT_WINDOW){
      BitBlt((HDC)wdev->getContextResource(),dx,dy,w,h,_mdc,x,y,SRCCOPY);
    }
  }
  if (palette != 0){
    SelectPalette(hdc,palette_bk,False);
  }
  ReleaseDC((HWND)dest->getWindowResource(), hdc);
  return WS_NO_ERR;
}
long WSDwinmwinDev::copyToWindowWithMask(WSDdev* dest,short x,short y,
                                         WSCushort w,WSCushort h,short dx,short dy,
                                         WSDimage* image){
  if (_hBitmap == 0 || _mdc == 0){
    return WS_ERR;
  }
  HDC hdc = GetDC((HWND)dest->getWindowResource());
  HPALETTE palette = WSGIwinAppDev()->getPalette();
  HPALETTE palette_bk = 0;
  if (palette != 0 ){
     palette_bk = SelectPalette(hdc,palette,False);
     RealizePalette(hdc);
  }
  HDC src = CreateCompatibleDC(hdc);
  HBITMAP srcp = CreateCompatibleBitmap(hdc,w,h);
  SelectObject(src,srcp);
  BitBlt(src,0,0,w,h,_mdc,x,y,SRCCOPY);

  HDC mask = CreateCompatibleDC(hdc);
  HBITMAP maskp = (HBITMAP)image->getValue1();

  HDC mask2 = CreateCompatibleDC(hdc);
  HBITMAP maskp2 = CreateCompatibleBitmap(hdc,w,h);
  SelectObject(mask2,maskp2);


  if (maskp != (HBITMAP)-1 && maskp != (HBITMAP)0){
    SelectObject(mask,maskp);
    BitBlt(mask2,0,0,w,h,mask,0,0,NOTSRCCOPY);
    BitBlt(src,0,0,w,h,mask,0,0,SRCAND);
    if (dest->cast("WSDnwDev") != NULL){
      int vx = 0;
      int vy = 0;
      if (dest->getAttachedClient() != NULL){
        vx = dest->getAttachedClient()->getProperty(WSNx);
        vy = dest->getAttachedClient()->getProperty(WSNy);
      }
      BitBlt(hdc,vx+dx,vy+dy,w,h,mask2,0,0,SRCAND);
      WSDwinformDev* wdev = (WSDwinformDev*)dest->getParentDev()->cast("WSDwinformDev");
      if (wdev != NULL && wdev->getPixmapStyle() != WS_DIRECT_WINDOW){
        BitBlt((HDC)wdev->getContextResource(),vx+dx,vy+dy,w,h,mask2,0,0,SRCAND);
      }
    }else{
      BitBlt(hdc,dx,dy,w,h,mask2,0,0,SRCAND);
      WSDwinformDev* wdev = (WSDwinformDev*)dest->cast("WSDwinformDev");
      if (wdev != NULL && wdev->getPixmapStyle() != WS_DIRECT_WINDOW){
        BitBlt((HDC)wdev->getContextResource(),dx,dy,w,h,mask2,0,0,SRCAND);
      }
    }
  }
  if (dest->cast("WSDnwDev") != NULL){
    int vx = 0;
    int vy = 0;
    if (dest->getAttachedClient() != NULL){
      vx = dest->getAttachedClient()->getProperty(WSNx);
      vy = dest->getAttachedClient()->getProperty(WSNy);
    }
    BitBlt(hdc,vx+dx,vy+dy,w,h,src,0,0,SRCPAINT);
    WSDwinformDev* wdev = (WSDwinformDev*)dest->getParentDev()->cast("WSDwinformDev");
    if (wdev != NULL && wdev->getPixmapStyle() != WS_DIRECT_WINDOW){
      BitBlt((HDC)wdev->getContextResource(),vx+dx,vy+dy,w,h,src,0,0,SRCPAINT);
    }
  }else{
    BitBlt(hdc,dx,dy,w,h,src,0,0,SRCPAINT);
    WSDwinformDev* wdev = (WSDwinformDev*)dest->cast("WSDwinformDev");
    if (wdev != NULL && wdev->getPixmapStyle() != WS_DIRECT_WINDOW){
      BitBlt((HDC)wdev->getContextResource(),dx,dy,w,h,src,0,0,SRCPAINT);
    }
  }
  if (palette != 0){
    SelectPalette(hdc,palette_bk,False);
  }

  DeleteObject(srcp);
  DeleteDC(src);
  DeleteObject(maskp2);
  DeleteDC(mask2);
  DeleteDC(mask);

  ReleaseDC((HWND)dest->getWindowResource(), hdc);
  return WS_NO_ERR;
}

long WSDwinmwinDev::copyToWindowWithMask(WSDdev* dest,short x,short y,
                                         WSCushort w,WSCushort h,short dx,short dy,
                                         WSDmwindowDev* image){
  if (_hBitmap == 0 || _mdc == 0){
    return WS_ERR;
  }
  HDC hdc = GetDC((HWND)dest->getWindowResource());
  HPALETTE palette = WSGIwinAppDev()->getPalette();
  HPALETTE palette_bk = 0;
  if (palette != 0 ){
     palette_bk = SelectPalette(hdc,palette,False);
     RealizePalette(hdc);
  }
  HDC src = CreateCompatibleDC(hdc);
  HBITMAP srcp = CreateCompatibleBitmap(hdc,w,h);
  SelectObject(src,srcp);
  BitBlt(src,0,0,w,h,_mdc,x,y,SRCCOPY);

  WSDwinmwinDev* mimage = (WSDwinmwinDev*)image->cast("WSDwinmwinDev");
  if (mimage == NULL){
    DeleteObject(srcp);
    DeleteDC(src);
    ReleaseDC((HWND)dest->getWindowResource(), hdc);
    return WS_ERR;
  }
  HDC mask = mimage->_mdc;

  HDC mask2 = CreateCompatibleDC(hdc);
  HBITMAP maskp2 = CreateCompatibleBitmap(hdc,w,h);
  SelectObject(mask2,maskp2);


  if (_mdc != (HDC)NULL){
    BitBlt(mask2,0,0,w,h,mask,0,0,NOTSRCCOPY);
    BitBlt(src,0,0,w,h,mask,0,0,SRCAND);
    if (dest->cast("WSDnwDev") != NULL){
      int vx = 0;
      int vy = 0;
      if (dest->getAttachedClient() != NULL){
        vx = dest->getAttachedClient()->getProperty(WSNx);
        vy = dest->getAttachedClient()->getProperty(WSNy);
      }
      BitBlt(hdc,vx+dx,vy+dy,w,h,mask2,0,0,SRCAND);
      WSDwinformDev* wdev = (WSDwinformDev*)dest->getParentDev()->cast("WSDwinformDev");
      if (wdev != NULL && wdev->getPixmapStyle() != WS_DIRECT_WINDOW){
        BitBlt((HDC)wdev->getContextResource(),vx+dx,vy+dy,w,h,mask2,0,0,SRCAND);
      }
    }else{
      BitBlt(hdc,dx,dy,w,h,mask2,0,0,SRCAND);
      WSDwinformDev* wdev = (WSDwinformDev*)dest->cast("WSDwinformDev");
      if (wdev != NULL && wdev->getPixmapStyle() != WS_DIRECT_WINDOW){
        BitBlt((HDC)wdev->getContextResource(),dx,dy,w,h,mask2,0,0,SRCAND);
      }
    }
  }
  if (dest->cast("WSDnwDev") != NULL){
    int vx = 0;
    int vy = 0;
    if (dest->getAttachedClient() != NULL){
      vx = dest->getAttachedClient()->getProperty(WSNx);
      vy = dest->getAttachedClient()->getProperty(WSNy);
    }
    BitBlt(hdc,vx+dx,vy+dy,w,h,src,0,0,SRCPAINT);
    WSDwinformDev* wdev = (WSDwinformDev*)dest->getParentDev()->cast("WSDwinformDev");
    if (wdev != NULL && wdev->getPixmapStyle() != WS_DIRECT_WINDOW){
      BitBlt((HDC)wdev->getContextResource(),vx+dx,vy+dy,w,h,src,0,0,SRCPAINT);
    }
  }else{
    BitBlt(hdc,dx,dy,w,h,src,0,0,SRCPAINT);
    WSDwinformDev* wdev = (WSDwinformDev*)dest->cast("WSDwinformDev");
    if (wdev != NULL && wdev->getPixmapStyle() != WS_DIRECT_WINDOW){
      BitBlt((HDC)wdev->getContextResource(),dx,dy,w,h,src,0,0,SRCPAINT);
    }
  }
  if (palette != 0){
    SelectPalette(hdc,palette_bk,False);
  }

  DeleteObject(srcp);
  DeleteDC(src);
  DeleteObject(maskp2);
  DeleteDC(mask2);

  ReleaseDC((HWND)dest->getWindowResource(), hdc);
  return WS_NO_ERR;
}
long WSDwinmwinDev::copyFromWindow(WSDdev* dest,short x,short y,WSCushort w,WSCushort h,short dx,short dy){
  if (_hBitmap == 0 || _mdc == 0){
    return WS_ERR;
  }
  HDC hdc = GetDC((HWND)dest->getWindowResource());
  HPALETTE palette = WSGIwinAppDev()->getPalette();
  HPALETTE palette_bk = 0;
  if (palette != 0 ){
     palette_bk = SelectPalette(hdc,palette,False);
     RealizePalette(hdc);
  }

  if (dest->cast("WSDnwDev") != NULL){
    int vx = 0;
    int vy = 0;
    if (dest->getAttachedClient() != NULL){
      vx = dest->getAttachedClient()->getProperty(WSNx);
      vy = dest->getAttachedClient()->getProperty(WSNy);
    }
    BitBlt(_mdc,vx+dx,vy+dy,w,h,hdc,x,y,SRCCOPY);
  }else{
    BitBlt(_mdc,dx,dy,w,h,hdc,x,y,SRCCOPY);
  }
  if (palette != 0){
    SelectPalette(hdc,palette_bk,False);
  }
  ReleaseDC((HWND)dest->getWindowResource(), hdc);
  return WS_NO_ERR;
}

long WSDwinmwinDev::createPixmap(WSCushort w,WSCushort h){
  _w = w;
  _h = h;
  if (_w == 0 || _h == 0){
    return WS_ERR;
  }

  HDC hdc = GetDC((HWND)WSGIwinAppDev()->getWindowResource());
  HBITMAP hBitmap = CreateCompatibleBitmap(hdc,_w,_h);
  if (_mdc != 0){
    destroyPixmap();
  }
  if (_hBitmap != 0){
    destroyPixmap();
  }
  if (_mdc == 0){
    _mdc = CreateCompatibleDC(hdc);
    SelectObject(_mdc,hBitmap);
  }
  ReleaseDC((HWND)WSGIwinAppDev()->getWindowResource(),hdc);

  _hBitmap = hBitmap;
  return WS_NO_ERR;
}

long WSDwinmwinDev::destroyPixmap(){
  if (_hBitmap != 0){
    DeleteObject((void*)_hBitmap);
    _hBitmap = 0;
  }
  if (_mdc != 0){
    DeleteDC(_mdc);
    _mdc = 0;
  }
  return WS_NO_ERR;
}

long WSDwinmwinDev::beginDraw(short x,short y,WSCushort w,WSCushort h,WSCbool absolute,
                              WSCbool scaling){

  if (w  == 0){
    return WS_ERR;
  }
  if (h  == 0){
    return WS_ERR;
  }
  if (_mdc == 0){
    return WS_ERR;
  }

//  HDC hdc = GetDC((HWND)WSGIwinAppDev()->getWindowResource());
//  _mdc = CreateCompatibleDC(hdc);
//  SelectObject(_mdc,_hBitmap);
//  ReleaseDC((HWND)WSGIwinAppDev()->getWindowResource(),hdc);

  return WSDmwindowDev::beginDraw(x,y,w,h,absolute,scaling);
}

long WSDwinmwinDev::getDeviceResource(){
  return 0;
}


long WSDwinmwinDev::getContextResource(){
  if (_mdc == 0){
    return -1;
  }
  return (long)_mdc;
}

long WSDwinmwinDev::getSpecialResource(){
  return (long) WSGIwinAppDev()->getWindowResource();
}

long WSDwinmwinDev::getWindowResource(){
  return (long) WSGIwinAppDev()->getWindowResource();
}

long WSDwinmwinDev::initBuffer(){
  if (_hBitmap == NULL){
    return WS_ERR;
  }
  if (_mdc == 0){
    return WS_ERR;
  }
  BITMAPINFO bmi;
  bmi.bmiHeader.biSize=sizeof(BITMAPINFOHEADER);
  bmi.bmiHeader.biWidth=_w;
  bmi.bmiHeader.biHeight=_h;
  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;
  if (_bmi != NULL){
    delete _bmi;
    _bmi = NULL;
  }

  _bmi = (WSCuchar*)malloc( ((_w+1)*3 & ~3) * _h);
 
  int ret = GetDIBits(_mdc,_hBitmap,0,_h,_bmi,&bmi,DIB_RGB_COLORS);
  if (ret == 0){
    return WS_ERR;
  }
  return WS_NO_ERR;
}

long WSDwinmwinDev::setBufferRGB(WSCushort x,WSCushort y,WSCuchar r,WSCuchar g,WSCuchar b){
  if (_bmi == NULL){
    return WS_ERR;
  }
  if (x < _w && y < _h){
    long bytes_per_line = (_w+1)*3 & ~3;
    long base = (_h -1 - y)*bytes_per_line + x*3;
    _bmi[base+2] = r;
    _bmi[base+1] = g;
    _bmi[base] = b;
    return WS_NO_ERR;
  }
  return WS_ERR;
}
long WSDwinmwinDev::getBufferRGB(WSCushort x,WSCushort y, WSCuchar* r, WSCuchar* g,WSCuchar* b){
  if (_bmi == NULL){
    return WS_ERR;
  }
  if (x < _w && y < _h){
    long bytes_per_line = (_w+1)*3 & ~3;
    long base = (_h -1 - y)*bytes_per_line + x*3;
    *r = _bmi[base+2];
    *g = _bmi[base+1];
    *b = _bmi[base];
    return WS_NO_ERR;
  }
  return WS_ERR;
}
long WSDwinmwinDev::putBufferToPixmap(){
  if (_bmi == NULL){
    return WS_ERR;
  }
  if (_hBitmap == NULL){
    return 0;
  }
  if (_mdc == 0){
    return 0;
  }
  BITMAPINFO bmi;
  bmi.bmiHeader.biSize=sizeof(BITMAPINFOHEADER);
  bmi.bmiHeader.biWidth=_w;
  bmi.bmiHeader.biHeight=_h;
  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;

  int ret = SetDIBits(_mdc,_hBitmap,0,_h,_bmi,&bmi,DIB_RGB_COLORS);
  if (ret == 0){
    return WS_ERR;
  }
                     
  return WS_NO_ERR;
}

long WSDwinmwinDev::saveFile(char* fname,long type){
  if (_w == 0 || _h == 0){
    return WS_ERR;
  }
  if (type != WS_IMAGE_PNG && type != WS_IMAGE_JPG){
    return WSDmwindowDev::saveFile(fname,type);
  }
  if (type == WS_IMAGE_PNG){
    long ret = initBuffer();
    if (ret != WS_NO_ERR){
      return WS_ERR;
    }
    WSCuchar* buf = getBuffer();

    FILE* fp = fopen(fname,"wb");
    if (fp != NULL){
      long ret = PngWriteBufToFp(fp,(char*)buf,_w,_h);
      if (ret != WS_NO_ERR){
        fclose(fp);
        return WS_ERR;
      }
      fclose(fp);
      return WS_NO_ERR;
    }
  }
  if (type == WS_IMAGE_JPG){
    long ret = initBuffer();
    if (ret != WS_NO_ERR){
      return WS_ERR;
    }
    WSCuchar* buf = getBuffer();

    FILE* fp = fopen(fname,"wb");
    if (fp != NULL){
      long ret = JpegWriteBufToFp(fp,(char*)buf,_w,_h);
      if (ret != WS_NO_ERR){
        fclose(fp);
        return WS_ERR;
      }
      fclose(fp);
      return WS_NO_ERR;
    }
  }
  return WS_ERR;
}
void png_write_fn(png_structp png_ptr, png_bytep buf, png_size_t len) {
  FILE* fp = (FILE*)png_get_io_ptr(png_ptr);
  int ret = fwrite(buf, 1, len, fp);
//printf("ret=%d len=%d\n",ret,len);
  if (ret < (int)len) {
     png_error(png_ptr, "Write error");
  }
}

void png_flush_fn(png_structp png_ptr) {
}
long PngWriteBufToFp(FILE* fp, char* buf,long width,long height) {
  png_structp png_ptr = png_create_write_struct(
       PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
  if (!png_ptr) {
    return WS_ERR;
  }

  if (setjmp(png_jmpbuf(png_ptr))) {
    png_destroy_write_struct(&png_ptr, NULL);
    return WS_ERR;
  }

  png_infop info_ptr = png_create_info_struct(png_ptr);
  if (!info_ptr) {
    png_destroy_write_struct(&png_ptr, NULL);
    return WS_ERR;
  }

  png_set_write_fn(png_ptr, fp, png_write_fn, png_flush_fn);

  long color_bpp = 3;

  png_set_IHDR( png_ptr, info_ptr, width, height, 8, PNG_COLOR_TYPE_RGB,
                PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT,
                PNG_FILTER_TYPE_DEFAULT);

  void** png_row_ptrs = (void**)png_malloc(png_ptr, sizeof(void*) * height);
  for (int i = 0; i < height; ++i) {
    png_row_ptrs[i] = png_malloc(png_ptr, color_bpp * width);
    memcpy(png_row_ptrs[i], buf, color_bpp * width);
    buf += width * color_bpp;
  }

  png_set_rows(png_ptr, info_ptr, (png_bytepp)png_row_ptrs);
  info_ptr->valid |= PNG_INFO_IDAT;
  png_write_png(png_ptr, info_ptr, PNG_TRANSFORM_IDENTITY, NULL);

  for (int i = 0; i < height; ++i) {
    png_free(png_ptr, png_row_ptrs[i]);
  }
  png_free(png_ptr, png_row_ptrs);
  png_destroy_write_struct(&png_ptr, &info_ptr);

  return WS_NO_ERR;
}
long JpegWriteBufToFp(FILE* fp, char* buf,long width,long height){
  struct jpeg_compress_struct cinfo;
  struct jpeg_error_mgr jerr;

  JSAMPROW row_pointer[0];
  int row_stride;

  cinfo.err = jpeg_std_error(&jerr);
  jpeg_create_compress(&cinfo);

  jpeg_stdio_dest(&cinfo, fp);
  cinfo.image_width = width;
  cinfo.image_height = height;
  cinfo.input_components = 3;
  cinfo.in_color_space = JCS_RGB;

  jpeg_set_defaults(&cinfo);
  jpeg_set_quality(&cinfo, 100, TRUE );
  jpeg_start_compress(&cinfo, TRUE);

  row_stride = width * 3;

  while (cinfo.next_scanline < cinfo.image_height) {
    row_pointer[0] = (JSAMPLE*)& buf[cinfo.next_scanline * row_stride];
    (void) jpeg_write_scanlines(&cinfo, row_pointer, 1);
  }
  jpeg_finish_compress(&cinfo);
  jpeg_destroy_compress(&cinfo);

  return WS_NO_ERR;
}

