public override Bitmap GetImage() { Bitmap image = base.GetImage(); logger_.Verbose("Bitmap Size: {0}x{1}", image.Width, image.Height); eyes_.DebugScreenshotProvider.Save(image, "ANDROID"); if (eyes_.IsCutProviderExplicitlySet) { return(image); } Size originalViewportSize = GetViewportSize(); logger_.Verbose("logical viewport size: " + originalViewportSize); int imageWidth = image.Width; int imageHeight = image.Height; logger_.Verbose("physical device pixel size: {0}x{1}", imageWidth, imageHeight); float widthRatio = image.Width / (float)originalViewportSize.Width; float height = widthRatio * originalViewportSize.Height; Rectangle cropRect = new Rectangle(0, 0, imageWidth, (int)Math.Round(height)); Bitmap croppedImage = BasicImageUtils.Crop(image, cropRect); image.Dispose(); return(croppedImage); }
/// <summary> /// Encapsulates an algorithm for creating full-page images of a page. /// </summary> /// <param name="positionProvider">The position provider used for stitching.</param> /// <returns>The screenshot as PNG byte array.</returns> public byte[] GetFullPageScreenshot(IPositionProvider positionProvider) { using (Bitmap screenshotImage = GetStitchedRegion(Region.Empty, Region.Empty, positionProvider, positionProvider)) { return(BasicImageUtils.EncodeAsPng(screenshotImage)); } }
public Bitmap GetImage() { byte[] screenshotBytes; Bitmap result; if (eyes_.CachedViewport.IsEmpty) { try { string screenshot64 = (string)jsExecutor_.ExecuteScript("mobile: viewportScreenshot"); screenshotBytes = Convert.FromBase64String(screenshot64); } catch (Exception e) { logger_.Log("Warning: " + e); screenshotBytes = tsInstance_.GetScreenshot().AsByteArray; } result = BasicImageUtils.CreateBitmap(screenshotBytes); } else { screenshotBytes = tsInstance_.GetScreenshot().AsByteArray; Bitmap screenshotBitmap = BasicImageUtils.CreateBitmap(screenshotBytes); eyes_.DebugScreenshotProvider.Save(screenshotBitmap, "DEVICE_ORIGINAL"); result = BasicImageUtils.Crop(screenshotBitmap, eyes_.CachedViewport); } result = BasicImageUtils.ScaleImage(result, eyes_.ScaleRatio); return(result); }
//[Test] //[Ignore("fails if not set up correctly")] public void TestSetViewportSize() { ChromeOptions options = new ChromeOptions(); options.AddArgument("disable-infobars"); if (TestUtils.RUN_HEADLESS) { options.AddArgument("headless"); } IWebDriver driver = new RemoteWebDriver(options); Size expectedSize = new Size(700, 499); try { driver.Url = "http://viewportsizes.com/mine/"; SeleniumEyes.SetViewportSize(driver, expectedSize); Bitmap screenshot = BasicImageUtils.CreateBitmap(((ITakesScreenshot)driver).GetScreenshot().AsByteArray); screenshot.Save($@"{logsPath_}\TestSetViewportSize.png"); Size actualSize = SeleniumEyes.GetViewportSize(driver); Assert.AreEqual(expectedSize, actualSize, "Sizes differ"); } finally { driver.Quit(); } }
public override EyesScreenshot GetSubScreenshot(Region region, bool throwIfClipped) { logger_.Verbose(nameof(GetSubScreenshot) + "([{0}], {1})", region, throwIfClipped); ArgumentGuard.NotNull(region, nameof(region)); // We calculate intersection based on as-is coordinates. Region asIsSubScreenshotRegion = GetIntersectedRegion(region, CoordinatesTypeEnum.SCREENSHOT_AS_IS); if (asIsSubScreenshotRegion.IsSizeEmpty || (throwIfClipped && !asIsSubScreenshotRegion.Size.Equals(region.Size))) { throw new OutOfBoundsException($"Region [{region}] is out of screenshot bounds [{frameWindow_}]"); } Bitmap subScreenshotImage = BasicImageUtils.Crop(Image, asIsSubScreenshotRegion.ToRectangle()); // The frame location in the sub screenshot is the negative of the // context-as-is location of the region. Point contextAsIsRegionLocation = ConvertLocation(asIsSubScreenshotRegion.Location, CoordinatesTypeEnum.SCREENSHOT_AS_IS, CoordinatesTypeEnum.CONTEXT_AS_IS); Point frameLocationInSubScreenshot = new Point(-contextAsIsRegionLocation.X, -contextAsIsRegionLocation.Y); EyesWebDriverScreenshot result = new EyesWebDriverScreenshot(logger_, driver_, subScreenshotImage, subScreenshotImage.Size, Point.Empty); result.UpdateFrameLocationInScreenshot_(new Point(-region.Location.X, -region.Location.Y)); result.DomUrl = DomUrl; logger_.Verbose("Done!"); return(result); }
public void TestScaleDownImage2() { Bitmap src = LoadBitmapFromResource("scale_bug_to_540x768.png"); Bitmap expected = LoadBitmapFromResource("ScaledDownTo540x768Image.png"); Bitmap dest = BasicImageUtils.ScaleImage(src, 540 / (double)src.Width); CompareBitmaps_(expected, dest); }
private Bitmap CropScreenshot_(Bitmap initialScreenshot, Region regionInScreenshot) { if (!regionInScreenshot.IsSizeEmpty) { Bitmap croppedInitialScreenshot = BasicImageUtils.Crop(initialScreenshot, regionInScreenshot.ToRectangle()); initialScreenshot.Dispose(); initialScreenshot = croppedInitialScreenshot; SaveDebugScreenshotPart_(croppedInitialScreenshot, regionInScreenshot.ToRectangle(), "cropped"); } return(initialScreenshot); }
public override Bitmap GetImage() { Bitmap image = base.GetImage(); logger_.Verbose("Bitmap Size: {0}x{1}", image.Width, image.Height); eyes_.DebugScreenshotProvider.Save(image, "SAFARI"); if (eyes_.IsCutProviderExplicitlySet) { return(image); } double scaleRatio = eyes_.DevicePixelRatio; Size originalViewportSize = GetViewportSize(); Size viewportSize = new Size( (int)Math.Ceiling(originalViewportSize.Width * scaleRatio), (int)Math.Ceiling(originalViewportSize.Height * scaleRatio)); logger_.Verbose("logical viewport size: " + originalViewportSize); if (userAgent_.OS.Equals(OSNames.IOS)) { image = CropIOSImage(image, originalViewportSize, logger_); } else if (!eyes_.ForceFullPageScreenshot) { Point loc; FrameChain currentFrameChain = eyes_.GetDriver().GetFrameChain(); if (currentFrameChain.Count == 0) { IWebElement scrollRootElement = eyes_.GetCurrentFrameScrollRootElement(); IPositionProvider positionProvider = SeleniumPositionProviderFactory.GetPositionProvider(logger_, StitchModes.Scroll, jsExecutor_, scrollRootElement, userAgent_); loc = positionProvider.GetCurrentPosition(); } else { loc = currentFrameChain.GetDefaultContentScrollPosition(); } loc = new Point((int)Math.Ceiling(loc.X * scaleRatio), (int)Math.Ceiling(loc.Y * scaleRatio)); image = BasicImageUtils.Crop(image, new Rectangle(loc, viewportSize)); } eyes_.DebugScreenshotProvider.Save(image, "SAFARI_CROPPED"); return(image); }
internal static Bitmap CropIOSImage(Bitmap image, Size originalViewportSize, Logger logger = null) { if (logger == null) { logger = new Logger(); } if (devicesRegions == null) { InitDeviceRegionsTable_(); } int imageWidth = image.Width; int imageHeight = image.Height; logger.Verbose("physical device pixel size: {0}x{1}", imageWidth, imageHeight); if (devicesRegions.TryGetValue(image.Size, out List <Rectangle> resolutions)) { int renderedWidth = resolutions[0].Width; int relevantViewportHeight = (renderedWidth < image.Width) ? originalViewportSize.Height - 21 : originalViewportSize.Height; float widthRatio = renderedWidth / (float)originalViewportSize.Width; float height = widthRatio * relevantViewportHeight; if (Math.Abs(height - image.Height) > 1.5) { Rectangle bestMatchingRect = resolutions[0]; float bestHeightDiff = Math.Abs(bestMatchingRect.Height - height); logger.Verbose("bestMatchingRect: {0} ; bestHeightDiff: {1}", bestMatchingRect, bestHeightDiff); for (int i = 1; i < resolutions.Count; ++i) { Rectangle rect = resolutions[i]; float heightDiff = Math.Abs(rect.Height - height); logger.Verbose("rect: {0} ; heightDiff: {1} ; bestHeightDiff: {2}", rect, heightDiff, bestHeightDiff); if (heightDiff < bestHeightDiff) { bestHeightDiff = heightDiff; bestMatchingRect = rect; logger.Verbose("updated bestHeightDiff to {0} and bestMatchingRect to {1}", bestHeightDiff, bestMatchingRect); } } logger.Verbose("closest crop rect found: {0}", bestMatchingRect); image = BasicImageUtils.Crop(image, bestMatchingRect); } else { logger.Verbose("no crop needed. must be using chrome emulator."); } } return(image); }
public override EyesScreenshot GetSubScreenshot(Region region, bool throwIfClipped) { Region asIsSubScreenshotRegion = GetIntersectedRegion(region, CoordinatesTypeEnum.SCREENSHOT_AS_IS); if (asIsSubScreenshotRegion.IsSizeEmpty || (throwIfClipped && !asIsSubScreenshotRegion.Size.Equals(region.Size))) { throw new OutOfBoundsException($"Region [{region}] is out of screenshot bounds [{Image.Size}]"); } Bitmap subScreenshotImage = BasicImageUtils.Crop(Image, asIsSubScreenshotRegion.ToRectangle()); EyesImagesScreenshot result = new EyesImagesScreenshot(subScreenshotImage); return(result); }
public void TestScaleDownImage1() { Bitmap src = LoadBitmapFromResource("SrcImage.png"); Bitmap expected = LoadBitmapFromResource("ScaledDownImage.png"); Bitmap dest = BasicImageUtils.ScaleImage(src, expected.Width / (double)src.Width); if (!TestUtils.RUNS_ON_CI) { Directory.CreateDirectory(testScaleDownImageOutputPath_); src.Save(Path.Combine(testScaleDownImageOutputPath_, "orig.png")); expected.Save(Path.Combine(testScaleDownImageOutputPath_, "expected.png")); dest.Save(Path.Combine(testScaleDownImageOutputPath_, "actual_result.png")); } CompareBitmaps_(expected, dest); }
public override EyesScreenshot GetSubScreenshot(Region subregion, bool throwIfClipped) { logger_.Verbose("GetSubScreenshot([{0}], {1})", subregion, throwIfClipped); ArgumentGuard.NotNull(subregion, nameof(subregion)); if (eyes_.CutProvider != null && !(eyes_.CutProvider is NullCutProvider)) { subregion = new Region(Point.Empty, workingArea_.Size); } Bitmap subScreenshotImage = BasicImageUtils.Crop(Image, subregion.ToRectangle()); subregion = subregion.Offset(workingArea_.Left, workingArea_.Top); EyesAppiumScreenshot result = new EyesAppiumScreenshot(logger_, subScreenshotImage, subregion, eyes_); logger_.Verbose("Done!"); return(result); }
public override Bitmap GetImage() { logger_.Verbose("Getting current position..."); Point loc; double scaleRatio = eyes_.DevicePixelRatio; FrameChain currentFrameChain = eyes_.GetDriver().GetFrameChain(); IPositionProvider positionProvider = null; if (currentFrameChain.Count == 0) { IWebElement scrollRootElement = eyes_.GetCurrentFrameScrollRootElement(); positionProvider = eyes_.GetPositionProvider(logger_, StitchModes.Scroll, jsExecutor_, scrollRootElement, userAgent_); loc = positionProvider.GetCurrentPosition(); } else { loc = currentFrameChain.GetDefaultContentScrollPosition(); } Point scaledLoc = new Point((int)Math.Round(loc.X * scaleRatio), (int)Math.Round(loc.Y * scaleRatio)); Bitmap image = base.GetImage(); EyesWebDriver driver = eyes_.GetDriver(); RectangleSize originalViewportSize = EyesSeleniumUtils.GetViewportSize(logger_, driver); RectangleSize viewportSize = originalViewportSize.Scale(scaleRatio); if (image.Height > viewportSize.Height || image.Width > viewportSize.Width) { //Damn IE driver returns full page screenshot even when not asked to! logger_.Verbose("seems IE returned full page screenshot rather than only the viewport."); eyes_.DebugScreenshotProvider.Save(image, "IE"); if (!eyes_.IsCutProviderExplicitlySet) { Bitmap croppedImage = BasicImageUtils.Crop(image, new Rectangle(scaledLoc, viewportSize)); image.Dispose(); image = croppedImage; } } positionProvider?.SetPosition(loc); return(image); }
public override Bitmap GetImage() { logger_.Verbose("Getting screenshot..."); EyesWebDriver eyesWebDriver = eyes_.GetDriver(); FrameChain frameChain = eyesWebDriver.GetFrameChain().Clone(); logger_.Verbose("temporarilly switching to default content."); eyesWebDriver.SwitchTo().DefaultContent(); Bitmap image = BasicImageUtils.CreateBitmap(tsInstance_.GetScreenshot().AsByteArray); eyes_.DebugScreenshotProvider.Save(image, "FIREFOX"); logger_.Verbose("switching back to original frame."); ((EyesWebDriverTargetLocator)eyesWebDriver.SwitchTo()).Frames(frameChain); return(image); }
public virtual Bitmap GetImage() { return(BasicImageUtils.CreateBitmap(tsInstance_.GetScreenshot().AsByteArray)); }
public EyesLeanFTScreenshot(byte[] screenshotBytes) : base(BasicImageUtils.CreateBitmap(screenshotBytes)) { }
public static SimpleImageProvider FromBytes(byte[] screenshotBytes) { Bitmap bmp = BasicImageUtils.CreateBitmap(screenshotBytes); return(new SimpleImageProvider(bmp)); }
public Bitmap Cut(Bitmap image) { Rectangle rect = ToRectangle(image.Size); return(BasicImageUtils.Crop(image, rect)); }
public override Bitmap GetImage() { Bitmap image = base.GetImage(); if (eyes_.CachedSessionDetails != null && eyes_.CachedSessionDetails.TryGetValue("deviceOrientation", out object orientation)) { if ("landscape".Equals(orientation as string, StringComparison.OrdinalIgnoreCase) && image.Width < image.Height) { logger_.Verbose("rotating image..."); image.RotateFlip(RotateFlipType.Rotate270FlipNone); } } logger_.Verbose("Bitmap Size: {0}x{1}", image.Width, image.Height); eyes_.DebugScreenshotProvider.Save(image, "SAFARI"); if (eyes_.IsCutProviderExplicitlySet) { return(image); } double scaleRatio = eyes_.DevicePixelRatio; Size originalViewportSize = GetViewportSize(); Size viewportSize = new Size( (int)Math.Ceiling(originalViewportSize.Width * scaleRatio), (int)Math.Ceiling(originalViewportSize.Height * scaleRatio)); logger_.Verbose("logical viewport size: " + originalViewportSize); Bitmap croppedImage = null; if (userAgent_.IsiOS || "ios".Equals(eyes_.PlatformName, StringComparison.OrdinalIgnoreCase) || (eyes_.CachedSessionDetails.TryGetValue("PlatformName", out object platformName) && "ios".Equals(platformName as string, StringComparison.OrdinalIgnoreCase))) { croppedImage = CropIOSImage(image, originalViewportSize, logger_); } else if (!eyes_.ForceFullPageScreenshot) { Point loc; FrameChain currentFrameChain = eyes_.GetDriver().GetFrameChain(); if (currentFrameChain.Count == 0) { IWebElement scrollRootElement = eyes_.GetCurrentFrameScrollRootElement(); IPositionProvider positionProvider = eyes_.GetPositionProvider( logger_, StitchModes.Scroll, jsExecutor_, scrollRootElement, userAgent_); loc = positionProvider.GetCurrentPosition(); } else { loc = currentFrameChain.GetDefaultContentScrollPosition(); } loc = new Point((int)Math.Ceiling(loc.X * scaleRatio), (int)Math.Ceiling(loc.Y * scaleRatio)); croppedImage = BasicImageUtils.Crop(image, new Rectangle(loc, viewportSize)); } if (croppedImage != null && !ReferenceEquals(croppedImage, image)) { image.Dispose(); image = croppedImage; } eyes_.DebugScreenshotProvider.Save(image, "SAFARI_CROPPED"); return(image); }
private void StitchScreenshot_(Size stitchOffset, IPositionProvider stitchProvider, ICollection <SubregionForStitching> screenshotParts, Bitmap stitchedImage, double scaleRatio, ICutProvider scaledCutProvider, float sizeRatio) { int index = 0; logger_.Verbose($"enter: {nameof(stitchOffset)}: {{0}} ; {nameof(screenshotParts)}.Count: {{1}}, {nameof(scaleRatio)}: {{2}}", stitchOffset, screenshotParts.Count, scaleRatio); Stopwatch stopwatch = Stopwatch.StartNew(); foreach (SubregionForStitching partRegion in screenshotParts) { if (stopwatch.Elapsed > TimeSpan.FromMinutes(5)) { logger_.Log("Still Running..."); // this is so CI systems won't kill the build due to lack of activity. stopwatch.Restart(); } logger_.Verbose("Part: {0}", partRegion); // Scroll to the part's top/left Point partAbsoluteLocationInCurrentFrame = partRegion.ScrollTo; partAbsoluteLocationInCurrentFrame += stitchOffset; Point scrollPosition = new Point( (int)Math.Round(partAbsoluteLocationInCurrentFrame.X * sizeRatio), (int)Math.Round(partAbsoluteLocationInCurrentFrame.Y * sizeRatio)); Point originPosition = stitchProvider.SetPosition(scrollPosition); int dx = scrollPosition.X - originPosition.X; int dy = scrollPosition.Y - originPosition.Y; Point partPastePosition = partRegion.PasteLocation; //partPastePosition.Offset(-fullarea.Left, -fullarea.Top); partPastePosition.Offset(dx, dy); // Actually taking the screenshot. Thread.Sleep(waitBeforeScreenshots_); using (Bitmap partImage = imageProvider_.GetImage()) using (Bitmap cutPart = scaledCutProvider.Cut(partImage)) { Bitmap croppedPart; Rectangle r = partRegion.PhysicalCropArea; if ((r.Width * r.Height) != 0) { croppedPart = BasicImageUtils.Crop(cutPart, r); } else { croppedPart = cutPart; } Rectangle r2 = partRegion.LogicalCropArea; using (Bitmap scaledPartImage = BasicImageUtils.ScaleImage(croppedPart, scaleRatio)) using (Bitmap scaledCroppedPartImage = BasicImageUtils.Crop(scaledPartImage, r2)) using (Graphics g = Graphics.FromImage(stitchedImage)) { debugScreenshotsProvider_.Save(partImage, "partImage-" + originPosition.X + "_" + originPosition.Y); debugScreenshotsProvider_.Save(cutPart, "cutPart-" + originPosition.X + "_" + originPosition.Y); debugScreenshotsProvider_.Save(croppedPart, "croppedPart-" + originPosition.X + "_" + originPosition.Y); debugScreenshotsProvider_.Save(scaledPartImage, "scaledPartImage-" + originPosition.X + "_" + originPosition.Y); debugScreenshotsProvider_.Save(scaledCroppedPartImage, "scaledCroppedPartImage-" + partPastePosition.X + "_" + partPastePosition.Y); logger_.Verbose("pasting part at {0}", partPastePosition); g.DrawImage(scaledCroppedPartImage, partPastePosition); } if (!object.ReferenceEquals(croppedPart, cutPart)) { croppedPart.Dispose(); } debugScreenshotsProvider_.Save(stitchedImage, $"stitched_{index}_({partPastePosition.X}_{partPastePosition.Y})"); index++; } } debugScreenshotsProvider_.Save(stitchedImage, "stitched"); }
/// <summary> /// Encapsulates an algorithm for creating full-page images of a page. /// </summary> /// <param name="positionProvider">The position provider used for moving to the actual stitch points.</param> /// <param name="region">The region to stitch. If <see cref="Region.Empty"/>, the entire image will be stitched.</param> /// <param name="fullarea">The wanted area of the resulting image. If unknown, pass in <c>null</c> or <see cref="Region.Empty"/>.</param> /// <param name="originProvider">A position provider used for saving the state before /// starting the stitching, as well as moving to (0,0). The reason it is separated from /// the <c>stitchProvider</c>is that the stitchProvider might have side-effects /// (e.g., changing the CSS transform of the page can cause a layout change at the /// top of the page), which we can avoid for the first screenshot (since it might be a /// full page screenshot anyway).</param> /// <param name="stitchOffset"></param> /// <returns>The screenshot as Bitmap.</returns> public Bitmap GetStitchedRegion(Region region, Region fullarea, IPositionProvider positionProvider, IPositionProvider originProvider, Size stitchOffset) { ArgumentGuard.NotNull(region, nameof(region)); ArgumentGuard.NotNull(positionProvider, nameof(positionProvider)); logger_.Verbose("region: {0} ; fullarea: {1} ; positionProvider: {2}", region, fullarea, positionProvider.GetType().Name); Point originalStitchedState = positionProvider.GetCurrentPosition(); logger_.Verbose("region size: {0}, originalStitchedState: {1}", region, originalStitchedState); PositionMemento originProviderState = originProvider.GetState(); logger_.Verbose("originProviderState: {0}", originProviderState); originProvider.SetPosition(Point.Empty); Thread.Sleep(waitBeforeScreenshots_); Bitmap initialScreenshot = imageProvider_.GetImage(); Size initialPhysicalSize = initialScreenshot.Size; SaveDebugScreenshotPart_(initialScreenshot, region.ToRectangle(), "initial"); IScaleProvider scaleProvider = scaleProviderFactory_.GetScaleProvider(initialScreenshot.Width); double pixelRatio = 1 / scaleProvider.ScaleRatio; Size initialSizeScaled = new Size((int)Math.Round(initialScreenshot.Width / pixelRatio), (int)Math.Round(initialScreenshot.Height / pixelRatio)); ICutProvider scaledCutProvider = cutProvider_.Scale(pixelRatio); if (pixelRatio != 1 && !(scaledCutProvider is NullCutProvider)) { initialScreenshot = cutProvider_.Cut(initialScreenshot); debugScreenshotsProvider_.Save(initialScreenshot, "original-cut"); } Region regionInScreenshot = GetRegionInScreenshot_(region, initialScreenshot, pixelRatio); Bitmap croppedInitialScreenshot = CropScreenshot_(initialScreenshot, regionInScreenshot); debugScreenshotsProvider_.Save(croppedInitialScreenshot, "cropped"); Bitmap scaledInitialScreenshot = BasicImageUtils.ScaleImage(croppedInitialScreenshot, scaleProvider); if (!object.ReferenceEquals(scaledInitialScreenshot, croppedInitialScreenshot)) { SaveDebugScreenshotPart_(scaledInitialScreenshot, regionInScreenshot.ToRectangle(), "scaled"); } if (fullarea.IsEmpty) { Size entireSize; try { entireSize = positionProvider.GetEntireSize(); logger_.Verbose("Entire size of region context: {0}", entireSize); } catch (EyesException e) { logger_.Log("WARNING: Failed to extract entire size of region context" + e.Message); logger_.Log("Using image size instead: " + scaledInitialScreenshot.Width + "x" + scaledInitialScreenshot.Height); entireSize = new Size(scaledInitialScreenshot.Width, scaledInitialScreenshot.Height); } // Notice that this might still happen even if we used // "getImagePart", since "entirePageSize" might be that of a frame. if (scaledInitialScreenshot.Width >= entireSize.Width && scaledInitialScreenshot.Height >= entireSize.Height) { logger_.Log("WARNING: Seems the image is already a full page screenshot."); if (!object.ReferenceEquals(scaledInitialScreenshot, initialScreenshot)) { initialScreenshot.Dispose(); } return(scaledInitialScreenshot); } fullarea = new Region(Point.Empty, entireSize, CoordinatesTypeEnum.SCREENSHOT_AS_IS); } float currentFullWidth = fullarea.Width; fullarea = sizeAdjuster_.AdjustRegion(fullarea, initialSizeScaled); float sizeRatio = currentFullWidth / fullarea.Width; logger_.Verbose("adjusted fullarea: {0}", fullarea); Point scaledCropLocation = fullarea.Location; Point physicalCropLocation = new Point( (int)Math.Ceiling(scaledCropLocation.X * pixelRatio), (int)Math.Ceiling(scaledCropLocation.Y * pixelRatio)); Rectangle sourceRegion; if (regionInScreenshot.IsSizeEmpty) { Size physicalCropSize = new Size(initialPhysicalSize.Width - physicalCropLocation.X, initialPhysicalSize.Height - physicalCropLocation.Y); sourceRegion = new Rectangle(physicalCropLocation, physicalCropSize); } else { // Starting with the screenshot we already captured at (0,0). sourceRegion = regionInScreenshot.ToRectangle(); } Rectangle scaledCroppedSourceRect = cutProvider_.ToRectangle(sourceRegion.Size); scaledCroppedSourceRect.Offset(sourceRegion.Location); Rectangle scaledCroppedSourceRegion = new Rectangle( (int)Math.Ceiling(scaledCroppedSourceRect.X / pixelRatio), (int)Math.Ceiling(scaledCroppedSourceRect.Y / pixelRatio), (int)Math.Ceiling(scaledCroppedSourceRect.Width / pixelRatio), (int)Math.Ceiling(scaledCroppedSourceRect.Height / pixelRatio)); Size scaledCropSize = scaledCroppedSourceRegion.Size; // The screenshot part is a bit smaller than the screenshot size, in order to eliminate // duplicate bottom/right-side scroll bars, as well as fixed position footers. Size screenshotPartSize = new Size( Math.Max(scaledCropSize.Width, MinScreenshotPartSize_), Math.Max(scaledCropSize.Height, MinScreenshotPartSize_) ); logger_.Verbose("Screenshot part size: {0}", screenshotPartSize); // Getting the list of viewport regions composing the page (we'll take screenshot for each one). Rectangle rectInScreenshot; if (regionInScreenshot.IsSizeEmpty) { int x = Math.Max(0, fullarea.Left); int y = Math.Max(0, fullarea.Top); int w = Math.Min(fullarea.Width, scaledCropSize.Width); int h = Math.Min(fullarea.Height, scaledCropSize.Height); rectInScreenshot = new Rectangle( (int)Math.Round(x * pixelRatio), (int)Math.Round(y * pixelRatio), (int)Math.Round(w * pixelRatio), (int)Math.Round(h * pixelRatio)); } else { rectInScreenshot = regionInScreenshot.Rectangle; } fullarea = CoerceImageSize_(fullarea); ICollection <SubregionForStitching> screenshotParts = fullarea.GetSubRegions(screenshotPartSize, stitchOverlap_, pixelRatio, rectInScreenshot, logger_); Bitmap stitchedImage = new Bitmap(fullarea.Width, fullarea.Height); // Take screenshot and stitch for each screenshot part. StitchScreenshot_(stitchOffset, positionProvider, screenshotParts, stitchedImage, scaleProvider.ScaleRatio, scaledCutProvider, sizeRatio); positionProvider.SetPosition(originalStitchedState); originProvider.RestoreState(originProviderState); croppedInitialScreenshot.Dispose(); return(stitchedImage); }