1 /*!
2   \file lib/cairodriver/raster.c
3 
4   \brief GRASS cairo display driver - draw raster
5 
6   (C) 2007-2014 by Lars Ahlzen and the GRASS Development Team
7 
8   This program is free software under the GNU General Public License
9   (>=v2). Read the file COPYING that comes with GRASS for details.
10 
11   \author Lars Ahlzen <lars ahlzen.com> (original contibutor)
12   \author Glynn Clements
13 */
14 
15 #include <math.h>
16 
17 #include "cairodriver.h"
18 #include <grass/glocale.h>
19 
20 #define MAX_IMAGE_SIZE 32767
21 #ifndef MIN
22 #define MIN(a,b) ((a)<(b)?(a):(b))
23 #endif
24 #ifndef MAX
25 #define MAX(a,b) ((a)>(b)?(a):(b))
26 #endif
27 
28 static int src_t, src_b, src_l, src_r, src_w, src_h;
29 static int dst_t, dst_b, dst_l, dst_r, dst_w, dst_h;
30 
31 static int *trans;
32 
33 static cairo_surface_t *src_surf;
34 static unsigned char *src_data;
35 static int src_stride, ca_row;
36 
37 static int masked;
38 
scale(double k,int src_0,int src_1,int dst_0,int dst_1)39 static double scale(double k, int src_0, int src_1, int dst_0, int dst_1)
40 {
41     return dst_0 + (double) (k - src_0) * (dst_1 - dst_0) / (src_1 - src_0);
42 }
43 
scale_fwd_y(int sy)44 static int scale_fwd_y(int sy)
45 {
46     return (int)floor(scale(sy, src_t, src_b, dst_t, dst_b) + 0.5);
47 }
48 
scale_rev_x(int dx)49 static int scale_rev_x(int dx)
50 {
51     return (int)floor(scale(dx + 0.5, dst_l, dst_r, src_l, src_r));
52 }
53 
next_row(int sy,int dy)54 static int next_row(int sy, int dy)
55 {
56     sy++;
57 
58     for (;;) {
59 	int y = scale_fwd_y(sy);
60 
61 	if (y > dy)
62 	    return sy - 1;
63 	sy++;
64     }
65 }
66 
67 /*!
68   \brief Start drawing raster
69 
70   \todo are top and left swapped?
71 
72   \param mask non-zero int for mask
73   \param s source (map) extent (left, right, top, bottom)
74   \param d destination (image) extent (left, right, top, bottom)
75 */
Cairo_begin_raster(int mask,int s[2][2],double d[2][2])76 void Cairo_begin_raster(int mask, int s[2][2], double d[2][2])
77 {
78     int i;
79     cairo_status_t status;
80 
81     masked = mask;
82 
83     src_l = s[0][0];
84     src_r = s[0][1];
85     src_t = s[1][0];
86     src_b = s[1][1];
87 
88     src_w = src_r - src_l;
89     src_h = src_b - src_t;
90 
91     dst_l = (int) floor(d[0][0] + 0.5);
92     dst_r = (int) floor(d[0][1] + 0.5);
93     dst_t = (int) floor(d[1][0] + 0.5);
94     dst_b = (int) floor(d[1][1] + 0.5);
95 
96     dst_w = dst_r - dst_l;
97     dst_h = dst_b - dst_t;
98 
99     G_debug(1, "Cairo_begin_raster(): masked=%d, src_lrtb=%d %d %d %d -> w/h=%d %d, "
100             "dst_lrtb=%d %d %d %d -> w/h=%d %d",
101             masked, src_l, src_r, src_t, src_b, src_w, src_h,
102             dst_l, dst_r, dst_t, dst_b, dst_w, dst_h);
103 
104     /* create source surface */
105     src_surf = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, ca.width, ca.height);
106     status = cairo_surface_status(src_surf);
107     if (status != CAIRO_STATUS_SUCCESS)
108         G_fatal_error("%s - %s - size: %dx%d (cairo limit: %dx%d)",
109                       _("Failed to create cairo surface"),
110                       cairo_status_to_string (status), ca.width, ca.height,
111                       MAX_IMAGE_SIZE, MAX_IMAGE_SIZE);
112 
113     src_data = cairo_image_surface_get_data(src_surf);
114     src_stride = cairo_image_surface_get_stride(src_surf);
115     ca_row = 0;
116 
117     /* allocate buffer for down-sampling data */
118     trans = G_malloc(dst_w * sizeof(int));
119     for (i = 0; i < dst_w; i++)
120         trans[i] = scale_rev_x(dst_l + i);
121 }
122 
123 /*!
124   \brief Draw raster row
125 
126   \param n number of cells
127   \param row raster row (starting at 0)
128   \param red,grn,blu,nul red,green,blue and null value
129 
130   \return next row
131 */
Cairo_raster(int n,int row,const unsigned char * red,const unsigned char * grn,const unsigned char * blu,const unsigned char * nul)132 int Cairo_raster(int n, int row,
133 		 const unsigned char *red, const unsigned char *grn,
134 		 const unsigned char *blu, const unsigned char *nul)
135 {
136     int d_y0 = scale_fwd_y(row + 0);
137     int d_y1 = scale_fwd_y(row + 1);
138     int d_rows = d_y1 - d_y0;
139     int x0 = MAX(0        - dst_l, 0);
140     int x1 = MIN(ca.width - dst_l, dst_w);
141     int y0 = MAX(0         - d_y0, 0);
142     int y1 = MIN(ca.height - d_y0, d_rows);
143     int x, y;
144 
145     if (y1 <= y0)
146         return next_row(row, d_y1);
147 
148     G_debug(3, "Cairo_raster(): n=%d row=%d", n, row);
149 
150     for (x = x0; x < x1; x++) {
151 	int xx = dst_l + x;
152         int j = trans[x];
153 	unsigned int c;
154 
155 	if (masked && nul && nul[j])
156 	    c = 0;
157 	else {
158 	    unsigned int r = red[j];
159 	    unsigned int g = grn[j];
160 	    unsigned int b = blu[j];
161 	    unsigned int a = 0xFF;
162 	    c = (a << 24) + (r << 16) + (g << 8) + (b << 0);
163 	}
164 
165 	for (y = y0; y < y1; y++) {
166 	    int yy = d_y0 + y;
167 	    *(unsigned int *)(src_data + yy * src_stride + xx * 4) = c;
168         }
169     }
170 
171     ca.modified = 1;
172     ca_row++;
173 
174     return next_row(row, d_y1);
175 }
176 
177 /*!
178   \brief Finish drawing raster
179 */
Cairo_end_raster(void)180 void Cairo_end_raster(void)
181 {
182     G_debug(1, "Cairo_end_raster()");
183 
184     /* paint source surface onto destination (scaled) */
185     cairo_save(cairo);
186     /* cairo_translate(cairo, dst_l, dst_t); */
187     /* cairo_scale(cairo, dst_w / src_w, dst_h / src_h); */
188     cairo_surface_mark_dirty(src_surf);
189     cairo_set_source_surface(cairo, src_surf, 0, 0);
190     cairo_pattern_set_filter(cairo_get_source(cairo), CAIRO_FILTER_NEAREST);
191     cairo_paint(cairo);
192     cairo_restore(cairo);
193 
194     /* cleanup */
195     G_free(trans);
196     cairo_surface_destroy(src_surf);
197     ca.modified = 1;
198 }
199