Analysis of Drawing Principle of Inkpad

Inkpad is a very good iPad vector drawing software, you can't forget it as soon as you see it. My feeling is that "all the mountains are small at a glance" and "see you very late", so that the TouchVG I wrote is "small in size". It is necessary to study the code of this software and crack the mystery of its high-performance drawing.

If you read this article and feel that you don’t understand something, you can ask it for communication. If this article can help you a little bit, it’s OK. I’m also learning, and my intention is not to write beautiful articles.

1. Touch interactive drawing

Of course, interactive drawing must first look at the touch response mechanism, first look at a sequence diagram:

Touch Draw Response Sequence Diagram

WDCanvas is the main view that represents the drawing canvas and responds directly to the original touch events (touchesBegan, touchesMoved, etc.), without using the popular gesture recognizers such as double-click, rotation, long-press, etc., strange?
I guess Inkpad does not use gesture recognizer for two reasons: (1) Gesture recognizer uses delayed recognition technology to judge gesture ambiguity, there is a delay of hundreds of milliseconds, which will affect the feeling of fast response of drawing; (2) Inkpad is Standalone program, all interface is its own and does not need to coexist with various interface components with gesture recognizers (e.g. drawing in scroll views, e-book pages).     


Pass the touch event to the current command through "[[WDToolManager sharedInstance].activeTool touchesBegan:touches withEvent:event inCanvas:self]" in WDCanvas, which of course uses single instance mode and command mode .  

 
In the touchesBegan function of WDCanvas, the touch start event is not sent to the interactive command WDTool immediately, but is delayed until touchesMoved. I guess the author wanted to distinguish between normal tap (Tap) and drag (Pan), but it was not thorough in the implementation: in the following snippet of the touchesEnded function, touchesBegan is also sent without movement. I think you should check the moving distance in touchesMoved to determine if it is moved, instead of simply switching to the moved state.

if (!controlGesture_ && [self canSendTouchToActiveTool]) {
    if (!moved_) {
        [[WDToolManager sharedInstance].activeTool touchesBegan:touches withEvent:event inCanvas:self];
    }
    [[WDToolManager sharedInstance].activeTool touchesEnded:touches withEvent:event inCanvas:self];
}

 

Set the activePath of the WDCanvas to null (self.drawingController.activePath = nil) when starting to touch, not currently a pen command (WDPenTool). The current path (activePath) is used to memorize the current graphics path of the pen command. Defining activePath in WDDrawingController instead of WDPenTool is convenient for checking and using in multiple classes, otherwise it is too troublesome to obtain data from a specific command class .

The touch response function of the interactive command class uses the template method pattern shown below. The specific command class rewrites the beginWithEvent, moveWithEvent, and endWithEvent functions to implement specific drawing logic.

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event inCanvas:(WDCanvas *)canvas
{
    //.....
    WDEvent *genericEvent = [self genericEventForTouch:primaryTouch_ inCanvas:canvas];
    [self beginWithEvent:genericEvent inCanvas:canvas];
    self.previousEvent = genericEvent;
    //......
}
    
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event inCanvas:(WDCanvas *)canvas
{
    //......
    [self moveWithEvent:genericEvent inCanvas:canvas];
    self.previousEvent = genericEvent;
    //......
}
    
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event inCanvas:(WDCanvas *)canvas
{
    [self endWithEvent:genericEvent inCanvas:canvas];
}
    
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event inCanvas:(WDCanvas *)canvas
{
    [self touchesEnded:touches withEvent:event inCanvas:canvas];
}

Let's talk about one of the highlights of Inkpad : use OpenGL ES high-speed drawing in dynamic drag drawing!

In a drawing command (such as WDShapeTool, which can draw various shapes), set the shapeUnderConstruction property of WDCanvas (shape to be constructed, WDPath object), and pass the current temporary shape (construct a WDPath object each time) to WDCanvas. The setShapeUnderConstruction function of WDCanvas will call the immediate drawing function of the WDSelectionView member, which uses OpenGL ES 1.1 to draw various dynamic graphics. Using OpenGL ES, dragging thousands of graphics can also echo without delay, which is the highlight of Inkpad.

(I think just extracting this OpenGL ES rendering vector graphics and text class is enough to realize a very dazzling animation display software)

When Inkpad uses OpenGL ES 1.1 to draw dynamic graphics, the optimized display methods used are: (1) single-point line width, (2) ignore dashed line style, (3) no fill, (4) cache graphics outline path. Inkpad works hard on display performance.

 

After the touch operation is completed, it is necessary to submit static graphics to the document: (1) The drawing command submits a new graphics object ([canvas.drawing addObject:path]) to the current layer (WDLayer) in endWithEvent; (2) WDDrawingController triggers the WDCanvas view (3) In the drawRect: function of WDCanvas, traverse each graphic, call the renderInContext function of each object to display all graphics, and its essence is to display the path of the graphic to the current CGContext (seemingly ordinary).

Compared with the rendering of dynamic graphics, OpenGL ES is not used to submit static graphics, but the simplest CGContext display method is used, and all graphics are redrawn, and advanced rendering methods such as CALayer are not used. This raises the question, will a large number of graphics be too slow? I haven't done a performance analysis experiment yet, and I preliminarily estimate that the display optimization method used by Inkpad is: (1) cache objects such as CGPath outline paths and fill paths; (2) avoid geometric calculations and object reconstruction.

Inkpad is already so good, we need to re-evaluate some recent display optimization experiments done by TouchVG to see if these calculations are necessary: ​​(1) Asynchronous drawing in GCD, using foreground graphics list and background graphics list to avoid multi-thread conflicts; ( 2) Draw on CALayer in advance, and only texture in the main view (using renderInContext, using GPU texture); (3) One CALayer for each layer of graphics to avoid redrawing all graphics; (4) Whether it is necessary to use OpenGL ES 2.0 to draw Static graphics?

 

2. Related core classes

Inkpad drawing core class

1. WDCanvas: main drawing view, including ruler view, selection set rendering view, delete prompt line view, including temporary graphics objects used by interactive command classes.

2. WDCanvasController: The drawing interface operation class is responsible for the management and operation distribution of various UI controls.

3. WDDrawingController: Responsible for adding, deleting, modifying and checking logic of various graphics. WDCanvasController is a shell function and WDDrawingController is a kernel function.

4. WDDrawing: Graphic document class, which accommodates all drawing contents.

5. WDLayer: A layer that contains multiple graphic elements WDElement.

6. WDStylable: The base class for strokeable and filled graphics.

7. WDAbstractPath: Base class for graphics with vector paths.

8. WDPath: A graphic class that can specify a vector path in the shape of an arrow at the end of a line.

9. WDCompoundPath: Graphical class for compound paths.

 

10. WDTool: Command base class.

11. WDShapeTool: Commands for adding geometric shapes, which can draw rectangles, ellipses, stars, polygons, straight line segments, and spirals. The drawing tools for these different shapes are constructed in WDToolManager.

12. WDPenTool: Bezier curve drawing tool.

13. WDFreehandTool: Freehand drawing smooth curve tool.

 

For the Core.Model class in the blue part, the geometric model class is mainly designed based on the vector path, which does not seem to contain the feature data of the graph (that is, the subsequent editing maintains the shape feature).

I think we can expand the types of graphics and interactive command tools based on Inkpad, and make some industry-related software. If you are interested, join the discussion.

These two UML diagrams are drawn with EA, and InkpadUml.xmi can be imported into other UML modeling tools.

 

I'm a little tired after writing for a long time, so let's write this first. The hope is to take Inkpad thoroughly and create a new framework or software by combining TouchVG and TouchVAnimation :)

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=324387004&siteId=291194637