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

Fixed handling of NSImage by cocoa Image backend #2355

Merged
merged 12 commits into from
Jan 30, 2024

Conversation

proneon267
Copy link
Contributor

@proneon267 proneon267 commented Jan 20, 2024

The toga.Image() class should be able to handle a NSImage. But the current implementation is non functional and would pass a NoneType object to nsdata_to_bytes, when a NSImage is passed.

This PR fixes this bug and ensures that a valid NSData is passed to nsdata_to_bytes

This bug was discovered during the implementation of #1930 and since that PR would be undergoing more reviews before getting merged, I figured this bugfix should be a separate PR.

The latest commit on #1930 is the validity of the correct working of this PR. Locally tested on MacOS.

PR Checklist:

  • All new features have been tested
  • All new features have been documented
  • I have read the CONTRIBUTING.md file
  • I will abide by the code of conduct

Copy link
Member

@freakboy3742 freakboy3742 left a comment

Choose a reason for hiding this comment

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

If this problem affects Cocoa, it very likely affects iOS as well.

This definitely needs a test of some kind. In this case, the issue is being caused when an image is created with the native image type, and then being converted to data. That's a test you can write in a completely platform independent way by leveraging ._impl.native to get the native object.

Copy link
Member

@freakboy3742 freakboy3742 left a comment

Choose a reason for hiding this comment

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

A couple of minor tweaks to the actual test.

However, the bigger question is why this extra test is need at all. What is this new test validating that test_data_image and test_raw_image aren't already validating?

Comment on lines 141 to 145
buffer = io.BytesIO()
pil_image.save(buffer, format="png", compress_level=0)

# Construct a Toga image from PIL image
image1 = toga.Image(buffer.getvalue())
Copy link
Member

Choose a reason for hiding this comment

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

This step shouldn't be needed - Toga can accept PIL images directly as input (and this would be a good in-practice test of that).

Copy link
Member

Choose a reason for hiding this comment

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

Also - more meaningful variable names wouldn't hurt; orig_image instead of image1 in this case.

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. Thanks.



async def test_native_image(app):
# Generate an image using pillow
Copy link
Member

Choose a reason for hiding this comment

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

As I've noted in previous reviews, every test should start with a 1 line docstring that describes the purpose of the test.

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.

assert image_from_native.height == 30

# Construct an image from `image_from_native`'s data
image2 = toga.Image(image_from_native.data)
Copy link
Member

Choose a reason for hiding this comment

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

Again; variable naming - image_from_data maybe?

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.

@proneon267
Copy link
Contributor Author

However, the bigger question is why this extra test is need at all. What is this new test validating that test_data_image and test_raw_image aren't already validating?

Since, this bug was caused when the data was requested from the image created with the native image, this test validates that the data from an image created from the raw image representation is valid. Both of the tests, test_data_image and test_raw_image do not test this behavior.

But the behaviors tested by test_data_image and test_raw_image is tested in this new test. So, I propose that we remove test_raw_image and repurpose test_data_image as test_buffer_image(Test image creation from buffer data.)

Copy link
Member

@freakboy3742 freakboy3742 left a comment

Choose a reason for hiding this comment

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

As a general guide, there needs to be a very good reason to remove a test - if a test was previously passing, that's a code usage that is known to be good, and needs to stay that way. Unless you can give a good rationale for why a new test makes an old test unnecessary, you shouldn't drop tests.

In this case, the test that has been deleted is a different use case - and, more importantly, it's the fact that the use case is different that was the problem. Under Cocoa, creating an image from a PNG source is apparently different to creating it from a buffer/native source - so we need to test that discrepancy.

@@ -22,16 +22,6 @@ async def test_local_image(app):
assert image.height == 72


async def test_raw_image(app):
Copy link
Member

Choose a reason for hiding this comment

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

Presumably we still need to test this case - it was valid previously, and clearly has different semantics on some platforms.

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 have added it back.

@proneon267
Copy link
Contributor Author

In this case, the test that has been deleted is a different use case - and, more importantly, it's the fact that the use case is different that was the problem. Under Cocoa, creating an image from a PNG source is apparently different to creating it from a buffer/native source - so we need to test that discrepancy.

On second thought, the test does seems to test a different thing. But it felt very similar to test_local_image test. Anyways, I have added it back.

Copy link
Member

@freakboy3742 freakboy3742 left a comment

Choose a reason for hiding this comment

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

A couple of cosmetic cleanups, but I think this is done. Thanks for the fix!

changes/2355.bugfix.rst Outdated Show resolved Hide resolved
cocoa/src/toga_cocoa/images.py Outdated Show resolved Hide resolved
@freakboy3742 freakboy3742 merged commit e2b6e71 into beeware:main Jan 30, 2024
35 checks passed
@proneon267 proneon267 deleted the ns_image_fix branch January 30, 2024 14:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants