Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
102 changes: 95 additions & 7 deletions scripts/common/java/ProcessScreenshots.java
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,9 @@ public static void main(String[] args) throws Exception {
arguments.referenceDir,
arguments.actualEntries,
arguments.emitBase64,
arguments.previewDir
arguments.previewDir,
arguments.maxChannelDelta,
arguments.maxMismatchPercent
);
String json = JsonUtil.stringify(payload);
System.out.print(json);
Expand All @@ -51,7 +53,9 @@ static Map<String, Object> buildResults(
Path referenceDir,
List<Map.Entry<String, Path>> actualEntries,
boolean emitBase64,
Path previewDir
Path previewDir,
int maxChannelDelta,
double maxMismatchPercent
) throws IOException {
List<Map<String, Object>> results = new ArrayList<>();
for (Map.Entry<String, Path> entry : actualEntries) {
Expand All @@ -75,7 +79,7 @@ static Map<String, Object> buildResults(
try {
PNGImage actual = loadPngWithRetry(actualPath);
PNGImage expected = loadPngWithRetry(expectedPath);
Map<String, Object> outcome = compareImages(expected, actual);
Map<String, Object> outcome = compareImages(expected, actual, maxChannelDelta, maxMismatchPercent);
if (Boolean.TRUE.equals(outcome.get("equal"))) {
record.put("status", "equal");
} else {
Expand Down Expand Up @@ -318,21 +322,60 @@ private static int clamp(int value) {
return Math.max(0, Math.min(255, value));
}

private static Map<String, Object> compareImages(PNGImage expected, PNGImage actual) {
private static Map<String, Object> compareImages(PNGImage expected, PNGImage actual, int maxChannelDelta, double maxMismatchPercent) {
boolean equal = expected.width == actual.width
&& expected.height == actual.height
&& expected.bitDepth == actual.bitDepth
&& expected.colorType == actual.colorType
&& java.util.Arrays.equals(expected.pixels, actual.pixels);
Map<String, Object> result = new LinkedHashMap<>();
result.put("equal", equal);
result.put("width", actual.width);
result.put("height", actual.height);
result.put("bit_depth", actual.bitDepth);
result.put("color_type", actual.colorType);
if (!equal && maxChannelDelta > 0 && maxMismatchPercent >= 0 && expected.width == actual.width && expected.height == actual.height) {
int totalPixels = actual.width * actual.height;
int mismatchCount = countMismatchedPixels(expected, actual, maxChannelDelta);
double mismatchPercent = totalPixels == 0 ? 0d : (mismatchCount * 100d) / totalPixels;
result.put("mismatch_count", mismatchCount);
result.put("mismatch_percent", mismatchPercent);
result.put("max_channel_delta", maxChannelDelta);
result.put("max_mismatch_percent", maxMismatchPercent);
equal = mismatchPercent <= maxMismatchPercent;
}
result.put("equal", equal);
return result;
}

private static int countMismatchedPixels(PNGImage expected, PNGImage actual, int maxChannelDelta) {
int[] expectedRgb = toRgbArray(expected);
int[] actualRgb = toRgbArray(actual);
int mismatched = 0;
for (int i = 0; i < expectedRgb.length; i++) {
int e = expectedRgb[i];
int a = actualRgb[i];
int er = (e >> 16) & 0xff;
int eg = (e >> 8) & 0xff;
int eb = e & 0xff;
int ar = (a >> 16) & 0xff;
int ag = (a >> 8) & 0xff;
int ab = a & 0xff;
if (Math.abs(er - ar) > maxChannelDelta
|| Math.abs(eg - ag) > maxChannelDelta
|| Math.abs(eb - ab) > maxChannelDelta) {
mismatched++;
}
}
return mismatched;
}

private static int[] toRgbArray(PNGImage image) {
BufferedImage rgbImage = toRgbImage(image);
int[] pixels = new int[image.width * image.height];
rgbImage.getRGB(0, 0, image.width, image.height, pixels, 0, image.width);
return pixels;
}

private static PNGImage loadPngWithRetry(Path path) throws IOException {
int attempt = 0;
long lastSize = -1;
Expand Down Expand Up @@ -666,18 +709,25 @@ private static class Arguments {
final List<Map.Entry<String, Path>> actualEntries;
final boolean emitBase64;
final Path previewDir;
final int maxChannelDelta;
final double maxMismatchPercent;

private Arguments(Path referenceDir, List<Map.Entry<String, Path>> actualEntries, boolean emitBase64, Path previewDir) {
private Arguments(Path referenceDir, List<Map.Entry<String, Path>> actualEntries, boolean emitBase64, Path previewDir,
int maxChannelDelta, double maxMismatchPercent) {
this.referenceDir = referenceDir;
this.actualEntries = actualEntries;
this.emitBase64 = emitBase64;
this.previewDir = previewDir;
this.maxChannelDelta = maxChannelDelta;
this.maxMismatchPercent = maxMismatchPercent;
}

static Arguments parse(String[] args) {
Path reference = null;
boolean emitBase64 = false;
Path previewDir = null;
int maxChannelDelta = 0;
double maxMismatchPercent = 0d;
List<Map.Entry<String, Path>> actuals = new ArrayList<>();
for (int i = 0; i < args.length; i++) {
String arg = args[i];
Expand Down Expand Up @@ -712,6 +762,26 @@ static Arguments parse(String[] args) {
Path path = Path.of(value.substring(idx + 1));
actuals.add(Map.entry(name, path));
}
case "--max-channel-delta" -> {
if (++i >= args.length) {
System.err.println("Missing value for --max-channel-delta");
return null;
}
maxChannelDelta = parseIntArg("--max-channel-delta", args[i]);
if (maxChannelDelta < 0) {
return null;
}
}
case "--max-mismatch-percent" -> {
if (++i >= args.length) {
System.err.println("Missing value for --max-mismatch-percent");
return null;
}
maxMismatchPercent = parseDoubleArg("--max-mismatch-percent", args[i]);
if (maxMismatchPercent < 0) {
return null;
}
}
default -> {
System.err.println("Unknown argument: " + arg);
return null;
Expand All @@ -722,7 +792,25 @@ static Arguments parse(String[] args) {
System.err.println("--reference-dir is required");
return null;
}
return new Arguments(reference, actuals, emitBase64, previewDir);
return new Arguments(reference, actuals, emitBase64, previewDir, maxChannelDelta, maxMismatchPercent);
}

private static int parseIntArg(String flag, String value) {
try {
return Integer.parseInt(value);
} catch (NumberFormatException e) {
System.err.println("Invalid integer for " + flag + ": " + value);
return -1;
}
}

private static double parseDoubleArg(String flag, String value) {
try {
return Double.parseDouble(value);
} catch (NumberFormatException e) {
System.err.println("Invalid number for " + flag + ": " + value);
return -1d;
}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,25 +1,20 @@
package com.codenameone.examples.hellocodenameone.tests.graphics;

import com.codename1.ui.EncodedImage;
import com.codename1.ui.FontImage;
import com.codename1.ui.Graphics;
import com.codename1.ui.Image;
import com.codename1.ui.RGBImage;
import com.codename1.ui.geom.Rectangle;
import com.codenameone.examples.hellocodenameone.tests.AbstractGraphicsScreenshotTest;

public class DrawGradient extends AbstractGraphicsScreenshotTest {

@Override
protected void drawContent(Graphics g, Rectangle bounds) {
int height = bounds.getHeight() / 3;
int width = bounds.getWidth() / 2;
int y = bounds.getY();
g.fillRadialGradient(0xff, 0xff00, bounds.getX(), y, width, height);
g.fillRadialGradient(0xff, 0xff00, bounds.getX() + width, y, width, height,20, 200);
g.fillRadialGradient(0xff, 0xff00, bounds.getX() + width, y, width, height, 20, 200);
y += height;

g.fillRectRadialGradient(0xff0000, 0xcccccc, bounds.getX() + width, y, width, height,0.5f, 0.5f, 2);
g.fillRectRadialGradient(0xff0000, 0xcccccc, bounds.getX() + width, y, width, height, 0.5f, 0.5f, 2);
g.fillLinearGradient(0xff, 0x999999, bounds.getX(), y, width, height, true);
y += height;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,12 @@ protected void drawContent(Graphics g, Rectangle bounds) {
g.setColor(0xffffff);
for (int iter = 0 ; iter < bounds.getWidth() / 2 ; iter++) {
nextColor(g);
g.fillArc(bounds.getX() + iter, bounds.getY() + iter, bounds.getX() + bounds.getWidth() - iter, bounds.getY() + bounds.getHeight() + iter, iter, 180);
int width = bounds.getWidth() - (iter * 2);
int height = bounds.getHeight() - (iter * 2);
if (width <= 0 || height <= 0) {
break;
}
g.fillArc(bounds.getX() + iter, bounds.getY() + iter, width, height, iter, 180);
}
}

Expand Down
6 changes: 6 additions & 0 deletions scripts/lib/cn1ss.sh
Original file line number Diff line number Diff line change
Expand Up @@ -330,6 +330,12 @@ cn1ss_process_and_report() {

# Run ProcessScreenshots
local -a compare_args=("--reference-dir" "$ref_dir" "--emit-base64" "--preview-dir" "$preview_dir")
if [ -n "${CN1SS_MAX_CHANNEL_DELTA:-}" ]; then
compare_args+=("--max-channel-delta" "${CN1SS_MAX_CHANNEL_DELTA}")
fi
if [ -n "${CN1SS_MAX_MISMATCH_PERCENT:-}" ]; then
compare_args+=("--max-mismatch-percent" "${CN1SS_MAX_MISMATCH_PERCENT}")
fi
for entry in "${actual_entries[@]}"; do
compare_args+=("--actual" "$entry")
done
Expand Down
57 changes: 56 additions & 1 deletion scripts/run-javase-device-tests.sh
Original file line number Diff line number Diff line change
Expand Up @@ -239,4 +239,59 @@ SUMMARY_FILE="$ARTIFACTS_DIR/summary.txt"

jd_log "Desktop device-runner artifacts stored in $ARTIFACTS_DIR"

exit $SIM_EXIT_CODE
SCREENSHOT_REF_DIR_BASE="${SCREENSHOT_REF_DIR:-$SCRIPT_DIR/android/screenshots}"
SCREENSHOT_REF_DIR="$SCREENSHOT_REF_DIR_BASE"
if [ -n "${CN1SS_ANDROID_BASELINE:-}" ]; then
SCREENSHOT_REF_DIR="$CN1SS_ANDROID_BASELINE"
else
baseline_variant=""
if [ "$JAVA_VERSION_MAJOR" -ge 21 ]; then
baseline_variant="21"
elif [ "$JAVA_VERSION_MAJOR" -ge 17 ]; then
baseline_variant="17"
fi
if [ -n "$baseline_variant" ] && [ -d "$SCREENSHOT_REF_DIR_BASE/$baseline_variant" ]; then
SCREENSHOT_REF_DIR="$SCREENSHOT_REF_DIR_BASE/$baseline_variant"
fi
fi
jd_log "Using screenshot baseline directory: $SCREENSHOT_REF_DIR"

COMPARE_ENTRIES=()
for test in "${TEST_NAMES[@]}"; do
png_dest="$SCREENSHOT_DIR/${test}.png"
if [ -f "$png_dest" ]; then
COMPARE_ENTRIES+=("${test}=${png_dest}")
fi
done

COMPARE_JSON="$ARTIFACTS_DIR/screenshot-compare.json"
SUMMARY_FILE="$ARTIFACTS_DIR/screenshot-summary.txt"
COMMENT_FILE="$ARTIFACTS_DIR/screenshot-comment.md"

export CN1SS_PREVIEW_DIR="$PREVIEW_DIR"
export CN1SS_COMMENT_MARKER="<!-- CN1SS_JAVASE_COMMENT -->"
export CN1SS_COMMENT_LOG_PREFIX="[run-javase-device-tests]"
export CN1SS_PREVIEW_SUBDIR="javase"
export CN1SS_MAX_CHANNEL_DELTA="${CN1SS_MAX_CHANNEL_DELTA:-4}"
export CN1SS_MAX_MISMATCH_PERCENT="${CN1SS_MAX_MISMATCH_PERCENT:-0.25}"

if [ "${#COMPARE_ENTRIES[@]}" -gt 0 ]; then
cn1ss_process_and_report \
"Java SE screenshot updates" \
"$COMPARE_JSON" \
"$SUMMARY_FILE" \
"$COMMENT_FILE" \
"$SCREENSHOT_REF_DIR" \
"$PREVIEW_DIR" \
"$ARTIFACTS_DIR" \
"${COMPARE_ENTRIES[@]}"
compare_rc=$?
else
compare_rc=0
fi

if [ "$SIM_EXIT_CODE" -ne 0 ] && [ "$compare_rc" -eq 0 ]; then
exit "$SIM_EXIT_CODE"
fi

exit "$compare_rc"
Loading