Problems with OutputDevice

Chris Sherlock chris.sherlock79 at gmail.com
Sun Apr 11 23:23:11 UTC 2021


On 1 Jan 2021, at 4:44 am, Chris Sherlock <chris.sherlock79 at gmail.com> wrote:
> 
> Parallel task: decouple metafile recording from drawing functionality
> 
> My plan for this is to do the following:
> 
> 1. I create a RenderContext2 class which OutputDevice derives from
> 2. Carefully and gradually move the drawing code into parallel drawing functions in RenderContext2. I keep the metafile recording functionality in OutputDevice and have it call on the parallel RenderContext drawing function.
> 
> What this essentially does is implement a decorator pattern. This can be done gradually. In fact, when I looked at the code, the sequence of drawing functions look like they should be:
> 
> a. state and mapping functions - beginning with push and pop, then the functions that set line colour, etc.
> b. clipping functions
> c. line and pixel drawing functions
> d. Ellipse, arc, pie and chord drawing functions
> e. Rectangle drawing functions
> f. Polygon drawing functions
> g. Polypolygon drawing functions
> h. Polyline drawing functions (this will be more tricky)
> 
> The rest will be trickier to migrate and require a fair amount of refactoring to seperate them. I fully expect a bit of code duplication for a short time whilst the metafile code is seperated from the drawing code.

This is a followup to my email from the very beginning of the year.
It turns out that moving code into SalGraphics, whilst I still think 
is a very good idea, is quite a bit more tricky than I had
anticipated. Therefore I have decided that the task of decoupling
metafile recording from actual drawing would be the first concrete
step I need to undertake. 

To decouple metafile recording from actual rendering from
OutputDevice, I have created a new class, RenderContext2. This class 
focuses on only rendering output and deliberately does not make any
attempt to do any metafile recording. Instead, I have made
OutputDevice a wrapper class that does metafile recording (if the
mpMetafile instance has been instantiated) and it then calls up the
corresponding RenderContext2 function to do the rendering. 

Note: I have called it RenderContext2 because there is a
vcl::RenderContext in the codebase already, which is actually just a 
typedef to OutputDevice. 

Drawing lines is a good example. The line color is set as a distinct
rendering operation, but also is recorded in a metafile as an
action. Thus, the rendering functionality is now in RenderContext2:

void RenderContext2::SetLineColor()
{
    if (mbLineColor)
    {
        mbInitLineColor = true;
        mbLineColor = false;
        maLineColor = COL_TRANSPARENT;
    }

    if (mpAlphaVDev)
        mpAlphaVDev->SetLineColor();
}

And the recording is done in OutputDevice:

void OutputDevice::SetLineColor()
{
    if (mpMetaFile)
        mpMetaFile->AddAction(new MetaLineColorAction(Color(), false));

    RenderContext2::SetLineColor();
}

As you can see, the metafile is recorded, and then OutputDevice
immediately calls upon the rendering class, RenderContext2. 

I have done this to reduce the amount of code churn in the codebase.
OutputDevice works *exactly* the same as it always has, and we don’t 
need to go through the code and replace all instances of
OutputDevice with RenderContext2. 

I have endeavoured to reduce the amount of code that I have changed,
but this has not always been entirely possible. The following are
where I have diverted from the existing code:

- there was much code duplication in determining colors and fonts
depending on what DrawMode was set to. Thus I have introduced a
number of ancillary functions around the drawing mode - these can be
found in vcl/source/rendercontext/drawmode.cxx

- in order to migrate the EPS drawing code into RenderContext2, I
had to introduce Animation::IsLoopTerminated() and I had to migrate
the drawing functionality back into OutputDevice::DrawAnimation().
In order to get this fully working, unfortunately we have an
GetOutDevType() check in ImplAnimView::draw() and
ImplAnimView::drawToPos(), so I have also had to migrate this
functionality into RenderContext2 (for now), thus RenderContext2
also handles drawing the animation view. 

- gradient code migration was especially tricky. The code for this
had metafile processing strongly intertwined into the rendering
code. Luckily quite a bit of it was separated already, which I had
done some time ago when I did some major function extractions in
this part of the codebase. 

- the bitmap drawing code signatures were changed. Currently
DrawBitmap() and DrawBitmapEx() uses a MetaActionType (which used a
default value for the parameter in the case when it is called
directly anyway) to determine scaling and cropping. I have removed
this unnecessary parameter in both the functions in order to migrate
the rendering code to RenderContext2. 

One are of the code that needs to be improved is there are now quite
a lot of protected member variables. Eventually I hope to remove the
need a lot of them, or place them behind protected getters and
setters. 

Any feedback would be appreciated!

The repo can be found here:

https://github.com/chrissherlock/libreoffice-experimental/tree/RenderContext2

I have tagged the repo:

https://github.com/chrissherlock/libreoffice-experimental/releases/tag/rendercontext2

Chris


More information about the LibreOffice mailing list