How can I measure pixels in Chrome without an extension? - google-chrome

Due to security limitations at work, I am not allowed to install Chrome extensions. Chrome has a ruler built in to the developer tools, but I can't figure out how to define start and end points like a ruler would permit.
Are there any tools or techniques for measuring pixels that don't require installing a Chrome extension?

You could create your own ruler functionality and paste it into the console. Here's a basic example:
var fromX, fromY;
var svg = document.createElementNS ('http://www.w3.org/2000/svg',"svg");
svg.setAttribute("style", "position: absolute; top:0;left:0;height: " + document.body.clientHeight + "px;width: 100%");
var line = document.createElementNS('http://www.w3.org/2000/svg','line');
line.setAttribute("style", "stroke-width: 4; stroke: red");
svg.appendChild(line);
document.body.appendChild(svg);
document.body.addEventListener("mousedown", function (e) {
fromX = e.pageX;
fromY = e.pageY;
});
document.body.addEventListener("mousemove", function (e) {
if (fromX === undefined) {
return;
}
line.setAttribute("x1", fromX);
line.setAttribute("x2", e.pageX);
line.setAttribute("y1", fromY);
line.setAttribute("y2", e.pageY);
console.log(
[fromX, fromY], " to ", [e.pageX, e.pageY], "Distance:",
Math.sqrt(Math.pow(fromX - e.pageX, 2) + Math.pow(fromY - e.pageY, 2))
);
});
document.body.addEventListener("mouseup", function (e) {
fromX = undefined;
fromY = undefined;
});
You could also save it as a snippet.
Chrome extension code is also just JavaScript, so you can find the code used by the extension and save that as a snippet. The downside here is that the code might be compressed, and rely on features that aren't normally available in the browser.

If you are not looking for exact measurements, but a ballpark estimate, a tool I use to measure pixels on Chrome without using a Chrome extension is the macOS screenshot tool.
Press Command + shift + 4, click and drag to measure pixels, and press ESC or right-click (if left is your primary mouse button) to prevent screenshot from being taken.
Here's more info
According to link, you can zoom in apparently while in screenshot mode before taking a measurement, but I haven't tried it before.

Another way to measure pixel in chrome without extension is:
Open Developer tool by pressing F12 in window or mouse right click + inspect element
Inspect element in the browser to measure
Open Computed tab and mouse over on blocks to see highlighted area in browse.

I think the best you can do without any extensions is a mixture of using the ruler with Inspector, the Computed metrics panel, and the Command Line API to view offsets (as per #amza's suggestion).
In the following screenshot, I have inspected mainbar element of this page. You can see the pixel offset from the top-left, but the values aren't shown unfortunately. You can access the five most recently inspected elements in the Console using the variables $0-$4. In this case, I use $0, which is the currently selected one. The offsetLeft and offsetTop will give you the corresponding values that match what you see on the ruler. The Computing metrics panel shows the dimensions, including the padding, border and margin values. In this case, there's no outer values, but you would add those on to the 728x1032 dimension you see if there were.
Something like Page Ruler would be a lot easier, but given your limitation it's not possible.

Related

Chrome-specific issue when using D3 zoom

I'm working on a D3 application and have bumped into a strange zooming issue that occurs in Chrome but not Firefox (these are the only browsers I've tested my code in). I've boiled the problem down to the code snippet below.
Basically, in certain conditions, the zoom handler I have defined does not get called when I scroll over my canvas element (using either my mouse's scroll button or two fingers on my laptop's trackpad). However, clicking and dragging on the canvas to try to pan it does successfully invoke the zoom handler.
Details of the issue:
Zooming works as expected as long as the "click" event handler does not get called. Once it's called, scroll zooming will not work. And it has something to do with invoking ctx.getImageData in the click event handler.
If I omit the .call at the end of the canvas selection that invokes clearRect, the zooming issue occurs even if you did not trigger a "click" event
The issue occurs in other versions of D3 besides v5. I've tried v6 and v7.
The Chrome version I'm using is 92.0.4515.159
There are a couple ways I know of to resolve the issue:
The issue is resolved if the canvas element's opacity is not set to 0.
It can also be resolved by calling window.addEventListener('wheel', () => {})
From what I recall, another solution is defining the zoom behavior on a parent element of the canvas instead.
The code below creates a canvas element in the top left corner (it's not visible since its opacity is 0). Zooming on the element will print a "zooming" message to the console. Clicking on the canvas will print "click". You'll find that with the code as is, if you click on the element and then try to scroll zoom, "zooming" will not get printed. You can also run the code in JSFiddle: https://jsfiddle.net/yr3jdvhw/37/
I'd like to know what's causing this issue. As I mentioned, I could just set the canvas's opacity to a non-zero value to avoid the problem. But I'd really like to know the root cause.
<html>
<head>
<script src="https://d3js.org/d3.v5.min.js"></script>
</head>
<body>
<canvas class="my-canvas"></canvas>
<script>
const myCanvas = d3.select(".my-canvas")
.style('opacity', 0)
.on('click', function () {
console.log("click")
let ctx = this.getContext('2d')
const pixel = ctx.getImageData(0, 0, 1, 1)
})
.call(d3.zoom().on('zoom', () => {
console.log("zooming")
}))
.call(function (s) {
let ctx = s.node().getContext('2d')
ctx.clearRect(0, 0, s.node().width, s.node().height)
})
</script>
</body>
</html>
This is a Chrome bug, I just opened a new issue about it, let's hope this will get fixed soon.
From my investigations, the root issues are that they do handle wheel events at the compositor level and that when your canvas gets deaccelerated, it doesn't take the good path in the compositor anymore and thus isn't seen as a "wheel event handler region" anymore.
Calling getImageData() currently deaccelerates your canvas, it goes from the GPU to the CPU and stays there, which is why calling this method causes the issue. Similarly, until you perform any drawing operation, the canvas isn't moved to the GPU yet, and thus here too the bug reproduces.
Note that hopefully in a few versions { willReadFrequently } will be available without a flag, and that at this moment most canvases will always be accelerated.

threejs canvas todataurl is blank

Okay, I know that canvas.toDataUrl() will produce an image in png format. However, when I try to get the image from http://threejs.org/examples/#webgl_lines_sphere. All I see is a black image on chrome. To replicate the steps -
1) Open dev console and select the canvas element.
2) canvas = $0
3) context = canvas.getContext('webgl', {preserveDrawingBuffer: true})
4) img = canvas.toDataUrl()
5) document.write('<img src="'+img+'"/>')
The image is blank.
However, I tried with a different canvas at link http://threejs.org/examples/#canvas_geometry_cube. Please do the following steps to replicate.
1) Open dev console and select the canvas element.
2) canvas = $0
3) context = canvas.getContext('2d', {preserveDrawingBuffer: true})
4) img = canvas.toDataUrl()
5) document.write('<img src="'+img+'"/>')
This gave the expected result. Why is there a difference and how can this be avoided to retrieve first image too?
I was also getting a solid black image.
My code previously was:
this.renderer = new THREE.WebGLRenderer({premultipliedAlpha: false});
I have changed the parameter in the THREE.WebGLRenderer to:
this.renderer = new THREE.WebGLRenderer({preserveDrawingBuffer: true});
I am getting an image on taking a snapshot.
Hope it helps.
This is because the first example (see sources line 103) does use a THREE.WebGLRenderer creator, while the second one (see sources line 92) uses a THREE.CanvasRenderer.
Some notes :
There is no preserveDrawingBuffer contextAttribute in the context2d API, only in the WebGL one.
You can create only one context per canvas element.
You can't set the preserveDrawingBuffer flag after context's creation
With three.js you can simply call renderer.domElement.toDataURL() (you'll need to go to the iframe target to be able to call from the dev tools).
Another solution, (and better than preserveDrawingBuffer flag) is to call canvas.toDataURL() in the rendering loop itself, before the browser takes controls again.

Unexpected CORS issue for normal images in Chrome and iOS Safari

I'm facing a CORS issue that is driving me insane. Allow me to share an example URL:
http://www.jungledragon.com/image/19905/mature_female_eastern_forktail.html/zoom
As the issue can only be reproduced once per page, here is a list of other images:
http://www.jungledragon.com/all/recent
From that overview, you can open any photo page. Next, from that photo page click the image once more to launch it fullscreen, as that is where the issue lies.
Now allow me to explain the setup, and the problem. The site itself is hosted on a Linux server within my control. The site is at www.jungledragon.com. The images, however, are stored at Amazon S3, where the image bucket has an alias of media.jungledragon.com.
The basic situation is simple:
<div id="slideshow-image-container">
<div class="slideshow-image-wrapper">
<img src="http://media.jungledragon.com/images/1755/19907_large.jpg?AWSAccessKeyId=05GMT0V3GWVNE7GGM1R2&Expires=1409788810&Signature=QH26XDrVuhyr1Qimd7IOBsnui5s%3D" id="19907" class="img-slideshow img-sec wide" data-constrained="true" data-maxheight="2056" crossorigin="anonymous">
</div>
</div>
As you can see, I'm just using the normal 'html' way of loading an image. The image URL is signed and can time out, but that shouldn't be relevant. It is my understanding that CORS does not apply to this situation, since loading images from an external domain this way has been supported for decades. The image is not loaded using javascript, after all.
Just to be sure though, the crossorigin attribute is set in HTML. Furthermore, as a way of testing, I have set a very liberal CORS policy on the image bucket:
<?xml version="1.0" encoding="UTF-8"?>
<CORSConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
<CORSRule>
<AllowedOrigin>*</AllowedOrigin>
<AllowedMethod>GET</AllowedMethod>
<MaxAgeSeconds>3000</MaxAgeSeconds>
<AllowedHeader>Authorization</AllowedHeader>
</CORSRule>
<CORSRule>
<AllowedOrigin>*</AllowedOrigin>
<AllowedMethod>PUT</AllowedMethod>
<MaxAgeSeconds>3000</MaxAgeSeconds>
<AllowedHeader>Content-Type</AllowedHeader>
<AllowedHeader>x-amz-acl</AllowedHeader>
<AllowedHeader>origin</AllowedHeader>
</CORSRule>
</CORSConfiguration>
Now, the situation gets a bit more complicated. The fullscreen image viewer is supposed to get a background color that is the dominant/average color of the actual image on screen. That color is calculated using canvas, yet it is only calculated once. The first time it is calculated for that image, the result is communicated to the back-end using an ajax call and then stored forever. Subsequent visits to the image will not run the calculation logic again, it will simply set the background color of the body element and all is good.
Here is the logic that does the calculation:
<script>
$( document ).ready(function() {
<?php if (!$bigimage['dominantcolor']) { ?>
$('#<?= $bigimage['image_id'] ?>').load(function(){
var rgb = getAverageRGB(document.getElementById('<?= $bigimage['image_id'] ?>'));
document.body.style.backgroundColor = 'rgb('+rgb.r+','+rgb.g+','+rgb.b+')';
if (rgb!==false) {
$.get(basepath + "image/<?= $bigimage['image_id'] ?>/setcolor/" + rgb.r + "-" + rgb.g + "-" + rgb.b);
}
});
<?php } ?>
});
Yes, I'm mixing in back-end code with front-end code. The above code says that if we do not yet know the dominant color in the scene, calculate it. The load function is used because at document ready, the actual image from the normal html may not have been loaded completely. Next, if the dominant color is not known yet, and the image is loaded, we trigger the function that calculates the dominant color. Here it is:
function getAverageRGB(imgEl) {
var blockSize = 5, // only visit every 5 pixels
defaultRGB = {r:0,g:0,b:0}, // for non-supporting envs
canvas = document.createElement('canvas'),
context = canvas.getContext && canvas.getContext('2d'),
data, width, height,
i = -4,
length,
rgb = {r:0,g:0,b:0},
count = 0;
if (!context) {
return defaultRGB;
}
height = canvas.height = imgEl.naturalHeight || imgEl.offsetHeight || imgEl.height;
width = canvas.width = imgEl.naturalWidth || imgEl.offsetWidth || imgEl.width;
imgEl.crossOrigin = "anonymous";
context.drawImage(imgEl, 0, 0);
try {
data = context.getImageData(0, 0, width, height);
} catch(e) {
/* security error, img on diff domain */
return false;
}
length = data.data.length;
while ( (i += blockSize * 4) < length ) {
++count;
rgb.r += data.data[i];
rgb.g += data.data[i+1];
rgb.b += data.data[i+2];
}
// ~~ used to floor values
rgb.r = ~~(rgb.r/count);
rgb.g = ~~(rgb.g/count);
rgb.b = ~~(rgb.b/count);
return rgb;
}
The following line is CORS-relevant:
data = context.getImageData(0, 0, width, height);
Although I believe I have set up CORS correctly, I can live with this code failing in some browsers. It seems to work fine in Firefox and IE11, for example. If it fails, I would expect it to fail calculating the dominant color. However, something far worse is happening in highly specific cases: the image is not shown alltogether.
My thinking is that my 'classic' loading of the image via img src tags should have nothing to do with this script working or failing, in all cases at least the image should just load, irrespective of the canvas trick.
Here are the situations I discovered where the image does not load alltogether, which I consider a major issue:
On iOS7 on iPhone 5, the first load works fine. The calculation may fail but the image loads. Refreshing the page often breaks the image. 3rd and 4th tries then continue to succeed, and so on.
Worse, at work in Chrome 36 the image does not load alltogether. I say at work, since at home it is not an issue. Possibly a proxy makes the difference. I can refresh all I want, for images that do not have the calculation ran yet, it keeps failing.
The natural thing to do then is to debug it using Chrome's inspector. Guess what? With the inspector open, it always succeeds. The image will always load and the CORS request headers and responses look perfectly fine. This leaves me with virtually no way to debug this. I can tell though that when opening the inspector when the image does not load does give me the "CORS error" in the console, from the previous request I made. Refreshing with the inspector open will then make that go away.
From reading other questions I've learned that cache may be an influence, yet more likely the issue lies in the origin header not sent by the browser. I believe the issue may be in that direction, yet I fail to understand this:
How it influences my "normal" loading of the image using img tags
How it is only an issue behind a proxy (supposedly) in Chrome, and only when the inspector windows is closed
How it works so unreliably and inconsistently in Safari on iOS
As said, I can live with only some browsers succeeding with the canvas part, but I can't live with the image not being normally loaded in any case. That part should just work.
I realize the situation is incredibly hard for you to debug, but I hope my explanation triggers some much-needed help.
Update: I've discovered that when I remove crossorigin="anonymous" from the img tag, the image will load correctly in the specific scenarios I mentioned. However, the consequence of that move is that the color calculation will no longer work in Chrome, not at home and not at work. It continues to work in Firefox though. I'm investigating what to do next.
I managed to solve the issue myself. I still cannot fully explain cause and effect here, but this is what I did:
I removed crossorigin="anonymous" from the html's img element. This will at least make sure that the image is always loaded.
The color calculation part I solved by basically rewriting its logic:
var imgSrc = $('#<?= $bigimage['image_id'] ?>').attr('src');
var cacheBurstPrefix = imgSrc.indexOf("?") > -1 ? '&' : '?';
imgSrc += cacheBurstPrefix + 'ts=' + new Date().getTime();
var imagePreloader = new Image();
imagePreloader.crossOrigin = "Anonymous";
imagePreloader.src = imgSrc;
$(imagePreloader).imagesLoaded(function() {
var rgb = getAverageRGB(imagePreloader);
document.body.style.backgroundColor = 'rgb('+rgb.r+','+rgb.g+','+rgb.b+')';
if (rgb!==false) {
$.get(basepath + "image/<?= $bigimage['image_id'] ?>/setcolor/" + rgb.r + "-" + rgb.g + "-" + rgb.b);
}
});
Instead of reusing the img element from the html, I'm creating a new in-memory image element. Using a cache bursting technique I'm making sure it is freshly loaded. Next, I'm using imagesLoaded (a 3rd party plugin) to detect the event of this in-memory image being loaded, which is far more reliable than jQuery's load() event.
I've tested extensively and can confirm that in no case does normal image loading ever break again. It works in every browser and proxy situation. As an added bonus, the color calculation part now seems to work in far more browsers, including several mobile browsers.
Although I am still not confident on the root cause, after much frustration I'm very happy with the new situation.

WriteableBitmap.Render() not capturing when control not in visual tree (all black pixels instead)

I'm trying to capture the content of a WebBrowser control that is not in the visible tree, but the WriteableBitmap.Render() is capturing a (correctly sized) bitmap of all black pixels. If I put the WebBrowser into the visual tree, the capture works fine.
WriteableBitmap doc is emphatic that it works on controls not in the visual tree, and I'm making the required calls to Measure() and Arrange(). Relevant code (originally in WebBrowser_LoadCompleted event, for demo moved to renderButton_OnClick)
hiddenBrowser.Measure(new Size(hiddenBrowser.Width, hiddenBrowser.Height)); // pretend there's plenty of space.
hiddenBrowser.Arrange(new Rect(0.0, 0.0, hiddenBrowser.Width, hiddenBrowser.Height)); // pretend we know where this is going.
//noHelp hiddenBrowser.UpdateLayout();
WriteableBitmap _bitmap = new WriteableBitmap((int)theImage.Width, (int)theImage.Height);
_bitmap.Render(hiddenBrowser, new ScaleTransform());
_bitmap.Invalidate();
theImage.Source = _bitmap;
A complete project demonstrating the problem is available at: https://skydrive.live.com/redir?resid=193BF22F5BBA1A84!10526&authkey=!AGeH6YC_NttOmj0
Press Unhide (webBrowser shows in visual tree), then Go, then Render --> render captures OK.
Then press Hide (webBrowser disappears), then Go, then Render --> Black Screen of Ignorance.
Originally, I thought this was a timing issue (e.g maybe browser hadn't finished painting the web page even though LoadComplete event fired), but it cannot be; in the demo, I wait for 10s of seconds before pressing 'render' button which does the render, and still get black image.
I reproduced your problem. It seems like the Control isn't rendered if not necessary.
Do you really need the WebBrowser to be in the Visual Tree?
If you just want it to be invisible, setting the Visibility to Collapsed won't work either (I tried).
An ugly trick that works for the WebBrowser to be invisible to the user but still allows a WriteableBitmap rendering is to translate the control out of the ViewPort. To do that, just use a TranslateTransform:
hiddenBrowser = new WebBrowser();
hiddenBrowser.Width = theImage.Width;
hiddenBrowser.Height = theImage.Height;
hiddenBrowser.LoadCompleted += hiddenBrowser_LoadCompleted;
hiddenBrowser.NavigationFailed += hiddenBrowser_NavigationFailed;
hiddenBrowser.LayoutUpdated += hiddenBrowser_LayoutUpdated;
hiddenBrowser.RenderTransform = new TranslateTransform { X = 2000, Y = 2000 }; // this is the code I added.
Ugly but working solution!
This turns out to have been doomed from the start.
You can't run the WebBrowser control in the background on WP8, period. It's an Unsupported API.
And, as noted above, there's the issue (maybe not a bug?) that WebBrowser won't provide a bitmap when it's not in the visual tree, anyway.

How to solve webkitRequestAnimationFrame giving extremely high FPS

Today I've been trying to get myself acquainted with anything new HTML5 has to offer, in particular the canvas. I came upon the site www.html5canvastutorials.com and began following some of the tutorials and playing around with the code a bit in different browsers. When I got to the following example I noticed something odd in google chrome. http://www.html5canvastutorials.com/advanced/html5-canvas-oscillation-animation/
The webkitRequestAnimationFrame function is supposed to help reduce FPS (and thus CPU costs) when not actively on the site, for example when you go to a different tab. However, when I tried the example, I noticed that this does not always appear to give pleasant results.
Google Chrome as active window, site on current tab: Get around 60
FPS, great!
Google Chrome as active window, on a different tab: Get
around 1 FPS, very good.
Google Chrome as active window, on my TV
(used as second monitor), 120 FPS, odd, but no complaints.
Google Chrome not as active window, but on a different tab, also around 1
FPS or so, perfect.
Then the bad part:
If my site is on the current tab, but I have another window completely covering the google chrome window (say a maximized window for example), the FPS shoots up to around 2500 (and consequently maxes one CPU core).
Everything works perfectly normal when I try the same site in Firefox.
This fiddle's an example where it shows the average FPS since the last refresh: http://jsfiddle.net/kmKZa/55/
(I pretty much copied the code from the tutorial site)
I would like to know how I can prevent these scary CPU spikes if anybody has any ideas. Thanks in advance for any advice!
One possible solution is to simply cancel the AnimationFrame loop when you blur the window, and request it again when you refocus it.
var isPaused = false,
animFrame;
loop();
$(window)
.on('focus', function() {
if( isPaused ) {
isPaused = false;
loop();
}
})
.on('blur', function() {
cancelRequestAnimationFrame( animFrame );
isPaused = true;
});
function loop() {
//your crazy loop code
animFrame = requestAnimationFrame( loop );
}
RAF automatically stop execution on "idle".
U don't need to stop RAF loop execution