Migrate new beauty controls with continuous protection, local detail recovery and legacy state compatibility; 1636 checks passed

这个提交包含在:
dayu 于 2026-10-22 15:07:02 +08:00 提交
1 父节点 2e79602761
当前提交 2f2c081980
16 个文件已更改
+350 -44

没有与搜索条件相匹配的文件

+4 -4
查看文件
@@ -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>
+48 -4
查看文件
@@ -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) {
+11 -1
查看文件
@@ -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;
+29 -8
查看文件
@@ -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 {
+5 -3
查看文件
@@ -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
};
+15 -4
查看文件
@@ -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>