Тонирование оттенков серого NSImage (или CIImage)
У меня есть изображение в градациях серого, которое я хочу использовать для рисования элементов управления Cocoa. Изображение имеет различные уровни серого. Где это самое мрачное, я хочу, чтобы он нарисовал указанный оттенок темного цвета. Я хочу, чтобы он был прозрачным, где исходное изображение белого.
В принципе, я хочу воспроизвести поведение tintColor, наблюдаемое в UINavigationBar на iPhone.
До сих пор я изучил несколько вариантов:
-
Нарисуйте цвет оттенка над полутоновым изображением, используя композицию SourceOver
- > Для этого требуется непрозрачный оттенок цвета
- > Результат получается намного темнее, чем хотелось бы
-
Используйте CIFultiplyCompositing CIFilter, чтобы направить изображение
- > Я не могу [CIImage drawAtPoint: fromRect: operation: fraction:] рисовать только часть изображения. То же самое отлично работает с NSImage
- > Я получаю случайные сбои, которые я не могу понять
-
Преобразуйте изображение в оттенках серого в маску. То есть Черный должен быть непрозрачным. Белый должен быть прозрачным. Серый должен иметь промежуточные значения альфа.
- > Это, казалось бы, лучшее решение
- > Попробуйте, как я мог, я не могу этого добиться.
Ответы
Ответ 1
- (NSImage *)imageTintedWithColor:(NSColor *)tint
{
if (tint != nil) {
NSSize size = [self size];
NSRect bounds = { NSZeroPoint, size };
NSImage *tintedImage = [[NSImage alloc] initWithSize:size];
[tintedImage lockFocus];
CIFilter *colorGenerator = [CIFilter filterWithName:@"CIConstantColorGenerator"];
CIColor *color = [[[CIColor alloc] initWithColor:tint] autorelease];
[colorGenerator setValue:color forKey:@"inputColor"];
CIFilter *monochromeFilter = [CIFilter filterWithName:@"CIColorMonochrome"];
CIImage *baseImage = [CIImage imageWithData:[self TIFFRepresentation]];
[monochromeFilter setValue:baseImage forKey:@"inputImage"];
[monochromeFilter setValue:[CIColor colorWithRed:0.75 green:0.75 blue:0.75] forKey:@"inputColor"];
[monochromeFilter setValue:[NSNumber numberWithFloat:1.0] forKey:@"inputIntensity"];
CIFilter *compositingFilter = [CIFilter filterWithName:@"CIMultiplyCompositing"];
[compositingFilter setValue:[colorGenerator valueForKey:@"outputImage"] forKey:@"inputImage"];
[compositingFilter setValue:[monochromeFilter valueForKey:@"outputImage"] forKey:@"inputBackgroundImage"];
CIImage *outputImage = [compositingFilter valueForKey:@"outputImage"];
[outputImage drawAtPoint:NSZeroPoint
fromRect:bounds
operation:NSCompositeCopy
fraction:1.0];
[tintedImage unlockFocus];
return [tintedImage autorelease];
}
else {
return [[self copy] autorelease];
}
}
- (NSImage*)imageCroppedToRect:(NSRect)rect
{
NSPoint point = { -rect.origin.x, -rect.origin.y };
NSImage *croppedImage = [[NSImage alloc] initWithSize:rect.size];
[croppedImage lockFocus];
{
[self compositeToPoint:point operation:NSCompositeCopy];
}
[croppedImage unlockFocus];
return [croppedImage autorelease];
}
Ответ 2
Вышеупомянутое решение не сработало для меня. Но это гораздо более легкое решение отлично подходит для меня.
- (NSImage *)imageTintedWithColor:(NSColor *)tint
{
NSImage *image = [self copy];
if (tint) {
[image lockFocus];
[tint set];
NSRect imageRect = {NSZeroPoint, [image size]};
NSRectFillUsingOperation(imageRect, NSCompositeSourceAtop);
[image unlockFocus];
}
return image;
}
Ответ 3
Быстрая реализация ответа на синий бамбук:
func tintedImage(_ image: NSImage, tint: NSColor) -> NSImage {
guard let tinted = image.copy() as? NSImage else { return image }
tinted.lockFocus()
tint.set()
let imageRect = NSRect(origin: NSZeroPoint, size: image.size)
NSRectFillUsingOperation(imageRect, .sourceAtop)
tinted.unlockFocus()
return tinted
}
Ответ 4
Быстрая версия в виде расширения:
extension NSImage {
func tintedImageWithColor(color:NSColor) -> NSImage {
let size = self.size
let imageBounds = NSMakeRect(0, 0, size.width, size.height)
let copiedImage = self.copy() as! NSImage
copiedImage.lockFocus()
color.set()
NSRectFillUsingOperation(imageBounds, .CompositeSourceAtop)
copiedImage.unlockFocus()
return copiedImage
}
}
Ответ 5
Фильтр CIMultiplyCompositing определенно является способом сделать это. Если он сбой, можете ли вы отправить трассировку стека? Я сильно использую CIFilters и не имею проблем с ошибкой.
//assume inputImage is the greyscale CIImage you want to tint
CIImage* outputImage = nil;
//create some green
CIFilter* greenGenerator = [CIFilter filterWithName:@"CIConstantColorGenerator"];
CIColor* green = [CIColor colorWithRed:0.30 green:0.596 blue:0.172];
[greenGenerator setValue:green forKey:@"inputColor"];
CIImage* greenImage = [greenGenerator valueForKey:@"outputImage"];
//apply a multiply filter
CIFilter* filter = [CIFilter filterWithName:@"CIMultiplyCompositing"];
[filter setValue:greenImage forKey:@"inputImage"];
[filter setValue:inputImage forKey:@"inputBackgroundImage"];
outputImage = [filter valueForKey:@"outputImage"];
[outputImage drawAtPoint:NSZeroPoint fromRect:NSRectFromCGRect([outputImage extent]) operation:NSCompositeCopy fraction:1.0];
Ответ 6
Я хотел закрасить изображение цветом оттенка, который имел альфа, не видя, как проходят оригинальные цвета изображения. Вот как вы можете это сделать:
extension NSImage {
func tinting(with tintColor: NSColor) -> NSImage {
guard let cgImage = self.cgImage(forProposedRect: nil, context: nil, hints: nil) else { return self }
return NSImage(size: size, flipped: false) { bounds in
guard let context = NSGraphicsContext.current()?.cgContext else { return false }
tintColor.set()
context.clip(to: bounds, mask: cgImage)
context.fill(bounds)
return true
}
}
}
Ответ 7
Для более мелкозернистого вы можете использовать подход с полиномиальным цветом, используя подход, который я предложил в следующем: Как отобразить вращающийся NSProgressIndicator в другом цвете?