Git Product home page Git Product logo

blend2d's Introduction

blend2d's People

Contributors

dan-eicher avatar kobalicek avatar luizzak avatar qulogic avatar wmamrak avatar yzrmn avatar zeewanderer avatar zhouyuchen 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  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

blend2d's Issues

Stroke dash line not working

Hey guys,

I wanted to stoke a dash line, so I did something like this:

BLArray<double> dash;
dash.append(10, 10);
ctx.setStrokeDashArray(dash);
ctx.setStrokeDashOffset(0);
ctx.strokeLine(10, 10, 110, 110);

but it is not working (it strokes a normal non-dahsed line). Where did I do it wrong?

Thank you very much!

Skia performance comparison

I find this project fascinating, and has a lot of room for improvement.
BUT https://blend2d.com/performance.html
Should show a comparison against latest Skia.

Less importantly, it would be nice to compare performance with pathfinder and other state of the art renderers
https://github.com/pcwalton/pathfinder
https://github.com/intel/fastuidraw
https://www.google.fr/amp/s/www.amanithvg.com/

I can't find on internet an exhaustive performance comparison of 2D renderers.
Your website could become a great reference for choice.
Thus it would be nice to compare performance with direct2D and openVG too :) and even higher level libs like SDL?

building apps with blend2d

This is not an issue, more like a progress report.
winman

Assuming the image I just dragged in is visible, I'm essentially creating a "desktop" environment completely from the ground up using blend2d (LuaJIT binding) as the 2D graphics engine. The only thing used in the host environment is getting a basic window up on the screen (win32) and doing a blit from my 'framebuffer' to the host window.

Lots of fun tidbits in there from having a nice 'WinMan' compositor, to translucent 'windows', multiple BLContext objects interacting on their own individual BLImage backends, a DrawingContext thing that wraps up BLContext, and provides a 'Processing' like interface to boot.

I've taken the original 'getting-started' examples, and turned them into 'graphics', which can be rendered anywhere, any size in a 'window'. This nicely shows off the scaling capabilities, and highlights any challenges thereof.

I'll be adding keyboard/mouse interaction, but that has more to do with the environment than the graphics themselves, other than testing out Path 'include'. So, great fun, great exercise of the APIs.

The fundamental binding to blend2d hasn't changed much since I first did it, so the C API is pretty decent from that perspective. I haven't tried every feature, such as the move, assign, sort of stuff, but I simply haven't figured out the relevance of those APIs in my garbage collected environment.

So, progress is being made.

I don't know where else I could post this progress report, so I put it here for discussion.

Occasionally crash loading image

qTable from blJpegIDCT8_SSE2 wasn't aligned to 16 bytes.

=========================================

Exception thrown at 0x01448BB3 in bl-getting-started-3.exe: 0xC0000005: Access violation reading location 0xFFFFFFFF.

bl-getting-started-3.exe!SIMD::`anonymous namespace'::vmuli16(const __m128i & x, const __m128i & y) Line 364
at d:\dependencies\blend2d\src\blend2d\blsimd_x86_p.h(364)
bl-getting-started-3.exe!blJpegIDCT8_SSE2(unsigned char * dst, int dstStride, const short * src, const unsigned short * qTable) Line 155
at d:\dependencies\blend2d\src\blend2d\codec\bljpegops_sse2.cpp(155)
bl-getting-started-3.exe!blJpegDecoderImplProcessMCUs(BLJpegDecoderImpl * impl) Line 1179
at d:\dependencies\blend2d\src\blend2d\codec\bljpegcodec.cpp(1179)
bl-getting-started-3.exe!blJpegDecoderImplReadFrameInternal(BLJpegDecoderImpl * impl, BLImage * imageOut, const unsigned char * p, unsigned int size) Line 1402
at d:\dependencies\blend2d\src\blend2d\codec\bljpegcodec.cpp(1402)
bl-getting-started-3.exe!blJpegDecoderImplReadFrame(BLJpegDecoderImpl * impl, BLImage * imageOut, const unsigned char * p, unsigned int size) Line 1473
at d:\dependencies\blend2d\src\blend2d\codec\bljpegcodec.cpp(1473)
bl-getting-started-3.exe!blImageDecoderReadFrame(BLImageDecoderCore * self, BLImageCore * imageOut, const unsigned char * data, unsigned int size) Line 614
at d:\dependencies\blend2d\src\blend2d\blimage.cpp(614)
bl-getting-started-3.exe!BLImageDecoder::readFrame(BLImage & dst, const BLArray & buffer) Line 546
at d:\dependencies\blend2d\src\blend2d\blimage.h(546)
bl-getting-started-3.exe!blImageReadFromFile(BLImageCore * self, const char * fileName, const BLArrayCore * codecs) Line 445
at d:\dependencies\blend2d\src\blend2d\blimage.cpp(445)
bl-getting-started-3.exe!BLImage::readFromFile(const char * fileName, const BLArray & codecs) Line 323
at d:\dependencies\blend2d\src\blend2d\blimage.h(323)
bl-getting-started-3.exe!main(int argc, char * * argv) Line 13
at d:\dependencies\bl-samples\src\bl-getting-started-3.cpp(13)

crash while stroking rect with clip translate combo

This test case fairly isolates a crash I have found. Basically, the combination of doing a clip, and then a translate, and then a strokeRect which would fall outside the bounds of the underlying image, will crash. I suspect it's outside the bounds of checking on the image, and writes into random memory or something.

The code here is lua, but the C equivalent should be the same.

package.path = "../?.lua;"..package.path;

local ffi = require("ffi")
local C = ffi.C 

local blapi = require("blend2d/blend2d_ffi")

local BLContext = ffi.typeof("BLContextCore")
local BLImage = ffi.typeof("BLImageCore")
local BLRectI = ffi.typeof("BLRectI")
local BLRect = ffi.typeof("BLRect")


local function _applyMatrixOpV(ctx, opType, ...)
    local opData = ffi.new("double[?]",select('#',...), {...});
    --print("_applyMatrixOpV: ", opData[0], opData[1])
    local bResult = ctx.impl.virt.matrixOp(ctx.impl, opType, opData);
    if bResult == C.BL_SUCCESS then
        return self;
    end

    return false, bResult
end

local function translate (ctx, x, y)
    return _applyMatrixOpV(ctx, C.BL_MATRIX2D_OP_TRANSLATE, x, y);
end

local function main()
    local x = 390
    local y = 10
    local width = 100
    local height = 100

    local img = BLImage()
    blapi.blImageInitAs(img, 400, 400, C.BL_FORMAT_PRGB32)
    local ctx = BLContext()
    blapi.blContextInitAs(ctx, img, nil)

    local rect = BLRectI({x,y,width,height})
    blapi.blContextClipToRectI(ctx, rect) ;
    
    local err = translate(ctx, x, y)

    -- stroke
    -- THIS WILL CRASH
    local bResult = blapi.blContextStrokeRectD(ctx, rect) ;
end

main()

Expired certificate

Hi,

I'm sure you've already noticed, but in case you haven't, the certificate for blend2d.com expired yesterday.

Systematic use of uint32_t instead of enum

The systematic use of uint32_t instead of the enum type is making the API very painful to use. I do not care much that the compiler cannot check that I am using the right enum but I am spending far more time than acceptable trying to figure out which enum type I should use for each function parameter.

I do not now why that was done like that but I assume that there must be a reason (backward compatibility? consistency with the C api? make bindings easier? ...)

If keeping uint32_t is really important then I propose the following approach that should solve the problem for the C++ api by providing the required information to Doxygen without changing the ABI.

  1. Declare the following template type

     #include <type_traits>
     template<typename T> using bl_enum = typename std::underlying_type<T>::type;
    

or if you know that all enums are uint32_t

    template<typename T> using bl_enum = uint32_t;
  1. And replace each uint32_t by bl_enum<XXX> where XXX is the required enum type.

ctx->fillStyleType always returns 1 (BL_STYLE_TYPE_SOLID)

Please, see sample "bl-getting-started-2.cpp".

After setting a a fill-style with a gradient
ctx.setFillStyle(linear);
if a try to check the "current-fill style"
ctx.fillStyleType()
this method return 1 (i.e. BL_STYLE_TYPE_SOLID), instead of 3 (BL_STYLE_TYPE_GRADIENT).

C API Help

This issue is provided for C-API users.

If you think that something in C-API is not documented well please add a comment here and we will try to update the documentation.

Handling NaN and INF values in EdgeBuilder and Stroker

At the moment EdgeBuilder cannot handle NaN values properly in the following cases:

  • NaN is part of start/end point
  • NaN is part of a quadratic/cubic curve that is being flattened

The thing is that we do not handle NaNs at BLPath level, because of the following reasons:

  • Vertices can be transformed, so safe path values won't protect us from NaNs resulting from other computations
  • The plan is to allow users passing their own buffers that are not allocated by Blend2D nor part of BLPath, so Blend2D can never trust user input or BLPath content for security reasons

The problem is not how to handle NaNs, it's more about the behavior when NaN is encountered at this level. The following options are available:

  • Reset the whole EdgeBuffer content and return BL_ERROR_INVALID_GEOMETRY error
  • Treat NaN as some safe value like [0, 0] and render a corrupted image

I would prefer the first option as corrupted image is probably not what users would want anyway, but if anyone has different ideas then I'm interested.

how will multi-threading work

What is the intention for multi-threading? I am currently contemplating having several BLContext objects connected to the same BLImage. This is to support multiple "windows" on the same "framebuffer". The challenge for the window manager will be in some way controller the ordering of blits into the framebuffer. Of course, having each window with its own backing store, and just bliting in z order will do the trick.

Is the intention of multi-threading to simply deal with multiple threads within a single drawing context? Or is there any intention of helping to manager multiple contexts? I assume the former (multi-thread within single context).

Just want to be clear so I don't waste time creating something that's already on the roadmap.

Linker error due to unimplemented functions

Currently a build I'm doing fails because of these unimplemented functions that are defined in public headers:

blContextStrokeGlyphRunD
blContextStrokeGlyphRunI
blContextStrokeTextD
blContextStrokeTextI

Linker messages:

Undefined symbols for architecture x86_64:
  "_blContextStrokeGlyphRunD", referenced from:
      SwiftBlend2D.BLContext.strokeGlyphRun(_: __C.BLGlyphRun, at: __C.BLPoint, font: __C.BLFontCore) -> () in BLContext.o
  "_blContextStrokeGlyphRunI", referenced from:
      SwiftBlend2D.BLContext.strokeGlyphRun(_: __C.BLGlyphRun, at: __C.BLPointI, font: __C.BLFontCore) -> () in BLContext.o
  "_blContextStrokeTextD", referenced from:
      SwiftBlend2D.BLContext.strokeText(_: Swift.String, at: __C.BLPoint, font: __C.BLFontCore) -> () in BLContext.o
  "_blContextStrokeTextI", referenced from:
      SwiftBlend2D.BLContext.strokeText(_: Swift.String, at: __C.BLPointI, font: __C.BLFontCore) -> () in BLContext.o

Harfbuzz ?

Does Blend2D support Harfbuzz for layout of international text ?

AVX/AVX2 detection vs target runtime

Hello

I have some doubts regarding AVX/AVX2. Blend2D checks if the compiler supports avx/avx2 and if so, it sets its flags:

blend2d_detect_cflags(BLEND2D_CFLAGS_AVX "-arch:AVX")
blend2d_detect_cflags(BLEND2D_CFLAGS_AVX2 "-arch:AVX2")

Then we can see in the runtime_p.h that if certain flags regarding AVX/AVX2 were enabled then everything is const valued and evaluates to true without checking actual runtime:

#ifdef BL_TARGET_OPT_AVX
constexpr bool blRuntimeHasAVX(BLRuntimeContext* rt) noexcept { return true; }
#else
inline bool blRuntimeHasAVX(BLRuntimeContext* rt) noexcept { return (rt->systemInfo.cpuFeatures & BL_RUNTIME_CPU_FEATURE_X86_AVX) != 0; }
#endif

#ifdef BL_TARGET_OPT_AVX2
constexpr bool blRuntimeHasAVX2(BLRuntimeContext* rt) noexcept { return true; }
#else
inline bool blRuntimeHasAVX2(BLRuntimeContext* rt) noexcept { return (rt->systemInfo.cpuFeatures & BL_RUNTIME_CPU_FEATURE_X86_AVX2) != 0; }
#endif

Basically what I want to make sure of is that runtime detects CPU features on the target computer despite my compiler supporting AVX/AVX2. Just because my compiler supports it doesn't mean the end user will have a CPU supporting AVX/AVX2.

Then again I can see this part within api-build_p.h:

#if defined(__AVX2__)
  #define BL_TARGET_OPT_AVX2
#endif
#if defined(BL_TARGET_OPT_AVX2) || defined(__AVX__)
  #define BL_TARGET_OPT_AVX
#endif

Since I do use MSVC 19 I can assume __AVX2__ won't be defined unless I specify AVX2 extension within code generation section.

By looking at gradient.cpp I can see that the runtime is checked:

  #ifdef BL_BUILD_OPT_SSE2
  if (blRuntimeHasSSE2(rt)) {
    blGradientOps.interpolate32 = blGradientInterpolate32_SSE2;
  }
  #endif

  #ifdef BL_BUILD_OPT_AVX2
  if (blRuntimeHasAVX2(rt)) {
    blGradientOps.interpolate32 = blGradientInterpolate32_AVX2;
  }
  #endif

So I can only assume those flags are only to check whether compiler supports AVX/AVX2, but it is always checked at the runtime nonetheless, and if so, used, if not - not.

Source?

Is Blend2D a thing? Where is the source located? Was looking to try it out.

Seams with repeating pattern fill style

Hi again, there's a problem with filtering across pattern edges when
applying a matrix to a style that contains a rotation. Depending on the
rotation angle there are zero, two or four hard unfiltered seams in the
output. Here are some screenshots taken from my project:

0_90

90_180

180_270

270_360

the test case:

pattern_seams.cpp.txt

the checker image to use with the test case:

checker

However I'm not able to fully replicate the behaviour of my app in the test case,
since it seems to show always either no seams (when angle is between 0
and 90ยฐ) or four seams, across both u and v texture axis.

Incompatibility with Unreal4 requirements

Impossible to build blend2d (or use the prebuilt dll) with Unreal Engine. There are "warnings" 4582 and 4583, preventing build (for whatever reason, multiplatform and all that). Disabling warnings does not help too, other errors arise.
I'm not sure it's a true bug, just C++ code imperfection. Yet here you go:

2>D:\Work\Unreal\TerrainGeneration01\Source\blend2d\src\blend2d./blpath.h(225): error C4582: 'BLStrokeOptionsCore::dashArray': constructor is not implicitly called
2>D:\Work\Unreal\TerrainGeneration01\Source\blend2d\src\blend2d./blpath.h(225): error C4583: 'BLStrokeOptionsCore::dashArray': destructor is not implicitly called
2>D:\Work\Unreal\TerrainGeneration01\Source\blend2d\src\blend2d./blfont.h(411): error C4582: 'BLFontFaceImpl::data': constructor is not implicitly called
2>D:\Work\Unreal\TerrainGeneration01\Source\blend2d\src\blend2d./blfont.h(411): error C4582: 'BLFontFaceImpl::loader': constructor is not implicitly called
2>D:\Work\Unreal\TerrainGeneration01\Source\blend2d\src\blend2d./blfont.h(411): error C4582: 'BLFontFaceImpl::fullName': constructor is not implicitly called
2>D:\Work\Unreal\TerrainGeneration01\Source\blend2d\src\blend2d./blfont.h(411): error C4582: 'BLFontFaceImpl::familyName': constructor is not implicitly called
2>D:\Work\Unreal\TerrainGeneration01\Source\blend2d\src\blend2d./blfont.h(411): error C4582: 'BLFontFaceImpl::subfamilyName': constructor is not implicitly called
2>D:\Work\Unreal\TerrainGeneration01\Source\blend2d\src\blend2d./blfont.h(411): error C4582: 'BLFontFaceImpl::postScriptName': constructor is not implicitly called
2>D:\Work\Unreal\TerrainGeneration01\Source\blend2d\src\blend2d./blfont.h(411): error C4583: 'BLFontFaceImpl::data': destructor is not implicitly called
2>D:\Work\Unreal\TerrainGeneration01\Source\blend2d\src\blend2d./blfont.h(411): error C4583: 'BLFontFaceImpl::loader': destructor is not implicitly called
2>D:\Work\Unreal\TerrainGeneration01\Source\blend2d\src\blend2d./blfont.h(411): error C4583: 'BLFontFaceImpl::fullName': destructor is not implicitly called
2>D:\Work\Unreal\TerrainGeneration01\Source\blend2d\src\blend2d./blfont.h(411): error C4583: 'BLFontFaceImpl::familyName': destructor is not implicitly called
2>D:\Work\Unreal\TerrainGeneration01\Source\blend2d\src\blend2d./blfont.h(411): error C4583: 'BLFontFaceImpl::subfamilyName': destructor is not implicitly called
2>D:\Work\Unreal\TerrainGeneration01\Source\blend2d\src\blend2d./blfont.h(411): error C4583: 'BLFontFaceImpl::postScriptName': destructor is not implicitly called
2>D:\Work\Unreal\TerrainGeneration01\Source\blend2d\src\blend2d./blfont.h(627): error C4582: 'BLFontImpl::face': constructor is not implicitly called
2>D:\Work\Unreal\TerrainGeneration01\Source\blend2d\src\blend2d./blfont.h(627): error C4582: 'BLFontImpl::features': constructor is not implicitly called
2>D:\Work\Unreal\TerrainGeneration01\Source\blend2d\src\blend2d./blfont.h(627): error C4582: 'BLFontImpl::variations': constructor is not implicitly called
2>D:\Work\Unreal\TerrainGeneration01\Source\blend2d\src\blend2d./blfont.h(627): error C4583: 'BLFontImpl::face': destructor is not implicitly called
2>D:\Work\Unreal\TerrainGeneration01\Source\blend2d\src\blend2d./blfont.h(627): error C4583: 'BLFontImpl::features': destructor is not implicitly called
2>D:\Work\Unreal\TerrainGeneration01\Source\blend2d\src\blend2d./blfont.h(627): error C4583: 'BLFontImpl::variations': destructor is not implicitly called
2>D:\Work\Unreal\TerrainGeneration01\Source\blend2d\src\blend2d./blimage.h(606): error C4582: 'BLImageDecoderImpl::codec': constructor is not implicitly called
2>D:\Work\Unreal\TerrainGeneration01\Source\blend2d\src\blend2d./blimage.h(606): error C4583: 'BLImageDecoderImpl::codec': destructor is not implicitly called
2>D:\Work\Unreal\TerrainGeneration01\Source\blend2d\src\blend2d./blimage.h(733): error C4582: 'BLImageEncoderImpl::codec': constructor is not implicitly called
2>D:\Work\Unreal\TerrainGeneration01\Source\blend2d\src\blend2d./blimage.h(733): error C4583: 'BLImageEncoderImpl::codec': destructor is not implicitly called
2>D:\Work\Unreal\TerrainGeneration01\Source\blend2d\src./blend2d/blcontext.h(367): error C4582: 'BLContextState::strokeOptions': constructor is not implicitly called
2>D:\Work\Unreal\TerrainGeneration01\Source\blend2d\src./blend2d/blcontext.h(367): error C4583: 'BLContextState::strokeOptions': destructor is not implicitly called
2>D:\Work\Unreal\TerrainGeneration01\Source\blend2d\src./blend2d/blpattern.h(52): error C4582: 'BLPatternImpl::image': constructor is not implicitly called
2>D:\Work\Unreal\TerrainGeneration01\Source\blend2d\src./blend2d/blpattern.h(52): error C4583: 'BLPatternImpl::image': destructor is not implicitly called

index_sequence should be renamed to integer_sequence

blend2d fails to build on platforms that properly support C++ 14, which provides a class called std::integer_sequence (with 2 template arguments) rather than a class called std::index_sequence (with 1 template argument).

I recommend using TravisCI to build blend2d with a small handful of platforms, it is free and surprisingly easy to set up.

Build error MSVC 19.16.27030.1

Visual Studio 15 2017
CMake creates project and all that.
ultimately cl compiler will report error:
D8016: '/Ox' and '/RTC1' command-line options are incompatible.
I'm compiling for 32-bit also. Last time around I just went into the project and disabled either or both of these and moved along, but I think it's worth reporting if you want to fix this at the source.

Bad gradients with source over operator

Hi, apparently there's a problem when filling paths with linear or radial gradients and source over operator on non transparent backgrounds (other composition modes are ok, and if I recall correctly beta9 was working good). "Bands" appear where not fully opaque pixels are drawn. I'm attaching screenshots, debug info, isolated test case program code and output.

screenshots

blend2d debug output.txt

test_case.cpp.txt

test_case_output

How are path line options specified

I suspect it's related to the BLPathOptionsCore, but I'm not sure how to specify "on the line", "outside", or "inside". So, when I've got a thickness beyond 1 unit, which way will things be drawn? Is it controllable (I hope so).

Clang in MSVC 2019

Hello again

Blend2D suggest to use Clang, so I did generate the build project files for MSVC 2019 with clang installed using the following command:

cmake -T ClangCL .. -DBLEND2D_STATIC=True

It did generate project with LLVM (clang-cl) as Platform Toolset, but there are some warnings:

2>clang-cl : warning : unknown argument ignored in clang-cl: '-fno-exceptions' [-Wunknown-argument]
2>clang-cl : warning : unknown argument ignored in clang-cl: '-fno-rtti' [-Wunknown-argument]
2>clang-cl : warning : unknown argument ignored in clang-cl: '-fno-math-errno' [-Wunknown-argument]

Which I suppose might be crucial for those optimizations mentioned on the website regarding choosing compiler.

The project does compile fine though.

Is there an Easy way to measure text?

I am getting lost between font, metrics, glyph buffers and the like. Is there an obvious way to measure the size of a string?

More specifically, the text bounding box for some text. I know it's an obviously complex thing, but looking for the Cairo equivalent "toy" interface.

radial gradient as fill style for fillGeometry seems to be crashing

It seems that a recent change broke radial gradients. below is my test case. It proves that with linear gradient, things are fine, but just switch to radial gradient and you get a crash. All other things isolated, used as raw interfaces as possible, only variance is whether it's linear or radial.

local ffi = require("ffi")
local C = ffi.C 

--local blapi = require("blend2d.blend2d_ffi")
local blapi = require("blend2d.blend2d")

local Gradient = require("Gradient")
local LinearGradient, RadialGradient = Gradient.LinearGradient, Gradient.RadialGradient


local function main()
    local img = ffi.new("struct BLImageCore");
    blapi.blImageInitAs(img, 480, 480, C.BL_FORMAT_PRGB32) ;

    local ctx = ffi.new("struct BLContextCore");
    blapi.blContextInitAs(ctx, img, nil);

    blapi.blContextSetCompOp(ctx, C.BL_COMP_OP_SRC_COPY);
    blapi.blContextFillAll(ctx);

---[[
    local values = BLLinearGradientValues( 0, 0, 256, 256 );
    local gradient, err = BLGradient(C.BL_GRADIENT_TYPE_LINEAR, values, C.BL_EXTEND_MODE_PAD, nil, 0, nil);
    print("BLGradient: ", gradient, err)
    blapi.blGradientAddStopRgba32(gradient, 1.0, 0xFFFFFFFF)
    blapi.blGradientAddStopRgba32(gradient, 0.5, 0xFFFF6F3F)
    --blapi.blGradientAddStopRgba32(gradient, 1.0, 0xFFff0000)
--]]

--[[
    -- THIS WILL CRASH
    -- it is very specific to radial gradient
    local values = BLRadialGradientValues(180, 180, 180, 180, 180)
    local gradient, err = BLGradient(values)
    print("BLGradient: ", gradient, err)

    blapi.blGradientAddStopRgba32(gradient, 1.0, 0xFFFFFFFF)
    blapi.blGradientAddStopRgba32(gradient, 0.5, 0xFFFFAF00)
    blapi.blGradientAddStopRgba32(gradient, 1.0, 0xFFff0000)
--]]


    blapi.blContextSetCompOp(ctx, C.BL_COMP_OP_SRC_OVER);
    blapi.blContextSetFillStyle(ctx, gradient)

    local circle = BLCircle();
    circle.cx = 180;
    circle.cy = 180;
    circle.r = 160;
   
    -- low level to ensure it's not the C api wrapper doing anything
    ctx.impl.virt.fillGeometry(ctx.impl, C.BL_GEOMETRY_TYPE_CIRCLE, circle);


    -- Second Shape
    -- prove that normal object construction works fine
    -- when you use linear gradient
    local gradient2 = LinearGradient({values = {195, 195, 470, 470},
    stops = {
      {offset = 0.0, uint32 = 0xFFFFFFFF},
      {offset = 1.0, uint32 = 0xFF3F9FFF}
    }
  })

    ctx:setCompOp(C.BL_COMP_OP_DIFFERENCE);
    ctx:setFillStyle(gradient2);
    ctx:fillRoundRect(BLRoundRect(195, 195, 270, 270, 25,25));



-- These are only here to ensure GC does not kick in too early
    --print(img)
    --print(ctx)
    --print(values)
    --print(gradient)
    --print(circle)

    blapi.blContextEnd(ctx)


    BLImageCodec("BMP"):writeImageToFile(img, "output/test_radialgradient.bmp")
end

How To: Applying gradient per object

keyboard
I have this keyboard graphic that I've constructed in blend2d. I want to apply a gradient per key.

    obj.linear = Gradient.LinearGradient({
        values = {0, 0, 0, self.unit};
        stops = {
          {offset = 0, uint32 = 0xFFFFFFFF},
          {offset = 1, uint32 = 0xFF1F7FFF}
        }
      });

I don't want to create a gradient per key (with the key specific coordinates), I want to use the same gradient for all. Is the proper way to do this using the matrix operation on the gradient before drawing each key?

BL_API_C BLResult BL_CDECL blGradientApplyMatrixOp(BLGradientCore* self, uint32_t opType, const void* opData) BL_NOEXCEPT_C;

I'm assuming if I make my gradient a unit of 1, then just scale and translate appropriately, that's the right way to do it? Or is there another method I'm missing?

fonts need to be opened in shared read mode

While trying to use fonts from the windows\fonts*.ttf files, some of them allow opening, and some do not. It is typically the ones in use by the system that don't allow opening (Calibri, times, cour, etc).

Reading the fontloader, I see they are opened in READ mode, not READ_SHARE. Doing separate testing, trying to open files using BLFile shows there is an error in trying to open in shared mode: 32, ERROR_SHARING_VIOLATION, which is ultimately reported as: BL_ERROR_FILE_NAME_TOO_LONG?

At any rate, since I can not control the read flags on the 'createFromFile' calls of BLFont or BLFontLoader, I'm kinda stuck on reading these font files. It may turn out that the system won't allow for shared reading of these particular font files, which would be a bug in Windows, but I'm thinking it should work.

I don't know what the situation would be in Linux

Is there a dirty region available?

I'd like to optimize drawing to the screen. I think I'd like to have access to a dirty region of that's available. Otherwise, is the general idea to simply blit the whole framebuffer as that's fast enough these days? I did see a discussion of 30fps blits on 4k displays.

Unused variable warnings

I'm able to build blend2d from a fresh clone with very little pain (nice job!) but see lots of warnings:

[8/124] Building CXX object extern/blend2d/CMakeFiles/blend2d.dir/src/blend2d/blgradient_sse2.cpp.o
../extern/blend2d/src/blend2d/blgradient_sse2.cpp:149:7: warning: unused label 'OnLoop_End' [-Wunused-label]
      BL_SIMD_LOOP_32x4_MAIN_END(Loop)
      ^
../extern/blend2d/src/blend2d/./blsimd_p.h:70:79: note: expanded from macro 'BL_SIMD_LOOP_32x4_MAIN_END'
                                                                              \
                                                                              ^
<scratch space>:8:1: note: expanded from here
OnLoop_End
^
1 warning generated.
[10/124] Building CXX object extern/blend2d/CMakeFiles/blend2d.dir/src/blend2d/blfont.cpp.o
../extern/blend2d/src/blend2d/blfont.cpp:989:58: warning: unused parameter 'path' [-Wunused-parameter]
static BLResult BL_CDECL blFontDummyPathSink(BLPathCore* path, const void* info, void* closure) noexcept {
                                                         ^
../extern/blend2d/src/blend2d/blfont.cpp:989:76: warning: unused parameter 'info' [-Wunused-parameter]
static BLResult BL_CDECL blFontDummyPathSink(BLPathCore* path, const void* info, void* closure) noexcept {
                                                                           ^
../extern/blend2d/src/blend2d/blfont.cpp:989:88: warning: unused parameter 'closure' [-Wunused-parameter]
static BLResult BL_CDECL blFontDummyPathSink(BLPathCore* path, const void* info, void* closure) noexcept {
                                                                                       ^
3 warnings generated.
[14/124] Building CXX object extern/blend2d/CMakeFiles/blend2d.dir/src/blend2d/blgradient.cpp.o
../extern/blend2d/src/blend2d/blgradient.cpp:926:14: warning: unused variable 'result' [-Wunused-variable]
    BLResult result = blGradientRemoveStop(self, index);
             ^
1 warning generated.
[21/124] Building CXX object extern/blend2d/CMakeFiles/blend2d.dir/src/blend2d/blpipedefs.cpp.o
../extern/blend2d/src/blend2d/blpipedefs.cpp:506:10: warning: unused variable 'angle' [-Wunused-variable]
  double angle = values.angle;
         ^
../extern/blend2d/src/blend2d/blpipedefs.cpp:502:138: warning: unused parameter 'extendMode' [-Wunused-parameter]
static BL_INLINE uint32_t blPipeFetchDataInitConicalGradient(BLPipeFetchData* fetchData, const BLConicalGradientValues& values, uint32_t extendMode, const BLMatrix2D& m, const BLMatrix2D& mInv) noexcept {
                                                                                                                                         ^
2 warnings generated.
[29/124] Building CXX object extern/blend2d/CMakeFiles/blend2d.dir/src/blend2d/blpixelconverter_avx2.cpp.o
../extern/blend2d/src/blend2d/blpixelconverter_avx2.cpp:238:23: warning: unused variable 'dstInfo' [-Wunused-variable]
  const BLFormatInfo& dstInfo = blPixelConverterFormatInfo[dstFormat];
                      ^
1 warning generated.
[36/124] Building CXX object extern/blend2d/CMakeFiles/blend2d.dir/src/blend2d/blpath.cpp.o
../extern/blend2d/src/blend2d/blpath.cpp:1899:91: warning: unused parameter 'fitFlags' [-Wunused-parameter]
BLResult blPathFitTo(BLPathCore* self, const BLRange* range, const BLRect* rect, uint32_t fitFlags) noexcept {
                                                                                          ^
1 warning generated.
[40/124] Building CXX object extern/blend2d/CMakeFiles/blend2d.dir/src/blend2d/blpixelconverter.cpp.o
../extern/blend2d/src/blend2d/blpixelconverter.cpp:818:12: warning: unused variable 'isGray' [-Wunused-variable]
      bool isGray = (srcInfo.flags & BL_FORMAT_FLAG_LUM) != 0;
           ^
../extern/blend2d/src/blend2d/blpixelconverter.cpp:943:12: warning: unused variable 'isGray' [-Wunused-variable]
      bool isGray = (dstInfo.flags & BL_FORMAT_FLAG_LUM) != 0;
           ^
2 warnings generated.
[51/124] Building CXX object extern/blend2d/CMakeFiles/blend2d.dir/src/blend2d/codec/bljpegcodec.cpp.o
../extern/blend2d/src/blend2d/codec/bljpegcodec.cpp:1530:105: warning: unused parameter 'dst' [-Wunused-parameter]
static BLResult BL_CDECL blJpegCodecImplCreateEncoder(const BLImageCodecImpl* impl, BLImageEncoderCore* dst) noexcept {
                                                                                                        ^
../extern/blend2d/src/blend2d/codec/bljpegcodec.cpp:48:22: warning: unused variable 'blJpegExifLE' [-Wunused-const-variable]
static const uint8_t blJpegExifLE[4] = { 0x49, 0x49, 0x2A, 0x00 };
                     ^
../extern/blend2d/src/blend2d/codec/bljpegcodec.cpp:49:22: warning: unused variable 'blJpegExifBE' [-Wunused-const-variable]
static const uint8_t blJpegExifBE[4] = { 0x4D, 0x4D, 0x00, 0x2A };
                     ^
3 warnings generated.
[56/124] Building CXX object extern/blend2d/CMakeFiles/blend2d.dir/src/blend2d/blpathstroke.cpp.o
../extern/blend2d/src/blend2d/blpathstroke.cpp:797:87: warning: unused parameter 'n1' [-Wunused-parameter]
  BL_INLINE BLResult dullRoundJoin(BLPathAppender& out, uint32_t side, const BLPoint& n1, const BLPoint& w1) noexcept {
                                                                                      ^
../extern/blend2d/src/blend2d/blpathstroke.cpp:31:25: warning: unused variable 'BL_STROKE_COLLINEARITY_EPSILON_SQ' [-Wunused-const-variable]
static constexpr double BL_STROKE_COLLINEARITY_EPSILON_SQ = blSquare(BL_STROKE_COLLINEARITY_EPSILON);
                        ^
2 warnings generated.
[61/124] Building CXX object extern/blend2d/CMakeFiles/blend2d.dir/src/blend2d/codec/blpngcodec.cpp.o
../extern/blend2d/src/blend2d/codec/blpngcodec.cpp:1331:104: warning: unused parameter 'dst' [-Wunused-parameter]
static BLResult BL_CDECL blPngCodecImplCreateEncoder(const BLImageCodecImpl* impl, BLImageEncoderCore* dst) noexcept {
                                                                                                       ^
1 warning generated.
[63/124] Building CXX object extern/blend2d/CMakeFiles/blend2d.dir/src/blend2d/opentype/blotcff.cpp.o
../extern/blend2d/src/blend2d/opentype/blotcff.cpp:484:27: warning: unused variable 'kCFFValueStackSizeV2' [-Wunused-const-variable]
static constexpr uint32_t kCFFValueStackSizeV2 = 513;
                          ^
1 warning generated.
[70/124] Building CXX object extern/blend2d/CMakeFiles/blend2d.dir/src/blend2d/opentype/blotlayout.cpp.o
../extern/blend2d/src/blend2d/opentype/blotlayout.cpp:500:12: warning: unused variable 'attachListOffset' [-Wunused-variable]
  uint32_t attachListOffset         = gdef->v1_0()->attachListOffset();
           ^
../extern/blend2d/src/blend2d/opentype/blotlayout.cpp:501:12: warning: unused variable 'ligCaretListOffset' [-Wunused-variable]
  uint32_t ligCaretListOffset       = gdef->v1_0()->ligCaretListOffset();
           ^
../extern/blend2d/src/blend2d/opentype/blotlayout.cpp:503:12: warning: unused variable 'markGlyphSetsDefOffset' [-Wunused-variable]
  uint32_t markGlyphSetsDefOffset   = version >= 0x00010002u ? uint32_t(gdef->v1_2()->markGlyphSetsDefOffset()) : uint32_t(0);
           ^
../extern/blend2d/src/blend2d/opentype/blotlayout.cpp:504:12: warning: unused variable 'itemVarStoreOffset' [-Wunused-variable]
  uint32_t itemVarStoreOffset       = version >= 0x00010003u ? uint32_t(gdef->v1_3()->itemVarStoreOffset()    ) : uint32_t(0);
           ^
../extern/blend2d/src/blend2d/opentype/blotlayout.cpp:1870:12: warning: unused variable 'featureParamsOffset' [-Wunused-variable]
  uint32_t featureParamsOffset = table->featureParamsOffset();
           ^
../extern/blend2d/src/blend2d/opentype/blotlayout.cpp:1924:14: warning: unused variable 'lookupOrderOffset' [-Wunused-variable]
    uint32_t lookupOrderOffset = langSys->lookupOrderOffset();
             ^
../extern/blend2d/src/blend2d/opentype/blotlayout.cpp:1902:12: warning: unused variable 'langSysDefault' [-Wunused-variable]
  uint32_t langSysDefault = table->langSysDefault();
           ^
7 warnings generated.
[75/124] Building CXX object extern/blend2d/CMakeFiles/blend2d.dir/src/blend2d/pipegen/blcompoppart.cpp.o
../extern/blend2d/src/blend2d/pipegen/blcompoppart.cpp:1329:10: warning: unused variable 'useSa' [-Wunused-variable]
    bool useSa = hasSa() || hasMask;
         ^
1 warning generated.
[83/124] Building CXX object extern/blend2d/CMakeFiles/blend2d.dir/src/blend2d/pipegen/blpipegenruntime.cpp.o
../extern/blend2d/src/blend2d/pipegen/blpipegenruntime.cpp:179:114: warning: unused parameter 'cache' [-Wunused-parameter]
static BLPipeFillFunc BL_CDECL blPipeGenRuntimeTest(BLPipeRuntime* self_, uint32_t signature, BLPipeLookupCache* cache) noexcept {
                                                                                                                 ^
1 warning generated.
[107/124] Building CXX object extern/blend2d/CMakeFiles/blend2d.dir/__/asmjit/src/asmjit/core/ralocal.cpp.o
../extern/asmjit/src/asmjit/core/ralocal.cpp:833:79: warning: unused parameter 'cont' [-Wunused-parameter]
Error RALocalAllocator::allocBranch(InstNode* node, RABlock* target, RABlock* cont) noexcept {
                                                                              ^
1 warning generated.
[118/124] Building CXX object extern/blend2d/CMakeFiles/blend2d.dir/__/asmjit/src/asmjit/x86/x86internal.cpp.o
../extern/asmjit/src/asmjit/x86/x86internal.cpp:1318:13: warning: unused function 'dumpAssignment' [-Wunused-function]
static void dumpAssignment(String& sb, const X86FuncArgsContext& ctx) noexcept {

need null checking when calling C API

Null checking might be a good idea in the C interface as it goes through obj->impl->virt->func(ctx)โ€ฆ

Right now it will crash. Maybe those functions should return 'false' and a parameter error? That will really muddy the ease of the interface, but might be necessary? Or just allow to crash as a fail fast, where params checking can happen elsewhere.

image transformation bug

Tried getting-started-4 and the whole image is brown.
I found that translation works, but rotation and scaling are not.

Error loading PNG with transparent pixels

Here is the original PNG file (background is completeley transparent)
RGBtransp.
I simply loaded this PNG using
img.readFromFile("RGBtransp.png")
.. see the full code below.
and here is how it appears, and how it is saved (as a BMP), here post-converted to PNG just for attaching it in this post)
resultingRGB

I guess the bug is within readFromFile() method and not in the writeToFile() method, because (using another app) I can see the (wrong) image just loaded.
I used the following code.

int main(int argc, char* argv[]) {
  BLImage img;
  BLResult err = img.readFromFile("RGBtransp.png");
  if (err) {
    printf("Failed to load a texture (err=%u)\n", err);
    return 1;
  }
  BLContext ctx(img);
  ctx.end();

  BLImageCodec codec;
  codec.findByName("BMP");
  img.writeToFile("resultingRGBtransp.bmp", codec);

  return 0;
}

Color issues in latest beta release

The latest release on master seems to have broken something related to colors, as is shown on this sample:

let img = BLImage(width: 480, height: 480, format: .prgb32)

let ctx = BLContext(image: img)!

// Fill a white background
ctx.compOp = .sourceCopy
ctx.setFillStyleRgba32(0xFFFFFFFF)
ctx.fillAll()

// Fill a centered blue box
ctx.compOp = .sourceOver
ctx.setFillStyleRgba32(0xFF0000FF)
ctx.fillRect(BLRect(x: 100, y: 100, w: 280, h: 280))

ctx.end()

I expect a blue rectangle to be produced, but instead I get a black box:

snappy

The standard tiger sample also shows wrong colors now:

tiger_actual

Odd behavior when subtracting box inside another box with BLRegion

When applying the following operations to an empty BLRegion:

region.combine(box: BLBoxI(x0: 0, y0: 0, x1: 100, y1: 100), operation: BL_BOOLEAN_OP_OR)
region.combine(box: BLBoxI(x0: 25, y0: 25, x1: 75, y1: 75), operation: BL_BOOLEAN_OP_SUB)

I expected the resulting region to contain four rectangles, outlining an empty square of size 50x50 within, but the result only contains three rectangles with a missing right rectangle:

impl->view:

[
    BLBoxI(x0: 0, y0: 0, x1: 100, y1: 25),
    BLBoxI(x0: 0, y0: 25, x1: 25, y1: 75),
    BLBoxI(x0: 0, y0: 75, x1: 100, y1: 100)
]

Illustrated diagram:

Untitled Diagram

Wrong saved BMPs

Please see the attached BMP within the zip file.
They have been generated with the same simple app (see below), a variant of "bl-getting-started-1.cpp"
The bmp generated from a BLImage sized 330x330 (XRGB32) is wrong,
The bmp generated from a BLImage sized 332x332 (XRGB32) iscorrect,

I guess it's because the image-width (330) is not divisible by 4.
Note that a BLImage 300x300 (PRGB32) is saved correctly.

#include <blend2d.h>

int main(int argc, char* argv[]) {
// BLImage img(332, 332, BL_FORMAT_XRGB32); // OK
// BLImage img(330, 330, BL_FORMAT_PRGB32); // OK

BLImage img(330, 330, BL_FORMAT_XRGB32); // wrong saved image

BLContext ctx(img);
// Clear the image.
ctx.setCompOp(BL_COMP_OP_SRC_COPY);
ctx.fillAll();

ctx.setFillStyle(BLRgba32(0xFFFF0000)); //red
ctx.fillCircle(100,100,50);

// Detach the rendering context from img.
ctx.end();

// Let's use some built-in codecs provided by Blend2D.
BLImageCodec codec;
codec.findByName("BMP");
img.writeToFile("bl-getting-started-1x.bmp", codec);

return 0;
}

XRGB-test.zip

Performance Comparison

As far I remember, NVidia peoples of the NVPath project did interesting comparisons with libraries like Cairo etc. especially cases where rendering was wrong.

Also it would be interesting to benchmark on GPU-less ARM platforms, Qt do benchmark driven optimisations on such platforms (with or no OpenGL GPU).

Edited by Blend2D Team:

Performance comparison of the following libraries would be welcome:

  • SKIA
  • Direct2D
  • FastUIDraw

C++ Api: BLContext missing fillArc function

While looking through the C++ API for recreating it in Rust I noticed that BLContext seems to be missing an implementation for fillArc, it has the chord and pie functions

//! Fills a chord.
BL_INLINE BLResult fillChord(const BLArc& chord) noexcept { return fillGeometry(BL_GEOMETRY_TYPE_CHORD, &chord); }
//! \overload
BL_INLINE BLResult fillChord(double cx, double cy, double r, double start, double sweep) noexcept { return fillChord(BLArc(cx, cy, r, r, start, sweep)); }
//! \overload
BL_INLINE BLResult fillChord(double cx, double cy, double rx, double ry, double start, double sweep) noexcept { return fillChord(BLArc(cx, cy, rx, ry, start, sweep)); }
//! Fills a pie.
BL_INLINE BLResult fillPie(const BLArc& pie) noexcept { return fillGeometry(BL_GEOMETRY_TYPE_PIE, &pie); }
//! \overload
BL_INLINE BLResult fillPie(double cx, double cy, double r, double start, double sweep) noexcept { return fillPie(BLArc(cx, cy, r, r, start, sweep)); }
//! \overload
BL_INLINE BLResult fillPie(double cx, double cy, double rx, double ry, double start, double sweep) noexcept { return fillPie(BLArc(cx, cy, rx, ry, start, sweep)); }

and it has all three, arc, pie and chord functions for strokes.
//! Strokes an arc.
BL_INLINE BLResult strokeArc(const BLArc& arc) noexcept { return strokeGeometry(BL_GEOMETRY_TYPE_ARC, &arc); }
//! \overload
BL_INLINE BLResult strokeArc(double cx, double cy, double r, double start, double sweep) noexcept { return strokeArc(BLArc(cx, cy, r, r, start, sweep)); }
//! \overload
BL_INLINE BLResult strokeArc(double cx, double cy, double rx, double ry, double start, double sweep) noexcept { return strokeArc(BLArc(cx, cy, rx, ry, start, sweep)); }
//! Strokes a chord.
BL_INLINE BLResult strokeChord(const BLArc& chord) noexcept { return strokeGeometry(BL_GEOMETRY_TYPE_CHORD, &chord); }
//! \overload
BL_INLINE BLResult strokeChord(double cx, double cy, double r, double start, double sweep) noexcept { return strokeChord(BLArc(cx, cy, r, r, start, sweep)); }
//! \overload
BL_INLINE BLResult strokeChord(double cx, double cy, double rx, double ry, double start, double sweep) noexcept { return strokeChord(BLArc(cx, cy, rx, ry, start, sweep)); }
//! Strokes a pie.
BL_INLINE BLResult strokePie(const BLArc& pie) noexcept { return strokeGeometry(BL_GEOMETRY_TYPE_PIE, &pie); }
//! \overload
BL_INLINE BLResult strokePie(double cx, double cy, double r, double start, double sweep) noexcept { return strokePie(BLArc(cx, cy, r, r, start, sweep)); }
//! \overload
BL_INLINE BLResult strokePie(double cx, double cy, double rx, double ry, double start, double sweep) noexcept { return strokePie(BLArc(cx, cy, rx, ry, start, sweep)); }

So I assume this one has been forgotten?

blGradientEquals returning inverted result when implementations arent the same object

Found a small error in the the function which gives you the opposite bool result when the implementation pointers arent equal and the stops are equal the function returns true if one of the other fields are unequal and false if they are all equal.

bool blGradientEquals(const BLGradientCore* a, const BLGradientCore* b) noexcept {
const BLGradientImpl* aI = a->impl;
const BLGradientImpl* bI = b->impl;
if (aI == bI)
return true;
size_t size = aI->size;
bool eq = ((aI->gradientType != bI->gradientType) |
(aI->extendMode != bI->extendMode ) |
(aI->matrixType != bI->matrixType ) |
(aI->matrix != bI->matrix ) |
(size != bI->size ));
return eq && memcmp(aI->stops, bI->stops, size * sizeof(BLGradientStop)) == 0;
}

blFontGetTextMetrics() & blFontGetGlyphBounds() fails with OTF/CFF fonts

I happened to have a handful of OTF fonts installed. I seem to get the same behavior with all of them: in the Glyph Buffer sample, the call to font.getTextMetrics() results in an empty bounding box and returns an error originating from within the BL_PROPAGATE( blFontGetGlyphBounds() ) in blFontGetTextMetrics(). It does seem to render the font though. Attaching one of the fonts.
font.zip

need a pen that does not scale

I have been trying to draw some graphics that are designed for a unit 1x1 and then scaled to the appropriate size at runtime. The challenge I have is the pen needs to be a hairline pen (one pixel wide no matter the scaling). Right now, the pen will scale with the scaling of the graphic, and I don't see a way to specify 'no-scaling' for the pen.

One way I've found is to specify a strokeWidth that is a fraction of the intended size, so it almost looks right, but that's not ideal as the x and y scaling will be applied differently, and I don't know at design time what scale something is going to have.

Is there a way to more accurately controle the stroke in this way?

Maybe image read/write could be a separate thing

I don't find image reading/writing to be an integral part of a 2D graphics library. Maybe this could be a separate 'module'. That way it can easily be added onto (I want Targa), without waiting on the primary library?

As long as it's there, it would be nice to be able to write the format into a memory buffer, or into a 'stream-like' interface. In many of my scenarios, I want to stream an image across the net for live viewing, and writing it into a file first isn't necessary. Maybe the file writing could simply use this 'write to buffer' thing, or simply be separate (yah, not everyone wants to create a buffer just to write in a particular format).

At any rate, more of a suggestion than 'issue'.

Feature Request: set a BLPath using the SVG geometry specification

I think it might be useful to add an alternative mode for building a BLPath, using the SVG notation:
something like
blPath->set("M 10 20 C -0 20 .......")
and conversely, get the path geometry as an equivalent internal string (.. just using the M, L, C, Q, Z 'commands' ...)

Feature parity with Cairo?

Does this have complete feature parity with Cairo for drawing operations? I would be interested in integrating it with Rust via the C API.

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.