为 Adaptive Sharpen Effect 添加 Strength 属性

This commit is contained in:
Xu Liu 2021-02-22 17:53:34 +08:00
commit 52eca2e45b
19 changed files with 313 additions and 144 deletions

View file

@ -3,7 +3,9 @@
#include "GUIDs.h"
#include "Shaders.h"
#include "SimpleDrawTransform.h"
#include "AdaptiveSharpenPass2Transform.h"
#include "EffectBase.h"
#include <d2d1effecthelpers.h>
// Adaptive sharpen Ëã·¨
@ -16,17 +18,15 @@ public:
HRESULT hr = SimpleDrawTransform::Create(
pEffectContext,
&_pass1Transform,
ADAPTIVE_SHARPEN_PASS1_SHADER,
MAGPIE_ADAPTIVE_SHARPEN_PASS1_SHADER,
GUID_MAGPIE_ADAPTIVE_SHARPEN_PASS1_SHADER
);
if (FAILED(hr)) {
return hr;
}
hr = SimpleDrawTransform::Create(
hr = AdaptiveSharpenPass2Transform::Create(
pEffectContext,
&_pass2Transform,
ADAPTIVE_SHARPEN_PASS2_SHADER,
GUID_MAGPIE_ADAPTIVE_SHARPEN_PASS2_SHADER
&_pass2Transform
);
if (FAILED(hr)) {
return hr;
@ -57,20 +57,51 @@ public:
return S_OK;
}
HRESULT SetStrength(FLOAT value) {
if (value < 0 || value > 1) {
return E_INVALIDARG;
}
// 将 0~1 映射到 0.3~2
_pass2Transform->SetCurveHeight(value * 1.7f + 0.3f);
return S_OK;
}
FLOAT GetStrength() const {
// 将 0.3~2 映射到 0~1
return (_pass2Transform->GetCurveHeight() - 0.3f) / 1.7f;
}
enum PROPS {
// FLOAT 类型。指示锐化强度,取值范围为 0~1默认值为 0
PROP_STRENGTH = 0
};
static HRESULT Register(_In_ ID2D1Factory1* pFactory) {
const D2D1_PROPERTY_BINDING bindings[] =
{
D2D1_VALUE_TYPE_BINDING(L"Strength", &SetStrength, &GetStrength),
};
HRESULT hr = pFactory->RegisterEffectFromString(CLSID_MAGPIE_ADAPTIVE_SHARPEN_EFFECT, XML(
<?xml version='1.0'?>
<Effect>
<!--System Properties-->
<Property name='DisplayName' type='string' value='Ripple' />
<Property name='Author' type='string' value='Microsoft Corporation' />
<Property name='Category' type='string' value='Stylize' />
<Property name='Description' type='string' value='Adds a ripple effect that can be animated' />
<Property name='DisplayName' type='string' value='Adaptive Sharpen' />
<Property name='Author' type='string' value='Xu Liu' />
<Property name='Category' type='string' value='Sharpen' />
<Property name='Description' type='string' value='Adaptive Sharpen' />
<Inputs>
<Input name='Source' />
</Inputs>
<Property name='Strength' type='float'>
<Property name='DisplayName' type='string' value='Strength' />
<Property name='Default' type='float' value='0' />
<Property name='Min' type='float' value='0' />
<Property name='Max' type='float' value='1' />
</Property>
</Effect>
), nullptr, 0, CreateEffect);
), bindings, ARRAYSIZE(bindings), CreateEffect);
return hr;
}
@ -90,5 +121,5 @@ private:
AdaptiveSharpenEffect() {}
ComPtr<SimpleDrawTransform> _pass1Transform = nullptr;
ComPtr<SimpleDrawTransform> _pass2Transform = nullptr;
ComPtr<AdaptiveSharpenPass2Transform> _pass2Transform = nullptr;
};

View file

@ -31,12 +31,45 @@ D2D_PS_ENTRY(main) {
// [ c20, c6, c7, c8, c17 ]
// [ c15, c12, c14 ]
// [ c13 ]
float3 c[25] = {
SampleInputCur(0), SampleInputOffCheckLeftTop(0, -1, -1), SampleInputOffCheckTop(0, 0, -1), SampleInputOffCheckRightTop(0, 1, -1), SampleInputOffCheckLeft(0, -1, 0),
SampleInputOffCheckRight(0, 1, 0), SampleInputOffCheckLeftBottom(0, -1, 1), SampleInputOffCheckBottom(0, 0, 1), SampleInputOffCheckRightBottom(0, 1, 1), SampleInputOffCheckTop(0, 0, -2),
SampleInputOffCheckLeft(0, -2, 0), SampleInputOffCheckRight(0, 2, 0), SampleInputOffCheckBottom(0, 0, 2), SampleInputOffCheckBottom(0, 0, 3), SampleInputOffCheckRightBottom(0, 1, 2),
SampleInputOffCheckLeftBottom(0, -1, 2), SampleInputOffCheckRight(0, 3, 0), SampleInputOffCheckRightBottom(0, 2, 1), SampleInputOffCheckRightTop(0, 2,-1), SampleInputOffCheckLeft(0, -3, 0),
SampleInputOffCheckLeftBottom(0, -2, 1), SampleInputOffCheckLeftTop(0, -2, -1), SampleInputOffCheckTop(0, 0, -3), SampleInputOffCheckRightTop(0, 1, -2), SampleInputOffCheckLeftTop(0, -1, -2)
float left1X = GetCheckedLeft(0, 1);
float left2X = GetCheckedLeft(0, 2);
float left3X = GetCheckedLeft(0, 3);
float right1X = GetCheckedRight(0, 1);
float right2X = GetCheckedRight(0, 2);
float right3X = GetCheckedRight(0, 3);
float top1Y = GetCheckedTop(0, 1);
float top2Y = GetCheckedTop(0, 2);
float top3Y = GetCheckedTop(0, 3);
float bottom1Y = GetCheckedBottom(0, 1);
float bottom2Y = GetCheckedBottom(0, 2);
float bottom3Y = GetCheckedBottom(0, 3);
float3 c[25] = {
SampleInputCur(0), // c0
SampleInputNoCheck(0, float2(left1X, top1Y)), // c1
SampleInputNoCheck(0, float2(coord.x, top1Y)), // c2
SampleInputNoCheck(0, float2(right1X, top1Y)), // c3
SampleInputNoCheck(0, float2(left1X, coord.y)), // c4
SampleInputNoCheck(0, float2(right1X, coord.y)), // c5
SampleInputNoCheck(0, float2(left1X, bottom1Y)), // c6
SampleInputNoCheck(0, float2(coord.x, bottom1Y)), // c7
SampleInputNoCheck(0, float2(right1X, bottom1Y)), // c8
SampleInputNoCheck(0, float2(coord.x, top2Y)), // c9
SampleInputNoCheck(0, float2(left2X, coord.y)), // c10
SampleInputNoCheck(0, float2(right2X, coord.y)), // c11
SampleInputNoCheck(0, float2(coord.x, bottom2Y)), // c12
SampleInputNoCheck(0, float2(coord.x, bottom3Y)), // c13
SampleInputNoCheck(0, float2(right1X, bottom2Y)), // c14
SampleInputNoCheck(0, float2(left1X, bottom2Y)), // c15
SampleInputNoCheck(0, float2(right3X, coord.y)), // c16
SampleInputNoCheck(0, float2(right2X, bottom1Y)), // c17
SampleInputNoCheck(0, float2(right2X, top1Y)), // c18
SampleInputNoCheck(0, float2(left3X, coord.y)), // c19
SampleInputNoCheck(0, float2(left2X, bottom1Y)), // c20
SampleInputNoCheck(0, float2(left2X, top1Y)), // c21
SampleInputNoCheck(0, float2(coord.x, top3Y)), // c22
SampleInputNoCheck(0, float2(right1X, top2Y)), // c23
SampleInputNoCheck(0, float2(left1X, top2Y)) // c24
};
// Blur, gauss 3x3
@ -57,5 +90,5 @@ D2D_PS_ENTRY(main) {
+ b_diff(4) + b_diff(5) + b_diff(6) + b_diff(7) + b_diff(8)
+ 0.25 * (b_diff(9) + b_diff(10) + b_diff(11) + b_diff(12)));
return float4(D2DSampleInput(0, coord.xy).rgb, Compress(edge * c_comp));
return float4(c[0].rgb, Compress(edge * c_comp));
}

View file

@ -1,24 +1,25 @@
// 自适应锐化算法 Pass2
// 移植自 https://github.com/libretro/common-shaders/blob/master/sharpen/shaders/adaptive-sharpen-pass2.cg
//
// Adaptive sharpen
// Tuned for use post resize, EXPECTS FULL RANGE GAMMA LIGHT
cbuffer constants : register(b0) {
int2 srcSize : packoffset(c0.x);
//float curve_height : packoffset(c0.z); // 锐化强度,必须为正值。一般在 0.3~2.0 之间
float curveHeight : packoffset(c0.z); // 锐化强度,必须为正值。一般在 0.3~2.0 之间
};
#define curve_height 1
#define D2D_INPUT_COUNT 1
#define D2D_INPUT0_COMPLEX
#define MAGPIE_USE_SAMPLE_INPUT
#include "AdaptiveSharpen.hlsli"
// Adaptive sharpen
// Tuned for use post resize, EXPECTS FULL RANGE GAMMA LIGHT
//-------------------------------------------------------------------------------------------------
// Defined values under this row are "optimal" DO NOT CHANGE IF YOU DO NOT KNOW WHAT YOU ARE DOING!
#define curveslope (curve_height*0.7) // Sharpening curve slope, high edge values
#define curveslope (curveHeight*0.7) // Sharpening curve slope, high edge values
#define D_overshoot 0.009 // Max dark overshoot before max compression, >0!
#define D_compr_low 0.250 // Max compression ratio, dark overshoot (1/0.250=4x)
@ -40,8 +41,6 @@ cbuffer constants : register(b0) {
// Soft limit
#define soft_lim(v,s) (((exp(2*min(abs(v),s*16)/s)-1)/(exp(2*min(abs(v),s*16)/s)+1))*s)
#define sat(input) (float4(saturate((input).xyz),(input).w))
// Colour to luma, fast approx gamma
#define CtL(RGB) (sqrt(dot(float3(0.256,0.651,0.093),saturate((RGB).rgb*abs(RGB).rgb))))
@ -49,30 +48,20 @@ cbuffer constants : register(b0) {
#define mdiff(a,b,c,d,e,f,g) (abs(luma[g]-luma[a])+abs(luma[g]-luma[b])+abs(luma[g]-luma[c])+abs(luma[g]-luma[d])+0.5*(abs(luma[g]-luma[e])+abs(luma[g]-luma[f])))
float4 sample0(float2 pos) {
float4 coord = D2DGetInputCoordinate(0);
pos.x = max(0, pos.x);
pos.x = min((srcSize.x - 1) * coord.z, pos.x);
pos.y = max(0, pos.y);
pos.y = min((srcSize.y - 1) * coord.w, pos.y);
return D2DSampleInput(0, pos);
}
float4 get(float x, float y) {
float4 coord = D2DGetInputCoordinate(0);
float4 s = sample0(coord.xy + float2(x, y) * coord.zw);
float4 s = SampleInputRGBANoCheck(0, float2(x, y));
return float4(s.xyz, Uncompress(s.w));
}
D2D_PS_ENTRY(main) {
float4 coord = D2DGetInputCoordinate(0);
float4 orig0 = get(0,0);
D2D_PS_ENTRY(main) {
InitMagpieSampleInput();
float4 orig0 = get(coord.x, coord.y);
float c_edge = orig0.w;
// Displays a green screen if the edge data is not inside a valid range in the .w channel
// Displays the origin image if the edge data is not inside a valid range in the .w channel
if (c_edge > 32 || c_edge < -0.5) { return float4(orig0.xyz, 1); }
// Get points, saturate colour data in c[0]
@ -83,11 +72,46 @@ D2D_PS_ENTRY(main) {
// [ c20, c6, c7, c8, c17 ]
// [ c15, c12, c14 ]
// [ c13 ]
float4 c[25] = { orig0, get(-1,-1), get(0,-1), get(1,-1), get(-1, 0),
get(1, 0), get(-1, 1), get(0, 1), get(1, 1), get(0,-2),
get(-2, 0), get(2, 0), get(0, 2), get(0, 3), get(1, 2),
get(-1, 2), get(3, 0), get(2, 1), get(2,-1), get(-3, 0),
get(-2, 1), get(-2,-1), get(0,-3), get(1,-2), get(-1,-2) };
float left1X = GetCheckedLeft(0, 1);
float left2X = GetCheckedLeft(0, 2);
float left3X = GetCheckedLeft(0, 3);
float right1X = GetCheckedRight(0, 1);
float right2X = GetCheckedRight(0, 2);
float right3X = GetCheckedRight(0, 3);
float top1Y = GetCheckedTop(0, 1);
float top2Y = GetCheckedTop(0, 2);
float top3Y = GetCheckedTop(0, 3);
float bottom1Y = GetCheckedBottom(0, 1);
float bottom2Y = GetCheckedBottom(0, 2);
float bottom3Y = GetCheckedBottom(0, 3);
float4 c[25] = {
orig0, // c0
get(left1X, top1Y), // c1
get(coord.x, top1Y), // c2
get(right1X, top1Y), // c3
get(left1X, coord.y), // c4
get(right1X, coord.y), // c5
get(left1X, bottom1Y), // c6
get(coord.x, bottom1Y), // c7
get(right1X, bottom1Y), // c8
get(coord.x, top2Y), // c9
get(left2X, coord.y), // c10
get(right2X, coord.y), // c11
get(coord.x, bottom2Y), // c12
get(coord.x, bottom3Y), // c13
get(right1X, bottom2Y), // c14
get(left1X, bottom2Y), // c15
get(right3X, coord.y), // c16
get(right2X, bottom1Y), // c17
get(right2X, top1Y), // c18
get(left3X, coord.y), // c19
get(left2X, bottom1Y), // c20
get(left2X, top1Y), // c21
get(coord.x, top3Y), // c22
get(right1X, top2Y), // c23
get(left1X, top2Y) // c24
};
// Allow for higher overshoot if the current edge pixel is surrounded by similar edge pixels
float maxedge = (max4(max4(c[1].w, c[2].w, c[3].w, c[4].w), max4(c[5].w, c[6].w, c[7].w, c[8].w),
@ -163,7 +187,7 @@ D2D_PS_ENTRY(main) {
neg_laplace = pow((neg_laplace / weightsum), (1.0 / 2.4)) - 0.064;
// Compute sharpening magnitude function
float sharpen_val = (lowthsum / 12) * (curve_height / (curveslope * pow(abs(c_edge), 3.5) + 0.5));
float sharpen_val = (lowthsum / 12) * (curveHeight / (curveslope * pow(abs(c_edge), 3.5) + 0.5));
// Calculate sharpening diff and scale
float sharpdiff = (c0_Y - neg_laplace) * (sharpen_val * 0.8 + 0.01);
@ -197,5 +221,5 @@ D2D_PS_ENTRY(main) {
// Normal path
if (sharpdiff > 0) { return float4(minim_satloss, 1); }
else { return float4((c[0].rgb + sharpdiff), 1); }
else { return float4((orig0.rgb + sharpdiff), 1); }
}

View file

@ -0,0 +1,55 @@
#pragma once
#include "pch.h"
#include "GUIDs.h"
#include "SimpleDrawTransform.h"
// Adaptive Sharpen Pass 2 需要 curveHeight 参数
// curveHeight 越大,锐化程度越大
// curveHeight 的取值在 0.3~2 之间,默认值为 0.3
class AdaptiveSharpenPass2Transform : public SimpleDrawTransform {
private:
AdaptiveSharpenPass2Transform(): SimpleDrawTransform(GUID_MAGPIE_ADAPTIVE_SHARPEN_PASS2_SHADER) {}
public:
static HRESULT Create(_In_ ID2D1EffectContext* d2dEC, _Outptr_ AdaptiveSharpenPass2Transform** ppOutput) {
*ppOutput = nullptr;
HRESULT hr = DrawTransformBase::LoadShader(
d2dEC,
MAGPIE_ADAPTIVE_SHARPEN_PASS2_SHADER,
GUID_MAGPIE_ADAPTIVE_SHARPEN_PASS2_SHADER
);
if (FAILED(hr)) {
return hr;
}
*ppOutput = new AdaptiveSharpenPass2Transform();
return S_OK;
}
void SetCurveHeight(float value) {
assert(value >= 0.3 && value <= 2);
_curveHeight = value;
}
float GetCurveHeight() {
return _curveHeight;
}
protected:
void SetShaderContantBuffer(const SIZE& srcSize) override {
struct {
INT32 srcWidth;
INT32 srcHeight;
FLOAT curveHeight;
} shaderConstants {
srcSize.cx,
srcSize.cy,
_curveHeight
};
_drawInfo->SetPixelShaderConstantBuffer((BYTE*)&shaderConstants, sizeof(shaderConstants));
}
private:
float _curveHeight = 0.3f;
};

View file

@ -13,7 +13,7 @@ public:
HRESULT hr = DrawTransformBase::LoadShader(
d2dEC,
ANIME4K_UPSCALE_CONV_REDUCE_SHADER,
MAGPIE_ANIME4K_UPSCALE_CONV_REDUCE_SHADER,
GUID_MAGPIE_ANIME4K_UPSCALE_CONV_REDUCE_SHADER
);
if (FAILED(hr)) {

View file

@ -35,7 +35,7 @@ public:
HRESULT hr = SimpleDrawTransform::Create(
pEffectContext,
&_conv4x3x3x1Transform,
ANIME4K_UPSCALE_CONV_4x3x3x1_SHADER,
MAGPIE_ANIME4K_UPSCALE_CONV_4x3x3x1_SHADER,
GUID_MAGPIE_ANIME4K_UPSCALE_CONV_4x3x3x1_SHADER);
if (FAILED(hr)) {
return hr;
@ -43,7 +43,7 @@ public:
hr = SimpleDrawTransform::Create(
pEffectContext,
&_conv4x3x3x8Transform1,
ANIME4K_UPSCALE_CONV_4x3x3x8_SHADER1,
MAGPIE_ANIME4K_UPSCALE_CONV_4x3x3x8_SHADER1,
GUID_MAGPIE_ANIME4K_UPSCALE_CONV_4x3x3x8_SHADER_1);
if (FAILED(hr)) {
return hr;
@ -51,7 +51,7 @@ public:
hr = SimpleDrawTransform::Create(
pEffectContext,
&_conv4x3x3x8Transform2,
ANIME4K_UPSCALE_CONV_4x3x3x8_SHADER2,
MAGPIE_ANIME4K_UPSCALE_CONV_4x3x3x8_SHADER2,
GUID_MAGPIE_ANIME4K_UPSCALE_CONV_4x3x3x8_SHADER_2
);
if (FAILED(hr)) {
@ -60,7 +60,7 @@ public:
hr = SimpleDrawTransform::Create(
pEffectContext,
&_conv4x3x3x8Transform3,
ANIME4K_UPSCALE_CONV_4x3x3x8_SHADER3,
MAGPIE_ANIME4K_UPSCALE_CONV_4x3x3x8_SHADER3,
GUID_MAGPIE_ANIME4K_UPSCALE_CONV_4x3x3x8_SHADER_3
);
if (FAILED(hr)) {
@ -69,7 +69,7 @@ public:
hr = SimpleDrawTransform::Create(
pEffectContext,
&_conv4x3x3x8Transform4,
ANIME4K_UPSCALE_CONV_4x3x3x8_SHADER4,
MAGPIE_ANIME4K_UPSCALE_CONV_4x3x3x8_SHADER4,
GUID_MAGPIE_ANIME4K_UPSCALE_CONV_4x3x3x8_SHADER_4
);
if (FAILED(hr)) {
@ -78,7 +78,7 @@ public:
hr = SimpleDrawTransform::Create(
pEffectContext,
&_conv4x3x3x8Transform5,
ANIME4K_UPSCALE_CONV_4x3x3x8_SHADER5,
MAGPIE_ANIME4K_UPSCALE_CONV_4x3x3x8_SHADER5,
GUID_MAGPIE_ANIME4K_UPSCALE_CONV_4x3x3x8_SHADER_5
);
if (FAILED(hr)) {
@ -91,7 +91,7 @@ public:
hr = SimpleDrawTransform::Create(
pEffectContext,
&_deblurKernelXTransform,
ANIME4K_DEBLUR_KERNEL_X_SHADER,
MAGPIE_ANIME4K_DEBLUR_KERNEL_X_SHADER,
GUID_MAGPIE_ANIME4K_DEBLUR_KERNEL_X_SHADER
);
if (FAILED(hr)) {
@ -100,7 +100,7 @@ public:
hr = SimpleDrawTransform::Create(
pEffectContext,
&_deblurKernelYTransform,
ANIME4K_DEBLUR_KERNEL_Y_SHADER,
MAGPIE_ANIME4K_DEBLUR_KERNEL_Y_SHADER,
GUID_MAGPIE_ANIME4K_DEBLUR_KERNEL_Y_SHADER
);
if (FAILED(hr)) {

View file

@ -13,7 +13,7 @@ public:
HRESULT hr = DrawTransformBase::LoadShader(
d2dEC,
ANIME4K_UPSCALE_DEBLUR_OUTPUT_SHADER,
MAGPIE_ANIME4K_UPSCALE_DEBLUR_OUTPUT_SHADER,
GUID_MAGPIE_ANIME4K_UPSCALE_DEBLUR_OUTPUT_SHADER
);
if (FAILED(hr)) {

View file

@ -35,7 +35,7 @@ public:
HRESULT hr = SimpleDrawTransform::Create(
pEffectContext,
&_conv4x3x3x1Transform,
ANIME4K_UPSCALE_CONV_4x3x3x1_SHADER,
MAGPIE_ANIME4K_UPSCALE_CONV_4x3x3x1_SHADER,
GUID_MAGPIE_ANIME4K_UPSCALE_CONV_4x3x3x1_SHADER);
if (FAILED(hr)) {
return hr;
@ -43,7 +43,7 @@ public:
hr = SimpleDrawTransform::Create(
pEffectContext,
&_conv4x3x3x8Transform1,
ANIME4K_UPSCALE_CONV_4x3x3x8_SHADER1,
MAGPIE_ANIME4K_UPSCALE_CONV_4x3x3x8_SHADER1,
GUID_MAGPIE_ANIME4K_UPSCALE_CONV_4x3x3x8_SHADER_1);
if (FAILED(hr)) {
return hr;
@ -51,7 +51,7 @@ public:
hr = SimpleDrawTransform::Create(
pEffectContext,
&_conv4x3x3x8Transform2,
ANIME4K_UPSCALE_CONV_4x3x3x8_SHADER2,
MAGPIE_ANIME4K_UPSCALE_CONV_4x3x3x8_SHADER2,
GUID_MAGPIE_ANIME4K_UPSCALE_CONV_4x3x3x8_SHADER_2
);
if (FAILED(hr)) {
@ -60,7 +60,7 @@ public:
hr = SimpleDrawTransform::Create(
pEffectContext,
&_conv4x3x3x8Transform3,
ANIME4K_UPSCALE_CONV_4x3x3x8_SHADER3,
MAGPIE_ANIME4K_UPSCALE_CONV_4x3x3x8_SHADER3,
GUID_MAGPIE_ANIME4K_UPSCALE_CONV_4x3x3x8_SHADER_3
);
if (FAILED(hr)) {
@ -69,7 +69,7 @@ public:
hr = SimpleDrawTransform::Create(
pEffectContext,
&_conv4x3x3x8Transform4,
ANIME4K_UPSCALE_CONV_4x3x3x8_SHADER4,
MAGPIE_ANIME4K_UPSCALE_CONV_4x3x3x8_SHADER4,
GUID_MAGPIE_ANIME4K_UPSCALE_CONV_4x3x3x8_SHADER_4
);
if (FAILED(hr)) {
@ -78,7 +78,7 @@ public:
hr = SimpleDrawTransform::Create(
pEffectContext,
&_conv4x3x3x8Transform5,
ANIME4K_UPSCALE_CONV_4x3x3x8_SHADER5,
MAGPIE_ANIME4K_UPSCALE_CONV_4x3x3x8_SHADER5,
GUID_MAGPIE_ANIME4K_UPSCALE_CONV_4x3x3x8_SHADER_5
);
if (FAILED(hr)) {

View file

@ -13,7 +13,7 @@ public:
HRESULT hr = DrawTransformBase::LoadShader(
d2dEC,
ANIME4K_UPSCALE_OUTPUT_SHADER,
MAGPIE_ANIME4K_UPSCALE_OUTPUT_SHADER,
GUID_MAGPIE_ANIME4K_UPSCALE_OUTPUT_SHADER
);
if (FAILED(hr)) {

View file

@ -79,7 +79,7 @@ private:
const auto& subType = effect.value("type", "");
if (subType == "adaptive") {
_AddAdaptiveSharpenEffect();
_AddAdaptiveSharpenEffect(effect);
_AddBuiltInSharpenEffect();
}
} else {
@ -89,13 +89,31 @@ private:
}
void _AddAdaptiveSharpenEffect() {
void _AddAdaptiveSharpenEffect(const nlohmann::json& props) {
ComPtr<ID2D1Effect> adaptiveSharpenEffect = nullptr;
Debug::ThrowIfFailed(
_d2dDC->CreateEffect(CLSID_MAGPIE_ADAPTIVE_SHARPEN_EFFECT, &adaptiveSharpenEffect),
L"创建 Adaptive sharpen effect 失败"
);
// strength 属性
const auto& it = props.find("strength");
if (it != props.end()) {
const auto& value = *it;
Debug::ThrowIfFalse(value.is_number(), L"非法的 strength 属性值");
float strength = value.get<float>();
Debug::ThrowIfFalse(
strength >= 0 && strength <= 1,
L"非法的 strength 属性值"
);
Debug::ThrowIfFailed(
adaptiveSharpenEffect->SetValue(AdaptiveSharpenEffect::PROP_STRENGTH, strength),
L"设置 strength 属性失败"
);
}
// 替换 output effect
adaptiveSharpenEffect->SetInputEffect(0, _outputEffect.Get());
_outputEffect = adaptiveSharpenEffect;
@ -166,6 +184,10 @@ private:
);
D2D1_VECTOR_2F scale{ scaleValues[0], scaleValues[1]};
Debug::ThrowIfFalse(
scale.x >= 0 && scale.y >= 0,
L"scale 属性的值非法"
);
if (scale.x == 0 || scale.y == 0) {
// 输出图像充满屏幕
@ -175,7 +197,7 @@ private:
Debug::ThrowIfFailed(
jinc2Effect->SetValue(ScaleEffect::PROP_SCALE, scale),
L"»ñÈ¡ scale ʧ°Ü"
L"设置 scale 属性失败"
);
// 存在 scale 则输出图像尺寸改变

View file

@ -5,6 +5,7 @@
// Jinc2 ²åÖµËã·¨
class Jinc2Transform : public SimpleScaleTransform {
private:
Jinc2Transform() : SimpleScaleTransform(GUID_MAGPIE_JINC2_SCALE_SHADER) {}
public:
static HRESULT Create(_In_ ID2D1EffectContext* d2dEC, _Outptr_ Jinc2Transform** ppOutput) {
@ -12,7 +13,7 @@ public:
return E_INVALIDARG;
}
HRESULT hr = DrawTransformBase::LoadShader(d2dEC, JINC2_SCALE_SHADER, GUID_MAGPIE_JINC2_SCALE_SHADER);
HRESULT hr = DrawTransformBase::LoadShader(d2dEC, MAGPIE_JINC2_SCALE_SHADER, GUID_MAGPIE_JINC2_SCALE_SHADER);
if (FAILED(hr)) {
return hr;
}

View file

@ -187,6 +187,7 @@
</ItemDefinitionGroup>
<ItemGroup>
<ClInclude Include="AdaptiveSharpenEffect.h" />
<ClInclude Include="AdaptiveSharpenPass2Transform.h" />
<ClInclude Include="Anime4KUpscaleDeblurEffect.h" />
<ClInclude Include="Anime4KUpscaleDeblurOutputTransform.h" />
<ClInclude Include="Anime4KUpscaleOutputTransform.h" />

View file

@ -143,6 +143,9 @@
<ClInclude Include="ScaleEffect.h">
<Filter>头文件\Effect</Filter>
</ClInclude>
<ClInclude Include="AdaptiveSharpenPass2Transform.h">
<Filter>头文件\Effect</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<None Include="cpp.hint" />

View file

@ -48,7 +48,8 @@ int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
},
{
"effect": "sharpen",
"type": "adaptive"
"type": "adaptive",
"strength": 0.6
}
])"));
} else {

View file

@ -12,7 +12,7 @@ public:
_In_ ID2D1EffectContext* pEffectContext,
_In_ ID2D1TransformGraph* pTransformGraph
) {
HRESULT hr = SimpleScaleTransform::Create(pEffectContext, &_jinc2SharpTransform, MITCHELL_NETRAVALI_SCALE_SHADER, GUID_MAGPIE_MITCHELL_NETRAVALI_SCALE_SHADER);
HRESULT hr = SimpleScaleTransform::Create(pEffectContext, &_jinc2SharpTransform, MAGPIE_MITCHELL_NETRAVALI_SCALE_SHADER, GUID_MAGPIE_MITCHELL_NETRAVALI_SCALE_SHADER);
if (FAILED(hr)) {
return hr;
}

View file

@ -4,31 +4,31 @@
*
*/
// Adaptive sharpen
constexpr auto ADAPTIVE_SHARPEN_PASS1_SHADER = L"shaders/AdaptiveSharpenPass1Shader.cso";
constexpr auto ADAPTIVE_SHARPEN_PASS2_SHADER = L"shaders/AdaptiveSharpenPass2Shader.cso";
constexpr auto MAGPIE_ADAPTIVE_SHARPEN_PASS1_SHADER = L"shaders/AdaptiveSharpenPass1Shader.cso";
constexpr auto MAGPIE_ADAPTIVE_SHARPEN_PASS2_SHADER = L"shaders/AdaptiveSharpenPass2Shader.cso";
/*
* Anime4K
*/
constexpr auto ANIME4K_UPSCALE_CONV_4x3x3x1_SHADER = L"shaders/Anime4KUpscaleConv4x3x3x1Shader.cso";
constexpr auto ANIME4K_UPSCALE_CONV_4x3x3x8_SHADER1 = L"shaders/Anime4KUpscaleConv4x3x3x8Shader1.cso";
constexpr auto ANIME4K_UPSCALE_CONV_4x3x3x8_SHADER2 = L"shaders/Anime4KUpscaleConv4x3x3x8Shader2.cso";
constexpr auto ANIME4K_UPSCALE_CONV_4x3x3x8_SHADER3 = L"shaders/Anime4KUpscaleConv4x3x3x8Shader3.cso";
constexpr auto ANIME4K_UPSCALE_CONV_4x3x3x8_SHADER4 = L"shaders/Anime4KUpscaleConv4x3x3x8Shader4.cso";
constexpr auto ANIME4K_UPSCALE_CONV_4x3x3x8_SHADER5 = L"shaders/Anime4KUpscaleConv4x3x3x8Shader5.cso";
constexpr auto ANIME4K_UPSCALE_CONV_REDUCE_SHADER = L"shaders/Anime4KUpscaleConvReduceShader.cso";
constexpr auto ANIME4K_UPSCALE_OUTPUT_SHADER = L"shaders/Anime4KUpscaleOutputShader.cso";
constexpr auto ANIME4K_DEBLUR_KERNEL_X_SHADER = L"shaders/Anime4KDeblurKernelXShader.cso";
constexpr auto ANIME4K_DEBLUR_KERNEL_Y_SHADER = L"shaders/Anime4KDeblurKernelYShader.cso";
constexpr auto ANIME4K_UPSCALE_DEBLUR_OUTPUT_SHADER = L"shaders/Anime4KUpscaleDeblurOutputShader.cso";
constexpr auto MAGPIE_ANIME4K_UPSCALE_CONV_4x3x3x1_SHADER = L"shaders/Anime4KUpscaleConv4x3x3x1Shader.cso";
constexpr auto MAGPIE_ANIME4K_UPSCALE_CONV_4x3x3x8_SHADER1 = L"shaders/Anime4KUpscaleConv4x3x3x8Shader1.cso";
constexpr auto MAGPIE_ANIME4K_UPSCALE_CONV_4x3x3x8_SHADER2 = L"shaders/Anime4KUpscaleConv4x3x3x8Shader2.cso";
constexpr auto MAGPIE_ANIME4K_UPSCALE_CONV_4x3x3x8_SHADER3 = L"shaders/Anime4KUpscaleConv4x3x3x8Shader3.cso";
constexpr auto MAGPIE_ANIME4K_UPSCALE_CONV_4x3x3x8_SHADER4 = L"shaders/Anime4KUpscaleConv4x3x3x8Shader4.cso";
constexpr auto MAGPIE_ANIME4K_UPSCALE_CONV_4x3x3x8_SHADER5 = L"shaders/Anime4KUpscaleConv4x3x3x8Shader5.cso";
constexpr auto MAGPIE_ANIME4K_UPSCALE_CONV_REDUCE_SHADER = L"shaders/Anime4KUpscaleConvReduceShader.cso";
constexpr auto MAGPIE_ANIME4K_UPSCALE_OUTPUT_SHADER = L"shaders/Anime4KUpscaleOutputShader.cso";
constexpr auto MAGPIE_ANIME4K_DEBLUR_KERNEL_X_SHADER = L"shaders/Anime4KDeblurKernelXShader.cso";
constexpr auto MAGPIE_ANIME4K_DEBLUR_KERNEL_Y_SHADER = L"shaders/Anime4KDeblurKernelYShader.cso";
constexpr auto MAGPIE_ANIME4K_UPSCALE_DEBLUR_OUTPUT_SHADER = L"shaders/Anime4KUpscaleDeblurOutputShader.cso";
/*
*
*/
// Lanczos6
constexpr auto LANCZOS6_SCALE_SHADER = L"shaders/Lanczos6ScaleShader.cso";
constexpr auto MAGPIE_LANCZOS6_SCALE_SHADER = L"shaders/Lanczos6ScaleShader.cso";
// Jinc2
constexpr auto JINC2_SCALE_SHADER = L"shaders/Jinc2ScaleShader.cso";
constexpr auto MAGPIE_JINC2_SCALE_SHADER = L"shaders/Jinc2ScaleShader.cso";
// Mitchell-Netravali
constexpr auto MITCHELL_NETRAVALI_SCALE_SHADER = L"shaders/MitchellNetravaliScaleShader.cso";
constexpr auto MAGPIE_MITCHELL_NETRAVALI_SCALE_SHADER = L"shaders/MitchellNetravaliScaleShader.cso";

View file

@ -9,7 +9,7 @@
// * 只是简单地对输入应用了一个像素着色器
// * 无状态
class SimpleDrawTransform : public DrawTransformBase {
private:
protected:
SimpleDrawTransform(const GUID &shaderID): _shaderID(shaderID) {}
public:
@ -50,11 +50,10 @@ public:
return hr;
}
_shaderConstants = {
pInputRects[0].right - pInputRects[0].left,
pInputRects[0].bottom - pInputRects[0].top,
};
_drawInfo->SetPixelShaderConstantBuffer((BYTE*)&_shaderConstants, sizeof(_shaderConstants));
SetShaderContantBuffer(SIZE {
pInputRects->right - pInputRects->left,
pInputRects->bottom - pInputRects->top
});
return S_OK;
}
@ -64,16 +63,22 @@ public:
return pDrawInfo->SetPixelShader(_shaderID);
}
private:
protected:
// 继承的类可以覆盖此方法向着色器传递参数
virtual void SetShaderContantBuffer(const SIZE& srcSize) {
struct {
INT32 srcWidth;
INT32 srcHeight;
} shaderConstants{
srcSize.cx,
srcSize.cy
};
_drawInfo->SetPixelShaderConstantBuffer((BYTE*)&shaderConstants, sizeof(shaderConstants));
}
ComPtr<ID2D1DrawInfo> _drawInfo = nullptr;
// 输出图像的尺寸
D2D1_SIZE_U _destSize{};
struct {
INT32 width;
INT32 height;
} _shaderConstants{};
private:
const GUID& _shaderID;
};

View file

@ -1,15 +1,15 @@
#pragma once
#include "pch.h"
#include "GUIDs.h"
#include "DrawTransformBase.h"
#include "SimpleDrawTransform.h"
#include "Utils.h"
// 通用的 scale transform
// Ö»Ö§³Ö scale ÊôÐÔ
class SimpleScaleTransform : public DrawTransformBase {
// 只支持 scale 属性,默认值为 1.0
class SimpleScaleTransform : public SimpleDrawTransform {
protected:
SimpleScaleTransform(const GUID& shaderID) : _shaderID(shaderID) {}
SimpleScaleTransform(const GUID& shaderID) : SimpleDrawTransform(shaderID) {}
public:
static HRESULT Create(
@ -40,10 +40,6 @@ public:
return _scale;
}
IFACEMETHODIMP_(UINT32) GetInputCount() const override {
return 1;
}
IFACEMETHODIMP MapInputRectsToOutputRect(
_In_reads_(inputRectCount) const D2D1_RECT_L* pInputRects,
_In_reads_(inputRectCount) const D2D1_RECT_L* pInputOpaqueSubRects,
@ -71,27 +67,11 @@ public:
};
*pOutputOpaqueSubRect = { 0,0,0,0 };
SetShaderContantBuffer(srcSize, destSize);
_SetShaderContantBuffer(srcSize, destSize);
return S_OK;
}
virtual void SetShaderContantBuffer(const SIZE& srcSize, const SIZE& destSize) {
struct {
INT32 srcWidth;
INT32 srcHeight;
INT32 destWidth;
INT32 destHeight;
} shaderConstants {
_inputRect.right - _inputRect.left,
_inputRect.bottom - _inputRect.top,
destSize.cx,
destSize.cy
};
_drawInfo->SetPixelShaderConstantBuffer((BYTE*)&shaderConstants, sizeof(shaderConstants));
}
IFACEMETHODIMP MapOutputRectToInputRects(
_In_ const D2D1_RECT_L* pOutputRect,
_Out_writes_(inputRectCount) D2D1_RECT_L* pInputRects,
@ -105,26 +85,25 @@ public:
return S_OK;
}
IFACEMETHODIMP MapInvalidRect(
UINT32 inputIndex,
D2D1_RECT_L invalidInputRect,
_Out_ D2D1_RECT_L* pInvalidOutputRect
) const override {
return DrawTransformBase::MapInvalidRect(inputIndex, invalidInputRect, pInvalidOutputRect);
}
IFACEMETHODIMP SetDrawInfo(ID2D1DrawInfo* pDrawInfo) override {
_drawInfo = pDrawInfo;
return pDrawInfo->SetPixelShader(_shaderID);
}
protected:
ComPtr<ID2D1DrawInfo> _drawInfo = nullptr;
// 继承的类可以覆盖此方法向着色器传递参数
virtual void _SetShaderContantBuffer(const SIZE& srcSize, const SIZE& destSize) {
struct {
INT32 srcWidth;
INT32 srcHeight;
INT32 destWidth;
INT32 destHeight;
} shaderConstants{
srcSize.cx,
srcSize.cy,
destSize.cx,
destSize.cy
};
_drawInfo->SetPixelShaderConstantBuffer((BYTE*)&shaderConstants, sizeof(shaderConstants));
}
private:
const GUID& _shaderID;
// 保存输入图像的尺寸
D2D1_RECT_L _inputRect{};

View file

@ -16,13 +16,17 @@
/*
* SampleInput
* 使 MAGPIE_USE_SAMPLE_INPUT main InitMagpieSampleInput
*/
static float4 coord = 0;
#define SampleInputNoCheck(index, pos) (D2DSampleInput(index, pos).rgb)
#define SampleInputRGBANoCheck(index, pos) (D2DSampleInput(index, pos))
#define SampleInputOffNoCheck(index, pos) (D2DSampleInputAtOffset(index, pos).rgb)
#define SampleInputRGBAOffNoCheck(index, pos) (D2DSampleInputAtOffset(index, pos))
#define SampleInputCur(index) (D2DSampleInput(index, coord.xy).rgb)
#define SampleInputRGBACur(index) (D2DSampleInput(index, coord.xy))
static float2 _maxCoord = 0;
@ -32,6 +36,7 @@ static float2 _maxCoord = 0;
#define _checkTop(y) (max(0, y))
#define _checkBottom(y) (min(_maxCoord.g, y))
// 检查边界的 D2DSampleInput
#define SampleInputCheckLeft(index, x, y) (SampleInputNoCheck(index, float2(_checkLeft(x), y)))
#define SampleInputCheckRight(index, x, y) (SampleInputNoCheck(index, float2(_checkRight(x), y)))
#define SampleInputCheckTop(index, x, y) (SampleInputNoCheck(index, float2(x, _checkTop(y))))
@ -41,6 +46,7 @@ static float2 _maxCoord = 0;
#define SampleInputCheckRightTop(index, x, y) (SampleInputNoCheck(index, float2(_checkRight(x), _checkTop(y))))
#define SampleInputCheckRightBottom(index, x, y) (SampleInputNoCheck(index, float2(_checkRight(x), _checkBottom(y))))
// 检查边界的 D2DSampleInputAtOffset
// 使用 rg 而不是 xy
#define SampleInputOffCheckLeft(index, x, y) SampleInputCheckLeft(index, x * coord.z + coord.r, y * coord.w + coord.g)
#define SampleInputOffCheckRight(index, x, y) SampleInputCheckRight(index, x * coord.z + coord.r, y * coord.w + coord.g)
@ -51,6 +57,13 @@ static float2 _maxCoord = 0;
#define SampleInputOffCheckRightTop(index, x, y) SampleInputCheckRightTop(index, x * coord.z + coord.r, y * coord.w + coord.g)
#define SampleInputOffCheckRightBottom(index, x, y) SampleInputCheckRightBottom(index, x * coord.z + coord.r, y * coord.w + coord.g)
// 限制坐标在边界内
// n 为 offset
#define GetCheckedLeft(index, n) _checkLeft(coord.x - n * coord.z)
#define GetCheckedRight(index, n) _checkRight(coord.x + n * coord.w)
#define GetCheckedTop(index, n) _checkTop(coord.y - n * coord.z)
#define GetCheckedBottom(index, n) _checkBottom(coord.y + n * coord.w)
// 需要 main 函数的开头调用
void InitMagpieSampleInput() {
@ -65,6 +78,7 @@ void InitMagpieSampleInput() {
/*
* RGB YUV
* MAGPIE_USE_YUV
*/
const static float3x3 _rgb2yuv = {