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

pixel: add NewImageFromBytes function #713

Merged
merged 2 commits into from
Oct 27, 2024
Merged

pixel: add NewImageFromBytes function #713

merged 2 commits into from
Oct 27, 2024

Conversation

deadprogram
Copy link
Member

This PR adds a new function NewImageFromBytes() to create a pixel.Image from an existing slice of bytes.

It is already being used as part of some refactoring that @conejoninja and I are doing on the badger2040 repo.

Thanks to @conejoninja for helping me debug these changes!

@@ -43,6 +43,40 @@ func NewImage[T Color](width, height int) Image[T] {
}
}

// NewImageFromBytes creates a new image of the given size using an existing data slice of bytes.
func NewImageFromBytes[T Color](width, height int, buf []byte) Image[T] {
Copy link

Choose a reason for hiding this comment

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

Should this return (Image[T], error) instead of panicking?

Copy link
Member Author

Choose a reason for hiding this comment

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

That is the current interface, which I was not trying to change in this PR.

@conejoninja conejoninja merged commit 4fb5d7a into dev Oct 27, 2024
1 check passed
@conejoninja conejoninja deleted the pixel-from-bytes branch October 27, 2024 23:59
@conejoninja
Copy link
Member

Thanks, merged.

In a later PR we might change the interface as proposed, but merge this now to keep things simple.

@conejoninja conejoninja restored the pixel-from-bytes branch October 28, 2024 00:11
conejoninja added a commit that referenced this pull request Oct 28, 2024
conejoninja added a commit that referenced this pull request Oct 28, 2024
@conejoninja
Copy link
Member

I undo this with the revert because it makes the test fails even thought the build succeeded before I merged it 🤔

@conejoninja conejoninja mentioned this pull request Oct 28, 2024
Copy link
Member

@aykevl aykevl left a comment

Choose a reason for hiding this comment

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

See my comments below. Unfortunately this PR will break existing programs that use pixel.Monochrome images, and the new NewImageFromBytes has some problems because of the different alignment of []byte and the image buffer.

if len(buf) != width*height*int(unsafe.Sizeof(zeroColor)) {
panic("NewImageFromBytes: data slice size mismatch")
}
data = unsafe.Pointer(&buf[0])
Copy link
Member

Choose a reason for hiding this comment

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

This isn't safe. If you create this from a plain old byte slice ([]byte) and the alignment of the new slice is different, this can lead to crashes on simpler cores like Cortex-M0 and I think even miscompiles (undefined behavior) which can result in any behavior and will be difficult to debug.

This applies to RGB565BE and RGB555 which have an underlying type of []uint16.

To check for this, you can do something like this:

if zeroColor.BitsPerPixel() % 8 == 0 && uintptr(data) % unsafe.Alignof(zeroColor) != 0 {
    panic("NewImageFromBytes: byte buffer is not aligned")
}

But really, if you allocate the slice using any normal Go way (global variable, make([]byte, ...), etc) there is no guarantee the byte slice is correctly aligned. If you want to be absolutely sure and don't want to rely on this behavior (and therefore exclude formats like RGB565BE and RGB555), you can also use a check like this:

if zeroColor.BitsPerPixel() % 8 == 0 && unsafe.Alignof(zeroColor) > 1 {
    panic("NewImageFromBytes: cannot convert byte buffer to image (different alignment)")
}

Copy link
Member Author

Choose a reason for hiding this comment

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

I will add a new PR with that additional check, since this code is specifically for mono.

Comment on lines -112 to +148
*((*byte)(ptr)) |= 1 << uint8(y%8)
*((*byte)(ptr)) |= (1 << (7 - uint8(x%8)))
} else {
*((*byte)(ptr)) &^= 1 << uint8(y%8)
*((*byte)(ptr)) &^= (1 << (7 - uint8(x%8)))
Copy link
Member

Choose a reason for hiding this comment

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

You are changing the format here! Specifically, this reverses the order of the bits in the Monochrome color type, thus breaking programs that were using pixel.Monochrome before with the old format.

If you want to add a new format, please do so by adding a new pixel type instead.

Copy link
Member Author

Choose a reason for hiding this comment

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

I think I was the only one trying to use this format, and I did not complete that work (on Thumby) due to this code being incorrect in the current form. So I am pretty sure this is fixing something broken, not introducing a new format.

Copy link
Member

Choose a reason for hiding this comment

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

Ok, seems reasonable.
Though we might want to add a test for this: just a small monochrome image (5 x 3 pixels or so - intentionally with a weird size) and check whether it is equivalent to a PNG image for example.

Copy link
Member Author

Choose a reason for hiding this comment

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

Good suggestion, I will do that in the same PR in which I add the alignment check.

Comment on lines -108 to +142
y := index / int(img.width)
offset := x + (y/8)*int(img.width)
offset := index / 8
Copy link
Member

Choose a reason for hiding this comment

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

You are also changing the format here! The offset calculation changed and now outputs different values.

Copy link
Member Author

Choose a reason for hiding this comment

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

See above comment.

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.

4 participants