Example #1
0
        private void PaintSubTree(Event e, VisualElement root, Matrix4x4 offset, Rect currentGlobalClip)
        {
            if (root == null || root.panel != this)
            {
                return;
            }

            if ((root.pseudoStates & PseudoStates.Invisible) == PseudoStates.Invisible ||
                root.style.opacity.GetSpecifiedValueOrDefault(1.0f) < Mathf.Epsilon)
            {
                return;
            }

            // update clip
            if (root.clippingOptions != VisualElement.ClippingOptions.NoClipping)
            {
                var worldBound = 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.x + worldBound.width, currentGlobalClip.x + currentGlobalClip.width);
                float y1 = Mathf.Max(worldBound.y, currentGlobalClip.y);
                float y2 = Mathf.Min(worldBound.y + worldBound.height, currentGlobalClip.y + currentGlobalClip.height);

                // new global clip
                currentGlobalClip = new Rect(x1, y1, x2 - x1, y2 - y1);
            }
            else
            {
                //since our children are not clipped, there is no early out.
            }

            if (ShouldUsePixelCache(root))
            {
                // now actually paint the texture to previous group
                IStylePainter painter = stylePainter;

                // validate cache texture size first
                var worldBound = root.worldBound;

                int w = (int)GUIUtility.Internal_Roundf(worldBound.xMax) - (int)GUIUtility.Internal_Roundf(worldBound.xMin);
                int h = (int)GUIUtility.Internal_Roundf(worldBound.yMax) - (int)GUIUtility.Internal_Roundf(worldBound.yMin);

                // This needs to be consistent with RoundRect() in GUITexture.cpp. Otherwise, the texture may be stretched.
                int textureWidth  = (int)GUIUtility.Internal_Roundf(w * GUIUtility.pixelsPerPoint);
                int textureHeight = (int)GUIUtility.Internal_Roundf(h * GUIUtility.pixelsPerPoint);

                // 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) &&
                    (!keepPixelCacheOnWorldBoundChange || root.IsDirty(ChangeType.Repaint)))
                {
                    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 (root.IsDirty(ChangeType.Repaint) ||
                    root.renderData.pixelCache == null ||
                    !root.renderData.pixelCache.IsCreated())
                {
                    // Recreate as needed
                    if (cache == null)
                    {
                        root.renderData.pixelCache = cache = new RenderTexture(
                            textureWidth,
                            textureHeight,
                            32,         // depth
                            RenderTextureFormat.ARGB32,
                            RenderTextureReadWrite.sRGB);
                    }


                    bool hasRoundedBorderRects = (root.style.borderTopLeftRadius > 0 ||
                                                  root.style.borderTopRightRadius > 0 ||
                                                  root.style.borderBottomLeftRadius > 0 ||
                                                  root.style.borderBottomRightRadius > 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)
                        {
                            temporaryTexture = cache = RenderTexture.GetTemporary(textureWidth, textureHeight, 32,
                                                                                  RenderTextureFormat.ARGB32, RenderTextureReadWrite.sRGB);
                        }

                        // 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.
                        var childrenOffset = Matrix4x4.Translate(new Vector3(-GUIUtility.Internal_Roundf(worldBound.x), -GUIUtility.Internal_Roundf(worldBound.y), 0));

                        Matrix4x4 offsetWorldTransform = childrenOffset * root.worldTransform;

                        // reset clipping
                        var textureClip = new Rect(0, 0, w, h);
                        painter.currentTransform = offsetWorldTransform;

                        // Metal ignores the sRGBWrite flag and will always do linear to gamma conversions
                        // when writing to an sRGB buffer.  In this situation, we disable the manual sRGB
                        // conversion since it will be done automatically when writing to the framebuffer.
                        bool manualTex2SRGBEnabled = (SystemInfo.graphicsDeviceType != GraphicsDeviceType.Metal);
                        using (new GUIUtility.ManualTex2SRGBScope(manualTex2SRGBEnabled))
                            using (new GUIClip.ParentClipScope(painter.currentTransform, textureClip))
                            {
                                // Paint self
                                painter.currentWorldClip = textureClip;
                                root.DoRepaint(painter);
                                root.ClearDirty(ChangeType.Repaint);

                                PaintSubTreeChildren(e, root, childrenOffset, textureClip);
                            }

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

                            // Fix up transform for subtree to match texture upper left
                            painter.currentTransform = Matrix4x4.identity;

                            using (new GUIUtility.ManualTex2SRGBScope(manualTex2SRGBEnabled))
                                using (new GUIClip.ParentClipScope(painter.currentTransform, textureClip))
                                {
                                    GL.Clear(true, true, new Color(0, 0, 0, 0));

                                    var textureParams = painter.GetDefaultTextureParameters(root);
                                    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;
                                    painter.DrawTexture(textureParams);
                                }

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

                // now actually paint the texture to previous group
                painter.currentWorldClip = currentGlobalClip;
                painter.currentTransform = offset * root.worldTransform;

                var painterParams = new TextureStylePainterParameters
                {
                    rect                = root.alignedRect,
                    uv                  = new Rect(0, 0, 1, 1),
                    texture             = root.renderData.pixelCache,
                    color               = Color.white,
                    scaleMode           = ScaleMode.StretchToFill,
                    usePremultiplyAlpha = true
                };

                using (new GUIClip.ParentClipScope(painter.currentTransform, currentGlobalClip))
                {
                    painter.DrawTexture(painterParams);
                }
            }
            else
            {
                stylePainter.currentTransform = offset * root.worldTransform;

                using (new GUIClip.ParentClipScope(stylePainter.currentTransform, currentGlobalClip))
                {
                    stylePainter.currentWorldClip = currentGlobalClip;
                    stylePainter.mousePosition    = root.worldTransform.inverse.MultiplyPoint3x4(e.mousePosition);

                    stylePainter.opacity = root.style.opacity.GetSpecifiedValueOrDefault(1.0f);

                    root.DoRepaint(stylePainter);
                    stylePainter.opacity = 1.0f;
                    root.ClearDirty(ChangeType.Repaint);

                    PaintSubTreeChildren(e, root, offset, currentGlobalClip);
                }
            }
        }