Skip to content

Commit 5131d22

Browse files
committed
Implemented automatic window snap to image displayed in Firefox application
1 parent 4abb91c commit 5131d22

2 files changed

Lines changed: 128 additions & 42 deletions

File tree

Src/ScreenGrid.Models/FlatImage.cs

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,17 @@ public UInt32[] GetHorizontalStripe(int iy)
5555
return result;
5656
}
5757

58+
public UInt32[] GetVerticalStripe(int ix)
59+
{
60+
var result = new UInt32[this.Height];
61+
for (var iy = 0; iy < this.Height; iy++)
62+
{
63+
result[iy] = this.pixels[ix, iy];
64+
}
65+
66+
return result;
67+
}
68+
5869
// TODO: move to separate class
5970

6071
public static UInt32[] GetDerivative(UInt32[] stripe)
@@ -176,6 +187,51 @@ public static IList<Tuple<int, int>> IntersectionOfSegments(IEnumerable<IList<Tu
176187
return sum;
177188
}
178189

190+
public static Tuple<int, int> SegmentsWithMaxDistance(IList<Tuple<int, int>> segments)
191+
{
192+
if (segments.Count < 2)
193+
{
194+
throw new ArgumentException();
195+
}
196+
197+
var i1 = 0;
198+
var i2 = 0;
199+
var maxDistance = 0;
200+
for (var i = 0; i < segments.Count - 1; i++)
201+
{
202+
var distance = segments[i + 1].Item1 - segments[i].Item2;
203+
if (distance > maxDistance)
204+
{
205+
i1 = segments[i].Item2;
206+
i2 = segments[i + 1].Item1;
207+
maxDistance = distance;
208+
}
209+
}
210+
211+
return new Tuple<int, int>(i1, i2);
212+
}
213+
214+
public Models.Geometry.Rectangle FindBoundingsOfInnerImage()
215+
{
216+
// TODO: add tests for this method
217+
const int minimalSegmentLength = 8;
218+
219+
// TODO: Very simple method, need to perform more checks and do more searches
220+
var stripeCH = FlatImage.FindZeroSegments(FlatImage.GetDerivative(this.GetHorizontalStripe(this.Height / 2)), minimalSegmentLength);
221+
var stripeCV = FlatImage.FindZeroSegments(FlatImage.GetDerivative(this.GetVerticalStripe(this.Width / 2)), minimalSegmentLength);
222+
223+
var maxH = FlatImage.SegmentsWithMaxDistance(stripeCH);
224+
var maxV = FlatImage.SegmentsWithMaxDistance(stripeCV);
225+
226+
return new Geometry.Rectangle
227+
{
228+
X = maxH.Item1 + 1,
229+
Y = maxV.Item1 + 1,
230+
Width = maxH.Item2 - maxH.Item1 - 2,
231+
Height = maxV.Item2 - maxV.Item1 - 2,
232+
};
233+
}
234+
179235
public bool CompareWithFragment(FlatImage fragment, int startX, int startY)
180236
{
181237
for (var x = 0; x < fragment.Width; x++)
@@ -238,5 +294,30 @@ public bool CompareWithFragmentWithTolerance(FlatImage fragment, int startX, int
238294

239295
return res;
240296
}
297+
298+
public unsafe System.Drawing.Bitmap ToBitmap()
299+
{
300+
var bitmap = new System.Drawing.Bitmap(this.Width, this.Height, System.Drawing.Imaging.PixelFormat.Format24bppRgb);
301+
302+
const int pixelSize = 3;
303+
var rect = new System.Drawing.Rectangle(0, 0, bitmap.Width, bitmap.Height);
304+
var bitmapData = bitmap.LockBits(rect, System.Drawing.Imaging.ImageLockMode.WriteOnly, bitmap.PixelFormat);
305+
306+
for (var y = 0; y < bitmapData.Height; y++)
307+
{
308+
var row = (byte*)bitmapData.Scan0 + (y * bitmapData.Stride);
309+
for (var x = 0; x < bitmapData.Width; x++)
310+
{
311+
var pix = BitConverter.GetBytes(this.pixels[x, y]);
312+
row[x * pixelSize + 0] = pix[1];
313+
row[x * pixelSize + 1] = pix[2];
314+
row[x * pixelSize + 2] = pix[3];
315+
}
316+
}
317+
318+
bitmap.UnlockBits(bitmapData);
319+
320+
return bitmap;
321+
}
241322
}
242323
}

Src/ScreenGrid.ViewModels/ScreenGridViewModel.cs

Lines changed: 47 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -253,14 +253,14 @@ public string CaptionText
253253
{
254254
get
255255
{
256-
return String.Format(CultureInfo.InvariantCulture,
257-
"{0}\u00D7{1}",
256+
return String.Format(CultureInfo.InvariantCulture,
257+
"{0}\u00D7{1}",
258258
(int)(this.WindowWidth - 2 * OuterBorderWidth),
259259
(int)(this.WindowHeight - 2 * OuterBorderWidth - HeaderHeight));
260260
}
261261
}
262262

263-
public void SnapToRenderView()
263+
private IList<string> GetWindowClassNames()
264264
{
265265
var classNames = new List<string>();
266266

@@ -269,61 +269,66 @@ public void SnapToRenderView()
269269
var orw = Models.AppsInterop.OctaneRenderWindow.GetFromAllProcesses();
270270
classNames.AddRange(orw.Select(w => w.ClassName));
271271

272+
classNames.Add("MozillaWindowClass"); // TODO: do not use window class names, check any applications with topmost windows
273+
274+
return classNames;
275+
}
276+
277+
public void SnapToRenderView()
278+
{
272279
// select foreground window from several processes of supported applications
273-
var window = Models.AppsInterop.NativeWindow.GetTopMostWindow(classNames);
280+
var nativeWindow = Models.AppsInterop.NativeWindow.GetTopMostWindow(this.GetWindowClassNames());
274281

275-
if (window != null)
282+
if (nativeWindow != null)
276283
{
277-
switch (window.ClassName)
284+
if (nativeWindow.ClassName == Models.AppsInterop.PhotoViewerWindow.MainWindowClassName)
278285
{
279-
case Models.AppsInterop.PhotoViewerWindow.MainWindowClassName:
280-
{
281-
var photoViewerWindow = new Models.AppsInterop.PhotoViewerWindow(window.Handle);
286+
var photoViewerWindow = new Models.AppsInterop.PhotoViewerWindow(nativeWindow.Handle);
282287

283-
var rectViewedImage = photoViewerWindow.PhotoCanvasRect();
284-
if (!rectViewedImage.IsEmpty)
285-
{
286-
this.PositionWindow(Models.Geometry.Point.Zero, rectViewedImage);
287-
}
288+
var rectViewedImage = photoViewerWindow.PhotoCanvasRect();
289+
if (!rectViewedImage.IsEmpty)
290+
{
291+
this.PositionWindow(Models.Geometry.Point.Zero, rectViewedImage);
292+
}
293+
}
294+
else
295+
{
296+
Task.Factory.StartNew<Tuple<Models.Geometry.Rectangle, Models.Geometry.Point>>(() =>
297+
{
298+
var bitmap = nativeWindow.GetShot();
299+
var flatImage = new Models.FlatImage(bitmap);
288300

289-
break;
301+
Models.Geometry.Rectangle rectRenderedImage;
302+
if (Models.AppsInterop.OctaneRenderWindow.GetFromAllProcesses().Any(w => w.ClassName == nativeWindow.ClassName))
303+
{
304+
// TODO: remove this Octane Render specific code
305+
rectRenderedImage = Models.AppsInterop.OctaneRenderWindow.FindRenderedImageBorders(flatImage);
290306
}
291-
default: // TODO: check is Octane Render!
292-
//case Models.AppInterop.OctaneRender.OctaneRenderWindow.MainWindowClassName:
307+
else
308+
{
309+
rectRenderedImage = flatImage.FindBoundingsOfInnerImage();
310+
}
311+
312+
var nativeWindowLocation = new Models.Geometry.Point(nativeWindow.Location.X, nativeWindow.Location.Y);
313+
return new Tuple<Models.Geometry.Rectangle, Models.Geometry.Point>(rectRenderedImage, nativeWindowLocation);
314+
}).ContinueWith((t) =>
315+
{
316+
var rectRenderedImage = t.Result.Item1;
317+
var windowLocation = t.Result.Item2;
318+
if (!rectRenderedImage.IsEmpty)
293319
{
294-
Task.Factory.StartNew<Tuple<Models.Geometry.Rectangle, Models.Geometry.Point>>(() =>
295-
{
296-
var octaneWindow = new Models.AppsInterop.OctaneRenderWindow(window.Handle);
297-
var bitmap = octaneWindow.GetShot();
298-
var flatImage = new Models.FlatImage(bitmap);
299-
var rectRenderedImage = Models.AppsInterop.OctaneRenderWindow.FindRenderedImageBorders(flatImage);
300-
var octaneWindowLocation = new Models.Geometry.Point(octaneWindow.Location.X, octaneWindow.Location.Y);
301-
return new Tuple<Models.Geometry.Rectangle, Models.Geometry.Point>(rectRenderedImage, octaneWindowLocation);
302-
}).ContinueWith((t) =>
303-
{
304-
var rectRenderedImage = t.Result.Item1;
305-
var octaneWindowLocation = t.Result.Item2;
306-
if (!rectRenderedImage.IsEmpty)
307-
{
308-
this.PositionWindow(t.Result.Item2, t.Result.Item1);
309-
}
310-
},
311-
TaskScheduler.FromCurrentSynchronizationContext());
312-
313-
break;
320+
this.PositionWindow(t.Result.Item2, t.Result.Item1);
314321
}
315-
//default:
316-
//{
317-
// throw new NotSupportedException(window.ClassName);
318-
//}
322+
},
323+
TaskScheduler.FromCurrentSynchronizationContext());
319324
}
320325
}
321326
}
322327

323328
private void PositionWindow(Models.Geometry.Point parentLocation, Models.Geometry.Rectangle rectRenderedImage)
324329
{
325330
var diffX = 0.0;
326-
var diffY = HeaderHeight;
331+
var diffY = HeaderHeight;
327332
// TODO: remove magiс numbers
328333
this.WindowLeft = rectRenderedImage.Left + parentLocation.X - diffX - 1;
329334
this.WindowTop = rectRenderedImage.Top + parentLocation.Y - diffY - 1;

0 commit comments

Comments
 (0)