Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Gradient Brushes #542

Merged
merged 54 commits into from
May 14, 2018
Merged

Gradient Brushes #542

merged 54 commits into from
May 14, 2018

Conversation

jongleur1983
Copy link
Contributor

@jongleur1983 jongleur1983 commented Apr 19, 2018

Prerequisites

  • I have written a descriptive pull-request title
  • I have verified that there are no overlapping pull-requests open
  • I have verified that I am following matches the existing coding patterns and practice as demonstrated in the repository. These follow strict Stylecop rules 👮.
  • I have provided test coverage for my change (where applicable)

Description

Implementing Gradient Brushes:

  • Linear Gradient
  • Radial Gradient
  • Elliptical Gradient (supports size, height/length ratios and rotation of the ellipse)

Color Repetition Modes

  • DontFill: fill between start and end of the gradient, don't touch anything else.
  • None: fill between start and end of the gradient. Beyond those borders, stay with the same color
  • Repeat: repeat the gradient endlessly
  • Reflect: same as repeat, but each other repetition is inverted (=reflected).

What's missing

  • proper API documentation

What I'd like to add in future PRs:

  • gradienting modes (e.g. HSV-gradienting. According to @JimBobSquarePants he might implement the corresponding formulas/algorithms somewhere else, so it might just have to be utilized here.
  • performance improvements at least for the common cases
    • Linear Gradients: vertical lin gradients: each row is unicolored, so we have to calculate the color only once per row. Horizontal gradients: not sure how best to do it, but here each row is the same bunch of pixels, so we can cache it between the Apply calls.
    • Radial Gradients: from the center point towards the edges, each row is mirrored, so only half of the colors have to be calculated.
  • Use different BrushApplicators to speed up the special cases (e.g. Elliptic Brush with AxisRatio 1 is a RadialBrush)
  • Another gradient Brush: StarShapedGradient: Consider a Polygon and a point inside, where the direct connection between that point and any position along the outline does not intersect the outline before. The class of these polygons contains all convex polygons, simple stars and more. Given a Star-Shaped polygon and one point inside where the condition described above holds, it's possible to color each point in an unambiguous way referring to a gradient description. ColorRepetitionMode.Reflect and ColorRepetitionMode.Repeat would not be supported here as that get's ambiguous outside the polygon.

@CLAassistant
Copy link

CLAassistant commented Apr 19, 2018

CLA assistant check
All committers have signed the CLA.

@tocsoft tocsoft self-requested a review April 19, 2018 18:25
@tocsoft tocsoft self-assigned this Apr 19, 2018
@jongleur1983 jongleur1983 force-pushed the GradientBrush branch 6 times, most recently from d284b76 to bc6e35c Compare April 19, 2018 19:40
@jongleur1983 jongleur1983 changed the title #86: started to work on Gradient Brushes: Linear gradient brush, #86: Linear gradient brush Apr 19, 2018

private readonly Point end;

private readonly Tuple<float, TPixel>[] colorStops;
Copy link
Member

@antonfirsov antonfirsov Apr 19, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suggest to define a small private struct instead of Tuple<float, TPixel>. It provides:

  • Better readability
  • Better memory locality (Tuple<>-s are classes on the heap)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good point. Thanks. Except that the struct can't be private as it's used as a parameter in the Brush constructor. Therefore I'll use a public nested struct instead.

Copy link
Member

@tocsoft tocsoft left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the reason you are getting a black screen is because of a bug in the base Apply method.

the test if (this.Options.BlendPercentage < 1) is incorrect and instead should read if (this.Options.BlendPercentage <= 1) i.e. less than or equal to instead of less than

Assert.Equal(Rgba32.Red, sourcePixels[0, 0]);
Assert.Equal(Rgba32.Red, sourcePixels[9, 9]);
Assert.Equal(Rgba32.Red, sourcePixels[199, 149]);
Assert.Equal(Rgba32.Red, sourcePixels[500, 500]);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this need to be 499 not 500 as its one pixel wider than the buffer.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thanks, you're right. fixed.

@tocsoft tocsoft changed the title #86: Linear gradient brush [WIP] Linear gradient brush Apr 19, 2018
@tocsoft
Copy link
Member

tocsoft commented Apr 19, 2018

I've renamed the PR, we will happily keep reviewing it as you make improvements, once your fully happy and want a final review/merge just update the title again and drop the [WIP] prefix

thanks @tocsoft for investigation and finding the bug.
He proposed to just replace < by <= in line 78, but that would only shift the problem to values > 1.
Those values should not be used from a semantic point, but it's not forbidden in a float value.
My fix here keeps the <, but adds an else path that uses the original value from scanline[i]. This adds an else to the code (which technically adds a jump after the then-part), but omits the multiplication.
The simple solution @tocsoft proposed might be faster, but should be preferred if and only if BlendPercentage can be guaranteed to be <=1.
as proposed by @antonfirsov: improving readability and memory locality.
@codecov
Copy link

codecov bot commented Apr 19, 2018

Codecov Report

Merging #542 into master will decrease coverage by 0.4%.
The diff coverage is 100%.

Impacted file tree graph

@@            Coverage Diff             @@
##           master     #542      +/-   ##
==========================================
- Coverage   87.67%   87.26%   -0.41%     
==========================================
  Files         838      840       +2     
  Lines       34684    35107     +423     
  Branches     2533     2553      +20     
==========================================
+ Hits        30410    30637     +227     
- Misses       3524     3719     +195     
- Partials      750      751       +1
Impacted Files Coverage Δ
.../Processing/Drawing/Brushes/LinearGradientBrush.cs 100% <100%> (ø)
...harp.Tests/Drawing/FillLinearGradientBrushTests.cs 100% <100%> (ø)
...wing/Processing/Drawing/Brushes/BrushApplicator.cs 86.95% <100%> (+48.86%) ⬆️
...mats/Generated/Rgba32.PixelOperations.Generated.cs 75% <0%> (-25%) ⬇️
...ats/Generated/PixelOperations{TPixel}.Generated.cs 80% <0%> (-20%) ⬇️
src/ImageSharp/PixelFormats/Bgr24.cs 78.26% <0%> (-19.04%) ⬇️
src/ImageSharp/PixelFormats/Rgb24.cs 75.55% <0%> (-18.89%) ⬇️
src/ImageSharp/PixelFormats/Alpha8.cs 82.5% <0%> (-17.5%) ⬇️
src/ImageSharp/PixelFormats/NormalizedByte2.cs 83.95% <0%> (-16.05%) ⬇️
src/ImageSharp/PixelFormats/NormalizedShort2.cs 82.71% <0%> (-15.82%) ⬇️
... and 23 more

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 07a811c...d29b601. Read the comment docs.

test images don't have to be that big for axial gradients.
It's sufficient to show they're constant across the axis and correct along the axis.
the theory lacks color checks yet that should be added, but it produces output images for visual control
somehow these don't look correct yet, containing hard edges sometimes.
@jongleur1983
Copy link
Contributor Author

@antonfirsov thanks for the changes. I still have 71 failing tests locally. On the first sight those are the same we thought to fail due the Vector2 issue, and: the same tests fail on SixLabors/master, too here.

So I'm fine with merging the branch.

@antonfirsov
Copy link
Member

antonfirsov commented May 13, 2018

Everything looks good now @jongleur1983's local test running issues are unrelated to his PR.

I'm planning to merge this soon, let me know if there are blocker issues we've missed, so I shouldn't!

/// Base class for Gradient brushes
/// </summary>
/// <typeparam name="TPixel">The pixel format</typeparam>
public abstract class AbstractGradientBrush<TPixel> : IBrush<TPixel>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, one naming question mostly to @JimBobSquarePants :
Shouldn't we name this GradientBrushBase or simply GradientBrush to match our current naming patterns?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah this should be GradientBrushBase.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done (and renamed the BrushApplicator as well)

@antonfirsov antonfirsov merged commit dbe2b1b into SixLabors:master May 14, 2018
@antonfirsov
Copy link
Member

Finally merged!
@jongleur1983 thanks for pushing the rename!

antonfirsov pushed a commit to antonfirsov/ImageSharp that referenced this pull request Nov 11, 2019
antonfirsov pushed a commit to antonfirsov/ImageSharp that referenced this pull request Nov 11, 2019
as proposed by @antonfirsov: improving readability and memory locality.
antonfirsov pushed a commit to antonfirsov/ImageSharp that referenced this pull request Nov 11, 2019
test images don't have to be that big for axial gradients.
It's sufficient to show they're constant across the axis and correct along the axis.
antonfirsov pushed a commit to antonfirsov/ImageSharp that referenced this pull request Nov 11, 2019
antonfirsov pushed a commit to antonfirsov/ImageSharp that referenced this pull request Nov 11, 2019
the theory lacks color checks yet that should be added, but it produces output images for visual control
antonfirsov pushed a commit to antonfirsov/ImageSharp that referenced this pull request Nov 11, 2019
somehow these don't look correct yet, containing hard edges sometimes.
antonfirsov pushed a commit to antonfirsov/ImageSharp that referenced this pull request Nov 11, 2019
antonfirsov pushed a commit to antonfirsov/ImageSharp that referenced this pull request Nov 11, 2019
antonfirsov pushed a commit to antonfirsov/ImageSharp that referenced this pull request Nov 11, 2019
The current implementation lacks accurrancy and prefers to be fast.
For usual color gradients that shouldn't matter: They're quite smooth, for the black-white gradient on very small length's it's easy to spot these rounding errors as they affect whole pixels one can count manually.
antonfirsov pushed a commit to antonfirsov/ImageSharp that referenced this pull request Nov 11, 2019
antonfirsov pushed a commit to antonfirsov/ImageSharp that referenced this pull request Nov 11, 2019
antonfirsov pushed a commit to antonfirsov/ImageSharp that referenced this pull request Nov 11, 2019
- move to GradientBrushes namespace
- add abstract base class for Gradient Brushes, that implements the GetGradientSegment implementtion and the color picking.
antonfirsov pushed a commit to antonfirsov/ImageSharp that referenced this pull request Nov 11, 2019
antonfirsov pushed a commit to antonfirsov/ImageSharp that referenced this pull request Nov 11, 2019
antonfirsov pushed a commit to antonfirsov/ImageSharp that referenced this pull request Nov 11, 2019
antonfirsov pushed a commit to antonfirsov/ImageSharp that referenced this pull request Nov 11, 2019
antonfirsov pushed a commit to antonfirsov/ImageSharp that referenced this pull request Nov 11, 2019
changing parameters so that visual evaluation of correctness is easier.
antonfirsov pushed a commit to antonfirsov/ImageSharp that referenced this pull request Nov 11, 2019
antonfirsov pushed a commit to antonfirsov/ImageSharp that referenced this pull request Nov 11, 2019
antonfirsov pushed a commit to antonfirsov/ImageSharp that referenced this pull request Nov 11, 2019
antonfirsov pushed a commit to antonfirsov/ImageSharp that referenced this pull request Nov 11, 2019
antonfirsov pushed a commit to antonfirsov/ImageSharp that referenced this pull request Nov 11, 2019
antonfirsov added a commit to antonfirsov/ImageSharp that referenced this pull request Nov 11, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants