LayerRenderConfPartition.java
package swingtree.style;
import org.jspecify.annotations.Nullable;
import swingtree.UI;
import java.util.Comparator;
/**
* This enum specifies multiple ways in which the {@link LayerRenderConf} of a {@link UI.Layer}
* can be "narrowed down" or "restricted" to a new {@link LayerRenderConf} instance.
* This narrowed down version of a layer render configuration can then be used to
* feed the different {@link LayerPartitionCache}s of a {@link StyleLayerCache}.
* The purpose of this is to create cached renderings from simplified style
* configurations in order to improve cache hit rate and robustness.<br>
* <br>
* <b>A style consists of, and is rendered in, the following order:</b>
* <ul>
* <li>1. Foundation</li>
* <li>2. Border</li>
* <li>3. Images</li>
* <li>4. Gradients</li>
* <li>5. Noises</li>
* <li>6. Shadows</li>
* <li>7. Text</li>
* <li>8. Painters</li>
* </ul>
* <i>
* Checkout {@link StyleRenderer#renderStyleOn(UI.Layer, LayerRenderConf, java.awt.Graphics2D)}
* to see the above list unfold...
* </i>
*/
enum LayerRenderConfPartition
{
/** The entire layer, used when there is nothing to split around. */
WHOLE,
/** Everything the renderer draws before the noises: fill, border, images and gradients. */
UNDER_NOISE,
/** The noises themselves, replayed on every paint rather than cached - which is cheap
* because the noise tile cache one level down keeps them in a size independent noise
* space. {@link StyleLayerCache} never hands this part to a {@link LayerPartitionCache}. */
NOISES,
/** Everything the renderer draws after the noises: shadows, texts and painters. */
OVER_NOISE,
/** Everything except the painters, which is the whole layer minus the one kind of style
* that can never be cached - see {@link #PAINTERS}. */
UNDER_PAINTERS,
/** The user painters, replayed straight onto the destination on every paint. Unlike
* {@link #NOISES} this is not cheap - it is arbitrary user code. */
PAINTERS;
/**
* Narrows the supplied render configuration down to just this part of the layer, by
* emptying out the style kinds which belong to the other parts. Handing the result to
* the style renderer draws this part alone.
*/
LayerRenderConf restrict( LayerRenderConf conf ) {
switch ( this ) {
case WHOLE:
return conf;
case UNDER_NOISE:
return conf.withLayer(
conf.layer()
.withNoises(StyleConfLayer._NO_NOISES)
.withShadows(StyleConfLayer._NO_SHADOWS)
.withTexts(StyleConfLayer._NO_TEXTS)
.withPainters(StyleConfLayer._NO_PAINTERS)
);
case NOISES:
return conf.withBaseColors(BaseColorConf.none())
.withLayer(
conf.layer()
.withImages(StyleConfLayer._NO_IMAGES)
.withGradients(StyleConfLayer._NO_GRADIENTS)
.withShadows(StyleConfLayer._NO_SHADOWS)
.withTexts(StyleConfLayer._NO_TEXTS)
.withPainters(StyleConfLayer._NO_PAINTERS)
);
case OVER_NOISE:
return conf.withBaseColors(BaseColorConf.none())
.withLayer(
conf.layer()
.withImages(StyleConfLayer._NO_IMAGES)
.withGradients(StyleConfLayer._NO_GRADIENTS)
.withNoises(StyleConfLayer._NO_NOISES)
);
case UNDER_PAINTERS:
return conf.withLayer( conf.layer().withPainters(_paintersBeforeTheFirstUncacheableOne(conf)) );
case PAINTERS:
return conf.withBaseColors(BaseColorConf.none())
.withLayer(
conf.layer()
.withImages(StyleConfLayer._NO_IMAGES)
.withGradients(StyleConfLayer._NO_GRADIENTS)
.withNoises(StyleConfLayer._NO_NOISES)
.withShadows(StyleConfLayer._NO_SHADOWS)
.withTexts(StyleConfLayer._NO_TEXTS)
.withPainters(_paintersFromTheFirstUncacheableOne(conf))
);
}
throw new IllegalStateException("Unknown style layer part: " + this);
}
private static NamedConfigs<PainterConf> _paintersBeforeTheFirstUncacheableOne( LayerRenderConf conf ) {
return _paintersSplitAtTheFirstUncacheableOne(conf, true);
}
private static NamedConfigs<PainterConf> _paintersFromTheFirstUncacheableOne( LayerRenderConf conf ) {
return _paintersSplitAtTheFirstUncacheableOne(conf, false);
}
private static NamedConfigs<PainterConf> _paintersSplitAtTheFirstUncacheableOne(
LayerRenderConf conf, boolean wantThoseBefore
) {
final NamedConfigs<PainterConf> painters = conf.layer().painters();
final @Nullable String cut = _firstUncacheablePainterName(painters);
if ( cut == null )
return ( wantThoseBefore ? painters : StyleConfLayer._NO_PAINTERS ); // All of them are cacheable.
return painters.namedStylesStream()
.filter( named -> ( named.name().compareTo(cut) < 0 ) == wantThoseBefore )
.filter( named -> !named.style().equals(PainterConf.none()) )
.reduce( StyleConfLayer._NO_PAINTERS,
( keptSoFar, named ) -> keptSoFar.withNamedStyle(named.name(), named.style()),
( a, b ) -> a ); // Never called: the stream is sequential.
}
private static @Nullable String _firstUncacheablePainterName( NamedConfigs<PainterConf> painters ) {
return painters.namedStylesStream()
.filter( named -> !named.style().painter().canBeCached() )
.map( NamedConf::name )
.min( Comparator.naturalOrder() )
.orElse(null);
}
}