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

Support Paparazzi#gif with APNGs #1186

Merged
merged 1 commit into from
Apr 11, 2024
Merged

Support Paparazzi#gif with APNGs #1186

merged 1 commit into from
Apr 11, 2024

Conversation

gamepro65
Copy link
Collaborator

Handles all the different error configurations that can happen during gif verification. By default the verifier is only used for gif, potentially could be used for all snapshots but wanted to take it one step at a time. General performance testing a base gif during recordPaparazzi averaged ~35ms per frame on an M1 Pro with verifyPaparazzi delta video (which is 3x as wide) taking upward of ~130ms per frame.

Refactored the hard coded failure dir from ImageUtils so we can seed it properly. For testing the delta image generation we opt out of rendering java text (IE. Expected / Actual) because it is different between platforms and would require a non-zero % diff image assertion.

Mismatched frames:
delta-app cash paparazzi sample_KeypadViewTest_testViews_spin

More frames than golden:
delta-app cash paparazzi sample_KeypadViewTest_testViews_spin

Less frames than golden:
delta-app cash paparazzi sample_KeypadViewTest_testViews_spin

Mismatched fps:
delta-app cash paparazzi sample_KeypadViewTest_testViews_spin

@saket
Copy link
Collaborator

saket commented Dec 23, 2023

HYPE!

@saket
Copy link
Collaborator

saket commented Dec 23, 2023

Will this be compatible with composables?

@gamepro65
Copy link
Collaborator Author

Will this be compatible with composables?

Yup! Works as expected out of the box 🙌

@oldergod
Copy link
Contributor

What's blocking this?

Copy link
Collaborator

@geoff-powell geoff-powell left a comment

Choose a reason for hiding this comment

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

This PR looks good to me. Anything blocking this from getting merged? cc @gamepro65

Copy link
Collaborator

@BrianGardnerAtl BrianGardnerAtl left a comment

Choose a reason for hiding this comment

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

Looks good. Are you planning on adding the HtmlReportWriter test for the .apng file check?

Copy link
Collaborator

@jrodbx jrodbx left a comment

Choose a reason for hiding this comment

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

Partial feedback; will give another pass, but wanted to get this out to start the conversation.

@gamepro65 gamepro65 force-pushed the cdrury/gifVerifications branch 2 times, most recently from e9ba4a9 to 664fe3d Compare April 5, 2024 15:48
Copy link
Collaborator

@jrodbx jrodbx left a comment

Choose a reason for hiding this comment

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

More feedback, mostly on the record/report mode; will give one last pass for verification.

@gamepro65 gamepro65 force-pushed the cdrury/gifVerifications branch 2 times, most recently from ee736eb to 3fc88fd Compare April 10, 2024 15:31
Copy link
Collaborator

@jrodbx jrodbx left a comment

Choose a reason for hiding this comment

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

awesome stuff, @gamepro65!

// Expected on the left
// Golden on the right
g.drawImage(goldenImage, 0, 0, null)
g.drawImage(image, goldenImageWidth + deltaWidth, 0, null)
Copy link
Collaborator

Choose a reason for hiding this comment

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

before the delta was only drawn on error, but this proposes drawing it on every similarity check. is there any perf impact to take into account here, i.e., does this contribute much to that ~130ms per frame analysis?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

The delta image is generated while calculating the % error, we previously were just throwing it away when no error.

}
readNextFrame()
}
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

this method is tough to read, given all the implicit receivers. consider rewriting as:

  private fun ApngReader.initializeWriter(): ApngWriter {
    val writer = ApngWriter(deltaFilePath, commonFrameRate, fileSystem)

    val currentFrameNumber = frameNumber - 1
    reset()
    repeat(currentFrameNumber) {
      val nextFrame = readNextFrame() ?: blankFrame
      val (deltaImage) = ImageUtils.compareImages(
        goldenImage = nextFrame,
        image = nextFrame,
        withText = withErrorText
      )
      writer.writeImage(deltaImage)
    }
    readNextFrame()

    return writer
  }

or

  private fun initializeWriter(reader: ApngReader): ApngWriter {
    return ApngWriter(deltaFilePath, commonFrameRate, fileSystem).apply {
      val currentFrameNumber = reader.frameNumber - 1
      reader.reset()
      repeat(currentFrameNumber) {
        val nextFrame = reader.readNextFrame() ?: blankFrame
        val (deltaImage) = ImageUtils.compareImages(
          goldenImage = nextFrame,
          image = nextFrame,
          withText = withErrorText
        )
        writeImage(deltaImage)
      }
      reader.readNextFrame()
    }
  }

to reduce one of the implicit receivers. FWIW, I like the first slightly better.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

It seems like the hard part is discerning reader from writer. I think this would strike a happy medium

  private fun ApngReader.initializeWriter(): ApngWriter {
    val currentFrameNumber = frameNumber - 1
    reset()

    return ApngWriter(deltaFilePath, commonFrameRate, fileSystem).apply {
      repeat(currentFrameNumber) {
        val nextFrame = readNextFrame() ?: blankFrame
        val (deltaImage) = ImageUtils.compareImages(
          expected = nextFrame,
          actual = nextFrame,
          withText = withErrorText
        )

        writeImage(deltaImage)
      }
      readNextFrame()
    }
  }

import kotlin.math.max
import kotlin.math.min

internal class ApngVerifier(
Copy link
Collaborator

Choose a reason for hiding this comment

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

this is a work of art. kudos!

import java.io.File
import java.lang.AssertionError

class ApngVerifierTest {
Copy link
Collaborator

Choose a reason for hiding this comment

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

I really like this test

@gamepro65 gamepro65 merged commit 096f603 into master Apr 11, 2024
14 checks passed
@gamepro65 gamepro65 deleted the cdrury/gifVerifications branch April 11, 2024 14:01
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.

None yet

6 participants