Code Monkey home page Code Monkey logo

femtovg's Introduction

Crates.io API dependency status rust: 1.66 Discord

femtovg

GPU Antialiased 2D vector drawing library written in Rust.


Ported from https://github.com/memononen/nanovg

Most of the implementation is the same as the original C code with some bug fixes, some features added and several parts have been made more Rust-y. Rendering is done via one OpenGl (ES) 3.0+ backend.

Screenshots

Demo

demo Run with cargo run --example demo

Breakout

breakout Run with cargo run --example breakout

SVG

svg Run with cargo run --example svg

Text

text Run with cargo run --example text

Features

  • Anti-aliasing
  • Bézier paths filling and stroking
  • Solid color and image pattern fills and strokes
  • Gradients - box, linear and radial
  • Stroke width and miterlimit
  • Stroke caps: butt, round and square
  • Stroke joins: miter, round and bevel
  • Fill rules - EvenOdd/NonZero
  • Rectangle scissoring
  • Composition modes (SourceOver, SourceIn, SourceOut, Atop, etc..)
  • Global alpha
  • Text filling and stroking
  • Text shaping
  • Text alignment: (left center right), (top, middle, alphabetic, bottom)
  • Nearest font matching
  • Path hit testing
  • OpenGl ES2 backend

In progress

Not supported

  • Stroke dashing
  • Path scissoring
  • Custom shaders
  • 3D transforms
  • Color fonts

License

Licensed under either of

Fonts used in examples:

femtovg's People

Contributors

adamnemecek avatar adroitwhiz avatar albertogp avatar colrad avatar dependabot[bot] avatar drewol avatar geom3trik avatar lemmih avatar nasso avatar nununoisy avatar ogoffart avatar ollpu avatar peterprototypes avatar porky11 avatar rhelmot avatar rmazeiks avatar sky1e avatar sumibi-yakitori avatar tronical avatar whatisaphone avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

femtovg's Issues

Canvas::realloc_image is broken

When I use Canvas::realloc_image the screen is black.
When I delete the image and then create a new one, the window is filled with stars (which is what I want).

I want to generate stars when the window resizes, but only when the window resizes.
I then save the stars in an image, so I can display it later.
I'm going to add dynamic stuff to the front of the stars (the image).

I run linux with x11.

Here is the code:

use femtovg::renderer::OpenGl;
use femtovg::RenderTarget::Image;
use femtovg::{Canvas, Color, ImageFlags, Paint, Path, PixelFormat, RenderTarget};
use glutin::ContextBuilder;
use rand::Rng;
use std::error::Error;
use winit::event::{Event, WindowEvent};
use winit::event_loop::{ControlFlow, EventLoop};
use winit::window::WindowBuilder;

fn main() -> Result<(), Box<dyn Error>> {
    println!("Hello, world!");

    let mut rng = rand::thread_rng();

    let event_loop = EventLoop::new();

    let window_builder = WindowBuilder::new()
        .with_resizable(true)
        .with_title("Fireworks!");

    let window_context = ContextBuilder::new()
        .with_vsync(true)
        .build_windowed(window_builder, &event_loop)?;

    let window_context = unsafe { window_context.make_current().unwrap() };

    let renderer = OpenGl::new_from_glutin_context(&window_context)?;

    let mut canvas = Canvas::new(renderer)?;
    let size = window_context.window().inner_size();
    canvas.set_size(
        size.width,
        size.height,
        window_context.window().scale_factor() as f32,
    );

    let mut star_image = canvas.create_image_empty(
        size.width as usize,
        size.height as usize,
        PixelFormat::Rgb8,
        ImageFlags::empty(),
    )?;

    let mut star_paint = Paint::color(Color::black());

    event_loop.run(move |event, _, control_flow| {
        *control_flow = ControlFlow::Poll;

        let window = window_context.window();

        match event {
            Event::WindowEvent { ref event, .. } => match event {
                WindowEvent::Resized(size) => {
                    window_context.resize(*size);
                    canvas.set_size(size.width, size.height, window.scale_factor() as f32);

                    // Works:
                    canvas.delete_image(star_image);
                    star_image = canvas.create_image_empty(
                        size.width as usize,
                        size.height as usize,
                        PixelFormat::Rgb8,
                        ImageFlags::empty(),
                    ).unwrap();

                    // Does not work:
                    /*
                    canvas.realloc_image(
                        star_image,
                        size.width as usize,
                        size.height as usize,
                        PixelFormat::Rgb8,
                        ImageFlags::empty()
                    ).unwrap();
                     */

                    println!("Resize! w: {} h: {}", size.width, size.height);
                    star_paint = Paint::image(star_image, 0.0, 0.0, size.width as f32, size.height as f32, 0.0, 1.0);

                    canvas.set_render_target(Image(star_image));

                    canvas.clear_rect(0, 0, size.width, size.height, Color::black());

                    for _ in 0..1000 {
                        let x = rng.gen_range(0.0..(size.width as f32));
                        let y = rng.gen_range(0.0..(size.height as f32));

                        let mut path = Path::new();
                        path.rect(x, y, 1.0, 1.0);

                        let paint = Paint::color(Color::white());

                        canvas.fill_path(&mut path, paint);
                    }

                    canvas.set_render_target(RenderTarget::Screen);
                }
                WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit,
                _ => {}
            },
            Event::MainEventsCleared => {
                window.request_redraw();
            },
            Event::RedrawRequested(_) => {
                let dpi_factor = window.scale_factor();
                let size = window.inner_size();
                //canvas.set_size(size.width, size.height, dpi_factor as f32);
                canvas.clear_rect(0, 0, size.width, size.height, Color::white());

                let mut star_path = Path::new();
                star_path.rect(0.0, 0.0, size.width as f32, size.height as f32);

                canvas.fill_path(&mut star_path, star_paint);

                canvas.flush();
                window_context.swap_buffers().unwrap();
            }
            Event::LoopDestroyed => *control_flow = ControlFlow::Exit,
            _ => {}
        }
    });
}

Text breaking broken

If a text is too long to fit into a single line, splitting the word fails.
For example, let's assume, a single line can only contain three letters.

// the index of break_text will be 0. I would expect 3 (5 would be fine, too)
assert_eq!(canvas.break_text(width_of_three_letters, "Hello World", text_paint), Ok(0));

// just returns an empty Vec, I would expect vec!["Hel", "lo", "Wor", "ld"] (vec!["Hello", "World"] would be fine, too)
assert_eq!(canvas.break_text_vec(width_of_three_letters, "Hello World", text_paint), Vec::new());

So the main problem is, that text is never split in the middle of words (or an alternative: allow more letters than the maximum specified line width)

It's just a corner case, so it does not matter what it does exactly, as long as I get indices to all the text in some way.

Add a variant of `Path`, where the vertex buffer is interchangable and you specify indices instead of positions

Instead of adding vertices using move_to, line_to, bezier_to, etc. you would supply the vertex buffer once and then only add verbs using index based variants.
This way, you could create some path once, and update the positions afterwards. This would allow more efficient animations.

Maybe the existing Path type can be used, or a new IndexedPath type.
If there is a new path type, there could be a generic path type, where both specific paths are just specializations maybe even having their own methods like this:

struct GenericPath<SpecificPathSettings> {
    // mostly the current path implementation, some of it moved to the specific path settings
}

struct Path = GenericPath<SimplePathSettings>;
struct IndexedPath = GenericPath<IndexedPathSettings>;

Using the existing path might be a benefit, since it allows the path creation in the original way, and there just needs to be a way to access the vertex buffer (coords).

There needs to be a way to update the vertex buffer. Indexed based variants of the verbs would not be required.
This would be the easiest implementation, but it's probably not that helpful, since it's not straightforward to find out to which vertex a index belongs to, especially when using more complex shapes.

Two stop gradients don't show transparent color

If you make a gradient from a fully transparent color to some other fully opaque color, you should see some of the fully transparent color in the gradient. Currently we don't see that in FemtoVG because we pass the gradient colors to the fragment shader in premultiplied form, making every transparent color turn into transparent black.

We could avoid this by either:

  1. make gradients with transparent colors use multi-stop gradients internally
  2. make the shader do the alpha multiplication after mixing gradient colors

The trade off is that option (2) will have a small perf impact on every gradient, even fully opaque ones (3 float * float ops), and option (1) runs the gradient texture generation code.

Also, however we solve it, fixing this will change behavior, so an application that's currently passing in a fully transparent color accidentally will see different rendering afterwards.

OpenGl::new is unsound

This function takes an arbitrary function that returns a *const c_void and then passes it to a C function. Because it's possible to call this function like this:

OpenGl::new(|_| 42 as *const c_void);

and the fact the C function would happily dereference this address, this function should be marked unsafe.

Ghostscript tiger in the example is rendered incorrectly

The screenshot of the SVG example in the README shows the GhostScript tiger being rendered as follows:

screenshot

However, Firefox, Chrome, librsvg and resvg render it like this:
Ghostscript_Tiger

Notice the absence of a large dark patch on the nose bridge.

resvg is written in Rust and exposes its SVG parsing capabilities for reuse, e.g. via the usvg crate. I see it is in the dependency tree, but the example mostly uses svg crate, which seems to have undergone less testing. If this is a bug in the SVG pass rather than the rendering pass, perhaps reusing more of the resvg infrastructure could fix this.

Decouple DPI and text anti-alias in Canvas

femtovg/src/lib.rs

Lines 368 to 371 in 7ac3df8

self.fringe_width = 1.0 / dpi;
self.tess_tol = 0.25 / dpi;
self.dist_tol = 0.01 / dpi;
self.device_px_ratio = dpi;

This is an issue to solve the blurry text on Windows of slint. Slint handles HiDPI itself, but the text rendering needs a larger value dpi for anti-alias on Windows. It would be better if we can pass two values, one for the scale factor in measure_font, and other for anti-alias.

Related: slint-ui/slint#2615 slint-ui/slint#2618

Text doesn't render correctly when the inner window size is too small

If the inner window size is set too small text either renders incorrectly or completely dissapears. For example, on the Demo example, setting the height to 500 causes some characters to dissapear, like lowercase 'e', and setting the height to 400 causes all text to dissapear. The same is true for width but not with the same values.

Femtovg port

I just ported the femtovg from 'glow' to 'ux-dx'. And I put it in ux-dx.
I would like to know your opinion on this matter. I hope there will be no problems with licensing.
I have my own vision of a general purpose 3D library, so I developed ux-dx, which is OpenGL ES 2.0 / 3.0 + binding at the moment.
I will also be glad if you join the ux-dx project.

Is Web Supported/Planned?

Hey there, this looks pretty nice. :) I was curious if it is possible to run in the browser. Maybe glow could be useful for that if it isn't supported already.

Text rendering glitch, random noise around glyphs.

At first this seemed to affect only some machines, but it turns out to be a general problem.

Look closely at the black text on grey background and you’ll see random pixel lines around the glyphs.
The white-on-black text with red areas at the left side is a view of the font atlas texture.

Void Linux, AMD Radeon 5570:
femtovg-text-glitch-Radeon-5570 Captura de pantalla_2021-01-08_12-11-34

Raspbian, Raspberry Pi 400:
femtovg-text-glitch-RPi-400 Captura de pantalla_2021-01-08_10-34-40

Pop_OS (Ubuntu), Nvidia RTX 2080:
femtovg-text-glitch-RTX2080 Captura de pantalla 2021-01-08 09-31-16

The screenshots show a slightly modified femtovg, where each font atlas texture is filled-in with red when allocating it. That ensures that the glitch appears. In the default configuration, if the textures are allocated in fresh empty VRAM the glitch is not visible because the wrongly-sampled pixels happen to be transparent.

It does not appear in nanovg because they zero the texture memory when allocating each atlas texture.

The cause is a known source of confusion: when using bilinear filtering for rendering the textures, the color of a pixel depends on its neighbors, even if they lie outside the sampled area. See how, in this composite of two screenshots (one with bilinear filtering, another without) the red border, that I added around each glyph outside of the sampled area (the quad in the function render_atlas), encroaches into the black pixels of the glyph padding. That’s the glitch. Without that extra border, the interference pixels belong in many cases to the padding of their neighbors and have no visible effect. Then it only appears if the gaps between glyph boxes, which are uninitialized memory areas, contain values not close to zero.
bilinear-sampling

There is a brief explanation of the interaction between bilinear filtering and texel coordinates here: http://drilian.com/2008/11/25/understanding-half-pixel-and-half-texel-offsets/

Solution

The solution is to add a 1-pixel margin of transparent pixels around the padding around the glyph box:
Captura de pantalla_2021-01-02_12-44-24 padding1margin1 diagram boxes

Looking at the font atlas texture, it seems that a padding of one pixel is enough, so changing GLYPH_PADDING from 2 to 1 allows us to add the margin without consuming more memory than before.

I’m finishing a PR with this fix.

How to do animations triggered by events?

Hello,

First of all, thank you for this nice lib.
I'm trying to figure out how best to do animations triggered by an event (scroll wheel, button, etc..)

Basically, I'm trying to recreate something like this. It is a contextual menu where its state is triggered by events. However, each change of state is associated with an event.

Is there a clever way to represent this in a nice idiomatic way?

Many thanks!
Cheers

Allow specifying a texture as a image source.

I would like to create a image from a texture id that I manually obtain from OpenGL (related: #423). There's no API to do this yet. Such an API could be very useful to other people looking to embed other graphical applications into their own, by means of displaying FBOs.

I would like to add this API in. Looking at the source code I think the best approach would be to add a new entry in the ImageSource enum, when we call update on some Texture we just swap the id field in the texture. I'm not entirely sure, but I think such an approach should be sufficient.

Trouble with FillRule::NonZero and Solidity

The Solidity feature re-orientates the user supplied winding direction of a given path. This might be useful in some cases, but in others this could be a problem: For example i'm drawing externally generated SVG files that make heavy use of the nonzero fillrule to create polygons with "holes". Without the Solidity feature the polygons would show up correctly, but femtovg reverses the winding direction of the given paths and in doing so corrupting the nonzero fillrule semantic.

Of course one could try to detect the winding direction and manually set the solidity to Solid or Hole to counteract femtovg's reversing, but this would be cumbersome and inefficient. I would much rather prefer to have something like Solidity::Natural that instructs femtovg to not touch the supplied path and use it as is (or make solidity optional, like my proposal below).

Here is a small example to reproduce the problem:

let paint = Paint::color(Color::rgb(255, 255, 255))
    .with_fill_rule(FillRule::NonZero);

let mut path = Path::new();
path.move_to(300., 100.);
path.line_to(100., 100.);
path.line_to(100., 300.);
path.line_to(300., 300.);
path.close();

path.move_to(150., 150.);
path.line_to(250., 150.);
path.line_to(250., 250.);
path.line_to(150., 250.);
path.close();

canvas.fill_path(&mut path, &paint);

This should draw a white square with a square hole in it, but does not. Of course here path.solidity(Solidity::Hole) would make it work, but this is not well suited for more complex cases where the winding direction is already defining what is inside the polygon and what not.

I'm sure there are multiple ways to fix this, but something like that should work: Make solidity field in Contour an Option<Solidity> instead of just Solidity (with default None) and in PathCache::new:

// Enforce solidity by reversing the winding, if the user wishes for it
if let Some(solidity) = contour.solidity {
    let area = Contour::polygon_area(points);

    if solidity == Solidity::Solid && area < 0.0 {
        points.reverse();
    }

    if solidity == Solidity::Hole && area > 0.0 {
        points.reverse();
    }
}

Demo applies mouse>canvas transformation twice?

I just tried the femtovg demo for the first time, and looking for which user interactions the code supports, I saw that the two pentagrams are supposed to change color based on whether the mouse in inside.
This works if the canvas is in the default view, but when the canvas has been translated or scaled (drag+drop/mouse wheel), the positions don't match.

At 100% of the original scale, it seems that the mouse activates the shape at double the distance to the bottom right corner from the actual shape. At other scale factors, it's different. It looks vaguely as if the transformation from mouse coordinates to canvas coordinates is resolved double.

Expose function on `OpenGl` struct to allow passing a glow context.

I'm creating a gui with egui and eframe. The egui context allows you to get a reference to a glow context, which is exactly what the OpenGl struct stores. However, there isn't a public function that allows you to instance the OpenGl struct straight from a glow context. I don't see any possible way to convert from a glow to glutin context, and even if there was, it's counterintuitive as the glutin context is converted back to a glow context anyway.
Is there any possibility of a function like this being added?

Render quality and performance regression on v0.4.0

I just updated my femtovg renderer to the new v0.4.0 from v0.3.7 and immediately noticed that everything became very blurry.
So I tried running the demo in this repo on 98ffbf8 and 4b857cc and noticed the same regression. Here's the screenshots for comparison - it's especially obvious when looking at the text:

v0.3.7 v0.4.0
v037 v04

You'll also notice that the FPS dropped from a 1000 to 60 on the new version. I didn't see any framerate caps in the demo code but maybe it's implemented in the library itself? So what could be causing these issues?

I tested this on Linux 6.1.9-arch1-1. Haven't tried Windows or MacOS.

Text line splitting improvements

Short list of problems:

  • 1: symbols should belong to words and should not be split from them if possible
  • 2: multiple symbols belong together and should not be split if possible
  • 3: spaces should be trimmed between split lines

Letters and symbols

In the current version of femtovg (0.3.5), text line splitting, for example using canvas.break_text doesn't work as you would expect.

For example if I have a sentence like this: "Sentence which ends with a dot." And the dot (".") does not fit in the line, while the word "dot" does, it will be split like this:

Line 1: "Sentence which ends with a dot"
Line 2: "."

I would expect it to be like that:

Line 1: "Sentence which ends with a"
Line 2: "dot."

The rule would be, if a letter is followed by a symbol, the symbol still belongs to that word.

Multiple symbols

Same should be true for a sequence of multiple symbols (like ...).
This would be split into multiple lines as well, if it does not fit in.

For example a line like this "Hello ..."
Might be split into lines like this:

Line 1: "Hello ."
Line 2: ".."

In this case, I would expect this:

Line 1: "Hello"
Line 2: "..."

Combined this means if I have this sentence "He thinks: I would like to go, but..."

It currently could be like this:

Line 1: "He thinks: I would like to go, but.."
Line 2: "."

While it should be like this:

Line 1: "He thinks: I would like to go,"
Line 2: "but..."

Spaces

Besides that there's a third problem:

If I have multiple words like this split using spaces: "Hello world!"

It could be split like this:

Line 1: "Hello " (see there's still a space after Hello"
Line 2: "World!"

It should rather be split like this:

Line 1: "Hello"
Line 2: "World!"

Else it might cause alignment issues when rendering.

(not sure about this last one. Manual trimming should also be possible, I guess)

Inter font is rendered incorrectly

Hi, I noticed that some characters from the Inter font are rendered incorrectly.

https://fonts.google.com/specimen/Inter

image

Some things to note:

  • Characters f, e, t and 4 are rendered incorrectly (and more, I would imagine)
  • It's a very popular font, so I do not think the font itself is at fault.
  • Happens at any font size.
  • The gaps get larger when a bolder variant of the font is chosen.
  • Turning on/off anti-aliasing has no effect.
  • I'm on Windows 11.
  • Happens on macOS too it seems.

Reproduction

  1. Drop Inter-Medium.ttf into examples/assets/
  2. In examples/text.rs, append 0123456789 to the The quick brown fox jumps over the lazy dog string
  3. In examples/text.rs, change the sans font from Roboto-Medium.ttf to Inter-Medium.ttf.
  4. cargo run --example text

Text stroke is incorrectly sized after rendering first glyph with direct rendering

I'm running into an odd issue with text strokes where occasionally the stroke width will be wrong after the first glyph.
Image of text with buggy outline
Same as above with no fill

I did some debugging and determined that the issue crops up whenever the text is rendered with text::render_direct(). The issue is this snippet:

femtovg/src/text.rs

Lines 1268 to 1273 in 7bdd8e2

let mut line_width = stroke.line_width;
if mode == RenderMode::Stroke && !scaled {
line_width /= scale;
scaled = true;
}

I'm guessing the intent was to avoid repeatedly dividing the line width by the scale factor. However, because line_width is rebound every loop iteration, the effect is that the division only occurs for the first glyph, then does not occur for the rest.

I'll submit a PR shortly with a fix.

Linear gradients render incorrectly on NVIDIA/Linux

I noticed that linear gradients aren't rendered correctly on NVIDIA on Linux (but Intel graphics on Linux works fine). I believe the problem is because the linear case uses extents for the signed distance round-rect function in the fragment shader which are on the edge of mediump's precision, causing an unexpectedly lumpy interpolation.

If I use highp precision in the fragment shader, then NVIDIA renders as expected (and the same as Intel).

Weird Stencil Stroke Artifacts

Hi! I wanna simulate a text marker and have found a method with_stencil_stroke for a Paint to make it overlap in the same path as you can see in the pictures. but now it leaves some weird artifacts behind when i move slowly.

Stencil Stroke = true (seems like the default)
image

Stencil Stroke = false
image

Is there a way to still have that overlapping without having those artifacts? Thanks :d

Subpixel image compositing (or: three-channel alpha)

Say I have an image with separate alpha values for red green and blue. Does femtovg have a way to load that data into an image (or maybe even 3 images) and composite it accurately onto the canvas?

It might seem like a strange question, but those are the kinds of images generated by subpixel text rendering algorithms, so it should be a broadly applicable question. Scroll down for the triple-wide lowercase a. I'm not asking about changing the rustybuzz-based rendering btw. I'll bring my own pixels, I'm just trying to figure out the low-level way to get those pixels onto the canvas accurately.

Cosmic text example set color

I was playing around with the external_text example, is it possible to set color? And display red colored text for example?

Setting Attrs in the cosmic text buffer does not seem to be changing the color.

wgpu backend?

Hi there! I am wondering if there are plans to make a backend for wgpu? I see that @adamnemecek has a fork with one, but not sure how far away that is from merging. No worries if that’s not in scope, I just wanted to ask. Thank you.

Using femtovg with swash

swash is a new library for text shaping/scaling/rasterization. In particular, it is the output format of the also-new COSMIC text library, which seems to be an extremely strong base for text layout and editing. From what I can tell, swash can provide vector rendering instructions in the form of layered zeno paths, but it can also just do the rasterization itself.

  • do we want to replace femtovg's text layout with COSMIC text?
  • do we want to replace femtovg's shaping algorithm with swash?
  • do we want to provide a public interface for rendering zeno vectors? swash bitmaps?

I will be working on the translation layer from swash image bitmaps to bitmaps that femtovg can consume. This will live in vizia for the time being, but if you want it I can PR it :)

Rendered to memory buffer?

I would like to try using femtovg for drawing on images in a CLI app running on a headless server and don't want the trouble of setting up OpenGL. I looked around this repo a little bit but didn't see anything. Is there an offscreen renderer somewhere? My use case does not involve an event loop, etc. Is this use case envisioned as relevant for femtovg? It seems like it could be useful for testing femtovg itself, as well.

Thanks in advance for any pointers or information.

Device Pixel Ratio handling is incorrect.

As it stands right now, femtovg assumes that the given Canvas width and height are in logical units if device_px_ratio isn't 1. This assumption is wrong and none of the code that depends on device_px_ratio works as expected.

I think we should:

  1. Make it clear that Canvas::width() and Canvas::height() return physical units (and change their return type from f32 to u32).
  2. Add Canvas::logical_width() and Canvas::logical_height() that return logical units.
  3. Make the pixel ratio a read-only value. The pixel ratio can change dynamically when moving a window from one screen to another.
  4. Scale the root-level transformation matrix by the pixel ratio.

I'll implement this in PR #69.

Expanded image loading API?

I don't know if my use case for this is valid so I'll describe where I'm at and what I'd like and you can tell me if it seems like a good idea.

I would like to be able to load images in a background thread. Currently, to do this I have to depend on the exact same version of image, rgb, and imgref that femtovg does, and then rely on e.g. the From<ImgRef<'a, RGBA8>> impl to get myself a ImageSource, which I can ferry to the main thread and then load into my canvas lazily.

Instead, I would like to just be able to say femtovg::ImageSource::load_from_rgba_buf(mem, pitch) and femtovg::ImageSource::load_from_mem(mem, femtovg::guess_format(mem)) - basically the same api as image and imgref provide. In fact, it might be best to cut out the middle man and just directly re-export image and imgref and rgb.

Let me know what you think!

Build broken on Linux when depending on just femtovg

A simple bin crate that depends just on femtovg doesn't build on Linux. This is because by default, femtovg depends on glutin, which in turn depends on winit. Winit requires somebody in the dependency tree to make the choice whether to support x11 and/or wayland via cargo features.

Before commit d26bd1b femtovg made that choice for everyone, by depending on glutin's default features, and thus winit's default and thus taking in wayland and x11 always.

That's good in the sense that femtovg shouldn't make that choice. But it's also bad because this breaks the build that previously worked.

Text rendering: line spacing problem

I have a problem where vertical line spacing is not sufficient. I followed the examples on text renderings.. here is my setup

My canvas has a scale that is applied "top-level". Its 0.35 in this instance.

        let mut transform = Transform2D::identity();
        transform.scale(self.scale_factor, self.scale_factor);
        canvas.reset_transform();
        canvas.set_transform(&transform);

I load the roboto regular font

       canvas.add_font_mem(&resource!("src/assets/Roboto-Regular.ttf"))

I set the following paint for the font

        let mut paint = Paint::color(self.style.color.into());
        paint.set_font(&[font]);
        paint.set_font_size(72.0);

An I render like this


        let lines = canvas.break_text_vec(width, text, &paint)?;

        for line_range in lines {
            if let Ok(text_metrics) =
                canvas.fill_text(self.pos.x as f32, y, &text[line_range], &paint)
            {
                y += font_metrics.height();
            }

The problem is, that the front_metrics.height() = 38) while the rendered glyphs height is somewhere around 56. The result is that vertical line collide.. Any idea what I am doing wrong?

Canvas.width() and Canvas.height() return f32 values.

Canvas.width and Canvas.height are u32 values. Canvas.set_size takes u32 values. If we're rendering to an image instead of the screen the image sizes appear to be stored as usize instead of u32 for some reason but they're still integer.

Why does Canvas.width() convert these to f32 ? Fractional values don't appear to make any sense here.

I believe the return types should be changed to u32

fill_text fills glyph box rather than glyph with sdl2

Thanks for the amazing port! I've been using it for a personal project that uses sdl2 (v0.35.1). Everything (rounded boxes, lines, fill, strokes, transforms) works as expected, except text. When trying to use fill_text, the entire glyph box is filled. Below is a minimal example:

extern crate sdl2;

use sdl2::event::Event;
use sdl2::keyboard::Scancode;

use femtovg::{renderer::OpenGl, Canvas, Color, Paint};


const WIDTH:  u32 = 400;
const HEIGHT: u32 = 300;

fn main() {
    let sdl_context = sdl2::init().expect("Couldn't init SDL2");
    let video_subsystem = sdl_context.video().expect("Couldn't init video");
    let (_, dpi_factor, _) = video_subsystem.display_dpi(0).expect("Can't get a dpi");
  
    let window = video_subsystem
                       .window("Test", WIDTH, HEIGHT)
                       .opengl()
                       .build()
                       .unwrap();

    let _gl_context = window.gl_create_context().unwrap();
    let renderer = unsafe{OpenGl::new_from_function(|string| video_subsystem.gl_get_proc_address(string).cast())}.expect("Cannot create renderer");


    let mut canvas = Canvas::new(renderer).expect("Cannot create canvas");

    let font_id = 
        canvas
            .add_font("examples/assets/Roboto-Bold.ttf")
            .expect("Cannot add font");

    let mut event_pump = sdl_context.event_pump().unwrap();
    'main : loop {

        for event in event_pump.poll_iter() {
            match event {
                Event::Quit {..} |
                Event::KeyDown { scancode: Some(Scancode::Escape), .. } => {
                    break 'main
                },
                _ => (),
            }

        }

        canvas.set_size(WIDTH, HEIGHT, dpi_factor as f32);
        canvas.clear_rect(0, 0, WIDTH as u32, HEIGHT as u32, Color::white());

        let mut paint = Paint::color(Color::black());
        paint.set_font(&[font_id]);
        let _ = canvas.fill_text(
            (WIDTH  as f32)/ 2.0, (HEIGHT as f32)/ 2.0,
            "Hello world!", paint,
        );
        let _ = canvas.stroke_text(
            (WIDTH  as f32)/ 2.0, (HEIGHT as f32)/ 2.0 + 20.,
            "Hello world!", paint,
        );

        canvas.flush();
        window.gl_swap_window();
    }
}

Which results in:

image

stroke_textgives the expected result. fill_text, on the other hand, fills the entire glyph box.

Remarks:

  • Because of #89, I've been using commit de0f9d6 (release v0.3.0). I haven't tried later commits.
  • The text.rs example in the repo (which uses glutin) builds and runs as expected.

Any idea why things break with sdl2?

Make shaping LRU caches tunable

I'm developing a GUI application which needs to layout a couple thousand lines of text on each refresh. It would be nice if I could nudge the LRU_CACHE_CAPACITY upwards a little bit, just for my application.

Investigate using exact coverage for antialiasing

nanovgXC shows how to adapt Pathfinder's exact coverage calculation method to NanoVG. It minimally requires support for an F32 single channel render target, and has some optional paths to use the shader framebuffer fetch (which is supported on Intel gfx, but not on NVIDIA's PC GPUs) or atomic image operations.

This method's most noticeable impact is on glyph rasterization when compared with standard nanovg. I don't think it provides any new capabilities (like path masking) compared to the current nanovg method.

Documentation on docs.rs failed to build

https://docs.rs/femtovg is broken:

docs.rs failed to build femtovg-0.8.0
Please check the build logs for more information.
See Builds for ideas on how to fix a failed build, or Metadata for how to configure docs.rs builds.
If you believe this is docs.rs' fault, open an issue.

Visit the last successful build: femtovg-0.7.1

From that build log, the problem seems to be that no glutin API backend is selected.

[INFO] [stderr]     Checking glutin v0.30.10
[INFO] [stderr] error: Please select at least one api backend
[INFO] [stderr]   --> /opt/rustwide/cargo-home/registry/src/index.crates.io-6f17d22bba15001f/glutin-0.30.10/src/lib.rs:24:1
[INFO] [stderr]    |
[INFO] [stderr] 24 | compile_error!("Please select at least one api backend");
[INFO] [stderr]    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
[INFO] [stderr] 
[INFO] [stderr] error[E0392]: parameter `T` is never used
[INFO] [stderr]    --> /opt/rustwide/cargo-home/registry/src/index.crates.io-6f17d22bba15001f/glutin-0.30.10/src/surface.rs:268:18
[INFO] [stderr]     |
[INFO] [stderr] 268 | pub enum Surface<T: SurfaceTypeTrait> {
[INFO] [stderr]     |                  ^ unused parameter
[INFO] [stderr]     |
[INFO] [stderr]     = help: consider removing `T`, referring to it in a field, or using a marker such as `PhantomData`
[INFO] [stderr] 
[INFO] [stderr] error[E0277]: `()` doesn't implement `std::fmt::Display`
[INFO] [stderr]    --> /opt/rustwide/cargo-home/registry/src/index.crates.io-6f17d22bba15001f/glutin-0.30.10/src/display.rs:515:57
[INFO] [stderr]     |
[INFO] [stderr] 515 |         f.write_fmt(format_args!("DisplayApiPreference::{api}"))
[INFO] [stderr]     |                                                         ^^^^^ `()` cannot be formatted with the default formatter
[INFO] [stderr]     |
[INFO] [stderr]     = help: the trait `std::fmt::Display` is not implemented for `()`
[INFO] [stderr]     = note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead
[INFO] [stderr]     = note: this error originates in the macro `format_args` (in Nightly builds, run with -Z macro-backtrace for more info)
[INFO] [stderr] 
[INFO] [stderr] Some errors have detailed explanations: E0277, E0392.
[INFO] [stderr] For more information about an error, try `rustc --explain E0277`.
[INFO] [stderr] error: could not compile `glutin` (lib) due to 3 previous errors

Questions regarding this project

Hi, I am interested in using your crate but I have some questions:

  1. What is the license of this crate? Is it MIT, GPL etc?
  2. Is it similar to using something like Processing? Can I please get a basic example if possible of where I can get started?
  3. How well maintained is this project?
  4. Is it Alpha, Beta, or Stable this project?
  5. Can I display stuff on the Wayland display?
  6. Can I use it to create a taskbar similar to Polybar?
  7. Can I use it to create my own widgets, something that looks like these:

Desktop
Desktop
8. Is there any man pages/docs available for this crate?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.