放大镜实现代码2

#ifndef __STFilter__

#define __STFilter__


#include "cocos2d.h"

#include "STMacro.h"



NS_ST_BEGIN


USING_NS_CC;

using namespace std;


/**

 类似滤镜的效果。如显微镜等。

 */

class STFilter : public Node

{

    typedef std::function<void(const string &,const Rect &)> OverlapListener;

    

    

    CC_SYNTHESIZE_READONLY(ClippingNode*, m_pClipper, ClippingNode);

    

    CC_SYNTHESIZE_READONLY(Sprite*, m_pStencil, Stencil);

    

    CC_SYNTHESIZE_READONLY(Sprite*, m_pContent, Content);

    

    CC_SYNTHESIZE_READONLY(Sprite*, m_pFilterEffect, FilterEffect);

    

    CC_SYNTHESIZE(bool, m_limitedInScreen, LimitedInScreen);

    

private:

    

    Vec2 _moveLeftBottom, _moveRightTop;

    

    map<string, Rect> m_CheckList;

    

    float _overlapThreshold;

    

    float _targetOverlapArea;

    

    OverlapListener _overlapListener;

    

public:

    

    /**

     使用图片路径来初始化

     @param stencil 模板图片路径

     @param content 底板图片路径

     */

    static STFilter * create(Sprite *stencil, Sprite *content);

    

    /**

     使用图片路径来初始化

     @param stencil 模板图片路径

     @param content 底板图片路径

     */

    static STFilter * createWithPath(const string &stencil, const string &content);

    

    /**

     使用图片Frame名称来初始化

     @param stencil 模板图片Frame名称

     @param content 底板图片Frame名称

     */

    static STFilter * createWithFrameName(const string &stencil, const string &content);

    

    /** 设置绘制底板需要过滤的Alpha */

    void setAlphaThreshold(float alphaThreshold);

    

    /**

     设置模板范围内可见,还是模板范围外可见

     

     @param true 模板范围内可见, false 模板范围外可见

     */

    void setInverted(bool inverted);

    

    

    void addFilterEffect(Sprite *effect);

    

    /**

     模板中点默认在中间,位置为 Vec2(0, 0).

     左下角 Vec2(-visibleRect.width / 2, -visibleRect.height / 2)

     右上角 Vec2(visibleRect.width / 2, visibleRect.height / 2)

     */

    void move(const Vec2 &pos);

    

protected:

    

    STFilter();

    

    virtual ~STFilter();

    

    virtual bool init(Sprite *stencil, Sprite *content);

    

    virtual bool initWithFrameName(const string &stencil, const string &content);

    

    virtual bool initWithPath(const string &stencil, const string &content);

    

private:

    

    CC_SYNTHESIZE(bool, _overlapEnable, OverlapEnable);

    

    /** 判断 stencil target 之间是否重叠 */

    void checkOverlap();

    

public:

    

    /**

     设置重叠检测成功的阀值。

     当设置为1时,表示全部覆盖了目标物才算成功。设置为 0.5 表示,当覆盖面积达到目标物的50%时,检测成功。

     取值范围在 [0, 1] 之间。默认为 1

     */

    void setOverlapThreshold(float threshold)

    {

        _overlapThreshold = clampf(threshold, 0.f, 1.f);

    }

    

    void setOverlapListener(OverlapListener lsr)

    {

        _overlapListener = lsr;

        

        _overlapEnable = true;

    }

    

    /**

     name作为key,需保持唯一,否则不会添加。

     矩形区域是基于世界坐标系。

     */

    void addCheckRect(const string &name, const Rect &rect);

    

    void addCheckObject(const string &name, Node *target);

    

    void removeCheckRect(const string &name);

    

    int getCheckCount()

    {

        return m_CheckList.size();

    }

    

    bool isCheckListEmpty()

    {

        return m_CheckList.empty();

    }

};


NS_ST_END


#endif


#include "STFilter.h"

#include "VisibleRect.h"


USING_NS_ST;


#define NODE_WIDTH(obj)         (obj)->getContentSize().width

#define NODE_HEIGHT(obj)        (obj)->getContentSize().height



STFilter * STFilter::create(Sprite *stencil, Sprite *content)

{

    auto node = new (std::nothrow) STFilter();

    if ( node && node->init(stencil, content) )

    {

        node->autorelease();

        return node;

    }

    

    CC_SAFE_DELETE(node);

    return nullptr;

}



STFilter * STFilter::createWithPath(const std::string &stencil, const std::string &content)

{

    auto node = new (std::nothrow) STFilter();

    if ( node && node->initWithPath(stencil, content) )

    {

        node->autorelease();

        return node;

    }

    

    CC_SAFE_DELETE(node);

    return nullptr;

}


STFilter * STFilter::createWithFrameName(const std::string &stencil, const std::string &content)

{

    auto node = new (std::nothrow) STFilter();

    if ( node && node->initWithFrameName(stencil, content) )

    {

        node->autorelease();

        return node;

    }

    

    CC_SAFE_DELETE(node);

    return nullptr;

}


STFilter::STFilter()

: m_pFilterEffect(nullptr)

, _moveLeftBottom(Vec2::ZERO)

, _moveRightTop(Vec2::ZERO)

, m_CheckList()

, _overlapEnable(false)

, _overlapThreshold(1.f)

, _targetOverlapArea(0.f)

, _overlapListener(nullptr)

, m_limitedInScreen(true)

{

    

}


STFilter::~STFilter()

{

    

}


bool STFilter::initWithFrameName(const std::string &stencilName, const std::string &contentName)

{

    auto stencil = Sprite::createWithSpriteFrameName(stencilName);

    

    if (!stencil)

        return false;

    

    auto content = Sprite::createWithSpriteFrameName(contentName);

    

    if (!content)

        return false;

    

    return init(stencil, content);

}



bool STFilter::initWithPath(const std::string &stencilPath, const std::string &contentPath)

{

    auto stencil = Sprite::create(stencilPath);

    

    if (!stencil)

        return false;

    

    auto content = Sprite::create(contentPath);

    

    if (!content)

        return false;

    

    return init(stencil, content);

}


bool STFilter::init(Sprite *stencil, Sprite *content)

{

    bool bRet = false;

    

    do

    {

        CC_BREAK_IF(!stencil);

        

        CC_BREAK_IF(!content);

        

        CC_BREAK_IF( !Node::init() );

        

        //

        m_pStencil = stencil;

        

        m_pContent = content;


        //

        m_pClipper = ClippingNode::create();

        CC_BREAK_IF(!m_pClipper);

        

        this->addChild(m_pClipper, 0);

        

        //  at the center of screen as default

        m_pClipper->setPosition( VisibleRect::center() );


        m_pClipper->setContentSize( m_pContent->getContentSize() );


        // 设置裁剪模板

        m_pClipper->setStencil(m_pStencil);

  

        m_pClipper->addChild(m_pContent, 0);

        

        

        auto rect = VisibleRect::getVisibleRect();


        _moveLeftBottom = Vec2(-rect.size.width / 2 + NODE_WIDTH(m_pStencil) / 2,

                               -rect.size.height / 2 + NODE_HEIGHT(m_pStencil) / 2);

        

        _moveRightTop = Vec2(rect.size.width / 2 - NODE_WIDTH(m_pStencil) / 2,

               rect.size.height / 2 - NODE_HEIGHT(m_pStencil) / 2);

        

        

        //

        auto boxSize = m_pStencil->getBoundingBox().size;

        float stenArea = boxSize.width * boxSize.height;

        

         _targetOverlapArea = MAX(_targetOverlapArea, stenArea);

         

        bRet = true;

        

    } while (false);

    

    return bRet;

}


void STFilter::setAlphaThreshold(float alphaThreshold)

{

    m_pClipper->setAlphaThreshold(0.f);

}


void STFilter::setInverted(bool inverted)

{

    m_pClipper->setInverted(inverted);

}


void STFilter::addFilterEffect(Sprite *effect)

{

    if (effect == nullptr)

        return;

    

    m_pFilterEffect = effect;

    

    m_pClipper->addChild(m_pFilterEffect, 10);

}


void STFilter::move(const Vec2 &deltaPos)

{

    

    auto beforePos = m_pStencil->getPosition();

    

    auto newPos = beforePos + deltaPos;

    

    if(m_limitedInScreen)

    {

        newPos.x = clampf(newPos.x, _moveLeftBottom.x, _moveRightTop.x);

        newPos.y = clampf(newPos.y, _moveLeftBottom.y, _moveRightTop.y);

    }

    

    m_pStencil->setPosition(newPos);

    

    auto sten = m_pClipper->convertToWorldSpace(newPos);

    

    

    if (m_pFilterEffect)

    {

        auto pos = m_pFilterEffect->getPosition() + (newPos - beforePos);

        

        m_pFilterEffect->setPosition(pos);

    }

    

    if (_overlapEnable)

    {

        checkOverlap();

    }

    

}


void STFilter::checkOverlap()

{

    Rect steRect = m_pStencil->boundingBox();

    steRect.origin = m_pClipper->convertToWorldSpaceAR(steRect.origin);

    

    for (auto iter = m_CheckList.begin(); iter != m_CheckList.end(); ++iter)

    {

        Rect rect = iter->second;

        

        auto big = rect.unionWithRect(steRect);

        

        const float bigZone = big.size.width * big.size.height;

        

        if ( bigZone <= _targetOverlapArea * (2 - _overlapThreshold) )

        {

            if (_overlapListener)

                _overlapListener(iter->first,steRect);

            

            return;

        }

    }

    

}


void STFilter::addCheckRect(const string &name, const Rect &rect)

{

    auto itr = m_CheckList.find(name);

    

    // the unique key

    if (itr == m_CheckList.end())

    {

        m_CheckList.insert( {name, rect} );

        

        //

        float targetArea = rect.size.width * rect.size.height;

        

        // Stencil面积相比较,因为可能出现完全包含的情况,所以取较大的面积作为测试条件

        _targetOverlapArea = MAX(_targetOverlapArea, targetArea);


    }

}


void STFilter::addCheckObject(const string &name, Node *target)

{

    Rect rect;

    

    rect.origin = target->convertToWorldSpace(Vec2::ZERO);

    

    rect.size = target->getBoundingBox().size;

    

    this->addCheckRect(name, rect);

}



void STFilter::removeCheckRect(const string &name)

{

    auto itr = m_CheckList.find(name);

    

    if (itr != m_CheckList.end())

    {

        m_CheckList.erase(itr);

    }

}


转载于:https://my.oschina.net/yizhangxyz/blog/657937

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值