Skip to content

Commit

Permalink
Optimize masks with feather and opacity. (#528)
Browse files Browse the repository at this point in the history
* Move to new branch.

* Adjust

Co-authored-by: fusionxu <fusionxu@tencent.com>
  • Loading branch information
FusionXu and fusionxu authored Sep 15, 2022
1 parent 2b5ce68 commit cb71396
Show file tree
Hide file tree
Showing 19 changed files with 363 additions and 47 deletions.
2 changes: 2 additions & 0 deletions include/pag/file.h
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,7 @@ enum class TagCode {
RadialBlurEffect = 81,
MosaicEffect = 82,
EditableIndices = 83,
MaskBlockV2 = 84,
GradientOverlayStyle = 85,
// add new tags here...

Expand Down Expand Up @@ -375,6 +376,7 @@ class PAG_API MaskData {
bool inverted = false;
Enum maskMode = MaskMode::Add;
Property<PathHandle>* maskPath = nullptr;
Property<Point>* maskFeather = nullptr;
Property<Opacity>* maskOpacity = nullptr;
Property<float>* maskExpansion = nullptr;

Expand Down
3 changes: 3 additions & 0 deletions resources/filter/FeatherMask.pag
Git LFS file not shown
4 changes: 4 additions & 0 deletions src/base/MaskData.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,16 @@
namespace pag {
MaskData::~MaskData() {
delete maskPath;
delete maskFeather;
delete maskOpacity;
delete maskExpansion;
}

void MaskData::excludeVaryingRanges(std::vector<TimeRange>* timeRanges) const {
maskPath->excludeVaryingRanges(timeRanges);
if (maskFeather != nullptr) {
maskFeather->excludeVaryingRanges(timeRanges);
}
maskOpacity->excludeVaryingRanges(timeRanges);
maskExpansion->excludeVaryingRanges(timeRanges);
}
Expand Down
20 changes: 19 additions & 1 deletion src/codec/tags/LayerTag.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,10 @@ void ReadTagsOfLayer(DecodeStream* stream, TagCode code, Layer* layer) {
auto mask = ReadTagBlock(stream, MaskTag);
layer->masks.push_back(mask);
} break;
case TagCode::MaskBlockV2: {
auto mask = ReadTagBlock(stream, MaskTagV2);
layer->masks.push_back(mask);
} break;
case TagCode::MarkerList: {
ReadMarkerList(stream, &layer->markers);
} break;
Expand Down Expand Up @@ -193,6 +197,16 @@ Layer* ReadLayer(DecodeStream* stream) {

#undef Condition

static bool CheckMaskFeatherValid(Property<Point>* maskFeather) {
if (maskFeather == nullptr) {
return false;
}
if (maskFeather->animatable()) {
return true;
}
return maskFeather->value.x != 0.0f || maskFeather->value.y != 0.0f;
}

TagCode WriteLayer(EncodeStream* stream, Layer* layer) {
stream->writeUint8(static_cast<uint8_t>(layer->type()));
stream->writeEncodedUint32(layer->id);
Expand All @@ -207,7 +221,11 @@ TagCode WriteLayer(EncodeStream* stream, Layer* layer) {
}

for (auto& mask : layer->masks) {
WriteTagBlock(stream, mask, MaskTag);
if (CheckMaskFeatherValid(mask->maskFeather)) {
WriteTagBlock(stream, mask, MaskTagV2);
} else {
WriteTagBlock(stream, mask, MaskTag);
}
}
if (layer->markers.size() > 0) {
WriteTag(stream, &layer->markers, WriteMarkerList);
Expand Down
17 changes: 14 additions & 3 deletions src/codec/tags/MaskTag.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,26 @@
#include "MaskTag.h"

namespace pag {
std::unique_ptr<BlockConfig> MaskTag(MaskData* mask) {
auto tagConfig = new BlockConfig(TagCode::MaskBlock);
static std::unique_ptr<BlockConfig> MaskTagInternal(MaskData* mask, TagCode tagCode) {
auto tagConfig = new BlockConfig(tagCode);
AddAttribute(tagConfig, &mask->id, AttributeType::FixedValue, ZeroID);
AddAttribute(tagConfig, &mask->inverted, AttributeType::BitFlag, false);
AddAttribute(tagConfig, &mask->maskMode, AttributeType::Value, MaskMode::Add);
AddAttribute(tagConfig, &mask->maskPath, AttributeType::SimpleProperty,
PathHandle(new PathData()));
if (tagCode == TagCode::MaskBlockV2) {
AddAttribute(tagConfig, &mask->maskFeather, AttributeType::SpatialProperty, Point::Zero());
}
AddAttribute(tagConfig, &mask->maskOpacity, AttributeType::SimpleProperty, Opaque);
AddAttribute(tagConfig, &mask->maskExpansion, AttributeType::SimpleProperty, 0.0f);
return std::unique_ptr<BlockConfig>(tagConfig);
}
} // namespace pag

std::unique_ptr<BlockConfig> MaskTag(MaskData* mask) {
return MaskTagInternal(mask, TagCode::MaskBlock);
}

std::unique_ptr<BlockConfig> MaskTagV2(MaskData* mask) {
return MaskTagInternal(mask, TagCode::MaskBlockV2);
}
} // namespace pag
3 changes: 2 additions & 1 deletion src/codec/tags/MaskTag.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,5 @@

namespace pag {
std::unique_ptr<BlockConfig> MaskTag(MaskData* mask);
}
std::unique_ptr<BlockConfig> MaskTagV2(MaskData* mask);
} // namespace pag
20 changes: 18 additions & 2 deletions src/rendering/caches/LayerCache.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@
/////////////////////////////////////////////////////////////////////////////////////////////////

#include "LayerCache.h"
#include "base/utils/TGFXCast.h"
#include "rendering/caches/ImageContentCache.h"
#include "rendering/caches/PreComposeContentCache.h"
#include "rendering/caches/ShapeContentCache.h"
Expand Down Expand Up @@ -57,7 +56,13 @@ LayerCache::LayerCache(Layer* layer) : layer(layer) {
}
contentCache->update();
transformCache = new TransformCache(layer);
if (!layer->masks.empty()) {
for (auto mask : layer->masks) {
if (mask->maskFeather != nullptr ||
(mask->maskOpacity->animatable() || mask->maskOpacity->value != 255)) {
featherMaskCache = new FeatherMaskCache(layer);
}
}
if (!layer->masks.empty() && featherMaskCache == nullptr) {
maskCache = new MaskCache(layer);
}
updateStaticTimeRanges();
Expand All @@ -82,6 +87,14 @@ tgfx::Path* LayerCache::getMasks(Frame contentFrame) {
return mask;
}

std::shared_ptr<Modifier> LayerCache::getFeatherMask(Frame contentFrame) {
if (featherMaskCache == nullptr) {
return nullptr;
}
auto featherMaskContent = featherMaskCache->getCache(contentFrame);
return Modifier::MakeMask(featherMaskContent->graphic, false, false);
}

Content* LayerCache::getContent(Frame contentFrame) {
return contentCache->getCache(contentFrame);
}
Expand Down Expand Up @@ -131,6 +144,9 @@ void LayerCache::updateStaticTimeRanges() {
if (maskCache) {
MergeTimeRanges(&staticTimeRanges, maskCache->getStaticTimeRanges());
}
if (featherMaskCache) {
MergeTimeRanges(&staticTimeRanges, featherMaskCache->getStaticTimeRanges());
}
if (layer->trackMatteLayer) {
auto timeRanges = getTrackMatteStaticTimeRanges();
MergeTimeRanges(&staticTimeRanges, &timeRanges);
Expand Down
5 changes: 4 additions & 1 deletion src/rendering/caches/LayerCache.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
#include "rendering/caches/ContentCache.h"
#include "rendering/caches/MaskCache.h"
#include "rendering/caches/TransformCache.h"
#include "rendering/graphics/Modifier.h"

namespace pag {
class LayerCache : public Cache {
Expand All @@ -34,6 +35,8 @@ class LayerCache : public Cache {

tgfx::Path* getMasks(Frame contentFrame);

std::shared_ptr<Modifier> getFeatherMask(Frame contentFrame);

Content* getContent(Frame contentFrame);

Layer* getLayer() const;
Expand Down Expand Up @@ -64,10 +67,10 @@ class LayerCache : public Cache {
Layer* layer = nullptr;
TransformCache* transformCache = nullptr;
MaskCache* maskCache = nullptr;
FeatherMaskCache* featherMaskCache = nullptr;
ContentCache* contentCache = nullptr;
tgfx::Point maxScaleFactor = {};
std::vector<TimeRange> staticTimeRanges;

explicit LayerCache(Layer* layer);
void updateStaticTimeRanges();
std::vector<TimeRange> getTrackMatteStaticTimeRanges();
Expand Down
17 changes: 16 additions & 1 deletion src/rendering/caches/MaskCache.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
/////////////////////////////////////////////////////////////////////////////////////////////////

#include "MaskCache.h"
#include "rendering/graphics/FeatherMask.h"
#include "rendering/renderers/MaskRenderer.h"

namespace pag {
Expand All @@ -34,4 +35,18 @@ tgfx::Path* MaskCache::createCache(Frame layerFrame) {
RenderMasks(maskContent, layer->masks, layerFrame);
return maskContent;
}
} // namespace pag

FeatherMaskCache::FeatherMaskCache(Layer* layer)
: FrameCache<GraphicContent>(layer->startTime, layer->duration), layer(layer) {
std::vector<TimeRange> timeRanges = {layer->visibleRange()};
for (auto& mask : layer->masks) {
mask->excludeVaryingRanges(&timeRanges);
}
staticTimeRanges = OffsetTimeRanges(timeRanges, -layer->startTime);
}

GraphicContent* FeatherMaskCache::createCache(Frame layerFrame) {
auto featherMask = FeatherMask::MakeFrom(layer->masks, layerFrame);
return new GraphicContent(featherMask);
}
} // namespace pag
12 changes: 12 additions & 0 deletions src/rendering/caches/MaskCache.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
#pragma once

#include "FrameCache.h"
#include "GraphicContent.h"
#include "tgfx/core/Path.h"

namespace pag {
Expand All @@ -32,4 +33,15 @@ class MaskCache : public FrameCache<tgfx::Path> {
private:
Layer* layer = nullptr;
};

class FeatherMaskCache : public FrameCache<GraphicContent> {
public:
explicit FeatherMaskCache(Layer* layer);

protected:
GraphicContent* createCache(Frame layerFrame) override;

private:
Layer* layer = nullptr;
};
} // namespace pag
151 changes: 151 additions & 0 deletions src/rendering/graphics/FeatherMask.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
/////////////////////////////////////////////////////////////////////////////////////////////////
//
// Tencent is pleased to support the open source community by making libpag available.
//
// Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
// except in compliance with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// unless required by applicable law or agreed to in writing, software distributed under the
// license is distributed on an "as is" basis, without warranties or conditions of any kind,
// either express or implied. see the license for the specific language governing permissions
// and limitations under the license.
//
/////////////////////////////////////////////////////////////////////////////////////////////////

#include "FeatherMask.h"
#include "pag/file.h"
#include "rendering/caches/RenderCache.h"
#include "rendering/filters/gaussianblur/GaussianBlurFilter.h"
#include "rendering/utils/PathUtil.h"
#include "tgfx/core/Mask.h"
#include "tgfx/gpu/Canvas.h"
#include "tgfx/gpu/Surface.h"

namespace pag {
std::shared_ptr<Graphic> FeatherMask::MakeFrom(const std::vector<MaskData*>& masks,
Frame layerFrame) {
if (masks.empty()) {
return nullptr;
}
return std::shared_ptr<Graphic>(new FeatherMask(masks, layerFrame));
}

tgfx::Rect MeasureFeatherMaskBounds(const std::vector<MaskData*>& masks, Frame layerFrame) {
auto maskBounds = tgfx::Rect::MakeLTRB(0, 0, FLT_MIN, FLT_MIN);
for (auto mask : masks) {
auto maskPath = ToPath(*(mask->maskPath->getValueAt(layerFrame)));
auto expansion = mask->maskExpansion->getValueAt(layerFrame);
ExpandPath(&maskPath, expansion);
auto bounds = maskPath.getBounds();
if (bounds.right > maskBounds.right) {
maskBounds.right = bounds.right;
}
if (bounds.bottom > maskBounds.bottom) {
maskBounds.bottom = bounds.bottom;
}
}
maskBounds.outset(maskBounds.width() * 0.1, maskBounds.height() * 0.1);
return maskBounds;
}

FeatherMask::FeatherMask(const std::vector<MaskData*>& masks, Frame layerFrame)
: masks(masks), layerFrame(layerFrame) {
bounds = MeasureFeatherMaskBounds(masks, layerFrame);
}

void FeatherMask::measureBounds(tgfx::Rect* rect) const {
*rect = bounds;
}

bool FeatherMask::hitTest(RenderCache*, float, float) {
return false;
}

bool FeatherMask::getPath(tgfx::Path*) const {
return false;
}

void FeatherMask::prepare(RenderCache*) const {
}

std::unique_ptr<Snapshot> FeatherMask::drawFeatherMask(const std::vector<MaskData*>& masks,
Frame layerFrame, RenderCache* cache,
float scaleFactor) const {
bool isFirst = true;
auto surface = tgfx::Surface::Make(cache->getContext(),
static_cast<int>(ceilf(bounds.width() * scaleFactor)),
static_cast<int>(ceilf(bounds.height() * scaleFactor)), true);
if (surface == nullptr) {
return nullptr;
}
auto canvas = surface->getCanvas();
canvas->setMatrix(tgfx::Matrix::MakeTrans(bounds.x(), bounds.y()));
for (auto& mask : masks) {
auto path = mask->maskPath->getValueAt(layerFrame);
if (path == nullptr || !path->isClosed() || mask->maskMode == MaskMode::None) {
continue;
}
auto maskPath = ToPath(*path);
auto expansion = mask->maskExpansion->getValueAt(layerFrame);
ExpandPath(&maskPath, expansion);
auto inverted = mask->inverted;
if (isFirst) {
if (mask->maskMode == MaskMode::Subtract) {
inverted = !inverted;
}
}
if (inverted) {
maskPath.toggleInverseFillType();
}
auto maskBounds = maskPath.getBounds();
auto width = static_cast<int>(ceilf(maskBounds.width() * scaleFactor));
auto height = static_cast<int>(ceilf(maskBounds.height() * scaleFactor));
if (width == 0.0 || height == 0.0) {
continue;
}
tgfx::Paint maskPaint;
float alpha = mask->maskOpacity->getValueAt(layerFrame) / 255.0;
maskPaint.setAlpha(alpha);
auto maskSurface = tgfx::Surface::Make(cache->getContext(), width, height, true);
auto maskCanvas = maskSurface->getCanvas();
maskCanvas->setMatrix(tgfx::Matrix::MakeTrans(-maskBounds.x(), -maskBounds.y()));
maskCanvas->drawPath(maskPath, maskPaint);
auto maskTexture = maskSurface->getTexture();
tgfx::Paint blurPaint;
float blurrinessX = 0.0f;
float blurrinessY = 0.0f;
if (mask->maskFeather != nullptr) {
blurrinessX = mask->maskFeather->getValueAt(layerFrame).x * scaleFactor;
blurrinessY = mask->maskFeather->getValueAt(layerFrame).y * scaleFactor;
tgfx::TileMode tileMode = tgfx::TileMode::Decal;
tgfx::Rect cropRect = tgfx::Rect::MakeEmpty();
auto blurFilter = tgfx::ImageFilter::Blur(blurrinessX, blurrinessY, tileMode, cropRect);
blurPaint.setImageFilter(blurFilter);
}
canvas->save();
canvas->setMatrix(tgfx::Matrix::MakeTrans(maskBounds.x(), maskBounds.y()));
canvas->drawTexture(maskTexture, &blurPaint);
canvas->restore();
}

auto texture = surface->getTexture();
if (texture == nullptr) {
return nullptr;
}

auto matrix = tgfx::Matrix::MakeScale(scaleFactor);
auto drawingMatrix = tgfx::Matrix::I();
matrix.invert(&drawingMatrix);

return std::make_unique<Snapshot>(texture, drawingMatrix);
}

void FeatherMask::draw(tgfx::Canvas* canvas, RenderCache* renderCache) const {
auto snapshot = drawFeatherMask(masks, layerFrame, renderCache);
canvas->drawTexture(snapshot->getTexture());
}
} // namespace pag
Loading

0 comments on commit cb71396

Please sign in to comment.