public void DrawTexture(TextureStylePainterParameters painterParams)
        {
            Rect      screenRect          = painterParams.rect;
            Rect      sourceRect          = painterParams.uv != Rect.zero ? painterParams.uv : new Rect(0, 0, 1, 1);
            Texture   texture             = painterParams.texture;
            Color     color               = painterParams.color * UIElementsUtility.editorPlayModeTintColor;
            ScaleMode scaleMode           = painterParams.scaleMode;
            int       sliceLeft           = painterParams.sliceLeft;
            int       sliceTop            = painterParams.sliceTop;
            int       sliceRight          = painterParams.sliceRight;
            int       sliceBottom         = painterParams.sliceBottom;
            bool      usePremultiplyAlpha = painterParams.usePremultiplyAlpha;

            Rect textureRect = screenRect;

            // Comparing aspects ratio is error-prone because the <c>screenRect</c> may end up being scaled by the
            // transform and the corners will end up being pixel aligned, possibly resulting in blurriness.
            float srcAspect  = (texture.width * sourceRect.width) / (texture.height * sourceRect.height);
            float destAspect = screenRect.width / screenRect.height;

            switch (scaleMode)
            {
            case ScaleMode.StretchToFill:
                break;

            case ScaleMode.ScaleAndCrop:
                if (destAspect > srcAspect)
                {
                    float stretch = sourceRect.height * (srcAspect / destAspect);
                    float crop    = (sourceRect.height - stretch) * 0.5f;
                    sourceRect = new Rect(sourceRect.x, sourceRect.y + crop, sourceRect.width, stretch);
                }
                else
                {
                    float stretch = sourceRect.width * (destAspect / srcAspect);
                    float crop    = (sourceRect.width - stretch) * 0.5f;
                    sourceRect = new Rect(sourceRect.x + crop, sourceRect.y, stretch, sourceRect.height);
                }
                break;

            case ScaleMode.ScaleToFit:
                if (destAspect > srcAspect)
                {
                    float stretch = srcAspect / destAspect;
                    textureRect = new Rect(screenRect.xMin + screenRect.width * (1.0f - stretch) * .5f, screenRect.yMin, stretch * screenRect.width, screenRect.height);
                }
                else
                {
                    float stretch = destAspect / srcAspect;
                    textureRect = new Rect(screenRect.xMin, screenRect.yMin + screenRect.height * (1.0f - stretch) * .5f, screenRect.width, stretch * screenRect.height);
                }
                break;
            }

            var borderWidths   = painterParams.border.GetWidths();
            var borderRadiuses = painterParams.border.GetRadiuses();

            DrawTexture(textureRect, texture, sourceRect, color * m_OpacityColor, borderWidths, borderRadiuses, sliceLeft, sliceTop, sliceRight, sliceBottom, usePremultiplyAlpha);
        }
        public static TextureStylePainterParameters GetDefault(VisualElement ve)
        {
            ComputedStyle style         = ve.computedStyle;
            var           painterParams = new TextureStylePainterParameters
            {
                rect        = GUIUtility.AlignRectToDevice(ve.rect),
                uv          = new Rect(0, 0, 1, 1),
                color       = (Color)Color.white,
                texture     = style.backgroundImage.value.texture,
                scaleMode   = style.unityBackgroundScaleMode.value,
                sliceLeft   = style.unitySliceLeft.value,
                sliceTop    = style.unitySliceTop.value,
                sliceRight  = style.unitySliceRight.value,
                sliceBottom = style.unitySliceBottom.value
            };

            BorderParameters.SetFromStyle(ref painterParams.border, style);
            return(painterParams);
        }
Example #3
0
        internal override void DoRepaint(IStylePainter painter)
        {
            Texture current = image;

            if (current == null)
            {
                return;
            }

            var painterParams = new TextureStylePainterParameters
            {
                rect      = contentRect,
                uv        = uv,
                texture   = current,
                color     = tintColor,
                scaleMode = scaleMode
            };
            var stylePainter = (IStylePainterInternal)painter;

            stylePainter.DrawTexture(painterParams);
        }
        public void DrawBackground()
        {
            ComputedStyle style = currentElement.computedStyle;

            if (style.backgroundColor != Color.clear)
            {
                var painterParams = RectStylePainterParameters.GetDefault(currentElement);
                painterParams.border.SetWidth(0.0f);
                DrawRect(painterParams);
            }
            if (style.backgroundImage.value.texture != null)
            {
                var painterParams = TextureStylePainterParameters.GetDefault(currentElement);
                if (style.unityBackgroundImageTintColor != Color.clear)
                {
                    painterParams.color = style.unityBackgroundImageTintColor.value;
                }
                painterParams.border.SetWidth(0.0f);
                DrawTexture(painterParams);
            }
        }
Example #5
0
 internal static void MakeTexture(TextureStylePainterParameters textureParams, float posZ, VertexFlags vertexFlags, AllocMeshData meshAlloc)
 {
     if (textureParams.sliceLeft <= Mathf.Epsilon &&
         textureParams.sliceTop <= Mathf.Epsilon &&
         textureParams.sliceRight <= Mathf.Epsilon &&
         textureParams.sliceBottom <= Mathf.Epsilon)
     {
         if (IsSimpleRect(textureParams.border))
         {
             MakeQuad(textureParams.rect, textureParams.uv, textureParams.color, posZ, vertexFlags, meshAlloc);
         }
         else
         {
             UIRTessellation.TessellateBorderedRect(textureParams.rect, textureParams.uv, textureParams.color, textureParams.border, posZ, vertexFlags, meshAlloc);
         }
     }
     else
     {
         MakeSlicedQuad(textureParams, posZ, vertexFlags, meshAlloc);
     }
 }
Example #6
0
        private static void MakeSlicedQuad(TextureStylePainterParameters texParams, float posZ, VertexFlags vertexFlags, AllocMeshData meshAlloc)
        {
            var texture = texParams.texture;

            if (texture == null)
            {
                // Early exit without slicing.
                MakeQuad(texParams.rect, texParams.uv, texParams.color, posZ, vertexFlags, meshAlloc);
                return;
            }

            var mesh = meshAlloc(16, 9 * 6);

            float pixelsPerPoint = 1;
            var   texture2D      = texParams.texture as Texture2D;

            if (texture2D != null)
            {
                pixelsPerPoint = texture2D.pixelsPerPoint;
            }

            // The following offsets are in texels (not normalized).
            float uvSliceLeft   = texParams.sliceLeft * pixelsPerPoint;
            float uvSliceTop    = texParams.sliceTop * pixelsPerPoint;
            float uvSliceRight  = texParams.sliceRight * pixelsPerPoint;
            float uvSliceBottom = texParams.sliceBottom * pixelsPerPoint;

            // When an atlas is used, relative coordinates must not be used.
            bool  isAtlassed  = vertexFlags == VertexFlags.IsTextured;
            float uConversion = isAtlassed ? 1 : 1f / texture.width;
            float vConversion = isAtlassed ? 1 : 1f / texture.height;

            k_TexCoordSlicesX[0] = texParams.uv.min.x;
            k_TexCoordSlicesX[1] = texParams.uv.min.x + uvSliceLeft * uConversion;
            k_TexCoordSlicesX[2] = texParams.uv.max.x - uvSliceRight * uConversion;
            k_TexCoordSlicesX[3] = texParams.uv.max.x;

            k_TexCoordSlicesY[0] = texParams.uv.max.y;
            k_TexCoordSlicesY[1] = texParams.uv.max.y - uvSliceBottom * vConversion;
            k_TexCoordSlicesY[2] = texParams.uv.min.y + uvSliceTop * vConversion;
            k_TexCoordSlicesY[3] = texParams.uv.min.y;

            k_PositionSlicesX[0] = texParams.rect.x;
            k_PositionSlicesX[1] = texParams.rect.x + texParams.sliceLeft;
            k_PositionSlicesX[2] = texParams.rect.xMax - texParams.sliceRight;
            k_PositionSlicesX[3] = texParams.rect.xMax;

            k_PositionSlicesY[0] = texParams.rect.yMax;
            k_PositionSlicesY[1] = texParams.rect.yMax - texParams.sliceBottom;
            k_PositionSlicesY[2] = texParams.rect.y + texParams.sliceTop;
            k_PositionSlicesY[3] = texParams.rect.y;

            for (int i = 0; i < 16; ++i)
            {
                int x = i % 4;
                int y = i / 4;
                mesh.vertices[i] = new Vertex()
                {
                    position = new Vector3(k_PositionSlicesX[x], k_PositionSlicesY[y], posZ),
                    uv       = new Vector2(k_TexCoordSlicesX[x], texParams.uv.min.y + texParams.uv.max.y - k_TexCoordSlicesY[y]),
                    tint     = texParams.color,
                    flags    = (float)vertexFlags
                };
            }
            mesh.indices.CopyFrom(slicedQuadIndices);
        }
        private void PaintSubTree(VisualElement root, Matrix4x4 offset, bool shouldClip, bool shouldCache, Rect currentGlobalClip)
        {
            if (root == null || root.panel != panel)
            {
                return;
            }

            if (root.visible == false ||
                root.resolvedStyle.opacity < Mathf.Epsilon ||
                root.resolvedStyle.display == DisplayStyle.None)
            {
                return;
            }

            // update clip
            if (root.ShouldClip())
            {
                var worldBound = VisualElement.ComputeAAAlignedBound(root.rect, offset * root.worldTransform);
                // are we and our children clipped?
                if (!worldBound.Overlaps(currentGlobalClip))
                {
                    return;
                }

                float x1 = Mathf.Max(worldBound.x, currentGlobalClip.x);
                float x2 = Mathf.Min(worldBound.xMax, currentGlobalClip.xMax);
                float y1 = Mathf.Max(worldBound.y, currentGlobalClip.y);
                float y2 = Mathf.Min(worldBound.yMax, currentGlobalClip.yMax);

                // new global clip and hierarchical clip space option.
                currentGlobalClip = new Rect(x1, y1, x2 - x1, y2 - y1);
                shouldClip        = true;
                shouldCache       = root.cacheAsBitmap;
            }

            var prevElement = m_StylePainter.currentElement;

            m_StylePainter.currentElement = root;

            var repaintData = panel.repaintData;

            if (ShouldUsePixelCache(root))
            {
                // now actually paint the texture to previous group
                // validate cache texture size first
                var worldBound = root.worldBound;

                Rect alignedRect;
                int  textureWidth, textureHeight;
                repaintData.currentWorldClip = currentGlobalClip;
                repaintData.currentOffset    = offset;
                using (new GUIClip.ParentClipScope(offset * root.worldTransform, currentGlobalClip))
                {
                    alignedRect = GUIUtility.AlignRectToDevice(root.rect, out textureWidth, out textureHeight);
                }

                // Prevent the texture size from going empty, which may occur if the element has a sub-pixel size
                textureWidth  = Math.Max(textureWidth, 1);
                textureHeight = Math.Max(textureHeight, 1);

                var cache = root.renderData.pixelCache;

                if (cache != null &&
                    (cache.width != textureWidth || cache.height != textureHeight) &&
                    (!panel.keepPixelCacheOnWorldBoundChange || m_RepaintList.Contains(root)))
                {
                    Object.DestroyImmediate(cache);
                    cache = root.renderData.pixelCache = null;
                }

                // if the child node world transforms are not up to date due to changes below the pixel cache this is fine.
                if (m_RepaintList.Contains(root) ||
                    root.renderData.pixelCache == null ||
                    !root.renderData.pixelCache.IsCreated())
                {
                    // Recreate as needed
                    if (cache == null)
                    {
                        root.renderData.pixelCache = cache = new RenderTexture(
                            textureWidth,
                            textureHeight,
                            32,         // depth
                            GraphicsFormat.R8G8B8A8_UNorm);
                    }

                    bool hasRoundedBorderRects = (root.computedStyle.borderTopLeftRadius.value.value > 0 ||
                                                  root.computedStyle.borderTopRightRadius.value.value > 0 ||
                                                  root.computedStyle.borderBottomLeftRadius.value.value > 0 ||
                                                  root.computedStyle.borderBottomRightRadius.value.value > 0);

                    RenderTexture temporaryTexture = null;
                    var           old = RenderTexture.active;

                    try
                    {
                        // We first render to a temp texture, then blit the result into the result pixelCache again to mask the rounder corners
                        if (hasRoundedBorderRects)
                        {
                            // This should be an sRGB texture to conform with editor texture guidelines, however
                            // converting the cache to sRGB requires a shader to do it, and this whole pixel cache
                            // thing is slated to go away, so we take a short-cut here and use a linear texture
                            // along with the use of manualTex2SRGBEnabled to get the correct results.
                            temporaryTexture = cache = RenderTexture.GetTemporary(textureWidth, textureHeight, 32, GraphicsFormat.R8G8B8A8_UNorm);
                        }

                        // render the texture again to clip the round rect borders
                        RenderTexture.active = cache;

                        GL.Clear(true, true, new Color(0, 0, 0, 0));

                        // Calculate the offset required to translate the origin of the rect to the upper left corner
                        // of the pixel cache. We need to round because the rect will be rounded when rendered.
                        Rect worldAlignedRect = root.LocalToWorld(alignedRect);
                        var  childrenOffset   = Matrix4x4.Translate(new Vector3(-worldAlignedRect.x, -worldAlignedRect.y, 0));

                        Matrix4x4 offsetWorldTransform = childrenOffset * root.worldTransform;

                        // reset clipping
                        var textureClip = new Rect(0, 0, worldAlignedRect.width, worldAlignedRect.height);
                        repaintData.currentOffset = childrenOffset;

                        using (new GUIClip.ParentClipScope(offsetWorldTransform, textureClip))
                        {
                            // Paint self
                            repaintData.currentWorldClip = textureClip;

                            m_StylePainter.opacity = root.resolvedStyle.opacity;
                            root.Repaint(m_StylePainter);
                            m_StylePainter.opacity = 1.0f;

                            PaintSubTreeChildren(root, childrenOffset, shouldClip, shouldCache, textureClip);
                        }

                        if (hasRoundedBorderRects)
                        {
                            RenderTexture.active = root.renderData.pixelCache;

                            bool oldManualTex2SRGBEnabled = GUIUtility.manualTex2SRGBEnabled;
                            GUIUtility.manualTex2SRGBEnabled = false;
                            using (new GUIClip.ParentClipScope(Matrix4x4.identity, textureClip))
                            {
                                GL.Clear(true, true, new Color(0, 0, 0, 0));

                                var textureParams = TextureStylePainterParameters.GetDefault(root);
                                textureParams.color     = Color.white;
                                textureParams.texture   = cache;
                                textureParams.scaleMode = ScaleMode.StretchToFill;
                                textureParams.rect      = textureClip;

                                textureParams.border.SetWidth(0.0f);

                                // The rect of the temporary texture implicitly carries the scale factor of the
                                // transform. Since we are blitting with an identity matrix, we need to scale the
                                // radius manually.
                                // We assume uniform positive scaling without rotations.
                                Vector4 toScale     = new Vector4(1, 0, 0, 0);
                                Vector4 scaled      = offsetWorldTransform * toScale;
                                float   radiusScale = scaled.x;
                                textureParams.border.SetRadius(
                                    textureParams.border.topLeftRadius * radiusScale,
                                    textureParams.border.topRightRadius * radiusScale,
                                    textureParams.border.bottomRightRadius * radiusScale,
                                    textureParams.border.bottomLeftRadius * radiusScale);

                                // No border is drawn but the rounded corners are clipped properly.
                                // Use premultiply alpha to avoid blending again.
                                textureParams.usePremultiplyAlpha = true;
                                m_StylePainter.DrawTexture(textureParams);
                            }

                            // Redraw the border (border was already drawn in first root.DoRepaint call).
                            using (new GUIClip.ParentClipScope(offsetWorldTransform, textureClip))
                            {
                                m_StylePainter.DrawBorder();
                            }
                            GUIUtility.manualTex2SRGBEnabled = oldManualTex2SRGBEnabled;
                        }
                    }
                    finally
                    {
                        cache = null;
                        if (temporaryTexture != null)
                        {
                            RenderTexture.ReleaseTemporary(temporaryTexture);
                        }
                        RenderTexture.active = old;
                    }
                }

                // now actually paint the texture to previous group
                repaintData.currentWorldClip = currentGlobalClip;
                repaintData.currentOffset    = offset;

                bool oldManualTex2SRGBEnabled2 = GUIUtility.manualTex2SRGBEnabled;
                GUIUtility.manualTex2SRGBEnabled = false;
                using (new GUIClip.ParentClipScope(offset * root.worldTransform, currentGlobalClip))
                {
                    var painterParams = new TextureStylePainterParameters
                    {
                        rect                = GUIUtility.AlignRectToDevice(root.rect),
                        uv                  = new Rect(0, 0, 1, 1),
                        texture             = root.renderData.pixelCache,
                        color               = Color.white,
                        scaleMode           = ScaleMode.StretchToFill,
                        usePremultiplyAlpha = true
                    };

                    // We must not reapply the editor Play Mode Tint if it was already applied !
                    var playModeTint = UIElementsUtility.editorPlayModeTintColor;
                    UIElementsUtility.editorPlayModeTintColor = Color.white;

                    m_StylePainter.DrawTexture(painterParams);
                    UIElementsUtility.editorPlayModeTintColor = playModeTint;
                }
                GUIUtility.manualTex2SRGBEnabled = oldManualTex2SRGBEnabled2;
            }
            else
            {
                repaintData.currentOffset = offset;

                using (new GUIClip.ParentClipScope(offset * root.worldTransform, currentGlobalClip))
                {
                    repaintData.currentWorldClip = currentGlobalClip;
                    repaintData.mousePosition    = VisualElement.MultiplyMatrix44Point2(root.worldTransformInverse, repaintData.repaintEvent.mousePosition);

                    m_StylePainter.opacity = root.resolvedStyle.opacity;
                    root.Repaint(m_StylePainter);
                    m_StylePainter.opacity = 1.0f;


                    PaintSubTreeChildren(root, offset, shouldClip, shouldCache, currentGlobalClip);
                }
            }

            m_StylePainter.currentElement = prevElement;
        }