Создать маску слоя с отверстием пользовательской формы - PullRequest
16 голосов
/ 19 апреля 2011

Я потратил слишком много времени, пытаясь понять это, и просто не могу найти подходящее решение.

Положение: 1. Изображение «что-то» отображается на телефоне. 2. Полупрозрачный (например, синий) слой помещается поверх изображения, полностью покрывая его. 3. «Дыра» в этом слое существует там, где эта часть слоя полностью прозрачна и подвижна.

Примером может служить эффект масштабирования, когда вы перемещаете эту «дыру» вокруг изображения. Внутри отверстия вы можете нормально видеть изображение, а снаружи оно покрыто полупрозрачным слоем. ПРИМЕЧАНИЕ: я реализую это в слое cocos2d, где изображение представлено CCSprite. Это не должно иметь значения, если кокосы не используются.

Проблема: Я пытался использовать CAShapeLayer и растровые изображения в качестве масок, но ничего не работает (см. Фрагменты кода ниже). С помощью CAShapeLayer я создаю UIBezierPath для «дыры» и применяю его к цветному слою. Тем не менее, только отверстие показывает цвет, а остальное прозрачно. С изображением маска просто не работает (понятия не имею почему). Я даже пробовал маскировать маски, чтобы посмотреть, сработает ли это. Я также попытался поменять цвета вокруг ... с белого на черный, чтобы очистить для заливки и фона.

Простым решением, если бы оно существовало, было бы инвертировать область UIBezierPath. Я тоже попробовал отсечь, используя путь ... но не повезло.

Я надеюсь, что это что-то простое-глупое, что я просто упускаю из виду. Возможно, один из вас увидит это. Подвижная часть меня пока не волнует. Мне нужно, чтобы фактическая маска работала первой. В примере кода игнорируются различия по оси Y между iPhone SDK и openGL.

CAShapeLayer Пример:

CGSize winSize = [[CCDirector sharedDirector] winSize];
UIImage* img = [UIImage imageNamed:@"zebra.png"];
CCSprite* spr = [CCSprite spriteWithCGImage:img.CGImage key:@"img"];
spr.position = ccp( winSize.width / 2, winSize.width / 2 );
[self addSprite:spr];

UIBezierPath* path = [UIBezierPath bezierPathWithRect:rectHole];
CAShapeLayer* maskLayer = [CAShapeLayer layer];
maskLayer.bounds = [spr boundingBox];
maskLayer.position = spr.position;
maskLayer.fillColor = [UIColor whiteColor].CGColor;
maskLayer.backgroundColor = [UIColor clearColor].CGColor;
maskLayer.path = path.CGPath;

CALayer* colorLayer = [CALayer layer];
colorLayer.bounds = [spr boundingBox];
colorLayer.position = maskLayer.position;
[colorLayer setMask:maskLayer];

[[[[CCDirector sharedDirector] openGLView] layer] addSublayer:colorLayer];

Пример многослойной маски:

CGSize winSize = [[CCDirector sharedDirector] winSize];
UIImage* img = [UIImage imageNamed:@"zebra.png"];
CCSprite* spr = [CCSprite spriteWithCGImage:img.CGImage key:@"img"];
spr.position = ccp( winSize.width / 2, winSize.width / 2 );
[self addSprite:spr];

UIBezierPath* path = [UIBezierPath bezierPathWithRect:rectHole];
CAShapeLayer* maskLayer = [CAShapeLayer layer];
maskLayer.bounds = [spr boundingBox];
maskLayer.position = spr.position;
maskLayer.fillColor = [UIColor whiteColor].CGColor;
maskLayer.backgroundColor = [UIColor clearColor].CGColor;
maskLayer.path = path.CGPath;

UIBezierPath* pathOuter = [UIBezierPath bezierPathWithRect:img.frame];
CAShapeLayer* outerLayer = [CAShapeLayer layer];
outerLayer.bounds = [spr boundingBox];
outerLayer.position = spr.position;
outerLayer.fillColor = [UIColor blackColor].CGColor;
outerLayer.backgroundColor = [UIColor whiteColor].CGColor;
outerLayer = pathOuter.CGPath;
[outerLayer setMask:maskLayer];

CALayer* colorLayer = [CALayer layer];
colorLayer.bounds = [spr boundingBox];
colorLayer.position = outerLayer.position;
[colorLayer setMask:outerLayer];

[[[[CCDirector sharedDirector] openGLView] layer] addSublayer:colorLayer];

Пример маски изображения:

CGSize winSize = [[CCDirector sharedDirector] winSize];
UIImage* img = [UIImage imageNamed:@"zebra.png"];
CCSprite* spr = [CCSprite spriteWithCGImage:img.CGImage key:@"img"];
spr.position = ccp( winSize.width / 2, winSize.width / 2 );
[self addSprite:spr];

CGRect r = [spr boundingBox];
CGSize sz = CGSizeMake( r.size.width, r.size.height );
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceGray();
CGContextRef context = CGBitmapContextCreate( NULL, w, h, 8, 0, colorSpace, kCGBitmapByteOrderDefault | kCGImageAlphaNone );
CGColorSpaceRelease( colorSpace );
CGContextSetFillColorWithColor( context, [UIColor whiteColor].CGColor );
CGContextFillRect( context, r );
CGContextSetFillColorWithColor( context, [UIColor blackColor].CGColor );
CGContextFillRect( context, rectHole );
CGImageRef ref = CGBitmapContextCreateImage( context );
CGContextRelease( context );

CALayer* maskLayer = [CALayer layer];
maskLayer.bounds = [spr boundingBox];
maskLayer.position = spr.position;
[maskLayer setContents:(id)ref];

CALayer* colorLayer = [CALayer layer];
colorLayer.bounds = [spr boundingBox];
colorLayer.position = maskLayer.position;
[colorLayer setMask:maskLayer];

[[[[CCDirector sharedDirector] openGLView] layer] addSublayer:colorLayer];
CGImageRelease( ref );

1 Ответ

13 голосов
/ 12 мая 2011

Я вернулся к этому позже, изучив другие основные графические приемы.Решение является наиболее близким к Примеру многослойной маски выше.Однако вместо создания внутреннего и внешнего слоя необходимо объединить два пути в один UIBezierPath в противоположных направлениях.

Таким образом, например, создайте путь внутренней области, которую нужно обрезать (CW).ПРИМЕЧАНИЕ: x, y, w, h относятся к происхождению и размеру «дыры».

      [path moveToPoint:ccp(x,y)];
      [path addLineToPoint:ccp(x+w,y)];
      [path addLineToPoint:ccp(x+w,y+h)];
      [path addLineToPoint:ccp(x,y+h)];
      [path addLineToPoint:ccp(x,y)];

Затем добавьте к тому же пути внешнюю область в противоположном направлении (CCW).ПРИМЕЧАНИЕ: x, y, w, h относятся к началу и размеру внешнего прямоугольника.

      [path moveToPoint:ccp(x,y)];
      [path addLineToPoint:ccp(x,y+h)];
      [path addLineToPoint:ccp(x+w,y+h)];
      [path addLineToPoint:ccp(x+w,y)];
      [path addLineToPoint:ccp(x,y)];

Этот путь затем применяется к слою (maskLayer), который используется в качестве маски напоследний слой (colorLayer)."Внешний слой" не нужен.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...