1 /*
2 * AlphaLigne.cpp
3 * nlivarot
4 *
5 * Created by fred on Fri Jul 25 2003.
6 * public domain
7 *
8 */
10 #include "AlphaLigne.h"
12 #include <math.h>
13 #include <stdio.h>
14 #include <stdlib.h>
15 #include <glib/gmem.h>
17 AlphaLigne::AlphaLigne(int iMin,int iMax)
18 {
19 min=iMin;
20 max=iMax;
21 if ( max < min+1 ) max=min+1;
22 steps=NULL;
23 nbStep=maxStep=0;
24 before.x=min-1;
25 before.delta=0;
26 after.x=max+1;
27 after.delta=0;
28 }
29 AlphaLigne::~AlphaLigne(void)
30 {
31 g_free(steps);
32 steps=NULL;
33 nbStep=maxStep=0;
34 }
35 void AlphaLigne::Affiche(void)
36 {
37 printf("%i steps\n",nbStep);
38 for (int i=0;i<nbStep;i++) {
39 printf("(%i %f) ",steps[i].x,steps[i].delta); // localization ok
40 }
41 printf("\n");
42 }
45 void AlphaLigne::Reset(void)
46 {
47 // reset to empty line
48 // doesn't deallocate the steps array, to minimize memory operations
49 curMin=max;
50 curMax=min;
51 nbStep=0;
52 before.x=min-1;
53 before.delta=0;
54 after.x=max+1;
55 after.delta=0;
56 }
57 int AlphaLigne::AddBord(float spos,float sval,float epos,float eval,float tPente)
58 {
59 // printf("%f %f -> %f %f / %f\n",spos,sval,epos,eval,tPente);
60 if ( sval == eval ) return 0;
61 // compute the footprint of [spos,epos] on the line of pixels
62 float curStF=floor(spos);
63 float curEnF=floor(epos);
64 int curSt=(int)curStF;
65 int curEn=(int)curEnF;
67 // update curMin and curMax
68 if ( curSt > max ) {
69 // we're on the right of the visible portion of the line: bail out!
70 if ( eval < sval ) curMax=max;
71 return 0;
72 }
73 if ( curSt < curMin ) curMin=curSt;
74 if ( ceil(epos) > curMax ) curMax=(int)ceil(epos);
76 // clamp the changed portion to [min,max], no need for bigger
77 if ( curMax > max ) curMax=max;
78 if ( curMin < min ) curMin=min;
80 // total amount of change in pixel coverage from before the right to after the run
81 float needed=eval-sval;
82 float needC=/*(int)ldexpf(*/needed/*,24)*/;
84 if ( curEn < min ) {
85 // the added portion is entirely on the left, so we only have to change the initial coverage for the line
86 before.delta+=needC;
87 return 0;
88 }
90 // add the steps
91 // the pixels from [curSt..curEn] (included) intersect with [spos;epos]
92 // since we're dealing with delta in the coverage, there is also a curEn+1 delta, since the curEn pixel intersect
93 // with [spos;epos] and thus has some delta with respect to its next pixel
94 // lots of different cases... ugly
95 if ( curSt == curEn ) {
96 if ( curSt+1 < min ) {
97 before.delta+=needC;
98 } else {
99 if ( nbStep+2 >= maxStep ) {
100 maxStep=2*nbStep+2;
101 steps=(alpha_step*)g_realloc(steps,maxStep*sizeof(alpha_step));
102 }
103 float stC=/*(int)ldexpf(*/(eval-sval)*(0.5*(epos-spos)+curStF+1-epos)/*,24)*/;
104 steps[nbStep].x=curSt;
105 steps[nbStep].delta=stC;
106 nbStep++;
107 steps[nbStep].x=curSt+1;
108 steps[nbStep].delta=needC-stC; // au final, on a toujours le bon delta, meme avec une arete completement verticale
109 nbStep++;
110 }
111 } else if ( curEn == curSt+1 ) {
112 if ( curSt+2 < min ) {
113 before.delta+=needC;
114 } else {
115 if ( nbStep+3 >= maxStep ) {
116 maxStep=2*nbStep+3;
117 steps=(alpha_step*)g_realloc(steps,maxStep*sizeof(alpha_step));
118 }
119 float stC=/*(int)ldexpf(*/0.5*tPente*(curEnF-spos)*(curEnF-spos)/*,24)*/;
120 float enC=/*(int)ldexpf(*/tPente-0.5*tPente*((spos-curStF)*(spos-curStF)+(curEnF+1.0-epos)*(curEnF+1.0-epos))/*,24)*/;
121 steps[nbStep].x=curSt;
122 steps[nbStep].delta=stC;
123 nbStep++;
124 steps[nbStep].x=curEn;
125 steps[nbStep].delta=enC;
126 nbStep++;
127 steps[nbStep].x=curEn+1;
128 steps[nbStep].delta=needC-stC-enC;
129 nbStep++;
130 }
131 } else {
132 float stC=/*(int)ldexpf(*/0.5*tPente*(curStF+1-spos)*(curStF+1-spos)/*,24)*/;
133 float stFC=/*(int)ldexpf(*/tPente-0.5*tPente*(spos-curStF)*(spos-curStF)/*,24)*/;
134 float enC=/*(int)ldexpf(*/tPente-0.5*tPente*(curEnF+1.0-epos)*(curEnF+1.0-epos)/*,24)*/;
135 float miC=/*(int)ldexpf(*/tPente/*,24)*/;
136 if ( curSt < min ) {
137 if ( curEn > max ) {
138 if ( nbStep+(max-min) >= maxStep ) {
139 maxStep=2*nbStep+(max-min);
140 steps=(alpha_step*)g_realloc(steps,maxStep*sizeof(alpha_step));
141 }
142 float bfd=min-curSt-1;
143 bfd*=miC;
144 before.delta+=stC+bfd;
145 for (int i=min;i<max;i++) {
146 steps[nbStep].x=i;
147 steps[nbStep].delta=miC;
148 nbStep++;
149 }
150 } else {
151 if ( nbStep+(curEn-min)+2 >= maxStep ) {
152 maxStep=2*nbStep+(curEn-min)+2;
153 steps=(alpha_step*)g_realloc(steps,maxStep*sizeof(alpha_step));
154 }
155 float bfd=min-curSt-1;
156 bfd*=miC;
157 before.delta+=stC+bfd;
158 for (int i=min;i<curEn;i++) {
159 steps[nbStep].x=i;
160 steps[nbStep].delta=miC;
161 nbStep++;
162 }
163 steps[nbStep].x=curEn;
164 steps[nbStep].delta=enC;
165 nbStep++;
166 steps[nbStep].x=curEn+1;
167 steps[nbStep].delta=needC-stC-stFC-enC-(curEn-curSt-2)*miC;
168 nbStep++;
169 }
170 } else {
171 if ( curEn > max ) {
172 if ( nbStep+3+(max-curSt) >= maxStep ) {
173 maxStep=2*nbStep+3+(curEn-curSt);
174 steps=(alpha_step*)g_realloc(steps,maxStep*sizeof(alpha_step));
175 }
176 steps[nbStep].x=curSt;
177 steps[nbStep].delta=stC;
178 nbStep++;
179 steps[nbStep].x=curSt+1;
180 steps[nbStep].delta=stFC;
181 nbStep++;
182 for (int i=curSt+2;i<max;i++) {
183 steps[nbStep].x=i;
184 steps[nbStep].delta=miC;
185 nbStep++;
186 }
187 } else {
188 if ( nbStep+3+(curEn-curSt) >= maxStep ) {
189 maxStep=2*nbStep+3+(curEn-curSt);
190 steps=(alpha_step*)g_realloc(steps,maxStep*sizeof(alpha_step));
191 }
192 steps[nbStep].x=curSt;
193 steps[nbStep].delta=stC;
194 nbStep++;
195 steps[nbStep].x=curSt+1;
196 steps[nbStep].delta=stFC;
197 nbStep++;
198 for (int i=curSt+2;i<curEn;i++) {
199 steps[nbStep].x=i;
200 steps[nbStep].delta=miC;
201 nbStep++;
202 }
203 steps[nbStep].x=curEn;
204 steps[nbStep].delta=enC;
205 nbStep++;
206 steps[nbStep].x=curEn+1;
207 steps[nbStep].delta=needC-stC-stFC-enC-(curEn-curSt-2)*miC;
208 nbStep++;
209 }
210 }
211 }
213 return 0;
214 }
215 int AlphaLigne::AddBord(float spos,float sval,float epos,float eval)
216 {
217 // pas de pente dans ce cas; on ajoute le delta au premier pixel
218 float tPente=(eval-sval);
220 float curStF=floor(spos);
221 float curEnF=floor(epos);
222 int curSt=(int)curStF;
223 int curEn=(int)curEnF;
225 if ( curSt > max ) {
226 if ( eval < sval ) curMax=max;
227 return 0; // en dehors des limites (attention a ne pas faire ca avec curEn)
228 }
229 if ( curEn < min ) {
230 before.delta+=eval-sval;
231 return 0; // en dehors des limites (attention a ne pas faire ca avec curEn)
232 }
234 if ( curSt < curMin ) curMin=curSt;
235 // int curEn=(int)curEnF;
236 if ( ceil(epos) > curMax-1 ) curMax=1+(int)ceil(epos);
237 if ( curSt < min ) {
238 before.delta+=eval-sval;
239 } else {
240 AddRun(curSt,/*(int)ldexpf(*/(((float)(curSt+1))-spos)*tPente/*,24)*/);
241 AddRun(curSt+1,/*(int)ldexpf(*/(spos-((float)(curSt)))*tPente/*,24)*/);
242 }
243 return 0;
244 }
246 void AlphaLigne::Flatten(void)
247 {
248 // just sort
249 if ( nbStep > 0 ) qsort(steps,nbStep,sizeof(alpha_step),CmpStep);
250 }
251 void AlphaLigne::AddRun(int st,float pente)
252 {
253 if ( nbStep >= maxStep ) {
254 maxStep=2*nbStep+1;
255 steps=(alpha_step*)g_realloc(steps,maxStep*sizeof(alpha_step));
256 }
257 int nStep=nbStep++;
258 steps[nStep].x=st;
259 steps[nStep].delta=pente;
260 }
262 void AlphaLigne::Raster(raster_info &dest,void* color,RasterInRunFunc worker)
263 {
264 // start by checking if there are actually pixels in need of rasterization
265 if ( curMax <= curMin ) return;
266 if ( dest.endPix <= curMin || dest.startPix >= curMax ) return;
268 int nMin=curMin,nMax=curMax;
269 float alpSum=before.delta; // alpSum will be the pixel coverage value, so we start at before.delta
270 int curStep=0;
272 // first add all the deltas up to the first pixel in need of rasterization
273 while ( curStep < nbStep && steps[curStep].x < nMin ) {
274 alpSum+=steps[curStep].delta;
275 curStep++;
276 }
277 // just in case, if the line bounds are greater than the buffer bounds.
278 if ( nMin < dest.startPix ) {
279 for (;( curStep < nbStep && steps[curStep].x < dest.startPix) ;curStep++) alpSum+=steps[curStep].delta;
280 nMin=dest.startPix;
281 }
282 if ( nMax > dest.endPix ) nMax=dest.endPix;
284 // raster!
285 int curPos=dest.startPix;
286 for (;curStep<nbStep;curStep++) {
287 if ( alpSum > 0 && steps[curStep].x > curPos ) {
288 // we're going to change the pixel position curPos, and alpSum is > 0: rasterization needed from
289 // the last position (curPos) up to the pixel we're moving to (steps[curStep].x)
290 int nst=curPos,nen=steps[curStep].x;
291 //Buffer::RasterRun(dest,color,nst,alpSum,nen,alpSum);
292 (worker)(dest,color,nst,alpSum,nen,alpSum);
293 }
294 // add coverage deltas
295 alpSum+=steps[curStep].delta;
296 curPos=steps[curStep].x;
297 if ( curPos >= nMax ) break;
298 }
299 // if we ended the line with alpSum > 0, we need to raster from curPos to the right edge
300 if ( alpSum > 0 && curPos < nMax ) {
301 int nst=curPos,nen=max;
302 (worker)(dest,color,nst,alpSum,nen,alpSum);
303 //Buffer::RasterRun(dest,color,nst,alpSum,nen,alpSum);
304 }
305 }