!7 !include early #1033884 // Compact Module Include Gazelle V [dev version] module GazelleScreenCam { !include early #1025212 // +Enabled without visualization int pixelRows = 128, colors = 8; S script, testScreenScript; bool animate = true; bool horizontalLayout; // flat layout WatchTarget watchTarget; transient ImageSurface isPosterized, isRegions, isTestScreen; transient new ScreenCamStream imageStream; transient new BWIntegralImageStream integralImages; transient new SourceTriggeredStream scaledAndPosterizedImages; transient new DoubleFPSCounter fpsCounter; transient int fps; //transient ScreenSelectorRadioButtons screenSelector; transient WatchTargetSelector watchTargetSelector; transient new RollingAverage remainingMSPerFrame; transient int remainingMS; transient new FunctionTimings functionTimings; transient ReliableSingleThread rstRunScript = dm_rst(me(), r _runScript); transient JGazelleVScriptRunner scriptRunner; transient JGazelleVScriptRunner testScreenScriptRunner; transient Animation animation; transient BWImage_FastRegions mainScreenRegions; transient ImageSurface_HighlightRegion regionHighlighter; transient UIURLSystem uiURLs; // set by host transient Concepts concepts; S uiURL; transient FileWatchService fileWatcher; start { dm_onFieldChange horizontalLayout( //r dm_revisualize // deh buggy r dm_reload ); // non-standalone mode (doesn't work yet) if (concepts == null) { db(); concepts = db_mainConcepts(); } uiURLs = new UIURLSystem(me(), dm_fieldLiveValue uiURL()); scriptRunner = new JGazelleVScriptRunner(dm_fieldLiveValue script(me())); testScreenScriptRunner = new JGazelleVScriptRunner(dm_fieldLiveValue testScreenScript(me())); imageStream.directlyFeedInto(integralImages); integralImages.onNewElement(ii -> scaledAndPosterizedImages.newElement( posterizeBWImage_withMeta(colors, scaledBWImageFromBWIntegralImage_withMeta_height(pixelRows, ii)))); integralImages.onNewElement(r { if (shouldRunScript()) rstRunScript.go(); }); scaledAndPosterizedImages.onNewElement(img -> { fpsCounter.inc(); setField(fps := iround(fpsCounter!)); isPosterized?.setImage_thisThread(img); floodFill(img); }); // 20 Hz is enough for everyone dm_doEvery(1000/20, r { if (enabled) { watchTargetSelector?.updateScreenCount(); Timestamp deadline = tsNowPlusMS(1000/20); if (watchTarget cast WatchScreen) imageStream.useScreen(watchTarget.screenNr-1); else if (watchTarget cast WatchMouse) imageStream.area(mouseArea(watchTarget.width, watchTarget.height)); imageStream.step(); long remaining = deadline.minus(tsNow()); remainingMSPerFrame.add(remaining); setField(remainingMS := iround(remainingMSPerFrame!)); } }); startDirWatcher(); for (f : allImageFiles(galleryDir())) addToGallery(f); } void startDirWatcher { fileWatcher = new FileWatchService; fileWatcher.addRecursiveListener(picturesDir(), file -> { if (!isImageFile(f)) ret; addToGallery(file); }); } ImageSurface stdImageSurface() { ret pixelatedImageSurface().setAutoZoomToDisplay(true).repaintInThread(false); } visualize { /*screenSelector = new ScreenSelectorRadioButtons(dm_fieldLiveValue screenNr()); screenSelector.compactLayout(true); screenSelector.hideIfOnlyOne(true); screenSelector.screenLabel("");*/ isPosterized = stdImageSurface(); regionHighlighter = new ImageSurface_HighlightRegion(isPosterized); regionHighlighter.regionPainter(new RegionFillPainter); isRegions = stdImageSurface(); isTestScreen = stdImageSurface(); // when test screen is visible, do the animation awtEvery(isTestScreen, 1000/20, r stepAnimation); JTextArea taTimings = jTextArea_noUndo(); awtEveryAndNow(taTimings, .5, r { setText(taTimings, renderFunctionTimings()) }); var jSpeedInfo = dm_transientCalculatedToolTip speedInfo_long(rightAlignLabel(dm_transientCalculatedLabel speedInfo())); print("Labels: " + list(concepts, Label)); SimpleCRUD_v2 labelCRUD = new(concepts, Label); labelCRUD.hideFields("globalID"); labelCRUD.addCountToEnclosingTab(true); SimpleCRUD_v2 imageCRUD = new(concepts, GalleryImage); imageCRUD.addCountToEnclosingTab(true); // main visual var tabs = scrollingTabs(jTopOrLeftTabs(horizontalLayout, Posterized := withTools(isPosterized), Regions := jscroll_centered_borderless(isRegions), Gallery := wrapCRUD(imageCRUD), Labels := wrapCRUD(labelCRUD), Script := scriptRunner.scriptAndResultPanel(), "Test Screen" := testScreenPanel(), Timings := withRightAlignedButtons(taTimings, Reset := r resetTimings) )); // for tab titles labelCRUD.update(); imageCRUD.update(); watchTargetSelector = new WatchTargetSelector; var vis = northAndCenter( withSideAndTopMargin(uiURLs.urlBar()), centerAndSouthOrEast(horizontalLayout, horizontalLayout ? withMargin(tabs) : withSideMargin(tabs), /*withMargin*/(borderlessScrollPane(jHigherScrollPane( jfullcenter(vstack( withLeftAndRightMargin(hstack( //dm_rcheckBox enabled("Watch screen"), dm_rcheckBox enabled("Watch"), //screenSelector.visualize(), watchTargetSelector.visualize(), jlabel(" in "), withLabelToTheRight("colors @ ", dm_spinner colors(2, 256)), //withLabelToTheRight("p", dm_spinner pixelRows(1, 500)), withLabelToTheRight("p", dm_powersOfTwoSpinner pixelRows(512)), , /* speed info can also go here */ )), verticalStrut(2), withRightMargin(jSpeedInfo) ))))), )); setHorizontalMarginForAllButtons(vis, 4); ret vis; } S speedInfo() { ret "FPS " + fps + " idle " + remainingMS + " ms"; } S speedInfo_long() { ret "Screen cam running at " + nFrames(fps) + "/second. " + n2(remainingMS) + " ms remaining per frame in first core."; } void floodFill(BWImage img) { BWImage_FastRegions ff = new(img); //ff.tolerance(0); ff.collectBounds(); functionTimings.time("Regions", ff); mainScreenRegions = ff; regionHighlighter?.setRegions(ff); if (isRegions != null && isRegions.isShowing_quick()) { //print("Showing regions image"); isRegions.setImage_thisThread(ff.regionsImage()); } setEnclosingTabTitle(isRegions, nRegions(ff.regionCount())); } bool useErrorHandling() { false; } S renderFunctionTimings() { ret lines(ciSorted(map(functionTimings!, (f, avg) -> firstToUpper(f) + ": " + n2(iround(nsToMicroseconds(avg!))) + " " + microSymbol() + "s (" + n2(iround(avg.n())) + ")"))); } transient long _runScript_idx; void _runScript() { long n = integralImages.elementCount(); if (n > _runScript_idx) { _runScript_idx = n; scriptRunner.parseAndRunOn(integralImages!); } } bool shouldRunScript() { ret isShowing(scriptRunner.scpScriptResult); } void stepAnimation { if (!animate) ret; if (animation == null) { animation = new AnimatedLine; animation.start(); } animation.nextFrame(); var img = whiteImage(animation.w, animation.h); animation.setGraphics(createGraphics(img)); animation.paint(); isTestScreen?.setImage(img); var ii = bwIntegralImage_withMeta(img); testScreenScriptRunner.parseAndRunOn(ii); } JComponent testScreenPanel() { ret centerAndSouthWithMargin( hsplit( northAndCenterWithMargin(centerAndEastWithMargin( jlabel("Input"), dm_fieldCheckBox animate()), jscroll_centered_borderless(isTestScreen)), northAndCenterWithMargin(centerAndEastWithMargin( jlabel("Output"), testScreenScriptRunner.lblScore), testScreenScriptRunner.scpScriptResult) ), testScreenScriptRunner.scriptInputField() ); } L popDownItems() { ret ll(jCheckBoxMenuItem_dyn("Horizontal Layout", -> horizontalLayout, b -> setField(horizontalLayout := b))); } void unvisualize {} // don't zero transient component fields void resetTimings { functionTimings.reset(); } // add tool side bar to image surface JComponent withTools(ImageSurface is) { new ImageSurface_PositionToolTip(is); ret centerAndEastWithMargin(jscroll_centered_borderless(is), vstackWithSpacing(3, jimageButtonScaledToWidth(16, #1103054, "Save screenshot in gallery", rThread saveScreenshotToGallery), )); } class WatchTargetSelector { JComboBox cb = jComboBox(); int screenCount; visualize { updateList(); print("Selecting watchTarget: " + watchTarget); selectItem(cb, watchTarget); main onChange(cb, watchTarget -> { setField(+watchTarget); print("Chose watchTarget: " + GazelleScreenCam.this.watchTarget); }); ret cb; } void updateScreenCount() { if (screenCount != screenCount()) updateList(); } void updateList() swing { setComboBoxItems(cb, makeWatchTargets()); } L makeWatchTargets() { ret flattenToList( countIteratorAsList_incl(1, screenCount = screenCount(), i -> WatchScreen(i)), new WatchMouse ); } } JComponent wrapCRUD(SimpleCRUD_v2 crud) { ret crud == null ?: withTopMargin(jRaisedSection(withMargin(crud.make_dontStartBots()))); } File galleryDir() { ret picturesDir(gazelle22_imagesSubDirName()); } void saveScreenshotToGallery enter { var img = imageStream!; saveImageWithCounter(galleryDir(), "Screenshot", img); } void addToGallery(File imgFile) { if (!isImageFile(imgFile)) ret; var img = uniq(concepts, GalleryImage, path := imgFile); printVars("addToGallery", +imgFile, +img); } } // GalleryImageule concept Label > ConceptWithGlobalID { S name; //new RefL examples; toString { ret "Label " + name; } } concept GalleryImage { File path; } concept SavedRegion { Concept image; Rect area; //new RefL