private Size GetFrameSize_(IPositionProvider positionProvider) { Size frameSize; // If we're inside a frame, then the frame size is given by the frame // chain. Otherwise, it's the size of the entire page. if (frameChain_.Count != 0) { frameSize = frameChain_.GetCurrentFrameInnerSize(); } else { // get entire page size might throw an exception for applications // which don't support Javascript (e.g., Appium). In that case // we'll use the viewport size as the frame's size. try { logger_.Verbose("no framechain. positionProvider: {0}", positionProvider); frameSize = positionProvider.GetEntireSize(); logger_.Verbose("frameSize: {0}", frameSize); } catch (EyesDriverOperationException) { frameSize = driver_.GetDefaultContentViewportSize(); } } return(frameSize); }
/// <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); }