Image::Base::X11::Protocol::Drawable extends Image::Base to draw into X windows or pixmaps by sending drawing requests to an X server with X11::Protocol. There's no file load or save, just drawing operations.

The subclasses Image::Base::X11::Protocol::Pixmap and Image::Base::X11::Protocol::Window have things specific to a pixmap or window. Drawable is the common parts.

Native X drawing does much more than Image::Base but if you have some generic pixel twiddling code for Image::Base then this module lets you point it at an X window, pixmap, etc. Drawing directly into a window is a good way to show slow drawing progressively, rather than drawing a pixmap or image file and only displaying when complete. Or see Image::Base::Multiplex for a way to do both simultaneously.

Fetching a pixel is an X server round-trip so reading a big region will be slow. The protocol allows a big region or an entire drawable to be read in one go, so some function for that could be made if needed.

In the current code the colour returned is either the name used to draw it, or 4-digit hex #RRRRGGGGBBBB queried from the -colormap, or otherwise a raw pixel value. If two colour names are the same pixel value because that was as close as could be represented then fetching might give either name. The hex return is 4 digit components because that's the range in the X protocol.

If the drawable is a window then parts overlapped by another window (including a sub-window) generally read back as an random colour. Parts of a window which are off-screen have no data at all and the return is currently an empty string "". Would undef or the window background pixel be better? (An off-screen GetImage is actually a Match error reply, but that's turned into a plain return here since that will be much more helpful than the $X connection error handler.)

Allocate colours in the -colormap. Colour names are the same as for the drawing functions. For example,

$image->add_colours ('red', 'green', '#FF00FF');

Drawing automatically adds a colour if it doesn't already exist but using add_colours can do a set of pixel lookups in a single server round-trip instead of separate individual ones.

If using the default colormap of the screen then names "black" and "white" are taken from the screen info and don't query the server (neither in the drawing operations nor add_colours).

All colours, both named and hex, are sent to the server for interpretation. On a static visual like TrueColor a hex RGB might be turned into a pixel just on the client side, but the X spec allows non-linear colour ramps so only the server can do it properly.

The screen number of the -drawable, for example 0 for the first screen.

The depth and screen of a drawable cannot be changed, and for the purposes of this interface the width and height are regarded as fixed too. (Is that a good idea?)

get() of -width, -height, -depth or -screen for a root window uses values from the X11::Protocol object info without querying the server. For other drawables a GetGeometry request is made. If you already know the values of some of these attributes then include them in the new() to record ready for later get() and avoid that GetGeometry query. Of course if nothing ever does such a get() then there's no need. The plain drawing operations don't need the size.

ellipse() unfilled uses the X PolyArc line centred on the boundary pixels, being the midpoints of the $y1 row, $y2 row, $x1 column, etc. The way the pixel "centre within the shape" rule works should mean that circles are symmetric, but the X protocol spec allows the server some implementation-dependent latitude for ellipses width!=height.

ellipse() filled uses the X FillArc, but that means the area inside an ellipse centred on the boundary pixels, which is effectively 1/2 pixel in from the ellipse line edge. The pixel "centre on the boundary drawn if above or left" rule also means the bottom row and rightmost column aren't drawn at all. The current strategy is to draw a PolyArc on top for the extra 1/2 pixel radius.

For a filled circle an alternative strategy would be to set the line width to half the radius and draw from half way in from the edges. That means the line width is from the centre of the box to the outer edges. The way a line has linewidth/2 each side makes a resolution of 1/2 pixel possible. The disadvantage would be changing the GC each time, which might be undesirable if it came from the user (secret as-yet undocumented -gc attribute). Note also this is no good for an ellipse width!=height because if you draw a fixed distance tangent to an ellipse then it's not a bigger ellipse, but a shape fatter than an ellipse.

The FillArc plus PolyArc combination ends up drawing some pixels twice, which is no good for an "XOR" gc operation. Currently that doesn't affect Image::Base::X11::Protocol::Drawable, but if there was a user supplied -gc then more care might be wanted. At worst the base Image::Base code could be left to handle it all, or draw onto a temporary bitmap to make a mask of desired pixels, or something like that.

diamond() uses PolyLine and FillPoly in similar ways to the ellipse above. The FillPoly has the same 1/2 pixel inside as the FillArc and so a filled diamond is a PolyLine on top of a FillPoly.

The pixel values for each colour used for drawing are cached for later re-use. This is highly desirable to avoid a server round-trip on every drawing operation, but if you use a lot of different shades then the cache may become big. Perhaps some sort of least recently used discard could keep a lid on it. Perhaps the colour-to-pixel hash or some such attribute could be exposed so it could be both initialized, manipulated, or set to some tied LRU hash etc as desired.

Image-Base-X11-Protocol is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3, or (at your option) any later version.

Image-Base-X11-Protocol is distributed in the hope that it will be useful, but WITHOUT 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 along with Image-Base-X11-Protocol. If not, see <http://www.gnu.org/licenses/>.