/**********************************************************************
This file is part of Crack dot Com's free source code release of Golgotha.
for information about compiling & licensing issues visit this URL
 If that doesn't help, contact Jonathan Clark at 
  golgotha_source@usa.net (Subject should have "GOLG" in it) 
***********************************************************************/

#include "dx5/r1_dx5_texture.hh"
#include "video/win32/dx5_error.hh"
#include "video/win32/dx5.hh"
#include "time/profile.hh"
#include "image/image.hh"
#include "r1_clip.hh"
#include "r1_win.hh"

#define USE_BUFFER
void init_d3d_vert_buffer();

r1_dx5_class r1_dx5_class_instance;
    
i4_profile_class pf_dx5_use_texture("dx5::use_texture");
i4_profile_class pf_dx5_vertex_setup("dx5::vertex_setup");
i4_profile_class pf_dx5_drawprimitive("dx5::drawprimitive");

r1_dx5_render_window_class::~r1_dx5_render_window_class()
{
}

r1_dx5_render_window_class::r1_dx5_render_window_class(w16 w, w16 h,
                               r1_expand_type expand_type,
                               r1_render_api_class *api)
    : r1_render_window_class(w,h, expand_type, api) {}



void r1_dx5_class::copy_part(i4_image_class *im,                                          
                            int x, int y,             // position on screen
                            int x1, int y1,           // area of image to copy 
                            int x2, int y2)
{
  
  DDSURFACEDESC ddsd;
  memset(&ddsd,0,sizeof(DDSURFACEDESC));
  ddsd.dwSize = sizeof(DDSURFACEDESC);
  dx5_common.back_surface->Lock(NULL,&ddsd,DDLOCK_WAIT | DDLOCK_WRITEONLY,0);  
  
  //get frame buffer pointer
  w8 *fb = (w8 *)ddsd.lpSurface;
  w8 *im_src = (w8 *)im->data + x + y*im->bpl;
  
  sw32 w_pitch = ddsd.lPitch; 
  sw32 im_width  = x2-x1+1;
  sw32 im_height = y2-y1+1;
  sw32 i,j;
  
  sw32 ix_off = i4_f_to_i(x_off);
  sw32 iy_off = i4_f_to_i(y_off);
  if (fb)
  {
    fb += ((x+ix_off)*2 + (y+iy_off)*w_pitch);
    for (i=0;iwidth()*2;
      fb += w_pitch;
    }
  }
  dx5_common.back_surface->Unlock(NULL);
}


void r1_dx5_render_window_class::draw(i4_draw_context_class &context)
{
  r1_dx5_class_instance.x_off = context.xoff;
  r1_dx5_class_instance.y_off = context.yoff;
    
  clip_with_z(context);
  
  r1_dx5_class_instance.d3d_device->BeginScene();

  r1_render_window_class::draw(context);  

  r1_dx5_class_instance.flush_vert_buffer();

  r1_dx5_class_instance.d3d_device->EndScene();  
};

r1_render_window_class *r1_dx5_class::create_render_window(int visable_w, int visable_h,
                                                           r1_expand_type type)
{
  return new r1_dx5_render_window_class(visable_w, visable_h, type, this);
}

void r1_dx5_class::set_write_mode(r1_write_mask_type mask)
{
  if (mask==get_write_mask()) return;

  states_have_changed = i4_T;
  flush_vert_buffer();  
  

  if (mask & R1_WRITE_W)
    d3d_device->SetRenderState(D3DRENDERSTATE_ZWRITEENABLE,1);
  else
    d3d_device->SetRenderState(D3DRENDERSTATE_ZWRITEENABLE,0);

  if (mask & R1_COMPARE_W)
    d3d_device->SetRenderState(D3DRENDERSTATE_ZFUNC,D3DCMP_GREATER);
  else
    d3d_device->SetRenderState(D3DRENDERSTATE_ZFUNC,D3DCMP_ALWAYS);

  r1_render_api_class::set_write_mode(mask);
}

void r1_dx5_class::set_alpha_mode(r1_alpha_type type)
{
  if (type==get_alpha_mode()) return;

  states_have_changed = i4_T;
  flush_vert_buffer();  

  switch (type)
  {
    case R1_ALPHA_DISABLED :
      d3d_device->SetRenderState(D3DRENDERSTATE_ALPHABLENDENABLE,FALSE);
      break;

    case R1_ALPHA_CONSTANT :    // enable alpha
    case R1_ALPHA_LINEAR   :    // w/constant alpha, the constant alpha value is copied into the vertices
      d3d_device->SetRenderState(D3DRENDERSTATE_ALPHABLENDENABLE,TRUE);
      d3d_device->SetRenderState(D3DRENDERSTATE_SRCBLEND,D3DBLEND_SRCALPHA);
      d3d_device->SetRenderState(D3DRENDERSTATE_DESTBLEND,D3DBLEND_INVSRCALPHA);
      break;    
  }

  r1_render_api_class::set_alpha_mode(type);
}

r1_dx5_class::r1_dx5_class()
{    
  zbuffer_surface     = 0;
  d3d                 = 0;
  d3d_device          = 0;
  d3d_viewport        = 0;

  render_device_flags = 0;

  strcpy(dd_driver_name,"display");
  strcpy(d3d_driver_name,"Direct3D HAL");  
  
  last_node = 0;
  
  texture_mode = i4_F;
  holy_mode    = i4_F;
  
  states_have_changed = i4_F;
}

r1_dx5_class::~r1_dx5_class()
{
}

i4_bool r1_dx5_class::init(i4_display_class *display)
{    
  if (display!=i4_dx5_display || !i4_dx5_display->using_accelerated_driver())
    return i4_F;     
  
  set_color_tint(0);
  init_d3d_vert_buffer();

  dx5_d3d_info *info=dx5_common.get_driver_hardware_info(dx5_common.ddraw);
  if (!info) return i4_F;

  if (!i4_dx5_check(dx5_common.ddraw->QueryInterface(IID_IDirect3D2,(void **)&d3d)))
    return i4_F;

  D3DDEVICEDESC hw_desc = info->hw_desc; 
  D3DDEVICEDESC sw_desc = info->sw_desc;
  
  if (hw_desc.dpcTriCaps.dwTextureCaps & D3DPTEXTURECAPS_SQUAREONLY)
    needs_square_textures = i4_T;
  else
    needs_square_textures = i4_F;

  DDSURFACEDESC ddsd;
  memset(&ddsd,0,sizeof(DDSURFACEDESC));
  ddsd.dwSize  = sizeof(DDSURFACEDESC);  
  ddsd.dwFlags = DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH | DDSD_ZBUFFERBITDEPTH;

  ddsd.dwWidth  = i4_dx5_display->current_mode()->xres;
  ddsd.dwHeight = i4_dx5_display->current_mode()->yres;
  ddsd.ddsCaps.dwCaps = DDSCAPS_ZBUFFER;
  
  w32 bd = hw_desc.dwDeviceZBufferBitDepth;
  if (bd==0)
  {
    //zbuffer must be in system memory
    bd = sw_desc.dwDeviceZBufferBitDepth;
    ddsd.ddsCaps.dwCaps |= DDSCAPS_SYSTEMMEMORY;
    OutputDebugString("putting zbuffer in system memory\n");
  }
  else
  {
    //zbuffer must be in video memory
    ddsd.ddsCaps.dwCaps |= DDSCAPS_VIDEOMEMORY;
    OutputDebugString("putting zbuffer in video memory\n");
  }
  
  switch (bd)
  {
   case DDBD_8:  ddsd.dwZBufferBitDepth = 8; break;
   case DDBD_16: ddsd.dwZBufferBitDepth = 16; break;
   case DDBD_24: ddsd.dwZBufferBitDepth = 24; break;
   case DDBD_32: ddsd.dwZBufferBitDepth = 32; break;
  }

  IDirectDrawSurface *z_surf;  
  if (dx5_common.ddraw->CreateSurface(&ddsd, &z_surf,0) != DD_OK)  
    return i4_F;      
 
  if (!i4_dx5_check(z_surf->QueryInterface(IID_IDirectDrawSurface3,(void **)&zbuffer_surface)))
  {
    z_surf->Release();
    return i4_F;
  }

  z_surf->Release();

  if (dx5_common.back_surface->AddAttachedSurface(zbuffer_surface) != DD_OK)
    return i4_F;

  IDirectDrawSurface *back_surf;
  dx5_common.back_surface->QueryInterface(IID_IDirectDrawSurface,(void **)&back_surf);  

  if (d3d->CreateDevice(*info->lpGuid, back_surf, &d3d_device) !=D3D_OK)
    return i4_F; 
  
  back_surf->Release();
     
  if (d3d->CreateViewport(&d3d_viewport,0) != D3D_OK)
    return i4_F;

  if (d3d_device->AddViewport(d3d_viewport) != D3D_OK)
    return i4_F;

  hardware_tmapping = i4_T;
  
  /*
  use_stipled_alpha = i4_F;
  if (hw_desc.dwShadeCaps & D3DSHADECAPS_ALPHAFLATSTIPPLED)
  {
    if (!(hw_desc.dwShadeCaps & D3DPSHADECAPS_ALPHAGOURAUDBLEND))
    {
      //this device doesnt support gouraud alpha blending, but it does
      //support flat stipled alpha, so lets at least use the flat
      //stipled alpha where possible
      use_stipled_alpha = i4_T;
    }
  }
  */

  D3DVIEWPORT2 viewport_desc;
  
  float  aspect = (float)640/(float)480;

  memset(&viewport_desc, 0, sizeof(D3DVIEWPORT2));  
  viewport_desc.dwSize       = sizeof(D3DVIEWPORT2);
  viewport_desc.dwX          = 0;
  viewport_desc.dwY          = 0;
  viewport_desc.dwWidth      = 640;
  viewport_desc.dwHeight     = 480;
  viewport_desc.dvClipX      = -1.0f;
  viewport_desc.dvClipY      = aspect;
  viewport_desc.dvClipWidth  = 2.0f;
  viewport_desc.dvClipHeight = 2.0f * aspect;
  viewport_desc.dvMinZ       = 0.f;
  viewport_desc.dvMaxZ       = 1.f;
  
  if (d3d_viewport->SetViewport2(&viewport_desc) != D3D_OK)
    return i4_F;

  if (d3d_device->SetCurrentViewport(d3d_viewport) != D3D_OK)
    return i4_F;

  //turn zbuffering on
  d3d_device->SetRenderState(D3DRENDERSTATE_ZENABLE,TRUE);   

  //turn gouraud shading on the entire time
  d3d_device->SetRenderState(D3DRENDERSTATE_SHADEMODE,D3DSHADE_GOURAUD);

  //dithering.. eh..
  d3d_device->SetRenderState(D3DRENDERSTATE_DITHERENABLE,1);
  
  //no monochomatic shading?
  d3d_device->SetRenderState(D3DRENDERSTATE_MONOENABLE,FALSE);
  
  //texturemapping mode setup
  d3d_device->SetRenderState(D3DRENDERSTATE_TEXTUREMAPBLEND,D3DTBLEND_MODULATE);
  d3d_device->SetRenderState(D3DRENDERSTATE_TEXTUREHANDLE,0);  
  
  d3d_device->SetRenderState(D3DRENDERSTATE_TEXTUREADDRESS,D3DTADDRESS_CLAMP);
  d3d_device->SetRenderState(D3DRENDERSTATE_WRAPU,0);
  d3d_device->SetRenderState(D3DRENDERSTATE_WRAPV,0);

  d3d_device->SetRenderState(D3DRENDERSTATE_TEXTUREPERSPECTIVE,1);
    
  //no automatic face culling
  d3d_device->SetRenderState(D3DRENDERSTATE_CULLMODE,D3DCULL_NONE);

  //setup other basic modes
  set_write_mode(R1_WRITE_W | R1_COMPARE_W | R1_WRITE_COLOR);
  
  set_shading_mode(R1_COLORED_SHADING);

  set_alpha_mode(R1_ALPHA_DISABLED);  
      
  set_filter_mode(R1_NO_FILTERING);

  set_z_range(0.01,1.0);

  //clear the viewport, initialize type thing
  clear_area(0,0,639,479,0x00000000,1.0);

  //if we're page flipping, flip so we can clear the other page initially as well
  i4_dx5_display->flush();

  clear_area(0,0,639,479,0x00000000,1.0);  

  tmanager = new r1_dx5_texture_class(display->get_palette());

  return i4_T;
}

void r1_dx5_class::set_filter_mode(r1_filter_type type)
{
  if (type==R1_NO_FILTERING)
  {
    d3d_device->SetRenderState(D3DRENDERSTATE_TEXTUREMAG,D3DFILTER_LINEAR);//NEAREST);//D3DFILTER_LINEAR);    
    d3d_device->SetRenderState(D3DRENDERSTATE_TEXTUREMIN,D3DFILTER_LINEAR);//NEAREST);//D3DFILTER_LINEAR);
  }
  else
  if (type==R1_BILINEAR_FILTERING)
  {
    d3d_device->SetRenderState(D3DRENDERSTATE_TEXTUREMAG,D3DFILTER_LINEAR);    
    d3d_device->SetRenderState(D3DRENDERSTATE_TEXTUREMIN,D3DFILTER_LINEAR);
  }

  r1_render_api_class::set_filter_mode(type);
}

void r1_dx5_class::uninit()
{
  if (tmanager)
  {
    delete tmanager;
    tmanager = 0;  
  }

  if (d3d_viewport)
  {
    d3d_viewport->Release();
    d3d_viewport = 0;
  }

  if (d3d_device)
  {
    d3d_device->Release();
    d3d_device = 0;
  }

  if (d3d)
  {
    d3d->Release();  
    d3d = 0;
  }
}

void r1_dx5_class::enable_holy()
{
  if (!holy_mode)
  {
    states_have_changed = i4_T;
    flush_vert_buffer();    

    pre_holy_alpha_mode = get_alpha_mode();
    pre_holy_write_mask = get_write_mask();

    d3d_device->SetRenderState(D3DRENDERSTATE_TEXTUREMAPBLEND,D3DTBLEND_MODULATEALPHA);

    set_alpha_mode(R1_ALPHA_LINEAR);
    set_write_mode(R1_COMPARE_W);
    
    holy_mode = i4_T;    
  }
}

void r1_dx5_class::disable_holy()
{
  if (holy_mode)
  {    
    states_have_changed = i4_T;
    flush_vert_buffer();    
    
    d3d_device->SetRenderState(D3DRENDERSTATE_TEXTUREMAPBLEND,D3DTBLEND_MODULATE);
    
    set_alpha_mode(pre_holy_alpha_mode);
    set_write_mode(pre_holy_write_mask);    
    
    holy_mode = i4_F;    
  }
}

void r1_dx5_class::use_texture(r1_texture_handle material_ref, sw32 desired_width, w32 frame)
{  
  pf_dx5_use_texture.start();

  if (!tmanager->valid_handle(material_ref))
  {
    pf_dx5_use_texture.stop();
    disable_texture();
    return;
  }

  texture_mode = i4_T;

  sw32 width,height;    

  r1_miplevel_t *mip = tmanager->get_texture(material_ref, frame, desired_width, width, height);

  // don't select the texture again if it's the same one we used last time
  if (mip)
  {        
    i4_bool is_alpha = mip->entry->is_alphatexture();
    i4_bool is_holy  = mip->entry->is_transparent();

    if (is_alpha || is_holy)      
      enable_holy();      
    else
      disable_holy();

    if (mip != last_node)
    {
      states_have_changed = i4_T;
      flush_vert_buffer();

      last_node = mip;

      i4_float blahfloat_a, blahfloat_b;
      ((r1_dx5_texture_class *)tmanager)->select_texture(mip->vram_handle, blahfloat_a, blahfloat_b);            
    }
  }
  
  pf_dx5_use_texture.stop();
}

// drawing will the constant color to render with if textures are disabled
void r1_dx5_class::disable_texture()
{
  if (texture_mode)
  {
    states_have_changed = i4_T;
    flush_vert_buffer();

    d3d_device->SetRenderState(D3DRENDERSTATE_TEXTUREHANDLE,0);
    texture_mode = i4_F;
    last_node    = 0;
    
    disable_holy();
  }  
}

static i4_float f_0_1_to_i_0_255_255 = 255.f;

sw32 inline f_0_1_to_i_0_255(float f)
{
  sw32 res;
  __asm
  {
    fld f
    fld f_0_1_to_i_0_255_255
    fmul
    fistp res
  }
  return res;
}

i4_float inline i_to_f(sw32 i)
{
  i4_float res;
  __asm
  {
    fild i
    fstp res
  }
  return res;
}

w32 inline make_d3d_color(i4_float r, i4_float g, i4_float b)
{
  return (f_0_1_to_i_0_255(r) << 16) |
         (f_0_1_to_i_0_255(g) <<  8) |
         (f_0_1_to_i_0_255(b) <<  0);
}
 
static float dx5_z_scale, dx5_z_add, dx5_w_scale, dx5_w_add, dx5_near_z, dx5_far_z;


void r1_dx5_class::set_z_range(i4_float near_z, i4_float far_z)
{
  dx5_near_z = near_z;
  dx5_far_z  = far_z;

  dx5_z_scale = 0.999f / far_z;
  dx5_w_scale = near_z * 0.999f;
  dx5_z_add   = 0;//.01;
  dx5_w_add   = 0;//.01;

  r1_near_clip_z = near_z;
  r1_far_clip_z  = far_z;
}

inline void make_d3d_verts(D3DTLVERTEX *dxverts,r1_vert *r1verts,r1_dx5_class *c,int total)
{                                                                        
  int i;  
    
  D3DTLVERTEX *dx_v = dxverts;
  r1_vert     *r1_v = r1verts;

  //add vertex information
  for (i=0;isx       = r1_v->px + c->x_off;
    dx_v->sy       = r1_v->py + c->y_off;
                                    
    dx_v->sz       = r1_v->w * dx5_w_scale;

    if (dx_v->sz<0)
    {
      i4_warning("dx vert z too small");
      dx_v->sz=0;
    }
    else
    if (dx_v->sz>1)
    {
      i4_warning("dx vert z too large");
      dx_v->sz=1;
    }

    dx_v->rhw = r1_v->w;
    
    dx_v++;
    r1_v++;
  }

  //add texture information
  if (c->texture_mode)
  {
    dx_v = dxverts;
    r1_v = r1verts;
    
    for (i=0;itu = r1_v->s;
      dx_v->tv = r1_v->t;

      if (dx_v->tu > 1) dx_v->tu = 1;
      else
      if (dx_v->tu < 0) dx_v->tu = 0;

      if (dx_v->tv > 1) dx_v->tv = 1;
      else
      if (dx_v->tv < 0) dx_v->tv = 0;

      dx_v++;
      r1_v++;
    }
  }
    
  w32 ccolor;
  w32 red;
  w32 alpha;

  //add color information
  switch (c->shade_mode)                                                    
  {                                                                      
    /* routines will draw fullbright */
    case R1_SHADE_DISABLED:
      dx_v = dxverts;
      r1_v = r1verts;
      ccolor = 0x00FFFFFF;            
      for (i=0;icolor    = ccolor;
        dx_v++;
        r1_v++;
      }
      break;
    /* routines will use the constant color*/
    case R1_CONSTANT_SHADING:
      dx_v = dxverts;
      r1_v = r1verts;
      ccolor = c->const_color;
            
      for (i=0;icolor = ccolor;
        dx_v++;
        r1_v++;
      }
      break;
                                                                         
    /* routines will use only the red component (as white) */            
    case R1_WHITE_SHADING:                                               
      dx_v = dxverts;
      r1_v = r1verts;      
      
      for (i=0;ir);
        
        dx_v->color = (red<<16) | (red<<8) | (red);
        dx_v++;
        r1_v++;
      }      
      break;                                                             
                                                                         
    /* routines will use r,g, and b*/
    case R1_COLORED_SHADING:
      dx_v = dxverts;
      r1_v = r1verts;      
      
      for (i=0;icolor = make_d3d_color(r1_v->r,r1_v->g,r1_v->b);
        
        dx_v++;
        r1_v++;
      }      
      break;
  }    
                                                                         
  //add alpha - check constant alpha 1st
  if ((c->alpha_mode & R1_ALPHA_CONSTANT) && !(c->shade_mode==R1_SHADE_DISABLED))
  {
    dx_v = dxverts;
    r1_v = r1verts;          

    alpha = c->const_color & 0xFF000000;

    for (i=0;icolor |= alpha;
        
      dx_v++;
      r1_v++;
    }    
  }
  else
  if (c->alpha_mode & R1_ALPHA_LINEAR)
  {
    dx_v = dxverts;
    r1_v = r1verts;    

    for (i=0;icolor |= (f_0_1_to_i_0_255(r1_v->a) << 24);
        
      dx_v++;
      r1_v++;
    }
  }  
}                                                                        

sw32 used_verts   = 0;
sw32 used_indices = 0;

#define DX5_VERT_BUF_SIZE  128
#define DX5_INDEX_BUF_SIZE 128

D3DTLVERTEX r1_dx5_tmp_verts[DX5_VERT_BUF_SIZE];
WORD        r1_dx5_tmp_indices[DX5_INDEX_BUF_SIZE];

void init_d3d_vert_buffer()
{
  sw32 i;
  for (i=0; i<128; i++)
  {
    r1_dx5_tmp_verts[i].specular = 0;
  }
  
  used_verts   = 0;
  used_indices = 0;
}

void r1_dx5_class::flush_vert_buffer()
{
#ifdef USE_BUFFER
  if (states_have_changed && (used_verts!=0))
  {
    pf_dx5_drawprimitive.start();
    d3d_device->DrawIndexedPrimitive(D3DPT_TRIANGLELIST,
                                     D3DVT_TLVERTEX,
                                     (void *)r1_dx5_tmp_verts,
                                     used_verts,
                                     r1_dx5_tmp_indices,
                                     used_indices,
                                     D3DDP_DONOTCLIP | D3DDP_DONOTUPDATEEXTENTS);  
    pf_dx5_drawprimitive.stop();
    
    used_verts   = 0;
    used_indices = 0;

    states_have_changed = i4_F;
  }
#endif
}

void r1_dx5_class::render_poly(int t_verts, r1_vert *verts)
{   
  if (t_verts > DX5_VERT_BUF_SIZE)  return;

#ifdef USE_BUFFER
  sw32 t_index = (t_verts-2)*3;
    
  if (t_index > DX5_INDEX_BUF_SIZE) return;
  
  if (t_verts + used_verts   > DX5_VERT_BUF_SIZE ||
      t_index + used_indices > DX5_INDEX_BUF_SIZE)
  {
    states_have_changed = i4_T;
    flush_vert_buffer();
  }
  
#endif

  sw32 i;

  pf_dx5_vertex_setup.start();
  if (color_tint_on)
  {
    for (i=0; iDrawPrimitive(D3DPT_TRIANGLEFAN,
                            D3DVT_TLVERTEX,
                            (void *)r1_dx5_tmp_verts,
                            t_verts,
                            D3DDP_DONOTCLIP | D3DDP_DONOTUPDATEEXTENTS);

  pf_dx5_drawprimitive.stop();

#endif
}

void r1_dx5_class::render_pixel(r1_vert *pixel)
{
}

void r1_dx5_class::render_lines(int t_lines, r1_vert *verts)
{
  if (t_lines+1>256) return;
  D3DTLVERTEX dx_verts[256];

  sw32 i;

  for (i=0;iDrawPrimitive(D3DPT_LINELIST,
                              D3DVT_TLVERTEX,
                              (void *)&dx_verts[i],
                              2,
                              D3DDP_DONOTCLIP);  
  }
}

void r1_dx5_class::clear_area(int x1, int y1, int x2, int y2, w32 color, float z)
{  
  RECT    rect;
  HRESULT res;
  
  rect.left   = x_off + x1;
  rect.top    = y_off + y1;  
  rect.right  = x_off + x2+1;
  rect.bottom = y_off + y2+1;

  DDBLTFX fx;
  memset(&fx,0,sizeof(DDBLTFX));
  fx.dwSize = sizeof(DDBLTFX);
    
  if (write_mask & R1_WRITE_COLOR)
  {
    fx.dwFillColor = color;

    dx5_common.back_surface->Blt(&rect,NULL,NULL,DDBLT_COLORFILL,&fx);
  }

  if (write_mask & R1_WRITE_W)
  {
    if (z == dx5_near_z)
    {  
      fx.dwFillDepth = 0xFFFF;
    }
    else
    if (z == dx5_far_z)
    {
      fx.dwFillDepth = 0;
    }
    else
      fx.dwFillDepth = i4_f_to_i((1.f/(float)z) * dx5_w_scale * (float)0xFFFF);

    res = zbuffer_surface->Blt(&rect,NULL,NULL,DDBLT_DEPTHFILL,&fx);
  }
}

i4_image_class *r1_dx5_class::create_compatible_image(w16 w, w16 h)
{  
  return i4_create_image(w,h,i4_dx5_display->get_palette());
}


// void r1_dx5_class::copy_part(i4_image_class *im,                                          
//                             int x, int y,             // position on screen
//                             int x1, int y1,           // area of image to copy 
//                             int x2, int y2)
// {
  
//   DDSURFACEDESC ddsd;
//   memset(&ddsd,0,sizeof(DDSURFACEDESC));
//   ddsd.dwSize = sizeof(DDSURFACEDESC);
//   dx5_common.back_surface->Lock(NULL,&ddsd,DDLOCK_WAIT | DDLOCK_WRITEONLY,0);  
  
//   //get frame buffer pointer
//   w8 *fb = (w8 *)ddsd.lpSurface;
//   w8 *im_src = (w8 *)im->data + x + y*im->bpl;
  
//   sw32 w_pitch = ddsd.lPitch; 
//   sw32 im_width  = x2-x1+1;
//   sw32 im_height = y2-y1+1;
//   sw32 i,j;
  
//   sw32 ix_off = i4_f_to_i(x_off);
//   sw32 iy_off = i4_f_to_i(y_off);
//   if (fb)
//   {
//     fb += ((x+ix_off)*2 + (y+iy_off)*w_pitch);
//     for (i=0;iwidth()*2;
//       fb += w_pitch;
//     }
//   }
//   dx5_common.back_surface->Unlock(NULL);
// }