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 #ifndef NDEBUG
47     int            num_comp_lim = ct->num_comps;
48 #endif
49     int            max_x_offset = ct->max_x_offset;
50     int            max_y_offset = ct->max_y_offset;
51     int            span         = ct->span;
52     int            lines_in_buf = ct->lines_in_buf;
53     unsigned char *linebuf      = ct->linebuf;
54     int            y            = ct->y;
55     /* Some offsets we will use repeatedly */
56     int            oc           = x + comp * width;
57     /* p != 0 if we need to be processed because a previous component shadows us.
58      * If we're the first component then no one can shadow us. */
59     int            p            = (first_comp ? 0 : *process);
60     int            sx, sy, ex, ey, lo, v;
61     unsigned char *pc;
62     unsigned char *ppc;
63 
64     assert((first_comp != 1) ^ (prev_comp == -1));
65     assert((last_comp != 1) ^ (comp == ct->comp_order[num_comp_lim]));
66 
67     /* Work out the search region bounds */
68     sy = y - max_y_offset;
69     if (clips_on_y && sy < 0)
70         sy = 0;
71     ey = y + max_y_offset;
72     if (clips_on_y && ey >= height)
73         ey = height-1;
74     sx = x - max_x_offset;
75     if (clips_on_x && sx < 0)
76         sx = 0;
77     ex = x + max_x_offset;
78     if (clips_on_x && ex >= width)
79         ex = width-1;
80 
81     /* We only need to check for shadowing lower components if we're
82      * not the last last component (!last_comp). We can only need to process
83      * here if we are not the first component (!first_comp) and
84      * if (p != 0) then we need to search for the maximum local value
85      * of  this component. */
86     v = linebuf[line_offset + oc];
87     if (!last_comp || (!first_comp && p))
88     {
89         int min_v, max_v;
90 
91         lo = sy % lines_in_buf;
92         if (!first_comp)
93             max_v = v;
94         if (!last_comp)
95             min_v = v;
96         pc = &linebuf[lo * span + comp * width + sx];
97         ex -= sx;
98         for (sy = ey-sy; sy >= 0; sy--)
99         {
100             ppc = pc;
101             for (sx = ex; sx >= 0; sx--)
102             {
103                 int cv = *ppc++;
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             /* Update the next components process flag if required */
126             *process = np;
127 #ifdef SAVE_PROCESS_BUFFER
128             buffer[x] = np;
129             return;
130 #endif
131         }
132 
133         if (!first_comp && p > v && trap_here(v, max_v, comp))
134         {
135             if (max_v < p)
136                 p = max_v;
137             v = p;
138         }
139     }
140     buffer[x] = v;
141 }
142 
ClapTrap_GetLinePlanar(ClapTrap * gs_restrict ct,unsigned char ** gs_restrict buffer)143 int ClapTrap_GetLinePlanar(ClapTrap       * gs_restrict ct,
144                            unsigned char ** gs_restrict buffer)
145 {
146     int max_y;
147     int l_margin;
148     int r_margin;
149     int comp_idx;
150     int prev_comp;
151     int comp;
152     int x;
153     int line_offset;
154     unsigned char *process;
155     int num_comp_lim = ct->num_comps;
156 
157     /* Read in as many lines as we need */
158     max_y = ct->y + ct->max_y_offset;
159     if (max_y > ct->height-1)
160         max_y = ct->height-1;
161     while (ct->lines_read <= max_y)
162     {
163         int bufpos = ct->span * (ct->lines_read % ct->lines_in_buf);
164         int code = ct->get_line(ct->get_line_arg, &ct->linebuf[bufpos]);
165         if (code < 0)
166             return code;
167         ct->lines_read++;
168     }
169 
170     /* Now we have enough information to calculate the process map for the next line of data */
171     l_margin = ct->max_x_offset;
172     r_margin = ct->width - ct->max_x_offset;
173     if (r_margin < 0)
174     {
175         r_margin = 0;
176         l_margin = 0;
177     }
178     x = (ct->y % ct->lines_in_buf);
179     process = &ct->process[x * ct->width];
180     line_offset = x * ct->span;
181     if (ct->y < ct->max_y_offset || ct->y >= ct->height - ct->max_y_offset)
182     {
183         unsigned char *p = process;
184         /* Some of our search area is off the end of the bitmap. We must be careful. */
185         comp = ct->comp_order[0];
186         for (x = 0; x < l_margin; x++)
187         {
188             process_at_pixel(ct, buffer[comp], x, 1, 1, 1, 0, -1, comp, line_offset, p++);
189         }
190         for (; x < r_margin; x++)
191         {
192             process_at_pixel(ct, buffer[comp], x, 0, 1, 1, 0, -1, comp, line_offset, p++);
193         }
194         for (; x < ct->width; x++)
195         {
196             process_at_pixel(ct, buffer[comp], x, 1, 1, 1, 0, -1, comp, line_offset, p++);
197         }
198         for (comp_idx = 1; comp_idx < num_comp_lim; comp_idx++)
199         {
200             prev_comp = comp;
201             p = process;
202             comp = ct->comp_order[comp_idx];
203             for (x = 0; x < l_margin; x++)
204             {
205                 process_at_pixel(ct, buffer[comp], x, 1, 1, 0, 0, prev_comp, comp, line_offset, p++);
206             }
207             for (; x < r_margin; x++)
208             {
209                 process_at_pixel(ct, buffer[comp], x, 0, 1, 0, 0, prev_comp, comp, line_offset, p++);
210             }
211             for (; x < ct->width; x++)
212             {
213                 process_at_pixel(ct, buffer[comp], x, 1, 1, 0, 0, prev_comp, comp, line_offset, p++);
214             }
215         }
216         prev_comp = comp;
217         p = process;
218         comp = ct->comp_order[comp_idx];
219         for (x = 0; x < l_margin; x++)
220         {
221             process_at_pixel(ct, buffer[comp], x, 1, 1, 0, 1, prev_comp, comp, line_offset, p++);
222         }
223         for (; x < r_margin; x++)
224         {
225             process_at_pixel(ct, buffer[comp], x, 0, 1, 0, 1, prev_comp, comp, line_offset, p++);
226         }
227         for (; x < ct->width; x++)
228         {
229             process_at_pixel(ct, buffer[comp], x, 1, 1, 0, 1, prev_comp, comp, line_offset, p++);
230         }
231     }
232     else
233     {
234         /* Our search area never clips on y at least. */
235         unsigned char *p = process;
236         comp = ct->comp_order[0];
237         for (x = 0; x < l_margin; x++)
238         {
239             process_at_pixel(ct, buffer[comp], x, 1, 0, 1, 0, -1, comp, line_offset, p++);
240         }
241         for (; x < r_margin; x++)
242         {
243             process_at_pixel(ct, buffer[comp], x, 0, 0, 1, 0, -1, comp, line_offset, p++);
244         }
245         for (; x < ct->width; x++)
246         {
247             process_at_pixel(ct, buffer[comp], x, 1, 0, 1, 0, -1, comp, line_offset, p++);
248         }
249         for (comp_idx = 1; comp_idx < num_comp_lim; comp_idx++)
250         {
251             prev_comp = comp;
252             p = process;
253             comp = ct->comp_order[comp_idx];
254             for (x = 0; x < l_margin; x++)
255             {
256                 process_at_pixel(ct, buffer[comp], x, 1, 0, 0, 0, prev_comp, comp, line_offset, p++);
257             }
258             for (; x < r_margin; x++)
259             {
260                 process_at_pixel(ct, buffer[comp], x, 0, 0, 0, 0, prev_comp, comp, line_offset, p++);
261             }
262             for (; x < ct->width; x++)
263             {
264                 process_at_pixel(ct, buffer[comp], x, 1, 0, 0, 0, prev_comp, comp, line_offset, p++);
265             }
266         }
267         prev_comp = comp;
268         p = process;
269         comp = ct->comp_order[comp_idx];
270         for (x = 0; x < l_margin; x++)
271         {
272             process_at_pixel(ct, buffer[comp], x, 1, 0, 0, 1, prev_comp, comp, line_offset, p++);
273         }
274         for (; x < r_margin; x++)
275         {
276             process_at_pixel(ct, buffer[comp], x, 0, 0, 0, 1, prev_comp, comp, line_offset, p++);
277         }
278         for (; x < ct->width; x++)
279         {
280             process_at_pixel(ct, buffer[comp], x, 1, 0, 0, 1, prev_comp, comp, line_offset, p++);
281         }
282     }
283     ct->y++;
284     if (ct->y == ct->height)
285     {
286         ct->y = 0;
287         ct->lines_read = 0;
288     }
289 
290     return 0;
291 }
292