The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
/*-
 * Copyright (c) 1997-2002 The Protein Laboratory, University of Copenhagen
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 * $Id$
 */
/* Created by Dmitry Karasik <dk@plab.ku.dk> */
#ifndef _APRICOT_H_
#include "apricot.h"
#endif
#include "win32\win32guts.h"
#include "Window.h"
#include "Icon.h"
#include "DeviceBitmap.h"

#ifdef __cplusplus
extern "C" {
#endif


#define  sys (( PDrawableData)(( PComponent) self)-> sysData)->
#define  dsys( view) (( PDrawableData)(( PComponent) view)-> sysData)->
#define var (( PWidget) self)->
#define HANDLE sys handle
#define DHANDLE(x) dsys(x) handle

Bool
apc_gp_init( Handle self)
{
   objCheck false;
   sys lineWidth = 1;
   sys res = guts. displayResolution;
   return true;
}

Bool
apc_gp_done( Handle self)
{
   objCheck false;
   if ( sys bm)
      if ( !DeleteObject( sys bm)) apiErr;
   if ( sys pal)
      if ( !DeleteObject( sys pal)) apiErr;
   if ( sys ps)
   {
      if ( is_apt( aptWinPS) && is_apt( aptWM_PAINT)) {
         if ( !EndPaint(( HWND) var handle, &sys paintStruc)) apiErr;
      } else if ( is_apt( aptWinPS)) {
         if ( self == application)
             dc_free();
         else {
             if ( !ReleaseDC(( HWND) var handle,  sys ps)) apiErr;
         }
      }
   }
   if ( sys linePatternLen  > 3) free( sys linePattern);

   if ( sys linePatternLen2 > 3) free( sys linePattern2);
   font_free( sys fontResource, false);
   if ( sys p256) free( sys p256);
   sys bm = nil;
   sys pal = nil;
   sys ps = nil;
   sys bm = nil;
   sys p256 = nil;
   sys fontResource = nil;
   sys linePattern = nil;
   return true;
}

void
adjust_line_end( int  x1, int  y1, int * x2, int * y2, Bool forth)
{
   if ( forth) {
      if ( x1 == *x2)
         ( y1 < *y2) ? ( *y2)++ : ( *y2)--;
      else if ( y1 == *y2)
         ( x1 < *x2) ? ( *x2)++ : ( *x2)--;
      else
      {
         //     Zinc Application Framework - W_GDIDSP.CPP COPYRIGHT (C) 1990-1998
         long tan = ( *y2 - y1) * 1000L / ( *x2 - x1);
         if ( tan < 1000 && tan > -1000) {
            int dx = *x2 - x1;
            ( dx > 0) ? dx++ : dx--;
            *x2 = x1 + dx;
            *y2 = (int)( y1 + dx * tan / 1000L);
         } else {
            int dy = *y2 - y1;
            ( dy > 0) ? dy++ : dy--;
            *x2 = (int)( x1 + dy * 1000L / tan);
            *y2 = y1 + dy;
         }
         // eo Zinc
      }
   } else {
      if ( x1 == *x2)
         ( y1 < *y2) ? ( *y2)-- : ( *y2)++;
      else if ( y1 == *y2)
         ( x1 < *x2) ? ( *x2)-- : ( *x2)++;
      else
      {
         //     Zinc Application Framework - W_GDIDSP.CPP COPYRIGHT (C) 1990-1998
         long tan = ( *y2 - y1) * 1000L / ( *x2 - x1);
         if ( tan < 1000 && tan > -1000) {
            int dx = *x2 - x1;
            ( dx > 0) ? dx-- : dx++;
            *x2 = x1 + dx;
            *y2 = (int)( y1 + dx * tan / 1000L);
         } else {
            int dy = *y2 - y1;
            ( dy > 0) ? dy-- : dy++;
            *x2 = (int)( x1 + dy * 1000L / tan);
            *y2 = y1 + dy;
         }
         // eo Zinc
      }
   }
}


#define GRAD 57.29577951

#define check_swap( parm1, parm2) if ( parm1 > parm2) { int parm3 = parm1; parm1 = parm2; parm2 = parm3;}

#define ELLIPSE_RECT (int)(x - ( dX - 1) / 2), (int)(y - dY / 2), (int)(x + dX / 2 + 1), (int)(y + (dY - 1) / 2 + 1)
#define ELLIPSE_RECT_SUPERINCLUSIVE (int)(x - ( dX - 1) / 2), (int)(y - dY / 2), (int)(x + dX / 2 + 2), (int)(y + (dY - 1) / 2 + 2)
#define ARC_COMPLETE (int)(x + dX / 2 + 1), y, (int)(x + dX / 2 + 1), y
#define ARC_ANGLED   (int)(x + cos( angleStart / GRAD) * dX / 2 + 0.5), (int)(y - sin( angleStart / GRAD) * dY / 2 + 0.5), \
                     (int)(x + cos( angleEnd / GRAD) * dX / 2 + 0.5),   (int)(y - sin( angleEnd / GRAD) * dY / 2 + 0.5)
#define ARC_ANGLED_SUPERINCLUSIVE   (int)(x + cos( angleStart / GRAD) * dX / 2 + 0.5), (int)(y - sin( angleStart / GRAD) * dY / 2 + 0.5), \
                     (int)(x + cos( angleEnd / GRAD) * dX / 2 + 1.5),   (int)(y - sin( angleEnd / GRAD) * dY / 2 + 1.5)


Bool
apc_gp_arc( Handle self, int x, int y, int dX, int dY, double angleStart, double angleEnd)
{ objCheck false; {
   int compl, needf, drawState = 0;
   Bool erratic = erratic_line( self);
   HDC     ps = sys ps;
   STYLUS_USE_PEN( ps);
   compl = arc_completion( &angleStart, &angleEnd, &needf);
   y = sys lastSize. y - y - 1;
   while( compl--) {
      if ( erratic)
         drawState = gp_arc( self, x, y, dX, dY, 0, 360, drawState);
      else
         Arc( ps, ELLIPSE_RECT, ARC_COMPLETE);
   }
   if ( !needf) return true;
   if ( erratic)
      gp_arc( self, x, y, dX, dY, angleStart, angleEnd, drawState);
   else
      if ( !Arc( ps, ELLIPSE_RECT, ARC_ANGLED)) apiErrRet;
   return true;
}}

Bool
apc_gp_bar( Handle self, int x1, int y1, int x2, int y2)
{objCheck false;{
   HDC     ps = sys ps;
   HGDIOBJ old = SelectObject( ps, hPenHollow);
   Bool ok = true;
   STYLUS_USE_BRUSH( ps);
   check_swap( x1, x2);
   check_swap( y1, y2);
   if ( !( ok = Rectangle( ps, x1, sys lastSize. y - y2 - 1, x2 + 2, sys lastSize. y - y1 + 1))) apiErr;
   SelectObject( ps, old);
   return ok;
}}

Bool
apc_gp_clear( Handle self, int x1, int y1, int x2, int y2)
{objCheck false;{
   Bool     ok = true;
   HDC      ps   = sys ps;
   HGDIOBJ  oldp = SelectObject( ps, hPenHollow);
   LOGBRUSH ers;
   HGDIOBJ  oldh;
   ers. lbStyle = PS_SOLID;
   ers. lbColor = sys lbs[ 1];
   ers. lbHatch = 0;
   oldh = CreateBrushIndirect( &ers);
   oldh = SelectObject( ps, oldh);
   if ( x1 < 0 && y1 < 0 && x2 < 0 && y2 < 0) {
      x1 = y1 = 0;
      x2 = sys lastSize. x - 1;
      y2 = sys lastSize. y - 1;
   }
   check_swap( x1, x2);
   check_swap( y1, y2);
   if ( !( ok = Rectangle( sys ps, x1, sys lastSize. y - y2 - 1, x2 + 2, sys lastSize. y - y1 + 1))) apiErr;
   SelectObject( ps, oldp);
   DeleteObject( SelectObject( ps, oldh));
   return ok;
}}

Bool
apc_gp_chord( Handle self, int x, int y, int dX, int dY, double angleStart, double angleEnd)
{objCheck false;{
   Bool ok = true;
   HDC     ps = sys ps;
   HGDIOBJ old = SelectObject( ps, hBrushHollow);
   int compl, needf, drawState = 0;
   Bool erratic = erratic_line( self);
   compl = arc_completion( &angleStart, &angleEnd, &needf);
   STYLUS_USE_PEN( ps);
   y = sys lastSize. y - y - 1;
   while( compl--) {
      if ( erratic)
         drawState = gp_arc( self, x, y,  dX, dY, 0, 360, drawState);
      else
         Arc( ps, ELLIPSE_RECT, ARC_COMPLETE);
   }
   if ( needf) {
      if ( erratic) {
         drawState = gp_arc( self, x, y,  dX, dY, angleStart, angleEnd, drawState);
         gp_line( self, ARC_ANGLED, drawState);
      } else
         if ( !( ok = Chord( ps, ELLIPSE_RECT, ARC_ANGLED))) apiErr;
   }
   SelectObject( ps, old);
   return ok;
}}

Bool
apc_gp_draw_poly( Handle self, int numPts, Point * points)
{objCheck false;{
   int i, dy = sys lastSize. y;
   for ( i = 0; i < numPts; i++)  points[ i]. y = dy - points[ i]. y - 1;
   if ( points[ 0]. x != points[ numPts - 1].x || points[ 0]. y != points[ numPts - 1].y)
      adjust_line_end( points[ numPts - 2].x, points[ numPts - 2].y, &points[ numPts - 1].x, &points[ numPts - 1].y, true);
   STYLUS_USE_PEN( sys ps);
   if ( erratic_line( self)) {
      int draw = 0;
      for ( i = 0; i < numPts - 1; i++)
         draw = gp_line( self, points[i].x, points[i].y, points[i+1].x, points[i+1].y, draw);
   } else {
      if ( !Polyline( sys ps, ( POINT*) points, numPts)) apiErrRet;
   }
   return true;
}}

Bool
apc_gp_draw_poly2( Handle self, int numPts, Point * points)
{objCheck false;{
   Bool ok = true;
   int i, dy = sys lastSize. y;
   DWORD * pts = ( DWORD *) malloc( sizeof( DWORD) * numPts);
   if ( !pts) return false;

   for ( i = 0; i < numPts; i++)  {
      points[ i]. y = dy - points[ i]. y - 1;
      pts[ i] = 2;
      if ( i & 1)
         adjust_line_end( points[ i - 1].x, points[ i - 1].y, &points[ i].x, &points[ i].y, true);
   }
   STYLUS_USE_PEN( sys ps);
   if ( erratic_line( self)) {
      for ( i = 0; i < numPts; i++)  {
         if ( i & 1)
            gp_line( self, points[ i - 1].x, points[ i - 1].y, points[ i].x, points[ i].y, 0);
      }
   } else
      if ( !( ok = PolyPolyline( sys ps, ( POINT*) points, pts, numPts/2))) apiErr;
   free( pts);
   return ok;
}}


Bool
apc_gp_ellipse( Handle self, int x, int y, int dX, int dY)
{objCheck false;{
   Bool    ok = true;
   HDC     ps = sys ps;
   HGDIOBJ old = SelectObject( ps, hBrushHollow);

   STYLUS_USE_PEN( ps);
   y = sys lastSize. y - y - 1;
   if ( erratic_line( self))
      gp_arc( self, x, y, dX, dY, 0, 360, 0);
   else {
      // if ( !( ok = Ellipse( ps, x - dX - 0.5, y - dY + 0.5, x + dX + 1.0, y + dY + 1.5))) apiErr;
      if ( !( ok = Ellipse( ps, ELLIPSE_RECT))) apiErr;
   }
   SelectObject( ps, old);
   return ok;
}}

Bool
apc_gp_fill_chord( Handle self, int x, int y, int dX, int dY, double angleStart, double angleEnd)
{objCheck false;{
   Bool ok = true;
   HDC     ps = sys ps;
   HGDIOBJ old;
   Bool   comp;
   int compl, needf;

   compl = arc_completion( &angleStart, &angleEnd, &needf);
   comp = stylus_complex( &sys stylus, ps);
   y = sys lastSize. y - y - 1;
   STYLUS_USE_BRUSH( ps);
   if ( comp) {
      old  = SelectObject( ps, hPenHollow);
      while ( compl--)
         if ( !( ok = Ellipse( ps, ELLIPSE_RECT_SUPERINCLUSIVE))) apiErr;
      if ( !( ok = !needf || Chord(
          ps, ELLIPSE_RECT_SUPERINCLUSIVE, ARC_ANGLED_SUPERINCLUSIVE
      ))) apiErr;
   } else {
      old = SelectObject( ps, CreatePen( PS_SOLID, 1, sys stylus. brush. lb. lbColor));
      while ( compl--)
         if ( !( ok = Ellipse( ps, ELLIPSE_RECT))) apiErr;
      if ( !( ok = !needf || Chord(
          ps, ELLIPSE_RECT, ARC_ANGLED_SUPERINCLUSIVE
      ))) apiErr;
   }
   old = SelectObject( ps, old);
   if ( !comp) DeleteObject( old);
   return ok;
}}

Bool
apc_gp_fill_ellipse( Handle self, int x, int y, int dX, int dY)
{objCheck false;{
   Bool ok = true;
   HDC     ps  = sys ps;
   HGDIOBJ old;
   Bool    comp = stylus_complex( &sys stylus, ps);
   STYLUS_USE_BRUSH( ps);
   y = sys lastSize. y - y - 1;
   if ( comp) {
      old  = SelectObject( ps, hPenHollow);
      if ( !( ok = Ellipse( ps, ELLIPSE_RECT_SUPERINCLUSIVE))) apiErr;
   } else {
      old = SelectObject( ps, CreatePen( PS_SOLID, 1, sys stylus. brush. lb. lbColor));
      if ( !( ok = Ellipse( ps, ELLIPSE_RECT))) apiErr;
   }
   old = SelectObject( ps, old);
   if ( !comp) DeleteObject( old);
   return ok;
}}

static Handle ctx_R22R4[] = {
  R2_COPYPEN      ,  SRCCOPY          ,
  R2_XORPEN       ,  SRCINVERT        ,
  R2_MASKPEN      ,  SRCAND           ,
  R2_MERGEPEN     ,  SRCPAINT         ,
  R2_NOTCOPYPEN   ,  NOTSRCCOPY       ,
  R2_MASKPENNOT   ,  SRCERASE         ,
  R2_MERGEPENNOT  ,  0x00DD0228       ,
  R2_MASKNOTPEN   ,  0x00220326       ,
  R2_MERGENOTPEN  ,  MERGEPAINT       ,
  R2_NOTXORPEN    ,  0x00990066       ,
  R2_NOTMASKPEN   ,  0x007700E6       ,
  R2_NOTMERGEPEN  ,  NOTSRCERASE      ,
  R2_NOP          ,  0x00AA0029       ,
  R2_BLACK        ,  BLACKNESS        ,
  R2_WHITE        ,  WHITENESS        ,
  R2_NOT          ,  DSTINVERT        ,
  endCtx
};

Bool
apc_gp_fill_poly( Handle self, int numPts, Point * points)
{Bool ok = true; objCheck false;{
   HDC     ps = sys ps;
   int i,  dy = sys lastSize. y;

   for ( i = 0; i < numPts; i++) points[ i]. y = dy - points[ i]. y - 1;

   if ( !stylus_complex( &sys stylus, ps)) {
      HPEN old = SelectObject( ps, CreatePen( PS_SOLID, 1, sys stylus. brush. lb. lbColor));
      STYLUS_USE_BRUSH( ps);
      if ( !( ok = Polygon( ps, ( POINT *) points, numPts))) apiErr;
      DeleteObject( SelectObject( ps, old));
   } else {
      int dx    = sys lastSize. x;
      int rop      = ctx_remap_def( GetROP2( ps), ctx_R22R4, true, SRCCOPY);
      Point bound  = {0,0};
      Point trans;
      HBITMAP bmMask, bmSrc, bmJ;
      HDC dc;
      HGDIOBJ old1, oldelta;
      Bool db = is_apt( aptDeviceBitmap) || is_apt( aptBitmap);
      trans. x = dx;
      trans. y = dy;
      for ( i = 0; i < numPts; i++)  {
         if ( points[ i]. x > bound. x) bound. x = points[ i]. x;
         if ( points[ i]. y > bound. y) bound. y = points[ i]. y;
         if ( points[ i] .x < trans. x) trans. x = points[ i]. x;
         if ( points[ i] .y < trans. y) trans. y = points[ i]. y;
      }
      if (( trans. x == dx) || ( trans. y == dy)) return false;
      if ( bound. x > dx) bound. x = dx;
      if ( bound. y > dy) bound. y = dy;
      for ( i = 0; i < numPts; i++)  {
         points[ i]. x -= trans. x;
         points[ i]. y -= trans. y;
      }
      bound. x -= trans. x - 1;
      bound. y -= trans. y - 1;

      if ( !( dc  = dc_compat_alloc( ps))) apiErrRet;
      if ( db) {
         if ( !( ps = dc_alloc())) { // fact that if dest ps is memory dc, CCB will result mono-bitmap
            dc_compat_free();
            return false;
         }
      }
      if ( !( bmSrc  = CreateCompatibleBitmap( ps, bound. x, bound. y))) {
         apiErr;
         if ( db) dc_free();
         dc_compat_free();
         return false;
      }
      if ( db) {
         dc_free();
         ps = sys ps;
      }
      if ( !( bmMask = CreateBitmap( bound. x, bound. y, 1, 1, nil))) {
         apiErr;
         dc_compat_free();
         return false;
      }
      bmJ = SelectObject( dc, bmSrc);
      old1 = SelectObject( dc, sys stylusResource-> hbrush);
      oldelta = SelectObject( dc, hPenHollow);
      Rectangle( dc, 0, 0, bound. x + 1, bound. y + 1);
      SelectObject( dc, oldelta);
      SelectObject( dc, old1);
      SelectObject( dc, bmMask);
      SetROP2( dc, R2_WHITE);
      Rectangle( dc, 0, 0, bound. x, bound. y);
      SetROP2( dc, R2_BLACK);
      if ( !( ok = Polygon( dc, ( POINT *) points, numPts))) apiErr;
      SelectObject( dc, bmSrc);
      if ( !( ok &= MaskBlt( ps, trans. x, trans. y, bound. x, bound. y, dc, 0, 0, bmMask, 0, 0,
               MAKEROP4( 0x00AA0029, rop)))) apiErr;
      SelectObject( dc, bmJ);
      dc_compat_free();
      DeleteObject( bmMask);
      DeleteObject( bmSrc);
   }
}return ok;}

Bool
apc_gp_fill_sector( Handle self, int x, int y, int dX, int dY, double angleStart, double angleEnd)
{objCheck false;{
   Bool ok = true;
   HDC     ps = sys ps;
   HGDIOBJ old;
   int newY  = sys lastSize. y - y - 1;
   POINT   pts[ 3];
   Bool comp;
   int compl, needf;

   compl = arc_completion( &angleStart, &angleEnd, &needf);
   comp = stylus_complex( &sys stylus, ps);

   pts[ 0]. x = x + cos( angleEnd / GRAD) * dX / 2 + 0.5;
   pts[ 0]. y = newY - sin( angleEnd / GRAD) * dY / 2 + 0.5;
   pts[ 1]. x = x + cos( angleStart / GRAD) * dX / 2 + 0.5;
   pts[ 1]. y = newY - sin( angleStart / GRAD) * dY / 2 + 0.5;

   STYLUS_USE_BRUSH( ps);
   y = newY;
   if ( comp) {
      old = SelectObject( ps, hPenHollow);
      while ( compl--)
         if ( !( ok = Ellipse( ps, ELLIPSE_RECT_SUPERINCLUSIVE))) apiErr;
      if ( !( ok = !needf || Pie(
          ps, ELLIPSE_RECT_SUPERINCLUSIVE,
          pts[ 1]. x, pts[ 1]. y,
          pts[ 0]. x, pts[ 0]. y
      ))) apiErr;
   } else {
      old = SelectObject( ps, CreatePen( PS_SOLID, 1, sys stylus. brush. lb. lbColor));
      while ( compl--)
         if ( !( ok = Ellipse( ps, ELLIPSE_RECT))) apiErr;
      if ( !( ok = !needf || Pie(
          ps, ELLIPSE_RECT,
          pts[ 1]. x, pts[ 1]. y,
          pts[ 0]. x, pts[ 0]. y
      ))) apiErr;
   }
   old = SelectObject( ps, old);
   if ( !comp) DeleteObject( old);
   return ok;
}}

Bool
apc_gp_flood_fill( Handle self, int x, int y, Color borderColor, Bool singleBorder)
{objCheck false;{
   HDC ps = sys ps;
   STYLUS_USE_BRUSH( ps);
   if ( !ExtFloodFill( ps, x, sys lastSize. y - y - 1, remap_color( borderColor, true),
      singleBorder ? FLOODFILLSURFACE : FLOODFILLBORDER)) apiErrRet;
   return true;
}}

Color
apc_gp_get_pixel( Handle self, int x, int y)
{objCheck clInvalid;{
   COLORREF c = GetPixel( sys ps, x, sys lastSize. y - y - 1);
   if ( c == CLR_INVALID) return clInvalid;
   return remap_color(( Color) c, false);
}}

ApiHandle
apc_gp_get_handle( Handle self)
{
   objCheck 0;
   return ( ApiHandle) sys ps;
}


Bool
apc_gp_line( Handle self, int x1, int y1, int x2, int y2)
{objCheck false;{
   HDC ps = sys ps;

   STYLUS_USE_PEN( ps);

   adjust_line_end( x1, y1, &x2, &y2, true);
   y1 = sys lastSize. y - y1 - 1;
   y2 = sys lastSize. y - y2 - 1;
   if ( erratic_line( self))
      gp_line( self, x1, y1, x2, y2, 0);
   else {
      MoveToEx( ps, x1, y1, nil);
      if ( !LineTo( ps, x2, y2)) apiErrRet;
   }
   return true;
}}

Bool
apc_gp_put_image( Handle self, Handle image, int x, int y, int xFrom, int yFrom, int xLen, int yLen, int rop)
{
   return apc_gp_stretch_image ( self, image, x, y, xFrom, yFrom, xLen, yLen, xLen, yLen, rop);
}

Bool
apc_gp_rectangle( Handle self, int x1, int y1, int x2, int y2)
{objCheck false;{
   Bool ok = true;
   HDC     ps = sys ps;
   HGDIOBJ old = SelectObject( ps, hBrushHollow);
   STYLUS_USE_PEN( ps);
   check_swap( x1, x2);
   if ( erratic_line( self)) {
      int draw;
      y1 = sys lastSize. y - y1 - 1;
      y2 = sys lastSize. y - y2 - 1;
      check_swap( y1, y2);
      draw = gp_line( self, x2, y1, x1 + 1, y1, 0);
      draw = gp_line( self, x1, y1, x1, y2 - 1, draw);
      draw = gp_line( self, x1, y2, x2 - 1, y2, draw);
      gp_line( self, x2, y2, x2, y1 + 1, draw);
   } else {
      check_swap( y1, y2);
      if ( sys stylus. pen. lopnWidth. x > 1 &&
           (sys stylus. pen. lopnWidth. x % 2) == 0
	 ) {
	 /* change up-winding to down-winding */
	 y1--;
	 y2--;
      }
      if ( !( ok = Rectangle( sys ps, x1, sys lastSize. y - y1, x2 + 1, sys lastSize. y - y2 - 1))) apiErr;
   }
   SelectObject( ps, old);
   return ok;
}}

Bool
apc_gp_sector( Handle self, int x, int y, int dX, int dY, double angleStart, double angleEnd)
{objCheck false;{
   Bool ok = true;
   HDC     ps = sys ps;
   int compl, needf, newY = sys lastSize. y - y - 1, drawState = 0;
   Bool erratic = erratic_line( self);
   POINT   pts[ 2];
   HGDIOBJ old;

   compl = arc_completion( &angleStart, &angleEnd, &needf);
   old = SelectObject( ps, hBrushHollow);
   pts[ 0]. x = x + cos( angleEnd / GRAD) * dX / 2 + 0.5;
   pts[ 0]. y = newY - sin( angleEnd / GRAD) * dY / 2 + 0.5;
   pts[ 1]. x = x + cos( angleStart / GRAD) * dX / 2 + 0.5;
   pts[ 1]. y = newY - sin( angleStart / GRAD) * dY / 2 + 0.5;
   STYLUS_USE_PEN( ps);
   y = newY;

   while( compl--) {
      if ( erratic)
         drawState = gp_arc( self, x, y, dX, dY, 0, 360, drawState);
      else
         Arc( ps, ELLIPSE_RECT, ARC_COMPLETE);
   }
   if ( needf) {
      if ( erratic) {
         drawState = gp_arc( self, x, y, dX, dY, angleStart, angleEnd, drawState);
         drawState = gp_line( self, pts[ 1]. x, pts[ 1]. y, x, y, drawState);
         gp_line( self, x, y, pts[ 0]. x, pts[ 0]. y, drawState);
      } else
         if ( !( ok = Pie(
             ps, ELLIPSE_RECT,
             pts[ 1]. x, pts[ 1]. y,
             pts[ 0]. x, pts[ 0]. y
         ))) apiErr;
   }

   SelectObject( ps, old);
   return ok;
}}

Bool
apc_gp_set_pixel( Handle self, int x, int y, Color color)
{
   objCheck false;
   SetPixelV( sys ps, x, sys lastSize. y - y - 1, remap_color( color, true));
   return true;
}


static Handle ctx_rop2R4[] = {
  ropCopyPut      ,  SRCCOPY          ,
  ropXorPut       ,  SRCINVERT        ,
  ropAndPut       ,  SRCAND           ,
  ropOrPut        ,  SRCPAINT         ,
  ropNotPut       ,  NOTSRCCOPY       ,
  ropNotDestAnd   ,  SRCERASE         ,
  ropNotDestOr    ,  0x00DD0228       ,
  ropNotSrcAnd    ,  0x00220326       ,
  ropNotSrcOr     ,  MERGEPAINT       ,
  ropNotXor       ,  0x00990066       ,
  ropNotAnd       ,  0x007700E6       ,
  ropNotOr        ,  NOTSRCERASE      ,
  ropNoOper       ,  0x00AA0029       ,
  ropBlackness    ,  BLACKNESS        ,
  ropWhiteness    ,  WHITENESS        ,
  ropInvert       ,  DSTINVERT        ,
  endCtx
};

/*
static void dc2screen( HDC dc, Handle self)
{
   HDC xdc = dc_alloc();
   if ( !BitBlt( xdc, 0, 0, var w, var h, dc, 0, 0, SRCCOPY)) apiErr;
   dc_free();
}
*/

Bool
apc_gp_stretch_image( Handle self, Handle image, int x, int y, int xFrom, int yFrom, int xDestLen, int yDestLen, int xLen, int yLen, int rop)
{objCheck false;{
   PIcon  i    = ( PIcon) image;
   Handle deja = image;
   int    ly   = sys lastSize. y;
   HDC    xdc  = sys ps;
   HPALETTE p1, p2 = nil, p3;
   HBITMAP  b1;
   HDC dc;
   DWORD theRop;
   Bool ok = true, db, dcmono = false;
   POINT tr = {0, 0};

   COLORREF oFore = 0, oBack = 0;

   dobjCheck(image) false;
   db = dsys( image) options. aptDeviceBitmap || i-> options. optInDraw;

   if ( xLen == 0 || yLen == 0) return false;

   // Determinig whether we have bitmap adapted for output or it's just bare bits
   if ( db) {
      if (
          ( guts. displayBMInfo. bmiHeader. biBitCount == 8) &&
          ( !is_apt( aptCompatiblePS)) &&
          // ( is_apt( aptWinPS) || is_apt( aptBitmap))
          is_apt( aptBitmap)
         )
      {
         // There is a big uncertainity about 8-bit BitBlt's, based on fact that
         // memory DCs aren't really compatible with screen DCs, - correct results
         // could be guaranteed only when you Blt source DC = CreateCompatibleDC( dest DC),
         // not when source = CCDC( thirdDC), dest = CCDC( thirdDC) - that's right, I mean it!
         // or just SetDIBits; - latter is slow, stupid, memory-greedy - but surely can be trusted.
         Bool ok;
         Handle img      = ( Handle) create_object("Prima::Image", "");
         HDC adc         = dsys( image) ps;
         HBITMAP abitmap = dsys( image) bm;
         dsys( img) ps   = dsys( image) ps;
         dsys( img) bm   = dsys( image) bm;
         image_query_bits( img, true);
         dsys( img) ps   = adc;
         dsys( img) bm   = abitmap;
         ok = apc_gp_stretch_image( self, img, x, y, xFrom, yFrom, xDestLen, yDestLen, xLen, yLen, rop);
         Object_destroy( img);
         return ok;
      }

      dc = dsys( image) ps;
      SetViewportOrgEx( dc, 0, 0, &tr);
   } else {
      if ( is_apt( aptCompatiblePS))
         dc = CreateCompatibleDC( xdc);
      else
         dc = nil;
      if ( dc) {
         if ( dsys( image) bm == nil) {
            image_destroy_cache( image); // if palette still exists
            deja = image_enscreen( image, self);
            if ( !image_set_cache( deja, image)) {
	       // we're low on memory, reverting to StretchDIBits
	       DeleteDC( dc);
	       dc = nil;
	    }
         }
      } else
         deja = image_enscreen( image, self);
   }

   // actions for mono images
   if ( dsys( image) options. aptDeviceBitmap) {
      if ((( PDeviceBitmap) image)-> monochrome)
         STYLUS_USE_TEXT( sys ps);
   } else
      dcmono = i-> options. optInDraw ?  ( dsys( image) bpp == 1) : (( i-> type & imBPP) == 1);

   if ( dcmono) {
      PRGBColor pal = i-> palette;
      oFore  = GetTextColor( sys ps);
      oBack  = GetBkColor( sys ps);
      SetTextColor( sys ps,
         i-> palSize > 0
         ? RGB( pal[0].r, pal[0].g, pal[0].b)
         : RGB( 0, 0, 0));
      SetBkColor( sys ps,
         i-> palSize > 1
         ? RGB( pal[1].r, pal[1].g, pal[1].b)
         : RGB( 0xff, 0xff, 0xff));
   }

   // Winsloth 95 is morbid, but 98 is yet formidable!
   if ( IS_WIN95) {
      if ( xLen + xFrom > i-> w) {
         xDestLen = xDestLen * ( i-> w - xFrom) / xLen;
         xLen = i-> w - xFrom;
      }
      if ( yFrom < 0) {
         y -= yFrom * yDestLen / yLen;
         yDestLen += yFrom * yDestLen / yLen;
         yLen  += yFrom;
         yFrom = 0;
      }
   }

   // if image is actually icon, drawing and-mask
   if ( kind_of( deja, CIcon)) {
      XBITMAPINFO xbi = {
         { sizeof( BITMAPINFOHEADER), 0, 0, 1, 1, BI_RGB, 0, 0, 0, 2, 2},
         { {0,0,0,0}, {255,255,255,0}}
      };
      xbi. bmiHeader. biWidth = i-> w;
      xbi. bmiHeader. biHeight = i-> h;
      if ( StretchDIBits(
         xdc, x, ly - y - yDestLen, xDestLen, yDestLen, xFrom, yFrom, xLen, yLen,
         i-> mask, ( BITMAPINFO*) &xbi, DIB_RGB_COLORS, SRCAND
         ) == GDI_ERROR) {
         ok = false;
         apiErr;
      }
      theRop = SRCINVERT;
   } else {
      theRop = ctx_remap_def( rop, ctx_rop2R4, true, SRCCOPY);
   }

   // saving bitmap and palette ( if available) to both dc and xdc.
   p3 = dsys( image) pal;

   if ( p3 && !db && dc) 
      p1 = SelectPalette( dc, p3, 0);
   else
      p1 = nil;

   if ( db)
      b1 = nil;
   else {
      if ( dc)
         b1 = SelectObject( dc, dsys( image) bm);
      else
         b1 = nil;
   }

   if ( p3) {
      p2 = SelectPalette( xdc, p3, 1);
      RealizePalette( xdc);
   }

   //
   if ( dc) {
      if ( !StretchBlt( xdc, x, ly - y - yDestLen, xDestLen, yDestLen, dc,
            xFrom, i-> h - yFrom - yLen, xLen, yLen, theRop)) {
	 ok = false;
         apiErr;
      }
   } else {
      XBITMAPINFO xbi;
      BITMAPINFO * bi = image_get_binfo( deja, &xbi);
      if ( bi-> bmiHeader. biClrUsed > 0)
         bi-> bmiHeader. biClrUsed = bi-> bmiHeader. biClrImportant = PImage(deja)-> palSize;
      if ( StretchDIBits( xdc, x, ly - y - yDestLen, xDestLen, yDestLen,
            xFrom, yFrom,
            xLen, yLen, ((PImage) deja)-> data, bi,
            DIB_RGB_COLORS, theRop) == GDI_ERROR) {
         ok = false;
         apiErr;
      }
   }

   // restoring gdiobjects back
   if ( p3) {
      if ( p1) SelectPalette( dc,  p1, 1);
   }
   if ( p2) SelectPalette( xdc, p2, 1);
   if ( b1) SelectObject( dc, b1);

   if ( dcmono) {
      SetTextColor( sys ps, oFore);
      SetBkColor( sys ps, oBack);
   }

   if ( dc && tr. x != 0 && tr. y != 0) 
      SetViewportOrgEx( dc, tr. x, tr. y, NULL);

   if ( !db) {
      if ( dc) DeleteDC( dc);
      if ( deja != image) Object_destroy( deja);
   }

   return ok;
}}

Bool
apc_gp_text_out( Handle self, const char * text, int x, int y, int len, Bool utf8 )
{objCheck false;{
   Bool ok = true;
   HDC ps = sys ps;
   int bk  = GetBkMode( ps);
   int opa = is_apt( aptTextOpaque) ? OPAQUE : TRANSPARENT;
   int div = 32768L / (var font. maximalWidth ? var font. maximalWidth : 1);
   if ( div <= 0) div = 1;

   /* Win32 has problems with text_out strings that are wider than
      32K pixel - it doesn't plot the string at all. This hack is
      although ugly, but is better that Win32 default behaviour, and
      at least can be excused by the fact that all GP spaces have
      their geometrical limits. */
   if ( len > div) len = div;

   if ( !HAS_WCHAR) utf8 = false;

   if ( utf8) {
      int mb_len;
      if ( !( text = ( char *) alloc_utf8_to_wchar( text, len, &mb_len))) return false;
      len = mb_len;
   }      

   if ( GetROP2( sys ps) != R2_COPYPEN) {
      STYLUS_USE_BRUSH( ps);
      BeginPath(ps);
      if ( utf8) 
         TextOutW( ps, x, sys lastSize. y - y, ( U16*)text, len);
      else
         TextOutA( ps, x, sys lastSize. y - y, text, len);
      EndPath(ps);
      FillPath(ps);
   } else {
      STYLUS_USE_TEXT( ps);
      if ( opa != bk) SetBkMode( ps, opa);

      if ( utf8) {
         if ( !( ok = TextOutW( ps, x, sys lastSize. y - y, ( U16*)text, len))) apiErr;
      } else {
         if ( !( ok = TextOutA( ps, x, sys lastSize. y - y, text, len))) apiErr;
      }
      if ( opa != bk) SetBkMode( ps, bk);
   }
   if ( utf8) free(( char *) text);
   return ok;
}}


// This procedure is about to compensate morbid Win95 behavior when
// calling GetCharABCWidthsFloat() - it returns ERROR_CALL_NOT_IMPLEMENTED. psst.
static BOOL
gp_GetCharABCWidthsFloat( HDC dc, UINT iFirstChar, UINT iLastChar, LPABCFLOAT lpABCF, Bool unicode)
{
   UINT i, d = iLastChar - iFirstChar + 1;
   TEXTMETRICW tm;

   if ( IS_NT)
      return unicode ? 
         GetCharABCWidthsFloatW( dc, iFirstChar, iLastChar, lpABCF) :
         GetCharABCWidthsFloatA( dc, iFirstChar, iLastChar, lpABCF);

   if ( iFirstChar > iLastChar)  // checking bound as far as we can
      return FALSE;

   if ( !HAS_WCHAR) unicode = false;
   
   if ( !gp_GetTextMetrics( dc, &tm)) // determining font
      return FALSE;

   if ( tm. tmPitchAndFamily & TMPF_TRUETYPE) {
      ABC *abc;
      int charExtra;
      INT fb;
      
      if ( unicode) {
         if ( !GetCharWidthW( dc, iFirstChar, iFirstChar, &fb))
            return FALSE;
      } else {
         if ( !GetCharWidthA( dc, iFirstChar, iFirstChar, &fb))
            return FALSE;
      }

      if ( !( abc = malloc( d * sizeof( ABC)))) 
         return FALSE;

      if ( unicode) {
         if ( !GetCharABCWidthsW( dc, iFirstChar, iLastChar, abc))
            apiErrRet;
      } else {
         if ( !GetCharABCWidthsA( dc, iFirstChar, iLastChar, abc))
            apiErrRet;
      }

      charExtra = fb - tm. tmOverhang - ( abc[0].abcA + abc[0].abcB + abc[0].abcC);
      if ( charExtra < 0) charExtra = 0;

      for ( i = 0; i <= iLastChar - iFirstChar; i++) {
         lpABCF[i]. abcfA = abc[ i]. abcA;
         lpABCF[i]. abcfB = abc[ i]. abcB + charExtra;
         lpABCF[i]. abcfC = abc[ i]. abcC;
      }

      free( abc);
   } else {
      INT * fb;

      if ( !( fb = malloc( d * sizeof( INT)))) 
         return FALSE;

      if ( unicode) {
         if ( !GetCharWidthW( dc, iFirstChar, iLastChar, fb)) {// checking full widths
            free( fb);
            return FALSE;
         }
      } else {
         if ( !GetCharWidthA( dc, iFirstChar, iLastChar, fb)) {// checking full widths
            free( fb);
            return FALSE;
         }
      }

      memset( lpABCF, 0, sizeof( ABCFLOAT) * ( iLastChar - iFirstChar + 1));

      for ( i = 0; i <= iLastChar - iFirstChar; i++) {
         lpABCF[i]. abcfA = tm. tmOverhang;
         lpABCF[i]. abcfB = fb[ i] - tm. tmOverhang;
         lpABCF[i]. abcfC = -tm. tmOverhang;
      }

      free( fb);
   }
   
   return TRUE;
}

#define TM(field) to->field = from->field
void
textmetric_c2w( LPTEXTMETRICA from, LPTEXTMETRICW to)
{
    TM(tmHeight); TM(tmAscent); TM(tmDescent); TM(tmInternalLeading);
    TM(tmExternalLeading); TM(tmAveCharWidth); TM(tmMaxCharWidth);
    TM(tmWeight); TM(tmOverhang); TM(tmDigitizedAspectX);
    TM(tmDigitizedAspectY); TM(tmFirstChar); TM(tmLastChar);
    TM(tmDefaultChar); TM(tmBreakChar); TM(tmItalic); TM(tmUnderlined);
    TM(tmStruckOut); TM(tmPitchAndFamily); TM(tmCharSet);
}
#undef TM
                

BOOL
gp_GetTextMetrics( HDC dc, LPTEXTMETRICW tm)
{
   BOOL ret;
   TEXTMETRICA a;
   if ( HAS_WCHAR)
      return GetTextMetricsW( dc, tm);
   ret = GetTextMetricsA( dc, &a);
   textmetric_c2w( &a, tm);
   return ret;
}

PFontABC
apc_gp_get_font_abc( Handle self, int first, int last, Bool unicode)
{objCheck nil;{
   int i;
   ABCFLOAT *f2;
   PFontABC  f1;

   f1 = ( PFontABC) malloc(( last - first + 1) * sizeof( FontABC));
   if ( !f1) return nil;
   f2 = ( ABCFLOAT*) malloc(( last - first + 1) * sizeof( ABCFLOAT));
   if ( !f2) {
      free( f1);
      return nil;
   }

   if ( !gp_GetCharABCWidthsFloat( sys ps, first, last, f2, unicode)) apiErr;
   for ( i = 0; i <= last - first; i++) {
      f1[i].a = f2[i].abcfA;
      f1[i].b = f2[i].abcfB;
      f1[i].c = f2[i].abcfC;
   }
   free( f2);
   return f1;
}}

static Handle ipa_ranges[] = {
  0x0250 , 0x02AF,	// 4  IPA Extensions
  0x1D00 , 0x1D7F,  //    Phonetic Extensions
  0x1D80 , 0x1DBF,  //    Phonetic Extensions Supplement
};

static Handle bopomoto_ranges[] = {
  0x3100 , 0x312f,  // 51 Bopomofo  
  0x31a0 , 0x31bf,  //    Extended Bopomofo 
};

static Handle cjk_ranges[] = {
  0x4e00 , 0x9fff,  // 59 CJK Unified Ideographs
  0x2e80 , 0x2eff,  //    CJK Radicals Supplement
  0x2f00 , 0x2fdf,  //    Kangxi Radicals
  0x2ff0 , 0x2fff,  //    Ideographic Description
  0x3400 , 0x4dbf,  //    CJK Unified Ideograph Extension A 
};  

static Handle unicode_subranges[ 84 * 2] = {
  0x0020 , 0x007e,  // 0  Basic Latin 
  0x0080 , 0x00ff,  // 1  Latin-1 Supplement 
  0x0100 , 0x017f,  // 2  Latin Extended-A 
  0x0180 , 0x024f,  // 3  Latin Extended-B 
        3, (Handle) &ipa_ranges,
  0x02b0 , 0x02ff,  // 5  Spacing Modifier Letters 
  0x0300 , 0x036f,  // 6  Combining Diacritical Marks 
  0x0370 , 0x03ff,  // 7  Basic Greek 
       0 ,      0,  // 8  Reserved  
  0x0400 , 0x04ff,  // 9  Cyrillic 
  0x0530 , 0x058f,  // 10 Armenian 
  0x0590 , 0x05ff,  // 11 Basic Hebrew 
       0 ,      0,  // 12 Reserved  
  0x0600 , 0x06ff,  // 13 Basic Arabic 
       0 ,      0,  // 14 Reserved  
  0x0900 , 0x097f,  // 15 Devanagari 
  0x0980 , 0x09ff,  // 16 Bengali 
  0x0a00 , 0x0a7f,  // 17 Gurmukhi 
  0x0a80 , 0x0aff,  // 18 Gujarati 
  0x0b00 , 0x0b7f,  // 19 Oriya 
  0x0b80 , 0x0bff,  // 20 Tamil 
  0x0c00 , 0x0c7f,  // 21 Telugu 
  0x0c80 , 0x0cff,  // 22 Kannada 
  0x0d00 , 0x0d7f,  // 23 Malayalam 
  0x0e00 , 0x0e7f,  // 24 Thai 
  0x0e80 , 0x0eff,  // 25 Lao 
  0x10a0 , 0x10ff,  // 26 Basic Georgian 
       0 ,      0,  // 27 Reserved  
  0x1100 , 0x11ff,  // 28 Hangul Jamo 
  0x1e00 , 0x1eff,  // 29 Latin Extended Additional 
  0x1f00 , 0x1fff,  // 30 Greek Extended 
  0x2000 , 0x206f,  // 31 General Punctuation 
  0x2070 , 0x209f,  // 32 Subscripts and Superscripts 
  0x20a0 , 0x20cf,  // 33 Currency Symbols 
  0x20d0 , 0x20ff,  // 34 Combining Diacritical Marks for Symbols 
  0x2100 , 0x214f,  // 35 Letter-like Symbols 
  0x2150 , 0x218f,  // 36 Number Forms 
  0x2190 , 0x21ff,  // 37 Arrows 
  0x2200 , 0x22ff,  // 38 Mathematical Operators 
  0x2300 , 0x23ff,  // 39 Miscellaneous Technical 
  0x2400 , 0x243f,  // 40 Control Pictures 
  0x2440 , 0x245f,  // 41 Optical Character Recognition 
  0x2460 , 0x24ff,  // 42 Enclosed Alphanumerics 
  0x2500 , 0x257f,  // 43 Box Drawing 
  0x2580 , 0x259f,  // 44 Block Elements 
  0x25a0 , 0x25ff,  // 45 Geometric Shapes 
  0x2600 , 0x26ff,  // 46 Miscellaneous Symbols 
  0x2700 , 0x27bf,  // 47 Dingbats 
  0x3000 , 0x303f,  // 48 Chinese, Japanese, and Korean (CJK) Symbols and Punctuation 
  0x3040 , 0x309f,  // 49 Hiragana 
  0x30a0 , 0x30ff,  // 50 Katakana 
       2,  (Handle) &bopomoto_ranges,
  0x3130 , 0x318f,  // 52 Hangul Compatibility Jamo 
  0x3190 , 0x319f,  // 53 CJK Miscellaneous 
  0x3200 , 0x32ff,  // 54 Enclosed CJK Letters and Months 
  0x3300 , 0x33ff,  // 55 CJK Compatibility 
  0xac00 , 0xd7a3,  // 56 Hangul 
  0xd800 , 0xdfff,  // 57 Surrogates
       0 ,      0,  // 58 Reserved  
       5,  (Handle) &cjk_ranges,
  0xe000 , 0xf8ff,  // 60 Private Use Area 
  0xf900 , 0xfaff,  // 61 CJK Compatibility Ideographs 
  0xfb00 , 0xfb4f,  // 62 Alphabetic Presentation Forms 
  0xfb50 , 0xfdff,  // 63 Arabic Presentation Forms-A 
  0xfe20 , 0xfe2f,  // 64 Combining Half Marks 
  0xfe30 , 0xfe4f,  // 65 CJK Compatibility Forms 
  0xfe50 , 0xfe6f,  // 66 Small Form Variants 
  0xfe70 , 0xfefe,  // 67 Arabic Presentation Forms-B 
  0xff00 , 0xffef,  // 68 Halfwidth and Fullwidth Forms 
  0xfff0 , 0xfffd,  // 69 Specials 
  0x0f00 , 0x0fcf,  // 70 Tibetan 
  0x0700 , 0x074f,  // 71 Syriac 
  0x0780 , 0x07bf,  // 72 Thaana 
  0x0d80 , 0x0dff,  // 73 Sinhala 
  0x1000 , 0x109f,  // 74 Myanmar 
  0x1200 , 0x12bf,  // 75 Ethiopic 
  0x13a0 , 0x13ff,  // 76 Cherokee 
  0x1400 , 0x14df,  // 77 Canadian Aboriginal Syllabics 
  0x1680 , 0x169f,  // 78 Ogham 
  0x16a0 , 0x16ff,  // 79 Runic 
  0x1780 , 0x17ff,  // 80 Khmer 
  0x1800 , 0x18af,  // 81 Mongolian 
  0x2800 , 0x28ff,  // 82 Braille 
  0xa000 , 0xa48c   // 83 YI, Yi Radicals 
};

static Handle ctx_CHARSET2index[] = {
  // SHIFTJIS_CHARSET   ??
  HANGEUL_CHARSET     , 28, 
  // GB2312_CHARSET     ?? 
  // CHINESEBIG5_CHARSET ??
#ifdef JOHAB_CHARSET
  GREEK_CHARSET       , 7,
  HEBREW_CHARSET      , 11,
  ARABIC_CHARSET      , 13,
  // TURKISH_CHARSET    ??
  // VIETNAMESE_CHARSET ??
  THAI_CHARSET        , 24,
  EASTEUROPE_CHARSET  , 3,
  RUSSIAN_CHARSET     , 9,
  // MAC_CHARSET        ??
  BALTIC_CHARSET      , 2,
#endif  
  endCtx
};

unsigned long *
apc_gp_get_font_ranges( Handle self, int * count)
{objCheck nil;{
   int i;
   unsigned long * ret;
   FONTSIGNATURE f;

   if ( !HAS_WCHAR) {
      TEXTMETRIC tm;
      GetTextMetrics( sys ps, &tm);
      if ( !( ret = malloc( sizeof( unsigned long) * 2)))
        return nil;
      ret[0] = tm. tmFirstChar;
      ret[1] = tm. tmLastChar;
      *count = 2;
      return ret;
   }
   
   memset( &f, 0, sizeof(f));
   i = GetTextCharsetInfo( sys ps, &f, 0);
   if ( i == DEFAULT_CHARSET)
      apiErrRet;

   if ( f. fsUsb[0] == 0 && f. fsUsb[1] == 0 && f. fsUsb[2] == 0 && f. fsUsb[3] == 0) {
      int index = ctx_remap_end( i, ctx_CHARSET2index, true);
      TEXTMETRICW tm;
      if ( !( ret = malloc( sizeof( unsigned long) * 4)))
        return nil;
      gp_GetTextMetrics( sys ps, &tm);
      if ( index == endCtx) {
         ret[0] = 0x20;
         ret[1] = 0xff;
         *count = 2;
      } else {
         *count = 0;
         if ( index > 0) {
            ret[(*count)++] = unicode_subranges[ 0];
            ret[(*count)++] = unicode_subranges[ 1];
         }
         ret[(*count)++] = unicode_subranges[ index * 2];
         ret[(*count)++] = unicode_subranges[ index * 2 + 1];
      }
      if ( ret[0] > tm. tmFirstChar) ret[0] = tm.tmFirstChar;
      return ret;
   }

   for ( i = 0; i < 84; i++) 
      if ( f. fsUsb[ i / 32] & ( 1 << (i % 32))) {
         Handle x = unicode_subranges[i * 2];
         if ( x < 0x20)
            (*count) += x * 2;
         else
            (*count) += 2;
      }

   if ( !( ret = malloc( sizeof( long) * (*count))))
      return nil;
   
   *count = 0;
   for ( i = 0; i < 84; i++) 
      if ( f. fsUsb[ i / 32] & ( 1 << (i % 32))) {
         Handle x = unicode_subranges[i * 2];
         if ( x < 0x20) {
            Handle * z = ( Handle *) unicode_subranges[i * 2 + 1];
            while ( x--) {
               ret[ (*count)++] = *(z++);
               ret[ (*count)++] = *(z++);
            }
         } else {
            ret[ (*count)++] = x;
            ret[ (*count)++] = unicode_subranges[i * 2 + 1];
         }
      }

   return ret;
}}

// gpi settings
Color
apc_gp_get_back_color( Handle self)
{
   objCheck 0;
   return remap_color( sys lbs[1], false);
}

int
apc_gp_get_bpp( Handle self)
{
   objCheck 0;
   return sys bpp;
}

Color
apc_gp_get_color( Handle self)
{
   objCheck 0;
   return remap_color( sys ps ? sys stylus. pen. lopnColor : sys lbs[0], false);
}

Rect
apc_gp_get_clip_rect( Handle self)
{
   RECT r;
   Rect rr = {0,0,0,0};
   objCheck rr;
   if ( !is_opt( optInDraw) || !sys ps) return rr;
   if ( !GetClipBox( sys ps, &r)) apiErr;
   if ( IsRectEmpty( &r)) return rr;
   rr. left   = r. left;
   rr. right  = r. right - 1;
   rr. bottom = sys lastSize. y - r. bottom;
   rr. top    = sys lastSize. y - r. top    - 1;
// rr. left   += sys transform2. x;
// rr. right  += sys transform2. x;
// rr. top    -= sys transform2. y;
// rr. bottom -= sys transform2. y;
   return rr;
}

Bool
apc_gp_get_fill_winding( Handle self)
{
   objCheck 0;
   if ( ! sys ps) return sys fillWinding;
   return GetPolyFillMode( sys ps) == WINDING;
}

static Handle ctx_le2PS_ENDCAP[] = {
    leRound,          PS_ENDCAP_ROUND             ,
    leSquare,         PS_ENDCAP_SQUARE            ,
    leFlat,           PS_ENDCAP_FLAT              ,
    endCtx
};

int
apc_gp_get_line_end( Handle self)
{
   objCheck 0;
   if ( !sys ps) return sys lineEnd;
   return ctx_remap_def( sys stylus. extPen. lineEnd, ctx_le2PS_ENDCAP, false, leRound);
}

static Handle ctx_lj2PS_JOIN[] = {
    ljRound,          PS_JOIN_ROUND             ,
    ljBevel,          PS_JOIN_BEVEL             ,
    ljMiter,          PS_JOIN_MITER             ,
    endCtx
};

int
apc_gp_get_line_join( Handle self)
{
   objCheck 0;
   if ( !sys ps) return sys lineJoin;
   return ctx_remap_def( sys stylus. extPen. lineJoin, ctx_lj2PS_JOIN, false, ljRound);
}

int
apc_gp_get_line_width( Handle self)
{
   objCheck 0;
   if ( !sys ps) return sys lineWidth;
   return sys stylus. pen. lopnWidth. x;
}

int
apc_gp_get_line_pattern( Handle self, unsigned char * buffer)
{
   objCheck 0;
   if ( !sys ps) {
      strcpy(( char *) buffer, (char*)(( sys linePatternLen > 3) ? sys linePattern : (Byte*)(&sys linePattern)));
      return sys linePatternLen;
   }

   switch ( sys stylus. pen. lopnStyle) {
   case PS_NULL:
       strcpy(( char *) buffer, "");
       return 0;
   case PS_DASH:
       strcpy(( char *) buffer, psDash);
       return 2;
   case PS_DOT:
       strcpy(( char *) buffer, psDot);
       return 2;
   case PS_DASHDOT:
       strcpy(( char *) buffer, psDashDot);
       return 4;
   case PS_DASHDOTDOT:
       strcpy(( char *) buffer, psDashDotDot);
       return 6;
   case PS_USERSTYLE:
       {
          int i;
          int len = sys stylus. extPen. patResource-> dotsCount;
          if ( len > 255) len = 255;
          for ( i = 0; i < len; i++)
             buffer[ i] = sys stylus. extPen. patResource-> dots[ i];
          return len;
       }
   default:
       strcpy(( char *) buffer, "\1");
       return 1;
   }
}

Color
apc_gp_get_nearest_color( Handle self, Color color)
{
#define quit return remap_color( GetNearestColor( sys ps, clr), false)

   XLOGPALETTE lpLoc, lpGlob;
   int locIdx, globIdx, cdiff;
   long clrGlob, clr = remap_color( color, true);
   objCheck 0;

   if ( !sys pal || ( sys bpp > 8)) quit;
   lpLoc. palNumEntries = GetPaletteEntries( sys pal, 0, 256, lpLoc. palPalEntry);
   if ( lpLoc. palNumEntries == 0)  quit;
   lpGlob. palNumEntries = GetSystemPaletteEntries( sys ps, 0, 256, lpGlob. palPalEntry);
   if ( lpGlob. palNumEntries == 0) quit;

   locIdx = palette_match_color( &lpLoc, clr, &cdiff);
   if ( cdiff >= COLOR_TOLERANCE)   quit;

   clrGlob = ARGB(
      lpLoc. palPalEntry[ locIdx]. peBlue,
      lpLoc. palPalEntry[ locIdx]. peGreen,
      lpLoc. palPalEntry[ locIdx]. peRed
   );
   globIdx = palette_match_color( &lpGlob, clrGlob, &cdiff);
   if ( cdiff >= COLOR_TOLERANCE)   quit;
   return ARGB(
      lpGlob. palPalEntry[ globIdx]. peRed,
      lpGlob. palPalEntry[ globIdx]. peGreen,
      lpGlob. palPalEntry[ globIdx]. peBlue
   );
#undef quit
}

PRGBColor
apc_gp_get_physical_palette( Handle self, int * color)
{
   XLOGPALETTE lpGlob;
   int i, nCol;
   PRGBColor r;

   *color = 0;
   objCheck nil;

   if (( GetDeviceCaps( sys ps, RASTERCAPS) & RC_PALETTE) == 0)
      return nil;

   nCol = GetDeviceCaps( sys ps, NUMCOLORS);
   if ( nCol <= 0 || nCol > 256)
      return nil;


   if ( sys pal && ( nCol > 16)) {
      XLOGPALETTE lp;
      int i, lpCount = 0;
      int map[ 256];

      lp. palNumEntries = GetPaletteEntries( sys pal, 0, 256, lp. palPalEntry);
      lpGlob. palNumEntries = GetSystemPaletteEntries( sys ps, 0, 256, lpGlob. palPalEntry);

      for ( i = 0; i < lp. palNumEntries; i++) {
         long clr = ARGB(
           lp. palPalEntry[ i]. peBlue,
           lp. palPalEntry[ i]. peGreen,
           lp. palPalEntry[ i]. peRed
         );
         int j, cdiff;
         int idx = palette_match_color( &lpGlob, clr, &cdiff);
         Bool hasmatch = 0;

         if ( cdiff >= COLOR_TOLERANCE) continue;

         if ( idx < 10 || idx > 245) continue;

         for ( j = 0; j < lpCount; j++)
            if ( map[ j] == idx) {
               hasmatch = 1;
               break;
            }
         if ( hasmatch) continue;
         map[ lpCount++] = idx;
      }

      for ( i = 0; i < lpCount; i++)
         lp. palPalEntry[ i] = lpGlob. palPalEntry[ map[ i]];
      for ( i = 0; i < lpCount; i++)
         lpGlob. palPalEntry[ i + nCol] = lp. palPalEntry[ i];
      *color = nCol + lpCount;
   } else {
      *color = GetSystemPaletteEntries( sys ps, 0, 256, lpGlob. palPalEntry);
      if (( nCol == 20) && ( *color == 256))
         *color = 20;
   }

   if ( nCol == 20) {
      int i;
      for ( i = 0; i < 10; i++)
         lpGlob. palPalEntry[ i + 10] = lpGlob. palPalEntry[ 255 - i];
   }

   r = ( PRGBColor) malloc( sizeof( RGBColor) * *color);
   if ( !r) return nil;

   for ( i = 0; i < *color; i++) {
      r[i].r = lpGlob. palPalEntry[i]. peRed;
      r[i].g = lpGlob. palPalEntry[i]. peGreen;
      r[i].b = lpGlob. palPalEntry[i]. peBlue;
   }
   return r;
}

Bool
apc_gp_get_region( Handle self, Handle mask)
{
   HRGN rgn;
   int res;
   HBITMAP bm, bmSave;
   HBRUSH  brSave;
   HDC dc;
   XBITMAPINFO xbi;
   BITMAPINFO * bi;
   RECT clipEx;

   objCheck false;
   if ( !is_opt( optInDraw) || !sys ps) return false;

   rgn = CreateRectRgn(0,0,0,0);

   res = GetClipRgn( sys ps, rgn);
   if ( res <= 0) {        // error or just no region
      DeleteObject( rgn);
      return false;
   }
   if ( !mask) {
      DeleteObject( rgn);
      return true;
   }

   GetClipBox( sys ps, &clipEx);
   OffsetRgn( rgn, sys transform2. x, sys transform2. y);
   OffsetRgn( rgn, 0,  clipEx. bottom - clipEx. top - sys lastSize.y);

   CImage( mask)-> create_empty( mask, clipEx. right, sys lastSize.y - clipEx. top, imBW);

   if ( !( dc = dc_compat_alloc(0))) return true;
   if ( !( bm = CreateBitmap( PImage( mask)-> w, PImage( mask)-> h, 1, 1, nil))) {
      dc_compat_free();
      return true;
   }

   bmSave = SelectObject( dc, bm);
   brSave = SelectObject( dc, CreateSolidBrush( RGB(0,0,0)));
   Rectangle( dc, 0, 0, PImage( mask)-> w, PImage( mask)-> h);
   DeleteObject( SelectObject( dc, CreateSolidBrush( RGB( 255, 255, 255))));
   PaintRgn( dc, rgn);
   DeleteObject( SelectObject( dc, brSave));

   bi = image_get_binfo( mask, &xbi);
   if ( !GetDIBits( dc, bm, 0, PImage( mask)-> h, PImage( mask)-> data, bi, DIB_RGB_COLORS)) apiErr;
   SelectObject( dc, bmSave);
   DeleteObject( bm);
   dc_compat_free();

   DeleteObject( rgn);

   return true;
}


Point
apc_gp_get_resolution( Handle self)
{
   Point p = guts. displayResolution;
   if ( !self) return p;
   objCheck p;
   return sys res;
}

static Handle ctx_rop2R2[] = {
   ropCopyPut       , R2_COPYPEN      ,
   ropXorPut        , R2_XORPEN       ,
   ropAndPut        , R2_MASKPEN      ,
   ropOrPut         , R2_MERGEPEN     ,
   ropNotPut        , R2_NOTCOPYPEN   ,
   ropNotDestAnd    , R2_MASKPENNOT   ,
   ropNotDestOr     , R2_MERGEPENNOT  ,
   ropNotSrcAnd     , R2_MASKNOTPEN   ,
   ropNotSrcOr      , R2_MERGENOTPEN  ,
   ropNotXor        , R2_NOTXORPEN    ,
   ropNotAnd        , R2_NOTMASKPEN   ,
   ropNotOr         , R2_NOTMERGEPEN  ,
   ropNoOper        , R2_NOP          ,
   ropBlackness     , R2_BLACK        ,
   ropWhiteness     , R2_WHITE        ,
   ropInvert        , R2_NOT          ,
   endCtx
};


int
apc_gp_get_rop( Handle self)
{
   objCheck 0;
   if ( !sys ps) return sys rop;
   return ctx_remap_def( GetROP2( sys ps), ctx_rop2R2, false, ropCopyPut);
}

int
apc_gp_get_rop2( Handle self)
{
   objCheck 0;
   if ( !sys ps) return sys rop2;
   return ( GetBkMode( sys ps) == OPAQUE) ? ropCopyPut : ropNoOper;
}

Bool
apc_gp_get_text_out_baseline( Handle self)
{
   objCheck 0;
   return is_apt( aptTextOutBaseline);
}


static int
gp_get_text_width( Handle self, const char* text, int len, Bool addOverhang, Bool wide)
{
   SIZE  sz;
   int   div, offset = 0, ret = 0;
   
   objCheck 0;
   if ( len == 0) return 0;

   if ( !HAS_WCHAR) wide = false;

   /* width more that 32K returned incorrectly by Win32 core */
   if (( div = 32768L / ( var font. maximalWidth ? var font. maximalWidth : 1)) == 0)
      div = 1;

   while ( offset < len) {
      int chunk_len = ( offset + div > len) ? ( len - offset) : div;
      if ( wide) {
         if ( !GetTextExtentPoint32W( sys ps, ( WCHAR*) text + offset, chunk_len, &sz)) apiErr;
      } else {
         if ( !GetTextExtentPoint32( sys ps, text + offset, chunk_len, &sz)) apiErr;
      }
      ret += sz. cx;
      if ( !IS_NT && offset > 0) ret -= sys tmOverhang;
      offset += div;
   }
   
   if ( addOverhang) {
      if ( sys tmPitchAndFamily & TMPF_TRUETYPE) {
         ABC abc[2];
         if ( wide) {
            GetCharABCWidthsW( sys ps, *((WCHAR*)text), *((WCHAR*)text), &abc[0]);
            GetCharABCWidthsW( sys ps, *((WCHAR*)text + len - 1), *((WCHAR*)text + len - 1), &abc[1]);
         } else {
            GetCharABCWidths( sys ps, text[ 0    ], text[ 0    ], &abc[0]);
            GetCharABCWidths( sys ps, text[ len-1], text[ len-1], &abc[1]);
         }
         if ( abc[0]. abcA < 0) ret -= abc[0]. abcA;
         if ( abc[1]. abcC < 0) ret -= abc[1]. abcC;
      } else if ( IS_NT)
         ret += sys tmOverhang;
   } else if ( !IS_NT) 
      ret -= sys tmOverhang;

   return ret;
}

int
apc_gp_get_text_width( Handle self, const char* text, int len, Bool addOverhang, Bool utf8)
{
   int ret;
   if ( !HAS_WCHAR) utf8 = false;
   if ( utf8) {
      int mb_len;
      if ( !( text = ( char *) alloc_utf8_to_wchar( text, len, &mb_len))) return 0;
      len = mb_len;
   }      
   ret = gp_get_text_width( self, text, len, addOverhang, utf8);
   if ( utf8)
      free(( char*) text);
   return ret;
}   

Point *
apc_gp_get_text_box( Handle self, const char* text, int len, Bool utf8)
{objCheck nil;{
   Point * pt = ( Point *) malloc( sizeof( Point) * 5);
   if ( !pt) return nil;

   if ( !HAS_WCHAR) utf8 = false;
   memset( pt, 0, sizeof( Point) * 5);

   if ( utf8) {
      int mb_len;
      if ( !( text = ( char *) alloc_utf8_to_wchar( text, len, &mb_len))) {
         free( pt);
         return nil;
      }
      len = mb_len;
   }

   pt[0].y = pt[2]. y = var font. ascent - 1;
   pt[1].y = pt[3]. y = - var font. descent;
   pt[4].y = pt[0]. x = pt[1].x = 0;
   pt[3].x = pt[2]. x = pt[4].x = gp_get_text_width( self, text, len, false, utf8);
   if ( len > 0 && !IS_NT) {
      pt[2].x += sys tmOverhang;
      pt[3].x += sys tmOverhang;
   }

   if ( !is_apt( aptTextOutBaseline)) {
      int i = 4, d = var font. descent;
      while ( i--) pt[ i]. y += d;
   }

   if ( sys tmPitchAndFamily & TMPF_TRUETYPE) {
      ABC abc[2];
      if ( utf8) {
         GetCharABCWidthsW( sys ps, *((WCHAR*)text), *((WCHAR*)text), &abc[0]);
         GetCharABCWidthsW( sys ps, *((WCHAR*)text + len - 1), *((WCHAR*)text + len - 1), &abc[1]);
      } else {
         GetCharABCWidths( sys ps, text[ 0    ], text[ 0    ], &abc[0]);
         GetCharABCWidths( sys ps, text[ len-1], text[ len-1], &abc[1]);
      }
      if ( abc[0]. abcA < 0) {
         pt[0].x += abc[0]. abcA;
         pt[1].x += abc[0]. abcA;
      }
      if ( abc[1]. abcC < 0) {
         pt[2].x -= abc[1]. abcC;
         pt[3].x -= abc[1]. abcC;
      }
   }

   if ( var font. direction != 0) {
      int i;
      float s = sin( var font. direction / GRAD);
      float c = cos( var font. direction / GRAD);
      for ( i = 0; i < 5; i++) {
         float x = pt[i]. x * c - pt[i]. y * s;
         float y = pt[i]. x * s + pt[i]. y * c;
         pt[i]. x = x + (( x > 0) ? 0.5 : -0.5);
         pt[i]. y = y + (( y > 0) ? 0.5 : -0.5);
      }
   }

   if ( utf8) free(( char*) text);
   
   return pt;
}}

Point
apc_gp_get_transform( Handle self)
{
   Point p = {0,0};
   objCheck p;
   if ( !sys ps) return sys transform;
   if ( !GetViewportOrgEx( sys ps, (POINT*)&p)) apiErr;
   p. y = -p. y;
   p. x += sys transform2. x;
   p. y -= sys transform2. y;
   return p;
}

Bool
apc_gp_get_text_opaque( Handle self)
{
   objCheck false;
   return is_apt( aptTextOpaque);
}

#define pal_ok ((sys bpp <= 8) && ( sys pal))

Bool
apc_gp_set_back_color( Handle self, Color color)
{
   long clr = remap_color( color, true);
   objCheck false;
   if ( sys ps) {
      PStylus s = & sys stylus;
      if ( pal_ok) clr = palette_match( self, clr);
      if ( SetBkColor( sys ps, clr) == CLR_INVALID) apiErr;
      s-> brush. backColor = clr;
      if ( s-> brush. lb. lbStyle == BS_DIBPATTERNPT)
         stylus_change( self);
   }
   sys lbs[1] = clr;
   return true;
}

Bool
apc_gp_set_clip_rect( Handle self, Rect c)
{
   HRGN rgn;
   objCheck false;
   if ( !is_opt( optInDraw) || !sys ps) return true;
   // inclusive-exclusive
   c. left   -= sys transform2. x;
   c. right  -= sys transform2. x;
   c. top    += sys transform2. y;
   c. bottom += sys transform2. y;
   check_swap( c. top, c. bottom);
   check_swap( c. left, c. right);
   if ( !( rgn = CreateRectRgn( c. left,  sys lastSize. y - c. top,
                                c. right + 1, sys lastSize. y - c. bottom - 1))) apiErrRet;
   if ( !SelectClipRgn( sys ps, rgn)) apiErr;
   if ( !DeleteObject( rgn)) apiErr;
   return true;
}

Bool
apc_gp_set_color( Handle self, Color color)
{
   long clr = remap_color( color, true);
   objCheck false;
   if ( !sys ps)
      sys lbs[0] = clr;
   else {
      PStylus s = & sys stylus;
      if ( pal_ok) clr = palette_match( self, clr);
      s-> pen. lopnColor = ( COLORREF) clr;
      if ( s-> brush. lb. lbStyle != BS_DIBPATTERNPT) s-> brush. lb. lbColor = ( COLORREF) clr;
      stylus_change( self);
   }
   return true;
}

Bool
apc_gp_set_fill_winding( Handle self, Bool fillWinding)
{
   objCheck false;
   if ( sys ps) 
      SetPolyFillMode( sys ps, fillWinding ? WINDING : ALTERNATE);
   else
      sys fillWinding = fillWinding;
   return true;
}

Bool
apc_gp_set_fill_pattern( Handle self, FillPattern pattern)
{
   objCheck false;
{
   HDC ps    = sys ps;
   PStylus s = & sys stylus;
   long *p1 = ( long*) pattern;
   long *p2 = p1 + 1;
   if ( !ps) {
      memcpy( &sys fillPattern2, pattern, sizeof( FillPattern));
      return true;
   }
   memcpy( &sys fillPattern, pattern, sizeof( FillPattern));
   if (( *p1 == 0) && ( *p2 == 0)) {
      s-> brush. lb. lbStyle = BS_SOLID;
      s-> brush. lb. lbColor = GetBkColor( ps);
      s-> brush. lb. lbHatch = 0;
      s-> brush. backColor   = 0;
      memset( s-> brush. pattern, 0, sizeof( s-> brush. pattern));
   } else if (( *p1 == 0xFFFFFFFF) && ( *p2 == 0xFFFFFFFF)) {
      s-> brush. lb. lbStyle = BS_SOLID;
      s-> brush. lb. lbColor = s-> pen. lopnColor;
      s-> brush. lb. lbHatch = 0;
      s-> brush. backColor   = 0;
      memset( s-> brush. pattern, 0, sizeof( s-> brush. pattern));
   } else {
      s-> brush. lb. lbStyle = BS_DIBPATTERNPT;
      s-> brush. lb. lbColor = DIB_RGB_COLORS;
      s-> brush. lb. lbHatch = ( LONG_PTR) &bmiHatch;
      s-> brush. backColor   = GetBkColor( ps);
      memcpy( s-> brush. pattern, pattern, sizeof( FillPattern));
   }
   stylus_change( self);
   return true;
}}

Bool
apc_gp_set_font( Handle self, PFont font)
{
   TEXTMETRICW tm;
   objCheck false;
   if ( !sys ps) return true;
   font_change( self, font);
   gp_GetTextMetrics( sys ps, &tm);
   sys tmOverhang       = tm. tmOverhang;
   sys tmPitchAndFamily = tm. tmPitchAndFamily;
   return true;
}

FillPattern *
apc_gp_get_fill_pattern( Handle self)
{
   objCheck nil;
   return sys ps ? &sys fillPattern : &sys fillPattern2;
}

Bool
apc_gp_set_line_end( Handle self, int lineEnd)
{
   objCheck false;
   if ( !sys ps) sys lineEnd = lineEnd; else {
      PStylus s         = &sys stylus;
      PEXTPEN ep        = &s-> extPen;
      ep-> lineEnd      = ctx_remap_def( lineEnd, ctx_le2PS_ENDCAP, true, PS_ENDCAP_ROUND);
      if (( ep-> actual  = stylus_extpenned( s, 0 & exsLineEnd)))
         ep-> style = stylus_get_extpen_style( s);
      stylus_change( self);
   }
   return true;
}

Bool
apc_gp_set_line_join( Handle self, int lineJoin)
{
   objCheck false;
   if ( !sys ps) sys lineJoin = lineJoin; else {
      PStylus s         = &sys stylus;
      PEXTPEN ep        = &s-> extPen;
      ep-> lineJoin     = ctx_remap_def( lineJoin, ctx_lj2PS_JOIN, true, PS_JOIN_ROUND);
      if (( ep-> actual = stylus_extpenned( s, 0 & exsLineJoin)))
         ep-> style = stylus_get_extpen_style( s);
      stylus_change( self);
   }
   return true;
}

Bool
apc_gp_set_line_width( Handle self, int lineWidth)
{
   objCheck false;
   if ( !sys ps) sys lineWidth = lineWidth; else {
      PStylus s = &sys stylus;
      if ( lineWidth < 0 || lineWidth > 8192) lineWidth = 0;
      s-> pen. lopnWidth. x = lineWidth;
      stylus_change( self);
   }
   return true;
}

Bool
apc_gp_set_line_pattern( Handle self, unsigned char * pattern, int len)
{
   objCheck false;
   if ( !sys ps) {
      if ( sys linePatternLen > 3)
         free( sys linePattern);
      if ( len > 3) {
         sys linePattern = ( unsigned char *) malloc( len);
         if ( !sys linePattern) {
            sys linePatternLen = 0;
            return false;
         }
         memcpy( sys linePattern, pattern, len);
      } else
         memcpy( &sys linePattern, pattern, len);
      sys linePatternLen = len;
   } else {
      PStylus s           = &sys stylus;
      PEXTPEN ep          = &s-> extPen;

      if ( IS_WIN95) {
         if ( sys linePatternLen2 > 3)
            free( sys linePattern2);
         if ( len > 3) {
            sys linePattern2 = ( unsigned char *) malloc( len);
            if ( !sys linePattern2) {
               sys linePatternLen2 = 0;
               return false;
            }
            memcpy( sys linePattern2, pattern, len);
         } else
            memcpy( &sys linePattern2, pattern, len);
         sys linePatternLen2 = len;
      }

      s-> pen. lopnStyle  = patres_user( pattern, len);
      if (( ep-> actual    = stylus_extpenned( s, 0 & exsLinePattern))) {
         ep-> style       = stylus_get_extpen_style( s);
         ep-> patResource = ( s-> pen. lopnStyle == PS_USERSTYLE) ?
            patres_fetch( pattern, len) : &hPatHollow;
      } else
         ep-> patResource = &hPatHollow;
      stylus_change( self);
   }
   return true;
}

Bool
apc_gp_set_palette( Handle self)
{
   HPALETTE pal;

   objCheck false;
   if ( sys p256) {
      free( sys p256);
      sys p256 = nil;
   }

   pal = palette_create( self);
   if ( sys ps) {
      if ( pal)
         SelectPalette( sys ps, pal, 0);
      else
         SelectPalette( sys ps, sys stockPalette, 1);
      RealizePalette( sys ps);
   }
   if ( sys pal) DeleteObject( sys pal);
   sys pal = pal;
   return true;
}

Bool
apc_gp_set_region( Handle self, Handle mask)
{
   HRGN rgn = nil;
   objCheck false;

   if ( !is_opt( optInDraw) || !sys ps) return true;

   rgn = region_create( mask);
   if ( !rgn) {
      SelectClipRgn( sys ps, nil);
      return true;
   }
   OffsetRgn( rgn, -sys transform2. x, -sys transform2. y);
   OffsetRgn( rgn, 0, sys lastSize.y - PImage(mask)-> h);
   SelectClipRgn( sys ps, rgn);
   DeleteObject( rgn);
   return true;
}

Bool
apc_gp_set_rop( Handle self, int rop)
{
   objCheck false;
   if ( !sys ps) { sys rop = rop; return true; }
   if ( !SetROP2( sys ps, ctx_remap_def( rop, ctx_rop2R2, true, R2_COPYPEN))) apiErr;
   return true;
}

Bool
apc_gp_set_rop2( Handle self, int rop)
{
   objCheck false;
   if ( !sys ps) { sys rop2 = rop; return true; }
   if ( rop != ropCopyPut) rop = ropNoOper;
   if ( !SetBkMode( sys ps, ( rop == ropCopyPut) ? OPAQUE : TRANSPARENT)) apiErr;
   return true;
}

Bool
apc_gp_set_transform( Handle self, int x, int y)
{
   objCheck false;
   if ( !sys ps) {
      sys transform. x = x;
      sys transform. y = y;
      return true;
   }
   if ( !SetViewportOrgEx( sys ps, x - sys transform2. x, - ( y + sys transform2. y), nil)) apiErr;
   return true;
}

Bool
apc_gp_set_text_opaque( Handle self, Bool opaque)
{
   objCheck false;
   apt_assign( aptTextOpaque, opaque);
   return true;
}


Bool
apc_gp_set_text_out_baseline( Handle self, Bool baseline)
{
   objCheck false;
   apt_assign( aptTextOutBaseline, baseline);
   if ( sys ps) SetTextAlign( sys ps, baseline ? TA_BASELINE : TA_BOTTOM);
   return true;
}

#ifdef __cplusplus
}
#endif