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
Init color array when decode for bitmap #6147
Init color array when decode for bitmap #6147
Conversation
@hoisie , could you help to review this PR? Thanks. |
Awesome, thanks for doing this. I'm running it against some tests internally to see if any issues come up. |
There were a few failures that got caught, here is one reduced example of a failure. It's a bitmap that gets serialized to a file as PNG, re-read, and then compared with the original. It passes without these changes: @Test
public void testWriteToFile() throws IOException {
byte[] bitmapData = new byte[] {23, 100, 23, 52, 23, 18, 76, 43};
Bitmap bitmap1 = BitmapFactory.decodeByteArray(bitmapData, 0, bitmapData.length);
Path tempFile = Files.createTempFile("bitmap", null);
FileOutputStream fileOutputStream = new FileOutputStream(tempFile.toFile());
bitmap1.compress(Bitmap.CompressFormat.PNG, 100, fileOutputStream); // lossless compression
fileOutputStream.close();
Bitmap bitmap2 = BitmapFactory.decodeFile(tempFile.toAbsolutePath().toString());
ByteBuffer buffer1 = ByteBuffer.allocate(bitmap1.getHeight() * bitmap1.getRowBytes());
bitmap1.copyPixelsToBuffer(buffer1);
ByteBuffer buffer2 = ByteBuffer.allocate(bitmap2.getHeight() * bitmap2.getRowBytes());
bitmap2.copyPixelsToBuffer(buffer2);
assertThat(buffer1.array()).isEqualTo(buffer2.array());
} |
Thanks. Maybe the compress method doesn't write color correctly to file. I will add this test method local to retest it. |
1d447b3
to
0af51b1
Compare
@hoisie , I rebase the pr to the current master branch, and add commits to write color array to stream when compressing. The old logic will use png image writer for png format, and jpeg image writer for other formats(jpeg, webp*), but the jpeg image writer of openjdk can't process alpha channel correctly, so I switch to use png image writer for all formats when compressing. And I don't remove unused format parameter of |
0af51b1
to
fa7833d
Compare
Rebase to master again after many merges. |
fa7833d
to
4e8cb64
Compare
Rebase after 4.5 release, @hoisie could you help to review it? Thanks. |
Thanks for the update, I am taking a closer look at this to try to understand it more deeply. As I am sure you have discovered by now, ShadowBitmap and ShadowBitmapFactory suffer from a lot of legacy APIs and buggy implementations, so it is difficult to navigate through that code. |
shadows/framework/src/main/java/org/robolectric/shadows/ImageUtil.java
Outdated
Show resolved
Hide resolved
I just realized the bitmap data I pasted in #6147 (comment) wasn't even a valid bitmap. Perhaps like other APIs in ShadowBitmap or ShadowBitmapFactory, getPixel and compress should only do real actions if the underlying data is valid. Otherwise, they should be no-ops or return 0. I am still looking into why #6147 (comment) is failing. Long-term I would really like to enforce that the bitmap data passed to BitmapFactory is real bitmap data. |
I definitely want to make forward progress on this issue, so I'll do some more investigation tomorrow. I'm trying to gauge how many tests depend on the broken behavior of the existing ShadowBitmapFactory. One thing I'd like to do is to have warnings if users supply invalid image data in supplied |
FYI another test that passes before color array init and fails after: @Test
public void testSameAs() {
Bitmap bitmap = Bitmap.createBitmap(/* width= */ 10, /* height= */ 10, Config.ARGB_8888);
ByteArrayOutputStream outStream = new ByteArrayOutputStream();
bitmap.compress(CompressFormat.PNG, /* quality= */ 100, outStream);
byte[] outBytes = outStream.toByteArray();
ByteArrayInputStream inStream = new ByteArrayInputStream(outBytes);
Bitmap bitmap2 = BitmapFactory.decodeStream(inStream);
assertThat(bitmap.sameAs(bitmap2)).isTrue();
} I want to try this on real Android to see what happens. |
A couple other thoughts:
|
The
Awesome, it may solve this problem completely. https://github.com/JetBrains/skija can be a good choice for integrate |
4e8cb64
to
f9c1045
Compare
@hoisie I add a new commit to add this test, and it passed without error. |
I will bring back |
6bf8748
to
a47bc60
Compare
The new commit bring back the support for Another thing should be noted is the @test
fun testBitmapJpegCompress() {
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
val oldBitmap = BitmapFactory.decodeResource(appContext.resources, R.drawable.test_jpeg)
val baos = ByteArrayOutputStream()
oldBitmap.compress(Bitmap.CompressFormat.JPEG, 100, baos)
val bytes = baos.toByteArray()
val newBitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.size)
val oldBuffer = ByteBuffer.allocate(oldBitmap.height * oldBitmap.rowBytes)
oldBitmap.copyPixelsToBuffer(oldBuffer)
val newBuffer = ByteBuffer.allocate(newBitmap.height * newBitmap.rowBytes)
newBitmap.copyPixelsToBuffer(newBuffer)
assertEquals(oldBitmap.width, newBitmap.width)
assertEquals(newBitmap.height, newBitmap.height)
for (i in 0 until oldBitmap.width) {
for (j in 0 until oldBitmap.height) {
assertEquals(oldBitmap.getPixel(i, j), newBitmap.getPixel(i, j))
}
}
} So the test method to read color array from compressed @hoisie could you help to review to see whether there are other tests should be added? Thanks. |
a47bc60
to
2da4b18
Compare
@hoisie rebased on recent master. |
a77d448
to
10df7ee
Compare
shadows/framework/src/main/java/org/robolectric/shadows/ShadowBitmapFactory.java
Outdated
Show resolved
Hide resolved
shadows/framework/src/main/java/org/robolectric/shadows/ImageUtil.java
Outdated
Show resolved
Hide resolved
shadows/framework/src/main/java/org/robolectric/shadows/ShadowBitmapFactory.java
Outdated
Show resolved
Hide resolved
Just one minor mimeType change and I can merge after that. |
Signed-off-by: utzcoz <utzcoz@outlook.com>
Thanks. The change has be made by the new commit. |
robolectric/src/test/java/org/robolectric/shadows/ShadowBitmapFactoryTest.java
Outdated
Show resolved
Hide resolved
Looking into one last weird ColorDrawable related issue. Probably minor, and likely has a simple fix. |
Signed-off-by: utzcoz <utzcoz@outlook.com>
Excellent! Thanks for all your hard work and patience! |
Thanks your patience to review it, and your awesome suggestions. |
This was noticed when calling BitmapFactory.decodeResource, which sets the Bitmap to immutable by default. This is related to #6147. PiperOrigin-RevId: 360580115
This makes it possible to do ColorDrawable.draw(Canvas) and draw color rects to a bitmap-backed Canvas. This is related to #6147. PiperOrigin-RevId: 361476836
Previously it did not call Drawable.draw(Canvas) on the underlying Drawable from the ImageView. The logic to do this had to go into ShadowView as ImageView does not override draw(Canvas). Another option was to create a ShadowImageView and extend draw(Canvas), but that would cause problems for tests that have custom shadows for View, which is one of the more commonly occurring shadows. This is related to #6147. PiperOrigin-RevId: 361385295
It will try to initialize
ShadowBitmap.color
when decode those source to try to fix #3743.It supports
decodeByteArray
,decodeFile
,decodeResource
,decodeStream
, anddecodeResourceStream
.