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

Reimplement color processing #5529

Closed
wants to merge 1 commit into from
Closed

Reimplement color processing #5529

wants to merge 1 commit into from

Conversation

vjeux
Copy link
Contributor

@vjeux vjeux commented Jan 25, 2016

Summary:

Problem:

As I was trying to document what color formats we supported, I realized that our current implementation based on the open source project tinycolor supported some crazy things. A few examples that were all valid:

tinycolor('abc')
tinycolor(' #abc ')
tinycolor('##abc')
tinycolor('rgb 255 0 0')
tinycolor('RGBA(0, 1, 2)')
tinycolor('rgb (0, 1, 2)')
tinycolor('hsv(0, 1, 2)')
tinycolor({r: 10, g: 10, b: 10})
tinycolor('hsl(1%, 2, 3)')
tinycolor('rgb(1.0, 2.0, 3.0)')
tinycolor('rgb(1%, 2%, 3%)')

The integrations of tinycolor were also really bad. processColor added "support" for pure numbers and an array of colors!?? ColorPropTypes did some crazy trim().toString() and repeated a bad error message twice.

Solution:

While iteratively cleaning the file, I eventually ended up reimplementing it entierly. Major changes are:

  • The API is now dead simple: returns null if it doesn't parse or returns the int32 representation of the color
  • Stricter parsing of attributes. Instead of allowing all the arguments to be numbers with or without percentages, now you need to specify the correct type between integer, number and percentage.
  • The internals only work on int32 numbers, there's no longer tons of object creations and copy
  • The color map is now storing the computed value directly instead of parsing it over and over again
  • Made the wrapper functions just delegate to tinycolor instead of implementing diverging logic
  • Added unit tests on the allowed and not allowed formats
  • Flowified the file
  • Fixed a bug where hsl values with s === 0 would return the wrong result

I'm not sure how many times this function is being called but we were allocating 5 (!!!!) rgba objects for each call and doing a lot of expensive string parsing. The new implementation should be much faster.

Risk:

I'm confident that the values that were supported before still work fine. But there may be cases where people used an unsupported variant that is now going to redbox in dev and ignore the color in prod. I think that it's worth doing as what was supported was really clowny.

Test Plan:
Unit tests for now. I want to gather feedback and if people are happy with it I'll push it forward.

@facebook-github-bot
Copy link
Contributor

By analyzing the blame information on this pull request, we identified @javache, @nicklockwood and @a2 to be potential reviewers.

@facebook-github-bot facebook-github-bot added GH Review: review-needed CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. labels Jan 25, 2016
expect(interpolation(0)).toBe('rgba(100, 120, 140, 0.5)');
expect(interpolation(0.5)).toBe('rgba(117.5, 186, 126, 0.75)');
expect(interpolation(0)).toBe('rgba(100, 120, 140, 0.4)');
expect(interpolation(0.5)).toBe('rgba(117.5, 186, 126, 0.7)');
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Needed to change those as we are now only having 8 bits of precision. 0.75 * 255 = 191.25 is not a round number. It doesn't really change anything, the platform unlikely supports more than 8 bits of precision for the alpha channel.

s = bound01(s, 100);
l = bound01(l, 100);

function hue2rgb(p, q, t) {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

no need to keep it inside anymore (we have private scope with commonjs modules)

@ide
Copy link
Contributor

ide commented Jan 25, 2016

Can you move this out of vendor if it's a new implementation?


var match;

// Ordered based on occurences on Facebook codebase
Copy link
Contributor

Choose a reason for hiding this comment

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

spelling: occurrences
also basing off of frequency is smart

Copy link
Contributor Author

Choose a reason for hiding this comment

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

👍

@nicklockwood
Copy link
Contributor

I was planning to optimize this, so thanks for saving me the trouble!

You've removed a few deliberately supported features (#rgba and color arrays) along with the crud though, so we'll need to restore those.

Also, I think it should probably be renamed to something other than 'tinycolor', since it no longer shares any heritage with the original lib.

@vjeux
Copy link
Contributor Author

vjeux commented Jan 25, 2016

Thanks for the very good review! I'll address all the feedback and rename the file/move it somewhere else :)

Summary:

**Problem:**

As I was trying to document what color formats we supported, I realized that our current implementation based on the open source project tinycolor supported some crazy things. A few examples that were all valid:

```
tinycolor('abc')
tinycolor(' #abc ')
tinycolor('##abc')
tinycolor('rgb 255 0 0')
tinycolor('RGBA(0, 1, 2)')
tinycolor('rgb (0, 1, 2)')
tinycolor('hsv(0, 1, 2)')
tinycolor({r: 10, g: 10, b: 10})
tinycolor('hsl(1%, 2, 3)')
tinycolor('rgb(1.0, 2.0, 3.0)')
tinycolor('rgb(1%, 2%, 3%)')
```

The integrations of tinycolor were also really bad. processColor added "support" for pure numbers and an array of colors!?? ColorPropTypes did some crazy trim().toString() and repeated a bad error message twice.

**Solution:**

While iteratively cleaning the file, I eventually ended up reimplementing it entierly. Major changes are:
- The API is now dead simple: returns null if it doesn't parse or returns the int32 representation of the color
- Stricter parsing of attributes. Instead of allowing all the arguments to be numbers with or without percentages, now you need to specify the correct type between integer, number and percentage.
- The internals only work on int32 numbers, there's no longer tons of object creations and copy
- The color map is now storing the computed value directly instead of parsing it over and over again
- Made the wrapper functions just delegate to tinycolor instead of implementing diverging logic
- Added unit tests on the allowed and not allowed formats
- Flowified the file
- Fixed a bug where hsl values with s === 0 would return the wrong result

**Risk:**

I'm confident that the values that were supported before still work fine. But there may be cases where people used an unsupported variant that is now going to redbox in dev and ignore the color in prod. I think that it's worth doing as what was supported was really clowny.

Test Plan:
Unit tests for now. I want to gather feedback and if people are happy with it I'll push it forward.
@facebook-github-bot
Copy link
Contributor

@vjeux updated the pull request.

@vjeux
Copy link
Contributor Author

vjeux commented Jan 27, 2016

@facebook-github-bot import

'use strict';

var tinycolor = require('tinycolor');
var normalizeColor = require('normalizeColor');
Copy link
Contributor Author

Choose a reason for hiding this comment

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

I renamed it to normalizeColor

@facebook-github-bot
Copy link
Contributor

Thanks for importing. If you are an FB employee go to https://our.intern.facebook.com/intern/opensource/github/pull_request/929606970420579/int_phab to review.

@vjeux
Copy link
Contributor Author

vjeux commented Jan 27, 2016

The error is pretty awesome

simulator screen shot jan 27 2016 2 50 51 pm

@vjeux
Copy link
Contributor Author

vjeux commented Jan 27, 2016

Grrr, import doesn't trigger tests for some reasons :( Would love a review so that I can to try and shipit

@ide
Copy link
Contributor

ide commented Jan 28, 2016

LGTM

@vjeux
Copy link
Contributor Author

vjeux commented Jan 28, 2016

@facebook-github-bot shipit

@facebook-github-bot
Copy link
Contributor

Thanks for importing. If you are an FB employee go to https://our.intern.facebook.com/intern/opensource/github/pull_request/929606970420579/int_phab to review.

@ide
Copy link
Contributor

ide commented Jan 28, 2016

If you have internal TTI profiling tools that show the before/after effects of this diff I'd be interested in seeing them since this should speed up the first render() pass especially.

@nicklockwood
Copy link
Contributor

@ide the results show a slight improvement, but below the threshold of confidence for the accuracy of our tests ¯_(ツ)_/¯

@ide
Copy link
Contributor

ide commented Jan 28, 2016

@nicklockwood thanks for the report. A slight improvement is better than nothing =)

@ghost ghost closed this in c8a0a3e Jan 29, 2016
doostin pushed a commit to doostin/react-native that referenced this pull request Feb 1, 2016
Summary:
**Problem:**

As I was trying to document what color formats we supported, I realized that our current implementation based on the open source project tinycolor supported some crazy things. A few examples that were all valid:

```
tinycolor('abc')
tinycolor(' #abc ')
tinycolor('##abc')
tinycolor('rgb 255 0 0')
tinycolor('RGBA(0, 1, 2)')
tinycolor('rgb (0, 1, 2)')
tinycolor('hsv(0, 1, 2)')
tinycolor({r: 10, g: 10, b: 10})
tinycolor('hsl(1%, 2, 3)')
tinycolor('rgb(1.0, 2.0, 3.0)')
tinycolor('rgb(1%, 2%, 3%)')
```

The integrations of tinycolor were also really bad. processColor added "support" for pure numbers and an array of colors!?? ColorPropTypes did some crazy trim().toString() and repeated a bad error message twice.

**Solution:**

While iteratively cleaning the file, I eventually ended up reimplementing it entierly. Major changes are:
- The API is now dead simple: returns null if it doesn't parse or returns the int32 representation of the color
- Stricter parsing of at
Closes facebook#5529

Reviewed By: svcscm

Differential Revision: D2872015

Pulled By: nicklockwood

fb-gh-sync-id: df78244eefce6cf8e8ed2ea51f58d6b232de16f9
This pull request was closed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants