Algorithm for a smudge tool?

2019-07-16 23:46发布

问题:

I'm trying to implement a smudge tool like one you would find in Gimp or Photoshop. I've tried lots of variations but they all have problems. The basic method I've tried for smudging from position P1 to P2 on an image is:

  1. Copy a rectangle the size of the current brush from P1.
  2. Draw this rectangle at P2 with a low opacity.

It looks fine and smudges as expected but the main problems I'm having is that the smudge seems to make things darker. Especially when using a small brush spacing, repeated smudging will turn the area to black. Any suggestions on what I'm doing wrong or some standard algorithms I can look at? I've had a look at the Gimp source but it's very hard to follow.

If it matters, I'm working on a mobile device (Android, Java) so something fast would be preferable.

回答1:

I suspect that you algorithm constantly renders your rectangles on top of each other, likely averaging their values to get the new color value below. Depending on how the particulars of you program is set up, I would wager that the RGB values are going to 0x000000 due to this repeated averaging. This also explains why smaller step distances blacken faster, as more steps equates to more averaging. I had a similar problem with a color changer that was repeatedly blurred going black on the edges.

A good tutorial on creating your own smudge tool can be found here: http://losingfight.com/blog/2007/09/04/how-to-implement-smudge-and-stamp-tools/

Unfortunately, the examples are all in Objective-C but the text explains fairly well what is going on.

Hope this helps.



回答2:

Your problem is that Android uses premultiplied alpha for bitmaps. The alpha channel is multiplied in with the RGB to save time when compositing.

Here's an example of the problem. Both of these brushes are exactly the same, other than that the bottom is being rendered at 5% alpha.

How this enters when creating a smudge tool is that the smudge is rendering very low alpha pixels (usually from a masking effect, or simply from the original bitmap itself). When many of these bitmaps are rendered on top of each other (as in a smudge) it turns grey.

I came across this error when building a smudge tool as well for a photo editing application I'm writing for Android and I sadly haven't found a good solution.

The alpha and rgb channels need to be rendered separately in a opaque bitmaps and then somehow recombined. Unfortunately there seems no good way of doing this in Android at the moment.

If you come up with a solution, I'd love to hear it. Hopefully this will give you some information about the cause of the problem and maybe some inspiration for a fix.



回答3:

pretty old topic but I found myself having the same problem (smudge going to black). I was also following this tutorial :

http://losingfight.com/blog/2007/09/05/how-to-implement-smudge-and-stamp-tools/

As GuyNoir mentions, the problem is pre-multiplied alpha for bitmaps in android where color values stored in memory are already multiplied by the alpha value. Bluring suffers from the same issue in the form of a dark halo by the way.

There is no way around this with the canvas API since:

"Only pre-multiplied bitmaps may be drawn by the view system or Canvas."

So we have to extract the pixels via Bitmap#getPixels() and do the blending manually. This definitely has a performance impact but it's not too hard to implement.

so for each pixel:

alpha = ( SourceAlpha * strength ) + ( DestinationAlpha * ( 1 - strength ) )

color = ( SourceColor * strength ) + ( DestinationColor * ( 1 - strength ) )

The destination is the image to smudge and the source is the brush. The strength represents how hard you're pressing on the smudge brush.