| /* | |
| * Copyright (c) 2019, 2021, Oracle and/or its affiliates. All rights reserved. | |
| * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | |
| * | |
| * This code is free software; you can redistribute it and/or modify it | |
| * under the terms of the GNU General Public License version 2 only, as | |
| * published by the Free Software Foundation. Oracle designates this | |
| * particular file as subject to the "Classpath" exception as provided | |
| * by Oracle in the LICENSE file that accompanied this code. | |
| * | |
| * This code is distributed in the hope that it will be useful, but WITHOUT | |
| * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
| * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | |
| * version 2 for more details (a copy is included in the LICENSE file that | |
| * accompanied this code). | |
| * | |
| * You should have received a copy of the GNU General Public License version | |
| * 2 along with this work; if not, write to the Free Software Foundation, | |
| * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | |
| * | |
| * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA | |
| * or visit www.oracle.com if you need additional information or have any | |
| * questions. | |
| */ | |
| #include "MTLPaints.h" | |
| #include "MTLClip.h" | |
| #include "common.h" | |
| #include "sun_java2d_SunGraphics2D.h" | |
| #include "sun_java2d_pipe_BufferedPaints.h" | |
| #import "MTLComposite.h" | |
| #import "MTLBufImgOps.h" | |
| #include "MTLRenderQueue.h" | |
| #define RGBA_TO_V4(c) \ | |
| { \ | |
| (((c) >> 16) & (0xFF))/255.0f, \ | |
| (((c) >> 8) & 0xFF)/255.0f, \ | |
| ((c) & 0xFF)/255.0f, \ | |
| (((c) >> 24) & 0xFF)/255.0f \ | |
| } | |
| #define FLOAT_ARR_TO_V4(p) \ | |
| { \ | |
| p[0], \ | |
| p[1], \ | |
| p[2], \ | |
| p[3] \ | |
| } | |
| static MTLRenderPipelineDescriptor * templateRenderPipelineDesc = nil; | |
| static MTLRenderPipelineDescriptor * templateTexturePipelineDesc = nil; | |
| static MTLRenderPipelineDescriptor * templateAATexturePipelineDesc = nil; | |
| static MTLRenderPipelineDescriptor * templateLCDPipelineDesc = nil; | |
| static MTLRenderPipelineDescriptor * templateAAPipelineDesc = nil; | |
| static void | |
| setTxtUniforms(MTLContext *mtlc, int color, id <MTLRenderCommandEncoder> encoder, int interpolation, bool repeat, | |
| jfloat extraAlpha, const SurfaceRasterFlags *srcFlags, int mode); | |
| static void initTemplatePipelineDescriptors() { | |
| if (templateRenderPipelineDesc != nil && templateTexturePipelineDesc != nil && | |
| templateAATexturePipelineDesc != nil && templateLCDPipelineDesc != nil && | |
| templateAAPipelineDesc != nil) | |
| return; | |
| MTLVertexDescriptor *vertDesc = [[MTLVertexDescriptor new] autorelease]; | |
| vertDesc.attributes[VertexAttributePosition].format = MTLVertexFormatFloat2; | |
| vertDesc.attributes[VertexAttributePosition].offset = 0; | |
| vertDesc.attributes[VertexAttributePosition].bufferIndex = MeshVertexBuffer; | |
| vertDesc.layouts[MeshVertexBuffer].stride = sizeof(struct Vertex); | |
| vertDesc.layouts[MeshVertexBuffer].stepRate = 1; | |
| vertDesc.layouts[MeshVertexBuffer].stepFunction = MTLVertexStepFunctionPerVertex; | |
| templateRenderPipelineDesc = [MTLRenderPipelineDescriptor new]; | |
| templateRenderPipelineDesc.sampleCount = 1; | |
| templateRenderPipelineDesc.vertexDescriptor = vertDesc; | |
| templateRenderPipelineDesc.colorAttachments[0].pixelFormat = MTLPixelFormatBGRA8Unorm; | |
| templateRenderPipelineDesc.colorAttachments[0].rgbBlendOperation = MTLBlendOperationAdd; | |
| templateRenderPipelineDesc.colorAttachments[0].alphaBlendOperation = MTLBlendOperationAdd; | |
| templateRenderPipelineDesc.colorAttachments[0].sourceRGBBlendFactor = MTLBlendFactorOne; | |
| templateRenderPipelineDesc.colorAttachments[0].sourceAlphaBlendFactor = MTLBlendFactorOne; | |
| templateRenderPipelineDesc.colorAttachments[0].destinationRGBBlendFactor = MTLBlendFactorOneMinusSourceAlpha; | |
| templateRenderPipelineDesc.colorAttachments[0].destinationAlphaBlendFactor = MTLBlendFactorOneMinusSourceAlpha; | |
| templateRenderPipelineDesc.label = @"template_render"; | |
| templateTexturePipelineDesc = [templateRenderPipelineDesc copy]; | |
| templateTexturePipelineDesc.vertexDescriptor.attributes[VertexAttributeTexPos].format = MTLVertexFormatFloat2; | |
| templateTexturePipelineDesc.vertexDescriptor.attributes[VertexAttributeTexPos].offset = 2*sizeof(float); | |
| templateTexturePipelineDesc.vertexDescriptor.attributes[VertexAttributeTexPos].bufferIndex = MeshVertexBuffer; | |
| templateTexturePipelineDesc.vertexDescriptor.layouts[MeshVertexBuffer].stride = sizeof(struct TxtVertex); | |
| templateTexturePipelineDesc.vertexDescriptor.layouts[MeshVertexBuffer].stepRate = 1; | |
| templateTexturePipelineDesc.vertexDescriptor.layouts[MeshVertexBuffer].stepFunction = MTLVertexStepFunctionPerVertex; | |
| templateTexturePipelineDesc.label = @"template_texture"; | |
| templateAATexturePipelineDesc = [templateTexturePipelineDesc copy]; | |
| templateAATexturePipelineDesc.label = @"template_aa_texture"; | |
| templateLCDPipelineDesc = [MTLRenderPipelineDescriptor new]; | |
| templateLCDPipelineDesc.sampleCount = 1; | |
| templateLCDPipelineDesc.vertexDescriptor = vertDesc; | |
| templateLCDPipelineDesc.colorAttachments[0].pixelFormat = MTLPixelFormatBGRA8Unorm; | |
| templateLCDPipelineDesc.vertexDescriptor.attributes[VertexAttributeTexPos].format = MTLVertexFormatFloat2; | |
| templateLCDPipelineDesc.vertexDescriptor.attributes[VertexAttributeTexPos].offset = 2*sizeof(float); | |
| templateLCDPipelineDesc.vertexDescriptor.attributes[VertexAttributeTexPos].bufferIndex = MeshVertexBuffer; | |
| templateLCDPipelineDesc.vertexDescriptor.layouts[MeshVertexBuffer].stride = sizeof(struct TxtVertex); | |
| templateLCDPipelineDesc.vertexDescriptor.layouts[MeshVertexBuffer].stepRate = 1; | |
| templateLCDPipelineDesc.vertexDescriptor.layouts[MeshVertexBuffer].stepFunction = MTLVertexStepFunctionPerVertex; | |
| templateLCDPipelineDesc.label = @"template_lcd"; | |
| vertDesc = [[MTLVertexDescriptor new] autorelease]; | |
| vertDesc.attributes[VertexAttributePosition].format = MTLVertexFormatFloat2; | |
| vertDesc.attributes[VertexAttributePosition].offset = 0; | |
| vertDesc.attributes[VertexAttributePosition].bufferIndex = MeshVertexBuffer; | |
| vertDesc.layouts[MeshVertexBuffer].stride = sizeof(struct AAVertex); | |
| vertDesc.layouts[MeshVertexBuffer].stepRate = 1; | |
| vertDesc.layouts[MeshVertexBuffer].stepFunction = MTLVertexStepFunctionPerVertex; | |
| templateAAPipelineDesc = [MTLRenderPipelineDescriptor new]; | |
| templateAAPipelineDesc.sampleCount = 1; | |
| templateAAPipelineDesc.vertexDescriptor = vertDesc; | |
| templateAAPipelineDesc.colorAttachments[0].pixelFormat = MTLPixelFormatBGRA8Unorm; | |
| templateAAPipelineDesc.colorAttachments[0].rgbBlendOperation = MTLBlendOperationAdd; | |
| templateAAPipelineDesc.colorAttachments[0].alphaBlendOperation = MTLBlendOperationAdd; | |
| templateAAPipelineDesc.colorAttachments[0].sourceRGBBlendFactor = MTLBlendFactorOne; | |
| templateAAPipelineDesc.colorAttachments[0].sourceAlphaBlendFactor = MTLBlendFactorOne; | |
| templateAAPipelineDesc.colorAttachments[0].destinationRGBBlendFactor = MTLBlendFactorOneMinusSourceAlpha; | |
| templateAAPipelineDesc.colorAttachments[0].destinationAlphaBlendFactor = MTLBlendFactorOneMinusSourceAlpha; | |
| templateAAPipelineDesc.colorAttachments[0].blendingEnabled = YES; | |
| templateAAPipelineDesc.vertexDescriptor.attributes[VertexAttributeTexPos].format = MTLVertexFormatFloat2; | |
| templateAAPipelineDesc.vertexDescriptor.attributes[VertexAttributeTexPos].offset = 2*sizeof(float); | |
| templateAAPipelineDesc.vertexDescriptor.attributes[VertexAttributeTexPos].bufferIndex = MeshVertexBuffer; | |
| templateAAPipelineDesc.vertexDescriptor.attributes[VertexAttributeITexPos].format = MTLVertexFormatFloat2; | |
| templateAAPipelineDesc.vertexDescriptor.attributes[VertexAttributeITexPos].offset = 4*sizeof(float); | |
| templateAAPipelineDesc.vertexDescriptor.attributes[VertexAttributeITexPos].bufferIndex = MeshVertexBuffer; | |
| templateAAPipelineDesc.label = @"template_aa"; | |
| } | |
| @implementation MTLColorPaint { | |
| // color-mode | |
| jint _color; | |
| } | |
| + (void)setPipelineState:(id <MTLRenderCommandEncoder>)encoder | |
| context:(MTLContext *)mtlc | |
| renderOptions:(const RenderOptions *)renderOptions | |
| pipelineStateStorage:(MTLPipelineStatesStorage *)pipelineStateStorage | |
| rpDesc:(MTLRenderPipelineDescriptor *)rpDesc | |
| vertShader:(NSString *)vertShader | |
| fragShader:(NSString *)fragShader | |
| color:(jint)color { | |
| struct FrameUniforms uf = {RGBA_TO_V4(color)}; | |
| [encoder setVertexBytes:&uf length:sizeof(uf) atIndex:FrameUniformBuffer]; | |
| id <MTLRenderPipelineState> pipelineState = [pipelineStateStorage getPipelineState:rpDesc | |
| vertexShaderId:vertShader | |
| fragmentShaderId:fragShader | |
| composite:mtlc.composite | |
| renderOptions:renderOptions | |
| stencilNeeded:[mtlc.clip isShape]]; | |
| [encoder setRenderPipelineState:pipelineState]; | |
| } | |
| - (id)initWithColor:(jint)color { | |
| self = [super initWithState:sun_java2d_SunGraphics2D_PAINT_ALPHACOLOR]; | |
| if (self) { | |
| _color = color; | |
| } | |
| return self; | |
| } | |
| - (jint)color { | |
| return _color; | |
| } | |
| - (BOOL)isEqual:(MTLColorPaint *)other { | |
| if (other == self) | |
| return YES; | |
| if (!other || ![[other class] isEqual:[self class]]) | |
| return NO; | |
| return _color == other->_color; | |
| } | |
| - (NSUInteger)hash { | |
| NSUInteger h = [super hash]; | |
| h = h*31 + _color; | |
| return h; | |
| } | |
| - (NSString *)description { | |
| return [NSString stringWithFormat: | |
| @"[r=%d g=%d b=%d a=%d]", | |
| (_color >> 16) & (0xFF), | |
| (_color >> 8) & 0xFF, | |
| (_color) & 0xFF, | |
| (_color >> 24) & 0xFF]; | |
| } | |
| - (void)setPipelineState:(id<MTLRenderCommandEncoder>)encoder | |
| context:(MTLContext *)mtlc | |
| renderOptions:(const RenderOptions *)renderOptions | |
| pipelineStateStorage:(MTLPipelineStatesStorage *)pipelineStateStorage | |
| { | |
| initTemplatePipelineDescriptors(); | |
| MTLRenderPipelineDescriptor *rpDesc = nil; | |
| NSString *vertShader = @"vert_col"; | |
| NSString *fragShader = @"frag_col"; | |
| if (renderOptions->isTexture) { | |
| vertShader = @"vert_txt"; | |
| fragShader = @"frag_txt"; | |
| rpDesc = [[templateTexturePipelineDesc copy] autorelease]; | |
| if (renderOptions->isAA) { | |
| fragShader = @"aa_frag_txt"; | |
| rpDesc = [[templateAATexturePipelineDesc copy] autorelease]; | |
| } | |
| if (renderOptions->isText) { | |
| fragShader = @"frag_text"; | |
| } | |
| if (renderOptions->isLCD) { | |
| vertShader = @"vert_txt_lcd"; | |
| fragShader = @"lcd_color"; | |
| rpDesc = [[templateLCDPipelineDesc copy] autorelease]; | |
| } | |
| setTxtUniforms(mtlc, _color, encoder, | |
| renderOptions->interpolation, NO, [mtlc.composite getExtraAlpha], | |
| &renderOptions->srcFlags, 1); | |
| } else if (renderOptions->isAAShader) { | |
| vertShader = @"vert_col_aa"; | |
| fragShader = @"frag_col_aa"; | |
| rpDesc = [[templateAAPipelineDesc copy] autorelease]; | |
| } else { | |
| rpDesc = [[templateRenderPipelineDesc copy] autorelease]; | |
| } | |
| [MTLColorPaint setPipelineState:encoder | |
| context:mtlc | |
| renderOptions:renderOptions | |
| pipelineStateStorage:pipelineStateStorage | |
| rpDesc:rpDesc | |
| vertShader:vertShader | |
| fragShader:fragShader | |
| color:_color]; | |
| } | |
| - (void)setXorModePipelineState:(id<MTLRenderCommandEncoder>)encoder | |
| context:(MTLContext *)mtlc | |
| renderOptions:(const RenderOptions *)renderOptions | |
| pipelineStateStorage:(MTLPipelineStatesStorage *)pipelineStateStorage | |
| { | |
| initTemplatePipelineDescriptors(); | |
| NSString * vertShader = @"vert_col_xorMode"; | |
| NSString * fragShader = @"frag_col_xorMode"; | |
| MTLRenderPipelineDescriptor * rpDesc = nil; | |
| jint xorColor = (jint) [mtlc.composite getXorColor]; | |
| // Calculate _color ^ xorColor for RGB components | |
| // This color gets XORed with destination framebuffer pixel color | |
| const int col = _color ^ xorColor; | |
| BMTLSDOps *dstOps = MTLRenderQueue_GetCurrentDestination(); | |
| if (renderOptions->isTexture) { | |
| vertShader = @"vert_txt_xorMode"; | |
| fragShader = @"frag_txt_xorMode"; | |
| rpDesc = [[templateTexturePipelineDesc copy] autorelease]; | |
| setTxtUniforms(mtlc, col, encoder, | |
| renderOptions->interpolation, NO, [mtlc.composite getExtraAlpha], | |
| &renderOptions->srcFlags, 1); | |
| [encoder setFragmentBytes:&xorColor length:sizeof(xorColor) atIndex:0]; | |
| [encoder setFragmentTexture:dstOps->pTexture atIndex:1]; | |
| } else { | |
| struct FrameUniforms uf = {RGBA_TO_V4(col)}; | |
| rpDesc = [[templateRenderPipelineDesc copy] autorelease]; | |
| [encoder setVertexBytes:&uf length:sizeof(uf) atIndex:FrameUniformBuffer]; | |
| [encoder setFragmentTexture:dstOps->pTexture atIndex:0]; | |
| } | |
| id <MTLRenderPipelineState> pipelineState = [pipelineStateStorage getPipelineState:rpDesc | |
| vertexShaderId:vertShader | |
| fragmentShaderId:fragShader | |
| composite:mtlc.composite | |
| renderOptions:renderOptions | |
| stencilNeeded:[mtlc.clip isShape]]; | |
| [encoder setRenderPipelineState:pipelineState]; | |
| } | |
| @end | |
| @implementation MTLBaseGradPaint { | |
| jboolean _useMask; | |
| @protected | |
| jint _cyclic; | |
| } | |
| - (id)initWithState:(jint)state mask:(jboolean)useMask cyclic:(jboolean)cyclic { | |
| self = [super initWithState:state]; | |
| if (self) { | |
| _useMask = useMask; | |
| _cyclic = cyclic; | |
| } | |
| return self; | |
| } | |
| - (BOOL)isEqual:(MTLBaseGradPaint *)other { | |
| if (other == self) | |
| return YES; | |
| if (!other || ![[other class] isEqual:[self class]]) | |
| return NO; | |
| return [super isEqual:self] && _cyclic == other->_cyclic && _useMask == other->_useMask; | |
| } | |
| - (NSUInteger)hash { | |
| NSUInteger h = [super hash]; | |
| h = h*31 + _cyclic; | |
| h = h*31 + _useMask; | |
| return h; | |
| } | |
| @end | |
| @implementation MTLGradPaint { | |
| jdouble _p0; | |
| jdouble _p1; | |
| jdouble _p3; | |
| jint _pixel1; | |
| jint _pixel2; | |
| } | |
| - (id)initWithUseMask:(jboolean)useMask | |
| cyclic:(jboolean)cyclic | |
| p0:(jdouble)p0 | |
| p1:(jdouble)p1 | |
| p3:(jdouble)p3 | |
| pixel1:(jint)pixel1 | |
| pixel2:(jint)pixel2 | |
| { | |
| self = [super initWithState:sun_java2d_SunGraphics2D_PAINT_GRADIENT | |
| mask:useMask | |
| cyclic:cyclic]; | |
| if (self) { | |
| _p0 = p0; | |
| _p1 = p1; | |
| _p3 = p3; | |
| _pixel1 = pixel1; | |
| _pixel2 = pixel2; | |
| } | |
| return self; | |
| } | |
| - (BOOL)isEqual:(MTLGradPaint *)other { | |
| if (other == self) | |
| return YES; | |
| if (!other || ![[other class] isEqual:[self class]]) | |
| return NO; | |
| return [super isEqual:self] && _p0 == other->_p0 && | |
| _p1 == other->_p1 && _p3 == other->_p3 && | |
| _pixel1 == other->_pixel1 && _pixel2 == other->_pixel2; | |
| } | |
| - (NSUInteger)hash { | |
| NSUInteger h = [super hash]; | |
| h = h*31 + [@(_p0) hash]; | |
| h = h*31 + [@(_p1) hash]; | |
| h = h*31 + [@(_p3) hash]; | |
| h = h*31 + _pixel1; | |
| h = h*31 + _pixel2; | |
| return h; | |
| } | |
| - (NSString *)description { | |
| return [NSString stringWithFormat:@"gradient"]; | |
| } | |
| - (void)setPipelineState:(id)encoder | |
| context:(MTLContext *)mtlc | |
| renderOptions:(const RenderOptions *)renderOptions | |
| pipelineStateStorage:(MTLPipelineStatesStorage *)pipelineStateStorage | |
| { | |
| initTemplatePipelineDescriptors(); | |
| MTLRenderPipelineDescriptor *rpDesc = nil; | |
| NSString *vertShader = @"vert_grad"; | |
| NSString *fragShader = @"frag_grad"; | |
| struct GradFrameUniforms uf = { | |
| {_p0, _p1, _p3}, | |
| RGBA_TO_V4(_pixel1), | |
| RGBA_TO_V4(_pixel2), | |
| _cyclic, | |
| [mtlc.composite getExtraAlpha] | |
| }; | |
| [encoder setFragmentBytes:&uf length:sizeof(uf) atIndex:0]; | |
| if (renderOptions->isTexture) { | |
| vertShader = @"vert_txt_grad"; | |
| fragShader = @"frag_txt_grad"; | |
| rpDesc = [[templateTexturePipelineDesc copy] autorelease]; | |
| } else { | |
| rpDesc = [[templateRenderPipelineDesc copy] autorelease]; | |
| } | |
| id <MTLRenderPipelineState> pipelineState = [pipelineStateStorage getPipelineState:rpDesc | |
| vertexShaderId:vertShader | |
| fragmentShaderId:fragShader | |
| composite:mtlc.composite | |
| renderOptions:renderOptions | |
| stencilNeeded:[mtlc.clip isShape]]; | |
| [encoder setRenderPipelineState:pipelineState]; | |
| } | |
| - (void)setXorModePipelineState:(id)encoder | |
| context:(MTLContext *)mtlc | |
| renderOptions:(const RenderOptions *)renderOptions | |
| pipelineStateStorage:(MTLPipelineStatesStorage *)pipelineStateStorage | |
| { | |
| // This block is not reached in current implementation. | |
| // Gradient paint XOR mode rendering uses a tile based rendering using a SW pipe (similar to OGL) | |
| initTemplatePipelineDescriptors(); | |
| NSString* vertShader = @"vert_grad_xorMode"; | |
| NSString* fragShader = @"frag_grad_xorMode"; | |
| MTLRenderPipelineDescriptor *rpDesc = [[templateRenderPipelineDesc copy] autorelease]; | |
| jint xorColor = (jint) [mtlc.composite getXorColor]; | |
| struct GradFrameUniforms uf = { | |
| {_p0, _p1, _p3}, | |
| RGBA_TO_V4(_pixel1 ^ xorColor), | |
| RGBA_TO_V4(_pixel2 ^ xorColor), | |
| _cyclic, | |
| [mtlc.composite getExtraAlpha] | |
| }; | |
| [encoder setFragmentBytes: &uf length:sizeof(uf) atIndex:0]; | |
| BMTLSDOps *dstOps = MTLRenderQueue_GetCurrentDestination(); | |
| [encoder setFragmentTexture:dstOps->pTexture atIndex:0]; | |
| J2dTraceLn(J2D_TRACE_INFO, "MTLPaints - setXorModePipelineState -- PAINT_GRADIENT"); | |
| id <MTLRenderPipelineState> pipelineState = [pipelineStateStorage getPipelineState:rpDesc | |
| vertexShaderId:vertShader | |
| fragmentShaderId:fragShader | |
| composite:mtlc.composite | |
| renderOptions:renderOptions | |
| stencilNeeded:[mtlc.clip isShape]]; | |
| [encoder setRenderPipelineState:pipelineState]; | |
| } | |
| @end | |
| @implementation MTLBaseMultiGradPaint { | |
| @protected | |
| jboolean _linear; | |
| jint _numFracts; | |
| jfloat _fract[GRAD_MAX_FRACTIONS]; | |
| jint _pixel[GRAD_MAX_FRACTIONS]; | |
| } | |
| - (id)initWithState:(jint)state | |
| mask:(jboolean)useMask | |
| linear:(jboolean)linear | |
| cycleMethod:(jboolean)cycleMethod | |
| numStops:(jint)numStops | |
| fractions:(jfloat *)fractions | |
| pixels:(jint *)pixels | |
| { | |
| self = [super initWithState:state | |
| mask:useMask | |
| cyclic:cycleMethod]; | |
| if (self) { | |
| _linear = linear; | |
| memcpy(_fract, fractions,numStops*sizeof(jfloat)); | |
| memcpy(_pixel, pixels, numStops*sizeof(jint)); | |
| _numFracts = numStops; | |
| } | |
| return self; | |
| } | |
| - (BOOL)isEqual:(MTLBaseMultiGradPaint *)other { | |
| if (other == self) | |
| return YES; | |
| if (!other || ![[other class] isEqual:[self class]]) | |
| return NO; | |
| if (_numFracts != other->_numFracts || ![super isEqual:self]) | |
| return NO; | |
| for (int i = 0; i < _numFracts; i++) { | |
| if (_fract[i] != other->_fract[i]) return NO; | |
| if (_pixel[i] != other->_pixel[i]) return NO; | |
| } | |
| return YES; | |
| } | |
| - (NSUInteger)hash { | |
| NSUInteger h = [super hash]; | |
| h = h*31 + _numFracts; | |
| for (int i = 0; i < _numFracts; i++) { | |
| h = h*31 + [@(_fract[i]) hash]; | |
| h = h*31 + _pixel[i]; | |
| } | |
| return h; | |
| } | |
| @end | |
| @implementation MTLLinearGradPaint { | |
| jdouble _p0; | |
| jdouble _p1; | |
| jdouble _p3; | |
| } | |
| - (void)setPipelineState:(id)encoder | |
| context:(MTLContext *)mtlc | |
| renderOptions:(const RenderOptions *)renderOptions | |
| pipelineStateStorage:(MTLPipelineStatesStorage *)pipelineStateStorage | |
| { | |
| initTemplatePipelineDescriptors(); | |
| MTLRenderPipelineDescriptor *rpDesc = nil; | |
| NSString *vertShader = @"vert_grad"; | |
| NSString *fragShader = @"frag_lin_grad"; | |
| if (renderOptions->isTexture) { | |
| vertShader = @"vert_txt_grad"; | |
| fragShader = @"frag_txt_lin_grad"; | |
| rpDesc = [[templateTexturePipelineDesc copy] autorelease]; | |
| } else { | |
| rpDesc = [[templateRenderPipelineDesc copy] autorelease]; | |
| } | |
| struct LinGradFrameUniforms uf = { | |
| {_p0, _p1, _p3}, | |
| {}, | |
| {}, | |
| _numFracts, | |
| _linear, | |
| _cyclic, | |
| [mtlc.composite getExtraAlpha] | |
| }; | |
| memcpy(uf.fract, _fract, _numFracts*sizeof(jfloat)); | |
| for (int i = 0; i < _numFracts; i++) { | |
| vector_float4 v = RGBA_TO_V4(_pixel[i]); | |
| uf.color[i] = v; | |
| } | |
| [encoder setFragmentBytes:&uf length:sizeof(uf) atIndex:0]; | |
| id <MTLRenderPipelineState> pipelineState = [pipelineStateStorage getPipelineState:rpDesc | |
| vertexShaderId:vertShader | |
| fragmentShaderId:fragShader | |
| composite:mtlc.composite | |
| renderOptions:renderOptions | |
| stencilNeeded:[mtlc.clip isShape]]; | |
| [encoder setRenderPipelineState:pipelineState]; | |
| } | |
| - (id)initWithUseMask:(jboolean)useMask | |
| linear:(jboolean)linear | |
| cycleMethod:(jboolean)cycleMethod | |
| numStops:(jint)numStops | |
| p0:(jfloat)p0 | |
| p1:(jfloat)p1 | |
| p3:(jfloat)p3 | |
| fractions:(jfloat *)fractions | |
| pixels:(jint *)pixels | |
| { | |
| self = [super initWithState:sun_java2d_SunGraphics2D_PAINT_LIN_GRADIENT | |
| mask:useMask | |
| linear:linear | |
| cycleMethod:cycleMethod | |
| numStops:numStops | |
| fractions:fractions | |
| pixels:pixels]; | |
| if (self) { | |
| _p0 = p0; | |
| _p1 = p1; | |
| _p3 = p3; | |
| } | |
| return self; | |
| } | |
| - (BOOL)isEqual:(MTLLinearGradPaint *)other { | |
| if (other == self) | |
| return YES; | |
| if (!other || ![[other class] isEqual:[self class]] || ![super isEqual:other]) | |
| return NO; | |
| return _p0 == other->_p0 && _p1 == other->_p1 && _p3 == other->_p3; | |
| } | |
| - (NSUInteger)hash { | |
| NSUInteger h = [super hash]; | |
| h = h*31 + [@(_p0) hash]; | |
| h = h*31 + [@(_p1) hash]; | |
| h = h*31 + [@(_p3) hash]; | |
| return h; | |
| } | |
| - (NSString *)description { | |
| return [NSString stringWithFormat:@"linear_gradient"]; | |
| } | |
| @end | |
| @implementation MTLRadialGradPaint { | |
| jfloat _m00; | |
| jfloat _m01; | |
| jfloat _m02; | |
| jfloat _m10; | |
| jfloat _m11; | |
| jfloat _m12; | |
| jfloat _focusX; | |
| } | |
| - (id)initWithUseMask:(jboolean)useMask | |
| linear:(jboolean)linear | |
| cycleMethod:(jint)cycleMethod | |
| numStops:(jint)numStops | |
| m00:(jfloat)m00 | |
| m01:(jfloat)m01 | |
| m02:(jfloat)m02 | |
| m10:(jfloat)m10 | |
| m11:(jfloat)m11 | |
| m12:(jfloat)m12 | |
| focusX:(jfloat)focusX | |
| fractions:(void *)fractions | |
| pixels:(void *)pixels | |
| { | |
| self = [super initWithState:sun_java2d_SunGraphics2D_PAINT_RAD_GRADIENT | |
| mask:useMask | |
| linear:linear | |
| cycleMethod:cycleMethod | |
| numStops:numStops | |
| fractions:fractions | |
| pixels:pixels]; | |
| if (self) { | |
| _m00 = m00; | |
| _m01 = m01; | |
| _m02 = m02; | |
| _m10 = m10; | |
| _m11 = m11; | |
| _m12 = m12; | |
| _focusX = focusX; | |
| } | |
| return self; | |
| } | |
| - (BOOL)isEqual:(MTLRadialGradPaint *)other { | |
| if (other == self) | |
| return YES; | |
| if (!other || ![[other class] isEqual:[self class]] | |
| || ![super isEqual:self]) | |
| return NO; | |
| return _m00 == other->_m00 && _m01 == other->_m01 && _m02 == other->_m02 && | |
| _m10 == other->_m10 && _m11 == other->_m11 && _m12 == other->_m12 && | |
| _focusX == other->_focusX; | |
| } | |
| - (NSUInteger)hash { | |
| NSUInteger h = [super hash]; | |
| h = h*31 + [@(_m00) hash]; | |
| h = h*31 + [@(_m01) hash]; | |
| h = h*31 + [@(_m02) hash]; | |
| h = h*31 + [@(_m10) hash]; | |
| h = h*31 + [@(_m11) hash]; | |
| h = h*31 + [@(_m12) hash]; | |
| return h; | |
| } | |
| - (NSString *)description { | |
| return [NSString stringWithFormat:@"radial_gradient"]; | |
| } | |
| - (void)setPipelineState:(id)encoder | |
| context:(MTLContext *)mtlc | |
| renderOptions:(const RenderOptions *)renderOptions | |
| pipelineStateStorage:(MTLPipelineStatesStorage *)pipelineStateStorage | |
| { | |
| initTemplatePipelineDescriptors(); | |
| MTLRenderPipelineDescriptor *rpDesc = nil; | |
| NSString *vertShader = @"vert_grad"; | |
| NSString *fragShader = @"frag_rad_grad"; | |
| if (renderOptions->isTexture) { | |
| vertShader = @"vert_txt_grad"; | |
| fragShader = @"frag_txt_rad_grad"; | |
| rpDesc = [[templateTexturePipelineDesc copy] autorelease]; | |
| } else { | |
| rpDesc = [[templateRenderPipelineDesc copy] autorelease]; | |
| } | |
| struct RadGradFrameUniforms uf = { | |
| {}, | |
| {}, | |
| _numFracts, | |
| _linear, | |
| _cyclic, | |
| {_m00, _m01, _m02}, | |
| {_m10, _m11, _m12}, | |
| {}, | |
| [mtlc.composite getExtraAlpha] | |
| }; | |
| uf.precalc[0] = _focusX; | |
| uf.precalc[1] = 1.0 - (_focusX * _focusX); | |
| uf.precalc[2] = 1.0 / uf.precalc[1]; | |
| memcpy(uf.fract, _fract, _numFracts*sizeof(jfloat)); | |
| for (int i = 0; i < _numFracts; i++) { | |
| vector_float4 v = RGBA_TO_V4(_pixel[i]); | |
| uf.color[i] = v; | |
| } | |
| [encoder setFragmentBytes:&uf length:sizeof(uf) atIndex:0]; | |
| id <MTLRenderPipelineState> pipelineState = [pipelineStateStorage getPipelineState:rpDesc | |
| vertexShaderId:vertShader | |
| fragmentShaderId:fragShader | |
| composite:mtlc.composite | |
| renderOptions:renderOptions | |
| stencilNeeded:[mtlc.clip isShape]]; | |
| [encoder setRenderPipelineState:pipelineState]; | |
| } | |
| @end | |
| @implementation MTLTexturePaint { | |
| struct AnchorData _anchor; | |
| id <MTLTexture> _paintTexture; | |
| jboolean _isOpaque; | |
| } | |
| - (id)initWithUseMask:(jboolean)useMask | |
| textureID:(id)textureId | |
| isOpaque:(jboolean)isOpaque | |
| filter:(jboolean)filter | |
| xp0:(jdouble)xp0 | |
| xp1:(jdouble)xp1 | |
| xp3:(jdouble)xp3 | |
| yp0:(jdouble)yp0 | |
| yp1:(jdouble)yp1 | |
| yp3:(jdouble)yp3 | |
| { | |
| self = [super initWithState:sun_java2d_SunGraphics2D_PAINT_TEXTURE]; | |
| if (self) { | |
| _paintTexture = textureId; | |
| _anchor.xParams[0] = xp0; | |
| _anchor.xParams[1] = xp1; | |
| _anchor.xParams[2] = xp3; | |
| _anchor.yParams[0] = yp0; | |
| _anchor.yParams[1] = yp1; | |
| _anchor.yParams[2] = yp3; | |
| _isOpaque = isOpaque; | |
| } | |
| return self; | |
| } | |
| - (BOOL)isEqual:(MTLTexturePaint *)other { | |
| if (other == self) | |
| return YES; | |
| if (!other || ![[other class] isEqual:[self class]]) | |
| return NO; | |
| return [_paintTexture isEqual:other->_paintTexture] | |
| && _anchor.xParams[0] == other->_anchor.xParams[0] | |
| && _anchor.xParams[1] == other->_anchor.xParams[1] | |
| && _anchor.xParams[2] == other->_anchor.xParams[2] | |
| && _anchor.yParams[0] == other->_anchor.yParams[0] | |
| && _anchor.yParams[1] == other->_anchor.yParams[1] | |
| && _anchor.yParams[2] == other->_anchor.yParams[2]; | |
| } | |
| - (NSString *)description { | |
| return [NSString stringWithFormat:@"texture_paint"]; | |
| } | |
| - (void)setPipelineState:(id)encoder | |
| context:(MTLContext *)mtlc | |
| renderOptions:(const RenderOptions *)renderOptions | |
| pipelineStateStorage:(MTLPipelineStatesStorage *)pipelineStateStorage | |
| { | |
| initTemplatePipelineDescriptors(); | |
| MTLRenderPipelineDescriptor *rpDesc = nil; | |
| NSString* vertShader = @"vert_tp"; | |
| NSString* fragShader = @"frag_tp"; | |
| [encoder setVertexBytes:&_anchor length:sizeof(_anchor) atIndex:FrameUniformBuffer]; | |
| if (renderOptions->isTexture) { | |
| vertShader = @"vert_txt_tp"; | |
| fragShader = @"frag_txt_tp"; | |
| rpDesc = [[templateTexturePipelineDesc copy] autorelease]; | |
| [encoder setFragmentTexture:_paintTexture atIndex:1]; | |
| } else { | |
| rpDesc = [[templateRenderPipelineDesc copy] autorelease]; | |
| [encoder setFragmentTexture:_paintTexture atIndex:0]; | |
| } | |
| const SurfaceRasterFlags srcFlags = {_isOpaque, renderOptions->srcFlags.isPremultiplied}; | |
| setTxtUniforms(mtlc, 0, encoder, renderOptions->interpolation, YES, [mtlc.composite getExtraAlpha], | |
| &srcFlags, 0); | |
| id <MTLRenderPipelineState> pipelineState = [pipelineStateStorage getPipelineState:rpDesc | |
| vertexShaderId:vertShader | |
| fragmentShaderId:fragShader | |
| composite:mtlc.composite | |
| renderOptions:renderOptions | |
| stencilNeeded:[mtlc.clip isShape]]; | |
| [encoder setRenderPipelineState:pipelineState]; | |
| } | |
| - (void)setXorModePipelineState:(id)encoder | |
| context:(MTLContext *)mtlc | |
| renderOptions:(const RenderOptions *)renderOptions | |
| pipelineStateStorage:(MTLPipelineStatesStorage *)pipelineStateStorage | |
| { | |
| initTemplatePipelineDescriptors(); | |
| // This block is not reached in current implementation. | |
| // Texture paint XOR mode rendering uses a tile based rendering using a SW pipe (similar to OGL) | |
| NSString* vertShader = @"vert_tp_xorMode"; | |
| NSString* fragShader = @"frag_tp_xorMode"; | |
| MTLRenderPipelineDescriptor *rpDesc = [[templateRenderPipelineDesc copy] autorelease]; | |
| jint xorColor = (jint) [mtlc.composite getXorColor]; | |
| [encoder setVertexBytes:&_anchor length:sizeof(_anchor) atIndex:FrameUniformBuffer]; | |
| [encoder setFragmentTexture:_paintTexture atIndex: 0]; | |
| BMTLSDOps *dstOps = MTLRenderQueue_GetCurrentDestination(); | |
| [encoder setFragmentTexture:dstOps->pTexture atIndex:1]; | |
| [encoder setFragmentBytes:&xorColor length:sizeof(xorColor) atIndex: 0]; | |
| J2dTraceLn(J2D_TRACE_INFO, "MTLPaints - setXorModePipelineState -- PAINT_TEXTURE"); | |
| id <MTLRenderPipelineState> pipelineState = [pipelineStateStorage getPipelineState:rpDesc | |
| vertexShaderId:vertShader | |
| fragmentShaderId:fragShader | |
| composite:mtlc.composite | |
| renderOptions:renderOptions | |
| stencilNeeded:[mtlc.clip isShape]]; | |
| [encoder setRenderPipelineState:pipelineState]; | |
| } | |
| @end | |
| @implementation MTLPaint { | |
| jint _paintState; | |
| } | |
| - (instancetype)init { | |
| self = [super init]; | |
| if (self) { | |
| _paintState = sun_java2d_SunGraphics2D_PAINT_UNDEFINED; | |
| } | |
| return self; | |
| } | |
| - (instancetype)initWithState:(jint)state { | |
| self = [super init]; | |
| if (self) { | |
| _paintState = state; | |
| } | |
| return self; | |
| } | |
| - (BOOL)isEqual:(MTLPaint *)other { | |
| if (other == self) | |
| return YES; | |
| if (!other || ![other isKindOfClass:[self class]]) | |
| return NO; | |
| return _paintState == other->_paintState; | |
| } | |
| - (NSUInteger)hash { | |
| return _paintState; | |
| } | |
| - (NSString *)description { | |
| return @"unknown-paint"; | |
| } | |
| static void | |
| setTxtUniforms(MTLContext *mtlc, int color, id <MTLRenderCommandEncoder> encoder, int interpolation, bool repeat, | |
| jfloat extraAlpha, const SurfaceRasterFlags *srcFlags, int mode) { | |
| struct TxtFrameUniforms uf = {RGBA_TO_V4(color), mode, srcFlags->isOpaque, extraAlpha}; | |
| [encoder setFragmentBytes:&uf length:sizeof(uf) atIndex:FrameUniformBuffer]; | |
| [mtlc.samplerManager setSamplerWithEncoder:encoder interpolation:interpolation repeat:repeat]; | |
| } | |
| // For the current paint mode: | |
| // 1. Selects vertex+fragment shaders (and corresponding pipelineDesc) and set pipelineState | |
| // 2. Set vertex and fragment buffers | |
| // Base implementation is used in drawTex2Tex | |
| - (void)setPipelineState:(id <MTLRenderCommandEncoder>)encoder | |
| context:(MTLContext *)mtlc | |
| renderOptions:(const RenderOptions *)renderOptions | |
| pipelineStateStorage:(MTLPipelineStatesStorage *)pipelineStateStorage | |
| { | |
| initTemplatePipelineDescriptors(); | |
| // Called from drawTex2Tex used in flushBuffer and for buffered image ops | |
| if (renderOptions->isTexture) { | |
| NSString * vertShader = @"vert_txt"; | |
| NSString * fragShader = @"frag_txt"; | |
| MTLRenderPipelineDescriptor* rpDesc = [[templateTexturePipelineDesc copy] autorelease]; | |
| NSObject *bufImgOp = [mtlc getBufImgOp]; | |
| if (bufImgOp != nil) { | |
| if ([bufImgOp isKindOfClass:[MTLRescaleOp class]]) { | |
| MTLRescaleOp *rescaleOp = bufImgOp; | |
| fragShader = @"frag_txt_op_rescale"; | |
| struct TxtFrameOpRescaleUniforms uf = { | |
| RGBA_TO_V4(0), [mtlc.composite getExtraAlpha], renderOptions->srcFlags.isOpaque, | |
| rescaleOp.isNonPremult, | |
| FLOAT_ARR_TO_V4([rescaleOp getScaleFactors]), FLOAT_ARR_TO_V4([rescaleOp getOffsets]) | |
| }; | |
| [encoder setFragmentBytes:&uf length:sizeof(uf) atIndex:FrameUniformBuffer]; | |
| [mtlc.samplerManager setSamplerWithEncoder:encoder interpolation:renderOptions->interpolation repeat:NO]; | |
| } else if ([bufImgOp isKindOfClass:[MTLConvolveOp class]]) { | |
| MTLConvolveOp *convolveOp = bufImgOp; | |
| fragShader = @"frag_txt_op_convolve"; | |
| struct TxtFrameOpConvolveUniforms uf = { | |
| [mtlc.composite getExtraAlpha], renderOptions->srcFlags.isOpaque, | |
| FLOAT_ARR_TO_V4([convolveOp getImgEdge]), | |
| convolveOp.kernelSize, convolveOp.isEdgeZeroFill, | |
| }; | |
| [encoder setFragmentBytes:&uf length:sizeof(uf) atIndex:FrameUniformBuffer]; | |
| [mtlc.samplerManager setSamplerWithEncoder:encoder interpolation:renderOptions->interpolation repeat:NO]; | |
| [encoder setFragmentBuffer:[convolveOp getBuffer] offset:0 atIndex:2]; | |
| } else if ([bufImgOp isKindOfClass:[MTLLookupOp class]]) { | |
| MTLLookupOp *lookupOp = bufImgOp; | |
| fragShader = @"frag_txt_op_lookup"; | |
| struct TxtFrameOpLookupUniforms uf = { | |
| [mtlc.composite getExtraAlpha], renderOptions->srcFlags.isOpaque, | |
| FLOAT_ARR_TO_V4([lookupOp getOffset]), lookupOp.isUseSrcAlpha, lookupOp.isNonPremult, | |
| }; | |
| [encoder setFragmentBytes:&uf length:sizeof(uf) atIndex:FrameUniformBuffer]; | |
| [mtlc.samplerManager setSamplerWithEncoder:encoder interpolation:renderOptions->interpolation repeat:NO]; | |
| [encoder setFragmentTexture:[lookupOp getLookupTexture] atIndex:1]; | |
| } | |
| } else { | |
| setTxtUniforms(mtlc, 0, encoder, | |
| renderOptions->interpolation, NO, [mtlc.composite getExtraAlpha], | |
| &renderOptions->srcFlags, 0); | |
| } | |
| id <MTLRenderPipelineState> pipelineState = [pipelineStateStorage getPipelineState:rpDesc | |
| vertexShaderId:vertShader | |
| fragmentShaderId:fragShader | |
| composite:mtlc.composite | |
| renderOptions:renderOptions | |
| stencilNeeded:[mtlc.clip isShape]]; | |
| [encoder setRenderPipelineState:pipelineState]; | |
| } else { | |
| // Fallback to default pipeline state | |
| [MTLColorPaint setPipelineState:encoder | |
| context:mtlc | |
| renderOptions:renderOptions | |
| pipelineStateStorage:pipelineStateStorage | |
| rpDesc:[[templateRenderPipelineDesc copy] autorelease] | |
| vertShader:@"vert_col" | |
| fragShader:@"frag_col" | |
| color:0]; | |
| } | |
| } | |
| // For the current paint mode: | |
| // 1. Selects vertex+fragment shaders (and corresponding pipelineDesc) and set pipelineState | |
| // 2. Set vertex and fragment buffers | |
| - (void)setXorModePipelineState:(id <MTLRenderCommandEncoder>)encoder | |
| context:(MTLContext *)mtlc | |
| renderOptions:(const RenderOptions *)renderOptions | |
| pipelineStateStorage:(MTLPipelineStatesStorage *)pipelineStateStorage | |
| { | |
| initTemplatePipelineDescriptors(); | |
| if (renderOptions->isTexture) { | |
| jint xorColor = (jint) [mtlc.composite getXorColor]; | |
| NSString * vertShader = @"vert_txt_xorMode"; | |
| NSString * fragShader = @"frag_txt_xorMode"; | |
| MTLRenderPipelineDescriptor * rpDesc = [[templateTexturePipelineDesc copy] autorelease]; | |
| const int col = 0 ^ xorColor; | |
| setTxtUniforms(mtlc, col, encoder, | |
| renderOptions->interpolation, NO, [mtlc.composite getExtraAlpha], | |
| &renderOptions->srcFlags, 0); | |
| [encoder setFragmentBytes:&xorColor length:sizeof(xorColor) atIndex: 0]; | |
| BMTLSDOps *dstOps = MTLRenderQueue_GetCurrentDestination(); | |
| [encoder setFragmentTexture:dstOps->pTexture atIndex:1]; | |
| setTxtUniforms(mtlc, 0, encoder, | |
| renderOptions->interpolation, NO, [mtlc.composite getExtraAlpha], | |
| &renderOptions->srcFlags, 0); | |
| id <MTLRenderPipelineState> pipelineState = [pipelineStateStorage getPipelineState:rpDesc | |
| vertexShaderId:vertShader | |
| fragmentShaderId:fragShader | |
| composite:mtlc.composite | |
| renderOptions:renderOptions | |
| stencilNeeded:[mtlc.clip isShape]]; | |
| [encoder setRenderPipelineState:pipelineState]; | |
| } else { | |
| // Fallback to default pipeline state | |
| [MTLColorPaint setPipelineState:encoder | |
| context:mtlc | |
| renderOptions:renderOptions | |
| pipelineStateStorage:pipelineStateStorage | |
| rpDesc:[[templateRenderPipelineDesc copy] autorelease] | |
| vertShader:@"vert_col" | |
| fragShader:@"frag_col" | |
| color:0]; | |
| } | |
| } | |
| @end |