Skip to content

Commit

Permalink
Support high DPI rendering on canvas
Browse files Browse the repository at this point in the history
Detect the devicePixelRatio and calculate accordingly.
  • Loading branch information
sebdesign committed Mar 24, 2016
1 parent e144277 commit e87b054
Show file tree
Hide file tree
Showing 4 changed files with 133 additions and 63 deletions.
97 changes: 66 additions & 31 deletions dist/bundle.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion dist/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<html lang="en">
<head>
<meta charset="utf-8">
<title></title>
<title>Cap height</title>
</head>
<body>
<script src="bundle.js"></script>
Expand Down
95 changes: 65 additions & 30 deletions lib/capHeight.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,13 @@ var format = require("string-format");
*/
var threshold = 0.75;

/**
* The device pixel ratio, used to calculate the dimensions on high-DPI screens.
*
* @type Number
*/
var dpr = window.devicePixelRatio;

/**
* The element that will contain the canvases.
*
Expand Down Expand Up @@ -114,27 +121,35 @@ function measureTextHeight(properties, text) {
throw new RangeError("Cannot calculate the height of whitespace.");
}

var multiplier = 2;
var width = multiplier * fontSize * text.length;
var height = multiplier * fontSize;
var base = 2 * fontSize;

// Set the actual pixel dimensions
var dimensions = {
width: base * text.length,
height: base
};

var canvas = createCanvas(width, height);
var ctx = getContext(canvas);
ctx = drawContext(ctx, properties, width, height, text);
// Set the physical pixel dimensions
var physicalDimensions = _.mapValues(dimensions, toDot);

var pixelData = getPixelData(ctx, width, height);
var ascent = getAscent(pixelData, width);
var descent = getDescent(pixelData, width);
var canvas = createCanvas(dimensions, physicalDimensions);
var context = getContext(canvas);

context = drawContext(context, properties, dimensions, text);

var pixelData = getPixelData(context, physicalDimensions);
var ascent = getAscent(pixelData, physicalDimensions.width);
var descent = getDescent(pixelData, physicalDimensions.width);
var capHeight = getHeight(descent, ascent);

// Draw a rectangle marking the cap height bounds
ctx.fillStyle = "rgba(255, 85, 51, .1)";
ctx.fillRect(0, ascent, width, capHeight);
context.fillStyle = "rgba(255, 85, 51, .1)";
context.fillRect(0, toPx(ascent), toPx(physicalDimensions.width), toPx(capHeight));

// Display the canvas
displayCanvas(canvas);

return capHeight;
return toPx(capHeight);
}

/**
Expand All @@ -160,15 +175,20 @@ function getFontSize(properties) {
/**
* Create a canvas with the given dimensions.
*
* @param Number width
* @param Number height
* @param Object dimensions
* @param Object physicalDimensions
* @return HTMLCanvasElement
*/
function createCanvas(width, height) {
function createCanvas(dimensions, physicalDimensions) {
var canvas = document.createElement("canvas");

canvas.width = width;
canvas.height = height;
canvas.width = physicalDimensions.width;
canvas.height = physicalDimensions.height;

canvas.style.width = dimensions.width + "px";
canvas.style.height = dimensions.height + "px";

canvas.style.margin = "10px";

return canvas;
}
Expand All @@ -191,20 +211,23 @@ function displayCanvas(canvas) {
* @return CanvasRenderingContext2D
*/
function getContext(canvas) {
return canvas.getContext("2d");
var context = canvas.getContext("2d");

context.scale(dpr, dpr);

return context;
}

/**
* Draw the text and the background in the canvas context.
*
* @param CanvasRenderingContext2D context
* @param Object properties
* @param Number width
* @param Number height
* @param Object dimensions
* @param String context
* @return CanvasRenderingContext2D
*/
function drawContext(context, properties, width, height, text) {
function drawContext(context, properties, dimensions, text) {
context.font = composeFontProperty(properties);

// Align the text horizontally and vertically
Expand All @@ -214,11 +237,11 @@ function drawContext(context, properties, width, height, text) {
// Set all canvas pixeldata values to 255, with all the content
// data being 0. This lets us scan for data[i] != 255.
context.fillStyle = "white";
context.fillRect(0, 0, width, height);
context.fillRect(0, 0, dimensions.width, dimensions.height);
context.fillStyle = "black";

// Draw the text at the center
context.fillText(text, width / 2, height / 2);
context.fillText(text, dimensions.width / 2, dimensions.height / 2);

return context;
}
Expand All @@ -239,12 +262,11 @@ function composeFontProperty(properties) {
* Get the pixel data of the context.
*
* @param CanvasRenderingContext2D context
* @param Number width
* @param Number height
* @param Object dimensions
* @return Uint8ClampedArray
*/
function getPixelData(context, width, height) {
return context.getImageData(0, 0, width, height).data;
function getPixelData(context, dimensions) {
return context.getImageData(0, 0, dimensions.width, dimensions.height).data;
}

/**
Expand Down Expand Up @@ -289,15 +311,14 @@ function getDescent(pixelData, width) {
}

/**
* Get the y of the pixel data index, given the x width.
* Get the vertical position of the pixel data index, given the width.
*
* @param Number i
* @param Number width
* @return Number
*/
function getYPosition(i, width) {

return _.floor(i / (4 * width));
return _.floor((i / 4) / width);
}

/**
Expand All @@ -309,3 +330,17 @@ function getYPosition(i, width) {
function isNotWhite(value) {
return value < (255 * threshold);
}

/**
* Convert a physical pixel value to a CSS pixel
*/
function toPx(value) {
return value / dpr;
}

/**
* Convert a CSS pixel to a physical pixel
*/
function toDot(value) {
return value * dpr;
}
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "cap-height",
"version": "1.1.1",
"version": "1.1.2",
"description": "Calculate the cap height of fonts loaded with Web Font Loader",
"main": "./lib/capHeight.js",
"dependencies": {
Expand Down

0 comments on commit e87b054

Please sign in to comment.