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 #ifndef HEADLESS 27 28 #include "MTLPaints.h" 29 30 #include "MTLClip.h" 31 32 #include "common.h" 33 34 #include "sun_java2d_SunGraphics2D.h" 35 #include "sun_java2d_pipe_BufferedPaints.h" 36 #import "MTLComposite.h" 37 38 #define RGBA_TO_V4(c) \ 39 { \ 40 (((c) >> 16) & (0xFF))/255.0f, \ 41 (((c) >> 8) & 0xFF)/255.0f, \ 42 ((c) & 0xFF)/255.0f, \ 43 (((c) >> 24) & 0xFF)/255.0f \ 44 } 45 46 static MTLRenderPipelineDescriptor * templateRenderPipelineDesc = nil; 47 static MTLRenderPipelineDescriptor * templateTexturePipelineDesc = nil; 48 static MTLRenderPipelineDescriptor * templateAATexturePipelineDesc = nil; 49 50 static void initTemplatePipelineDescriptors() { 51 if (templateRenderPipelineDesc != nil && templateTexturePipelineDesc != nil) 52 return; 53 54 MTLVertexDescriptor *vertDesc = [[MTLVertexDescriptor new] autorelease]; 55 vertDesc.attributes[VertexAttributePosition].format = MTLVertexFormatFloat2; 56 vertDesc.attributes[VertexAttributePosition].offset = 0; 57 vertDesc.attributes[VertexAttributePosition].bufferIndex = MeshVertexBuffer; 58 vertDesc.layouts[MeshVertexBuffer].stride = sizeof(struct Vertex); 59 vertDesc.layouts[MeshVertexBuffer].stepRate = 1; 60 vertDesc.layouts[MeshVertexBuffer].stepFunction = MTLVertexStepFunctionPerVertex; 61 62 templateRenderPipelineDesc = [[MTLRenderPipelineDescriptor new] autorelease]; 63 templateRenderPipelineDesc.sampleCount = 1; 64 templateRenderPipelineDesc.vertexDescriptor = vertDesc; 65 templateRenderPipelineDesc.colorAttachments[0].pixelFormat = MTLPixelFormatBGRA8Unorm; 66 templateRenderPipelineDesc.label = @"template_render"; 67 68 templateTexturePipelineDesc = [[templateRenderPipelineDesc copy] autorelease]; 69 templateTexturePipelineDesc.vertexDescriptor.attributes[VertexAttributeTexPos].format = MTLVertexFormatFloat2; 70 templateTexturePipelineDesc.vertexDescriptor.attributes[VertexAttributeTexPos].offset = 2*sizeof(float); 71 templateTexturePipelineDesc.vertexDescriptor.attributes[VertexAttributeTexPos].bufferIndex = MeshVertexBuffer; 72 templateTexturePipelineDesc.vertexDescriptor.layouts[MeshVertexBuffer].stride = sizeof(struct TxtVertex); 73 templateTexturePipelineDesc.vertexDescriptor.layouts[MeshVertexBuffer].stepRate = 1; 74 templateTexturePipelineDesc.vertexDescriptor.layouts[MeshVertexBuffer].stepFunction = MTLVertexStepFunctionPerVertex; 75 templateTexturePipelineDesc.label = @"template_texture"; 76 77 templateAATexturePipelineDesc = [[templateTexturePipelineDesc copy] autorelease]; 78 templateAATexturePipelineDesc.colorAttachments[0].sourceRGBBlendFactor = MTLBlendFactorOne; 79 templateAATexturePipelineDesc.colorAttachments[0].sourceAlphaBlendFactor = MTLBlendFactorOne; 80 templateAATexturePipelineDesc.colorAttachments[0].destinationRGBBlendFactor = MTLBlendFactorOneMinusSourceAlpha; 81 templateAATexturePipelineDesc.colorAttachments[0].destinationAlphaBlendFactor = MTLBlendFactorOneMinusSourceAlpha; 82 templateAATexturePipelineDesc.label = @"template_aa_texture"; 83 84 } 85 86 @implementation MTLPaint { 87 // TODO: remove paintState, split into heirarchy of Paint-objects (i.e. PaintColor, PaintGrad, e.t.c) 88 jint _paintState; 89 90 // color-mode 91 jint _color; 92 93 // lin-grad-mode 94 jdouble _p0; 95 jdouble _p1; 96 jdouble _p3; 97 jboolean _cyclic; 98 jint _pixel1; 99 jint _pixel2; 100 jboolean _useMask; 101 102 // texture paint 103 id<MTLTexture> _paintTexture; 104 struct AnchorData _anchor; 105 } 106 107 - (id)init { 108 self = [super init]; 109 if (self) { 110 _paintState = sun_java2d_SunGraphics2D_PAINT_UNDEFINED; 111 } 112 return self; 113 } 114 115 - (BOOL)isEqual:(MTLPaint *)other { 116 if (self == other) 117 return YES; 118 if (_paintState == sun_java2d_SunGraphics2D_PAINT_UNDEFINED) 119 return _paintState == other->_paintState; 120 if (_paintState != other->_paintState) 121 return NO; 122 if (_paintState == sun_java2d_SunGraphics2D_PAINT_GRADIENT) { 123 return _p0 == other->_p0 124 && _p1 == other->_p1 125 && _p3 == other->_p3 126 && _pixel1 == other->_pixel1 127 && _pixel2 == other->_pixel2; 128 } 129 if (_paintState == sun_java2d_SunGraphics2D_PAINT_ALPHACOLOR) { 130 return _color == other->_color; 131 } 132 if (_paintState == sun_java2d_SunGraphics2D_PAINT_TEXTURE) { 133 return _paintTexture == other->_paintTexture 134 && _anchor.xParams[0] == other->_anchor.xParams[0] 135 && _anchor.xParams[1] == other->_anchor.xParams[1] 136 && _anchor.xParams[2] == other->_anchor.xParams[2] 137 && _anchor.yParams[0] == other->_anchor.yParams[0] 138 && _anchor.yParams[1] == other->_anchor.yParams[1] 139 && _anchor.yParams[2] == other->_anchor.yParams[2]; 140 } 141 142 J2dTraceLn1(J2D_TRACE_ERROR, "Unimplemented paint mode %d", _paintState); 143 return NO; 144 } 145 146 - (void)copyFrom:(MTLPaint *)other { 147 _paintState = other->_paintState; 148 if (other->_paintState == sun_java2d_SunGraphics2D_PAINT_UNDEFINED) 149 return; 150 151 if (other->_paintState == sun_java2d_SunGraphics2D_PAINT_GRADIENT) { 152 _p0 = other->_p0; 153 _p1 = other->_p1; 154 _p3 = other->_p3; 155 _pixel1 = other->_pixel1; 156 _pixel2 = other->_pixel2; 157 return; 158 } 159 if (_paintState == sun_java2d_SunGraphics2D_PAINT_ALPHACOLOR) { 160 _color = other->_color; 161 return; 162 } 163 164 if (_paintState == sun_java2d_SunGraphics2D_PAINT_TEXTURE) { 165 _color = other->_color; 166 _paintTexture = other->_paintTexture; 167 _anchor = other->_anchor; 168 return; 169 } 170 171 J2dTraceLn1(J2D_TRACE_ERROR, "Unsupported paint mode %d", _paintState); 172 } 173 174 - (NSString *)getDescription { 175 if (_paintState == sun_java2d_SunGraphics2D_PAINT_ALPHACOLOR) { 176 return [NSString stringWithFormat:@"[r=%d g=%d b=%d a=%d]", (_color >> 16) & (0xFF), (_color >> 8) & 0xFF, (_color) & 0xFF, (_color >> 24) & 0xFF]; 177 } 178 179 if (_paintState == sun_java2d_SunGraphics2D_PAINT_GRADIENT) { 180 return [NSString stringWithFormat:@"gradient"]; 181 } 182 183 if (_paintState == sun_java2d_SunGraphics2D_PAINT_TEXTURE) { 184 return [NSString stringWithFormat:@"texture_paint"]; 185 } 186 187 return @"unknown-paint"; 188 } 189 190 - (jint)getColor { 191 return _color; 192 } 193 194 - (void)reset { 195 _paintState = sun_java2d_SunGraphics2D_PAINT_UNDEFINED; 196 _paintTexture = nil; 197 _anchor.xParams[0] = _anchor.xParams[1] = _anchor.xParams[2] = 0.0f; 198 _anchor.yParams[0] = _anchor.yParams[1] = _anchor.yParams[2] = 0.0f; 199 } 200 201 - (void)setColor:(jint)pixelColor { 202 _paintState = sun_java2d_SunGraphics2D_PAINT_ALPHACOLOR; 203 _color = pixelColor; 204 } 205 206 - (void)setGradientUseMask:(jboolean)useMask 207 cyclic:(jboolean)cyclic 208 p0:(jdouble)p0 209 p1:(jdouble)p1 210 p3:(jdouble)p3 211 pixel1:(jint)pixel1 212 pixel2:(jint)pixel2 213 { 214 //TODO Resolve gradient distribution problem 215 //TODO Implement useMask 216 //TODO Implement cyclic 217 //fprintf(stderr, 218 // "MTLPaints_SetGradientPaint useMask=%d cyclic=%d " 219 // "p0=%f p1=%f p3=%f pix1=%d pix2=%d\n", useMask, cyclic, 220 // p0, p1, p3, pixel1, pixel2); 221 222 _paintState = sun_java2d_SunGraphics2D_PAINT_GRADIENT; 223 _useMask = useMask; 224 _pixel1 = pixel1; 225 _pixel2 = pixel2; 226 _p0 = p0; 227 _p1 = p1; 228 _p3 = p3; 229 _cyclic = cyclic; 230 } 231 232 - (void)setLinearGradient:(jboolean)useMask 233 linear:(jboolean)linear 234 cycleMethod:(jboolean)cycleMethod 235 numStops:(jint)numStops 236 p0:(jfloat)p0 237 p1:(jfloat)p1 238 p3:(jfloat)p3 239 fractions:(void *)fractions 240 pixels:(void *)pixels 241 { 242 J2dTraceLn(J2D_TRACE_ERROR, "setLinearGradient: UNIMPLEMENTED"); 243 [self setColor:0]; 244 } 245 246 - (void)setRadialGradient:(jboolean)useMask 247 linear:(jboolean)linear 248 cycleMethod:(jboolean)cycleMethod 249 numStops:(jint)numStops 250 m00:(jfloat)m00 251 m01:(jfloat)m01 252 m02:(jfloat)m02 253 m10:(jfloat)m10 254 m11:(jfloat)m11 255 m12:(jfloat)m12 256 focusX:(jfloat)focusX 257 fractions:(void *)fractions 258 pixels:(void *)pixels 259 { 260 J2dTraceLn(J2D_TRACE_ERROR, "setRadialGradient: UNIMPLEMENTED"); 261 [self setColor:0]; 262 } 263 264 - (void)setTexture:(jboolean)useMask 265 textureID:(id<MTLTexture>)textureID 266 filter:(jboolean)filter 267 xp0:(jdouble)xp0 268 xp1:(jdouble)xp1 269 xp3:(jdouble)xp3 270 yp0:(jdouble)yp0 271 yp1:(jdouble)yp1 272 yp3:(jdouble)yp3 273 { 274 _paintState = sun_java2d_SunGraphics2D_PAINT_TEXTURE; 275 _paintTexture = textureID; 276 277 _anchor.xParams[0] = xp0; 278 _anchor.xParams[1] = xp1; 279 _anchor.xParams[2] = xp3; 280 281 _anchor.yParams[0] = yp0; 282 _anchor.yParams[1] = yp1; 283 _anchor.yParams[2] = yp3; 284 } 285 286 // For the current paint mode: 287 // 1. Selects vertex+fragment shaders (and corresponding pipelineDesc) and set pipelineState 288 // 2. Set vertex and fragment buffers 289 - (void)setPipelineState:(id<MTLRenderCommandEncoder>)encoder 290 composite:(MTLComposite *)composite 291 isStencilUsed:(jboolean)isStencilUsed 292 isTexture:(jboolean)isTexture 293 isAA:(jboolean)isAA 294 srcFlags:(const SurfaceRasterFlags *)srcFlags 295 dstFlags:(const SurfaceRasterFlags *)dstFlags 296 pipelineStateStorage:(MTLPipelineStatesStorage *)pipelineStateStorage 297 { 298 initTemplatePipelineDescriptors(); 299 300 const bool stencil = isStencilUsed == JNI_TRUE; 301 302 id<MTLRenderPipelineState> pipelineState = nil; 303 if (isTexture) { 304 305 if (_paintState == sun_java2d_SunGraphics2D_PAINT_TEXTURE) { 306 pipelineState = [pipelineStateStorage getPipelineState:templateTexturePipelineDesc 307 vertexShaderId:@"vert_txt_tp" 308 fragmentShaderId:@"frag_txt_tp" 309 compositeRule:[composite getRule] 310 isAA:JNI_FALSE 311 srcFlags:srcFlags 312 dstFlags:dstFlags 313 stencilNeeded:stencil]; 314 [encoder setVertexBytes:&_anchor length:sizeof(_anchor) atIndex:FrameUniformBuffer]; 315 [encoder setFragmentTexture:_paintTexture atIndex: 1]; 316 317 struct TxtFrameUniforms uf = {RGBA_TO_V4(0), 0, srcFlags->isOpaque, 318 dstFlags->isOpaque, [composite getExtraAlpha]}; 319 [encoder setFragmentBytes:&uf length:sizeof(uf) 320 atIndex:FrameUniformBuffer]; 321 322 } else if (_paintState == sun_java2d_SunGraphics2D_PAINT_GRADIENT) { 323 pipelineState = [pipelineStateStorage getPipelineState:templateTexturePipelineDesc 324 vertexShaderId:@"vert_txt_grad" 325 fragmentShaderId:@"frag_txt_grad" 326 compositeRule:[composite getRule] 327 isAA:JNI_FALSE 328 srcFlags:srcFlags 329 dstFlags:dstFlags 330 stencilNeeded:stencil]; 331 struct GradFrameUniforms uf = { 332 {_p0, _p1, _p3}, 333 RGBA_TO_V4(_pixel1), 334 RGBA_TO_V4(_pixel2)}; 335 [encoder setFragmentBytes: &uf length:sizeof(uf) atIndex:0]; 336 337 } else { 338 if (isAA) { 339 pipelineState = [pipelineStateStorage 340 getPipelineState:templateAATexturePipelineDesc 341 vertexShaderId:@"vert_txt" 342 fragmentShaderId:@"aa_frag_txt" 343 compositeRule:[composite getRule] 344 isAA:JNI_FALSE 345 srcFlags:srcFlags 346 dstFlags:dstFlags 347 stencilNeeded:stencil]; 348 349 } else { 350 pipelineState = 351 [pipelineStateStorage getPipelineState:templateTexturePipelineDesc 352 vertexShaderId:@"vert_txt" 353 fragmentShaderId:@"frag_txt" 354 compositeRule:[composite getRule] 355 composite:composite 356 isAA:JNI_FALSE 357 srcFlags:srcFlags 358 dstFlags:dstFlags 359 stencilNeeded:stencil]; 360 } 361 362 if (_paintState == sun_java2d_SunGraphics2D_PAINT_ALPHACOLOR) { 363 struct TxtFrameUniforms uf = {RGBA_TO_V4(_color), 1, 364 srcFlags->isOpaque, dstFlags->isOpaque, [composite getExtraAlpha]}; 365 [encoder setFragmentBytes:&uf length:sizeof(uf) atIndex:FrameUniformBuffer]; 366 } else { 367 struct TxtFrameUniforms uf = {RGBA_TO_V4(0), 0, 368 srcFlags->isOpaque, dstFlags->isOpaque, [composite getExtraAlpha]}; 369 [encoder setFragmentBytes:&uf length:sizeof(uf) atIndex:FrameUniformBuffer]; 370 } 371 } 372 } else { 373 if (_paintState == sun_java2d_SunGraphics2D_PAINT_ALPHACOLOR) { 374 pipelineState = [pipelineStateStorage getPipelineState:templateRenderPipelineDesc 375 vertexShaderId:@"vert_col" 376 fragmentShaderId:@"frag_col" 377 compositeRule:[composite getRule] 378 isAA:isAA 379 srcFlags:srcFlags 380 dstFlags:dstFlags 381 stencilNeeded:stencil]; 382 383 struct FrameUniforms uf = {RGBA_TO_V4(_color)}; 384 [encoder setVertexBytes:&uf length:sizeof(uf) atIndex:FrameUniformBuffer]; 385 } else if (_paintState == sun_java2d_SunGraphics2D_PAINT_GRADIENT) { 386 pipelineState = [pipelineStateStorage getPipelineState:templateRenderPipelineDesc 387 vertexShaderId:@"vert_grad" 388 fragmentShaderId:@"frag_grad" 389 compositeRule:[composite getRule] 390 isAA:isAA 391 srcFlags:srcFlags 392 dstFlags:dstFlags 393 stencilNeeded:stencil]; 394 395 struct GradFrameUniforms uf = { 396 {_p0, _p1, _p3}, 397 RGBA_TO_V4(_pixel1), 398 RGBA_TO_V4(_pixel2)}; 399 [encoder setFragmentBytes: &uf length:sizeof(uf) atIndex:0]; 400 } else if (_paintState == sun_java2d_SunGraphics2D_PAINT_TEXTURE) { 401 pipelineState = [pipelineStateStorage getPipelineState:templateRenderPipelineDesc 402 vertexShaderId:@"vert_tp" 403 fragmentShaderId:@"frag_tp" 404 compositeRule:[composite getRule] 405 isAA:isAA 406 srcFlags:srcFlags 407 dstFlags:dstFlags 408 stencilNeeded:stencil]; 409 410 [encoder setVertexBytes:&_anchor length:sizeof(_anchor) atIndex:FrameUniformBuffer]; 411 [encoder setFragmentTexture:_paintTexture atIndex: 0]; 412 } 413 } 414 415 [encoder setRenderPipelineState:pipelineState]; 416 } 417 418 419 // For the current paint mode: and for XOR composite - a separate method is added as fragment shader differ in some cases 420 // 1. Selects vertex+fragment shaders (and corresponding pipelineDesc) and set pipelineState 421 // 2. Set vertex and fragment buffers 422 - (void)setXorModePipelineState:(id<MTLRenderCommandEncoder>)encoder 423 composite:(MTLComposite *)composite 424 isStencilUsed:(jboolean)isStencilUsed 425 isTexture:(jboolean)isTexture 426 srcFlags:(const SurfaceRasterFlags *)srcFlags 427 dstFlags:(const SurfaceRasterFlags *)dstFlags 428 pipelineStateStorage:(MTLPipelineStatesStorage *)pipelineStateStorage { 429 initTemplatePipelineDescriptors(); 430 431 const bool stencil = isStencilUsed == JNI_TRUE; 432 jint xorColor = (jint) [composite getXorColor]; 433 434 id<MTLRenderPipelineState> pipelineState = nil; 435 if (isTexture) { 436 pipelineState = [pipelineStateStorage getXorModePipelineState:templateTexturePipelineDesc 437 vertexShaderId:@"vert_txt" 438 fragmentShaderId:@"frag_txt" 439 srcFlags:srcFlags 440 dstFlags:dstFlags 441 stencilNeeded:stencil]; 442 443 if (_paintState == sun_java2d_SunGraphics2D_PAINT_ALPHACOLOR) { 444 struct TxtFrameUniforms uf = {RGBA_TO_V4(_color ^ xorColor), 1, 445 srcFlags->isOpaque, dstFlags->isOpaque, [composite getExtraAlpha]}; 446 [encoder setFragmentBytes:&uf length:sizeof(uf) atIndex:FrameUniformBuffer]; 447 } else { 448 struct TxtFrameUniforms uf = {RGBA_TO_V4(0 ^ xorColor), 0, 449 srcFlags->isOpaque, dstFlags->isOpaque, [composite getExtraAlpha]}; 450 [encoder setFragmentBytes:&uf length:sizeof(uf) atIndex:FrameUniformBuffer]; 451 } 452 [encoder setFragmentBytes:&xorColor length:sizeof(xorColor) atIndex: 0]; 453 } else { 454 if (_paintState == sun_java2d_SunGraphics2D_PAINT_ALPHACOLOR) { 455 456 pipelineState = [pipelineStateStorage getXorModePipelineState:templateRenderPipelineDesc 457 vertexShaderId:@"vert_col" 458 fragmentShaderId:@"frag_col" 459 srcFlags:srcFlags 460 dstFlags:dstFlags 461 stencilNeeded:stencil]; 462 463 // Calculate _color ^ xorColor for RGB components 464 // This color gets XORed with destination framebuffer pixel color 465 struct FrameUniforms uf = {RGBA_TO_V4(_color ^ xorColor)}; 466 [encoder setVertexBytes:&uf length:sizeof(uf) atIndex:FrameUniformBuffer]; 467 468 } else if (_paintState == sun_java2d_SunGraphics2D_PAINT_GRADIENT) { 469 470 pipelineState = [pipelineStateStorage getXorModePipelineState:templateRenderPipelineDesc 471 vertexShaderId:@"vert_grad" 472 fragmentShaderId:@"frag_grad" 473 srcFlags:srcFlags 474 dstFlags:dstFlags 475 stencilNeeded:stencil]; 476 477 struct GradFrameUniforms uf = { 478 {_p0, _p1, _p3}, 479 RGBA_TO_V4(_pixel1 ^ xorColor), 480 RGBA_TO_V4(_pixel2 ^ xorColor)}; 481 [encoder setFragmentBytes: &uf length:sizeof(uf) atIndex:0]; 482 } else if (_paintState == sun_java2d_SunGraphics2D_PAINT_TEXTURE) { 483 484 pipelineState = [pipelineStateStorage getXorModePipelineState:templateRenderPipelineDesc 485 vertexShaderId:@"vert_tp" 486 fragmentShaderId:@"frag_tp_xorMode" 487 srcFlags:srcFlags 488 dstFlags:dstFlags 489 stencilNeeded:stencil]; 490 491 [encoder setVertexBytes:&_anchor length:sizeof(_anchor) atIndex:FrameUniformBuffer]; 492 [encoder setFragmentTexture:_paintTexture atIndex: 0]; 493 [encoder setFragmentBytes:&xorColor length:sizeof(xorColor) atIndex: 0]; 494 } 495 } 496 [encoder setRenderPipelineState:pipelineState]; 497 } 498 499 @end 500 501 /************************* GradientPaint support ****************************/ 502 503 static void 504 MTLPaints_InitGradientTexture() 505 { 506 //TODO 507 J2dTraceLn(J2D_TRACE_INFO, "MTLPaints_InitGradientTexture -- :TODO"); 508 } 509 510 /****************** Shared MultipleGradientPaint support ********************/ 511 512 /** 513 * These constants are identical to those defined in the 514 * MultipleGradientPaint.CycleMethod enum; they are copied here for 515 * convenience (ideally we would pull them directly from the Java level, 516 * but that entails more hassle than it is worth). 517 */ 518 #define CYCLE_NONE 0 519 #define CYCLE_REFLECT 1 520 #define CYCLE_REPEAT 2 521 522 /** 523 * The following constants are flags that can be bitwise-or'ed together 524 * to control how the MultipleGradientPaint shader source code is generated: 525 * 526 * MULTI_CYCLE_METHOD 527 * Placeholder for the CycleMethod enum constant. 528 * 529 * MULTI_LARGE 530 * If set, use the (slower) shader that supports a larger number of 531 * gradient colors; otherwise, use the optimized codepath. See 532 * the MAX_FRACTIONS_SMALL/LARGE constants below for more details. 533 * 534 * MULTI_USE_MASK 535 * If set, apply the alpha mask value from texture unit 0 to the 536 * final color result (only used in the MaskFill case). 537 * 538 * MULTI_LINEAR_RGB 539 * If set, convert the linear RGB result back into the sRGB color space. 540 */ 541 #define MULTI_CYCLE_METHOD (3 << 0) 542 #define MULTI_LARGE (1 << 2) 543 #define MULTI_USE_MASK (1 << 3) 544 #define MULTI_LINEAR_RGB (1 << 4) 545 546 /** 547 * This value determines the size of the array of programs for each 548 * MultipleGradientPaint type. This value reflects the maximum value that 549 * can be represented by performing a bitwise-or of all the MULTI_* 550 * constants defined above. 551 */ 552 #define MAX_PROGRAMS 32 553 554 /** Evaluates to true if the given bit is set on the local flags variable. */ 555 #define IS_SET(flagbit) \ 556 (((flags) & (flagbit)) != 0) 557 558 /** Composes the given parameters as flags into the given flags variable.*/ 559 #define COMPOSE_FLAGS(flags, cycleMethod, large, useMask, linear) \ 560 do { \ 561 flags |= ((cycleMethod) & MULTI_CYCLE_METHOD); \ 562 if (large) flags |= MULTI_LARGE; \ 563 if (useMask) flags |= MULTI_USE_MASK; \ 564 if (linear) flags |= MULTI_LINEAR_RGB; \ 565 } while (0) 566 567 /** Extracts the CycleMethod enum value from the given flags variable. */ 568 #define EXTRACT_CYCLE_METHOD(flags) \ 569 ((flags) & MULTI_CYCLE_METHOD) 570 571 /** 572 * The maximum number of gradient "stops" supported by the fragment shader 573 * and related code. When the MULTI_LARGE flag is set, we will use 574 * MAX_FRACTIONS_LARGE; otherwise, we use MAX_FRACTIONS_SMALL. By having 575 * two separate values, we can have one highly optimized shader (SMALL) that 576 * supports only a few fractions/colors, and then another, less optimal 577 * shader that supports more stops. 578 */ 579 #define MAX_FRACTIONS sun_java2d_pipe_BufferedPaints_MULTI_MAX_FRACTIONS 580 #define MAX_FRACTIONS_LARGE MAX_FRACTIONS 581 #define MAX_FRACTIONS_SMALL 4 582 583 /** 584 * The maximum number of gradient colors supported by all of the gradient 585 * fragment shaders. Note that this value must be a power of two, as it 586 * determines the size of the 1D texture created below. It also must be 587 * greater than or equal to MAX_FRACTIONS (there is no strict requirement 588 * that the two values be equal). 589 */ 590 #define MAX_COLORS 16 591 592 /** 593 * The handle to the gradient color table texture object used by the shaders. 594 */ 595 static jint multiGradientTexID = 0; 596 597 /** 598 * This is essentially a template of the shader source code that can be used 599 * for either LinearGradientPaint or RadialGradientPaint. It includes the 600 * structure and some variables that are common to each; the remaining 601 * code snippets (for CycleMethod, ColorSpaceType, and mask modulation) 602 * are filled in prior to compiling the shader at runtime depending on the 603 * paint parameters. See MTLPaints_CreateMultiGradProgram() for more details. 604 */ 605 static const char *multiGradientShaderSource = 606 // gradient texture size (in texels) 607 "const int TEXTURE_SIZE = %d;" 608 // maximum number of fractions/colors supported by this shader 609 "const int MAX_FRACTIONS = %d;" 610 // size of a single texel 611 "const float FULL_TEXEL = (1.0 / float(TEXTURE_SIZE));" 612 // size of half of a single texel 613 "const float HALF_TEXEL = (FULL_TEXEL / 2.0);" 614 // texture containing the gradient colors 615 "uniform sampler1D colors;" 616 // array of gradient stops/fractions 617 "uniform float fractions[MAX_FRACTIONS];" 618 // array of scale factors (one for each interval) 619 "uniform float scaleFactors[MAX_FRACTIONS-1];" 620 // (placeholder for mask variable) 621 "%s" 622 // (placeholder for Linear/RadialGP-specific variables) 623 "%s" 624 "" 625 "void main(void)" 626 "{" 627 " float dist;" 628 // (placeholder for Linear/RadialGradientPaint-specific code) 629 " %s" 630 "" 631 " float tc;" 632 // (placeholder for CycleMethod-specific code) 633 " %s" 634 "" 635 // calculate interpolated color 636 " vec4 result = texture1D(colors, tc);" 637 "" 638 // (placeholder for ColorSpace conversion code) 639 " %s" 640 "" 641 // (placeholder for mask modulation code) 642 " %s" 643 "" 644 // modulate with gl_Color in order to apply extra alpha 645 " gl_FragColor = result * gl_Color;" 646 "}"; 647 648 /** 649 * This code takes a "dist" value as input (as calculated earlier by the 650 * LGP/RGP-specific code) in the range [0,1] and produces a texture 651 * coordinate value "tc" that represents the position of the chosen color 652 * in the one-dimensional gradient texture (also in the range [0,1]). 653 * 654 * One naive way to implement this would be to iterate through the fractions 655 * to figure out in which interval "dist" falls, and then compute the 656 * relative distance between the two nearest stops. This approach would 657 * require an "if" check on every iteration, and it is best to avoid 658 * conditionals in fragment shaders for performance reasons. Also, one might 659 * be tempted to use a break statement to jump out of the loop once the 660 * interval was found, but break statements (and non-constant loop bounds) 661 * are not natively available on most graphics hardware today, so that is 662 * a non-starter. 663 * 664 * The more optimal approach used here avoids these issues entirely by using 665 * an accumulation function that is equivalent to the process described above. 666 * The scaleFactors array is pre-initialized at enable time as follows: 667 * scaleFactors[i] = 1.0 / (fractions[i+1] - fractions[i]); 668 * 669 * For each iteration, we subtract fractions[i] from dist and then multiply 670 * that value by scaleFactors[i]. If we are within the target interval, 671 * this value will be a fraction in the range [0,1] indicating the relative 672 * distance between fraction[i] and fraction[i+1]. If we are below the 673 * target interval, this value will be negative, so we clamp it to zero 674 * to avoid accumulating any value. If we are above the target interval, 675 * the value will be greater than one, so we clamp it to one. Upon exiting 676 * the loop, we will have accumulated zero or more 1.0's and a single 677 * fractional value. This accumulated value tells us the position of the 678 * fragment color in the one-dimensional gradient texture, i.e., the 679 * texcoord called "tc". 680 */ 681 static const char *texCoordCalcCode = 682 "int i;" 683 "float relFraction = 0.0;" 684 "for (i = 0; i < MAX_FRACTIONS-1; i++) {" 685 " relFraction +=" 686 " clamp((dist - fractions[i]) * scaleFactors[i], 0.0, 1.0);" 687 "}" 688 // we offset by half a texel so that we find the linearly interpolated 689 // color between the two texel centers of interest 690 "tc = HALF_TEXEL + (FULL_TEXEL * relFraction);"; 691 692 /** Code for NO_CYCLE that gets plugged into the CycleMethod placeholder. */ 693 static const char *noCycleCode = 694 "if (dist <= 0.0) {" 695 " tc = 0.0;" 696 "} else if (dist >= 1.0) {" 697 " tc = 1.0;" 698 "} else {" 699 // (placeholder for texcoord calculation) 700 " %s" 701 "}"; 702 703 /** Code for REFLECT that gets plugged into the CycleMethod placeholder. */ 704 static const char *reflectCode = 705 "dist = 1.0 - (abs(fract(dist * 0.5) - 0.5) * 2.0);" 706 // (placeholder for texcoord calculation) 707 "%s"; 708 709 /** Code for REPEAT that gets plugged into the CycleMethod placeholder. */ 710 static const char *repeatCode = 711 "dist = fract(dist);" 712 // (placeholder for texcoord calculation) 713 "%s"; 714 715 static void 716 MTLPaints_InitMultiGradientTexture() 717 { 718 J2dTraceLn(J2D_TRACE_INFO, "MTLPaints_InitMultiGradientTexture -- :TODO"); 719 } 720 721 /** 722 * Compiles and links the MultipleGradientPaint shader program. If 723 * successful, this function returns a handle to the newly created 724 * shader program; otherwise returns 0. 725 */ 726 static void* 727 MTLPaints_CreateMultiGradProgram(jint flags, 728 char *paintVars, char *distCode) 729 { 730 731 //TODO 732 J2dTraceLn(J2D_TRACE_INFO, "MTLPaints_CreateMultiGradProgram -- :TODO"); 733 734 return NULL; 735 } 736 737 /** 738 * Called from the MTLPaints_SetLinear/RadialGradientPaint() methods 739 * in order to setup the fraction/color values that are common to both. 740 */ 741 static void 742 MTLPaints_SetMultiGradientPaint(void* multiGradProgram, 743 jint numStops, 744 void *pFractions, void *pPixels) 745 { 746 //TODO 747 J2dTraceLn(J2D_TRACE_INFO, "MTLPaints_SetMultiGradientPaint -- :TODO"); 748 749 } 750 751 /********************** LinearGradientPaint support *************************/ 752 753 /** 754 * The handles to the LinearGradientPaint fragment program objects. The 755 * index to the array should be a bitwise-or'ing of the MULTI_* flags defined 756 * above. Note that most applications will likely need to initialize one 757 * or two of these elements, so the array is usually sparsely populated. 758 */ 759 static void* linearGradPrograms[MAX_PROGRAMS]; 760 761 /** 762 * Compiles and links the LinearGradientPaint shader program. If successful, 763 * this function returns a handle to the newly created shader program; 764 * otherwise returns 0. 765 */ 766 static void* 767 MTLPaints_CreateLinearGradProgram(jint flags) 768 { 769 char *paintVars; 770 char *distCode; 771 772 J2dTraceLn1(J2D_TRACE_INFO, 773 "MTLPaints_CreateLinearGradProgram", 774 flags); 775 776 /* 777 * To simplify the code and to make it easier to upload a number of 778 * uniform values at once, we pack a bunch of scalar (float) values 779 * into vec3 values below. Here's how the values are related: 780 * 781 * params.x = p0 782 * params.y = p1 783 * params.z = p3 784 * 785 * yoff = dstOps->yOffset + dstOps->height 786 */ 787 paintVars = 788 "uniform vec3 params;" 789 "uniform float yoff;"; 790 distCode = 791 // note that gl_FragCoord is in window space relative to the 792 // lower-left corner, so we have to flip the y-coordinate here 793 "vec3 fragCoord = vec3(gl_FragCoord.x, yoff-gl_FragCoord.y, 1.0);" 794 "dist = dot(params, fragCoord);"; 795 796 return MTLPaints_CreateMultiGradProgram(flags, paintVars, distCode); 797 } 798 799 /********************** RadialGradientPaint support *************************/ 800 801 /** 802 * The handles to the RadialGradientPaint fragment program objects. The 803 * index to the array should be a bitwise-or'ing of the MULTI_* flags defined 804 * above. Note that most applications will likely need to initialize one 805 * or two of these elements, so the array is usually sparsely populated. 806 */ 807 static void* radialGradPrograms[MAX_PROGRAMS]; 808 809 /** 810 * Compiles and links the RadialGradientPaint shader program. If successful, 811 * this function returns a handle to the newly created shader program; 812 * otherwise returns 0. 813 */ 814 static void* 815 MTLPaints_CreateRadialGradProgram(jint flags) 816 { 817 char *paintVars; 818 char *distCode; 819 820 J2dTraceLn1(J2D_TRACE_INFO, 821 "MTLPaints_CreateRadialGradProgram", 822 flags); 823 824 /* 825 * To simplify the code and to make it easier to upload a number of 826 * uniform values at once, we pack a bunch of scalar (float) values 827 * into vec3 and vec4 values below. Here's how the values are related: 828 * 829 * m0.x = m00 830 * m0.y = m01 831 * m0.z = m02 832 * 833 * m1.x = m10 834 * m1.y = m11 835 * m1.z = m12 836 * 837 * precalc.x = focusX 838 * precalc.y = yoff = dstOps->yOffset + dstOps->height 839 * precalc.z = 1.0 - (focusX * focusX) 840 * precalc.w = 1.0 / precalc.z 841 */ 842 paintVars = 843 "uniform vec3 m0;" 844 "uniform vec3 m1;" 845 "uniform vec4 precalc;"; 846 847 /* 848 * The following code is derived from Daniel Rice's whitepaper on 849 * radial gradient performance (attached to the bug report for 6521533). 850 * Refer to that document as well as the setup code in the Java-level 851 * BufferedPaints.setRadialGradientPaint() method for more details. 852 */ 853 distCode = 854 // note that gl_FragCoord is in window space relative to the 855 // lower-left corner, so we have to flip the y-coordinate here 856 "vec3 fragCoord =" 857 " vec3(gl_FragCoord.x, precalc.y - gl_FragCoord.y, 1.0);" 858 "float x = dot(fragCoord, m0);" 859 "float y = dot(fragCoord, m1);" 860 "float xfx = x - precalc.x;" 861 "dist = (precalc.x*xfx + sqrt(xfx*xfx + y*y*precalc.z))*precalc.w;"; 862 863 return MTLPaints_CreateMultiGradProgram(flags, paintVars, distCode); 864 } 865 866 #endif /* !HEADLESS */