thorvg: Update to 0.15.13
This commit is contained in:
3
thirdparty/README.md
vendored
3
thirdparty/README.md
vendored
@ -970,7 +970,7 @@ Patches:
|
|||||||
## thorvg
|
## thorvg
|
||||||
|
|
||||||
- Upstream: https://github.com/thorvg/thorvg
|
- Upstream: https://github.com/thorvg/thorvg
|
||||||
- Version: 0.15.12 (91bd6f35b94e92abfc1a320632e66cd124943524, 2025)
|
- Version: 0.15.13 (c597365b99f27cb46e2a5ac2942da45bb73d5a55, 2025)
|
||||||
- License: MIT
|
- License: MIT
|
||||||
|
|
||||||
Files extracted from upstream source:
|
Files extracted from upstream source:
|
||||||
@ -981,7 +981,6 @@ Files extracted from upstream source:
|
|||||||
Patches:
|
Patches:
|
||||||
|
|
||||||
- `0001-revert-tvglines-bezier-precision.patch` (GH-96658)
|
- `0001-revert-tvglines-bezier-precision.patch` (GH-96658)
|
||||||
- `0002-png-explicit-variable-scope.patch` (GH-105093)
|
|
||||||
|
|
||||||
|
|
||||||
## tinyexr
|
## tinyexr
|
||||||
|
|||||||
2
thirdparty/thorvg/inc/config.h
vendored
2
thirdparty/thorvg/inc/config.h
vendored
@ -15,5 +15,5 @@
|
|||||||
// For internal debugging:
|
// For internal debugging:
|
||||||
//#define THORVG_LOG_ENABLED
|
//#define THORVG_LOG_ENABLED
|
||||||
|
|
||||||
#define THORVG_VERSION_STRING "0.15.12"
|
#define THORVG_VERSION_STRING "0.15.13"
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@ -1,13 +0,0 @@
|
|||||||
diff --git a/thirdparty/thorvg/src/loaders/external_png/tvgPngLoader.cpp b/thirdparty/thorvg/src/loaders/external_png/tvgPngLoader.cpp
|
|
||||||
index 71bf25a62b..c362403125 100644
|
|
||||||
--- a/thirdparty/thorvg/src/loaders/external_png/tvgPngLoader.cpp
|
|
||||||
+++ b/thirdparty/thorvg/src/loaders/external_png/tvgPngLoader.cpp
|
|
||||||
@@ -88,7 +88,7 @@ bool PngLoader::read()
|
|
||||||
|
|
||||||
if (w == 0 || h == 0) return false;
|
|
||||||
|
|
||||||
- if (cs == ColorSpace::ARGB8888 || cs == ColorSpace::ARGB8888S) {
|
|
||||||
+ if (ImageLoader::cs == ColorSpace::ARGB8888 || ImageLoader::cs == ColorSpace::ARGB8888S) {
|
|
||||||
image->format = PNG_FORMAT_BGRA;
|
|
||||||
surface.cs = ColorSpace::ARGB8888S;
|
|
||||||
} else {
|
|
||||||
@ -132,7 +132,7 @@ bool JpgLoader::read()
|
|||||||
|
|
||||||
//determine the image format
|
//determine the image format
|
||||||
TJPF format;
|
TJPF format;
|
||||||
if (cs == ColorSpace::ARGB8888 || cs == ColorSpace::ARGB8888S) {
|
if (ImageLoader::cs == ColorSpace::ARGB8888 || ImageLoader::cs == ColorSpace::ARGB8888S) {
|
||||||
format = TJPF_BGRX;
|
format = TJPF_BGRX;
|
||||||
surface.cs = ColorSpace::ARGB8888;
|
surface.cs = ColorSpace::ARGB8888;
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@ -3368,6 +3368,13 @@ static void _svgLoaderParserXmlClose(SvgLoaderData* loader, const char* content,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (unsigned int i = 0; i < sizeof(gradientTags) / sizeof(gradientTags[0]); i++) {
|
||||||
|
if (!strncmp(tagName, gradientTags[i].tag, sz)) {
|
||||||
|
loader->gradientStack.pop();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for (unsigned int i = 0; i < sizeof(graphicsTags) / sizeof(graphicsTags[0]); i++) {
|
for (unsigned int i = 0; i < sizeof(graphicsTags) / sizeof(graphicsTags[0]); i++) {
|
||||||
if (!strncmp(tagName, graphicsTags[i].tag, sz)) {
|
if (!strncmp(tagName, graphicsTags[i].tag, sz)) {
|
||||||
loader->currentGraphicsNode = nullptr;
|
loader->currentGraphicsNode = nullptr;
|
||||||
@ -3439,7 +3446,6 @@ static void _svgLoaderParserXmlOpen(SvgLoaderData* loader, const char* content,
|
|||||||
if (node->type != SvgNodeType::Defs || !empty) {
|
if (node->type != SvgNodeType::Defs || !empty) {
|
||||||
loader->stack.push(node);
|
loader->stack.push(node);
|
||||||
}
|
}
|
||||||
loader->latestGradient = nullptr;
|
|
||||||
} else if ((method = _findGraphicsFactory(tagName))) {
|
} else if ((method = _findGraphicsFactory(tagName))) {
|
||||||
if (loader->stack.count > 0) parent = loader->stack.last();
|
if (loader->stack.count > 0) parent = loader->stack.last();
|
||||||
else parent = loader->doc;
|
else parent = loader->doc;
|
||||||
@ -3450,24 +3456,26 @@ static void _svgLoaderParserXmlOpen(SvgLoaderData* loader, const char* content,
|
|||||||
loader->stack.push(defs);
|
loader->stack.push(defs);
|
||||||
loader->currentGraphicsNode = node;
|
loader->currentGraphicsNode = node;
|
||||||
}
|
}
|
||||||
loader->latestGradient = nullptr;
|
|
||||||
} else if ((gradientMethod = _findGradientFactory(tagName))) {
|
} else if ((gradientMethod = _findGradientFactory(tagName))) {
|
||||||
SvgStyleGradient* gradient;
|
SvgStyleGradient* gradient;
|
||||||
gradient = gradientMethod(loader, attrs, attrsLength);
|
gradient = gradientMethod(loader, attrs, attrsLength);
|
||||||
//FIXME: The current parsing structure does not distinguish end tags.
|
//Gradients do not allow nested declarations, so only the earliest declared Gradient is valid.
|
||||||
// There is no way to know if the currently parsed gradient is in defs.
|
if (loader->gradientStack.count == 0) {
|
||||||
// If a gradient is declared outside of defs after defs is set, it is included in the gradients of defs.
|
//FIXME: The current parsing structure does not distinguish end tags.
|
||||||
// But finally, the loader has a gradient style list regardless of defs.
|
// There is no way to know if the currently parsed gradient is in defs.
|
||||||
// This is only to support this when multiple gradients are declared, even if no defs are declared.
|
// If a gradient is declared outside of defs after defs is set, it is included in the gradients of defs.
|
||||||
// refer to: https://developer.mozilla.org/en-US/docs/Web/SVG/Element/defs
|
// But finally, the loader has a gradient style list regardless of defs.
|
||||||
if (loader->def && loader->doc->node.doc.defs) {
|
// This is only to support this when multiple gradients are declared, even if no defs are declared.
|
||||||
loader->def->node.defs.gradients.push(gradient);
|
// refer to: https://developer.mozilla.org/en-US/docs/Web/SVG/Element/defs
|
||||||
} else {
|
if (loader->def && loader->doc->node.doc.defs) {
|
||||||
loader->gradients.push(gradient);
|
loader->def->node.defs.gradients.push(gradient);
|
||||||
|
} else {
|
||||||
|
loader->gradients.push(gradient);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
loader->latestGradient = gradient;
|
if (!empty) loader->gradientStack.push(gradient);
|
||||||
} else if (!strcmp(tagName, "stop")) {
|
} else if (!strcmp(tagName, "stop")) {
|
||||||
if (!loader->latestGradient) {
|
if (loader->gradientStack.count == 0) {
|
||||||
TVGLOG("SVG", "Stop element is used outside of the Gradient element");
|
TVGLOG("SVG", "Stop element is used outside of the Gradient element");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -3475,9 +3483,8 @@ static void _svgLoaderParserXmlOpen(SvgLoaderData* loader, const char* content,
|
|||||||
loader->svgParse->gradStop = {0.0f, 0, 0, 0, 255};
|
loader->svgParse->gradStop = {0.0f, 0, 0, 0, 255};
|
||||||
loader->svgParse->flags = SvgStopStyleFlags::StopDefault;
|
loader->svgParse->flags = SvgStopStyleFlags::StopDefault;
|
||||||
simpleXmlParseAttributes(attrs, attrsLength, _attrParseStops, loader);
|
simpleXmlParseAttributes(attrs, attrsLength, _attrParseStops, loader);
|
||||||
loader->latestGradient->stops.push(loader->svgParse->gradStop);
|
loader->gradientStack.last()->stops.push(loader->svgParse->gradStop);
|
||||||
} else {
|
} else {
|
||||||
loader->latestGradient = nullptr;
|
|
||||||
if (!isIgnoreUnsupportedLogElements(tagName)) TVGLOG("SVG", "Unsupported elements used [Elements: %s]", tagName);
|
if (!isIgnoreUnsupportedLogElements(tagName)) TVGLOG("SVG", "Unsupported elements used [Elements: %s]", tagName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -577,7 +577,7 @@ struct SvgLoaderData
|
|||||||
SvgNode* def = nullptr; //also used to store nested graphic nodes
|
SvgNode* def = nullptr; //also used to store nested graphic nodes
|
||||||
SvgNode* cssStyle = nullptr;
|
SvgNode* cssStyle = nullptr;
|
||||||
Array<SvgStyleGradient*> gradients;
|
Array<SvgStyleGradient*> gradients;
|
||||||
SvgStyleGradient* latestGradient = nullptr; //For stops
|
Array<SvgStyleGradient*> gradientStack; //For stops
|
||||||
SvgParser* svgParse = nullptr;
|
SvgParser* svgParse = nullptr;
|
||||||
Array<SvgNodeIdPair> cloneNodes;
|
Array<SvgNodeIdPair> cloneNodes;
|
||||||
Array<SvgNodeIdPair> nodesToStyle;
|
Array<SvgNodeIdPair> nodesToStyle;
|
||||||
|
|||||||
@ -752,12 +752,22 @@ static unique_ptr<Scene> _useBuildHelper(SvgLoaderData& loaderData, const SvgNod
|
|||||||
}
|
}
|
||||||
viewBoxClip->transform(mClipTransform);
|
viewBoxClip->transform(mClipTransform);
|
||||||
|
|
||||||
scene->clip(std::move(viewBoxClip));
|
auto clippingLayer = Scene::gen();
|
||||||
|
clippingLayer->clip(std::move(viewBoxClip));
|
||||||
|
clippingLayer->push(std::move(scene));
|
||||||
|
return clippingLayer;
|
||||||
}
|
}
|
||||||
} else {
|
return scene;
|
||||||
scene->transform(mUseTransform);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (auto clipper = scene->Paint::pImpl->clipper) {
|
||||||
|
auto clipTransform = clipper->transform();
|
||||||
|
Matrix inv;
|
||||||
|
if (node->transform && inverse(node->transform, &inv)) clipTransform = inv * clipTransform;
|
||||||
|
clipper->transform(mUseTransform * clipTransform);
|
||||||
|
}
|
||||||
|
|
||||||
|
scene->transform(mUseTransform);
|
||||||
return scene;
|
return scene;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -118,50 +118,41 @@ struct SwShapeTask : SwTask
|
|||||||
|
|
||||||
auto strokeWidth = validStrokeWidth();
|
auto strokeWidth = validStrokeWidth();
|
||||||
SwBBox renderRegion{};
|
SwBBox renderRegion{};
|
||||||
auto visibleFill = false;
|
auto updateShape = flags & (RenderUpdateFlag::Path | RenderUpdateFlag::Transform | RenderUpdateFlag::Clip);
|
||||||
|
auto updateFill = false;
|
||||||
//This checks also for the case, if the invisible shape turned to visible by alpha.
|
|
||||||
auto prepareShape = !shapePrepared(&shape) && flags & (RenderUpdateFlag::Color | RenderUpdateFlag::Gradient);
|
|
||||||
|
|
||||||
//Shape
|
//Shape
|
||||||
if (flags & (RenderUpdateFlag::Path | RenderUpdateFlag::Transform) || prepareShape) {
|
if (updateShape || flags & (RenderUpdateFlag::Color | RenderUpdateFlag::Gradient)) {
|
||||||
uint8_t alpha = 0;
|
uint8_t alpha = 0;
|
||||||
rshape->fillColor(nullptr, nullptr, nullptr, &alpha);
|
rshape->fillColor(nullptr, nullptr, nullptr, &alpha);
|
||||||
alpha = MULTIPLY(alpha, opacity);
|
updateFill = (MULTIPLY(alpha, opacity) || rshape->fill);
|
||||||
visibleFill = (alpha > 0 || rshape->fill);
|
if (updateShape) shapeReset(&shape);
|
||||||
shapeReset(&shape);
|
if (updateFill || clipper) {
|
||||||
if (visibleFill || clipper) {
|
if (shapePrepare(&shape, rshape, transform, bbox, renderRegion, mpool, tid, clips.count > 0 ? true : false)) {
|
||||||
if (!shapePrepare(&shape, rshape, transform, bbox, renderRegion, mpool, tid, clips.count > 0 ? true : false)) {
|
if (!shapeGenRle(&shape, rshape, antialiasing(strokeWidth))) goto err;
|
||||||
visibleFill = false;
|
} else {
|
||||||
|
updateFill = false;
|
||||||
renderRegion.reset();
|
renderRegion.reset();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//Fill
|
//Fill
|
||||||
if (flags & (RenderUpdateFlag::Path |RenderUpdateFlag::Gradient | RenderUpdateFlag::Transform | RenderUpdateFlag::Color)) {
|
if (updateFill) {
|
||||||
if (visibleFill || clipper) {
|
|
||||||
if (!shapeGenRle(&shape, rshape, antialiasing(strokeWidth))) goto err;
|
|
||||||
}
|
|
||||||
if (auto fill = rshape->fill) {
|
if (auto fill = rshape->fill) {
|
||||||
auto ctable = (flags & RenderUpdateFlag::Gradient) ? true : false;
|
auto ctable = (flags & RenderUpdateFlag::Gradient) ? true : false;
|
||||||
if (ctable) shapeResetFill(&shape);
|
if (ctable) shapeResetFill(&shape);
|
||||||
if (!shapeGenFillColors(&shape, fill, transform, surface, opacity, ctable)) goto err;
|
if (!shapeGenFillColors(&shape, fill, transform, surface, opacity, ctable)) goto err;
|
||||||
} else {
|
|
||||||
shapeDelFill(&shape);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//Stroke
|
//Stroke
|
||||||
if (flags & (RenderUpdateFlag::Path | RenderUpdateFlag::Stroke | RenderUpdateFlag::Transform)) {
|
if (updateShape || flags & RenderUpdateFlag::Stroke) {
|
||||||
if (strokeWidth > 0.0f) {
|
if (strokeWidth > 0.0f) {
|
||||||
shapeResetStroke(&shape, rshape, transform);
|
shapeResetStroke(&shape, rshape, transform);
|
||||||
|
|
||||||
if (!shapeGenStrokeRle(&shape, rshape, transform, bbox, renderRegion, mpool, tid)) goto err;
|
if (!shapeGenStrokeRle(&shape, rshape, transform, bbox, renderRegion, mpool, tid)) goto err;
|
||||||
if (auto fill = rshape->strokeFill()) {
|
if (auto fill = rshape->strokeFill()) {
|
||||||
auto ctable = (flags & RenderUpdateFlag::GradientStroke) ? true : false;
|
auto ctable = (flags & RenderUpdateFlag::GradientStroke) ? true : false;
|
||||||
if (ctable) shapeResetStrokeFill(&shape);
|
if (ctable) shapeResetStrokeFill(&shape);
|
||||||
if (!shapeGenStrokeFillColors(&shape, fill, transform, surface, opacity, ctable)) goto err;
|
if (!shapeGenStrokeFillColors(&shape, fill, transform, surface, opacity, ctable)) goto err;
|
||||||
} else {
|
|
||||||
shapeDelStrokeFill(&shape);
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
shapeDelStroke(&shape);
|
shapeDelStroke(&shape);
|
||||||
|
|||||||
@ -1025,7 +1025,7 @@ void rleFree(SwRle* rle)
|
|||||||
bool rleClip(SwRle *rle, const SwRle *clip)
|
bool rleClip(SwRle *rle, const SwRle *clip)
|
||||||
{
|
{
|
||||||
if (rle->size == 0 || clip->size == 0) return false;
|
if (rle->size == 0 || clip->size == 0) return false;
|
||||||
auto spanCnt = rle->size > clip->size ? rle->size : clip->size;
|
auto spanCnt = 2 * (rle->size > clip->size ? rle->size : clip->size); //factor 2 added for safety (no real cases observed where the factor exceeded 1.4)
|
||||||
auto spans = static_cast<SwSpan*>(malloc(sizeof(SwSpan) * (spanCnt)));
|
auto spans = static_cast<SwSpan*>(malloc(sizeof(SwSpan) * (spanCnt)));
|
||||||
auto spansEnd = _intersectSpansRegion(clip, rle, spans, spanCnt);
|
auto spansEnd = _intersectSpansRegion(clip, rle, spans, spanCnt);
|
||||||
|
|
||||||
|
|||||||
15
thirdparty/thorvg/src/renderer/tvgPaint.cpp
vendored
15
thirdparty/thorvg/src/renderer/tvgPaint.cpp
vendored
@ -276,16 +276,19 @@ RenderData Paint::Impl::update(RenderMethod* renderer, const Matrix& pm, Array<R
|
|||||||
|
|
||||||
/* 2. Clipping */
|
/* 2. Clipping */
|
||||||
if (this->clipper) {
|
if (this->clipper) {
|
||||||
P(this->clipper)->ctxFlag &= ~ContextFlag::FastTrack; //reset
|
auto pclip = P(this->clipper);
|
||||||
|
if (pclip->renderFlag | static_cast<Shape*>(this->clipper)->pImpl->rFlag) renderFlag |= RenderUpdateFlag::Clip;
|
||||||
|
pclip->ctxFlag &= ~ContextFlag::FastTrack; //reset
|
||||||
viewport = renderer->viewport();
|
viewport = renderer->viewport();
|
||||||
/* TODO: Intersect the clipper's clipper, if both are FastTrack.
|
/* TODO: Intersect the clipper's clipper, if both are FastTrack.
|
||||||
Update the subsequent clipper first and check its ctxFlag. */
|
Update the subsequent clipper first and check its ctxFlag. */
|
||||||
if (!P(this->clipper)->clipper && (compFastTrack = _compFastTrack(renderer, this->clipper, pm, viewport)) == Result::Success) {
|
if (!pclip->clipper && _compFastTrack(renderer, this->clipper, pm, viewport) == Result::Success) {
|
||||||
P(this->clipper)->ctxFlag |= ContextFlag::FastTrack;
|
pclip->ctxFlag |= ContextFlag::FastTrack;
|
||||||
}
|
compFastTrack = Result::Success;
|
||||||
if (compFastTrack == Result::InsufficientCondition) {
|
} else {
|
||||||
trd = P(this->clipper)->update(renderer, pm, clips, 255, pFlag, true);
|
trd = pclip->update(renderer, pm, clips, 255, pFlag, true);
|
||||||
clips.push(trd);
|
clips.push(trd);
|
||||||
|
compFastTrack = Result::InsufficientCondition;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
2
thirdparty/thorvg/src/renderer/tvgPaint.h
vendored
2
thirdparty/thorvg/src/renderer/tvgPaint.h
vendored
@ -72,8 +72,8 @@ namespace tvg
|
|||||||
tvg::rotate(&m, degree);
|
tvg::rotate(&m, degree);
|
||||||
}
|
}
|
||||||
} tr;
|
} tr;
|
||||||
|
RenderUpdateFlag renderFlag = RenderUpdateFlag::None;
|
||||||
BlendMethod blendMethod;
|
BlendMethod blendMethod;
|
||||||
uint8_t renderFlag;
|
|
||||||
uint8_t ctxFlag;
|
uint8_t ctxFlag;
|
||||||
uint8_t opacity;
|
uint8_t opacity;
|
||||||
uint8_t refCnt = 0; //reference count
|
uint8_t refCnt = 0; //reference count
|
||||||
|
|||||||
7
thirdparty/thorvg/src/renderer/tvgRender.h
vendored
7
thirdparty/thorvg/src/renderer/tvgRender.h
vendored
@ -37,9 +37,14 @@ using pixel_t = uint32_t;
|
|||||||
|
|
||||||
#define DASH_PATTERN_THRESHOLD 0.001f
|
#define DASH_PATTERN_THRESHOLD 0.001f
|
||||||
|
|
||||||
enum RenderUpdateFlag : uint8_t {None = 0, Path = 1, Color = 2, Gradient = 4, Stroke = 8, Transform = 16, Image = 32, GradientStroke = 64, Blend = 128, All = 255};
|
enum RenderUpdateFlag : uint16_t {None = 0, Path = 1, Color = 2, Gradient = 4, Stroke = 8, Transform = 16, Image = 32, GradientStroke = 64, Blend = 128, Clip = 256, All = 0xffff};
|
||||||
enum CompositionFlag : uint8_t {Invalid = 0, Opacity = 1, Blending = 2, Masking = 4, PostProcessing = 8}; //Composition Purpose
|
enum CompositionFlag : uint8_t {Invalid = 0, Opacity = 1, Blending = 2, Masking = 4, PostProcessing = 8}; //Composition Purpose
|
||||||
|
|
||||||
|
static inline void operator|=(RenderUpdateFlag& a, const RenderUpdateFlag b)
|
||||||
|
{
|
||||||
|
a = RenderUpdateFlag(uint16_t(a) | uint16_t(b));
|
||||||
|
}
|
||||||
|
|
||||||
//TODO: Move this in public header unifying with SwCanvas::Colorspace
|
//TODO: Move this in public header unifying with SwCanvas::Colorspace
|
||||||
enum ColorSpace : uint8_t
|
enum ColorSpace : uint8_t
|
||||||
{
|
{
|
||||||
|
|||||||
2
thirdparty/thorvg/src/renderer/tvgShape.h
vendored
2
thirdparty/thorvg/src/renderer/tvgShape.h
vendored
@ -33,7 +33,7 @@ struct Shape::Impl
|
|||||||
RenderShape rs; //shape data
|
RenderShape rs; //shape data
|
||||||
RenderData rd = nullptr; //engine data
|
RenderData rd = nullptr; //engine data
|
||||||
Shape* shape;
|
Shape* shape;
|
||||||
uint8_t rFlag = RenderUpdateFlag::None;
|
RenderUpdateFlag rFlag = RenderUpdateFlag::None;
|
||||||
uint8_t cFlag = CompositionFlag::Invalid;
|
uint8_t cFlag = CompositionFlag::Invalid;
|
||||||
uint8_t opacity; //for composition
|
uint8_t opacity; //for composition
|
||||||
|
|
||||||
|
|||||||
2
thirdparty/thorvg/update-thorvg.sh
vendored
2
thirdparty/thorvg/update-thorvg.sh
vendored
@ -1,6 +1,6 @@
|
|||||||
#!/bin/bash -e
|
#!/bin/bash -e
|
||||||
|
|
||||||
VERSION=0.15.12
|
VERSION=0.15.13
|
||||||
# Uncomment and set a git hash to use specific commit instead of tag.
|
# Uncomment and set a git hash to use specific commit instead of tag.
|
||||||
#GIT_COMMIT=
|
#GIT_COMMIT=
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user