lottie: revise the masking logic

- Allow the masking data even though they were mask None mode.
  Those will be used by the layer stroke effect.
- Fixed masking Offset to apply to all masking chains.
- Optimized fast track masking with resolving the opaicty condition.
- Clean up the overall code.
This commit is contained in:
Hermet Park 2024-12-18 15:29:58 +09:00 committed by Hermet Park
parent 29bf244265
commit 838785d75a
2 changed files with 52 additions and 62 deletions

View file

@ -1224,32 +1224,58 @@ void LottieBuilder::updateMaskings(LottieLayer* layer, float frameNo)
{ {
if (layer->masks.count == 0) return; if (layer->masks.count == 0) return;
//Apply the base mask Shape* pShape = nullptr;
auto pMask = static_cast<LottieMask*>(layer->masks[0]); MaskMethod pMethod;
auto pMethod = pMask->method; uint8_t pOpacity;
auto opacity = pMask->opacity(frameNo);
auto expand = pMask->expand(frameNo);
auto pShape = layer->pooling(); for (auto m = layer->masks.begin(); m < layer->masks.end(); ++m) {
pShape->reset(); auto mask = *m;
pShape->fill(255, 255, 255, opacity); if (mask->method == MaskMethod::None) continue;
pShape->transform(layer->cache.matrix);
//Apply Masking Expansion (Offset) auto method = mask->method;
if (expand == 0.0f) { auto opacity = mask->opacity(frameNo);
pMask->pathset(frameNo, SHAPE(pShape)->rs.path.cmds, SHAPE(pShape)->rs.path.pts, nullptr, nullptr, nullptr, exps); auto expand = mask->expand(frameNo);
} else { auto fastTrack = false; //single clipping
//TODO: Once path direction support is implemented, ensure that the direction is ignored here
auto offset = LottieOffsetModifier(pMask->expand(frameNo));
pMask->pathset(frameNo, SHAPE(pShape)->rs.path.cmds, SHAPE(pShape)->rs.path.pts, nullptr, nullptr, &offset, exps);
}
auto compMethod = (pMethod == MaskMethod::Subtract || pMethod == MaskMethod::InvAlpha) ? MaskMethod::InvAlpha : MaskMethod::Alpha; //the first mask
if (!pShape) {
pShape = layer->pooling();
pShape->reset();
pShape->fill(255, 255, 255, opacity);
pShape->transform(layer->cache.matrix);
auto compMethod = (method == MaskMethod::Subtract || method == MaskMethod::InvAlpha) ? MaskMethod::InvAlpha : MaskMethod::Alpha;
//Cheaper. Replace the masking with a clipper
if (layer->masks.count == 1 && compMethod == MaskMethod::Alpha) {
layer->scene->opacity(MULTIPLY(layer->scene->opacity(), opacity));
layer->scene->clip(pShape);
fastTrack = true;
} else {
layer->scene->mask(pShape, compMethod);
}
//Chain mask composition
} else if (pMethod != method || pOpacity != opacity || (method != MaskMethod::Subtract && method != MaskMethod::Difference)) {
auto shape = layer->pooling();
shape->reset();
shape->fill(255, 255, 255, opacity);
shape->transform(layer->cache.matrix);
pShape->mask(shape, method);
pShape = shape;
}
//Cheaper. Replace the masking with a clipper //Default Masking
if (layer->masks.count == 1 && compMethod == MaskMethod::Alpha && opacity == 255) { if (expand == 0.0f) {
layer->scene->clip(pShape); mask->pathset(frameNo, SHAPE(pShape)->rs.path.cmds, SHAPE(pShape)->rs.path.pts, nullptr, nullptr, nullptr, exps);
return; //Masking with Expansion (Offset)
} else {
//TODO: Once path direction support is implemented, ensure that the direction is ignored here
auto offset = LottieOffsetModifier(expand);
mask->pathset(frameNo, SHAPE(pShape)->rs.path.cmds, SHAPE(pShape)->rs.path.pts, nullptr, nullptr, &offset, exps);
}
if (fastTrack) return;
pOpacity = opacity;
pMethod = method;
} }
//Introduce an intermediate scene for embracing the matte + masking //Introduce an intermediate scene for embracing the matte + masking
@ -1258,30 +1284,6 @@ void LottieBuilder::updateMaskings(LottieLayer* layer, float frameNo)
scene->push(layer->scene); scene->push(layer->scene);
layer->scene = scene; layer->scene = scene;
} }
layer->scene->mask(pShape, compMethod);
//Apply the subsquent masks
for (auto m = layer->masks.begin() + 1; m < layer->masks.end(); ++m) {
auto mask = static_cast<LottieMask*>(*m);
auto method = mask->method;
if (method == MaskMethod::None) continue;
//Append the mask shape
if (pMethod == method && (method == MaskMethod::Subtract || method == MaskMethod::Difference)) {
mask->pathset(frameNo, SHAPE(pShape)->rs.path.cmds, SHAPE(pShape)->rs.path.pts, nullptr, nullptr, nullptr, exps);
//Chain composition
} else {
auto shape = layer->pooling();
shape->reset();
shape->fill(255, 255, 255, mask->opacity(frameNo));
shape->transform(layer->cache.matrix);
mask->pathset(frameNo, SHAPE(shape)->rs.path.cmds, SHAPE(shape)->rs.path.pts, nullptr, nullptr, nullptr, exps);
pShape->mask(shape, method);
pShape = shape;
pMethod = method;
}
}
} }

View file

@ -1235,27 +1235,15 @@ void LottieParser::getLayerSize(float& val)
LottieMask* LottieParser::parseMask() LottieMask* LottieParser::parseMask()
{ {
auto mask = new LottieMask; auto mask = new LottieMask;
auto valid = true; //skip if the mask mode is none.
enterObject(); enterObject();
while (auto key = nextObjectKey()) { while (auto key = nextObjectKey()) {
if (KEY_AS("inv")) mask->inverse = getBool(); if (KEY_AS("inv")) mask->inverse = getBool();
else if (KEY_AS("mode")) else if (KEY_AS("mode")) mask->method = getMaskMethod(mask->inverse);
{ else if (KEY_AS("pt")) getPathSet(mask->pathset);
mask->method = getMaskMethod(mask->inverse); else if (KEY_AS("o")) parseProperty<LottieProperty::Type::Opacity>(mask->opacity);
if (mask->method == MaskMethod::None) valid = false; else if (KEY_AS("x")) parseProperty<LottieProperty::Type::Float>(mask->expand);
}
else if (valid && KEY_AS("pt")) getPathSet(mask->pathset);
else if (valid && KEY_AS("o")) parseProperty<LottieProperty::Type::Opacity>(mask->opacity);
else if (valid && KEY_AS("x")) parseProperty<LottieProperty::Type::Float>(mask->expand);
else skip(); else skip();
} }
if (!valid) {
delete(mask);
return nullptr;
}
return mask; return mask;
} }