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 }