Skip to content

Commit

Permalink
Refactor display code to be Screen centric (MakieOrg#2306)
Browse files Browse the repository at this point in the history
* Only rasterize if the backend is not itself raster

* Define width of CairoScreen

* Make colorbuffer use the size of the screen, not the scene

This allows arbitrary scaling in CairoMakie, remains constant in GLMakie, and probably breaks WGLMakie.

* Add GeometryBasics.widths for ThreeDisplay

* Add GeometryBasics.widths for WebDisplay

* Fix inadvertent bug

* Add news entry

* Decrease the number of evaljs_value calls

* broadcast in WGLMakie method

* Pass kwargs along to backend_show in backend_display for CairoMakie

* Pass arbitrary kwargs along

* convert Iterators.Pairs to namedtuple in backend_display

for CairoMakie

* Splat kwargs as a vector of pairs

* refactor display code

* new screen constructors

* get CairoMakie & GLMakie to work

* fix tests

* more context switching

* try thiss

* hm

* make sure we track context in shader compilation

* clean up ROBJ and context switching

* fix RPRMakie

* fix thy backends

* fix stepper and docs

* remove all inline! use

* fix GLMakie screen deregistering

* fix offset in Stepper for CairoMakie

* fix benchmarks + precompiles

* implement switching of mimes

* move display config to theme

* fix parse error

* bring back precompiles

* small fixes/improvements

* incorperate changes from MakieOrg#2231 and refactor scene

Co-authored-by: Robert Bennett <[email protected]>

* fix CI ?

* improved screen clean up

* clean up and tests

* add logging

* fix makie unit tests

* let CI fail gracefully

* fix import

* remove last GLMakie.Screen related globals

* small improvements for Pluto + friends

* add comment

* clean up docs

* move preferred mime machinery to CairoMakie, since its not needed anywhere else

* remove set_preferred_mime! from tests etc

* fix mime test

* last clean up & address review

* update docs

* initialize array

* deprecate set_window_config! properly

* forgot another constructor

* small clean up and doc fixes

* small doc fixes

Co-authored-by: Anshul Singhvi <[email protected]>
Co-authored-by: Anshul Singhvi <[email protected]>
Co-authored-by: Robert Bennett <[email protected]>
  • Loading branch information
4 people authored and t-bltg committed Dec 31, 2022
1 parent 773cabb commit 27cee5e
Show file tree
Hide file tree
Showing 127 changed files with 2,483 additions and 1,785 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/Docs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ jobs:
- name: Install Julia
uses: julia-actions/setup-julia@v1
with:
version: 1.6
version: '1'
- name: Build and deploy docs
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
Expand Down
1 change: 0 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,6 @@ jobs:
with:
version: ${{ matrix.version }}
arch: ${{ matrix.arch }}
run: sudo apt-get -y install xclip
- uses: julia-actions/cache@v1
- name: Install Julia dependencies
shell: julia --project=monorepo {0}
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/compilation-benchmark.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ jobs:
run: sudo apt-get update && sudo apt-get install -y xorg-dev mesa-utils xvfb libgl1 freeglut3-dev libxrandr-dev libxinerama-dev libxcursor-dev libxi-dev libxext-dev
- uses: julia-actions/setup-julia@v1
with:
version: nightly
version: '1'
arch: x64
- uses: julia-actions/cache@v1
- name: Benchmark
Expand Down
1 change: 0 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ Manifest.toml
/build

\.DS_Store
CairoMakie/src/display.*

WGLMakie/test/recorded_reference_images/
GLMakie/test/recorded_reference_images/
Expand Down
2 changes: 1 addition & 1 deletion CairoMakie/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ function drawonto(canvas, figure)
@guarded draw(canvas) do _
scene = figure.scene
resize!(scene, Gtk.width(canvas), Gtk.height(canvas))
screen = CairoMakie.CairoScreen(scene, Gtk.cairo_surface(canvas), getgc(canvas), nothing)
screen = CairoMakie.Screen(scene, Gtk.cairo_surface(canvas), getgc(canvas), nothing)
CairoMakie.cairo_draw(screen, scene)
end
end
Expand Down
35 changes: 4 additions & 31 deletions CairoMakie/src/CairoMakie.jl
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,8 @@ import Cairo

using Makie: Scene, Lines, Text, Image, Heatmap, Scatter, @key_str, broadcast_foreach
using Makie: convert_attribute, @extractvalue, LineSegments, to_ndim, NativeFont
using Makie: @info, @get_attribute, Combined
using Makie: @info, @get_attribute, Combined, MakieScreen
using Makie: to_value, to_colormap, extrema_nan
using Makie: inline!
using Makie.Observables
using Makie: spaces, is_data_space, is_pixel_space, is_relative_space, is_clip_space
using Makie: numbers_to_colors
Expand All @@ -22,45 +21,19 @@ for name in names(Makie, all=true)
@eval export $(name)
end
end
export inline!

include("cairo-extension.jl")
include("screen.jl")
include("display.jl")
include("infrastructure.jl")
include("utils.jl")
include("fonts.jl")
include("primitives.jl")
include("overrides.jl")

function __init__()
activate!()
Makie.register_backend!(Makie.current_backend[])
end

function display_path(type::String)
if !(type in ("svg", "png", "pdf", "eps"))
error("Only \"svg\", \"png\", \"eps\" and \"pdf\" are allowed for `type`. Found: $(type)")
end
return joinpath(@__DIR__, "display." * type)
end

const _last_inline = Ref(true)
const _last_type = Ref("png")
const _last_px_per_unit = Ref(1.0)
const _last_pt_per_unit = Ref(0.75)
const _last_antialias = Ref(Cairo.ANTIALIAS_BEST)

function activate!(; inline = _last_inline[], type = _last_type[], px_per_unit=_last_px_per_unit[], pt_per_unit=_last_pt_per_unit[], antialias = _last_antialias[])
backend = CairoBackend(display_path(type); px_per_unit=px_per_unit, pt_per_unit=pt_per_unit, antialias = antialias)
Makie.current_backend[] = backend
Makie.use_display[] = !inline
_last_inline[] = inline
_last_type[] = type
_last_px_per_unit[] = px_per_unit
_last_pt_per_unit[] = pt_per_unit
_last_antialias[] = antialias
return
end

include("precompiles.jl")


end
75 changes: 75 additions & 0 deletions CairoMakie/src/cairo-extension.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
# TODO, move those to Cairo?

function set_font_matrix(ctx, matrix)
ccall((:cairo_set_font_matrix, Cairo.libcairo), Cvoid, (Ptr{Cvoid}, Ptr{Cvoid}), ctx.ptr, Ref(matrix))
end

function get_font_matrix(ctx)
matrix = Cairo.CairoMatrix()
ccall((:cairo_get_font_matrix, Cairo.libcairo), Cvoid, (Ptr{Cvoid}, Ptr{Cvoid}), ctx.ptr, Ref(matrix))
return matrix
end

function cairo_font_face_destroy(font_face)
ccall(
(:cairo_font_face_destroy, Cairo.libcairo),
Cvoid, (Ptr{Cvoid},),
font_face
)
end

function set_ft_font(ctx, font)

font_face = ccall(
(:cairo_ft_font_face_create_for_ft_face, Cairo.libcairo),
Ptr{Cvoid}, (Makie.FreeTypeAbstraction.FT_Face, Cint),
font, 0
)

ccall((:cairo_set_font_face, Cairo.libcairo), Cvoid, (Ptr{Cvoid}, Ptr{Cvoid}), ctx.ptr, font_face)

return font_face
end

struct CairoGlyph
index::Culong
x::Cdouble
y::Cdouble
end

function show_glyph(ctx, glyph, x, y)
cg = Ref(CairoGlyph(glyph, x, y))
ccall((:cairo_show_glyphs, Cairo.libcairo),
Nothing, (Ptr{Nothing}, Ptr{CairoGlyph}, Cint),
ctx.ptr, cg, 1)
end

function glyph_path(ctx, glyph::Culong, x, y)
cg = Ref(CairoGlyph(glyph, x, y))
ccall((:cairo_glyph_path, Cairo.libcairo),
Nothing, (Ptr{Nothing}, Ptr{CairoGlyph}, Cint),
ctx.ptr, cg, 1)
end

function surface_set_device_scale(surf, device_x_scale, device_y_scale=device_x_scale)
# this sets a scaling factor on the lowest level that is "hidden" so its even
# enabled when the drawing space is reset for strokes
# that means it can be used to increase or decrease the image resolution
ccall(
(:cairo_surface_set_device_scale, Cairo.libcairo),
Cvoid, (Ptr{Nothing}, Cdouble, Cdouble),
surf.ptr, device_x_scale, device_y_scale)
end

function set_miter_limit(ctx, limit)
ccall((:cairo_set_miter_limit, Cairo.libcairo), Cvoid, (Ptr{Nothing}, Cdouble), ctx.ptr, limit)
end

function get_render_type(surface::Cairo.CairoSurface)
typ = ccall((:cairo_surface_get_type, Cairo.libcairo), Cint, (Ptr{Nothing},), surface.ptr)
typ == Cairo.CAIRO_SURFACE_TYPE_PDF && return PDF
typ == Cairo.CAIRO_SURFACE_TYPE_PS && return EPS
typ == Cairo.CAIRO_SURFACE_TYPE_SVG && return SVG
typ == Cairo.CAIRO_SURFACE_TYPE_IMAGE && return IMAGE
error("Unsupported surface type: $(typ)")
end
171 changes: 171 additions & 0 deletions CairoMakie/src/display.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@

#########################################
# Backend interface to Makie #
#########################################

"""
tryrun(cmd::Cmd)
Try to run a command. Return `true` if `cmd` runs and is successful (exits with a code of `0`).
Return `false` otherwise.
"""
function tryrun(cmd::Cmd)
try
return success(cmd)
catch e
return false
end
end

function openurl(url::String)
if Sys.isapple()
tryrun(`open $url`) && return
elseif Sys.iswindows()
tryrun(`powershell.exe start $url`) && return
elseif Sys.isunix()
tryrun(`xdg-open $url`) && return
tryrun(`gnome-open $url`) && return
end
tryrun(`python -mwebbrowser $(url)`) && return
# our last hope
tryrun(`python3 -mwebbrowser $(url)`) && return
@warn("Can't find a way to open a browser, open $(url) manually!")
end

function display_path(type::String)
if !(type in ("svg", "png", "pdf", "eps"))
error("Only \"svg\", \"png\", \"eps\" and \"pdf\" are allowed for `type`. Found: $(type)")
end
return abspath(joinpath(@__DIR__, "display." * type))
end

function Base.display(screen::Screen{IMAGE}, scene::Scene; connect=false)
path = display_path("png")
cairo_draw(screen, scene)
Cairo.write_to_png(screen.surface, path)
if screen.visible
openurl("file:///" * path)
end
end

function Makie.backend_show(screen::Screen{SVG}, io::IO, ::MIME"image/svg+xml", scene::Scene)

cairo_draw(screen, scene)
Cairo.flush(screen.surface)
Cairo.finish(screen.surface)

svg = String(take!(Makie.raw_io(screen.surface.stream)))

# for some reason, in the svg, surfaceXXX ids keep counting up,
# even with the very same figure drawn again and again
# so we need to reset them to counting up from 1
# so that the same figure results in the same svg and in the same salt
surfaceids = sort(unique(collect(m.match for m in eachmatch(r"surface\d+", svg))))

for (i, id) in enumerate(surfaceids)
svg = replace(svg, id => "surface$i")
end

# salt svg ids with the first 8 characters of the base64 encoded
# sha512 hash to avoid collisions across svgs when embedding them on
# websites. the hash and therefore the salt will always be the same for the same file
# so the output is deterministic
salt = String(Base64.base64encode(SHA.sha512(svg)))[1:8]

ids = sort(unique(collect(m[1] for m in eachmatch(r"id\s*=\s*\"([^\"]*)\"", svg))))

for id in ids
svg = replace(svg, id => "$id-$salt")
end

print(io, svg)
return screen
end

function Makie.backend_show(screen::Screen{PDF}, io::IO, ::MIME"application/pdf", scene::Scene)
cairo_draw(screen, scene)
Cairo.finish(screen.surface)
return screen
end

function Makie.backend_show(screen::Screen{EPS}, io::IO, ::MIME"application/postscript", scene::Scene)
cairo_draw(screen, scene)
Cairo.finish(screen.surface)
return screen
end

function Makie.backend_show(screen::Screen{IMAGE}, io::IO, ::MIME"image/png", scene::Scene)
cairo_draw(screen, scene)
Cairo.write_to_png(screen.surface, io)
return screen
end

# Disabling mimes and showable

const DISABLED_MIMES = Set{String}()
const SUPPORTED_MIMES = Set([
"image/svg+xml",
"application/pdf",
"application/postscript",
"image/png"
])

function Makie.backend_showable(::Type{Screen}, ::MIME{SYM}) where SYM
supported_mimes = Base.setdiff(SUPPORTED_MIMES, DISABLED_MIMES)
return string(SYM) in supported_mimes
end

"""
to_mime_string(mime::Union{String, Symbol, MIME})
Converts anything like `"png", :png, "image/png", MIME"image/png"()` to `"image/png"`.
"""
function to_mime_string(mime::Union{String, Symbol, MIME})
if mime isa MIME
mime_str = string(mime)
if !(mime_str in SUPPORTED_MIMES)
error("Mime $(mime) not supported by CairoMakie")
end
return mime_str
else
mime_str = string(mime)
if !(mime_str in SUPPORTED_MIMES)
mime_str = string(to_mime(convert(RenderType, mime_str)))
end
return mime_str
end
end

"""
disable_mime!(mime::Union{String, Symbol, MIME}...)
The default is automatic, which lets the display system figure out the best mime.
If set to any other valid mime, will result in `showable(any_other_mime, figurelike)` to return false and only return true for `showable(preferred_mime, figurelike)`.
Depending on the display system used, this may result in nothing getting displayed.
"""
function disable_mime!(mimes::Union{String, Symbol, MIME}...)
empty!(DISABLED_MIMES) # always start from 0
if isempty(mimes)
# Reset disabled mimes when called with no arguments
return
end
mime_strings = Set{String}()
for mime in mimes
push!(mime_strings, to_mime_string(mime))
end
union!(DISABLED_MIMES, mime_strings)
return
end

function enable_only_mime!(mimes::Union{String, Symbol, MIME}...)
empty!(DISABLED_MIMES) # always start from 0
if isempty(mimes)
# Reset disabled mimes when called with no arguments
return
end
mime_strings = Set{String}()
for mime in mimes
push!(mime_strings, to_mime_string(mime))
end
union!(DISABLED_MIMES, setdiff(SUPPORTED_MIMES, mime_strings))
return
end
46 changes: 0 additions & 46 deletions CairoMakie/src/fonts.jl

This file was deleted.

Loading

0 comments on commit 27cee5e

Please sign in to comment.