PreviousUpNext

15.4.1615  src/lib/x-kit/xclient/src/window/cs-pixmat.pkg

## cs-pixmat.pkg                        "cs" == "client-side"
#
# A replacement for src/lib/x-kit/xclient/src/window/cs-pixmap.pkg
#
#   Client-side rectangular arrays of pixels,
#   Support for copying back and forth between them
#   and server-side windows makes them useful for
#   specifying icons, tiling patterns and other
#   client-originated image data intended for X display.
#
# See also:
#     src/lib/x-kit/xclient/src/window/ro-pixmap-old.pkg
#     src/lib/x-kit/xclient/src/window/window-old.pkg
#     src/lib/x-kit/xclient/src/window/rw-pixmap-old.pkg

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



#
# TODO                  XXX SUCKO FIXME
#   - support a left-pad
#   - support Z format



###                  "Science is what we understand well enough to explain
###                   to a computer.  Art is everything else we do."
###
###                                          -- Donald Knuth



stipulate
    include package   threadkit;                        # threadkit                     is from   src/lib/src/lib/thread-kit/src/core-thread-kit/threadkit.pkg
    #
    package byt =  byte;                                # byte                          is from   src/lib/std/src/byte.pkg
    package mtx =  rw_matrix;                           # rw_matrix                     is from   src/lib/std/src/rw-matrix.pkg
    package r8  =  rgb8;                                # rgb8                          is from   src/lib/x-kit/xclient/src/color/rgb8.pkg
    package s1u =  vector_slice_of_one_byte_unts;       # vector_slice_of_one_byte_unts is from   src/lib/std/src/vector-slice-of-one-byte-unts.pkg
    package u1b =  one_byte_unt;                        # one_byte_unt                  is from   src/lib/std/one-byte-unt.pkg
    package v1u =  vector_of_one_byte_unts;             # vector_of_one_byte_unts       is from   src/lib/std/src/vector-of-one-byte-unts.pkg
    package v2w =  value_to_wire;                       # value_to_wire                 is from   src/lib/x-kit/xclient/src/wire/value-to-wire.pkg
    package w2v =  wire_to_value;                       # wire_to_value                 is from   src/lib/x-kit/xclient/src/wire/wire-to-value.pkg
    package w8  =  one_byte_unt;                        # one_byte_unt                  is from   src/lib/std/one-byte-unt.pkg
    package g2d =  geometry2d;                          # geometry2d                    is from   src/lib/std/2d/geometry2d.pkg
    package xt  =  xtypes;                              # xtypes                        is from   src/lib/x-kit/xclient/src/wire/xtypes.pkg
    package xtr =  xlogger;                             # xlogger                       is from   src/lib/x-kit/xclient/src/stuff/xlogger.pkg
    #
    package di  =  xserver_ximp;                        # xserver_ximp                  is from   src/lib/x-kit/xclient/src/window/xserver-ximp.pkg
#   package dt  =  draw_types;                          # draw_types                    is from   src/lib/x-kit/xclient/src/window/draw-types.pkg
    package dy  =  display;                             # display                       is from   src/lib/x-kit/xclient/src/wire/display.pkg
    package w2x =  windowsystem_to_xserver;             # windowsystem_to_xserver       is from   src/lib/x-kit/xclient/src/window/windowsystem-to-xserver.pkg
    package pn  =  pen;                                 # pen                           is from   src/lib/x-kit/xclient/src/window/pen.pkg
    package xj  =  xsession_junk;                       # xsession_junk                 is from   src/lib/x-kit/xclient/src/window/xsession-junk.pkg
#   package x2s =  xclient_to_sequencer;                # xclient_to_sequencer          is from   src/lib/x-kit/xclient/src/wire/xclient-to-sequencer.pkg
    package wpm =  rw_pixmap;                           # rw_pixmap                     is from   src/lib/x-kit/xclient/src/window/rw-pixmap.pkg
    #
    trace =  xtr::log_if  xtr::io_logging 0;            # Conditionally write strings to tracing.log or whatever.
herein


    package   cs_pixmat
    : (weak)  Cs_Pixmat                                 # Cs_Pixmat                     is from   src/lib/x-kit/xclient/src/window/cs-pixmat.api
    {
        exception BAD_CS_PIXMAT_DATA;

        v1uextract =    s1u::to_vector
                        o
                        s1u::make_slice;

        Cs_Pixmat = CS_PIXMAT { size:  g2d::Size,
                                data:  v1u::Vector
                              };

        # Two cs_pixmats   are the same
        # iff their fields are the same:
        #
        fun same_cs_pixmat
            ( CS_PIXMAT { size => size1, data => data1 },
              CS_PIXMAT { size => size2, data => data2 }
            )
            =
            if (not (g2d::size::eq (size1, size2)))
                #
                FALSE;
            else
                data1 == data2;
            fi;

        #
        fun string_to_data (wid, s)                                                                                             # Map a row of data coded as  a string to a bit representation.
            =                                                                                                                   # The data may be either encoded in hex (with a preceding "0x")
            case (string::explode s)                                                                                            # or in binary (with a preceeding "0b").
                #
                ('0' ! 'x' ! r)
                    =>
                    make_row (nbytes, r, [])
                    where
                        nbytes = ((wid + 7) / 8);   #  # of bytes per line 

                        fun cvt_char c
                            =
                            if (char::is_digit c)
                                #
                                byte::char_to_byte c - byte::char_to_byte '0';
                            else
                                if (char::is_hex_digit c)
                                    #
                                    char::is_upper c
                                    ??  byte::char_to_byte c - byte::char_to_byte 'A'
                                    ::  byte::char_to_byte c - byte::char_to_byte 'a';
                                else
                                    raise exception BAD_CS_PIXMAT_DATA;
                                fi;
                            fi;

                        fun make_row (0, [], l) =>  v1u::from_list (reverse l);
                            make_row (0,  _, _) =>  raise exception BAD_CS_PIXMAT_DATA;

                            make_row (i, d1 ! d0 ! r, l)
                                =>
                                make_row (i - 1, r,
                                  w8::bitwise_or (w8::(<<) (cvt_char d1, 0u4), cvt_char d0) ! l);

                            make_row _
                                =>
                                raise exception BAD_CS_PIXMAT_DATA;
                        end;
                    end;

                ('0' ! 'b' ! r)
                    =>
                    make_row (wid, 0ux80, r, 0u0, [])
                    where
                        fun make_row (0, _, [], b, l)
                                =>
                                v1u::from_list (reverse (b ! l));

                            make_row (_, _, [], _, _)
                                =>
                                raise exception BAD_CS_PIXMAT_DATA;

                            make_row (i, 0u0, l1, b, l2)
                               =>
                               make_row (i, 0ux80, l1, 0u0, b ! l2);

                            make_row (i, m, '0' ! r, b, l)
                               =>
                               make_row (i - 1, w8::(>>) (m, 0u1), r, b, l);

                            make_row (i, m, '1' ! r, b, l)
                               =>
                               make_row (i - 1, w8::(>>) (m, 0u1), r, w8::bitwise_or (m, b), l);

                            make_row _
                                =>
                                raise exception BAD_CS_PIXMAT_DATA;
                        end;
                    end;

                _   => raise exception BAD_CS_PIXMAT_DATA;
            esac;


        reverse_bits =  byt::reverse_byte_bits;                                                                                 # Reverse the bit-order of a byte 

        # Routines to re-order bits and bytes to the server's format (stolen from
        # XPutImage::c in Xlib).  We represent data in the following format:
        #
        #   scan-line unit = 1 byte
        #   byte-order     = MSB first (doen't matter for 1-byte scan units)
        #   bit-order      = MSB first (bit 0 is leftmost on display)
        #
        # This is the "1Mm" format of XPutImage.c in Xlib.  The relevant lines
        # in the conversion table are:
        #
        #         1Mm 2Mm 4Mm 1Ml 2Ml 4Ml 1Lm 2Lm 4Lm 1Ll 2Ll 4Ll
        #   1Mm:   n   n   n   R   S   L   n   s   l   R   R   R
        #   1Ml:   R   R   R   n   s   l   R   S   L   n   n   n
        #
        #   legend:
        #               n   no changes
        #               s   reverse 8-bit units within 16-bit units
        #               l   reverse 8-bit units within 32-bit units
        #               R   reverse bits within 8-bit units
        #               S   s+R
        #               L   l+R

        fun no_swap x =  x;

        fun swap_bits data
            =
            v1u::from_list
                (v1u::fold_backward (\\ (b, l) = reverse_bits b ! l)
                                    []
                                    data
                );

        fun explode_v data
            =
            v1u::fold_backward  (!)  []  data;

        fun swap_two_bytes s
            =
            v1u::from_list (swap (explode_v s))
            where
                fun swap (a ! b ! r) =>  b ! a ! (swap r);
                    swap [] =>  [];
                    swap _  =>  xgripe::impossible "[swap_two_bytes: bad image data]";
                end;
            end;

        fun swap_four_bytes s
            =
            v1u::from_list (swap (explode_v s))
            where
                fun swap (a ! b ! c ! d ! r) =>  d ! c ! b ! a ! (swap r);
                    swap [] =>  [];
                    swap _  =>  xgripe::impossible "[swap_four_bytes: bad image data]";
                end;
            end;

        fun swap_bits_and_two_bytes s
            =
            v1u::from_list (swap (explode_v s))
            where
                fun swap (a ! b ! r) =>  (reverse_bits b) ! (reverse_bits a) ! (swap r);
                    swap [] =>  [];
                    swap _  =>  xgripe::impossible "[swap_bits_and_two_bytes: bad image data]";
                end;
            end;

        fun swap_bits_and_four_bytes  s
            =
            v1u::from_list (swap (explode_v s))
            where
                fun swap (a ! b ! c ! d ! r)
                        =>
                        (reverse_bits d) ! (reverse_bits c) ! (reverse_bits b) ! (reverse_bits a) ! (swap r);

                    swap [] =>   [];
                    swap _  =>   xgripe::impossible "[swap_bits_and_four_bytes: bad image data]";
                end;
            end;

        fun swap_func (xt::RAW08, xt::MSBFIRST, xt::MSBFIRST) =>  no_swap;
            swap_func (xt::RAW16, xt::MSBFIRST, xt::MSBFIRST) =>  no_swap;
            swap_func (xt::RAW32, xt::MSBFIRST, xt::MSBFIRST) =>  no_swap;
            swap_func (xt::RAW08, xt::MSBFIRST, xt::LSBFIRST) =>  swap_bits;
            swap_func (xt::RAW16, xt::MSBFIRST, xt::LSBFIRST) =>  swap_bits_and_two_bytes;
            swap_func (xt::RAW32, xt::MSBFIRST, xt::LSBFIRST) =>  swap_bits_and_four_bytes;
            swap_func (xt::RAW08, xt::LSBFIRST, xt::MSBFIRST) =>  no_swap;
            swap_func (xt::RAW16, xt::LSBFIRST, xt::MSBFIRST) =>  swap_two_bytes;
            swap_func (xt::RAW32, xt::LSBFIRST, xt::MSBFIRST) =>  swap_four_bytes;
            swap_func (xt::RAW08, xt::LSBFIRST, xt::LSBFIRST) =>  swap_bits;
            swap_func (xt::RAW16, xt::LSBFIRST, xt::LSBFIRST) =>  swap_bits;
            swap_func (xt::RAW32, xt::LSBFIRST, xt::LSBFIRST) =>  swap_bits;
        end;

        fun pad_to_bits xt::RAW08 =>   0u8;
            pad_to_bits xt::RAW16 =>  0u16;
            pad_to_bits xt::RAW32 =>  0u32;
        end;

        fun round_down (nbytes, pad)
            =
            unt::to_int_x(
              unt::bitwise_and (unt::from_int nbytes, unt::bitwise_not((pad_to_bits pad) - 0u1)));

        fun round_up (nbytes, pad)
            =
            {   bits = (pad_to_bits pad) - 0u1;
                #
                unt::to_int_x (unt::bitwise_and (unt::from_int nbytes + bits, unt::bitwise_not bits));
            };

        # Pad and re-order image data as necessary
        # to match the server's format.
        #
        stipulate
            #
            pad1 = v1u::from_fn (1, \\ _ = 0u0);
            pad2 = v1u::from_fn (2, \\ _ = 0u0);
            pad3 = v1u::from_fn (3, \\ _ = 0u0);
            #   
        herein
            #
            fun adjust_image_data (dpy_info: dy::Xdisplay)
                =
                {
                    fun extra (v, m)
                        =
                        unt::bitwise_and (unt::from_int (v1u::length v), m);

                    pad_scan_line
                        =
                        case dpy_info.bitmap_scanline_pad
                            #
                            xt::RAW08
                                =>
                                \\ s = s;

                            xt::RAW16
                                =>
                                \\ s =
                                    if (extra (s, 0u1) == 0u0)  s;
                                    else                        v1u::cat [s, pad1];
                                    fi;

                            xt::RAW32
                                =>
                                \\ s =  case (extra (s, 0u3))
                                            #
                                            0u0 => s;
                                            0u1 => v1u::cat [s, pad3];
                                            0u2 => v1u::cat [s, pad2];
                                            _   => v1u::cat [s, pad1];
                                        esac;


                        esac;

                    swapfn =    swap_func
                                  (
                                    dpy_info.bitmap_scanline_unit,
                                    dpy_info.image_byte_order,
                                    dpy_info.bitmap_bit_order
                                  );

                    \\ data =   map (\\ s = swapfn (pad_scan_line s))
                                    data;
                };
        end;

        # Copy rectangle from clientside window
        # into server-side offscreen window.
        #
        # It wouldn't take much to generalize
        # this to all drawables & pens. Additional
        # efficiency could be gained by having the
        # extract_row function extract rows already
        # padded correctly for the display when possible. XXX SUCKO FIXME
        #
        fun make_clientside_pixmat_to_pixmap_copy_drawop
                #
                (to:            xt::Window_Id)          # This will currently be either   window.window_id   or   (the window.subwindow_or_view).
#               (window:        xj::Window)
                (dpy_info:      dy::Xdisplay)
#               (pixmap_id:     xt::Window_Id)          # Maybe should be xt::Drawable_Id?  They are both just defined as Xid though.
                #
#               (screen: xj::Screen)
                #
                { from => (m as { rw_vector, rows, cols }):  mtx::Rw_Matrix( r8::Rgb8 ), from_box, to_point }
            =
            case (g2d::box::intersection (from_box, g2d::box::make (g2d::point::zero, { wide => cols, high => rows })))         # Clip from_box to clientside window:
                #
                NULL => [];                                                                                                     # No intersection so nothing to do.
                #
                THE from_box'
                    =>
                    {   ops = put_sub_image (from_box', g2d::point::add (to_point, delta));
                        #
                        [ { to,
                            pen =>  pn::default_pen,
                            op  =>  w2x::x::PUT_IMAGE ops
                          }
                        ];
                    }
                    where
                        delta     = g2d::point::subtract
                                      ( g2d::box::upperleft  from_box',
                                        g2d::box::upperleft  from_box
                                      );

                        depth           = 24;                                           # XXX SUCKO FIXME should be deriving this from xserver_info or visual or screen or such.
                        bytes_per_pixel =  4;                                           # XXX SUCKO FIXME should be deriving this from xserver_info or visual or screen or such.

                        # Minimum no. of 4-byte words needed for PutImage.
                        # There should be a function in XRequest to provide this.       XXX SUCKO FIXME
                        #
                        request_size = 6;

                        # Number of image bytes per request:
                        #
                        available =  (int::min (dpy_info.max_request_length, 65536) - request_size) * 4;

                        fun copy_from_clientside_pixmat_to_pixmap_request (r as { col, row, wide, high }, to_point)
                            =
                            {
                                pixels_to_send =  wide * high;
                                bytes_to_send  =  pixels_to_send * bytes_per_pixel;

                                vectors_to_send =  REF ([]: List(v1u::Vector));                                                 # This isn't terribly efficient;  we should have a PUT_IMAGE that takes rw_vectors, or build in a rw_vector and convert to vector or something. All in good time.

                                fun note vec
                                    =
                                    vectors_to_send  :=  vec ! *vectors_to_send;

                                fun col_lup (r, c)
                                    =
                                    if (c == wide)   ();
                                    else             
                                        (r8::rgb8_to_ints  m[row+r,col+c]) ->  (red, green, blue);
                                        note (v1u::from_list (map u1b::from_int [ blue, green, red, 0 ]));
                                        col_lup (r, c+1);
                                    fi;

                                fun row_lup r
                                    =
                                    if (r == high)  ();
                                    else            col_lup (r, 0);
                                                    row_lup (r+1);
                                    fi;

                                row_lup 0;

                                data = v1u::cat (reverse *vectors_to_send);

                                [ { to_point,
                                    size => { wide, high },
                                    depth,
                                    lpad => 0,
                                    format => xt::ZPIXMAP,
                                    data
                                  }
                                ];
                            };                                                                                                          # fun copy_from_clientside_pixmat_to_pixmap_request

                        # Decompose copy_from_clientside_pixmat_to_pixmap
                        # into multiple requests smaller than max size.
                        #
                        # I'm ignoring the possiblity that the X server                                                                 # Current xorg server max request size is 64K bytes.
                        # max request size might be too small to accept                                                                 # A 5000 pixel wide monitor at 4 bytes/pixel yields 20KB/row.
                        # a single row of pixels:
                        #
                        fun put_sub_image (r as { col, row, wide, high }, pt as { col=>dx, row=>dy } )
                            =
                            {   left_pad = 0;
                                #
                                bytes_per_row = 4 * wide;

                                if ((bytes_per_row * high) <= available)
                                    #
                                    copy_from_clientside_pixmat_to_pixmap_request (r, pt);
                                else
                                    if (high > 1)
                                        #
                                        high' = int::max (1, available / bytes_per_row);

                                        put_sub_image ({ col, row, wide, high=>high' }, pt)
                                        @
                                        put_sub_image ({ col, row=>row+high', wide, high=>high-high' }, { col=>dx, row=>dy+high' } );
                                    else
                                        put_sub_image ({ col, row, wide, high=>1 }, pt);
                                    fi;
                                fi;
                            };
                    end;                                                                                                                # fun copy_from_clientside_pixmat_to_pixmap 
            esac;

        fun copy_from_clientside_pixmat_to_pixmap
                #
                (window: xj::Window)
                #
                (arg as { from => (m as { rw_vector, rows, cols }):  mtx::Rw_Matrix( r8::Rgb8 ), from_box, to_point })
            =
            window.windowsystem_to_xserver.draw_ops
                (
                    make_clientside_pixmat_to_pixmap_copy_drawop
                        window.window_id
                        window.screen.xsession.xdisplay
                        arg
                );

#       #  Create image data from an ascii representation 
#       #
#       fun make_clientside_pixmat_from_ascii (wide, p0 ! rest)
#               =>
#               {   fun mk (n, [],    l) =>   (n, reverse l);
#                       mk (n, s ! r, l) =>   mk (n+1, r, string_to_data (wide, s) ! l);
#                   end;
#
#                   (mk (0, p0, []))
#                       ->
#                       (high, plane0);
#
#                   fun check data
#                       =
#                       {   (mk (0, data,[]))
#                               ->
#                               (h, plane);
#
#                           if (h == high)    plane;
#                           else              raise exception  BAD_CS_PIXMAT_DATA;
#                           fi;
#                       };
#
#                   CS_PIXMAT {
#                       size =>   { wide, high },
#                       data =>   plane0 ! (map check rest)
#                   };
#              };
#
#           make_clientside_pixmat_from_ascii (wide, [])
#               =>
#               raise exception BAD_CS_PIXMAT_DATA;
#       end;



        # Create a server-side offscreen window from
        # data in a client-side window:
        #
#       fun make_readwrite_pixmap_from_clientside_pixmat
#               screen
#               (cs_pixmat_old as CS_PIXMAT { size, data } )
#           =
#           pixmap
#           where
#               depth = length data;
#
#               pixmap
#                   =
#                   wpm::make_readwrite_pixmap
#                       screen
#                       (size, depth);
#
#               copy_from_clientside_pixmat_to_pixmap
#                   pixmap
#                   {
#                     from     =>  cs_pixmat_old, 
#                     from_box =>  g2d::box::make (g2d::point::zero, size), 
#                     to_point =>  g2d::point::zero
#                   };
#           end;


        # Create a pixmap from ascii data:
        #
#       fun make_readwrite_pixmap_from_ascii_data
#               screen
#               (wide, ascii_rep)
#           =
#           make_readwrite_pixmap_from_clientside_pixmat
#               screen
#               (make_clientside_pixmat_from_ascii (wide, ascii_rep));


# rgb8.pkg encodes as:
#     red   = 0xFF0000
#     green = 0x00FF00
#     blue  = 0x0000FF
#
# In xclient-unit-test.pgk we set 
#               background_pixel =  r8::rgb8_from_ints (128+64, 1, 255);
#
# and it reads back as 512 bytes looking so:
#
#    FF.01.C0.00. FF.01.C0.00. FF.01.C0.00. FF.01.C0.00. ...
# so

        stipulate

            fun make_clientside_pixmat_from_pixmap_or_window'
                (
                  box,                                          # Get the pixelmap pixel contents from this part of
                  pixmap_or_window_id,                          # this server-side pixmap or window.
                  screen,
                  reply_image
                )
                =
                {   image = w2v::decode_get_image_reply  reply_image;
                    #
                    (g2d::box::size  box)     ->  our_size;

                    (xj::xsession_of_screen  screen)
                        ->
                        { xdisplay, windowsystem_to_xserver, ... }: xj::Xsession;


                    image ->    { depth, data, visualid };

                    swapfn =    swap_func
                                  (
                                    xdisplay.bitmap_scanline_unit,
                                    xdisplay.image_byte_order,
                                    xdisplay.bitmap_bit_order
                                  );

                    lines = our_size.high;

                    bytes_per_line  =  round_up (our_size.wide, xdisplay.bitmap_scanline_pad) / 8;
#                   bytes_per_plane =  bytes_per_line * lines_per_plane;

#                   fun do_line start
#                       =
#                       swapfn (v1uextract (data, start, THE bytes_per_line));
#
#                   fun make_line (i, start)
#                       =
#                       i == lines_per_plane
#                         ?? []
#                         :: (do_line start) ! (make_line (i+1, start+bytes_per_line));
#
#                   fun make_plane (i, start)
#                       =
#                       i == depth
#                         ?? []
#                         :: (make_line (0, start)) ! (make_plane (i+1, start+bytes_per_plane));


                    bytes_per_pixel = 4;                                                                                        # XXX SUCKO FIXME We should be prying this out of xserver_info or such.

                    expected_bytes =  our_size.high * our_size.wide * bytes_per_pixel;
                    actual_bytes   =  v1u::length data;
                    if (actual_bytes != expected_bytes)
                        #
                        msg =   (sprintf "make_clientside_pixmat_from_pixmap_or_window: Expected read of (%d rows, %d cols) to produce %d bytes but got %d instead."
                                                    our_size.high  our_size.wide  expected_bytes  actual_bytes
                                                );
                        log::fatal            msg;
                        raise exception  DIE msg;       # Shouldn't get here, but compiler doesn't know that log::fatal doesn't return.
                    fi;

                    m =  mtx::make_rw_matrix ((our_size.high, our_size.wide), r8::rgb8_black);

                    d =  (data: v1u::Vector);

                    for (i = 0, row = 0, col = 0;  i < actual_bytes;  i = i + 4) {
                        #
                        blue  =   u1b::to_int (d[ i+0 ]);
                        green =   u1b::to_int (d[ i+1 ]);
                        red   =   u1b::to_int (d[ i+2 ]);

                        rgb8  =   r8::rgb8_from_ints (red, green, blue);

                        m[row,col] :=   rgb8;

                        my (row, col) =     if (col < our_size.wide - 1)        (row, col+1);
                                            else                                (row+1,   0);
                                            fi;
                    };

                    m;
                };

            # Create a client-side pixmat from
            # a server-side offscreen window.
            #
            fun make_clientside_pixmat_from_pixmap_or_window
                (
                  box,                                          # Get the pixelmap pixel contents from this part of
                  pixmap_or_window_id,                          # this server-side pixmap or window.
                  screen
                )
                =
                {   (xj::xsession_of_screen  screen)
                        ->
                        { windowsystem_to_xserver, ... }: xj::Xsession;


                    all_planes =  unt::bitwise_not  0u0;

                    msg =   v2w::encode_get_image
                              { 
                                drawable   =>  pixmap_or_window_id, 
                                box,
                                plane_mask =>  xt::PLANEMASK all_planes, 
                                format     =>  xt::ZPIXMAP
                              };

                    reply_image
                        =
                        block_until_mailop_fires                                        # XXX SUCKO FIXME
#                       ========================
                          (
                            windowsystem_to_xserver.xclient_to_sequencer.send_xrequest_and_read_reply    msg                    # src/lib/x-kit/xclient/src/wire/xclient-to-sequencer.pkg
                          );

                    make_clientside_pixmat_from_pixmap_or_window'
                      (
                        box,                                                                                                    # Get the pixelmap pixel contents from this part of
                        pixmap_or_window_id,                                                                                    # this server-side pixmap or window.
                        screen,
                        reply_image
                      );
                };                                                                                                              # fun make_clientside_pixmat_from_pixmap_or_window


            # Same as above, done nonblocking for imps:
            #
            fun pass_clientside_pixmat_from_pixmap_or_window
                (
                  box,                                                                                                          # Get the pixelmap pixel contents from this part of
                  pixmap_or_window_id,                                                                                          # this server-side pixmap or window.
                  screen,
                  (to:                  Replyqueue),
                  (sink_fn:             (mtx::Rw_Matrix( r8::Rgb8 ) -> Void))
                )
                =
                {   (xj::xsession_of_screen  screen)
                        ->
                        { windowsystem_to_xserver, ... }: xj::Xsession;


                    all_planes =  unt::bitwise_not  0u0;

                    msg =   v2w::encode_get_image
                              { 
                                drawable   =>  pixmap_or_window_id, 
                                box,
                                plane_mask =>  xt::PLANEMASK all_planes, 
                                format     =>  xt::ZPIXMAP
                              };

                    windowsystem_to_xserver.xclient_to_sequencer.send_xrequest_and_pass_reply                                   # src/lib/x-kit/xclient/src/wire/xclient-to-sequencer.pkg
                        #
                        msg
                        to
                        (\\ reply_image
                            =
                            {   rw_matrix
                                    =
                                    make_clientside_pixmat_from_pixmap_or_window'
                                      (
                                        box,                                                                                    # Get the pixelmap pixel contents from this part of
                                        pixmap_or_window_id,                                                                    # this server-side pixmap or window.
                                        screen,
                                        reply_image
                                      );

                                sink_fn  rw_matrix;
                            }
                        );
                };                                                                                                              # fun make_clientside_pixmat_from_pixmap_or_window

        herein

            # Create a client-side window from
            # a server-side offscreen window.
            #
            fun make_clientside_pixmat_from_readwrite_pixmap
                    #
                    ( box:                                              g2d::Box,
                      { pixmap_id, size, screen, per_depth_imps }:      xj::Rw_Pixmap
                    )
                =
                {
#                   box = g2d::box::make (g2d::point::zero, size);      # Copy all of pixmap.
                    #
                    make_clientside_pixmat_from_pixmap_or_window (box, pixmap_id, screen);
                };

            # Same as above, done nonblocking for imps:
            #
            fun pass_clientside_pixmat_from_readwrite_pixmap
                    #
                    ( box:                                              g2d::Box,
                      { pixmap_id, size, screen, per_depth_imps }:      xj::Rw_Pixmap
                    )
                    (to:                Replyqueue)
                    (sink_fn:           (mtx::Rw_Matrix( r8::Rgb8 ) -> Void))
                =
                {
#                   box = g2d::box::make (g2d::point::zero, size);      # Copy all of pixmap.
                    #
                    pass_clientside_pixmat_from_pixmap_or_window (box, pixmap_id, screen, to, sink_fn);
                };

            # Create a client-side window from part of
            # a server-side onscreen window.  The underlying
            # GetImage X call is snarky:
            #
            #   o The window must be entirely onscreen.
            #   o Any parts of it obscured by non-descendents      come back undefined.
            #   o Any parts of it obscured by different-depth kids come back undefined.
            #
            # According to the docs on p57 of http://mythryl.org/pub/exene/X-protocol-R6.pdf
            #
            #    "This request is not general-purpose in the same sense
            #     as other graphics-related requests.  It is intended
            #     specifically for rudimentary hardcopy support." 
            #
            fun make_clientside_pixmat_from_window
                    #
                    (box, window as { window_id, screen, windowsystem_to_xserver, ... }: xj::Window)
                =
                {   
                    make_clientside_pixmat_from_pixmap_or_window    (box, window_id, screen);
                };

            # Same as above, done nonblocking for imps:
            #
            fun pass_clientside_pixmat_from_window
                    #
                    (box, window as { window_id, screen, windowsystem_to_xserver, ... }: xj::Window)
                    (to:                Replyqueue)
                    (sink_fn:           (mtx::Rw_Matrix( r8::Rgb8 ) -> Void))
                =
                {   
                    pass_clientside_pixmat_from_pixmap_or_window    (box, window_id, screen, to, sink_fn);
                };
        end;



        fun make_clientside_pixmat_from_readonly_pixmap (box: g2d::Box, xj::RO_PIXMAP pm)
            =
            make_clientside_pixmat_from_readwrite_pixmap   (box, pm);

        # Same as above, done nonblocking for imps:
        #
        fun pass_clientside_pixmat_from_readonly_pixmap (box: g2d::Box, xj::RO_PIXMAP pm)
            =
            pass_clientside_pixmat_from_readwrite_pixmap   (box, pm);



    };                                                                  # package cs_pixmat_old

end;


########################################################################################################
# Note[1]: XYPIXMAP vs ZPIXMAP image interchange with the X server.
#
# From http://mythryl.org/pub/exene/X-protocol-R6.pdf
#
#     PutImage: The left-pad must be zero for ZPixmap format (or a Match error results).
#               The width argument defines the width of the actual image and does not include left-pad.
#
#     GetImage: If ZPixmap is specified, then bits in all planes not specified in plane-mask are transmitted as zero.
#               The returned depth is as specified when the drawable was created and is the same as a depth component in a FORMAT structure (in the connection setup), not a bits-per-pixel component.
#
# NB: On my xorg x86 Linux we have
#     image_byte_order      s= LEAST_SIGNIFICANT_BYTE_FIRST
#     bitmap_order          s= LEAST_SIGNIFICANT_BIT_FIRST  
# (so XYNORMALIZE and ZNORMALIZE are no-ops)
#    bitmap_scanline_unit  s= 32 bits
#    bitmap_scanline_pad   s= 32 bits
# and the visuals I see actually in use all look like
#            depth             d=24
#            colormap entries  d=256
#            colorbits_per_rgb d=8
#            red_mask          x=00ff0000
#            green_mask        x=0000ff00
#            blue_mask         x=000000ff
#            display_class     s=TRUE_COLOR
#    
# In practice, the best documentation of this stuff appears to be the
# xlib source code, so I'm excerpting here the parts relevant to
# processing XYPIXMAP vs ZPIXMAP images:
#    
# From /mit/lib/X/XImUtil.c
#  * The ROUNDUP macro rounds up a quantity to the specified boundary, 
#  * then truncates to bytes. 
#  * 
#  * The XYNORMALIZE macro determines whether XY format data requires  
#  * normalization and calls a routine to do so if needed. The logic in 
#  * this module is designed for LSBFirst byte and bit order, so  
#  * normalization is done as required to present the data in this order. 
#  * 
#  * The ZNORMALIZE macro performs byte and nibble order normalization if  
#  * required for Z format data. 
#  * 
#  * The XYINDEX macro computes the index to the starting byte (char) boundary 
#  * for a bitmap_unit containing a pixel with coordinates x and y for image 
#  * data in XY format. 
#  *  
#  * The ZINDEX macro computes the index to the starting byte (char) boundary  
#  * for a pixel with coordinates x and y for image data in ZPixmap format. 
#  *  
#  */ 
#  
# #define ROUNDUP(nbytes, pad) ((((nbytes) + ((pad)-1)) / (pad)) * ((pad)>>3)) 

# #define XYNORMALIZE(bp, img) \ 
#     if ((img->byte_order == MSBFirst) || (img->bitmap_bit_order == MSBFirst)) \ 
#         _xynormalizeimagebits((unsigned char *)(bp), img) 
#  
# #define ZNORMALIZE(bp, img) \ 
#     if (img->byte_order == MSBFirst) \ 
#         _znormalizeimagebits((unsigned char *)(bp), img) 
#  
# #define XYINDEX(x, y, img) \ 
#     ((y) * img->bytes_per_line) + \ 
#     (((x) + img->xoffset) / img->bitmap_unit) * (img->bitmap_unit >> 3) 
#  
# #define ZINDEX(x, y, img) ((y) * img->bytes_per_line) + \ 
#     (((x) * img->bits_per_pixel) >> 3) 
#  

#        if (format == ZPixmap) 
#            image->bytes_per_line =  
#               ROUNDUP((bits_per_pixel * width), image->bitmap_pad); 
#        else 
#            image->bytes_per_line = 
#               ROUNDUP((width + offset), image->bitmap_pad); 
# /* 
#  * GetPixel 
#  *  
#  * Returns the specified pixel.  The X and Y coordinates are relative to  
#  * the origin (upper left [0,0]) of the image.  The pixel value is returned 
#  * in normalized format, i.e. the LSB of the long is the LSB of the pixel. 
#  * The algorithm used is: 
#  * 
#  *      copy the source bitmap_unit or Zpixel into temp 
#  *      normalize temp if needed 
#  *      extract the pixel bits into return value 
#  * 
#  */ 
#  
# static unsigned long Const low_bits_table[] = { 
#     0x00000000, 0x00000001, 0x00000003, 0x00000007, 
#     0x0000000f, 0x0000001f, 0x0000003f, 0x0000007f, 
#     0x000000ff, 0x000001ff, 0x000003ff, 0x000007ff, 
#     0x00000fff, 0x00001fff, 0x00003fff, 0x00007fff, 
#     0x0000ffff, 0x0001ffff, 0x0003ffff, 0x0007ffff, 
#     0x000fffff, 0x001fffff, 0x003fffff, 0x007fffff, 
#     0x00ffffff, 0x01ffffff, 0x03ffffff, 0x07ffffff, 
#     0x0fffffff, 0x1fffffff, 0x3fffffff, 0x7fffffff, 
#     0xffffffff 
# }; 
#  
# static unsigned long _XGetPixel (ximage, x, y) 
#     register XImage *ximage; 
#     int x; 
#     int y; 
#  
# { 
#         unsigned long pixel, px; 
#         register char *src; 
#         register char *dst; 
#         register int i, j; 
#         int bits, nbytes; 
#         long plane; 
#       
#         if (ximage->depth == 1) { 
#                 src = &ximage->data[XYINDEX(x, y, ximage)]; 
#                 dst = (char *)&pixel; 
#                 pixel = 0; 
#                 for (i = ximage->bitmap_unit >> 3; --i >= 0; ) *dst++ = *src++; 
#                 XYNORMALIZE(&pixel, ximage); 
#                 bits = (x + ximage->xoffset) % ximage->bitmap_unit; 
#                 pixel = ((((char *)&pixel)[bits>>3])>>(bits&7)) & 1; 
#         } else if (ximage->format == XYPixmap) { 
#                 pixel = 0; 
#                 plane = 0; 
#                 nbytes = ximage->bitmap_unit >> 3; 
#                 for (i = ximage->depth; --i >= 0; ) { 
#                     src = &ximage->data[XYINDEX(x, y, ximage)+ plane]; 
#                     dst = (char *)&px; 
#                     px = 0; 
#                     for (j = nbytes; --j >= 0; ) *dst++ = *src++; 
#                     XYNORMALIZE(&px, ximage); 
#                     bits = (x + ximage->xoffset) % ximage->bitmap_unit; 
#                     pixel = (pixel << 1) 
#                             (((((char *)&px)[bits>>3])>>(bits&7)) & 1); 
#                     plane = plane + (ximage->bytes_per_line * ximage->height); 
#                 } 
#         } else if (ximage->format == ZPixmap) { 
#                 src = &ximage->data[ZINDEX(x, y, ximage)]; 
#                 dst = (char *)&px; 
#                 px = 0; 
#                 for (i = (ximage->bits_per_pixel + 7) >> 3; --i >= 0; ) 
#                     *dst++ = *src++;             
#                 ZNORMALIZE(&px, ximage); 
#                 pixel = 0; 
#                 for (i=sizeof(unsigned long); --i >= 0; ) 
#                     pixel = (pixel << 8) | ((unsigned char *)&px)[i]; 
#                 if (ximage->bits_per_pixel == 4) { 
#                     if (x & 1) 
#                         pixel >>= 4; 
#                     else 
#                         pixel &= 0xf; 
#                 } 
#         } else { 
#                 return 0; /* bad image */ 
#         } 
#         if (ximage->bits_per_pixel == ximage->depth) 
#           return pixel; 
#         else 
#           return (pixel & low_bits_table[ximage->depth]); 
# } 
#
# /* 
#  * This module provides rudimentary manipulation routines for image data 
#  * structures.  The functions provided are: 
#  * 
#  *      XCreateImage    Creates a default XImage data structure 
#  *      _XDestroyImage  Deletes an XImage data structure 
#  *      _XGetPixel      Reads a pixel from an image data structure 
#  *      _XGetPixel32    Reads a pixel from a 32-bit Z image data structure 
#  *      _XGetPixel16    Reads a pixel from a 16-bit Z image data structure 
#  *      _XGetPixel8     Reads a pixel from an 8-bit Z image data structure 
#  *      _XGetPixel1     Reads a pixel from an 1-bit image data structure 
#  *      _XPutPixel      Writes a pixel into an image data structure 
#  *      _XPutPixel32    Writes a pixel into a 32-bit Z image data structure 
#  *      _XPutPixel16    Writes a pixel into a 16-bit Z image data structure 
#  *      _XPutPixel8     Writes a pixel into an 8-bit Z image data structure 
#  *      _XPutPixel1     Writes a pixel into an 1-bit image data structure 
#  *      _XSubImage      Clones a new (sub)image from an existing one 
#  *      _XSetImage      Writes an image data pattern into another image 
#  *      _XAddPixel      Adds a constant value to every pixel in an image 
#  * 
#  * The logic contained in these routines makes several assumptions about 
#  * the image data structures, and at least for current implementations 
#  * these assumptions are believed to be true.  They are:  
#  * 
#  *      For all formats, bits_per_pixel is less than or equal to 32. 
#  *      For XY formats, bitmap_unit is always less than or equal to bitmap_pad. 
#  *      For XY formats, bitmap_unit is 8, 16, or 32 bits. 
#  *      For Z format, bits_per_pixel is 1, 4, 8, 16, 24, or 32 bits. 
#  */ 
# static _xynormalizeimagebits (bp, img) 
#     register unsigned char *bp; 
#     register XImage *img; 
# { 
#         register unsigned char c; 
#  
#         if (img->byte_order != img->bitmap_bit_order) { 
#             switch (img->bitmap_unit) { 
#  
#                 case 16: 
#                     c = *bp; 
#                     *bp = *(bp + 1); 
#                     *(bp + 1) = c; 
#                     break; 
#  
#                 case 32: 
#                     c = *(bp + 3); 
#                     *(bp + 3) = *bp; 
#                     *bp = c; 
#                     c = *(bp + 2); 
#                     *(bp + 2) = *(bp + 1); 
#                     *(bp + 1) = c; 
#                     break; 
#             } 
#         } 
#         if (img->bitmap_bit_order == MSBFirst) 
#             _XReverse_Bytes (bp, img->bitmap_unit >> 3); 
# } 
#
# static _znormalizeimagebits (bp, img) 
#     register unsigned char *bp; 
#     register XImage *img; 
# { 
#         register unsigned char c; 
#         switch (img->bits_per_pixel) { 
#  
#             case 4: 
#                 *bp = ((*bp >> 4) & 0xF) | ((*bp << 4) & ~0xF); 
#                 break; 
#  
#             case 16: 
#                 c = *bp; 
#                 *bp = *(bp + 1); 
#                 *(bp + 1) = c; 
#                 break; 
#  
#             case 24: 
#                 c = *(bp + 2); 
#                 *(bp + 2) = *bp; 
#                 *bp = c; 
#                 break; 
#  
#             case 32: 
#                 c = *(bp + 3); 
#                 *(bp + 3) = *bp; 
#                 *bp = c; 
#                 c = *(bp + 2); 
#                 *(bp + 2) = *(bp + 1); 
#                 *(bp + 1) = c; 
#                 break; 
#         } 
# } 

# /* 
#  * GetPixel 
#  *  
#  * Returns the specified pixel.  The X and Y coordinates are relative to  
#  * the origin (upper left [0,0]) of the image.  The pixel value is returned 
#  * in normalized format, i.e. the LSB of the long is the LSB of the pixel. 
#  * The algorithm used is: 
#  * 
#  *      copy the source bitmap_unit or Zpixel into temp 
#  *      normalize temp if needed 
#  *      extract the pixel bits into return value 
#  * 
#  */ 
#
# static unsigned long Const low_bits_table[] = { 
#     0x00000000, 0x00000001, 0x00000003, 0x00000007, 
#     0x0000000f, 0x0000001f, 0x0000003f, 0x0000007f, 
#     0x000000ff, 0x000001ff, 0x000003ff, 0x000007ff, 
#     0x00000fff, 0x00001fff, 0x00003fff, 0x00007fff, 
#     0x0000ffff, 0x0001ffff, 0x0003ffff, 0x0007ffff, 
#     0x000fffff, 0x001fffff, 0x003fffff, 0x007fffff, 
#     0x00ffffff, 0x01ffffff, 0x03ffffff, 0x07ffffff, 
#     0x0fffffff, 0x1fffffff, 0x3fffffff, 0x7fffffff, 
#     0xffffffff 
# }; 
#  
# static unsigned long _XGetPixel (ximage, x, y) 
#     register XImage *ximage; 
#     int x; 
#     int y; 
#  
# { 
#         unsigned long pixel, px; 
#         register char *src; 
#         register char *dst; 
#         register int i, j; 
#         int bits, nbytes; 
#         long plane; 
#       
#         if (ximage->depth == 1) { 
#                 src = &ximage->data[XYINDEX(x, y, ximage)]; 
#                 dst = (char *)&pixel; 
#                 pixel = 0; 
#                 for (i = ximage->bitmap_unit >> 3; --i >= 0; ) *dst++ = *src++; 
#                 XYNORMALIZE(&pixel, ximage); 
#                 bits = (x + ximage->xoffset) % ximage->bitmap_unit; 
#                 pixel = ((((char *)&pixel)[bits>>3])>>(bits&7)) & 1; 
#         } else if (ximage->format == XYPixmap) { 
#                 pixel = 0; 
#                 plane = 0; 
#                 nbytes = ximage->bitmap_unit >> 3; 
#                 for (i = ximage->depth; --i >= 0; ) { 
#                     src = &ximage->data[XYINDEX(x, y, ximage)+ plane]; 
#                     dst = (char *)&px; 
#                     px = 0; 
#                     for (j = nbytes; --j >= 0; ) *dst++ = *src++; 
#                     XYNORMALIZE(&px, ximage); 
#                     bits = (x + ximage->xoffset) % ximage->bitmap_unit; 
#                     pixel = (pixel << 1) 
#                             (((((char *)&px)[bits>>3])>>(bits&7)) & 1); 
#                     plane = plane + (ximage->bytes_per_line * ximage->height); 
#                 } 
#         } else if (ximage->format == ZPixmap) { 
#                 src = &ximage->data[ZINDEX(x, y, ximage)]; 
#                 dst = (char *)&px; 
#                 px = 0; 
#                 for (i = (ximage->bits_per_pixel + 7) >> 3; --i >= 0; ) 
#                     *dst++ = *src++;             
#                 ZNORMALIZE(&px, ximage); 
#                 pixel = 0; 
#                 for (i=sizeof(unsigned long); --i >= 0; ) 
#                     pixel = (pixel << 8) | ((unsigned char *)&px)[i]; 
#                 if (ximage->bits_per_pixel == 4) { 
#                     if (x & 1) 
#                         pixel >>= 4; 
#                     else 
#                         pixel &= 0xf; 
#                 } 
#         } else { 
#                 return 0; /* bad image */ 
#         } 
#         if (ximage->bits_per_pixel == ximage->depth) 
#           return pixel; 
#         else 
#           return (pixel & low_bits_table[ximage->depth]); 
# } 
#  

# /* 
#  * PutPixel 
#  *  
#  * Overwrites the specified pixel.  The X and Y coordinates are relative to  
#  * the origin (upper left [0,0]) of the image.  The input pixel value must be 
#  * in normalized format, i.e. the LSB of the long is the LSB of the pixel. 
#  * The algorithm used is: 
#  * 
#  *      copy the destination bitmap_unit or Zpixel to temp 
#  *      normalize temp if needed 
#  *      copy the pixel bits into the temp 
#  *      renormalize temp if needed 
#  *      copy the temp back into the destination image data 
#  * 
#  */ 
#  
#  
# static int _XPutPixel (ximage, x, y, pixel) 
#     register XImage *ximage; 
#     int x; 
#     int y; 
#     unsigned long pixel; 
#  
# { 
#         unsigned long px, npixel; 
#         register char *src; 
#         register char *dst; 
#         register int i; 
#         int j, nbytes; 
#         long plane; 
#  
#         if (ximage->depth == 4) 
#             pixel &= 0xf; 
#         npixel = pixel; 
#         for (i=0, px=pixel; i<sizeof(unsigned long); i++, px>>=8) 
#             ((unsigned char *)&pixel)[i] = px; 
#         if (ximage->depth == 1) { 
#                 src = &ximage->data[XYINDEX(x, y, ximage)]; 
#                 dst = (char *)&px; 
#                 px = 0; 
#                 nbytes = ximage->bitmap_unit >> 3; 
#                 for (i = nbytes; --i >= 0; ) *dst++ = *src++; 
#                 XYNORMALIZE(&px, ximage); 
#                 i = ((x + ximage->xoffset) % ximage->bitmap_unit); 
#                 _putbits ((char *)&pixel, i, 1, (char *)&px); 
#                 XYNORMALIZE(&px, ximage); 
#                 src = (char *) &px; 
#                 dst = &ximage->data[XYINDEX(x, y, ximage)]; 
#                 for (i = nbytes; --i >= 0; ) *dst++ = *src++; 
#         } else if (ximage->format == XYPixmap) { 
#                 plane = (ximage->bytes_per_line * ximage->height) * 
#                     (ximage->depth - 1); /* do least signif plane 1st */ 
#                 nbytes = ximage->bitmap_unit >> 3; 
#                 for (j = ximage->depth; --j >= 0; ) { 
#                     src = &ximage->data[XYINDEX(x, y, ximage) + plane]; 
#                     dst = (char *) &px; 
#                     px = 0; 
#                     for (i = nbytes; --i >= 0; ) *dst++ = *src++; 
#                     XYNORMALIZE(&px, ximage); 
#                     i = ((x + ximage->xoffset) % ximage->bitmap_unit); 
#                     _putbits ((char *)&pixel, i, 1, (char *)&px); 
#                     XYNORMALIZE(&px, ximage); 
#                     src = (char *)&px; 
#                     dst = &ximage->data[XYINDEX(x, y, ximage) + plane]; 
#                     for (i = nbytes; --i >= 0; ) *dst++ = *src++; 
#                     npixel = npixel >> 1; 
#                     for (i=0, px=npixel; i<sizeof(unsigned long); i++, px>>=8) 
#                         ((unsigned char *)&pixel)[i] = px; 
#                     plane = plane - (ximage->bytes_per_line * ximage->height); 
#                 } 
#         } else if (ximage->format == ZPixmap) { 
#                 src = &ximage->data[ZINDEX(x, y, ximage)]; 
#                 dst = (char *)&px; 
#                 px = 0; 
#                 nbytes = (ximage->bits_per_pixel + 7) >> 3; 
#                 for (i = nbytes; --i >= 0; ) *dst++ = *src++; 
#                 ZNORMALIZE(&px, ximage); 
#                 _putbits ((char *)&pixel,  
#                           (x * ximage->bits_per_pixel) & 7,  
#                           ximage->bits_per_pixel, (char *)&px); 
#                 ZNORMALIZE(&px, ximage); 
#                 src = (char *)&px; 
#                 dst = &ximage->data[ZINDEX(x, y, ximage)]; 
#                 for (i = nbytes; --i >= 0; ) *dst++ = *src++; 
#         } else { 
#                 return 0; /* bad image */ 
#         } 
#         return 1; 
# } 



Comments and suggestions to: bugs@mythryl.org

PreviousUpNext