!7 !include early #1033506 // Compact Module Include Gazelle V module GazelleScreenCam > DynEnabled { int screenNr; int pixelRows = 100, colors = 8; transient ImageSurface imageSurface; transient new ScreenCamStream images; transient new BWIntegralImageStream integralImages; transient new SourceTriggeredStream scaledAndPosterizedImages; transient new DoubleFPSCounter fpsCounter; transient int fps; transient ScreenSelectorRadioButtons screenSelector; transient new RollingAverage remainingMSPerFrame; transient int remainingMS; start { images.directlyFeedInto(integralImages); integralImages.onNewElement(ii -> scaledAndPosterizedImages.newElement( posterizeBWImage_withMeta(colors, scaledBWImageFromBWIntegralImage_withMeta(pixelRows, ii)))); scaledAndPosterizedImages.onNewElement(img -> { fpsCounter.inc(); setField(fps := iround(fpsCounter!)); imageSurface?.setImage(img); }); // 20 Hz is enough for everyone dm_doEvery(1000/20, r { if (enabled) { Timestamp deadline = tsNowPlusMS(1000/20); images.useScreen(screenNr); images.step(); long remaining = deadline.minus(tsNow()); remainingMSPerFrame.add(remaining); setField(remainingMS := iround(remainingMSPerFrame!)); } }); } visualize { screenSelector = new ScreenSelectorRadioButtons(dm_fieldLiveValue screenNr()); imageSurface = pixelatedImageSurface().setAutoZoomToDisplay(true); var vis = centerAndSouth( withMargin(jscroll_centered(imageSurface)), vstackWithSpacing( super.visualize(), jline( dm_fieldLabelWithLabel fps(), dm_fieldLabelWithLabel remainingMS("Remaining ms") ))); addControl(screenSelector); addControl(withLabelToTheRight("p", dm_spinner pixelRows(1, 500))); addControl(dm_spinnerWithLabel colors("Colors", 2, 256)); ret vis; } }