-
Notifications
You must be signed in to change notification settings - Fork 3.1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
This change creates a new API method for the CanvasRenderingContext2d that enables the rendering of the TextCluster objects returned by the API implemented on the previous CL in the stack. The new method receives a TextCluster object, an x value, and a y value. This coordinates represent by how much to shift the rendered text from the x and y attributes of the TextCluster object. In practice, this means that calling fillText() from a point p will have the same result as measuring that text and calling fillTextCluster() on each cluster passing the same point as parameter. This is true provided that the textAlign and textBaseline values for the rendering context are set to the same values as passed to TextMetrics::getTextClustersForRange(). The implementation required a new Font::SubRunWidth to calculate the width and the bounds of the TextCluster object to be rendered. It runs the bidi algorithm to get the visual runs, and once it finds the correct one, uses GetCharacterRange() to obtain these values. The new paths are only used when rendering a TextCluster to avoid potential performance issues for the existing ctx.fillText() and ctx.StrokeText() methods. The new API was enabled under the `ExtendedTextMetrics` flag. Bug: 341213359 Change-Id: I2f58b3ef958704910aedaa450923ed3751a77bdf Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/5854270 Reviewed-by: Jean-Philippe Gravel <jpgravel@chromium.org> Commit-Queue: Andres Ricardo Perez <andresrperez@chromium.org> Cr-Commit-Position: refs/heads/main@{#1370047}
- Loading branch information
1 parent
b9d1a97
commit 0af1c92
Showing
19 changed files
with
1,616 additions
and
0 deletions.
There are no files selected for viewing
77 changes: 77 additions & 0 deletions
77
html/canvas/element/text/2d.text.measure.text-clusters-range.tentative.html
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
<!DOCTYPE html> | ||
<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. --> | ||
<meta charset="UTF-8"> | ||
<title>Canvas test: 2d.text.measure.text-clusters-range.tentative</title> | ||
<script src="/resources/testharness.js"></script> | ||
<script src="/resources/testharnessreport.js"></script> | ||
<script src="/html/canvas/resources/canvas-tests.js"></script> | ||
<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css"> | ||
<style> | ||
@font-face { | ||
font-family: CanvasTest; | ||
src: url("/fonts/CanvasTest.ttf"); | ||
} | ||
</style> | ||
<body class="show_output"> | ||
|
||
<h1>2d.text.measure.text-clusters-range.tentative</h1> | ||
<p class="desc">Test that getTextClusters() and fillTextCluster() correctly render different ranges of the input text.</p> | ||
|
||
|
||
<span style="font-family: CanvasTest; position: absolute; visibility: hidden">A</span> | ||
<p class="output">Actual output:</p> | ||
<canvas id="c" class="output" width="400" height="300"><p class="fallback">FAIL (fallback content)</p></canvas> | ||
|
||
<ul id="d"></ul> | ||
<script> | ||
promise_test(async t => { | ||
|
||
var canvas = document.getElementById('c'); | ||
var ctx = canvas.getContext('2d'); | ||
|
||
// Renders all the clusters in the list from position (x, y). | ||
function renderClusters(clusters, x, y) { | ||
for (const cluster of clusters) { | ||
ctx.fillTextCluster(cluster, x, y); | ||
} | ||
} | ||
|
||
await document.fonts.ready; | ||
|
||
ctx.font = '50px CanvasTest'; | ||
ctx.textAlign = 'left'; | ||
ctx.textBaseline = 'top'; | ||
const text = 'EEEEE'; | ||
let tm = ctx.measureText(text); | ||
|
||
// Background color. | ||
ctx.fillStyle = '#f00'; | ||
ctx.fillRect(0, 0, canvas.width, canvas.height); | ||
|
||
ctx.fillStyle = '#0f0'; | ||
|
||
// Without the first character. | ||
renderClusters(tm.getTextClusters(1, 5), 0, 0); | ||
_assertPixelApprox(canvas, 5,5, 255,0,0,255, 2); | ||
_assertPixelApprox(canvas, 55,5, 0,255,0,255, 2); | ||
_assertPixelApprox(canvas, 105,5, 0,255,0,255, 2); | ||
_assertPixelApprox(canvas, 155,5, 0,255,0,255, 2); | ||
_assertPixelApprox(canvas, 205,5, 0,255,0,255, 2); | ||
// Without the last character. | ||
renderClusters(tm.getTextClusters(0, 4), 0, 100); | ||
_assertPixelApprox(canvas, 5,105, 0,255,0,255, 2); | ||
_assertPixelApprox(canvas, 55,105, 0,255,0,255, 2); | ||
_assertPixelApprox(canvas, 105,105, 0,255,0,255, 2); | ||
_assertPixelApprox(canvas, 155,105, 0,255,0,255, 2); | ||
_assertPixelApprox(canvas, 205,105, 255,0,0,255, 2); | ||
// Only the middle character. | ||
renderClusters(tm.getTextClusters(2, 3), 0, 150); | ||
_assertPixelApprox(canvas, 5,155, 255,0,0,255, 2); | ||
_assertPixelApprox(canvas, 55,155, 255,0,0,255, 2); | ||
_assertPixelApprox(canvas, 105,155, 0,255,0,255, 2); | ||
_assertPixelApprox(canvas, 155,155, 255,0,0,255, 2); | ||
_assertPixelApprox(canvas, 205,155, 255,0,0,255, 2); | ||
|
||
}, "Test that getTextClusters() and fillTextCluster() correctly render different ranges of the input text."); | ||
</script> | ||
|
77 changes: 77 additions & 0 deletions
77
...canvas/element/text/2d.text.measure.text-clusters-rendering-align.tentative-expected.html
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
<!DOCTYPE html> | ||
<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. --> | ||
<meta charset="UTF-8"> | ||
<title>Canvas test: 2d.text.measure.text-clusters-rendering-align.tentative</title> | ||
<h1 style="font-size: 20px;">2d.text.measure.text-clusters-rendering-align.tentative</h1> | ||
<p class="desc">Test that fillTextCluster() correctly positions the text, taking into account the textAlign from the context at the time the text was measured.</p> | ||
|
||
<div style="display: grid; grid-gap: 4px; | ||
grid-template-columns: repeat(3, max-content); | ||
font-size: 13px; text-align: center;"> | ||
<span> | ||
<div>ctx_align_left</div> | ||
<canvas id="canvas0" width="250" height="43" style="outline: 1px solid"> | ||
<p class="fallback">FAIL (fallback content)</p> | ||
</canvas> | ||
<script type="module"> | ||
const canvas = document.getElementById("canvas0"); | ||
const ctx = canvas.getContext('2d'); | ||
|
||
ctx.font = '20px serif'; | ||
const text = 'Test ☺️ א'; | ||
const x = canvas.width / 2; | ||
const y = canvas.height / 2; | ||
|
||
ctx.textAlign = 'left'; | ||
|
||
// Rendering all clusters with the same (x, y) parameters must be | ||
// equivalent to a fillText() call at (x, y). | ||
ctx.fillText(text, x, y); | ||
</script> | ||
</span> | ||
|
||
<span> | ||
<div>ctx_align_center</div> | ||
<canvas id="canvas1" width="250" height="43" style="outline: 1px solid"> | ||
<p class="fallback">FAIL (fallback content)</p> | ||
</canvas> | ||
<script type="module"> | ||
const canvas = document.getElementById("canvas1"); | ||
const ctx = canvas.getContext('2d'); | ||
|
||
ctx.font = '20px serif'; | ||
const text = 'Test ☺️ א'; | ||
const x = canvas.width / 2; | ||
const y = canvas.height / 2; | ||
|
||
ctx.textAlign = 'center'; | ||
|
||
// Rendering all clusters with the same (x, y) parameters must be | ||
// equivalent to a fillText() call at (x, y). | ||
ctx.fillText(text, x, y); | ||
</script> | ||
</span> | ||
|
||
<span> | ||
<div>ctx_align_right</div> | ||
<canvas id="canvas2" width="250" height="43" style="outline: 1px solid"> | ||
<p class="fallback">FAIL (fallback content)</p> | ||
</canvas> | ||
<script type="module"> | ||
const canvas = document.getElementById("canvas2"); | ||
const ctx = canvas.getContext('2d'); | ||
|
||
ctx.font = '20px serif'; | ||
const text = 'Test ☺️ א'; | ||
const x = canvas.width / 2; | ||
const y = canvas.height / 2; | ||
|
||
ctx.textAlign = 'right'; | ||
|
||
// Rendering all clusters with the same (x, y) parameters must be | ||
// equivalent to a fillText() call at (x, y). | ||
ctx.fillText(text, x, y); | ||
</script> | ||
</span> | ||
|
||
</div> |
90 changes: 90 additions & 0 deletions
90
html/canvas/element/text/2d.text.measure.text-clusters-rendering-align.tentative.html
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,90 @@ | ||
<!DOCTYPE html> | ||
<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. --> | ||
<meta charset="UTF-8"> | ||
<link rel="match" href="2d.text.measure.text-clusters-rendering-align.tentative-expected.html"> | ||
<title>Canvas test: 2d.text.measure.text-clusters-rendering-align.tentative</title> | ||
<h1 style="font-size: 20px;">2d.text.measure.text-clusters-rendering-align.tentative</h1> | ||
<p class="desc">Test that fillTextCluster() correctly positions the text, taking into account the textAlign from the context at the time the text was measured.</p> | ||
|
||
<div style="display: grid; grid-gap: 4px; | ||
grid-template-columns: repeat(3, max-content); | ||
font-size: 13px; text-align: center;"> | ||
<span> | ||
<div>ctx_align_left</div> | ||
<canvas id="canvas0" width="250" height="43" style="outline: 1px solid"> | ||
<p class="fallback">FAIL (fallback content)</p> | ||
</canvas> | ||
<script type="module"> | ||
const canvas = document.getElementById("canvas0"); | ||
const ctx = canvas.getContext('2d'); | ||
|
||
ctx.font = '20px serif'; | ||
const text = 'Test ☺️ א'; | ||
const x = canvas.width / 2; | ||
const y = canvas.height / 2; | ||
|
||
ctx.textAlign = 'left'; | ||
let tm = ctx.measureText(text); | ||
const clusters = tm.getTextClusters(); | ||
|
||
// Rendering all clusters with the same (x, y) parameters must be | ||
// equivalent to a fillText() call at (x, y). | ||
for (const cluster of clusters) { | ||
ctx.fillTextCluster(cluster, x, y); | ||
} | ||
</script> | ||
</span> | ||
|
||
<span> | ||
<div>ctx_align_center</div> | ||
<canvas id="canvas1" width="250" height="43" style="outline: 1px solid"> | ||
<p class="fallback">FAIL (fallback content)</p> | ||
</canvas> | ||
<script type="module"> | ||
const canvas = document.getElementById("canvas1"); | ||
const ctx = canvas.getContext('2d'); | ||
|
||
ctx.font = '20px serif'; | ||
const text = 'Test ☺️ א'; | ||
const x = canvas.width / 2; | ||
const y = canvas.height / 2; | ||
|
||
ctx.textAlign = 'center'; | ||
let tm = ctx.measureText(text); | ||
const clusters = tm.getTextClusters(); | ||
|
||
// Rendering all clusters with the same (x, y) parameters must be | ||
// equivalent to a fillText() call at (x, y). | ||
for (const cluster of clusters) { | ||
ctx.fillTextCluster(cluster, x, y); | ||
} | ||
</script> | ||
</span> | ||
|
||
<span> | ||
<div>ctx_align_right</div> | ||
<canvas id="canvas2" width="250" height="43" style="outline: 1px solid"> | ||
<p class="fallback">FAIL (fallback content)</p> | ||
</canvas> | ||
<script type="module"> | ||
const canvas = document.getElementById("canvas2"); | ||
const ctx = canvas.getContext('2d'); | ||
|
||
ctx.font = '20px serif'; | ||
const text = 'Test ☺️ א'; | ||
const x = canvas.width / 2; | ||
const y = canvas.height / 2; | ||
|
||
ctx.textAlign = 'right'; | ||
let tm = ctx.measureText(text); | ||
const clusters = tm.getTextClusters(); | ||
|
||
// Rendering all clusters with the same (x, y) parameters must be | ||
// equivalent to a fillText() call at (x, y). | ||
for (const cluster of clusters) { | ||
ctx.fillTextCluster(cluster, x, y); | ||
} | ||
</script> | ||
</span> | ||
|
||
</div> |
87 changes: 87 additions & 0 deletions
87
...vas/element/text/2d.text.measure.text-clusters-rendering-baseline.tentative-expected.html
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
<!DOCTYPE html> | ||
<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. --> | ||
<meta charset="UTF-8"> | ||
<title>Canvas test: 2d.text.measure.text-clusters-rendering-baseline.tentative</title> | ||
<h1 style="font-size: 20px;">2d.text.measure.text-clusters-rendering-baseline.tentative</h1> | ||
<p class="desc">Test that fillTextCluster() correctly positions the text, taking into account the textBaseline from the context at the time the text was measured.</p> | ||
|
||
<div style="display: grid; grid-gap: 4px; | ||
grid-template-columns: repeat(4, max-content); | ||
font-size: 13px; text-align: center;"> | ||
<span> | ||
<div>ctx_baseline_top</div> | ||
<canvas id="canvas0" width="180" height="43" style="outline: 1px solid"> | ||
<p class="fallback">FAIL (fallback content)</p> | ||
</canvas> | ||
<script type="module"> | ||
const canvas = document.getElementById("canvas0"); | ||
const ctx = canvas.getContext('2d'); | ||
|
||
ctx.font = '20px serif'; | ||
const text = 'Test ☺️ א'; | ||
const x = 20; | ||
const y = canvas.height / 2; | ||
|
||
ctx.textBaseline = 'top'; | ||
ctx.fillText(text, x, y); | ||
</script> | ||
</span> | ||
|
||
<span> | ||
<div>ctx_baseline_middle</div> | ||
<canvas id="canvas1" width="180" height="43" style="outline: 1px solid"> | ||
<p class="fallback">FAIL (fallback content)</p> | ||
</canvas> | ||
<script type="module"> | ||
const canvas = document.getElementById("canvas1"); | ||
const ctx = canvas.getContext('2d'); | ||
|
||
ctx.font = '20px serif'; | ||
const text = 'Test ☺️ א'; | ||
const x = 20; | ||
const y = canvas.height / 2; | ||
|
||
ctx.textBaseline = 'middle'; | ||
ctx.fillText(text, x, y); | ||
</script> | ||
</span> | ||
|
||
<span> | ||
<div>ctx_baseline_bottom</div> | ||
<canvas id="canvas2" width="180" height="43" style="outline: 1px solid"> | ||
<p class="fallback">FAIL (fallback content)</p> | ||
</canvas> | ||
<script type="module"> | ||
const canvas = document.getElementById("canvas2"); | ||
const ctx = canvas.getContext('2d'); | ||
|
||
ctx.font = '20px serif'; | ||
const text = 'Test ☺️ א'; | ||
const x = 20; | ||
const y = canvas.height / 2; | ||
|
||
ctx.textBaseline = 'bottom'; | ||
ctx.fillText(text, x, y); | ||
</script> | ||
</span> | ||
|
||
<span> | ||
<div>ctx_baseline_alphabetic</div> | ||
<canvas id="canvas3" width="180" height="43" style="outline: 1px solid"> | ||
<p class="fallback">FAIL (fallback content)</p> | ||
</canvas> | ||
<script type="module"> | ||
const canvas = document.getElementById("canvas3"); | ||
const ctx = canvas.getContext('2d'); | ||
|
||
ctx.font = '20px serif'; | ||
const text = 'Test ☺️ א'; | ||
const x = 20; | ||
const y = canvas.height / 2; | ||
|
||
ctx.textBaseline = 'alphabetic'; | ||
ctx.fillText(text, x, y); | ||
</script> | ||
</span> | ||
|
||
</div> |
Oops, something went wrong.