Rasterizer clipping

View: New views
7 Messages — Rating Filter:   Alert me  

Rasterizer clipping

by Petr Kobalíček :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

Hi devs,

is there way to set clipping to rasterizer in pixel based coordinates?

I'm working with agg::rasterizer_scanline_aa and there is method void
clip_box(double x1, double y1, double x2, double y2),
if I use for example clip_box(1.0, 1.0, 4.0, 4.0) is there chance that
there will be some content in scanlines outside these bounds (I mean
something with 0 cover) ?

Note: I'm using this rasterizer instead of other classes that's using
it to render real content.

Thanks for ideas
- Petr

------------------------------------------------------------------------------
Register Now & Save for Velocity, the Web Performance & Operations
Conference from O'Reilly Media. Velocity features a full day of
expert-led, hands-on workshops and two days of sessions from industry
leaders in dedicated Performance & Operations tracks. Use code vel09scf
and Save an extra 15% before 5/3. http://p.sf.net/sfu/velocityconf
_______________________________________________
Vector-agg-general mailing list
Vector-agg-general@...
https://lists.sourceforge.net/lists/listinfo/vector-agg-general

Re: Rasterizer clipping

by Stephan Assmus :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

Petr Kobalíček schrieb:
> is there way to set clipping to rasterizer in pixel based coordinates?
>
> I'm working with agg::rasterizer_scanline_aa and there is method void
> clip_box(double x1, double y1, double x2, double y2),
> if I use for example clip_box(1.0, 1.0, 4.0, 4.0) is there chance that
> there will be some content in scanlines outside these bounds (I mean
> something with 0 cover) ?

AFAIK, there should not be any pixels rendered outside these bounds.

> Note: I'm using this rasterizer instead of other classes that's using
> it to render real content.

What is the actual problem that you observe?

Best regards,
-Stephan

------------------------------------------------------------------------------
Register Now & Save for Velocity, the Web Performance & Operations
Conference from O'Reilly Media. Velocity features a full day of
expert-led, hands-on workshops and two days of sessions from industry
leaders in dedicated Performance & Operations tracks. Use code vel09scf
and Save an extra 15% before 5/3. http://p.sf.net/sfu/velocityconf
_______________________________________________
Vector-agg-general mailing list
Vector-agg-general@...
https://lists.sourceforge.net/lists/listinfo/vector-agg-general

Re: Rasterizer clipping

by Petr Kobalíček :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

Hi stefan,

thanks for reply. To give you basic understanding what I'm doing I
will show you snippet of code:

template<int BytesPerPixel, class Rasterizer, class Scanline>
static void FOG_OPTIMIZEDCALL AggRenderScanlines(RasterPainterDevice*
d, Rasterizer& ras, Scanline& sl)
{
  if (!ras.rewind_scanlines()) return;

  uint8_t* pBase = d->_workRaster;
  uint8_t* pRas;
  uint8_t* pCur;
  sysint_t stride = d->_stride;

  sl.reset(ras.min_x(), ras.max_x());

  // TODO: Not needed ?
  // int extx1 = painter_d->_realRegion.extents().x1();
  // int exty1 = d->_clipBox.y1();
  // int extx2 = painter_d->_realRegion.extents().x2();
  // int exty2 = d->_clipBox.y2();

  FillSpan fillSpan = d->_fillFuncs.fillSpan;
  FillSpanM fillSpanM_A8 = d->_fillFuncs.fillSpanM_A8;

  // solid source
  if (1)
  {
    while (ras.sweep_scanline(sl))
    {
      unsigned num_spans = sl.num_spans();
      typename Scanline::const_iterator span = sl.begin();

      sysint_t y = sl.y();

      // TODO: Not needed ?
      // Vertical clipping to extents.
      // if (y < exty1) continue;
      // if (y >= exty2) break;

      pRas = pBase + y * stride;

      for (;;)
      {
        int x = span->x;
        int len = span->len;

        pCur = pRas + Raster::mul<int, BytesPerPixel>(x);

        if (len > 0)
        {
          fillSpanM_A8(pCur, &d->_source, span->covers, (unsigned)len);
        }
        else
        {
          len = -len;
          FOG_ASSERT(len > 0);

          uint32_t cover = (uint32_t)*(span->covers);
          if (cover == 0xFF)
          {
            fillSpan(pCur, &d->_source, len);
          }
          else
          {
            uint32_t t = Raster::bytemul(d->_source.i, cover);
            fillSpan(pCur, &t, len);
          }
        }

        if (--num_spans == 0) break;
        ++span;
      }
    }
  }
}

This is function that renders content using antigrain rasterizer and
BlitJit jit optimized fillers/blitters. I will probably create own
rasterizer class and I will try some optimizations (I have some
ideas), but currently I'm finding bug in my code that causes sometimes
invalid writes (I'm using valgrind to profile it) so I wondered if
this can happen through clipping.

Btw: the posted function is called from this:

void RasterPainterDevice::_renderPath(const Path& path, bool stroke)
{
  AggPath aggPath(path);
  ConvCurve curvesPath(aggPath);

  _ras.reset();
  _ras.filling_rule(static_cast<agg::filling_rule_e>(_fillMode));
  _ras.clip_box(
    (double)_clipBox.x1(),
    (double)_clipBox.y1(),
    (double)_clipBox.x2()-0.000001,
    (double)_clipBox.y2()-0.000001);

  // This can be a bit messy, but it's here to increase performance. We will
  // not calculate using transformations if they are not used. Also we add
  // stroke and line dash pipeline only if it's needed. This is goal of
  // AntiGrain to be able to setup only pipelines what are really need.
  if (_transformationsUsed)
  {
    if (stroke)
    {
      ConvStroke strokePath(curvesPath);
      ConvStrokeTransform strokeTransform(
        strokePath, *((agg::trans_affine *)&_transformations));

      strokePath.width(_lineWidth);
      strokePath.line_join(static_cast<agg::line_join_e>(_lineJoin));
      strokePath.line_cap(static_cast<agg::line_cap_e>(_lineCap));
      strokePath.miter_limit(_miterLimit);

      _ras.add_path(strokeTransform);
    }
    else
    {
      ConvCurveTransform curvesTransform(
        curvesPath, *((agg::trans_affine *)&_transformations));

      _ras.add_path(curvesTransform);
    }
  }
  else
  {
    if (stroke)
    {
      ConvStroke strokePath(curvesPath);

      strokePath.width(_lineWidth);
      strokePath.line_join(static_cast<agg::line_join_e>(_lineJoin));
      strokePath.line_cap(static_cast<agg::line_cap_e>(_lineCap));
      strokePath.miter_limit(_miterLimit);

      _ras.add_path(strokePath);
    }
    else
    {
      _ras.add_path(curvesPath);
    }
  }

  AggRenderScanlines<4, Rasterizer, ScanlineP8>(this, _ras, _slP8);
}

I only think that there should be way how to clip to pixel units,
because windowing systems and painters usually contains pixel based
clipping (my painter too) and converting this to double to convert it
back to pixels in rasterizer seems to be a bit hacky :) But antigrain
is nice library to play with, I really think it's powerful, because if
I need I can replace any part to fit my needs:)

- Petr

2009/4/30 Stephan Aßmus <superstippi@...>:

> Petr Kobalíček schrieb:
>> is there way to set clipping to rasterizer in pixel based coordinates?
>>
>> I'm working with agg::rasterizer_scanline_aa and there is method void
>> clip_box(double x1, double y1, double x2, double y2),
>> if I use for example clip_box(1.0, 1.0, 4.0, 4.0) is there chance that
>> there will be some content in scanlines outside these bounds (I mean
>> something with 0 cover) ?
>
> AFAIK, there should not be any pixels rendered outside these bounds.
>
>> Note: I'm using this rasterizer instead of other classes that's using
>> it to render real content.
>
> What is the actual problem that you observe?
>
> Best regards,
> -Stephan
>
> ------------------------------------------------------------------------------
> Register Now & Save for Velocity, the Web Performance & Operations
> Conference from O'Reilly Media. Velocity features a full day of
> expert-led, hands-on workshops and two days of sessions from industry
> leaders in dedicated Performance & Operations tracks. Use code vel09scf
> and Save an extra 15% before 5/3. http://p.sf.net/sfu/velocityconf
> _______________________________________________
> Vector-agg-general mailing list
> Vector-agg-general@...
> https://lists.sourceforge.net/lists/listinfo/vector-agg-general
>

------------------------------------------------------------------------------
Register Now & Save for Velocity, the Web Performance & Operations
Conference from O'Reilly Media. Velocity features a full day of
expert-led, hands-on workshops and two days of sessions from industry
leaders in dedicated Performance & Operations tracks. Use code vel09scf
and Save an extra 15% before 5/3. http://p.sf.net/sfu/velocityconf
_______________________________________________
Vector-agg-general mailing list
Vector-agg-general@...
https://lists.sourceforge.net/lists/listinfo/vector-agg-general

Re: Rasterizer clipping

by Stephan Assmus :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

Hi Petr,

I flipped your functions, because I need to discuss them in reverted
order... :-)

> void RasterPainterDevice::_renderPath(const Path& path, bool stroke) {
>   AggPath aggPath(path);
>   ConvCurve curvesPath(aggPath);
>
>   _ras.reset();
>   _ras.filling_rule(static_cast<agg::filling_rule_e>(_fillMode));
>   _ras.clip_box(
>     (double)_clipBox.x1(),
>     (double)_clipBox.y1(),
>     (double)_clipBox.x2()-0.000001,
>     (double)_clipBox.y2()-0.000001);
[...]

If your _clipBox is in pixel indices, ie. x1() refers to the index of the
left pixel and x2() refers to the index of the right pixel, then it needs
to be like this:

   _ras.clip_box(
     (double)_clipBox.x1(),
     (double)_clipBox.y1(),
     (double)_clipBox.x2() + 1,
     (double)_clipBox.y2() + 1);

That's because the rasterizer clipping is still at a "geometry level". If
your clipBox can be non-integer, but you still want pixel based clipping,
then it needs to be like this:

   _ras.clip_box(
     floor(_clipBox.x1()),
     floor(_clipBox.y1(),
     ceil(_clipBox.x2()),
     ceil(_clipBox.y2()));

In any case, you need to include the pixels at the last colum/row by
rounding up or adding "+ 1" for integer based pixel indices clipping boxen.



> template<int BytesPerPixel, class Rasterizer, class Scanline> static void
> FOG_OPTIMIZEDCALL AggRenderScanlines(RasterPainterDevice* d, Rasterizer&
> ras, Scanline& sl)
> {
>   if (!ras.rewind_scanlines()) return;
>
>   uint8_t* pBase = d->_workRaster;
>   uint8_t* pRas;
>   uint8_t* pCur;
>   sysint_t stride = d->_stride;
>
>   sl.reset(ras.min_x(), ras.max_x());
>
>   // TODO: Not needed ?
>   // int extx1 = painter_d->_realRegion.extents().x1();
>   // int exty1 = d->_clipBox.y1();
>   // int extx2 = painter_d->_realRegion.extents().x2();
>   // int exty2 = d->_clipBox.y2();
>
>   FillSpan fillSpan = d->_fillFuncs.fillSpan;
>   FillSpanM fillSpanM_A8 = d->_fillFuncs.fillSpanM_A8;
>
>   // solid source
>   if (1)
>   {
>     while (ras.sweep_scanline(sl))
>     {
>       unsigned num_spans = sl.num_spans();
>       typename Scanline::const_iterator span = sl.begin();
>
>       sysint_t y = sl.y();
>
>       // TODO: Not needed ?
>       // Vertical clipping to extents.
>       // if (y < exty1) continue;
>       // if (y >= exty2) break;
>
>       pRas = pBase + y * stride;
>
>       for (;;)
>       {
>         int x = span->x;
>         int len = span->len;
>
>         pCur = pRas + Raster::mul<int, BytesPerPixel>(x);
>
>         if (len > 0)
>         {
>           fillSpanM_A8(pCur, &d->_source, span->covers, (unsigned)len);
>         }
>         else
>         {
>           len = -len;
>           FOG_ASSERT(len > 0);
>
>           uint32_t cover = (uint32_t)*(span->covers);
>           if (cover == 0xFF)
>           {
>             fillSpan(pCur, &d->_source, len);
>           }
>           else
>           {
>             uint32_t t = Raster::bytemul(d->_source.i, cover);
>             fillSpan(pCur, &t, len);
>           }
>         }
>
>         if (--num_spans == 0) break;
>         ++span;
>       }
>     }
>   }
> }

Since you are doing your own rendering, I suspect that the error is
somewhere there. I am not 100% sure that if you setup rasterizer clipping,
that it will never render outside that box. I think it doesn't, but it
would be thinkable that it still touches outside pixels with cover of 0.
But I find that highly unlikely. At least I have never seen any problems in
this regard, but I also usually setup clipping on the renderer as well. In
your place, I would simply add the necessary debugging facilities to make
sure of this. My first suspicion would be that the out of bounds access
happens somewhere in your code. Possibly the fillSpan routines.

Best regards,
-Stephan

------------------------------------------------------------------------------
The NEW KODAK i700 Series Scanners deliver under ANY circumstances! Your
production scanning environment may not be a perfect world - but thanks to
Kodak, there's a perfect scanner to get the job done! With the NEW KODAK i700
Series Scanner you'll get full speed at 300 dpi even with all image
processing features enabled. http://p.sf.net/sfu/kodak-com
_______________________________________________
Vector-agg-general mailing list
Vector-agg-general@...
https://lists.sourceforge.net/lists/listinfo/vector-agg-general

Re: Rasterizer clipping

by Petr Kobalíček :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

Hi Stephan,

thanks for detailed reply:) I think that my code is right, because my
geometry model is quite different. The x1,y1 positions are first and
x2,y2 positions are last, but they are not in region. I think that
RECT structure and HREGION from WinAPI should explain this:

width = x2 - x1;
height = y2 - y1;

x2/y2 are not part of rect. Only code I'm not sure is -0.000001, I
think that after your explanations it's not necessary, thank you.

Currently my problem is different. Part of my thesis is multithreading
and I don't know how to access scanlines from multiple threads, to be
hones I don't know how AGG rasterizer exactly works. My current idea
is to sweep_scanlines() from different threads, but this can't be done
without locking, so my second idea is rasterizer for each thread and
set different clipping for each. This will isolate and optimize
access, but the path will be computed in all threads.

BTW: The out of bounds problem is probably gone. I think I fixed it in
BlitJit, but I'm not sure where exactly:)

Cheers
- Petr

2009/5/9 Stephan Assmus <superstippi@...>:

> Hi Petr,
>
> I flipped your functions, because I need to discuss them in reverted
> order... :-)
>
>> void RasterPainterDevice::_renderPath(const Path& path, bool stroke) {
>>   AggPath aggPath(path);
>>   ConvCurve curvesPath(aggPath);
>>
>>   _ras.reset();
>>   _ras.filling_rule(static_cast<agg::filling_rule_e>(_fillMode));
>>   _ras.clip_box(
>>     (double)_clipBox.x1(),
>>     (double)_clipBox.y1(),
>>     (double)_clipBox.x2()-0.000001,
>>     (double)_clipBox.y2()-0.000001);
> [...]
>
> If your _clipBox is in pixel indices, ie. x1() refers to the index of the
> left pixel and x2() refers to the index of the right pixel, then it needs
> to be like this:
>
>   _ras.clip_box(
>     (double)_clipBox.x1(),
>     (double)_clipBox.y1(),
>     (double)_clipBox.x2() + 1,
>     (double)_clipBox.y2() + 1);
>
> That's because the rasterizer clipping is still at a "geometry level". If
> your clipBox can be non-integer, but you still want pixel based clipping,
> then it needs to be like this:
>
>   _ras.clip_box(
>     floor(_clipBox.x1()),
>     floor(_clipBox.y1(),
>     ceil(_clipBox.x2()),
>     ceil(_clipBox.y2()));
>
> In any case, you need to include the pixels at the last colum/row by
> rounding up or adding "+ 1" for integer based pixel indices clipping boxen.
>
>
>
>> template<int BytesPerPixel, class Rasterizer, class Scanline> static void
>> FOG_OPTIMIZEDCALL AggRenderScanlines(RasterPainterDevice* d, Rasterizer&
>> ras, Scanline& sl)
>> {
>>   if (!ras.rewind_scanlines()) return;
>>
>>   uint8_t* pBase = d->_workRaster;
>>   uint8_t* pRas;
>>   uint8_t* pCur;
>>   sysint_t stride = d->_stride;
>>
>>   sl.reset(ras.min_x(), ras.max_x());
>>
>>   // TODO: Not needed ?
>>   // int extx1 = painter_d->_realRegion.extents().x1();
>>   // int exty1 = d->_clipBox.y1();
>>   // int extx2 = painter_d->_realRegion.extents().x2();
>>   // int exty2 = d->_clipBox.y2();
>>
>>   FillSpan fillSpan = d->_fillFuncs.fillSpan;
>>   FillSpanM fillSpanM_A8 = d->_fillFuncs.fillSpanM_A8;
>>
>>   // solid source
>>   if (1)
>>   {
>>     while (ras.sweep_scanline(sl))
>>     {
>>       unsigned num_spans = sl.num_spans();
>>       typename Scanline::const_iterator span = sl.begin();
>>
>>       sysint_t y = sl.y();
>>
>>       // TODO: Not needed ?
>>       // Vertical clipping to extents.
>>       // if (y < exty1) continue;
>>       // if (y >= exty2) break;
>>
>>       pRas = pBase + y * stride;
>>
>>       for (;;)
>>       {
>>         int x = span->x;
>>         int len = span->len;
>>
>>         pCur = pRas + Raster::mul<int, BytesPerPixel>(x);
>>
>>         if (len > 0)
>>         {
>>           fillSpanM_A8(pCur, &d->_source, span->covers, (unsigned)len);
>>         }
>>         else
>>         {
>>           len = -len;
>>           FOG_ASSERT(len > 0);
>>
>>           uint32_t cover = (uint32_t)*(span->covers);
>>           if (cover == 0xFF)
>>           {
>>             fillSpan(pCur, &d->_source, len);
>>           }
>>           else
>>           {
>>             uint32_t t = Raster::bytemul(d->_source.i, cover);
>>             fillSpan(pCur, &t, len);
>>           }
>>         }
>>
>>         if (--num_spans == 0) break;
>>         ++span;
>>       }
>>     }
>>   }
>> }
>
> Since you are doing your own rendering, I suspect that the error is
> somewhere there. I am not 100% sure that if you setup rasterizer clipping,
> that it will never render outside that box. I think it doesn't, but it
> would be thinkable that it still touches outside pixels with cover of 0.
> But I find that highly unlikely. At least I have never seen any problems in
> this regard, but I also usually setup clipping on the renderer as well. In
> your place, I would simply add the necessary debugging facilities to make
> sure of this. My first suspicion would be that the out of bounds access
> happens somewhere in your code. Possibly the fillSpan routines.
>
> Best regards,
> -Stephan
>
> ------------------------------------------------------------------------------
> The NEW KODAK i700 Series Scanners deliver under ANY circumstances! Your
> production scanning environment may not be a perfect world - but thanks to
> Kodak, there's a perfect scanner to get the job done! With the NEW KODAK i700
> Series Scanner you'll get full speed at 300 dpi even with all image
> processing features enabled. http://p.sf.net/sfu/kodak-com
> _______________________________________________
> Vector-agg-general mailing list
> Vector-agg-general@...
> https://lists.sourceforge.net/lists/listinfo/vector-agg-general
>

------------------------------------------------------------------------------
The NEW KODAK i700 Series Scanners deliver under ANY circumstances! Your
production scanning environment may not be a perfect world - but thanks to
Kodak, there's a perfect scanner to get the job done! With the NEW KODAK i700
Series Scanner you'll get full speed at 300 dpi even with all image
processing features enabled. http://p.sf.net/sfu/kodak-com
_______________________________________________
Vector-agg-general mailing list
Vector-agg-general@...
https://lists.sourceforge.net/lists/listinfo/vector-agg-general

Re: Rasterizer clipping

by Stephan Assmus :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

Hi Petr,

On 2009-05-09 at 12:00:45 [+0200], Petr Kobalíček
<kobalicek.petr@...> wrote:

> thanks for detailed reply:) I think that my code is right, because my
> geometry model is quite different. The x1,y1 positions are first and
> x2,y2 positions are last, but they are not in region. I think that RECT
> structure and HREGION from WinAPI should explain this:
>
> width = x2 - x1;
> height = y2 - y1;
>
> x2/y2 are not part of rect. Only code I'm not sure is -0.000001, I think
> that after your explanations it's not necessary, thank you.

Perfect, that's clear then.

> Currently my problem is different. Part of my thesis is multithreading
> and I don't know how to access scanlines from multiple threads, to be
> hones I don't know how AGG rasterizer exactly works. My current idea is
> to sweep_scanlines() from different threads, but this can't be done
> without locking, so my second idea is rasterizer for each thread and set
> different clipping for each. This will isolate and optimize access, but
> the path will be computed in all threads.

I think the best approach depends on your framework. Ie, do you render a
complete scene and want this to be multi-threaded? Or do you provide a
graphics API and render one path at a time or whatever the client code that
uses your lib can trigger to be rendered in one call into your lib? From
what I understand of what you are doing, it's the later. Then you have the
following problem: There is of course a slight overhead to managing the
threads, preparing jobs and triggering the threads to run. If the work to
be done falls below a certain threshold, it could be beneficial to switch
between single threaded rendering and multi-threaded rendering depending on
estimated penalty. However, this again could pose a problem. Assume client
code uses your library to render text, and enters your library for each
glyph in a text run. The glyphs are small and each time you may estimate
that it's better to stay single threaded, and effectively the whole glyph
run is rendered in a single thread as a result. To work around this, your
library could encourage "bulk rendering", where the client code schedules
more data to be rendered in one API call.

Anyways, in the background, I believe one "pipeline" per thread would be
best, ie including rasterizer, renderer, scanline storage, everything that
changes during rendering. And you need to be careful! For example, the
agg::path_storage has the iterator implementation built-in, IAW iterating
over a path storage is _not_ a read-only operation! So you should extract
that from the actual path storage, so that you can have one instance of a
path storage, but one iterator instance in each thread to iterate the same
path data.

Finally I would estimate that alternating scanlines based rendering should
yield the best balanceing effect. As you have already your own renderer
code where you iterate over scanlines, I would extend the AGG rasterizer
interface to be able to provide a "skip" value, ie the increment to be used
when iterating over scanlines during rasterizing. And the same for your
rendering implementation.


> BTW: The out of bounds problem is probably gone. I think I fixed it in
> BlitJit, but I'm not sure where exactly:)

Cool.

Best regards,
-Stephan

------------------------------------------------------------------------------
The NEW KODAK i700 Series Scanners deliver under ANY circumstances! Your
production scanning environment may not be a perfect world - but thanks to
Kodak, there's a perfect scanner to get the job done! With the NEW KODAK i700
Series Scanner you'll get full speed at 300 dpi even with all image
processing features enabled. http://p.sf.net/sfu/kodak-com
_______________________________________________
Vector-agg-general mailing list
Vector-agg-general@...
https://lists.sourceforge.net/lists/listinfo/vector-agg-general

Re: Rasterizer clipping

by Petr Kobalíček :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

Hi Stephan,

your comment is really great!

I think you understand what I'm are doing. Your guess about API is
right (API like cairo to render one primitive per function call). I
solved some problems you talked about. For example I'm not using
agg::path_storage, but I'm created own Path that is not changed during
iteration and with antigrain it's used through wrapper (antigrain
architecture is really good in wrapping everythin).

For example the path wrapper looks like this:

// Wraps Fog::Path to antigrain like vertex storage.
struct FOG_HIDDEN AggPath
{
  FOG_INLINE AggPath(const Path& path)
  {
    d = path._d;
    rewind(0);
  }

  FOG_INLINE ~AggPath()
  {
  }

  FOG_INLINE void rewind(unsigned index)
  {
    vCur = d->data + index;
    vEnd = d->data + d->length;
  }

  FOG_INLINE unsigned vertex(double* x, double* y)
  {
    if (vCur == vEnd) return Path::CmdStop;

    *x = vCur->x;
    *y = vCur->y;

    uint command = vCur->cmd.cmd();
    vCur++;
    return command;
  }

private:
  const Path::Data* d;
  const Path::Vertex* vCur;
  const Path::Vertex* vEnd;
};

It's nothing special, but allows to share path with multiple threads
without problems and removes "changing" problem during iteration. The
Fog API uses implicit sharing and copy-on-write, so I can share one
path or image (or pattern, ...) in multiple threads without copying
them.

So this problem is solved ;-)

--- Ideas...

While implementing rendering across multiple threads I'm considering
this idea (nothing finished, just idea). You have painter (vector
context) and painter operations are not flushed directly, but each job
will be queued to different thread that will prepare it for blitting.
Painter currently contains method flush() and end() that must be used
to finish rendering or to access attached image buffer.

For example this code:

  // Create image and painter context
  Image i(320, 200, Image::FormatPRGB32);
  Painter p(i);

  // Serialize commands
  p.clear(Rgba(0, 0, 0, 0));
  p.fillRect(Rgba(10, 10, 300, 300));
  p.drawText(Point(10, 10), StubAscii8("ABCDEF"), Font());

  // here some things can be drawn or not, flush() ensures it
  p.flush()

should not paint things immediately, but can paint it from different
thread while main thread is serializing work to it.

I can elaborate with it, because all painting methods will serialize
paint commands only into four virtual methods.

BTW: How is easiest way to implement "alternating scanlines"
rendering. I must go to lower layer than rasterizer ? I think that
replacement for function sweep_scanlines() can do the job.

For example here is sweep_scanlines():

        //--------------------------------------------------------------------
        template<class Scanline> bool sweep_scanline(Scanline& sl)
        {
            for(;;)
            {
                if(m_scan_y > m_outline.max_y()) return false;
                sl.reset_spans();
                unsigned num_cells = m_outline.scanline_num_cells(m_scan_y);
                const cell_aa* const* cells =
m_outline.scanline_cells(m_scan_y);
                int cover = 0;

                while(num_cells)
                {
                    const cell_aa* cur_cell = *cells;
                    int x    = cur_cell->x;
                    int area = cur_cell->area;
                    unsigned alpha;

                    cover += cur_cell->cover;

                    //accumulate all cells with the same X
                    while(--num_cells)
                    {
                        cur_cell = *++cells;
                        if(cur_cell->x != x) break;
                        area  += cur_cell->area;
                        cover += cur_cell->cover;
                    }

                    if(area)
                    {
                        alpha = calculate_alpha((cover <<
(poly_subpixel_shift + 1)) - area);
                        if(alpha)
                        {
                            sl.add_cell(x, alpha);
                        }
                        x++;
                    }

                    if(num_cells && cur_cell->x > x)
                    {
                        alpha = calculate_alpha(cover <<
(poly_subpixel_shift + 1));
                        if(alpha)
                        {
                            sl.add_span(x, cur_cell->x - x, alpha);
                        }
                    }
                }

                if(sl.num_spans()) break;
                ++m_scan_y;
            }

            sl.finalize(m_scan_y);
            ++m_scan_y;
            return true;
        }

I must debug it, but it seems that this function is not modifying
anything except some working variables (m_scan_y) and Scanline& sl
instance that will be unique to each thread, so it seems that there is
perfect way how to share rasterizer across threads without any
synchronization (and i think that sharing rasterizer means better
performance)

So, thanks for tips and good comment. On success I will inform here
about my results:)

Cheers
- Petr

2009/5/9 Stephan Assmus <superstippi@...>:

> Hi Petr,
>
> On 2009-05-09 at 12:00:45 [+0200], Petr Kobalíček
> <kobalicek.petr@...> wrote:
>> thanks for detailed reply:) I think that my code is right, because my
>> geometry model is quite different. The x1,y1 positions are first and
>> x2,y2 positions are last, but they are not in region. I think that RECT
>> structure and HREGION from WinAPI should explain this:
>>
>> width = x2 - x1;
>> height = y2 - y1;
>>
>> x2/y2 are not part of rect. Only code I'm not sure is -0.000001, I think
>> that after your explanations it's not necessary, thank you.
>
> Perfect, that's clear then.
>
>> Currently my problem is different. Part of my thesis is multithreading
>> and I don't know how to access scanlines from multiple threads, to be
>> hones I don't know how AGG rasterizer exactly works. My current idea is
>> to sweep_scanlines() from different threads, but this can't be done
>> without locking, so my second idea is rasterizer for each thread and set
>> different clipping for each. This will isolate and optimize access, but
>> the path will be computed in all threads.
>
> I think the best approach depends on your framework. Ie, do you render a
> complete scene and want this to be multi-threaded? Or do you provide a
> graphics API and render one path at a time or whatever the client code that
> uses your lib can trigger to be rendered in one call into your lib? From
> what I understand of what you are doing, it's the later. Then you have the
> following problem: There is of course a slight overhead to managing the
> threads, preparing jobs and triggering the threads to run. If the work to
> be done falls below a certain threshold, it could be beneficial to switch
> between single threaded rendering and multi-threaded rendering depending on
> estimated penalty. However, this again could pose a problem. Assume client
> code uses your library to render text, and enters your library for each
> glyph in a text run. The glyphs are small and each time you may estimate
> that it's better to stay single threaded, and effectively the whole glyph
> run is rendered in a single thread as a result. To work around this, your
> library could encourage "bulk rendering", where the client code schedules
> more data to be rendered in one API call.
>
> Anyways, in the background, I believe one "pipeline" per thread would be
> best, ie including rasterizer, renderer, scanline storage, everything that
> changes during rendering. And you need to be careful! For example, the
> agg::path_storage has the iterator implementation built-in, IAW iterating
> over a path storage is _not_ a read-only operation! So you should extract
> that from the actual path storage, so that you can have one instance of a
> path storage, but one iterator instance in each thread to iterate the same
> path data.
>
> Finally I would estimate that alternating scanlines based rendering should
> yield the best balanceing effect. As you have already your own renderer
> code where you iterate over scanlines, I would extend the AGG rasterizer
> interface to be able to provide a "skip" value, ie the increment to be used
> when iterating over scanlines during rasterizing. And the same for your
> rendering implementation.
>
>
>> BTW: The out of bounds problem is probably gone. I think I fixed it in
>> BlitJit, but I'm not sure where exactly:)
>
> Cool.
>
> Best regards,
> -Stephan
>
> ------------------------------------------------------------------------------
> The NEW KODAK i700 Series Scanners deliver under ANY circumstances! Your
> production scanning environment may not be a perfect world - but thanks to
> Kodak, there's a perfect scanner to get the job done! With the NEW KODAK i700
> Series Scanner you'll get full speed at 300 dpi even with all image
> processing features enabled. http://p.sf.net/sfu/kodak-com
> _______________________________________________
> Vector-agg-general mailing list
> Vector-agg-general@...
> https://lists.sourceforge.net/lists/listinfo/vector-agg-general
>

------------------------------------------------------------------------------
The NEW KODAK i700 Series Scanners deliver under ANY circumstances! Your
production scanning environment may not be a perfect world - but thanks to
Kodak, there's a perfect scanner to get the job done! With the NEW KODAK i700
Series Scanner you'll get full speed at 300 dpi even with all image
processing features enabled. http://p.sf.net/sfu/kodak-com
_______________________________________________
Vector-agg-general mailing list
Vector-agg-general@...
https://lists.sourceforge.net/lists/listinfo/vector-agg-general