diff --git a/demos/theming-demo/src/main/java/org/pushingpixels/radiance/demo/theming/main/palette/ControlStates.java b/demos/theming-demo/src/main/java/org/pushingpixels/radiance/demo/theming/main/palette/ControlStates.java index b1ed6f8eb..96528a243 100644 --- a/demos/theming-demo/src/main/java/org/pushingpixels/radiance/demo/theming/main/palette/ControlStates.java +++ b/demos/theming-demo/src/main/java/org/pushingpixels/radiance/demo/theming/main/palette/ControlStates.java @@ -115,6 +115,11 @@ public SampleSkin(RadianceColorScheme accentScheme) { this.registerDecorationAreaColorScheme(lightColorScheme, RadianceThemingSlices.DecorationAreaType.NONE); + this.registerAsDecorationArea(lightColorScheme.getTonalContainerTokens(), + RadianceThemingSlices.DecorationAreaType.PRIMARY_TITLE_PANE, + RadianceThemingSlices.DecorationAreaType.SECONDARY_TITLE_PANE, + RadianceThemingSlices.DecorationAreaType.HEADER); + this.buttonShaper = new ClassicButtonShaper(); this.fillPainter = new ClassicTonalFillPainter(); this.borderPainter = new ClassicTonalBorderPainter(); diff --git a/theming/src/main/java/org/pushingpixels/radiance/theming/api/RadianceSkin.java b/theming/src/main/java/org/pushingpixels/radiance/theming/api/RadianceSkin.java index cce24b6b1..492d8b547 100644 --- a/theming/src/main/java/org/pushingpixels/radiance/theming/api/RadianceSkin.java +++ b/theming/src/main/java/org/pushingpixels/radiance/theming/api/RadianceSkin.java @@ -201,6 +201,8 @@ public RadianceColorScheme getWindowChromeAccent() { */ private Map backgroundColorSchemeMap; + private Map tonalBackgroundRenderColorTokensMap; + /** * Maps decoration area type to the registered overlay painters. Each * decoration area type can have more than one overlay painter. @@ -263,6 +265,7 @@ protected RadianceSkin() { this.colorSchemeBundleMap = new HashMap<>(); this.tonalColorSchemeMap = new HashMap<>(); this.backgroundColorSchemeMap = new HashMap<>(); + this.tonalBackgroundRenderColorTokensMap = new HashMap<>(); this.overlayPaintersMap = new HashMap<>(); this.colorOverlayMap = new HashMap<>(); @@ -552,23 +555,23 @@ public void registerDecorationAreaSchemeBundle( this.statesWithAlpha.addAll(bundle.getStatesWithAlpha()); } - /** - * Registers the specified color scheme bundle and background color scheme - * to be used on controls in decoration areas. - * - * @param bundle The color scheme bundle to use on controls in decoration - * areas. - * @param areaTypes Enumerates the area types that are affected by the parameters. - */ - public void registerDecorationAreaColorScheme(RadianceColorScheme2 colorScheme, + public void registerDecorationAreaColorScheme( + RadianceColorScheme2 colorScheme, + ContainerRenderColorTokens backgroundRenderColorTokens, RadianceThemingSlices.DecorationAreaType... areaTypes) { if (colorScheme == null) { return; } + if (backgroundRenderColorTokens == null) { + throw new IllegalArgumentException( + "Cannot pass null background color render tokens"); + } + for (RadianceThemingSlices.DecorationAreaType areaType : areaTypes) { this.decoratedAreaSet.add(areaType); this.tonalColorSchemeMap.put(areaType, colorScheme); + this.tonalBackgroundRenderColorTokensMap.put(areaType, backgroundRenderColorTokens); } } @@ -586,6 +589,12 @@ public void registerDecorationAreaSchemeBundle( areaTypes); } + public void registerDecorationAreaColorScheme( + RadianceColorScheme2 colorScheme, RadianceThemingSlices.DecorationAreaType... areaTypes) { + this.registerDecorationAreaColorScheme(colorScheme, colorScheme.getMutedContainerTokens(), + areaTypes); + } + /** * Registers the specified background color scheme to be used on controls in * decoration areas. @@ -608,6 +617,18 @@ public void registerAsDecorationArea(RadianceColorScheme backgroundColorScheme, } } + public void registerAsDecorationArea(ContainerRenderColorTokens backgroundRenderColorTokens, + RadianceThemingSlices.DecorationAreaType... areaTypes) { + if (backgroundRenderColorTokens == null) { + throw new IllegalArgumentException( + "Cannot pass null background color tokens"); + } + for (RadianceThemingSlices.DecorationAreaType areaType : areaTypes) { + this.decoratedAreaSet.add(areaType); + this.tonalBackgroundRenderColorTokensMap.put(areaType, backgroundRenderColorTokens); + } + } + /** * Registers the specified background color scheme and a color scheme bundle overlay to be used * on controls in decoration areas. @@ -937,6 +958,24 @@ public final RadianceColorScheme getBackgroundColorScheme(RadianceThemingSlices. return this.backgroundColorSchemeMap.get(RadianceThemingSlices.DecorationAreaType.NONE); } + public final ContainerRenderColorTokens getBackgroundRenderColorTokens( + RadianceThemingSlices.DecorationAreaType decorationAreaType) { + // 1 - check the registered background scheme for this specific area type. + if (this.tonalBackgroundRenderColorTokensMap.containsKey(decorationAreaType)) { + return this.tonalBackgroundRenderColorTokensMap.get(decorationAreaType); + } + // 2 - check the registered scheme bundle for this specific area type. + if (this.tonalColorSchemeMap.containsKey(decorationAreaType)) { + ContainerRenderColorTokens registered = this.tonalColorSchemeMap.get( + decorationAreaType).getMutedContainerTokens(); + if (registered != null) { + return registered; + } + } + // 3 - return the background scheme for the default area type + return this.tonalBackgroundRenderColorTokensMap.get(RadianceThemingSlices.DecorationAreaType.NONE); + } + public void setOverlayColor(Color color, RadianceThemingSlices.ColorOverlayType colorOverlayType, RadianceThemingSlices.DecorationAreaType decorationAreaType, ComponentState... componentStates) { if (!this.colorOverlayMap.containsKey(colorOverlayType)) { diff --git a/theming/src/main/java/org/pushingpixels/radiance/theming/api/painter/decoration/ArcDecorationPainter.java b/theming/src/main/java/org/pushingpixels/radiance/theming/api/painter/decoration/ArcDecorationPainter.java index d3813c746..e95a3701c 100644 --- a/theming/src/main/java/org/pushingpixels/radiance/theming/api/painter/decoration/ArcDecorationPainter.java +++ b/theming/src/main/java/org/pushingpixels/radiance/theming/api/painter/decoration/ArcDecorationPainter.java @@ -29,9 +29,11 @@ */ package org.pushingpixels.radiance.theming.api.painter.decoration; -import org.pushingpixels.radiance.theming.api.RadianceThemingSlices; import org.pushingpixels.radiance.theming.api.RadianceSkin; +import org.pushingpixels.radiance.theming.api.RadianceThemingSlices; import org.pushingpixels.radiance.theming.api.colorscheme.RadianceColorScheme; +import org.pushingpixels.radiance.theming.api.palette.ContainerRenderColorTokens; +import org.pushingpixels.radiance.theming.api.palette.TonalSkin; import org.pushingpixels.radiance.theming.internal.utils.RadianceCoreUtilities; import javax.swing.*; @@ -58,14 +60,26 @@ public String getDisplayName() { @Override public void paintDecorationArea(Graphics2D graphics, Component comp, - RadianceThemingSlices.DecorationAreaType decorationAreaType, int width, int height, RadianceSkin skin) { - if ((decorationAreaType == RadianceThemingSlices.DecorationAreaType.PRIMARY_TITLE_PANE) - || (decorationAreaType == RadianceThemingSlices.DecorationAreaType.SECONDARY_TITLE_PANE)) { - this.paintTitleBackground(graphics, comp, width, height, - skin.getBackgroundColorScheme(decorationAreaType)); + RadianceThemingSlices.DecorationAreaType decorationAreaType, int width, int height, + RadianceSkin skin) { + if (skin instanceof TonalSkin) { + if ((decorationAreaType == RadianceThemingSlices.DecorationAreaType.PRIMARY_TITLE_PANE) || + (decorationAreaType == RadianceThemingSlices.DecorationAreaType.SECONDARY_TITLE_PANE)) { + this.paintTitleBackground(graphics, comp, width, height, + skin.getBackgroundRenderColorTokens(decorationAreaType)); + } else { + this.paintExtraBackground(graphics, RadianceCoreUtilities.getHeaderParent(comp), + comp, width, height, skin.getBackgroundRenderColorTokens(decorationAreaType)); + } } else { - this.paintExtraBackground(graphics, RadianceCoreUtilities.getHeaderParent(comp), comp, - width, height, skin.getBackgroundColorScheme(decorationAreaType)); + if ((decorationAreaType == RadianceThemingSlices.DecorationAreaType.PRIMARY_TITLE_PANE) || + (decorationAreaType == RadianceThemingSlices.DecorationAreaType.SECONDARY_TITLE_PANE)) { + this.paintTitleBackground(graphics, comp, width, height, + skin.getBackgroundColorScheme(decorationAreaType)); + } else { + this.paintExtraBackground(graphics, RadianceCoreUtilities.getHeaderParent(comp), + comp, width, height, skin.getBackgroundColorScheme(decorationAreaType)); + } } } @@ -100,7 +114,7 @@ private void paintTitleBackground(Graphics2D original, Component comp, int width g2d.setClip(clipTop); LinearGradientPaint gradientTop = new LinearGradientPaint(0, 0, width, 0, new float[] { 0.0f, 0.5f, 1.0f }, new Color[] { scheme.getLightColor(), - scheme.getUltraLightColor(), scheme.getLightColor() }, + scheme.getUltraLightColor(), scheme.getLightColor() }, CycleMethod.REPEAT); g2d.setPaint(gradientTop); g2d.fillRect(0, 0, width, height); @@ -131,6 +145,59 @@ private void paintTitleBackground(Graphics2D original, Component comp, int width g2d.dispose(); } + private void paintTitleBackground(Graphics2D original, Component comp, int width, int height, + ContainerRenderColorTokens renderColorTokens) { + // Create a new Graphics2D object so that we can apply clipping to it without having + // to reset the state after we're done + Graphics2D g2d = (Graphics2D) original.create(); + + // Fill background + GeneralPath clipTop = new GeneralPath(); + clipTop.moveTo(0, 0); + clipTop.lineTo(width, 0); + clipTop.lineTo(width, height / 2); + clipTop.quadTo(width / 2, height / 4, 0, height / 2); + clipTop.lineTo(0, 0); + + g2d.setClip(clipTop); + LinearGradientPaint gradientTop = new LinearGradientPaint(0, 0, width, 0, + new float[] { 0.0f, 0.5f, 1.0f }, new Color[] { + renderColorTokens.getContainerColorTokens().getContainer(), + renderColorTokens.getContainerColorTokens().getContainerLowest(), + renderColorTokens.getContainerColorTokens().getContainer() }, + CycleMethod.REPEAT); + g2d.setPaint(gradientTop); + g2d.fillRect(0, 0, width, height); + + GeneralPath clipBottom = new GeneralPath(); + clipBottom.moveTo(0, height); + clipBottom.lineTo(width, height); + clipBottom.lineTo(width, height / 2); + clipBottom.quadTo(width / 2, height / 4, 0, height / 2); + clipBottom.lineTo(0, height); + + g2d.setClip(clipBottom); + LinearGradientPaint gradientBottom = new LinearGradientPaint(0, 0, width, 0, + new float[] { 0.0f, 0.5f, 1.0f }, + new Color[] { + renderColorTokens.getContainerColorTokens().getContainerHighest(), + renderColorTokens.getContainerColorTokens().getContainer(), + renderColorTokens.getContainerColorTokens().getContainerHighest() }, + CycleMethod.REPEAT); + g2d.setPaint(gradientBottom); + g2d.fillRect(0, 0, width, height); + + GeneralPath mid = new GeneralPath(); + mid.moveTo(width, height / 2); + mid.quadTo(width / 2, height / 4, 0, height / 2); + g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, + RenderingHints.VALUE_ANTIALIAS_ON); + g2d.setClip(new Rectangle(0, 0, width, height)); + g2d.draw(mid); + + g2d.dispose(); + } + /** * Paints the background of non-title decoration areas. * @@ -162,7 +229,34 @@ private void paintExtraBackground(Graphics2D graphics, Container parent, Compone LinearGradientPaint gradientBottom = new LinearGradientPaint(-offset.x, 0, -offset.x + pWidth, 0, new float[] { 0.0f, 0.5f, 1.0f }, new Color[] { scheme.getMidColor(), scheme.getLightColor(), - scheme.getMidColor() }, + scheme.getMidColor() }, + CycleMethod.REPEAT); + Graphics2D g2d = (Graphics2D) graphics.create(); + g2d.setPaint(gradientBottom); + g2d.fillRect(-offset.x, 0, pWidth, height); + g2d.dispose(); + } + } + + private void paintExtraBackground(Graphics2D graphics, Container parent, Component comp, + int width, int height, ContainerRenderColorTokens renderColorTokens) { + Point offset = RadianceCoreUtilities.getOffsetInRootPaneCoords(comp); + JRootPane rootPane = SwingUtilities.getRootPane(parent); + // fix for bug 234 - Window doesn't have a root pane. + JLayeredPane layeredPane = rootPane.getLayeredPane(); + Insets layeredPaneInsets = (layeredPane != null) ? layeredPane.getInsets() : null; + + int pWidth = (layeredPane == null) ? parent.getWidth() + : layeredPane.getWidth() - layeredPaneInsets.left - layeredPaneInsets.right; + + if (pWidth != 0) { + LinearGradientPaint gradientBottom = new LinearGradientPaint(-offset.x, 0, + -offset.x + pWidth, 0, new float[] { 0.0f, 0.5f, 1.0f }, + new Color[] { + renderColorTokens.getContainerColorTokens().getContainerHighest(), + renderColorTokens.getContainerColorTokens().getContainer(), + renderColorTokens.getContainerColorTokens().getContainerHighest() + }, CycleMethod.REPEAT); Graphics2D g2d = (Graphics2D) graphics.create(); g2d.setPaint(gradientBottom); @@ -196,4 +290,34 @@ public void paintDecorationArea(Graphics2D graphics, Component comp, RadianceThe g2d.dispose(); } } + + @Override + public void paintDecorationArea(Graphics2D graphics, Component comp, + RadianceThemingSlices.DecorationAreaType decorationAreaType, Shape contour, + ContainerRenderColorTokens renderColorTokens) { + Component parent = RadianceCoreUtilities.getHeaderParent(comp); + Point offset = RadianceCoreUtilities.getOffsetInRootPaneCoords(comp); + JRootPane rootPane = SwingUtilities.getRootPane(parent); + // fix for bug 234 - Window doesn't have a root pane. + JLayeredPane layeredPane = rootPane.getLayeredPane(); + Insets layeredPaneInsets = (layeredPane != null) ? layeredPane.getInsets() : null; + + int pWidth = (layeredPane == null) ? parent.getWidth() + : layeredPane.getWidth() - layeredPaneInsets.left - layeredPaneInsets.right; + + if (pWidth != 0) { + LinearGradientPaint gradientBottom = new LinearGradientPaint(-offset.x, 0, + -offset.x + pWidth, 0, new float[] { 0.0f, 0.5f, 1.0f }, + new Color[] { + renderColorTokens.getContainerColorTokens().getContainerHighest(), + renderColorTokens.getContainerColorTokens().getContainer(), + renderColorTokens.getContainerColorTokens().getContainerHighest() + }, + CycleMethod.REPEAT); + Graphics2D g2d = (Graphics2D) graphics.create(); + g2d.setPaint(gradientBottom); + g2d.fill(contour); + g2d.dispose(); + } + } } diff --git a/theming/src/main/java/org/pushingpixels/radiance/theming/api/painter/decoration/ImageWrapperDecorationPainter.java b/theming/src/main/java/org/pushingpixels/radiance/theming/api/painter/decoration/ImageWrapperDecorationPainter.java index 8ef469675..09984b959 100644 --- a/theming/src/main/java/org/pushingpixels/radiance/theming/api/painter/decoration/ImageWrapperDecorationPainter.java +++ b/theming/src/main/java/org/pushingpixels/radiance/theming/api/painter/decoration/ImageWrapperDecorationPainter.java @@ -30,9 +30,11 @@ package org.pushingpixels.radiance.theming.api.painter.decoration; import org.pushingpixels.radiance.common.api.RadianceCommonCortex; -import org.pushingpixels.radiance.theming.api.RadianceThemingSlices; import org.pushingpixels.radiance.theming.api.RadianceSkin; +import org.pushingpixels.radiance.theming.api.RadianceThemingSlices; import org.pushingpixels.radiance.theming.api.colorscheme.RadianceColorScheme; +import org.pushingpixels.radiance.theming.api.palette.ContainerRenderColorTokens; +import org.pushingpixels.radiance.theming.api.palette.TonalSkin; import org.pushingpixels.radiance.theming.internal.utils.RadianceCoreUtilities; import org.pushingpixels.radiance.theming.internal.utils.RadianceImageCreator; import org.pushingpixels.radiance.theming.internal.utils.WidgetUtilities; @@ -87,7 +89,7 @@ protected boolean removeEldestEntry(Entry eldest) { @Override public void paintDecorationArea(Graphics2D graphics, Component comp, - RadianceThemingSlices.DecorationAreaType decorationAreaType, int width, int height, RadianceSkin skin) { + RadianceThemingSlices.DecorationAreaType decorationAreaType, int width, int height, RadianceSkin skin) { if ((decorationAreaType == RadianceThemingSlices.DecorationAreaType.PRIMARY_TITLE_PANE) || (decorationAreaType == RadianceThemingSlices.DecorationAreaType.SECONDARY_TITLE_PANE)) { this.paintTitleBackground(graphics, comp, decorationAreaType, width, height, skin); @@ -113,20 +115,35 @@ public void paintDecorationArea(Graphics2D graphics, Component comp, * Skin for painting the title background. */ private void paintTitleBackground(Graphics2D graphics, Component comp, - RadianceThemingSlices.DecorationAreaType decorationAreaType, int width, int height, RadianceSkin skin) { + RadianceThemingSlices.DecorationAreaType decorationAreaType, int width, int height, + RadianceSkin skin) { + if (skin instanceof TonalSkin) { + ContainerRenderColorTokens tileRenderColorTokens = + skin.getBackgroundRenderColorTokens(decorationAreaType); + if (this.baseDecorationPainter == null) { + graphics.setColor(tileRenderColorTokens.getContainerColorTokens().getContainerHigh()); + graphics.fillRect(0, 0, width, height); + } else { + this.baseDecorationPainter.paintDecorationArea(graphics, comp, decorationAreaType, + width, height, skin); + } - RadianceColorScheme tileScheme = skin.getBackgroundColorScheme(decorationAreaType); - if (this.baseDecorationPainter == null) { - graphics.setColor(tileScheme.getMidColor()); - graphics.fillRect(0, 0, width, height); + Graphics2D temp = (Graphics2D) graphics.create(); + this.tileArea(temp, comp, tileRenderColorTokens, 0, 0, width, height); + temp.dispose(); } else { - this.baseDecorationPainter.paintDecorationArea(graphics, comp, decorationAreaType, - width, height, skin); - } + RadianceColorScheme tileScheme = skin.getBackgroundColorScheme(decorationAreaType); + if (this.baseDecorationPainter == null) { + graphics.setColor(tileScheme.getMidColor()); + graphics.fillRect(0, 0, width, height); + } else { + this.baseDecorationPainter.paintDecorationArea(graphics, comp, decorationAreaType, width, height, skin); + } - Graphics2D temp = (Graphics2D) graphics.create(); - this.tileArea(temp, comp, tileScheme, 0, 0, width, height); - temp.dispose(); + Graphics2D temp = (Graphics2D) graphics.create(); + this.tileArea(temp, comp, tileScheme, 0, 0, width, height); + temp.dispose(); + } } /** @@ -146,20 +163,34 @@ private void paintTitleBackground(Graphics2D graphics, Component comp, * Skin for painting the background of non-title decoration areas. */ private void paintExtraBackground(Graphics2D graphics, Component comp, - RadianceThemingSlices.DecorationAreaType decorationAreaType, int width, int height, RadianceSkin skin) { + RadianceThemingSlices.DecorationAreaType decorationAreaType, int width, int height, + RadianceSkin skin) { Point offset = RadianceCoreUtilities.getOffsetInRootPaneCoords(comp); - RadianceColorScheme tileScheme = skin.getBackgroundColorScheme(decorationAreaType); - if (this.baseDecorationPainter != null) { - this.baseDecorationPainter.paintDecorationArea(graphics, comp, decorationAreaType, - width, height, skin); + if (skin instanceof TonalSkin) { + ContainerRenderColorTokens tileRenderColorTokens = + skin.getBackgroundRenderColorTokens(decorationAreaType); + if (this.baseDecorationPainter != null) { + this.baseDecorationPainter.paintDecorationArea(graphics, comp, decorationAreaType, width, height, skin); + } else { + graphics.setColor(tileRenderColorTokens.getContainerColorTokens().getContainerHigh()); + graphics.fillRect(0, 0, width, height); + } + Graphics2D temp = (Graphics2D) graphics.create(); + this.tileArea(temp, comp, tileRenderColorTokens, offset.x, offset.y, width, height); + temp.dispose(); } else { - graphics.setColor(tileScheme.getMidColor()); - graphics.fillRect(0, 0, width, height); + RadianceColorScheme tileScheme = skin.getBackgroundColorScheme(decorationAreaType); + if (this.baseDecorationPainter != null) { + this.baseDecorationPainter.paintDecorationArea(graphics, comp, decorationAreaType, width, height, skin); + } else { + graphics.setColor(tileScheme.getMidColor()); + graphics.fillRect(0, 0, width, height); + } + Graphics2D temp = (Graphics2D) graphics.create(); + this.tileArea(temp, comp, tileScheme, offset.x, offset.y, width, height); + temp.dispose(); } - Graphics2D temp = (Graphics2D) graphics.create(); - this.tileArea(temp, comp, tileScheme, offset.x, offset.y, width, height); - temp.dispose(); } @Override @@ -183,6 +214,28 @@ public void paintDecorationArea(Graphics2D graphics, Component comp, RadianceThe temp.dispose(); } + @Override + public void paintDecorationArea(Graphics2D graphics, Component comp, + RadianceThemingSlices.DecorationAreaType decorationAreaType, Shape contour, + ContainerRenderColorTokens renderColorTokens) { + Point offset = RadianceCoreUtilities.getOffsetInRootPaneCoords(comp); + if (this.baseDecorationPainter != null) { + this.baseDecorationPainter.paintDecorationArea(graphics, comp, decorationAreaType, + contour, renderColorTokens); + } else { + graphics.setColor(renderColorTokens.getContainerColorTokens().getContainerHigh()); + graphics.fill(contour); + } + Graphics2D temp = (Graphics2D) graphics.create(); + // Clip the area for tiling with the image. Ideally this would be done + // with soft clipping (in RadianceCoreUtilities), but that creates an + // additional image. For now do hard clipping instead. + temp.setClip(contour); + this.tileArea(temp, comp, renderColorTokens, offset.x, offset.y, comp.getWidth(), + comp.getHeight()); + temp.dispose(); + } + /** * Tiles the specified area with colorized version of the image tile. This is called after the * {@link #baseDecorationPainter} has painted the area. This method should respect the current @@ -229,6 +282,32 @@ private void tileArea(Graphics2D g, Component comp, RadianceColorScheme tileSche graphics.dispose(); } + private void tileArea(Graphics2D g, Component comp, ContainerRenderColorTokens tileRenderColorTokens, + int offsetTextureX, int offsetTextureY, int width, int height) { + Graphics2D graphics = (Graphics2D) g.create(); + graphics.setComposite(WidgetUtilities.getAlphaComposite(comp, this.textureAlpha, g)); + + double scale = RadianceCommonCortex.getScaleFactor(comp); + Image colorizedTile = this.getColorizedTile(scale, tileRenderColorTokens); + int tileWidth = (int) (colorizedTile.getWidth(null) / scale); + int tileHeight = (int) (colorizedTile.getHeight(null) / scale); + + offsetTextureX = offsetTextureX % tileWidth; + offsetTextureY = offsetTextureY % tileHeight; + int currTileTop = -offsetTextureY; + do { + int currTileLeft = -offsetTextureX; + do { + graphics.drawImage(colorizedTile, currTileLeft, currTileTop, tileWidth, tileHeight, + null); + currTileLeft += tileWidth; + } while (currTileLeft < width); + currTileTop += tileHeight; + } while (currTileTop < height); + + graphics.dispose(); + } + /** * Sets the base decoration painter. * @@ -274,4 +353,23 @@ protected BufferedImage getColorizedTile(double scale, RadianceColorScheme schem } return result; } + + protected BufferedImage getColorizedTile(double scale, ContainerRenderColorTokens renderColorTokens) { + String key = scale + ":" + renderColorTokens.hashCode(); + BufferedImage result = this.colorizedTileMap.get(key); + if (result == null) { + int tileWidth = this.originalTile.getWidth(null); + int tileHeight = this.originalTile.getHeight(null); + BufferedImage tileBi = RadianceCoreUtilities.getBlankImage(scale, + (int) (tileWidth / scale), + (int) (tileHeight / scale)); + Graphics2D tile2D = tileBi.createGraphics(); + tile2D.drawImage(this.originalTile, 0, 0, (int) (tileWidth / scale), + (int) ( tileHeight / scale), null); + tile2D.dispose(); + result = RadianceImageCreator.getColorSchemeImage(tileBi, renderColorTokens, 0.0f, 1.0f); + this.colorizedTileMap.put(key, result); + } + return result; + } } diff --git a/theming/src/main/java/org/pushingpixels/radiance/theming/api/painter/decoration/RadianceDecorationPainter.java b/theming/src/main/java/org/pushingpixels/radiance/theming/api/painter/decoration/RadianceDecorationPainter.java index 36b54a661..b3382b7ff 100644 --- a/theming/src/main/java/org/pushingpixels/radiance/theming/api/painter/decoration/RadianceDecorationPainter.java +++ b/theming/src/main/java/org/pushingpixels/radiance/theming/api/painter/decoration/RadianceDecorationPainter.java @@ -29,9 +29,10 @@ */ package org.pushingpixels.radiance.theming.api.painter.decoration; -import org.pushingpixels.radiance.theming.api.RadianceThemingSlices; import org.pushingpixels.radiance.theming.api.RadianceSkin; +import org.pushingpixels.radiance.theming.api.RadianceThemingSlices; import org.pushingpixels.radiance.theming.api.colorscheme.RadianceColorScheme; +import org.pushingpixels.radiance.theming.api.palette.ContainerRenderColorTokens; import org.pushingpixels.radiance.theming.api.trait.RadianceTrait; import java.awt.*; @@ -67,6 +68,10 @@ void paintDecorationArea(Graphics2D graphics, Component comp, * @param colorScheme Color scheme for painting the decoration area. */ void paintDecorationArea(Graphics2D graphics, Component comp, - RadianceThemingSlices.DecorationAreaType decorationAreaType, Shape contour, - RadianceColorScheme colorScheme); + RadianceThemingSlices.DecorationAreaType decorationAreaType, Shape contour, + RadianceColorScheme colorScheme); + + default void paintDecorationArea(Graphics2D graphics, Component comp, + RadianceThemingSlices.DecorationAreaType decorationAreaType, Shape contour, + ContainerRenderColorTokens renderColorTokens) {} } diff --git a/theming/src/main/java/org/pushingpixels/radiance/theming/internal/painter/DecorationPainterUtils.java b/theming/src/main/java/org/pushingpixels/radiance/theming/internal/painter/DecorationPainterUtils.java index 61fc0c4b3..0b45f64da 100644 --- a/theming/src/main/java/org/pushingpixels/radiance/theming/internal/painter/DecorationPainterUtils.java +++ b/theming/src/main/java/org/pushingpixels/radiance/theming/internal/painter/DecorationPainterUtils.java @@ -168,7 +168,8 @@ public static RadianceThemingSlices.DecorationAreaType getImmediateDecorationTyp * If true, the painting of decoration background is enforced. */ public static void paintDecorationBackground(Graphics g, Component c, boolean force) { - RadianceThemingSlices.DecorationAreaType decorationType = RadianceThemingCortex.ComponentOrParentChainScope.getDecorationType(c); + RadianceThemingSlices.DecorationAreaType decorationType = + RadianceThemingCortex.ComponentOrParentChainScope.getDecorationType(c); paintDecorationBackground(g, c, decorationType, force); } @@ -190,8 +191,8 @@ public static void paintDecorationBackground(Graphics g, Component c, boolean fo private static void paintDecorationBackground(Graphics g, Component c, RadianceThemingSlices.DecorationAreaType decorationType, boolean force) { // System.out.println("Painting " + c.getClass().getSimpleName()); - boolean isInCellRenderer = (SwingUtilities.getAncestorOfClass(CellRendererPane.class, - c) != null); + boolean isInCellRenderer = + (SwingUtilities.getAncestorOfClass(CellRendererPane.class, c) != null); boolean isPreviewMode = false; if (c instanceof JComponent) { isPreviewMode = (Boolean.TRUE @@ -211,7 +212,6 @@ private static void paintDecorationBackground(Graphics g, Component c, Graphics2D g2d = (Graphics2D) g.create(); painter.paintDecorationArea(g2d, c, decorationType, c.getWidth(), c.getHeight(), skin); - g2d.dispose(); } diff --git a/theming/src/main/java/org/pushingpixels/radiance/theming/internal/utils/RadianceImageCreator.java b/theming/src/main/java/org/pushingpixels/radiance/theming/internal/utils/RadianceImageCreator.java index f16364b33..1d56d0732 100644 --- a/theming/src/main/java/org/pushingpixels/radiance/theming/internal/utils/RadianceImageCreator.java +++ b/theming/src/main/java/org/pushingpixels/radiance/theming/internal/utils/RadianceImageCreator.java @@ -33,8 +33,10 @@ import org.pushingpixels.radiance.theming.api.RadianceThemingCortex; import org.pushingpixels.radiance.theming.api.colorscheme.RadianceColorScheme; import org.pushingpixels.radiance.theming.api.painter.border.RadianceBorderPainter; +import org.pushingpixels.radiance.theming.api.palette.ContainerRenderColorTokens; import org.pushingpixels.radiance.theming.internal.utils.filters.ColorSchemeFilter; import org.pushingpixels.radiance.theming.internal.utils.filters.ImageColorFilter; +import org.pushingpixels.radiance.theming.internal.utils.filters.RenderColorTokensFilter; import javax.swing.*; import java.awt.*; @@ -418,6 +420,13 @@ public static BufferedImage getColorSchemeImage(BufferedImage original, .filter(original, null); } + public static BufferedImage getColorSchemeImage(BufferedImage original, + ContainerRenderColorTokens renderColorTokens, float originalBrightnessFactor, + float alpha) { + return RenderColorTokensFilter.getColorSchemeFilter(renderColorTokens, originalBrightnessFactor, alpha) + .filter(original, null); + } + public static BufferedImage getColorImage(Component comp, Icon original, Color color, float alpha) { int w = original.getIconWidth(); diff --git a/theming/src/main/java/org/pushingpixels/radiance/theming/internal/utils/border/RadiancePaneBorder.java b/theming/src/main/java/org/pushingpixels/radiance/theming/internal/utils/border/RadiancePaneBorder.java index 32a4d2260..8e92915af 100644 --- a/theming/src/main/java/org/pushingpixels/radiance/theming/internal/utils/border/RadiancePaneBorder.java +++ b/theming/src/main/java/org/pushingpixels/radiance/theming/internal/utils/border/RadiancePaneBorder.java @@ -31,9 +31,11 @@ import org.pushingpixels.radiance.common.api.RadianceCommonCortex; import org.pushingpixels.radiance.theming.api.ComponentState; -import org.pushingpixels.radiance.theming.api.RadianceThemingSlices; import org.pushingpixels.radiance.theming.api.RadianceSkin; +import org.pushingpixels.radiance.theming.api.RadianceThemingSlices; import org.pushingpixels.radiance.theming.api.colorscheme.RadianceColorScheme; +import org.pushingpixels.radiance.theming.api.palette.ContainerRenderColorTokens; +import org.pushingpixels.radiance.theming.api.palette.TonalSkin; import org.pushingpixels.radiance.theming.internal.utils.RadianceCoreUtilities; import javax.swing.*; @@ -67,44 +69,78 @@ public void paintBorder(Component c, Graphics g, int x, int y, int w, int h) { return; } - RadianceColorScheme scheme = skin - .getBackgroundColorScheme(RadianceThemingSlices.DecorationAreaType.PRIMARY_TITLE_PANE); - Component titlePaneComp = RadianceCoreUtilities - .getTitlePaneComponent(SwingUtilities.windowForComponent(c)); - RadianceColorScheme borderScheme = skin.getColorScheme(titlePaneComp, - RadianceThemingSlices.ColorSchemeAssociationKind.BORDER, ComponentState.ENABLED); - - Graphics2D graphics = (Graphics2D) g.create(); - - double scaleFactor = RadianceCommonCortex.getScaleFactor(c); - float strokeWidth = (scaleFactor <= 2.0f) ? 0.5f + (float) scaleFactor / 2.0f - : (float) scaleFactor; - graphics.setStroke(new BasicStroke(strokeWidth, BasicStroke.CAP_SQUARE, - BasicStroke.JOIN_MITER)); - - // bottom and right in ultra dark - graphics.setColor(borderScheme.getUltraDarkColor()); - graphics.drawLine(x, y + h - 1, x + w - 1, y + h - 1); - graphics.drawLine(x + w - 1, y, x + w - 1, y + h - 1); - // top and left - graphics.setColor(borderScheme.getDarkColor()); - graphics.drawLine(x, y, x + w - 2, y); - graphics.drawLine(x, y, x, y + h - 2); - // inner bottom and right - graphics.setColor(scheme.getMidColor()); - graphics.drawLine(x + 1, y + h - 2, x + w - 2, y + h - 2); - graphics.drawLine(x + w - 2, y + 1, x + w - 2, y + h - 2); - // inner top and left - graphics.setColor(scheme.getMidColor()); - graphics.drawLine(x + 1, y + 1, x + w - 3, y + 1); - graphics.drawLine(x + 1, y + 1, x + 1, y + h - 3); - // inner 2 and 3 - graphics.setColor(scheme.getLightColor()); - graphics.drawRect(x + 2, y + 2, w - 5, h - 5); - graphics.drawRect(x + 3, y + 3, w - 7, h - 7); - graphics.drawRect(x + 4, y + 4, w - 9, h - 9); - - graphics.dispose(); + if (skin instanceof TonalSkin) { + ContainerRenderColorTokens backgroundRenderColorTokens = skin.getBackgroundRenderColorTokens( + RadianceThemingSlices.DecorationAreaType.PRIMARY_TITLE_PANE); + Component titlePaneComp = RadianceCoreUtilities.getTitlePaneComponent( + SwingUtilities.windowForComponent(c)); + ContainerRenderColorTokens titleRenderColorTokens = skin.getColorRenderTokens( + titlePaneComp, ComponentState.ENABLED); + + Graphics2D graphics = (Graphics2D) g.create(); + + double scaleFactor = RadianceCommonCortex.getScaleFactor(c); + float strokeWidth = (scaleFactor <= 2.0f) ? 0.5f + (float) scaleFactor / 2.0f : (float) scaleFactor; + graphics.setStroke(new BasicStroke(strokeWidth, BasicStroke.CAP_SQUARE, BasicStroke.JOIN_MITER)); + + // bottom and right in ultra dark + graphics.setColor(titleRenderColorTokens.getOnContainerColorTokens().getOnContainer()); + graphics.drawLine(x, y + h - 1, x + w - 1, y + h - 1); + graphics.drawLine(x + w - 1, y, x + w - 1, y + h - 1); + // top and left + graphics.setColor(titleRenderColorTokens.getOnContainerColorTokens().getOnContainerVariant()); + graphics.drawLine(x, y, x + w - 2, y); + graphics.drawLine(x, y, x, y + h - 2); + // inner bottom and right + graphics.setColor(backgroundRenderColorTokens.getContainerColorTokens().getContainerHigh()); + graphics.drawLine(x + 1, y + h - 2, x + w - 2, y + h - 2); + graphics.drawLine(x + w - 2, y + 1, x + w - 2, y + h - 2); + // inner top and left + graphics.setColor(backgroundRenderColorTokens.getContainerColorTokens().getContainerHigh()); + graphics.drawLine(x + 1, y + 1, x + w - 3, y + 1); + graphics.drawLine(x + 1, y + 1, x + 1, y + h - 3); + // inner 2 and 3 + graphics.setColor(backgroundRenderColorTokens.getContainerColorTokens().getContainer()); + graphics.drawRect(x + 2, y + 2, w - 5, h - 5); + graphics.drawRect(x + 3, y + 3, w - 7, h - 7); + graphics.drawRect(x + 4, y + 4, w - 9, h - 9); + + graphics.dispose(); + } else { + RadianceColorScheme scheme = skin.getBackgroundColorScheme(RadianceThemingSlices.DecorationAreaType.PRIMARY_TITLE_PANE); + Component titlePaneComp = RadianceCoreUtilities.getTitlePaneComponent(SwingUtilities.windowForComponent(c)); + RadianceColorScheme borderScheme = skin.getColorScheme(titlePaneComp, RadianceThemingSlices.ColorSchemeAssociationKind.BORDER, ComponentState.ENABLED); + + Graphics2D graphics = (Graphics2D) g.create(); + + double scaleFactor = RadianceCommonCortex.getScaleFactor(c); + float strokeWidth = (scaleFactor <= 2.0f) ? 0.5f + (float) scaleFactor / 2.0f : (float) scaleFactor; + graphics.setStroke(new BasicStroke(strokeWidth, BasicStroke.CAP_SQUARE, BasicStroke.JOIN_MITER)); + + // bottom and right in ultra dark + graphics.setColor(borderScheme.getUltraDarkColor()); + graphics.drawLine(x, y + h - 1, x + w - 1, y + h - 1); + graphics.drawLine(x + w - 1, y, x + w - 1, y + h - 1); + // top and left + graphics.setColor(borderScheme.getDarkColor()); + graphics.drawLine(x, y, x + w - 2, y); + graphics.drawLine(x, y, x, y + h - 2); + // inner bottom and right + graphics.setColor(scheme.getMidColor()); + graphics.drawLine(x + 1, y + h - 2, x + w - 2, y + h - 2); + graphics.drawLine(x + w - 2, y + 1, x + w - 2, y + h - 2); + // inner top and left + graphics.setColor(scheme.getMidColor()); + graphics.drawLine(x + 1, y + 1, x + w - 3, y + 1); + graphics.drawLine(x + 1, y + 1, x + 1, y + h - 3); + // inner 2 and 3 + graphics.setColor(scheme.getLightColor()); + graphics.drawRect(x + 2, y + 2, w - 5, h - 5); + graphics.drawRect(x + 3, y + 3, w - 7, h - 7); + graphics.drawRect(x + 4, y + 4, w - 9, h - 9); + + graphics.dispose(); + } } @Override diff --git a/theming/src/main/java/org/pushingpixels/radiance/theming/internal/utils/filters/RenderColorTokensFilter.java b/theming/src/main/java/org/pushingpixels/radiance/theming/internal/utils/filters/RenderColorTokensFilter.java new file mode 100644 index 000000000..babd74098 --- /dev/null +++ b/theming/src/main/java/org/pushingpixels/radiance/theming/internal/utils/filters/RenderColorTokensFilter.java @@ -0,0 +1,241 @@ +/* + * $Id: ColorSchemeFilter.java 2353 2009-12-11 04:57:29Z kirillcool $ + * + * Dual-licensed under LGPL (Sun and Romain Guy) and BSD (Romain Guy). + * + * Copyright 2005 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * Copyright (c) 2006 Romain Guy + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.pushingpixels.radiance.theming.internal.utils.filters; + +import org.pushingpixels.radiance.common.api.filter.RadianceAbstractFilter; +import org.pushingpixels.radiance.theming.api.palette.ContainerRenderColorTokens; +import org.pushingpixels.radiance.theming.internal.utils.HashMapKey; +import org.pushingpixels.radiance.theming.internal.utils.LazyResettableHashMap; +import org.pushingpixels.radiance.theming.internal.utils.RadianceColorUtilities; +import org.pushingpixels.radiance.theming.internal.utils.RadianceCoreUtilities; + +import java.awt.*; +import java.awt.image.BufferedImage; +import java.util.List; +import java.util.*; + +/** + * @author Romain Guy + * @author Kirill Grouchnikov + */ + +public class RenderColorTokensFilter extends RadianceAbstractFilter { + private int[] interpolated; + + private static final int MAPSTEPS = 512; + + private final static LazyResettableHashMap filters = + new LazyResettableHashMap<>("ColorSchemeFilter"); + + private float originalBrightnessFactor; + private float alpha; + + public static RenderColorTokensFilter getColorTokensFilter( + ContainerRenderColorTokens containerRenderColorTokens, + float originalBrightnessFactor, float alpha) { + HashMapKey key = RadianceCoreUtilities.getHashKey(containerRenderColorTokens.hashCode(), + originalBrightnessFactor, alpha); + RenderColorTokensFilter filter = filters.get(key); + if (filter == null) { + filter = new RenderColorTokensFilter(containerRenderColorTokens, originalBrightnessFactor, alpha); + filters.put(key, filter); + } + return filter; + } + + public static RenderColorTokensFilter getColorSchemeFilter( + ContainerRenderColorTokens renderColorTokens, + float originalBrightnessFactor, float alpha) { + HashMapKey key = RadianceCoreUtilities.getHashKey(renderColorTokens.hashCode(), + originalBrightnessFactor, alpha); + RenderColorTokensFilter filter = filters.get(key); + if (filter == null) { + filter = new RenderColorTokensFilter(renderColorTokens, originalBrightnessFactor, alpha); + filters.put(key, filter); + } + return filter; + } + + public static int[] getInterpolatedColors(ContainerRenderColorTokens renderColorTokens) { + // collect the brightness factors of the color tokens + Map tokenColorMapping = new TreeMap<>(); + + int containerLowest = renderColorTokens.getContainerColorTokens().getContainerLowest().getRGB(); + int containerLow = renderColorTokens.getContainerColorTokens().getContainerLow().getRGB(); + int container = renderColorTokens.getContainerColorTokens().getContainer().getRGB(); + int containerHigh = renderColorTokens.getContainerColorTokens().getContainerHigh().getRGB(); + int containerHighest = renderColorTokens.getContainerColorTokens().getContainerHighest().getRGB(); + int onContainerVariant = renderColorTokens.getOnContainerColorTokens().getOnContainerVariant().getRGB(); + int onContainer = renderColorTokens.getOnContainerColorTokens().getOnContainer().getRGB(); + + tokenColorMapping.put( + RadianceColorUtilities.getColorBrightness(containerLowest), + renderColorTokens.getContainerColorTokens().getContainerLowest()); + tokenColorMapping.put( + RadianceColorUtilities.getColorBrightness(containerLow), + renderColorTokens.getContainerColorTokens().getContainerLow()); + tokenColorMapping.put( + RadianceColorUtilities.getColorBrightness(container), + renderColorTokens.getContainerColorTokens().getContainer()); + tokenColorMapping.put( + RadianceColorUtilities.getColorBrightness(containerHigh), + renderColorTokens.getContainerColorTokens().getContainerHigh()); + tokenColorMapping.put( + RadianceColorUtilities.getColorBrightness(containerHighest), + renderColorTokens.getContainerColorTokens().getContainerHighest()); + tokenColorMapping.put( + RadianceColorUtilities.getColorBrightness(onContainerVariant), + renderColorTokens.getOnContainerColorTokens().getOnContainerVariant()); + tokenColorMapping.put( + RadianceColorUtilities.getColorBrightness(onContainer), + renderColorTokens.getOnContainerColorTokens().getOnContainer()); + + List tokensBrightness = new ArrayList<>(tokenColorMapping.keySet()); + Collections.sort(tokensBrightness); + + int lowestTokensBrightness = tokensBrightness.get(0); + int highestTokensBrightness = tokensBrightness.get(tokensBrightness.size() - 1); + boolean hasSameBrightness = (highestTokensBrightness == lowestTokensBrightness); + + Map stretchedColorMapping = new TreeMap<>(); + for (Map.Entry entry : tokenColorMapping.entrySet()) { + int brightness = entry.getKey(); + int stretched = hasSameBrightness ? brightness + : 255 - 255 * (highestTokensBrightness - brightness) + / (highestTokensBrightness - lowestTokensBrightness); + stretchedColorMapping.put(stretched, entry.getValue()); + } + tokensBrightness = new ArrayList<>(stretchedColorMapping.keySet()); + Collections.sort(tokensBrightness); + + int[] interpolated = new int[MAPSTEPS]; + for (int i = 0; i < MAPSTEPS; i++) { + int brightness = (int) (256.0 * i / MAPSTEPS); + if (tokensBrightness.contains(brightness)) { + interpolated[i] = stretchedColorMapping.get(brightness).getRGB(); + } else { + if (hasSameBrightness) { + interpolated[i] = stretchedColorMapping.get(lowestTokensBrightness) + .getRGB(); + } else { + int currIndex = 0; + while (true) { + int currStopValue = tokensBrightness.get(currIndex); + int nextStopValue = tokensBrightness.get(currIndex + 1); + if ((brightness > currStopValue) && (brightness < nextStopValue)) { + // interpolate + Color currStopColor = stretchedColorMapping.get(currStopValue); + Color nextStopColor = stretchedColorMapping.get(nextStopValue); + interpolated[i] = RadianceColorUtilities.getInterpolatedRGB( + currStopColor, nextStopColor, + 1.0 - (double) (brightness - currStopValue) + / (double) (nextStopValue - currStopValue)); + break; + } + currIndex++; + } + } + } + } + return interpolated; + } + + /** + * @throws IllegalArgumentException if containerRenderColorTokens is null + */ + private RenderColorTokensFilter(ContainerRenderColorTokens containerRenderColorTokens, + float originalBrightnessFactor, float alpha) { + if (containerRenderColorTokens == null) { + throw new IllegalArgumentException("Color tokens cannot be null"); + } + + this.originalBrightnessFactor = originalBrightnessFactor; + this.alpha = alpha; + this.interpolated = getInterpolatedColors(containerRenderColorTokens); + } + + @Override + public BufferedImage filter(BufferedImage src, BufferedImage dst) { + if (dst == null) { + dst = createCompatibleDestImage(src, null); + } + + int width = src.getWidth(); + int height = src.getHeight(); + + int[] pixels = new int[width * height]; + getPixels(src, 0, 0, width, height, pixels); + mixColor(pixels); + setPixels(dst, 0, 0, width, height, pixels); + + return dst; + } + + private void mixColor(int[] pixels) { + for (int i = 0; i < pixels.length; i++) { + int argb = pixels[i]; + + int brightness = RadianceColorUtilities.getColorBrightness(argb); + + int a = (argb >>> 24) & 0xFF; + int r = (argb >>> 16) & 0xFF; + int g = (argb >>> 8) & 0xFF; + int b = (argb >>> 0) & 0xFF; + + float[] hsb = Color.RGBtoHSB(r, g, b, null); + int pixelColor = interpolated[brightness * MAPSTEPS / 256]; + + int ri = (pixelColor >>> 16) & 0xFF; + int gi = (pixelColor >>> 8) & 0xFF; + int bi = (pixelColor >>> 0) & 0xFF; + float[] hsbi = Color.RGBtoHSB(ri, gi, bi, null); + + hsb[0] = hsbi[0]; + hsb[1] = hsbi[1]; + if (this.originalBrightnessFactor >= 0.0f) { + hsb[2] = this.originalBrightnessFactor * hsb[2] + + (1.0f - this.originalBrightnessFactor) * hsbi[2]; + } else { + hsb[2] = hsb[2] * hsbi[2] * (1.0f + this.originalBrightnessFactor); + } + + int result = Color.HSBtoRGB(hsb[0], hsb[1], hsb[2]); + int finalAlpha = (int) (a * this.alpha); + + pixels[i] = (finalAlpha << 24) | ((result >> 16) & 0xFF) << 16 + | ((result >> 8) & 0xFF) << 8 | (result & 0xFF); + } + } +}