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 Mira Grudzinska
parent 2eed9f9024
commit 00324f5e19
2 changed files with 53 additions and 64 deletions

View file

@ -1227,34 +1227,60 @@ void LottieBuilder::updateText(LottieLayer* layer, float frameNo)
void LottieBuilder::updateMaskings(LottieLayer* layer, float frameNo)
{
if (layer->masks.count == 0) return;
if (layer->masks.count == 0) return;
//Apply the base mask
auto pMask = static_cast<LottieMask*>(layer->masks[0]);
auto pMethod = pMask->method;
auto opacity = pMask->opacity(frameNo);
auto expand = pMask->expand(frameNo);
Shape* pShape = nullptr;
CompositeMethod pMethod;
uint8_t pOpacity;
auto pShape = layer->pooling();
pShape->reset();
pShape->fill(255, 255, 255, opacity);
pShape->transform(layer->cache.matrix);
for (auto m = layer->masks.begin(); m < layer->masks.end(); ++m) {
auto mask = *m;
if (mask->method == CompositeMethod::None) continue;
//Apply Masking Expansion (Offset)
if (expand == 0.0f) {
pMask->pathset(frameNo, P(pShape)->rs.path.cmds, P(pShape)->rs.path.pts, nullptr, nullptr, nullptr, exps);
} else {
//TODO: Once path direction support is implemented, ensure that the direction is ignored here
auto offset = LottieOffsetModifier(pMask->expand(frameNo));
pMask->pathset(frameNo, P(pShape)->rs.path.cmds, P(pShape)->rs.path.pts, nullptr, nullptr, &offset, exps);
}
auto method = mask->method;
auto opacity = mask->opacity(frameNo);
auto expand = mask->expand(frameNo);
auto fastTrack = false; //single clipping
auto compMethod = (pMethod == CompositeMethod::SubtractMask || pMethod == CompositeMethod::InvAlphaMask) ? CompositeMethod::InvAlphaMask : CompositeMethod::AlphaMask;
//the first mask
if (!pShape) {
pShape = layer->pooling();
pShape->reset();
pShape->fill(255, 255, 255, opacity);
pShape->transform(layer->cache.matrix);
auto compMethod = (method == CompositeMethod::SubtractMask || method == CompositeMethod::InvAlphaMask) ? CompositeMethod::InvAlphaMask : CompositeMethod::AlphaMask;
//Cheaper. Replace the masking with a clipper
if (layer->masks.count == 1 && compMethod == CompositeMethod::AlphaMask) {
layer->scene->opacity(MULTIPLY(layer->scene->opacity(), opacity));
layer->scene->clip(cast(pShape));
fastTrack = true;
} else {
layer->scene->composite(cast(pShape), compMethod);
}
//Chain mask composition
} else if (pMethod != method || pOpacity != opacity || (method != CompositeMethod::SubtractMask && method != CompositeMethod::DifferenceMask)) {
auto shape = layer->pooling();
shape->reset();
shape->fill(255, 255, 255, opacity);
shape->transform(layer->cache.matrix);
pShape->composite(cast(shape), method);
pShape = shape;
}
//Cheaper. Replace the masking with a clipper
if (layer->masks.count == 1 && compMethod == CompositeMethod::AlphaMask && opacity == 255) {
layer->scene->clip(tvg::cast(pShape));
return;
//Default Masking
if (expand == 0.0f) {
mask->pathset(frameNo, P(pShape)->rs.path.cmds, P(pShape)-> rs.path.pts, nullptr, nullptr, nullptr, exps);
//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, P(pShape)->rs.path.cmds, P(pShape)->rs.path.pts, nullptr, nullptr, &offset, exps);
}
if (fastTrack) return;
pOpacity = opacity;
pMethod = method;
}
//Introduce an intermediate scene for embracing the matte + masking
@ -1263,30 +1289,6 @@ void LottieBuilder::updateMaskings(LottieLayer* layer, float frameNo)
scene->push(cast(layer->scene));
layer->scene = scene;
}
layer->scene->composite(tvg::cast(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 == CompositeMethod::None) continue;
//Append the mask shape
if (pMethod == method && (method == CompositeMethod::SubtractMask || method == CompositeMethod::DifferenceMask)) {
mask->pathset(frameNo, P(pShape)->rs.path.cmds, P(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, P(shape)->rs.path.cmds, P(shape)->rs.path.pts, nullptr, nullptr, nullptr, exps);
pShape->composite(tvg::cast(shape), method);
pShape = shape;
pMethod = method;
}
}
}

View file

@ -1235,27 +1235,15 @@ void LottieParser::getLayerSize(float& val)
LottieMask* LottieParser::parseMask()
{
auto mask = new LottieMask;
auto valid = true; //skip if the mask mode is none.
enterObject();
while (auto key = nextObjectKey()) {
if (KEY_AS("inv")) mask->inverse = getBool();
else if (KEY_AS("mode"))
{
mask->method = getMaskMethod(mask->inverse);
if (mask->method == CompositeMethod::None) valid = false;
}
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 if (KEY_AS("mode")) mask->method = getMaskMethod(mask->inverse);
else if (KEY_AS("pt")) getPathSet(mask->pathset);
else if (KEY_AS("o")) parseProperty<LottieProperty::Type::Opacity>(mask->opacity);
else if (KEY_AS("x")) parseProperty<LottieProperty::Type::Float>(mask->expand);
else skip(key);
}
if (!valid) {
delete(mask);
return nullptr;
}
return mask;
}
@ -1319,7 +1307,6 @@ void LottieParser::parseTritone(LottieFxTritone* effect)
}
void LottieParser::parseFill(LottieFxFill* effect)
{
int idx = 0; //fill mask -> all mask -> color -> invert -> h feather -> v feather -> opacity