PreviousUpNext

15.4.1470  src/lib/x-kit/xclient/pkg/window/draw.pkg

## draw.pkg
#
# Routines for drawing on windows and pixmaps.
#
# This is the library-internal version of this package;
# the client-level version is in:
#
#     src/lib/x-kit/xclient/xclient.pkg

# Compiled by:
#     src/lib/x-kit/xclient/xclient-internals.sublib






###                 "Humanity has advanced, when it has advanced,
###                  not because it has been sober, responsible,
###                  and cautious, but because it has been playful,
###                  rebellious, and immature."
###
###                                          -- Tom Robbins


stipulate
    include threadkit;                                  # threadkit     is from   src/lib/src/lib/thread-kit/src/core-thread-kit/threadkit.pkg
    #
    package sn = xsession;                              # xsession      is from   src/lib/x-kit/xclient/pkg/window/xsession.pkg
    package xg = xgeometry;                             # xgeometry     is from   src/lib/std/2d/xgeometry.pkg
    package fb = font_base;                             # font_base     is from   src/lib/x-kit/xclient/pkg/window/font-base.pkg
    package di = draw_imp;                              # draw_imp      is from   src/lib/x-kit/xclient/pkg/window/draw-imp.pkg
    package dt = draw_types;                            # draw_types    is from   src/lib/x-kit/xclient/pkg/window/draw-types.pkg
herein

    package draw {
        #
        exception BAD_DRAW_PARAMETER;

        stipulate

            # Extract from a drawable its
            #     draw_fn,
            #     xid
            #     depth
            #
            fun info_of_drawable (dt::DRAWABLE { to_drawimp, root => dt::r::WINDOW w } )
                    =>
                    {   w ->   dt::WINDOW { window_id, screen_pen_and_draw_imps => sn::SCREEN_PEN_AND_DRAW_IMPS { depth, ... }, ... };

                        { to_drawimp, id => window_id, depth };
                    };

                info_of_drawable (dt::DRAWABLE { to_drawimp, root => dt::r::PIXMAP pm } )
                    =>
                    {   pm -> dt::RW_PIXMAP { pixmap_id, screen_pen_and_draw_imps => sn::SCREEN_PEN_AND_DRAW_IMPS { depth, ... }, ... };

                        { to_drawimp, id => pixmap_id, depth };
                    };
            end;


            # Extract the xid and depth of a source drawable 
            #
            fun info_of_src (dt::FROM_WINDOW    (dt::WINDOW    { window_id, screen_pen_and_draw_imps => sn::SCREEN_PEN_AND_DRAW_IMPS { depth, ... }, ... } ))
                    =>
                    (window_id, depth);

                info_of_src (dt::FROM_RW_PIXMAP (dt::RW_PIXMAP { pixmap_id, screen_pen_and_draw_imps => sn::SCREEN_PEN_AND_DRAW_IMPS { depth, ... }, ... } ))
                    =>
                    (pixmap_id, depth);

                info_of_src (dt::FROM_RO_PIXMAP (dt::RO_PIXMAP (dt::RW_PIXMAP { pixmap_id, screen_pen_and_draw_imps => sn::SCREEN_PEN_AND_DRAW_IMPS { depth, ... }, ... } )))
                    =>
                    (pixmap_id, depth);
            end;


            # Build a drawing function from an encoding function.
            #
            # The functions have the basic type scheme
            #
            #   Drawable -> Pen -> Args -> Void
            #
            fun draw_fn f drawable pen
                =
                {   (info_of_drawable  drawable)
                        ->
                        { to_drawimp, id, ... };
                        

                    fn x =  to_drawimp (di::d::DRAW { to => id, pen, op => (f x) } );
                };

            fun draw_fn2 f drawable pen
                =
                {   my { to_drawimp, id, ... }
                        =
                        info_of_drawable  drawable;

                    fn x =   fn x' =   to_drawimp (di::d::DRAW { to => id, pen, op => (f x x') } );
                };

            fun check_list chkfn l
                =
                {   apply
                        (fn x = {   chkfn x;
                                    ();
                                }
                        )
                        l;

                    l;
                };

            fun check_item chkfn
                =
                (fn v = if (chkfn v)  v;
                        else          raise exception BAD_DRAW_PARAMETER;
                        fi
                );

            check_point =  check_item  xg::valid_point;
            check_box   =  check_item  xg::valid_box;
            check_line  =  check_item  xg::valid_line;
            check_arc   =  check_item  xg::valid_arc;

            check_pts   =  check_list  check_point;
            check_boxes =  check_list  check_box;
            check_lines =  check_list  check_line;
            check_arcs  =  check_list  check_arc;

        herein

            # Points:
            #
            draw_points     =  draw_fn  (fn pts =  di::o::POLY_POINT (FALSE, check_pts pts));
            draw_point_path =  draw_fn  (fn pts =  di::o::POLY_POINT (TRUE,  check_pts pts));
            draw_point      =  draw_fn  (fn pt  =  di::o::POLY_POINT (FALSE, [check_point pt]));

            # Lines and segments:
            #
            draw_lines   =  draw_fn  (fn pts   =  di::o::POLY_LINE (FALSE, check_pts pts));
            draw_path    =  draw_fn  (fn pts   =  di::o::POLY_LINE (TRUE,  check_pts pts));
            draw_segs    =  draw_fn  (fn lines =  di::o::POLY_SEG  (check_lines lines));
            draw_seg     =  draw_fn  (fn seg   =  di::o::POLY_SEG  [check_line seg]);

            # Filled polygons:
            #
            fill_polygon =  draw_fn  (fn { verts, shape } =  di::o::FILL_POLY (shape, FALSE, check_pts verts));
            fill_path    =  draw_fn  (fn { path,  shape } =  di::o::FILL_POLY (shape, TRUE,  check_pts path ));

            # Rectangles:
            #
            draw_boxes  =  draw_fn  (fn boxes =  di::o::POLY_BOX (check_boxes boxes));
            draw_box    =  draw_fn  (fn box   =  di::o::POLY_BOX [check_box box]);

            fill_boxes  =  draw_fn  (fn boxes =  di::o::POLY_FILL_BOX (check_boxes boxes));
            fill_box    =  draw_fn  (fn box   =  di::o::POLY_FILL_BOX [check_box box]);

            # Arcs:
            #
            draw_arcs    =  draw_fn  (fn arcs =  di::o::POLY_ARC      (check_arcs arcs));
            draw_arc     =  draw_fn  (fn arc  =  di::o::POLY_ARC      [check_arc arc]);
            fill_arcs    =  draw_fn  (fn arcs =  di::o::POLY_FILL_ARC (check_arcs arcs));
            fill_arc     =  draw_fn  (fn arc  =  di::o::POLY_FILL_ARC [check_arc arc]);

            # Circles:
            #
            fun circle_to_arc { center => xg::POINT { col, row }, rad }
                =
                {   diam = rad + rad;

                    xg::ARC {
                        col => col-rad, row => row-rad,
                        wide => diam, high => diam,
                        angle1 => 0, angle2 => 64*360
                    };
                };

            draw_circle =  draw_fn  (fn arg =  di::o::POLY_ARC      [circle_to_arc arg]);
            fill_circle =  draw_fn  (fn arg =  di::o::POLY_FILL_ARC [circle_to_arc arg]);

            # Text drawing:
            #
            draw_transparent_string
                =
                draw_fn2 (
                    fn (fb::FONT { id, ... } )
                        =
                        fn (pt, s)
                            =
                            di::o::POLY_TEXT8 (id, check_point pt, [di::t::TEXT (0, s)]
                    )
                );

            draw_opaque_string
                =
                draw_fn2
                    (fn (fb::FONT { id, ... } )
                        =
                        fn (pt, s)
                            =
                            di::o::IMAGE_TEXT8 (id, check_point pt, s)
                    );

            # Polytext drawing:
            #
            #    "There are two styles of text drawing: opaque and transparent.
            #
            #    "Opaque text [...] is drawn by first filling in the bounding box
            #     with the background color and then drawing the text with the
            #     foreground color.  The function and fill-style of the pen are
            #     ignored, replaced in effect by OP_COPY and pen::FILL_STYLE_SOLID
            #
            #    "In transparent text [...] the pixels corresponding to bits set in
            #     a character's glyph are drawn using the foreground color in the
            #     context of the other relevant pen values, while the other pixels
            #     are unmodified.
            #
            #    "The [draw_transparent_text] function provides a user-level batching
            #     mechanism for drawing multiple strings of the same line with possible
            #     intervening font changes or horizontal shifts."
            #
            #         -- p22-3 http:://mythryl.org/pub/exene/1993-lib.ps
            #            (Reppy + Gansner's 1993 eXene library manual.)
            package t {
                #
                Text      = TEXT         (fb::Font, List(Text_Item))
                also
                Text_Item = FONT         (fb::Font, List(Text_Item))
                          | STRING        String
                          | BLANK_PIXELS  Int                   # Skip this many pixels before next STRING.
                          ;
            };

            draw_transparent_text
                =
                draw_fn f
                where 
                    fun f (pt, t::TEXT (fb::FONT { id=>font, ... }, items))
                        =
                        di::o::POLY_TEXT8
                          ( font,
                            check_point pt,
                            reverse (#2 (flat (font, 0, items, [])))
                          )
                        where
                            fun flat (_, d, [], l)
                                    =>
                                    (d, l);

                                flat (font, d, (t::STRING s) ! r, l)
                                    =>
                                    flat (font, 0, r, di::t::TEXT (d, s) ! l);

                                flat (font, d, (t::BLANK_PIXELS d') ! r, l)
                                    =>
                                    flat (font, d+d', r, l);

                                flat (_, d, [t::FONT (fb::FONT { id=>font, ... }, items)], l)
                                    =>
                                    flat (font, d, items, (di::t::FONT font) ! l);

                                flat (font, d, (t::FONT (fb::FONT { id=>font', ... }, items)) ! r, l)
                                    =>
                                    {   my (d', l')
                                            =
                                            flat (font', d, items, (di::t::FONT font') ! l);

                                        flat (font, d', r, (di::t::FONT font) ! l');
                                    };
                            end;
                        end;
                end;

            # TODO: imageText (what does it mean?? *            XXX BUGGO FIXME


            #  BLT operations                           # "BLT" == "Block Transfer" -- dates back to Xerox Alto bitmapped display "bitblt" days.

            exception DEPTH_MISMATCH;
            exception BAD_PLANE;

            stipulate

                # * NOTE: we should probably check that 'from' and 'to' are on the same display *       XXX BUGGO FIXME

                fun copy_area_fn  msg_fn  (to, pen, to_pos, from, from_box)
                    =
                    {   my { id=>to_id, to_drawimp, depth=>to_depth }
                            =
                            info_of_drawable  to;

                        my (from_id, from_depth)
                            =
                            info_of_src  from;

                        my (msg, result)
                            =
                            msg_fn (check_point to_pos, from_id, from_box);

                        if (from_depth != to_depth)
                            #
                            raise exception DEPTH_MISMATCH;
                        fi;

                        to_drawimp (di::d::DRAW { to => to_id, pen, op => msg } );

                        result;
                    };

                fun copy_plane_fn msg_fn (to, pen, to_pos, from, from_box, plane)
                    =
                    {   my { id=>to_id, to_drawimp, ... }
                            =
                            info_of_drawable  to;


                        my (from_id, from_depth)
                            =
                            info_of_src  from;


                        my (msg, result)
                            =
                            msg_fn (check_point to_pos, from_id, from_box, plane);


                        if (plane < 0  or from_depth <= plane)
                            #
                            raise exception BAD_PLANE;
                        fi;

                        to_drawimp (di::d::DRAW { to => to_id, pen, op => msg } );

                        result;
                    };

                copy_area
                    =
                    copy_area_fn
                        (fn (to_pos, from_id, from_box)
                            =
                            {   sync_1shot = make_oneshot_maildrop ();

                                (di::o::COPY_AREA (to_pos, from_id, from_box, sync_1shot), sync_1shot);
                            }
                        );

                copy_plane
                    =
                    copy_plane_fn
                        (fn (to_pos, from_id, from_box, plane)
                            =
                            {   sync_1shot = make_oneshot_maildrop ();

                                (di::o::COPY_PLANE (to_pos, from_id, from_box, plane, sync_1shot), sync_1shot);
                            }
                        );

                copy_pmarea  = copy_area_fn  (fn arg = (di::o::COPY_PMAREA arg,  ()));
                copy_pmplane = copy_plane_fn (fn arg = (di::o::COPY_PMPLANE arg, ()));

                                                                        # "They made us many promises,
                                                                        #  more than I can remember,
                fun promise_event (to_drawimp, sync_1shot)              #  but they never kept but one;
                    =                                                   #  they promised to take our land,
                    {   sync_mailop                                     #  and they took it." -- Red Cloud
                            =
                            get'  sync_1shot;

                        guard .{

                            case (nonblocking_get  sync_1shot)

                                THE boxes_fn
                                    =>
                                    always_mailop ()
                                        ==>
                                        boxes_fn;

                                NULL
                                    =>
                                    {   dt::flush_drawimp  to_drawimp;

                                        sync_mailop
                                            ==>
                                            (fn boxes_fn = boxes_fn ());
                                    };
                            esac;
                        };
                    };
            herein

                fun pixel_blt to pen { from as (dt::FROM_WINDOW _), from_box, to_pos }
                        =>
                        {   to ->  dt::DRAWABLE { to_drawimp, ... };

                            sync_1shot = copy_area (to, pen, to_pos, from, from_box);

                            dt::flush_drawimp  to_drawimp;

                            (get  sync_1shot) ();
                        };

                    pixel_blt  to  pen  { from, from_box, to_pos }
                        =>
                        {   copy_pmarea (to, pen, to_pos, from, from_box);
                            [];
                        };
                end;

                fun pixel_blt_mailop to pen { from as (dt::FROM_WINDOW _), from_box, to_pos }
                        =>
                        {   to ->  dt::DRAWABLE { to_drawimp, ... };

                            sync_v = copy_area (to, pen, to_pos, from, from_box);

                            promise_event (to_drawimp, sync_v);
                        };

                    pixel_blt_mailop to pen { from, from_box, to_pos }
                        =>
                        {   copy_pmarea (to, pen, to_pos, from, from_box);

                            always_mailop [];
                        };
                end;

                fun plane_blt to pen { from as (dt::FROM_WINDOW _), from_box, to_pos, plane }
                        =>
                        {   to ->  dt::DRAWABLE { to_drawimp, ... };

                            sync_1shot = copy_plane (to, pen, to_pos, from, from_box, plane);

                            dt::flush_drawimp  to_drawimp;

                            (get  sync_1shot) ();
                        };

                    plane_blt to pen { from, from_box, to_pos, plane }
                        =>
                        {   copy_pmplane (to, pen, to_pos, from, from_box, plane);
                            [];
                        };
                end;

                fun plane_blt_mailop to pen { from as (dt::FROM_WINDOW _), from_box, to_pos, plane }
                        =>
                        {   to ->  dt::DRAWABLE { to_drawimp, ... };

                            sync_v = copy_plane (to, pen, to_pos, from, from_box, plane);

                            promise_event (to_drawimp, sync_v);
                        };          

                    plane_blt_mailop to pen { from, from_box, to_pos, plane }
                        =>
                        {   copy_pmplane (to, pen, to_pos, from, from_box, plane);

                            always_mailop [];
                        };
                end;


                fun bitblt to pen { from, from_box, to_pos }
                    =
                    plane_blt to pen { from, from_box, to_pos, plane=>0 };


                fun bitblt_mailop to pen { from, from_box, to_pos }
                    =
                    plane_blt_mailop to pen { from, from_box, to_pos, plane=>0 };


                fun texture_blt to pen { from, to_pos }
                    =
                    {   my xg::SIZE { wide, high }
                            =
                            dt::size_of_ro_pixmap  from;

                        box = xg::BOX { col=>0, row=>0, wide, high };

                        plane_blt  to pen { from=>dt::FROM_RO_PIXMAP from, from_box=>box, to_pos, plane=>0 };

                        ();
                    };

                fun tile_blt to pen { from, to_pos }
                    =
                    {   my xg::SIZE { wide, high }
                            =
                            dt::size_of_ro_pixmap  from;

                        box = xg::BOX { col=>0, row=>0, wide, high };

                        pixel_blt  to pen { from=>dt::FROM_RO_PIXMAP from, from_box=>box, to_pos };

                        ();
                    };

                fun copy_blt drawable pen { to_pos, from_box }
                    =
                    {   from = case drawable
                                  #                     
                                  dt::DRAWABLE { root => dt::r::WINDOW w,  ... } =>  dt::FROM_WINDOW     w;
                                  dt::DRAWABLE { root => dt::r::PIXMAP pm, ... } =>  dt::FROM_RW_PIXMAP pm;
                              esac;


                        pixel_blt
                            drawable
                            pen
                            { from, to_pos, from_box };
                    };

                fun copy_blt_mailop  drawable  pen  { to_pos, from_box }
                    =
                    {   from = case drawable
                                  #                     
                                  dt::DRAWABLE { root => dt::r::WINDOW w,  ... } =>  dt::FROM_WINDOW     w;
                                  dt::DRAWABLE { root => dt::r::PIXMAP pm, ... } =>  dt::FROM_RW_PIXMAP pm;
                              esac;

                        pixel_blt_mailop
                            drawable
                            pen
                            { from, to_pos, from_box };
                    };

            end;                                                        # stipulate

            # Clear part of a destination drawable.
            # For windows, this fills in the background color;
            # for pixmaps, this fills in all 0's (which is actually
            # the default foreground pixel value).
            #
            stipulate

                clear_pen
                    =
                    pen::make_pen  [  pen::p::FOREGROUND  rgb8::rgb8_color0  ];

            herein

                fun clear_box drawable
                    =
                    {   my { to_drawimp, id, ... }
                            =
                            info_of_drawable  drawable;

                        fn box
                            =
                            to_drawimp (
                                di::d::DRAW
                                   { to  => id,
                                     pen => clear_pen,
                                     op  => (di::o::CLEAR_AREA (check_box box))
                                   }
                            );
                    };
            end;

            # Clear the whole area of a drawable:
            #
            fun clear_drawable  to
                =
                clear_box  to  (xg::BOX { col=>0, row=>0, wide=>0, high=>0 } );


            fun flush drawable
                =
                {   (info_of_drawable  drawable)
                        ->
                        { to_drawimp, ... };

                    dt::flush_drawimp  to_drawimp;
                };

            fun drawimp_thread_id_of drawable
                =
                {   (info_of_drawable  drawable)
                        ->
                        { to_drawimp, ... };

                    dt::drawimp_thread_id_of  to_drawimp;
                };


        end;            # stipulate
    };                  # package draw 
end;                    # stipulate


Comments and suggestions to: bugs@mythryl.org

PreviousUpNext