I am new to Xamarin android and for below code am getting OutOfMemoryException. Here profilebitMap is a Bitmap and mProfileImage is an ImageView. I have tried this with using block and dispose/recycle methods also but still getting the same error after multiple returns to the image page. Please help me in this.
if (!string.IsNullOrEmpty(profile.Image))
{
string[] fileExtension = profile.Image.Split('/');
string _imagePath = System.IO.Path.Combine(_documentsPath.ToString(), profile.ID + fileExtension[fileExtension.Length - 1]);
if (File.Exists(_imagePath))
{
profilebitMap = await BitmapFactory.DecodeFileAsync(_imagePath);
//profilebitMap = Util.Base64ToBitmap(appPreferencce.getAccessKey(profile.Image));
}
else
{
profilebitMap = Util.GetImageBitmapFromUrl(profile.Image, appPreferencce.getAccessKey("username"), appPreferencce.getAccessKey("password"));
using (var stream = new MemoryStream())
{
profilebitMap.Compress(Bitmap.CompressFormat.Png, 100, stream);
var imageBytes = stream.ToArray();
File.WriteAllBytes(_imagePath, imageBytes);
}
}
}
else
{
profilebitMap = BitmapFactory.DecodeResource(this.ApplicationContext.Resources, Resource.Drawable.dummyuser);
}
CircularDrawable d = new CircularDrawable(profilebitMap,
(int)Util.ConvertDpToPx(ApplicationContext, margin),
Util.ConvertDpToPx(ApplicationContext, strokeWidth),
new Android.Graphics.Color(ContextCompat.GetColor(this, Resource.Color.normal3)));
mProfileImage.SetBackgroundDrawable(d);
You don't have to reinvent the wheel, you can use libraries that load you pictures in few lines. You should take a look to the awesome library Picasso.
Bye
To avoid the OutOfMemoryException we should use BitmapFactory.Options class.
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeResource(getResources(), R.id.myimage, options);
int imageHeight = options.outHeight;
int imageWidth = options.outWidth;
String imageType = options.outMimeType;
Setting the inJustDecodeBounds property to true while decoding avoids memory allocation, returning null for the bitmap object but setting outWidth, outHeight and outMimeType.
Now that the image dimensions are known, they can be used to decide if the full image should be loaded into memory or if a subsampled version should be loaded instead.
For example, an image with resolution 2048x1536 that is decoded with an inSampleSize of 4 produces a bitmap of approximately 512x384. Loading this into memory uses 0.75MB rather than 12MB for the full image.
public static int calculateInSampleSize(
BitmapFactory.Options options, int reqWidth, int reqHeight) {
// Raw height and width of image
final int height = options.outHeight;
final int width = options.outWidth;
int inSampleSize = 1;
if (height > reqHeight || width > reqWidth) {
final int halfHeight = height / 2;
final int halfWidth = width / 2;
// Calculate the largest inSampleSize value that is a power of 2 and keeps both
// height and width larger than the requested height and width.
while ((halfHeight / inSampleSize) >= reqHeight
&& (halfWidth / inSampleSize) >= reqWidth) {
inSampleSize *= 2;
}
}
return inSampleSize;
}
Please follow this url https://developer.android.com/training/displaying-bitmaps/load-bitmap.html
Related
I am trying to create an Image with a Caption/Text on it for Youtube-Thumbnails.
Following Rules are defined:
The Text is the Title of the Video and always changes from Thumbnail to Thumbnail.
The Porgram uses a pre-defined Text-Width which must not be touched by the Text on the Image.
The Text should be as close to the pre-defined with as possible.
So my thoughts were that I would use Graphics.MeasureString to be able to track the Width of the String on the Image and increase the Font-Size and repeat this until the pre-defined Width is closely reached but not touched.
But I have tested it with MeasureString and found out that it isn't that accurate. And also found confirmation here: Graphics.MeasureCharacterRanges giving wrong size calculations
I have tried the things they have recommended but with no success as the final width of my string always overflooded the image borders. Even if my pre-defined Width was way smaller than the Image Width. (Image Width: 1920; Pre-Defined Width: 1600)
So I came up with the Idea to create a custom Measurement Method and to write the String as I want it on a new Bitmap and to count the maximum Pixels of the String in Height and Width. (The Height is just for future stuff)
My current Code is:
public static SizeF MeasuredStringSize(string text, Bitmap originBitmap, FontFamily fontFamily, StringFormat strformat)
{
int currentFontSize = 10;
SizeF measuredSize = new();
var highestWidth = 0;
var highestHeight = 0;
while (highestWidth < maximumTextWidth)
{
Bitmap bitmap = new(originBitmap);
Bitmap _bitmap = new(bitmap.Width, bitmap.Height);
using Graphics graphics = Graphics.FromImage(bitmap);
if (graphics != null)
{
graphics.TranslateTransform(bitmap.Width / 2, bitmap.Height / 2);
currentFontSize++;
graphics.Clear(Color.White);
using GraphicsPath path = new();
using SolidBrush brush = new(Color.Red);
using Pen pen = new(Color.Red, 6)
{
LineJoin = LineJoin.Round
};
path.AddString(text, fontFamily, (int)fontStyle, currentFontSize, new Point(0, 0), strformat);
graphics.DrawPath(pen, path);
graphics.FillPath(brush, path);
Dictionary<int, List<int>> redPixelMatrix = new();
for (int i = 0; i < bitmap.Width; i++)
{
for (int j = 0; j < bitmap.Height; j++)
{
var currentPixelColor = bitmap.GetPixel(i, j);
if (currentPixelColor.B != 255 && currentPixelColor.G != 255 && currentPixelColor.R == 255)
{
if (!redPixelMatrix.ContainsKey(i))
{
redPixelMatrix.Add(i, new());
}
redPixelMatrix[i].Add(j);
}
}
}
highestWidth = redPixelMatrix.Keys.Count;
highestHeight = redPixelMatrix.Aggregate((l, r) => l.Value.Count > r.Value.Count ? l : r).Value.Count;
Console.WriteLine($"X:{highestWidth};Y:{highestHeight}");
//Debugging the final Image with Text to see the Result
bitmap.Save(ResultPath);
}
}
measuredSize = new SizeF(highestWidth, highestHeight);
return measuredSize;
}
The Resulting Image from bitmap.Save(ResultPath); as the String reaches the Image borders looks like this:
But the exact String width is 1742 instead of the width of my Image 1920 which should be more or less the same at this moment.
So, why is the Text nearly as wide as the Image but doesn't have the same width?
highestWidth = redPixelMatrix.Keys.Count; This will just count the number of columns containing red pixels, excluding any spaces in the text. You presumably want the minimum and maximum indices.
I.e.
var minX = int.MaxValue;
var maxX = int.MinValue;
// Loops over rows & columns
// Check if pixel is red
if(i > maxX) maxX = i;
if(i < minX) minX = i;
If you only want the text width and not the bounds you can just do maxX - minX.
I am taking an image, decoding the byte array that is generated by TakePicture method and then rotating the bitmap 270 degrees. The problem is that i seem to run out of memory, and i do not know how to solve it. Here is the code:
Bitmap image = BitmapFactory.DecodeByteArray (data, 0, data.Length);
data = null;
Bitmap rotatedBitmap = Bitmap.CreateBitmap (image, 0, 0, image.Width,
image.Height, matrix, true);
Please have a look at Load Large Bitmaps Efficiently from official Xamarin documentation, which explains how you can load large images into memory without the application throwing an OutOfMemoryException by loading a smaller subsampled version in memory.
Read Bitmap Dimensions and Type
async Task<BitmapFactory.Options> GetBitmapOptionsOfImageAsync()
{
BitmapFactory.Options options = new BitmapFactory.Options
{
InJustDecodeBounds = true
};
// The result will be null because InJustDecodeBounds == true.
Bitmap result= await BitmapFactory.DecodeResourceAsync(Resources, Resource.Drawable.someImage, options);
int imageHeight = options.OutHeight;
int imageWidth = options.OutWidth;
_originalDimensions.Text = string.Format("Original Size= {0}x{1}", imageWidth, imageHeight);
return options;
}
Load a Scaled Down Version into Memory
public static int CalculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight)
{
// Raw height and width of image
float height = options.OutHeight;
float width = options.OutWidth;
double inSampleSize = 1D;
if (height > reqHeight || width > reqWidth)
{
int halfHeight = (int)(height / 2);
int halfWidth = (int)(width / 2);
// Calculate a inSampleSize that is a power of 2 - the decoder will use a value that is a power of two anyway.
while ((halfHeight / inSampleSize) > reqHeight && (halfWidth / inSampleSize) > reqWidth)
{
inSampleSize *= 2;
}
}
return (int)inSampleSize;
}
Load the Image as Async
public async Task<Bitmap> LoadScaledDownBitmapForDisplayAsync(Resources res, BitmapFactory.Options options, int reqWidth, int reqHeight)
{
// Calculate inSampleSize
options.InSampleSize = CalculateInSampleSize(options, reqWidth, reqHeight);
// Decode bitmap with inSampleSize set
options.InJustDecodeBounds = false;
return await BitmapFactory.DecodeResourceAsync(res, Resource.Drawable.someImage, options);
}
And call it to load Image, say in OnCreate
protected async override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);
SetContentView(Resource.Layout.Main);
_imageView = FindViewById<ImageView>(Resource.Id.resized_imageview);
BitmapFactory.Options options = await GetBitmapOptionsOfImageAsync();
Bitmap bitmapToDisplay = await LoadScaledDownBitmapForDisplayAsync (Resources,
options,
150, //for 150 X 150 resolution
150);
_imageView.SetImageBitmap(bitmapToDisplay);
}
Hope it helps.
I'm trying to compare two Images via their byte content. However, they do not match.
Both images were generated from the same source image, using the same method with the same parameters. I am guessing that something in the image generation or the way I convert to a byte array is not deterministic. Does anyone know where the non-deterministic behavior is occurring and whether or not I can readily force deterministic behavior for my unit testing?
This method within my test class converts the image to a byte array - is image.Save deterministic? Is memStream.ToArray() deterministic?
private static byte[] ImageToByteArray(Image image)
{
byte[] actualBytes;
using (MemoryStream memStream = new MemoryStream())
{
image.Save(memStream, ImageFormat.Bmp);
actualBytes = memStream.ToArray();
}
return actualBytes;
}
Here is the unit test, which is failing - TestImageLandscapeDesertResized_300_300 was generated from TestImageLandscapeDesert using the ImageHelper.ResizeImage(testImageLandscape, 300, 300) and then saving to a file before loading into the project's resource file. If all calls within my code were deterministic based upon my input parameters, this test should pass.
public void ResizeImage_Landscape_SmallerLandscape()
{
Image testImageLandscape = Resources.TestImageLandscapeDesert;
Image expectedImage = Resources.TestImageLandscapeDesertResized_300_300;
byte[] expectedBytes = ImageToByteArray(expectedImage);
byte[] actualBytes;
using (Image resizedImage = ImageHelper.ResizeImage(testImageLandscape, 300, 300))
{
actualBytes = ImageToByteArray(resizedImage);
}
Assert.IsTrue(expectedBytes.SequenceEqual(actualBytes));
}
The method under test - this method will shrink the input image so its height and width are less than maxHeight and maxWidth, retaining the existing aspect ratio. Some of the graphics calls may be non-deterministic, I cannot tell from Microsoft's limited documentation.
public static Image ResizeImage(Image image, int maxWidth, int maxHeight)
{
decimal width = image.Width;
decimal height = image.Height;
decimal newWidth;
decimal newHeight;
//Calculate new width and height
if (width > maxWidth || height > maxHeight)
{
// need to preserve the original aspect ratio
decimal originalAspectRatio = width / height;
decimal widthReductionFactor = maxWidth / width;
decimal heightReductionFactor = maxHeight / height;
if (widthReductionFactor < heightReductionFactor)
{
newWidth = maxWidth;
newHeight = newWidth / originalAspectRatio;
}
else
{
newHeight = maxHeight;
newWidth = newHeight * originalAspectRatio;
}
}
else
//Return a copy of the image if smaller than allowed width and height
return new Bitmap(image);
//Resize image
Bitmap bitmap = new Bitmap((int)newWidth, (int)newHeight, PixelFormat.Format48bppRgb);
Graphics graphic = Graphics.FromImage(bitmap);
graphic.InterpolationMode = InterpolationMode.HighQualityBicubic;
graphic.DrawImage(image, 0, 0, (int)newWidth, (int)newHeight);
graphic.Dispose();
return bitmap;
}
This eventually worked. I don't know whether or not this is a good idea for unit tests, but with the GDI+ logic being non-deterministic (or my logic interfacing with it), this seems the best approach.
I use MS Fakes Shimming feature to Shim the dependent calls and verify expected values are passed to the called methods. Then I call the native methods to get the required functionality for the rest of the method under test. And finally I verify a few attributes of the returned image.
Still, I would prefer to perform a straight comparison of expected output against actual output...
[TestMethod]
[TestCategory("ImageHelper")]
[TestCategory("ResizeImage")]
public void ResizeImage_LandscapeTooLarge_SmallerLandscape()
{
Image testImageLandscape = Resources.TestImageLandscapeDesert;
const int HEIGHT = 300;
const int WIDTH = 300;
const int EXPECTED_WIDTH = WIDTH;
const int EXPECTED_HEIGHT = (int)(EXPECTED_WIDTH / (1024m / 768m));
const PixelFormat EXPECTED_FORMAT = PixelFormat.Format48bppRgb;
bool calledBitMapConstructor = false;
bool calledGraphicsFromImage = false;
bool calledGraphicsDrawImage = false;
using (ShimsContext.Create())
{
ShimBitmap.ConstructorInt32Int32PixelFormat = (instance, w, h, f) => {
calledBitMapConstructor = true;
Assert.AreEqual(EXPECTED_WIDTH, w);
Assert.AreEqual(EXPECTED_HEIGHT, h);
Assert.AreEqual(EXPECTED_FORMAT, f);
ShimsContext.ExecuteWithoutShims(() => {
ConstructorInfo constructor = typeof(Bitmap).GetConstructor(new[] { typeof(int), typeof(int), typeof(PixelFormat) });
Assert.IsNotNull(constructor);
constructor.Invoke(instance, new object[] { w, h, f });
});
};
ShimGraphics.FromImageImage = i => {
calledGraphicsFromImage = true;
Assert.IsNotNull(i);
return ShimsContext.ExecuteWithoutShims(() => Graphics.FromImage(i));
};
ShimGraphics.AllInstances.DrawImageImageInt32Int32Int32Int32 = (instance, i, x, y, w, h) => {
calledGraphicsDrawImage = true;
Assert.IsNotNull(i);
Assert.AreEqual(0, x);
Assert.AreEqual(0, y);
Assert.AreEqual(EXPECTED_WIDTH, w);
Assert.AreEqual(EXPECTED_HEIGHT, h);
ShimsContext.ExecuteWithoutShims(() => instance.DrawImage(i, x, y, w, h));
};
using (Image resizedImage = ImageHelper.ResizeImage(testImageLandscape, HEIGHT, WIDTH))
{
Assert.IsNotNull(resizedImage);
Assert.AreEqual(EXPECTED_WIDTH, resizedImage.Size.Width);
Assert.AreEqual(EXPECTED_HEIGHT, resizedImage.Size.Height);
Assert.AreEqual(EXPECTED_FORMAT, resizedImage.PixelFormat);
}
}
Assert.IsTrue(calledBitMapConstructor);
Assert.IsTrue(calledGraphicsFromImage);
Assert.IsTrue(calledGraphicsDrawImage);
}
A bit late to the table with this, but adding this in case it helps anyone out. In my unit tests, this reliably compared images I had dynamically generated using GDI+.
private static bool CompareImages(string source, string expected)
{
var image1 = new Bitmap($".\\{source}");
var image2 = new Bitmap($".\\Expected\\{expected}");
var converter = new ImageConverter();
var image1Bytes = (byte[])converter.ConvertTo(image1, typeof(byte[]));
var image2Bytes = (byte[])converter.ConvertTo(image2, typeof(byte[]));
// ReSharper disable AssignNullToNotNullAttribute
var same = image1Bytes.SequenceEqual(image2Bytes);
// ReSharper enable AssignNullToNotNullAttribute
return same;
}
I have been using GDI+ implementation in Asp.Net hadler for resizing images. I've found article Resizing images from the server using WPF/WIC instead of GDI+ describing that resizing images using Windows.Media.Imaging is more effective (much faster and consume less system resources).
This approach has one more advantage, I can read individual frames from source image, as gif. Here is part of my code:
var photoDecoder = BitmapDecoder.Create(inputStream, BitmapCreateOptions.PreservePixelFormat,
BitmapCacheOption.None);
IEnumerable<BitmapFrame> bitmapFrames = from bitmapFrame in photoDecoder.Frames
select bitmapFrame;
var bitmapFramesList = new List<BitmapFrame>();
bitmapFramesList.AddRange(bitmapFrames);
var firstFrame = bitmapFramesList.First();
int inputWidth = Convert.ToInt32(firstFrame.Width);
int inputHeight = Convert.ToInt32(firstFrame.Height);
var firstFrameSize= new Size(inputWidth, inputHeight);
//if targetSize is bigger than image Size render image as is
bool targetSizeIsBiggerOrEmpty = targetSize.IsEmpty || (targetSize.Width > firstFrameSize.Width && targetSize.Height > firstFrameSize.Height);
if (targetSizeIsBiggerOrEmpty)
{
inputStream.Position = ZERO;
inputStream.CopyTo(catalogImage.Stream);
}
else
{
//calculate size of target image
Size imageSize = SizeCalculator.CalculateImageSize(new Size(Convert.ToInt32(firstFrame.Width),Convert.ToInt32(firstFrame.Height)), targetSize);
if (targetSize.Width == ZERO)
targetSize.Width = imageSize.Width;
if (targetSize.Height == ZERO)
targetSize.Height = imageSize.Height;
double scaleX = Convert.ToDouble(imageSize.Width) / Convert.ToDouble(firstFrame.Width);
double scaleY = Convert.ToDouble(imageSize.Height) / Convert.ToDouble(firstFrame.Height);
double scale = Math.Min(scaleX, scaleY);
var targetFrames= new Collection<BitmapFrame>();
foreach (var sourceFrame in bitmapFramesList)
{
var transform = new ScaleTransform {ScaleX = scale, ScaleY = scale};
var transformedBitmap = new TransformedBitmap(sourceFrame, transform);
BitmapFrame bitmapFrame = BitmapFrame.Create(transformedBitmap);
targetFrames.Add(bitmapFrame);
}
//my own method for saving image to response output stream
SaveCatalogImage(catalogImage, targetFrames);
}
But here is a small problem. Each frame has different size and I'm not able to find each frame position according to first frame size. I also need timing in target image as is in source.
Where I can find those information in source image?
When I take screenshots with ChromeDriver I get screens with the size of my viewport.
When I take screenshots with FirefoxDriver I get what I want, which is a full screen print of a website.
ChromeDriver is declared like this:
IWebDriver driver = new ChromeDriver();
FirefoxDriver is declared like this:
IWebDriver driver = new FirefoxDriver();
Both drivers execute identical code:
driver.Manage().Window.Maximize();
driver.Navigate().GoToUrl(url);//url is a string variable
ITakesScreenshot screenshotDriver = driver as ITakesScreenshot;
Screenshot screenshot = screenshotDriver.GetScreenshot();
screenshot.SaveAsFile("c:/test.png", ImageFormat.Png);
ChromeDriver's test.png is of 1920x1099 resolution and contains only the browser viewport.
FirefoxDriver's test.png is of 1903x16559 resolution and contains the whole page.
I know that GetScreenshot() method doesn't return identical resolution sizes because it has slightly different implementations in IEDriver, FirefoxDriver, OperaDriver, ChromeDriver.
My questions are:
Why is there such difference between ChromeDriver's and FirefoxDriver's .GetScreenshot() method, even tho they use an identical interface (ITakesScreenshot)?
Is there a way to make ChromeDriver's GetScreenshot() method return the whole webpage screen instead of just the viewport?
we can't get the entire page screenshot with ChromeDriver2, we need to go for manual implementation.I have modified a method with is available in a blog which works fine with ChromeDriver.
use this method as following :
private IWebDriver _driver = new ChromeDriver(CHROME_DRIVER_PATH);
screenshot.SaveAsFile(saveFileName, ImageFormat.Jpeg);
public Bitmap GetEntereScreenshot()
{
Bitmap stitchedImage = null;
try
{
long totalwidth1 = (long)((IJavaScriptExecutor)_driver).ExecuteScript("return document.body.offsetWidth");//documentElement.scrollWidth");
long totalHeight1 = (long)((IJavaScriptExecutor)_driver).ExecuteScript("return document.body.parentNode.scrollHeight");
int totalWidth = (int)totalwidth1;
int totalHeight = (int)totalHeight1;
// Get the Size of the Viewport
long viewportWidth1 = (long)((IJavaScriptExecutor)_driver).ExecuteScript("return document.body.clientWidth");//documentElement.scrollWidth");
long viewportHeight1 = (long)((IJavaScriptExecutor)_driver).ExecuteScript("return window.innerHeight");//documentElement.scrollWidth");
int viewportWidth = (int)viewportWidth1;
int viewportHeight = (int)viewportHeight1;
// Split the Screen in multiple Rectangles
List<Rectangle> rectangles = new List<Rectangle>();
// Loop until the Total Height is reached
for (int i = 0; i < totalHeight; i += viewportHeight)
{
int newHeight = viewportHeight;
// Fix if the Height of the Element is too big
if (i + viewportHeight > totalHeight)
{
newHeight = totalHeight - i;
}
// Loop until the Total Width is reached
for (int ii = 0; ii < totalWidth; ii += viewportWidth)
{
int newWidth = viewportWidth;
// Fix if the Width of the Element is too big
if (ii + viewportWidth > totalWidth)
{
newWidth = totalWidth - ii;
}
// Create and add the Rectangle
Rectangle currRect = new Rectangle(ii, i, newWidth, newHeight);
rectangles.Add(currRect);
}
}
// Build the Image
stitchedImage = new Bitmap(totalWidth, totalHeight);
// Get all Screenshots and stitch them together
Rectangle previous = Rectangle.Empty;
foreach (var rectangle in rectangles)
{
// Calculate the Scrolling (if needed)
if (previous != Rectangle.Empty)
{
int xDiff = rectangle.Right - previous.Right;
int yDiff = rectangle.Bottom - previous.Bottom;
// Scroll
//selenium.RunScript(String.Format("window.scrollBy({0}, {1})", xDiff, yDiff));
((IJavaScriptExecutor)_driver).ExecuteScript(String.Format("window.scrollBy({0}, {1})", xDiff, yDiff));
System.Threading.Thread.Sleep(200);
}
// Take Screenshot
var screenshot = ((ITakesScreenshot)_driver).GetScreenshot();
// Build an Image out of the Screenshot
Image screenshotImage;
using (MemoryStream memStream = new MemoryStream(screenshot.AsByteArray))
{
screenshotImage = Image.FromStream(memStream);
}
// Calculate the Source Rectangle
Rectangle sourceRectangle = new Rectangle(viewportWidth - rectangle.Width, viewportHeight - rectangle.Height, rectangle.Width, rectangle.Height);
// Copy the Image
using (Graphics g = Graphics.FromImage(stitchedImage))
{
g.DrawImage(screenshotImage, rectangle, sourceRectangle, GraphicsUnit.Pixel);
}
// Set the Previous Rectangle
previous = rectangle;
}
}
catch (Exception ex)
{
// handle
}
return stitchedImage;
}
I cleaned up #Selvantharajah Roshanth's answer and added a check so that it won't try to stitch together screenshots that already fit in the viewport.
public Image GetEntireScreenshot()
{
// Get the total size of the page
var totalWidth = (int) (long) ((IJavaScriptExecutor) driver).ExecuteScript("return document.body.offsetWidth"); //documentElement.scrollWidth");
var totalHeight = (int) (long) ((IJavaScriptExecutor) driver).ExecuteScript("return document.body.parentNode.scrollHeight");
// Get the size of the viewport
var viewportWidth = (int) (long) ((IJavaScriptExecutor) driver).ExecuteScript("return document.body.clientWidth"); //documentElement.scrollWidth");
var viewportHeight = (int) (long) ((IJavaScriptExecutor) driver).ExecuteScript("return window.innerHeight"); //documentElement.scrollWidth");
// We only care about taking multiple images together if it doesn't already fit
if (totalWidth <= viewportWidth && totalHeight <= viewportHeight)
{
var screenshot = driver.TakeScreenshot();
return ScreenshotToImage(screenshot);
}
// Split the screen in multiple Rectangles
var rectangles = new List<Rectangle>();
// Loop until the totalHeight is reached
for (var y = 0; y < totalHeight; y += viewportHeight)
{
var newHeight = viewportHeight;
// Fix if the height of the element is too big
if (y + viewportHeight > totalHeight)
{
newHeight = totalHeight - y;
}
// Loop until the totalWidth is reached
for (var x = 0; x < totalWidth; x += viewportWidth)
{
var newWidth = viewportWidth;
// Fix if the Width of the Element is too big
if (x + viewportWidth > totalWidth)
{
newWidth = totalWidth - x;
}
// Create and add the Rectangle
var currRect = new Rectangle(x, y, newWidth, newHeight);
rectangles.Add(currRect);
}
}
// Build the Image
var stitchedImage = new Bitmap(totalWidth, totalHeight);
// Get all Screenshots and stitch them together
var previous = Rectangle.Empty;
foreach (var rectangle in rectangles)
{
// Calculate the scrolling (if needed)
if (previous != Rectangle.Empty)
{
var xDiff = rectangle.Right - previous.Right;
var yDiff = rectangle.Bottom - previous.Bottom;
// Scroll
((IJavaScriptExecutor) driver).ExecuteScript(String.Format("window.scrollBy({0}, {1})", xDiff, yDiff));
}
// Take Screenshot
var screenshot = driver.TakeScreenshot();
// Build an Image out of the Screenshot
var screenshotImage = ScreenshotToImage(screenshot);
// Calculate the source Rectangle
var sourceRectangle = new Rectangle(viewportWidth - rectangle.Width, viewportHeight - rectangle.Height, rectangle.Width, rectangle.Height);
// Copy the Image
using (var graphics = Graphics.FromImage(stitchedImage))
{
graphics.DrawImage(screenshotImage, rectangle, sourceRectangle, GraphicsUnit.Pixel);
}
// Set the Previous Rectangle
previous = rectangle;
}
return stitchedImage;
}
private static Image ScreenshotToImage(Screenshot screenshot)
{
Image screenshotImage;
using (var memStream = new MemoryStream(screenshot.AsByteArray))
{
screenshotImage = Image.FromStream(memStream);
}
return screenshotImage;
}
It appears as though full-screen screenshots are not yet implemented in the ChromeDriver, due to some inaccuracies in its previous implementation.
Source: https://code.google.com/p/chromedriver/issues/detail?id=294
I have recently written a Selenium based application to test an Internet Explorer UI and found that:
Taking screenshots with selenium was not as quick as using .NET, and
Selenium is unable to take screenshots when dialog boxes are present. This was a major drawback, as I needed to identify unexpected dialogs during interaction with the pages.
Investigate using the Graphics.CopyFromScreen method in System.Drawing as an alternative solution until the feature is implemented in Chrome. Once you have tried .the Net approach however, I don't think you will look back =]
I stumbled accross the same problem and ChromeDriver2 just does not support it.
So I created a little script which scrolls thru the page, takes screenshots and stitches everything together.
You can find the script in my blog post here:
http://dev.flauschig.ch/wordpress/?p=341