#include "Painter.h" // This code is based in AGG rasterizer with this original information: //---------------------------------------------------------------------------- // Anti-Grain Geometry - Version 2.4 // Copyright (C) 2002-2005 Maxim Shemanarev (http://www.antigrain.com) // // Permission to copy, use, modify, sell and distribute this software // is granted provided this copyright notice appears in all copies. // This software is provided "as is" without express or implied // warranty, and with no claim as to its suitability for any purpose. // //---------------------------------------------------------------------------- // // The author gratefully acknowleges the support of David Turner, // Robert Wilhelm, and Werner Lemberg - the authors of the FreeType // libray - in producing this work. See http://www.freetype.org for details. // //---------------------------------------------------------------------------- // Contact: mcseem@antigrain.com // mcseemagg@yahoo.com // http://www.antigrain.com //---------------------------------------------------------------------------- #define LLOG(x) // DLOG(x) namespace Upp { void Rasterizer::Create(int cx, int cy, bool subpixel) { Free(); mx = subpixel ? 3 * 256 : 256; sz.cx = cx; sz.cy = cy; cell.Alloc(sz.cy + 1); // one more for overrun // STATIC_ASSERT(sizeof(CellArray) == 256); cliprect = Sizef(sz); Init(); Reset(); // TODO: Can be removed (cell is just constructed, Init called above) } void Rasterizer::Free() { if(cell) { for(int i = min_y; i <= max_y; i++) { if(cell[i].alloc != SVO_ALLOC) { MemoryFree(cell[i].ptr); cell[i].alloc = SVO_ALLOC; } cell[i].count = 0; } if(cell[sz.cy].alloc != SVO_ALLOC) { // check overrun MemoryFree(cell[sz.cy].ptr); cell[sz.cy].alloc = SVO_ALLOC; } cell[sz.cy].count = 0; } } void Rasterizer::Init() { p0 = Pointf(0, 0); min_y = INT_MAX; max_y = INT_MIN; } void Rasterizer::Reset() { Free(); Init(); } void Rasterizer::SetClip(const Rectf& rect) { cliprect = rect & Sizef(sz); } force_inline Rasterizer::Cell *Rasterizer::AddCells(int y, int n) { CellArray& a = cell[y]; if(a.count + n <= SVO_ALLOC) { Cell *r = a.svo + a.count; a.count += n; return r; } if(a.count + n > a.alloc) { size_t sz = sizeof(Cell) * (a.alloc + (a.alloc >> 1) + n); Cell *cells = (Cell *)MemoryAllocSz(sz); if(a.alloc == SVO_ALLOC) memcpy_t(cells, a.svo, a.count); else { memcpy_t(cells, a.ptr, a.count); MemoryFree(a.ptr); } a.ptr = cells; a.alloc = int(sz / sizeof(Cell)); } Cell *r = a.ptr + a.count; a.count += n; return r; } inline void Rasterizer::RenderHLine(int ey, int x1, int y1, int x2, int y2) { int ex1 = x1 >> 8; int ex2 = x2 >> 8; int fx1 = x1 & 255; int fx2 = x2 & 255; Cell *c; if(y1 == y2) return; int dy = y2 - y1; if(ex1 == ex2) { c = AddCells(ey, 1); c->x = ex1; c->cover = dy; c->area = (fx1 + fx2) * dy; return; } int dx = x2 - x1; if(dx < 0) { int p = fx1 * dy; dx = -dx; int delta = p / dx; int mod = p % dx; if(mod < 0) { delta--; mod += dx; } c = AddCells(ey, abs(ex2 - ex1) + 1); c->x = ex1; c->cover = delta; c->area = fx1 * delta; c++; ex1--; y1 += delta; if(ex1 != ex2) { p = (y2 - y1 + delta) * 256; int lift = p / dx; int rem = p % dx; if (rem < 0) { lift--; rem += dx; } mod -= dx; while(ex1 != ex2) { delta = lift; mod += rem; if(mod >= 0) { mod -= dx; delta++; } c->x = ex1; c->cover = delta; c->area = delta * 256; c++; y1 += delta; ex1--; } } c->x = ex2; dy = y2 - y1; c->cover = dy; c->area = (fx2 + 256) * dy; } else { int p = (256 - fx1) * dy; int delta = p / dx; int mod = p % dx; if(mod < 0) { delta--; mod += dx; } c = AddCells(ey, abs(ex2 - ex1) + 1); c->x = ex1; c->cover = delta; c->area = (fx1 + 256) * delta; c++; ex1++; y1 += delta; if(ex1 != ex2) { p = (y2 - y1 + delta) * 256; int lift = p / dx; int rem = p % dx; if (rem < 0) { lift--; rem += dx; } mod -= dx; while(ex1 != ex2) { delta = lift; mod += rem; if(mod >= 0) { mod -= dx; delta++; } c->x = ex1; c->cover = delta; c->area = delta * 256; c++; y1 += delta; ex1++; } } c->x = ex2; dy = y2 - y1; c->cover = dy; c->area = fx2 * dy; } } void Rasterizer::LineRaw(int x1, int y1, int x2, int y2) { PAINTER_TIMING("LineRaw"); LLOG("Rasterizer::LineRaw " << x1 / 256.0 << ':' << y1 / 256.0 << " - " << x2 / 256.0 << ':' << y2 / 256.0); int ex1 = x1 >> 8; int ey1 = y1 >> 8; int ey2 = y2 >> 8; ASSERT(ey1 >= 0 && ey1 <= sz.cy && ey2 >= 0 && ey2 <= sz.cy); if(ey1 < min_y) { if(ey1 < 0) return; min_y = ey1; } if(ey1 > max_y) { if(ey1 > sz.cy) return; max_y = min(ey1, sz.cy - 1); } if(ey2 < min_y) { if(ey2 < 0) return; min_y = ey2; } if(ey2 > max_y) { if(ey2 > sz.cy) return; max_y = min(ey2, sz.cy - 1); } enum dx_limit_e { dx_limit = 16384 << 8 }; int dx = x2 - x1; if(dx >= dx_limit || dx <= -dx_limit) { int cx = (x1 + x2) >> 1; int cy = (y1 + y2) >> 1; LineRaw(x1, y1, cx, cy); LineRaw(cx, cy, x2, y2); return; } int dy = y2 - y1; int fy1 = y1 & 255; int fy2 = y2 & 255; Cell *c; int x_from, x_to; int p, rem, mod, lift, delta, first, incr; if(ey1 == ey2) { RenderHLine(ey1, x1, fy1, x2, fy2); return; } incr = 1; if(dx == 0) { int two_fx = (x1 - (ex1 << 8)) << 1; int area; first = 256; if(dy < 0) { first = 0; incr = -1; } x_from = x1; delta = first - fy1; c = AddCells(ey1, 1); c->x = ex1; c->cover = delta; c->area = two_fx * delta; ey1 += incr; delta = first + first - 256; area = two_fx * delta; while(ey1 != ey2) { c = AddCells(ey1, 1); c->x = ex1; c->cover = delta; c->area = area; ey1 += incr; } delta = fy2 - 256 + first; c = AddCells(ey1, 1); c->x = ex1; c->cover = delta; c->area = two_fx * delta; return; } p = (256 - fy1) * dx; first = 256; if(dy < 0) { p = fy1 * dx; first = 0; incr = -1; dy = -dy; } delta = p / dy; mod = p % dy; if(mod < 0) { delta--; mod += dy; } x_from = x1 + delta; RenderHLine(ey1, x1, fy1, x_from, first); ey1 += incr; if(ey1 != ey2) { p = dx << 8; lift = p / dy; rem = p % dy; if(rem < 0) { lift--; rem += dy; } mod -= dy; while(ey1 != ey2) { delta = lift; mod += rem; if(mod >= 0) { mod -= dy; delta++; } x_to = x_from + delta; RenderHLine(ey1, x_from, 256 - first, x_to, first); x_from = x_to; ey1 += incr; } } RenderHLine(ey1, x_from, 256 - first, x2, fy2); } void Rasterizer::Filler::End() {} void Rasterizer::Render(int y, Rasterizer::Filler& g, bool evenodd) { PAINTER_TIMING("Render"); CellArray& a = cell[y]; if(a.count == 0) return; Cell *c = a.Get(); Cell *e = c + a.count; Sort(SubRange(c, e)); g.Start(c->x, (e - 1)->x); int cover = 0; while(c < e) { int x = c->x; int area = c->area; cover += c->cover; c++; while(c < e && c->x == x) { area += c->area; cover += c->cover; c++; } if(evenodd) { if(area) { int h = abs(((cover << 9) - area) >> 9) & 511; g.Render(h > 256 ? 512 - h : h); x++; } if(c < e && c->x > x) { int h = abs(cover) & 511; g.Render(h > 256 ? 512 - h : h, c->x - x); } } else { if(area) { g.Render(min(abs(((cover << 9) - area) >> 9), 256)); x++; } if(c < e && c->x > x) g.Render(min(abs(cover), 256), c->x - x); } } g.End(); } }