import java.util.*;
import java.util.zip.*;
import java.util.List;
import java.util.regex.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.*;
import java.util.concurrent.locks.*;
import java.util.function.*;
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.text.*;
import javax.swing.table.*;
import java.io.*;
import java.net.*;
import java.lang.reflect.*;
import java.lang.ref.*;
import java.lang.management.*;
import java.security.*;
import java.security.spec.*;
import java.awt.*;
import java.awt.event.*;
import java.awt.image.*;
import java.awt.geom.*;
import javax.imageio.*;
import java.math.*;
import java.time.Duration;
import static x30_pkg.x30_util.DynamicObject;
import java.awt.geom.*;
import java.text.*;
import java.text.NumberFormat;
import java.util.TimeZone;
class main {
static class G22BurnIn {
final public G22BurnIn setAlphaStep(double alphaStep){ return alphaStep(alphaStep); }
public G22BurnIn alphaStep(double alphaStep) { this.alphaStep = alphaStep; return this; } final public double getAlphaStep(){ return alphaStep(); }
public double alphaStep() { return alphaStep; }
double alphaStep = 0.1;
final public G22BurnIn setTolerance(double tolerance){ return tolerance(tolerance); }
public G22BurnIn tolerance(double tolerance) { this.tolerance = tolerance; return this; } final public double getTolerance(){ return tolerance(); }
public double tolerance() { return tolerance; }
double tolerance = 0.1;
final public G22BurnIn setColorMorph(double colorMorph){ return colorMorph(colorMorph); }
public G22BurnIn colorMorph(double colorMorph) { this.colorMorph = colorMorph; return this; } final public double getColorMorph(){ return colorMorph(); }
public double colorMorph() { return colorMorph; }
double colorMorph = 0.1; // speed to update color within tolerance
final public G22BurnIn setBackgroundColor(Color backgroundColor){ return backgroundColor(backgroundColor); }
public G22BurnIn backgroundColor(Color backgroundColor) { this.backgroundColor = backgroundColor; return this; } final public Color getBackgroundColor(){ return backgroundColor(); }
public Color backgroundColor() { return backgroundColor; }
Color backgroundColor = Color.black;
int toleranceSquaredInt;
int w, h;
int[] mask;
// how much motion is there in the image overall? (0 to 1)
double motionFactor = Double.NaN;
void processFrame(BufferedImage frame) {
toleranceSquaredInt = iround(sqr(tolerance*255)*3);
int w = frame.getWidth(), h = frame.getHeight();
if (mask == null || this.w != w || this.h != h) {
this.w = w;
this.h = h;
this.mask = new int[w*h];
}
// We assume the frame has no transparency
var gp = grabbableIntPixels_fastOrSlow(frame);
int n = w*h, iMask = 0, iFrame = gp.offset;
int[] pixels = gp.data;
int[] mask = this.mask;
for (int y = 0; y < h; y++) {
for (int x = 0; x < w; x++) {
int mpix = mask[iMask];
int fcol = pixels[iFrame++] & 0xFFFFFF;
if (mpix == 0)
mpix = differentColor(mpix, fcol);
else {
int mcol = mpix & 0xFFFFFF;
int diff = rgbDistanceSquaredInt(mcol, fcol);
if (diff <= toleranceSquaredInt)
mpix = sameColor(mpix, fcol);
else
mpix = differentColor(mpix, fcol);
}
mask[iMask++] = mpix;
}
iFrame += gp.scanlineStride-w;
}
motionFactor = Double.NaN;
}
boolean isSameColor(int col1, int col2) {
return toleranceSquaredInt == 0
? col1 == col2
: rgbDistanceSquaredInt(col1, col2) <= tolerance;
}
int sameColor(int mpix, int fcol) {
double newAlpha = rgbAlphaZeroToOne(mpix)+alphaStep;
int newColor = blendRGBInts(mpix, fcol, colorMorph);
return withAlpha(newAlpha, newColor);
}
int differentColor(int mpix, int fcol) {
return withAlpha(alphaStep, fcol);
}
BufferedImage image() {
if (mask == null) return null;
var img = bufferedImage(w, h, mask);
return renderImageOnBackground(backgroundColor, img);
}
BufferedImage motionImage() {
if (mask == null) return null;
int[] mask2 = pixelsWithInvertedAlpha(mask);
var img = bufferedImage(w, h, mask2);
return renderImageOnBackground(backgroundColor, img);
}
BWImage motionDetectionImage() {
return new BWImage(w, h, alphaChannelFromPixels(mask));
}
double motionFactor() {
if (isNaN(motionFactor) && mask != null)
motionFactor = 1-alphaChannelAverage(mask)/255.0;
return motionFactor;
}
}
static int iround(double d) {
return (int) Math.round(d);
}
static int iround(Number n) {
return iround(toDouble(n));
}
static int sqr(int i) {
return i*i;
}
static long sqr(long l) {
return l*l;
}
static double sqr(double d) {
return d*d;
}
static float sqr(float f) {
return f*f;
}
static GrabbableIntPixels grabbableIntPixels_fastOrSlow(BufferedImage image) { try {
try {
GrabbableIntPixels gp = grabbableIntPixels(image);
if (gp != null) return gp;
} catch (Throwable __e) { printStackTrace(__e); }
// Use pixelGrabber if quick method fails
int w = image.getWidth(), h = image.getHeight();
int[] data = new int[w*h];
PixelGrabber pixelGrabber = new PixelGrabber(image, 0, 0, w, h, data, 0, w);
if (!pixelGrabber.grabPixels())
throw fail("Could not grab pixels");
return new GrabbableIntPixels(data, w, h, 0, w);
} catch (Exception __e) { throw rethrow(__e); } }
// 0xRRGGBB - result is between 0 and 255*255*3
static int rgbDistanceSquaredInt(int a, int b) {
return
sqrUbyte(absDiff(rgbRed(a), rgbRed(b))) +
sqrUbyte(absDiff(rgbGreen(a), rgbGreen(b))) +
sqrUbyte(absDiff(rgbBlue(a), rgbBlue(b)));
}
static double rgbAlphaZeroToOne(int rgb) {
return rgbAlpha(rgb)/255.0;
}
static int blendRGBInts(double bish, int a, int b) {
return blendRGBInts(a, b, bish);
}
static int blendRGBInts(int a, int b, double bish) {
bish = normalizeTo0to1(bish);
return rgbInt(
blend(rgbRed(a), rgbRed(b), bish),
blend(rgbGreen(a), rgbGreen(b), bish),
blend(rgbBlue(a), rgbBlue(b), bish));
}
static Color withAlpha(Color c, double alpha) {
return colorWithAlpha(c, alpha);
}
static Color withAlpha(double alpha, Color c) {
return colorWithAlpha(alpha, c);
}
static int withAlpha(double alpha, int rgb) {
return rgb & 0xFFFFFF | (iround(clampToZeroOne(alpha)*255) << 24);
}
static BufferedImage bufferedImage(int[] pixels, int w, int h) {
return intArrayToBufferedImage(pixels, w, h);
}
static BufferedImage bufferedImage(int[] pixels, WidthAndHeight size) {
return bufferedImage(pixels, size.getWidth(), size.getHeight());
}
static BufferedImage bufferedImage(int w, int h, int[] pixels) {
return intArrayToBufferedImage(pixels, w, h);
}
// undefined color, seems to be all black in practice
static BufferedImage bufferedImage(int w, int h) {
return newBufferedImage(w, h);
}
static BufferedImage bufferedImage(int w, int h, RGB rgb) {
return newBufferedImage(w, h, rgb);
}
static BufferedImage bufferedImage(int w, Color color) { return bufferedImage(w, w, color); }
static BufferedImage bufferedImage(int w, int h, Color color) {
return newBufferedImage(w, h, color);
}
static BufferedImage bufferedImage(Pt p, Color color) {
return newBufferedImage(p, color);
}
static BufferedImage bufferedImage(WidthAndHeight size, Color color) {
return newBufferedImage(size.w(), size.h(), color);
}
static BufferedImage bufferedImage(Color color, WidthAndHeight size) {
return bufferedImage(size, color);
}
static BufferedImage renderImageOnBackground(Color bg, BufferedImage img) {
if (img == null) return null;
BufferedImage canvas = newBufferedImage(img.getWidth(), img.getHeight(), bg);
drawImageOnImage(img, canvas);
return canvas;
}
static int[] pixelsWithInvertedAlpha(int[] pixels) {
int n = pixels.length;
int[] pixels2 = new int[n];
for (int i = 0; i < n; i++)
pixels2[i] = pixels[i]^0xFF000000;
return pixels2;
}
static byte[] alphaChannelFromPixels(int[] pixels) {
int n = pixels.length;
byte[] pixels2 = new byte[n];
for (int i = 0; i < n; i++)
pixels2[i] = (byte) (pixels[i] >> 24);
return pixels2;
}
static boolean isNaN(double d) {
return Double.isNaN(d);
}
static boolean isNaN(float f) {
return Float.isNaN(f);
}
static double alphaChannelAverage(int[] pixels) {
int n = pixels.length;
long sum = 0;
for (int i = 0; i < n; i++)
sum += rgbAlpha(pixels[i]);
return doubleRatio(sum, n);
}
static double toDouble(Object o) {
if (o instanceof Number)
return ((Number) o).doubleValue();
if (o instanceof BigInteger)
return ((BigInteger) o).doubleValue();
if (o instanceof String)
return parseDouble((String) o);
if (o == null) return 0.0;
throw fail(o);
}
static Boolean grabbableIntPixels_succeeded;
static boolean grabbableIntPixels_enable = true;
static GrabbableIntPixels grabbableIntPixels(BufferedImage img) {
if (img == null || !grabbableIntPixels_enable) return null;
try {
var result = grabbableIntPixels_impl(img);
grabbableIntPixels_succeeded = result != null;
return result;
} catch (Throwable _e) {
grabbableIntPixels_succeeded = false;
throw rethrow(_e); }
}
static GrabbableIntPixels grabbableIntPixels_impl(BufferedImage img) {
Raster raster = img.getRaster();
SampleModel _sampleModel = raster.getSampleModel();
if (!(_sampleModel instanceof SinglePixelPackedSampleModel)) return null;
SinglePixelPackedSampleModel sampleModel = (SinglePixelPackedSampleModel) _sampleModel;
DataBufferInt dataBuffer = (DataBufferInt) (raster.getDataBuffer());
assertEquals(1, dataBuffer.getNumBanks());
assertEquals(DataBuffer.TYPE_INT, dataBuffer.getDataType());
// Let's at this point assume the raster data is what we
// think it is... (TODO: test on unusual platforms)
int w = img.getWidth(), h = img.getHeight();
int scanlineStride = sampleModel.getScanlineStride();
int[] pixels = dataBuffer.getData();
int offset = dataBuffer.getOffset();
int translateX = raster.getSampleModelTranslateX();
int translateY = raster.getSampleModelTranslateY();
offset += -translateX-translateY*scanlineStride;
return new GrabbableIntPixels(pixels, w, h, offset, scanlineStride);
}
static A printStackTrace(A e) {
// we go to system.out now - system.err is nonsense
if (e != null) print(getStackTrace(e));
return e;
}
static void printStackTrace() {
printStackTrace(new Throwable());
}
static void printStackTrace(String msg) {
printStackTrace(new Throwable(msg));
}
static void printStackTrace(String msg, Throwable e) {
printStackTrace(new Throwable(msg, e));
}
static RuntimeException fail() { throw new RuntimeException("fail"); }
static RuntimeException fail(Throwable e) { throw asRuntimeException(e); }
static RuntimeException fail(Object msg) { throw new RuntimeException(String.valueOf(msg)); }
static RuntimeException fail(Object... objects) { throw new Fail(objects); }
static RuntimeException fail(String msg) { throw new RuntimeException(msg == null ? "" : msg); }
static RuntimeException fail(String msg, Throwable innerException) { throw new RuntimeException(msg, innerException); }
static RuntimeException rethrow(Throwable t) {
if (t instanceof Error)
_handleError((Error) t);
throw t instanceof RuntimeException ? (RuntimeException) t : new RuntimeException(t);
}
static RuntimeException rethrow(String msg, Throwable t) {
throw new RuntimeException(msg, t);
}
static final short[] sqrUbyte_table = sqrUbyte_makeTable();
static int sqrUbyte(int ubyte) {
if ((ubyte & ~0xFF) != 0)
throw fail("bad ubyte: " + ubyte);
return unsignedShortToInt(sqrUbyte_table[ubyte]);
}
static short[] sqrUbyte_makeTable() {
short[] tbl = new short[256];
for (int i = 0; i < 256; i++)
tbl[i] = (short) (i*i);
return tbl;
}
static int absDiff(int a, int b) {
return abs(a-b);
}
static double absDiff(double a, double b) {
return abs(a-b);
}
static int rgbRed(int rgb) {
return (rgb >> 16) & 0xFF;
}
static int rgbGreen(int rgb) {
return (rgb >> 8) & 0xFF;
}
static int rgbBlue(int rgb) {
return rgb & 0xFF;
}
static int rgbAlpha(int rgb) {
return (rgb >> 24) & 0xFF;
}
static double normalizeTo0to1(double x) {
return Math.min(Math.max(x, 0), 1);
}
static int rgbInt(int r, int g, int b) {
return (clamp(r, 0, 255) << 16) | (clamp(g, 0, 255) << 8) | clamp(b, 0, 255);
}
static int rgbInt(byte r, byte g, byte b) {
return (ubyteToInt(r) << 16) | (ubyteToInt(g) << 8) | ubyteToInt(b);
}
static int blend(int x, int y, double yish) {
double xish = 1-yish;
return (int) (x*xish+y*yish);
}
static double blend(double x, double y, double yish) {
double xish = 1-yish;
return x*xish+y*yish;
}
static Color colorWithAlpha(Color c, double alpha) {
return new Color(c.getRed()/255f,
c.getGreen()/255f,
c.getBlue()/255f,
(float) clampZeroToOne(alpha));
}
static Color colorWithAlpha(double alpha, Color c) {
return colorWithAlpha(c, alpha);
}
static float clampToZeroOne(float x) {
return x < 0 ? 0 : x > 1 ? 1 : x;
}
static double clampToZeroOne(double x) {
return x < 0 ? 0 : x > 1 ? 1 : x;
}
// from: https://stackoverflow.com/questions/14416107/int-array-to-bufferedimage
// pixels are RGB pixels
static BufferedImage intArrayToBufferedImage(int[] pixels, int w, int h) {
int[] bitMasks = new int[]{0xFF0000, 0xFF00, 0xFF, 0xFF000000};
SinglePixelPackedSampleModel sm = new SinglePixelPackedSampleModel(DataBuffer.TYPE_INT, w, h, bitMasks);
DataBufferInt db = new DataBufferInt(pixels, pixels.length);
WritableRaster wr = Raster.createWritableRaster(sm, db, new Point());
return new BufferedImageWithMeta(ColorModel.getRGBdefault(), wr, false, null);
}
// undefined color, seems to be all black in practice
// This is without alpha?
static BufferedImage newBufferedImage(int w, int h) {
return new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB);
}
static BufferedImage newBufferedImage(int w, int h, RGB rgb) {
return newBufferedImage(w, h, rgb.getColor());
}
static BufferedImage newBufferedImage(int w, int h, Color color) {
BufferedImage img = newBufferedImage(w, h);
Graphics2D g = img.createGraphics();
g.setColor(or(color, Color.white));
g.fillRect(0, 0, w, h);
return img;
}
static BufferedImage newBufferedImage(Pt p, Color color) {
return newBufferedImage(p.x, p.y, color);
}
// This one is with alpha...
static BufferedImage newBufferedImage(int w, int h, int[] pixels) {
return intArrayToBufferedImage(pixels, w, h);
}
// changes & returns canvas
static BufferedImage drawImageOnImage(Image img, BufferedImage canvas, int x, int y) {
createGraphics(canvas).drawImage(img, x, y, null);
return canvas;
}
static BufferedImage drawImageOnImage(Image img, BufferedImage canvas) {
return drawImageOnImage(img, canvas, 0, 0);
}
static double doubleRatio(double x, double y) {
return y == 0 ? 0 : x/y;
}
static double doubleRatio(Seconds x, Seconds y) {
return doubleRatio(x.get(), y.get());
}
static double parseDouble(String s) {
return empty(s) ? 0.0 : Double.parseDouble(s);
}
static A assertEquals(Object x, A y) {
return assertEquals("", x, y);
}
static A assertEquals(String msg, Object x, A y) {
if (assertVerbose()) return assertEqualsVerbose(msg, x, y);
if (!(x == null ? y == null : x.equals(y)))
throw fail((msg != null ? msg + ": " : "") + y + " != " + x);
return y;
}
static volatile StringBuffer local_log = new StringBuffer(); // not redirected
static boolean printAlsoToSystemOut = true;
static volatile Appendable print_log = local_log; // might be redirected, e.g. to main bot
// in bytes - will cut to half that
static volatile int print_log_max = 1024*1024;
static volatile int local_log_max = 100*1024;
static boolean print_silent = false; // total mute if set
static Object print_byThread_lock = new Object();
static volatile ThreadLocal