//----------------------------------------------------------------------------
// Anti-Grain Geometry - Version 2.4
// Copyright (C) 2002-2005 Maxim Shemanarev (http://www.antigrain.com)
//
// Permission to copy, use, modify, sell and distribute this software 
// is granted provided this copyright notice appears in all copies. 
// This software is provided "as is" without express or implied
// warranty, and with no claim as to its suitability for any purpose.
//
//----------------------------------------------------------------------------
// Contact: mcseem@antigrain.com
//          mcseemagg@yahoo.com
//          http://www.antigrain.com
//----------------------------------------------------------------------------
//
// class platform_support. X11 version.
//
//----------------------------------------------------------------------------
#ifndef AGG_PLATFORM_SUPPORT
#define AGG_PLATFORM_SUPPORT

#include <Core/Core.h>

#ifdef PLATFORM_X11

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include <time.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/Xatom.h>
#include <X11/keysym.h>
#include <agg24/agg_basics.h>
#include <agg24/agg_color_conv_rgb8.h>
#include <agg24/agg_platform_support.h>


namespace agg
{
    //------------------------------------------------------------------------
    class platform_specific
    {
    public:
        platform_specific(pix_format_e format, bool flip_y);
        ~platform_specific();
        
        void caption(const char* capt);
        void put_image(const rendering_buffer* src);
       
        pix_format_e         m_format;
        pix_format_e         m_sys_format;
        int                  m_byte_order;
        bool                 m_flip_y;
        unsigned             m_bpp;
        unsigned             m_sys_bpp;
        Display*             m_display;
        int                  m_screen;
        int                  m_depth;
        Visual*              m_visual;
        Window               m_window;
        GC                   m_gc;
        XImage*              m_ximg_window;
        XSetWindowAttributes m_window_attributes;
        Atom                 m_close_atom;
        unsigned char*       m_buf_window;
        unsigned char*       m_buf_img[platform_support::max_images];
        unsigned             m_keymap[256];
       
        bool m_update_flag;
        bool m_resize_flag;
        bool m_initialized;
        //bool m_wait_mode;
        clock_t m_sw_start;
    };



    //------------------------------------------------------------------------
    platform_specific::platform_specific(pix_format_e format, bool flip_y) :
        m_format(format),
        m_sys_format(pix_format_undefined),
        m_byte_order(LSBFirst),
        m_flip_y(flip_y),
        m_bpp(0),
        m_sys_bpp(0),
        m_display(0),
        m_screen(0),
        m_depth(0),
        m_visual(0),
        m_window(0),
        m_gc(0),
        m_ximg_window(0),
        m_close_atom(0),

        m_buf_window(0),
        
        m_update_flag(true), 
        m_resize_flag(true),
        m_initialized(false)
        //m_wait_mode(true)
    {
        memset(m_buf_img, 0, sizeof(m_buf_img));

        unsigned i;
        for(i = 0; i < 256; i++)
        {
            m_keymap[i] = i;
        }

        m_keymap[XK_Pause&0xFF] = key_pause;
        m_keymap[XK_Clear&0xFF] = key_clear;

        m_keymap[XK_KP_0&0xFF] = key_kp0;
        m_keymap[XK_KP_1&0xFF] = key_kp1;
        m_keymap[XK_KP_2&0xFF] = key_kp2;
        m_keymap[XK_KP_3&0xFF] = key_kp3;
        m_keymap[XK_KP_4&0xFF] = key_kp4;
        m_keymap[XK_KP_5&0xFF] = key_kp5;
        m_keymap[XK_KP_6&0xFF] = key_kp6;
        m_keymap[XK_KP_7&0xFF] = key_kp7;
        m_keymap[XK_KP_8&0xFF] = key_kp8;
        m_keymap[XK_KP_9&0xFF] = key_kp9;

        m_keymap[XK_KP_Insert&0xFF]    = key_kp0;
        m_keymap[XK_KP_End&0xFF]       = key_kp1;   
        m_keymap[XK_KP_Down&0xFF]      = key_kp2;
        m_keymap[XK_KP_Page_Down&0xFF] = key_kp3;
        m_keymap[XK_KP_Left&0xFF]      = key_kp4;
        m_keymap[XK_KP_Begin&0xFF]     = key_kp5;
        m_keymap[XK_KP_Right&0xFF]     = key_kp6;
        m_keymap[XK_KP_Home&0xFF]      = key_kp7;
        m_keymap[XK_KP_Up&0xFF]        = key_kp8;
        m_keymap[XK_KP_Page_Up&0xFF]   = key_kp9;
        m_keymap[XK_KP_Delete&0xFF]    = key_kp_period;
        m_keymap[XK_KP_Decimal&0xFF]   = key_kp_period;
        m_keymap[XK_KP_Divide&0xFF]    = key_kp_divide;
        m_keymap[XK_KP_Multiply&0xFF]  = key_kp_multiply;
        m_keymap[XK_KP_Subtract&0xFF]  = key_kp_minus;
        m_keymap[XK_KP_Add&0xFF]       = key_kp_plus;
        m_keymap[XK_KP_Enter&0xFF]     = key_kp_enter;
        m_keymap[XK_KP_Equal&0xFF]     = key_kp_equals;

        m_keymap[XK_Up&0xFF]           = key_up;
        m_keymap[XK_Down&0xFF]         = key_down;
        m_keymap[XK_Right&0xFF]        = key_right;
        m_keymap[XK_Left&0xFF]         = key_left;
        m_keymap[XK_Insert&0xFF]       = key_insert;
        m_keymap[XK_Home&0xFF]         = key_delete;
        m_keymap[XK_End&0xFF]          = key_end;
        m_keymap[XK_Page_Up&0xFF]      = key_page_up;
        m_keymap[XK_Page_Down&0xFF]    = key_page_down;

        m_keymap[XK_F1&0xFF]           = key_f1;
        m_keymap[XK_F2&0xFF]           = key_f2;
        m_keymap[XK_F3&0xFF]           = key_f3;
        m_keymap[XK_F4&0xFF]           = key_f4;
        m_keymap[XK_F5&0xFF]           = key_f5;
        m_keymap[XK_F6&0xFF]           = key_f6;
        m_keymap[XK_F7&0xFF]           = key_f7;
        m_keymap[XK_F8&0xFF]           = key_f8;
        m_keymap[XK_F9&0xFF]           = key_f9;
        m_keymap[XK_F10&0xFF]          = key_f10;
        m_keymap[XK_F11&0xFF]          = key_f11;
        m_keymap[XK_F12&0xFF]          = key_f12;
        m_keymap[XK_F13&0xFF]          = key_f13;
        m_keymap[XK_F14&0xFF]          = key_f14;
        m_keymap[XK_F15&0xFF]          = key_f15;

        m_keymap[XK_Num_Lock&0xFF]     = key_numlock;
        m_keymap[XK_Caps_Lock&0xFF]    = key_capslock;
        m_keymap[XK_Scroll_Lock&0xFF]  = key_scrollock;

        switch(m_format)
        {
        default: break;
        case pix_format_gray8:
            m_bpp = 8;
            break;

        case pix_format_rgb565:
        case pix_format_rgb555:
            m_bpp = 16;
            break;

        case pix_format_rgb24:
        case pix_format_bgr24:
            m_bpp = 24;
            break;

        case pix_format_bgra32:
        case pix_format_abgr32:
        case pix_format_argb32:
        case pix_format_rgba32:
            m_bpp = 32;
            break;
        }
        m_sw_start = clock();
    }

    //------------------------------------------------------------------------
    platform_specific::~platform_specific()
    {
    }

    //------------------------------------------------------------------------
    void platform_specific::caption(const char* capt)
    {
        XTextProperty tp;
        tp.value = (unsigned char *)capt;
        tp.encoding = XA_WM_NAME;
        tp.format = 8;
        tp.nitems = strlen(capt);
        XSetWMName(m_display, m_window, &tp);
        XStoreName(m_display, m_window, capt);
        XSetIconName(m_display, m_window, capt);
        XSetWMIconName(m_display, m_window, &tp);
    }

    
    //------------------------------------------------------------------------
    void platform_specific::put_image(const rendering_buffer* src)
    {    
        if(m_ximg_window == 0) return;
        m_ximg_window->data = (char*)m_buf_window;
        
        if(m_format == m_sys_format)
        {
            XPutImage(m_display, 
                      m_window, 
                      m_gc, 
                      m_ximg_window, 
                      0, 0, 0, 0,
                      src->width(), 
                      src->height());
        }
        else
        {
            int row_len = src->width() * m_sys_bpp / 8;
            unsigned char* buf_tmp = 
                new unsigned char[row_len * src->height()];
            
            rendering_buffer rbuf_tmp;
            rbuf_tmp.attach(buf_tmp, 
                            src->width(), 
                            src->height(), 
                            m_flip_y ? -row_len : row_len);

            switch(m_sys_format)            
            {
                default: break;
                case pix_format_rgb555:
                    switch(m_format)
                    {
                        default: break;
                        case pix_format_rgb555: color_conv(&rbuf_tmp, src, color_conv_rgb555_to_rgb555()); break;
                        case pix_format_rgb565: color_conv(&rbuf_tmp, src, color_conv_rgb565_to_rgb555()); break;
                        case pix_format_rgb24:  color_conv(&rbuf_tmp, src, color_conv_rgb24_to_rgb555());  break;
                        case pix_format_bgr24:  color_conv(&rbuf_tmp, src, color_conv_bgr24_to_rgb555());  break;
                        case pix_format_rgba32: color_conv(&rbuf_tmp, src, color_conv_rgba32_to_rgb555()); break;
                        case pix_format_argb32: color_conv(&rbuf_tmp, src, color_conv_argb32_to_rgb555()); break;
                        case pix_format_bgra32: color_conv(&rbuf_tmp, src, color_conv_bgra32_to_rgb555()); break;
                        case pix_format_abgr32: color_conv(&rbuf_tmp, src, color_conv_abgr32_to_rgb555()); break;
                    }
                    break;
                    
                case pix_format_rgb565:
                    switch(m_format)
                    {
                        default: break;
                        case pix_format_rgb555: color_conv(&rbuf_tmp, src, color_conv_rgb555_to_rgb565()); break;
                        case pix_format_rgb565: color_conv(&rbuf_tmp, src, color_conv_rgb565_to_rgb565()); break;
                        case pix_format_rgb24:  color_conv(&rbuf_tmp, src, color_conv_rgb24_to_rgb565());  break;
                        case pix_format_bgr24:  color_conv(&rbuf_tmp, src, color_conv_bgr24_to_rgb565());  break;
                        case pix_format_rgba32: color_conv(&rbuf_tmp, src, color_conv_rgba32_to_rgb565()); break;
                        case pix_format_argb32: color_conv(&rbuf_tmp, src, color_conv_argb32_to_rgb565()); break;
                        case pix_format_bgra32: color_conv(&rbuf_tmp, src, color_conv_bgra32_to_rgb565()); break;
                        case pix_format_abgr32: color_conv(&rbuf_tmp, src, color_conv_abgr32_to_rgb565()); break;
                    }
                    break;
                    
                case pix_format_rgba32:
                    switch(m_format)
                    {
                        default: break;
                        case pix_format_rgb555: color_conv(&rbuf_tmp, src, color_conv_rgb555_to_rgba32()); break;
                        case pix_format_rgb565: color_conv(&rbuf_tmp, src, color_conv_rgb565_to_rgba32()); break;
                        case pix_format_rgb24:  color_conv(&rbuf_tmp, src, color_conv_rgb24_to_rgba32());  break;
                        case pix_format_bgr24:  color_conv(&rbuf_tmp, src, color_conv_bgr24_to_rgba32());  break;
                        case pix_format_rgba32: color_conv(&rbuf_tmp, src, color_conv_rgba32_to_rgba32()); break;
                        case pix_format_argb32: color_conv(&rbuf_tmp, src, color_conv_argb32_to_rgba32()); break;
                        case pix_format_bgra32: color_conv(&rbuf_tmp, src, color_conv_bgra32_to_rgba32()); break;
                        case pix_format_abgr32: color_conv(&rbuf_tmp, src, color_conv_abgr32_to_rgba32()); break;
                    }
                    break;
                    
                case pix_format_abgr32:
                    switch(m_format)
                    {
                        default: break;
                        case pix_format_rgb555: color_conv(&rbuf_tmp, src, color_conv_rgb555_to_abgr32()); break;
                        case pix_format_rgb565: color_conv(&rbuf_tmp, src, color_conv_rgb565_to_abgr32()); break;
                        case pix_format_rgb24:  color_conv(&rbuf_tmp, src, color_conv_rgb24_to_abgr32());  break;
                        case pix_format_bgr24:  color_conv(&rbuf_tmp, src, color_conv_bgr24_to_abgr32());  break;
                        case pix_format_abgr32: color_conv(&rbuf_tmp, src, color_conv_abgr32_to_abgr32()); break;
                        case pix_format_rgba32: color_conv(&rbuf_tmp, src, color_conv_rgba32_to_abgr32()); break;
                        case pix_format_argb32: color_conv(&rbuf_tmp, src, color_conv_argb32_to_abgr32()); break;
                        case pix_format_bgra32: color_conv(&rbuf_tmp, src, color_conv_bgra32_to_abgr32()); break;
                    }
                    break;
                    
                case pix_format_argb32:
                    switch(m_format)
                    {
                        default: break;
                        case pix_format_rgb555: color_conv(&rbuf_tmp, src, color_conv_rgb555_to_argb32()); break;
                        case pix_format_rgb565: color_conv(&rbuf_tmp, src, color_conv_rgb565_to_argb32()); break;
                        case pix_format_rgb24:  color_conv(&rbuf_tmp, src, color_conv_rgb24_to_argb32());  break;
                        case pix_format_bgr24:  color_conv(&rbuf_tmp, src, color_conv_bgr24_to_argb32());  break;
                        case pix_format_rgba32: color_conv(&rbuf_tmp, src, color_conv_rgba32_to_argb32()); break;
                        case pix_format_argb32: color_conv(&rbuf_tmp, src, color_conv_argb32_to_argb32()); break;
                        case pix_format_abgr32: color_conv(&rbuf_tmp, src, color_conv_abgr32_to_argb32()); break;
                        case pix_format_bgra32: color_conv(&rbuf_tmp, src, color_conv_bgra32_to_argb32()); break;
                    }
                    break;
                    
                case pix_format_bgra32:
                    switch(m_format)
                    {
                        default: break;
                        case pix_format_rgb555: color_conv(&rbuf_tmp, src, color_conv_rgb555_to_bgra32()); break;
                        case pix_format_rgb565: color_conv(&rbuf_tmp, src, color_conv_rgb565_to_bgra32()); break;
                        case pix_format_rgb24:  color_conv(&rbuf_tmp, src, color_conv_rgb24_to_bgra32());  break;
                        case pix_format_bgr24:  color_conv(&rbuf_tmp, src, color_conv_bgr24_to_bgra32());  break;
                        case pix_format_rgba32: color_conv(&rbuf_tmp, src, color_conv_rgba32_to_bgra32()); break;
                        case pix_format_argb32: color_conv(&rbuf_tmp, src, color_conv_argb32_to_bgra32()); break;
                        case pix_format_abgr32: color_conv(&rbuf_tmp, src, color_conv_abgr32_to_bgra32()); break;
                        case pix_format_bgra32: color_conv(&rbuf_tmp, src, color_conv_bgra32_to_bgra32()); break;
                    }
                    break;
            }
            
            m_ximg_window->data = (char*)buf_tmp;
            XPutImage(m_display, 
                      m_window, 
                      m_gc, 
                      m_ximg_window, 
                      0, 0, 0, 0,
                      src->width(), 
                      src->height());
            
            delete [] buf_tmp;
        }
    }
    

    //------------------------------------------------------------------------
    platform_support::platform_support(pix_format_e format, bool flip_y) :
        m_specific(new platform_specific(format, flip_y)),
        m_format(format),
        m_bpp(m_specific->m_bpp),
        m_window_flags(0),
        m_wait_mode(true),
        m_flip_y(flip_y),
        m_initial_width(10),
        m_initial_height(10)
    {
        strcpy(m_caption, "AGG Application");
    }

    //------------------------------------------------------------------------
    platform_support::~platform_support()
    {
        delete m_specific;
    }



    //------------------------------------------------------------------------
    void platform_support::caption(const char* cap)
    {
        strcpy(m_caption, cap);
        if(m_specific->m_initialized)
        {
            m_specific->caption(cap);
        }
    }

   
    //------------------------------------------------------------------------
    enum xevent_mask_e
    { 
        xevent_mask =
            PointerMotionMask|
            ButtonPressMask|
            ButtonReleaseMask|
            ExposureMask|
            KeyPressMask|
            StructureNotifyMask
    };


    //------------------------------------------------------------------------
    bool platform_support::init(unsigned width, unsigned height, unsigned flags)
    {
        m_window_flags = flags;
        
        m_specific->m_display = XOpenDisplay(NULL);
        if(m_specific->m_display == 0) 
        {
            fprintf(stderr, "Unable to open DISPLAY!\n");
            return false;
        }
        
        m_specific->m_screen = XDefaultScreen(m_specific->m_display);
        m_specific->m_depth  = XDefaultDepth(m_specific->m_display, 
                                             m_specific->m_screen);
        m_specific->m_visual = XDefaultVisual(m_specific->m_display, 
                                              m_specific->m_screen);
        unsigned long r_mask = m_specific->m_visual->red_mask;
        unsigned long g_mask = m_specific->m_visual->green_mask;
        unsigned long b_mask = m_specific->m_visual->blue_mask;
                
//printf("depth=%d, red=%08x, green=%08x, blue=%08x\n",
//       m_specific->m_depth,
//       m_specific->m_visual->red_mask,
//       m_specific->m_visual->green_mask,
//       m_specific->m_visual->blue_mask);
           

//         // NOT COMPLETED YET!
//         // Try to find an appropriate Visual if the default doesn't fit.
//         if(m_specific->m_depth < 15 ||
//            r_mask == 0 || g_mask == 0 || b_mask == 0)
//         {
//             
//             // This is an attempt to find an appropriate Visual if         
//             // the default one doesn't match the minumum requirements
//             static int depth[] = { 32, 24, 16, 15 };
//             int i;
//             for(int i = 0; i < 4; i++)
//             {
//                 XVisualInfo vi;
//                 if(XMatchVisualInfo(m_specific->m_display, 
//                                     m_specific->m_screen, 
//                                     depth[i], 
//                                     TrueColor, 
//                                     &vi)) 
//                 {
// //                     printf("TrueColor  depth=%d, red=%08x, green=%08x, blue=%08x, bits=%d\n",
// //                         vi.depth,
// //                         vi.visual->red_mask,
// //                         vi.visual->green_mask,
// //                         vi.visual->blue_mask,
// //                         vi.bits_per_rgb);
//                     m_specific->m_depth  = vi.depth;
//                     m_specific->m_visual = vi.visual;
//                     r_mask = m_specific->m_visual->red_mask;
//                     g_mask = m_specific->m_visual->green_mask;
//                     b_mask = m_specific->m_visual->blue_mask;
//                     break;
//                 }
//                 if(XMatchVisualInfo(m_specific->m_display, 
//                                     m_specific->m_screen, 
//                                     depth[i], 
//                                     DirectColor, 
//                                     &vi)) 
//                 {
// //                     printf("DirectColor depth=%d, red=%08x, green=%08x, blue=%08x, bits=%d\n",
// //                         vi.depth,
// //                         vi.visual->red_mask,
// //                         vi.visual->green_mask,
// //                         vi.visual->blue_mask,
// //                         vi.bits_per_rgb);
//                     m_specific->m_depth  = vi.depth;
//                     m_specific->m_visual = vi.visual;
//                     r_mask = m_specific->m_visual->red_mask;
//                     g_mask = m_specific->m_visual->green_mask;
//                     b_mask = m_specific->m_visual->blue_mask;
//                     break;
//                 }
//             }
//         }

        if(m_specific->m_depth < 15 ||
           r_mask == 0 || g_mask == 0 || b_mask == 0)
        {
            fprintf(stderr,
                   "There's no Visual compatible with minimal AGG requirements:\n"
                   "At least 15-bit color depth and True- or DirectColor class.\n\n");
            XCloseDisplay(m_specific->m_display);
            return false;
        }
        
        int t = 1;
        int hw_byte_order = LSBFirst;
        if(*(char*)&t == 0) hw_byte_order = MSBFirst;
        
        // Perceive SYS-format by mask
        switch(m_specific->m_depth)
        {
            case 15:
                m_specific->m_sys_bpp = 16;
                if(r_mask == 0x7C00 && g_mask == 0x3E0 && b_mask == 0x1F)
                {
                    m_specific->m_sys_format = pix_format_rgb555;
                    m_specific->m_byte_order = hw_byte_order;
                }
                break;
                
            case 16:
                m_specific->m_sys_bpp = 16;
                if(r_mask == 0xF800 && g_mask == 0x7E0 && b_mask == 0x1F)
                {
                    m_specific->m_sys_format = pix_format_rgb565;
                    m_specific->m_byte_order = hw_byte_order;
                }
                break;
                
            case 24:
            case 32:
                m_specific->m_sys_bpp = 32;
                if(g_mask == 0xFF00)
                {
                    if(r_mask == 0xFF && b_mask == 0xFF0000)
                    {
                        switch(m_specific->m_format)
                        {
                            case pix_format_rgba32:
                                m_specific->m_sys_format = pix_format_rgba32;
                                m_specific->m_byte_order = LSBFirst;
                                break;
                                
                            case pix_format_abgr32:
                                m_specific->m_sys_format = pix_format_abgr32;
                                m_specific->m_byte_order = MSBFirst;
                                break;

                            default:                            
                                m_specific->m_byte_order = hw_byte_order;
                                m_specific->m_sys_format = 
                                    (hw_byte_order == LSBFirst) ?
                                    pix_format_rgba32 :
                                    pix_format_abgr32;
                                break;
                        }
                    }
                    
                    if(r_mask == 0xFF0000 && b_mask == 0xFF)
                    {
                        switch(m_specific->m_format)
                        {
                            case pix_format_argb32:
                                m_specific->m_sys_format = pix_format_argb32;
                                m_specific->m_byte_order = MSBFirst;
                                break;
                                
                            case pix_format_bgra32:
                                m_specific->m_sys_format = pix_format_bgra32;
                                m_specific->m_byte_order = LSBFirst;
                                break;

                            default:                            
                                m_specific->m_byte_order = hw_byte_order;
                                m_specific->m_sys_format = 
                                    (hw_byte_order == MSBFirst) ?
                                    pix_format_argb32 :
                                    pix_format_bgra32;
                                break;
                        }
                    }
                }
                break;
        }
        
        if(m_specific->m_sys_format == pix_format_undefined)
        {
            fprintf(stderr,
                   "RGB masks are not compatible with AGG pixel formats:\n"
                   "R=%08x, R=%08x, B=%08x\n", r_mask, g_mask, b_mask);
            XCloseDisplay(m_specific->m_display);
            return false;
        }
                
        
        
        memset(&m_specific->m_window_attributes, 
               0, 
               sizeof(m_specific->m_window_attributes)); 
        
        m_specific->m_window_attributes.border_pixel = 
            XBlackPixel(m_specific->m_display, m_specific->m_screen);

        m_specific->m_window_attributes.background_pixel = 
            XWhitePixel(m_specific->m_display, m_specific->m_screen);

        m_specific->m_window_attributes.override_redirect = 0;

        unsigned long window_mask = CWBackPixel | CWBorderPixel;

        m_specific->m_window = 
            XCreateWindow(m_specific->m_display, 
                          XDefaultRootWindow(m_specific->m_display), 
                          0, 0,
                          width,
                          height,
                          0, 
                          m_specific->m_depth, 
                          InputOutput, 
                          CopyFromParent,
                          window_mask,
                          &m_specific->m_window_attributes);


        m_specific->m_gc = XCreateGC(m_specific->m_display, 
                                     m_specific->m_window, 
                                     0, 0); 
        m_specific->m_buf_window = 
            new unsigned char[width * height * (m_bpp / 8)];

        memset(m_specific->m_buf_window, 255, width * height * (m_bpp / 8));
        
        m_rbuf_window.attach(m_specific->m_buf_window,
                             width,
                             height,
                             m_flip_y ? -width * (m_bpp / 8) : width * (m_bpp / 8));
            
        m_specific->m_ximg_window = 
            XCreateImage(m_specific->m_display, 
                         m_specific->m_visual, //CopyFromParent, 
                         m_specific->m_depth, 
                         ZPixmap, 
                         0,
                         (char*)m_specific->m_buf_window, 
                         width,
                         height, 
                         m_specific->m_sys_bpp,
                         width * (m_specific->m_sys_bpp / 8));
        m_specific->m_ximg_window->byte_order = m_specific->m_byte_order;

        m_specific->caption(m_caption); 
        m_initial_width = width;
        m_initial_height = height;
        
        if(!m_specific->m_initialized)
        {
            on_init();
            m_specific->m_initialized = true;
        }

        trans_affine_resizing(width, height);
        on_resize(width, height);
        m_specific->m_update_flag = true;

        XSizeHints *hints = XAllocSizeHints();
        if(hints) 
        {
            if(flags & window_resize)
            {
                hints->min_width = 32;
                hints->min_height = 32;
                hints->max_width = 4096;
                hints->max_height = 4096;
            }
            else
            {
                hints->min_width  = width;
                hints->min_height = height;
                hints->max_width  = width;
                hints->max_height = height;
            }
            hints->flags = PMaxSize | PMinSize;

            XSetWMNormalHints(m_specific->m_display, 
                              m_specific->m_window, 
                              hints);

            XFree(hints);
        }


        XMapWindow(m_specific->m_display, 
                   m_specific->m_window);

        XSelectInput(m_specific->m_display, 
                     m_specific->m_window, 
                     xevent_mask);

        
        m_specific->m_close_atom = XInternAtom(m_specific->m_display, 
                                               "WM_DELETE_WINDOW", 
                                               false);

        XSetWMProtocols(m_specific->m_display, 
                        m_specific->m_window, 
                        &m_specific->m_close_atom, 
                        1);

        return true;
    }



    //------------------------------------------------------------------------
    void platform_support::update_window()
    {
        m_specific->put_image(&m_rbuf_window);
        
        // When m_wait_mode is true we can discard all the events 
        // came while the image is being drawn. In this case 
        // the X server does not accumulate mouse motion events.
        // When m_wait_mode is false, i.e. we have some idle drawing
        // we cannot afford to miss any events
        XSync(m_specific->m_display, m_wait_mode);
    }


    //------------------------------------------------------------------------
    int platform_support::run()
    {
        XFlush(m_specific->m_display);
        
        bool quit = false;
        unsigned flags;
        int cur_x;
        int cur_y;

        while(!quit)
        {
            if(m_specific->m_update_flag)
            {
                on_draw();
                update_window();
                m_specific->m_update_flag = false;
            }

            if(!m_wait_mode)
            {
                if(XPending(m_specific->m_display) == 0)
                {
                    on_idle();
                    continue;
                }
            }

            XEvent x_event;
            XNextEvent(m_specific->m_display, &x_event);
            
            // In the Idle mode discard all intermediate MotionNotify events
            if(!m_wait_mode && x_event.type == MotionNotify)
            {
                XEvent te = x_event;
                for(;;)
                {
                    if(XPending(m_specific->m_display) == 0) break;
                    XNextEvent(m_specific->m_display, &te);
                    if(te.type != MotionNotify) break;
                }
                x_event = te;
            }

            switch(x_event.type) 
            {
            case ConfigureNotify: 
                {
                    if(x_event.xconfigure.width  != int(m_rbuf_window.width()) ||
                       x_event.xconfigure.height != int(m_rbuf_window.height()))
                    {
                        int width  = x_event.xconfigure.width;
                        int height = x_event.xconfigure.height;

                        delete [] m_specific->m_buf_window;
                        m_specific->m_ximg_window->data = 0;
                        XDestroyImage(m_specific->m_ximg_window);

                        m_specific->m_buf_window = 
                            new unsigned char[width * height * (m_bpp / 8)];

                        m_rbuf_window.attach(m_specific->m_buf_window,
                                             width,
                                             height,
                                             m_flip_y ? 
                                             -width * (m_bpp / 8) : 
                                             width * (m_bpp / 8));
            
                        m_specific->m_ximg_window = 
                            XCreateImage(m_specific->m_display, 
                                         m_specific->m_visual, //CopyFromParent, 
                                         m_specific->m_depth, 
                                         ZPixmap, 
                                         0,
                                         (char*)m_specific->m_buf_window, 
                                         width,
                                         height, 
                                         m_specific->m_sys_bpp,
                                         width * (m_specific->m_sys_bpp / 8));
                        m_specific->m_ximg_window->byte_order = m_specific->m_byte_order;

                        trans_affine_resizing(width, height);
                        on_resize(width, height);
                        on_draw();
                        update_window();
                    }
                }
                break;

            case Expose:
                m_specific->put_image(&m_rbuf_window);
                XFlush(m_specific->m_display);
                XSync(m_specific->m_display, false);
                break;

            case KeyPress:
                {
                    KeySym key = XLookupKeysym(&x_event.xkey, 0);
                    flags = 0;
                    if(x_event.xkey.state & Button1Mask) flags |= mouse_left;
                    if(x_event.xkey.state & Button3Mask) flags |= mouse_right;
                    if(x_event.xkey.state & ShiftMask)   flags |= kbd_shift;
                    if(x_event.xkey.state & ControlMask) flags |= kbd_ctrl;

                    bool left  = false;
                    bool up    = false;
                    bool right = false;
                    bool down  = false;

                    switch(m_specific->m_keymap[key & 0xFF])
                    {
                    case key_left:
                        left = true;
                        break;

                    case key_up:
                        up = true;
                        break;

                    case key_right:
                        right = true;
                        break;

                    case key_down:
                        down = true;
                        break;

                    case key_f2:                        
                        copy_window_to_img(max_images - 1);
                        save_img(max_images - 1, "screenshot");
                        break;
                    }

                    if(m_ctrls.on_arrow_keys(left, right, down, up))
                    {
                        on_ctrl_change();
                        force_redraw();
                    }
                    else
                    {
                        on_key(x_event.xkey.x, 
                               m_flip_y ? 
                                   m_rbuf_window.height() - x_event.xkey.y :
                                   x_event.xkey.y,
                               m_specific->m_keymap[key & 0xFF],
                               flags);
                    }
                }
                break;


            case ButtonPress:
                {
                    flags = 0;
                    if(x_event.xbutton.state & ShiftMask)   flags |= kbd_shift;
                    if(x_event.xbutton.state & ControlMask) flags |= kbd_ctrl;
                    if(x_event.xbutton.button == Button1)   flags |= mouse_left;
                    if(x_event.xbutton.button == Button3)   flags |= mouse_right;

                    cur_x = x_event.xbutton.x;
                    cur_y = m_flip_y ? m_rbuf_window.height() - x_event.xbutton.y :
                                       x_event.xbutton.y;

                    if(flags & mouse_left)
                    {
                        if(m_ctrls.on_mouse_button_down(cur_x, cur_y))
                        {
                            m_ctrls.set_cur(cur_x, cur_y);
                            on_ctrl_change();
                            force_redraw();
                        }
                        else
                        {
                            if(m_ctrls.in_rect(cur_x, cur_y))
                            {
                                if(m_ctrls.set_cur(cur_x, cur_y))
                                {
                                    on_ctrl_change();
                                    force_redraw();
                                }
                            }
                            else
                            {
                                on_mouse_button_down(cur_x, cur_y, flags);
                            }
                        }
                    }
                    if(flags & mouse_right)
                    {
                        on_mouse_button_down(cur_x, cur_y, flags);
                    }
                    //m_specific->m_wait_mode = m_wait_mode;
                    //m_wait_mode = true;
                }
                break;

                
            case MotionNotify:
                {
                    flags = 0;
                    if(x_event.xmotion.state & Button1Mask) flags |= mouse_left;
                    if(x_event.xmotion.state & Button3Mask) flags |= mouse_right;
                    if(x_event.xmotion.state & ShiftMask)   flags |= kbd_shift;
                    if(x_event.xmotion.state & ControlMask) flags |= kbd_ctrl;

                    cur_x = x_event.xbutton.x;
                    cur_y = m_flip_y ? m_rbuf_window.height() - x_event.xbutton.y :
                                       x_event.xbutton.y;

                    if(m_ctrls.on_mouse_move(cur_x, cur_y, (flags & mouse_left) != 0))
                    {
                        on_ctrl_change();
                        force_redraw();
                    }
                    else
                    {
                        if(!m_ctrls.in_rect(cur_x, cur_y))
                        {
                            on_mouse_move(cur_x, cur_y, flags);
                        }
                    }
                }
                break;
                
            case ButtonRelease:
                {
                    flags = 0;
                    if(x_event.xbutton.state & ShiftMask)   flags |= kbd_shift;
                    if(x_event.xbutton.state & ControlMask) flags |= kbd_ctrl;
                    if(x_event.xbutton.button == Button1)   flags |= mouse_left;
                    if(x_event.xbutton.button == Button3)   flags |= mouse_right;

                    cur_x = x_event.xbutton.x;
                    cur_y = m_flip_y ? m_rbuf_window.height() - x_event.xbutton.y :
                                       x_event.xbutton.y;

                    if(flags & mouse_left)
                    {
                        if(m_ctrls.on_mouse_button_up(cur_x, cur_y))
                        {
                            on_ctrl_change();
                            force_redraw();
                        }
                    }
                    if(flags & (mouse_left | mouse_right))
                    {
                        on_mouse_button_up(cur_x, cur_y, flags);
                    }
                }
                //m_wait_mode = m_specific->m_wait_mode;
                break;

            case ClientMessage:
                if((x_event.xclient.format == 32) &&
                (x_event.xclient.data.l[0] == int(m_specific->m_close_atom)))
                {
                    quit = true;
                }
                break;
            }           
        }


        unsigned i = platform_support::max_images;
        while(i--)
        {
            if(m_specific->m_buf_img[i]) 
            {
                delete [] m_specific->m_buf_img[i];
            }
        }

        delete [] m_specific->m_buf_window;
        m_specific->m_ximg_window->data = 0;
        XDestroyImage(m_specific->m_ximg_window);
        XFreeGC(m_specific->m_display, m_specific->m_gc);
        XDestroyWindow(m_specific->m_display, m_specific->m_window);
        XCloseDisplay(m_specific->m_display);
        
        return 0;
    }



    //------------------------------------------------------------------------
    const char* platform_support::img_ext() const { return ".ppm"; }

    //------------------------------------------------------------------------
    const char* platform_support::full_file_name(const char* file_name)
    {
        return file_name;
    }

    //------------------------------------------------------------------------
    bool platform_support::load_img(unsigned idx, const char* file)
    {
        if(idx < max_images)
        {
            char buf[1024];
            strcpy(buf, file);
            int len = strlen(buf);
            if(len < 4 || strcasecmp(buf + len - 4, ".ppm") != 0)
            {
                strcat(buf, ".ppm");
            }
            
            FILE* fd = fopen(buf, "rb");
            if(fd == 0) return false;

            if((len = fread(buf, 1, 1022, fd)) == 0)
            {
                fclose(fd);
                return false;
            }
            buf[len] = 0;
            
            if(buf[0] != 'P' && buf[1] != '6')
            {
                fclose(fd);
                return false;
            }
            
            char* ptr = buf + 2;
            
            while(*ptr && !isdigit(*ptr)) ptr++;
            if(*ptr == 0)
            {
                fclose(fd);
                return false;
            }
            
            unsigned width = atoi(ptr);
            if(width == 0 || width > 4096)
            {
                fclose(fd);
                return false;
            }
            while(*ptr && isdigit(*ptr)) ptr++;
            while(*ptr && !isdigit(*ptr)) ptr++;
            if(*ptr == 0)
            {
                fclose(fd);
                return false;
            }
            unsigned height = atoi(ptr);
            if(height == 0 || height > 4096)
            {
                fclose(fd);
                return false;
            }
            while(*ptr && isdigit(*ptr)) ptr++;
            while(*ptr && !isdigit(*ptr)) ptr++;
            if(atoi(ptr) != 255)
            {
                fclose(fd);
                return false;
            }
            while(*ptr && isdigit(*ptr)) ptr++;
            if(*ptr == 0)
            {
                fclose(fd);
                return false;
            }
            ptr++;
            fseek(fd, long(ptr - buf), SEEK_SET);
            
            create_img(idx, width, height);
            bool ret = true;
            
            if(m_format == pix_format_rgb24)
            {
                fread(m_specific->m_buf_img[idx], 1, width * height * 3, fd);
            }
            else
            {
                unsigned char* buf_img = new unsigned char [width * height * 3];
                rendering_buffer rbuf_img;
                rbuf_img.attach(buf_img,
                                width,
                                height,
                                m_flip_y ?
                                  -width * 3 :
                                   width * 3);
                
                fread(buf_img, 1, width * height * 3, fd);
                
                switch(m_format)
                {
                    case pix_format_rgb555:
                        color_conv(m_rbuf_img+idx, &rbuf_img, color_conv_rgb24_to_rgb555());
                        break;
                        
                    case pix_format_rgb565:
                        color_conv(m_rbuf_img+idx, &rbuf_img, color_conv_rgb24_to_rgb565());
                        break;
                        
                    case pix_format_bgr24:
                        color_conv(m_rbuf_img+idx, &rbuf_img, color_conv_rgb24_to_bgr24());
                        break;
                        
                    case pix_format_rgba32:
                        color_conv(m_rbuf_img+idx, &rbuf_img, color_conv_rgb24_to_rgba32());
                        break;
                        
                    case pix_format_argb32:
                        color_conv(m_rbuf_img+idx, &rbuf_img, color_conv_rgb24_to_argb32());
                        break;
                        
                    case pix_format_bgra32:
                        color_conv(m_rbuf_img+idx, &rbuf_img, color_conv_rgb24_to_bgra32());
                        break;
                        
                    case pix_format_abgr32:
                        color_conv(m_rbuf_img+idx, &rbuf_img, color_conv_rgb24_to_abgr32());
                        break;
                        
                    default:
                        ret = false;
                }
                delete [] buf_img;
            }
                        
            fclose(fd);
            return ret;
        }
        return false;
    }




    //------------------------------------------------------------------------
    bool platform_support::save_img(unsigned idx, const char* file)
    {
        if(idx < max_images &&  rbuf_img(idx).buf())
        {
            char buf[1024];
            strcpy(buf, file);
            int len = strlen(buf);
            if(len < 4 || strcasecmp(buf + len - 4, ".ppm") != 0)
            {
                strcat(buf, ".ppm");
            }
            
            FILE* fd = fopen(buf, "wb");
            if(fd == 0) return false;
            
            unsigned w = rbuf_img(idx).width();
            unsigned h = rbuf_img(idx).height();
            
            fprintf(fd, "P6\n%d %d\n255\n", w, h);
                
            unsigned y; 
            unsigned char* tmp_buf = new unsigned char [w * 3];
            for(y = 0; y < rbuf_img(idx).height(); y++)
            {
                const unsigned char* src = rbuf_img(idx).row_ptr(m_flip_y ? h - 1 - y : y);
                switch(m_format)
                {
                    default: break;
                    case pix_format_rgb555:
                        color_conv_row(tmp_buf, src, w, color_conv_rgb555_to_rgb24());
                        break;
                        
                    case pix_format_rgb565:
                        color_conv_row(tmp_buf, src, w, color_conv_rgb565_to_rgb24());
                        break;
                        
                    case pix_format_bgr24:
                        color_conv_row(tmp_buf, src, w, color_conv_bgr24_to_rgb24());
                        break;
                        
                    case pix_format_rgb24:
                        color_conv_row(tmp_buf, src, w, color_conv_rgb24_to_rgb24());
                        break;
                       
                    case pix_format_rgba32:
                        color_conv_row(tmp_buf, src, w, color_conv_rgba32_to_rgb24());
                        break;
                        
                    case pix_format_argb32:
                        color_conv_row(tmp_buf, src, w, color_conv_argb32_to_rgb24());
                        break;
                        
                    case pix_format_bgra32:
                        color_conv_row(tmp_buf, src, w, color_conv_bgra32_to_rgb24());
                        break;
                        
                    case pix_format_abgr32:
                        color_conv_row(tmp_buf, src, w, color_conv_abgr32_to_rgb24());
                        break;
                }
                fwrite(tmp_buf, 1, w * 3, fd);
            }
            delete [] tmp_buf;
            fclose(fd);
            return true;
        }
        return false;
    }



    //------------------------------------------------------------------------
    bool platform_support::create_img(unsigned idx, unsigned width, unsigned height)
    {
        if(idx < max_images)
        {
            if(width  == 0) width  = rbuf_window().width();
            if(height == 0) height = rbuf_window().height();
            delete [] m_specific->m_buf_img[idx];
            m_specific->m_buf_img[idx] = 
                new unsigned char[width * height * (m_bpp / 8)];

            m_rbuf_img[idx].attach(m_specific->m_buf_img[idx],
                                   width,
                                   height,
                                   m_flip_y ? 
                                       -width * (m_bpp / 8) : 
                                        width * (m_bpp / 8));
            return true;
        }
        return false;
    }


    //------------------------------------------------------------------------
    void platform_support::force_redraw()
    {
        m_specific->m_update_flag = true;
    }


    //------------------------------------------------------------------------
    void platform_support::message(const char* msg)
    {
        fprintf(stderr, "%s\n", msg);
    }

    //------------------------------------------------------------------------
    void platform_support::start_timer()
    {
        m_specific->m_sw_start = clock();
    }

    //------------------------------------------------------------------------
    double platform_support::elapsed_time() const
    {
        clock_t stop = clock();
        return double(stop - m_specific->m_sw_start) * 1000.0 / CLOCKS_PER_SEC;
    }


    //------------------------------------------------------------------------
    void platform_support::on_init() {}
    void platform_support::on_resize(int sx, int sy) {}
    void platform_support::on_idle() {}
    void platform_support::on_mouse_move(int x, int y, unsigned flags) {}
    void platform_support::on_mouse_button_down(int x, int y, unsigned flags) {}
    void platform_support::on_mouse_button_up(int x, int y, unsigned flags) {}
    void platform_support::on_key(int x, int y, unsigned key, unsigned flags) {}
    void platform_support::on_ctrl_change() {}
    void platform_support::on_draw() {}
    void platform_support::on_post_draw(void* raw_handler) {}



}

#endif

//----------------------------------------------------------------------------
// Anti-Grain Geometry - Version 2.4
// Copyright (C) 2002-2005 Maxim Shemanarev (http://www.antigrain.com)
//
// Permission to copy, use, modify, sell and distribute this software 
// is granted provided this copyright notice appears in all copies. 
// This software is provided "as is" without express or implied
// warranty, and with no claim as to its suitability for any purpose.
//
//----------------------------------------------------------------------------
// Contact: mcseem@antigrain.com
//          mcseemagg@yahoo.com
//          http://www.antigrain.com
//----------------------------------------------------------------------------
//
// class platform_support
//
//----------------------------------------------------------------------------

#ifdef PLATFORM_WIN32
#include <windows.h>
#include <string.h>
#include "agg_platform_support.h"
#include "agg_win32_bmp.h"
#include "agg_color_conv_rgb8.h"
#include "agg_color_conv_rgb16.h"


namespace agg
{
    
    //------------------------------------------------------------------------
    HINSTANCE g_windows_instance = 0;
    int       g_windows_cmd_show = 0;


    //------------------------------------------------------------------------
    class platform_specific
    {
    public:
        platform_specific(pix_format_e format, bool flip_y);

        void create_pmap(unsigned width, unsigned height, 
                         rendering_buffer* wnd);

        void display_pmap(HDC dc, const rendering_buffer* src);
        bool load_pmap(const char* fn, unsigned idx, 
                       rendering_buffer* dst);

        bool save_pmap(const char* fn, unsigned idx, 
                       const rendering_buffer* src);

        unsigned translate(unsigned keycode);

        pix_format_e  m_format;
        pix_format_e  m_sys_format;
        bool          m_flip_y;
        unsigned      m_bpp;
        unsigned      m_sys_bpp;
        HWND          m_hwnd;
        pixel_map     m_pmap_window;
        pixel_map     m_pmap_img[platform_support::max_images];
        unsigned      m_keymap[256];
        unsigned      m_last_translated_key;
        int           m_cur_x;
        int           m_cur_y;
        unsigned      m_input_flags;
        bool          m_redraw_flag;
        HDC           m_current_dc;
        LARGE_INTEGER m_sw_freq;
        LARGE_INTEGER m_sw_start;
    };


    //------------------------------------------------------------------------
    platform_specific::platform_specific(pix_format_e format, bool flip_y) :
        m_format(format),
        m_sys_format(pix_format_undefined),
        m_flip_y(flip_y),
        m_bpp(0),
        m_sys_bpp(0),
        m_hwnd(0),
        m_last_translated_key(0),
        m_cur_x(0),
        m_cur_y(0),
        m_input_flags(0),
        m_redraw_flag(true),
        m_current_dc(0)
    {
        memset(m_keymap, 0, sizeof(m_keymap));

        m_keymap[VK_PAUSE]      = key_pause;
        m_keymap[VK_CLEAR]      = key_clear;

        m_keymap[VK_NUMPAD0]    = key_kp0;
        m_keymap[VK_NUMPAD1]    = key_kp1;
        m_keymap[VK_NUMPAD2]    = key_kp2;
        m_keymap[VK_NUMPAD3]    = key_kp3;
        m_keymap[VK_NUMPAD4]    = key_kp4;
        m_keymap[VK_NUMPAD5]    = key_kp5;
        m_keymap[VK_NUMPAD6]    = key_kp6;
        m_keymap[VK_NUMPAD7]    = key_kp7;
        m_keymap[VK_NUMPAD8]    = key_kp8;
        m_keymap[VK_NUMPAD9]    = key_kp9;
        m_keymap[VK_DECIMAL]    = key_kp_period;
        m_keymap[VK_DIVIDE]     = key_kp_divide;
        m_keymap[VK_MULTIPLY]   = key_kp_multiply;
        m_keymap[VK_SUBTRACT]   = key_kp_minus;
        m_keymap[VK_ADD]        = key_kp_plus;

        m_keymap[VK_UP]         = key_up;
        m_keymap[VK_DOWN]       = key_down;
        m_keymap[VK_RIGHT]      = key_right;
        m_keymap[VK_LEFT]       = key_left;
        m_keymap[VK_INSERT]     = key_insert;
        m_keymap[VK_DELETE]     = key_delete;
        m_keymap[VK_HOME]       = key_home;
        m_keymap[VK_END]        = key_end;
        m_keymap[VK_PRIOR]      = key_page_up;
        m_keymap[VK_NEXT]       = key_page_down;

        m_keymap[VK_F1]         = key_f1;
        m_keymap[VK_F2]         = key_f2;
        m_keymap[VK_F3]         = key_f3;
        m_keymap[VK_F4]         = key_f4;
        m_keymap[VK_F5]         = key_f5;
        m_keymap[VK_F6]         = key_f6;
        m_keymap[VK_F7]         = key_f7;
        m_keymap[VK_F8]         = key_f8;
        m_keymap[VK_F9]         = key_f9;
        m_keymap[VK_F10]        = key_f10;
        m_keymap[VK_F11]        = key_f11;
        m_keymap[VK_F12]        = key_f12;
        m_keymap[VK_F13]        = key_f13;
        m_keymap[VK_F14]        = key_f14;
        m_keymap[VK_F15]        = key_f15;

        m_keymap[VK_NUMLOCK]    = key_numlock;
        m_keymap[VK_CAPITAL]    = key_capslock;
        m_keymap[VK_SCROLL]     = key_scrollock;


        switch(m_format)
        {
        case pix_format_bw:
            m_sys_format = pix_format_bw;
            m_bpp = 1;
            m_sys_bpp = 1;
            break;

        case pix_format_gray8:
            m_sys_format = pix_format_gray8;
            m_bpp = 8;
            m_sys_bpp = 8;
            break;

        case pix_format_gray16:
            m_sys_format = pix_format_gray8;
            m_bpp = 16;
            m_sys_bpp = 8;
            break;

        case pix_format_rgb565:
        case pix_format_rgb555:
            m_sys_format = pix_format_rgb555;
            m_bpp = 16;
            m_sys_bpp = 16;
            break;

        case pix_format_rgbAAA:
        case pix_format_bgrAAA:
        case pix_format_rgbBBA:
        case pix_format_bgrABB:
            m_sys_format = pix_format_bgr24;
            m_bpp = 32;
            m_sys_bpp = 24;
            break;

        case pix_format_rgb24:
        case pix_format_bgr24:
            m_sys_format = pix_format_bgr24;
            m_bpp = 24;
            m_sys_bpp = 24;
            break;

        case pix_format_rgb48:
        case pix_format_bgr48:
            m_sys_format = pix_format_bgr24;
            m_bpp = 48;
            m_sys_bpp = 24;
            break;

        case pix_format_bgra32:
        case pix_format_abgr32:
        case pix_format_argb32:
        case pix_format_rgba32:
            m_sys_format = pix_format_bgra32;
            m_bpp = 32;
            m_sys_bpp = 32;
            break;

        case pix_format_bgra64:
        case pix_format_abgr64:
        case pix_format_argb64:
        case pix_format_rgba64:
            m_sys_format = pix_format_bgra32;
            m_bpp = 64;
            m_sys_bpp = 32;
            break;
        }
        ::QueryPerformanceFrequency(&m_sw_freq);
        ::QueryPerformanceCounter(&m_sw_start);
    }


    //------------------------------------------------------------------------
    void platform_specific::create_pmap(unsigned width, 
                                        unsigned height,
                                        rendering_buffer* wnd)
    {
        m_pmap_window.create(width, height, org_e(m_bpp));
        wnd->attach(m_pmap_window.buf(), 
                    m_pmap_window.width(),
                    m_pmap_window.height(),
                      m_flip_y ?
                      m_pmap_window.stride() :
                     -m_pmap_window.stride());
    }


    //------------------------------------------------------------------------
    static void convert_pmap(rendering_buffer* dst, 
                             const rendering_buffer* src, 
                             pix_format_e format)
    {
        switch(format)
        {
        case pix_format_gray8:
            break;

        case pix_format_gray16:
            color_conv(dst, src, color_conv_gray16_to_gray8());
            break;

        case pix_format_rgb565:
            color_conv(dst, src, color_conv_rgb565_to_rgb555());
            break;

        case pix_format_rgbAAA:
            color_conv(dst, src, color_conv_rgbAAA_to_bgr24());
            break;

        case pix_format_bgrAAA:
            color_conv(dst, src, color_conv_bgrAAA_to_bgr24());
            break;

        case pix_format_rgbBBA:
            color_conv(dst, src, color_conv_rgbBBA_to_bgr24());
            break;

        case pix_format_bgrABB:
            color_conv(dst, src, color_conv_bgrABB_to_bgr24());
            break;

        case pix_format_rgb24:
            color_conv(dst, src, color_conv_rgb24_to_bgr24());
            break;

        case pix_format_rgb48:
            color_conv(dst, src, color_conv_rgb48_to_bgr24());
            break;

        case pix_format_bgr48:
            color_conv(dst, src, color_conv_bgr48_to_bgr24());
            break;

        case pix_format_abgr32:
            color_conv(dst, src, color_conv_abgr32_to_bgra32());
            break;

        case pix_format_argb32:
            color_conv(dst, src, color_conv_argb32_to_bgra32());
            break;

        case pix_format_rgba32:
            color_conv(dst, src, color_conv_rgba32_to_bgra32());
            break;

        case pix_format_bgra64:
            color_conv(dst, src, color_conv_bgra64_to_bgra32());
            break;

        case pix_format_abgr64:
            color_conv(dst, src, color_conv_abgr64_to_bgra32());
            break;

        case pix_format_argb64:
            color_conv(dst, src, color_conv_argb64_to_bgra32());
            break;

        case pix_format_rgba64:
            color_conv(dst, src, color_conv_rgba64_to_bgra32());
            break;
        }
    }


    //------------------------------------------------------------------------
    void platform_specific::display_pmap(HDC dc, const rendering_buffer* src)
    {
        if(m_sys_format == m_format)
        {
            m_pmap_window.draw(dc);
        }
        else
        {
            pixel_map pmap_tmp;
            pmap_tmp.create(m_pmap_window.width(), 
                            m_pmap_window.height(),
                            org_e(m_sys_bpp));

            rendering_buffer rbuf_tmp;
            rbuf_tmp.attach(pmap_tmp.buf(),
                            pmap_tmp.width(),
                            pmap_tmp.height(),
                            m_flip_y ?
                              pmap_tmp.stride() :
                             -pmap_tmp.stride());

            convert_pmap(&rbuf_tmp, src, m_format);
            pmap_tmp.draw(dc);
        }
    }



    //------------------------------------------------------------------------
    bool platform_specific::save_pmap(const char* fn, unsigned idx, 
                                      const rendering_buffer* src)
    {
        if(m_sys_format == m_format)
        {
            return m_pmap_img[idx].save_as_bmp(fn);
        }

        pixel_map pmap_tmp;
        pmap_tmp.create(m_pmap_img[idx].width(), 
                          m_pmap_img[idx].height(),
                          org_e(m_sys_bpp));

        rendering_buffer rbuf_tmp;
        rbuf_tmp.attach(pmap_tmp.buf(),
                          pmap_tmp.width(),
                          pmap_tmp.height(),
                          m_flip_y ?
                          pmap_tmp.stride() :
                          -pmap_tmp.stride());

        convert_pmap(&rbuf_tmp, src, m_format);
        return pmap_tmp.save_as_bmp(fn);
    }



    //------------------------------------------------------------------------
    bool platform_specific::load_pmap(const char* fn, unsigned idx, 
                                      rendering_buffer* dst)
    {
        pixel_map pmap_tmp;
        if(!pmap_tmp.load_from_bmp(fn)) return false;

        rendering_buffer rbuf_tmp;
        rbuf_tmp.attach(pmap_tmp.buf(),
                        pmap_tmp.width(),
                        pmap_tmp.height(),
                        m_flip_y ?
                          pmap_tmp.stride() :
                         -pmap_tmp.stride());

        m_pmap_img[idx].create(pmap_tmp.width(), 
                               pmap_tmp.height(), 
                               org_e(m_bpp),
                               0);

        dst->attach(m_pmap_img[idx].buf(),
                    m_pmap_img[idx].width(),
                    m_pmap_img[idx].height(),
                    m_flip_y ?
                       m_pmap_img[idx].stride() :
                      -m_pmap_img[idx].stride());

        switch(m_format)
        {
        case pix_format_gray8:
            switch(pmap_tmp.bpp())
            {
            //case 16: color_conv(dst, &rbuf_tmp, color_conv_rgb555_to_gray8()); break;
            case 24: color_conv(dst, &rbuf_tmp, color_conv_bgr24_to_gray8()); break;
            //case 32: color_conv(dst, &rbuf_tmp, color_conv_bgra32_to_gray8()); break;
            }
            break;

        case pix_format_gray16:
            switch(pmap_tmp.bpp())
            {
            //case 16: color_conv(dst, &rbuf_tmp, color_conv_rgb555_to_gray16()); break;
            case 24: color_conv(dst, &rbuf_tmp, color_conv_bgr24_to_gray16()); break;
            //case 32: color_conv(dst, &rbuf_tmp, color_conv_bgra32_to_gray16()); break;
            }
            break;

        case pix_format_rgb555:
            switch(pmap_tmp.bpp())
            {
            case 16: color_conv(dst, &rbuf_tmp, color_conv_rgb555_to_rgb555()); break;
            case 24: color_conv(dst, &rbuf_tmp, color_conv_bgr24_to_rgb555()); break;
            case 32: color_conv(dst, &rbuf_tmp, color_conv_bgra32_to_rgb555()); break;
            }
            break;

        case pix_format_rgb565:
            switch(pmap_tmp.bpp())
            {
            case 16: color_conv(dst, &rbuf_tmp, color_conv_rgb555_to_rgb565()); break;
            case 24: color_conv(dst, &rbuf_tmp, color_conv_bgr24_to_rgb565()); break;
            case 32: color_conv(dst, &rbuf_tmp, color_conv_bgra32_to_rgb565()); break;
            }
            break;

        case pix_format_rgb24:
            switch(pmap_tmp.bpp())
            {
            case 16: color_conv(dst, &rbuf_tmp, color_conv_rgb555_to_rgb24()); break;
            case 24: color_conv(dst, &rbuf_tmp, color_conv_bgr24_to_rgb24()); break;
            case 32: color_conv(dst, &rbuf_tmp, color_conv_bgra32_to_rgb24()); break;
            }
            break;

        case pix_format_bgr24:
            switch(pmap_tmp.bpp())
            {
            case 16: color_conv(dst, &rbuf_tmp, color_conv_rgb555_to_bgr24()); break;
            case 24: color_conv(dst, &rbuf_tmp, color_conv_bgr24_to_bgr24()); break;
            case 32: color_conv(dst, &rbuf_tmp, color_conv_bgra32_to_bgr24()); break;
            }
            break;

        case pix_format_rgb48:
            switch(pmap_tmp.bpp())
            {
            //case 16: color_conv(dst, &rbuf_tmp, color_conv_rgb555_to_rgb48()); break;
            case 24: color_conv(dst, &rbuf_tmp, color_conv_bgr24_to_rgb48()); break;
            //case 32: color_conv(dst, &rbuf_tmp, color_conv_bgra32_to_rgb48()); break;
            }
            break;

        case pix_format_bgr48:
            switch(pmap_tmp.bpp())
            {
            //case 16: color_conv(dst, &rbuf_tmp, color_conv_rgb555_to_bgr48()); break;
            case 24: color_conv(dst, &rbuf_tmp, color_conv_bgr24_to_bgr48()); break;
            //case 32: color_conv(dst, &rbuf_tmp, color_conv_bgra32_to_bgr48()); break;
            }
            break;

        case pix_format_abgr32:
            switch(pmap_tmp.bpp())
            {
            case 16: color_conv(dst, &rbuf_tmp, color_conv_rgb555_to_abgr32()); break;
            case 24: color_conv(dst, &rbuf_tmp, color_conv_bgr24_to_abgr32()); break;
            case 32: color_conv(dst, &rbuf_tmp, color_conv_bgra32_to_abgr32()); break;
            }
            break;

        case pix_format_argb32:
            switch(pmap_tmp.bpp())
            {
            case 16: color_conv(dst, &rbuf_tmp, color_conv_rgb555_to_argb32()); break;
            case 24: color_conv(dst, &rbuf_tmp, color_conv_bgr24_to_argb32()); break;
            case 32: color_conv(dst, &rbuf_tmp, color_conv_bgra32_to_argb32()); break;
            }
            break;

        case pix_format_bgra32:
            switch(pmap_tmp.bpp())
            {
            case 16: color_conv(dst, &rbuf_tmp, color_conv_rgb555_to_bgra32()); break;
            case 24: color_conv(dst, &rbuf_tmp, color_conv_bgr24_to_bgra32()); break;
            case 32: color_conv(dst, &rbuf_tmp, color_conv_bgra32_to_bgra32()); break;
            }
            break;

        case pix_format_rgba32:
            switch(pmap_tmp.bpp())
            {
            case 16: color_conv(dst, &rbuf_tmp, color_conv_rgb555_to_rgba32()); break;
            case 24: color_conv(dst, &rbuf_tmp, color_conv_bgr24_to_rgba32()); break;
            case 32: color_conv(dst, &rbuf_tmp, color_conv_bgra32_to_rgba32()); break;
            }
            break;

        case pix_format_abgr64:
            switch(pmap_tmp.bpp())
            {
            //case 16: color_conv(dst, &rbuf_tmp, color_conv_rgb555_to_abgr64()); break;
            case 24: color_conv(dst, &rbuf_tmp, color_conv_bgr24_to_abgr64()); break;
            //case 32: color_conv(dst, &rbuf_tmp, color_conv_bgra32_to_abgr64()); break;
            }
            break;

        case pix_format_argb64:
            switch(pmap_tmp.bpp())
            {
            //case 16: color_conv(dst, &rbuf_tmp, color_conv_rgb555_to_argb64()); break;
            case 24: color_conv(dst, &rbuf_tmp, color_conv_bgr24_to_argb64()); break;
            //case 32: color_conv(dst, &rbuf_tmp, color_conv_bgra32_to_argb64()); break;
            }
            break;

        case pix_format_bgra64:
            switch(pmap_tmp.bpp())
            {
            //case 16: color_conv(dst, &rbuf_tmp, color_conv_rgb555_to_bgra64()); break;
            case 24: color_conv(dst, &rbuf_tmp, color_conv_bgr24_to_bgra64()); break;
            //case 32: color_conv(dst, &rbuf_tmp, color_conv_bgra32_to_bgra64()); break;
            }
            break;

        case pix_format_rgba64:
            switch(pmap_tmp.bpp())
            {
            //case 16: color_conv(dst, &rbuf_tmp, color_conv_rgb555_to_rgba64()); break;
            case 24: color_conv(dst, &rbuf_tmp, color_conv_bgr24_to_rgba64()); break;
            //case 32: color_conv(dst, &rbuf_tmp, color_conv_bgra32_to_rgba64()); break;
            }
            break;

        }

        return true;
    }








    //------------------------------------------------------------------------
    unsigned platform_specific::translate(unsigned keycode)
    {
        return m_last_translated_key = (keycode > 255) ? 0 : m_keymap[keycode];
    }



    //------------------------------------------------------------------------
    platform_support::platform_support(pix_format_e format, bool flip_y) :
        m_specific(new platform_specific(format, flip_y)),
        m_format(format),
        m_bpp(m_specific->m_bpp),
        m_window_flags(0),
        m_wait_mode(true),
        m_flip_y(flip_y),
        m_initial_width(10),
        m_initial_height(10)
    {
        strcpy(m_caption, "Anti-Grain Geometry Application");
    }


    //------------------------------------------------------------------------
    platform_support::~platform_support()
    {
        delete m_specific;
    }



    //------------------------------------------------------------------------
    void platform_support::caption(const char* cap)
    {
        strcpy(m_caption, cap);
        if(m_specific->m_hwnd)
        {
            SetWindowText(m_specific->m_hwnd, m_caption);
        }
    }

    //------------------------------------------------------------------------
    void platform_support::start_timer()
    {
        ::QueryPerformanceCounter(&(m_specific->m_sw_start));
    }

    //------------------------------------------------------------------------
    double platform_support::elapsed_time() const
    {
        LARGE_INTEGER stop;
        ::QueryPerformanceCounter(&stop);
        return double(stop.QuadPart - 
                      m_specific->m_sw_start.QuadPart) * 1000.0 / 
                      double(m_specific->m_sw_freq.QuadPart);
    }



    //------------------------------------------------------------------------
    static unsigned get_key_flags(int wflags)
    {
        unsigned flags = 0;
        if(wflags & MK_LBUTTON) flags |= mouse_left;
        if(wflags & MK_RBUTTON) flags |= mouse_right;
        if(wflags & MK_SHIFT)   flags |= kbd_shift;
        if(wflags & MK_CONTROL) flags |= kbd_ctrl;
        return flags;
    }


    void* platform_support::raw_display_handler()
    {
        return m_specific->m_current_dc;
    }


    //------------------------------------------------------------------------
    LRESULT CALLBACK window_proc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
    {
        PAINTSTRUCT ps;
        HDC paintDC;


        void* user_data = reinterpret_cast<void*>(::GetWindowLong(hWnd, GWL_USERDATA));
        platform_support* app = 0;

        if(user_data)
        {
            app = reinterpret_cast<platform_support*>(user_data);
        }

        if(app == 0)
        {
            if(msg == WM_DESTROY)
            {
                ::PostQuitMessage(0);
                return 0;
            }
            return ::DefWindowProc(hWnd, msg, wParam, lParam);
        }

        HDC dc = ::GetDC(app->m_specific->m_hwnd);
        app->m_specific->m_current_dc = dc;
        LRESULT ret = 0;

        switch(msg) 
        {
        //--------------------------------------------------------------------
        case WM_CREATE:
            break;
        
        //--------------------------------------------------------------------
        case WM_SIZE:
            app->m_specific->create_pmap(LOWORD(lParam), 
                                         HIWORD(lParam),
                                         &app->rbuf_window());

            app->trans_affine_resizing(LOWORD(lParam), HIWORD(lParam));
            app->on_resize(LOWORD(lParam), HIWORD(lParam));
            app->force_redraw();
            break;
        
        //--------------------------------------------------------------------
        case WM_ERASEBKGND:
            break;
        
        //--------------------------------------------------------------------
        case WM_LBUTTONDOWN:
            ::SetCapture(app->m_specific->m_hwnd);
            app->m_specific->m_cur_x = int16(LOWORD(lParam));
            if(app->flip_y())
            {
                app->m_specific->m_cur_y = app->rbuf_window().height() - int16(HIWORD(lParam));
            }
            else
            {
                app->m_specific->m_cur_y = int16(HIWORD(lParam));
            }
            app->m_specific->m_input_flags = mouse_left | get_key_flags(wParam);
            
            app->m_ctrls.set_cur(app->m_specific->m_cur_x, 
                                 app->m_specific->m_cur_y);
            if(app->m_ctrls.on_mouse_button_down(app->m_specific->m_cur_x, 
                                                 app->m_specific->m_cur_y))
            {
                app->on_ctrl_change();
                app->force_redraw();
            }
            else
            {
                if(app->m_ctrls.in_rect(app->m_specific->m_cur_x, 
                                        app->m_specific->m_cur_y))
                {
                    if(app->m_ctrls.set_cur(app->m_specific->m_cur_x, 
                                            app->m_specific->m_cur_y))
                    {
                        app->on_ctrl_change();
                        app->force_redraw();
                    }
                }
                else
                {
                    app->on_mouse_button_down(app->m_specific->m_cur_x, 
                                              app->m_specific->m_cur_y, 
                                              app->m_specific->m_input_flags);
                }
            }
/*
            if(!app->wait_mode())
            {
                app->on_idle();
            }
*/
            break;

        //--------------------------------------------------------------------
        case WM_LBUTTONUP:
            ::ReleaseCapture();
            app->m_specific->m_cur_x = int16(LOWORD(lParam));
            if(app->flip_y())
            {
                app->m_specific->m_cur_y = app->rbuf_window().height() - int16(HIWORD(lParam));
            }
            else
            {
                app->m_specific->m_cur_y = int16(HIWORD(lParam));
            }
            app->m_specific->m_input_flags = mouse_left | get_key_flags(wParam);

            if(app->m_ctrls.on_mouse_button_up(app->m_specific->m_cur_x, 
                                               app->m_specific->m_cur_y))
            {
                app->on_ctrl_change();
                app->force_redraw();
            }
            app->on_mouse_button_up(app->m_specific->m_cur_x, 
                                    app->m_specific->m_cur_y, 
                                    app->m_specific->m_input_flags);
/*
            if(!app->wait_mode())
            {
                app->on_idle();
            }
*/
            break;


        //--------------------------------------------------------------------
        case WM_RBUTTONDOWN:
            ::SetCapture(app->m_specific->m_hwnd);
            app->m_specific->m_cur_x = int16(LOWORD(lParam));
            if(app->flip_y())
            {
                app->m_specific->m_cur_y = app->rbuf_window().height() - int16(HIWORD(lParam));
            }
            else
            {
                app->m_specific->m_cur_y = int16(HIWORD(lParam));
            }
            app->m_specific->m_input_flags = mouse_right | get_key_flags(wParam);
            app->on_mouse_button_down(app->m_specific->m_cur_x, 
                                      app->m_specific->m_cur_y, 
                                      app->m_specific->m_input_flags);
/*
            if(!app->wait_mode())
            {
                app->on_idle();
            }
*/
            break;

        //--------------------------------------------------------------------
        case WM_RBUTTONUP:
            ::ReleaseCapture();
            app->m_specific->m_cur_x = int16(LOWORD(lParam));
            if(app->flip_y())
            {
                app->m_specific->m_cur_y = app->rbuf_window().height() - int16(HIWORD(lParam));
            }
            else
            {
                app->m_specific->m_cur_y = int16(HIWORD(lParam));
            }
            app->m_specific->m_input_flags = mouse_right | get_key_flags(wParam);
            app->on_mouse_button_up(app->m_specific->m_cur_x, 
                                    app->m_specific->m_cur_y, 
                                    app->m_specific->m_input_flags);
/*
            if(!app->wait_mode())
            {
                app->on_idle();
            }
*/
            break;

        //--------------------------------------------------------------------
        case WM_MOUSEMOVE:
            app->m_specific->m_cur_x = int16(LOWORD(lParam));
            if(app->flip_y())
            {
                app->m_specific->m_cur_y = app->rbuf_window().height() - int16(HIWORD(lParam));
            }
            else
            {
                app->m_specific->m_cur_y = int16(HIWORD(lParam));
            }
            app->m_specific->m_input_flags = get_key_flags(wParam);


            if(app->m_ctrls.on_mouse_move(
                app->m_specific->m_cur_x, 
                app->m_specific->m_cur_y,
                (app->m_specific->m_input_flags & mouse_left) != 0))
            {
                app->on_ctrl_change();
                app->force_redraw();
            }
            else
            {
                if(!app->m_ctrls.in_rect(app->m_specific->m_cur_x, 
                                         app->m_specific->m_cur_y))
                {
                    app->on_mouse_move(app->m_specific->m_cur_x, 
                                       app->m_specific->m_cur_y, 
                                       app->m_specific->m_input_flags);
                }
            }
/*
            if(!app->wait_mode())
            {
                app->on_idle();
            }
*/
            break;

        //--------------------------------------------------------------------
        case WM_SYSKEYDOWN:
        case WM_KEYDOWN:
            app->m_specific->m_last_translated_key = 0;
            switch(wParam) 
            {
                case VK_CONTROL:
                    app->m_specific->m_input_flags |= kbd_ctrl;
                    break;

                case VK_SHIFT:
                    app->m_specific->m_input_flags |= kbd_shift;
                    break;

                default:
                    app->m_specific->translate(wParam);
                    break;
            }
        
            if(app->m_specific->m_last_translated_key)
            {
                bool left  = false;
                bool up    = false;
                bool right = false;
                bool down  = false;

                switch(app->m_specific->m_last_translated_key)
                {
                case key_left:
                    left = true;
                    break;

                case key_up:
                    up = true;
                    break;

                case key_right:
                    right = true;
                    break;

                case key_down:
                    down = true;
                    break;

                case key_f2:                        
                    app->copy_window_to_img(agg::platform_support::max_images - 1);
                    app->save_img(agg::platform_support::max_images - 1, "screenshot");
                    break;
                }

                if(app->window_flags() & window_process_all_keys)
                {
                    app->on_key(app->m_specific->m_cur_x,
                                app->m_specific->m_cur_y,
                                app->m_specific->m_last_translated_key,
                                app->m_specific->m_input_flags);
                }
                else
                {
                    if(app->m_ctrls.on_arrow_keys(left, right, down, up))
                    {
                        app->on_ctrl_change();
                        app->force_redraw();
                    }
                    else
                    {
                        app->on_key(app->m_specific->m_cur_x,
                                    app->m_specific->m_cur_y,
                                    app->m_specific->m_last_translated_key,
                                    app->m_specific->m_input_flags);
                    }
                }
            }
/*
            if(!app->wait_mode())
            {
                app->on_idle();
            }
*/
            break;

        //--------------------------------------------------------------------
        case WM_SYSKEYUP:
        case WM_KEYUP:
            app->m_specific->m_last_translated_key = 0;
            switch(wParam) 
            {
                case VK_CONTROL:
                    app->m_specific->m_input_flags &= ~kbd_ctrl;
                    break;

                case VK_SHIFT:
                    app->m_specific->m_input_flags &= ~kbd_shift;
                    break;
            }
            break;

        //--------------------------------------------------------------------
        case WM_CHAR:
        case WM_SYSCHAR:
            if(app->m_specific->m_last_translated_key == 0)
            {
                app->on_key(app->m_specific->m_cur_x,
                            app->m_specific->m_cur_y,
                            wParam,
                            app->m_specific->m_input_flags);
            }
            break;
        
        //--------------------------------------------------------------------
        case WM_PAINT:
            paintDC = ::BeginPaint(hWnd, &ps);
            app->m_specific->m_current_dc = paintDC;
            if(app->m_specific->m_redraw_flag)
            {
                app->on_draw();
                app->m_specific->m_redraw_flag = false;
            }
            app->m_specific->display_pmap(paintDC, &app->rbuf_window());
            app->on_post_draw(paintDC);
            app->m_specific->m_current_dc = 0;
            ::EndPaint(hWnd, &ps);
            break;
        
        //--------------------------------------------------------------------
        case WM_COMMAND:
            break;
        
        //--------------------------------------------------------------------
        case WM_DESTROY:
            ::PostQuitMessage(0);
            break;
        
        //--------------------------------------------------------------------
        default:
            ret = ::DefWindowProc(hWnd, msg, wParam, lParam);
            break;
        }
        app->m_specific->m_current_dc = 0;
        ::ReleaseDC(app->m_specific->m_hwnd, dc);
        return ret;
    }


    //------------------------------------------------------------------------
    void platform_support::message(const char* msg)
    {
        ::MessageBox(m_specific->m_hwnd, msg, "AGG Message", MB_OK);
    }


    //------------------------------------------------------------------------
    bool platform_support::init(unsigned width, unsigned height, unsigned flags)
    {
        if(m_specific->m_sys_format == pix_format_undefined)
        {
            return false;
        }

        m_window_flags = flags;

        int wflags = CS_OWNDC | CS_VREDRAW | CS_HREDRAW;

        WNDCLASS wc;
        wc.lpszClassName = "AGGAppClass";
        wc.lpfnWndProc = window_proc;
        wc.style = wflags;
        wc.hInstance = g_windows_instance;
        wc.hIcon = LoadIcon(0, IDI_APPLICATION);
        wc.hCursor = LoadCursor(0, IDC_ARROW);
        wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
        wc.lpszMenuName = "AGGAppMenu";
        wc.cbClsExtra = 0;
        wc.cbWndExtra = 0;
        ::RegisterClass(&wc);

        wflags = WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX;

        if(m_window_flags & window_resize)
        {
            wflags |= WS_THICKFRAME | WS_MAXIMIZEBOX;
        }

        m_specific->m_hwnd = ::CreateWindow("AGGAppClass",
                                            m_caption,
                                            wflags,
                                            100,
                                            100,
                                            width,
                                            height,
                                            0,
                                            0,
                                            g_windows_instance,
                                            0);

        if(m_specific->m_hwnd == 0)
        {
            return false;
        }


        RECT rct;
        ::GetClientRect(m_specific->m_hwnd, &rct);

        ::MoveWindow(m_specific->m_hwnd,   // handle to window
                     100,                  // horizontal position
                     100,                  // vertical position
                     width + (width - (rct.right - rct.left)),
                     height + (height - (rct.bottom - rct.top)),
                     FALSE);
   
        ::SetWindowLong(m_specific->m_hwnd, GWL_USERDATA, (LONG)this);
        m_specific->create_pmap(width, height, &m_rbuf_window);
        m_initial_width = width;
        m_initial_height = height;
        on_init();
        m_specific->m_redraw_flag = true;
        ::ShowWindow(m_specific->m_hwnd, g_windows_cmd_show);
        return true;
    }



    //------------------------------------------------------------------------
    int platform_support::run()
    {
        MSG msg;

        for(;;)
        {
            if(m_wait_mode)
            {
                if(!::GetMessage(&msg, 0, 0, 0))
                {
                    break;
                }
                ::TranslateMessage(&msg);
                ::DispatchMessage(&msg);
            }
            else
            {
                if(::PeekMessage(&msg, 0, 0, 0, PM_REMOVE))
                {
                    ::TranslateMessage(&msg);
                    if(msg.message == WM_QUIT)
                    {
                        break;
                    }
                    ::DispatchMessage(&msg);
                }
                else
                {
                    on_idle();
                }
            }
        }
        return (int)msg.wParam;
    }


    //------------------------------------------------------------------------
    const char* platform_support::img_ext() const { return ".bmp"; }


    //------------------------------------------------------------------------
    const char* platform_support::full_file_name(const char* file_name)
    {
        return file_name;
    }

    //------------------------------------------------------------------------
    bool platform_support::load_img(unsigned idx, const char* file)
    {
        if(idx < max_images)
        {
            char fn[1024];
            strcpy(fn, file);
            int len = strlen(fn);
            if(len < 4 || stricmp(fn + len - 4, ".BMP") != 0)
            {
                strcat(fn, ".bmp");
            }
            return m_specific->load_pmap(fn, idx, &m_rbuf_img[idx]);
        }
        return true;
    }



    //------------------------------------------------------------------------
    bool platform_support::save_img(unsigned idx, const char* file)
    {
        if(idx < max_images)
        {
            char fn[1024];
            strcpy(fn, file);
            int len = strlen(fn);
            if(len < 4 || stricmp(fn + len - 4, ".BMP") != 0)
            {
                strcat(fn, ".bmp");
            }
            return m_specific->save_pmap(fn, idx, &m_rbuf_img[idx]);
        }
        return true;
    }



    //------------------------------------------------------------------------
    bool platform_support::create_img(unsigned idx, unsigned width, unsigned height)
    {
        if(idx < max_images)
        {
            if(width  == 0) width  = m_specific->m_pmap_window.width();
            if(height == 0) height = m_specific->m_pmap_window.height();
            m_specific->m_pmap_img[idx].create(width, height, org_e(m_specific->m_bpp));
            m_rbuf_img[idx].attach(m_specific->m_pmap_img[idx].buf(), 
                                   m_specific->m_pmap_img[idx].width(),
                                   m_specific->m_pmap_img[idx].height(),
                                   m_flip_y ?
                                    m_specific->m_pmap_img[idx].stride() :
                                   -m_specific->m_pmap_img[idx].stride());
            return true;
        }
        return false;
    }


    //------------------------------------------------------------------------
    void platform_support::force_redraw()
    {
        m_specific->m_redraw_flag = true;
        ::InvalidateRect(m_specific->m_hwnd, 0, FALSE);
    }



    //------------------------------------------------------------------------
    void platform_support::update_window()
    {
        HDC dc = ::GetDC(m_specific->m_hwnd);
        m_specific->display_pmap(dc, &m_rbuf_window);
        ::ReleaseDC(m_specific->m_hwnd, dc);
    }


    //------------------------------------------------------------------------
    void platform_support::on_init() {}
    void platform_support::on_resize(int sx, int sy) {}
    void platform_support::on_idle() {}
    void platform_support::on_mouse_move(int x, int y, unsigned flags) {}
    void platform_support::on_mouse_button_down(int x, int y, unsigned flags) {}
    void platform_support::on_mouse_button_up(int x, int y, unsigned flags) {}
    void platform_support::on_key(int x, int y, unsigned key, unsigned flags) {}
    void platform_support::on_ctrl_change() {}
    void platform_support::on_draw() {}
    void platform_support::on_post_draw(void* raw_handler) {}
};




namespace agg
{
    // That's ridiculous. I have to parse the command line by myself
    // because Windows doesn't provide a method of getting the command
    // line arguments in a form of argc, argv. Of course, there's 
    // CommandLineToArgv() but first, it returns Unicode that I don't
    // need to deal with, but most of all, it's not compatible with Win98.
    //-----------------------------------------------------------------------
    class tokenizer
    {
    public:
        enum sep_flag
        {
            single,
            multiple,
            whole_str
        };

        struct token
        {
            const char* ptr;
            unsigned    len;
        };

    public:
        tokenizer(const char* sep, 
                  const char* trim=0,
                  const char* quote="\"",
                  char mask_chr='\\',
                  sep_flag sf=multiple);

        void  set_str(const char* str);
        token next_token();

    private:
        int  check_chr(const char *str, char chr);

    private:
        const char* m_src_string;
        int         m_start;
        const char* m_sep;
        const char* m_trim;
        const char* m_quote;
        char        m_mask_chr;
        unsigned    m_sep_len;
        sep_flag    m_sep_flag;
    };



    //-----------------------------------------------------------------------
    inline void tokenizer::set_str(const char* str) 
    { 
        m_src_string = str; 
        m_start = 0;
    }


    //-----------------------------------------------------------------------
    inline int tokenizer::check_chr(const char *str, char chr)
    {
        return int(strchr(str, chr));
    }


    //-----------------------------------------------------------------------
    tokenizer::tokenizer(const char* sep, 
                         const char* trim,
                         const char* quote,
                         char mask_chr,
                         sep_flag sf) :
        m_src_string(0),
        m_start(0),
        m_sep(sep),
        m_trim(trim),
        m_quote(quote),
        m_mask_chr(mask_chr),
        m_sep_len(sep ? strlen(sep) : 0),
        m_sep_flag(sep ? sf : single)
    {
    }


    //-----------------------------------------------------------------------
    tokenizer::token tokenizer::next_token()
    {
        unsigned count = 0;
        char quote_chr = 0;
        token tok;

        tok.ptr = 0;
        tok.len = 0;
        if(m_src_string == 0 || m_start == -1) return tok;

        register const char *pstr = m_src_string + m_start;

        if(*pstr == 0) 
        {
            m_start = -1;
            return tok;
        }

        int sep_len = 1;
        if(m_sep_flag == whole_str) sep_len = m_sep_len;

        if(m_sep_flag == multiple)
        {
            //Pass all the separator symbols at the begin of the string
            while(*pstr && check_chr(m_sep, *pstr)) 
            {
                ++pstr;
                ++m_start;
            }
        }

        if(*pstr == 0) 
        {
            m_start = -1;
            return tok;
        }

        for(count = 0;; ++count) 
        {
            char c = *pstr;
            int found = 0;

            //We are outside of qotation: find one of separator symbols
            if(quote_chr == 0)
            {
                if(sep_len == 1)
                {
                    found = check_chr(m_sep, c);
                }
                else
                {
                    found = strncmp(m_sep, pstr, m_sep_len) == 0; 
                }
            }

            ++pstr;

            if(c == 0 || found) 
            {
                if(m_trim)
                {
                    while(count && 
                          check_chr(m_trim, m_src_string[m_start]))
                    {
                        ++m_start;
                        --count;
                    }

                    while(count && 
                          check_chr(m_trim, m_src_string[m_start + count - 1]))
                    {
                        --count;
                    }
                }

                tok.ptr = m_src_string + m_start;
                tok.len = count;

                //Next time it will be the next separator character
                //But we must check, whether it is NOT the end of the string.
                m_start += count;
                if(c) 
                {
                    m_start += sep_len;
                    if(m_sep_flag == multiple)
                    {
                        //Pass all the separator symbols 
                        //after the end of the string
                        while(check_chr(m_sep, m_src_string[m_start])) 
                        {
                            ++m_start;
                        }
                    }
                }
                break;
            }

            //Switch quote. If it is not a quote yet, try to check any of
            //quote symbols. Otherwise quote must be finished with quote_symb
            if(quote_chr == 0)
            {
                if(check_chr(m_quote, c)) 
                {
                    quote_chr = c;
                    continue;
                }
            }
            else
            {
                //We are inside quote: pass all the mask symbols
                if(m_mask_chr && c == m_mask_chr)
                {
                    if(*pstr) 
                    {
                        ++count;
                        ++pstr;
                    }
                    continue; 
                }
                if(c == quote_chr) 
                {
                    quote_chr = 0;
                    continue;
                }
            }
        }
        return tok;
    }


}



//----------------------------------------------------------------------------
int agg_main(int argc, char* argv[]);



//----------------------------------------------------------------------------
/*int PASCAL WinMain(HINSTANCE hInstance,
                   HINSTANCE hPrevInstance,
                   LPSTR lpszCmdLine,
                   int nCmdShow)
{
    agg::g_windows_instance = hInstance;
    agg::g_windows_cmd_show = nCmdShow;

    char* argv_str = new char [strlen(lpszCmdLine) + 3];
    char* argv_ptr = argv_str;

    char* argv[64];
    memset(argv, 0, sizeof(argv));

    agg::tokenizer cmd_line(" ", "\"' ", "\"'", '\\', agg::tokenizer::multiple);
    cmd_line.set_str(lpszCmdLine);

    int argc = 0;
    argv[argc++] = argv_ptr;
    *argv_ptr++ = 0;

    while(argc < 64)
    {
        agg::tokenizer::token tok = cmd_line.next_token();
        if(tok.ptr == 0) break;
        if(tok.len)
        {
            memcpy(argv_ptr, tok.ptr, tok.len);
            argv[argc++] = argv_ptr;
            argv_ptr += tok.len;
            *argv_ptr++ = 0;
        }
    }

    int ret = agg_main(argc, argv);
    delete [] argv_str;

    return ret;
}
*/

#endif


//----------------------------------------------------------------------------
// Anti-Grain Geometry - Version 2.4
// Copyright (C) 2002-2005 Maxim Shemanarev (http://www.antigrain.com)
//
// Permission to copy, use, modify, sell and distribute this software 
// is granted provided this copyright notice appears in all copies. 
// This software is provided "as is" without express or implied
// warranty, and with no claim as to its suitability for any purpose.
//
//----------------------------------------------------------------------------
// Contact: mcseem@antigrain.com
//          mcseemagg@yahoo.com
//          http://www.antigrain.com
//----------------------------------------------------------------------------
//
// class platform_support
//
//----------------------------------------------------------------------------

#ifdef PLATFORM_AMIGA

#include <agg24/agg_platform_support.h>
#include <agg24/agg_color_conv_rgb8.h>

#include <sys/time.h>
#include <cstring>

#include <classes/requester.h>
#include <classes/window.h>
#include <datatypes/pictureclass.h>
#include <proto/exec.h>
#include <proto/datatypes.h>
#include <proto/dos.h>
#include <proto/graphics.h>
#include <proto/intuition.h>
#include <proto/keymap.h>
#include <proto/Picasso96API.h>
#include <proto/utility.h>

Library* DataTypesBase = 0;
Library* GraphicsBase = 0;
Library* IntuitionBase = 0;
Library* KeymapBase = 0;
Library* P96Base = 0;

DataTypesIFace* IDataTypes = 0;
GraphicsIFace* IGraphics = 0;
IntuitionIFace* IIntuition = 0;
KeymapIFace* IKeymap = 0;
P96IFace* IP96 = 0;

Class* RequesterClass = 0;
Class* WindowClass = 0;


namespace agg
{
	void handle_idcmp(Hook* hook, APTR win, IntuiMessage* msg);

	//------------------------------------------------------------------------
	class platform_specific
	{
	public:
		platform_specific(platform_support& support, pix_format_e format,
			bool flip_y);
		~platform_specific();
		bool handle_input();
		bool load_img(const char* file, unsigned idx, rendering_buffer* rbuf);
		bool create_img(unsigned idx, rendering_buffer* rbuf, unsigned width,
			unsigned height);
		bool make_bitmap();
	public:
		platform_support& m_support;
		RGBFTYPE m_ftype;
		pix_format_e m_format;
		unsigned m_bpp;
		BitMap* m_bitmap;
		bool m_flip_y;
		uint16 m_width;
		uint16 m_height;
		APTR m_window_obj;
		Window* m_window;
		Hook* m_idcmp_hook;
		unsigned m_input_flags;
		bool m_dragging;
		double m_start_time;
		uint16 m_last_key;
		BitMap* m_img_bitmaps[platform_support::max_images];
	};

	//------------------------------------------------------------------------
	platform_specific::platform_specific(platform_support& support,
		pix_format_e format, bool flip_y) :
		m_support(support),
		m_ftype(RGBFB_NONE),
		m_format(format),
		m_bpp(0),
		m_bitmap(0),
		m_flip_y(flip_y),
		m_width(0),
		m_height(0),
		m_window_obj(0),
		m_window(0),
		m_idcmp_hook(0),
		m_input_flags(0),
		m_dragging(false),
		m_start_time(0.0),
		m_last_key(0)
	{
		switch ( format )
		{
		case pix_format_gray8:
			// Not supported.
			break;
		case pix_format_rgb555:
			m_ftype = RGBFB_R5G5B5;
			m_bpp = 15;
			break;
		case pix_format_rgb565:
			m_ftype = RGBFB_R5G6B5;
			m_bpp = 16;
			break;
		case pix_format_rgb24:
			m_ftype = RGBFB_R8G8B8;
			m_bpp = 24;
			break;
		case pix_format_bgr24:
			m_ftype = RGBFB_B8G8R8;
			m_bpp = 24;
			break;
		case pix_format_bgra32:
			m_ftype = RGBFB_B8G8R8A8;
			m_bpp = 32;
			break;
		case pix_format_abgr32:
			m_ftype = RGBFB_A8B8G8R8;
			m_bpp = 32;
			break;
		case pix_format_argb32:
			m_ftype = RGBFB_A8R8G8B8;
			m_bpp = 32;
			break;
        case pix_format_rgba32:
			m_ftype = RGBFB_R8G8B8A8;
			m_bpp = 32;
			break;
		}

		for ( unsigned i = 0; i < platform_support::max_images; ++i )
		{
			m_img_bitmaps[i] = 0;
		}
	}

	//------------------------------------------------------------------------
	platform_specific::~platform_specific()
	{
		IIntuition->DisposeObject(m_window_obj);

		IP96->p96FreeBitMap(m_bitmap);

		for ( unsigned i = 0; i < platform_support::max_images; ++i )
		{
			IP96->p96FreeBitMap(m_img_bitmaps[i]);
		}

		if ( m_idcmp_hook != 0 )
		{
			IExec->FreeSysObject(ASOT_HOOK, m_idcmp_hook);
		}
	}

	//------------------------------------------------------------------------
	bool platform_specific::handle_input()
	{
		int16 code = 0;
		uint32 result = 0;
		Object* obj = reinterpret_cast<Object*>(m_window_obj);

		while ( (result = IIntuition->IDoMethod(obj, WM_HANDLEINPUT,
				&code)) != WMHI_LASTMSG )
		{
			switch ( result & WMHI_CLASSMASK )
			{
			case WMHI_CLOSEWINDOW:
				return true;
				break;
			case WMHI_INTUITICK:
				if ( !m_support.wait_mode() )
				{
					m_support.on_idle();
				}
				break;
			case WMHI_NEWSIZE:
				if ( make_bitmap() )
				{
					m_support.trans_affine_resizing(m_width, m_height);
					m_support.on_resize(m_width, m_height);
					m_support.force_redraw();
				}
				break;
			}
		}

		return false;
	}		

	//------------------------------------------------------------------------
	bool platform_specific::load_img(const char* file, unsigned idx,
		rendering_buffer* rbuf)
	{
		if ( m_img_bitmaps[idx] != 0 )
		{
			IP96->p96FreeBitMap(m_img_bitmaps[idx]);
			m_img_bitmaps[idx] = 0;
		}

		bool result = false;

		Object* picture = IDataTypes->NewDTObject(const_cast<STRPTR>(file),
			DTA_GroupID, GID_PICTURE,
			PDTA_DestMode, PMODE_V43,
			PDTA_Remap, FALSE,
			TAG_END);
		if ( picture != 0 )
		{
			gpLayout layout;
			layout.MethodID = DTM_PROCLAYOUT;
			layout.gpl_GInfo = 0;
			layout.gpl_Initial = 1;
			ULONG loaded = IDataTypes->DoDTMethodA(picture, 0, 0,
				reinterpret_cast<Msg>(&layout));
			if ( loaded != 0 )
			{
				BitMap* src_bitmap = 0;
				IDataTypes->GetDTAttrs(picture,
					PDTA_ClassBitMap, &src_bitmap,
					TAG_END);

				bool supported = false;

				RGBFTYPE ftype = static_cast<RGBFTYPE>(IP96->p96GetBitMapAttr(
					src_bitmap, P96BMA_RGBFORMAT));

				switch ( ftype )
				{
				case RGBFB_R8G8B8:
					supported = true;
					break;
				default:
					m_support.message("File uses unsupported graphics mode.");
					break;
				}

				if ( supported )  {
					uint16 width = IP96->p96GetBitMapAttr(src_bitmap,
						P96BMA_WIDTH);
					uint16 height = IP96->p96GetBitMapAttr(src_bitmap,
						P96BMA_HEIGHT);

					m_img_bitmaps[idx] = IP96->p96AllocBitMap(width, height,
						m_bpp, BMF_USERPRIVATE, 0, m_ftype);
					if ( m_img_bitmaps[idx] != 0 )
					{
						int8u* buf = reinterpret_cast<int8u*>(
							IP96->p96GetBitMapAttr(m_img_bitmaps[idx],
							P96BMA_MEMORY));
						int bpr = IP96->p96GetBitMapAttr(m_img_bitmaps[idx],
							P96BMA_BYTESPERROW);
						int stride = (m_flip_y) ? -bpr : bpr;
						rbuf->attach(buf, width, height, stride);

						// P96 sets the alpha to zero so it can't be used to
						// color convert true color modes.
						if ( m_bpp == 32 )
						{
							RenderInfo ri;
							int32 lock = IP96->p96LockBitMap(src_bitmap,
								reinterpret_cast<uint8*>(&ri),
								sizeof(RenderInfo));

							rendering_buffer rbuf_src;
							rbuf_src.attach(
								reinterpret_cast<int8u*>(ri.Memory),
								width, height, (m_flip_y) ?
									-ri.BytesPerRow : ri.BytesPerRow);

							switch ( m_format )
							{
							case pix_format_bgra32:
								color_conv(rbuf, &rbuf_src,
									color_conv_rgb24_to_bgra32());
								break;
							case pix_format_abgr32:
								color_conv(rbuf, &rbuf_src,
									color_conv_rgb24_to_abgr32());
								break;
							case pix_format_argb32:
								color_conv(rbuf, &rbuf_src,
									color_conv_rgb24_to_argb32());
								break;
							case pix_format_rgba32:
								color_conv(rbuf, &rbuf_src,
									color_conv_rgb24_to_rgba32());
								break;
							}

							IP96->p96UnlockBitMap(src_bitmap, lock);
						}
						else
						{
							IGraphics->BltBitMap(src_bitmap, 0, 0,
								m_img_bitmaps[idx], 0, 0, width, height,
								ABC|ABNC, 0xFF, 0);
						}

						result = true;
					}
				}
			}
		}

		IGraphics->WaitBlit();
		IDataTypes->DisposeDTObject(picture);

		return result;
	}

	//------------------------------------------------------------------------
	bool platform_specific::create_img(unsigned idx, rendering_buffer* rbuf,
		unsigned width, unsigned height)
	{
		if ( m_img_bitmaps[idx] != 0 )
		{
			IP96->p96FreeBitMap(m_img_bitmaps[idx]);
			m_img_bitmaps[idx] = 0;
		}

		m_img_bitmaps[idx] = IP96->p96AllocBitMap(width, height,
			m_bpp, BMF_USERPRIVATE, m_bitmap, m_ftype);
		if ( m_img_bitmaps[idx] != 0 )
		{
			int8u* buf = reinterpret_cast<int8u*>(
				IP96->p96GetBitMapAttr(m_img_bitmaps[idx],
				P96BMA_MEMORY));
			int bpr = IP96->p96GetBitMapAttr(m_img_bitmaps[idx],
				P96BMA_BYTESPERROW);
			int stride = (m_flip_y) ? -bpr : bpr;

			rbuf->attach(buf, width, height, stride);

			return true;
		}

		return false;
	}

	//------------------------------------------------------------------------
	bool platform_specific::make_bitmap()
	{
		uint32 width = 0;
		uint32 height = 0;
		IIntuition->GetWindowAttrs(m_window,
			WA_InnerWidth, &width,
			WA_InnerHeight, &height,
			TAG_END);

		BitMap* bm = IP96->p96AllocBitMap(width, height, m_bpp,
			BMF_USERPRIVATE|BMF_CLEAR, 0, m_ftype);
		if ( bm == 0 )
		{
			return false;
		}

		int8u* buf = reinterpret_cast<int8u*>(
			IP96->p96GetBitMapAttr(bm, P96BMA_MEMORY));
		int bpr = IP96->p96GetBitMapAttr(bm, P96BMA_BYTESPERROW);
		int stride = (m_flip_y) ? -bpr : bpr;

		m_support.rbuf_window().attach(buf, width, height, stride);

		if ( m_bitmap != 0 )
		{
			IP96->p96FreeBitMap(m_bitmap);
			m_bitmap = 0;
		}

		m_bitmap = bm;
		m_width = width;
		m_height = height;

		return true;
	}

	//------------------------------------------------------------------------
	platform_support::platform_support(pix_format_e format, bool flip_y) :
		m_specific(new platform_specific(*this, format, flip_y)),
		m_format(format),
		m_bpp(m_specific->m_bpp),
		m_window_flags(0),
		m_wait_mode(true),
		m_flip_y(flip_y),
		m_initial_width(10),
		m_initial_height(10)
	{
		std::strncpy(m_caption, "Anti-Grain Geometry", 256);
	}

	//------------------------------------------------------------------------
	platform_support::~platform_support()
	{
		delete m_specific;
	}

	//------------------------------------------------------------------------
	void platform_support::caption(const char* cap)
	{
		std::strncpy(m_caption, cap, 256);
		if ( m_specific->m_window != 0 )
		{
			const char* ignore = reinterpret_cast<const char*>(-1);
			IIntuition->SetWindowAttr(m_specific->m_window,
				WA_Title, m_caption, sizeof(char*));
		}
	}

	//------------------------------------------------------------------------
	void platform_support::start_timer()
	{
		timeval tv;
		gettimeofday(&tv, 0);
		m_specific->m_start_time = tv.tv_secs + tv.tv_micro/1e6;
	}

	//------------------------------------------------------------------------
	double platform_support::elapsed_time() const
	{
		timeval tv;
		gettimeofday(&tv, 0);
		double end_time = tv.tv_secs + tv.tv_micro/1e6;

		double elasped_seconds = end_time - m_specific->m_start_time;
		double elasped_millis = elasped_seconds*1e3;

		return elasped_millis;
	}

	//------------------------------------------------------------------------
	void* platform_support::raw_display_handler()
	{
		return 0;	// Not available.
	}

	//------------------------------------------------------------------------
	void platform_support::message(const char* msg)
	{
		APTR req = IIntuition->NewObject(RequesterClass, 0,
			REQ_TitleText, "Anti-Grain Geometry",
			REQ_Image, REQIMAGE_INFO,
			REQ_BodyText, msg,
			REQ_GadgetText, "_Ok",
			TAG_END);
		if ( req == 0 )
		{
			IDOS->Printf("Message: %s\n", msg);
			return;
		}

		orRequest reqmsg;
		reqmsg.MethodID = RM_OPENREQ;
		reqmsg.or_Attrs = 0;
		reqmsg.or_Window = m_specific->m_window;
		reqmsg.or_Screen = 0;
		
		IIntuition->IDoMethodA(reinterpret_cast<Object*>(req),
			reinterpret_cast<Msg>(&reqmsg));
		IIntuition->DisposeObject(req);
	}

	//------------------------------------------------------------------------
	bool platform_support::init(unsigned width, unsigned height,
		unsigned flags)
	{
		if( m_specific->m_ftype == RGBFB_NONE )
		{
			message("Unsupported mode requested.");
			return false;
		}

		m_window_flags = flags;

		m_specific->m_idcmp_hook = reinterpret_cast<Hook*>(
			IExec->AllocSysObjectTags(ASOT_HOOK,
				ASOHOOK_Entry, handle_idcmp,
				ASOHOOK_Data, this,
				TAG_END));
		if ( m_specific->m_idcmp_hook == 0 )
		{
			return false;
		}

		m_specific->m_window_obj = IIntuition->NewObject(WindowClass, 0,
				WA_Title, m_caption,
				WA_AutoAdjustDClip, TRUE,
				WA_InnerWidth, width,
				WA_InnerHeight, height,
				WA_Activate, TRUE,
				WA_SmartRefresh, TRUE,
				WA_NoCareRefresh, TRUE,
				WA_CloseGadget, TRUE,
				WA_DepthGadget, TRUE,
				WA_SizeGadget, (flags & agg::window_resize) ? TRUE : FALSE,
				WA_DragBar, TRUE,
				WA_AutoAdjust, TRUE,
				WA_ReportMouse, TRUE,
				WA_RMBTrap, TRUE,
				WA_MouseQueue, 1,
				WA_IDCMP,
					IDCMP_NEWSIZE |
					IDCMP_MOUSEBUTTONS |
					IDCMP_MOUSEMOVE |
					IDCMP_RAWKEY |
					IDCMP_INTUITICKS,
				WINDOW_IDCMPHook, m_specific->m_idcmp_hook,
				WINDOW_IDCMPHookBits,
					IDCMP_MOUSEBUTTONS |
					IDCMP_MOUSEMOVE |
					IDCMP_RAWKEY,
				TAG_END);
		if ( m_specific->m_window_obj == 0 )
		{
			return false;
		}

		Object* obj = reinterpret_cast<Object*>(m_specific->m_window_obj);
		m_specific->m_window =
			reinterpret_cast<Window*>(IIntuition->IDoMethod(obj, WM_OPEN));
		if ( m_specific->m_window == 0 )
		{
			return false;
		}

		RGBFTYPE ftype = static_cast<RGBFTYPE>(IP96->p96GetBitMapAttr(
			m_specific->m_window->RPort->BitMap, P96BMA_RGBFORMAT));

		switch ( ftype )
		{
		case RGBFB_A8R8G8B8:
		case RGBFB_B8G8R8A8:
		case RGBFB_R5G6B5PC:
			break;
		default:
			message("Unsupported screen mode.\n");
			return false;
		}

		if ( !m_specific->make_bitmap() )
		{
			return false;
		}

		m_initial_width = width;
		m_initial_height = height;

		on_init();
		on_resize(width, height);
		force_redraw();

		return true;
	}

	//------------------------------------------------------------------------
	int platform_support::run()
	{
		uint32 window_mask = 0;
		IIntuition->GetAttr(WINDOW_SigMask, m_specific->m_window_obj,
			&window_mask);
		uint32 wait_mask = window_mask | SIGBREAKF_CTRL_C;

		bool done = false;

		while ( !done )
		{
			uint32 sig_mask = IExec->Wait(wait_mask);
			if ( sig_mask & SIGBREAKF_CTRL_C )
			{
				done = true;
			}
			else
			{
				done = m_specific->handle_input();
			}
		}

		return 0;
	}

	//------------------------------------------------------------------------
	const char* platform_support::img_ext() const
	{
		return ".bmp";
	}

	//------------------------------------------------------------------------
	const char* platform_support::full_file_name(const char* file_name)
	{
		return file_name;
	}

	//------------------------------------------------------------------------
	bool platform_support::load_img(unsigned idx, const char* file)
	{
		if ( idx < max_images )
		{
			static char fn[1024];
			std::strncpy(fn, file, 1024);
			int len = std::strlen(fn);
			if ( len < 4 || std::strcmp(fn + len - 4, ".bmp") != 0 )
			{
				std::strncat(fn, ".bmp", 1024);
			}

			return m_specific->load_img(fn, idx, &m_rbuf_img[idx]);
		}

		return false;
	}

	//------------------------------------------------------------------------
	bool platform_support::save_img(unsigned idx, const char* file)
	{
		message("Not supported");
		return false;
	}

	//------------------------------------------------------------------------
	bool platform_support::create_img(unsigned idx, unsigned width,
		unsigned height)
	{
		if ( idx < max_images )
		{
			if ( width == 0 )
			{
				width = m_specific->m_width;
			}

			if ( height == 0 )
			{
				height = m_specific->m_height;
			}

			return m_specific->create_img(idx, &m_rbuf_img[idx], width,
				height);
		}

		return false;
	}

	//------------------------------------------------------------------------
	void platform_support::force_redraw()
	{
		on_draw();
		update_window();
	}

	//------------------------------------------------------------------------
	void platform_support::update_window()
	{
		// Note this function does automatic color conversion.
		IGraphics->BltBitMapRastPort(m_specific->m_bitmap, 0, 0,
			m_specific->m_window->RPort, m_specific->m_window->BorderLeft,
			m_specific->m_window->BorderTop, m_specific->m_width,
			m_specific->m_height, ABC|ABNC);
	}

	//------------------------------------------------------------------------
	void platform_support::on_init() {}
	void platform_support::on_resize(int sx, int sy) {}
	void platform_support::on_idle() {}
	void platform_support::on_mouse_move(int x, int y, unsigned flags) {}
	void platform_support::on_mouse_button_down(int x, int y, unsigned flags) {}
	void platform_support::on_mouse_button_up(int x, int y, unsigned flags) {}
	void platform_support::on_key(int x, int y, unsigned key, unsigned flags) {}
	void platform_support::on_ctrl_change() {}
	void platform_support::on_draw() {}
	void platform_support::on_post_draw(void* raw_handler) {}

	//------------------------------------------------------------------------
	void handle_idcmp(Hook* hook, APTR obj, IntuiMessage* msg)
	{
		platform_support* app =
			reinterpret_cast<platform_support*>(hook->h_Data);
		Window* window = app->m_specific->m_window;

		int16 x = msg->MouseX - window->BorderLeft;

		int16 y = 0;
		if ( app->flip_y() )
		{
			y = window->Height - window->BorderBottom - msg->MouseY;
		}
		else
		{
			y = msg->MouseY - window->BorderTop;
		}

		switch ( msg->Class )
		{
		case IDCMP_MOUSEBUTTONS:
			if ( msg->Code & IECODE_UP_PREFIX )
			{
				if ( msg->Code == SELECTUP )
				{
					app->m_specific->m_input_flags = mouse_left;
					app->m_specific->m_dragging = false;
				}
				else if ( msg->Code == MENUUP )
				{
					app->m_specific->m_input_flags = mouse_right;
					app->m_specific->m_dragging = false;
				}
				else
				{
					return;
				}


				if ( app->m_ctrls.on_mouse_button_up(x, y) )
				{
					app->on_ctrl_change();
					app->force_redraw();
				}

				app->on_mouse_button_up(x, y, app->m_specific->m_input_flags);
			}
			else
			{
				if ( msg->Code == SELECTDOWN )
				{
					app->m_specific->m_input_flags = mouse_left;
					app->m_specific->m_dragging = true;
				}
				else if ( msg->Code == MENUDOWN )
				{
					app->m_specific->m_input_flags = mouse_right;
					app->m_specific->m_dragging = true;
				}
				else
				{
					return;
				}

				app->m_ctrls.set_cur(x, y);
				if ( app->m_ctrls.on_mouse_button_down(x, y) )
				{
					app->on_ctrl_change();
					app->force_redraw();
				}
				else
				{
					if ( app->m_ctrls.in_rect(x, y) )
					{
						if ( app->m_ctrls.set_cur(x, y) )
						{
							app->on_ctrl_change();
							app->force_redraw();
						}
					}
					else
					{
						app->on_mouse_button_down(x, y,
							app->m_specific->m_input_flags);
					}
				}
			}
			break;
		case IDCMP_MOUSEMOVE:
			if ( app->m_specific->m_dragging )  {
				if ( app->m_ctrls.on_mouse_move(x, y,
					 app->m_specific->m_input_flags & mouse_left) != 0 )
				{
					app->on_ctrl_change();
					app->force_redraw();
				}
				else
				{
					if ( !app->m_ctrls.in_rect(x, y) )
					{
						app->on_mouse_move(x, y,
							app->m_specific->m_input_flags);
					}
				}
			}
			break;
		case IDCMP_RAWKEY:
		{
			static InputEvent ie = { 0 };
			ie.ie_Class = IECLASS_RAWKEY;
			ie.ie_Code = msg->Code;
			ie.ie_Qualifier = msg->Qualifier;

			static const unsigned BUF_SIZE = 16;
			static char key_buf[BUF_SIZE];
			int16 num_chars = IKeymap->MapRawKey(&ie, key_buf, BUF_SIZE, 0);

			uint32 code = 0x00000000;
			switch ( num_chars )
			{
			case 1:
				code = key_buf[0];
				break;
			case 2:
				code = key_buf[0]<<8 | key_buf[1];
				break;
			case 3:
				code = key_buf[0]<<16 | key_buf[1]<<8 | key_buf[2];
				break;
			}

			uint16 key_code = 0;

			if ( num_chars == 1 )
			{
				if ( code >= IECODE_ASCII_FIRST && code <= IECODE_ASCII_LAST )
				{
					key_code = code;
				}
			}

			if ( key_code == 0 )
			{
				switch ( code )
				{
				case 0x00000008: key_code = key_backspace;	break;
				case 0x00000009: key_code = key_tab;		break;
				case 0x0000000D: key_code = key_return;		break;
				case 0x0000001B: key_code = key_escape;		break;
				case 0x0000007F: key_code = key_delete;		break;
				case 0x00009B41:
				case 0x00009B54: key_code = key_up;			break;
				case 0x00009B42:
				case 0x00009B53: key_code = key_down;		break;
				case 0x00009B43:
				case 0x009B2040: key_code = key_right;		break;
				case 0x00009B44:
				case 0x009B2041: key_code = key_left;		break;
				case 0x009B307E: key_code = key_f1;			break;
				case 0x009B317E: key_code = key_f2;			break;
				case 0x009B327E: key_code = key_f3;			break;
				case 0x009B337E: key_code = key_f4;			break;
				case 0x009B347E: key_code = key_f5;			break;
				case 0x009B357E: key_code = key_f6;			break;
				case 0x009B367E: key_code = key_f7;			break;
				case 0x009B377E: key_code = key_f8;			break;
				case 0x009B387E: key_code = key_f9;			break;
				case 0x009B397E: key_code = key_f10;		break;
				case 0x009B3F7E: key_code = key_scrollock;	break;
				}
			}

			if ( ie.ie_Code & IECODE_UP_PREFIX )
			{
				if ( app->m_specific->m_last_key != 0 )
				{
					bool left = (key_code == key_left) ? true : false;
					bool right = (key_code == key_right) ? true : false;
					bool down = (key_code == key_down) ? true : false;
					bool up = (key_code == key_up) ? true : false;

					if ( app->m_ctrls.on_arrow_keys(left, right, down, up) )
					{
						app->on_ctrl_change();
						app->force_redraw();
					}
					else
					{
						app->on_key(x, y, app->m_specific->m_last_key, 0);
					}

					app->m_specific->m_last_key = 0;
				}
			}
			else
			{
				app->m_specific->m_last_key = key_code;
			}
			break;
		}
		default:
			break;
		}
	}
}

//----------------------------------------------------------------------------
int agg_main(int argc, char* argv[]);
bool open_libs();
void close_libs();

//----------------------------------------------------------------------------
bool open_libs()
{
	DataTypesBase = IExec->OpenLibrary("datatypes.library", 51);
	GraphicsBase = IExec->OpenLibrary("graphics.library", 51);
	IntuitionBase = IExec->OpenLibrary("intuition.library", 51);
	KeymapBase = IExec->OpenLibrary("keymap.library", 51);
	P96Base = IExec->OpenLibrary("Picasso96API.library", 2);

	IDataTypes = reinterpret_cast<DataTypesIFace*>(
		IExec->GetInterface(DataTypesBase, "main", 1, 0));
	IGraphics = reinterpret_cast<GraphicsIFace*>(
		IExec->GetInterface(GraphicsBase, "main", 1, 0));
	IIntuition = reinterpret_cast<IntuitionIFace*>(
		IExec->GetInterface(IntuitionBase, "main", 1, 0));
	IKeymap = reinterpret_cast<KeymapIFace*>(
		IExec->GetInterface(KeymapBase, "main", 1, 0));
	IP96 = reinterpret_cast<P96IFace*>(
		IExec->GetInterface(P96Base, "main", 1, 0));

	if ( IDataTypes == 0 ||
		 IGraphics == 0 ||
		 IIntuition == 0 ||
		 IKeymap == 0 ||
		 IP96 == 0 )
	{
		close_libs();
		return false;
	}
	else
	{
		return true;
	}
}

//----------------------------------------------------------------------------
void close_libs()
{
	IExec->DropInterface(reinterpret_cast<Interface*>(IP96));
	IExec->DropInterface(reinterpret_cast<Interface*>(IKeymap));
	IExec->DropInterface(reinterpret_cast<Interface*>(IIntuition));
	IExec->DropInterface(reinterpret_cast<Interface*>(IGraphics));
	IExec->DropInterface(reinterpret_cast<Interface*>(IDataTypes));

	IExec->CloseLibrary(P96Base);
	IExec->CloseLibrary(KeymapBase);
	IExec->CloseLibrary(IntuitionBase);
	IExec->CloseLibrary(GraphicsBase);
	IExec->CloseLibrary(DataTypesBase);
}

//----------------------------------------------------------------------------
int main(int argc, char* argv[])
{
	if ( !open_libs() )  {
		IDOS->Printf("Can't open libraries.\n");
		return -1;
	}

	ClassLibrary* requester =
		IIntuition->OpenClass("requester.class", 51, &RequesterClass);
	ClassLibrary* window =
		IIntuition->OpenClass("window.class", 51, &WindowClass);
	if ( requester == 0 || window == 0 )
	{
		IDOS->Printf("Can't open classes.\n");
		IIntuition->CloseClass(requester);
		IIntuition->CloseClass(window);
		close_libs();
		return -1;
	}

	int rc = agg_main(argc, argv);

	IIntuition->CloseClass(window);
	IIntuition->CloseClass(requester);
	close_libs();

	return rc;
}

#endif

//----------------------------------------------------------------------------
// Anti-Grain Geometry - Version 2.4
// Copyright (C) 2002-2005 Maxim Shemanarev (http://www.antigrain.com)
//
// Permission to copy, use, modify, sell and distribute this software 
// is granted provided this copyright notice appears in all copies. 
// This software is provided "as is" without express or implied
// warranty, and with no claim as to its suitability for any purpose.
//
//----------------------------------------------------------------------------
// Contact: superstippi@gmx.de
//----------------------------------------------------------------------------
//
// class platform_support
//
//----------------------------------------------------------------------------

#ifdef PLATFORM_BEOS

#include <new>
#include <stdio.h>

#include <Alert.h>
#include <Application.h>
#include <Bitmap.h>
#include <Message.h>
#include <MessageRunner.h>
#include <Messenger.h>
#include <Path.h>
#include <Roster.h>
#include <TranslationUtils.h>
#include <View.h>
#include <Window.h>

#include <string.h>
#include <agg24/agg_platform_support.h>
#include <agg24/agg_color_conv_rgb8.h>

using std::nothrow;


static void
attach_buffer_to_BBitmap(agg::rendering_buffer& buffer, BBitmap* bitmap, bool flipY)
{
    uint8* bits = (uint8*)bitmap->Bits();
    uint32 width = bitmap->Bounds().IntegerWidth() + 1;
    uint32 height = bitmap->Bounds().IntegerHeight() + 1;
    int32 bpr = bitmap->BytesPerRow();
    if (flipY) {
// XXX: why don't I have to do this?!?
//        bits += bpr * (height - 1);
        bpr = -bpr;
    }
    buffer.attach(bits, width, height, bpr);
}


static color_space
pix_format_to_color_space(agg::pix_format_e format)
{
    color_space bitmapFormat = B_NO_COLOR_SPACE;
    switch (format) {
        case agg::pix_format_rgb555:

            bitmapFormat = B_RGB15;
            break;

        case agg::pix_format_rgb565:

            bitmapFormat = B_RGB16;
            break;

        case agg::pix_format_rgb24:
        case agg::pix_format_bgr24:

            bitmapFormat = B_RGB24;
            break;

        case agg::pix_format_rgba32:
        case agg::pix_format_argb32:
        case agg::pix_format_abgr32:
        case agg::pix_format_bgra32:

            bitmapFormat = B_RGBA32;
            break;
    }
    return bitmapFormat;
}


// #pragma mark -


class AGGView : public BView {
 public:
                            AGGView(BRect frame, agg::platform_support* agg,
                                    agg::pix_format_e format, bool flipY);
    virtual                 ~AGGView();

    virtual void            AttachedToWindow();
    virtual void            DetachedFromWindow();

    virtual void            MessageReceived(BMessage* message);
    virtual void            Draw(BRect updateRect);
    virtual void            FrameResized(float width, float height);

    virtual void            KeyDown(const char* bytes, int32 numBytes);

    virtual void            MouseDown(BPoint where);
    virtual void            MouseMoved(BPoint where, uint32 transit,
                               const BMessage* dragMesage);
    virtual void            MouseUp(BPoint where);

            BBitmap*        Bitmap() const;

            uint8           LastKeyDown() const;
            uint32          MouseButtons();

            void            Update();
            void            ForceRedraw();

            unsigned        GetKeyFlags();

 private:
    BBitmap*                fBitmap;
    agg::pix_format_e       fFormat;
    bool                    fFlipY;

    agg::platform_support*  fAGG;

    uint32                  fMouseButtons;
    int32                   fMouseX;
    int32                   fMouseY;

    uint8                   fLastKeyDown;

    bool                    fRedraw;

    BMessageRunner*         fPulse;
    bigtime_t               fLastPulse;
    bool                    fEnableTicks;
};

AGGView::AGGView(BRect frame,
                 agg::platform_support* agg,
                 agg::pix_format_e format,
                 bool flipY)
    : BView(frame, "AGG View", B_FOLLOW_ALL,
            B_FRAME_EVENTS | B_WILL_DRAW),
      fFormat(format),
      fFlipY(flipY),

      fAGG(agg),

      fMouseButtons(0),
      fMouseX(-1),
      fMouseY(-1),
      
      fLastKeyDown(0),

      fRedraw(true),

      fPulse(NULL),
      fLastPulse(0),
      fEnableTicks(true)
{
    SetViewColor(B_TRANSPARENT_32_BIT);
    
    frame.OffsetTo(0.0, 0.0);
    fBitmap = new BBitmap(frame, 0, pix_format_to_color_space(fFormat));
    if (fBitmap->IsValid()) {
        attach_buffer_to_BBitmap(fAGG->rbuf_window(), fBitmap, fFlipY);
    } else {
        delete fBitmap;
        fBitmap = NULL;
    }
}


AGGView::~AGGView()
{
    delete fBitmap;
    delete fPulse;
}


void
AGGView::AttachedToWindow()
{
    BMessage message('tick');
    BMessenger target(this, Looper());
    delete fPulse;
//    BScreen screen;
//    TODO: calc screen retrace
    fPulse = new BMessageRunner(target, &message, 40000);

    // make sure we call this once
    fAGG->on_resize(Bounds().IntegerWidth() + 1,
                    Bounds().IntegerHeight() + 1);
    MakeFocus();
}


void
AGGView::DetachedFromWindow()
{
    delete fPulse;
    fPulse = NULL;
}


void
AGGView::MessageReceived(BMessage* message)
{
    bigtime_t now = system_time();
    switch (message->what) {
        case 'tick':
            // drop messages that have piled up
            if (/*now - fLastPulse > 30000*/fEnableTicks) {
                fLastPulse = now;
                if (!fAGG->wait_mode())
                    fAGG->on_idle();
                Window()->PostMessage('entk', this);
                fEnableTicks = false;
            } else {
//                printf("dropping tick message (%lld)\n", now - fLastPulse);
            }
            break;
        case 'entk':
            fEnableTicks = true;
            if (now - fLastPulse > 30000) {
                fLastPulse = now;
                if (!fAGG->wait_mode())
                    fAGG->on_idle();
            }
            break;
        default:
            BView::MessageReceived(message);
            break;
    }
}


void
AGGView::Draw(BRect updateRect)
{
    if (fBitmap) {
        if (fRedraw) {
            fAGG->on_draw();
            fRedraw = false;
        }
        if (fFormat == agg::pix_format_bgra32) {
            DrawBitmap(fBitmap, updateRect, updateRect);
        } else {
            BBitmap* bitmap = new BBitmap(fBitmap->Bounds(), 0, B_RGBA32);

            agg::rendering_buffer rbufSrc;
            attach_buffer_to_BBitmap(rbufSrc, fBitmap, false);

            agg::rendering_buffer rbufDst;
            attach_buffer_to_BBitmap(rbufDst, bitmap, false);

            switch(fFormat) {
                case agg::pix_format_rgb555:
                    agg::color_conv(&rbufDst, &rbufSrc,
                                    agg::color_conv_rgb555_to_bgra32());
                    break;
                case agg::pix_format_rgb565:
                    agg::color_conv(&rbufDst, &rbufSrc,
                                    agg::color_conv_rgb565_to_bgra32());
                    break;
                case agg::pix_format_rgb24:
                    agg::color_conv(&rbufDst, &rbufSrc,
                                    agg::color_conv_rgb24_to_bgra32());
                    break;
                case agg::pix_format_bgr24:
                    agg::color_conv(&rbufDst, &rbufSrc,
                                    agg::color_conv_bgr24_to_bgra32());
                    break;
                case agg::pix_format_rgba32:
                    agg::color_conv(&rbufDst, &rbufSrc,
                                    agg::color_conv_rgba32_to_bgra32());
                    break;
                case agg::pix_format_argb32:
                    agg::color_conv(&rbufDst, &rbufSrc,
                                    agg::color_conv_argb32_to_bgra32());
                    break;
                case agg::pix_format_abgr32:
                    agg::color_conv(&rbufDst, &rbufSrc,
                                    agg::color_conv_abgr32_to_bgra32());
                    break;
                case agg::pix_format_bgra32:
                    agg::color_conv(&rbufDst, &rbufSrc,
                                    agg::color_conv_bgra32_to_bgra32());
                    break;
            }
            DrawBitmap(bitmap, updateRect, updateRect);
            delete bitmap;
        }
    } else {
        FillRect(updateRect);
    }
}


void
AGGView::FrameResized(float width, float height)
{
    BRect r(0.0, 0.0, width, height);
    BBitmap* bitmap = new BBitmap(r, 0, pix_format_to_color_space(fFormat));
    if (bitmap->IsValid()) {
        delete fBitmap;
        fBitmap = bitmap;
           attach_buffer_to_BBitmap(fAGG->rbuf_window(), fBitmap, fFlipY);

        fAGG->trans_affine_resizing((int)width + 1,
                                    (int)height + 1);

        // pass the event on to AGG
        fAGG->on_resize((int)width + 1, (int)height + 1);
        
        fRedraw = true;
        Invalidate();
    } else
        delete bitmap;
}


void
AGGView::KeyDown(const char* bytes, int32 numBytes)
{
    if (bytes && numBytes > 0) {
        fLastKeyDown = bytes[0];

        bool left  = false;
        bool up    = false;
        bool right = false;
        bool down  = false;

        switch (fLastKeyDown) {

            case B_LEFT_ARROW:
                left = true;
                break;

            case B_UP_ARROW:
                up = true;
                break;

            case B_RIGHT_ARROW:
                right = true;
                break;

            case B_DOWN_ARROW:
                down = true;
                break;
        }

/*            case key_f2:                        
fAGG->copy_window_to_img(agg::platform_support::max_images - 1);
fAGG->save_img(agg::platform_support::max_images - 1, "screenshot");
break;
}*/


        if (fAGG->m_ctrls.on_arrow_keys(left, right, down, up)) {
            fAGG->on_ctrl_change();
            fAGG->force_redraw();
        } else {
            fAGG->on_key(fMouseX, fMouseY, fLastKeyDown, GetKeyFlags());
        }
//        fAGG->on_key(fMouseX, fMouseY, fLastKeyDown, GetKeyFlags());

    }
}


void
AGGView::MouseDown(BPoint where)
{
    BMessage* currentMessage = Window()->CurrentMessage();
    if (currentMessage) {
        if (currentMessage->FindInt32("buttons", (int32*)&fMouseButtons) < B_OK)
            fMouseButtons = B_PRIMARY_MOUSE_BUTTON;
    } else
        fMouseButtons = B_PRIMARY_MOUSE_BUTTON;

    fMouseX = (int)where.x;
    fMouseY = fFlipY ? (int)(Bounds().Height() - where.y) : (int)where.y;

    // pass the event on to AGG
    if (fMouseButtons == B_PRIMARY_MOUSE_BUTTON) {
        // left mouse button -> see if to handle in controls
        fAGG->m_ctrls.set_cur(fMouseX, fMouseY);
        if (fAGG->m_ctrls.on_mouse_button_down(fMouseX, fMouseY)) {
            fAGG->on_ctrl_change();
            fAGG->force_redraw();
        } else {
            if (fAGG->m_ctrls.in_rect(fMouseX, fMouseY)) {
                if (fAGG->m_ctrls.set_cur(fMouseX, fMouseY)) {
                    fAGG->on_ctrl_change();
                    fAGG->force_redraw();
                }
            } else {
                fAGG->on_mouse_button_down(fMouseX, fMouseY, GetKeyFlags());
            }
        }
    } else if (fMouseButtons & B_SECONDARY_MOUSE_BUTTON) {
        // right mouse button -> simple
        fAGG->on_mouse_button_down(fMouseX, fMouseY, GetKeyFlags());
    }
    SetMouseEventMask(B_POINTER_EVENTS, B_LOCK_WINDOW_FOCUS);
}


void
AGGView::MouseMoved(BPoint where, uint32 transit, const BMessage* dragMesage)
{
    // workarround missed mouse up events
    // (if we react too slowly, app_server might have dropped events)
    BMessage* currentMessage = Window()->CurrentMessage();
    int32 buttons = 0;
    if (currentMessage->FindInt32("buttons", &buttons) < B_OK) {
        buttons = 0;
    }
    if (!buttons)
        MouseUp(where);

    fMouseX = (int)where.x;
    fMouseY = fFlipY ? (int)(Bounds().Height() - where.y) : (int)where.y;

    // pass the event on to AGG
    if (fAGG->m_ctrls.on_mouse_move(fMouseX, fMouseY,
                                    (GetKeyFlags() & agg::mouse_left) != 0)) {
        fAGG->on_ctrl_change();
        fAGG->force_redraw();
    } else {
        if (!fAGG->m_ctrls.in_rect(fMouseX, fMouseY)) {
            fAGG->on_mouse_move(fMouseX, fMouseY, GetKeyFlags());
        }
    }
}


void
AGGView::MouseUp(BPoint where)
{
    fMouseX = (int)where.x;
    fMouseY = fFlipY ? (int)(Bounds().Height() - where.y) : (int)where.y;

    // pass the event on to AGG
    if (fMouseButtons == B_PRIMARY_MOUSE_BUTTON) {
        fMouseButtons = 0;

        if (fAGG->m_ctrls.on_mouse_button_up(fMouseX, fMouseY)) {
            fAGG->on_ctrl_change();
            fAGG->force_redraw();
        }
        fAGG->on_mouse_button_up(fMouseX, fMouseY, GetKeyFlags());
    } else if (fMouseButtons == B_SECONDARY_MOUSE_BUTTON) {
        fMouseButtons = 0;

        fAGG->on_mouse_button_up(fMouseX, fMouseY, GetKeyFlags());
    }
}


BBitmap*
AGGView::Bitmap() const
{
    return fBitmap;
}


uint8
AGGView::LastKeyDown() const
{
    return fLastKeyDown;
}


uint32
AGGView::MouseButtons()
{
    uint32 buttons = 0;
    if (LockLooper()) {
        buttons = fMouseButtons;
        UnlockLooper();
    }
    return buttons;
}


void
AGGView::Update()
{
    // trigger display update
    if (LockLooper()) {
        Invalidate();
        UnlockLooper();
    }
}


void
AGGView::ForceRedraw()
{
    // force a redraw (fRedraw = true;)
    // and trigger display update
    if (LockLooper()) {
        fRedraw = true;
        Invalidate();
        UnlockLooper();
    }
}


unsigned
AGGView::GetKeyFlags()
{
    uint32 buttons = fMouseButtons;
    uint32 mods = modifiers();
    unsigned flags = 0;
    if (buttons & B_PRIMARY_MOUSE_BUTTON)   flags |= agg::mouse_left;
    if (buttons & B_SECONDARY_MOUSE_BUTTON) flags |= agg::mouse_right;
    if (mods & B_SHIFT_KEY)                 flags |= agg::kbd_shift;
    if (mods & B_COMMAND_KEY)               flags |= agg::kbd_ctrl;
    return flags;
}

// #pragma mark -


class AGGWindow : public BWindow {
 public:
                    AGGWindow()
                    : BWindow(BRect(-50.0, -50.0, -10.0, -10.0),
                              "AGG Application", B_TITLED_WINDOW, B_ASYNCHRONOUS_CONTROLS)
                    {
                    }

    virtual bool    QuitRequested()
                    {
                        be_app->PostMessage(B_QUIT_REQUESTED);
                        return true;
                    }

            bool    Init(BRect frame, agg::platform_support* agg, agg::pix_format_e format,
                              bool flipY, uint32 flags)
                    {
                        MoveTo(frame.LeftTop());
                        ResizeTo(frame.Width(), frame.Height());

                        SetFlags(flags);

                        frame.OffsetTo(0.0, 0.0);
                        fView = new AGGView(frame, agg, format, flipY);
                        AddChild(fView);

                        return fView->Bitmap() != NULL;
                    }


        AGGView*    View() const
                    {
                        return fView;
                    }
 private:
    AGGView*        fView;
};

// #pragma mark -


class AGGApplication : public BApplication {
 public:
                    AGGApplication()
                    : BApplication("application/x-vnd.AGG-AGG")
                    {
                        fWindow = new AGGWindow();
                    }

    virtual void    ReadyToRun()
                    {
                        if (fWindow) {
                            fWindow->Show();
                        }
                    }

    virtual bool    Init(agg::platform_support* agg, int width, int height,
                         agg::pix_format_e format, bool flipY, uint32 flags)
                    {
                        BRect r(50.0, 50.0,
                                50.0 + width - 1.0,
                                50.0 + height - 1.0);
                        uint32 windowFlags = B_ASYNCHRONOUS_CONTROLS;
                        if (!(flags & agg::window_resize))
                            windowFlags |= B_NOT_RESIZABLE;

                        return fWindow->Init(r, agg, format, flipY, windowFlags);;
                    }


        AGGWindow*  Window() const
                    {
                        return fWindow;
                    }

 private:
    AGGWindow*      fWindow;
};


// #pragma mark -


namespace agg
{

class platform_specific {
 public:
                    platform_specific(agg::platform_support* agg,
                                      agg::pix_format_e format, bool flip_y)
                        : fAGG(agg),
                          fApp(NULL),
                          fFormat(format),
                          fFlipY(flip_y),
                          fTimerStart(system_time())
                    {
                        memset(fImages, 0, sizeof(fImages));
                        fApp = new AGGApplication();
                        fAppPath[0] = 0;
                        // figure out where we're running from
                        app_info info;
                        status_t ret = fApp->GetAppInfo(&info);
                        if (ret >= B_OK) {
                            BPath path(&info.ref);
                            ret = path.InitCheck();
                            if (ret >= B_OK) {
                                ret = path.GetParent(&path);
                                if (ret >= B_OK) {
                                    sprintf(fAppPath, "%s", path.Path());
                                } else {
                                    fprintf(stderr, "getting app parent folder failed: %s\n", strerror(ret));
                                }
                            } else {
                                fprintf(stderr, "making app path failed: %s\n", strerror(ret));
                            }
                        } else {
                            fprintf(stderr, "GetAppInfo() failed: %s\n", strerror(ret));
                        }
                    }
                    ~platform_specific()
                    {
                        for (int32 i = 0; i < agg::platform_support::max_images; i++)
                            delete fImages[i];
                        delete fApp;
                    }

    bool            Init(int width, int height, unsigned flags)
                    {
                        return fApp->Init(fAGG, width, height, fFormat, fFlipY, flags);
                    }

    int             Run()
                    {
                        status_t ret = B_NO_INIT;
                        if (fApp) {
                            fApp->Run();
                            ret = B_OK;
                        }
                        return ret;
                    }

    void            SetTitle(const char* title)
                    {
                        if (fApp && fApp->Window() && fApp->Window()->Lock()) {
                            fApp->Window()->SetTitle(title);
                            fApp->Window()->Unlock();
                        }
                    }
    void            StartTimer()
                    {
                        fTimerStart = system_time();
                    }
    double          ElapsedTime() const
                    {
                        return (system_time() - fTimerStart) / 1000.0;
                    }

    void            ForceRedraw()
                    {
                        fApp->Window()->View()->ForceRedraw();
                    }
    void            UpdateWindow()
                    {
                        fApp->Window()->View()->Update();
                    }


    agg::platform_support*  fAGG;
    AGGApplication*     fApp;
    agg::pix_format_e    fFormat;
    bool                fFlipY;
    bigtime_t           fTimerStart;
    BBitmap*            fImages[agg::platform_support::max_images];

    char                fAppPath[B_PATH_NAME_LENGTH];
    char                fFilePath[B_PATH_NAME_LENGTH];
};


    //------------------------------------------------------------------------
    platform_support::platform_support(pix_format_e format, bool flip_y) :
        m_specific(new platform_specific(this, format, flip_y)),
        m_format(format),
        m_bpp(32/*m_specific->m_bpp*/),
        m_window_flags(0),
        m_wait_mode(true),
        m_flip_y(flip_y),
        m_initial_width(10),
        m_initial_height(10)
    {
        strcpy(m_caption, "Anti-Grain Geometry Application");
    }


    //------------------------------------------------------------------------
    platform_support::~platform_support()
    {
        delete m_specific;
    }

    //------------------------------------------------------------------------
    void platform_support::caption(const char* cap)
    {
        strcpy(m_caption, cap);
        m_specific->SetTitle(cap);
    }

    //------------------------------------------------------------------------
    void platform_support::start_timer()
    {
        m_specific->StartTimer();
    }

    //------------------------------------------------------------------------
    double platform_support::elapsed_time() const
    {
        return m_specific->ElapsedTime();
    }

    //------------------------------------------------------------------------
    void* platform_support::raw_display_handler()
    {
        // TODO: if we ever support BDirectWindow here, that would
        // be the frame buffer pointer with offset to the window top left
        return NULL;
    }

    //------------------------------------------------------------------------
    void platform_support::message(const char* msg)
    {
        BAlert* alert = new BAlert("AGG Message", msg, "Ok");
        alert->Go(/*NULL*/);
    }


    //------------------------------------------------------------------------
    bool platform_support::init(unsigned width, unsigned height, unsigned flags)
    {
        m_initial_width = width;
        m_initial_height = height;
        m_window_flags = flags;

        if (m_specific->Init(width, height, flags)) {
            on_init();
            return true;
        }

        return false;
    }


    //------------------------------------------------------------------------
    int platform_support::run()
    {
        return m_specific->Run();
    }


    //------------------------------------------------------------------------
    const char* platform_support::img_ext() const { return ".ppm"; }


    const char* platform_support::full_file_name(const char* file_name)
    {
        sprintf(m_specific->fFilePath, "%s/%s", m_specific->fAppPath, file_name);
        return m_specific->fFilePath;
    }


    //------------------------------------------------------------------------
    bool platform_support::load_img(unsigned idx, const char* file)
    {
        if (idx < max_images)
        {
            char path[B_PATH_NAME_LENGTH];
            sprintf(path, "%s/%s%s", m_specific->fAppPath, file, img_ext());
            BBitmap* transBitmap = BTranslationUtils::GetBitmap(path);
            if (transBitmap && transBitmap->IsValid()) {
                if(transBitmap->ColorSpace() != B_RGB32 && transBitmap->ColorSpace() != B_RGBA32) {
                    // ups we got a smart ass Translator making our live harder
                    delete transBitmap;
                    return false;
                }

                color_space format = B_RGB24;

                switch (m_format) {
                    case pix_format_gray8:
                        format = B_GRAY8;
                        break;
                    case pix_format_rgb555:
                        format = B_RGB15;
                        break;
                    case pix_format_rgb565:
                        format = B_RGB16;
                        break;
                    case pix_format_rgb24:
                        format = B_RGB24_BIG;
                        break;
                    case pix_format_bgr24:
                        format = B_RGB24;
                        break;
                    case pix_format_abgr32:
                    case pix_format_argb32:
                    case pix_format_bgra32:
                        format = B_RGB32;
                        break;
                    case pix_format_rgba32:
                        format = B_RGB32_BIG;
                        break;
                }
                BBitmap* bitmap = new (nothrow) BBitmap(transBitmap->Bounds(), 0, format);
                if (!bitmap || !bitmap->IsValid()) {
                    fprintf(stderr, "failed to allocate temporary bitmap!\n");
                    delete transBitmap;
                    delete bitmap;
                    return false;
                }

                delete m_specific->fImages[idx];

                rendering_buffer rbuf_tmp;
                attach_buffer_to_BBitmap(rbuf_tmp, transBitmap, m_flip_y);
        
                m_specific->fImages[idx] = bitmap;
        
                attach_buffer_to_BBitmap(m_rbuf_img[idx], bitmap, m_flip_y);
        
                rendering_buffer* dst = &m_rbuf_img[idx];

                switch(m_format)
                {
                case pix_format_gray8:
                    return false;
//                  color_conv(dst, &rbuf_tmp, color_conv_bgra32_to_gray8()); break;
                    break;
        
                case pix_format_rgb555:
                    color_conv(dst, &rbuf_tmp, color_conv_bgra32_to_rgb555()); break;
                    break;
        
                case pix_format_rgb565:
                    color_conv(dst, &rbuf_tmp, color_conv_bgra32_to_rgb565()); break;
                    break;
        
                case pix_format_rgb24:
                    color_conv(dst, &rbuf_tmp, color_conv_bgra32_to_rgb24()); break;
                    break;
        
                case pix_format_bgr24:
                    color_conv(dst, &rbuf_tmp, color_conv_bgra32_to_bgr24()); break;
                    break;
        
                case pix_format_abgr32:
                    color_conv(dst, &rbuf_tmp, color_conv_bgra32_to_abgr32()); break;
                    break;
        
                case pix_format_argb32:
                    color_conv(dst, &rbuf_tmp, color_conv_bgra32_to_argb32()); break;
                    break;
        
                case pix_format_bgra32:
                    color_conv(dst, &rbuf_tmp, color_conv_bgra32_to_bgra32()); break;
                    break;
        
                case pix_format_rgba32:
                    color_conv(dst, &rbuf_tmp, color_conv_bgra32_to_rgba32()); break;
                    break;
                }
                delete transBitmap;
                
                return true;

            } else {
                fprintf(stderr, "failed to load bitmap: '%s'\n", full_file_name(file));
            }
        }
        return false;
    }



    //------------------------------------------------------------------------
    bool platform_support::save_img(unsigned idx, const char* file)
    {
        // TODO: implement using BTranslatorRoster and friends
        return false;
    }



    //------------------------------------------------------------------------
    bool platform_support::create_img(unsigned idx, unsigned width, unsigned height)
    {
        if(idx < max_images)
        {
            if(width  == 0) width  = m_specific->fApp->Window()->View()->Bitmap()->Bounds().IntegerWidth() + 1;
            if(height == 0) height = m_specific->fApp->Window()->View()->Bitmap()->Bounds().IntegerHeight() + 1;
            BBitmap* bitmap = new BBitmap(BRect(0.0, 0.0, width - 1, height - 1), 0, B_RGBA32);;
            if (bitmap && bitmap->IsValid()) {
                delete m_specific->fImages[idx];
                m_specific->fImages[idx] = bitmap;
                attach_buffer_to_BBitmap(m_rbuf_img[idx], bitmap, m_flip_y);
                return true;
            } else {
                delete bitmap;
            }
        }
        return false;
    }


    //------------------------------------------------------------------------
    void platform_support::force_redraw()
    {
        m_specific->ForceRedraw();
    }



    //------------------------------------------------------------------------
    void platform_support::update_window()
    {
        m_specific->UpdateWindow();
    }


    //------------------------------------------------------------------------
    void platform_support::on_init() {}
    void platform_support::on_resize(int sx, int sy) {}
    void platform_support::on_idle() {}
    void platform_support::on_mouse_move(int x, int y, unsigned flags) {}
    void platform_support::on_mouse_button_down(int x, int y, unsigned flags) {}
    void platform_support::on_mouse_button_up(int x, int y, unsigned flags) {}
    void platform_support::on_key(int x, int y, unsigned key, unsigned flags) {}
    void platform_support::on_ctrl_change() {}
    void platform_support::on_draw() {}
    void platform_support::on_post_draw(void* raw_handler) {}
}






//----------------------------------------------------------------------------
int agg_main(int argc, char* argv[]);



int
main(int argc, char* argv[])
{
    return agg_main(argc, argv);
}


#endif

//----------------------------------------------------------------------------
// Anti-Grain Geometry - Version 2.4
// Copyright (C) 2002-2005 Maxim Shemanarev (http://www.antigrain.com)
//
// Permission to copy, use, modify, sell and distribute this software 
// is granted provided this copyright notice appears in all copies. 
// This software is provided "as is" without express or implied
// warranty, and with no claim as to its suitability for any purpose.
//
//----------------------------------------------------------------------------
// Contact: mcseem@antigrain.com
//          mcseemagg@yahoo.com
//          http://www.antigrain.com
//----------------------------------------------------------------------------
//
// class platform_support. SDL version.
//
//----------------------------------------------------------------------------

#ifdef USE_SDL

#include <string.h>
#include <agg24/agg_platform_support.h>
#include "SDL.h"
#include "SDL_byteorder.h"


namespace agg
{

    //------------------------------------------------------------------------
    class platform_specific
    {
    public:
        platform_specific(pix_format_e format, bool flip_y);
        ~platform_specific();

        pix_format_e  m_format;
        pix_format_e  m_sys_format;
        bool          m_flip_y;
        unsigned      m_bpp;
        unsigned      m_sys_bpp;
        unsigned      m_rmask;
        unsigned      m_gmask;
        unsigned      m_bmask;
        unsigned      m_amask;
        bool          m_update_flag;
        bool          m_resize_flag;
        bool          m_initialized;
        SDL_Surface*  m_surf_screen;
        SDL_Surface*  m_surf_window;
        SDL_Surface*  m_surf_img[platform_support::max_images];
        int           m_cur_x;
        int           m_cur_y;
	int          m_sw_start;
    };



    //------------------------------------------------------------------------
    platform_specific::platform_specific(pix_format_e format, bool flip_y) :
        m_format(format),
        m_sys_format(pix_format_undefined),
        m_flip_y(flip_y),
        m_bpp(0),
        m_sys_bpp(0),
        m_update_flag(true), 
        m_resize_flag(true),
        m_initialized(false),
        m_surf_screen(0),
        m_surf_window(0),
        m_cur_x(0),
        m_cur_y(0)
    {
        memset(m_surf_img, 0, sizeof(m_surf_img));

        switch(m_format)
        {
			case pix_format_gray8:
            m_bpp = 8;
            break;

        case pix_format_rgb565:
            m_rmask = 0xF800;
            m_gmask = 0x7E0;
            m_bmask = 0x1F;
            m_amask = 0;
            m_bpp = 16;
            break;

        case pix_format_rgb555:
            m_rmask = 0x7C00;
            m_gmask = 0x3E0;
            m_bmask = 0x1F;
            m_amask = 0;
            m_bpp = 16;
            break;
			
#if SDL_BYTEORDER == SDL_LIL_ENDIAN
        case pix_format_rgb24:
			m_rmask = 0xFF;
            m_gmask = 0xFF00;
            m_bmask = 0xFF0000;
            m_amask = 0;
            m_bpp = 24;
            break;

        case pix_format_bgr24:
            m_rmask = 0xFF0000;
            m_gmask = 0xFF00;
            m_bmask = 0xFF;
            m_amask = 0;
            m_bpp = 24;
            break;

        case pix_format_bgra32:
            m_rmask = 0xFF0000;
            m_gmask = 0xFF00;
            m_bmask = 0xFF;
            m_amask = 0xFF000000;
            m_bpp = 32;
            break;

        case pix_format_abgr32:
            m_rmask = 0xFF000000;
            m_gmask = 0xFF0000;
            m_bmask = 0xFF00;
            m_amask = 0xFF;
            m_bpp = 32;
            break;

        case pix_format_argb32:
            m_rmask = 0xFF00;
            m_gmask = 0xFF0000;
            m_bmask = 0xFF000000;
            m_amask = 0xFF;
            m_bpp = 32;
            break;

        case pix_format_rgba32:
            m_rmask = 0xFF;
            m_gmask = 0xFF00;
            m_bmask = 0xFF0000;
            m_amask = 0xFF000000;
            m_bpp = 32;
            break;
#else //SDL_BIG_ENDIAN (PPC)
        case pix_format_rgb24:
			m_rmask = 0xFF0000;
            m_gmask = 0xFF00;
            m_bmask = 0xFF;
            m_amask = 0;
            m_bpp = 24;
            break;

        case pix_format_bgr24:
            m_rmask = 0xFF;
            m_gmask = 0xFF00;
            m_bmask = 0xFF0000;
            m_amask = 0;
            m_bpp = 24;
            break;

        case pix_format_bgra32:
            m_rmask = 0xFF00;
            m_gmask = 0xFF0000;
            m_bmask = 0xFF000000;
            m_amask = 0xFF;
            m_bpp = 32;
            break;

        case pix_format_abgr32:
            m_rmask = 0xFF;
            m_gmask = 0xFF00;
            m_bmask = 0xFF0000;
            m_amask = 0xFF000000;
            m_bpp = 32;
            break;

        case pix_format_argb32:
            m_rmask = 0xFF0000;
            m_gmask = 0xFF00;
            m_bmask = 0xFF;
            m_amask = 0xFF000000;
            m_bpp = 32;
            break;

        case pix_format_rgba32:
            m_rmask = 0xFF000000;
            m_gmask = 0xFF0000;
            m_bmask = 0xFF00;
            m_amask = 0xFF;
            m_bpp = 32;
            break;
#endif
        }
    }

    //------------------------------------------------------------------------
    platform_specific::~platform_specific()
    {
        int i;
        for(i = platform_support::max_images - 1; i >= 0; --i)
        {
            if(m_surf_img[i]) SDL_FreeSurface(m_surf_img[i]);
        }
        if(m_surf_window) SDL_FreeSurface(m_surf_window);
        if(m_surf_screen) SDL_FreeSurface(m_surf_screen);
    }



    //------------------------------------------------------------------------
    platform_support::platform_support(pix_format_e format, bool flip_y) :
        m_specific(new platform_specific(format, flip_y)),
        m_format(format),
        m_bpp(m_specific->m_bpp),
        m_window_flags(0),
        m_wait_mode(true),
        m_flip_y(flip_y)
    {
        SDL_Init(SDL_INIT_VIDEO);
        strcpy(m_caption, "Anti-Grain Geometry Application");
    }


    //------------------------------------------------------------------------
    platform_support::~platform_support()
    {
        delete m_specific;
    }



    //------------------------------------------------------------------------
    void platform_support::caption(const char* cap)
    {
        strcpy(m_caption, cap);
        if(m_specific->m_initialized)
        {
            SDL_WM_SetCaption(cap, 0);
        }
    }
    




    //------------------------------------------------------------------------
    bool platform_support::init(unsigned width, unsigned height, unsigned flags)
    {
        m_window_flags = flags;
        unsigned wflags = SDL_SWSURFACE;

        if(m_window_flags & window_hw_buffer)
        {
            wflags = SDL_HWSURFACE;
        }

        if(m_window_flags & window_resize)
        {
            wflags |= SDL_RESIZABLE;
        }

        if(m_specific->m_surf_screen) SDL_FreeSurface(m_specific->m_surf_screen);

        m_specific->m_surf_screen = SDL_SetVideoMode(width, height, m_bpp, wflags);
        if(m_specific->m_surf_screen == 0) 
        {
            fprintf(stderr, 
                    "Unable to set %dx%d %d bpp video: %s\n", 
                    width, 
                    height, 
                    m_bpp, 
                    ::SDL_GetError());
            return false;
        }

        SDL_WM_SetCaption(m_caption, 0);

        if(m_specific->m_surf_window) SDL_FreeSurface(m_specific->m_surf_window);

        m_specific->m_surf_window = 
            SDL_CreateRGBSurface(SDL_HWSURFACE, 
                                 m_specific->m_surf_screen->w, 
                                 m_specific->m_surf_screen->h,
                                 m_specific->m_surf_screen->format->BitsPerPixel,
                                 m_specific->m_rmask, 
                                 m_specific->m_gmask, 
                                 m_specific->m_bmask, 
                                 m_specific->m_amask);

        if(m_specific->m_surf_window == 0) 
        {
            fprintf(stderr, 
                    "Unable to create image buffer %dx%d %d bpp: %s\n", 
                    width, 
                    height, 
                    m_bpp, 
                    SDL_GetError());
            return false;
        }

        m_rbuf_window.attach((unsigned char*)m_specific->m_surf_window->pixels, 
                             m_specific->m_surf_window->w, 
                             m_specific->m_surf_window->h, 
                             m_flip_y ? -m_specific->m_surf_window->pitch : 
                                         m_specific->m_surf_window->pitch);

        if(!m_specific->m_initialized)
        {
            m_initial_width = width;
            m_initial_height = height;
            on_init();
            m_specific->m_initialized = true;
        }
        on_resize(m_rbuf_window.width(), m_rbuf_window.height());
        m_specific->m_update_flag = true;
        return true;
    }



    //------------------------------------------------------------------------
    void platform_support::update_window()
    {
        SDL_BlitSurface(m_specific->m_surf_window, 0, m_specific->m_surf_screen, 0);
        SDL_UpdateRect(m_specific->m_surf_screen, 0, 0, 0, 0);
    }


    //------------------------------------------------------------------------
    int platform_support::run()
    {
        SDL_Event event;
        bool ev_flag = false;

        for(;;)
        {
            if(m_specific->m_update_flag)
            {
                on_draw();
                update_window();
                m_specific->m_update_flag = false;
            }

            ev_flag = false;
            if(m_wait_mode)
            {
                SDL_WaitEvent(&event);
                ev_flag = true;
            }
            else
            {
                if(SDL_PollEvent(&event))
                {
                    ev_flag = true;
                }
                else
                {
                    on_idle();
                }
            }

            if(ev_flag)
            {
                if(event.type == SDL_QUIT)
                {
                    break;
                }

                int y;
                unsigned flags = 0;

                switch (event.type) 
                {
                case SDL_VIDEORESIZE:
                    if(!init(event.resize.w, event.resize.h, m_window_flags)) return false;
                    on_resize(m_rbuf_window.width(), m_rbuf_window.height());
                    trans_affine_resizing(event.resize.w, event.resize.h);
                    m_specific->m_update_flag = true;
                    break;

                case SDL_KEYDOWN:
                    {
                        flags = 0;
                        if(event.key.keysym.mod & KMOD_SHIFT) flags |= kbd_shift;
                        if(event.key.keysym.mod & KMOD_CTRL)  flags |= kbd_ctrl;

                        bool left  = false;
                        bool up    = false;
                        bool right = false;
                        bool down  = false;

                        switch(event.key.keysym.sym)
                        {
                        case key_left:
                            left = true;
                            break;

                        case key_up:
                            up = true;
                            break;

                        case key_right:
                            right = true;
                            break;

                        case key_down:
                            down = true;
                            break;
                        }

                        if(m_ctrls.on_arrow_keys(left, right, down, up))
                        {
                            on_ctrl_change();
                            force_redraw();
                        }
                        else
                        {
                            on_key(m_specific->m_cur_x,
                                   m_specific->m_cur_y,
                                   event.key.keysym.sym,
                                   flags);
                        }
                    }
                    break;

                case SDL_MOUSEMOTION:
                    y = m_flip_y ? 
                        m_rbuf_window.height() - event.motion.y : 
                        event.motion.y;

                    m_specific->m_cur_x = event.motion.x;
                    m_specific->m_cur_y = y;
                    flags = 0;
                    if(event.motion.state & SDL_BUTTON_LMASK) flags |= mouse_left;
                    if(event.motion.state & SDL_BUTTON_RMASK) flags |= mouse_right;

                    if(m_ctrls.on_mouse_move(m_specific->m_cur_x, 
                                             m_specific->m_cur_y,
                                             (flags & mouse_left) != 0))
                    {
                        on_ctrl_change();
                        force_redraw();
                    }
                    else
                    {
                        on_mouse_move(m_specific->m_cur_x, 
                                      m_specific->m_cur_y, 
                                      flags);
                    }
		    SDL_Event eventtrash;
		    while (SDL_PeepEvents(&eventtrash, 1, SDL_GETEVENT, SDL_EVENTMASK(SDL_MOUSEMOTION))!=0){;}
                    break;

		case SDL_MOUSEBUTTONDOWN:
                    y = m_flip_y
                        ? m_rbuf_window.height() - event.button.y
                        : event.button.y;

                    m_specific->m_cur_x = event.button.x;
                    m_specific->m_cur_y = y;
                    flags = 0;
                    switch(event.button.button)
                    {
                    case SDL_BUTTON_LEFT:
                        {
                            flags = mouse_left;

if(m_ctrls.on_mouse_button_down(m_specific->m_cur_x,
                                m_specific->m_cur_y))
                            {
                                m_ctrls.set_cur(m_specific->m_cur_x, 
                                    m_specific->m_cur_y);
                                on_ctrl_change();
                                force_redraw();
                            }
                            else
                            {
                                if(m_ctrls.in_rect(m_specific->m_cur_x, 
                                    m_specific->m_cur_y))
                                {
                                    if(m_ctrls.set_cur(m_specific->m_cur_x, 
                                        m_specific->m_cur_y))
                                    {
                                        on_ctrl_change();
                                        force_redraw();
                                    }
                                }
                                else
                                {
                                    on_mouse_button_down(m_specific->m_cur_x, 
                                        m_specific->m_cur_y, 
                                        flags);
                                }
                            }
                        }
                        break;
                    case SDL_BUTTON_RIGHT:
                        flags = mouse_right;
                        on_mouse_button_down(m_specific->m_cur_x, 
                            m_specific->m_cur_y, 
                            flags);
                        break;
                    } //switch(event.button.button)
                    break;
		    
                case SDL_MOUSEBUTTONUP:
                    y = m_flip_y
                        ? m_rbuf_window.height() - event.button.y
                        : event.button.y;

                    m_specific->m_cur_x = event.button.x;
                    m_specific->m_cur_y = y;
                    flags = 0;
                    if(m_ctrls.on_mouse_button_up(m_specific->m_cur_x, 
                                                  m_specific->m_cur_y))
                    {
                        on_ctrl_change();
                        force_redraw();
                    }
                    on_mouse_button_up(m_specific->m_cur_x, 
                                       m_specific->m_cur_y, 
                                       flags);
                    break;
                }
            }
        }
        return 0;
    }



    //------------------------------------------------------------------------
    const char* platform_support::img_ext() const { return ".bmp"; }

    //------------------------------------------------------------------------
    const char* platform_support::full_file_name(const char* file_name)
    {
        return file_name;
    }

    //------------------------------------------------------------------------
    bool platform_support::load_img(unsigned idx, const char* file)
    {
        if(idx < max_images)
        {
            if(m_specific->m_surf_img[idx]) SDL_FreeSurface(m_specific->m_surf_img[idx]);

            char fn[1024];
            strcpy(fn, file);
            int len = strlen(fn);
            if(len < 4 || strcmp(fn + len - 4, ".bmp") != 0)
            {
                strcat(fn, ".bmp");
            }

            SDL_Surface* tmp_surf = SDL_LoadBMP(fn);
            if (tmp_surf == 0) 
            {
                fprintf(stderr, "Couldn't load %s: %s\n", fn, SDL_GetError());
                return false;
            }

            SDL_PixelFormat format;
            format.palette = 0;
            format.BitsPerPixel = m_bpp;
            format.BytesPerPixel = m_bpp >> 8;
            format.Rmask = m_specific->m_rmask;
            format.Gmask = m_specific->m_gmask;
            format.Bmask = m_specific->m_bmask;
            format.Amask = m_specific->m_amask;
            format.Rshift = 0;
            format.Gshift = 0;
            format.Bshift = 0;
            format.Ashift = 0;
            format.Rloss = 0;
            format.Gloss = 0;
            format.Bloss = 0;
            format.Aloss = 0;
            format.colorkey = 0;
            format.alpha = 0;

            m_specific->m_surf_img[idx] = 
                SDL_ConvertSurface(tmp_surf, 
                                   &format, 
                                   SDL_SWSURFACE);

            SDL_FreeSurface(tmp_surf);
            
            if(m_specific->m_surf_img[idx] == 0) return false;

            m_rbuf_img[idx].attach((unsigned char*)m_specific->m_surf_img[idx]->pixels, 
                                   m_specific->m_surf_img[idx]->w, 
                                   m_specific->m_surf_img[idx]->h, 
                                   m_flip_y ? -m_specific->m_surf_img[idx]->pitch : 
                                               m_specific->m_surf_img[idx]->pitch);
            return true;

        }
        return false;
    }




    //------------------------------------------------------------------------
    bool platform_support::save_img(unsigned idx, const char* file)
    {
        if(idx < max_images && m_specific->m_surf_img[idx])
        {
            char fn[1024];
            strcpy(fn, file);
            int len = strlen(fn);
            if(len < 4 || strcmp(fn + len - 4, ".bmp") != 0)
            {
                strcat(fn, ".bmp");
            }
            return SDL_SaveBMP(m_specific->m_surf_img[idx], fn) == 0;
        }
        return false;
    }



    //------------------------------------------------------------------------
    bool platform_support::create_img(unsigned idx, unsigned width, unsigned height)
    {
        if(idx < max_images)
        {

            if(m_specific->m_surf_img[idx]) SDL_FreeSurface(m_specific->m_surf_img[idx]);

             m_specific->m_surf_img[idx] = 
                 SDL_CreateRGBSurface(SDL_SWSURFACE, 
                                      width, 
                                      height,
                                      m_specific->m_surf_screen->format->BitsPerPixel,
                                      m_specific->m_rmask, 
                                      m_specific->m_gmask, 
                                      m_specific->m_bmask, 
                                      m_specific->m_amask);
            if(m_specific->m_surf_img[idx] == 0) 
            {
                fprintf(stderr, "Couldn't create image: %s\n", SDL_GetError());
                return false;
            }

            m_rbuf_img[idx].attach((unsigned char*)m_specific->m_surf_img[idx]->pixels, 
                                   m_specific->m_surf_img[idx]->w, 
                                   m_specific->m_surf_img[idx]->h, 
                                   m_flip_y ? -m_specific->m_surf_img[idx]->pitch : 
                                               m_specific->m_surf_img[idx]->pitch);

            return true;
        }

        return false;
    }
    
    //------------------------------------------------------------------------
    void platform_support::start_timer()
    {
        m_specific->m_sw_start = SDL_GetTicks();
    }

    //------------------------------------------------------------------------
    double platform_support::elapsed_time() const
    {
        int stop = SDL_GetTicks();
        return double(stop - m_specific->m_sw_start);
    }

    //------------------------------------------------------------------------
    void platform_support::message(const char* msg)
    {
        fprintf(stderr, "%s\n", msg);
    }

    //------------------------------------------------------------------------
    void platform_support::force_redraw()
    {
        m_specific->m_update_flag = true;
    }


    //------------------------------------------------------------------------
    void platform_support::on_init() {}
    void platform_support::on_resize(int sx, int sy) {}
    void platform_support::on_idle() {}
    void platform_support::on_mouse_move(int x, int y, unsigned flags) {}
    void platform_support::on_mouse_button_down(int x, int y, unsigned flags) {}
    void platform_support::on_mouse_button_up(int x, int y, unsigned flags) {}
    void platform_support::on_key(int x, int y, unsigned key, unsigned flags) {}
    void platform_support::on_ctrl_change() {}
    void platform_support::on_draw() {}
    void platform_support::on_post_draw(void* raw_handler) {}


}


int agg_main(int argc, char* argv[]);

int main(int argc, char* argv[])
{
    return agg_main(argc, argv);
}

#endif



//----------------------------------------------------------------------------
// Anti-Grain Geometry - Version 2.4
// Copyright (C) 2002-2005 Maxim Shemanarev (http://www.antigrain.com)
//
// Permission to copy, use, modify, sell and distribute this software 
// is granted provided this copyright notice appears in all copies. 
// This software is provided "as is" without express or implied
// warranty, and with no claim as to its suitability for any purpose.
//
//----------------------------------------------------------------------------
// Contact: superstippi@gmx.de
//----------------------------------------------------------------------------
//
// class platform_support
//
//----------------------------------------------------------------------------

#ifdef PLATFORM_BEOS

#include <new>
#include <stdio.h>

#include <Alert.h>
#include <Application.h>
#include <Bitmap.h>
#include <Message.h>
#include <MessageRunner.h>
#include <Messenger.h>
#include <Path.h>
#include <Roster.h>
#include <TranslationUtils.h>
#include <View.h>
#include <Window.h>

#include <string.h>
#include <agg24/agg_platform_support.h>
#include <agg24/agg_color_conv_rgb8.h>

using std::nothrow;


static void
attach_buffer_to_BBitmap(agg::rendering_buffer& buffer, BBitmap* bitmap, bool flipY)
{
    uint8* bits = (uint8*)bitmap->Bits();
    uint32 width = bitmap->Bounds().IntegerWidth() + 1;
    uint32 height = bitmap->Bounds().IntegerHeight() + 1;
    int32 bpr = bitmap->BytesPerRow();
    if (flipY) {
// XXX: why don't I have to do this?!?
//        bits += bpr * (height - 1);
        bpr = -bpr;
    }
    buffer.attach(bits, width, height, bpr);
}


static color_space
pix_format_to_color_space(agg::pix_format_e format)
{
    color_space bitmapFormat = B_NO_COLOR_SPACE;
    switch (format) {
        case agg::pix_format_rgb555:

            bitmapFormat = B_RGB15;
            break;

        case agg::pix_format_rgb565:

            bitmapFormat = B_RGB16;
            break;

        case agg::pix_format_rgb24:
        case agg::pix_format_bgr24:

            bitmapFormat = B_RGB24;
            break;

        case agg::pix_format_rgba32:
        case agg::pix_format_argb32:
        case agg::pix_format_abgr32:
        case agg::pix_format_bgra32:

            bitmapFormat = B_RGBA32;
            break;
    }
    return bitmapFormat;
}


// #pragma mark -


class AGGView : public BView {
 public:
                            AGGView(BRect frame, agg::platform_support* agg,
                                    agg::pix_format_e format, bool flipY);
    virtual                 ~AGGView();

    virtual void            AttachedToWindow();
    virtual void            DetachedFromWindow();

    virtual void            MessageReceived(BMessage* message);
    virtual void            Draw(BRect updateRect);
    virtual void            FrameResized(float width, float height);

    virtual void            KeyDown(const char* bytes, int32 numBytes);

    virtual void            MouseDown(BPoint where);
    virtual void            MouseMoved(BPoint where, uint32 transit,
                               const BMessage* dragMesage);
    virtual void            MouseUp(BPoint where);

            BBitmap*        Bitmap() const;

            uint8           LastKeyDown() const;
            uint32          MouseButtons();

            void            Update();
            void            ForceRedraw();

            unsigned        GetKeyFlags();

 private:
    BBitmap*                fBitmap;
    agg::pix_format_e       fFormat;
    bool                    fFlipY;

    agg::platform_support*  fAGG;

    uint32                  fMouseButtons;
    int32                   fMouseX;
    int32                   fMouseY;

    uint8                   fLastKeyDown;

    bool                    fRedraw;

    BMessageRunner*         fPulse;
    bigtime_t               fLastPulse;
    bool                    fEnableTicks;
};

AGGView::AGGView(BRect frame,
                 agg::platform_support* agg,
                 agg::pix_format_e format,
                 bool flipY)
    : BView(frame, "AGG View", B_FOLLOW_ALL,
            B_FRAME_EVENTS | B_WILL_DRAW),
      fFormat(format),
      fFlipY(flipY),

      fAGG(agg),

      fMouseButtons(0),
      fMouseX(-1),
      fMouseY(-1),
      
      fLastKeyDown(0),

      fRedraw(true),

      fPulse(NULL),
      fLastPulse(0),
      fEnableTicks(true)
{
    SetViewColor(B_TRANSPARENT_32_BIT);
    
    frame.OffsetTo(0.0, 0.0);
    fBitmap = new BBitmap(frame, 0, pix_format_to_color_space(fFormat));
    if (fBitmap->IsValid()) {
        attach_buffer_to_BBitmap(fAGG->rbuf_window(), fBitmap, fFlipY);
    } else {
        delete fBitmap;
        fBitmap = NULL;
    }
}


AGGView::~AGGView()
{
    delete fBitmap;
    delete fPulse;
}


void
AGGView::AttachedToWindow()
{
    BMessage message('tick');
    BMessenger target(this, Looper());
    delete fPulse;
//    BScreen screen;
//    TODO: calc screen retrace
    fPulse = new BMessageRunner(target, &message, 40000);

    // make sure we call this once
    fAGG->on_resize(Bounds().IntegerWidth() + 1,
                    Bounds().IntegerHeight() + 1);
    MakeFocus();
}


void
AGGView::DetachedFromWindow()
{
    delete fPulse;
    fPulse = NULL;
}


void
AGGView::MessageReceived(BMessage* message)
{
    bigtime_t now = system_time();
    switch (message->what) {
        case 'tick':
            // drop messages that have piled up
            if (/*now - fLastPulse > 30000*/fEnableTicks) {
                fLastPulse = now;
                if (!fAGG->wait_mode())
                    fAGG->on_idle();
                Window()->PostMessage('entk', this);
                fEnableTicks = false;
            } else {
//                printf("dropping tick message (%lld)\n", now - fLastPulse);
            }
            break;
        case 'entk':
            fEnableTicks = true;
            if (now - fLastPulse > 30000) {
                fLastPulse = now;
                if (!fAGG->wait_mode())
                    fAGG->on_idle();
            }
            break;
        default:
            BView::MessageReceived(message);
            break;
    }
}


void
AGGView::Draw(BRect updateRect)
{
    if (fBitmap) {
        if (fRedraw) {
            fAGG->on_draw();
            fRedraw = false;
        }
        if (fFormat == agg::pix_format_bgra32) {
            DrawBitmap(fBitmap, updateRect, updateRect);
        } else {
            BBitmap* bitmap = new BBitmap(fBitmap->Bounds(), 0, B_RGBA32);

            agg::rendering_buffer rbufSrc;
            attach_buffer_to_BBitmap(rbufSrc, fBitmap, false);

            agg::rendering_buffer rbufDst;
            attach_buffer_to_BBitmap(rbufDst, bitmap, false);

            switch(fFormat) {
                case agg::pix_format_rgb555:
                    agg::color_conv(&rbufDst, &rbufSrc,
                                    agg::color_conv_rgb555_to_bgra32());
                    break;
                case agg::pix_format_rgb565:
                    agg::color_conv(&rbufDst, &rbufSrc,
                                    agg::color_conv_rgb565_to_bgra32());
                    break;
                case agg::pix_format_rgb24:
                    agg::color_conv(&rbufDst, &rbufSrc,
                                    agg::color_conv_rgb24_to_bgra32());
                    break;
                case agg::pix_format_bgr24:
                    agg::color_conv(&rbufDst, &rbufSrc,
                                    agg::color_conv_bgr24_to_bgra32());
                    break;
                case agg::pix_format_rgba32:
                    agg::color_conv(&rbufDst, &rbufSrc,
                                    agg::color_conv_rgba32_to_bgra32());
                    break;
                case agg::pix_format_argb32:
                    agg::color_conv(&rbufDst, &rbufSrc,
                                    agg::color_conv_argb32_to_bgra32());
                    break;
                case agg::pix_format_abgr32:
                    agg::color_conv(&rbufDst, &rbufSrc,
                                    agg::color_conv_abgr32_to_bgra32());
                    break;
                case agg::pix_format_bgra32:
                    agg::color_conv(&rbufDst, &rbufSrc,
                                    agg::color_conv_bgra32_to_bgra32());
                    break;
            }
            DrawBitmap(bitmap, updateRect, updateRect);
            delete bitmap;
        }
    } else {
        FillRect(updateRect);
    }
}


void
AGGView::FrameResized(float width, float height)
{
    BRect r(0.0, 0.0, width, height);
    BBitmap* bitmap = new BBitmap(r, 0, pix_format_to_color_space(fFormat));
    if (bitmap->IsValid()) {
        delete fBitmap;
        fBitmap = bitmap;
           attach_buffer_to_BBitmap(fAGG->rbuf_window(), fBitmap, fFlipY);

        fAGG->trans_affine_resizing((int)width + 1,
                                    (int)height + 1);

        // pass the event on to AGG
        fAGG->on_resize((int)width + 1, (int)height + 1);
        
        fRedraw = true;
        Invalidate();
    } else
        delete bitmap;
}


void
AGGView::KeyDown(const char* bytes, int32 numBytes)
{
    if (bytes && numBytes > 0) {
        fLastKeyDown = bytes[0];

        bool left  = false;
        bool up    = false;
        bool right = false;
        bool down  = false;

        switch (fLastKeyDown) {

            case B_LEFT_ARROW:
                left = true;
                break;

            case B_UP_ARROW:
                up = true;
                break;

            case B_RIGHT_ARROW:
                right = true;
                break;

            case B_DOWN_ARROW:
                down = true;
                break;
        }

/*            case key_f2:                        
fAGG->copy_window_to_img(agg::platform_support::max_images - 1);
fAGG->save_img(agg::platform_support::max_images - 1, "screenshot");
break;
}*/


        if (fAGG->m_ctrls.on_arrow_keys(left, right, down, up)) {
            fAGG->on_ctrl_change();
            fAGG->force_redraw();
        } else {
            fAGG->on_key(fMouseX, fMouseY, fLastKeyDown, GetKeyFlags());
        }
//        fAGG->on_key(fMouseX, fMouseY, fLastKeyDown, GetKeyFlags());

    }
}


void
AGGView::MouseDown(BPoint where)
{
    BMessage* currentMessage = Window()->CurrentMessage();
    if (currentMessage) {
        if (currentMessage->FindInt32("buttons", (int32*)&fMouseButtons) < B_OK)
            fMouseButtons = B_PRIMARY_MOUSE_BUTTON;
    } else
        fMouseButtons = B_PRIMARY_MOUSE_BUTTON;

    fMouseX = (int)where.x;
    fMouseY = fFlipY ? (int)(Bounds().Height() - where.y) : (int)where.y;

    // pass the event on to AGG
    if (fMouseButtons == B_PRIMARY_MOUSE_BUTTON) {
        // left mouse button -> see if to handle in controls
        fAGG->m_ctrls.set_cur(fMouseX, fMouseY);
        if (fAGG->m_ctrls.on_mouse_button_down(fMouseX, fMouseY)) {
            fAGG->on_ctrl_change();
            fAGG->force_redraw();
        } else {
            if (fAGG->m_ctrls.in_rect(fMouseX, fMouseY)) {
                if (fAGG->m_ctrls.set_cur(fMouseX, fMouseY)) {
                    fAGG->on_ctrl_change();
                    fAGG->force_redraw();
                }
            } else {
                fAGG->on_mouse_button_down(fMouseX, fMouseY, GetKeyFlags());
            }
        }
    } else if (fMouseButtons & B_SECONDARY_MOUSE_BUTTON) {
        // right mouse button -> simple
        fAGG->on_mouse_button_down(fMouseX, fMouseY, GetKeyFlags());
    }
    SetMouseEventMask(B_POINTER_EVENTS, B_LOCK_WINDOW_FOCUS);
}


void
AGGView::MouseMoved(BPoint where, uint32 transit, const BMessage* dragMesage)
{
    // workarround missed mouse up events
    // (if we react too slowly, app_server might have dropped events)
    BMessage* currentMessage = Window()->CurrentMessage();
    int32 buttons = 0;
    if (currentMessage->FindInt32("buttons", &buttons) < B_OK) {
        buttons = 0;
    }
    if (!buttons)
        MouseUp(where);

    fMouseX = (int)where.x;
    fMouseY = fFlipY ? (int)(Bounds().Height() - where.y) : (int)where.y;

    // pass the event on to AGG
    if (fAGG->m_ctrls.on_mouse_move(fMouseX, fMouseY,
                                    (GetKeyFlags() & agg::mouse_left) != 0)) {
        fAGG->on_ctrl_change();
        fAGG->force_redraw();
    } else {
        if (!fAGG->m_ctrls.in_rect(fMouseX, fMouseY)) {
            fAGG->on_mouse_move(fMouseX, fMouseY, GetKeyFlags());
        }
    }
}


void
AGGView::MouseUp(BPoint where)
{
    fMouseX = (int)where.x;
    fMouseY = fFlipY ? (int)(Bounds().Height() - where.y) : (int)where.y;

    // pass the event on to AGG
    if (fMouseButtons == B_PRIMARY_MOUSE_BUTTON) {
        fMouseButtons = 0;

        if (fAGG->m_ctrls.on_mouse_button_up(fMouseX, fMouseY)) {
            fAGG->on_ctrl_change();
            fAGG->force_redraw();
        }
        fAGG->on_mouse_button_up(fMouseX, fMouseY, GetKeyFlags());
    } else if (fMouseButtons == B_SECONDARY_MOUSE_BUTTON) {
        fMouseButtons = 0;

        fAGG->on_mouse_button_up(fMouseX, fMouseY, GetKeyFlags());
    }
}


BBitmap*
AGGView::Bitmap() const
{
    return fBitmap;
}


uint8
AGGView::LastKeyDown() const
{
    return fLastKeyDown;
}


uint32
AGGView::MouseButtons()
{
    uint32 buttons = 0;
    if (LockLooper()) {
        buttons = fMouseButtons;
        UnlockLooper();
    }
    return buttons;
}


void
AGGView::Update()
{
    // trigger display update
    if (LockLooper()) {
        Invalidate();
        UnlockLooper();
    }
}


void
AGGView::ForceRedraw()
{
    // force a redraw (fRedraw = true;)
    // and trigger display update
    if (LockLooper()) {
        fRedraw = true;
        Invalidate();
        UnlockLooper();
    }
}


unsigned
AGGView::GetKeyFlags()
{
    uint32 buttons = fMouseButtons;
    uint32 mods = modifiers();
    unsigned flags = 0;
    if (buttons & B_PRIMARY_MOUSE_BUTTON)   flags |= agg::mouse_left;
    if (buttons & B_SECONDARY_MOUSE_BUTTON) flags |= agg::mouse_right;
    if (mods & B_SHIFT_KEY)                 flags |= agg::kbd_shift;
    if (mods & B_COMMAND_KEY)               flags |= agg::kbd_ctrl;
    return flags;
}

// #pragma mark -


class AGGWindow : public BWindow {
 public:
                    AGGWindow()
                    : BWindow(BRect(-50.0, -50.0, -10.0, -10.0),
                              "AGG Application", B_TITLED_WINDOW, B_ASYNCHRONOUS_CONTROLS)
                    {
                    }

    virtual bool    QuitRequested()
                    {
                        be_app->PostMessage(B_QUIT_REQUESTED);
                        return true;
                    }

            bool    Init(BRect frame, agg::platform_support* agg, agg::pix_format_e format,
                              bool flipY, uint32 flags)
                    {
                        MoveTo(frame.LeftTop());
                        ResizeTo(frame.Width(), frame.Height());

                        SetFlags(flags);

                        frame.OffsetTo(0.0, 0.0);
                        fView = new AGGView(frame, agg, format, flipY);
                        AddChild(fView);

                        return fView->Bitmap() != NULL;
                    }


        AGGView*    View() const
                    {
                        return fView;
                    }
 private:
    AGGView*        fView;
};

// #pragma mark -


class AGGApplication : public BApplication {
 public:
                    AGGApplication()
                    : BApplication("application/x-vnd.AGG-AGG")
                    {
                        fWindow = new AGGWindow();
                    }

    virtual void    ReadyToRun()
                    {
                        if (fWindow) {
                            fWindow->Show();
                        }
                    }

    virtual bool    Init(agg::platform_support* agg, int width, int height,
                         agg::pix_format_e format, bool flipY, uint32 flags)
                    {
                        BRect r(50.0, 50.0,
                                50.0 + width - 1.0,
                                50.0 + height - 1.0);
                        uint32 windowFlags = B_ASYNCHRONOUS_CONTROLS;
                        if (!(flags & agg::window_resize))
                            windowFlags |= B_NOT_RESIZABLE;

                        return fWindow->Init(r, agg, format, flipY, windowFlags);;
                    }


        AGGWindow*  Window() const
                    {
                        return fWindow;
                    }

 private:
    AGGWindow*      fWindow;
};


// #pragma mark -


namespace agg
{

class platform_specific {
 public:
                    platform_specific(agg::platform_support* agg,
                                      agg::pix_format_e format, bool flip_y)
                        : fAGG(agg),
                          fApp(NULL),
                          fFormat(format),
                          fFlipY(flip_y),
                          fTimerStart(system_time())
                    {
                        memset(fImages, 0, sizeof(fImages));
                        fApp = new AGGApplication();
                        fAppPath[0] = 0;
                        // figure out where we're running from
                        app_info info;
                        status_t ret = fApp->GetAppInfo(&info);
                        if (ret >= B_OK) {
                            BPath path(&info.ref);
                            ret = path.InitCheck();
                            if (ret >= B_OK) {
                                ret = path.GetParent(&path);
                                if (ret >= B_OK) {
                                    sprintf(fAppPath, "%s", path.Path());
                                } else {
                                    fprintf(stderr, "getting app parent folder failed: %s\n", strerror(ret));
                                }
                            } else {
                                fprintf(stderr, "making app path failed: %s\n", strerror(ret));
                            }
                        } else {
                            fprintf(stderr, "GetAppInfo() failed: %s\n", strerror(ret));
                        }
                    }
                    ~platform_specific()
                    {
                        for (int32 i = 0; i < agg::platform_support::max_images; i++)
                            delete fImages[i];
                        delete fApp;
                    }

    bool            Init(int width, int height, unsigned flags)
                    {
                        return fApp->Init(fAGG, width, height, fFormat, fFlipY, flags);
                    }

    int             Run()
                    {
                        status_t ret = B_NO_INIT;
                        if (fApp) {
                            fApp->Run();
                            ret = B_OK;
                        }
                        return ret;
                    }

    void            SetTitle(const char* title)
                    {
                        if (fApp && fApp->Window() && fApp->Window()->Lock()) {
                            fApp->Window()->SetTitle(title);
                            fApp->Window()->Unlock();
                        }
                    }
    void            StartTimer()
                    {
                        fTimerStart = system_time();
                    }
    double          ElapsedTime() const
                    {
                        return (system_time() - fTimerStart) / 1000.0;
                    }

    void            ForceRedraw()
                    {
                        fApp->Window()->View()->ForceRedraw();
                    }
    void            UpdateWindow()
                    {
                        fApp->Window()->View()->Update();
                    }


    agg::platform_support*  fAGG;
    AGGApplication*     fApp;
    agg::pix_format_e    fFormat;
    bool                fFlipY;
    bigtime_t           fTimerStart;
    BBitmap*            fImages[agg::platform_support::max_images];

    char                fAppPath[B_PATH_NAME_LENGTH];
    char                fFilePath[B_PATH_NAME_LENGTH];
};


    //------------------------------------------------------------------------
    platform_support::platform_support(pix_format_e format, bool flip_y) :
        m_specific(new platform_specific(this, format, flip_y)),
        m_format(format),
        m_bpp(32/*m_specific->m_bpp*/),
        m_window_flags(0),
        m_wait_mode(true),
        m_flip_y(flip_y),
        m_initial_width(10),
        m_initial_height(10)
    {
        strcpy(m_caption, "Anti-Grain Geometry Application");
    }


    //------------------------------------------------------------------------
    platform_support::~platform_support()
    {
        delete m_specific;
    }

    //------------------------------------------------------------------------
    void platform_support::caption(const char* cap)
    {
        strcpy(m_caption, cap);
        m_specific->SetTitle(cap);
    }

    //------------------------------------------------------------------------
    void platform_support::start_timer()
    {
        m_specific->StartTimer();
    }

    //------------------------------------------------------------------------
    double platform_support::elapsed_time() const
    {
        return m_specific->ElapsedTime();
    }

    //------------------------------------------------------------------------
    void* platform_support::raw_display_handler()
    {
        // TODO: if we ever support BDirectWindow here, that would
        // be the frame buffer pointer with offset to the window top left
        return NULL;
    }

    //------------------------------------------------------------------------
    void platform_support::message(const char* msg)
    {
        BAlert* alert = new BAlert("AGG Message", msg, "Ok");
        alert->Go(/*NULL*/);
    }


    //------------------------------------------------------------------------
    bool platform_support::init(unsigned width, unsigned height, unsigned flags)
    {
        m_initial_width = width;
        m_initial_height = height;
        m_window_flags = flags;

        if (m_specific->Init(width, height, flags)) {
            on_init();
            return true;
        }

        return false;
    }


    //------------------------------------------------------------------------
    int platform_support::run()
    {
        return m_specific->Run();
    }


    //------------------------------------------------------------------------
    const char* platform_support::img_ext() const { return ".ppm"; }


    const char* platform_support::full_file_name(const char* file_name)
    {
        sprintf(m_specific->fFilePath, "%s/%s", m_specific->fAppPath, file_name);
        return m_specific->fFilePath;
    }


    //------------------------------------------------------------------------
    bool platform_support::load_img(unsigned idx, const char* file)
    {
        if (idx < max_images)
        {
            char path[B_PATH_NAME_LENGTH];
            sprintf(path, "%s/%s%s", m_specific->fAppPath, file, img_ext());
            BBitmap* transBitmap = BTranslationUtils::GetBitmap(path);
            if (transBitmap && transBitmap->IsValid()) {
                if(transBitmap->ColorSpace() != B_RGB32 && transBitmap->ColorSpace() != B_RGBA32) {
                    // ups we got a smart ass Translator making our live harder
                    delete transBitmap;
                    return false;
                }

                color_space format = B_RGB24;

                switch (m_format) {
                    case pix_format_gray8:
                        format = B_GRAY8;
                        break;
                    case pix_format_rgb555:
                        format = B_RGB15;
                        break;
                    case pix_format_rgb565:
                        format = B_RGB16;
                        break;
                    case pix_format_rgb24:
                        format = B_RGB24_BIG;
                        break;
                    case pix_format_bgr24:
                        format = B_RGB24;
                        break;
                    case pix_format_abgr32:
                    case pix_format_argb32:
                    case pix_format_bgra32:
                        format = B_RGB32;
                        break;
                    case pix_format_rgba32:
                        format = B_RGB32_BIG;
                        break;
                }
                BBitmap* bitmap = new (nothrow) BBitmap(transBitmap->Bounds(), 0, format);
                if (!bitmap || !bitmap->IsValid()) {
                    fprintf(stderr, "failed to allocate temporary bitmap!\n");
                    delete transBitmap;
                    delete bitmap;
                    return false;
                }

                delete m_specific->fImages[idx];

                rendering_buffer rbuf_tmp;
                attach_buffer_to_BBitmap(rbuf_tmp, transBitmap, m_flip_y);
        
                m_specific->fImages[idx] = bitmap;
        
                attach_buffer_to_BBitmap(m_rbuf_img[idx], bitmap, m_flip_y);
        
                rendering_buffer* dst = &m_rbuf_img[idx];

                switch(m_format)
                {
                case pix_format_gray8:
                    return false;
//                  color_conv(dst, &rbuf_tmp, color_conv_bgra32_to_gray8()); break;
                    break;
        
                case pix_format_rgb555:
                    color_conv(dst, &rbuf_tmp, color_conv_bgra32_to_rgb555()); break;
                    break;
        
                case pix_format_rgb565:
                    color_conv(dst, &rbuf_tmp, color_conv_bgra32_to_rgb565()); break;
                    break;
        
                case pix_format_rgb24:
                    color_conv(dst, &rbuf_tmp, color_conv_bgra32_to_rgb24()); break;
                    break;
        
                case pix_format_bgr24:
                    color_conv(dst, &rbuf_tmp, color_conv_bgra32_to_bgr24()); break;
                    break;
        
                case pix_format_abgr32:
                    color_conv(dst, &rbuf_tmp, color_conv_bgra32_to_abgr32()); break;
                    break;
        
                case pix_format_argb32:
                    color_conv(dst, &rbuf_tmp, color_conv_bgra32_to_argb32()); break;
                    break;
        
                case pix_format_bgra32:
                    color_conv(dst, &rbuf_tmp, color_conv_bgra32_to_bgra32()); break;
                    break;
        
                case pix_format_rgba32:
                    color_conv(dst, &rbuf_tmp, color_conv_bgra32_to_rgba32()); break;
                    break;
                }
                delete transBitmap;
                
                return true;

            } else {
                fprintf(stderr, "failed to load bitmap: '%s'\n", full_file_name(file));
            }
        }
        return false;
    }



    //------------------------------------------------------------------------
    bool platform_support::save_img(unsigned idx, const char* file)
    {
        // TODO: implement using BTranslatorRoster and friends
        return false;
    }



    //------------------------------------------------------------------------
    bool platform_support::create_img(unsigned idx, unsigned width, unsigned height)
    {
        if(idx < max_images)
        {
            if(width  == 0) width  = m_specific->fApp->Window()->View()->Bitmap()->Bounds().IntegerWidth() + 1;
            if(height == 0) height = m_specific->fApp->Window()->View()->Bitmap()->Bounds().IntegerHeight() + 1;
            BBitmap* bitmap = new BBitmap(BRect(0.0, 0.0, width - 1, height - 1), 0, B_RGBA32);;
            if (bitmap && bitmap->IsValid()) {
                delete m_specific->fImages[idx];
                m_specific->fImages[idx] = bitmap;
                attach_buffer_to_BBitmap(m_rbuf_img[idx], bitmap, m_flip_y);
                return true;
            } else {
                delete bitmap;
            }
        }
        return false;
    }


    //------------------------------------------------------------------------
    void platform_support::force_redraw()
    {
        m_specific->ForceRedraw();
    }



    //------------------------------------------------------------------------
    void platform_support::update_window()
    {
        m_specific->UpdateWindow();
    }


    //------------------------------------------------------------------------
    void platform_support::on_init() {}
    void platform_support::on_resize(int sx, int sy) {}
    void platform_support::on_idle() {}
    void platform_support::on_mouse_move(int x, int y, unsigned flags) {}
    void platform_support::on_mouse_button_down(int x, int y, unsigned flags) {}
    void platform_support::on_mouse_button_up(int x, int y, unsigned flags) {}
    void platform_support::on_key(int x, int y, unsigned key, unsigned flags) {}
    void platform_support::on_ctrl_change() {}
    void platform_support::on_draw() {}
    void platform_support::on_post_draw(void* raw_handler) {}
}






//----------------------------------------------------------------------------
int agg_main(int argc, char* argv[]);



int
main(int argc, char* argv[])
{
    return agg_main(argc, argv);
}


#endif

//----------------------------------------------------------------------------
// Anti-Grain Geometry - Version 2.4 
// Copyright (C) 2002-2005 Maxim Shemanarev (McSeem)
// Copyright (C) 2003 Hansruedi Baer (MacOS support)
//
// Permission to copy, use, modify, sell and distribute this software 
// is granted provided this copyright notice appears in all copies. 
// This software is provided "as is" without express or implied
// warranty, and with no claim as to its suitability for any purpose.
//
//----------------------------------------------------------------------------
// Contact: mcseem@antigrain.com
//          mcseemagg@yahoo.com
//          http://www.antigrain.com
//			baer@karto.baug.eth.ch
//----------------------------------------------------------------------------
//
// class platform_support
//
//----------------------------------------------------------------------------
//
// Note:
// I tried to retain the original structure for the Win32 platform as far
// as possible. Currently, not all features are implemented but the examples
// should work properly.
// HB
//----------------------------------------------------------------------------

#ifdef PLATFORM_OSX11

#include <Carbon.h>
#if defined(__MWERKS__)
#include "console.h"
#endif
#include <string.h>
#include <unistd.h>
#include <agg24/agg_platform_support.h>
#include <agg24/agg_mac_pmap.h>
#include <agg24/agg_color_conv_rgb8.h>


namespace agg
{
    
pascal OSStatus DoWindowClose (EventHandlerCallRef nextHandler, EventRef theEvent, void* userData);
pascal OSStatus DoWindowDrawContent (EventHandlerCallRef nextHandler, EventRef theEvent, void* userData);
pascal OSStatus DoAppQuit (EventHandlerCallRef nextHandler, EventRef theEvent, void* userData);
pascal OSStatus DoMouseDown (EventHandlerCallRef nextHandler, EventRef theEvent, void* userData);
pascal OSStatus DoMouseUp (EventHandlerCallRef nextHandler, EventRef theEvent, void* userData);
pascal OSStatus DoMouseDragged (EventHandlerCallRef nextHandler, EventRef theEvent, void* userData);
pascal OSStatus DoKeyDown (EventHandlerCallRef nextHandler, EventRef theEvent, void* userData);
pascal OSStatus DoKeyUp (EventHandlerCallRef nextHandler, EventRef theEvent, void* userData);
pascal void DoPeriodicTask (EventLoopTimerRef theTimer, void* userData);


    //------------------------------------------------------------------------
    class platform_specific
    {
    public:
        platform_specific(pix_format_e format, bool flip_y);

        void create_pmap(unsigned width, unsigned height, 
                         rendering_buffer* wnd);

        void display_pmap(WindowRef window, const rendering_buffer* src);
        bool load_pmap(const char* fn, unsigned idx, 
                       rendering_buffer* dst);

        bool save_pmap(const char* fn, unsigned idx, 
                       const rendering_buffer* src);

        unsigned translate(unsigned keycode);

        pix_format_e     m_format;
        pix_format_e     m_sys_format;
        bool             m_flip_y;
        unsigned         m_bpp;
        unsigned         m_sys_bpp;
        WindowRef        m_window;
        pixel_map   	 m_pmap_window;
        pixel_map        m_pmap_img[platform_support::max_images];
        unsigned         m_keymap[256];
        unsigned         m_last_translated_key;
        int              m_cur_x;
        int              m_cur_y;
        unsigned         m_input_flags;
        bool             m_redraw_flag;
        UnsignedWide     m_sw_freq;
        UnsignedWide     m_sw_start;
    };


    //------------------------------------------------------------------------
    platform_specific::platform_specific(pix_format_e format, bool flip_y) :
       	m_format(format),
        m_sys_format(pix_format_undefined),
        m_flip_y(flip_y),
        m_bpp(0),
        m_sys_bpp(0),
        m_window(nil),
        m_last_translated_key(0),
        m_cur_x(0),
        m_cur_y(0),
        m_input_flags(0),
        m_redraw_flag(true)
    {
        memset(m_keymap, 0, sizeof(m_keymap));

        //Keyboard input is not yet fully supported nor tested
        //m_keymap[VK_PAUSE]       = key_pause;
        m_keymap[kClearCharCode]      = key_clear;

        //m_keymap[VK_NUMPAD0]    = key_kp0;
        //m_keymap[VK_NUMPAD1]    = key_kp1;
        //m_keymap[VK_NUMPAD2]    = key_kp2;
        //m_keymap[VK_NUMPAD3]    = key_kp3;
        //m_keymap[VK_NUMPAD4]    = key_kp4;
        //m_keymap[VK_NUMPAD5]    = key_kp5;
        //m_keymap[VK_NUMPAD6]    = key_kp6;
        //m_keymap[VK_NUMPAD7]    = key_kp7;
        //m_keymap[VK_NUMPAD8]    = key_kp8;
        //m_keymap[VK_NUMPAD9]    = key_kp9;
        //m_keymap[VK_DECIMAL]    = key_kp_period;
        //m_keymap[VK_DIVIDE]     = key_kp_divide;
        //m_keymap[VK_MULTIPLY]   = key_kp_multiply;
        //m_keymap[VK_SUBTRACT]   = key_kp_minus;
        //m_keymap[VK_ADD]        = key_kp_plus;

        m_keymap[kUpArrowCharCode]    = key_up;
        m_keymap[kDownArrowCharCode]  = key_down;
        m_keymap[kRightArrowCharCode] = key_right;
        m_keymap[kLeftArrowCharCode]  = key_left;
        //m_keymap[VK_INSERT]     = key_insert;
        m_keymap[kDeleteCharCode]     = key_delete;
        m_keymap[kHomeCharCode]       = key_home;
        m_keymap[kEndCharCode]        = key_end;
        m_keymap[kPageUpCharCode]     = key_page_up;
        m_keymap[kPageDownCharCode]   = key_page_down;

        //m_keymap[VK_F1]         = key_f1;
        //m_keymap[VK_F2]         = key_f2;
        //m_keymap[VK_F3]         = key_f3;
        //m_keymap[VK_F4]         = key_f4;
        //m_keymap[VK_F5]         = key_f5;
        //m_keymap[VK_F6]         = key_f6;
        //m_keymap[VK_F7]         = key_f7;
        //m_keymap[VK_F8]         = key_f8;
        //m_keymap[VK_F9]         = key_f9;
        //m_keymap[VK_F10]        = key_f10;
        //m_keymap[VK_F11]        = key_f11;
        //m_keymap[VK_F12]        = key_f12;
        //m_keymap[VK_F13]        = key_f13;
        //m_keymap[VK_F14]        = key_f14;
        //m_keymap[VK_F15]        = key_f15;

        //m_keymap[VK_NUMLOCK]    = key_numlock;
        //m_keymap[VK_CAPITAL]    = key_capslock;
        //m_keymap[VK_SCROLL]     = key_scrollock;

        switch(m_format)
        {
        case pix_format_gray8:
            m_sys_format = pix_format_gray8;
            m_bpp = 8;
            m_sys_bpp = 8;
            break;

        case pix_format_rgb565:
        case pix_format_rgb555:
            m_sys_format = pix_format_rgb555;
            m_bpp = 16;
            m_sys_bpp = 16;
            break;

        case pix_format_rgb24:
        case pix_format_bgr24:
            m_sys_format = pix_format_rgb24;
            m_bpp = 24;
            m_sys_bpp = 24;
            break;

        case pix_format_bgra32:
        case pix_format_abgr32:
        case pix_format_argb32:
        case pix_format_rgba32:
            m_sys_format = pix_format_argb32;
            m_bpp = 32;
            m_sys_bpp = 32;
            break;
        }
        ::Microseconds(&m_sw_freq);
        ::Microseconds(&m_sw_start);
    }


    //------------------------------------------------------------------------
    void platform_specific::create_pmap(unsigned width, 
                                        unsigned height,
                                        rendering_buffer* wnd)
    {
        m_pmap_window.create(width, height, org_e(m_bpp));
        wnd->attach(m_pmap_window.buf(), 
                    m_pmap_window.width(),
                    m_pmap_window.height(),
                      m_flip_y ?
                     -m_pmap_window.row_bytes() :
                      m_pmap_window.row_bytes());
    }


    //------------------------------------------------------------------------
    void platform_specific::display_pmap(WindowRef window, const rendering_buffer* src)
    {
        if(m_sys_format == m_format)
        {
            m_pmap_window.draw(window);
        }
        else
        {
            pixel_map pmap_tmp;
            pmap_tmp.create(m_pmap_window.width(), 
                            m_pmap_window.height(),
                            org_e(m_sys_bpp));

            rendering_buffer rbuf_tmp;
            rbuf_tmp.attach(pmap_tmp.buf(),
                            pmap_tmp.width(),
                            pmap_tmp.height(),
                            m_flip_y ?
                             -pmap_tmp.row_bytes() :
                              pmap_tmp.row_bytes());

            switch(m_format)
            {
            case pix_format_gray8:
                return;

            case pix_format_rgb565:
                color_conv(&rbuf_tmp, src, color_conv_rgb565_to_rgb555());
                break;

            case pix_format_bgr24:
                color_conv(&rbuf_tmp, src, color_conv_bgr24_to_rgb24());
                break;

            case pix_format_abgr32:
                color_conv(&rbuf_tmp, src, color_conv_abgr32_to_argb32());
                break;

            case pix_format_bgra32:
                color_conv(&rbuf_tmp, src, color_conv_bgra32_to_argb32());
                break;

            case pix_format_rgba32:
                color_conv(&rbuf_tmp, src, color_conv_rgba32_to_argb32());
                break;
            }
            pmap_tmp.draw(window);
        }
    }


    //------------------------------------------------------------------------
    bool platform_specific::save_pmap(const char* fn, unsigned idx, 
                                      const rendering_buffer* src)
    {
        if(m_sys_format == m_format)
        {
            return m_pmap_img[idx].save_as_qt(fn);
        }
        else
        {
            pixel_map pmap_tmp;
            pmap_tmp.create(m_pmap_img[idx].width(), 
                            m_pmap_img[idx].height(),
                            org_e(m_sys_bpp));

            rendering_buffer rbuf_tmp;
            rbuf_tmp.attach(pmap_tmp.buf(),
                            pmap_tmp.width(),
                            pmap_tmp.height(),
                            m_flip_y ?
                             -pmap_tmp.row_bytes() :
                              pmap_tmp.row_bytes());
            switch(m_format)
            {
            case pix_format_gray8:
                return false;

            case pix_format_rgb565:
                color_conv(&rbuf_tmp, src, color_conv_rgb565_to_rgb555());
                break;

            case pix_format_rgb24:
                color_conv(&rbuf_tmp, src, color_conv_rgb24_to_bgr24());
                break;

            case pix_format_abgr32:
                color_conv(&rbuf_tmp, src, color_conv_abgr32_to_bgra32());
                break;

            case pix_format_argb32:
                color_conv(&rbuf_tmp, src, color_conv_argb32_to_bgra32());
                break;

            case pix_format_rgba32:
                color_conv(&rbuf_tmp, src, color_conv_rgba32_to_bgra32());
                break;
            }
            return pmap_tmp.save_as_qt(fn);
        }
        return true;
    }



    //------------------------------------------------------------------------
    bool platform_specific::load_pmap(const char* fn, unsigned idx, 
                                      rendering_buffer* dst)
    {
        pixel_map pmap_tmp;
        if(!pmap_tmp.load_from_qt(fn)) return false;

        rendering_buffer rbuf_tmp;
        rbuf_tmp.attach(pmap_tmp.buf(),
                        pmap_tmp.width(),
                        pmap_tmp.height(),
                        m_flip_y ?
                         -pmap_tmp.row_bytes() :
                          pmap_tmp.row_bytes());

        m_pmap_img[idx].create(pmap_tmp.width(), 
                               pmap_tmp.height(), 
                               org_e(m_bpp),
                               0);

        dst->attach(m_pmap_img[idx].buf(),
                    m_pmap_img[idx].width(),
                    m_pmap_img[idx].height(),
                    m_flip_y ?
                      -m_pmap_img[idx].row_bytes() :
                       m_pmap_img[idx].row_bytes());

        switch(m_format)
        {
        case pix_format_gray8:
            return false;
            break;

        case pix_format_rgb555:
            switch(pmap_tmp.bpp())
            {
            case 16: color_conv(dst, &rbuf_tmp, color_conv_rgb555_to_rgb555()); break;
            case 24: color_conv(dst, &rbuf_tmp, color_conv_rgb24_to_rgb555()); break;
            case 32: color_conv(dst, &rbuf_tmp, color_conv_argb32_to_rgb555()); break;
            }
            break;

        case pix_format_rgb565:
            switch(pmap_tmp.bpp())
            {
            case 16: color_conv(dst, &rbuf_tmp, color_conv_rgb555_to_rgb565()); break;
            case 24: color_conv(dst, &rbuf_tmp, color_conv_rgb24_to_rgb565()); break;
            case 32: color_conv(dst, &rbuf_tmp, color_conv_argb32_to_rgb565()); break;
            }
            break;

        case pix_format_rgb24:
            switch(pmap_tmp.bpp())
            {
            case 16: color_conv(dst, &rbuf_tmp, color_conv_rgb555_to_rgb24()); break;
            case 24: color_conv(dst, &rbuf_tmp, color_conv_rgb24_to_rgb24()); break;
            case 32: color_conv(dst, &rbuf_tmp, color_conv_argb32_to_rgb24()); break;
            }
            break;

        case pix_format_bgr24:
            switch(pmap_tmp.bpp())
            {
            case 16: color_conv(dst, &rbuf_tmp, color_conv_rgb555_to_bgr24()); break;
            case 24: color_conv(dst, &rbuf_tmp, color_conv_rgb24_to_bgr24()); break;
            case 32: color_conv(dst, &rbuf_tmp, color_conv_argb32_to_bgr24()); break;
            }
            break;

        case pix_format_abgr32:
            switch(pmap_tmp.bpp())
            {
            case 16: color_conv(dst, &rbuf_tmp, color_conv_rgb555_to_abgr32()); break;
            case 24: color_conv(dst, &rbuf_tmp, color_conv_rgb24_to_abgr32()); break;
            case 32: color_conv(dst, &rbuf_tmp, color_conv_argb32_to_abgr32()); break;
            }
            break;

        case pix_format_argb32:
            switch(pmap_tmp.bpp())
            {
            case 16: color_conv(dst, &rbuf_tmp, color_conv_rgb555_to_argb32()); break;
            case 24: color_conv(dst, &rbuf_tmp, color_conv_rgb24_to_argb32()); break;
            case 32: color_conv(dst, &rbuf_tmp, color_conv_argb32_to_argb32()); break;
            }
            break;

        case pix_format_bgra32:
            switch(pmap_tmp.bpp())
            {
            case 16: color_conv(dst, &rbuf_tmp, color_conv_rgb555_to_bgra32()); break;
            case 24: color_conv(dst, &rbuf_tmp, color_conv_rgb24_to_bgra32()); break;
            case 32: color_conv(dst, &rbuf_tmp, color_conv_argb32_to_bgra32()); break;
            }
            break;

        case pix_format_rgba32:
            switch(pmap_tmp.bpp())
            {
            case 16: color_conv(dst, &rbuf_tmp, color_conv_rgb555_to_rgba32()); break;
            case 24: color_conv(dst, &rbuf_tmp, color_conv_rgb24_to_rgba32()); break;
            case 32: color_conv(dst, &rbuf_tmp, color_conv_argb32_to_rgba32()); break;
            }
            break;
        }
        
        return true;
    }








    //------------------------------------------------------------------------
    unsigned platform_specific::translate(unsigned keycode)
    {
        return m_last_translated_key = (keycode > 255) ? 0 : m_keymap[keycode];
    }



    //------------------------------------------------------------------------
    platform_support::platform_support(pix_format_e format, bool flip_y) :
        m_specific(new platform_specific(format, flip_y)),
        m_format(format),
        m_bpp(m_specific->m_bpp),
        m_window_flags(0),
        m_wait_mode(true),
        m_flip_y(flip_y),
        m_initial_width(10),
        m_initial_height(10)
    {
        strcpy(m_caption, "Anti-Grain Geometry Application");
    }


    //------------------------------------------------------------------------
    platform_support::~platform_support()
    {
        delete m_specific;
    }



    //------------------------------------------------------------------------
    void platform_support::caption(const char* cap)
    {
        strcpy(m_caption, cap);
        if(m_specific->m_window)
        {
        	SetWindowTitleWithCFString (m_specific->m_window, CFStringCreateWithCStringNoCopy (nil, cap, kCFStringEncodingASCII, nil));
        }
    }



    //------------------------------------------------------------------------
    static unsigned get_key_flags(UInt32 wflags)
    {
        unsigned flags = 0;
        
         if(wflags & shiftKey)   flags |= kbd_shift;
         if(wflags & controlKey) flags |= kbd_ctrl;

        return flags;
    }


    //------------------------------------------------------------------------
    void platform_support::message(const char* msg)
    {
		SInt16 item;
		Str255 p_msg;
		
		::CopyCStringToPascal (msg, p_msg);
		::StandardAlert (kAlertPlainAlert, (const unsigned char*) "\013AGG Message", p_msg, NULL, &item);
		//::StandardAlert (kAlertPlainAlert, (const unsigned char*) "\pAGG Message", p_msg, NULL, &item);
    }


    //------------------------------------------------------------------------
    void platform_support::start_timer()
    {
		::Microseconds (&(m_specific->m_sw_start));
    }


    //------------------------------------------------------------------------
    double platform_support::elapsed_time() const
    {
        UnsignedWide stop;
        ::Microseconds(&stop);
        return double(stop.lo - 
                      m_specific->m_sw_start.lo) * 1e6 / 
                      double(m_specific->m_sw_freq.lo);
    }


    //------------------------------------------------------------------------
    bool platform_support::init(unsigned width, unsigned height, unsigned flags)
    {
        if(m_specific->m_sys_format == pix_format_undefined)
        {
            return false;
        }

        m_window_flags = flags;

		// application
		EventTypeSpec		eventType;
		EventHandlerUPP		handlerUPP;

		eventType.eventClass = kEventClassApplication;
		eventType.eventKind = kEventAppQuit;

		handlerUPP = NewEventHandlerUPP(DoAppQuit);

		InstallApplicationEventHandler (handlerUPP, 1, &eventType, nil, nil);

		eventType.eventClass = kEventClassMouse;
		eventType.eventKind = kEventMouseDown;
		handlerUPP = NewEventHandlerUPP(DoMouseDown);
		InstallApplicationEventHandler (handlerUPP, 1, &eventType, this, nil);

		eventType.eventKind = kEventMouseUp;
		handlerUPP = NewEventHandlerUPP(DoMouseUp);
		InstallApplicationEventHandler (handlerUPP, 1, &eventType, this, nil);
		
		eventType.eventKind = kEventMouseDragged;
		handlerUPP = NewEventHandlerUPP(DoMouseDragged);
		InstallApplicationEventHandler (handlerUPP, 1, &eventType, this, nil);

		eventType.eventClass = kEventClassKeyboard;
		eventType.eventKind = kEventRawKeyDown;
		handlerUPP = NewEventHandlerUPP(DoKeyDown);
		InstallApplicationEventHandler (handlerUPP, 1, &eventType, this, nil);

		eventType.eventKind = kEventRawKeyUp;
		handlerUPP = NewEventHandlerUPP(DoKeyUp);
		InstallApplicationEventHandler (handlerUPP, 1, &eventType, this, nil);

		eventType.eventKind = kEventRawKeyRepeat;
		handlerUPP = NewEventHandlerUPP(DoKeyDown);		// 'key repeat' is translated to 'key down'
		InstallApplicationEventHandler (handlerUPP, 1, &eventType, this, nil);

		WindowAttributes	windowAttrs;
		Rect				bounds;

		// window
		windowAttrs = kWindowCloseBoxAttribute | kWindowCollapseBoxAttribute | kWindowStandardHandlerAttribute;
		SetRect (&bounds, 0, 0, width, height);
		OffsetRect (&bounds, 100, 100);
		CreateNewWindow (kDocumentWindowClass, windowAttrs, &bounds, &m_specific->m_window);

        if(m_specific->m_window == nil)
        {
            return false;
        }

		// I assume the text is ASCII.
		// Change to kCFStringEncodingMacRoman, kCFStringEncodingISOLatin1, kCFStringEncodingUTF8 or what else you need.
        SetWindowTitleWithCFString (m_specific->m_window, CFStringCreateWithCStringNoCopy (nil, m_caption, kCFStringEncodingASCII, nil));
		
		eventType.eventClass = kEventClassWindow;
		eventType.eventKind = kEventWindowClose;

		handlerUPP = NewEventHandlerUPP(DoWindowClose);
		InstallWindowEventHandler (m_specific->m_window, handlerUPP, 1, &eventType, this, NULL);

		eventType.eventKind = kEventWindowDrawContent;
		handlerUPP = NewEventHandlerUPP(DoWindowDrawContent);
		InstallWindowEventHandler (m_specific->m_window, handlerUPP, 1, &eventType, this, NULL);
		
		// Periodic task
		// Instead of an idle function I use the Carbon event timer.
		// You may decide to change the wait value which is currently 50 milliseconds.
		EventLoopRef		mainLoop;
		EventLoopTimerUPP	timerUPP;
		EventLoopTimerRef	theTimer;

		mainLoop = GetMainEventLoop();
		timerUPP = NewEventLoopTimerUPP (DoPeriodicTask);
		InstallEventLoopTimer (mainLoop, 0, 50 * kEventDurationMillisecond, timerUPP, this, &theTimer);

        m_specific->create_pmap(width, height, &m_rbuf_window);
        m_initial_width = width;
        m_initial_height = height;
        on_init();
        on_resize(width, height);
        m_specific->m_redraw_flag = true;
		
  		ShowWindow (m_specific->m_window);
  		SetPortWindowPort (m_specific->m_window);
		
      return true;
    }


    //------------------------------------------------------------------------
    int platform_support::run()
    {
		
		RunApplicationEventLoop ();
        return true;
    }


    //------------------------------------------------------------------------
    const char* platform_support::img_ext() const { return ".bmp"; }

    //------------------------------------------------------------------------
    const char* platform_support::full_file_name(const char* file_name)
    {
        return file_name;
    }

    //------------------------------------------------------------------------
    bool platform_support::load_img(unsigned idx, const char* file)
    {
        if(idx < max_images)
        {
            char fn[1024];
            strcpy(fn, file);
            int len = strlen(fn);
#if defined(__MWERKS__)
            if(len < 4 || stricmp(fn + len - 4, ".BMP") != 0)
#else
	        if(len < 4 || strncasecmp(fn + len - 4, ".BMP", 4) != 0)
#endif
            {
                strcat(fn, ".bmp");
            }
            return m_specific->load_pmap(fn, idx, &m_rbuf_img[idx]);
        }
        return true;
    }



    //------------------------------------------------------------------------
    bool platform_support::save_img(unsigned idx, const char* file)
    {
        if(idx < max_images)
        {
            char fn[1024];
            strcpy(fn, file);
            int len = strlen(fn);
#if defined(__MWERKS__)
            if(len < 4 || stricmp(fn + len - 4, ".BMP") != 0)
#else
	        if(len < 4 || strncasecmp(fn + len - 4, ".BMP", 4) != 0)
#endif
            {
                strcat(fn, ".bmp");
            }
            return m_specific->save_pmap(fn, idx, &m_rbuf_img[idx]);
        }
        return true;
    }



    //------------------------------------------------------------------------
    bool platform_support::create_img(unsigned idx, unsigned width, unsigned height)
    {
        if(idx < max_images)
        {
            if(width  == 0) width  = m_specific->m_pmap_window.width();
            if(height == 0) height = m_specific->m_pmap_window.height();
            m_specific->m_pmap_img[idx].create(width, height, org_e(m_specific->m_bpp));
            m_rbuf_img[idx].attach(m_specific->m_pmap_img[idx].buf(), 
                                   m_specific->m_pmap_img[idx].width(),
                                   m_specific->m_pmap_img[idx].height(),
                                   m_flip_y ?
                                   -m_specific->m_pmap_img[idx].row_bytes() :
                                    m_specific->m_pmap_img[idx].row_bytes());
            return true;
        }
        return false;
    }


    //------------------------------------------------------------------------
    void platform_support::force_redraw()
    {
    	Rect	bounds;
    	
        m_specific->m_redraw_flag = true;
        // on_ctrl_change ();
		on_draw();

    	SetRect(&bounds, 0, 0, m_rbuf_window.width(), m_rbuf_window.height());
    	InvalWindowRect(m_specific->m_window, &bounds);
    }



    //------------------------------------------------------------------------
    void platform_support::update_window()
    {
        m_specific->display_pmap(m_specific->m_window, &m_rbuf_window);
    }


    //------------------------------------------------------------------------
    void platform_support::on_init() {}
    void platform_support::on_resize(int sx, int sy) {}
    void platform_support::on_idle() {}
    void platform_support::on_mouse_move(int x, int y, unsigned flags) {}
    void platform_support::on_mouse_button_down(int x, int y, unsigned flags) {}
    void platform_support::on_mouse_button_up(int x, int y, unsigned flags) {}
    void platform_support::on_key(int x, int y, unsigned key, unsigned flags) {}
    void platform_support::on_ctrl_change() {}
    void platform_support::on_draw() {}
    void platform_support::on_post_draw(void* raw_handler) {}


//------------------------------------------------------------------------
pascal OSStatus DoWindowClose (EventHandlerCallRef nextHandler, EventRef theEvent, void* userData)
{
	userData;
	
	QuitApplicationEventLoop ();

	return CallNextEventHandler (nextHandler, theEvent);
}


//------------------------------------------------------------------------
pascal OSStatus DoAppQuit (EventHandlerCallRef nextHandler, EventRef theEvent, void* userData)
{
	userData;
	
	return CallNextEventHandler (nextHandler, theEvent);
}


//------------------------------------------------------------------------
pascal OSStatus DoMouseDown (EventHandlerCallRef nextHandler, EventRef theEvent, void* userData)
{
	Point wheresMyMouse;
	UInt32 modifier;
	
	GetEventParameter (theEvent, kEventParamMouseLocation, typeQDPoint, NULL, sizeof(Point), NULL, &wheresMyMouse);
	GlobalToLocal (&wheresMyMouse);
	GetEventParameter (theEvent, kEventParamKeyModifiers, typeUInt32, NULL, sizeof(UInt32), NULL, &modifier);

    platform_support * app = reinterpret_cast<platform_support*>(userData);

    app->m_specific->m_cur_x = wheresMyMouse.h;
    if(app->flip_y())
    {
        app->m_specific->m_cur_y = app->rbuf_window().height() - wheresMyMouse.v;
    }
    else
    {
        app->m_specific->m_cur_y = wheresMyMouse.v;
    }
    app->m_specific->m_input_flags = mouse_left | get_key_flags(modifier);
    
    app->m_ctrls.set_cur(app->m_specific->m_cur_x, 
                         app->m_specific->m_cur_y);
    if(app->m_ctrls.on_mouse_button_down(app->m_specific->m_cur_x, 
                                         app->m_specific->m_cur_y))
    {
        app->on_ctrl_change();
        app->force_redraw();
    }
    else
    {
        if(app->m_ctrls.in_rect(app->m_specific->m_cur_x, 
                                app->m_specific->m_cur_y))
        {
            if(app->m_ctrls.set_cur(app->m_specific->m_cur_x, 
                                    app->m_specific->m_cur_y))
            {
                app->on_ctrl_change();
                app->force_redraw();
            }
        }
        else
        {
            app->on_mouse_button_down(app->m_specific->m_cur_x, 
                                      app->m_specific->m_cur_y, 
                                      app->m_specific->m_input_flags);
        }
    }

	return CallNextEventHandler (nextHandler, theEvent);
}


//------------------------------------------------------------------------
pascal OSStatus DoMouseUp (EventHandlerCallRef nextHandler, EventRef theEvent, void* userData)
{
	Point wheresMyMouse;
	UInt32 modifier;
	
	GetEventParameter (theEvent, kEventParamMouseLocation, typeQDPoint, NULL, sizeof(Point), NULL, &wheresMyMouse);
	GlobalToLocal (&wheresMyMouse);
	GetEventParameter (theEvent, kEventParamKeyModifiers, typeUInt32, NULL, sizeof(UInt32), NULL, &modifier);

    platform_support * app = reinterpret_cast<platform_support*>(userData);

    app->m_specific->m_cur_x = wheresMyMouse.h;
    if(app->flip_y())
    {
        app->m_specific->m_cur_y = app->rbuf_window().height() - wheresMyMouse.v;
    }
    else
    {
        app->m_specific->m_cur_y = wheresMyMouse.v;
    }
    app->m_specific->m_input_flags = mouse_left | get_key_flags(modifier);

    if(app->m_ctrls.on_mouse_button_up(app->m_specific->m_cur_x, 
                                       app->m_specific->m_cur_y))
    {
        app->on_ctrl_change();
        app->force_redraw();
    }
    app->on_mouse_button_up(app->m_specific->m_cur_x, 
                            app->m_specific->m_cur_y, 
                            app->m_specific->m_input_flags);

	return CallNextEventHandler (nextHandler, theEvent);
}


//------------------------------------------------------------------------
pascal OSStatus DoMouseDragged (EventHandlerCallRef nextHandler, EventRef theEvent, void* userData)
{
	Point wheresMyMouse;
	UInt32 modifier;
	
	GetEventParameter (theEvent, kEventParamMouseLocation, typeQDPoint, NULL, sizeof(Point), NULL, &wheresMyMouse);
	GlobalToLocal (&wheresMyMouse);
	GetEventParameter (theEvent, kEventParamKeyModifiers, typeUInt32, NULL, sizeof(UInt32), NULL, &modifier);

    platform_support * app = reinterpret_cast<platform_support*>(userData);

    app->m_specific->m_cur_x = wheresMyMouse.h;
    if(app->flip_y())
    {
        app->m_specific->m_cur_y = app->rbuf_window().height() - wheresMyMouse.v;
    }
    else
    {
        app->m_specific->m_cur_y = wheresMyMouse.v;
    }
    app->m_specific->m_input_flags = mouse_left | get_key_flags(modifier);


    if(app->m_ctrls.on_mouse_move(
        app->m_specific->m_cur_x, 
        app->m_specific->m_cur_y,
        (app->m_specific->m_input_flags & mouse_left) != 0))
    {
        app->on_ctrl_change();
        app->force_redraw();
    }
    else
    {
        app->on_mouse_move(app->m_specific->m_cur_x, 
                           app->m_specific->m_cur_y, 
                           app->m_specific->m_input_flags);
    }

	return CallNextEventHandler (nextHandler, theEvent);
}


//------------------------------------------------------------------------
pascal OSStatus DoKeyDown (EventHandlerCallRef nextHandler, EventRef theEvent, void* userData)
{
	char key_code;
	UInt32 modifier;
	
	GetEventParameter (theEvent, kEventParamKeyMacCharCodes, typeChar, NULL, sizeof(char), NULL, &key_code);
	GetEventParameter (theEvent, kEventParamKeyModifiers, typeUInt32, NULL, sizeof(UInt32), NULL, &modifier);

	platform_support * app = reinterpret_cast<platform_support*>(userData);

	app->m_specific->m_last_translated_key = 0;
    switch(modifier) 
    {
        case controlKey:
            app->m_specific->m_input_flags |= kbd_ctrl;
            break;

        case shiftKey:
            app->m_specific->m_input_flags |= kbd_shift;
            break;

        default:
            app->m_specific->translate(key_code);
            break;
    }

    if(app->m_specific->m_last_translated_key)
    {
        bool left  = false;
        bool up    = false;
        bool right = false;
        bool down  = false;

        switch(app->m_specific->m_last_translated_key)
        {
        case key_left:
            left = true;
            break;

        case key_up:
            up = true;
            break;

        case key_right:
            right = true;
            break;

        case key_down:
            down = true;
            break;

		//On a Mac, screenshots are handled by the system.
        case key_f2:                        
            app->copy_window_to_img(agg::platform_support::max_images - 1);
            app->save_img(agg::platform_support::max_images - 1, "screenshot");
            break;
        }


        if(app->m_ctrls.on_arrow_keys(left, right, down, up))
        {
            app->on_ctrl_change();
            app->force_redraw();
        }
        else
        {
            app->on_key(app->m_specific->m_cur_x,
                        app->m_specific->m_cur_y,
                        app->m_specific->m_last_translated_key,
                        app->m_specific->m_input_flags);
        }
    }

	return CallNextEventHandler (nextHandler, theEvent);
}


//------------------------------------------------------------------------
pascal OSStatus DoKeyUp (EventHandlerCallRef nextHandler, EventRef theEvent, void* userData)
{
	char key_code;
	UInt32 modifier;
	
	GetEventParameter (theEvent, kEventParamKeyMacCharCodes, typeChar, NULL, sizeof(char), NULL, &key_code);
	GetEventParameter (theEvent, kEventParamKeyModifiers, typeUInt32, NULL, sizeof(UInt32), NULL, &modifier);

	platform_support * app = reinterpret_cast<platform_support*>(userData);

    app->m_specific->m_last_translated_key = 0;
    switch(modifier) 
    {
        case controlKey:
            app->m_specific->m_input_flags &= ~kbd_ctrl;
            break;

        case shiftKey:
            app->m_specific->m_input_flags &= ~kbd_shift;
            break;
    }
    
	return CallNextEventHandler (nextHandler, theEvent);
}


//------------------------------------------------------------------------
pascal OSStatus DoWindowDrawContent (EventHandlerCallRef nextHandler, EventRef theEvent, void* userData)
{
    platform_support * app = reinterpret_cast<platform_support*>(userData);

    if(app)
    {
        if(app->m_specific->m_redraw_flag)
        {
            app->on_draw();
            app->m_specific->m_redraw_flag = false;
        }
        app->m_specific->display_pmap(app->m_specific->m_window, &app->rbuf_window());
    }

	return CallNextEventHandler (nextHandler, theEvent);
}


//------------------------------------------------------------------------
pascal void DoPeriodicTask (EventLoopTimerRef theTimer, void* userData)
{
    platform_support * app = reinterpret_cast<platform_support*>(userData);
    
    if(!app->wait_mode())
		app->on_idle();
}


}




//----------------------------------------------------------------------------
int agg_main(int argc, char* argv[]);


// Hm. Classic MacOS does not know command line input.
// CodeWarrior provides a way to mimic command line input.
// The function 'ccommand' can be used to get the command
// line arguments.
//----------------------------------------------------------------------------
int main(int argc, char* argv[])
{
#if defined(__MWERKS__)
	// argc = ccommand (&argv);
#endif
    
    // Check if we are launched by double-clicking under OSX 
	// Get rid of extra argument, this will confuse the standard argument parsing
	// calls used in the examples to get the name of the image file to be used
    if ( argc >= 2 && strncmp (argv[1], "-psn", 4) == 0 ) {
        argc = 1;
    } 

launch:
    return agg_main(argc, argv);
}

#endif

#endif

