Migrate new beauty controls with continuous protection, local detail recovery and legacy state compatibility; 1636 checks passed
这个提交包含在:
1 父节点
2e79602761
当前提交
2f2c081980
16 个文件已更改
+350
-44
没有与搜索条件相匹配的文件
@@ -17,9 +17,9 @@
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>XPC!</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.2.0</string>
|
||||
<string>1.3.0</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>14</string>
|
||||
<string>15</string>
|
||||
<key>NSHumanReadableCopyright</key>
|
||||
<string></string>
|
||||
<key>PlugInKit</key>
|
||||
@@ -72,7 +72,7 @@
|
||||
<key>uuid</key>
|
||||
<string>FB14B0E6-59B2-4836-916F-CD0C559E9241</string>
|
||||
<key>version</key>
|
||||
<string>1.2.0</string>
|
||||
<string>1.3.0</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>className</key>
|
||||
@@ -84,7 +84,7 @@
|
||||
<key>uuid</key>
|
||||
<string>E0E432C8-3D71-4ABA-B006-E7F519E06C87</string>
|
||||
<key>version</key>
|
||||
<string>1.2.0</string>
|
||||
<string>1.3.0</string>
|
||||
<key>protocolNames</key>
|
||||
<array>
|
||||
<string>FxOnScreenControl</string>
|
||||
|
||||
@@ -82,6 +82,26 @@ static NSDictionary<NSString *, CIColorKernel *> *PGKernels(void) {
|
||||
"kernel vec4 pgMaskedMix(__sample source,__sample effect,__sample mask,float amount) {"
|
||||
"vec4 s=unpremultiply(source); vec4 e=unpremultiply(effect);"
|
||||
"return premultiply(vec4(mix(s.rgb,e.rgb,clamp(mask.r*amount,0.0,1.0)),s.a)); }");
|
||||
// Keep smoothing support separate from the exposure/color mask. The
|
||||
// two new controls independently release feature cutouts and background.
|
||||
k[@"smoothingSupport"] = PGKernel(@
|
||||
"kernel vec4 pgSmoothingSupport(__sample protectedMask,__sample baseMask,float featureProtection,float skinProtection) {"
|
||||
"float base=clamp(baseMask.r,0.0,1.0);float protectedValue=clamp(protectedMask.r,0.0,1.0);"
|
||||
"float value=mix(base,protectedValue,featureProtection);"
|
||||
"if(skinProtection<1.0){float extended=1.0-skinProtection*(1.0-base);"
|
||||
"float retention=base>0.00001?clamp(protectedValue/base,0.0,1.0):1.0;"
|
||||
"value=extended*mix(1.0,retention,featureProtection);}"
|
||||
"return vec4(value,value,value,1.0); }");
|
||||
k[@"microDetail"] = PGKernel(@
|
||||
"kernel vec4 pgMicroDetail(__sample processed,__sample original,__sample low,__sample coverage,float amount) {"
|
||||
"vec4 outPixel=unpremultiply(processed);vec3 src=unpremultiply(original).rgb;"
|
||||
"vec3 high=src-unpremultiply(low).rgb;vec3 weights=vec3(0.299,0.587,0.114);"
|
||||
"float y=dot(src,weights);float detailY=dot(high,weights);float size=length(high);"
|
||||
"float lightGate=smoothstep(0.07,0.22,y)*(1.0-smoothstep(0.86,0.99,y));"
|
||||
"float textureGate=smoothstep(0.0015,0.010,size)*(1.0-smoothstep(0.16,0.36,size));"
|
||||
"float gain=amount*clamp(coverage.r,0.0,1.0)*lightGate*textureGate*1.18;"
|
||||
"vec3 detail=vec3(detailY*0.92)+(high-vec3(detailY))*0.28;"
|
||||
"return premultiply(vec4(clamp(outPixel.rgb+detail*gain,0.0,1.0),outPixel.a)); }");
|
||||
k[@"colorCoverage"] = PGKernel(@
|
||||
"kernel vec4 pgColorCoverage(__sample coverage,__sample image,float invert) {"
|
||||
"vec3 c=unpremultiply(image).rgb; float y=dot(c,vec3(0.299,0.587,0.114));"
|
||||
@@ -157,7 +177,7 @@ static NSDictionary<NSString *, CIColorKernel *> *PGKernels(void) {
|
||||
float mix = PGClamp(s.mix, 0, 1);
|
||||
if (mix == 0 && !s.showMask) return image;
|
||||
NSDictionary *k = PGKernels();
|
||||
if (k.count != 11) {
|
||||
if (k.count != 13) {
|
||||
if (error) *error = [NSError errorWithDomain:@"PGBeautyRenderer" code:2
|
||||
userInfo:@{NSLocalizedDescriptionKey:@"The beauty image kernels could not be compiled."}];
|
||||
return nil;
|
||||
@@ -173,7 +193,10 @@ static NSDictionary<NSString *, CIColorKernel *> *PGKernels(void) {
|
||||
float sharpness = PGClamp(s.sharpness,0,2);
|
||||
float teeth = PGClamp(s.teethWhitening,0,1);
|
||||
float eyes = PGClamp(s.eyeBrightening,0,1);
|
||||
BOOL needsSkin = local*amount>0 || brightness>0 || hue!=0 || saturation!=0 || even>0 ||
|
||||
float protection = s.featureProtection ? PGClamp(s.featureProtectionAmount,0,1) : 0;
|
||||
float skinProtection = PGClamp(s.nonSkinProtection,0,1);
|
||||
float micro = PGClamp(s.microDetail,0,1)*amount;
|
||||
BOOL needsSkin = local*amount>0 || micro>0 || brightness>0 || hue!=0 || saturation!=0 || even>0 ||
|
||||
(s.skinTonePreset>0 && s.skinTonePreset<=6) || (s.showMask && s.debugChannel==0);
|
||||
CIImage *black = [[CIImage imageWithColor:[CIColor colorWithRed:0 green:0 blue:0 alpha:1]] imageByCroppingToRect:extent];
|
||||
CIImage *teethInput = teethMask ? [teethMask imageByCroppingToRect:extent] : black;
|
||||
@@ -200,12 +223,29 @@ static NSDictionary<NSString *, CIColorKernel *> *PGKernels(void) {
|
||||
float invert = s.debugChannel==1 || s.debugChannel==2 ? (s.invertMask ? 1 : 0) : 0;
|
||||
return PGApply(k[@"debug"],extent,@[image,preview,@(invert)]);
|
||||
}
|
||||
CIImage *smoothingMask=mask;
|
||||
if((local*amount>0 || micro>0) && (protection<1 || skinProtection<1)) {
|
||||
CIImage *baseRaw=colorMask ? [colorMask imageByCroppingToRect:extent] : raw;
|
||||
float soft=PGClamp(s.maskSoftness,0,1);
|
||||
CIImage *baseMask=PGApply(k[@"mask"],extent,@[baseRaw,PGBlur(baseRaw,0.35+soft*radius*0.35,extent),
|
||||
image,PGBlur(image,1.5,extent),eyeInput,teethInput,
|
||||
@(PGClamp(s.maskThreshold,0,255)/255.f),@(soft),@(PGClamp(s.maskEdgeProtect,0,1)),@0,@(s.invertMask?1:0)]);
|
||||
smoothingMask=PGApply(k[@"smoothingSupport"],extent,@[mask,baseMask,@(protection),@(skinProtection)]);
|
||||
}
|
||||
// Detail protection may contain small eye, brow, nose and lip cutouts.
|
||||
// Exposure and color must not reuse those holes or sharpen a face ellipse
|
||||
// with the detail threshold; that creates visible rings and islands.
|
||||
CIImage *toneMask=mask;
|
||||
BOOL changesColor=brightness>0 || hue!=0 || saturation!=0 || even>0 ||
|
||||
(s.skinTonePreset>0 && s.skinTonePreset<=6);
|
||||
if(!colorMask && changesColor && s.featureProtection) {
|
||||
// Standalone callers may not supply the separate uncut coverage.
|
||||
// At least keep our eye/teeth cutouts out of their color adjustment.
|
||||
float soft=PGClamp(s.maskSoftness,0,1);
|
||||
toneMask=PGApply(k[@"mask"],extent,@[raw,PGBlur(raw,0.35+soft*radius*0.35,extent),
|
||||
image,PGBlur(image,1.5,extent),eyeInput,teethInput,
|
||||
@(PGClamp(s.maskThreshold,0,255)/255.f),@(soft),@(PGClamp(s.maskEdgeProtect,0,1)),@0,@(s.invertMask?1:0)]);
|
||||
}
|
||||
if(colorMask && changesColor) {
|
||||
CGFloat feather=MAX(3,MIN(extent.size.width,extent.size.height)*0.03);
|
||||
CIImage *support=[colorMask imageByCroppingToRect:extent];
|
||||
@@ -219,8 +259,12 @@ static NSDictionary<NSString *, CIColorKernel *> *PGKernels(void) {
|
||||
CGFloat broad = fmin(16,(2.55+0.45*local)*fine+1.1+1.3*local);
|
||||
float compression = 0.12+2.4*powf(local,2.15)+1.25*local;
|
||||
CIImage *detail = PGApply(k[@"detail"],extent,@[result,PGBlur(result,fine,extent),
|
||||
PGBlur(result,broad,extent),@(compression),@(s.featureProtection ? 1 : 0)]);
|
||||
result = PGApply(k[@"maskedMix"],extent,@[result,detail,mask,@(amount)]);
|
||||
PGBlur(result,broad,extent),@(compression),@(protection)]);
|
||||
result = PGApply(k[@"maskedMix"],extent,@[result,detail,smoothingMask,@(amount)]);
|
||||
}
|
||||
if(micro>0) {
|
||||
CGFloat fine=fmin(7.4,2.275+3.15*pow(local,0.72)+0.95*local);
|
||||
result=PGApply(k[@"microDetail"],extent,@[result,image,PGBlur(image,fine,extent),smoothingMask,@(micro)]);
|
||||
}
|
||||
CIImage *color = result;
|
||||
if (brightness>0) {
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
#pragma once
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <math.h>
|
||||
|
||||
// Persisted FxPlug render state. Append fields and bump version for migrations.
|
||||
typedef struct {
|
||||
@@ -12,11 +14,17 @@ typedef struct {
|
||||
float faceJumpGuardRange, selectionX, selectionY;
|
||||
uint32_t featureProtection, skinTonePreset, showMask, invertMask, debugChannel;
|
||||
uint32_t showFaceBoxes, showTrackBox, singleFace, fastMultiFace, trackingEnabled, targetIndex;
|
||||
// v2 keeps the entire 160-byte v1 prefix, including legacy bool/strength IDs.
|
||||
float featureProtectionAmount, nonSkinProtection, microDetail;
|
||||
} PGBeautySettings;
|
||||
|
||||
enum { PGBeautySettingsV1Size = 160, PGBeautySettingsVersion = 2 };
|
||||
_Static_assert(offsetof(PGBeautySettings,featureProtectionAmount)==PGBeautySettingsV1Size,
|
||||
"The v1 render-state prefix must remain stable");
|
||||
|
||||
static inline PGBeautySettings PGBeautyDefaults(void) {
|
||||
PGBeautySettings s = {0};
|
||||
s.version = 1;
|
||||
s.version = PGBeautySettingsVersion;
|
||||
s.amount = 1.0f;
|
||||
s.radius = 8.0f;
|
||||
s.mix = 1.0f;
|
||||
@@ -24,6 +32,8 @@ static inline PGBeautySettings PGBeautyDefaults(void) {
|
||||
s.maskSoftness = 0.6f;
|
||||
s.maskEdgeProtect = 0.4f;
|
||||
s.featureProtection = 1;
|
||||
s.featureProtectionAmount = 1;
|
||||
s.nonSkinProtection = 1;
|
||||
s.faceJumpGuardRange = 1.8f;
|
||||
s.selectionX = s.selectionY = 0.5f;
|
||||
return s;
|
||||
|
||||
@@ -11,9 +11,10 @@ NSArray<NSDictionary *> *PGInspectorControls(void) {
|
||||
#define ITEM(ID,NAME,FIELD,SECTION,KIND,FORMAT,MIN,MAX,BYPASS,STORAGE) \
|
||||
[items addObject:@{@"id":@(ID),@"name":@NAME,@"section":@SECTION,@"kind":@KIND,@"format":@FORMAT,@"min":@(MIN),@"max":@(MAX),@"default":@(defaults.FIELD),@"bypass":@(BYPASS),@"offset":@(offsetof(PGBeautySettings,FIELD)),@"storage":@STORAGE}]
|
||||
ITEM(PGSmoothingEnabled,"磨皮",featureProtection,"smooth","master","integer",0,1,NO,"master");
|
||||
ITEM(PGMidtone,"局部磨皮",midtoneDetail,"smooth","slider","percent",0,1,YES,"float");
|
||||
ITEM(PGHighPass,"全局磨皮",highPassDetail,"smooth","slider","percent",0,1,YES,"float");
|
||||
ITEM(PGFeatureProtect,"五官保护",featureProtection,"smooth","toggle","integer",0,1,NO,"uint");
|
||||
ITEM(PGMidtone,"磨皮程度",midtoneDetail,"smooth","slider","percent",0,1,YES,"float");
|
||||
ITEM(PGMicroDetail,"微细锐化",microDetail,"smooth","slider","percent",0,1,YES,"float");
|
||||
ITEM(PGFeatureProtectionAmount,"五官保护",featureProtectionAmount,"smooth","slider","percent",0,1,YES,"float");
|
||||
ITEM(PGNonSkinProtection,"非肤色区保护",nonSkinProtection,"smooth","slider","percent",0,1,YES,"float");
|
||||
ITEM(PGColorEnabled,"美白",featureProtection,"color","master","integer",0,1,NO,"master");
|
||||
ITEM(PGTonePreset,"肤色",skinTonePreset,"color","preset","integer",0,6,NO,"uint");
|
||||
NSMutableDictionary *preset=[items.lastObject mutableCopy];
|
||||
@@ -22,9 +23,10 @@ NSArray<NSDictionary *> *PGInspectorControls(void) {
|
||||
ITEM(PGTeeth,"白牙",teethWhitening,"color","slider","percent",0,1,YES,"float");
|
||||
ITEM(PGHue,"皮肤色相",skinHue,"color","slider","degrees",-30,30,YES,"float");
|
||||
ITEM(PGShapeEnabled,"瘦脸",featureProtection,"shape","master","integer",0,1,NO,"master");
|
||||
ITEM(PGTrackBoxes,"人脸框预览",showTrackBox,"shape","toggle","integer",0,1,NO,"uint");
|
||||
ITEM(PGTrackBoxes,"检测框预览",showTrackBox,"shape","toggle","integer",0,1,NO,"uint");
|
||||
ITEM(PGFastMulti,"多人模式",fastMultiFace,"shape","toggle","integer",0,1,NO,"uint");
|
||||
ITEM(PGJumpGuard,"防跳脸范围",faceJumpGuardRange,"shape","slider","percent",1,4,NO,"float");
|
||||
NSMutableDictionary *multi=[items.lastObject mutableCopy];
|
||||
multi[@"tooltip"]=@"多人模式易抖动,请谨慎使用";items[items.count-1]=multi;
|
||||
ITEM(PGThin,"瘦脸",faceThin,"shape","slider","signed",0,1,YES,"float");
|
||||
ITEM(PGBigEye,"大眼",faceBigEye,"shape","slider","signed",0,1,YES,"float");
|
||||
ITEM(PGShort,"短脸",faceShort,"shape","slider","signed",-1,1,YES,"float");
|
||||
@@ -36,11 +38,13 @@ NSArray<NSDictionary *> *PGInspectorControls(void) {
|
||||
ITEM(PGSmallHead,"小头",faceSmallHead,"shape","slider","signed",0,1,YES,"float");
|
||||
ITEM(PGSmallHeadRange,"小头范围",faceSmallHeadRange,"shape","slider","percent",0,1,NO,"float");
|
||||
ITEM(PGAmount,"磨皮强度",amount,"more","slider","percent",0,1,YES,"float");
|
||||
ITEM(PGHighPass,"全局磨皮",highPassDetail,"more","slider","percent",0,1,YES,"float");
|
||||
ITEM(PGRadius,"磨皮半径",radius,"more","slider","radius",1,30,NO,"float");
|
||||
ITEM(PGSaturation,"肤色饱和度",skinSaturation,"more","slider","percent",-100,100,YES,"float");
|
||||
ITEM(PGEvenTone,"均匀肤色",skinEvenTone,"more","slider","percent",0,1,YES,"float");
|
||||
ITEM(PGEyeBright,"眼睛提亮",eyeBrightening,"more","slider","percent",0,1,YES,"float");
|
||||
ITEM(PGSharpness,"锐化",sharpness,"more","slider","percent",0,1,YES,"float");
|
||||
ITEM(PGJumpGuard,"防跳脸范围",faceJumpGuardRange,"more","slider","percent",1,4,NO,"float");
|
||||
ITEM(PGMix,"整体混合",mix,"more","slider","percent",0,1,NO,"float");
|
||||
#undef ITEM
|
||||
controls=[items copy];
|
||||
@@ -62,7 +66,12 @@ static double PGControlValue(id raw,NSDictionary *d) {
|
||||
+ (instancetype)stateWithValues:(NSDictionary *)values disabledParameters:(NSSet *)disabled hasEdits:(BOOL)edited {
|
||||
PGInspectorState *state=[self new]; NSMutableDictionary *safe=[NSMutableDictionary new];NSMutableSet *bypassed=[NSMutableSet new];
|
||||
for(NSDictionary *d in PGInspectorControls()) {
|
||||
NSNumber *key=d[@"id"];safe[key]=@(PGControlValue(values[key],d));
|
||||
NSNumber *key=d[@"id"];id raw=values[key];
|
||||
// Schema 1 documents stored five-feature protection as the legacy bool.
|
||||
// New numeric values win when present; old archives gain its exact 0/1.
|
||||
if(key.unsignedIntValue==PGFeatureProtectionAmount && !raw && [values[@(PGFeatureProtect)] isKindOfClass:NSNumber.class])
|
||||
raw=@([values[@(PGFeatureProtect)] boolValue]?1:0);
|
||||
safe[key]=@(PGControlValue(raw,d));
|
||||
if([d[@"bypass"] boolValue] && [disabled containsObject:key]) [bypassed addObject:key];
|
||||
}
|
||||
state->_values=[safe copy];state->_disabledParameters=[bypassed copy];state->_hasEdits=edited;return state;
|
||||
@@ -115,7 +124,8 @@ PGBeautySettings PGSettingsWithInspectorState(PGBeautySettings s,PGInspectorStat
|
||||
if([storage isEqual:@"float"]) *(float *)((uint8_t *)&s+offset)=value;
|
||||
else *(uint32_t *)((uint8_t *)&s+offset)=(uint32_t)value;
|
||||
}
|
||||
if(![state valueForParameter:PGSmoothingEnabled])s.amount=s.midtoneDetail=s.highPassDetail=0;
|
||||
s.featureProtection=s.featureProtectionAmount>0;
|
||||
if(![state valueForParameter:PGSmoothingEnabled])s.amount=s.midtoneDetail=s.highPassDetail=s.microDetail=0;
|
||||
if(![state valueForParameter:PGColorEnabled]) {s.skinBrightness=s.skinHue=s.skinSaturation=s.skinEvenTone=s.teethWhitening=s.eyeBrightening=0;s.skinTonePreset=0;}
|
||||
if(![state valueForParameter:PGShapeEnabled]) {s.faceThin=s.faceBigEye=s.faceShort=s.faceNarrow=s.faceChin=s.faceV=s.faceJaw=s.faceCheekbone=s.faceSmallHead=0;s.showTrackBox=0;}
|
||||
return s;
|
||||
@@ -189,6 +199,12 @@ PGBeautySettings PGSettingsWithInspectorState(PGBeautySettings s,PGInspectorStat
|
||||
else [api getFloatValue:&value fromParameter:key atTime:time];
|
||||
values[@(key)]=@(value);
|
||||
}
|
||||
// The old bool can still disable protection. A true value must preserve a
|
||||
// numeric setting already read above; its registered default is one for
|
||||
// old documents that have no independently stored numeric parameter.
|
||||
BOOL legacyFeatureProtection=YES;
|
||||
if([api getBoolValue:&legacyFeatureProtection fromParameter:PGFeatureProtect atTime:time] && !legacyFeatureProtection)
|
||||
values[@(PGFeatureProtectionAmount)]=@0;
|
||||
BOOL legacyDetail=YES;[api getBoolValue:&legacyDetail fromParameter:PGDetailEnabled atTime:time];
|
||||
NSMutableSet *disabled=legacyDetail?[NSMutableSet set]:[NSMutableSet setWithObjects:@(PGHighPass),@(PGSharpness),nil];
|
||||
// Earlier versions put high-pass smoothing under a different group. Keep
|
||||
@@ -196,7 +212,7 @@ PGBeautySettings PGSettingsWithInspectorState(PGBeautySettings s,PGInspectorStat
|
||||
// off, rather than turning a still-active global effect off on first edit.
|
||||
if(![values[@(PGSmoothingEnabled)] boolValue] && legacyDetail && [values[@(PGHighPass)] doubleValue]>0) {
|
||||
values[@(PGSmoothingEnabled)]=@YES;
|
||||
[disabled addObject:@(PGMidtone)];[disabled addObject:@(PGAmount)];
|
||||
[disabled addObject:@(PGMidtone)];[disabled addObject:@(PGAmount)];[disabled addObject:@(PGMicroDetail)];
|
||||
}
|
||||
_state=[PGInspectorState stateWithValues:values disabledParameters:disabled hasEdits:NO];
|
||||
}
|
||||
@@ -232,6 +248,11 @@ PGBeautySettings PGSettingsWithInspectorState(PGBeautySettings s,PGInspectorStat
|
||||
else if([d[@"kind"] isEqual:@"toggle"] || [d[@"kind"] isEqual:@"master"]) [api setBoolValue:value!=0 toParameter:key atTime:time];
|
||||
else [api setFloatValue:value toParameter:key atTime:time];
|
||||
}
|
||||
BOOL featureProtection=[next isParameterEnabled:PGFeatureProtectionAmount] && [next valueForParameter:PGFeatureProtectionAmount]>0;
|
||||
id<FxParameterRetrievalAPI_v6> reader=[_manager apiForProtocol:@protocol(FxParameterRetrievalAPI_v6)];
|
||||
BOOL previousProtection=!featureProtection;
|
||||
if(![reader getBoolValue:&previousProtection fromParameter:PGFeatureProtect atTime:time] || previousProtection!=featureProtection)
|
||||
[api setBoolValue:featureProtection toParameter:PGFeatureProtect atTime:time];
|
||||
_state=next;_pendingUntil=NSDate.timeIntervalSinceReferenceDate+.35;_pendingTime=time;_statusMessage=@"";return YES;
|
||||
}
|
||||
- (BOOL)setValue:(double)value forParameter:(UInt32)identifier {
|
||||
|
||||
@@ -202,6 +202,8 @@ static PGInspectorGlyphButton *PGUIButton(NSString *glyph, NSColor *accent, id t
|
||||
}
|
||||
}
|
||||
NSString *key=[descriptor[@"id"]stringValue];
|
||||
NSString *tooltip=descriptor[@"tooltip"];
|
||||
if(tooltip.length){self.toolTip=tooltip;_label.toolTip=tooltip;_toggle.toolTip=tooltip;_slider.toolTip=tooltip;_valueField.toolTip=tooltip;}
|
||||
_slider.identifier=[@"pg.value."stringByAppendingString:key];_valueField.identifier=[@"pg.number."stringByAppendingString:key];
|
||||
_toggle.identifier=[@"pg.toggle."stringByAppendingString:key];_eye.identifier=[@"pg.enable."stringByAppendingString:key];_reset.identifier=[@"pg.reset."stringByAppendingString:key];
|
||||
}return self;
|
||||
@@ -282,11 +284,11 @@ static PGInspectorGlyphButton *PGUIButton(NSString *glyph, NSColor *accent, id t
|
||||
@implementation PGInspectorCard
|
||||
- (BOOL)isFlipped { return YES; }
|
||||
- (CGFloat)heightForWidth:(CGFloat)width {
|
||||
if(!_expanded)return 44;CGFloat height=49;for(PGInspectorRow *row in _rows){height+=[row heightForWidth:MAX(0,width-24)];if([row.descriptor[@"id"]unsignedIntValue]==PGJumpGuard)height+=8;}return height+7;
|
||||
if(!_expanded)return 44;CGFloat height=49;for(PGInspectorRow *row in _rows){height+=[row heightForWidth:MAX(0,width-24)];if([row.descriptor[@"id"]unsignedIntValue]==PGFastMulti)height+=8;}return height+7;
|
||||
}
|
||||
- (void)layout {
|
||||
[super layout];CGFloat w=NSWidth(self.bounds);_titleLabel.frame=NSMakeRect(25,13,MAX(70,w-80),22);_master.frame=NSMakeRect(w-39,10,25,25);
|
||||
CGFloat y=49;for(PGInspectorRow *row in _rows){row.hidden=!_expanded;if(!_expanded)continue;CGFloat h=[row heightForWidth:w-24];row.frame=NSMakeRect(12,y,w-24,h);y+=h;if([row.descriptor[@"id"]unsignedIntValue]==PGJumpGuard)y+=8;}
|
||||
CGFloat y=49;for(PGInspectorRow *row in _rows){row.hidden=!_expanded;if(!_expanded)continue;CGFloat h=[row heightForWidth:w-24];row.frame=NSMakeRect(12,y,w-24,h);y+=h;if([row.descriptor[@"id"]unsignedIntValue]==PGFastMulti)y+=8;}
|
||||
}
|
||||
- (void)drawRect:(NSRect)dirtyRect {
|
||||
(void)dirtyRect;NSBezierPath *shape=[NSBezierPath bezierPathWithRoundedRect:NSInsetRect(self.bounds,.5,.5) xRadius:13 yRadius:13];
|
||||
@@ -294,7 +296,7 @@ static PGInspectorGlyphButton *PGUIButton(NSString *glyph, NSColor *accent, id t
|
||||
[[_accent colorWithAlphaComponent:.22]setStroke];shape.lineWidth=.8;[shape stroke];
|
||||
NSRect pill=NSMakeRect(12,10,MAX(53,[_heading length]*13+20),25);[[_accent colorWithAlphaComponent:.10]setFill];NSBezierPath *p=[NSBezierPath bezierPathWithRoundedRect:pill xRadius:8 yRadius:8];[p fill];[[_accent colorWithAlphaComponent:.32]setStroke];p.lineWidth=.7;[p stroke];
|
||||
if(_expanded){[[PGUIMuted() colorWithAlphaComponent:.20]setStroke];NSBezierPath *line=[NSBezierPath bezierPath];[line moveToPoint:NSMakePoint(12,43)];[line lineToPoint:NSMakePoint(NSWidth(self.bounds)-12,43)];line.lineWidth=.5;[line stroke];
|
||||
for(PGInspectorRow *row in _rows)if([row.descriptor[@"id"]unsignedIntValue]==PGJumpGuard){CGFloat y=NSMaxY(row.frame)+4;[line removeAllPoints];[line moveToPoint:NSMakePoint(12,y)];[line lineToPoint:NSMakePoint(NSWidth(self.bounds)-12,y)];[line stroke];}
|
||||
for(PGInspectorRow *row in _rows)if([row.descriptor[@"id"]unsignedIntValue]==PGFastMulti){CGFloat y=NSMaxY(row.frame)+4;[line removeAllPoints];[line moveToPoint:NSMakePoint(12,y)];[line lineToPoint:NSMakePoint(NSWidth(self.bounds)-12,y)];[line stroke];}
|
||||
}
|
||||
}
|
||||
@end
|
||||
|
||||
@@ -14,5 +14,6 @@ enum {
|
||||
PGTrackData1=60, PGTrackData2=61, PGTrackData3=62,
|
||||
PGSmoothingEnabled=70, PGColorEnabled=71, PGShapeEnabled=72,
|
||||
PGDetailEnabled=73, PGHelp=74,
|
||||
PGFeatureProtectionAmount=75, PGNonSkinProtection=76, PGMicroDetail=77,
|
||||
PGInspectorPanel=210
|
||||
};
|
||||
@@ -30,6 +30,9 @@ static const PGSlider PGSliders[] = {
|
||||
PGS(PGEyeBright,"眼睛提亮",eyeBrightening,0,1,.01,YES),
|
||||
PGS(PGHighPass,"高频细节平滑",highPassDetail,0,1,.01,YES),
|
||||
PGS(PGSharpness,"锐化",sharpness,0,1,.01,YES),
|
||||
PGS(PGFeatureProtectionAmount,"五官保护强度",featureProtectionAmount,0,1,.01,YES),
|
||||
PGS(PGNonSkinProtection,"非肤色区保护",nonSkinProtection,0,1,.01,YES),
|
||||
PGS(PGMicroDetail,"微细锐化",microDetail,0,1,.01,YES),
|
||||
PGS(PGThin,"瘦脸",faceThin,0,1,.01,YES),
|
||||
PGS(PGBigEye,"大眼",faceBigEye,0,1,.01,YES),
|
||||
PGS(PGShort,"短脸",faceShort,-1,1,.01,NO),
|
||||
@@ -141,6 +144,7 @@ static CIImage *PGImageFromTile(FxImageTile *tile) {
|
||||
void (^end)(void)=^{ok=[api endParameterSubGroup]&&ok;};
|
||||
group(@"磨皮",100); toggle(@"启用磨皮",PGSmoothingEnabled,YES);
|
||||
slider(PGAmount); slider(PGMidtone); slider(PGRadius); toggle(@"保护五官",PGFeatureProtect,YES); end();
|
||||
group(@"磨皮保护与细节",106); slider(PGFeatureProtectionAmount); slider(PGNonSkinProtection); slider(PGMicroDetail); end();
|
||||
group(@"肤色",101); toggle(@"启用肤色",PGColorEnabled,YES);
|
||||
ok=[api addPopupMenuWithName:@"肤色风格" parameterID:PGTonePreset defaultValue:0 menuEntries:@[@"原色",@"自然白",@"冷白",@"暖白",@"红润白",@"小麦",@"古铜"] parameterFlags:kFxParameterFlag_HIDDEN]&&ok;
|
||||
slider(PGBrightness); slider(PGHue); slider(PGSaturation); slider(PGEvenTone); slider(PGTeeth); slider(PGEyeBright); end();
|
||||
@@ -207,7 +211,7 @@ static CIImage *PGImageFromTile(FxImageTile *tile) {
|
||||
double x=.5,y=.5; [api getXValue:&x YValue:&y fromParameter:PGSelection atTime:time];
|
||||
s.selectionX=isfinite(x)?fmin(1,fmax(0,x)):.5; s.selectionY=isfinite(y)?fmin(1,fmax(0,y)):.5;
|
||||
BOOL enabled=YES;
|
||||
[api getBoolValue:&enabled fromParameter:PGSmoothingEnabled atTime:time]; if(!enabled) s.amount=s.midtoneDetail=0;
|
||||
[api getBoolValue:&enabled fromParameter:PGSmoothingEnabled atTime:time]; if(!enabled) s.amount=s.midtoneDetail=s.microDetail=0;
|
||||
enabled=YES; [api getBoolValue:&enabled fromParameter:PGColorEnabled atTime:time];
|
||||
if(!enabled) {s.skinBrightness=s.skinHue=s.skinSaturation=s.skinEvenTone=s.teethWhitening=s.eyeBrightening=0;s.skinTonePreset=0;}
|
||||
enabled=YES; [api getBoolValue:&enabled fromParameter:PGDetailEnabled atTime:time]; if(!enabled) s.highPassDetail=s.sharpness=0;
|
||||
@@ -263,9 +267,16 @@ static CIImage *PGImageFromTile(FxImageTile *tile) {
|
||||
- (BOOL)renderDestinationImage:(FxImageTile *)destination sourceImages:(NSArray<FxImageTile *> *)sources pluginState:(NSData *)state atTime:(CMTime)time error:(NSError **)error {
|
||||
if(sources.count==0||!sources.firstObject.ioSurface||!destination.ioSurface||![state isKindOfClass:NSData.class]||state.length==0||state.length>320*1024*1024) return PGFail(error,@"宿主传入的画面或状态无效。");
|
||||
NSDictionary *plist=[NSPropertyListSerialization propertyListWithData:state options:NSPropertyListImmutable format:nil error:error];
|
||||
if(![plist isKindOfClass:NSDictionary.class]||![plist[@"settings"] isKindOfClass:NSData.class]||[plist[@"settings"] length]!=sizeof(PGBeautySettings)) return PGFail(error,@"渲染状态版本无效。");
|
||||
PGBeautySettings s;[plist[@"settings"] getBytes:&s length:sizeof(s)];
|
||||
if(s.version!=1) return PGFail(error,@"此版本不支持保存的渲染状态。");
|
||||
if(![plist isKindOfClass:NSDictionary.class]||![plist[@"settings"] isKindOfClass:NSData.class]) return PGFail(error,@"渲染状态版本无效。");
|
||||
NSData *encoded=plist[@"settings"]; uint32_t version=0;
|
||||
if(encoded.length<sizeof(version)) return PGFail(error,@"渲染状态版本无效。");
|
||||
[encoded getBytes:&version length:sizeof(version)];
|
||||
PGBeautySettings s=PGBeautyDefaults();
|
||||
if(version==1 && encoded.length==PGBeautySettingsV1Size) {
|
||||
[encoded getBytes:&s length:PGBeautySettingsV1Size];s.version=PGBeautySettingsVersion;
|
||||
} else if(version==PGBeautySettingsVersion && encoded.length==sizeof(s)) {
|
||||
[encoded getBytes:&s length:sizeof(s)];
|
||||
} else return PGFail(error,@"此版本不支持保存的渲染状态。");
|
||||
s.trackingEnabled=0;s.singleFace=0;
|
||||
CIImage *input=PGImageFromTile(sources.firstObject); if(!input) return PGFail(error,@"无法读取 IOSurface。");
|
||||
@synchronized(self) {
|
||||
|
||||
@@ -17,9 +17,9 @@
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.2.0</string>
|
||||
<string>1.3.0</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>14</string>
|
||||
<string>15</string>
|
||||
<key>LSMinimumSystemVersion</key>
|
||||
<string>$(MACOSX_DEPLOYMENT_TARGET)</string>
|
||||
<key>NSHumanReadableCopyright</key>
|
||||
|
||||
在新工单中引用
屏蔽一个用户