5 // Written by Alexis Naveros 2015
7 // Based on The ‘‘dead reckoning’’ signed distance transform
8 // by George J. Grevera
10 struct MapDistMapPoint
17 #define IMGDISTMAP_SQRT2 (1.41421356237f)
18 #define IMGDISTMAP_SQRT5 (2.2360679775f)
19 #define IMGDISTMAP_SQRT10 (3.16227766017f)
20 #define IMGDISTMAP_SQRT13 (3.60555127546f)
24 #define ALWAYS_INLINE __attribute__((always_inline))
30 static inline ALWAYS_INLINE void imgDistMapCheck( MapDistMapPoint pt, float fx, float fy, MapDistMapPoint ptref, float delta )
33 if( ( ptref.distance + delta ) < pt.distance )
39 pt.distance = sqrtf( ( dx * dx ) + ( dy * dy ) ) + ptref.bias;
44 #define IMGDIST_OPFLAGS_XP1_SHIFT (0)
45 #define IMGDIST_OPFLAGS_XP2_SHIFT (1)
46 #define IMGDIST_OPFLAGS_XP3_SHIFT (2)
47 #define IMGDIST_OPFLAGS_XM1_SHIFT (3)
48 #define IMGDIST_OPFLAGS_XM2_SHIFT (4)
49 #define IMGDIST_OPFLAGS_XM3_SHIFT (5)
50 #define IMGDIST_OPFLAGS_YP1_SHIFT (6)
51 #define IMGDIST_OPFLAGS_YP2_SHIFT (7)
52 #define IMGDIST_OPFLAGS_YP3_SHIFT (8)
53 #define IMGDIST_OPFLAGS_YM1_SHIFT (9)
54 #define IMGDIST_OPFLAGS_YM2_SHIFT (10)
55 #define IMGDIST_OPFLAGS_YM3_SHIFT (11)
57 #define IMGDIST_OPFLAGS_XP1 (1<<IMGDIST_OPFLAGS_XP1_SHIFT)
58 #define IMGDIST_OPFLAGS_XP2 (1<<IMGDIST_OPFLAGS_XP2_SHIFT)
59 #define IMGDIST_OPFLAGS_XP3 (1<<IMGDIST_OPFLAGS_XP3_SHIFT)
60 #define IMGDIST_OPFLAGS_XM1 (1<<IMGDIST_OPFLAGS_XM1_SHIFT)
61 #define IMGDIST_OPFLAGS_XM2 (1<<IMGDIST_OPFLAGS_XM2_SHIFT)
62 #define IMGDIST_OPFLAGS_XM3 (1<<IMGDIST_OPFLAGS_XM3_SHIFT)
63 #define IMGDIST_OPFLAGS_YP1 (1<<IMGDIST_OPFLAGS_YP1_SHIFT)
64 #define IMGDIST_OPFLAGS_YP2 (1<<IMGDIST_OPFLAGS_YP2_SHIFT)
65 #define IMGDIST_OPFLAGS_YP3 (1<<IMGDIST_OPFLAGS_YP3_SHIFT)
66 #define IMGDIST_OPFLAGS_YM1 (1<<IMGDIST_OPFLAGS_YM1_SHIFT)
67 #define IMGDIST_OPFLAGS_YM2 (1<<IMGDIST_OPFLAGS_YM2_SHIFT)
68 #define IMGDIST_OPFLAGS_YM3 (1<<IMGDIST_OPFLAGS_YM3_SHIFT)
71 static inline ALWAYS_INLINE int imgDistMapForwardOpFlags( int x, int y, int sizex, int sizey )
75 opflags |= ( x >= 1 ) << IMGDIST_OPFLAGS_XM1_SHIFT;
76 opflags |= ( x >= 2 ) << IMGDIST_OPFLAGS_XM2_SHIFT;
77 opflags |= ( x >= 3 ) << IMGDIST_OPFLAGS_XM3_SHIFT;
78 opflags |= ( y >= 1 ) << IMGDIST_OPFLAGS_YM1_SHIFT;
79 opflags |= ( y >= 2 ) << IMGDIST_OPFLAGS_YM2_SHIFT;
80 opflags |= ( y >= 3 ) << IMGDIST_OPFLAGS_YM3_SHIFT;
81 opflags |= ( x < (sizex-1) ) << IMGDIST_OPFLAGS_XP1_SHIFT;
82 opflags |= ( x < (sizex-2) ) << IMGDIST_OPFLAGS_XP2_SHIFT;
83 opflags |= ( x < (sizex-3) ) << IMGDIST_OPFLAGS_XP3_SHIFT;
87 static void imgDistMapForwardClamp( MapDistMapPoint *p, float fx, float fy, int x, int y, int sizex, int opflags )
89 if( opflags & IMGDIST_OPFLAGS_XM1 )
90 imgDistMapCheck( p, fx, fy, p+( 0*sizex)+(-1), 1.0f );
91 if( opflags & IMGDIST_OPFLAGS_YM1 )
93 imgDistMapCheck( p, fx, fy, p+(-1*sizex)+( 0), 1.0f );
94 if( opflags & IMGDIST_OPFLAGS_XM1 )
95 imgDistMapCheck( p, fx, fy, p+(-1*sizex)+(-1), IMGDISTMAP_SQRT2 );
96 if( opflags & IMGDIST_OPFLAGS_XP1 )
97 imgDistMapCheck( p, fx, fy, p+(-1*sizex)+( 1), IMGDISTMAP_SQRT2 );
98 if( opflags & IMGDIST_OPFLAGS_XM2 )
99 imgDistMapCheck( p, fx, fy, p+(-1*sizex)+(-2), IMGDISTMAP_SQRT5 );
100 if( opflags & IMGDIST_OPFLAGS_XP2 )
101 imgDistMapCheck( p, fx, fy, p+(-1*sizex)+( 2), IMGDISTMAP_SQRT5 );
102 if( opflags & IMGDIST_OPFLAGS_XM3 )
103 imgDistMapCheck( p, fx, fy, p+(-1*sizex)+(-3), IMGDISTMAP_SQRT10 );
104 if( opflags & IMGDIST_OPFLAGS_XP3 )
105 imgDistMapCheck( p, fx, fy, p+(-1*sizex)+( 3), IMGDISTMAP_SQRT10 );
107 if( opflags & IMGDIST_OPFLAGS_YM2 )
109 if( opflags & IMGDIST_OPFLAGS_XM1 )
110 imgDistMapCheck( p, fx, fy, p+(-2*sizex)+(-1), IMGDISTMAP_SQRT5 );
111 if( opflags & IMGDIST_OPFLAGS_XP1 )
112 imgDistMapCheck( p, fx, fy, p+(-2*sizex)+( 1), IMGDISTMAP_SQRT5 );
113 if( opflags & IMGDIST_OPFLAGS_XM3 )
114 imgDistMapCheck( p, fx, fy, p+(-2*sizex)+(-3), IMGDISTMAP_SQRT13 );
115 if( opflags & IMGDIST_OPFLAGS_XP3 )
116 imgDistMapCheck( p, fx, fy, p+(-2*sizex)+( 3), IMGDISTMAP_SQRT13 );
118 if( opflags & IMGDIST_OPFLAGS_YM3 )
120 if( opflags & IMGDIST_OPFLAGS_XM1 )
121 imgDistMapCheck( p, fx, fy, p+(-3*sizex)+(-1), IMGDISTMAP_SQRT10 );
122 if( opflags & IMGDIST_OPFLAGS_XP1 )
123 imgDistMapCheck( p, fx, fy, p+(-3*sizex)+( 1), IMGDISTMAP_SQRT10 );
124 if( opflags & IMGDIST_OPFLAGS_XM2 )
125 imgDistMapCheck( p, fx, fy, p+(-3*sizex)+(-2), IMGDISTMAP_SQRT13 );
126 if( opflags & IMGDIST_OPFLAGS_XP2 )
127 imgDistMapCheck( p, fx, fy, p+(-3*sizex)+( 2), IMGDISTMAP_SQRT13 );
131 static inline ALWAYS_INLINE int imgDistMapBackwardOpFlags( int x, int y, int sizex, int sizey )
135 opflags |= ( x >= 1 ) << IMGDIST_OPFLAGS_XM1_SHIFT;
136 opflags |= ( x >= 2 ) << IMGDIST_OPFLAGS_XM2_SHIFT;
137 opflags |= ( x >= 3 ) << IMGDIST_OPFLAGS_XM3_SHIFT;
138 opflags |= ( x < (sizex-1) ) << IMGDIST_OPFLAGS_XP1_SHIFT;
139 opflags |= ( x < (sizex-2) ) << IMGDIST_OPFLAGS_XP2_SHIFT;
140 opflags |= ( x < (sizex-3) ) << IMGDIST_OPFLAGS_XP3_SHIFT;
141 opflags |= ( y < (sizey-1) ) << IMGDIST_OPFLAGS_YP1_SHIFT;
142 opflags |= ( y < (sizey-2) ) << IMGDIST_OPFLAGS_YP2_SHIFT;
143 opflags |= ( y < (sizey-3) ) << IMGDIST_OPFLAGS_YP3_SHIFT;
147 static void imgDistMapBackwardClamp( MapDistMapPoint *p, float fx, float fy, int x, int y, int sizex, int opflags )
149 if( opflags & IMGDIST_OPFLAGS_XP1 )
150 imgDistMapCheck( p, fx, fy, p+( 0*sizex)+( 1), 1.0f );
151 if( opflags & IMGDIST_OPFLAGS_YP1 )
153 imgDistMapCheck( p, fx, fy, p+( 1*sizex)+( 0), 1.0f );
154 if( opflags & IMGDIST_OPFLAGS_XM1 )
155 imgDistMapCheck( p, fx, fy, p+( 1*sizex)+(-1), IMGDISTMAP_SQRT2 );
156 if( opflags & IMGDIST_OPFLAGS_XP1 )
157 imgDistMapCheck( p, fx, fy, p+( 1*sizex)+( 1), IMGDISTMAP_SQRT2 );
158 if( opflags & IMGDIST_OPFLAGS_XM2 )
159 imgDistMapCheck( p, fx, fy, p+( 1*sizex)+(-2), IMGDISTMAP_SQRT5 );
160 if( opflags & IMGDIST_OPFLAGS_XP2 )
161 imgDistMapCheck( p, fx, fy, p+( 1*sizex)+( 2), IMGDISTMAP_SQRT5 );
162 if( opflags & IMGDIST_OPFLAGS_XM3 )
163 imgDistMapCheck( p, fx, fy, p+( 1*sizex)+(-3), IMGDISTMAP_SQRT10 );
164 if( opflags & IMGDIST_OPFLAGS_XP3 )
165 imgDistMapCheck( p, fx, fy, p+( 1*sizex)+( 3), IMGDISTMAP_SQRT10 );
167 if( opflags & IMGDIST_OPFLAGS_YP2 )
169 if( opflags & IMGDIST_OPFLAGS_XM1 )
170 imgDistMapCheck( p, fx, fy, p+( 2*sizex)+(-1), IMGDISTMAP_SQRT5 );
171 if( opflags & IMGDIST_OPFLAGS_XP1 )
172 imgDistMapCheck( p, fx, fy, p+( 2*sizex)+( 1), IMGDISTMAP_SQRT5 );
173 if( opflags & IMGDIST_OPFLAGS_XM3 )
174 imgDistMapCheck( p, fx, fy, p+( 2*sizex)+(-3), IMGDISTMAP_SQRT13 );
175 if( opflags & IMGDIST_OPFLAGS_XP3 )
176 imgDistMapCheck( p, fx, fy, p+( 2*sizex)+( 3), IMGDISTMAP_SQRT13 );
178 if( opflags & IMGDIST_OPFLAGS_YP3 )
180 if( opflags & IMGDIST_OPFLAGS_XM1 )
181 imgDistMapCheck( p, fx, fy, p+( 3*sizex)+(-1), IMGDISTMAP_SQRT10 );
182 if( opflags & IMGDIST_OPFLAGS_XP1 )
183 imgDistMapCheck( p, fx, fy, p+( 3*sizex)+( 1), IMGDISTMAP_SQRT10 );
184 if( opflags & IMGDIST_OPFLAGS_XM2 )
185 imgDistMapCheck( p, fx, fy, p+( 3*sizex)+(-2), IMGDISTMAP_SQRT13 );
186 if( opflags & IMGDIST_OPFLAGS_XP2 )
187 imgDistMapCheck( p, fx, fy, p+( 3*sizex)+( 2), IMGDISTMAP_SQRT13 );
192 // The outer edge must be all zeroes
193 void imgDistMapBuild( float *distancemap, byte *src, int sizex, int sizey, int srcbytesperpixel, int srcbytesperline )
195 int x, y, mapindex, srcindex;
197 float fx, fy, bias, maxdistance;
198 MapDistMapPoint *ptmap;
201 if( ( sizex < 4 ) || ( sizey < 4 ) )
204 ptmap = new MapDistMapPoint[sizex * sizey];
206 maxdistance = sizex + sizey;
208 for( y = 0 ; y < sizey ; y++ )
210 srcindex = y * srcbytesperline;
211 for( x = 0 ; x < sizex ; x++ )
213 pixel = src[ srcindex ];
218 bias = 1.0f - ( (float)pixel * (1.0f/255.0f) );
219 ptmap[mapindex].distance = bias;
220 ptmap[mapindex].x = fx;
221 ptmap[mapindex].y = fy;
222 ptmap[mapindex].bias = bias;
226 ptmap[mapindex].distance = maxdistance;
227 ptmap[mapindex].x = -1.0f;
228 ptmap[mapindex].y = -1.0f;
229 ptmap[mapindex].bias = 0.0f;
232 srcindex += srcbytesperpixel;
237 for( y = 0 ; y < 3 ; y++ )
239 mapindex = y * sizex;
240 for( x = 0 ; x < sizex ; x++ )
242 p = &ptmap[mapindex];
245 imgDistMapForwardClamp( p, fx, fy, x, y, sizex, imgDistMapForwardOpFlags( x, y, sizex, sizey ) );
249 for( ; y < sizey - 3 ; y++ )
251 mapindex = y * sizex;
252 for( x = 0 ; x < 3 ; x++ )
254 p = &ptmap[mapindex];
257 imgDistMapForwardClamp( p, fx, fy, x, y, sizex, imgDistMapForwardOpFlags( x, y, sizex, sizey ) );
260 for( x = 3 ; x < sizex - 3 ; x++ )
262 p = &ptmap[mapindex];
266 imgDistMapCheck( p, fx, fy, p+( 0*sizex)+(-1), 1.0f );
267 imgDistMapCheck( p, fx, fy, p+(-1*sizex)+( 0), 1.0f );
268 imgDistMapCheck( p, fx, fy, p+(-1*sizex)+(-1), IMGDISTMAP_SQRT2 );
269 imgDistMapCheck( p, fx, fy, p+(-1*sizex)+( 1), IMGDISTMAP_SQRT2 );
271 imgDistMapCheck( p, fx, fy, p+(-2*sizex)+(-1), IMGDISTMAP_SQRT5 );
272 imgDistMapCheck( p, fx, fy, p+(-2*sizex)+( 1), IMGDISTMAP_SQRT5 );
273 imgDistMapCheck( p, fx, fy, p+(-1*sizex)+(-2), IMGDISTMAP_SQRT5 );
274 imgDistMapCheck( p, fx, fy, p+(-1*sizex)+( 2), IMGDISTMAP_SQRT5 );
276 imgDistMapCheck( p, fx, fy, p+(-3*sizex)+(-1), IMGDISTMAP_SQRT10 );
277 imgDistMapCheck( p, fx, fy, p+(-3*sizex)+( 1), IMGDISTMAP_SQRT10 );
278 imgDistMapCheck( p, fx, fy, p+(-1*sizex)+(-3), IMGDISTMAP_SQRT10 );
279 imgDistMapCheck( p, fx, fy, p+(-1*sizex)+( 3), IMGDISTMAP_SQRT10 );
281 imgDistMapCheck( p, fx, fy, p+(-3*sizex)+(-2), IMGDISTMAP_SQRT13 );
282 imgDistMapCheck( p, fx, fy, p+(-3*sizex)+( 2), IMGDISTMAP_SQRT13 );
283 imgDistMapCheck( p, fx, fy, p+(-2*sizex)+(-3), IMGDISTMAP_SQRT13 );
284 imgDistMapCheck( p, fx, fy, p+(-2*sizex)+( 3), IMGDISTMAP_SQRT13 );
287 for( ; x < sizex ; x++ )
289 p = &ptmap[mapindex];
292 imgDistMapForwardClamp( p, fx, fy, x, y, sizex, imgDistMapForwardOpFlags( x, y, sizex, sizey ) );
296 for( ; y < sizey ; y++ )
298 mapindex = y * sizex;
299 for( x = 0 ; x < sizex ; x++ )
301 p = &ptmap[mapindex];
304 imgDistMapForwardClamp( p, fx, fy, x, y, sizex, imgDistMapForwardOpFlags( x, y, sizex, sizey ) );
310 for( y = sizey - 1 ; y >= sizey - 3 ; y-- )
312 mapindex = ( y * sizex ) + ( sizex - 1 );
313 for( x = sizex - 1 ; x >= 0 ; x-- )
315 p = &ptmap[mapindex];
318 imgDistMapBackwardClamp( p, fx, fy, x, y, sizex, imgDistMapBackwardOpFlags( x, y, sizex, sizey ) );
319 distancemap[ mapindex ] = p->distance;
323 for( ; y >= 3 ; y-- )
325 mapindex = ( y * sizex ) + ( sizex - 1 );
326 for( x = sizex - 1 ; x >= sizex - 3 ; x-- )
328 p = &ptmap[mapindex];
331 imgDistMapBackwardClamp( p, fx, fy, x, y, sizex, imgDistMapBackwardOpFlags( x, y, sizex, sizey ) );
332 distancemap[ mapindex ] = p->distance;
335 for( ; x >= 3 ; x-- )
337 p = &ptmap[mapindex];
341 imgDistMapCheck( p, fx, fy, p+( 0*sizex)+( 1), 1.0f );
342 imgDistMapCheck( p, fx, fy, p+( 1*sizex)+( 0), 1.0f );
343 imgDistMapCheck( p, fx, fy, p+( 1*sizex)+(-1), IMGDISTMAP_SQRT2 );
344 imgDistMapCheck( p, fx, fy, p+( 1*sizex)+( 1), IMGDISTMAP_SQRT2 );
346 imgDistMapCheck( p, fx, fy, p+( 2*sizex)+(-1), IMGDISTMAP_SQRT5 );
347 imgDistMapCheck( p, fx, fy, p+( 2*sizex)+( 1), IMGDISTMAP_SQRT5 );
348 imgDistMapCheck( p, fx, fy, p+( 1*sizex)+(-2), IMGDISTMAP_SQRT5 );
349 imgDistMapCheck( p, fx, fy, p+( 1*sizex)+( 2), IMGDISTMAP_SQRT5 );
351 imgDistMapCheck( p, fx, fy, p+( 3*sizex)+(-1), IMGDISTMAP_SQRT10 );
352 imgDistMapCheck( p, fx, fy, p+( 3*sizex)+( 1), IMGDISTMAP_SQRT10 );
353 imgDistMapCheck( p, fx, fy, p+( 1*sizex)+(-3), IMGDISTMAP_SQRT10 );
354 imgDistMapCheck( p, fx, fy, p+( 1*sizex)+( 3), IMGDISTMAP_SQRT10 );
356 imgDistMapCheck( p, fx, fy, p+( 3*sizex)+(-2), IMGDISTMAP_SQRT13 );
357 imgDistMapCheck( p, fx, fy, p+( 3*sizex)+( 2), IMGDISTMAP_SQRT13 );
358 imgDistMapCheck( p, fx, fy, p+( 2*sizex)+(-3), IMGDISTMAP_SQRT13 );
359 imgDistMapCheck( p, fx, fy, p+( 2*sizex)+( 3), IMGDISTMAP_SQRT13 );
361 distancemap[ mapindex ] = p->distance;
364 for( ; x >= 0 ; x-- )
366 p = &ptmap[mapindex];
369 imgDistMapBackwardClamp( p, fx, fy, x, y, sizex, imgDistMapBackwardOpFlags( x, y, sizex, sizey ) );
370 distancemap[ mapindex ] = p->distance;
374 for( ; y >= 0 ; y-- )
376 mapindex = ( y * sizex ) + ( sizex - 1 );
377 for( x = sizex - 1 ; x >= 0 ; x-- )
379 p = &ptmap[mapindex];
382 imgDistMapBackwardClamp( p, fx, fy, x, y, sizex, imgDistMapBackwardOpFlags( x, y, sizex, sizey ) );
383 distancemap[ mapindex ] = p->distance;