зеркало из https://github.com/mozilla/gecko-dev.git
Merge mozilla-central to autoland. a=merge CLOSED TREE
This commit is contained in:
Коммит
f64dbd1034
|
@ -147,12 +147,6 @@ skip-if = e10s # Bug 1221911, bug 1222289, frequent e10s timeouts
|
|||
skip-if = e10s # Bug 1221911, bug 1222289, frequent e10s timeouts
|
||||
[browser_graphs-10c.js]
|
||||
skip-if = e10s # Bug 1221911, bug 1222289, frequent e10s timeouts
|
||||
[browser_graphs-11a.js]
|
||||
skip-if = e10s # Bug 1221911, bug 1222289, frequent e10s timeouts
|
||||
[browser_graphs-11b.js]
|
||||
skip-if = e10s # Bug 1221911, bug 1222289, frequent e10s timeouts
|
||||
[browser_graphs-12.js]
|
||||
skip-if = e10s # Bug 1221911, bug 1222289, frequent e10s timeouts
|
||||
[browser_graphs-13.js]
|
||||
skip-if = e10s # Bug 1221911, bug 1222289, frequent e10s timeouts
|
||||
[browser_graphs-14.js]
|
||||
|
|
|
@ -1,87 +0,0 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
// Tests that bar graph create a legend as expected.
|
||||
|
||||
const BarGraphWidget = require("devtools/client/shared/widgets/BarGraphWidget");
|
||||
|
||||
const CATEGORIES = [
|
||||
{ color: "#46afe3", label: "Foo" },
|
||||
{ color: "#eb5368", label: "Bar" },
|
||||
{ color: "#70bf53", label: "Baz" },
|
||||
];
|
||||
|
||||
add_task(async function() {
|
||||
await addTab("about:blank");
|
||||
await performTest();
|
||||
gBrowser.removeCurrentTab();
|
||||
});
|
||||
|
||||
async function performTest() {
|
||||
const [host, , doc] = await createHost();
|
||||
const graph = new BarGraphWidget(doc.body);
|
||||
await graph.once("ready");
|
||||
|
||||
testGraph(graph);
|
||||
|
||||
await graph.destroy();
|
||||
host.destroy();
|
||||
}
|
||||
|
||||
function testGraph(graph) {
|
||||
graph.format = CATEGORIES;
|
||||
graph.setData([{ delta: 0, values: [] }]);
|
||||
|
||||
const legendContainer = graph._document.querySelector(
|
||||
".bar-graph-widget-legend"
|
||||
);
|
||||
ok(legendContainer, "A legend container should be available.");
|
||||
is(
|
||||
legendContainer.childNodes.length,
|
||||
3,
|
||||
"Three legend items should have been created."
|
||||
);
|
||||
|
||||
const legendItems = graph._document.querySelectorAll(
|
||||
".bar-graph-widget-legend-item"
|
||||
);
|
||||
is(
|
||||
legendItems.length,
|
||||
3,
|
||||
"Three legend items should exist in the entire graph."
|
||||
);
|
||||
|
||||
is(
|
||||
legendItems[0].querySelector("[view=color]").style.backgroundColor,
|
||||
"rgb(70, 175, 227)",
|
||||
"The first legend item has the correct color."
|
||||
);
|
||||
is(
|
||||
legendItems[1].querySelector("[view=color]").style.backgroundColor,
|
||||
"rgb(235, 83, 104)",
|
||||
"The second legend item has the correct color."
|
||||
);
|
||||
is(
|
||||
legendItems[2].querySelector("[view=color]").style.backgroundColor,
|
||||
"rgb(112, 191, 83)",
|
||||
"The third legend item has the correct color."
|
||||
);
|
||||
|
||||
is(
|
||||
legendItems[0].querySelector("[view=label]").textContent,
|
||||
"Foo",
|
||||
"The first legend item has the correct label."
|
||||
);
|
||||
is(
|
||||
legendItems[1].querySelector("[view=label]").textContent,
|
||||
"Bar",
|
||||
"The second legend item has the correct label."
|
||||
);
|
||||
is(
|
||||
legendItems[2].querySelector("[view=label]").textContent,
|
||||
"Baz",
|
||||
"The third legend item has the correct label."
|
||||
);
|
||||
}
|
|
@ -1,179 +0,0 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
// Tests that bar graph's legend items handle mouseover/mouseout.
|
||||
|
||||
const BarGraphWidget = require("devtools/client/shared/widgets/BarGraphWidget");
|
||||
|
||||
const CATEGORIES = [
|
||||
{ color: "#46afe3", label: "Foo" },
|
||||
{ color: "#eb5368", label: "Bar" },
|
||||
{ color: "#70bf53", label: "Baz" },
|
||||
];
|
||||
|
||||
add_task(async function() {
|
||||
await addTab("about:blank");
|
||||
await performTest();
|
||||
gBrowser.removeCurrentTab();
|
||||
});
|
||||
|
||||
async function performTest() {
|
||||
const [host, , doc] = await createHost();
|
||||
doc.body.setAttribute(
|
||||
"style",
|
||||
"position: fixed; width: 100%; height: 100%; margin: 0;"
|
||||
);
|
||||
|
||||
const graph = new BarGraphWidget(doc.body, 1);
|
||||
graph.fixedWidth = 200;
|
||||
graph.fixedHeight = 100;
|
||||
|
||||
await graph.once("ready");
|
||||
await testGraph(graph);
|
||||
|
||||
await graph.destroy();
|
||||
host.destroy();
|
||||
}
|
||||
|
||||
async function testGraph(graph) {
|
||||
graph.format = CATEGORIES;
|
||||
graph.dataOffsetX = 1000;
|
||||
graph.setData([
|
||||
{
|
||||
delta: 1100,
|
||||
values: [0, 2, 3],
|
||||
},
|
||||
{
|
||||
delta: 1200,
|
||||
values: [1, 0, 2],
|
||||
},
|
||||
{
|
||||
delta: 1300,
|
||||
values: [2, 1, 0],
|
||||
},
|
||||
{
|
||||
delta: 1400,
|
||||
values: [0, 3, 1],
|
||||
},
|
||||
{
|
||||
delta: 1500,
|
||||
values: [3, 0, 2],
|
||||
},
|
||||
{
|
||||
delta: 1600,
|
||||
values: [3, 2, 0],
|
||||
},
|
||||
]);
|
||||
|
||||
/* eslint-disable max-len */
|
||||
is(
|
||||
graph._blocksBoundingRects.toSource(),
|
||||
"[{type:1, start:0, end:33.33333333333333, top:70, bottom:100}, {type:2, start:0, end:33.33333333333333, top:24, bottom:69}, {type:0, start:34.33333333333333, end:66.66666666666666, top:85, bottom:100}, {type:2, start:34.33333333333333, end:66.66666666666666, top:54, bottom:84}, {type:0, start:67.66666666666666, end:100, top:70, bottom:100}, {type:1, start:67.66666666666666, end:100, top:54, bottom:69}, {type:1, start:101, end:133.33333333333331, top:55, bottom:100}, {type:2, start:101, end:133.33333333333331, top:39, bottom:54}, {type:0, start:134.33333333333331, end:166.66666666666666, top:55, bottom:100}, {type:2, start:134.33333333333331, end:166.66666666666666, top:24, bottom:54}, {type:0, start:167.66666666666666, end:200, top:55, bottom:100}, {type:1, start:167.66666666666666, end:200, top:24, bottom:54}]",
|
||||
"The correct blocks bounding rects were calculated for the bar graph."
|
||||
);
|
||||
|
||||
const legendItems = graph._document.querySelectorAll(
|
||||
".bar-graph-widget-legend-item"
|
||||
);
|
||||
is(
|
||||
legendItems.length,
|
||||
3,
|
||||
"Three legend items should exist in the entire graph."
|
||||
);
|
||||
|
||||
await testLegend(graph, 0, {
|
||||
highlights:
|
||||
"[{type:0, start:34.33333333333333, end:66.66666666666666, top:85, bottom:100}, {type:0, start:67.66666666666666, end:100, top:70, bottom:100}, {type:0, start:134.33333333333331, end:166.66666666666666, top:55, bottom:100}, {type:0, start:167.66666666666666, end:200, top:55, bottom:100}]",
|
||||
selection: "({start:34.33333333333333, end:200})",
|
||||
leftmost:
|
||||
"({type:0, start:34.33333333333333, end:66.66666666666666, top:85, bottom:100})",
|
||||
rightmost:
|
||||
"({type:0, start:167.66666666666666, end:200, top:55, bottom:100})",
|
||||
});
|
||||
await testLegend(graph, 1, {
|
||||
highlights:
|
||||
"[{type:1, start:0, end:33.33333333333333, top:70, bottom:100}, {type:1, start:67.66666666666666, end:100, top:54, bottom:69}, {type:1, start:101, end:133.33333333333331, top:55, bottom:100}, {type:1, start:167.66666666666666, end:200, top:24, bottom:54}]",
|
||||
selection: "({start:0, end:200})",
|
||||
leftmost: "({type:1, start:0, end:33.33333333333333, top:70, bottom:100})",
|
||||
rightmost:
|
||||
"({type:1, start:167.66666666666666, end:200, top:24, bottom:54})",
|
||||
});
|
||||
await testLegend(graph, 2, {
|
||||
highlights:
|
||||
"[{type:2, start:0, end:33.33333333333333, top:24, bottom:69}, {type:2, start:34.33333333333333, end:66.66666666666666, top:54, bottom:84}, {type:2, start:101, end:133.33333333333331, top:39, bottom:54}, {type:2, start:134.33333333333331, end:166.66666666666666, top:24, bottom:54}]",
|
||||
selection: "({start:0, end:166.66666666666666})",
|
||||
leftmost: "({type:2, start:0, end:33.33333333333333, top:24, bottom:69})",
|
||||
rightmost:
|
||||
"({type:2, start:134.33333333333331, end:166.66666666666666, top:24, bottom:54})",
|
||||
});
|
||||
/* eslint-enable max-len */
|
||||
}
|
||||
|
||||
async function testLegend(
|
||||
graph,
|
||||
index,
|
||||
{ highlights, selection, leftmost, rightmost }
|
||||
) {
|
||||
// Hover.
|
||||
|
||||
const legendItems = graph._document.querySelectorAll(
|
||||
".bar-graph-widget-legend-item"
|
||||
);
|
||||
const colorBlock = legendItems[index].querySelector("[view=color]");
|
||||
|
||||
const debounced = graph.once("legend-hover");
|
||||
graph._onLegendMouseOver({ target: colorBlock });
|
||||
ok(!graph.hasMask(), "The graph shouldn't get highlights immediately.");
|
||||
|
||||
const [type, rects] = await debounced;
|
||||
ok(graph.hasMask(), "The graph should now have highlights.");
|
||||
|
||||
is(type, index, "The legend item was correctly hovered.");
|
||||
is(
|
||||
rects.toSource(),
|
||||
highlights,
|
||||
"The legend item highlighted the correct regions."
|
||||
);
|
||||
|
||||
// Unhover.
|
||||
|
||||
const unhovered = graph.once("legend-unhover");
|
||||
graph._onLegendMouseOut();
|
||||
ok(!graph.hasMask(), "The graph shouldn't have highlights anymore.");
|
||||
|
||||
await unhovered;
|
||||
ok(true, "The 'legend-mouseout' event was emitted.");
|
||||
|
||||
// Select.
|
||||
|
||||
const selected = graph.once("legend-selection");
|
||||
graph._onLegendMouseDown(mockEvent(colorBlock));
|
||||
ok(graph.hasSelection(), "The graph should now have a selection.");
|
||||
is(
|
||||
graph.getSelection().toSource(),
|
||||
selection,
|
||||
"The graph has a correct selection."
|
||||
);
|
||||
|
||||
const [left, right] = await selected;
|
||||
is(left.toSource(), leftmost, "The correct leftmost data block was found.");
|
||||
is(
|
||||
right.toSource(),
|
||||
rightmost,
|
||||
"The correct rightmost data block was found."
|
||||
);
|
||||
|
||||
// Deselect.
|
||||
|
||||
graph.dropSelection();
|
||||
}
|
||||
|
||||
function mockEvent(node) {
|
||||
return {
|
||||
target: node,
|
||||
preventDefault: () => {},
|
||||
stopPropagation: () => {},
|
||||
};
|
||||
}
|
|
@ -1,255 +0,0 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
// Tests that canvas graphs can have their selection linked.
|
||||
|
||||
const LineGraphWidget = require("devtools/client/shared/widgets/LineGraphWidget");
|
||||
const BarGraphWidget = require("devtools/client/shared/widgets/BarGraphWidget");
|
||||
const { CanvasGraphUtils } = require("devtools/client/shared/widgets/Graphs");
|
||||
|
||||
add_task(async function() {
|
||||
await addTab("about:blank");
|
||||
await performTest();
|
||||
gBrowser.removeCurrentTab();
|
||||
});
|
||||
|
||||
async function performTest() {
|
||||
const [host, , doc] = await createHost();
|
||||
doc.body.setAttribute(
|
||||
"style",
|
||||
"position: fixed; width: 100%; height: 100%; margin: 0;"
|
||||
);
|
||||
|
||||
const first = document.createElement("div");
|
||||
first.setAttribute(
|
||||
"style",
|
||||
"display: inline-block; width: 100%; height: 50%;"
|
||||
);
|
||||
doc.body.appendChild(first);
|
||||
|
||||
const second = document.createElement("div");
|
||||
second.setAttribute(
|
||||
"style",
|
||||
"display: inline-block; width: 100%; height: 50%;"
|
||||
);
|
||||
doc.body.appendChild(second);
|
||||
|
||||
const graph1 = new LineGraphWidget(first, "js");
|
||||
const graph2 = new BarGraphWidget(second);
|
||||
|
||||
CanvasGraphUtils.linkAnimation(graph1, graph2);
|
||||
CanvasGraphUtils.linkSelection(graph1, graph2);
|
||||
|
||||
await graph1.ready();
|
||||
await graph2.ready();
|
||||
|
||||
testGraphs(graph1, graph2);
|
||||
|
||||
await graph1.destroy();
|
||||
await graph2.destroy();
|
||||
host.destroy();
|
||||
}
|
||||
|
||||
function testGraphs(graph1, graph2) {
|
||||
info("Making a selection in the first graph.");
|
||||
|
||||
dragStart(graph1, 300);
|
||||
ok(graph1.hasSelectionInProgress(), "The selection should start (1.1).");
|
||||
ok(
|
||||
!graph2.hasSelectionInProgress(),
|
||||
"The selection should not start yet in the second graph (1.2)."
|
||||
);
|
||||
is(
|
||||
graph1.getSelection().start,
|
||||
300,
|
||||
"The current selection start value is correct (1.1)."
|
||||
);
|
||||
is(
|
||||
graph2.getSelection().start,
|
||||
300,
|
||||
"The current selection start value is correct (1.2)."
|
||||
);
|
||||
is(
|
||||
graph1.getSelection().end,
|
||||
300,
|
||||
"The current selection end value is correct (1.1)."
|
||||
);
|
||||
is(
|
||||
graph2.getSelection().end,
|
||||
300,
|
||||
"The current selection end value is correct (1.2)."
|
||||
);
|
||||
|
||||
hover(graph1, 400);
|
||||
ok(
|
||||
graph1.hasSelectionInProgress(),
|
||||
"The selection should still be in progress (2.1)."
|
||||
);
|
||||
ok(
|
||||
!graph2.hasSelectionInProgress(),
|
||||
"The selection should not be in progress in the second graph (2.2)."
|
||||
);
|
||||
is(
|
||||
graph1.getSelection().start,
|
||||
300,
|
||||
"The current selection start value is correct (2.1)."
|
||||
);
|
||||
is(
|
||||
graph2.getSelection().start,
|
||||
300,
|
||||
"The current selection start value is correct (2.2)."
|
||||
);
|
||||
is(
|
||||
graph1.getSelection().end,
|
||||
400,
|
||||
"The current selection end value is correct (2.1)."
|
||||
);
|
||||
is(
|
||||
graph2.getSelection().end,
|
||||
400,
|
||||
"The current selection end value is correct (2.2)."
|
||||
);
|
||||
|
||||
dragStop(graph1, 500);
|
||||
ok(
|
||||
!graph1.hasSelectionInProgress(),
|
||||
"The selection should have stopped (3.1)."
|
||||
);
|
||||
ok(
|
||||
!graph2.hasSelectionInProgress(),
|
||||
"The selection should have stopped (3.2)."
|
||||
);
|
||||
is(
|
||||
graph1.getSelection().start,
|
||||
300,
|
||||
"The current selection start value is correct (3.1)."
|
||||
);
|
||||
is(
|
||||
graph2.getSelection().start,
|
||||
300,
|
||||
"The current selection start value is correct (3.2)."
|
||||
);
|
||||
is(
|
||||
graph1.getSelection().end,
|
||||
500,
|
||||
"The current selection end value is correct (3.1)."
|
||||
);
|
||||
is(
|
||||
graph2.getSelection().end,
|
||||
500,
|
||||
"The current selection end value is correct (3.2)."
|
||||
);
|
||||
|
||||
info("Making a new selection in the second graph.");
|
||||
|
||||
dragStart(graph2, 200);
|
||||
ok(
|
||||
!graph1.hasSelectionInProgress(),
|
||||
"The selection should not start yet in the first graph (4.1)."
|
||||
);
|
||||
ok(graph2.hasSelectionInProgress(), "The selection should start (4.2).");
|
||||
is(
|
||||
graph1.getSelection().start,
|
||||
200,
|
||||
"The current selection start value is correct (4.1)."
|
||||
);
|
||||
is(
|
||||
graph2.getSelection().start,
|
||||
200,
|
||||
"The current selection start value is correct (4.2)."
|
||||
);
|
||||
is(
|
||||
graph1.getSelection().end,
|
||||
200,
|
||||
"The current selection end value is correct (4.1)."
|
||||
);
|
||||
is(
|
||||
graph2.getSelection().end,
|
||||
200,
|
||||
"The current selection end value is correct (4.2)."
|
||||
);
|
||||
|
||||
hover(graph2, 300);
|
||||
ok(
|
||||
!graph1.hasSelectionInProgress(),
|
||||
"The selection should not be in progress in the first graph (2.2)."
|
||||
);
|
||||
ok(
|
||||
graph2.hasSelectionInProgress(),
|
||||
"The selection should still be in progress (5.2)."
|
||||
);
|
||||
is(
|
||||
graph1.getSelection().start,
|
||||
200,
|
||||
"The current selection start value is correct (5.1)."
|
||||
);
|
||||
is(
|
||||
graph2.getSelection().start,
|
||||
200,
|
||||
"The current selection start value is correct (5.2)."
|
||||
);
|
||||
is(
|
||||
graph1.getSelection().end,
|
||||
300,
|
||||
"The current selection end value is correct (5.1)."
|
||||
);
|
||||
is(
|
||||
graph2.getSelection().end,
|
||||
300,
|
||||
"The current selection end value is correct (5.2)."
|
||||
);
|
||||
|
||||
dragStop(graph2, 400);
|
||||
ok(
|
||||
!graph1.hasSelectionInProgress(),
|
||||
"The selection should have stopped (6.1)."
|
||||
);
|
||||
ok(
|
||||
!graph2.hasSelectionInProgress(),
|
||||
"The selection should have stopped (6.2)."
|
||||
);
|
||||
is(
|
||||
graph1.getSelection().start,
|
||||
200,
|
||||
"The current selection start value is correct (6.1)."
|
||||
);
|
||||
is(
|
||||
graph2.getSelection().start,
|
||||
200,
|
||||
"The current selection start value is correct (6.2)."
|
||||
);
|
||||
is(
|
||||
graph1.getSelection().end,
|
||||
400,
|
||||
"The current selection end value is correct (6.1)."
|
||||
);
|
||||
is(
|
||||
graph2.getSelection().end,
|
||||
400,
|
||||
"The current selection end value is correct (6.2)."
|
||||
);
|
||||
}
|
||||
|
||||
// EventUtils just doesn't work!
|
||||
|
||||
function hover(graph, x, y = 1) {
|
||||
x /= window.devicePixelRatio;
|
||||
y /= window.devicePixelRatio;
|
||||
graph._onMouseMove({ testX: x, testY: y });
|
||||
}
|
||||
|
||||
function dragStart(graph, x, y = 1) {
|
||||
x /= window.devicePixelRatio;
|
||||
y /= window.devicePixelRatio;
|
||||
graph._onMouseMove({ testX: x, testY: y });
|
||||
graph._onMouseDown({ testX: x, testY: y });
|
||||
}
|
||||
|
||||
function dragStop(graph, x, y = 1) {
|
||||
x /= window.devicePixelRatio;
|
||||
y /= window.devicePixelRatio;
|
||||
graph._onMouseMove({ testX: x, testY: y });
|
||||
graph._onMouseUp({ testX: x, testY: y });
|
||||
}
|
|
@ -1,520 +0,0 @@
|
|||
"use strict";
|
||||
|
||||
const { extend } = require("devtools/shared/extend");
|
||||
const {
|
||||
setNamedTimeout,
|
||||
clearNamedTimeout,
|
||||
} = require("devtools/client/shared/widgets/view-helpers");
|
||||
const {
|
||||
AbstractCanvasGraph,
|
||||
CanvasGraphUtils,
|
||||
} = require("devtools/client/shared/widgets/Graphs");
|
||||
|
||||
const HTML_NS = "http://www.w3.org/1999/xhtml";
|
||||
|
||||
// Bar graph constants.
|
||||
|
||||
const GRAPH_DAMPEN_VALUES_FACTOR = 0.75;
|
||||
|
||||
// The following are in pixels
|
||||
const GRAPH_BARS_MARGIN_TOP = 1;
|
||||
const GRAPH_BARS_MARGIN_END = 1;
|
||||
const GRAPH_MIN_BARS_WIDTH = 5;
|
||||
const GRAPH_MIN_BLOCKS_HEIGHT = 1;
|
||||
|
||||
const GRAPH_BACKGROUND_GRADIENT_START = "rgba(0,136,204,0.0)";
|
||||
const GRAPH_BACKGROUND_GRADIENT_END = "rgba(255,255,255,0.25)";
|
||||
|
||||
const GRAPH_CLIPHEAD_LINE_COLOR = "#666";
|
||||
const GRAPH_SELECTION_LINE_COLOR = "#555";
|
||||
const GRAPH_SELECTION_BACKGROUND_COLOR = "rgba(0,136,204,0.25)";
|
||||
const GRAPH_SELECTION_STRIPES_COLOR = "rgba(255,255,255,0.1)";
|
||||
const GRAPH_REGION_BACKGROUND_COLOR = "transparent";
|
||||
const GRAPH_REGION_STRIPES_COLOR = "rgba(237,38,85,0.2)";
|
||||
|
||||
const GRAPH_HIGHLIGHTS_MASK_BACKGROUND = "rgba(255,255,255,0.75)";
|
||||
const GRAPH_HIGHLIGHTS_MASK_STRIPES = "rgba(255,255,255,0.5)";
|
||||
|
||||
// in ms
|
||||
const GRAPH_LEGEND_MOUSEOVER_DEBOUNCE = 50;
|
||||
|
||||
/**
|
||||
* A bar graph, plotting tuples of values as rectangles.
|
||||
*
|
||||
* @see AbstractCanvasGraph for emitted events and other options.
|
||||
*
|
||||
* Example usage:
|
||||
* let graph = new BarGraphWidget(node);
|
||||
* graph.format = ...;
|
||||
* graph.once("ready", () => {
|
||||
* graph.setData(src);
|
||||
* });
|
||||
*
|
||||
* The `graph.format` traits are mandatory and will determine how the values
|
||||
* are styled as "blocks" in every "bar":
|
||||
* [
|
||||
* { color: "#f00", label: "Foo" },
|
||||
* { color: "#0f0", label: "Bar" },
|
||||
* ...
|
||||
* { color: "#00f", label: "Baz" }
|
||||
* ]
|
||||
*
|
||||
* Data source format:
|
||||
* [
|
||||
* { delta: x1, values: [y11, y12, ... y1n] },
|
||||
* { delta: x2, values: [y21, y22, ... y2n] },
|
||||
* ...
|
||||
* { delta: xm, values: [ym1, ym2, ... ymn] }
|
||||
* ]
|
||||
* where each item in the array represents a "bar", for which every value
|
||||
* represents a "block" inside that "bar", plotted at the "delta" position.
|
||||
*
|
||||
* @param Node parent
|
||||
* The parent node holding the graph.
|
||||
*/
|
||||
this.BarGraphWidget = function(parent, ...args) {
|
||||
AbstractCanvasGraph.apply(this, [parent, "bar-graph", ...args]);
|
||||
|
||||
this.once("ready", () => {
|
||||
this._onLegendMouseOver = this._onLegendMouseOver.bind(this);
|
||||
this._onLegendMouseOut = this._onLegendMouseOut.bind(this);
|
||||
this._onLegendMouseDown = this._onLegendMouseDown.bind(this);
|
||||
this._onLegendMouseUp = this._onLegendMouseUp.bind(this);
|
||||
this._createLegend();
|
||||
});
|
||||
};
|
||||
|
||||
BarGraphWidget.prototype = extend(AbstractCanvasGraph.prototype, {
|
||||
clipheadLineColor: GRAPH_CLIPHEAD_LINE_COLOR,
|
||||
selectionLineColor: GRAPH_SELECTION_LINE_COLOR,
|
||||
selectionBackgroundColor: GRAPH_SELECTION_BACKGROUND_COLOR,
|
||||
selectionStripesColor: GRAPH_SELECTION_STRIPES_COLOR,
|
||||
regionBackgroundColor: GRAPH_REGION_BACKGROUND_COLOR,
|
||||
regionStripesColor: GRAPH_REGION_STRIPES_COLOR,
|
||||
|
||||
/**
|
||||
* List of colors used to fill each block inside every bar, also
|
||||
* corresponding to labels displayed in this graph's legend.
|
||||
* @see constructor
|
||||
*/
|
||||
format: null,
|
||||
|
||||
/**
|
||||
* Optionally offsets the `delta` in the data source by this scalar.
|
||||
*/
|
||||
dataOffsetX: 0,
|
||||
|
||||
/**
|
||||
* Optionally uses this value instead of the last tick in the data source
|
||||
* to compute the horizontal scaling.
|
||||
*/
|
||||
dataDuration: 0,
|
||||
|
||||
/**
|
||||
* The scalar used to multiply the graph values to leave some headroom
|
||||
* on the top.
|
||||
*/
|
||||
dampenValuesFactor: GRAPH_DAMPEN_VALUES_FACTOR,
|
||||
|
||||
/**
|
||||
* Bars that are too close too each other in the graph will be combined.
|
||||
* This scalar specifies the required minimum width of each bar.
|
||||
*/
|
||||
minBarsWidth: GRAPH_MIN_BARS_WIDTH,
|
||||
|
||||
/**
|
||||
* Blocks in a bar that are too thin inside the bar will not be rendered.
|
||||
* This scalar specifies the required minimum height of each block.
|
||||
*/
|
||||
minBlocksHeight: GRAPH_MIN_BLOCKS_HEIGHT,
|
||||
|
||||
/**
|
||||
* Renders the graph's background.
|
||||
* @see AbstractCanvasGraph.prototype.buildBackgroundImage
|
||||
*/
|
||||
buildBackgroundImage: function() {
|
||||
const { canvas, ctx } = this._getNamedCanvas("bar-graph-background");
|
||||
const width = this._width;
|
||||
const height = this._height;
|
||||
|
||||
const gradient = ctx.createLinearGradient(0, 0, 0, height);
|
||||
gradient.addColorStop(0, GRAPH_BACKGROUND_GRADIENT_START);
|
||||
gradient.addColorStop(1, GRAPH_BACKGROUND_GRADIENT_END);
|
||||
ctx.fillStyle = gradient;
|
||||
ctx.fillRect(0, 0, width, height);
|
||||
|
||||
return canvas;
|
||||
},
|
||||
|
||||
/**
|
||||
* Renders the graph's data source.
|
||||
* @see AbstractCanvasGraph.prototype.buildGraphImage
|
||||
*/
|
||||
buildGraphImage: function() {
|
||||
if (!this.format || !this.format.length) {
|
||||
throw new Error(
|
||||
"The graph format traits are mandatory to style " + "the data source."
|
||||
);
|
||||
}
|
||||
const { canvas, ctx } = this._getNamedCanvas("bar-graph-data");
|
||||
const width = this._width;
|
||||
const height = this._height;
|
||||
|
||||
const totalTypes = this.format.length;
|
||||
const totalTicks = this._data.length;
|
||||
const lastTick = this._data[totalTicks - 1].delta;
|
||||
|
||||
const minBarsWidth = this.minBarsWidth * this._pixelRatio;
|
||||
const minBlocksHeight = this.minBlocksHeight * this._pixelRatio;
|
||||
|
||||
const duration = this.dataDuration || lastTick;
|
||||
const dataScaleX = (this.dataScaleX =
|
||||
width / (duration - this.dataOffsetX));
|
||||
const dataScaleY = (this.dataScaleY =
|
||||
(height /
|
||||
this._calcMaxHeight({
|
||||
data: this._data,
|
||||
dataScaleX: dataScaleX,
|
||||
minBarsWidth: minBarsWidth,
|
||||
})) *
|
||||
this.dampenValuesFactor);
|
||||
|
||||
// Draw the graph.
|
||||
|
||||
// Iterate over the blocks, then the bars, to draw all rectangles of
|
||||
// the same color in a single pass. See the @constructor for more
|
||||
// information about the data source, and how a "bar" contains "blocks".
|
||||
|
||||
this._blocksBoundingRects = [];
|
||||
const prevHeight = [];
|
||||
const scaledMarginEnd = GRAPH_BARS_MARGIN_END * this._pixelRatio;
|
||||
const scaledMarginTop = GRAPH_BARS_MARGIN_TOP * this._pixelRatio;
|
||||
|
||||
for (let type = 0; type < totalTypes; type++) {
|
||||
ctx.fillStyle = this.format[type].color || "#000";
|
||||
ctx.beginPath();
|
||||
|
||||
let prevRight = 0;
|
||||
let skippedCount = 0;
|
||||
let skippedHeight = 0;
|
||||
|
||||
for (let tick = 0; tick < totalTicks; tick++) {
|
||||
const delta = this._data[tick].delta;
|
||||
const value = this._data[tick].values[type] || 0;
|
||||
const blockRight = (delta - this.dataOffsetX) * dataScaleX;
|
||||
const blockHeight = value * dataScaleY;
|
||||
|
||||
const blockWidth = blockRight - prevRight;
|
||||
if (blockWidth < minBarsWidth) {
|
||||
skippedCount++;
|
||||
skippedHeight += blockHeight;
|
||||
continue;
|
||||
}
|
||||
|
||||
const averageHeight =
|
||||
(blockHeight + skippedHeight) / (skippedCount + 1);
|
||||
if (averageHeight >= minBlocksHeight) {
|
||||
const bottom = height - ~~prevHeight[tick];
|
||||
ctx.moveTo(prevRight, bottom);
|
||||
ctx.lineTo(prevRight, bottom - averageHeight);
|
||||
ctx.lineTo(blockRight, bottom - averageHeight);
|
||||
ctx.lineTo(blockRight, bottom);
|
||||
|
||||
// Remember this block's type and location.
|
||||
this._blocksBoundingRects.push({
|
||||
type: type,
|
||||
start: prevRight,
|
||||
end: blockRight,
|
||||
top: bottom - averageHeight,
|
||||
bottom: bottom,
|
||||
});
|
||||
|
||||
if (prevHeight[tick] === undefined) {
|
||||
prevHeight[tick] = averageHeight + scaledMarginTop;
|
||||
} else {
|
||||
prevHeight[tick] += averageHeight + scaledMarginTop;
|
||||
}
|
||||
}
|
||||
|
||||
prevRight += blockWidth + scaledMarginEnd;
|
||||
skippedHeight = 0;
|
||||
skippedCount = 0;
|
||||
}
|
||||
|
||||
ctx.fill();
|
||||
}
|
||||
|
||||
// The blocks bounding rects isn't guaranteed to be sorted ascending by
|
||||
// block location on the X axis. This should be the case, for better
|
||||
// cache cohesion and a faster `buildMaskImage`.
|
||||
this._blocksBoundingRects.sort((a, b) => (a.start > b.start ? 1 : -1));
|
||||
|
||||
// Update the legend.
|
||||
|
||||
while (this._legendNode.hasChildNodes()) {
|
||||
this._legendNode.firstChild.remove();
|
||||
}
|
||||
for (const { color, label } of this.format) {
|
||||
this._createLegendItem(color, label);
|
||||
}
|
||||
|
||||
return canvas;
|
||||
},
|
||||
|
||||
/**
|
||||
* Renders the graph's mask.
|
||||
* Fades in only the parts of the graph that are inside the specified areas.
|
||||
*
|
||||
* @param array highlights
|
||||
* A list of { start, end } values. Optionally, each object
|
||||
* in the list may also specify { top, bottom } pixel values if the
|
||||
* highlighting shouldn't span across the full height of the graph.
|
||||
* @param boolean inPixels
|
||||
* Set this to true if the { start, end } values in the highlights
|
||||
* list are pixel values, and not values from the data source.
|
||||
* @param function unpack [optional]
|
||||
* @see AbstractCanvasGraph.prototype.getMappedSelection
|
||||
*/
|
||||
buildMaskImage: function(
|
||||
highlights,
|
||||
inPixels = false,
|
||||
unpack = e => e.delta
|
||||
) {
|
||||
// A null `highlights` array is used to clear the mask. An empty array
|
||||
// will mask the entire graph.
|
||||
if (!highlights) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Get a render target for the highlights. It will be overlaid on top of
|
||||
// the existing graph, masking the areas that aren't highlighted.
|
||||
|
||||
const { canvas, ctx } = this._getNamedCanvas("graph-highlights");
|
||||
const width = this._width;
|
||||
const height = this._height;
|
||||
|
||||
// Draw the background mask.
|
||||
|
||||
const pattern = AbstractCanvasGraph.getStripePattern({
|
||||
ownerDocument: this._document,
|
||||
backgroundColor: GRAPH_HIGHLIGHTS_MASK_BACKGROUND,
|
||||
stripesColor: GRAPH_HIGHLIGHTS_MASK_STRIPES,
|
||||
});
|
||||
ctx.fillStyle = pattern;
|
||||
ctx.fillRect(0, 0, width, height);
|
||||
|
||||
// Clear highlighted areas.
|
||||
|
||||
const totalTicks = this._data.length;
|
||||
const firstTick = unpack(this._data[0]);
|
||||
const lastTick = unpack(this._data[totalTicks - 1]);
|
||||
|
||||
for (let { start, end, top, bottom } of highlights) {
|
||||
if (!inPixels) {
|
||||
start = CanvasGraphUtils.map(start, firstTick, lastTick, 0, width);
|
||||
end = CanvasGraphUtils.map(end, firstTick, lastTick, 0, width);
|
||||
}
|
||||
const firstSnap = findFirst(
|
||||
this._blocksBoundingRects,
|
||||
e => e.start >= start
|
||||
);
|
||||
const lastSnap = findLast(
|
||||
this._blocksBoundingRects,
|
||||
e => e.start >= start && e.end <= end
|
||||
);
|
||||
|
||||
const x1 = firstSnap ? firstSnap.start : start;
|
||||
let x2;
|
||||
if (lastSnap) {
|
||||
x2 = lastSnap.end;
|
||||
} else {
|
||||
x2 = firstSnap ? firstSnap.end : end;
|
||||
}
|
||||
|
||||
const y1 = top || 0;
|
||||
const y2 = bottom || height;
|
||||
ctx.clearRect(x1, y1, x2 - x1, y2 - y1);
|
||||
}
|
||||
|
||||
return canvas;
|
||||
},
|
||||
|
||||
/**
|
||||
* A list storing the bounding rectangle for each drawn block in the graph.
|
||||
* Created whenever `buildGraphImage` is invoked.
|
||||
*/
|
||||
_blocksBoundingRects: null,
|
||||
|
||||
/**
|
||||
* Calculates the height of the tallest bar that would eventially be rendered
|
||||
* in this graph.
|
||||
*
|
||||
* Bars that are too close too each other in the graph will be combined.
|
||||
* @see `minBarsWidth`
|
||||
*
|
||||
* @return number
|
||||
* The tallest bar height in this graph.
|
||||
*/
|
||||
_calcMaxHeight: function({ data, dataScaleX, minBarsWidth }) {
|
||||
let maxHeight = 0;
|
||||
let prevRight = 0;
|
||||
let skippedCount = 0;
|
||||
let skippedHeight = 0;
|
||||
const scaledMarginEnd = GRAPH_BARS_MARGIN_END * this._pixelRatio;
|
||||
|
||||
for (const { delta, values } of data) {
|
||||
const barRight = (delta - this.dataOffsetX) * dataScaleX;
|
||||
const barHeight = values.reduce((a, b) => a + b, 0);
|
||||
|
||||
const barWidth = barRight - prevRight;
|
||||
if (barWidth < minBarsWidth) {
|
||||
skippedCount++;
|
||||
skippedHeight += barHeight;
|
||||
continue;
|
||||
}
|
||||
|
||||
const averageHeight = (barHeight + skippedHeight) / (skippedCount + 1);
|
||||
maxHeight = Math.max(averageHeight, maxHeight);
|
||||
|
||||
prevRight += barWidth + scaledMarginEnd;
|
||||
skippedHeight = 0;
|
||||
skippedCount = 0;
|
||||
}
|
||||
|
||||
return maxHeight;
|
||||
},
|
||||
|
||||
/**
|
||||
* Creates the legend container when constructing this graph.
|
||||
*/
|
||||
_createLegend: function() {
|
||||
const legendNode = (this._legendNode = this._document.createElementNS(
|
||||
HTML_NS,
|
||||
"div"
|
||||
));
|
||||
legendNode.className = "bar-graph-widget-legend";
|
||||
this._container.appendChild(legendNode);
|
||||
},
|
||||
|
||||
/**
|
||||
* Creates a legend item when constructing this graph.
|
||||
*/
|
||||
_createLegendItem: function(color, label) {
|
||||
const itemNode = this._document.createElementNS(HTML_NS, "div");
|
||||
itemNode.className = "bar-graph-widget-legend-item";
|
||||
|
||||
const colorNode = this._document.createElementNS(HTML_NS, "span");
|
||||
colorNode.setAttribute("view", "color");
|
||||
colorNode.setAttribute("data-index", this._legendNode.childNodes.length);
|
||||
colorNode.style.backgroundColor = color;
|
||||
colorNode.addEventListener("mouseover", this._onLegendMouseOver);
|
||||
colorNode.addEventListener("mouseout", this._onLegendMouseOut);
|
||||
colorNode.addEventListener("mousedown", this._onLegendMouseDown);
|
||||
colorNode.addEventListener("mouseup", this._onLegendMouseUp);
|
||||
|
||||
const labelNode = this._document.createElementNS(HTML_NS, "span");
|
||||
labelNode.setAttribute("view", "label");
|
||||
labelNode.textContent = label;
|
||||
|
||||
itemNode.appendChild(colorNode);
|
||||
itemNode.appendChild(labelNode);
|
||||
this._legendNode.appendChild(itemNode);
|
||||
},
|
||||
|
||||
/**
|
||||
* Invoked whenever a color node in the legend is hovered.
|
||||
*/
|
||||
_onLegendMouseOver: function(ev) {
|
||||
setNamedTimeout(
|
||||
"bar-graph-debounce",
|
||||
GRAPH_LEGEND_MOUSEOVER_DEBOUNCE,
|
||||
() => {
|
||||
const type = ev.target.dataset.index;
|
||||
const rects = this._blocksBoundingRects.filter(e => e.type == type);
|
||||
|
||||
this._originalHighlights = this._mask;
|
||||
this._hasCustomHighlights = true;
|
||||
this.setMask(rects, true);
|
||||
|
||||
this.emit("legend-hover", [type, rects]);
|
||||
}
|
||||
);
|
||||
},
|
||||
|
||||
/**
|
||||
* Invoked whenever a color node in the legend is unhovered.
|
||||
*/
|
||||
_onLegendMouseOut: function() {
|
||||
clearNamedTimeout("bar-graph-debounce");
|
||||
|
||||
if (this._hasCustomHighlights) {
|
||||
this.setMask(this._originalHighlights);
|
||||
this._hasCustomHighlights = false;
|
||||
this._originalHighlights = null;
|
||||
}
|
||||
|
||||
this.emit("legend-unhover");
|
||||
},
|
||||
|
||||
/**
|
||||
* Invoked whenever a color node in the legend is pressed.
|
||||
*/
|
||||
_onLegendMouseDown: function(ev) {
|
||||
ev.preventDefault();
|
||||
ev.stopPropagation();
|
||||
|
||||
const type = ev.target.dataset.index;
|
||||
const rects = this._blocksBoundingRects.filter(e => e.type == type);
|
||||
const leftmost = rects[0];
|
||||
const rightmost = rects[rects.length - 1];
|
||||
if (!leftmost || !rightmost) {
|
||||
this.dropSelection();
|
||||
} else {
|
||||
this.setSelection({ start: leftmost.start, end: rightmost.end });
|
||||
}
|
||||
|
||||
this.emit("legend-selection", [leftmost, rightmost]);
|
||||
},
|
||||
|
||||
/**
|
||||
* Invoked whenever a color node in the legend is released.
|
||||
*/
|
||||
_onLegendMouseUp: function(e) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
},
|
||||
});
|
||||
|
||||
/**
|
||||
* Finds the first element in an array that validates a predicate.
|
||||
* @param array
|
||||
* @param function predicate
|
||||
* @return number
|
||||
*/
|
||||
function findFirst(array, predicate) {
|
||||
for (let i = 0, len = array.length; i < len; i++) {
|
||||
const element = array[i];
|
||||
if (predicate(element)) {
|
||||
return element;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds the last element in an array that validates a predicate.
|
||||
* @param array
|
||||
* @param function predicate
|
||||
* @return number
|
||||
*/
|
||||
function findLast(array, predicate) {
|
||||
for (let i = array.length - 1; i >= 0; i--) {
|
||||
const element = array[i];
|
||||
if (predicate(element)) {
|
||||
return element;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
module.exports = BarGraphWidget;
|
|
@ -10,7 +10,6 @@ DIRS += [
|
|||
|
||||
DevToolsModules(
|
||||
'AbstractTreeItem.jsm',
|
||||
'BarGraphWidget.js',
|
||||
'Chart.js',
|
||||
'CubicBezierPresets.js',
|
||||
'CubicBezierWidget.js',
|
||||
|
|
|
@ -81,13 +81,19 @@ StackTraceCollector.prototype = {
|
|||
try {
|
||||
channel = subject.QueryInterface(Ci.nsIHttpChannel);
|
||||
id = channel.channelId;
|
||||
} catch (e) {
|
||||
} catch (e1) {
|
||||
// WebSocketChannels do not have IDs, so use the URL. When a WebSocket is
|
||||
// opened in a content process, a channel is created locally but the HTTP
|
||||
// channel for the connection lives entirely in the parent process. When
|
||||
// the server code running in the parent sees that HTTP channel, it will
|
||||
// look for the creation stack using the websocket's URL.
|
||||
channel = subject.QueryInterface(Ci.nsIWebSocketChannel);
|
||||
try {
|
||||
channel = subject.QueryInterface(Ci.nsIWebSocketChannel);
|
||||
} catch (e2) {
|
||||
// Channels which don't implement the above interfaces can appear here,
|
||||
// such as nsIFileChannel. Ignore these channels.
|
||||
return;
|
||||
}
|
||||
id = channel.URI.spec;
|
||||
}
|
||||
|
||||
|
|
|
@ -122,11 +122,6 @@ dependencies = [
|
|||
"serde 1.0.88 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bit-vec"
|
||||
version = "0.4.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "1.0.3"
|
||||
|
@ -705,11 +700,6 @@ dependencies = [
|
|||
"gl_generator 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "half"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "httparse"
|
||||
version = "1.2.4"
|
||||
|
@ -869,14 +859,6 @@ dependencies = [
|
|||
"scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.3.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.1"
|
||||
|
@ -885,24 +867,6 @@ dependencies = [
|
|||
"cfg-if 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lyon_geom"
|
||||
version = "0.12.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"arrayvec 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"euclid 0.19.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"num-traits 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lyon_path"
|
||||
version = "0.12.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"lyon_geom 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lzw"
|
||||
version = "0.10.0"
|
||||
|
@ -1172,63 +1136,6 @@ dependencies = [
|
|||
"winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pathfinder_font_renderer"
|
||||
version = "0.5.0"
|
||||
source = "git+https://github.com/pcwalton/pathfinder?branch=webrender#e8805413321edf85870deee5678751746ed61316"
|
||||
dependencies = [
|
||||
"app_units 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"core-graphics 0.17.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"core-text 13.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"euclid 0.19.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"freetype 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"lyon_path 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.88 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde_derive 1.0.88 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pathfinder_gfx_utils"
|
||||
version = "0.2.0"
|
||||
source = "git+https://github.com/pcwalton/pathfinder?branch=webrender#e8805413321edf85870deee5678751746ed61316"
|
||||
dependencies = [
|
||||
"euclid 0.19.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pathfinder_partitioner"
|
||||
version = "0.2.0"
|
||||
source = "git+https://github.com/pcwalton/pathfinder?branch=webrender#e8805413321edf85870deee5678751746ed61316"
|
||||
dependencies = [
|
||||
"arrayvec 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"bincode 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"bit-vec 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"byteorder 1.2.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"env_logger 0.5.10 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"euclid 0.19.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"half 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"lyon_geom 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"lyon_path 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"pathfinder_path_utils 0.2.0 (git+https://github.com/pcwalton/pathfinder?branch=webrender)",
|
||||
"serde 1.0.88 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde_derive 1.0.88 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pathfinder_path_utils"
|
||||
version = "0.2.0"
|
||||
source = "git+https://github.com/pcwalton/pathfinder?branch=webrender#e8805413321edf85870deee5678751746ed61316"
|
||||
dependencies = [
|
||||
"arrayvec 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"lyon_path 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.88 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde_derive 1.0.88 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "peek-poke"
|
||||
version = "0.2.0"
|
||||
|
@ -1987,10 +1894,6 @@ dependencies = [
|
|||
"malloc_size_of_derive 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"mozangle 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"num-traits 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"pathfinder_font_renderer 0.5.0 (git+https://github.com/pcwalton/pathfinder?branch=webrender)",
|
||||
"pathfinder_gfx_utils 0.2.0 (git+https://github.com/pcwalton/pathfinder?branch=webrender)",
|
||||
"pathfinder_partitioner 0.2.0 (git+https://github.com/pcwalton/pathfinder?branch=webrender)",
|
||||
"pathfinder_path_utils 0.2.0 (git+https://github.com/pcwalton/pathfinder?branch=webrender)",
|
||||
"plane-split 0.13.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"png 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
|
@ -2246,7 +2149,6 @@ dependencies = [
|
|||
"checksum base64 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "621fc7ecb8008f86d7fb9b95356cd692ce9514b80a86d85b397f32a22da7b9e2"
|
||||
"checksum binary-space-partition 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "88ceb0d16c4fd0e42876e298d7d3ce3780dd9ebdcbe4199816a32c77e08597ff"
|
||||
"checksum bincode 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bda13183df33055cbb84b847becce220d392df502ebe7a4a78d7021771ed94d0"
|
||||
"checksum bit-vec 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "02b4ff8b16e6076c3e14220b39fbc1fabb6737522281a388998046859400895f"
|
||||
"checksum bitflags 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "d0c54bb8f454c567f21197eefcdbf5679d0bd99f2ddbe52e84c77061952e6789"
|
||||
"checksum block 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a"
|
||||
"checksum block-buffer 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "49665c62e0e700857531fa5d3763e91b539ff1abeebd56808d378b495870d60d"
|
||||
|
@ -2312,7 +2214,6 @@ dependencies = [
|
|||
"checksum glutin_gles2_sys 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "89996c30857ae1b4de4b5189abf1ea822a20a9fe9e1c93e5e7b862ff0bdd5cdf"
|
||||
"checksum glutin_glx_sys 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "1290a5ca5e46fcfa7f66f949cc9d9194b2cb6f2ed61892c8c2b82343631dba57"
|
||||
"checksum glutin_wgl_sys 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "f801bbc91efc22dd1c4818a47814fc72bf74d024510451b119381579bfa39021"
|
||||
"checksum half 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d5c5f71a723d10dfc58927cbed37c3071a50afc7f073d86fd7d3e5727db890f"
|
||||
"checksum httparse 1.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "c2f407128745b78abc95c0ffbe4e5d37427fdc0d45470710cfef8c44522a2e37"
|
||||
"checksum humantime 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0484fda3e7007f2a4a0d9c3a703ca38c71c54c55602ce4660c419fd32e188c9e"
|
||||
"checksum idna 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "014b298351066f1512874135335d62a789ffe78a9974f94b43ed5621951eaf7d"
|
||||
|
@ -2333,10 +2234,7 @@ dependencies = [
|
|||
"checksum line_drawing 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5cc7ad3d82c845bdb5dde34ffdcc7a5fb4d2996e1e1ee0f19c33bc80e15196b9"
|
||||
"checksum linked-hash-map 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6d262045c5b87c0861b3f004610afd0e2c851e2908d08b6c870cbb9d5f494ecd"
|
||||
"checksum lock_api 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "949826a5ccf18c1b3a7c3d57692778d21768b79e46eb9dd07bfc4c2160036c54"
|
||||
"checksum log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "e19e8d5c34a3e0e2223db8e060f9e8264aeeb5c5fc64a4ee9965c062211c024b"
|
||||
"checksum log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "89f010e843f2b1a31dbd316b3b8d443758bc634bed37aabade59c686d644e0a2"
|
||||
"checksum lyon_geom 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)" = "216bfb2b880554865fa9deaa14671ab9f1368b942e7007cc8c97c083ac8a7f45"
|
||||
"checksum lyon_path 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e9dc8e0746b7cca11960b602f7fe037bb067746a01eab4aa502fed1494544843"
|
||||
"checksum lzw 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7d947cbb889ed21c2a84be6ffbaebf5b4e0f4340638cba0444907e38b56be084"
|
||||
"checksum malloc_buf 0.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "62bb907fe88d54d8d9ce32a3cceab4218ed2f6b7d35617cafe9adf84e43919cb"
|
||||
"checksum malloc_size_of_derive 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "35adee9ed962cf7d07d62cb58bc45029f3227f5b5b86246caa8632f06c187bc3"
|
||||
|
@ -2368,10 +2266,6 @@ dependencies = [
|
|||
"checksum parking_lot 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ab41b4aed082705d1056416ae4468b6ea99d52599ecf3169b00088d43113e337"
|
||||
"checksum parking_lot_core 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)" = "4db1a8ccf734a7bce794cc19b3df06ed87ab2f3907036b693c68f56b4d4537fa"
|
||||
"checksum parking_lot_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "94c8c7923936b28d546dfd14d4472eaf34c99b14e1c973a32b3e6d4eb04298c9"
|
||||
"checksum pathfinder_font_renderer 0.5.0 (git+https://github.com/pcwalton/pathfinder?branch=webrender)" = "<none>"
|
||||
"checksum pathfinder_gfx_utils 0.2.0 (git+https://github.com/pcwalton/pathfinder?branch=webrender)" = "<none>"
|
||||
"checksum pathfinder_partitioner 0.2.0 (git+https://github.com/pcwalton/pathfinder?branch=webrender)" = "<none>"
|
||||
"checksum pathfinder_path_utils 0.2.0 (git+https://github.com/pcwalton/pathfinder?branch=webrender)" = "<none>"
|
||||
"checksum percent-encoding 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "31010dd2e1ac33d5b46a5b413495239882813e0369f8ed8a5e266f173602f831"
|
||||
"checksum pkg-config 0.3.11 (registry+https://github.com/rust-lang/crates.io-index)" = "110d5ee3593dbb73f56294327fe5668bcc997897097cbc76b51e7aed3f52452f"
|
||||
"checksum plane-split 0.13.8 (registry+https://github.com/rust-lang/crates.io-index)" = "91c621d83b9c5a85b7ca7ca2bec643136debb327ad29d0a08768db1325780365"
|
||||
|
|
|
@ -17,4 +17,3 @@ build: false
|
|||
|
||||
test_script:
|
||||
- cmd.exe /c ci-scripts\windows-tests.cmd
|
||||
- cmd.exe /c ci-scripts\windows-pathfinder.cmd
|
||||
|
|
|
@ -31,7 +31,6 @@ cargo check ${CARGOFLAGS} --no-default-features
|
|||
cargo check ${CARGOFLAGS} --no-default-features --features capture
|
||||
cargo check ${CARGOFLAGS} --features capture,profiler
|
||||
cargo check ${CARGOFLAGS} --features replay
|
||||
cargo check ${CARGOFLAGS} --no-default-features --features pathfinder
|
||||
popd
|
||||
|
||||
pushd wrench
|
||||
|
|
|
@ -1,14 +0,0 @@
|
|||
:: This Source Code Form is subject to the terms of the Mozilla Public
|
||||
:: License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
:: file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
:: This must be run from the root webrender directory!
|
||||
:: Users may set the CARGOFLAGS environment variable to pass
|
||||
:: additional flags to cargo if desired.
|
||||
|
||||
if NOT DEFINED CARGOFLAGS SET CARGOFLAGS=--verbose
|
||||
|
||||
pushd webrender
|
||||
cargo check %CARGOFLAGS% --no-default-features --features pathfinder
|
||||
if %ERRORLEVEL% NEQ 0 EXIT /b %ERRORLEVEL%
|
||||
popd
|
|
@ -92,7 +92,7 @@ git log --graph --stat moz-gfx/wrupdater..wrupdater
|
|||
set +e
|
||||
FIXES=$(
|
||||
git log master..wrupdater |
|
||||
grep "\[import-pr\] From https://github.com/servo/webrender/pull" |
|
||||
grep "\[import_pr\] From https://github.com/servo/webrender/pull" |
|
||||
sed -e "s%.*pull/% Fixes #%" |
|
||||
uniq |
|
||||
tr '\n' ','
|
||||
|
|
|
@ -11,7 +11,6 @@ packages = [
|
|||
"gl_generator",
|
||||
"khronos_api",
|
||||
"lazy_static",
|
||||
"log",
|
||||
"nix",
|
||||
"parking_lot",
|
||||
"parking_lot_core",
|
||||
|
|
|
@ -16,7 +16,6 @@ debugger = ["ws", "serde_json", "serde", "image_loader", "base64"]
|
|||
capture = ["api/serialize", "ron", "serde", "smallvec/serde"]
|
||||
replay = ["api/deserialize", "ron", "serde", "smallvec/serde"]
|
||||
display_list_stats = ["api/display_list_stats"]
|
||||
pathfinder = ["pathfinder_font_renderer", "pathfinder_gfx_utils", "pathfinder_partitioner", "pathfinder_path_utils"]
|
||||
serialize_program = ["serde", "webrender_build/serialize_program"]
|
||||
no_static_freetype = []
|
||||
|
||||
|
@ -54,28 +53,6 @@ malloc_size_of = { version = "0.0.1", path = "../wr_malloc_size_of", package = "
|
|||
ws = { optional = true, version = "0.8" }
|
||||
svg_fmt = "0.4"
|
||||
|
||||
[dependencies.pathfinder_font_renderer]
|
||||
git = "https://github.com/pcwalton/pathfinder"
|
||||
branch = "webrender"
|
||||
optional = true
|
||||
# Uncomment to test FreeType on macOS:
|
||||
# features = ["freetype"]
|
||||
|
||||
[dependencies.pathfinder_gfx_utils]
|
||||
git = "https://github.com/pcwalton/pathfinder"
|
||||
branch = "webrender"
|
||||
optional = true
|
||||
|
||||
[dependencies.pathfinder_partitioner]
|
||||
git = "https://github.com/pcwalton/pathfinder"
|
||||
branch = "webrender"
|
||||
optional = true
|
||||
|
||||
[dependencies.pathfinder_path_utils]
|
||||
git = "https://github.com/pcwalton/pathfinder"
|
||||
branch = "webrender"
|
||||
optional = true
|
||||
|
||||
[dev-dependencies]
|
||||
mozangle = "0.1"
|
||||
rand = "0.4"
|
||||
|
|
|
@ -2,18 +2,13 @@
|
|||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#[cfg(feature = "pathfinder")]
|
||||
use crate::api::units::DeviceIntPoint;
|
||||
use crate::glyph_rasterizer::{FontInstance, GlyphFormat, GlyphKey, GlyphRasterizer};
|
||||
use crate::internal_types::FastHashMap;
|
||||
use crate::render_backend::{FrameId, FrameStamp};
|
||||
use crate::render_task::RenderTaskCache;
|
||||
#[cfg(feature = "pathfinder")]
|
||||
use crate::render_task::RenderTaskCacheKey;
|
||||
use crate::resource_cache::ResourceClassCache;
|
||||
use std::sync::Arc;
|
||||
use crate::texture_cache::{EvictionNotice, TextureCache};
|
||||
#[cfg(not(feature = "pathfinder"))]
|
||||
use crate::texture_cache::TextureCacheHandle;
|
||||
|
||||
#[cfg_attr(feature = "capture", derive(Serialize))]
|
||||
|
@ -21,12 +16,7 @@ use crate::texture_cache::TextureCacheHandle;
|
|||
#[derive(Clone, Debug)]
|
||||
pub struct CachedGlyphInfo {
|
||||
pub format: GlyphFormat,
|
||||
#[cfg(not(feature = "pathfinder"))]
|
||||
pub texture_cache_handle: TextureCacheHandle,
|
||||
#[cfg(feature = "pathfinder")]
|
||||
pub render_task_cache_key: RenderTaskCacheKey,
|
||||
#[cfg(feature = "pathfinder")]
|
||||
pub origin: DeviceIntPoint,
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "capture", derive(Serialize))]
|
||||
|
@ -43,26 +33,6 @@ pub enum GlyphCacheEntry {
|
|||
}
|
||||
|
||||
impl GlyphCacheEntry {
|
||||
#[cfg(feature = "pathfinder")]
|
||||
fn get_allocated_size(&self, texture_cache: &TextureCache, render_task_cache: &RenderTaskCache)
|
||||
-> Option<usize> {
|
||||
match *self {
|
||||
GlyphCacheEntry::Cached(ref glyph) => {
|
||||
let render_task_cache_key = &glyph.render_task_cache_key;
|
||||
render_task_cache.get_allocated_size_for_render_task(texture_cache,
|
||||
&render_task_cache_key)
|
||||
}
|
||||
GlyphCacheEntry::Pending => Some(0),
|
||||
// If the cache only has blank glyphs left, just get rid of it.
|
||||
GlyphCacheEntry::Blank => None,
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "pathfinder")]
|
||||
fn mark_unused(&self, _: &mut TextureCache) {
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "pathfinder"))]
|
||||
fn get_allocated_size(&self, texture_cache: &TextureCache, _: &RenderTaskCache)
|
||||
-> Option<usize> {
|
||||
match *self {
|
||||
|
@ -75,13 +45,11 @@ impl GlyphCacheEntry {
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "pathfinder"))]
|
||||
fn mark_unused(&self, texture_cache: &mut TextureCache) {
|
||||
if let GlyphCacheEntry::Cached(ref glyph) = *self {
|
||||
texture_cache.mark_unused(&glyph.texture_cache_handle);
|
||||
}
|
||||
}
|
||||
}
|
||||
}}
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[cfg_attr(feature = "capture", derive(Serialize))]
|
||||
|
|
|
@ -6,11 +6,22 @@ use api::{FontInstanceFlags, FontInstancePlatformOptions};
|
|||
use api::{FontKey, FontInstanceKey, FontRenderMode, FontTemplate, FontVariation};
|
||||
use api::{ColorU, GlyphIndex, GlyphDimensions, SyntheticItalics};
|
||||
use api::units::*;
|
||||
use euclid::approxeq::ApproxEq;
|
||||
use api::{ImageDescriptor, ImageFormat, DirtyRect};
|
||||
use crate::internal_types::ResourceCacheError;
|
||||
use malloc_size_of::{MallocSizeOf, MallocSizeOfOps};
|
||||
use crate::platform::font::FontContext;
|
||||
use crate::device::TextureFilter;
|
||||
use crate::gpu_types::UvRectKind;
|
||||
use crate::glyph_cache::{GlyphCache, CachedGlyphInfo, GlyphCacheEntry};
|
||||
use crate::resource_cache::CachedImageData;
|
||||
use crate::texture_cache::{TextureCache, TextureCacheHandle, Eviction};
|
||||
use crate::gpu_cache::GpuCache;
|
||||
use crate::render_task::{RenderTaskGraph, RenderTaskCache};
|
||||
use crate::profiler::TextureCacheProfileCounters;
|
||||
use malloc_size_of::{MallocSizeOf, MallocSizeOfOps};
|
||||
use rayon::ThreadPool;
|
||||
use rayon::prelude::*;
|
||||
use euclid::approxeq::ApproxEq;
|
||||
use euclid::size2;
|
||||
use std::cmp;
|
||||
use std::hash::{Hash, Hasher};
|
||||
use std::mem;
|
||||
|
@ -18,15 +29,181 @@ use std::ops::Deref;
|
|||
use std::sync::{Arc, Condvar, Mutex, MutexGuard};
|
||||
use std::sync::mpsc::{channel, Receiver, Sender};
|
||||
|
||||
#[cfg(feature = "pathfinder")]
|
||||
mod pathfinder;
|
||||
#[cfg(feature = "pathfinder")]
|
||||
use self::pathfinder::create_pathfinder_font_context;
|
||||
#[cfg(feature = "pathfinder")]
|
||||
pub use self::pathfinder::{ThreadSafePathfinderFontContext, NativeFontHandleWrapper};
|
||||
impl FontContexts {
|
||||
/// Get access to the font context associated to the current thread.
|
||||
pub fn lock_current_context(&self) -> MutexGuard<FontContext> {
|
||||
let id = self.current_worker_id();
|
||||
self.lock_context(id)
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "pathfinder"))]
|
||||
mod no_pathfinder;
|
||||
pub(in super) fn current_worker_id(&self) -> Option<usize> {
|
||||
self.workers.current_thread_index()
|
||||
}
|
||||
}
|
||||
|
||||
impl GlyphRasterizer {
|
||||
|
||||
pub fn request_glyphs(
|
||||
&mut self,
|
||||
glyph_cache: &mut GlyphCache,
|
||||
font: FontInstance,
|
||||
glyph_keys: &[GlyphKey],
|
||||
texture_cache: &mut TextureCache,
|
||||
gpu_cache: &mut GpuCache,
|
||||
_: &mut RenderTaskCache,
|
||||
_: &mut RenderTaskGraph,
|
||||
) {
|
||||
assert!(
|
||||
self.font_contexts
|
||||
.lock_shared_context()
|
||||
.has_font(&font.font_key)
|
||||
);
|
||||
let mut new_glyphs = Vec::new();
|
||||
|
||||
let glyph_key_cache = glyph_cache.get_glyph_key_cache_for_font_mut(font.clone());
|
||||
|
||||
// select glyphs that have not been requested yet.
|
||||
for key in glyph_keys {
|
||||
if let Some(entry) = glyph_key_cache.try_get(key) {
|
||||
match entry {
|
||||
GlyphCacheEntry::Cached(ref glyph) => {
|
||||
// Skip the glyph if it is already has a valid texture cache handle.
|
||||
if !texture_cache.request(&glyph.texture_cache_handle, gpu_cache) {
|
||||
continue;
|
||||
}
|
||||
// This case gets hit when we already rasterized the glyph, but the
|
||||
// glyph has been evicted from the texture cache. Just force it to
|
||||
// pending so it gets rematerialized.
|
||||
}
|
||||
// Otherwise, skip the entry if it is blank or pending.
|
||||
GlyphCacheEntry::Blank | GlyphCacheEntry::Pending => continue,
|
||||
}
|
||||
}
|
||||
new_glyphs.push(key.clone());
|
||||
glyph_key_cache.add_glyph(key.clone(), GlyphCacheEntry::Pending);
|
||||
}
|
||||
|
||||
if new_glyphs.is_empty() {
|
||||
return;
|
||||
}
|
||||
|
||||
self.pending_glyphs += 1;
|
||||
|
||||
self.request_glyphs_from_backend(font, new_glyphs);
|
||||
}
|
||||
|
||||
pub(in super) fn request_glyphs_from_backend(&mut self, font: FontInstance, glyphs: Vec<GlyphKey>) {
|
||||
let font_contexts = Arc::clone(&self.font_contexts);
|
||||
let glyph_tx = self.glyph_tx.clone();
|
||||
|
||||
// spawn an async task to get off of the render backend thread as early as
|
||||
// possible and in that task use rayon's fork join dispatch to rasterize the
|
||||
// glyphs in the thread pool.
|
||||
self.workers.spawn(move || {
|
||||
let jobs = glyphs
|
||||
.par_iter()
|
||||
.map(|key: &GlyphKey| {
|
||||
profile_scope!("glyph-raster");
|
||||
let mut context = font_contexts.lock_current_context();
|
||||
let mut job = GlyphRasterJob {
|
||||
key: key.clone(),
|
||||
result: context.rasterize_glyph(&font, key),
|
||||
};
|
||||
|
||||
if let Ok(ref mut glyph) = job.result {
|
||||
// Sanity check.
|
||||
let bpp = 4; // We always render glyphs in 32 bits RGBA format.
|
||||
assert_eq!(
|
||||
glyph.bytes.len(),
|
||||
bpp * (glyph.width * glyph.height) as usize
|
||||
);
|
||||
assert_eq!((glyph.left.fract(), glyph.top.fract()), (0.0, 0.0));
|
||||
|
||||
// Check if the glyph has a bitmap that needs to be downscaled.
|
||||
glyph.downscale_bitmap_if_required(&font);
|
||||
}
|
||||
|
||||
job
|
||||
})
|
||||
.collect();
|
||||
|
||||
glyph_tx.send(GlyphRasterJobs { font, jobs }).unwrap();
|
||||
});
|
||||
}
|
||||
|
||||
pub fn resolve_glyphs(
|
||||
&mut self,
|
||||
glyph_cache: &mut GlyphCache,
|
||||
texture_cache: &mut TextureCache,
|
||||
gpu_cache: &mut GpuCache,
|
||||
_: &mut RenderTaskCache,
|
||||
_: &mut RenderTaskGraph,
|
||||
_: &mut TextureCacheProfileCounters,
|
||||
) {
|
||||
// Pull rasterized glyphs from the queue and update the caches.
|
||||
while self.pending_glyphs > 0 {
|
||||
self.pending_glyphs -= 1;
|
||||
|
||||
// TODO: rather than blocking until all pending glyphs are available
|
||||
// we could try_recv and steal work from the thread pool to take advantage
|
||||
// of the fact that this thread is alive and we avoid the added latency
|
||||
// of blocking it.
|
||||
let GlyphRasterJobs { font, mut jobs } = self.glyph_rx
|
||||
.recv()
|
||||
.expect("BUG: Should be glyphs pending!");
|
||||
|
||||
// Ensure that the glyphs are always processed in the same
|
||||
// order for a given text run (since iterating a hash set doesn't
|
||||
// guarantee order). This can show up as very small float inaccuracy
|
||||
// differences in rasterizers due to the different coordinates
|
||||
// that text runs get associated with by the texture cache allocator.
|
||||
jobs.sort_by(|a, b| a.key.cmp(&b.key));
|
||||
|
||||
let glyph_key_cache = glyph_cache.get_glyph_key_cache_for_font_mut(font);
|
||||
|
||||
for GlyphRasterJob { key, result } in jobs {
|
||||
let glyph_info = match result {
|
||||
Err(_) => GlyphCacheEntry::Blank,
|
||||
Ok(ref glyph) if glyph.width == 0 || glyph.height == 0 => {
|
||||
GlyphCacheEntry::Blank
|
||||
}
|
||||
Ok(glyph) => {
|
||||
let mut texture_cache_handle = TextureCacheHandle::invalid();
|
||||
texture_cache.request(&texture_cache_handle, gpu_cache);
|
||||
texture_cache.update(
|
||||
&mut texture_cache_handle,
|
||||
ImageDescriptor {
|
||||
size: size2(glyph.width, glyph.height),
|
||||
stride: None,
|
||||
format: ImageFormat::BGRA8,
|
||||
is_opaque: false,
|
||||
allow_mipmaps: false,
|
||||
offset: 0,
|
||||
},
|
||||
TextureFilter::Linear,
|
||||
Some(CachedImageData::Raw(Arc::new(glyph.bytes))),
|
||||
[glyph.left, -glyph.top, glyph.scale],
|
||||
DirtyRect::All,
|
||||
gpu_cache,
|
||||
Some(glyph_key_cache.eviction_notice()),
|
||||
UvRectKind::Rect,
|
||||
Eviction::Auto,
|
||||
);
|
||||
GlyphCacheEntry::Cached(CachedGlyphInfo {
|
||||
texture_cache_handle,
|
||||
format: glyph.format,
|
||||
})
|
||||
}
|
||||
};
|
||||
glyph_key_cache.insert(key, glyph_info);
|
||||
}
|
||||
}
|
||||
|
||||
// Now that we are done with the critical path (rendering the glyphs),
|
||||
// we can schedule removing the fonts if needed.
|
||||
self.remove_dead_fonts();
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, PartialOrd)]
|
||||
#[cfg_attr(feature = "capture", derive(Serialize))]
|
||||
|
@ -573,8 +750,6 @@ pub struct FontContexts {
|
|||
// This worker should be accessed by threads that don't belong to the thread pool
|
||||
// (in theory that's only the render backend thread so no contention expected either).
|
||||
shared_context: Mutex<FontContext>,
|
||||
#[cfg(feature = "pathfinder")]
|
||||
pathfinder_context: Box<ThreadSafePathfinderFontContext>,
|
||||
// Stored here as a convenience to get the current thread index.
|
||||
#[allow(dead_code)]
|
||||
workers: Arc<ThreadPool>,
|
||||
|
@ -695,8 +870,6 @@ impl GlyphRasterizer {
|
|||
let font_context = FontContexts {
|
||||
worker_contexts: contexts,
|
||||
shared_context: Mutex::new(shared_context),
|
||||
#[cfg(feature = "pathfinder")]
|
||||
pathfinder_context: create_pathfinder_font_context()?,
|
||||
workers: Arc::clone(&workers),
|
||||
locked_mutex: Mutex::new(false),
|
||||
locked_cond: Condvar::new(),
|
||||
|
@ -715,9 +888,6 @@ impl GlyphRasterizer {
|
|||
}
|
||||
|
||||
pub fn add_font(&mut self, font_key: FontKey, template: FontTemplate) {
|
||||
#[cfg(feature = "pathfinder")]
|
||||
self.add_font_to_pathfinder(&font_key, &template);
|
||||
|
||||
self.font_contexts.async_for_each(move |mut context| {
|
||||
context.add_font(&font_key, &template);
|
||||
});
|
||||
|
|
|
@ -1,198 +0,0 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
//! Module only available when pathfinder is deactivated when webrender is
|
||||
//! compiled regularly (i.e. any configuration without feature = "pathfinder")
|
||||
|
||||
use api::{ImageDescriptor, ImageFormat, DirtyRect};
|
||||
use crate::device::TextureFilter;
|
||||
use euclid::size2;
|
||||
use crate::gpu_types::UvRectKind;
|
||||
use rayon::prelude::*;
|
||||
use std::sync::{Arc, MutexGuard};
|
||||
use crate::platform::font::FontContext;
|
||||
use crate::glyph_rasterizer::{FontInstance, FontContexts, GlyphKey};
|
||||
use crate::glyph_rasterizer::{GlyphRasterizer, GlyphRasterJob, GlyphRasterJobs};
|
||||
use crate::glyph_cache::{GlyphCache, CachedGlyphInfo, GlyphCacheEntry};
|
||||
use crate::resource_cache::CachedImageData;
|
||||
use crate::texture_cache::{TextureCache, TextureCacheHandle, Eviction};
|
||||
use crate::gpu_cache::GpuCache;
|
||||
use crate::render_task::{RenderTaskGraph, RenderTaskCache};
|
||||
use crate::profiler::TextureCacheProfileCounters;
|
||||
|
||||
impl FontContexts {
|
||||
/// Get access to the font context associated to the current thread.
|
||||
pub fn lock_current_context(&self) -> MutexGuard<FontContext> {
|
||||
let id = self.current_worker_id();
|
||||
self.lock_context(id)
|
||||
}
|
||||
|
||||
pub(in super) fn current_worker_id(&self) -> Option<usize> {
|
||||
self.workers.current_thread_index()
|
||||
}
|
||||
}
|
||||
|
||||
impl GlyphRasterizer {
|
||||
|
||||
pub fn request_glyphs(
|
||||
&mut self,
|
||||
glyph_cache: &mut GlyphCache,
|
||||
font: FontInstance,
|
||||
glyph_keys: &[GlyphKey],
|
||||
texture_cache: &mut TextureCache,
|
||||
gpu_cache: &mut GpuCache,
|
||||
_: &mut RenderTaskCache,
|
||||
_: &mut RenderTaskGraph,
|
||||
) {
|
||||
assert!(
|
||||
self.font_contexts
|
||||
.lock_shared_context()
|
||||
.has_font(&font.font_key)
|
||||
);
|
||||
let mut new_glyphs = Vec::new();
|
||||
|
||||
let glyph_key_cache = glyph_cache.get_glyph_key_cache_for_font_mut(font.clone());
|
||||
|
||||
// select glyphs that have not been requested yet.
|
||||
for key in glyph_keys {
|
||||
if let Some(entry) = glyph_key_cache.try_get(key) {
|
||||
match entry {
|
||||
GlyphCacheEntry::Cached(ref glyph) => {
|
||||
// Skip the glyph if it is already has a valid texture cache handle.
|
||||
if !texture_cache.request(&glyph.texture_cache_handle, gpu_cache) {
|
||||
continue;
|
||||
}
|
||||
// This case gets hit when we already rasterized the glyph, but the
|
||||
// glyph has been evicted from the texture cache. Just force it to
|
||||
// pending so it gets rematerialized.
|
||||
}
|
||||
// Otherwise, skip the entry if it is blank or pending.
|
||||
GlyphCacheEntry::Blank | GlyphCacheEntry::Pending => continue,
|
||||
}
|
||||
}
|
||||
new_glyphs.push(key.clone());
|
||||
glyph_key_cache.add_glyph(key.clone(), GlyphCacheEntry::Pending);
|
||||
}
|
||||
|
||||
if new_glyphs.is_empty() {
|
||||
return;
|
||||
}
|
||||
|
||||
self.pending_glyphs += 1;
|
||||
|
||||
self.request_glyphs_from_backend(font, new_glyphs);
|
||||
}
|
||||
|
||||
pub(in super) fn request_glyphs_from_backend(&mut self, font: FontInstance, glyphs: Vec<GlyphKey>) {
|
||||
let font_contexts = Arc::clone(&self.font_contexts);
|
||||
let glyph_tx = self.glyph_tx.clone();
|
||||
|
||||
// spawn an async task to get off of the render backend thread as early as
|
||||
// possible and in that task use rayon's fork join dispatch to rasterize the
|
||||
// glyphs in the thread pool.
|
||||
self.workers.spawn(move || {
|
||||
let jobs = glyphs
|
||||
.par_iter()
|
||||
.map(|key: &GlyphKey| {
|
||||
profile_scope!("glyph-raster");
|
||||
let mut context = font_contexts.lock_current_context();
|
||||
let mut job = GlyphRasterJob {
|
||||
key: key.clone(),
|
||||
result: context.rasterize_glyph(&font, key),
|
||||
};
|
||||
|
||||
if let Ok(ref mut glyph) = job.result {
|
||||
// Sanity check.
|
||||
let bpp = 4; // We always render glyphs in 32 bits RGBA format.
|
||||
assert_eq!(
|
||||
glyph.bytes.len(),
|
||||
bpp * (glyph.width * glyph.height) as usize
|
||||
);
|
||||
assert_eq!((glyph.left.fract(), glyph.top.fract()), (0.0, 0.0));
|
||||
|
||||
// Check if the glyph has a bitmap that needs to be downscaled.
|
||||
glyph.downscale_bitmap_if_required(&font);
|
||||
}
|
||||
|
||||
job
|
||||
})
|
||||
.collect();
|
||||
|
||||
glyph_tx.send(GlyphRasterJobs { font, jobs }).unwrap();
|
||||
});
|
||||
}
|
||||
|
||||
pub fn resolve_glyphs(
|
||||
&mut self,
|
||||
glyph_cache: &mut GlyphCache,
|
||||
texture_cache: &mut TextureCache,
|
||||
gpu_cache: &mut GpuCache,
|
||||
_: &mut RenderTaskCache,
|
||||
_: &mut RenderTaskGraph,
|
||||
_: &mut TextureCacheProfileCounters,
|
||||
) {
|
||||
// Pull rasterized glyphs from the queue and update the caches.
|
||||
while self.pending_glyphs > 0 {
|
||||
self.pending_glyphs -= 1;
|
||||
|
||||
// TODO: rather than blocking until all pending glyphs are available
|
||||
// we could try_recv and steal work from the thread pool to take advantage
|
||||
// of the fact that this thread is alive and we avoid the added latency
|
||||
// of blocking it.
|
||||
let GlyphRasterJobs { font, mut jobs } = self.glyph_rx
|
||||
.recv()
|
||||
.expect("BUG: Should be glyphs pending!");
|
||||
|
||||
// Ensure that the glyphs are always processed in the same
|
||||
// order for a given text run (since iterating a hash set doesn't
|
||||
// guarantee order). This can show up as very small float inaccuracy
|
||||
// differences in rasterizers due to the different coordinates
|
||||
// that text runs get associated with by the texture cache allocator.
|
||||
jobs.sort_by(|a, b| a.key.cmp(&b.key));
|
||||
|
||||
let glyph_key_cache = glyph_cache.get_glyph_key_cache_for_font_mut(font);
|
||||
|
||||
for GlyphRasterJob { key, result } in jobs {
|
||||
let glyph_info = match result {
|
||||
Err(_) => GlyphCacheEntry::Blank,
|
||||
Ok(ref glyph) if glyph.width == 0 || glyph.height == 0 => {
|
||||
GlyphCacheEntry::Blank
|
||||
}
|
||||
Ok(glyph) => {
|
||||
let mut texture_cache_handle = TextureCacheHandle::invalid();
|
||||
texture_cache.request(&texture_cache_handle, gpu_cache);
|
||||
texture_cache.update(
|
||||
&mut texture_cache_handle,
|
||||
ImageDescriptor {
|
||||
size: size2(glyph.width, glyph.height),
|
||||
stride: None,
|
||||
format: ImageFormat::BGRA8,
|
||||
is_opaque: false,
|
||||
allow_mipmaps: false,
|
||||
offset: 0,
|
||||
},
|
||||
TextureFilter::Linear,
|
||||
Some(CachedImageData::Raw(Arc::new(glyph.bytes))),
|
||||
[glyph.left, -glyph.top, glyph.scale],
|
||||
DirtyRect::All,
|
||||
gpu_cache,
|
||||
Some(glyph_key_cache.eviction_notice()),
|
||||
UvRectKind::Rect,
|
||||
Eviction::Auto,
|
||||
);
|
||||
GlyphCacheEntry::Cached(CachedGlyphInfo {
|
||||
texture_cache_handle,
|
||||
format: glyph.format,
|
||||
})
|
||||
}
|
||||
};
|
||||
glyph_key_cache.add_glyph(key, glyph_info);
|
||||
}
|
||||
}
|
||||
|
||||
// Now that we are done with the critical path (rendering the glyphs),
|
||||
// we can schedule removing the fonts if needed.
|
||||
self.remove_dead_fonts();
|
||||
}
|
||||
}
|
|
@ -1,303 +0,0 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
//! Module only available when pathfinder is activated
|
||||
|
||||
use api::{FontKey, FontTemplate, NativeFontHandle};
|
||||
use api::units::{DeviceIntPoint, DeviceIntSize, DevicePixel};
|
||||
use euclid::{TypedPoint2D, TypedSize2D, TypedVector2D};
|
||||
use pathfinder_font_renderer;
|
||||
use pathfinder_partitioner::mesh::Mesh as PathfinderMesh;
|
||||
use pathfinder_path_utils::cubic_to_quadratic::CubicToQuadraticTransformer;
|
||||
use crate::render_task::{RenderTask, RenderTaskGraph, RenderTaskCache, RenderTaskCacheKey,
|
||||
RenderTaskCacheEntryHandle, RenderTaskCacheKeyKind, RenderTaskId,
|
||||
RenderTaskLocation};
|
||||
use crate::resource_cache::CacheItem;
|
||||
use std::ops::Deref;
|
||||
use std::sync::{Arc, Mutex, MutexGuard};
|
||||
use crate::glyph_rasterizer::AddFont;
|
||||
use crate::internal_types::ResourceCacheError;
|
||||
use crate::glyph_cache::{GlyphCache, GlyphCacheEntry, CachedGlyphInfo};
|
||||
use std::f32;
|
||||
use crate::glyph_rasterizer::{FontInstance, GlyphRasterizer, GlyphFormat, GlyphKey, FontContexts};
|
||||
use crate::texture_cache::TextureCache;
|
||||
use crate::gpu_cache::GpuCache;
|
||||
use crate::profiler::TextureCacheProfileCounters;
|
||||
|
||||
/// Should match macOS 10.13 High Sierra.
|
||||
///
|
||||
/// We multiply by sqrt(2) to compensate for the fact that dilation amounts are relative to the
|
||||
/// pixel square on macOS and relative to the vertex normal in Pathfinder.
|
||||
const STEM_DARKENING_FACTOR_X: f32 = 0.0121 * f32::consts::SQRT_2;
|
||||
const STEM_DARKENING_FACTOR_Y: f32 = 0.0121 * 1.25 * f32::consts::SQRT_2;
|
||||
|
||||
/// Likewise, should match macOS 10.13 High Sierra.
|
||||
const MAX_STEM_DARKENING_AMOUNT: f32 = 0.3 * f32::consts::SQRT_2;
|
||||
|
||||
const CUBIC_TO_QUADRATIC_APPROX_TOLERANCE: f32 = 0.01;
|
||||
|
||||
type PathfinderFontContext = pathfinder_font_renderer::FontContext<FontKey>;
|
||||
|
||||
impl AddFont for PathfinderFontContext {
|
||||
fn add_font(&mut self, font_key: &FontKey, template: &FontTemplate) {
|
||||
match *template {
|
||||
FontTemplate::Raw(ref bytes, index) => {
|
||||
drop(self.add_font_from_memory(&font_key, bytes.clone(), index))
|
||||
}
|
||||
FontTemplate::Native(ref native_font_handle) => {
|
||||
drop(self.add_native_font(&font_key, NativeFontHandleWrapper(native_font_handle)))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(in super) fn create_pathfinder_font_context()
|
||||
-> Result<Box<ThreadSafePathfinderFontContext>, ResourceCacheError>
|
||||
{
|
||||
match PathfinderFontContext::new() {
|
||||
Ok(context) => Ok(Box::new(ThreadSafePathfinderFontContext(Mutex::new(context)))),
|
||||
Err(_) => {
|
||||
let msg = "Failed to create the Pathfinder font context!".to_owned();
|
||||
Err(ResourceCacheError::new(msg))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ThreadSafePathfinderFontContext(Mutex<PathfinderFontContext>);
|
||||
|
||||
impl Deref for ThreadSafePathfinderFontContext {
|
||||
type Target = Mutex<PathfinderFontContext>;
|
||||
|
||||
fn deref(&self) -> &Mutex<PathfinderFontContext> {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
/// PathfinderFontContext can contain a *mut IDWriteFactory.
|
||||
/// However, since we know that it is wrapped in a Mutex, it is safe
|
||||
/// to assume that this struct is thread-safe
|
||||
unsafe impl Send for ThreadSafePathfinderFontContext {}
|
||||
unsafe impl Sync for ThreadSafePathfinderFontContext { }
|
||||
|
||||
impl GlyphRasterizer {
|
||||
pub(in super) fn add_font_to_pathfinder(&mut self, font_key: &FontKey, template: &FontTemplate) {
|
||||
let font_contexts = Arc::clone(&self.font_contexts);
|
||||
debug!("add_font_to_pathfinder({:?})", font_key);
|
||||
font_contexts.lock_pathfinder_context().add_font(&font_key, &template);
|
||||
}
|
||||
|
||||
pub fn get_cache_item_for_glyph(
|
||||
&self,
|
||||
glyph_key: &GlyphKey,
|
||||
font: &FontInstance,
|
||||
glyph_cache: &GlyphCache,
|
||||
texture_cache: &TextureCache,
|
||||
render_task_cache: &RenderTaskCache)
|
||||
-> Option<(CacheItem, GlyphFormat)>
|
||||
{
|
||||
let glyph_key_cache = glyph_cache.get_glyph_key_cache_for_font(font);
|
||||
let render_task_cache_key = match *glyph_key_cache.get(glyph_key) {
|
||||
GlyphCacheEntry::Cached(ref cached_glyph) => {
|
||||
(*cached_glyph).render_task_cache_key.clone()
|
||||
}
|
||||
GlyphCacheEntry::Blank => return None,
|
||||
GlyphCacheEntry::Pending => {
|
||||
panic!("GlyphRasterizer::get_cache_item_for_glyph(): Glyph should have been \
|
||||
cached by now!")
|
||||
}
|
||||
};
|
||||
let cache_item = render_task_cache.get_cache_item_for_render_task(texture_cache,
|
||||
&render_task_cache_key);
|
||||
Some((cache_item, font.get_glyph_format()))
|
||||
}
|
||||
|
||||
pub(in super) fn request_glyph_from_pathfinder_if_necessary(
|
||||
&mut self,
|
||||
glyph_key: &GlyphKey,
|
||||
font: &FontInstance,
|
||||
scale: f32,
|
||||
cached_glyph_info: CachedGlyphInfo,
|
||||
texture_cache: &mut TextureCache,
|
||||
gpu_cache: &mut GpuCache,
|
||||
render_task_cache: &mut RenderTaskCache,
|
||||
render_task_tree: &mut RenderTaskGraph,
|
||||
) -> Result<(RenderTaskCacheEntryHandle,GlyphFormat), ()> {
|
||||
let mut pathfinder_font_context = self.font_contexts.lock_pathfinder_context();
|
||||
let render_task_cache_key = cached_glyph_info.render_task_cache_key;
|
||||
let (glyph_origin, glyph_size) = (cached_glyph_info.origin, render_task_cache_key.size);
|
||||
let user_data = [glyph_origin.x as f32, (glyph_origin.y - glyph_size.height) as f32, scale];
|
||||
let handle = render_task_cache.request_render_task(render_task_cache_key,
|
||||
texture_cache,
|
||||
gpu_cache,
|
||||
render_task_tree,
|
||||
Some(user_data),
|
||||
false,
|
||||
|render_tasks| {
|
||||
// TODO(pcwalton): Non-subpixel font render mode.
|
||||
request_render_task_from_pathfinder(
|
||||
glyph_key,
|
||||
font,
|
||||
scale,
|
||||
&glyph_origin,
|
||||
&glyph_size,
|
||||
&mut *pathfinder_font_context,
|
||||
render_tasks,
|
||||
)
|
||||
})?;
|
||||
Ok((handle, font.get_glyph_format()))
|
||||
}
|
||||
|
||||
pub fn request_glyphs(
|
||||
&mut self,
|
||||
glyph_cache: &mut GlyphCache,
|
||||
font: FontInstance,
|
||||
glyph_keys: &[GlyphKey],
|
||||
texture_cache: &mut TextureCache,
|
||||
gpu_cache: &mut GpuCache,
|
||||
render_task_cache: &mut RenderTaskCache,
|
||||
render_task_tree: &mut RenderTaskGraph,
|
||||
) {
|
||||
debug_assert!(self.font_contexts.lock_shared_context().has_font(&font.font_key));
|
||||
|
||||
let glyph_key_cache = glyph_cache.get_glyph_key_cache_for_font_mut(font.clone());
|
||||
|
||||
let (x_scale, y_scale) = font.transform.compute_scale().unwrap_or((1.0, 1.0));
|
||||
let scale = font.oversized_scale_factor(x_scale, y_scale) as f32;
|
||||
|
||||
// select glyphs that have not been requested yet.
|
||||
for glyph_key in glyph_keys {
|
||||
let mut cached_glyph_info = None;
|
||||
if let Some(GlyphCacheEntry::Cached(ref info)) = glyph_key_cache.try_get(glyph_key) {
|
||||
cached_glyph_info = Some(info.clone());
|
||||
} else {
|
||||
let pathfinder_font_context = self.font_contexts.lock_pathfinder_context();
|
||||
|
||||
let pathfinder_font_instance = pathfinder_font_renderer::FontInstance {
|
||||
font_key: font.font_key.clone(),
|
||||
size: font.size.scale_by(scale.recip()),
|
||||
};
|
||||
|
||||
// TODO: pathfinder will need to support 2D subpixel offset
|
||||
let pathfinder_subpixel_offset =
|
||||
pathfinder_font_renderer::SubpixelOffset(glyph_key.subpixel_offset().0 as u8);
|
||||
let pathfinder_glyph_key =
|
||||
pathfinder_font_renderer::GlyphKey::new(glyph_key.index(),
|
||||
pathfinder_subpixel_offset);
|
||||
|
||||
if let Ok(glyph_dimensions) =
|
||||
pathfinder_font_context.glyph_dimensions(&pathfinder_font_instance,
|
||||
&pathfinder_glyph_key,
|
||||
false) {
|
||||
let render_task_cache_key = RenderTaskCacheKey {
|
||||
size: TypedSize2D::from_untyped(&glyph_dimensions.size.to_i32()),
|
||||
kind: RenderTaskCacheKeyKind::Glyph(self.next_gpu_glyph_cache_key),
|
||||
};
|
||||
cached_glyph_info = Some(CachedGlyphInfo {
|
||||
render_task_cache_key,
|
||||
format: font.get_glyph_format(),
|
||||
origin: DeviceIntPoint::new(glyph_dimensions.origin.x as i32,
|
||||
-glyph_dimensions.origin.y as i32),
|
||||
});
|
||||
self.next_gpu_glyph_cache_key.0 += 1;
|
||||
}
|
||||
}
|
||||
|
||||
let handle = match cached_glyph_info {
|
||||
Some(glyph_info) => {
|
||||
match self.request_glyph_from_pathfinder_if_necessary(
|
||||
glyph_key,
|
||||
&font,
|
||||
scale,
|
||||
glyph_info.clone(),
|
||||
texture_cache,
|
||||
gpu_cache,
|
||||
render_task_cache,
|
||||
render_task_tree,
|
||||
) {
|
||||
Ok(_) => GlyphCacheEntry::Cached(glyph_info),
|
||||
Err(_) => GlyphCacheEntry::Blank,
|
||||
}
|
||||
}
|
||||
None => GlyphCacheEntry::Blank,
|
||||
};
|
||||
|
||||
glyph_key_cache.add_glyph(glyph_key.clone(), handle);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn resolve_glyphs(
|
||||
&mut self,
|
||||
_: &mut GlyphCache,
|
||||
_: &mut TextureCache,
|
||||
_: &mut GpuCache,
|
||||
_: &mut RenderTaskCache,
|
||||
_: &mut RenderTaskGraph,
|
||||
_: &mut TextureCacheProfileCounters,
|
||||
) {
|
||||
self.remove_dead_fonts();
|
||||
}
|
||||
}
|
||||
|
||||
impl FontContexts {
|
||||
pub fn lock_pathfinder_context(&self) -> MutexGuard<PathfinderFontContext> {
|
||||
self.pathfinder_context.lock().unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
fn compute_embolden_amount(ppem: f32) -> TypedVector2D<f32, DevicePixel> {
|
||||
TypedVector2D::new(f32::min(ppem * STEM_DARKENING_FACTOR_X, MAX_STEM_DARKENING_AMOUNT),
|
||||
f32::min(ppem * STEM_DARKENING_FACTOR_Y, MAX_STEM_DARKENING_AMOUNT))
|
||||
}
|
||||
|
||||
fn request_render_task_from_pathfinder(
|
||||
glyph_key: &GlyphKey,
|
||||
font: &FontInstance,
|
||||
scale: f32,
|
||||
glyph_origin: &DeviceIntPoint,
|
||||
glyph_size: &DeviceIntSize,
|
||||
font_context: &mut PathfinderFontContext,
|
||||
render_tasks: &mut RenderTaskGraph,
|
||||
) -> Result<RenderTaskId, ()> {
|
||||
let size = font.size.scale_by(scale.recip());
|
||||
let pathfinder_font_instance = pathfinder_font_renderer::FontInstance {
|
||||
font_key: font.font_key.clone(),
|
||||
size,
|
||||
};
|
||||
|
||||
// TODO: pathfinder will need to support 2D subpixel offset
|
||||
let pathfinder_subpixel_offset =
|
||||
pathfinder_font_renderer::SubpixelOffset(glyph_key.subpixel_offset().0 as u8);
|
||||
let glyph_subpixel_offset: f64 = glyph_key.subpixel_offset().0.into();
|
||||
let pathfinder_glyph_key = pathfinder_font_renderer::GlyphKey::new(glyph_key.index(),
|
||||
pathfinder_subpixel_offset);
|
||||
|
||||
// TODO(pcwalton): Fall back to CPU rendering if Pathfinder fails to collect the outline.
|
||||
let mut mesh = PathfinderMesh::new();
|
||||
let outline = font_context.glyph_outline(&pathfinder_font_instance,
|
||||
&pathfinder_glyph_key)?;
|
||||
let tolerance = CUBIC_TO_QUADRATIC_APPROX_TOLERANCE;
|
||||
mesh.push_stencil_segments(CubicToQuadraticTransformer::new(outline.iter(), tolerance));
|
||||
mesh.push_stencil_normals(CubicToQuadraticTransformer::new(outline.iter(), tolerance));
|
||||
|
||||
// FIXME(pcwalton): Support vertical subpixel offsets.
|
||||
// FIXME(pcwalton): Embolden amount should be 0 on macOS if "Use LCD font
|
||||
// smoothing" is unchecked in System Preferences.
|
||||
|
||||
let subpixel_offset = TypedPoint2D::new(glyph_subpixel_offset as f32, 0.0);
|
||||
let embolden_amount = compute_embolden_amount(size.to_f32_px());
|
||||
|
||||
let location = RenderTaskLocation::Dynamic(None, *glyph_size);
|
||||
let glyph_render_task = RenderTask::new_glyph(
|
||||
location.clone(),
|
||||
mesh,
|
||||
&glyph_origin,
|
||||
&subpixel_offset,
|
||||
font.render_mode,
|
||||
&embolden_amount,
|
||||
);
|
||||
|
||||
Ok(render_tasks.add(glyph_render_task))
|
||||
}
|
||||
|
||||
pub struct NativeFontHandleWrapper<'a>(pub &'a NativeFontHandle);
|
|
@ -1,297 +0,0 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
//! GPU glyph rasterization using Pathfinder.
|
||||
|
||||
use crate::api::{ImageFormat, FontRenderMode, TextureTarget};
|
||||
use crate::api::units::*;
|
||||
use crate::debug_colors;
|
||||
use crate::device::{DrawTarget, Device, Texture, TextureFilter, VAO};
|
||||
use euclid::{Point2D, Size2D, Transform3D, TypedVector2D, Vector2D};
|
||||
use crate::internal_types::RenderTargetInfo;
|
||||
use pathfinder_gfx_utils::ShelfBinPacker;
|
||||
use crate::profiler::GpuProfileTag;
|
||||
use crate::renderer::{self, ImageBufferKind, Renderer, RendererError, RendererStats};
|
||||
use crate::renderer::{TextureSampler, VertexArrayKind, ShaderPrecacheFlags};
|
||||
use crate::shade::{LazilyCompiledShader, ShaderKind};
|
||||
use crate::tiling::GlyphJob;
|
||||
|
||||
// The area lookup table in uncompressed grayscale TGA format (TGA image format 3).
|
||||
static AREA_LUT_TGA_BYTES: &'static [u8] = include_bytes!("../res/area-lut.tga");
|
||||
|
||||
const HORIZONTAL_BIN_PADDING: i32 = 3;
|
||||
|
||||
const GPU_TAG_GLYPH_STENCIL: GpuProfileTag = GpuProfileTag {
|
||||
label: "Glyph Stencil",
|
||||
color: debug_colors::STEELBLUE,
|
||||
};
|
||||
const GPU_TAG_GLYPH_COVER: GpuProfileTag = GpuProfileTag {
|
||||
label: "Glyph Cover",
|
||||
color: debug_colors::LIGHTSTEELBLUE,
|
||||
};
|
||||
|
||||
pub struct GpuGlyphRenderer {
|
||||
pub area_lut_texture: Texture,
|
||||
pub vector_stencil_vao: VAO,
|
||||
pub vector_cover_vao: VAO,
|
||||
|
||||
// These are Pathfinder shaders, used for rendering vector graphics.
|
||||
vector_stencil: LazilyCompiledShader,
|
||||
vector_cover: LazilyCompiledShader,
|
||||
}
|
||||
|
||||
impl GpuGlyphRenderer {
|
||||
pub fn new(device: &mut Device, prim_vao: &VAO, precache_flags: ShaderPrecacheFlags)
|
||||
-> Result<GpuGlyphRenderer, RendererError> {
|
||||
// Make sure the area LUT is uncompressed grayscale TGA, 8bpp.
|
||||
debug_assert!(AREA_LUT_TGA_BYTES[2] == 3);
|
||||
debug_assert!(AREA_LUT_TGA_BYTES[16] == 8);
|
||||
let area_lut_width = (AREA_LUT_TGA_BYTES[12] as u32) |
|
||||
((AREA_LUT_TGA_BYTES[13] as u32) << 8);
|
||||
let area_lut_height = (AREA_LUT_TGA_BYTES[14] as u32) |
|
||||
((AREA_LUT_TGA_BYTES[15] as u32) << 8);
|
||||
let area_lut_pixels =
|
||||
&AREA_LUT_TGA_BYTES[18..(18 + area_lut_width * area_lut_height) as usize];
|
||||
|
||||
let area_lut_texture = device.create_texture(
|
||||
TextureTarget::Default,
|
||||
ImageFormat::R8,
|
||||
area_lut_width as i32,
|
||||
area_lut_height as i32,
|
||||
TextureFilter::Linear,
|
||||
None,
|
||||
1,
|
||||
);
|
||||
device.upload_texture_immediate(&area_lut_texture, area_lut_pixels);
|
||||
|
||||
let vector_stencil_vao =
|
||||
device.create_vao_with_new_instances(&renderer::desc::VECTOR_STENCIL, prim_vao);
|
||||
let vector_cover_vao = device.create_vao_with_new_instances(&renderer::desc::VECTOR_COVER,
|
||||
prim_vao);
|
||||
|
||||
// Load Pathfinder vector graphics shaders.
|
||||
let vector_stencil =
|
||||
LazilyCompiledShader::new(ShaderKind::VectorStencil,
|
||||
"pf_vector_stencil",
|
||||
&[ImageBufferKind::Texture2D.get_feature_string()],
|
||||
device,
|
||||
precache_flags)?;
|
||||
let vector_cover =
|
||||
LazilyCompiledShader::new(ShaderKind::VectorCover,
|
||||
"pf_vector_cover",
|
||||
&[ImageBufferKind::Texture2D.get_feature_string()],
|
||||
device,
|
||||
precache_flags)?;
|
||||
Ok(GpuGlyphRenderer {
|
||||
area_lut_texture,
|
||||
vector_stencil_vao,
|
||||
vector_cover_vao,
|
||||
vector_stencil,
|
||||
vector_cover,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Renderer {
|
||||
/// Renders glyphs using the vector graphics shaders (Pathfinder).
|
||||
pub fn stencil_glyphs(&mut self,
|
||||
glyphs: &[GlyphJob],
|
||||
projection: &Transform3D<f32>,
|
||||
target_size: &DeviceIntSize,
|
||||
stats: &mut RendererStats)
|
||||
-> Option<StenciledGlyphPage> {
|
||||
if glyphs.is_empty() {
|
||||
return None
|
||||
}
|
||||
|
||||
let _timer = self.gpu_profile.start_timer(GPU_TAG_GLYPH_STENCIL);
|
||||
|
||||
let texture = self.device.create_texture(
|
||||
TextureTarget::Default,
|
||||
ImageFormat::RGBAF32,
|
||||
target_size.width,
|
||||
target_size.height,
|
||||
TextureFilter::Nearest,
|
||||
Some(RenderTargetInfo {
|
||||
has_depth: false,
|
||||
}),
|
||||
1,
|
||||
);
|
||||
|
||||
// Initialize temporary framebuffer.
|
||||
// FIXME(pcwalton): Cache this!
|
||||
// FIXME(pcwalton): Use RF32, not RGBAF32!
|
||||
let mut current_page = StenciledGlyphPage {
|
||||
texture,
|
||||
glyphs: vec![],
|
||||
};
|
||||
|
||||
// Allocate all target rects.
|
||||
let mut packer = ShelfBinPacker::new(&target_size.to_i32().to_untyped(),
|
||||
&Vector2D::new(HORIZONTAL_BIN_PADDING, 0));
|
||||
let mut glyph_indices: Vec<_> = (0..(glyphs.len())).collect();
|
||||
glyph_indices.sort_by(|&a, &b| {
|
||||
glyphs[b].target_rect.size.height.cmp(&glyphs[a].target_rect.size.height)
|
||||
});
|
||||
for &glyph_index in &glyph_indices {
|
||||
let glyph = &glyphs[glyph_index];
|
||||
let x_scale = x_scale_for_render_mode(glyph.render_mode);
|
||||
let stencil_size = Size2D::new(glyph.target_rect.size.width * x_scale,
|
||||
glyph.target_rect.size.height);
|
||||
match packer.add(&stencil_size) {
|
||||
Err(_) => return None,
|
||||
Ok(origin) => {
|
||||
current_page.glyphs.push(VectorCoverInstanceAttrs {
|
||||
target_rect: glyph.target_rect,
|
||||
stencil_origin: DeviceIntPoint::from_untyped(&origin),
|
||||
subpixel: (glyph.render_mode == FontRenderMode::Subpixel) as u16,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize path info.
|
||||
|
||||
let mut path_info_texels = Vec::with_capacity(glyphs.len() * 12);
|
||||
for (stenciled_glyph_index, &glyph_index) in glyph_indices.iter().enumerate() {
|
||||
let glyph = &glyphs[glyph_index];
|
||||
let stenciled_glyph = ¤t_page.glyphs[stenciled_glyph_index];
|
||||
let x_scale = x_scale_for_render_mode(glyph.render_mode) as f32;
|
||||
let glyph_origin = TypedVector2D::new(-glyph.origin.x as f32 * x_scale,
|
||||
-glyph.origin.y as f32);
|
||||
let subpixel_offset = TypedVector2D::new(glyph.subpixel_offset.x * x_scale,
|
||||
glyph.subpixel_offset.y);
|
||||
let rect = stenciled_glyph.stencil_rect()
|
||||
.to_f32()
|
||||
.translate(&glyph_origin)
|
||||
.translate(&subpixel_offset);
|
||||
path_info_texels.extend_from_slice(&[
|
||||
x_scale, 0.0, 0.0, -1.0,
|
||||
rect.origin.x, rect.max_y(), 0.0, 0.0,
|
||||
rect.size.width, rect.size.height,
|
||||
glyph.embolden_amount.x,
|
||||
glyph.embolden_amount.y,
|
||||
]);
|
||||
}
|
||||
|
||||
// TODO(pcwalton): Cache this texture!
|
||||
let path_info_texture = self.device.create_texture(
|
||||
TextureTarget::Default,
|
||||
ImageFormat::RGBAF32,
|
||||
3,
|
||||
glyphs.len() as i32,
|
||||
TextureFilter::Nearest,
|
||||
None,
|
||||
1,
|
||||
);
|
||||
self.device.upload_texture_immediate(&path_info_texture, &path_info_texels);
|
||||
|
||||
self.gpu_glyph_renderer.vector_stencil.bind(&mut self.device,
|
||||
projection,
|
||||
&mut self.renderer_errors);
|
||||
|
||||
self.device.bind_draw_target(DrawTarget::from_texture(
|
||||
¤t_page.texture,
|
||||
0,
|
||||
false,
|
||||
));
|
||||
self.device.clear_target(Some([0.0, 0.0, 0.0, 0.0]), None, None);
|
||||
|
||||
self.device.set_blend(true);
|
||||
self.device.set_blend_mode_subpixel_pass1();
|
||||
|
||||
let mut instance_data = vec![];
|
||||
for (path_id, &glyph_id) in glyph_indices.iter().enumerate() {
|
||||
let glyph = &glyphs[glyph_id];
|
||||
instance_data.extend(glyph.mesh
|
||||
.stencil_segments
|
||||
.iter()
|
||||
.zip(glyph.mesh.stencil_normals.iter())
|
||||
.map(|(segment, normals)| {
|
||||
VectorStencilInstanceAttrs {
|
||||
from_position: segment.from,
|
||||
ctrl_position: segment.ctrl,
|
||||
to_position: segment.to,
|
||||
from_normal: normals.from,
|
||||
ctrl_normal: normals.ctrl,
|
||||
to_normal: normals.to,
|
||||
path_id: path_id as u16,
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
self.device.bind_texture(TextureSampler::color(0),
|
||||
&self.gpu_glyph_renderer.area_lut_texture);
|
||||
self.device.bind_texture(TextureSampler::color(1), &path_info_texture);
|
||||
self.draw_instanced_batch_with_previously_bound_textures(&instance_data,
|
||||
VertexArrayKind::VectorStencil,
|
||||
stats);
|
||||
|
||||
self.device.delete_texture(path_info_texture);
|
||||
|
||||
Some(current_page)
|
||||
}
|
||||
|
||||
/// Blits glyphs from the stencil texture to the texture cache.
|
||||
///
|
||||
/// Deletes the stencil texture at the end.
|
||||
/// FIXME(pcwalton): This is bad. Cache it somehow.
|
||||
pub fn cover_glyphs(&mut self,
|
||||
stencil_page: StenciledGlyphPage,
|
||||
projection: &Transform3D<f32>,
|
||||
stats: &mut RendererStats) {
|
||||
debug_assert!(!stencil_page.glyphs.is_empty());
|
||||
|
||||
let _timer = self.gpu_profile.start_timer(GPU_TAG_GLYPH_COVER);
|
||||
|
||||
self.gpu_glyph_renderer.vector_cover.bind(&mut self.device,
|
||||
projection,
|
||||
&mut self.renderer_errors);
|
||||
|
||||
self.device.bind_texture(TextureSampler::color(0), &stencil_page.texture);
|
||||
self.draw_instanced_batch_with_previously_bound_textures(&stencil_page.glyphs,
|
||||
VertexArrayKind::VectorCover,
|
||||
stats);
|
||||
|
||||
self.device.delete_texture(stencil_page.texture);
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
#[repr(C)]
|
||||
struct VectorStencilInstanceAttrs {
|
||||
from_position: Point2D<f32>,
|
||||
ctrl_position: Point2D<f32>,
|
||||
to_position: Point2D<f32>,
|
||||
from_normal: Vector2D<f32>,
|
||||
ctrl_normal: Vector2D<f32>,
|
||||
to_normal: Vector2D<f32>,
|
||||
path_id: u16,
|
||||
}
|
||||
|
||||
pub struct StenciledGlyphPage {
|
||||
texture: Texture,
|
||||
glyphs: Vec<VectorCoverInstanceAttrs>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
#[repr(C)]
|
||||
struct VectorCoverInstanceAttrs {
|
||||
target_rect: DeviceIntRect,
|
||||
stencil_origin: DeviceIntPoint,
|
||||
subpixel: u16,
|
||||
}
|
||||
|
||||
impl VectorCoverInstanceAttrs {
|
||||
fn stencil_rect(&self) -> DeviceIntRect {
|
||||
DeviceIntRect::new(self.stencil_origin, self.target_rect.size)
|
||||
}
|
||||
}
|
||||
|
||||
fn x_scale_for_render_mode(render_mode: FontRenderMode) -> i32 {
|
||||
match render_mode {
|
||||
FontRenderMode::Subpixel => 3,
|
||||
FontRenderMode::Mono | FontRenderMode::Alpha => 1,
|
||||
}
|
||||
}
|
|
@ -98,8 +98,6 @@ mod gamma_lut;
|
|||
mod glyph_cache;
|
||||
mod glyph_rasterizer;
|
||||
mod gpu_cache;
|
||||
#[cfg(feature = "pathfinder")]
|
||||
mod gpu_glyph_renderer;
|
||||
mod gpu_types;
|
||||
mod hit_test;
|
||||
mod image;
|
||||
|
@ -174,14 +172,6 @@ pub extern crate euclid;
|
|||
extern crate fxhash;
|
||||
extern crate gleam;
|
||||
extern crate num_traits;
|
||||
#[cfg(feature = "pathfinder")]
|
||||
extern crate pathfinder_font_renderer;
|
||||
#[cfg(feature = "pathfinder")]
|
||||
extern crate pathfinder_gfx_utils;
|
||||
#[cfg(feature = "pathfinder")]
|
||||
extern crate pathfinder_partitioner;
|
||||
#[cfg(feature = "pathfinder")]
|
||||
extern crate pathfinder_path_utils;
|
||||
extern crate plane_split;
|
||||
extern crate rayon;
|
||||
#[cfg(feature = "ron")]
|
||||
|
|
|
@ -14,12 +14,10 @@ use core_graphics::base::{kCGImageAlphaNoneSkipFirst, kCGImageAlphaPremultiplied
|
|||
use core_graphics::base::{kCGBitmapByteOrder32Little};
|
||||
use core_graphics::color_space::CGColorSpace;
|
||||
use core_graphics::context::CGContext;
|
||||
#[cfg(not(feature = "pathfinder"))]
|
||||
use core_graphics::context::{CGBlendMode, CGTextDrawingMode};
|
||||
use core_graphics::data_provider::CGDataProvider;
|
||||
use core_graphics::font::{CGFont, CGGlyph};
|
||||
use core_graphics::geometry::{CGAffineTransform, CGPoint, CGSize};
|
||||
#[cfg(not(feature = "pathfinder"))]
|
||||
use core_graphics::geometry::{CG_AFFINE_TRANSFORM_IDENTITY, CGRect};
|
||||
use core_text;
|
||||
use core_text::font::{CTFont, CTFontRef};
|
||||
|
@ -27,9 +25,6 @@ use core_text::font_descriptor::{kCTFontDefaultOrientation, kCTFontColorGlyphsTr
|
|||
use euclid::Size2D;
|
||||
use crate::gamma_lut::{ColorLut, GammaLut};
|
||||
use crate::glyph_rasterizer::{FontInstance, FontTransform, GlyphKey};
|
||||
#[cfg(feature = "pathfinder")]
|
||||
use crate::glyph_rasterizer::NativeFontHandleWrapper;
|
||||
#[cfg(not(feature = "pathfinder"))]
|
||||
use crate::glyph_rasterizer::{GlyphFormat, GlyphRasterError, GlyphRasterResult, RasterizedGlyph};
|
||||
use crate::internal_types::{FastHashMap, ResourceCacheError};
|
||||
use std::collections::hash_map::Entry;
|
||||
|
@ -427,7 +422,6 @@ impl FontContext {
|
|||
}
|
||||
|
||||
// Assumes the pixels here are linear values from CG
|
||||
#[cfg(not(feature = "pathfinder"))]
|
||||
fn gamma_correct_pixels(
|
||||
&self,
|
||||
pixels: &mut Vec<u8>,
|
||||
|
@ -495,7 +489,6 @@ impl FontContext {
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "pathfinder"))]
|
||||
pub fn rasterize_glyph(&mut self, font: &FontInstance, key: &GlyphKey) -> GlyphRasterResult {
|
||||
let (x_scale, y_scale) = font.transform.compute_scale().unwrap_or((1.0, 1.0));
|
||||
let scale = font.oversized_scale_factor(x_scale, y_scale);
|
||||
|
@ -738,13 +731,6 @@ impl FontContext {
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "pathfinder")]
|
||||
impl<'a> Into<CGFont> for NativeFontHandleWrapper<'a> {
|
||||
fn into(self) -> CGFont {
|
||||
(self.0).0.clone()
|
||||
}
|
||||
}
|
||||
|
||||
// Avoids taking locks by recycling Core Graphics contexts.
|
||||
#[allow(dead_code)]
|
||||
struct GraphicsContext {
|
||||
|
|
|
@ -22,14 +22,10 @@ use freetype::freetype::{FT_FACE_FLAG_MULTIPLE_MASTERS};
|
|||
use freetype::succeeded;
|
||||
use crate::glyph_rasterizer::{FontInstance, GlyphFormat, GlyphKey};
|
||||
use crate::glyph_rasterizer::{GlyphRasterError, GlyphRasterResult, RasterizedGlyph};
|
||||
#[cfg(feature = "pathfinder")]
|
||||
use crate::glyph_rasterizer::NativeFontHandleWrapper;
|
||||
use crate::internal_types::{FastHashMap, ResourceCacheError};
|
||||
#[cfg(any(not(target_os = "android"), feature = "no_static_freetype"))]
|
||||
use libc::{dlsym, RTLD_DEFAULT};
|
||||
use libc::free;
|
||||
#[cfg(feature = "pathfinder")]
|
||||
use pathfinder_font_renderer::freetype as pf_freetype;
|
||||
use std::{cmp, mem, ptr, slice};
|
||||
use std::cmp::max;
|
||||
use std::collections::hash_map::Entry;
|
||||
|
@ -770,7 +766,6 @@ impl FontContext {
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "pathfinder"))]
|
||||
pub fn rasterize_glyph(&mut self, font: &FontInstance, key: &GlyphKey) -> GlyphRasterResult {
|
||||
let (slot, scale) = self.load_glyph(font, key).ok_or(GlyphRasterError::LoadFailed)?;
|
||||
|
||||
|
@ -977,12 +972,3 @@ impl Drop for FontContext {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "pathfinder")]
|
||||
impl<'a> Into<pf_freetype::FontDescriptor> for NativeFontHandleWrapper<'a> {
|
||||
fn into(self) -> pf_freetype::FontDescriptor {
|
||||
let NativeFontHandleWrapper(font_handle) = self;
|
||||
let str = font_handle.path.as_os_str().to_str().unwrap();
|
||||
pf_freetype::FontDescriptor::new(str.into(), font_handle.index)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,23 +8,15 @@ use dwrote;
|
|||
use crate::gamma_lut::ColorLut;
|
||||
use crate::glyph_rasterizer::{FontInstance, FontTransform, GlyphKey};
|
||||
use crate::internal_types::{FastHashMap, FastHashSet, ResourceCacheError};
|
||||
use crate::glyph_rasterizer::{GlyphFormat, GlyphRasterError, GlyphRasterResult, RasterizedGlyph};
|
||||
use crate::gamma_lut::GammaLut;
|
||||
use std::borrow::Borrow;
|
||||
use std::collections::hash_map::Entry;
|
||||
use std::hash::{Hash, Hasher};
|
||||
use std::path::Path;
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
cfg_if! {
|
||||
if #[cfg(feature = "pathfinder")] {
|
||||
use pathfinder_font_renderer::{PathfinderComPtr, IDWriteFontFace};
|
||||
use crate::glyph_rasterizer::NativeFontHandleWrapper;
|
||||
} else if #[cfg(not(feature = "pathfinder"))] {
|
||||
use api::FontInstancePlatformOptions;
|
||||
use crate::glyph_rasterizer::{GlyphFormat, GlyphRasterError, GlyphRasterResult, RasterizedGlyph};
|
||||
use crate::gamma_lut::GammaLut;
|
||||
use std::mem;
|
||||
}
|
||||
}
|
||||
use api::FontInstancePlatformOptions;
|
||||
use std::mem;
|
||||
|
||||
lazy_static! {
|
||||
static ref DEFAULT_FONT_DESCRIPTOR: dwrote::FontDescriptor = dwrote::FontDescriptor {
|
||||
|
@ -80,7 +72,6 @@ struct FontFace {
|
|||
pub struct FontContext {
|
||||
fonts: FastHashMap<FontKey, FontFace>,
|
||||
variations: FastHashMap<(FontKey, dwrote::DWRITE_FONT_SIMULATIONS, Vec<FontVariation>), dwrote::FontFace>,
|
||||
#[cfg(not(feature = "pathfinder"))]
|
||||
gamma_luts: FastHashMap<(u16, u16), GammaLut>,
|
||||
}
|
||||
|
||||
|
@ -148,7 +139,6 @@ impl FontContext {
|
|||
Ok(FontContext {
|
||||
fonts: FastHashMap::default(),
|
||||
variations: FastHashMap::default(),
|
||||
#[cfg(not(feature = "pathfinder"))]
|
||||
gamma_luts: FastHashMap::default(),
|
||||
})
|
||||
}
|
||||
|
@ -442,7 +432,6 @@ impl FontContext {
|
|||
}
|
||||
|
||||
// DWrite ClearType gives us values in RGB, but WR expects BGRA.
|
||||
#[cfg(not(feature = "pathfinder"))]
|
||||
fn convert_to_bgra(
|
||||
&self,
|
||||
pixels: &[u8],
|
||||
|
@ -511,7 +500,6 @@ impl FontContext {
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "pathfinder"))]
|
||||
pub fn rasterize_glyph(&mut self, font: &FontInstance, key: &GlyphKey) -> GlyphRasterResult {
|
||||
let (x_scale, y_scale) = font.transform.compute_scale().unwrap_or((1.0, 1.0));
|
||||
let scale = font.oversized_scale_factor(x_scale, y_scale);
|
||||
|
@ -605,16 +593,3 @@ impl FontContext {
|
|||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "pathfinder")]
|
||||
impl<'a> From<NativeFontHandleWrapper<'a>> for PathfinderComPtr<IDWriteFontFace> {
|
||||
fn from(font_handle: NativeFontHandleWrapper<'a>) -> Self {
|
||||
if let Some(file) = dwrote::FontFile::new_from_path(&font_handle.0.path) {
|
||||
let index = font_handle.0.index;
|
||||
if let Ok(face) = file.create_face(index, dwrote::DWRITE_FONT_SIMULATIONS_NONE) {
|
||||
return unsafe { PathfinderComPtr::new(face.as_ptr()) };
|
||||
}
|
||||
}
|
||||
panic!("missing font {:?}", font_handle.0)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,24 +5,17 @@
|
|||
use api::{ImageDescriptor, ImageFormat, FilterPrimitive, FilterPrimitiveInput, FilterPrimitiveKind};
|
||||
use api::{LineStyle, LineOrientation, ClipMode, DirtyRect, MixBlendMode, ColorF, ColorSpace};
|
||||
use api::units::*;
|
||||
#[cfg(feature = "pathfinder")]
|
||||
use api::FontRenderMode;
|
||||
use crate::border::BorderSegmentCacheKey;
|
||||
use crate::box_shadow::{BoxShadowCacheKey};
|
||||
use crate::clip::{ClipDataStore, ClipItem, ClipStore, ClipNodeRange, ClipNodeFlags};
|
||||
use crate::clip_scroll_tree::SpatialNodeIndex;
|
||||
use crate::device::TextureFilter;
|
||||
#[cfg(feature = "pathfinder")]
|
||||
use euclid::{TypedPoint2D, TypedVector2D};
|
||||
use crate::filterdata::SFilterData;
|
||||
use crate::frame_builder::FrameBuilderConfig;
|
||||
use crate::freelist::{FreeList, FreeListHandle, WeakFreeListHandle};
|
||||
use crate::glyph_rasterizer::GpuGlyphCacheKey;
|
||||
use crate::gpu_cache::{GpuCache, GpuCacheAddress, GpuCacheHandle};
|
||||
use crate::gpu_types::{BorderInstance, ImageSource, UvRectKind, SnapOffsets};
|
||||
use crate::internal_types::{CacheTextureId, FastHashMap, LayerIndex, SavedTargetIndex, TextureSource};
|
||||
#[cfg(feature = "pathfinder")]
|
||||
use pathfinder_partitioner::mesh::Mesh;
|
||||
use crate::prim_store::{PictureIndex, PrimitiveVisibilityMask};
|
||||
use crate::prim_store::image::ImageCacheKey;
|
||||
use crate::prim_store::gradient::{GRADIENT_FP_STOPS, GradientCacheKey, GradientStopKey};
|
||||
|
@ -549,25 +542,6 @@ pub struct ScalingTask {
|
|||
uv_rect_kind: UvRectKind,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[cfg(feature = "pathfinder")]
|
||||
#[cfg_attr(feature = "capture", derive(Serialize))]
|
||||
#[cfg_attr(feature = "replay", derive(Deserialize))]
|
||||
pub struct GlyphTask {
|
||||
/// After job building, this becomes `None`.
|
||||
pub mesh: Option<Mesh>,
|
||||
pub origin: DeviceIntPoint,
|
||||
pub subpixel_offset: TypedPoint2D<f32, DevicePixel>,
|
||||
pub render_mode: FontRenderMode,
|
||||
pub embolden_amount: TypedVector2D<f32, DevicePixel>,
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "pathfinder"))]
|
||||
#[derive(Debug)]
|
||||
#[cfg_attr(feature = "capture", derive(Serialize))]
|
||||
#[cfg_attr(feature = "replay", derive(Deserialize))]
|
||||
pub struct GlyphTask;
|
||||
|
||||
// Where the source data for a blit task can be found.
|
||||
#[derive(Debug)]
|
||||
#[cfg_attr(feature = "capture", derive(Serialize))]
|
||||
|
@ -658,8 +632,6 @@ pub enum RenderTaskKind {
|
|||
ClipRegion(ClipRegionTask),
|
||||
VerticalBlur(BlurTask),
|
||||
HorizontalBlur(BlurTask),
|
||||
#[allow(dead_code)]
|
||||
Glyph(GlyphTask),
|
||||
Readback(DeviceIntRect),
|
||||
Scaling(ScalingTask),
|
||||
Blit(BlitTask),
|
||||
|
@ -679,7 +651,6 @@ impl RenderTaskKind {
|
|||
RenderTaskKind::ClipRegion(..) => "ClipRegion",
|
||||
RenderTaskKind::VerticalBlur(..) => "VerticalBlur",
|
||||
RenderTaskKind::HorizontalBlur(..) => "HorizontalBlur",
|
||||
RenderTaskKind::Glyph(..) => "Glyph",
|
||||
RenderTaskKind::Readback(..) => "Readback",
|
||||
RenderTaskKind::Scaling(..) => "Scaling",
|
||||
RenderTaskKind::Blit(..) => "Blit",
|
||||
|
@ -1481,30 +1452,6 @@ impl RenderTask {
|
|||
)
|
||||
}
|
||||
|
||||
#[cfg(feature = "pathfinder")]
|
||||
pub fn new_glyph(
|
||||
location: RenderTaskLocation,
|
||||
mesh: Mesh,
|
||||
origin: &DeviceIntPoint,
|
||||
subpixel_offset: &TypedPoint2D<f32, DevicePixel>,
|
||||
render_mode: FontRenderMode,
|
||||
embolden_amount: &TypedVector2D<f32, DevicePixel>,
|
||||
) -> Self {
|
||||
RenderTask {
|
||||
children: vec![],
|
||||
location,
|
||||
kind: RenderTaskKind::Glyph(GlyphTask {
|
||||
mesh: Some(mesh),
|
||||
origin: *origin,
|
||||
subpixel_offset: *subpixel_offset,
|
||||
render_mode,
|
||||
embolden_amount: *embolden_amount,
|
||||
}),
|
||||
clear_mode: ClearMode::Transparent,
|
||||
saved_index: None,
|
||||
}
|
||||
}
|
||||
|
||||
fn uv_rect_kind(&self) -> UvRectKind {
|
||||
match self.kind {
|
||||
RenderTaskKind::CacheMask(..) |
|
||||
|
@ -1530,7 +1477,6 @@ impl RenderTask {
|
|||
}
|
||||
|
||||
RenderTaskKind::ClipRegion(..) |
|
||||
RenderTaskKind::Glyph(_) |
|
||||
RenderTaskKind::Border(..) |
|
||||
RenderTaskKind::Gradient(..) |
|
||||
RenderTaskKind::LineDecoration(..) |
|
||||
|
@ -1588,9 +1534,6 @@ impl RenderTask {
|
|||
0.0,
|
||||
]
|
||||
}
|
||||
RenderTaskKind::Glyph(_) => {
|
||||
[0.0, 1.0, 0.0]
|
||||
}
|
||||
RenderTaskKind::Readback(..) |
|
||||
RenderTaskKind::Scaling(..) |
|
||||
RenderTaskKind::Border(..) |
|
||||
|
@ -1656,8 +1599,7 @@ impl RenderTask {
|
|||
RenderTaskKind::Border(..) |
|
||||
RenderTaskKind::CacheMask(..) |
|
||||
RenderTaskKind::Gradient(..) |
|
||||
RenderTaskKind::LineDecoration(..) |
|
||||
RenderTaskKind::Glyph(..) => {
|
||||
RenderTaskKind::LineDecoration(..) => {
|
||||
panic!("texture handle not supported for this task kind");
|
||||
}
|
||||
#[cfg(test)]
|
||||
|
@ -1720,7 +1662,6 @@ impl RenderTask {
|
|||
match self.kind {
|
||||
RenderTaskKind::LineDecoration(..) |
|
||||
RenderTaskKind::Readback(..) |
|
||||
RenderTaskKind::Glyph(..) |
|
||||
RenderTaskKind::Border(..) |
|
||||
RenderTaskKind::Gradient(..) |
|
||||
RenderTaskKind::Picture(..) |
|
||||
|
@ -1772,8 +1713,7 @@ impl RenderTask {
|
|||
RenderTaskKind::Border(..) |
|
||||
RenderTaskKind::CacheMask(..) |
|
||||
RenderTaskKind::Gradient(..) |
|
||||
RenderTaskKind::LineDecoration(..) |
|
||||
RenderTaskKind::Glyph(..) => {
|
||||
RenderTaskKind::LineDecoration(..) => {
|
||||
return;
|
||||
}
|
||||
#[cfg(test)]
|
||||
|
@ -1862,9 +1802,6 @@ impl RenderTask {
|
|||
pt.new_level("Blit".to_owned());
|
||||
pt.add_item(format!("source: {:?}", task.source));
|
||||
}
|
||||
RenderTaskKind::Glyph(..) => {
|
||||
pt.new_level("Glyph".to_owned());
|
||||
}
|
||||
RenderTaskKind::Gradient(..) => {
|
||||
pt.new_level("Gradient".to_owned());
|
||||
}
|
||||
|
@ -1912,8 +1849,6 @@ impl RenderTask {
|
|||
pub enum RenderTaskCacheKeyKind {
|
||||
BoxShadow(BoxShadowCacheKey),
|
||||
Image(ImageCacheKey),
|
||||
#[allow(dead_code)]
|
||||
Glyph(GpuGlyphCacheKey),
|
||||
BorderSegment(BorderSegmentCacheKey),
|
||||
LineDecoration(LineDecorationCacheKey),
|
||||
Gradient(GradientCacheKey),
|
||||
|
|
|
@ -62,8 +62,6 @@ use crate::glyph_cache::GlyphCache;
|
|||
use crate::glyph_rasterizer::{GlyphFormat, GlyphRasterizer};
|
||||
use crate::gpu_cache::{GpuBlockData, GpuCacheUpdate, GpuCacheUpdateList};
|
||||
use crate::gpu_cache::{GpuCacheDebugChunk, GpuCacheDebugCmd};
|
||||
#[cfg(feature = "pathfinder")]
|
||||
use crate::gpu_glyph_renderer::GpuGlyphRenderer;
|
||||
use crate::gpu_types::{PrimitiveHeaderI, PrimitiveHeaderF, ScalingInstance, SvgFilterInstance, TransformData, ResolveInstanceData};
|
||||
use crate::internal_types::{TextureSource, ORTHO_FAR_PLANE, ORTHO_NEAR_PLANE, ResourceCacheError};
|
||||
use crate::internal_types::{CacheTextureId, DebugOutput, FastHashMap, FastHashSet, LayerIndex, RenderedDocument, ResultMsg};
|
||||
|
@ -107,8 +105,6 @@ use thread_profiler::{register_thread_with_profiler, write_profile};
|
|||
use crate::tiling::{AlphaRenderTarget, ColorRenderTarget, PictureCacheTarget};
|
||||
use crate::tiling::{BlitJob, BlitJobSource, RenderPassKind, RenderTargetList};
|
||||
use crate::tiling::{Frame, RenderTarget, RenderTargetKind, TextureCacheRenderTarget};
|
||||
#[cfg(not(feature = "pathfinder"))]
|
||||
use crate::tiling::GlyphJob;
|
||||
use time::precise_time_ns;
|
||||
|
||||
cfg_if! {
|
||||
|
@ -897,19 +893,6 @@ impl CpuProfile {
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "pathfinder"))]
|
||||
pub struct GpuGlyphRenderer;
|
||||
|
||||
#[cfg(not(feature = "pathfinder"))]
|
||||
impl GpuGlyphRenderer {
|
||||
fn new(_: &mut Device, _: &VAO, _: ShaderPrecacheFlags) -> Result<GpuGlyphRenderer, RendererError> {
|
||||
Ok(GpuGlyphRenderer)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "pathfinder"))]
|
||||
struct StenciledGlyphPage;
|
||||
|
||||
/// A Texture that has been initialized by the `device` module and is ready to
|
||||
/// be used.
|
||||
struct ActiveTexture {
|
||||
|
@ -1683,8 +1666,6 @@ pub struct Renderer {
|
|||
|
||||
shaders: Rc<RefCell<Shaders>>,
|
||||
|
||||
pub gpu_glyph_renderer: GpuGlyphRenderer,
|
||||
|
||||
max_recorded_profiles: usize,
|
||||
|
||||
clear_color: Option<ColorF>,
|
||||
|
@ -1999,10 +1980,6 @@ impl Renderer {
|
|||
device.update_vao_indices(&prim_vao, &quad_indices, VertexUsageHint::Static);
|
||||
device.update_vao_main_vertices(&prim_vao, &quad_vertices, VertexUsageHint::Static);
|
||||
|
||||
let gpu_glyph_renderer = r#try!(GpuGlyphRenderer::new(&mut device,
|
||||
&prim_vao,
|
||||
options.precache_flags));
|
||||
|
||||
let blur_vao = device.create_vao_with_new_instances(&desc::BLUR, &prim_vao);
|
||||
let clip_vao = device.create_vao_with_new_instances(&desc::CLIP, &prim_vao);
|
||||
let border_vao = device.create_vao_with_new_instances(&desc::BORDER, &prim_vao);
|
||||
|
@ -2235,7 +2212,6 @@ impl Renderer {
|
|||
enable_advanced_blend_barriers: !ext_blend_equation_advanced_coherent,
|
||||
last_time: 0,
|
||||
gpu_profile,
|
||||
gpu_glyph_renderer,
|
||||
vaos: RendererVAOs {
|
||||
prim_vao,
|
||||
blur_vao,
|
||||
|
@ -3274,7 +3250,7 @@ impl Renderer {
|
|||
// the batch.
|
||||
debug_assert!(!data.is_empty());
|
||||
|
||||
let vao = get_vao(vertex_array_kind, &self.vaos, &self.gpu_glyph_renderer);
|
||||
let vao = get_vao(vertex_array_kind, &self.vaos);
|
||||
|
||||
self.device.bind_vao(vao);
|
||||
|
||||
|
@ -4112,20 +4088,20 @@ impl Renderer {
|
|||
stats: &mut RendererStats,
|
||||
) {
|
||||
let texture_source = TextureSource::TextureCache(*texture);
|
||||
let (target_size, projection) = {
|
||||
let projection = {
|
||||
let texture = self.texture_resolver
|
||||
.resolve(&texture_source)
|
||||
.expect("BUG: invalid target texture");
|
||||
let target_size = texture.get_dimensions();
|
||||
let projection = Transform3D::ortho(
|
||||
|
||||
Transform3D::ortho(
|
||||
0.0,
|
||||
target_size.width as f32,
|
||||
0.0,
|
||||
target_size.height as f32,
|
||||
ORTHO_NEAR_PLANE,
|
||||
ORTHO_FAR_PLANE,
|
||||
);
|
||||
(target_size, projection)
|
||||
)
|
||||
};
|
||||
|
||||
self.device.disable_depth();
|
||||
|
@ -4133,9 +4109,6 @@ impl Renderer {
|
|||
|
||||
self.set_blend(false, FramebufferKind::Other);
|
||||
|
||||
// Handle any Pathfinder glyphs.
|
||||
let stencil_page = self.stencil_glyphs(&target.glyphs, &projection, &target_size, stats);
|
||||
|
||||
{
|
||||
let texture = self.texture_resolver
|
||||
.resolve(&texture_source)
|
||||
|
@ -4270,29 +4243,8 @@ impl Renderer {
|
|||
stats,
|
||||
);
|
||||
}
|
||||
|
||||
// Blit any Pathfinder glyphs to the cache texture.
|
||||
if let Some(stencil_page) = stencil_page {
|
||||
self.cover_glyphs(stencil_page, &projection, stats);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "pathfinder"))]
|
||||
fn stencil_glyphs(&mut self,
|
||||
_: &[GlyphJob],
|
||||
_: &Transform3D<f32>,
|
||||
_: &DeviceIntSize,
|
||||
_: &mut RendererStats)
|
||||
-> Option<StenciledGlyphPage> {
|
||||
None
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "pathfinder"))]
|
||||
fn cover_glyphs(&mut self,
|
||||
_: StenciledGlyphPage,
|
||||
_: &Transform3D<f32>,
|
||||
_: &mut RendererStats) {}
|
||||
|
||||
fn update_deferred_resolves(&mut self, deferred_resolves: &[DeferredResolve]) -> Option<GpuCacheUpdateList> {
|
||||
// The first thing we do is run through any pending deferred
|
||||
// resolves, and use a callback to get the UV rect for this
|
||||
|
@ -6042,30 +5994,8 @@ impl Renderer {
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "pathfinder")]
|
||||
fn get_vao<'a>(vertex_array_kind: VertexArrayKind,
|
||||
vaos: &'a RendererVAOs,
|
||||
gpu_glyph_renderer: &'a GpuGlyphRenderer)
|
||||
-> &'a VAO {
|
||||
match vertex_array_kind {
|
||||
VertexArrayKind::Primitive => &vaos.prim_vao,
|
||||
VertexArrayKind::Clip => &vaos.clip_vao,
|
||||
VertexArrayKind::Blur => &vaos.blur_vao,
|
||||
VertexArrayKind::VectorStencil => &gpu_glyph_renderer.vector_stencil_vao,
|
||||
VertexArrayKind::VectorCover => &gpu_glyph_renderer.vector_cover_vao,
|
||||
VertexArrayKind::Border => &vaos.border_vao,
|
||||
VertexArrayKind::Scale => &vaos.scale_vao,
|
||||
VertexArrayKind::LineDecoration => &vaos.line_vao,
|
||||
VertexArrayKind::Gradient => &vaos.gradient_vao,
|
||||
VertexArrayKind::Resolve => &vaos.resolve_vao,
|
||||
VertexArrayKind::SvgFilter => &vaos.svg_filter_vao,
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "pathfinder"))]
|
||||
fn get_vao<'a>(vertex_array_kind: VertexArrayKind,
|
||||
vaos: &'a RendererVAOs,
|
||||
_: &'a GpuGlyphRenderer)
|
||||
vaos: &'a RendererVAOs)
|
||||
-> &'a VAO {
|
||||
match vertex_array_kind {
|
||||
VertexArrayKind::Primitive => &vaos.prim_vao,
|
||||
|
|
|
@ -20,7 +20,6 @@ use crate::capture::CaptureConfig;
|
|||
use crate::device::TextureFilter;
|
||||
use euclid::{point2, size2};
|
||||
use crate::glyph_cache::GlyphCache;
|
||||
#[cfg(not(feature = "pathfinder"))]
|
||||
use crate::glyph_cache::GlyphCacheEntry;
|
||||
use crate::glyph_rasterizer::{BaseFontInstance, FontInstance, GlyphFormat, GlyphKey, GlyphRasterizer};
|
||||
use crate::gpu_cache::{GpuCache, GpuCacheAddress, GpuCacheHandle};
|
||||
|
@ -1429,57 +1428,6 @@ impl ResourceCache {
|
|||
self.texture_cache.pending_updates()
|
||||
}
|
||||
|
||||
#[cfg(feature = "pathfinder")]
|
||||
pub fn fetch_glyphs<F>(
|
||||
&self,
|
||||
mut font: FontInstance,
|
||||
glyph_keys: &[GlyphKey],
|
||||
fetch_buffer: &mut Vec<GlyphFetchResult>,
|
||||
gpu_cache: &mut GpuCache,
|
||||
mut f: F,
|
||||
) where
|
||||
F: FnMut(TextureSource, GlyphFormat, &[GlyphFetchResult]),
|
||||
{
|
||||
debug_assert_eq!(self.state, State::QueryResources);
|
||||
|
||||
self.glyph_rasterizer.prepare_font(&mut font);
|
||||
|
||||
let mut current_texture_id = TextureSource::Invalid;
|
||||
let mut current_glyph_format = GlyphFormat::Subpixel;
|
||||
debug_assert!(fetch_buffer.is_empty());
|
||||
|
||||
for (loop_index, key) in glyph_keys.iter().enumerate() {
|
||||
let (cache_item, glyph_format) =
|
||||
match self.glyph_rasterizer.get_cache_item_for_glyph(key,
|
||||
&font,
|
||||
&self.cached_glyphs,
|
||||
&self.texture_cache,
|
||||
&self.cached_render_tasks) {
|
||||
None => continue,
|
||||
Some(result) => result,
|
||||
};
|
||||
if current_texture_id != cache_item.texture_id ||
|
||||
current_glyph_format != glyph_format {
|
||||
if !fetch_buffer.is_empty() {
|
||||
f(current_texture_id, current_glyph_format, fetch_buffer);
|
||||
fetch_buffer.clear();
|
||||
}
|
||||
current_texture_id = cache_item.texture_id;
|
||||
current_glyph_format = glyph_format;
|
||||
}
|
||||
fetch_buffer.push(GlyphFetchResult {
|
||||
index_in_text_run: loop_index as i32,
|
||||
uv_rect_address: gpu_cache.get_address(&cache_item.uv_rect_handle),
|
||||
});
|
||||
}
|
||||
|
||||
if !fetch_buffer.is_empty() {
|
||||
f(current_texture_id, current_glyph_format, fetch_buffer);
|
||||
fetch_buffer.clear();
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "pathfinder"))]
|
||||
pub fn fetch_glyphs<F>(
|
||||
&self,
|
||||
mut font: FontInstance,
|
||||
|
|
|
@ -5,29 +5,23 @@
|
|||
use api::{ColorF, BorderStyle, FilterPrimitive, MixBlendMode, PipelineId, PremultipliedColorF};
|
||||
use api::{DocumentLayer, FilterData, ImageFormat, LineOrientation};
|
||||
use api::units::*;
|
||||
#[cfg(feature = "pathfinder")]
|
||||
use api::FontRenderMode;
|
||||
use crate::batch::{AlphaBatchBuilder, AlphaBatchContainer, BatchTextures, ClipBatcher, resolve_image, BatchBuilder};
|
||||
use crate::clip::ClipStore;
|
||||
use crate::clip_scroll_tree::{ClipScrollTree, ROOT_SPATIAL_NODE_INDEX};
|
||||
use crate::debug_render::DebugItem;
|
||||
use crate::device::{Texture};
|
||||
#[cfg(feature = "pathfinder")]
|
||||
use euclid::{TypedPoint2D, TypedVector2D};
|
||||
use crate::frame_builder::FrameGlobalResources;
|
||||
use crate::gpu_cache::{GpuCache, GpuCacheAddress};
|
||||
use crate::gpu_types::{BorderInstance, SvgFilterInstance, BlurDirection, BlurInstance, PrimitiveHeaders, ScalingInstance};
|
||||
use crate::gpu_types::{TransformData, TransformPalette, ZBufferIdGenerator};
|
||||
use crate::internal_types::{CacheTextureId, FastHashMap, SavedTargetIndex, TextureSource, Filter};
|
||||
#[cfg(feature = "pathfinder")]
|
||||
use pathfinder_partitioner::mesh::Mesh;
|
||||
use crate::picture::{RecordedDirtyRegion, SurfaceInfo};
|
||||
use crate::prim_store::gradient::GRADIENT_FP_STOPS;
|
||||
use crate::prim_store::{PrimitiveStore, DeferredResolve, PrimitiveScratchBuffer, PrimitiveVisibilityMask};
|
||||
use crate::profiler::FrameProfileCounters;
|
||||
use crate::render_backend::{DataStores, FrameId};
|
||||
use crate::render_task::{BlitSource, RenderTaskAddress, RenderTaskId, RenderTaskKind, SvgFilterTask, SvgFilterInfo};
|
||||
use crate::render_task::{BlurTask, ClearMode, GlyphTask, RenderTaskLocation, RenderTaskGraph, ScalingTask};
|
||||
use crate::render_task::{BlurTask, ClearMode, RenderTaskLocation, RenderTaskGraph, ScalingTask};
|
||||
use crate::resource_cache::ResourceCache;
|
||||
use std::{cmp, usize, f32, i32, mem};
|
||||
use crate::texture_allocator::{ArrayAllocationTracker, FreeRectSlice};
|
||||
|
@ -331,23 +325,6 @@ pub struct GradientJob {
|
|||
pub start_stop: [f32; 2],
|
||||
}
|
||||
|
||||
#[cfg(feature = "pathfinder")]
|
||||
#[cfg_attr(feature = "capture", derive(Serialize))]
|
||||
#[cfg_attr(feature = "replay", derive(Deserialize))]
|
||||
pub struct GlyphJob {
|
||||
pub mesh: Mesh,
|
||||
pub target_rect: DeviceIntRect,
|
||||
pub origin: DeviceIntPoint,
|
||||
pub subpixel_offset: TypedPoint2D<f32, DevicePixel>,
|
||||
pub render_mode: FontRenderMode,
|
||||
pub embolden_amount: TypedVector2D<f32, DevicePixel>,
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "pathfinder"))]
|
||||
#[cfg_attr(feature = "capture", derive(Serialize))]
|
||||
#[cfg_attr(feature = "replay", derive(Deserialize))]
|
||||
pub struct GlyphJob;
|
||||
|
||||
/// Contains the work (in the form of instance arrays) needed to fill a color
|
||||
/// color output surface (RGBA8).
|
||||
///
|
||||
|
@ -553,10 +530,6 @@ impl RenderTarget for ColorRenderTarget {
|
|||
RenderTaskKind::LineDecoration(..) => {
|
||||
panic!("Should not be added to color target!");
|
||||
}
|
||||
RenderTaskKind::Glyph(..) => {
|
||||
// FIXME(pcwalton): Support color glyphs.
|
||||
panic!("Glyphs should not be added to color target!");
|
||||
}
|
||||
RenderTaskKind::Readback(device_rect) => {
|
||||
self.readbacks.push(device_rect);
|
||||
}
|
||||
|
@ -699,7 +672,6 @@ impl RenderTarget for AlphaRenderTarget {
|
|||
RenderTaskKind::Border(..) |
|
||||
RenderTaskKind::LineDecoration(..) |
|
||||
RenderTaskKind::Gradient(..) |
|
||||
RenderTaskKind::Glyph(..) |
|
||||
RenderTaskKind::SvgFilter(..) => {
|
||||
panic!("BUG: should not be added to alpha target!");
|
||||
}
|
||||
|
@ -791,7 +763,6 @@ pub struct TextureCacheRenderTarget {
|
|||
pub target_kind: RenderTargetKind,
|
||||
pub horizontal_blurs: Vec<BlurInstance>,
|
||||
pub blits: Vec<BlitJob>,
|
||||
pub glyphs: Vec<GlyphJob>,
|
||||
pub border_segments_complex: Vec<BorderInstance>,
|
||||
pub border_segments_solid: Vec<BorderInstance>,
|
||||
pub clears: Vec<DeviceIntRect>,
|
||||
|
@ -805,7 +776,6 @@ impl TextureCacheRenderTarget {
|
|||
target_kind,
|
||||
horizontal_blurs: vec![],
|
||||
blits: vec![],
|
||||
glyphs: vec![],
|
||||
border_segments_complex: vec![],
|
||||
border_segments_solid: vec![],
|
||||
clears: vec![],
|
||||
|
@ -880,9 +850,6 @@ impl TextureCacheRenderTarget {
|
|||
}
|
||||
}
|
||||
}
|
||||
RenderTaskKind::Glyph(ref mut task_info) => {
|
||||
self.add_glyph_task(task_info, target_rect.0)
|
||||
}
|
||||
RenderTaskKind::Gradient(ref task_info) => {
|
||||
let mut stops = [0.0; 4];
|
||||
let mut colors = [PremultipliedColorF::BLACK; 4];
|
||||
|
@ -918,21 +885,6 @@ impl TextureCacheRenderTarget {
|
|||
RenderTaskKind::Test(..) => {}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "pathfinder")]
|
||||
fn add_glyph_task(&mut self, task_info: &mut GlyphTask, target_rect: DeviceIntRect) {
|
||||
self.glyphs.push(GlyphJob {
|
||||
mesh: task_info.mesh.take().unwrap(),
|
||||
target_rect,
|
||||
origin: task_info.origin,
|
||||
subpixel_offset: task_info.subpixel_offset,
|
||||
render_mode: task_info.render_mode,
|
||||
embolden_amount: task_info.embolden_amount,
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "pathfinder"))]
|
||||
fn add_glyph_task(&mut self, _: &mut GlyphTask, _: DeviceIntRect) {}
|
||||
}
|
||||
|
||||
/// Contains the set of `RenderTarget`s specific to the kind of pass.
|
||||
|
|
|
@ -39,7 +39,6 @@ core-foundation = "0.6"
|
|||
[features]
|
||||
default = [ "env_logger" ]
|
||||
headless = [ "osmesa-sys", "osmesa-src" ]
|
||||
pathfinder = [ "webrender/pathfinder" ]
|
||||
|
||||
[target.'cfg(target_os = "windows")'.dependencies]
|
||||
dwrote = "0.9"
|
||||
|
|
|
@ -60,4 +60,3 @@ powershell.exe 'iex (Get-Content -Raw ci-scripts\set-screenresolution.ps1); Set-
|
|||
export CARGOFLAGS='--verbose --frozen'
|
||||
export FREETYPE_CMAKE_GENERATOR=Ninja
|
||||
cmd.exe /c 'ci-scripts\windows-tests.cmd'
|
||||
cmd.exe /c 'ci-scripts\windows-pathfinder.cmd'
|
||||
|
|
Загрузка…
Ссылка в новой задаче