Aug 2004, S.Geerken@ping.de

=============
Image Buffers
=============

General
=======

Image buffers depend on the platform (see DwRender.txt), but have a
general, platform independant interface, which is described in this
section. The next section describes the Gdk version of Imgbuf.

The structure ImgBuf will become part of the image processing, between
image data decoding and the widget DwImage. Its purposes are

   1. storing the image data,
   2. handling scaled versions of this buffer, and
   3. drawing.

The latter must be done independently from the window.

Storing Image Data
------------------
Imgbuf supports five image types, which are listed in the table
below. The representation defines, how the colors are stored within
the data, which is passed to a_Imgbuf_copy_row().

                  | bytes per |
        type      |   pixel   |      representation
   ---------------+-----------+-------------------------
    RGB           |     3     | red, green, blue
    RGBA          |     4     | red, green, blue, alpha
    gray          |     1     | gray value
    indexed       |     1     | index to colormap
    indexed alpha |     1     | index to colormap

The last two types need a colormap, which is set by
a_Imgbuf_set_cmap(), which must be called before
a_Imgbuf_copy_row(). This function expects the colors as 32 bit
unsigned integers, which have the format 0xrrbbgg (for indexed
images), or 0xaarrggbb (for indexed alpha), respectively.

Scaling
-------
The buffer with the original size, which was created by
a_Imgbuf_new(), is called root buffer. Imgbuf provides the ability to
scale buffers. Generally, both root buffers, as well as scaled
buffers, may be shared, memory management is done by reference
counters.

Via a_Imgbuf_get_scaled_buf(), you can retrieve a scaled buffer. The
way, how this function works in detail, is described in the code, but
generally, something like this works always, in an efficient way:

   old_buf = cur_buf;
   cur_buf = a_Imgbuf_get_scaled_buf(old_buf, with, height);
   a_Imgbuf_unref (old_buf);

Old_buf may both be a root buffer, or a scaled buffer.

(As an exception, there should always be a reference on the root
buffer, since scaled buffers cannot exist without the root buffer, but
on the other side, do not hold references on it. So, if in the example
above, old_buf would be a root buffer, and there would, at the
beginning, only be one reference on it, new_buf would also be
destroyed, along with old_buf. Therefore, an external reference must
be added to the root buffer, which is in dillo done within the dicache
module.)

The root buffer keeps a list of all children, and all operations
operating on the image data (a_Imgbuf_copy_row() and
a_Imgbuf_set_cmap()) are delegated to the scaled buffers, when
processed, and inherited, when a new scaled buffer is created. This
means, that they must only be performed for the root buffer.

Drawing
-------
There are two situations, when drawing is necessary:

   1. To react on expose events, the function a_Imgbuf_draw() can be
      used. Notice that the exact signature of this function is
      platform dependant.

   2. When a row has been copied, it has to be drawn. To determine the
      area, which has to be drawn, the function
      a_Imgbuf_get_row_area() should be used. In dillo, the dicache
      module will first call a_Img_copy_row(), and then call
      a_Dw_image_draw_row() for the images connected to this image
      buffer. a_Dw_image_draw_row() will then call
      p_Dw_widget_queue_draw(), with an area determined by
      a_Imgbuf_get_row_area().


The Gdk Implementation
======================

The Gdk implementation is used by the Gtk+ platform. [... todo]


Global Scalers
==============

In some cases, there is a context, where images have to be scaled
often, by a relatively constant factor. For example, the preview
window (GtkDwPreview) draws images via the Imgbuf draw functions, but
uses scaled buffers. Scaling such a buffer each time it is needed,
causes huge performance losses. On the other hand, if the preview
window would keep these scaled buffers (e.g. by lazy mapping of the
original buffer to the scaled buffer), the scaled buffers get never
freed, since the view is not told about, when the original buffer is
not needed anymore. (n.b., that currently, the scaled buffers are
destroyed, when the original buffer is destroyed, but this may change,
and even this would leave "zombies" in this mapping structure, where
the values refer to dead pointers).

It is sufficient, that references on the scaled buffers are referred
somehow, so that they do not get destroyed between different
usages. The caller (in this case the preview) simply requests a scaled
buffer, but the Imgbuf returns this from the list of already scaled
buffers.

These references are hold by special structures, which are called
"scalers". There are two types of scalers, local scalers, which are
bound to image buffers, and global scalers, which refer to multiple
scalers.

What happens in different situations:

   - The caller (e.g. the preview) requests a scaled buffer. For this,
     it uses a special method, which also passes the global image
     scaler, which was created before (for the preview, there is a 1-1
     association). The Imgbuf uses this global image scaler, to
     identify the caller, and keeps a list of them. If this global
     scaler is not yet in the list, it is added, and a local scaler is
     created.



  -

There are three images in the page, i1a, i1b, and i2. I1a and i1b
refer to the same image recource, represented by the root image buffer
iba, which original size is 200 x 200. I1a is displayed in original
size, while i1b is displayed at 100 x 100. I2 refers to an other
recource, ibb, which has the size 300 x 300. I2 is shown in original
size.


                :DwRenderLayout ------------------- :DwPage ----------.
                     /    \                                           |
               ,----'      `----.               ,------ i1a:DwImage --+
              /                  \              |                     |
     view1:GtkDwViewport  view2:GtkDwPreview    | ,---- i1b:DwImage --|
                                  |             | |                   |
   ,------------------------------'             | | ,-- i2: DwImage --'
   |                                            | | |
   |      ,-------------------------------------' | |
   |      |      ,--------------------------------' |
   |      |      |                             ,----'
   |      |      |                             |
   |      V      |                             V
   | iba:Imgbuf  |                        ibb:Imgbuf -- 30x30
   |   |  |      V                             |          ^
   |   |  +- 100x100 ,- 20x20 ,- 10x10         |          |
   |   |  |          |    ^   |   ^            |          |
   |   |  `----------+----|---'   |            `--.    ,--'
   |   |   ,--------------'       |               |    |
   |   |   |   ,------------------'               |    |
   |   |   |   |                                  |    |
   | lca:ImgbufLSc                            lcb:ImgbufLSc
   | (factor 1/10)                            (factor 1/10)
   |        \                                      /
   |         `-----------.    ,-------------------'
   |                      \  /
   `------------------> scl:ImgbufGSc
                        (factor 1/10)
