agg_rasterizer_scanline_aa.cpp revision 33357cad1fd1321a2b38d2963e2585f27ce980a2
1 2//---------------------------------------------------------------------------- 3// XYQ: 2006-01-22 Copied from AGG project. 4// This file uses only integer data, so it's suitable for all platforms. 5//---------------------------------------------------------------------------- 6//---------------------------------------------------------------------------- 7// Anti-Grain Geometry - Version 2.3 8// Copyright (C) 2002-2005 Maxim Shemanarev (http://www.antigrain.com) 9// 10// Permission to copy, use, modify, sell and distribute this software 11// is granted provided this copyright notice appears in all copies. 12// This software is provided "as is" without express or implied 13// warranty, and with no claim as to its suitability for any purpose. 14// 15//---------------------------------------------------------------------------- 16// 17// The author gratefully acknowleges the support of David Turner, 18// Robert Wilhelm, and Werner Lemberg - the authors of the FreeType 19// libray - in producing this work. See http://www.freetype.org for details. 20// 21//---------------------------------------------------------------------------- 22// Contact: mcseem@antigrain.com 23// mcseemagg@yahoo.com 24// http://www.antigrain.com 25//---------------------------------------------------------------------------- 26// 27// Adaptation for 32-bit screen coordinates has been sponsored by 28// Liberty Technology Systems, Inc., visit http://lib-sys.com 29// 30// Liberty Technology Systems, Inc. is the provider of 31// PostScript and PDF technology for software developers. 32// 33//---------------------------------------------------------------------------- 34// 35// Class outline_aa - implementation. 36// 37// Initially the rendering algorithm was designed by David Turner and the 38// other authors of the FreeType library - see the above notice. I nearly 39// created a similar renderer, but still I was far from David's work. 40// I completely redesigned the original code and adapted it for Anti-Grain 41// ideas. Two functions - render_line and render_hline are the core of 42// the algorithm - they calculate the exact coverage of each pixel cell 43// of the polygon. I left these functions almost as is, because there's 44// no way to improve the perfection - hats off to David and his group! 45// 46// All other code is very different from the original. 47// 48//---------------------------------------------------------------------------- 49#include <limits.h> 50#include "agg_rasterizer_scanline_aa.h" 51#include "third_party/base/numerics/safe_math.h" 52namespace agg 53{ 54AGG_INLINE void cell_aa::set_cover(int c, int a) 55{ 56 cover = c; 57 area = a; 58} 59AGG_INLINE void cell_aa::add_cover(int c, int a) 60{ 61 cover += c; 62 area += a; 63} 64AGG_INLINE void cell_aa::set_coord(int cx, int cy) 65{ 66 x = cx; 67 y = cy; 68} 69AGG_INLINE void cell_aa::set(int cx, int cy, int c, int a) 70{ 71 x = cx; 72 y = cy; 73 cover = c; 74 area = a; 75} 76outline_aa::~outline_aa() 77{ 78 if(m_num_blocks) { 79 cell_aa** ptr = m_cells + m_num_blocks - 1; 80 while(m_num_blocks--) { 81 FX_Free(*ptr); 82 ptr--; 83 } 84 FX_Free(m_cells); 85 } 86} 87outline_aa::outline_aa() : 88 m_num_blocks(0), 89 m_max_blocks(0), 90 m_cur_block(0), 91 m_num_cells(0), 92 m_cells(0), 93 m_cur_cell_ptr(0), 94 m_cur_x(0), 95 m_cur_y(0), 96 m_min_x(0x7FFFFFFF), 97 m_min_y(0x7FFFFFFF), 98 m_max_x(-0x7FFFFFFF), 99 m_max_y(-0x7FFFFFFF), 100 m_sorted(false) 101{ 102 m_cur_cell.set(0x7FFF, 0x7FFF, 0, 0); 103} 104void outline_aa::reset() 105{ 106 m_num_cells = 0; 107 m_cur_block = 0; 108 m_cur_cell.set(0x7FFF, 0x7FFF, 0, 0); 109 m_sorted = false; 110 m_min_x = 0x7FFFFFFF; 111 m_min_y = 0x7FFFFFFF; 112 m_max_x = -0x7FFFFFFF; 113 m_max_y = -0x7FFFFFFF; 114} 115void outline_aa::allocate_block() 116{ 117 if(m_cur_block >= m_num_blocks) { 118 if(m_num_blocks >= m_max_blocks) { 119 cell_aa** new_cells = FX_Alloc( cell_aa*, m_max_blocks + cell_block_pool); 120 if(m_cells) { 121 FXSYS_memcpy(new_cells, m_cells, m_max_blocks * sizeof(cell_aa*)); 122 FX_Free(m_cells); 123 } 124 m_cells = new_cells; 125 m_max_blocks += cell_block_pool; 126 } 127 m_cells[m_num_blocks++] = FX_Alloc(cell_aa, cell_block_size); 128 } 129 m_cur_cell_ptr = m_cells[m_cur_block++]; 130} 131AGG_INLINE void outline_aa::add_cur_cell() 132{ 133 if(m_cur_cell.area | m_cur_cell.cover) { 134 if((m_num_cells & cell_block_mask) == 0) { 135 if(m_num_blocks >= cell_block_limit) { 136 return; 137 } 138 allocate_block(); 139 } 140 *m_cur_cell_ptr++ = m_cur_cell; 141 ++m_num_cells; 142 } 143} 144AGG_INLINE void outline_aa::set_cur_cell(int x, int y) 145{ 146 if(m_cur_cell.x != x || m_cur_cell.y != y) { 147 add_cur_cell(); 148 m_cur_cell.set(x, y, 0, 0); 149 if(x < m_min_x) { 150 m_min_x = x; 151 } 152 if(x > m_max_x) { 153 m_max_x = x; 154 } 155 if(y < m_min_y) { 156 m_min_y = y; 157 } 158 if(y > m_max_y) { 159 m_max_y = y; 160 } 161 } 162} 163AGG_INLINE void outline_aa::render_hline(int ey, int x1, int y1, int x2, int y2) 164{ 165 int ex1 = x1 >> poly_base_shift; 166 int ex2 = x2 >> poly_base_shift; 167 int fx1 = x1 & poly_base_mask; 168 int fx2 = x2 & poly_base_mask; 169 int delta, p, first, dx; 170 int incr, lift, mod, rem; 171 if(y1 == y2) { 172 set_cur_cell(ex2, ey); 173 return; 174 } 175 if(ex1 == ex2) { 176 delta = y2 - y1; 177 m_cur_cell.add_cover(delta, (fx1 + fx2) * delta); 178 return; 179 } 180 p = (poly_base_size - fx1) * (y2 - y1); 181 first = poly_base_size; 182 incr = 1; 183 dx = x2 - x1; 184 if(dx < 0) { 185 p = fx1 * (y2 - y1); 186 first = 0; 187 incr = -1; 188 dx = -dx; 189 } 190 delta = p / dx; 191 mod = p % dx; 192 if(mod < 0) { 193 delta--; 194 mod += dx; 195 } 196 m_cur_cell.add_cover(delta, (fx1 + first) * delta); 197 ex1 += incr; 198 set_cur_cell(ex1, ey); 199 y1 += delta; 200 if(ex1 != ex2) { 201 p = poly_base_size * (y2 - y1 + delta); 202 lift = p / dx; 203 rem = p % dx; 204 if (rem < 0) { 205 lift--; 206 rem += dx; 207 } 208 mod -= dx; 209 while (ex1 != ex2) { 210 delta = lift; 211 mod += rem; 212 if(mod >= 0) { 213 mod -= dx; 214 delta++; 215 } 216 m_cur_cell.add_cover(delta, (poly_base_size) * delta); 217 y1 += delta; 218 ex1 += incr; 219 set_cur_cell(ex1, ey); 220 } 221 } 222 delta = y2 - y1; 223 m_cur_cell.add_cover(delta, (fx2 + poly_base_size - first) * delta); 224} 225void outline_aa::render_line(int x1, int y1, int x2, int y2) 226{ 227 enum dx_limit_e { dx_limit = 16384 << poly_base_shift }; 228 int dx = x2 - x1; 229 if(dx >= dx_limit || dx <= -dx_limit) { 230 int cx = (x1 + x2) >> 1; 231 int cy = (y1 + y2) >> 1; 232 render_line(x1, y1, cx, cy); 233 render_line(cx, cy, x2, y2); 234 } 235 int dy = y2 - y1; 236 int ey1 = y1 >> poly_base_shift; 237 int ey2 = y2 >> poly_base_shift; 238 int fy1 = y1 & poly_base_mask; 239 int fy2 = y2 & poly_base_mask; 240 int x_from, x_to; 241 int rem, mod, lift, delta, first, incr; 242 if(ey1 == ey2) { 243 render_hline(ey1, x1, fy1, x2, fy2); 244 return; 245 } 246 incr = 1; 247 if(dx == 0) { 248 int ex = x1 >> poly_base_shift; 249 int two_fx = (x1 - (ex << poly_base_shift)) << 1; 250 int area; 251 first = poly_base_size; 252 if(dy < 0) { 253 first = 0; 254 incr = -1; 255 } 256 x_from = x1; 257 delta = first - fy1; 258 m_cur_cell.add_cover(delta, two_fx * delta); 259 ey1 += incr; 260 set_cur_cell(ex, ey1); 261 delta = first + first - poly_base_size; 262 area = two_fx * delta; 263 while(ey1 != ey2) { 264 m_cur_cell.set_cover(delta, area); 265 ey1 += incr; 266 set_cur_cell(ex, ey1); 267 } 268 delta = fy2 - poly_base_size + first; 269 m_cur_cell.add_cover(delta, two_fx * delta); 270 return; 271 } 272 pdfium::base::CheckedNumeric<int> safeP = poly_base_size - fy1; 273 safeP *= dx; 274 if (!safeP.IsValid()) 275 return; 276 first = poly_base_size; 277 if(dy < 0) { 278 safeP = fy1; 279 safeP *= dx; 280 if (!safeP.IsValid()) 281 return; 282 first = 0; 283 incr = -1; 284 dy = -dy; 285 } 286 delta = (safeP / dy).ValueOrDie(); 287 mod = (safeP % dy).ValueOrDie(); 288 if(mod < 0) { 289 delta--; 290 mod += dy; 291 } 292 x_from = x1 + delta; 293 render_hline(ey1, x1, fy1, x_from, first); 294 ey1 += incr; 295 set_cur_cell(x_from >> poly_base_shift, ey1); 296 if(ey1 != ey2) { 297 safeP = static_cast<int>(poly_base_size); 298 safeP *= dx; 299 if (!safeP.IsValid()) 300 return; 301 lift = (safeP / dy).ValueOrDie(); 302 rem = (safeP % dy).ValueOrDie(); 303 if (rem < 0) { 304 lift--; 305 rem += dy; 306 } 307 mod -= dy; 308 while(ey1 != ey2) { 309 delta = lift; 310 mod += rem; 311 if (mod >= 0) { 312 mod -= dy; 313 delta++; 314 } 315 x_to = x_from + delta; 316 render_hline(ey1, x_from, poly_base_size - first, x_to, first); 317 x_from = x_to; 318 ey1 += incr; 319 set_cur_cell(x_from >> poly_base_shift, ey1); 320 } 321 } 322 render_hline(ey1, x_from, poly_base_size - first, x2, fy2); 323} 324void outline_aa::move_to(int x, int y) 325{ 326 if(m_sorted) { 327 reset(); 328 } 329 set_cur_cell(x >> poly_base_shift, y >> poly_base_shift); 330 m_cur_x = x; 331 m_cur_y = y; 332} 333void outline_aa::line_to(int x, int y) 334{ 335 render_line(m_cur_x, m_cur_y, x, y); 336 m_cur_x = x; 337 m_cur_y = y; 338 m_sorted = false; 339} 340template <class T> static AGG_INLINE void swap_cells(T* a, T* b) 341{ 342 T temp = *a; 343 *a = *b; 344 *b = temp; 345} 346enum { 347 qsort_threshold = 9 348}; 349static void qsort_cells(cell_aa** start, unsigned num) 350{ 351 cell_aa** stack[80]; 352 cell_aa*** top; 353 cell_aa** limit; 354 cell_aa** base; 355 limit = start + num; 356 base = start; 357 top = stack; 358 for (;;) { 359 int len = int(limit - base); 360 cell_aa** i; 361 cell_aa** j; 362 cell_aa** pivot; 363 if(len > qsort_threshold) { 364 pivot = base + len / 2; 365 swap_cells(base, pivot); 366 i = base + 1; 367 j = limit - 1; 368 if((*j)->x < (*i)->x) { 369 swap_cells(i, j); 370 } 371 if((*base)->x < (*i)->x) { 372 swap_cells(base, i); 373 } 374 if((*j)->x < (*base)->x) { 375 swap_cells(base, j); 376 } 377 for(;;) { 378 int x = (*base)->x; 379 do { 380 i++; 381 } while( (*i)->x < x ); 382 do { 383 j--; 384 } while( x < (*j)->x ); 385 if(i > j) { 386 break; 387 } 388 swap_cells(i, j); 389 } 390 swap_cells(base, j); 391 if(j - base > limit - i) { 392 top[0] = base; 393 top[1] = j; 394 base = i; 395 } else { 396 top[0] = i; 397 top[1] = limit; 398 limit = j; 399 } 400 top += 2; 401 } else { 402 j = base; 403 i = j + 1; 404 for(; i < limit; j = i, i++) { 405 for(; j[1]->x < (*j)->x; j--) { 406 swap_cells(j + 1, j); 407 if (j == base) { 408 break; 409 } 410 } 411 } 412 if(top > stack) { 413 top -= 2; 414 base = top[0]; 415 limit = top[1]; 416 } else { 417 break; 418 } 419 } 420 } 421} 422void outline_aa::sort_cells() 423{ 424 if(m_sorted) { 425 return; 426 } 427 add_cur_cell(); 428 if(m_num_cells == 0) { 429 return; 430 } 431 m_sorted_cells.allocate(m_num_cells, 16); 432 if (m_max_y > 0 && m_min_y < 0 && -m_min_y > INT_MAX - m_max_y) { 433 return; 434 } 435 unsigned size = m_max_y - m_min_y; 436 if (size + 1 < size) { 437 return; 438 } 439 size++; 440 m_sorted_y.allocate(size, 16); 441 m_sorted_y.zero(); 442 cell_aa** block_ptr = m_cells; 443 cell_aa* cell_ptr = NULL; 444 unsigned nb = m_num_cells >> cell_block_shift; 445 unsigned i; 446 while(nb--) { 447 cell_ptr = *block_ptr++; 448 i = cell_block_size; 449 while(i--) { 450 m_sorted_y[cell_ptr->y - m_min_y].start++; 451 ++cell_ptr; 452 } 453 } 454 i = m_num_cells & cell_block_mask; 455 if (i) { 456 cell_ptr = *block_ptr++; 457 } 458 while(i--) { 459 m_sorted_y[cell_ptr->y - m_min_y].start++; 460 ++cell_ptr; 461 } 462 unsigned start = 0; 463 for(i = 0; i < m_sorted_y.size(); i++) { 464 unsigned v = m_sorted_y[i].start; 465 m_sorted_y[i].start = start; 466 start += v; 467 } 468 block_ptr = m_cells; 469 nb = m_num_cells >> cell_block_shift; 470 while(nb--) { 471 cell_ptr = *block_ptr++; 472 i = cell_block_size; 473 while(i--) { 474 sorted_y& cur_y = m_sorted_y[cell_ptr->y - m_min_y]; 475 m_sorted_cells[cur_y.start + cur_y.num] = cell_ptr; 476 ++cur_y.num; 477 ++cell_ptr; 478 } 479 } 480 i = m_num_cells & cell_block_mask; 481 if (i) { 482 cell_ptr = *block_ptr++; 483 } 484 while(i--) { 485 sorted_y& cur_y = m_sorted_y[cell_ptr->y - m_min_y]; 486 m_sorted_cells[cur_y.start + cur_y.num] = cell_ptr; 487 ++cur_y.num; 488 ++cell_ptr; 489 } 490 for(i = 0; i < m_sorted_y.size(); i++) { 491 const sorted_y& cur_y = m_sorted_y[i]; 492 if(cur_y.num) { 493 qsort_cells(m_sorted_cells.data() + cur_y.start, cur_y.num); 494 } 495 } 496 m_sorted = true; 497} 498} 499