2D Vector Graphics Powered by a JIT Compiler.
- Official Home Page (blend2d.com)
- Official Repository (blend2d/blend2d)
- Public Chat Channel
- Zlib License
See blend2d.com page for more details.
- Download Page provides a list of Blend2D bindings
2D Vector Graphics Engine Powered by a JIT Compiler
Home Page: https://blend2d.com
License: zlib License
2D Vector Graphics Powered by a JIT Compiler.
See blend2d.com page for more details.
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!
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?
This is not an issue, more like a progress report.
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.
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)
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()
Hi,
I'm sure you've already noticed, but in case you haven't, the certificate for blend2d.com expired yesterday.
Is there future intent for BLPath::fitTo
's fitFlags
parameter? If not, would its removal be considered?
I wasn't able to find any uin32_t flags in the docs, eventually had to dig up the source, and found the parameter isn't honored right now: https://github.com/blend2d/blend2d/blob/master/src/blend2d/blpath.cpp#L1912
p.s. Super wonderful project :) Very impressed with it overall.
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.
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;
uint32_t
by bl_enum<XXX>
where XXX is the required enum type.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).
It seems like capacity checking is performed, but it never properly grows because it's always grown to the current size
value (which is zero, for uninitialized BLRegion
s) in blRegionAssignValidBoxIArray
:
https://github.com/blend2d/blend2d/blob/master/src/blend2d/blregion.cpp#L470
Seems like it also happens with its overloaded definition just bellow, too.
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.
At the moment EdgeBuilder cannot handle NaN values properly in the following cases:
The thing is that we do not handle NaNs at BLPath
level, because of the following reasons:
BLPath
, so Blend2D can never trust user input or BLPath
content for security reasonsThe 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:
BL_ERROR_INVALID_GEOMETRY
errorI 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.
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.
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
It looks like you forgot -lasmjit when you linked the library.
asmjit is installed from the FreeBSD package (that I just created).
It should be able to link to the library from there properly.
Yuri
Does Blend2D support Harfbuzz for layout of international text ?
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.
The following line causes a build error due to an undefined member DST_PTR
when instantiating the template over BLFixedPipe_Composite_PRGB32_Src_Solid
:
I'm assuming this was supposed to read DST_BPP
, like above?
Caught this while messing around with the source code.
Is Blend2D a thing? Where is the source located? Was looking to try it out.
Is fillAll supposed to be equivalent with cairo_paint?
If yes, then Blend2D doesn't respect any transformation like this cairo sample.
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:
the test case:
the checker image to use with the test case:
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.
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
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.
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.
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.
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).
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.
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.
I'm assuming the angle
property of BLConicalGradientValues
is meant to affect the angle of the resulting conical gradient, but it seems like it does nothing.
I've checked and it seems like this value is read here: https://github.com/blend2d/blend2d/blob/master/src/blend2d/blpipedefs.cpp#L506
but it doesn't seem to use that variable at all.
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
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?
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
I got error 65577
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.
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 {
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.
Tried getting-started-4 and the whole image is brown.
I found that translation works, but rotation and scaling are not.
Here is the original PNG file (background is completeley transparent)
.
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)
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;
}
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:
The standard tiger sample also shows wrong colors now:
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:
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,
#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);
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:
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
blend2d/src/blend2d/blcontext.h
Lines 1011 to 1023 in 1087ac5
blend2d/src/blend2d/blcontext.h
Lines 1158 to 1177 in 1087ac5
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.
blend2d/src/blend2d/blgradient.cpp
Lines 980 to 994 in df7b257
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
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?
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'.
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' ...)
Does this have complete feature parity with Cairo for drawing operations? I would be interested in integrating it with Rust via the C API.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.