1 /*
2 * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26 #include <simd/simd.h>
27 #include <metal_stdlib>
28 #include "common.h"
29
30 using namespace metal;
31
32 struct VertexInput {
33 float2 position [[attribute(VertexAttributePosition)]];
34 };
35
36 struct TxtVertexInput {
37 float2 position [[attribute(VertexAttributePosition)]];
38 float2 texCoords [[attribute(VertexAttributeTexPos)]];
39 };
40
41 struct ColShaderInOut {
42 float4 position [[position]];
43 half4 color;
44 };
45
46 struct StencilShaderInOut {
47 float4 position [[position]];
48 char color;
49 };
50
51 struct TxtShaderInOut {
52 float4 position [[position]];
53 float2 texCoords;
54 float2 tpCoords;
55 };
56
57 struct GradShaderInOut {
58 float4 position [[position]];
59 float2 texCoords;
60 };
61
62 vertex ColShaderInOut vert_col(VertexInput in [[stage_in]],
63 constant FrameUniforms& uniforms [[buffer(FrameUniformBuffer)]],
64 constant TransformMatrix& transform [[buffer(MatrixBuffer)]]) {
65 ColShaderInOut out;
66 float4 pos4 = float4(in.position, 0.0, 1.0);
67 out.position = transform.transformMatrix*pos4;
68 out.color = half4(uniforms.color.r, uniforms.color.g, uniforms.color.b, uniforms.color.a);
69 return out;
70 }
71
72 vertex StencilShaderInOut vert_stencil(VertexInput in [[stage_in]],
73 constant FrameUniforms& uniforms [[buffer(FrameUniformBuffer)]],
74 constant TransformMatrix& transform [[buffer(MatrixBuffer)]]) {
75 StencilShaderInOut out;
76 float4 pos4 = float4(in.position, 0.0, 1.0);
77 out.position = transform.transformMatrix * pos4;
78 out.color = 0xFF;
79 return out;
80 }
81
82 vertex GradShaderInOut vert_grad(VertexInput in [[stage_in]], constant TransformMatrix& transform [[buffer(MatrixBuffer)]]) {
83 GradShaderInOut out;
84 float4 pos4 = float4(in.position, 0.0, 1.0);
85 out.position = transform.transformMatrix*pos4;
86 return out;
87 }
88
89 vertex TxtShaderInOut vert_txt(TxtVertexInput in [[stage_in]], constant TransformMatrix& transform [[buffer(MatrixBuffer)]]) {
90 TxtShaderInOut out;
91 float4 pos4 = float4(in.position, 0.0, 1.0);
92 out.position = transform.transformMatrix*pos4;
93 out.texCoords = in.texCoords;
94 return out;
95 }
96
97 vertex TxtShaderInOut vert_txt_tp(TxtVertexInput in [[stage_in]], constant AnchorData& anchorData [[buffer(FrameUniformBuffer)]], constant TransformMatrix& transform [[buffer(MatrixBuffer)]])
98 {
99 TxtShaderInOut out;
100 float4 pos4 = float4(in.position, 0.0, 1.0);
101 out.position = transform.transformMatrix * pos4;
102
103 // Compute texture coordinates here w.r.t. anchor rect of texture paint
104 out.tpCoords.x = (anchorData.xParams[0] * in.position.x) +
105 (anchorData.xParams[1] * in.position.y) +
106 (anchorData.xParams[2] * out.position.w);
107 out.tpCoords.y = (anchorData.yParams[0] * in.position.x) +
108 (anchorData.yParams[1] * in.position.y) +
109 (anchorData.yParams[2] * out.position.w);
110 out.texCoords = in.texCoords;
111
112 return out;
113 }
114
115 vertex GradShaderInOut vert_txt_grad(TxtVertexInput in [[stage_in]],
116 constant TransformMatrix& transform [[buffer(MatrixBuffer)]]) {
117 GradShaderInOut out;
118 float4 pos4 = float4(in.position, 0.0, 1.0);
119 out.position = transform.transformMatrix*pos4;
120 out.texCoords = in.texCoords;
121 return out;
122 }
123
124 fragment half4 frag_col(ColShaderInOut in [[stage_in]]) {
125 return in.color;
126 }
127
128 fragment unsigned int frag_stencil(StencilShaderInOut in [[stage_in]]) {
129 return in.color;
130 }
131
132 // NOTE:
133 // 1. consider to make shaders without IF-conditions
134 // 2. we can pass interpolation mode via uniforms and select corresponding sampler in shader
135 // but it can cause performance problems (something like getTextureSampler(hint) will be invoked
136 // for every pixel)
137
138 fragment half4 frag_txt(
139 TxtShaderInOut vert [[stage_in]],
140 texture2d<float, access::sample> renderTexture [[texture(0)]],
141 constant TxtFrameUniforms& uniforms [[buffer(1)]],
142 sampler textureSampler [[sampler(0)]]
143 ) {
144 float4 pixelColor = renderTexture.sample(textureSampler, vert.texCoords);
145 float srcA = uniforms.isSrcOpaque ? 1 : pixelColor.a;
146 if (uniforms.mode) {
147 float4 c = mix(pixelColor, uniforms.color, srcA);
148 return half4(c.r, c.g, c.b , c.a);
149 }
150
151 return half4(pixelColor.r,
152 pixelColor.g,
153 pixelColor.b, srcA*uniforms.extraAlpha);
154 }
155
156 fragment half4 frag_txt_tp(TxtShaderInOut vert [[stage_in]],
157 texture2d<float, access::sample> renderTexture [[texture(0)]],
158 texture2d<float, access::sample> paintTexture [[texture(1)]],
159 sampler textureSampler [[sampler(0)]]
160 ) {
161 float4 renderColor = renderTexture.sample(textureSampler, vert.texCoords);
162 float4 paintColor = paintTexture.sample(textureSampler, vert.tpCoords);
163 return half4(paintColor.r*renderColor.a,
164 paintColor.g*renderColor.a,
165 paintColor.b*renderColor.a,
166 renderColor.a);
167 }
168
169 fragment half4 frag_txt_grad(GradShaderInOut in [[stage_in]],
170 constant GradFrameUniforms& uniforms [[buffer(0)]],
171 texture2d<float, access::sample> renderTexture [[texture(0)]])
172 {
173 constexpr sampler textureSampler (address::repeat, mag_filter::nearest,
174 min_filter::nearest);
175
176 float4 renderColor = renderTexture.sample(textureSampler, in.texCoords);
177
178 float3 v = float3(in.position.x, in.position.y, 1);
179 float a = (dot(v,uniforms.params)-0.25)*2.0;
180 float4 c = mix(uniforms.color1, uniforms.color2, a);
181 return half4(c.r*renderColor.a,
182 c.g*renderColor.a,
183 c.b*renderColor.a,
184 renderColor.a);
185 }
186
187 fragment half4 aa_frag_txt(
188 TxtShaderInOut vert [[stage_in]],
189 texture2d<float, access::sample> renderTexture [[texture(0)]],
190 constant TxtFrameUniforms& uniforms [[buffer(1)]],
191 sampler textureSampler [[sampler(0)]]
192 ) {
193 float4 pixelColor = renderTexture.sample(textureSampler, vert.texCoords);
194 return half4(pixelColor.r, pixelColor.g, pixelColor.b, pixelColor.a);
195 }
196
197 fragment half4 frag_grad(GradShaderInOut in [[stage_in]],
198 constant GradFrameUniforms& uniforms [[buffer(0)]]) {
199 float3 v = float3(in.position.x, in.position.y, 1);
200 float a = (dot(v,uniforms.params)-0.25)*2.0;
201 float4 c = mix(uniforms.color1, uniforms.color2, a);
202 return half4(c);
203 }
204
205
206 vertex TxtShaderInOut vert_tp(VertexInput in [[stage_in]],
207 constant AnchorData& anchorData [[buffer(FrameUniformBuffer)]],
208 constant TransformMatrix& transform [[buffer(MatrixBuffer)]])
209 {
210 TxtShaderInOut out;
211 float4 pos4 = float4(in.position, 0.0, 1.0);
212 out.position = transform.transformMatrix * pos4;
213
214 // Compute texture coordinates here w.r.t. anchor rect of texture paint
215 out.texCoords.x = (anchorData.xParams[0] * in.position.x) +
216 (anchorData.xParams[1] * in.position.y) +
217 (anchorData.xParams[2] * out.position.w);
218 out.texCoords.y = (anchorData.yParams[0] * in.position.x) +
219 (anchorData.yParams[1] * in.position.y) +
220 (anchorData.yParams[2] * out.position.w);
221
222 return out;
223 }
224
225 fragment half4 frag_tp(
226 TxtShaderInOut vert [[stage_in]],
227 texture2d<float, access::sample> renderTexture [[texture(0)]])
228 {
229 constexpr sampler textureSampler (address::repeat,
230 mag_filter::nearest,
231 min_filter::nearest);
232
233 float4 pixelColor = renderTexture.sample(textureSampler, vert.texCoords);
234 return half4(pixelColor.r, pixelColor.g, pixelColor.b, 1.0);
235
236 // This implementation defaults alpha to 1.0 as if source is opaque
237 //TODO : implement alpha component value if source is transparent
238 }
239
240 fragment half4 frag_tp_xorMode(
241 TxtShaderInOut vert [[stage_in]],
242 texture2d<float, access::sample> renderTexture [[texture(0)]],
243 constant int& xorColor[[buffer(0)]])
244 {
245 constexpr sampler textureSampler (address::repeat,
246 mag_filter::nearest,
247 min_filter::nearest);
248
249 float4 pixelColor = renderTexture.sample(textureSampler, vert.texCoords);
250
251 pixelColor.r = float( (unsigned char)(pixelColor.r * 255.0) ^ ((xorColor >> 16) & 0xFF) ) / 255.0f;
252 pixelColor.g = float( (unsigned char)(pixelColor.g * 255.0) ^ ((xorColor >> 8) & 0xFF)) / 255.0f;
253 pixelColor.b = float( (unsigned char)(pixelColor.b * 255.0) ^ (xorColor & 0xFF)) / 255.0f;
254 pixelColor.a = 1.0;
255
256 return half4(pixelColor.r, pixelColor.g, pixelColor.b, 1.0);
257
258 // This implementation defaults alpha to 1.0 as if source is opaque
259 //TODO : implement alpha component value if source is transparent
260 }
261
262 /* The variables involved in the equation can be expressed as follows:
263 *
264 * Cs = Color component of the source (foreground color) [0.0, 1.0]
265 * Cd = Color component of the destination (background color) [0.0, 1.0]
266 * Cr = Color component to be written to the destination [0.0, 1.0]
267 * Ag = Glyph alpha (aka intensity or coverage) [0.0, 1.0]
268 * Ga = Gamma adjustment in the range [1.0, 2.5]
269 * (^ means raised to the power)
270 *
271 * And here is the theoretical equation approximated by this shader:
272 *
273 * Cr = (Ag*(Cs^Ga) + (1-Ag)*(Cd^Ga)) ^ (1/Ga)
274 */
275 fragment float4 lcd_color(
276 TxtShaderInOut vert [[stage_in]],
277 texture2d<float, access::sample> glyphTexture [[texture(0)]],
278 texture2d<float, access::sample> dstTexture [[texture(1)]],
279 constant LCDFrameUniforms& uniforms [[buffer(1)]])
280 {
281 float3 src_adj = uniforms.src_adj;
282 float3 gamma = uniforms.gamma;
283 float3 invgamma = uniforms.invgamma;
284
285 constexpr sampler glyphTextureSampler (mag_filter::linear,
286 min_filter::linear);
287
288 // load the RGB value from the glyph image at the current texcoord
289 float3 glyph_clr = float3(glyphTexture.sample(glyphTextureSampler, vert.texCoords));
290
291 if (glyph_clr.r == 0.0f && glyph_clr.g == 0.0f && glyph_clr.b == 0.0f) {
292 // zero coverage, so skip this fragment
293 discard_fragment();
294 }
295 constexpr sampler dstTextureSampler (mag_filter::linear,
296 min_filter::linear);
297 // load the RGB value from the corresponding destination pixel
298 float3 dst_clr = float3(dstTexture.sample(dstTextureSampler, vert.texCoords));
299
300 // gamma adjust the dest color
301 float3 dst_adj = pow(dst_clr.rgb, gamma);
302
303 // linearly interpolate the three color values
304 float3 result = mix(dst_adj, src_adj, glyph_clr);
305
306 // gamma re-adjust the resulting color (alpha is always set to 1.0)
307 return float4(pow(result.rgb, invgamma), 1.0);
308
309 }