Uses 1482K of libraries. Click here for Pure Java version (12308L/65K).
1 | !7 |
2 | |
3 | need latest onSelectedItem. |
4 | |
5 | !include early #1033506 // Compact Module Include Gazelle V |
6 | |
7 | module GazelleV > DynPrintLogAndEnabled { |
8 | /*switchable int x1; |
9 | switchable int y1; |
10 | switchable int w = screenWidth(); |
11 | switchable int h = screenHeight();*/ |
12 | switchable double maxFPS = 10; |
13 | switchable int previewWidth = 200; |
14 | switchable double statsEvery = 5; |
15 | switchable volatile bool brokenMethod; |
16 | int lookAtScreen; |
17 | S previewMode; |
18 | int gridRows = 20; |
19 | bool scaleDownPreviewImage; |
20 | int previewPosterization = 256; |
21 | S uiURL = "Home"; |
22 | //bool deepCompression; |
23 | |
24 | PersistableThrowable lastProcessingError; |
25 | transient WithTimestamp<OKOrError<BufferedImage>> lastScreen; |
26 | transient new Average screenShotTime; |
27 | transient new Average iiTime; |
28 | transient Timestamp loopStarted; |
29 | transient Sleeping screenShotCountdown; |
30 | transient long screenShotCountdownTotalSleepTime; |
31 | //transient ImageSurface imageSurface; |
32 | transient IIImageSurface imageSurface; |
33 | transient Throwable currentProcessingError; |
34 | transient JButton popDownButton; |
35 | transient JComponent topControls; |
36 | transient long screenShotsTaken; |
37 | transient double fps; |
38 | transient Compression ongoingCompression, completedCompression; |
39 | transient S longTermCompressionProbability; |
40 | transient S longTermCompressionScore; |
41 | transient JComponent topControls2; |
42 | transient JTabbedPane tabs; |
43 | transient ThreadPool threadPool; |
44 | transient SingleComponentPanel mainArea; |
45 | |
46 | // key: ui URL |
47 | // value: component maker |
48 | transient Map<S, IF0<JComponent>> uiMap = syncCIMap(); |
49 | |
50 | // ping source for everything we do in the background, including |
51 | // analyzing current screen. |
52 | // normally limited to 50% of one core. |
53 | transient PingSource backgroundPingSource; |
54 | |
55 | transient OnOffOscillator onOffOscillator; |
56 | |
57 | Rect area() { |
58 | ret screenBounds(min(lookAtScreen, screenCount()-1)); |
59 | } |
60 | |
61 | class Compression { |
62 | settable BufferedImage image; |
63 | settable BWIntegralImage ii; |
64 | CompressionSearch_AnyType search; |
65 | int baseLineLength; // length of trivial compression |
66 | GridCodec1 codec; |
67 | double compressionPercentage; |
68 | |
69 | S score() { |
70 | var submission = search?.bestSubmission(); |
71 | Int compressedSize = or(submission.compressedSize(), baseLineLength); |
72 | compressionPercentage = 100-doubleRatio(compressedSize, baseLineLength)*100; |
73 | ret iround(compressionPercentage) + "%"; |
74 | } |
75 | |
76 | IProbabilisticScheduler scheduler() { |
77 | ret search?.scheduler(); |
78 | } |
79 | |
80 | S probability() { |
81 | var scheduler = scheduler(); |
82 | if (scheduler == null) ret "-"; |
83 | var prob = scheduler.lastExecutedProbability(); |
84 | ret formatDouble(prob, 3); |
85 | } |
86 | |
87 | event compressionDone; |
88 | |
89 | run { |
90 | int lHex = pixelCount(image)*8; |
91 | baseLineLength = calculateLengthOfFunctionCall imageFromHex(lHex+2); |
92 | print(+baseLineLength); |
93 | |
94 | codec = new GridCodec1(image); |
95 | codec.rows = gridRows; |
96 | search = codec.forward(); |
97 | //showImage(codec.renderCellsLinearly()); |
98 | |
99 | repeat 60 { |
100 | stepForNSeconds(1, search); |
101 | //print(stepCount := search.scheduler().stepCount()); |
102 | setFields( |
103 | longTermCompressionProbability := probability(), |
104 | longTermCompressionScore := score()); |
105 | } |
106 | |
107 | try { |
108 | compressionDone(); |
109 | |
110 | L<IJavaExpr> cellCompressions = codec.strat.codeForElements(); |
111 | //showText("Compression", lines(map shorten_str(cellCompressions))); |
112 | |
113 | /*S code = str(codec.winnerCode()); |
114 | S name = "Screenshot " + ymdMinusHMS(); |
115 | File f = makeFileNameUnique_beforeExtension( |
116 | javaxDataDir("Compressed Screenshots/" + name + ".javax")); |
117 | saveTextFile(f, code); |
118 | printFileInfo(f); |
119 | saveGZTextFile(f = replaceExtension(f, ".jgz"), code); |
120 | printFileInfo(f);*/ |
121 | |
122 | /* |
123 | print("Evaling " + nChars(code)); |
124 | //LL<Int> lol = cast dm_javaEval(code); |
125 | BufferedImage restored = cast dm_javaEval(code); |
126 | assertImagesIdentical(restored, image); |
127 | print("Image restored!"); |
128 | |
129 | dm_showImage("Restored from " + nChars(code), restored); |
130 | */ |
131 | } catch e { printStackTrace(e); } |
132 | } |
133 | |
134 | void drawOverlay(ImageSurface is, Graphics2D g) pcall { |
135 | codec.drawOverlay(is, g); |
136 | } |
137 | |
138 | int gridCols() { ret widthForHeight(ii.getWidth(), ii.getHeight(), gridRows); } |
139 | } // end of Compression |
140 | |
141 | int gridCols(MakesBufferedImage ii) { ret widthForHeight(ii.getWidth(), ii.getHeight(), gridRows); } |
142 | |
143 | start { |
144 | if (threadPool == null) threadPool = maxThreadPool(); |
145 | |
146 | backgroundPingSource = new PingSource(threadPool, "Background"); |
147 | |
148 | onOffOscillator = new OnOffOscillator(backgroundPingSource); |
149 | ownResource(onOffOscillator); |
150 | onOffOscillator.start(); |
151 | |
152 | componentFieldsToKeep = litset("tabs"); |
153 | |
154 | dm_registerAs_direct gazelleV(); |
155 | dm_watchField enabled(r { if (enabled) dm_reload(); }); |
156 | |
157 | // TODO: react to event sent by OS |
158 | dm_doEvery(1.0, r { |
159 | if (dm_moduleIsPoppedOut()) adjustUIForPoppedOutWindow(); |
160 | }); |
161 | |
162 | print("Screen bounds: " + allScreenBounds()); |
163 | |
164 | initUIMap(); |
165 | |
166 | // We are making some UI elements even if the module is hidden |
167 | // because that's easier. |
168 | |
169 | tabs = jtabs( |
170 | "Log", super.visualize(), |
171 | "Local Data", jcenteredlabel("TODO")); |
172 | |
173 | dm_doEvery(min(statsEvery, 10.0), statsEvery, r { if (enabled) printStats(); }); |
174 | |
175 | loopStarted = tsNow(); |
176 | shootAndAnalyze(); |
177 | } |
178 | |
179 | void shootAndAnalyze { |
180 | backgroundPingSource.do(r shootAndAnalyze_impl); |
181 | } |
182 | |
183 | void shootAndAnalyze_impl enter { |
184 | if (!enabled) ret; |
185 | |
186 | var targetTime = tsNow().plus(1.0/maxFPS); |
187 | long time = nanoTime(); |
188 | |
189 | try { |
190 | lastScreen = withTimestamp(okOrError(-> screenshot(area()))); |
191 | ++screenShotsTaken; |
192 | setField(fps := doubleRatio(screenShotsTaken, elapsedSeconds(loopStarted))); |
193 | screenShotTime.add(nanosToSeconds(nanoTime()-time)); |
194 | time = nanoTime(); |
195 | ping(); |
196 | if (lastScreen->isOK()) { |
197 | var img = lastScreen!!; |
198 | var ii = BWIntegralImage(img); |
199 | //var ii = BWIntegralImage_luminosity(lastScreen!!); |
200 | iiTime.add(nanosToSeconds(nanoTime()-time)); |
201 | |
202 | if (!deepCompression()) |
203 | showPreview(ii, null); |
204 | else |
205 | startCompression(ii); |
206 | } |
207 | |
208 | cpuTotal(); |
209 | currentProcessingError = null; |
210 | } catch print e { |
211 | setField(lastProcessingError := currentProcessingError = e); |
212 | //sleepSeconds(60); // errors in processing are no good |
213 | } |
214 | |
215 | screenShotCountdown = sleeper().doLater(targetTime, r shootAndAnalyze); |
216 | screenShotCountdownTotalSleepTime += screenShotCountdown.remainingMS(); |
217 | } |
218 | |
219 | bool deepCompression() { ret !scaleDownPreviewImage; } |
220 | |
221 | void showPreview(BWIntegralImage ii, Compression completedCompression) { |
222 | IBWIntegralImage ii2 = ii; |
223 | if (scaleDownPreviewImage) { |
224 | ii2 = ScaledIBWIntegralImage(gridCols(ii2), gridRows, ii2); |
225 | ii2 = TruncatedIBWIntegralImage(ii2); |
226 | |
227 | if (previewPosterization < 256) |
228 | imageSurface?.setImage(posterizeIBWImage(previewPosterization, ii2)); |
229 | else |
230 | imageSurface?.setImage(ii2); |
231 | |
232 | imageSurface.setOverlay(null); |
233 | } else { |
234 | imageSurface?.setImage(completedCompression.ii, completedCompression.image); |
235 | if (completedCompression != null) |
236 | imageSurface.setOverlay(g -> completedCompression.drawOverlay(imageSurface, g)); |
237 | } |
238 | } |
239 | |
240 | void startCompression(BWIntegralImage ii) { |
241 | if (ongoingCompression == null) { |
242 | ping(); |
243 | ongoingCompression = new Compression() |
244 | .image(lastScreen!!) |
245 | .ii(ii); |
246 | ongoingCompression.onCompressionDone(-> { |
247 | completedCompression = ongoingCompression; |
248 | ongoingCompression = null; |
249 | |
250 | showPreview(ii, completedCompression); |
251 | }); |
252 | ongoingCompression.run(); |
253 | } |
254 | } |
255 | |
256 | void printStats() { print(stats()); } |
257 | |
258 | S stats() { |
259 | var screen = lastScreen?!; |
260 | ret formatColonProperties_noNulls( |
261 | "Monitored area", area(), |
262 | "Error", screen?.getError(), |
263 | "Last screenshot taken", lastScreen?.timeStamp(), |
264 | "Average time to take a screenshot", screenShotTime, |
265 | "Average time to make integral image", iiTime, |
266 | "Capacity left in first core", percentRatioStrOneDigit(freeInCore()), |
267 | "Total CPU used", cpuTotal(), |
268 | ); |
269 | } |
270 | |
271 | S cpuTotal() { |
272 | ret setFieldAndReturn(cpuTotal := percentRatioStrOneDigit((1-freeInCore())/numberOfCores())); |
273 | } |
274 | |
275 | double freeInCore() { |
276 | ret ratio(toSeconds(screenShotCountdownTotalSleepTime), |
277 | elapsedSeconds(loopStarted)); |
278 | } |
279 | |
280 | replace jSection with jCenteredSection. |
281 | {} |
282 | visualize { |
283 | imageSurface = new IIImageSurface; |
284 | setDoubleBuffered(imageSurface, true); |
285 | imageSurface.setPixelated(true); |
286 | imageSurface.noAlpha = true; |
287 | |
288 | ret northAndCenterWithMargin( |
289 | withMargin(withLabel("Show", |
290 | comboBoxAndButton( |
291 | onSelectedItem( |
292 | bindComboBoxToLiveValue(centerComboBox(autoComboBox(uiURL, cloneKeys(uiMap))), dm_fieldLiveValue uiURL()), |
293 | url -> showUIURL(url) |
294 | ), |
295 | "Go", url -> showUIURL(url)))), |
296 | mainArea = singleComponentPanel(renderUIUrl(uiURL))); |
297 | } |
298 | |
299 | BufferedImage lastScreenImage() { ret getVar(getVar(lastScreen)); } |
300 | |
301 | void adjustUIForPoppedOutWindow swing { |
302 | if (popDownButton == null) { |
303 | Window window = cast getWindow(dm_vis()); |
304 | |
305 | var popBackInButton = findButton(window, "Pop Back In"); |
306 | var parent = getParent(popBackInButton); |
307 | print(+parent); |
308 | |
309 | removeFromParent(parent); // hide controls |
310 | |
311 | popDownButton = jPopDownButton_noText( |
312 | "Pop Back In", r { dm_popInModule(me()) }, |
313 | jCheckBoxMenuItem("Always On Top", isAlwaysOnTop(window), |
314 | rEnter dm_toggleAlwaysOnTop), |
315 | ); |
316 | addControl(popDownButton); |
317 | } |
318 | } |
319 | |
320 | public bool useErrorHandling() { false; } |
321 | |
322 | JComponent renderUIUrl aka renderUIURL aka uiGet(S url) { |
323 | var maker = uiMap.get(url); |
324 | var component = callF(maker); |
325 | if (component != null) ret component; |
326 | ret jCenteredLabel("URL not found: " + url); |
327 | } |
328 | |
329 | void showUIURL(S url) enter { |
330 | url = trim(url); |
331 | setComponent(mainArea, renderUIUrl(url)); |
332 | } |
333 | |
334 | <A extends JComponent> A uiMapPut(S url, IF0<A> maker) { |
335 | uiMap.put(url, -> maker!); |
336 | ret maker!; |
337 | } |
338 | |
339 | void uiPut(S url, IF0<JComponent> maker) { |
340 | uiMap.put(url, maker); |
341 | } |
342 | |
343 | void initUIMap { |
344 | uiPut("FPS", -> jSection("FPS", dm_calculatedCenteredLabel(() -> iround(fps)))); |
345 | |
346 | uiPut("Screen Selector", -> { |
347 | var screenSelectors = jRadioButtons( |
348 | countIteratorAsList(screenCount(), |
349 | i -> "Screen " + (i+1))); |
350 | selectRadioButton(screenSelectors, lookAtScreen); |
351 | onRadioButtonChange(screenSelectors, i -> { setField(lookAtScreen := i); }); |
352 | ret jline(buttonsInGroup(screenSelectors)); |
353 | }); |
354 | |
355 | uiPut("Screenshot with controls", -> northAndCenterWithMargin( |
356 | topControls2 = /*jflowCenter*/ jPanel(new WrapLayout, flattenList2( |
357 | dm_checkBox enabled(), |
358 | getChildren(renderUIURL("Screen Selector")), |
359 | dm_spinnerWithLabel gridRows("Rows", 1, 500), |
360 | dm_checkBox("Scale down", "scaleDownPreviewImage"), |
361 | dm_spinnerWithLabel previewPosterization("Posterize", 2, 256) |
362 | )), |
363 | jscroll_center(imageSurface))); |
364 | |
365 | uiPut("Enabled", -> dm_checkBox enabled()); |
366 | |
367 | uiPut("Home", -> |
368 | hsplit(northAndCenterWithMargin( |
369 | topControls = hgrid( |
370 | renderUIUrl("FPS"), |
371 | jSection("Compression", dm_centeredFieldLabel longTermCompressionScore()), |
372 | //jSection("Probability", dm_centeredFieldLabel longTermCompressionProbability()), |
373 | jSection("CPU (" + numberOfCores() + ")", dm_centeredFieldLabel cpuTotal()) |
374 | ), |
375 | tabs), |
376 | renderUIUrl("Screenshot with controls") |
377 | )); |
378 | } |
379 | } // end of module |
Began life as a copy of #1033508
download show line numbers debug dex old transpilations
Travelled to 2 computer(s): bhatertpkbcr, mqqgnosmbjvj
No comments. add comment
Snippet ID: | #1033526 |
Snippet name: | Gazelle V, November Edition |
Eternal ID of this version: | #1033526/70 |
Text MD5: | 7e31572c1a1a6c6d683edf8c477da725 |
Transpilation MD5: | c466d7399a6a381f7245d715acbbc9c7 |
Author: | stefan |
Category: | javax / screen recognition |
Type: | JavaX source code (Dynamic Module) |
Public (visible to everyone): | Yes |
Archived (hidden from active list): | No |
Created/modified: | 2021-12-25 23:25:12 |
Source code size: | 12322 bytes / 379 lines |
Pitched / IR pitched: | No / No |
Views / Downloads: | 252 / 561 |
Version history: | 69 change(s) |
Referenced in: | [show references] |