render: Don't add overflows when header is contiguous with body.
[pspp] / src / output / render.c
index 05db149ab07a7a14b7a9b56f50ff8de0692d3ec5..0ef78f1af653b27bac865404a255df2aadaaa6eb 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 2009 Free Software Foundation, Inc.
+   Copyright (C) 2009, 2010, 2011, 2013 Free Software Foundation, Inc.
 
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
@@ -38,7 +38,7 @@
 
    May represent the layout of an entire table presented to
    render_page_create(), or a rectangular subregion of a table broken out using
-   render_page_next() to allow a table to be broken across multiple pages. */
+   render_break_next() to allow a table to be broken across multiple pages. */
 struct render_page
   {
     const struct render_params *params; /* Parameters of the target device. */
@@ -359,9 +359,14 @@ distribute_spanned_width (int width,
      w1 by the common denominator of all three calculations (d), dividing that
      out in the column width calculation, and then keeping the remainder for
      the next iteration.
+
+     (We actually compute the unspanned width of a column as twice the
+     unspanned width, plus the width of the rule on the left, plus the width of
+     the rule on the right.  That way each rule contributes to both the cell on
+     its left and on its right.)
   */
   d0 = n;
-  d1 = total_unspanned * 2.0;
+  d1 = 2.0 * (total_unspanned > 0 ? total_unspanned : 1.0);
   d = d0 * d1;
   if (total_unspanned > 0)
     d *= 2.0;
@@ -379,7 +384,7 @@ distribute_spanned_width (int width,
           w += width * unspanned * d0;
         }
 
-      rows[x].width = w / d;
+      rows[x].width = MAX (rows[x].width, w / d);
       w -= rows[x].width * d;
     }
 }
@@ -450,7 +455,7 @@ measure_rule (const struct render_params *params, const struct table *table,
   enum table_axis b = !a;
   unsigned int rules;
   int d[TABLE_N_AXES];
-  int width, i;
+  int width;
 
   /* Determine all types of rules that are present, as a bitmap in 'rules'
      where rule type 't' is present if bit 2**t is set. */
@@ -461,10 +466,11 @@ measure_rule (const struct render_params *params, const struct table *table,
 
   /* Calculate maximum width of the rules that are present. */
   width = 0;
-  for (i = 0; i < N_LINES; i++)
-    if (rules & (1u << i))
-      width = MAX (width, params->line_widths[a][rule_to_render_type (i)]);
-
+  if (rules & (1u << TAL_1)
+      || (z > 0 && z < table->n[a] && rules & (1u << TAL_GAP)))
+    width = params->line_widths[a][RENDER_LINE_SINGLE];
+  if (rules & (1u << TAL_2))
+    width = MAX (width, params->line_widths[a][RENDER_LINE_DOUBLE]);
   return width;
 }
 
@@ -778,6 +784,7 @@ render_page_unref (struct render_page *page)
 {
   if (page != NULL && --page->ref_cnt == 0)
     {
+      int i;
       struct render_overflow *overflow, *next;
 
       HMAP_FOR_EACH_SAFE (overflow, next, struct render_overflow, node,
@@ -786,8 +793,13 @@ render_page_unref (struct render_page *page)
       hmap_destroy (&page->overflows);
 
       table_unref (page->table);
-      free (page->cp[H]);
-      free (page->cp[V]);
+      
+      for (i = 0; i < TABLE_N_AXES; ++i)
+       {
+         free (page->join_crossing[i]);
+         free (page->cp[i]);
+       }
+
       free (page);
     }
 }
@@ -803,7 +815,7 @@ render_page_get_size (const struct render_page *page, enum table_axis axis)
 \f
 /* Drawing render_pages. */
 
-static enum render_line_style
+static inline enum render_line_style
 get_rule (const struct render_page *page, enum table_axis axis,
           const int d[TABLE_N_AXES])
 {
@@ -902,15 +914,15 @@ render_cell (const struct render_page *page, const struct table_cell *cell)
   page->params->draw_cell (page->params->aux, cell, bb, clip);
 }
 
-/* Renders PAGE, by calling the 'draw_line' and 'draw_cell' functions from the
-   render_params provided to render_page_create(). */
-void
-render_page_draw (const struct render_page *page)
+/* Draws the cells of PAGE indicated in BB. */
+static void
+render_page_draw_cells (const struct render_page *page,
+                        int bb[TABLE_N_AXES][2])
 {
   int x, y;
 
-  for (y = 0; y <= page->n[V] * 2; y++)
-    for (x = 0; x <= page->n[H] * 2; )
+  for (y = bb[V][0]; y < bb[V][1]; y++)
+    for (x = bb[H][0]; x < bb[H][1]; )
       if (is_rule (x) || is_rule (y))
         {
           int d[TABLE_N_AXES];
@@ -924,12 +936,91 @@ render_page_draw (const struct render_page *page)
           struct table_cell cell;
 
           table_get_cell (page->table, x / 2, y / 2, &cell);
-          if (y / 2 == cell.d[V][0])
+          if (y == bb[V][0] || y / 2 == cell.d[V][0])
             render_cell (page, &cell);
           x = rule_ofs (cell.d[H][1]);
           table_cell_free (&cell);
         }
 }
+
+/* Renders PAGE, by calling the 'draw_line' and 'draw_cell' functions from the
+   render_params provided to render_page_create(). */
+void
+render_page_draw (const struct render_page *page)
+{
+  int bb[TABLE_N_AXES][2];
+
+  bb[H][0] = 0;
+  bb[H][1] = page->n[H] * 2 + 1;
+  bb[V][0] = 0;
+  bb[V][1] = page->n[V] * 2 + 1;
+
+  render_page_draw_cells (page, bb);
+}
+
+/* Returns the greatest value i, 0 <= i < n, such that cp[i] <= x0. */
+static int
+get_clip_min_extent (int x0, const int cp[], int n)
+{
+  int low, high, best;
+
+  low = 0;
+  high = n;
+  best = 0;
+  while (low < high)
+    {
+      int middle = low + (high - low) / 2;
+
+      if (cp[middle] <= x0)
+        {
+          best = middle;
+          low = middle + 1;
+        }
+      else
+        high = middle;
+    }
+
+  return best;
+}
+
+/* Returns the least value i, 0 <= i < n, such that cp[i + 1] >= x1. */
+static int
+get_clip_max_extent (int x1, const int cp[], int n)
+{
+  int low, high, best;
+
+  low = 0;
+  high = n;
+  best = n;
+  while (low < high)
+    {
+      int middle = low + (high - low) / 2;
+
+      if (cp[middle] >= x1)
+        best = high = middle;
+      else
+        low = middle + 1;
+    }
+
+  return best;
+}
+
+/* Renders the cells of PAGE that intersect (X,Y)-(X+W,Y+H), by calling the
+   'draw_line' and 'draw_cell' functions from the render_params provided to
+   render_page_create(). */
+void
+render_page_draw_region (const struct render_page *page,
+                         int x, int y, int w, int h)
+{
+  int bb[TABLE_N_AXES][2];
+
+  bb[H][0] = get_clip_min_extent (x, page->cp[H], page->n[H] * 2 + 1);
+  bb[H][1] = get_clip_max_extent (x + w, page->cp[H], page->n[H] * 2 + 1);
+  bb[V][0] = get_clip_min_extent (y, page->cp[V], page->n[V] * 2 + 1);
+  bb[V][1] = get_clip_max_extent (y + h, page->cp[V], page->n[V] * 2 + 1);
+
+  render_page_draw_cells (page, bb);
+}
 \f
 /* Breaking up tables to fit on a page. */
 
@@ -955,12 +1046,27 @@ render_break_init (struct render_break *b, struct render_page *page,
   b->hw = headers_width (page, axis);
 }
 
+/* Initializes B as a render_break structure for which
+   render_break_has_next() always returns false. */
+void
+render_break_init_empty (struct render_break *b)
+{
+  b->page = NULL;
+  b->axis = TABLE_HORZ;
+  b->cell = 0;
+  b->pixel = 0;
+  b->hw = 0;
+}
+
 /* Frees B and unrefs the render_page that it owns. */
 void
 render_break_destroy (struct render_break *b)
 {
   if (b != NULL)
-    render_page_unref (b->page);
+    {
+      render_page_unref (b->page);
+      b->page = NULL;
+    }
 }
 
 /* Returns true if B still has cells that are yet to be returned,
@@ -971,7 +1077,7 @@ render_break_has_next (const struct render_break *b)
   const struct render_page *page = b->page;
   enum table_axis axis = b->axis;
 
-  return b->cell < page->n[axis] - page->h[axis][1];
+  return page != NULL && b->cell < page->n[axis] - page->h[axis][1];
 }
 
 /* Returns the minimum SIZE argument that, if passed to render_break_next(),
@@ -1112,7 +1218,7 @@ render_page_select (const struct render_page *page, enum table_axis axis,
   if (z0 == page->h[a][0] && p0 == 0
       && z1 == page->n[a] - page->h[a][1] && p1 == 0)
     {
-      struct render_page *page_rw = (struct render_page *) page;
+      struct render_page *page_rw = CONST_CAST (struct render_page *, page);
       page_rw->ref_cnt++;
       return page_rw;
     }
@@ -1182,50 +1288,52 @@ render_page_select (const struct render_page *page, enum table_axis axis,
   s.p1 = p1;
   s.subpage = subpage;
 
-  for (z = 0; z < page->n[b]; z++)
-    {
-      struct table_cell cell;
-      int d[TABLE_N_AXES];
+  if (!page->h[a][0] || z0 > page->h[a][0] || p0)
+    for (z = 0; z < page->n[b]; z++)
+      {
+        struct table_cell cell;
+        int d[TABLE_N_AXES];
 
-      d[a] = z0;
-      d[b] = z;
-      table_get_cell (page->table, d[H], d[V], &cell);
-      if ((z == cell.d[b][0] && (p0 || cell.d[a][0] < z0))
-          || (z == cell.d[b][1] - 1 && p1))
-        {
-          ro = insert_overflow (&s, &cell);
-          ro->overflow[a][0] += p0 + axis_width (page, a,
-                                                 cell_ofs (cell.d[a][0]),
-                                                 cell_ofs (z0));
-          if (z1 == z0 + 1)
-            ro->overflow[a][1] += p1;
-          if (page->h[a][0] && page->h[a][1])
-            ro->overflow[a][0] -= page->join_crossing[a][cell.d[a][0] + 1];
-          if (cell.d[a][1] > z1)
+        d[a] = z0;
+        d[b] = z;
+        table_get_cell (page->table, d[H], d[V], &cell);
+        if ((z == cell.d[b][0] && (p0 || cell.d[a][0] < z0))
+            || (z == cell.d[b][1] - 1 && p1))
+          {
+            ro = insert_overflow (&s, &cell);
+            ro->overflow[a][0] += p0 + axis_width (page, a,
+                                                   cell_ofs (cell.d[a][0]),
+                                                   cell_ofs (z0));
+            if (z1 == z0 + 1)
+              ro->overflow[a][1] += p1;
+            if (page->h[a][0] && page->h[a][1])
+              ro->overflow[a][0] -= page->join_crossing[a][cell.d[a][0] + 1];
+            if (cell.d[a][1] > z1)
+              ro->overflow[a][1] += axis_width (page, a, cell_ofs (z1),
+                                                cell_ofs (cell.d[a][1]));
+          }
+        table_cell_free (&cell);
+      }
+
+  if (!page->h[a][1] || z1 < page->n[a] - page->h[a][1] || p1)
+    for (z = 0; z < page->n[b]; z++)
+      {
+        struct table_cell cell;
+        int d[TABLE_N_AXES];
+
+        /* XXX need to handle p1 below */
+        d[a] = z1 - 1;
+        d[b] = z;
+        table_get_cell (page->table, d[H], d[V], &cell);
+        if (z == cell.d[b][0] && cell.d[a][1] > z1
+            && find_overflow_for_cell (&s, &cell) == NULL)
+          {
+            ro = insert_overflow (&s, &cell);
             ro->overflow[a][1] += axis_width (page, a, cell_ofs (z1),
                                               cell_ofs (cell.d[a][1]));
-        }
-      table_cell_free (&cell);
-    }
-
-  for (z = 0; z < page->n[b]; z++)
-    {
-      struct table_cell cell;
-      int d[TABLE_N_AXES];
-
-      /* XXX need to handle p1 below */
-      d[a] = z1 - 1;
-      d[b] = z;
-      table_get_cell (page->table, d[H], d[V], &cell);
-      if (z == cell.d[b][0] && cell.d[a][1] > z1
-          && find_overflow_for_cell (&s, &cell) == NULL)
-        {
-          ro = insert_overflow (&s, &cell);
-          ro->overflow[a][1] += axis_width (page, a, cell_ofs (z1),
-                                            cell_ofs (cell.d[a][1]));
-        }
-      table_cell_free (&cell);
-    }
+          }
+        table_cell_free (&cell);
+      }
 
   /* Copy overflows from PAGE into subpage. */
   HMAP_FOR_EACH (ro, struct render_overflow, node, &page->overflows)
@@ -1299,4 +1407,3 @@ insert_overflow (struct render_page_selection *s,
 
   return of;
 }
-