1 /* Copyright (C) 2015-2018 Artifex Software, Inc.
2    All Rights Reserved.
3 
4    This software is provided AS-IS with no warranty, either express or
5    implied.
6 
7    This software is distributed under license and may not be copied,
8    modified or distributed except as expressly authorized under the terms
9    of the license contained in the file LICENSE in this distribution.
10 
11    Refer to licensing information at http://www.artifex.com or contact
12    Artifex Software, Inc.,  1305 Grant Avenue - Suite 200, Novato,
13    CA 94945, U.S.A., +1(415)492-9861, for further information.
14 */
15 
16 #include <stdlib.h>
17 #include <string.h>
18 #include <assert.h>
19 
20 #include "claptrap.h"
21 #include "claptrap-impl.h"
22 
23 /* This is the actual guts of the per-pixel processing.
24  * We use a static inline, so the compiler can optimise
25  * out as many of the tests as possible. */
process_at_pixel(ClapTrap * gs_restrict ct,unsigned char * gs_restrict buffer,int x,int clips_on_x,int clips_on_y,int first_comp,int last_comp,int prev_comp,int comp,int line_offset,unsigned char * process)26 inline static void process_at_pixel(ClapTrap      * gs_restrict ct,
27                                     unsigned char * gs_restrict buffer,
28                                     int            x,
29                                     int            clips_on_x,
30                                     int            clips_on_y,
31                                     int            first_comp,
32                                     int            last_comp,
33                                     int            prev_comp,
34                                     int            comp,
35                                     int            line_offset,
36                                     unsigned char *process)
37 {
38     /* We look at the pixel values on comp.
39      * We look at the process values passed into us from prev_comp, and pass out
40      * into comp.
41      */
42 
43     /* Use local vars to avoid pointer aliasing */
44     int            width        = ct->width;
45     int            height       = ct->height;
46     int            num_comps    = ct->num_comps;
47     int            max_x_offset = ct->max_x_offset;
48     int            max_y_offset = ct->max_y_offset;
49     int            span         = ct->span;
50     int            lines_in_buf = ct->lines_in_buf;
51     unsigned char *linebuf      = ct->linebuf;
52     int            y            = ct->y;
53     /* Some offsets we will use repeatedly */
54     int            o            = x * num_comps;
55     int            oc           = o + comp;
56     /* p != 0 if we need to be processed because a previous component shadows us.
57      * If we're the first component then no one can shadow us. */
58     int            p            = (first_comp ? 0 : *process);
59     int            sx, sy, ex, ey, lo, v;
60     unsigned char *pc;
61     unsigned char *ppc;
62 
63     assert((first_comp != 1) ^ (prev_comp == -1));
64     assert((last_comp != 1) ^ (comp == ct->comp_order[num_comps-1]));
65 
66     /* Work out the search region bounds */
67     sy = y - max_y_offset;
68     if (clips_on_y && sy < 0)
69         sy = 0;
70     ey = y + max_y_offset;
71     if (clips_on_y && ey >= height)
72         ey = height-1;
73     sx = x - max_x_offset;
74     if (clips_on_x && sx < 0)
75         sx = 0;
76     ex = x + max_x_offset;
77     if (clips_on_x && ex >= width)
78         ex = width-1;
79 
80     /* We only need to check for shadowing lower components if we're
81      * not the last last component (!last_comp). We can only need to process
82      * here if we are not the first component (!first_comp) and
83      * if (p != 0) then we need to search for the maximum local value
84      * of  this component. */
85     v = linebuf[line_offset + oc];
86     if (!last_comp || (!first_comp && p))
87     {
88         int min_v, max_v;
89 
90         lo = sy % lines_in_buf;
91         if (!first_comp)
92             max_v = v;
93         if (!last_comp)
94             min_v = v;
95         pc = &linebuf[lo * span + sx * num_comps + comp];
96         ex -= sx;
97         for (sy = ey-sy; sy >= 0; sy--)
98         {
99             ppc = pc;
100             for (sx = ex; sx >= 0; sx--)
101             {
102                 int cv = *ppc;
103                 ppc += num_comps;
104                 if (!first_comp && cv > max_v)
105                     max_v = cv;
106                 else if (!last_comp && cv < min_v)
107                     min_v = cv;
108             }
109             pc += span;
110             lo++;
111             if (lo == lines_in_buf)
112             {
113                 pc -= span * lines_in_buf;
114             }
115         }
116         /* If we're not the last component, and we meet the criteria
117          * the next component needs processing. */
118         if (!last_comp)
119         {
120             /* Process flag for next component inherits from this one */
121             int np = p;
122             if (v > np && shadow_here(v, min_v, comp))
123                 np = v;
124 
125             *process = np;
126 #ifdef SAVE_PROCESS_BUFFER
127             buffer[oc] = np;
128             return;
129 #endif
130         }
131 
132         if (!first_comp && p > v && trap_here(v, max_v, comp))
133         {
134             if (max_v < p)
135                 p = max_v;
136             v = p;
137         }
138     }
139     buffer[oc] = v;
140 }
141 
ClapTrap_GetLine(ClapTrap * gs_restrict ct,unsigned char * gs_restrict buffer)142 int ClapTrap_GetLine(ClapTrap      * gs_restrict ct,
143                      unsigned char * gs_restrict buffer)
144 {
145     int max_y;
146     int l_margin;
147     int r_margin;
148     int comp_idx;
149     int prev_comp;
150     int comp;
151     int x;
152     int line_offset;
153     unsigned char *process;
154 
155     /* Read in as many lines as we need */
156     max_y = ct->y + ct->max_y_offset;
157     if (max_y > ct->height-1)
158         max_y = ct->height-1;
159     while (ct->lines_read <= max_y)
160     {
161         int bufpos = ct->span * (ct->lines_read % ct->lines_in_buf);
162         int code = ct->get_line(ct->get_line_arg, &ct->linebuf[bufpos]);
163         if (code < 0)
164             return code;
165         ct->lines_read++;
166     }
167 
168     /* Now we have enough information to calculate the process map for the next line of data */
169     l_margin = ct->max_x_offset;
170     r_margin = ct->width - ct->max_x_offset;
171     if (r_margin < 0)
172     {
173         r_margin = 0;
174         l_margin = 0;
175     }
176     x = (ct->y % ct->lines_in_buf);
177     process = &ct->process[x * ct->width];
178     line_offset = x * ct->span;
179     if (ct->y < ct->max_y_offset || ct->y >= ct->height - ct->max_y_offset)
180     {
181         unsigned char *p = process;
182         /* Some of our search area is off the end of the bitmap. We must be careful. */
183         comp = ct->comp_order[0];
184         for (x = 0; x < l_margin; x++)
185         {
186             process_at_pixel(ct, buffer, x, 1, 1, 1, 0, -1, comp, line_offset, p++);
187         }
188         for (; x < r_margin; x++)
189         {
190             process_at_pixel(ct, buffer, x, 0, 1, 1, 0, -1, comp, line_offset, p++);
191         }
192         for (; x < ct->width; x++)
193         {
194             process_at_pixel(ct, buffer, x, 1, 1, 1, 0, -1, comp, line_offset, p++);
195         }
196         for (comp_idx = 1; comp_idx < ct->num_comps-1; comp_idx++)
197         {
198             p = process;
199             prev_comp = comp;
200             comp = ct->comp_order[comp_idx];
201             for (x = 0; x < l_margin; x++)
202             {
203                 process_at_pixel(ct, buffer, x, 1, 1, 0, 0, prev_comp, comp, line_offset, p++);
204             }
205             for (; x < r_margin; x++)
206             {
207                 process_at_pixel(ct, buffer, x, 0, 1, 0, 0, prev_comp, comp, line_offset, p++);
208             }
209             for (; x < ct->width; x++)
210             {
211                 process_at_pixel(ct, buffer, x, 1, 1, 0, 0, prev_comp, comp, line_offset, p++);
212             }
213         }
214         p = process;
215         prev_comp = comp;
216         comp = ct->comp_order[comp_idx];
217         for (x = 0; x < l_margin; x++)
218         {
219             process_at_pixel(ct, buffer, x, 1, 1, 0, 1, prev_comp, comp, line_offset, p++);
220         }
221         for (; x < r_margin; x++)
222         {
223             process_at_pixel(ct, buffer, x, 0, 1, 0, 1, prev_comp, comp, line_offset, p++);
224         }
225         for (; x < ct->width; x++)
226         {
227             process_at_pixel(ct, buffer, x, 1, 1, 0, 1, prev_comp, comp, line_offset, p++);
228         }
229     }
230     else
231     {
232         unsigned char *p = process;
233         /* Our search area never clips on y at least. */
234         comp = ct->comp_order[0];
235         for (x = 0; x < l_margin; x++)
236         {
237             process_at_pixel(ct, buffer, x, 1, 0, 1, 0, -1, comp, line_offset, p++);
238         }
239         for (; x < r_margin; x++)
240         {
241             process_at_pixel(ct, buffer, x, 0, 0, 1, 0, -1, comp, line_offset, p++);
242         }
243         for (; x < ct->width; x++)
244         {
245             process_at_pixel(ct, buffer, x, 1, 0, 1, 0, -1, comp, line_offset, p++);
246         }
247         for (comp_idx = 1; comp_idx < ct->num_comps-1; comp_idx++)
248         {
249             p = process;
250             prev_comp = comp;
251             comp = ct->comp_order[comp_idx];
252             for (x = 0; x < l_margin; x++)
253             {
254                 process_at_pixel(ct, buffer, x, 1, 0, 0, 0, prev_comp, comp, line_offset, p++);
255             }
256             for (; x < r_margin; x++)
257             {
258                 process_at_pixel(ct, buffer, x, 0, 0, 0, 0, prev_comp, comp, line_offset, p++);
259             }
260             for (; x < ct->width; x++)
261             {
262                 process_at_pixel(ct, buffer, x, 1, 0, 0, 0, prev_comp, comp, line_offset, p++);
263             }
264         }
265         p = process;
266         prev_comp = comp;
267         comp = ct->comp_order[comp_idx];
268         for (x = 0; x < l_margin; x++)
269         {
270             process_at_pixel(ct, buffer, x, 1, 0, 0, 1, prev_comp, comp, line_offset, p++);
271         }
272         for (; x < r_margin; x++)
273         {
274             process_at_pixel(ct, buffer, x, 0, 0, 0, 1, prev_comp, comp, line_offset, p++);
275         }
276         for (; x < ct->width; x++)
277         {
278             process_at_pixel(ct, buffer, x, 1, 0, 0, 1, prev_comp, comp, line_offset, p++);
279         }
280     }
281     ct->y++;
282     if (ct->y == ct->height)
283     {
284         ct->y = 0;
285         ct->lines_read = 0;
286     }
287 
288     return 0;
289 }
290