9

Refer the below-attached images, how to paint inside the black edges without using masking [Ex- If we want to draw inside the cap, perfectly we need to fill the cap using brush tool].

We have already implemented bucket tool using flood fill algorithm.

Refer [IMAGE 4] this type of drawing is wrong, So we need to draw only inside the black edges refer [IMAGE 2] and [IMAGE 3].

We need to implement this in iOS SDK,

I have already tried these two ways -

  1. I have recorded the coordinates of a specific area, it's lagging too much and the memory leak is there. it's not a good way

  2. I have to fill specific area using bucket tool (flood fill algorithm) in the background, then try to match the color of the touch point. It also not gives the perfect output.

[IMAGE 1]

enter image description here

[IMAGE 2]

enter image description here

[IMAGE 3]

enter image description here

[IMAGE 4]

enter image description here

Ramkumar Paulraj
  • 1,841
  • 2
  • 20
  • 40
  • Which part of this functionality is OpenGL, and at what level of the stack are you trying to accomplish it? Is this user interaction driven? Is the content arbitrary or fixed? Drawing fills for 2D illustration and whatnot are not well suited to raw OpenGL work. Are you using GL for the output render alone and managing the drawing and bitmap at a higher level? Or something else. Stephen's answer below is appropriate for a more conventional (and interactive) approach here. – Ben Zotto Jun 28 '18 at 21:21
  • @BenZotto https://developer.apple.com/library/archive/samplecode/GLPaint/Introduction/Intro.html I am using apple gl paint and I have implemented a lot of sketches. Now I need to implement like above functionality. – Ramkumar Paulraj Jun 29 '18 at 05:54
  • In below answer, how to get the bezier path of this image outlines(black edges). Can you give any idea for that – Ramkumar Paulraj Jun 29 '18 at 06:14
  • 1
    Please note that `Open GL ES is deprecated in iOS 12`, see https://developer.apple.com/ios/whats-new/. Also like @BenZotto mentioned, OpenGL might be more complicated and not so well suited for this requirements. You might want perhaps to take a look at: https://www.raywenderlich.com/87899/make-simple-drawing-app-uikit-swift. – Stephan Schlecht Jun 29 '18 at 07:42
  • @ram, Can you please share the sample code if it worked!! Thanks in advance!!! – Paresh Patel Jul 05 '19 at 09:33

1 Answers1

1

There are several possible approaches. For example, a pure vector-based approach could be used. However, the conversion of the individual drawings is likely to be quite time-consuming.

A more pragmatic approach could be as follows:

Approach

You could define a list of Bézier paths (UIBezierPath), each describing a region that can be drawn independently.

The Bézier paths would serve two purposes:

  • determine in which area a stroke has begun
  • limit the user drawing to this area

How to draw perfectly inside the black lines

If you make the background of the given drawing transparent, you can create the user's drawing operations with clipping below the black line drawing. This means that the Bézier path does not have to be perfect, it just has to be sufficiently accurate.

Layers/Clipping

The steps would be then:

  • show initially the given drawing
  • determine the Bézier path where the user starts drawing in
  • clip to determined Bézier path
  • draw user strokes with clipping
  • draw on top the given transparaent line drawing

Quick Demo

Here is a short demo to show what it might look like when the user starts drawing within a clipped Bézier path using the described drawing order (see also code snippets below):

quick demo

Code Snippets

Example bezier path

bezierPath = UIBezierPath()
bezierPath.move(to: CGPoint(x: 55.94, y: 60.19))
bezierPath.addCurve(to: CGPoint(x: 50.32, y: 94.09), controlPoint1: CGPoint(x: 56.92, y: 59.69), controlPoint2: CGPoint(x: 55.25, y: 73.99))
bezierPath.addCurve(to: CGPoint(x: 48.81, y: 115.93), controlPoint1: CGPoint(x: 46.9, y: 107.97), controlPoint2: CGPoint(x: 49.09, y: 115.33))
bezierPath.addCurve(to: CGPoint(x: 74.36, y: 146.45), controlPoint1: CGPoint(x: 50.56, y: 127.4), controlPoint2: CGPoint(x: 60.85, y: 141.06))
bezierPath.addCurve(to: CGPoint(x: 119.11, y: 126.29), controlPoint1: CGPoint(x: 93.52, y: 141.34), controlPoint2: CGPoint(x: 107.61, y: 134.31))
bezierPath.addCurve(to: CGPoint(x: 223.27, y: 93.88), controlPoint1: CGPoint(x: 150.33, y: 112.49), controlPoint2: CGPoint(x: 183.59, y: 100.44))
bezierPath.addCurve(to: CGPoint(x: 300.41, y: 69.01), controlPoint1: CGPoint(x: 250.68, y: 87.71), controlPoint2: CGPoint(x: 276.22, y: 79.2))
bezierPath.addCurve(to: CGPoint(x: 297.23, y: 59.69), controlPoint1: CGPoint(x: 301.38, y: 65.82), controlPoint2: CGPoint(x: 300.22, y: 62.01))
bezierPath.addCurve(to: CGPoint(x: 212.85, y: 38.3), controlPoint1: CGPoint(x: 290.2, y: 62.67), controlPoint2: CGPoint(x: 256.59, y: 52.37))
bezierPath.addCurve(to: CGPoint(x: 137.05, y: 37.2), controlPoint1: CGPoint(x: 191.43, y: 31.36), controlPoint2: CGPoint(x: 158.26, y: 29.92))
bezierPath.addCurve(to: CGPoint(x: 100.74, y: 48.08), controlPoint1: CGPoint(x: 126.18, y: 40.93), controlPoint2: CGPoint(x: 111.78, y: 44.75))
bezierPath.addCurve(to: CGPoint(x: 55.94, y: 60.19), controlPoint1: CGPoint(x: 86.15, y: 52.48), controlPoint2: CGPoint(x: 70.56, y: 56.69))
bezierPath.close()

Clip to path

self.currentBezierPath.addClip()

Determining if a touch start inside a closed bezier path

Assuming that self is a custom view, one could write something like:

override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
    ...
    let point = touch.location(in: self)
    ...
    let inside = bezierPath.contains(point)
    ...

Draw transparaent drawing

First one has to make the background of the drawing transparent. Then one could store the drawing in a format that supports transparaency, e.g. .png.

context.saveGState()
context.scaleBy(x: 1, y: -1)
context.translateBy(x: 0, y: -oOV7Impng.size.height)
context.draw(oOV7Impng.cgImage!, in: CGRect(x: 0, y: 0, width: oOV7Impng.size.width, height: oOV7Impng.size.height))
context.restoreGState()

If there are many drawings in the app, one could think about making the background of the image programmatically transparent (e.g. by making white pixels transparent). Depends on the use case.

Stephan Schlecht
  • 26,556
  • 1
  • 33
  • 47
  • I need to implement this in OpenGL-ES for the variety of sketches. i'll try this also but how to get the bezier path of this image??? – Ramkumar Paulraj Jun 29 '18 at 05:45
  • 1
    One possibility: usage of vector graphics program that can place the line drawing image in the background, draw Bézier curves manually on top and export the drawing as .SVG. In the SVG you get ` – Stephan Schlecht Jun 29 '18 at 07:37
  • Hi @StephanSchlecht, I am facing some issue, can you help me? https://stackoverflow.com/questions/52235857/while-trying-to-fill-color-to-uiimageview-using-uiimagescanlinefloodfill-library – Anilkumar iOS - ReactNative Sep 08 '18 at 17:32
  • Unfortunately I have never worked with the Github project referenced by you. At first sight it seems to offer an example also (see screenshots in the readme). Does the sample project work correctly if it is used unchanged? – Stephan Schlecht Sep 09 '18 at 18:40
  • @StephanSchlecht : Can you please share the sample code ? I am also facing the same issue – Nirav Nov 26 '18 at 09:38
  • I don't have a complete polished drawing sample code. But you can perhaps take a look at this: https://www.raywenderlich.com/1934-how-to-make-a-simple-drawing-app-with-uikit-and-swift. Or do you have a very specific detail problem? – Stephan Schlecht Nov 27 '18 at 19:18
  • Hi, Can you please share the sample code?I am also facing the same issue – make Dec 20 '18 at 10:02
  • Hi, can you please share sample code? as of I am facing the same issue. Thanks In Advance – Paresh Patel Jul 05 '19 at 07:21
  • Hi, back then I did a short proof of concept and not a polished complete example. However, the important code snippets are included in the answer. If there are specific detail problems during implementation, you can ask a question here on Stackoverflow, usually you get a good answer. – Stephan Schlecht Jul 09 '19 at 18:55
  • @StephanSchlecht I have applied context?.setBlendMode(.sourceIn), but it took too much time, that first filled path draw and again new drawing path is drawing inside the line. – Paresh Patel Nov 29 '19 at 08:24