Skip to content

Commit

Permalink
Add support for bgcolor, fontcolor and frameopacity
Browse files Browse the repository at this point in the history
This changes the default background style.
This also slightly changes the highlighting behavior.
  • Loading branch information
kimikage committed Sep 8, 2020
1 parent f2ead5f commit 28a1e73
Show file tree
Hide file tree
Showing 6 changed files with 224 additions and 17 deletions.
22 changes: 22 additions & 0 deletions docs/src/assets/pattern.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
5 changes: 5 additions & 0 deletions docs/src/assets/profilesvg.css
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,9 @@ article svg {
overflow: auto;
max-width: 100%;
min-width: 500px;
border: 1px solid rgba(192, 192, 192, 0.2);
}

article div.bg {
background: url('pattern.svg');
}
96 changes: 94 additions & 2 deletions docs/src/coloration-schemes.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,99 @@ In the default `StackFrameCategory` scheme, "gray" indicates time spent in
LLVM, "orange" in other `ccalls`, "light blue" in `Base`, and "red" is
uncategorized (mostly package code).

## `bgcolor` and `fontcolor` options
[`ProfileSVG.view`](@ref) and [`ProfileSVG.save`](@ref) have optional keyword
arguments for specifying the SVG styles.

The `bgcolor` option specifies the background color style and the `fontcolor`
option specifies the font color style.

One of the following symbols is available for `bgcolor`:
- `:fcolor`
- `fcolor(:bg)` (default)
- `:classic`
- pale color gradation which comes from the original
[Flame Graphs](http://www.brendangregg.com/flamegraphs.html)
- `:transparent`
- fully transparent

One of the following symbols is available for `fontcolor`:
- `:fcolor`
- `fcolor(:font)` (default)
- `:classic`
- black
- `:currentcolor`
- the [`currentcolor`](https://www.w3.org/TR/css-color-3/#currentcolor) CSS keyword
- `:bw`
- black or white depending on the frame color

!!! info
You cannot specify a `Color` or a color name directly to `bgcolor` or
`fontcolor`. The `colorbg` and `colorfont` options of
`FlameGraphs.FlameColors` and `FlameGraphs.StackFrameCategory` are
available.

### `:fcolor` (default)
```@example ex
using FlameGraphs, Colors
fcolor = FlameColors(colorbg=colorant"steelblue", colorfont=colorant"lightyellow")
ProfileSVG.view(fcolor)
ProfileSVG.view(fcolor, g) # hide
```

### `:classic`
```@example ex
ProfileSVG.view(bgcolor=:classic, fontcolor=:classic)
ProfileSVG.view(g, bgcolor=:classic, fontcolor=:classic) # hide
```

### `:transparent` and `:currentcolor`
```@raw html
<div class="bg">
```
The contextual, i.e. parent, background here has a pattern, and the font color
depends on the theme (light or dark).

```@example ex
ProfileSVG.view(bgcolor=:transparent, fontcolor=:currentcolor)
ProfileSVG.view(g, bgcolor=:transparent, fontcolor=:currentcolor) # hide
```
```@raw html
</div>
```

### `:bw`
```@example ex
modcat(mod) = nothing
loccat(sf) = occursin("#", string(sf.func)) ? colorant"navy" : colorant"powderblue"
mysfc = StackFrameCategory(modcat, loccat, colorant"dimgray", colorant"red")
ProfileSVG.view(mysfc, fontcolor=:bw)
ProfileSVG.view(mysfc, g, fontcolor=:bw) # hide
```

!!! info
The `colorbg` and `colorfont` options of `FlameGraphs.FlameColors` and
`FlameGraphs.StackFrameCategory` are currently not supported and ignored.
The decision whether to use black or white is based on the assumption that
the frames are opaque. If the [`frameopacity` option](@ref) is set to a low
value, the texts may be hard to read.

## `frameopacity` option

The `frameopacity` option specifyies the opacity of frames in [0, 1]. This
option affects only the background of the frame, not the texts or the viewport
background.

```@raw html
<div class="bg">
```
The contextual background here has a pattern.

```@example ex
ProfileSVG.view(frameopacity=0.5, bgcolor=:transparent, fontcolor=:currentcolor)
ProfileSVG.view(g, frameopacity=0.5, bgcolor=:transparent, fontcolor=:currentcolor) # hide
```
```@raw html
</div>
```
36 changes: 34 additions & 2 deletions src/ProfileSVG.jl
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@ struct FGConfig
g::Union{FlameGraph, Nothing}
graph_options::Dict{Symbol, Any}
fcolor
bgcolor::Symbol
fontcolor::Symbol
frameopacity::Float64
maxdepth::Int
maxframes::Int
width::Float64
Expand All @@ -48,6 +51,9 @@ end
function FGConfig(g::Union{FlameGraph, Nothing} = nothing,
graph_options = nothing,
fcolor = default_config.fcolor;
bgcolor::Symbol = default_config.bgcolor,
fontcolor::Symbol = default_config.fontcolor,
frameopacity::Real = default_config.frameopacity,
maxdepth::Int = default_config.maxdepth,
maxframes::Int = default_config.maxframes,
width::Real = default_config.width,
Expand All @@ -60,6 +66,7 @@ function FGConfig(g::Union{FlameGraph, Nothing} = nothing,

gopts = graph_options === nothing ? flamegraph_kwargs(kwargs) : graph_options
FGConfig(g, gopts, fcolor,
bgcolor, fontcolor, frameopacity,
maxdepth, maxframes, width, height, roundradius, font, fontsize, notext)
end

Expand All @@ -73,6 +80,9 @@ function init()
global default_config = FGConfig(nothing,
Dict{Symbol, Any}(),
FlameGraphs.default_colors,
bgcolor=:fcolor,
fontcolor=:fcolor,
frameopacity=1,
maxdepth=50,
maxframes=2000,
width=960,
Expand Down Expand Up @@ -109,6 +119,12 @@ include("svgwriter.jl")
View profiling results.
# keywords for SVG style
- `bgcolor` (default: `:fcolor`)
- The background style. One of `:fcolor`/`:classic`/`:transparent`.
- `fontcolor` (default: `:fcolor`)
- The font color style. One of `:fcolor`/`:classic`/`:currentcolor`/`:bw`.
- `frameopacity` (default: `1`)
- The opacity of frames in [0, 1].
- `maxdepth` (default: `50`)
- The maximum number of the rendered rows.
- `maxframes` (default: `2000`)
Expand Down Expand Up @@ -237,6 +253,7 @@ function Base.show(io::IO, ::MIME"image/svg+xml", fg::FGConfig)
ndata = g.data
sf = ndata.sf
color = fg.fcolor(nextidx, j, ndata)::Color
bw = fg.fontcolor === :bw
x = (first(ndata.span)-1) * xstep + leftmargin
y = height - j * ystep - botmargin
w = length(ndata.span) * xstep
Expand All @@ -251,7 +268,7 @@ function Base.show(io::IO, ::MIME"image/svg+xml", fg::FGConfig)
basename = file
end
shortinfo = "$(sf.func) in $basename:$(sf.line)"
write_svgflamerect(io, x, y, w, ystep, r, shortinfo, dirinfo, color)
write_svgflamerect(io, x, y, w, ystep, r, shortinfo, dirinfo, color, bw)

for c in g
flamerects(io, c, j + 1, nextidx)
Expand All @@ -262,7 +279,9 @@ function Base.show(io::IO, ::MIME"image/svg+xml", fg::FGConfig)

write_svgdeclaration(io)

write_svgheader(io, fig_id, width, height, fg.font, fg.fontsize, fg.notext)
write_svgheader(io, fig_id, width, height,
bgcolor(fg), fontcolor(fg), fg.frameopacity,
fg.font, fg.fontsize, fg.notext)

nextidx = fill(1, nrows + 1) # nextidx[end]: framecount
flamerects(io, fg.g, 1, nextidx)
Expand All @@ -275,6 +294,19 @@ function Base.show(io::IO, ::MIME"image/svg+xml", fg::FGConfig)
write_svgfooter(io, fig_id)
end

function bgcolor(fg)
fg.bgcolor === :fcolor && return "#" * hex(fg.fcolor(:bg))
fg.bgcolor === :transparent && return "transparent"
return ""
end

function fontcolor(fg)
fg.fontcolor === :fcolor && return "#" * hex(fg.fcolor(:font))
fg.fontcolor === :currentcolor && return "currentcolor"
fg.fontcolor === :bw && return ""
return "black"
end

__init__() = init()

end # module
56 changes: 47 additions & 9 deletions src/svgwriter.jl
Original file line number Diff line number Diff line change
Expand Up @@ -23,31 +23,48 @@ function simplify(x::Real, digits=2)
ra == rd ? Int(ra) : rd
end

isdarkcolor(c::Color) = gray(Gray(c)) < 0.65

function write_svgdeclaration(io::IO)
println(io, """<?xml version="1.0" standalone="no"?>""")
println(io, """<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">""")
end

function write_svgheader(io::IO, fig_id, width, height, font, fontsize, notext)
function write_svgheader(io::IO, fig_id, width, height,
bgcolor, fontcolor, frameopacity,
font, fontsize, notext)
w = simplify(width)
h = simplify(height)
caption_size = simplify(fontsize * 1.4)
x_cap = simplify(width * 0.5)
y_cap = simplify(fontsize * 2)
x_msg = simplify(fontsize * 0.8)
y_msg = height - caption_size
fontcolorhex = "#000000" # FIXME
bg_fill = """url(#$fig_id-background)"""
textcolor = isempty(fontcolor) ? "black" : fontcolor
caption_color = textcolor
if isempty(fontcolor) && startswith(bgcolor, "#") && isdarkcolor(parse(Gray, bgcolor))
caption_color = "white"
end
bg_fill = isempty(bgcolor) ? "url(#$fig_id-background)" : bgcolor
details_bg_fill = isempty(bgcolor) ? "white" : bgcolor
text_stroke_opacity = notext ? 0.0 : 0.35
op = simplify(frameopacity)
hover_opacity = simplify(op > 0.75 ? op - 0.25 : op + 0.25)
print(io,
"""
<svg version="1.1" width="$w" height="$h" viewBox="0 0 $w $h"
xmlns="http://www.w3.org/2000/svg" id="$fig_id">
<defs>
""")
isempty(bgcolor) && print(io,
"""
<linearGradient id="$fig_id-background" y1="0" y2="1" x1="0" x2="0">
<stop stop-color="#eeeeee" offset="5%" />
<stop stop-color="#eeeeb0" offset="95%" />
</linearGradient>
""")
print(io,
"""
<clipPath id="$fig_id-clip">
<rect x="0" y="0" width="$w" height="$h"/>
</clipPath>
Expand All @@ -57,27 +74,47 @@ function write_svgheader(io::IO, fig_id, width, height, font, fontsize, notext)
pointer-events: none;
font-family: $font;
font-size: $(simplify(fontsize))px;
fill: $fontcolorhex;
fill: $textcolor;
}
text#$fig_id-caption {
font-size: $(caption_size)px;
fill: $caption_color;
text-anchor: middle;
}
#$fig_id-bg {
fill: $bg_fill;
}
#$fig_id-viewport rect, #$fig_id-viewport path {
vector-effect: non-scaling-stroke;
fill-opacity: $op;
}
#$fig_id-viewport text {
stroke: $fontcolorhex;
stroke: $textcolor;
stroke-width: 0;
stroke-opacity: $text_stroke_opacity;
}
#$fig_id-viewport rect:hover, #$fig_id-viewport path:hover {
stroke: $fontcolorhex;
stroke-width: 1;
fill-opacity: $hover_opacity;
stroke: $textcolor;
stroke-width: 0.5;
}
#$fig_id-viewport + rect {
fill: $details_bg_fill;
opacity: 0.8;
}
text#$fig_id-details{
fill: $caption_color;
}
""")
isempty(fontcolor) && print(io,
"""
#$fig_id-viewport text.w {
fill: white;
stroke: white;
}
""")
print(io,
"""
</style>
<g id="$fig_id-frame" clip-path="url(#$fig_id-clip)">
<rect id="$fig_id-bg" x="0" y="0" width="$w" height="$h"/>
Expand All @@ -87,7 +124,7 @@ function write_svgheader(io::IO, fig_id, width, height, font, fontsize, notext)
end

function write_svgflamerect(io::IO, xstart, ystart, width, height, roundradius,
shortinfo, dirinfo, color)
shortinfo, dirinfo, color, bw)
x = simplify(xstart)
y = simplify(ystart)
yt = simplify(y + height * 0.75)
Expand All @@ -96,13 +133,14 @@ function write_svgflamerect(io::IO, xstart, ystart, width, height, roundradius,
r = simplify(roundradius)
sinfo = escape_html(shortinfo)
dinfo = escape_html(dirinfo)
classw = (bw & isdarkcolor(color)) ? " class=\"w\"" : ""
if r > zero(r)
print(io, """<rect x="$x" y="$y" width="$w" height="$h" rx="$r" """)
else
print(io, """<path d="M$x,$(y)v$(h)h$(w)v-$(h)z" """)
end
println(io, """fill="#$(hex(color))" data-dinfo="$dinfo"/>""")
println(io, """<text x="$x" dx="4" y="$yt">$sinfo</text>""")
println(io, """<text x="$x" dx="4" y="$yt"$classw>$sinfo</text>""")
end

function write_svgfooter(io::IO, fig_id)
Expand Down
Loading

0 comments on commit 28a1e73

Please sign in to comment.