I am looking for firefox addon api to take screenshot of visible area of document.
Chrome and Safari have api's to achieve this. And they are pretty fast.
I could not find anything specific for firefox.
I found a workaround at How do I use the canvas drawWindow function in an addon created using the addon sdk? but this solution takes full page screenshot with scrolls including (hidden parts of document). There are 2 issues for this solution;
1- if page has long scroll, it takes long time to complete screenshot process. Because it is using canvas based drawing.
2- I would like to get screenshot of visible area of document, not whole document.
Is there any workaround for this?
Thanks.
Using the SDK you can do something like this:
const { window: { document } } = require('sdk/addon/window');
const { getTabContentWindow, getActiveTab } = require('sdk/tabs/utils');
const { getMostRecentBrowserWindow } = require('sdk/window/utils');
const canvas = document.createElementNS('http://www.w3.org/1999/xhtml', 'canvas');
document.documentElement.appendChild(canvas);
function captureTab(tab=getActiveTab(getMostRecentBrowserWindow())) {
let contentWindow = getTabContentWindow(tab);
let w = contentWindow.innerWidth;
let h = contentWindow.innerHeight;
let x = contentWindow.scrollX;
let y = contentWindow.scrollY;
canvas.width = w;
canvas.height = h;
let ctx = canvas.getContext('2d');
ctx.drawWindow(contentWindow, x, y, w, h, '#000');
return canvas.toDataURL();
}
That should takes only the visible area. By default, it grabs the active tab, but you can pass any other tab – because is designed as low level API it takes a native tab, however, not a SDK tab.
You can put in a module and exports just the captureTab function.
Edit: e10s version
The code above is not currently compatible with Firefox with e10s available, as Ian Bicking noted in the comment. An easy way to workaround this issue, is create a temporary canvas in the same document and content process we want to capture the screenshot:
const { getTabContentWindow, getActiveTab } = require('sdk/tabs/utils');
const { getMostRecentBrowserWindow } = require('sdk/window/utils');
function captureTab(tab=getActiveTab(getMostRecentBrowserWindow())) {
let contentWindow = getTabContentWindow(tab);
let { document } = contentWindow;
let w = contentWindow.innerWidth;
let h = contentWindow.innerHeight;
let x = contentWindow.scrollX;
let y = contentWindow.scrollY;
let canvas = document.createElementNS('http://www.w3.org/1999/xhtml', 'canvas');
canvas.width = w;
canvas.height = h;
let ctx = canvas.getContext('2d');
ctx.drawWindow(contentWindow, x, y, w, h, '#000');
let dataURL = canvas.toDataURL();
canvas = null;
return dataURL;
}
That works in both e10s and no-e10s FF version; the downside comparing to the previous one is creating a canvas every time we want to take a screenshot, but I think is acceptable.
Your assumption that taking a screenshot on Firefox with canvas is somehow slow is wrong.
I did a couple of screenshots and Firefox/canvas was faster than Chrome/captureVisibleTab.
Actually Firefox is better suited for as-fast-as-possible screenshots, since its canvas expose to privileged code the mozFetchAsStream method, allowing to bypass the actual bottleneck which is the base64 encoding of the image data.
Some numbers
Chrome: captureVisibleTab 200-205ms
Firefox: drawImage 20-25ms + toDataURL 125-130ms
The devtools screenshot command is a good example of how to capture just the visible part
In all fairness, to make a meaningful comparison one has to take into account whether Chrome's PNG encoder favors compression over speed. Still, this doesn't change the fact that Firefox's canvas is fine.
edit: OK, that base64 encoding remark is dumb, I don't know what I was thinking. Perhaps what I should write instead is that Firefox's canvas is not only fast but also versatile.
Related
This is a modification of this question. I have an isometric grid of images and curious - is it possible to scroll it in free ways (diagonals included). Currently I have this code and it's possible to scroll 2D:
val scrollStateHorizontal = rememberScrollState()
val scrollStateVertical = rememberScrollState()
Box(
modifier = Modifier
.horizontalScroll(scrollStateHorizontal)
.verticalScroll(scrollStateVertical)
) {
for (i in 0..8) {
for (j in 0..7) {
val start = (y % 2 * 0.5 + x) * width
val top = i * height * 0.5
Image(modifier = Modifier.padding(start = start.dp, top = top.dp), painter = ...)
}
}
}
What is interesting:
Can I scroll diagonally?
How can I optimize it (memory consumption, etc.)? Maybe I should use LazyHorizontalGrid including LazyVerticalGrid/LazyColumn/LazyRow?
It seems, LazyHorizontalGrid can't be modified simply adding Modifier.verticalScroll. And it has a lag for first few seconds of scrolling (maybe for caching items) while simple Box scrolling acts smoothly from the start.
As of now, there's no official support for this, as far as I know. The only way you could implement this as of the current stable, is by using LazyLayout Composable, or by modifying the verticalScroll (or horizontal) Modifiers. Read here. It is a recent addition to the APIs so the documentation is very scarce. You'll have to look it up on the web for sample implementations.
"Read here"
So this is really frustrating... on the mymaths website: https://www.mymaths.co.uk/, there's an image of a primary school child on a computer with this image address: https://www.mymaths.co.uk/assets/images/big/primary-school-photo-2.jpg.
I've tried so many things, but I can't seem to replace it.
Say I wanted to replace it with a picture of a hamburger, with this address: https://images.ctfassets.net/sd2voc54sjgs/5L6livQvCw28S04IUSAcm6/6482ea1819e86be1b4f7e85bfbbfe9a6/Blog_Header_Hamburger_History_Option.png?fm=jpg&q=80&fl=progressive&w=1100.
So far I've tried lots of threads, but this image seems to be different from images on other websites, which is why my code isn't working on it:
var images3 = document.getElementsByTagName ("img");
var i3=0;
while(i3<images3.length)
{
if(images[i3].src == "https://www.mymaths.co.uk/assets/images/big/primary-school-photo-2.jpg")
{
images[i3].src = "https://images.ctfassets.net/sd2voc54sjgs/5L6livQvCw28S04IUSAcm6/6482ea1819e86be1b4f7e85bfbbfe9a6/Blog_Header_Hamburger_History_Option.png?fm=jpg&q=80&fl=progressive&w=1100";
}
i3=i3+1;
}
Can somebody help me please? Thank you.
Well, this was new for me too. Apparently, the <picture> tag is not just a wrapper - it's a smarter version of <img>.
It allows to chose different URLs for the image tag depending on screen size and type. For example, try to do this in developper tools:
I replaced srcset for the <source> that has (max-width: 767px), which means it is active when browser window is smaller than 767px. Now if you resize browser window to make it smaller, at some point the original image will be raplaced with burger image.
So what you want to do is to replace all <source>'s srcset. This worked for me:
// Limit the list of omages on those that are under `<picture>` tag
const images = document.querySelectorAll("picture img, picture source");
// RegExp to check if we want to replace the URL
const replaceChecker = /primary-school-photo-2\.jpg$/i;
// The replacement URL
const replaceWith = "https://images.ctfassets.net/sd2voc54sjgs/5L6livQvCw28S04IUSAcm6/6482ea1819e86be1b4f7e85bfbbfe9a6/Blog_Header_Hamburger_History_Option.png?fm=jpg&q=80&fl=progressive&w=1100";
for(const image of images) {
// Pick the name of the attribute we want to change based on whether it's <img> or <source>
const srcAttributeName = image.tagName.toLowerCase() == "img" ? "src" : "srcset";
const oldURL = image[srcAttributeName] + "";
if(replaceChecker.test(oldURL)) {
image[srcAttributeName] = replaceWith;
}
}
You could improve that by checking the media attribute and if it says minimum screen width, use URL for smaller image of the hamburger. That is set by the w GET param in the hamburger image's URL.
I do a growth animation of a fixed number of items, and after growth, i moved it to left.
Make growth by apply matrix
var trans_vector = new THREE.Matrix4().makeTranslation(0, height / 2, 0);
var graphics = new Graphics();
var rectangle = graphics.box(width, height, material);
rectangle.geometry.applyMatrix(trans_vector);
When a new item is added, i remove one from the container that will be added to scene
var children = this.container.children;
if (this.current_number === this.max_number) {
this.container.remove(children[0]);
this.current_number = this.max_number - 1;
}
object.position.copy(this.position); // this is a fixed position
this.container.add(object);
this.current_number++;
I write a function to translate to left, using tweenjs (sole)
animateTranslation : function(object, padding, duration) {
var new_x = object.position.x - padding; // Move to left
console.log(new_x); // Duplicated item here :(
new TWEEN.Tween(object.position).to({
x : new_x
}, duration).start();
},
And I remove all the "previous" items, using for loop
for (var i = 0; i < this.current_number-1; i++) {
this.animateTranslation(this.container.children[i],
this.padding,
this.duration/4)
}
The above code run correctly if we open and keep this current tab.
The problem is when we move to another tab, do something and then move back, some objects have the same position, that cause the translation to the same position. It looks weird.
It appears on both Chrome, Firefox and IE11.
I dont know why, please point me out what happened.
Most modern browsers choose to limit the delay in setInterval and pause requestAnimationFrame in inactive tabs. This is done in order to preserve CPU cycles and to reduce power consumption, especially important for mobile devices. You can find more details about each browser in this answer
That means, while the tab is inactive, your tween animation is not working the way it is normally expected to.
A simple solution would be to halt the main animation loop if the tab is inactive using a variable.
window.onfocus = function () {
isActive = true;
};
window.onblur = function () {
isActive = false;
};
I am developping an app that has a main activity showing the app title (two animated imageviews overlayed), two animated pictures, also overlayed, and three buttons. This activity also has a background image, which is the same that is used by the other activities.
The app flows from one activity to another and, eventually, this main activity is launched again (with the FLAG_ACTIVITY_CLEAR_TOP). Everything works fine but, after reloading it several times, an Out Of Memory error happens on my Android 2.1 device.
At first, I had all the images in the drawable folder and the problem appeared after reaching the main activity 5 times. Then, I adjusted the bitmap sizes and put them in the approppriate folders depending on the density and the problem appeared after reaching the main activity 14 times. Now, I just removed the background image for test purposes and the Out Of Memory appears after more than 20 re-launches.
Also, if I press the Home button and then switch back to my app, the problem seems not to appear until much later.
Moreover, I tested the app in a Nexus 5 and the Out Of Memory never happens.
So... is that a problem with my phone? with Android 2.1?
Thanks!
[EDIT]
I think that I have located the problem but, still, strange behavior.
For example, at one point, I need to recreate the activity. As the "recreate" method is not available for my min API level (7), I do it as follows:
Intent refresh = new Intent(getActivity(), getActivity().getClass());
refresh.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(refresh);
Which, I think, is correct. I release the onClickListeners and clear the animations in onStop(). However, if I put a breakpoint in onStop(), it is not called when I expect it to happen. Sometimes it is called as soon as the activity is recreated but sometimes it is called several seconds later.
However, if I press the Home button, onStop is properly called and when I switch back to the application everything works fine.
The easiest solution is to add in manifest under application tag
android:largeHeap="true"
But this is won't solve your problem, just delay it to a few more rounds
This link will help you to analyze your application and see what cause this:
http://blogs.innovationm.com/android-out-of-memory-error-causes-solution-and-best-practices/
My Guess this is related to images because i had this issue also..
The android official link to this problem is:
http://developer.android.com/training/displaying-bitmaps/index.html
This the link that helped me.. Try it out
http://developer.android.com/training/displaying-bitmaps/load-bitmap.html
Hope that helps
check this outLoading Large Bitmaps Efficiently
Read Bitmap Dimensions and Type
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;
Load a Scaled Down Version into Memory
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;
}
public static Bitmap decodeSampledBitmapFromResource(Resources res, int resId,
int reqWidth, int reqHeight) {
// First decode with inJustDecodeBounds=true to check dimensions
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeResource(res, resId, options);
// Calculate inSampleSize
options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
// Decode bitmap with inSampleSize set
options.inJustDecodeBounds = false;
return BitmapFactory.decodeResource(res, resId, options);
}
imageView.setImageBitmap(
decodeSampledBitmapFromResource(getResources(), R.id.myimage, 100, 100));
I am in the process of moving a webforms app to MVC3. Ironically enough, everything is cool beans except one thing - images are served from a handler, specifically the Microsoft Generated Image Handler. It works really well - on average a 450kb photo gets output at roughly 20kb.
The actual photo on disk weighs in at 417kb, so i am getting a great reduction.
Moving over to MVC3 i would like to drop the handler and use a controller action. However i seem to be unable to achieve the same kind of file size reduction when rendering the image. I walked through the source and took an exact copy of their image transform code yet i am only achieving 230~kb, which is still a lot bigger than what the ms handler is outputting - 16kb.
You can see an example of both the controller and the handler here
I have walked through the handler source code and cannot see anything that is compressing the image further. If you examine both images you can see a difference - the handler rendered image is less clear, more grainy looking, but still what i would consider satisfactory for my needs.
Can anyone give me any pointers here? is output compression somehow being used? or am i overlooking something very obvious?
The code below is used in my home controller to render the image, and is an exact copy of the FitImage method in the Image Transform class that the handler uses ...
public ActionResult MvcImage()
{
var file = Server.MapPath("~/Content/test.jpg");
var img = System.Drawing.Image.FromFile(file);
var sizedImg = MsScale(img);
var newFile = Server.MapPath("~/App_Data/test.jpg");
if (System.IO.File.Exists(newFile))
{
System.IO.File.Delete(newFile);
}
sizedImg.Save(newFile);
return File(newFile, "image/jpeg");
}
private Image MsScale(Image img)
{
var scaled_height = 267;
var scaled_width = 400;
int resizeWidth = 400;
int resizeHeight = 267;
if (img.Height == 0)
{
resizeWidth = img.Width;
resizeHeight = scaled_height;
}
else if (img.Width == 0)
{
resizeWidth = scaled_width;
resizeHeight = img.Height;
}
else
{
if (((float)img.Width / (float)img.Width < img.Height / (float)img.Height))
{
resizeWidth = img.Width;
resizeHeight = scaled_height;
}
else
{
resizeWidth = scaled_width;
resizeHeight = img.Height;
}
}
Bitmap newimage = new Bitmap(resizeWidth, resizeHeight);
Graphics gra = Graphics.FromImage(newimage);
SetupGraphics(gra);
gra.DrawImage(img, 0, 0, resizeWidth, resizeHeight);
return newimage;
}
private void SetupGraphics(Graphics graphics)
{
graphics.CompositingMode = CompositingMode.SourceCopy;
graphics.CompositingQuality = CompositingQuality.HighSpeed;
graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
graphics.SmoothingMode = SmoothingMode.HighSpeed;
}
If you don't set the quality on the encoder, it uses 100 by default. You'll never get a good size reduction by using 100 due to the way image formats like JPEG work. I've got a VB.net code example of how to set the quality parameter that you should be able to adapt.
80L here is the quality setting. 80 still gives you a fairly high quality image, but at DRASTIC size reduction over 100.
Dim graphic As System.Drawing.Graphics = System.Drawing.Graphics.FromImage(newImage)
graphic.InterpolationMode = Drawing.Drawing2D.InterpolationMode.HighQualityBicubic
graphic.SmoothingMode = Drawing.Drawing2D.SmoothingMode.HighQuality
graphic.PixelOffsetMode = Drawing.Drawing2D.PixelOffsetMode.HighQuality
graphic.CompositingQuality = Drawing.Drawing2D.CompositingQuality.HighQuality
graphic.DrawImage(sourceImage, 0, 0, width, height)
' now encode and send the new image
' This is the important part
Dim info() As Drawing.Imaging.ImageCodecInfo = Drawing.Imaging.ImageCodecInfo.GetImageEncoders()
Dim encoderParameters As New Drawing.Imaging.EncoderParameters(1)
encoderParameters.Param(0) = New Drawing.Imaging.EncoderParameter(Drawing.Imaging.Encoder.Quality, 80L)
ms = New System.IO.MemoryStream
newImage.Save(ms, info(1), encoderParameters)
When you save or otherwise write the image after setting the encoder parameters, it'll output it using the JPEG encoder (in this case) set to quality 80. That will get you the size savings you're looking for.
I believe it's defaulting to PNG format also, although Tridus' solution solves that also.
However, I highly suggest using this MVC-friendly library instead, as it avoids all the image resizing pitfalls and doesn't leak memory. It's very lightweight, free, and fully supported.