------------------------------------------------------------------------------
-- PEN                                                                      --
-- The Pen Graphics Package.                                                --
--                                                                          --
-- Part of BUSH                                                             --
-- Designed and Programmed by Ken O. Burtch                                 --
------------------------------------------------------------------------------
--                                                                          --
--              Copyright (C) 2001-2005 Ken O. Burtch & FSF                 --
--                                                                          --
-- This is free software;  you can  redistribute it  and/or modify it under --
-- terms of the  GNU General Public License as published  by the Free Soft- --
-- ware  Foundation;  either version 2,  or (at your option) any later ver- --
-- sion.  This is distributed in the hope that it will be useful, but WITH- --
-- OUT ANY WARRANTY;  without even the  implied warranty of MERCHANTABILITY --
-- or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License --
-- for  more details.  You should have  received  a copy of the GNU General --
-- Public License  distributed with this;  see file COPYING.  If not, write --
-- to  the Free Software Foundation,  59 Temple Place - Suite 330,  Boston, --
-- MA 02111-1307, USA.                                                      --
--                                                                          --
-- This is maintained at http://www.pegasoft.ca                             --
--                                                                          --
------------------------------------------------------------------------------
-- CVS: $Id: pen.adb,v 1.3 2005/08/17 23:46:30 ken Exp $

-- with system;
with system.address_to_access_conversions;
with ada.text_io; use ada.text_io;
-- with ada.strings.unbounded; use ada.strings.unbounded;
with bush_os.sdl; use bush_os.sdl;
with interfaces; use interfaces;
with interfaces.c; use interfaces.c;
use bush_os.sdl.SDL_Surface_Conv;
-- with gen_list;

package body pen is

---> Rectangles

procedure SetRect( r : out ARect; left, top, right, bottom : ACoordinate ) is
-- initialize a rectangle
begin
  r.left := left;
  r.top  := top;
  r.right := right;
  r.bottom := bottom;
end SetRect;

function IsEmptyRect( r : ARect ) return boolean is
begin
  return (r.left > r.right ) or (r.top > r.bottom );
end IsEmptyRect;

procedure OffsetRect( r : in out ARect; dx, dy : ACoordinateOffset ) is
-- shift a rectangle
begin
  r.left := r.left + dx;
  r.top := r.top + dy;
  r.right := r.right + dx;
  r.bottom := r.bottom + dy;
end OffsetRect;

function OffsetRect( r : in ARect; dx, dy : ACoordinateOffset ) return ARect is
-- shift a rectangle returning the resulting rectangle
  newRect : ARect;
begin
  newRect.left := r.left + dx;
  newRect.top := r.top + dy;
  newRect.right := r.right + dx;
  newRect.bottom := r.bottom + dy;
  return newRect;
end OffsetRect;

procedure InsetRect( r : in out ARect; dx, dy : ACoordinateOffset ) is
-- change the size of a rectangle
begin
  r.left := r.left + dx;
  r.top := r.top + dy;
  r.right := r.right - dx;
  r.bottom := r.bottom - dy;
end InsetRect;

function InsetRect( r : in ARect; dx, dy : ACoordinateOffset ) return ARect is
-- change the size of a rectangle returning the resulting rectangle
  newRect : ARect;
begin
  newRect.left := r.left + dx;
  newRect.top := r.top + dy;
  newRect.right := r.right - dx;
  newRect.bottom := r.bottom - dy;
  return newRect;
end InsetRect;

procedure IntersectRect( r : in out ARect; r1, r2 : ARect ) is
begin
  if r1.top > r2.top then
     r.top := r1.top;
  else
     r.top := r2.top;
  end if;
  if r1.bottom < r2.bottom then
     r.bottom := r1.bottom;
  else
     r.bottom := r2.bottom;
  end if;
  if r1.left > r2.left then
     r.left := r1.left;
  else
     r.left := r2.left;
  end if;
  if r1.right < r2.right then
     r.right := r1.right;
  else
     r.right := r2.right;
  end if;
  if IsEmptyRect( r ) then
     r := NullRect;
  end if;
end IntersectRect;

function IntersectRect( r1, r2 : in ARect ) return ARect is
  newRect : ARect;
begin
  IntersectRect( newRect, r1, r2 );
  return newRect;
end IntersectRect;

function InsideRect( Inner, Outer : in ARect ) return boolean is
-- test for one rectangle inside of another
begin
  return (Inner.left   >= Outer.left)   and then
         (Inner.top    >= Outer.top)    and then
         (Inner.right  <= Outer.right ) and then
         (Inner.bottom <= Outer.bottom );
end InsideRect;

function InRect( x, y : ACoordinate ; r : ARect ) return boolean is
-- test for a point inside of a rectangle
begin
  return (x >= r.left and x <= r.right) and then
         (y >= r.top and y <= r.bottom);
end InRect;


---> The Drawing Environment
--

function ">"( left, right : aCanvas ) return boolean is
begin
   return left.displayInfo.H_Res > right.displayInfo.H_res;
end ">";

function "="( left, right : aCanvas ) return boolean is
begin
   return left.displayInfo.H_Res = right.displayInfo.H_res;
end "=";


---> SET PEN INK
--
-- Change the drawing colour or pattern of a canvas pen.
-----------------------------------------------------------------------------

procedure setPenInk( canvas : in out aCanvas; c : AColourName ) is
begin
  SDL_MapRGB( canvas.pen.pixel,
              canvas.surface_ptr.format,
              -- SDL_RGB_Component( ARGBComponent(svga_maxcomponent-1) * ColourNames( c ).red / 100.0 ),
              -- SDL_RGB_Component( ARGBComponent(svga_maxcomponent-1) * ColourNames( c ).green / 100.0 ),
              -- SDL_RGB_Component( ARGBComponent(svga_maxcomponent-1) * ColourNames( c ).blue / 100.0 ) );
              SDL_RGB_Component( 255.0 * ColourNames( c ).red / 100.0 ),
              SDL_RGB_Component( 255.0 * ColourNames( c ).green / 100.0 ),
              SDL_RGB_Component( 255.0 * ColourNames( c ).blue / 100.0 ) );
end setPenInk;

procedure setPenInk( canvas_id : canvasList.aListIndex; c : aColourName ) is
  theCanvas : aCanvas;
begin
  canvasList.Find( canvas, canvas_id, theCanvas );
  setPenInk( theCanvas, c );
  canvasList.Replace( canvas, canvas_id, theCanvas );
  setPenInk( theCanvas, c );
end setPenInk;


-- procedure SetPenColour( screen_ptr : SDL_Surface_Ptr; r, g, b : ARGBComponent ) is
procedure setPenInk( canvas : in out aCanvas; r, g, b : ARGBComponent ) is
begin
  canvas.pen.ink_r := r;
  canvas.pen.ink_g := g;
  canvas.pen.ink_b := b;
put_line( "Calling SDL_MapRGB" ); -- DEBUG
  -- canvas.surface_ptr := SDL_Surface_Conv.To_Pointer( canvas.surface ); -- VERIFYING
  SDL_MapRGB( canvas.pen.pixel,
              canvas.surface_ptr.format,
              SDL_RGB_Component( 255.0 * r / 100.0 ), -- DEBUG
              SDL_RGB_Component( 255.0 * g / 100.0 ),
              SDL_RGB_Component( 255.0 * b / 100.0 ) );
end setPenInk;

procedure setPenInk( canvas_id : canvasList.aListIndex; r, g, b : aRGBComponent ) is
  theCanvas : aCanvas;
begin
  canvasList.Find( canvas, canvas_id, theCanvas );
  setPenInk( theCanvas, r, g, b );
  canvasList.Replace( canvas, canvas_id, theCanvas );
end setPenInk;

procedure setPenInk( canvas : in out aCanvas; pattern : aCanvas ) is
begin
  canvas.pen.pattern := pattern.surface;
  canvas.pen.pattern_ptr := pattern.surface_ptr;
end setPenInk;

procedure setPenInk( canvas_id, pattern_id : canvasList.aListIndex ) is
  theCanvas : aCanvas;
  theBrush  : aCanvas;
begin
  canvasList.Find( canvas, pattern_id, theBrush );
  canvasList.Find( canvas, canvas_id, theCanvas );
  setPenInk( theCanvas, theBrush );
  canvasList.Replace( canvas, canvas_id, theCanvas );
end setPenInk;


---> GET PEN INK
--
-- Get the drawing ink of a canvas pen.
-----------------------------------------------------------------------------

procedure getPenInk( canvas : aCanvas; R, G, B : out aRGBComponent ) is
begin
  R := canvas.pen.ink_r;
  G := canvas.pen.ink_g;
  B := canvas.pen.ink_b;
end getPenInk;

procedure getPenInk( canvas_id : canvasList.aListIndex; R, G, B : out aRGBComponent ) is
  theCanvas : aCanvas;
begin
  canvasList.Find( canvas, canvas_id, theCanvas );
  getPenInk( theCanvas, R, G, B );
end getPenInk;


---> SET PEN BRUSH
--
-- Change the drawing pattern of a canvas pen.

procedure setPenBrush( canvas : in out aCanvas; newBrush : aPenBrush ) is
begin
  canvas.pen.brush := newBrush;
end setPenBrush;

procedure setPenBrush( canvas_id : canvasList.aListIndex; newBrush : aPenBrush ) is
  theCanvas : aCanvas;
begin
  canvasList.Find( canvas, canvas_id, theCanvas );
  setPenBrush( theCanvas, newBrush );
  canvasList.Replace( canvas, canvas_id, theCanvas );
end setPenBrush;

function getPenBrush( canvas_id : canvasList.aListIndex ) return aPenBrush is
  theCanvas : aCanvas;
begin
  canvasList.Find( canvas, canvas_id, theCanvas );
  return theCanvas.pen.brush;
end getPenBrush;

function getPenBrush( canvas : aCanvas ) return aPenBrush is
begin
  return canvas.pen.brush;
end getPenBrush;


---> SET PEN PATTERN
--
-- Change the drawing pattern of a canvas pen.
-----------------------------------------------------------------------------

procedure setPenPattern( canvas : in out aCanvas; pattern : aCanvas ) is
begin
  canvas.pen.pattern := pattern.surface;
  canvas.pen.pattern_ptr := pattern.surface_ptr;
end setPenPattern;

procedure setPenPattern( canvas_id, pattern_id : canvasList.aListIndex ) is
  theCanvas, theBrush  : aCanvas;
begin
  canvasList.Find( canvas, pattern_id, theBrush );
  canvasList.Find( canvas, canvas_id, theCanvas );
  setPenPattern( theCanvas, theBrush );
  canvasList.Replace( canvas, canvas_id, theCanvas );
end setPenPattern;

--function getPenPattern( canvas_id : canvasList.aListIndex ) return canvasList.aListIndex is
--  theCanvas : aCanvas;
--begin
--  canvasList.Find( canvas, canvas_id, theCanvas );
--  return canvas.pen.pattern;
--end getPenPattern;
--
--function getPenPattern( canvas : aCanvas ) return aCanvas is
--begin
--
--end getPenPattern;


---> SET PEN MODE
--
-- Change the drawing mode of a canvas pen.
-----------------------------------------------------------------------------

procedure setPenMode( canvas : in out aCanvas; newMode : aPenMode ) is
begin
  canvas.pen.mode := newMode;
end setPenMode;

procedure setPenMode( canvas_id : canvasList.aListIndex; newMode : aPenMode ) is
  theCanvas : aCanvas;
begin
  canvasList.Find( canvas, canvas_id, theCanvas );
  setPenMode( theCanvas, newMode );
  canvasList.Replace( canvas, canvas_id, theCanvas );
end setPenMode;

function getPenMode( canvas : aCanvas ) return aPenMode is
begin
  return canvas.pen.mode;
end getPenMode;

function getPenMode( canvas_id : canvasList.aListIndex ) return aPenMode is
  theCanvas : aCanvas;
begin
  canvasList.Find( canvas, canvas_id, theCanvas );
  return getPenMode( theCanvas );
end getPenMode;

procedure newPenState( ps : in out aPenState ) is
begin
  ps.X            := 0.0;
  ps.Y            := 0.0;
  ps.Angle        := 0.0;
  ps.ErrorX       := 0.0;
  ps.ErrorY       := 0.0;
  ps.Mode         := copy;
  ps.ink_r        := 100.0;
  ps.ink_g        := 100.0;
  ps.ink_b        := 100.0;
  ps.Brush        := pencil;
  ps.Pixel        := -1; -- white (usually)
  ps.pattern_ptr  := null;
  ps.revealCount  := 0;
end newPenState;


-----------------------------------------------------------------------------

---> DisplayInfo Conversion Functions

function svgah( canvas : aCanvas; ScreenPosition : ACoordinate ) return SDL_HCoordinate is
begin
  return SDL_HCoordinate( ACoordinate( canvas.displayInfo.H_Res ) * ScreenPosition / 100.0 );end svgah;

function svgav( canvas : aCanvas; ScreenPosition : ACoordinate ) return SDL_VCoordinate is
begin
  return SDL_VCoordinate( ACoordinate( canvas.displayInfo.V_Res ) * ScreenPosition / 100.0 );
end svgav;

function svga2comp( canvas : aCanvas; svga_value : Uint8 ) return ARGBComponent is
-- convert svga colour value to a standard RGB component
begin
  return ARGBComponent( 100.0 * float( svga_value ) /
     float( canvas.displayInfo.C_Res ) );
end svga2comp;

function svga2coord( canvas : aCanvas; x : Sint16 ) return ACoordinate is
begin
  return ACoordinate( float( x ) * 100.0 / float( canvas.displayInfo.C_Res - 1 ));
end svga2coord;


---> SVGA EFFECTIVE RECTANGLE
--
-- Convert rectangle r to a clipped pair of hardware pixel coordinates.  The
-- hardware coordinates are calculated.  Then the coordinates are constrained
-- to the intersection of the surface bounds rectangle and clipping rectangle.
-- If entire rectangle is clipped, svga_x1 and svga_x2 are 0.

procedure svgaEffectiveRectangle( theCanvas : aCanvas; r : aRect; svga_x1 : out SDL_HCoordinate; svga_y1 : out SDL_VCoordinate; svga_x2 : out SDL_HCoordinate; svga_y2 : out SDL_VCoordinate ) is
  clip_rect : SDL_Rect;
  clip_x : SDL_HCoordinate;
  clip_y : SDL_VCoordinate;
  clip_w : Uint16;
  clip_h : Uint16;
begin

  -- Convert to hardware coordinates

  svga_x1 := svgah( theCanvas, r.left );
  svga_y1 := svgav( theCanvas, r.top );
  svga_x2 := svgah( theCanvas, r.right )-1;
  svga_y2 := svgav( theCanvas, r.bottom )-1;

  -- Calculate Effective Clipping Rectangle
  --
  -- Surfaces always start at (0,0) but have w and h.

  SDL_GetClipRect( theCanvas.surface, clip_rect );
  clip_x := clip_rect.x;
  clip_y := clip_rect.y;
  if clip_rect.w > Uint16( theCanvas.surface_ptr.w ) then
     clip_w := theCanvas.surface_ptr.clip_rect.w;
  else
     clip_w := Uint16( theCanvas.surface_ptr.w ) - Uint16( clip_x ); -- surface width
  end if;
  if clip_rect.h > Uint16( theCanvas.surface_ptr.h ) then
     clip_h := clip_rect.h;
  else
     clip_h := Uint16( theCanvas.surface_ptr.h ) - Uint16( clip_y ); -- surface height
  end if;

  -- Apply Clipping

  -- put_line( "Original xy =" & svga_x1'img & "," & svga_y1'img );
  -- put_line( "Original  w  =" & svga_x2'img & " -" & svga_y2'img );
  -- put_line( "Clip xy =" & clip_x'img & "," & clip_y'img );
  -- put_line( "Clip wh =" & clip_w'img & " -" & clip_h'img );

  if svga_x2 < clip_x then
     svga_x1 := 0;
     svga_y1 := 0;
     svga_x2 := 0;
     svga_y2 := 0;
  elsif svga_y2 < clip_y then
     svga_x1 := 0;
     svga_y1 := 0;
     svga_x2 := 0;
     svga_y2 := 0;
  elsif svga_x1 > clip_x + SDL_HCoordinate( clip_w ) then
     svga_x1 := 0;
     svga_y1 := 0;
     svga_x2 := 0;
     svga_y2 := 0;
  elsif svga_y1 > clip_y + SDL_VCoordinate( clip_h ) then
     svga_x1 := 0;
     svga_y1 := 0;
     svga_x2 := 0;
     svga_y2 := 0;
  else
     if svga_x1 < clip_x then
        svga_x1 := clip_x;
     end if;
     if svga_y1 < clip_y then
        svga_y1 := clip_y;
     end if;
     if svga_x2 > clip_x + SDL_HCoordinate( clip_w ) then
        svga_x2 := clip_x + SDL_HCoordinate( clip_w );
     end if;
     if svga_y2 > clip_y + SDL_VCoordinate( clip_h ) then
        svga_y2 := clip_y + SDL_VCoordinate( clip_h );
     end if;
  end if;

end svgaEffectiveRectangle;


-----------------------------------------------------------------------------
-- COLOUR OPERATIONS
-----------------------------------------------------------------------------


---> GREY SCALE
--
-- Convert an RGB colour to greyscale weighted for the human eye.

function greyScale( redC, greenC, blueC : ARGBComponent ) return
  ARGBComponent is
begin
  return 0.53 * redC + 0.33 * greenC + 0.11 * blueC;
end greyScale;

function greyScale( colour : AColourName ) return ARGBComponent is
begin
  return 0.53 * ColourNames( colour ).red +
         0.33 * ColourNames( colour ).green +
         0.11 * ColourNames( colour ).blue;
end greyScale;


---> BLEND
--
-- Average two RGB colours to create a new colour.

procedure blend( redC1, greenC1, blueC1 : ARGBComponent;
                 redC2, greenC2, blueC2 : ARGBComponent;
                 redC3, greenC3, blueC3 : out ARGBComponent ) is
begin
  redC3   := ( redC1 + redC2 ) / 2.0;
  greenC3 := ( greenC1 + greenC2 ) / 2.0;
  blueC3  := ( blueC1 + blueC2 ) / 2.0;
end blend;


---> FADE
--
-- Brighten or darken a colour by a percentage.

procedure fade( redC1, greenC1, blueC1 : ARGBComponent;
                fadeamount : float;
                redC2, greenC2, blueC2 : out ARGBComponent ) is
  Temp : float;
begin
  Temp := (float( redC1 ) * ( 100.0 - fadeamount ))/100.0;
  if Temp < 0.0 then
     Temp := 0.0;
  end if;
  if Temp > 100.0 then
     Temp := 100.0;
  end if;
  redC2 := ARGBComponent( Temp );

  Temp := (float( greenC1 ) * ( 100.0 - fadeamount ))/100.0;
  if Temp < 0.0 then
     Temp := 0.0;
  end if;
  if Temp > 100.0 then
     Temp := 100.0;
  end if;
  greenC2 := ARGBComponent( Temp );

  Temp := (float( blueC1 ) * ( 100.0 - fadeamount ))/100.0;
  if Temp < 0.0 then
     Temp := 0.0;
  end if;
  if Temp > 100.0 then
     Temp := 100.0;
  end if;
  blueC2 := ARGBComponent( Temp );
end fade;

procedure fade( redC, greenC, blueC : in out ARGBComponent ;
                fadeamount : float ) is
begin
  fade( redC, greenC, blueC, fadeamount, redC, greenC, blueC );
end fade;


-----------------------------------------------------------------------------
-- HOUSEKEEPING
-----------------------------------------------------------------------------

moduleIsRunning : boolean := false;

---> Startup
--
-- Startup the pen package.
-----------------------------------------------------------------------------

procedure startupPen is
  res_int : integer;
begin

  res_int := SDL_Init( SDL_INIT_VIDEO );
  if res_int < 0 then
     put_line( standard_error, "startupPen: sdl_init failed, SDL error = " & to_string( get_sdl_error ) );
  else
     moduleIsRunning := true;
  end if;

  -- LastButtons := 0; -- assume all mouse buttons up
end startupPen;


---> Shutdown
--
-- Shutdown the pen package.
-----------------------------------------------------------------------------

procedure shutdownPen is
begin
  if moduleIsRunning then
     SDL_Quit;
     moduleIsRunning := false;
  end if;
end shutdownPen;


---> isRunning
--
-----------------------------------------------------------------------------

function isPenRunning return boolean is
begin
  return moduleIsRunning;
end isPenRunning;


-----------------------------------------------------------------------------
-- RECTANGLE DRAWING
-----------------------------------------------------------------------------


---> FRAME RECT
--
-- Draw the outline of a rectangle with the current pen.

procedure frameRect( theCanvas : aCanvas; r : ARect ) is
  svga_x1 : SDL_HCoordinate;
  svga_y1 : SDL_VCoordinate;
  svga_x2 : SDL_HCoordinate;
  svga_y2 : SDL_VCoordinate;
  -- res : SDL_Success;
begin

  svgaEffectiveRectangle( theCanvas, r, svga_x1, svga_y1, svga_x2, svga_y2 );

  if svga_x1 + svga_x2 = 0 then
     return;
  end if;

  case theCanvas.pen.brush is

  when stretch => -- not complete
     null;

  when tile =>
     SDL_EXT_Fill_Rect_Pattern( theCanvas.surface, svga_x1, svga_y1,
            svga_x1, svga_y2, 0, 0, theCanvas.pen.pattern, theCanvas.pen.mode );
     SDL_EXT_Fill_Rect_Pattern( theCanvas.surface, svga_x2, svga_y1,
            svga_x2, svga_y2, 0, 0, theCanvas.pen.pattern, theCanvas.pen.mode );
     SDL_EXT_Fill_Rect_Pattern( theCanvas.surface, svga_x1, svga_y1,
            svga_x2, svga_y1, 0, 0, theCanvas.pen.pattern, theCanvas.pen.mode );
     SDL_EXT_Fill_Rect_Pattern( theCanvas.surface, svga_x1, svga_y2,
            svga_x2, svga_y2, 0, 0, theCanvas.pen.pattern, theCanvas.pen.mode );
     SDL_UnlockSurface( theCanvas.surface  );

  when stamp =>
     SDL_EXT_Fill_Rect_Pattern( theCanvas.surface, svga_x1, svga_y1,
            svga_x1, svga_y2, svga_x1, svga_y1, theCanvas.pen.pattern, theCanvas.pen.mode );
     SDL_EXT_Fill_Rect_Pattern( theCanvas.surface, svga_x2, svga_y1,
            svga_x2, svga_y2, svga_x1, svga_y1, theCanvas.pen.pattern, theCanvas.pen.mode );
     SDL_EXT_Fill_Rect_Pattern( theCanvas.surface, svga_x1, svga_y1,
            svga_x2, svga_y1, svga_x1, svga_y1, theCanvas.pen.pattern, theCanvas.pen.mode );
     SDL_EXT_Fill_Rect_Pattern( theCanvas.surface, svga_x1, svga_y2,
            svga_x2, svga_y2, svga_x1, svga_y1, theCanvas.pen.pattern, theCanvas.pen.mode );
     SDL_UnlockSurface( theCanvas.surface  );

  when smear => -- not done
       null;

  when others => -- including pencil
     if SDL_LockSurface( theCanvas.surface  ) = SDL_OK then
        SDL_Ext_Line( theCanvas.surface, svgah( theCanvas, r.left ), svgav(
                   thecanvas, r.top ), svgah( theCanvas, r.right ), svgav(
                   thecanvas, r.top ), theCanvas.pen.pixel, copy );
        SDL_Ext_Line( theCanvas.surface, svgah( theCanvas, r.left ), svgav(
                   theCanvas, r.top ), svgah( theCanvas, r.left ), svgav(
                   theCanvas, r.bottom ), theCanvas.pen.pixel, copy );
        SDL_Ext_Line( theCanvas.surface, svgah( theCanvas, r.right ), svgav(
                   thecanvas, r.top ), svgah( theCanvas, r.right ), svgav(
                   thecanvas, r.bottom ), theCanvas.pen.pixel, copy );
        SDL_Ext_Line( theCanvas.surface, svgah( theCanvas, r.left ), svgav(
                   theCanvas, r.bottom ), svgah( theCanvas, r.right ), svgav(
                   theCanvas, r.bottom ), theCanvas.pen.pixel, copy );
        -- adjust -1, adjust mode
        SDL_UnlockSurface( theCanvas.surface  );
     end if;

  end case;
  if theCanvas.pen.revealCount = 0 then
     -- res := SDL_Flip( theCanvas.surface );
     SDL_UpdateRect( theCanvas.surface, svgah( theCanvas, r.left ), svgav( theCanvas, r.top ), Uint32( svgah( theCanvas, r.right ) - svgah( theCanvas, r.left ) + 1 ), Uint32( svgav( theCanvas, r.bottom ) - svgav( theCanvas, r.top ) + 1 ) );
  end if;
end frameRect;

procedure frameRect( canvas_id : canvasList.aListIndex; r : ARect ) is
  theCanvas : aCanvas;
begin
  canvasList.Find( canvas, canvas_id, theCanvas );
  frameRect( theCanvas, r );
end frameRect;

---> FILL RECT
--
-- Fill in a rectangle with the current pen.


-- procedure fillRect( theCanvas : aCanvas; r : ARect; pattern : system.address ) is
--   svga_x1 : SDL_HCoordinate;
--   svga_y1 : SDL_VCoordinate;
--   svga_x2 : SDL_HCoordinate;
--   svga_y2 : SDL_VCoordinate;
--   -- res : SDL_Success;
-- begin
-- 
--   svgaEffectiveRectangle( theCanvas, r, svga_x1, svga_y1, svga_x2, svga_y2 );
--   if svga_x1 + svga_x2 = 0 then
--      return;
--   end if;
-- 
--   put_line( "Drawing on canvas: " & to_string( theCanvas.name ) );
--   put_line( "Pen brush =" & theCanvas.pen.brush'img );
--   put_line( "Pen mode  =" & theCanvas.pen.mode'img );
-- 
--   case theCanvas.pen.brush is
-- 
--   when stretch => -- not complete
--      if theCanvas.pen.mode /= copy then
--         if SDL_LockSurface( theCanvas.surface  ) = SDL_OK then
--            SDL_EXT_Fill_Rect( theCanvas.surface, svga_x1, svga_y1,
--                svga_x2, svga_y2, theCanvas.pen.pixel, theCanvas.pen.mode );
--            SDL_UnlockSurface( theCanvas.surface  );
--         end if;
--      else
--         SDL_EXT_Fill_Rect( theCanvas.surface, svga_x1, svga_y1,
--             svga_x2, svga_y2, theCanvas.pen.pixel, theCanvas.pen.mode );
--      end if;
-- 
--   when tile =>
--      if theCanvas.pen.mode /= copy then
--         if SDL_LockSurface( theCanvas.surface  ) = SDL_OK then
--            SDL_EXT_Fill_Rect_Pattern( theCanvas.surface, svga_x1, svga_y1,
--                svga_x2, svga_y2, 0, 0, theCanvas.pen.pattern, theCanvas.pen.mode );
--            SDL_UnlockSurface( theCanvas.surface  );
--         end if;
--      else
-- put_line( "Copy Fill Rect - Part 1 - With Pattern" );
-- put_line( "CopyFillRect: x1/y1 = " & svga_x1'img & "," & svga_y1'img );
-- put_line( "CopyFillRect: x2/y2 = " & svga_x2'img & "," & svga_y2'img );
--         SDL_EXT_Copy_Fill_Rect_Pattern( theCanvas.surface, svga_x1,
--             -- svga_y1, svga_x2, svga_y2, theCanvas.pen.pattern );
--             svga_y1, svga_x2, svga_y2, pattern );
--      end if;
-- 
--   when stamp =>
--      -- if theCanvas.pen.mode /= copy then
-- put_line( "Stamping" );
-- put_line( "CopyFillRect: x1/y1 = " & svga_x1'img & "," & svga_y1'img );
-- put_line( "CopyFillRect: x2/y2 = " & svga_x2'img & "," & svga_y2'img );
--         if SDL_LockSurface( theCanvas.surface  ) = SDL_OK then
--            SDL_EXT_Fill_Rect_Pattern( theCanvas.Surface, svga_x1,
--                svga_y1, svga_x2, svga_y2, svga_x1, svga_y1, theCanvas.pen.pattern, theCanvas.pen.mode );
--            SDL_UnlockSurface( theCanvas.surface  );
--         end if;
     -- NOTE: Offset missing
     -- else
     --    SDL_EXT_Copy_Fill_Rect_Pattern( theCanvas.surface, svga_x1,
     --        svga_y1, svga_x2, svga_y2, theCanvas.pen.pattern );
     -- end if;

--   when smear => -- not done
--      if theCanvas.pen.mode /= copy then
--         if SDL_LockSurface( theCanvas.surface  ) = SDL_OK then
--            SDL_EXT_Fill_Rect_Pattern( theCanvas.surface, svga_x1,
--              svga_y1, svga_x2, svga_y2, 0, 0, theCanvas.pen.pattern,
--              theCanvas.pen.mode );
--            SDL_UnlockSurface( theCanvas.surface  );
--         end if;
--      else
--         SDL_EXT_Copy_Fill_Rect_Pattern( theCanvas.surface, svga_x1,
--             svga_y1, svga_x2, svga_y2, theCanvas.pen.pattern );
--      end if;
-- 
--   when others => -- including pencil
--      if SDL_LockSurface( theCanvas.surface  ) = SDL_OK then
--         SDL_EXT_Fill_Rect( theCanvas.surface, svga_x1, svga_y1,
--             svga_x2, svga_y2, theCanvas.pen.pixel, theCanvas.pen.mode );
--         SDL_UnlockSurface( theCanvas.surface  );
--      end if;
-- 
--   end case;
-- 
--   if theCanvas.pen.revealCount = 0 then
-- put_line( "Revealing!" );
--      SDL_UpdateRect( theCanvas.surface, svga_x1, svga_y1, Uint32( svga_x2 - svga_x1 ) + 1, Uint32( svga_y2 - svga_y1 ) + 1 );
--      --if SDL_Flip( theCanvas.surface ) /= SDL_OK then
     --end if;
--   end if;
-- 
-- end fillRect;

procedure fillRect( theCanvas : aCanvas; r : ARect ) is
  svga_x1 : SDL_HCoordinate;
  svga_y1 : SDL_VCoordinate;
  svga_x2 : SDL_HCoordinate;
  svga_y2 : SDL_VCoordinate;
  -- res : SDL_Success;
begin

  svgaEffectiveRectangle( theCanvas, r, svga_x1, svga_y1, svga_x2, svga_y2 );
  if svga_x1 + svga_x2 = 0 then
     return;
  end if;

  case theCanvas.pen.brush is

  when stretch => -- not complete
     if theCanvas.pen.mode /= copy then
        if SDL_LockSurface( theCanvas.surface  ) = SDL_OK then
           SDL_EXT_Fill_Rect( theCanvas.surface, svga_x1, svga_y1,
               svga_x2, svga_y2, theCanvas.pen.pixel, theCanvas.pen.mode );
           SDL_UnlockSurface( theCanvas.surface  );
        end if;
     else
        SDL_EXT_Fill_Rect( theCanvas.surface, svga_x1, svga_y1,
            svga_x2, svga_y2, theCanvas.pen.pixel, theCanvas.pen.mode );
     end if;

  when tile =>
     if theCanvas.pen.mode /= copy then
        if SDL_LockSurface( theCanvas.surface  ) = SDL_OK then
           SDL_EXT_Fill_Rect_Pattern( theCanvas.surface, svga_x1, svga_y1,
               svga_x2, svga_y2, 0, 0, theCanvas.pen.pattern, theCanvas.pen.mode );
           SDL_UnlockSurface( theCanvas.surface  );
        end if;
     else
        -- Not sure if this is right...
        SDL_EXT_Copy_Fill_Rect_Pattern( theCanvas.surface, svga_x1,
             svga_y1, svga_x2, svga_y2, theCanvas.pen.pattern );
        --SDL_EXT_Fill_Rect_Pattern( theCanvas.Surface, svga_x1,
        --    svga_y1, svga_x2, svga_y2, svga_x1, svga_y1, theCanvas.pen.pattern, theCanvas.pen.mode );
     end if;

  when stamp =>
     -- if theCanvas.pen.mode /= copy then
     if theCanvas.pen.mode /= copy then
        if SDL_LockSurface( theCanvas.surface  ) = SDL_OK then
           SDL_EXT_Fill_Rect_Pattern( theCanvas.Surface, svga_x1,
               svga_y1, svga_x2, svga_y2, svga_x1, svga_y1, theCanvas.pen.pattern, theCanvas.pen.mode );
           SDL_UnlockSurface( theCanvas.surface  );
        end if;
     else
        -- Copy but not-zero offset?  Fall back to regular Fill_Rect_Pattern.
        SDL_EXT_Fill_Rect_Pattern( theCanvas.Surface, svga_x1,
            svga_y1, svga_x2, svga_y2, svga_x1, svga_y1, theCanvas.pen.pattern, theCanvas.pen.mode );
     end if;

  when smear => -- not done
     if theCanvas.pen.mode /= copy then
        if SDL_LockSurface( theCanvas.surface  ) = SDL_OK then
           SDL_EXT_Fill_Rect_Pattern( theCanvas.surface, svga_x1,
               svga_y1, svga_x2, svga_y2, 0, 0, theCanvas.pen.pattern,
               theCanvas.pen.mode );
           SDL_UnlockSurface( theCanvas.surface  );
        end if;
     else
        SDL_EXT_Copy_Fill_Rect_Pattern( theCanvas.surface, svga_x1,
            svga_y1, svga_x2, svga_y2, theCanvas.pen.pattern );
     end if;

  when others => -- including pencil
     if SDL_LockSurface( theCanvas.surface  ) = SDL_OK then
        SDL_EXT_Fill_Rect( theCanvas.surface, svga_x1, svga_y1,
            svga_x2, svga_y2, theCanvas.pen.pixel, theCanvas.pen.mode );
        SDL_UnlockSurface( theCanvas.surface  );
     end if;

  end case;

  if theCanvas.pen.revealCount = 0 then
     SDL_UpdateRect( theCanvas.surface, svga_x1, svga_y1, Uint32( svga_x2 - svga_x1 ) + 1, Uint32( svga_y2 - svga_y1 ) + 1 );
     --if SDL_Flip( theCanvas.surface ) /= SDL_OK then
     --end if;
  end if;

end fillRect;

procedure fillRect( canvas_id : canvasList.aListIndex; r : ARect ) is
  theCanvas : aCanvas;
begin
  canvasList.Find( canvas, canvas_id, theCanvas );
  fillRect( theCanvas, r );
end fillRect;


-----------------------------------------------------------------------------
-- ELLIPSE DRAWING
-----------------------------------------------------------------------------


---> FRAME ELLIPSE
--
-- Draw the outline of an ellipse with the current pen.

procedure frameEllipse( theCanvas : aCanvas; r : ARect ) is
  svga_x1 : SDL_HCoordinate;
  svga_y1 : SDL_VCoordinate;
  svga_x2 : SDL_HCoordinate;
  svga_y2 : SDL_VCoordinate;
  res : SDL_Success;
begin

  svgaEffectiveRectangle( theCanvas, r, svga_x1, svga_y1, svga_x2, svga_y2 );

  if svga_x1 + svga_x2 = 0 then
     return;
  end if;

  case theCanvas.pen.brush is

  when stretch => -- not complete
     null;

  when tile =>
     if SDL_LockSurface( theCanvas.surface  ) = SDL_OK then
        SDL_EXT_Frame_Ellipse_Pattern( theCanvas.surface, svga_x1, svga_y1,
            svga_x2, svga_y2, 0, 0, theCanvas.pen.pattern, copy );
        SDL_UnlockSurface( theCanvas.surface  );
     end if;

  when stamp =>
     if SDL_LockSurface( theCanvas.surface  ) = SDL_OK then
        SDL_EXT_Frame_Ellipse_Pattern( theCanvas.surface, svga_x1, svga_y1,
            svga_x2, svga_y2, svga_x1, svga_y1, theCanvas.pen.pattern, copy );
        SDL_UnlockSurface( theCanvas.surface  );
     end if;

  when smear => -- not done
     null;

  when others => -- including pencil
     if SDL_LockSurface( theCanvas.surface  ) = SDL_OK then
        SDL_EXT_Frame_Ellipse( theCanvas.surface, svga_x1, svga_y1,
            svga_x2, svga_y2, theCanvas.pen.pixel, copy );
        -- adjust -1, adjust mode
        SDL_UnlockSurface( theCanvas.surface  );
     end if;

  end case;
  if theCanvas.pen.revealCount = 0 then
     res := SDL_Flip( theCanvas.surface );
     -- SDL_UpdateRect( theCanvas.surface, svga_x1, svga_y1, Uint32( svga_x2 - svga_x1 ) + 1, Uint32( svga_y2 - svga_y1 ) + 1 );
  end if;
end frameEllipse;

procedure frameEllipse( canvas_id : canvasList.aListIndex; r : ARect ) is
  theCanvas : aCanvas;
begin
  canvasList.Find( canvas, canvas_id, theCanvas );
  frameEllipse( theCanvas, r );
end frameEllipse;


---> FRAME ELLIPSE
--
-- Draw the outline of an ellipse with the current pen.

--procedure fillEllipse( theCanvas : aCanvas; r : ARect; pattern : system.address ) is
--  svga_x1 : SDL_HCoordinate;
--  svga_y1 : SDL_VCoordinate;
--  svga_x2 : SDL_HCoordinate;
--  svga_y2 : SDL_VCoordinate;
--  res : SDL_Success;
--begin
--
--  svgaEffectiveRectangle( theCanvas, r, svga_x1, svga_y1, svga_x2, svga_y2 );
--  if svga_x1 + svga_x2 = 0 then
--     return;
--  end if;
--
--  put_line( "Drawing filled ellipse on canvas: " & to_string( theCanvas.name ) );
--  put_line( "Pen brush =" & theCanvas.pen.brush'img );
--  put_line( "Pen mode  =" & theCanvas.pen.mode'img );
--
--  case theCanvas.pen.brush is
--
--  when stretch => -- not complete
--     if SDL_LockSurface( theCanvas.surface  ) = SDL_OK then
--        SDL_EXT_Fill_Ellipse( theCanvas.surface, svga_x1, svga_y1,
--            svga_x2, svga_y2, theCanvas.pen.pixel, theCanvas.pen.mode );
--        SDL_UnlockSurface( theCanvas.surface  );
--     end if;
--
--  when tile =>
--     if SDL_LockSurface( theCanvas.surface  ) = SDL_OK then
--        SDL_EXT_Fill_Ellipse_Pattern( theCanvas.surface, svga_x1, svga_y1,
--            svga_x2, svga_y2, 0, 0, theCanvas.pen.pattern, theCanvas.pen.mode );
--        SDL_UnlockSurface( theCanvas.surface  );
--     end if;
--
--  when stamp =>
--     if SDL_LockSurface( theCanvas.surface  ) = SDL_OK then
--        SDL_EXT_Fill_Ellipse_Pattern( theCanvas.Surface, svga_x1,
--            svga_y1, svga_x2, svga_y2, svga_x1, svga_y1, theCanvas.pen.pattern, theCanvas.pen.mode );
--        SDL_UnlockSurface( theCanvas.surface  );
--     end if;
--
--  when smear => -- not done
--     if SDL_LockSurface( theCanvas.surface  ) = SDL_OK then
--        SDL_EXT_Fill_Ellipse_Pattern( theCanvas.surface, svga_x1,
--          svga_y1, svga_x2, svga_y2, 0, 0, theCanvas.pen.pattern,
--          theCanvas.pen.mode );
--        SDL_UnlockSurface( theCanvas.surface  );
--     end if;
--
--  when others => -- including pencil
--     if SDL_LockSurface( theCanvas.surface  ) = SDL_OK then
--        SDL_EXT_Fill_Ellipse( theCanvas.surface, svga_x1, svga_y1,
--            svga_x2, svga_y2, theCanvas.pen.pixel, theCanvas.pen.mode );
--        SDL_UnlockSurface( theCanvas.surface  );
--     end if;
--
--  end case;
--
--  if theCanvas.pen.revealCount = 0 then
--put_line( "Revealing!" );
--     SDL_UpdateRect( theCanvas.surface, svga_x1, svga_y1, Uint32( svga_x2 - svga_x1 ) + 1, Uint32( svga_y2 - svga_y1 ) + 1 );
--     --if SDL_Flip( theCanvas.surface ) /= SDL_OK then
     --end if;
--  end if;
--
--end fillEllipse;

procedure fillEllipse( theCanvas : aCanvas; r : ARect ) is
  svga_x1 : SDL_HCoordinate;
  svga_y1 : SDL_VCoordinate;
  svga_x2 : SDL_HCoordinate;
  svga_y2 : SDL_VCoordinate;
begin

  svgaEffectiveRectangle( theCanvas, r, svga_x1, svga_y1, svga_x2, svga_y2 );
  if svga_x1 + svga_x2 = 0 then
     return;
  end if;

  case theCanvas.pen.brush is

  when stretch => -- not complete
     if SDL_LockSurface( theCanvas.surface  ) = SDL_OK then
        SDL_EXT_Fill_Ellipse( theCanvas.surface, svga_x1, svga_y1,
            svga_x2, svga_y2, theCanvas.pen.pixel, theCanvas.pen.mode );
        SDL_UnlockSurface( theCanvas.surface  );
     end if;

  when tile =>
     if SDL_LockSurface( theCanvas.surface  ) = SDL_OK then
        SDL_EXT_Fill_Ellipse_Pattern( theCanvas.surface, svga_x1, svga_y1,
            svga_x2, svga_y2, 0, 0, theCanvas.pen.pattern, theCanvas.pen.mode );
        SDL_UnlockSurface( theCanvas.surface  );
     end if;

  when stamp =>
     if SDL_LockSurface( theCanvas.surface  ) = SDL_OK then
        SDL_EXT_Fill_Ellipse_Pattern( theCanvas.Surface, svga_x1,
           svga_y1, svga_x2, svga_y2, svga_x1, svga_y1, theCanvas.pen.pattern, theCanvas.pen.mode );
        SDL_UnlockSurface( theCanvas.surface  );
     end if;

  when smear => -- not done
     if SDL_LockSurface( theCanvas.surface  ) = SDL_OK then
        SDL_EXT_Fill_Ellipse_Pattern( theCanvas.surface, svga_x1,
            svga_y1, svga_x2, svga_y2, 0, 0, theCanvas.pen.pattern,
            theCanvas.pen.mode );
        SDL_UnlockSurface( theCanvas.surface  );
     end if;

  when others => -- including pencil
     if SDL_LockSurface( theCanvas.surface  ) = SDL_OK then
        SDL_EXT_Fill_Ellipse( theCanvas.surface, svga_x1, svga_y1,
            svga_x2, svga_y2, theCanvas.pen.pixel, theCanvas.pen.mode );
        SDL_UnlockSurface( theCanvas.surface  );
     end if;

  end case;

  if theCanvas.pen.revealCount = 0 then
     SDL_UpdateRect( theCanvas.surface, svga_x1, svga_y1, Uint32( svga_x2 - svga_x1 ) + 1, Uint32( svga_y2 - svga_y1 ) + 1 );
     --if SDL_Flip( theCanvas.surface ) /= SDL_OK then
     --end if;
  end if;

end fillEllipse;

procedure fillEllipse( canvas_id : canvasList.aListIndex; r : ARect ) is
  theCanvas : aCanvas;
begin
  canvasList.Find( canvas, canvas_id, theCanvas );
  fillEllipse( theCanvas, r );
end fillEllipse;


-----------------------------------------------------------------------------
-- CANVAS
-----------------------------------------------------------------------------


---> NEW SCREEN CANVAS
--
-- Create a new on-screen canvas covering the whole screen.  The size and
-- resolution of the canvas is based on the canvas data structure.  There can
-- only be one screen canvas.

procedure newScreenCanvas( H_Res, V_Res, C_Res : positive; canvas_id : out canvasList.aListIndex ) is
  newcanvas : aCanvas;
begin
  newPenState( newCanvas.pen );
  newcanvas.displayInfo.H_Res := H_Res;
  newcanvas.displayInfo.V_Res := V_Res;
  newcanvas.displayInfo.C_Res := C_Res;
  newcanvas.hardware := SDL_GetVideoInfo;
  newcanvas.hardware_ptr := SDL_VideoInfo_Conv.To_Pointer( newcanvas.hardware );
  newcanvas.surface := SDL_SetVideoMode( Interfaces.C.int( H_Res ), Interfaces.C.int( V_Res ), Interfaces.C.int( C_Res ), SDL_FULLSCREEN + SDL_HWPALETTE );
  newcanvas.surface_ptr := SDL_Surface_Conv.To_Pointer( newcanvas.surface );
  if newcanvas.surface_ptr = null then
     put_line( standard_error, "newScreenCanvas: falied for " & H_Res'img & V_Res'img & C_Res'img & "SDL error = " & to_string( get_sdl_error ) );
  end if;
  newcanvas.kind := screen;
  newCanvas.name := to_unbounded_string( "Untitled Bush Screen Canvas" );
  canvasList.Queue( canvas, newCanvas );
  canvas_id := canvasList.Length( canvas );
end newScreenCanvas;


---> NEW WINDOW CANVAS
--
-- Create a new on-screen canvas with its own window.  The size and resolution
-- of the canvas is based on the canvas data structure.

procedure newWindowCanvas( H_Res, V_Res, C_Res : positive; canvas_id : out canvasList.aListIndex ) is
  newcanvas : aCanvas;
begin
  newPenState( newCanvas.pen );
  newcanvas.displayInfo.H_Res := H_Res;
  newcanvas.displayInfo.V_Res := V_Res;
  newcanvas.displayInfo.C_Res := C_Res;
  newcanvas.surface := SDL_SetVideoMode( Interfaces.C.int( H_Res ), Interfaces.C.int( V_Res ), Interfaces.C.int( C_Res ), SDL_HWSURFACE );
  newcanvas.surface_ptr := SDL_Surface_Conv.To_Pointer( newcanvas.surface );
  if newcanvas.surface_ptr = null then
     put_line( standard_error, "newWindowCanvas: falied for " & H_Res'img & V_Res'img & C_Res'img & "SDL error = " & to_string( get_sdl_error ) );
  end if;
  newcanvas.kind := window;
  newCanvas.name := to_unbounded_string( "Untitled BUSH Window Canvas" );
  SDL_EXT_Window_Title( "Untitled BUSH Window Canvas" & ASCII.NUL );
  canvasList.Queue( canvas, newCanvas );
  canvas_id := canvasList.Length( canvas );
end newWindowCanvas;


---> NEW CANVAS
--
-- Create a new off-screen canvas based on the canvas data structure

procedure newCanvas( H_Res, V_Res, C_Res : positive; canvas_id : out canvasList.aListIndex ) is
  newcanvas : aCanvas;
begin
  newPenState( newCanvas.pen );
  newcanvas.displayInfo.H_Res := H_Res;
  newcanvas.displayInfo.V_Res := V_Res;
  newcanvas.displayInfo.C_Res := C_Res;
  newcanvas.surface := SDL_SetVideoMode( Interfaces.C.int( H_Res ), Interfaces.C.int( V_Res ), Interfaces.C.int( C_Res ), SDL_SWSURFACE );
-- WRONG CALL: CreateRGBSurface?
  newcanvas.surface_ptr := SDL_Surface_Conv.To_Pointer( newcanvas.surface );
  if newcanvas.surface_ptr = null then
     put_line( standard_error, "newCanvas: falied for " & H_Res'img & V_Res'img & C_Res'img & "SDL error = " & to_string( get_sdl_error ) );
  end if;
  newcanvas.kind := offscreen;
  newcanvas.surface_ptr := SDL_Surface_Conv.To_Pointer( newcanvas.surface );
  newCanvas.name := to_unbounded_string( "Untitled Bush Offscreen Canvas" );
  canvasList.Queue( canvas, newCanvas );
  canvas_id := canvasList.Length( canvas );
  -- should use SDL_VideoInfo for additional information
end newCanvas;


procedure newCanvas( path : string; canvas_id : out canvasList.aListIndex ) is
  newcanvas : aCanvas;
  format_ptr : SDL_Pixelformat_Ptr;
begin
  newPenState( newCanvas.pen );
  newCanvas.surface := IMG_Load( path & ASCII.NUL );
  newcanvas.surface_ptr := SDL_Surface_Conv.To_Pointer( newcanvas.surface );
  if newcanvas.surface_ptr /= null then
    newCanvas.name := to_unbounded_string( path );
  else
    put_line( "Load failed, SDL error = " & to_string( get_sdl_error ) );
  end if;
  newcanvas.kind := offscreen;
  newcanvas.displayInfo.H_Res := integer( newCanvas.surface_ptr.w );
  newcanvas.displayInfo.V_Res := integer( newCanvas.surface_ptr.h );
  format_ptr := SDL_PixelFormat_Conv.To_Pointer( get_format_address( newcanvas.surface ) );
  newcanvas.displayInfo.C_Res := integer( format_ptr.BitsPerPixel );
  canvasList.Queue( canvas, newCanvas );
  canvas_id := canvasList.Length( canvas );
  -- should use SDL_VideoInfo for additional information
end newCanvas;


procedure setTitle( canvas_id : canvasList.aListIndex; title :unbounded_string ) is
  theCanvas : aCanvas;
begin
  canvasList.Find( canvas, canvas_id, theCanvas );
  theCanvas.name := title;
  canvasList.Replace( canvas, canvas_id, theCanvas );
  if theCanvas.kind = window then
     SDL_EXT_Window_Title( to_string( title ) & ASCII.NUL );
  end if;
end setTitle;


---> CLEAR CANVAS
--
-- Discard a canvas created with newCanvas, newScreenCanvas or newWindowCanvas.

procedure clearCanvas( canvas_id : in out canvasList.aListIndex ) is
begin
  canvasList.Clear( canvas, canvas_id );
end clearCanvas;


---> CLIP RECT
--
-- Set the clipping region to the given rectangle.

procedure clipRect( theCanvas : in out aCanvas; r : aRect ) is
  newClipRect : SDL_Rect;
  res : SDL_Bool;
begin
  -- null rect handling
  theCanvas.clipRect := r;
  newClipRect.x := svgah( theCanvas, r.left );
  newClipRect.y := svgav( theCanvas, r.top );
  newClipRect.w := Uint16( svgah( theCanvas, r.right ) - svgah( theCanvas, r.left ) );
  newClipRect.h := Uint16( svgav( theCanvas, r.bottom ) - svgav( theCanvas, r.top ) );
  SDL_SetClipRect( res, theCanvas.surface, newClipRect );
end clipRect;

procedure clipRect( canvas_id : canvasList.aListIndex; r : aRect ) is
  theCanvas : aCanvas;
begin
  canvasList.Find( canvas, canvas_id, theCanvas );
  clipRect( theCanvas, r );
  canvasList.Replace( canvas, canvas_id, theCanvas );
end clipRect;


---> MOVING
--
-- Moving the pen without drawing.
-----------------------------------------------------------------------------

procedure moveTo( theCanvas : in out aCanvas; x, y : aCoordinate ) is
begin
  theCanvas.pen.x := x;
  theCanvas.pen.y := y;
end moveTo;

procedure moveTo( canvas_id : canvasList.aListIndex; x, y : aCoordinate ) is
  theCanvas : aCanvas;
begin
  canvasList.Find( canvas, canvas_id, theCanvas );
  move( theCanvas, x, y );
  canvasList.Replace( canvas, canvas_id, theCanvas );
end moveTo;

procedure move( theCanvas : in out aCanvas; dx, dy : aCoordinate ) is
begin
  theCanvas.pen.x := theCanvas.pen.x + dx;
  theCanvas.pen.y := theCanvas.pen.y + dy;
end move;

procedure move( canvas_id : canvasList.aListIndex; dx, dy : aCoordinate ) is
  theCanvas : aCanvas;
begin
  canvasList.Find( canvas, canvas_id, theCanvas );
  move( theCanvas, dx, dy );
  canvasList.Replace( canvas, canvas_id, theCanvas );
end move;

---> LINES
--
-- Moving the pen with drawing.
-----------------------------------------------------------------------------

procedure lineTo( theCanvas : in out aCanvas; x, y : aCoordinate ) is
begin
  SDL_EXT_line( theCanvas.surface,
    svgah( theCanvas, theCanvas.pen.x ), svgav( theCanvas, theCanvas.pen.y ),
    svgah( theCanvas, x ), svgav( theCanvas, y ), theCanvas.pen.pixel, theCanvas.pen.mode, theCanvas.pen.revealCount = 0 );
  theCanvas.pen.x := x;
  theCanvas.pen.y := y;
end lineTo;

procedure lineTo( canvas_id : canvasList.aListIndex; x, y : aCoordinate ) is
  theCanvas : aCanvas;
begin
  canvasList.Find( canvas, canvas_id, theCanvas );
  lineTo( theCanvas, x, y );
  canvasList.Replace( canvas, canvas_id, theCanvas );
end lineTo;

procedure line( theCanvas : in out aCanvas; dx, dy : aCoordinate ) is
begin
  SDL_EXT_line( theCanvas.surface,
    svgah( theCanvas, theCanvas.pen.x ), svgav( theCanvas, theCanvas.pen.y ),
    svgah( theCanvas, theCanvas.pen.x + dx ), svgav( theCanvas, theCanvas.pen.y + dy ),
    theCanvas.pen.pixel, theCanvas.pen.mode, theCanvas.pen.revealCount = 0 );
  theCanvas.pen.x := theCanvas.pen.x + dx;
  theCanvas.pen.y := theCanvas.pen.y + dy;
end line;

procedure line( canvas_id : canvasList.aListIndex; dx, dy : aCoordinate ) is
  theCanvas : aCanvas;
begin
  canvasList.Find( canvas, canvas_id, theCanvas );
  line( theCanvas, dx, dy );
  canvasList.Replace( canvas, canvas_id, theCanvas );
end line;

procedure hline( theCanvas : in out aCanvas; x1, x2, y : aCoordinate ) is
begin
  case theCanvas.pen.brush is

  when stretch => -- not complete
     if SDL_LockSurface( theCanvas.surface  ) = SDL_OK then
        SDL_EXT_HLine_Pattern( theCanvas.surface, svgah( theCanvas, x1 ), svgah( theCanvas, x2 ), svgav( theCanvas, y ), theCanvas.pen.pattern, 0, 0, theCanvas.pen.mode );
        SDL_UnlockSurface( theCanvas.surface  );
     end if;

  when tile =>
     if SDL_LockSurface( theCanvas.surface  ) = SDL_OK then
        SDL_EXT_HLine_Pattern( theCanvas.surface, svgah( theCanvas, x1 ), svgah( theCanvas, x2 ), svgav( theCanvas, y ), theCanvas.pen.pattern, 0, 0, theCanvas.pen.mode );
        SDL_UnlockSurface( theCanvas.surface  );
     end if;

  when stamp =>
     if SDL_LockSurface( theCanvas.surface  ) = SDL_OK then
        SDL_EXT_HLine_Pattern( theCanvas.surface, svgah( theCanvas, x1 ), svgah( theCanvas, x2 ), svgav( theCanvas, y ), theCanvas.pen.pattern, svgah( theCanvas, x1 ), svgav( theCanvas, y ), theCanvas.pen.mode );
        SDL_UnlockSurface( theCanvas.surface  );
     end if;

  when smear => -- not done
     if SDL_LockSurface( theCanvas.surface  ) = SDL_OK then
        SDL_EXT_HLine_Pattern( theCanvas.surface, svgah( theCanvas, x1 ), svgah( theCanvas, x2 ), svgav( theCanvas, y ), theCanvas.pen.pattern, 0, 0, theCanvas.pen.mode );
        SDL_UnlockSurface( theCanvas.surface  );
     end if;

  when others => -- including pencil
     if SDL_LockSurface( theCanvas.surface  ) = SDL_OK then
        SDL_EXT_HLine( theCanvas.surface, svgah( theCanvas, x1 ), svgah( theCanvas, x2 ), svgav( theCanvas, y ), theCanvas.pen.pixel, theCanvas.pen.mode );
        SDL_UnlockSurface( theCanvas.surface  );
     end if;
  end case;
  if theCanvas.pen.revealCount = 0 then
     SDL_UpdateRect( theCanvas.surface, svgah( theCanvas, x1 ), svgav( theCanvas, y ), Uint32( svgav( theCanvas, x2 ) - svgav( theCanvas, x1 ) + 1 ), 1 );
  end if;
end hline;


procedure vline( theCanvas : in out aCanvas; x, y1, y2 : aCoordinate ) is
begin
  case theCanvas.pen.brush is

  when stretch => -- not complete
     if SDL_LockSurface( theCanvas.surface  ) = SDL_OK then
        --SDL_EXT_VLine_Pattern( theCanvas.surface, svgah( theCanvas, x ), svgav( theCanvas, y1 ), svgav( theCanvas, y2 ), theCanvas.pen.pattern, 0, 0, theCanvas.pen.mode );
        SDL_UnlockSurface( theCanvas.surface  );
     end if;

  when tile =>
     if SDL_LockSurface( theCanvas.surface  ) = SDL_OK then
        --SDL_EXT_VLine_Pattern( theCanvas.surface, svgah( theCanvas, x ), svgav( theCanvas, y1 ), svgav( theCanvas, y2 ), theCanvas.pen.pattern, 0, 0, theCanvas.pen.mode );
        SDL_UnlockSurface( theCanvas.surface  );
     end if;

  when stamp =>
     if SDL_LockSurface( theCanvas.surface  ) = SDL_OK then
        --SDL_EXT_VLine_Pattern( theCanvas.surface, svgah( theCanvas, x ), svgav( theCanvas, y1 ), svgav( theCanvas, y2 ), theCanvas.pen.pattern, svgah( theCanvas, x1 ), svgav( theCanvas, y ), theCanvas.pen.mode );
        SDL_UnlockSurface( theCanvas.surface  );
     end if;

  when smear => -- not done
     if SDL_LockSurface( theCanvas.surface  ) = SDL_OK then
        --SDL_EXT_VLine_Pattern( theCanvas.surface, svgah( theCanvas, x ), svgav( theCanvas, y1 ), svgav( theCanvas, y2 ), theCanvas.pen.pattern, 0, 0, theCanvas.pen.mode );
        SDL_UnlockSurface( theCanvas.surface  );
     end if;

  when others => -- including pencil
     if SDL_LockSurface( theCanvas.surface  ) = SDL_OK then
        --SDL_EXT_VLine( theCanvas.surface, svgah( theCanvas, x ), svgav( theCanvas, y1 ), svgav( theCanvas, y2 ), theCanvas.pen.pixel, theCanvas.pen.mode );
        SDL_UnlockSurface( theCanvas.surface  );
     end if;
  end case;
  if theCanvas.pen.revealCount = 0 then
     SDL_UpdateRect( theCanvas.surface, svgah( theCanvas, x ), svgav( theCanvas, y1 ), 1, Uint32( svgav( theCanvas, y2 ) - svgav( theCanvas, y1 ) + 1 ) );
  end if;
end vline;

procedure Stretch( sourceCanvas : aCanvas; targetCanvas : in out aCanvas; target_x, target_y : aCoordinate; newWidth, newHeight : aCoordinate ) is
  scaledSurface      : system.address;
  source_x1          : SDL_HCoordinate;
  source_y1          : SDL_VCoordinate;
  source_x2          : SDL_HCoordinate;
  source_y2          : SDL_VCoordinate;
  scaled_x1          : SDL_HCoordinate;
  scaled_y1          : SDL_VCoordinate;
  scaled_x2          : SDL_HCoordinate;
  scaled_y2          : SDL_VCoordinate;
  NoHorizontalChange : boolean;
  NoVerticalChange   : boolean;
  source_range       : SDL_Rect;
  target_range       : SDL_Rect;
  Rmask              : SDL_RGB_Mask;
  Gmask              : SDL_RGB_Mask;
  Bmask              : SDL_RGB_Mask;
  Amask              : SDL_RGB_Mask;
  res                : SDL_Success;
  surface_ptr        : SDL_Surface_Ptr;
begin

  -- Convert to SDL coordinates and compute the target surface bounds

  svgaEffectiveRectangle( sourceCanvas, sourceCanvas.bounds,
     source_x1, source_y1, source_x2, source_y2 );
  -- scaled_x2 := source_x2 + svgah( sourceCanvas, newWidth );
  -- scaled_y2 := source_y2 + svgav( sourceCanvas, newHeight );
  scaled_x2 := svgah( sourceCanvas, newWidth );
  scaled_y2 := svgav( sourceCanvas, newHeight );
  scaled_x1 := 0;
  scaled_y1 := 0;
  -- scaled_x2 := scaled_x2 - source_x1;
  -- scaled_y2 := scaled_y2 - source_y1;

  -- Create the temporary scaled surface and check surface sizes

  if source_x1 + source_x2 = 0 then
     return;
  end if;
  -- scaledSurface := SDL_SetVideoMode( scaled_x2, scaled_y2, Interfaces.C.int( theCanvas.displayInfo.C_Res ), SDL_SWSURFACE );
put_line( "A" );
  SDL_EXT_Get_Pixel_Masks( Interfaces.C.int( sourceCanvas.displayInfo.C_Res ), Rmask, Gmask, Bmask, Amask );
  scaledSurface := SDL_CreateRGBSurface( SDL_SWSURFACE, Interfaces.C.int( scaled_x2 ), Interfaces.C.int( scaled_y2 ), Interfaces.C.int( sourceCanvas.displayInfo.C_Res ), Rmask, Gmask, Bmask, Amask );
  surface_ptr := SDL_Surface_Conv.To_Pointer( scaledSurface );
  if surface_ptr = null then
     put_line( standard_error, to_string( get_sdl_error ) );
     return;
  end if;
  NoVerticalChange  := (source_y2 = scaled_y2);
  NoHorizontalChange:= (source_x2 = scaled_x2);
put_line( "B" );

  -- Copy the pixels

  if NoVerticalChange then
put_line( "C1" );
     for y in 0..scaled_y2 loop
         for x in 0..scaled_x2 loop
             SDL_EXT_Plot( scaledSurface, x, y, 
                SDL_EXT_Raw_Pixel( sourceCanvas.surface, scaledSurface,
                x * (scaled_x2/source_x2), y ), copy );
         end loop;
      end loop;
  elsif NoHorizontalChange then
put_line( "C2" );
     for y in 0..scaled_y2 loop
         for x in 0..scaled_x2 loop
             SDL_EXT_Plot( scaledSurface, x, y, 
                SDL_EXT_Raw_Pixel( sourceCanvas.surface, scaledSurface,
                x, y * (scaled_y2/source_y2) ), copy );
         end loop;
      end loop;
  else
put_line( "C3 - right" );
     for y in 0..scaled_y2 loop
         for x in 0..scaled_x2 loop
             -- SDL_EXT_Plot( scaledSurface, x, y, 
             --    SDL_EXT_Raw_Pixel( sourceCanvas.surface, scaledSurface,
             --    x * (scaled_x2/source_x2), y * (scaled_y2/source_y2) ), copy );
             SDL_EXT_Plot( scaledSurface, x, y, 10240, copy );
         end loop;
      end loop;
  end if;
put_line( "D" );

  -- Draw stretched image

  source_range := SDL_Rect'( 0, 0, w => Uint16( scaled_x2 ), h => Uint16( scaled_y2 ) );
  -- target_range := SDL_Rect'( svgah( targetCanvas, target_x ), svgav( targetCanvas, target_y ) + SDL_VCoordinate( Uint16( scaled_y2 ) ), 0, 0 );
  target_range := SDL_Rect'( svgah( targetCanvas, target_x ), svgav( targetCanvas, target_y ), 0, 0 );
put_line( "pattern source range = " & scaled_x2'img & scaled_y2'img );
put_line( "screen target range = " & target_range.x'img & target_range.y'img & SDL_HCoordinate'image( target_range.x + scaled_x2 ) & SDL_VCoordinate'image(target_range.y + scaled_y2 ) );
SDL_EXT_Frame_Rect( targetcanvas.surface, target_range.x, target_range.y, target_range.x + scaled_x2, target_range.y + scaled_y2, -1, copy );
  -- res := SDL_UpperBlit( scaledSurface, source_range, targetCanvas.surface, target_range );
  res := SDL_UpperBlit( scaledSurface, source_range, targetCanvas.surface, target_range );
  if res < 0 then
     put( standard_error, "SDL_UpperBlit blit failed: " & to_string( get_sdl_error ) );
     new_line( standard_error );
  end if;

  -- free scaled surface here else memory leak
put_line( "E" );

  -- SDL free's surfaces so we have a problem....
  -- SDL_FreeSurface( scaledSurface );
put_line( "F" );
     if SDL_Flip( targetCanvas.surface ) /= SDL_OK then
        null;
     end if;

end Stretch;

end pen;

