Bug 150049 - Add a CGContextStateSaver and use it
Summary: Add a CGContextStateSaver and use it
Status: RESOLVED FIXED
Alias: None
Product: WebKit
Classification: Unclassified
Component: New Bugs (show other bugs)
Version: WebKit Nightly Build
Hardware: Unspecified Unspecified
: P2 Normal
Assignee: Simon Fraser (smfr)
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2015-10-12 14:45 PDT by Simon Fraser (smfr)
Modified: 2015-10-14 10:39 PDT (History)
3 users (show)

See Also:


Attachments
Patch (13.08 KB, patch)
2015-10-12 14:51 PDT, Simon Fraser (smfr)
thorton: review+
simon.fraser: commit-queue+
Details | Formatted Diff | Diff

Note You need to log in before you can comment on or make changes to this bug.
Description Simon Fraser (smfr) 2015-10-12 14:45:50 PDT
Add a CGContextStateSaver and use it
Comment 1 Simon Fraser (smfr) 2015-10-12 14:51:18 PDT
Created attachment 262924 [details]
Patch
Comment 2 Myles C. Maxfield 2015-10-12 15:03:46 PDT
Why didn't you make GraphicsContextStateSaver use this internally?
Comment 3 Simon Fraser (smfr) 2015-10-12 15:35:26 PDT
(In reply to comment #2)
> Why didn't you make GraphicsContextStateSaver use this internally?

Because that saves GraphicsContext, not CGContextState.
Comment 4 Myles C. Maxfield 2015-10-12 15:36:55 PDT
(In reply to comment #3)
> (In reply to comment #2)
> > Why didn't you make GraphicsContextStateSaver use this internally?
> 
> Because that saves GraphicsContext, not CGContextState.

Surely saving a GraphicsContext involves saving CGContextState...
Comment 5 Simon Fraser (smfr) 2015-10-12 15:37:09 PDT
https://trac.webkit.org/r190894
Comment 6 Said Abou-Hallawa 2015-10-13 09:23:22 PDT
Comment on attachment 262924 [details]
Patch

View in context: https://bugs.webkit.org/attachment.cgi?id=262924&action=review

> Source/WebCore/platform/graphics/cg/GraphicsContextCG.h:86
> +};

Can't we write this class like this:

template <class T, void (*fnSave)(T), void (*fnRestore)(T)>
class ContextStateSaver
{
public:
    ContextStateSaver(T context, bool saveAndRestore = true)
        : m_context(context)
        , m_saveAndRestore(saveAndRestore)
    {
        if (m_saveAndRestore)
            fnSave(m_context);
    }

    ~ContextStateSaver()
    {
        if (m_saveAndRestore)
            fnRestore(m_context);
    }

    void save()
    {
        ASSERT(!m_saveAndRestore);
        fnRestore(m_context);
        m_saveAndRestore = true;
    }

    void restore()
    {
        ASSERT(m_saveAndRestore);
        fnRestore(m_context);
        m_saveAndRestore = false;
    }

private:
    T m_context;
    bool m_saveAndRestore;
};

typedef ContextStateSaver<CGContextRef, CGContextSaveGState, CGContextRestoreGState> CGContextStateSaver;

So if we want to do the same thing for any other port, we do not have to duplicate the code for the third time. Also GraphicsContextStateSaver can be a specialized case of this template, if we 

Implement these functions

    void GraphicsContextSaveGState(GraphicsContext& context) { context.save(); }
    void GraphicsContextRestoreGState(GraphicsContext& context) { context.restore(); }

And define 

    typedef ContextStateSaver<GraphicsContext&, GraphicsContextSaveGState, GraphicsContextRestoreGState> GraphicsContextStateSaver;
Comment 7 Myles C. Maxfield 2015-10-13 11:11:04 PDT
Comment on attachment 262924 [details]
Patch

View in context: https://bugs.webkit.org/attachment.cgi?id=262924&action=review

>> Source/WebCore/platform/graphics/cg/GraphicsContextCG.h:86
>> +};
> 
> Can't we write this class like this:
> 
> template <class T, void (*fnSave)(T), void (*fnRestore)(T)>
> class ContextStateSaver
> {
> public:
>     ContextStateSaver(T context, bool saveAndRestore = true)
>         : m_context(context)
>         , m_saveAndRestore(saveAndRestore)
>     {
>         if (m_saveAndRestore)
>             fnSave(m_context);
>     }
> 
>     ~ContextStateSaver()
>     {
>         if (m_saveAndRestore)
>             fnRestore(m_context);
>     }
> 
>     void save()
>     {
>         ASSERT(!m_saveAndRestore);
>         fnRestore(m_context);
>         m_saveAndRestore = true;
>     }
> 
>     void restore()
>     {
>         ASSERT(m_saveAndRestore);
>         fnRestore(m_context);
>         m_saveAndRestore = false;
>     }
> 
> private:
>     T m_context;
>     bool m_saveAndRestore;
> };
> 
> typedef ContextStateSaver<CGContextRef, CGContextSaveGState, CGContextRestoreGState> CGContextStateSaver;
> 
> So if we want to do the same thing for any other port, we do not have to duplicate the code for the third time. Also GraphicsContextStateSaver can be a specialized case of this template, if we 
> 
> Implement these functions
> 
>     void GraphicsContextSaveGState(GraphicsContext& context) { context.save(); }
>     void GraphicsContextRestoreGState(GraphicsContext& context) { context.restore(); }
> 
> And define 
> 
>     typedef ContextStateSaver<GraphicsContext&, GraphicsContextSaveGState, GraphicsContextRestoreGState> GraphicsContextStateSaver;

I think that currently this template stuff is more harm than good. However, if we add a single more class of this form, I would agree with Said.
Comment 8 Simon Fraser (smfr) 2015-10-13 11:17:01 PDT
Comment on attachment 262924 [details]
Patch

I agree with Myles.
Comment 9 Darin Adler 2015-10-14 09:39:41 PDT
Comment on attachment 262924 [details]
Patch

View in context: https://bugs.webkit.org/attachment.cgi?id=262924&action=review

A couple thoughts about refinement.

> Source/WebCore/platform/graphics/cg/GraphicsContextCG.h:53
> +class CGContextStateSaver {

Nothing currently makes this class non-movable and non-copyable, and it should be. Could use the non-copyable macro or some other technique.

> Source/WebCore/platform/graphics/cg/GraphicsContextCG.h:55
> +    CGContextStateSaver(CGContextRef context, bool saveAndRestore = true)

Should mark constructor this explicit.

I don’t think using a boolean to tell the thing whether to save or not is a clear design. Can we do better?
Comment 10 Said Abou-Hallawa 2015-10-14 09:53:40 PDT
Comment on attachment 262924 [details]
Patch

View in context: https://bugs.webkit.org/attachment.cgi?id=262924&action=review

>> Source/WebCore/platform/graphics/cg/GraphicsContextCG.h:55
>> +    CGContextStateSaver(CGContextRef context, bool saveAndRestore = true)
> 
> Should mark constructor this explicit.
> 
> I don’t think using a boolean to tell the thing whether to save or not is a clear design. Can we do better?

I would suggest removing the boolean saveAndRestore from the constructor argument list but keeping m_saveAndRestore and initialize it always with true. I can't think of any scenario that I will do: CGContextStateSaver saver(context, false) and after that I call saver.save(). So I would suggest also removing the function save() from this class but keeping the restore() function.  The constructor will always save the context state and the destructor will restore it unless restore() was called before.
Comment 11 Simon Fraser (smfr) 2015-10-14 10:39:55 PDT
(In reply to comment #10)
> Comment on attachment 262924 [details]
> Patch
> 
> View in context:
> https://bugs.webkit.org/attachment.cgi?id=262924&action=review
> 
> >> Source/WebCore/platform/graphics/cg/GraphicsContextCG.h:55
> >> +    CGContextStateSaver(CGContextRef context, bool saveAndRestore = true)
> > 
> > Should mark constructor this explicit.

Will fix.

> > I don’t think using a boolean to tell the thing whether to save or not is a clear design. Can we do better?

This actually happens a lot:

    bool drawOwnShadow = !isAcceleratedContext() && hasBlurredShadow() && !m_state.shadowsIgnoreTransforms; // Don't use ShadowBlur for canvas yet.
    CGContextStateSaver stateSaver(context, drawOwnShadow);
    if (drawOwnShadow) {

I did start with an enum argument, but that made these constructors all more ugly.

> I would suggest removing the boolean saveAndRestore from the constructor
> argument list but keeping m_saveAndRestore and initialize it always with
> true. I can't think of any scenario that I will do: CGContextStateSaver
> saver(context, false) and after that I call saver.save(). So I would suggest
> also removing the function save() from this class but keeping the restore()
> function.  The constructor will always save the context state and the
> destructor will restore it unless restore() was called before.

That sounds reasonable.