/******************************************************************************
 * $Id: ocpn_pixel.cpp,v 1.7 2008/08/09 23:58:40 bdbcat Exp $
 *
 * Project:  OpenCP
 * Purpose:  Optimized wxBitmap Object
 * Author:   David Register
 *
 ***************************************************************************
 *   Copyright (C) $YEAR$ by $AUTHOR$   *
 *   $EMAIL$   *
 *                                                                         *
 *   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.             *
 ***************************************************************************
 *
 * $Log: ocpn_pixel.cpp,v $
 * Revision 1.7  2008/08/09 23:58:40  bdbcat
 * Numerous revampings....
 *
 * Revision 1.6  2008/04/11 03:25:59  bdbcat
 * Clean up GTK Optimize option
 *
 * Revision 1.5  2008/04/10 01:03:09  bdbcat
 * Cleanup
 *
 * Revision 1.4  2008/03/30 22:27:32  bdbcat
 * Update for Mac OSX/Unicode
 *
 * Revision 1.3  2008/01/10 03:37:02  bdbcat
 * Update for Mac OSX
 *
 *
 */


//      Original comment header for xshm test code
//      imported from xshm.c
/* xshm.c */

/*
 * Example of how to use the X Shared Memory extension: MIT_SHM.
 * This code was lifted from my Mesa library.  It hasn't been tested
 * in this form but should be close enough for you to get it working.
 * Beware that this extension isn't available on all systems.  Your
 * application code should use #ifdef's around this code so it can be
 * omitted on systems that don't have it, then fallback to using a regular
 * XImage.
 *
 * Brian Paul  Sep, 20, 1995  brianp@ssec.wisc.edu
 */




// ============================================================================
// declarations
// ============================================================================
// ----------------------------------------------------------------------------
// headers
// ----------------------------------------------------------------------------

// For compilers that support precompilation, includes "wx.h".
#include "wx/wxprec.h"
#ifndef WX_PRECOMP
      #include "wx/wx.h"
#endif


#include "dychart.h"
#include "ocpn_pixel.h"

#ifndef WX_PRECOMP
    #include <stdio.h>

    #include "wx/list.h"
    #include "wx/utils.h"
    #include "wx/app.h"
    #include "wx/palette.h"
    #include "wx/dcmemory.h"
    #include "wx/bitmap.h"
    #include "wx/icon.h"
#endif


#ifdef __WXMSW__
      #include "wx/msw/private.h"
      #include "wx/log.h"
      #include "wx/msw/dib.h"
#endif

#include "wx/bitmap.h"
#include "wx/icon.h"
#include "wx/log.h"
#include "wx/image.h"
#include "wx/app.h"
#include "wx/math.h"
#include "wx/gdicmn.h"
#include "wx/palette.h"


// missing from mingw32 header
#ifndef CLR_INVALID
    #define CLR_INVALID ((COLORREF)-1)
#endif // no CLR_INVALID


#ifdef ocpnUSE_ocpnBitmap
#ifdef __WXX11__
#include <sys/ipc.h>
#include <sys/shm.h>
#include <X11/extensions/XShm.h>
#endif
#endif


CPL_CVSID("$Id: ocpn_pixel.cpp,v 1.7 2008/08/09 23:58:40 bdbcat Exp $");


#ifdef  __WXX11__

#ifdef ocpUSE_MITSHM
/*
 * MIT-SHM Test Error handling.
 */
static int MITErrorFlag = 0;
static int HandleXError( Display *dpy, XErrorEvent *event )
{
    MITErrorFlag = 1;
    return 0;
}
#endif

//----------------------------------------------------------------------
//      ocpnXImage Implementation
//----------------------------------------------------------------------

ocpnXImage::ocpnXImage(int width, int height)
{

    m_width = width;
    m_height = height;
    buse_mit = false;
    m_img = NULL;

    xdisplay = (Display *)wxGlobalDisplay();
    xscreen = DefaultScreen( xdisplay );
    xvisual = DefaultVisual( xdisplay, xscreen );
    int bpp = wxTheApp->GetVisualInfo(xdisplay)->m_visualDepth;

#ifdef ocpUSE_MITSHM

//      Check to see if the basic extension is supported
    int ignore;
    bool bMIT_SHM = XQueryExtension( xdisplay, "MIT-SHM", &ignore, &ignore, &ignore );


    if(bMIT_SHM)
    {
        m_img = XShmCreateImage( xdisplay, xvisual, bpp,
                                 ZPixmap, NULL, &shminfo,
                                 width, height );
        if (m_img == NULL)
        {
              wxLogError(_T("XShmCreateImage failed!"));
            goto after_check;
        }

//    Identify and allocate the shared memory buffer
        shminfo.shmid = shmget( IPC_PRIVATE, m_img->bytes_per_line * m_img->height, IPC_CREAT|0777 );
        if (shminfo.shmid < 0)
        {
            XDestroyImage( m_img );
            m_img = NULL;
            wxLogMessage( _T("alloc_back_buffer: Shared memory error (shmget), disabling.") );
            goto after_check;
        }

        shminfo.shmaddr = m_img->data  = (char*)shmat( shminfo.shmid, 0, 0 );

        if (shminfo.shmaddr == (char *) -1)
        {
            XDestroyImage( m_img );
            m_img = NULL;
            wxLogMessage(_T("shmat failed"));
            goto after_check;
        }

//    Make some further checks
        shminfo.readOnly = False;
        MITErrorFlag = 0;

        XSetErrorHandler( HandleXError );
// This may trigger the X protocol error we're ready to catch:
        XShmAttach( xdisplay, &shminfo );
        XSync( xdisplay, False );

        if (MITErrorFlag)
        {
// we are on a remote display, this error is normal, don't print it
            XFlush( xdisplay );
            MITErrorFlag = 0;
            XDestroyImage( m_img );
            m_img = NULL;
            shmdt( shminfo.shmaddr );
            shmctl( shminfo.shmid, IPC_RMID, 0 );
            goto after_check;
        }

        shmctl( shminfo.shmid, IPC_RMID, 0 ); /* nobody else needs it */

        buse_mit = true;                // passed all tests
    }

after_check:
          // if bMIT_SHM
#endif

    if(NULL == m_img)
    {
        m_img = XCreateImage( xdisplay, xvisual, bpp, ZPixmap, 0, 0, width, height, 32, 0 );
        m_img->data = (char*) x_malloc( m_img->bytes_per_line * m_img->height );

        if (m_img->data == NULL)
        {
            XDestroyImage( m_img );
            m_img = NULL;
            wxLogError( wxT("ocpn_Bitmap:Cannot malloc for data image.") );
        }
    }

}

ocpnXImage::~ocpnXImage()
{
#ifdef ocpUSE_MITSHM
    if(buse_mit)
    {
        XShmDetach( xdisplay, &shminfo );
        XDestroyImage( m_img );
        shmdt( shminfo.shmaddr );
    }
    else
    {
        XDestroyImage( m_img );
    }
#else
    XDestroyImage( m_img );
#endif
}



bool ocpnXImage::PutImage(Pixmap pixmap, GC gc)
{
#ifdef ocpUSE_MITSHM
    if(buse_mit)
        XShmPutImage( xdisplay, pixmap, gc, m_img, 0, 0, 0, 0, m_width, m_height, False );
    else
        XPutImage( xdisplay, pixmap, gc, m_img, 0, 0, 0, 0, m_width, m_height );

#else
    XPutImage( xdisplay, pixmap, gc, m_img, 0, 0, 0, 0, m_width, m_height );
#endif

    return true;
}


#endif      //  __WXX11__




/*  Class : PixelCache
    Why a specific class for what is, in effect, a simple unsigned char[] ?
    Answer: Allow performance optimization for specific platforms,
            such as MSW dibSections, and X11 Pixmaps
*/

// ============================================================================
// PixelCache Implementation
// ============================================================================
PixelCache::PixelCache(int width, int height, int depth)
{
    m_width = width;
    m_height = height;
    m_depth = depth;
    m_pbm = NULL;
    m_rgbo = RGB;                        // default value;
    pData = NULL;

    line_pitch_bytes =
            bytes_per_pixel = BPP / 8;
    line_pitch_bytes = bytes_per_pixel * width;


#ifdef ocpnUSE_ocpnBitmap
      m_rgbo = BGR;
#endif

#ifdef __PIX_CACHE_PIXBUF__
      m_rgbo = RGB;
#endif

#ifdef __PIX_CACHE_DIBSECTION__
      m_pDS = new wxDIB(width, -height, BPP);
      pData = m_pDS->GetData();
#endif


#ifdef __PIX_CACHE_WXIMAGE__
      m_pimage = new wxImage(m_width, m_height, (bool)FALSE);
      pData = m_pimage->GetData();
#endif

#ifdef __PIX_CACHE_X11IMAGE__
      m_pocpnXI = new ocpnXImage(width, height);
      pData = (unsigned char *)m_pocpnXI->m_img->data;
#endif            //__PIX_CACHE_X11IMAGE__

#ifdef __PIX_CACHE_PIXBUF__
//      m_pbm = new ocpnBitmap((unsigned char *)NULL, m_width, m_height, m_depth);

///      m_pbm = new ocpnBitmap();
///      m_pixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB,
///                                           1,
///                                           8, m_width, m_height);

///      m_pixbuf = m_pbm->GetPixbuf();
///      pData = gdk_pixbuf_get_pixels(m_pixbuf);
///      m_pbm->SetPixbuf(m_pixbuf, 32);

      pData = (unsigned char *)malloc(m_width * m_height * 4);
///      memset(pData, 255, m_width * m_height * 4);       // set alpha channel to 1
#endif
}

PixelCache::~PixelCache()
{
#ifdef __PIX_CACHE_WXIMAGE__
      delete m_pimage;
      delete m_pbm;
#endif


#ifdef __PIX_CACHE_DIBSECTION__
      delete m_pDS;
#endif

#ifdef __PIX_CACHE_X11IMAGE__
      delete m_pbm;
      delete m_pocpnXI;
#endif

#ifdef __PIX_CACHE_PIXBUF__
      free(pData);
      delete m_pbm;
#endif

}

void PixelCache::SelectIntoDC(wxMemoryDC &dc)
{

#ifdef __PIX_CACHE_DIBSECTION__
      ocpnMemDC *pmdc = dynamic_cast<ocpnMemDC*>(&dc);
      pmdc->SelectObject (*m_pDS);

#endif      //__PIX_CACHE_DIBSECTION__


#ifdef __PIX_CACHE_WXIMAGE__
//    delete m_pbm;                       // kill the old one

      //    Convert image to bitmap
#ifdef ocpnUSE_ocpnBitmap
      if(!m_pbm)
            m_pbm = new ocpnBitmap(*m_pimage, m_depth);
#else
      if(!m_pbm)
            m_pbm = new wxBitmap (*m_pimage, -1);
#endif

      if(m_pbm)
            dc.SelectObject(*m_pbm);
#endif            // __PIX_CACHE_WXIMAGE__


#ifdef __PIX_CACHE_X11IMAGE__
      if(!m_pbm)
        m_pbm = new ocpnBitmap(m_pocpnXI, m_width, m_height, m_depth);
      dc.SelectObject(*m_pbm);
#endif            //__PIX_CACHE_X11IMAGE__

#ifdef __PIX_CACHE_PIXBUF__
      if(!m_pbm)
            m_pbm = new ocpnBitmap(pData, m_width, m_height, m_depth);
      if(m_pbm)
      {
          dc.SelectObject(*m_pbm);
      }
#endif          //__PIX_CACHE_PIXBUF__


}


unsigned char *PixelCache::GetpData(void)
{
    return pData;
}




#ifdef ocpnUSE_ocpnBitmap
//-----------------------------------------------------------------------------
/*
    Class: ocpnBitmap

    Derived from wxBitmap

    Why?....
    wxWidgets does a very correct, but sometimes slow job of bitmap creation
    and copying. ocpnBitmap is an optimization of wxBitmap for specific
    platforms and color formats.

    ocpn_Bitmap is optimized specifically for Windows and X11 Linux/Unix systems,
    taking advantage of some known underlying data structures and formats.

    There is (currently) no optimization for for other platforms,
    such as GTK or MAC

    The included methods are very different for MSW and X11
          See the Code

    */

// ----------------------------------------------------------------------------
// macros
// ----------------------------------------------------------------------------
#define M_BMPDATA wx_static_cast(wxBitmapRefData*, m_refData)

IMPLEMENT_DYNAMIC_CLASS(ocpnBitmap, wxBitmap/*wxGDIObject*/)


//class wxBitmapRefData;

// ============================================================================
// implementation
// ============================================================================

ocpnBitmap::ocpnBitmap()
{
}

//ocpnBitmap::~ocpnBitmap()
//{
//}

/// This is the GDK version
#ifdef  __WXGTK__
#ifdef opcnUSE_GTK_OPTIMIZE

// ----------------------------------------------------------------------------
// Create from Data
// ----------------------------------------------------------------------------

bool ocpnBitmap::CreateFromData( void *pPix, int width, int height, int depth )
{
    /*
    XImage *img = NULL;

//    Do some basic setup in the parent wxBitmap class
    Create(width, height, -1);

    Display *xdisplay = (Display *)GetDisplay();

    ocpnXImage *pXI = new ocpnXImage(width, height);
    img = pXI->m_img;

//    Faster render from a 24 or 32 bit pixel buffer

    if((pPix != NULL ) && (NULL != img))
    {
        unsigned char* data = (unsigned char *)pPix;
        if(depth == 32)                          // special fast case
        {
            for (int y = 0; y < height; y++)
            {
                char *pd = img->data + (y * img->bytes_per_line);
                unsigned char *ps = data + (y * width * 4);
                memcpy(pd, ps, width*4);
            }
        }

        else
        {
            int *pi = (int *)img->data;
            int index = 0;
            for (int y = 0; y < height; y++)
            {
                for (int x = 0; x < width; x++)
                {
                    int ri = *(int *)(&data[index]);
                    index++;
                    index++;
                    index++;

                    *pi = ri;
                    pi++;

                }
            }
        }
    }

        // Blit picture

    Pixmap mypixmap = ((Pixmap )GetPixmap());
    GC gc = XCreateGC( xdisplay, mypixmap, 0, NULL );

    pXI->PutImage(mypixmap, gc);

    delete pXI;

    XFreeGC( xdisplay, gc );
    */

    /////////////////////
    Create(width, height, 32);

//    GdkPixbuf* pixbuf = wx_static_cast(wxBitmapRefData*, m_refData))->m_pixbuf;
 //   GdkPixbuf* pixbuf = M_BMPDATA->m_pixbuf;

    // Copy the data:
    if(NULL != pPix)
    {
        GdkPixbuf* pixbuf = GetPixbuf();

        if (!pixbuf)
            return false;

        unsigned char* in = (unsigned char *)pPix;
        unsigned char *out = gdk_pixbuf_get_pixels(pixbuf);

        int rowpad = gdk_pixbuf_get_rowstride(pixbuf) - 4 * width;

        for (int y = 0; y < height; y++, out += rowpad)
        {
///            memcpy(out, in, width * 4);
///            in  += 4 * width;
///            out += 4 * width;


            for (int x = 0; x < width; x++, out += 4, in += 4)
            {
                out[0] = in[0];
                out[1] = in[1];
                out[2] = in[2];
                out[3] = in[3];
            }

        }
    }
    return true;

}

/*
bool wxBitmap::CreateFromImageAsPixbuf(const wxImage& image)
{
    wxASSERT(image.HasAlpha());

    int width = image.GetWidth();
    int height = image.GetHeight();

    Create(width, height, 32);
    GdkPixbuf* pixbuf = M_BMPDATA->m_pixbuf;
    if (!pixbuf)
        return false;

    // Copy the data:
    const unsigned char* in = image.GetData();
    unsigned char *out = gdk_pixbuf_get_pixels(pixbuf);
    unsigned char *alpha = image.GetAlpha();

    int rowpad = gdk_pixbuf_get_rowstride(pixbuf) - 4 * width;

    for (int y = 0; y < height; y++, out += rowpad)
    {
        for (int x = 0; x < width; x++, alpha++, out += 4, in += 3)
        {
            out[0] = in[0];
            out[1] = in[1];
            out[2] = in[2];
            out[3] = *alpha;
        }
    }

    return true;
}
*/
#endif //opcnUSE_GTK_OPTIMIZE
#endif



#ifdef __WXX11__
//    This is the X11 Version

// ----------------------------------------------------------------------------
// Create from ocpnXImage
// ----------------------------------------------------------------------------

bool ocpnBitmap::CreateFromocpnXImage( ocpnXImage *poXI, int width, int height, int depth )
{
//    Do some basic setup in the parent  wxBitmap class
    Create(width, height, -1);

    Display *xdisplay = (Display *)GetDisplay();

    XImage *data_image = poXI->m_img;
    bool bShared = poXI->buse_mit;

// Blit picture

    Pixmap mypixmap = ((Pixmap )GetPixmap());

    GC gc = XCreateGC( xdisplay, mypixmap, 0, NULL );

    if(bShared)
            XShmPutImage( xdisplay, mypixmap, gc, data_image, 0, 0, 0, 0, width, height, False );
    else
            XPutImage( xdisplay, mypixmap, gc, data_image, 0, 0, 0, 0, width, height );

    XFreeGC( xdisplay, gc );

    return true;
}

// ----------------------------------------------------------------------------
// Create from Data
// ----------------------------------------------------------------------------

bool ocpnBitmap::CreateFromData( void *pPix, int width, int height, int depth )
{

    XImage *img = NULL;

//    Do some basic setup in the parent wxBitmap class
    Create(width, height, -1);

    Display *xdisplay = (Display *)GetDisplay();

    ocpnXImage *pXI = new ocpnXImage(width, height);
    img = pXI->m_img;

//    Faster render from a 24 or 32 bit pixel buffer

    if((pPix != NULL ) && (NULL != img))
    {
        unsigned char* data = (unsigned char *)pPix;
        if(depth == 32)                          // special fast case
        {
            for (int y = 0; y < height; y++)
            {
                char *pd = img->data + (y * img->bytes_per_line);
                unsigned char *ps = data + (y * width * 4);
                memcpy(pd, ps, width*4);
            }
        }

        else
        {
            int *pi = (int *)img->data;
            int index = 0;
            for (int y = 0; y < height; y++)
            {
                for (int x = 0; x < width; x++)
                {
                    int ri = *(int *)(&data[index]);
                    index++;
                    index++;
                    index++;

                    *pi = ri;
                    pi++;

                }
            }
        }
    }

        // Blit picture

    Pixmap mypixmap = ((Pixmap )GetPixmap());
    GC gc = XCreateGC( xdisplay, mypixmap, 0, NULL );

    pXI->PutImage(mypixmap, gc);

    delete pXI;

    XFreeGC( xdisplay, gc );

    return TRUE;
}

#endif            //__WXX11__


#ifdef __WXMSW__

// ----------------------------------------------------------------------------
// Create from Data
// ----------------------------------------------------------------------------
bool ocpnBitmap::CreateFromData( void *pPix, int width, int height, int depth )
{
      m_refData = CreateData();                 // found in wxBitmap
//    int width = image.GetWidth();
//    int height0 = image.GetHeight();

      int height0 = height;
    int sizeLimit = 1280*1024 *3;

    int bmpHeight = height0;
//    int height = bmpHeight;

    // calc the number of bytes per scanline and padding
    int bytePerLine = width*3;
    int sizeDWORD = sizeof( DWORD );
    int lineBoundary = bytePerLine % sizeDWORD;
    int padding = 0;
    if( lineBoundary > 0 )
    {
        padding = sizeDWORD - lineBoundary;
        bytePerLine += padding;
    }

    // set bitmap parameters
    SetWidth( width );
    SetHeight( bmpHeight );
    if (depth == -1) depth = wxDisplayDepth();
    SetDepth( depth );


    // create a DIB header
    int headersize = sizeof(BITMAPINFOHEADER);
    BITMAPINFO *lpDIBh = (BITMAPINFO *) malloc( headersize );
    wxCHECK_MSG( lpDIBh, FALSE, wxT("could not allocate memory for DIB header") );
    // Fill in the DIB header
    lpDIBh->bmiHeader.biSize = headersize;
    lpDIBh->bmiHeader.biWidth = (DWORD)width;
    lpDIBh->bmiHeader.biHeight = (DWORD)(-height);
    lpDIBh->bmiHeader.biSizeImage = bytePerLine*height;
    //   the general formula for biSizeImage:
    //      ( ( ( ((DWORD)width*24) +31 ) & ~31 ) >> 3 ) * height;
    lpDIBh->bmiHeader.biPlanes = 1;
    lpDIBh->bmiHeader.biBitCount = 24;
    lpDIBh->bmiHeader.biCompression = BI_RGB;
    lpDIBh->bmiHeader.biClrUsed = 0;
    // These seem not really needed for our purpose here.
    lpDIBh->bmiHeader.biClrImportant = 0;
    lpDIBh->bmiHeader.biXPelsPerMeter = 0;
    lpDIBh->bmiHeader.biYPelsPerMeter = 0;
    // memory for DIB data
    unsigned char *lpBits;
    lpBits = (unsigned char *)malloc( lpDIBh->bmiHeader.biSizeImage );
    if( !lpBits )
    {
        wxFAIL_MSG( wxT("could not allocate memory for DIB") );
        free( lpDIBh );
        return FALSE;
    }

    // create and set the device-dependent bitmap
    HDC hdc = ::GetDC(NULL);
    HDC memdc = ::CreateCompatibleDC( hdc );
    HBITMAP hbitmap;
//    hbitmap = ::CreateCompatibleBitmap( hdc, width, bmpHeight );

//    if(hbitmap == NULL)
//          int cop =0;

//    ::SelectObject( memdc, hbitmap);


// copy image data into DIB data
    unsigned char *data = (unsigned char *)pPix;
    int i, j;
    unsigned char *ptdata = data;
    unsigned char *ptbits;

    ptbits = lpBits;

    if(pPix)
      {
            for( j=0; j<height; j++ )
            {

                  memcpy(ptbits, ptdata, width * 3);
                  ptbits += width * 3;
                  ptdata += width * 3;

                  for( i=0; i< padding; i++ )   *(ptbits++) = 0;
            }
      }

      else
      {
            for( j=0; j<height; j++ )
            {

                  memset(ptbits, 0,  width * 3);
                  ptbits += width * 3;

                  for( i=0; i< padding; i++ )   *(ptbits++) = 0;
            }
      }




hbitmap = CreateDIBitmap( hdc, &(lpDIBh->bmiHeader), CBM_INIT, lpBits, lpDIBh, DIB_RGB_COLORS );
// The above line is equivalent to the following two lines.
//    hbitmap = ::CreateCompatibleBitmap( hdc, width, height );
//    ::SetDIBits( hdc, hbitmap, 0, height, lpBits, lpDIBh, DIB_RGB_COLORS);
    // or the following lines
//    hbitmap = ::CreateCompatibleBitmap( hdc, width, height );
//    HDC memdc = ::CreateCompatibleDC( hdc );
//    ::SelectObject( memdc, hbitmap);
//    ::SetDIBitsToDevice( memdc, 0, 0, width, height,
//              0, 0, 0, height, (void *)lpBits, lpDIBh, DIB_RGB_COLORS);
//    ::SelectObject( memdc, 0 );
//    ::DeleteDC( memdc );
    SetHBITMAP( (WXHBITMAP) hbitmap );


    // free allocated resources
    ::DeleteDC( memdc );
    ::ReleaseDC(NULL, hdc);
    free(lpDIBh);
    free(lpBits);

    return TRUE;
}






// ----------------------------------------------------------------------------
// wxImage from conversion
// ----------------------------------------------------------------------------


bool ocpnBitmap::CreateFromImage( const wxImage& image, int depth )
{
//    wxCHECK_MSG( image.Ok(), FALSE, wxT("invalid image") )

      m_refData = CreateData();                 // found in wxBitmap

    // sizeLimit is the MS upper limit for the DIB size
    int sizeLimit = 1280*1024 *3;

    // width and height of the device-dependent bitmap
    int width = image.GetWidth();
    int bmpHeight = image.GetHeight();

    // calc the number of bytes per scanline and padding
    int bytePerLine = width*3;
    int sizeDWORD = sizeof( DWORD );
    int lineBoundary = bytePerLine % sizeDWORD;
    int padding = 0;
    if( lineBoundary > 0 )
    {
        padding = sizeDWORD - lineBoundary;
        bytePerLine += padding;
    }
    // calc the number of DIBs and heights of DIBs
    int numDIB = 1;
    int hRemain = 0;
    int height = sizeLimit/bytePerLine;
    if( height >= bmpHeight )
        height = bmpHeight;
    else
    {
        numDIB =  bmpHeight / height;
        hRemain = bmpHeight % height;
        if( hRemain >0 )  numDIB++;
    }

    // set bitmap parameters
    wxCHECK_MSG( image.Ok(), FALSE, wxT("invalid image") );
    SetWidth( width );
    SetHeight( bmpHeight );
    if (depth == -1) depth = wxDisplayDepth();
    SetDepth( depth );

#if wxUSE_PALETTE
    // Copy the palette from the source image
    SetPalette(image.GetPalette());
#endif // wxUSE_PALETTE

    // create a DIB header
    int headersize = sizeof(BITMAPINFOHEADER);
    BITMAPINFO *lpDIBh = (BITMAPINFO *) malloc( headersize );
    wxCHECK_MSG( lpDIBh, FALSE, wxT("could not allocate memory for DIB header") );
    // Fill in the DIB header
    lpDIBh->bmiHeader.biSize = headersize;
    lpDIBh->bmiHeader.biWidth = (DWORD)width;
    lpDIBh->bmiHeader.biHeight = (DWORD)(-height);
    lpDIBh->bmiHeader.biSizeImage = bytePerLine*height;
    //   the general formula for biSizeImage:
    //      ( ( ( ((DWORD)width*24) +31 ) & ~31 ) >> 3 ) * height;
    lpDIBh->bmiHeader.biPlanes = 1;
    lpDIBh->bmiHeader.biBitCount = 24;
    lpDIBh->bmiHeader.biCompression = BI_RGB;
    lpDIBh->bmiHeader.biClrUsed = 0;
    // These seem not really needed for our purpose here.
    lpDIBh->bmiHeader.biClrImportant = 0;
    lpDIBh->bmiHeader.biXPelsPerMeter = 0;
    lpDIBh->bmiHeader.biYPelsPerMeter = 0;
    // memory for DIB data
    unsigned char *lpBits;
    lpBits = (unsigned char *)malloc( lpDIBh->bmiHeader.biSizeImage );
    if( !lpBits )
    {
        wxFAIL_MSG( wxT("could not allocate memory for DIB") );
        free( lpDIBh );
        return FALSE;
    }

    // create and set the device-dependent bitmap
    HDC hdc = ::GetDC(NULL);
    HDC memdc = ::CreateCompatibleDC( hdc );
    HBITMAP hbitmap;
//    hbitmap = ::CreateCompatibleBitmap( hdc, width, bmpHeight );

//    if(hbitmap == NULL)
//          int cop =0;

//    ::SelectObject( memdc, hbitmap);

#if wxUSE_PALETTE
    HPALETTE hOldPalette = 0;
    if (image.GetPalette().Ok())
    {
        hOldPalette = ::SelectPalette(memdc, (HPALETTE) image.GetPalette().GetHPALETTE(), FALSE);
        ::RealizePalette(memdc);
    }
#endif // wxUSE_PALETTE

    // copy image data into DIB data and then into DDB (in a loop)
    unsigned char *data = image.GetData();
    int i, j, n;
    int origin = 0;
    unsigned char *ptdata = data;
    unsigned char *ptbits;

    for( n=0; n<numDIB; n++ )
    {
        if( numDIB > 1 && n == numDIB-1 && hRemain > 0 )
        {
            // redefine height and size of the (possibly) last smaller DIB
            // memory is not reallocated
            height = hRemain;
            lpDIBh->bmiHeader.biHeight = (DWORD)(-height);
            lpDIBh->bmiHeader.biSizeImage = bytePerLine*height;
        }
        ptbits = lpBits;

        for( j=0; j<height; j++ )
        {

            memcpy(ptbits, ptdata, width * 3);
                  ptbits += width * 3;
                  ptdata += width * 3;

/*
                  for( i=0; i<width; i++ )
            {
                *(ptbits++) = *(ptdata+2);
                *(ptbits++) = *(ptdata+1);
                *(ptbits++) = *(ptdata  );
                ptdata += 3;
            }
*/
            for( i=0; i< padding; i++ )   *(ptbits++) = 0;
        }
//        ::StretchDIBits( memdc, 0, origin, width, height,
//            0, 0, width, height, lpBits, lpDIBh, DIB_RGB_COLORS, SRCCOPY);
        origin += height;
        // if numDIB = 1,  lines below can also be used
    hbitmap = CreateDIBitmap( hdc, &(lpDIBh->bmiHeader), CBM_INIT, lpBits, lpDIBh, DIB_RGB_COLORS );
//      if(hbitmap == NULL)
//            int cop =0;

        // The above line is equivalent to the following two lines.
//    hbitmap = ::CreateCompatibleBitmap( hdc, width, height );
//    ::SetDIBits( hdc, hbitmap, 0, height, lpBits, lpDIBh, DIB_RGB_COLORS);
        // or the following lines
//    hbitmap = ::CreateCompatibleBitmap( hdc, width, height );
//    HDC memdc = ::CreateCompatibleDC( hdc );
//    ::SelectObject( memdc, hbitmap);
//    ::SetDIBitsToDevice( memdc, 0, 0, width, height,
//              0, 0, 0, height, (void *)lpBits, lpDIBh, DIB_RGB_COLORS);
//    ::SelectObject( memdc, 0 );
//    ::DeleteDC( memdc );
    }
    SetHBITMAP( (WXHBITMAP) hbitmap );

//      if(!this->Ok())
//         int cop = this->Ok();

#if wxUSE_PALETTE
    if (hOldPalette)
        SelectPalette(memdc, hOldPalette, FALSE);
#endif // wxUSE_PALETTE

    // similarly, created an mono-bitmap for the possible mask
    if( image.HasMask() )
    {
        hbitmap = ::CreateBitmap( (WORD)width, (WORD)bmpHeight, 1, 1, NULL );
        HGDIOBJ hbmpOld = ::SelectObject( memdc, hbitmap);
        if( numDIB == 1 )   height = bmpHeight;
        else                height = sizeLimit/bytePerLine;
        lpDIBh->bmiHeader.biHeight = (DWORD)(-height);
        lpDIBh->bmiHeader.biSizeImage = bytePerLine*height;
        origin = 0;
        unsigned char r = image.GetMaskRed();
        unsigned char g = image.GetMaskGreen();
        unsigned char b = image.GetMaskBlue();
        unsigned char zero = 0, one = 255;
        ptdata = data;
        for( n=0; n<numDIB; n++ )
        {
            if( numDIB > 1 && n == numDIB - 1 && hRemain > 0 )
            {
                // redefine height and size of the (possibly) last smaller DIB
                // memory is not reallocated
                height = hRemain;
                lpDIBh->bmiHeader.biHeight = (DWORD)(-height);
                lpDIBh->bmiHeader.biSizeImage = bytePerLine*height;
            }
            ptbits = lpBits;
            for( int j=0; j<height; j++ )
            {
                for(i=0; i<width; i++ )
                {
                    // was causing a code gen bug in cw : if( ( cr !=r) || (cg!=g) || (cb!=b) )
                    unsigned char cr = (*(ptdata++)) ;
                    unsigned char cg = (*(ptdata++)) ;
                    unsigned char cb = (*(ptdata++)) ;

                    if( ( cr !=r) || (cg!=g) || (cb!=b) )
                    {
                        *(ptbits++) = one;
                        *(ptbits++) = one;
                        *(ptbits++) = one;
                    }
                    else
                    {
                        *(ptbits++) = zero;
                        *(ptbits++) = zero;
                        *(ptbits++) = zero;
                    }
                }
                for( i=0; i< padding; i++ )   *(ptbits++) = zero;
            }
            ::StretchDIBits( memdc, 0, origin, width, height,\
                0, 0, width, height, lpBits, lpDIBh, DIB_RGB_COLORS, SRCCOPY);
            origin += height;
        }
        // create a wxMask object
        wxMask *mask = new wxMask();
        mask->SetMaskBitmap( (WXHBITMAP) hbitmap );
        SetMask( mask );
        // It will be deleted when the wxBitmap object is deleted (as of 01/1999)
        /* The following can also be used but is slow to run
        wxColour colour( GetMaskRed(), GetMaskGreen(), GetMaskBlue());
        wxMask *mask = new wxMask( *this, colour );
        SetMask( mask );
        */

        ::SelectObject( memdc, hbmpOld );
    }

    // free allocated resources
    ::DeleteDC( memdc );
    ::ReleaseDC(NULL, hdc);
    free(lpDIBh);
    free(lpBits);

#if WXWIN_COMPATIBILITY_2
    // check the wxBitmap object
    GetBitmapData()->SetOk();
#endif // WXWIN_COMPATIBILITY_2

    return TRUE;
}



#endif            // __WXMSW__

#endif            //ocpnUSE_ocpnBitmap



// ============================================================================
// ocpnMemDC implementation
// ============================================================================

IMPLEMENT_DYNAMIC_CLASS(ocpnMemDC, wxMemoryDC)


ocpnMemDC::ocpnMemDC()
{
}


#ifdef ocpnUSE_DIBSECTION
void ocpnMemDC::SelectObject(wxDIB& dib)
{
    // select old bitmap out of the device context
    if ( m_oldBitmap )
    {
            ::SelectObject(GetHdc(), (HBITMAP) m_oldBitmap);
            if ( m_selectedBitmap.Ok() )
            {
#ifdef __WXDEBUG__
 //           m_selectedBitmap.SetSelectedInto(NULL);
#endif
            m_selectedBitmap = wxNullBitmap;
            }
    }

// check for whether the bitmap is already selected into a device context
//    wxASSERT_MSG( !bitmap.GetSelectedInto() ||
//                  (bitmap.GetSelectedInto() == this),
//                  wxT("Bitmap is selected in another wxMemoryDC, delete the first wxMemoryDC or use SelectObject(NULL)") );

/*
    m_selectedBitmap = bitmap;
    WXHBITMAP hBmp = m_selectedBitmap.GetHBITMAP();
    if ( !hBmp )
    return;                             // already selected
*/
    m_pselectedDIB = &dib;
    HBITMAP hDIB = m_pselectedDIB->GetHandle();
    if ( !hDIB)
        return;                             // already selected

#ifdef __WXDEBUG__
//    m_selectedBitmap.SetSelectedInto(this);
#endif

//    hBmp = (WXHBITMAP)::SelectObject(GetHdc(), (HBITMAP)hBmp);

      hDIB = (HBITMAP)::SelectObject(GetHdc(), hDIB);


      if ( !hDIB )
      {
          wxLogLastError(wxT("SelectObject(ocpnMemDC, DIB)"));

          wxFAIL_MSG(wxT("Couldn't select a DIB into ocpnMemDC"));
      }

      else if ( !m_oldBitmap )
      {
          m_oldBitmap = hDIB;
      }

}

#endif      //ocpnUSE_DIBSECTION
