Создайте маску слоя с выборочным отверстием
Я потратил слишком много времени, пытаясь понять это, и просто не могу найти работоспособного решения.
Ситуация:
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
Я вернулся к этому позже после изучения других основных графических технологий.
Решение ближе всего к описанному выше примеру многослойной маски.
Однако вместо создания внутреннего и внешнего слоев вам необходимо объединить два пути в один 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). "Внешний слой" не требуется.