/******************************************************************************
 *
 * Project:  OpenCPN
 * Purpose:  Optimized wxBitmap Object
 * Author:   David Register
 *
 ***************************************************************************
 *   Copyright (C) 2010 by David S. Register                               *
 *                                                                         *
 *   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.,                                       *
 *   51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,  USA.         *
 ***************************************************************************
 *
 */


//      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



#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



//---------------------------------------------------------------------------------------------------------
//              Private Memory Management
//---------------------------------------------------------------------------------------------------------

static void *x_malloc(size_t t)
{
    void *pr = malloc( t );

    //      malloc fails
    if( NULL == pr ) {
        wxLogMessage( _T("x_malloc...malloc fails with request of %d bytes."), t );

        // Cat the /proc/meminfo file

        char *p;
        char buf[2000];
        int len;

        int fd = open( "/proc/meminfo", O_RDONLY );

        if( fd == -1 ) exit( 1 );

        len = read( fd, buf, sizeof( buf ) - 1 );
        if( len <= 0 ) {
            close( fd );
            exit( 1 );
        }
        close( fd );
        buf[len] = 0;

        p = buf;
        while( *p ) {
//                        printf("%c", *p++);
        }

        exit( 0 );
        return NULL;                            // for MSVC
    }

    else {
        if( t > malloc_max ) {
            malloc_max = t;
//                      wxLogMessage(_T("New malloc_max: %d", malloc_max));
        }

        return pr;                                      // good return
    }

}


//----------------------------------------------------------------------
//      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;

    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();
      //        For DIBsections, each scan line is DWORD aligned, padded on the right
      line_pitch_bytes = (((m_width * 24) + 31) & ~31) >> 3;
      
#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

}

size_t PixelCache::GetLength(void)
{
#ifdef __PIX_CACHE_WXIMAGE__
    return m_width * m_height * 3;
#else
    return 0;
#endif    
}
    

void PixelCache::Update(void)
{
#ifdef __PIX_CACHE_WXIMAGE__
    delete m_pbm;                       // kill the old one
    m_pbm = NULL;
#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) const
{
    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




/*
 * Rotation code by Carlos Moreno
 * Adapted to static and modified for improved performance by dsr
 */

static const double wxROTATE_EPSILON = 1e-10;

// Auxiliary function to rotate a point (x,y) with respect to point p0
// make it inline and use a straight return to facilitate optimization
// also, the function receives the sine and cosine of the angle to avoid
// repeating the time-consuming calls to these functions -- sin/cos can
// be computed and stored in the calling function.


static inline wxRealPoint
            wxRotatePoint(const wxRealPoint& p, double cos_angle, double sin_angle,
                          const wxRealPoint& p0)
{
      return wxRealPoint(p0.x + (p.x - p0.x) * cos_angle - (p.y - p0.y) * sin_angle,
                         p0.y + (p.y - p0.y) * cos_angle + (p.x - p0.x) * sin_angle);
}

static inline wxRealPoint
            wxRotatePoint(double x, double y, double cos_angle, double sin_angle,
                          const wxRealPoint & p0)
{
      return wxRotatePoint (wxRealPoint(x,y), cos_angle, sin_angle, p0);
}


wxImage Image_Rotate(wxImage &base_image, double angle, const wxPoint & centre_of_rotation, bool interpolating, wxPoint * offset_after_rotation)
{
      int i;
      angle = -angle;     // screen coordinates are a mirror image of "real" coordinates

      bool has_alpha = base_image.HasAlpha();

      const int w = base_image.GetWidth(),
                             h = base_image.GetHeight();

    // Create pointer-based array to accelerate access to wxImage's data
                             unsigned char ** data = new unsigned char * [h];
                             data[0] = base_image.GetData();
                             for (i = 1; i < h; i++)
                                   data[i] = data[i - 1] + (3 * w);

    // Same for alpha channel
                             unsigned char ** alpha = NULL;
                             if (has_alpha)
                             {
                                   alpha = new unsigned char * [h];
                                   alpha[0] = base_image.GetAlpha();
                                   for (i = 1; i < h; i++)
                                         alpha[i] = alpha[i - 1] + w;
                             }

    // precompute coefficients for rotation formula
    // (sine and cosine of the angle)
                             const double cos_angle = cos(angle);
                             const double sin_angle = sin(angle);

    // Create new Image to store the result
    // First, find rectangle that covers the rotated image;  to do that,
    // rotate the four corners

                             const wxRealPoint p0(centre_of_rotation.x, centre_of_rotation.y);

                             wxRealPoint p1 = wxRotatePoint (0, 0, cos_angle, sin_angle, p0);
                             wxRealPoint p2 = wxRotatePoint (0, h, cos_angle, sin_angle, p0);
                             wxRealPoint p3 = wxRotatePoint (w, 0, cos_angle, sin_angle, p0);
                             wxRealPoint p4 = wxRotatePoint (w, h, cos_angle, sin_angle, p0);

                             int x1a = (int) floor (wxMin (wxMin(p1.x, p2.x), wxMin(p3.x, p4.x)));
                             int y1a = (int) floor (wxMin (wxMin(p1.y, p2.y), wxMin(p3.y, p4.y)));
                             int x2a = (int) ceil (wxMax (wxMax(p1.x, p2.x), wxMax(p3.x, p4.x)));
                             int y2a = (int) ceil (wxMax (wxMax(p1.y, p2.y), wxMax(p3.y, p4.y)));

    // Create rotated image
                             wxImage rotated (x2a - x1a + 1, y2a - y1a + 1, false);
    // With alpha channel
                             if (has_alpha)
                                   rotated.SetAlpha();

                             if (offset_after_rotation != NULL)
                             {
                                   *offset_after_rotation = wxPoint (x1a, y1a);
                             }

    // GRG: The rotated (destination) image is always accessed
    //      sequentially, so there is no need for a pointer-based
    //      array here (and in fact it would be slower).
                             //
                             unsigned char * dst = rotated.GetData();

                             unsigned char * alpha_dst = NULL;
                             if (has_alpha)
                                   alpha_dst = rotated.GetAlpha();

    // GRG: if the original image has a mask, use its RGB values
    //      as the blank pixel, else, fall back to default (black).
                             //
                             unsigned char blank_r = 0;
                             unsigned char blank_g = 0;
                             unsigned char blank_b = 0;

                             if (base_image.HasMask())
                             {
                                   blank_r = base_image.GetMaskRed();
                                   blank_g = base_image.GetMaskGreen();
                                   blank_b = base_image.GetMaskBlue();
                                   rotated.SetMaskColour( blank_r, blank_g, blank_b );
                             }

    // Now, for each point of the rotated image, find where it came from, by
    // performing an inverse rotation (a rotation of -angle) and getting the
    // pixel at those coordinates

                             const int rH = rotated.GetHeight();
                             const int rW = rotated.GetWidth();

    // GRG: I've taken the (interpolating) test out of the loops, so that
    //      it is done only once, instead of repeating it for each pixel.

                             if (interpolating)
                             {
                                   for (int y = 0; y < rH; y++)
                                   {
                                         for (int x = 0; x < rW; x++)
                                         {
                                               wxRealPoint src = wxRotatePoint (x + x1a, y + y1a, cos_angle, -sin_angle, p0);

                                               if (-0.25 < src.x && src.x < w - 0.75 &&
                                                    -0.25 < src.y && src.y < h - 0.75)
                                               {
                    // interpolate using the 4 enclosing grid-points.  Those
                    // points can be obtained using floor and ceiling of the
                    // exact coordinates of the point
                                                     int x1, y1, x2, y2;

                                                     if (0 < src.x && src.x < w - 1)
                                                     {
                                                           x1 = wxRound(floor(src.x));
                                                           x2 = wxRound(ceil(src.x));
                                                     }
                                                     else    // else means that x is near one of the borders (0 or width-1)
                                                     {
                                                           x1 = x2 = wxRound (src.x);
                                                     }

                                                     if (0 < src.y && src.y < h - 1)
                                                     {
                                                           y1 = wxRound(floor(src.y));
                                                           y2 = wxRound(ceil(src.y));
                                                     }
                                                     else
                                                     {
                                                           y1 = y2 = wxRound (src.y);
                                                     }

                    // get four points and the distances (square of the distance,
                    // for efficiency reasons) for the interpolation formula

                    // GRG: Do not calculate the points until they are
                    //      really needed -- this way we can calculate
                    //      just one, instead of four, if d1, d2, d3
                    //      or d4 are < wxROTATE_EPSILON

                                                     const double d1 = (src.x - x1) * (src.x - x1) + (src.y - y1) * (src.y - y1);
                                                     const double d2 = (src.x - x2) * (src.x - x2) + (src.y - y1) * (src.y - y1);
                                                     const double d3 = (src.x - x2) * (src.x - x2) + (src.y - y2) * (src.y - y2);
                                                     const double d4 = (src.x - x1) * (src.x - x1) + (src.y - y2) * (src.y - y2);

                    // Now interpolate as a weighted average of the four surrounding
                    // points, where the weights are the distances to each of those points

                    // If the point is exactly at one point of the grid of the source
                    // image, then don't interpolate -- just assign the pixel

                    // d1,d2,d3,d4 are positive -- no need for abs()
                                                     if (d1 < wxROTATE_EPSILON)
                                                     {
                                                           unsigned char *p = data[y1] + (3 * x1);
                                                           *(dst++) = *(p++);
                                                           *(dst++) = *(p++);
                                                           *(dst++) = *p;

                                                           if (has_alpha)
                                                                 *(alpha_dst++) = *(alpha[y1] + x1);
                                                     }
                                                     else if (d2 < wxROTATE_EPSILON)
                                                     {
                                                           unsigned char *p = data[y1] + (3 * x2);
                                                           *(dst++) = *(p++);
                                                           *(dst++) = *(p++);
                                                           *(dst++) = *p;

                                                           if (has_alpha)
                                                                 *(alpha_dst++) = *(alpha[y1] + x2);
                                                     }
                                                     else if (d3 < wxROTATE_EPSILON)
                                                     {
                                                           unsigned char *p = data[y2] + (3 * x2);
                                                           *(dst++) = *(p++);
                                                           *(dst++) = *(p++);
                                                           *(dst++) = *p;

                                                           if (has_alpha)
                                                                 *(alpha_dst++) = *(alpha[y2] + x2);
                                                     }
                                                     else if (d4 < wxROTATE_EPSILON)
                                                     {
                                                           unsigned char *p = data[y2] + (3 * x1);
                                                           *(dst++) = *(p++);
                                                           *(dst++) = *(p++);
                                                           *(dst++) = *p;

                                                           if (has_alpha)
                                                                 *(alpha_dst++) = *(alpha[y2] + x1);
                                                     }
                                                     else
                                                     {
                        // weights for the weighted average are proportional to the inverse of the distance
                                                           unsigned char *v1 = data[y1] + (3 * x1);
                                                           unsigned char *v2 = data[y1] + (3 * x2);
                                                           unsigned char *v3 = data[y2] + (3 * x2);
                                                           unsigned char *v4 = data[y2] + (3 * x1);

                                                           const double w1 = 1/d1, w2 = 1/d2, w3 = 1/d3, w4 = 1/d4;

                        // GRG: Unrolled.

                                                           *(dst++) = (unsigned char)
                                                                       ( (w1 * *(v1++) + w2 * *(v2++) +
                                                                       w3 * *(v3++) + w4 * *(v4++)) /
                                                                       (w1 + w2 + w3 + w4) );
                                                           *(dst++) = (unsigned char)
                                                                       ( (w1 * *(v1++) + w2 * *(v2++) +
                                                                       w3 * *(v3++) + w4 * *(v4++)) /
                                                                       (w1 + w2 + w3 + w4) );
                                                           *(dst++) = (unsigned char)
                                                                       ( (w1 * *v1 + w2 * *v2 +
                                                                       w3 * *v3 + w4 * *v4) /
                                                                       (w1 + w2 + w3 + w4) );

                                                           if (has_alpha)
                                                           {
                                                                 v1 = alpha[y1] + (x1);
                                                                 v2 = alpha[y1] + (x2);
                                                                 v3 = alpha[y2] + (x2);
                                                                 v4 = alpha[y2] + (x1);

                                                                 *(alpha_dst++) = (unsigned char)
                                                                             ( (w1 * *v1 + w2 * *v2 +
                                                                             w3 * *v3 + w4 * *v4) /
                                                                             (w1 + w2 + w3 + w4) );
                                                           }
                                                     }
                                               }
                                               else
                                               {
                                                     *(dst++) = blank_r;
                                                     *(dst++) = blank_g;
                                                     *(dst++) = blank_b;

                                                     if (has_alpha)
                                                           *(alpha_dst++) = 0;
                                               }
                                         }
                                   }
                             }
                             else    // not interpolating
                             {
                                   double x0 = p0.x;
                                   double y0 = p0.y;
                                   double x1b = x1a - p0.x;
                                   double y1b = y1a - p0.y;
                                   double msa = -sin_angle;

                                   for (int y = 0; y < rH; y++)
                                   {
                                         for (int x = 0; x < rW; x++)
                                         {
//                                               wxRealPoint src = wxRotatePoint (x + x1a, y + y1a, cos_angle, -sin_angle, p0);

//                                               double sx = p0.x + (x + x1a - p0.x) * cos_angle - (y + y1a - p0.y) * -sin_angle;
//                                               double sy=  p0.y + (y + y1a - p0.y) * cos_angle + (x + x1a - p0.x) * -sin_angle;

                                               double sx = x0 + (x + x1b) * cos_angle - (y + y1b) * msa;
                                               double sy=  y0 + (y + y1b) * cos_angle + (x + x1b) * msa;

                                               const int xs = (int)sx;
                                               const int ys = (int)sy;


//                                               return wxRealPoint(p0.x + (p.x - p0.x) * cos_angle - (p.y - p0.y) * sin_angle,
//                                                           p0.y + (p.y - p0.y) * cos_angle + (p.x - p0.x) * sin_angle);

//                                               const int xs = /*wxRound*/ (src.x);      // wxRound rounds to the
//                                               const int ys = /*wxRound*/ (src.y);      // closest integer

                                               if (0 <= xs && xs < w && 0 <= ys && ys < h)
                                               {
                                                     unsigned char *p = data[ys] + (3 * xs);
                                                     *(dst++) = *(p++);
                                                     *(dst++) = *(p++);
                                                     *(dst++) = *p;



                                                     if (has_alpha)
                                                           *(alpha_dst++) = *(alpha[ys] + (xs));
                                               }
                                               else
                                               {
                                                     *(dst++) = blank_r;
                                                     *(dst++) = blank_g;
                                                     *(dst++) = blank_b;

                                                     if (has_alpha)
                                                           *(alpha_dst++) = 255;
                                               }

                                         }
                                   }
                             }

                             delete [] data;

                             if (has_alpha)
                                   delete [] alpha;

                             return rotated;
}




