Skip to content

Screenshot test: drawImage under rotated clip (#3921)#4993

Closed
shai-almog wants to merge 2 commits into
masterfrom
issue-3921-image-clip-rotation-test
Closed

Screenshot test: drawImage under rotated clip (#3921)#4993
shai-almog wants to merge 2 commits into
masterfrom
issue-3921-image-clip-rotation-test

Conversation

@shai-almog
Copy link
Copy Markdown
Collaborator

Summary

  • Adds ImageClipUnderRotation screenshot test that reproduces the exact shape of Clipping region not respected with non-90 degree rotations. #3921 -- outer scale(2.5) + translate, per-tile rotateRadians(30deg) + clipRect(inner) + drawImage -- with the proper pushClip/popClip save/restore.
  • Companion to the existing ClipUnderRotation (PR Screenshot test for clip-under-rotation (#3921) #4924) which covers the same polygon-clip rasterisation path with fillRect. ddyer0's original screenshots show a misclipped EncodedImage, not a misclipped fillRect, so the drawImage path needs its own coverage.
  • Uses pushClip / popClip throughout. The original repro used int[] clip = g.getClip(); ...; g.setClip(clip); which can't preserve a rotated-rect clip shape -- ddyer0's own follow-up comment on the issue identified that round-trip as the source of his visible artefact, not a port bug. Using the documented save/restore API isolates the rasterisation half.

Expected outcomes (visually distinguishable against the navy axis-aligned outline of the pre-rotation inner rect)

  • Correct: red/yellow/green-bordered test-image slice appears as a 30deg-tilted rect, overhanging the navy outline at two diagonal corners and falling short at the other two.
  • Bug A (clip widened to axis-aligned bbox): image slice exactly matches the navy outline.
  • Bug B (polygon clip dropped entirely): full 60x60 test image is rendered, swamping the navy outline. This is the suspected pre-fix iOS Metal failure mode (ClipRect.m polygon initialiser stored x=y=w=h=-1, Metal execute then called CN1MetalSetScissor(0, 0, -2, -2) whose width<=0 / height<=0 branch sets the scissor to the full framebuffer).

The 2x2 grid from AbstractGraphicsScreenshotTest separates the form-graphics path (top cells) from the mutable-image path (bottom cells), so a Metal-only / mutable-only regression localises automatically.

Test plan

  • JavaSE simulator: tilted image slice inside each cell, navy outline visible.
  • Android: tilted image slice.
  • iOS GL: tilted image slice (stencil-based polygon clip).
  • iOS Metal: tilted image slice (post-#319c758b6 stencil-clip + post-#c56e7aab0 bbox fallback).
  • JS port: skipped via HTML5 skip list (matches other graphics screenshot tests).
  • Goldens captured per port from CI and committed in a follow-up.

🤖 Generated with Claude Code

Issue #3921 ("Clipping region not respected with non-90 degree
rotations") reports an EncodedImage misclip under a rotated clip when
the outer Graphics already has scale + translate applied. The existing
ClipUnderRotation screenshot test (added in 95d64ba) covers the
polygon-clip rasterisation path with fillRect; this new test exercises
the same path through drawImage, which is what ddyer0's original
screenshots actually show failing.

Uses pushClip / popClip throughout. ddyer0's repro relied on
`int[] clip = g.getClip(); ...; g.setClip(clip);` for save/restore,
which can't by API contract preserve a non-axis-aligned (rotated-rect)
clip shape -- ddyer0's own follow-up comment on the issue identified
that round-trip as the source of the artefact, not a port bug. This
test goes through the documented save/restore API so the rasterisation
half of the bug is isolated.

Three visual outcomes, distinguishable against a navy reference
outline of the pre-rotation inner clip:
- Correct: 30deg-tilted slice of the test image, overhanging the navy
  outline on two diagonal corners.
- Bug A: image slice matches the navy outline exactly (rasteriser
  collapsed the rotated-rect clip to its bbox).
- Bug B: full 60x60 image rendered, swamping the outline (rasteriser
  saw a polygon clip and disabled clipping; suspected pre-fix iOS
  Metal behaviour).

Registered in Cn1ssDeviceRunner alongside ClipUnderRotation; added to
the HTML5 skip list to match the other graphics screenshot tests.
Goldens not included -- they need a CI capture per port.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@shai-almog
Copy link
Copy Markdown
Collaborator Author

shai-almog commented May 21, 2026

JavaScript port screenshot updates

Compared 21 screenshots: 20 matched, 1 missing reference.

  • graphics-image-clip-under-rotation — missing reference. Reference screenshot missing at /home/runner/work/CodenameOne/CodenameOne/scripts/javascript/screenshots/graphics-image-clip-under-rotation.png.

    graphics-image-clip-under-rotation
    Preview info: JPEG preview quality 50; JPEG preview quality 50.
    Full-resolution PNG saved as graphics-image-clip-under-rotation.png in workflow artifacts.

@shai-almog
Copy link
Copy Markdown
Collaborator Author

shai-almog commented May 21, 2026

Android screenshot updates

Compared 111 screenshots: 110 matched, 1 missing reference.

  • graphics-image-clip-under-rotation — missing reference. Reference screenshot missing at /home/runner/work/CodenameOne/CodenameOne/scripts/android/screenshots/graphics-image-clip-under-rotation.png.

    graphics-image-clip-under-rotation
    Preview info: JPEG preview quality 70; JPEG preview quality 70.
    Full-resolution PNG saved as graphics-image-clip-under-rotation.png in workflow artifacts.

Native Android coverage

  • 📊 Line coverage: 11.83% (6652/56233 lines covered) [HTML preview] (artifact android-coverage-report, jacocoAndroidReport/html/index.html)
    • Other counters: instruction 9.55% (33353/349065), branch 4.14% (1371/33138), complexity 5.18% (1644/31761), method 9.00% (1336/14840), class 14.99% (303/2021)
    • Lowest covered classes
      • kotlin.collections.kotlin.collections.ArraysKt___ArraysKt – 0.00% (0/6327 lines covered)
      • kotlin.collections.unsigned.kotlin.collections.unsigned.UArraysKt___UArraysKt – 0.00% (0/2384 lines covered)
      • org.jacoco.agent.rt.internal_b6258fc.asm.org.jacoco.agent.rt.internal_b6258fc.asm.ClassReader – 0.00% (0/1519 lines covered)
      • kotlin.collections.kotlin.collections.CollectionsKt___CollectionsKt – 0.00% (0/1148 lines covered)
      • org.jacoco.agent.rt.internal_b6258fc.asm.org.jacoco.agent.rt.internal_b6258fc.asm.MethodWriter – 0.00% (0/923 lines covered)
      • kotlin.sequences.kotlin.sequences.SequencesKt___SequencesKt – 0.00% (0/730 lines covered)
      • kotlin.text.kotlin.text.StringsKt___StringsKt – 0.00% (0/623 lines covered)
      • org.jacoco.agent.rt.internal_b6258fc.asm.org.jacoco.agent.rt.internal_b6258fc.asm.Frame – 0.00% (0/564 lines covered)
      • kotlin.collections.kotlin.collections.ArraysKt___ArraysJvmKt – 0.00% (0/495 lines covered)
      • kotlinx.coroutines.kotlinx.coroutines.JobSupport – 0.00% (0/423 lines covered)

Benchmark Results

Detailed Performance Metrics

Metric Duration
Base64 payload size 8192 bytes
Base64 benchmark iterations 6000
Base64 native encode 1010.000 ms
Base64 CN1 encode 211.000 ms
Base64 encode ratio (CN1/native) 0.209x (79.1% faster)
Base64 native decode 992.000 ms
Base64 CN1 decode 307.000 ms
Base64 decode ratio (CN1/native) 0.309x (69.1% faster)
Image encode benchmark status skipped (SIMD unsupported)

@shai-almog
Copy link
Copy Markdown
Collaborator Author

shai-almog commented May 21, 2026

iOS Metal screenshot updates

Compared 111 screenshots: 110 matched, 1 missing reference.

  • graphics-image-clip-under-rotation — missing reference. Reference screenshot missing at /Users/runner/work/CodenameOne/CodenameOne/scripts/ios/screenshots-metal/graphics-image-clip-under-rotation.png.

    graphics-image-clip-under-rotation
    Preview info: JPEG preview quality 10; JPEG preview quality 10; downscaled to 825x1789.
    Full-resolution PNG saved as graphics-image-clip-under-rotation.png in workflow artifacts.

Benchmark Results

  • VM Translation Time: 0 seconds
  • Compilation Time: 372 seconds

Build and Run Timing

Metric Duration
Simulator Boot 103000 ms
Simulator Boot (Run) 1000 ms
App Install 19000 ms
App Launch 11000 ms
Test Execution 321000 ms

Detailed Performance Metrics

Metric Duration
Base64 payload size 8192 bytes
Base64 benchmark iterations 6000
Base64 native encode 921.000 ms
Base64 CN1 encode 2206.000 ms
Base64 encode ratio (CN1/native) 2.395x (139.5% slower)
Base64 native decode 481.000 ms
Base64 CN1 decode 1176.000 ms
Base64 decode ratio (CN1/native) 2.445x (144.5% slower)
Base64 SIMD encode 534.000 ms
Base64 encode ratio (SIMD/native) 0.580x (42.0% faster)
Base64 encode ratio (SIMD/CN1) 0.242x (75.8% faster)
Base64 SIMD decode 427.000 ms
Base64 decode ratio (SIMD/native) 0.888x (11.2% faster)
Base64 decode ratio (SIMD/CN1) 0.363x (63.7% faster)
Image encode benchmark iterations 100
Image createMask (SIMD off) 82.000 ms
Image createMask (SIMD on) 41.000 ms
Image createMask ratio (SIMD on/off) 0.500x (50.0% faster)
Image applyMask (SIMD off) 225.000 ms
Image applyMask (SIMD on) 90.000 ms
Image applyMask ratio (SIMD on/off) 0.400x (60.0% faster)
Image modifyAlpha (SIMD off) 174.000 ms
Image modifyAlpha (SIMD on) 66.000 ms
Image modifyAlpha ratio (SIMD on/off) 0.379x (62.1% faster)
Image modifyAlpha removeColor (SIMD off) 185.000 ms
Image modifyAlpha removeColor (SIMD on) 115.000 ms
Image modifyAlpha removeColor ratio (SIMD on/off) 0.622x (37.8% faster)
Image PNG encode (SIMD off) 4707.000 ms
Image PNG encode (SIMD on) 1493.000 ms
Image PNG encode ratio (SIMD on/off) 0.317x (68.3% faster)
Image JPEG encode 1358.000 ms

@shai-almog
Copy link
Copy Markdown
Collaborator Author

shai-almog commented May 21, 2026

iOS screenshot updates

Compared 111 screenshots: 110 matched, 1 missing reference.

  • graphics-image-clip-under-rotation — missing reference. Reference screenshot missing at /Users/runner/work/CodenameOne/CodenameOne/scripts/ios/screenshots/graphics-image-clip-under-rotation.png.

    graphics-image-clip-under-rotation
    Preview info: JPEG preview quality 10; JPEG preview quality 10; downscaled to 825x1789.
    Full-resolution PNG saved as graphics-image-clip-under-rotation.png in workflow artifacts.

Benchmark Results

  • VM Translation Time: 0 seconds
  • Compilation Time: 214 seconds

Build and Run Timing

Metric Duration
Simulator Boot 92000 ms
Simulator Boot (Run) 1000 ms
App Install 12000 ms
App Launch 4000 ms
Test Execution 299000 ms

Detailed Performance Metrics

Metric Duration
Base64 payload size 8192 bytes
Base64 benchmark iterations 6000
Base64 native encode 671.000 ms
Base64 CN1 encode 1318.000 ms
Base64 encode ratio (CN1/native) 1.964x (96.4% slower)
Base64 native decode 286.000 ms
Base64 CN1 decode 1060.000 ms
Base64 decode ratio (CN1/native) 3.706x (270.6% slower)
Base64 SIMD encode 427.000 ms
Base64 encode ratio (SIMD/native) 0.636x (36.4% faster)
Base64 encode ratio (SIMD/CN1) 0.324x (67.6% faster)
Base64 SIMD decode 409.000 ms
Base64 decode ratio (SIMD/native) 1.430x (43.0% slower)
Base64 decode ratio (SIMD/CN1) 0.386x (61.4% faster)
Image encode benchmark iterations 100
Image createMask (SIMD off) 58.000 ms
Image createMask (SIMD on) 10.000 ms
Image createMask ratio (SIMD on/off) 0.172x (82.8% faster)
Image applyMask (SIMD off) 126.000 ms
Image applyMask (SIMD on) 54.000 ms
Image applyMask ratio (SIMD on/off) 0.429x (57.1% faster)
Image modifyAlpha (SIMD off) 161.000 ms
Image modifyAlpha (SIMD on) 88.000 ms
Image modifyAlpha ratio (SIMD on/off) 0.547x (45.3% faster)
Image modifyAlpha removeColor (SIMD off) 243.000 ms
Image modifyAlpha removeColor (SIMD on) 92.000 ms
Image modifyAlpha removeColor ratio (SIMD on/off) 0.379x (62.1% faster)
Image PNG encode (SIMD off) 1077.000 ms
Image PNG encode (SIMD on) 794.000 ms
Image PNG encode ratio (SIMD on/off) 0.737x (26.3% faster)
Image JPEG encode 487.000 ms

Feedback on the first revision: the 4 grid cells looked different from
one another (the outer scale + translate I copied from ddyer0's repro
mapped the image to slightly different positions depending on cell
bounds), the colours were not vivid enough (the green 4px border
disappeared at the rendering scale), and the comment block kept
referring to a non-existent "red fill" carried over from the sibling
ClipUnderRotation.java.

Changes:
- Geometry computed from cell centre: `side = 0.7 * min(w, h)`,
  `imgX = x + (w - side) / 2`. Identical across all 4 cells.
- Outer scale + translate dropped. They were ddyer0's repro shape, but
  the bug is about rotation under clip, not scale, so the scale/translate
  was noise.
- Test image is now solid yellow with a 10-pixel magenta border and a
  black diagonal X. Yellow / magenta / black all distinct from the gray
  background and the navy reference outline.
- A dim (alpha=64) full-size copy of the image is drawn first as an
  underlay so the clipped over-paint has a baseline to compare against:
  if the bright over-paint extends past the dim underlay it's a no-clip
  bug; if it matches the navy outline exactly it's a bbox-collapse bug;
  if it's a tilted square overhanging the navy outline at two corners
  it's correct.
- Comment block shrunk to ~15 lines.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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.

1 participant