From 4d3f88eb0edb95b8bc7dedb136f0048ff3918506 Mon Sep 17 00:00:00 2001 From: Jussi Koskela Date: Thu, 2 Jul 2020 12:25:51 +0300 Subject: [PATCH] Render elements using custom color filters WIP: Still lacks a mechanism to read color filters for elements from DB. Currently filters can be set manually for elements with the ElementHints.KEY_COLOR_FILTER hint. gitlab simantics/platform#569 Change-Id: Id0bbbdeea3d4c9a27f2778f7bdcab74f5f784322 --- .../diagram/participant/ElementPainter.java | 4 + .../simantics/g2d/element/ElementHints.java | 6 + .../META-INF/MANIFEST.MF | 1 + .../scenegraph/g2d/G2DRenderingHints.java | 13 + .../scenegraph/g2d/color/BWColorFilter.java | 40 ++ .../scenegraph/g2d/color/ColorFilter.java | 18 + .../g2d/color/Graphics2DWithColorFilter.java | 354 ++++++++++++++++++ .../g2d/nodes/SingleElementNode.java | 21 +- .../scenegraph/utils/BufferedImage.java | 20 +- .../scenegraph/utils/MipMapBufferedImage.java | 36 +- .../utils/MipMapVRamBufferedImage.java | 23 +- 11 files changed, 519 insertions(+), 17 deletions(-) create mode 100644 bundles/org.simantics.scenegraph/src/org/simantics/scenegraph/g2d/color/BWColorFilter.java create mode 100644 bundles/org.simantics.scenegraph/src/org/simantics/scenegraph/g2d/color/ColorFilter.java create mode 100644 bundles/org.simantics.scenegraph/src/org/simantics/scenegraph/g2d/color/Graphics2DWithColorFilter.java diff --git a/bundles/org.simantics.g2d/src/org/simantics/g2d/diagram/participant/ElementPainter.java b/bundles/org.simantics.g2d/src/org/simantics/g2d/diagram/participant/ElementPainter.java index a8fee9582..d4d0609b2 100644 --- a/bundles/org.simantics.g2d/src/org/simantics/g2d/diagram/participant/ElementPainter.java +++ b/bundles/org.simantics.g2d/src/org/simantics/g2d/diagram/participant/ElementPainter.java @@ -84,6 +84,7 @@ import org.simantics.scenegraph.Node; import org.simantics.scenegraph.g2d.G2DParentNode; import org.simantics.scenegraph.g2d.G2DSceneGraph; import org.simantics.scenegraph.g2d.IG2DNode; +import org.simantics.scenegraph.g2d.color.ColorFilter; import org.simantics.scenegraph.g2d.nodes.ConnectionNode; import org.simantics.scenegraph.g2d.nodes.DataNode; import org.simantics.scenegraph.g2d.nodes.LinkNode; @@ -787,6 +788,9 @@ public class ElementPainter extends AbstractDiagramParticipant implements Compos } holder.setVisible(visible); + ColorFilter colorFilter = e.getHint(ElementHints.KEY_COLOR_FILTER); + holder.setColorFilter(colorFilter); + for (SceneGraph n : nodeHandlers) { n.init(e, holder); } diff --git a/bundles/org.simantics.g2d/src/org/simantics/g2d/element/ElementHints.java b/bundles/org.simantics.g2d/src/org/simantics/g2d/element/ElementHints.java index 35f3a8591..53cac820e 100644 --- a/bundles/org.simantics.g2d/src/org/simantics/g2d/element/ElementHints.java +++ b/bundles/org.simantics.g2d/src/org/simantics/g2d/element/ElementHints.java @@ -33,6 +33,7 @@ import org.simantics.g2d.element.handler.SceneGraph; import org.simantics.g2d.image.Image; import org.simantics.g2d.utils.Alignment; import org.simantics.scenegraph.Node; +import org.simantics.scenegraph.g2d.color.ColorFilter; import org.simantics.utils.datastructures.hints.IHintContext.Key; import org.simantics.utils.datastructures.hints.IHintContext.KeyOf; @@ -184,6 +185,11 @@ public class ElementHints { */ public static final Key KEY_ELEMENT_PROPERTIES = new KeyOf(Properties.class, "ELEMENT_PROPERTIES"); + /** + * Color filter to be applied when rendering nodes. + */ + public static final Key KEY_COLOR_FILTER = new KeyOf(ColorFilter.class, "COLOR_FILTER"); + public static class Properties extends HashMap { private static final long serialVersionUID = 6986415032113675720L; } diff --git a/bundles/org.simantics.scenegraph/META-INF/MANIFEST.MF b/bundles/org.simantics.scenegraph/META-INF/MANIFEST.MF index ebaffc234..99ebb152e 100644 --- a/bundles/org.simantics.scenegraph/META-INF/MANIFEST.MF +++ b/bundles/org.simantics.scenegraph/META-INF/MANIFEST.MF @@ -24,6 +24,7 @@ Export-Package: com.kitfox.svg, org.simantics.scenegraph, org.simantics.scenegraph.adapters, org.simantics.scenegraph.g2d, + org.simantics.scenegraph.g2d.color, org.simantics.scenegraph.g2d.events, org.simantics.scenegraph.g2d.events.adapter, org.simantics.scenegraph.g2d.events.command, diff --git a/bundles/org.simantics.scenegraph/src/org/simantics/scenegraph/g2d/G2DRenderingHints.java b/bundles/org.simantics.scenegraph/src/org/simantics/scenegraph/g2d/G2DRenderingHints.java index 992213c45..a01654ce2 100644 --- a/bundles/org.simantics.scenegraph/src/org/simantics/scenegraph/g2d/G2DRenderingHints.java +++ b/bundles/org.simantics.scenegraph/src/org/simantics/scenegraph/g2d/G2DRenderingHints.java @@ -16,6 +16,8 @@ import java.awt.geom.AffineTransform; import java.awt.geom.Rectangle2D; import java.util.Map; +import org.simantics.scenegraph.g2d.color.ColorFilter; + /** * @author Tuukka Lehtonen * See {@link G2DPDFRenderingHints} @@ -121,4 +123,15 @@ public final class G2DRenderingHints { } }; + /* + * A rendering hint for storing the active ColorFilter which can be used e.g. + * for coloring BufferedImages. + */ + public static final Key KEY_COLOR_FILTER = new Key(2007) { + @Override + public boolean isCompatibleValue(Object val) { + return val instanceof ColorFilter || val == null; + } + }; + } \ No newline at end of file diff --git a/bundles/org.simantics.scenegraph/src/org/simantics/scenegraph/g2d/color/BWColorFilter.java b/bundles/org.simantics.scenegraph/src/org/simantics/scenegraph/g2d/color/BWColorFilter.java new file mode 100644 index 000000000..a98c0ffb2 --- /dev/null +++ b/bundles/org.simantics.scenegraph/src/org/simantics/scenegraph/g2d/color/BWColorFilter.java @@ -0,0 +1,40 @@ +/******************************************************************************* + * Copyright (c) 2020 Association for Decentralized Information Management in + * Industry THTH ry. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Semantum Oy - initial API and implementation + *******************************************************************************/ +package org.simantics.scenegraph.g2d.color; + +import java.awt.Color; + +public class BWColorFilter implements ColorFilter { + + @Override + public Color filter(Color c) { + int avg = (c.getRed() + c.getGreen() + c.getBlue()) / 3; + Color c2 = new Color(avg, avg, avg, c.getAlpha()); + return c2; + } + + @Override + public int hashCode() { + return getClass().hashCode(); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + return true; + } +} diff --git a/bundles/org.simantics.scenegraph/src/org/simantics/scenegraph/g2d/color/ColorFilter.java b/bundles/org.simantics.scenegraph/src/org/simantics/scenegraph/g2d/color/ColorFilter.java new file mode 100644 index 000000000..105f8f7cd --- /dev/null +++ b/bundles/org.simantics.scenegraph/src/org/simantics/scenegraph/g2d/color/ColorFilter.java @@ -0,0 +1,18 @@ +/******************************************************************************* + * Copyright (c) 2020 Association for Decentralized Information Management in + * Industry THTH ry. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Semantum Oy - initial API and implementation + *******************************************************************************/ +package org.simantics.scenegraph.g2d.color; + +import java.awt.Color; + +public interface ColorFilter { + public Color filter(Color c); +} diff --git a/bundles/org.simantics.scenegraph/src/org/simantics/scenegraph/g2d/color/Graphics2DWithColorFilter.java b/bundles/org.simantics.scenegraph/src/org/simantics/scenegraph/g2d/color/Graphics2DWithColorFilter.java new file mode 100644 index 000000000..91e3cc007 --- /dev/null +++ b/bundles/org.simantics.scenegraph/src/org/simantics/scenegraph/g2d/color/Graphics2DWithColorFilter.java @@ -0,0 +1,354 @@ +/******************************************************************************* + * Copyright (c) 2020 Association for Decentralized Information Management in + * Industry THTH ry. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Semantum Oy - initial API and implementation + *******************************************************************************/ +package org.simantics.scenegraph.g2d.color; + +import java.awt.Color; +import java.awt.Composite; +import java.awt.Font; +import java.awt.FontMetrics; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.GraphicsConfiguration; +import java.awt.Image; +import java.awt.Paint; +import java.awt.Polygon; +import java.awt.Rectangle; +import java.awt.RenderingHints; +import java.awt.RenderingHints.Key; +import java.awt.Shape; +import java.awt.Stroke; +import java.awt.font.FontRenderContext; +import java.awt.font.GlyphVector; +import java.awt.geom.AffineTransform; +import java.awt.image.BufferedImage; +import java.awt.image.BufferedImageOp; +import java.awt.image.ImageObserver; +import java.awt.image.RenderedImage; +import java.awt.image.renderable.RenderableImage; +import java.text.AttributedCharacterIterator; +import java.util.Map; + +import com.kitfox.svg.batik.LinearGradientPaint; +import com.kitfox.svg.batik.RadialGradientPaint; + +/** + * Modified methods: setPaint, setColor. + * Colors of bitmap images must be handled externally. + */ +public class Graphics2DWithColorFilter extends Graphics2D { + private Graphics2D g2d; + private ColorFilter colorFilter; + + public Graphics2DWithColorFilter(Graphics2D g2d, ColorFilter colorFilter) { + this.g2d = g2d; + this.colorFilter = colorFilter; + } + + public void setColorFilter(ColorFilter colorFilter) { + this.colorFilter = colorFilter; + } + + public Color filter(Color c) { + if (colorFilter != null) { + return colorFilter.filter(c); + } else { + return c; + } + } + + public void setColor(Color c) { + g2d.setColor(filter(c)); + } + + public void setPaint(Paint paint) { + if (paint instanceof Color) { + g2d.setColor(filter((Color)paint)); + } else if (paint instanceof LinearGradientPaint) { + LinearGradientPaint lgp = (LinearGradientPaint)paint; + Color[] colors = new Color[lgp.getColors().length]; + for (int i = 0; i < lgp.getColors().length; i++) { + colors[i] = filter(lgp.getColors()[i]); + } + LinearGradientPaint lgp2 = new LinearGradientPaint(lgp.getStartPoint(), lgp.getEndPoint(), lgp.getFractions(), colors); + g2d.setPaint(lgp2); + } else if (paint instanceof RadialGradientPaint) { + RadialGradientPaint rgp = (RadialGradientPaint)paint; + Color[] colors = new Color[rgp.getColors().length]; + for (int i = 0; i < rgp.getColors().length; i++) { + colors[i] = filter(rgp.getColors()[i]); + } + RadialGradientPaint rgp2 = new RadialGradientPaint(rgp.getCenterPoint(), rgp.getRadius(), rgp.getFractions(), colors); + g2d.setPaint(rgp2); + } else { + // Not implemented yet. Show red! + Color c2 = new Color(255, 0, 0, 255); + g2d.setPaint(c2); + } + } + + public int hashCode() { + return g2d.hashCode(); + } + public boolean equals(Object obj) { + return g2d.equals(obj); + } + public Graphics create() { + return g2d.create(); + } + public Graphics create(int x, int y, int width, int height) { + return g2d.create(x, y, width, height); + } + public Color getColor() { + return g2d.getColor(); + } + public void setPaintMode() { + g2d.setPaintMode(); + } + public void setXORMode(Color c1) { + g2d.setXORMode(c1); + } + public Font getFont() { + return g2d.getFont(); + } + public void setFont(Font font) { + g2d.setFont(font); + } + public FontMetrics getFontMetrics() { + return g2d.getFontMetrics(); + } + public FontMetrics getFontMetrics(Font f) { + return g2d.getFontMetrics(f); + } + public Rectangle getClipBounds() { + return g2d.getClipBounds(); + } + public void clipRect(int x, int y, int width, int height) { + g2d.clipRect(x, y, width, height); + } + public void setClip(int x, int y, int width, int height) { + g2d.setClip(x, y, width, height); + } + public Shape getClip() { + return g2d.getClip(); + } + public void setClip(Shape clip) { + g2d.setClip(clip); + } + public void copyArea(int x, int y, int width, int height, int dx, int dy) { + g2d.copyArea(x, y, width, height, dx, dy); + } + public void drawLine(int x1, int y1, int x2, int y2) { + g2d.drawLine(x1, y1, x2, y2); + } + public void fillRect(int x, int y, int width, int height) { + g2d.fillRect(x, y, width, height); + } + public void drawRect(int x, int y, int width, int height) { + g2d.drawRect(x, y, width, height); + } + public void draw3DRect(int x, int y, int width, int height, boolean raised) { + g2d.draw3DRect(x, y, width, height, raised); + } + public void clearRect(int x, int y, int width, int height) { + g2d.clearRect(x, y, width, height); + } + public void drawRoundRect(int x, int y, int width, int height, int arcWidth, int arcHeight) { + g2d.drawRoundRect(x, y, width, height, arcWidth, arcHeight); + } + public void fill3DRect(int x, int y, int width, int height, boolean raised) { + g2d.fill3DRect(x, y, width, height, raised); + } + public void fillRoundRect(int x, int y, int width, int height, int arcWidth, int arcHeight) { + g2d.fillRoundRect(x, y, width, height, arcWidth, arcHeight); + } + public void draw(Shape s) { + g2d.draw(s); + } + public boolean drawImage(Image img, AffineTransform xform, ImageObserver obs) { + return g2d.drawImage(img, xform, obs); + } + public void drawImage(BufferedImage img, BufferedImageOp op, int x, int y) { + g2d.drawImage(img, op, x, y); + } + public void drawOval(int x, int y, int width, int height) { + g2d.drawOval(x, y, width, height); + } + public void drawRenderedImage(RenderedImage img, AffineTransform xform) { + g2d.drawRenderedImage(img, xform); + } + public void fillOval(int x, int y, int width, int height) { + g2d.fillOval(x, y, width, height); + } + public void drawRenderableImage(RenderableImage img, AffineTransform xform) { + g2d.drawRenderableImage(img, xform); + } + public void drawArc(int x, int y, int width, int height, int startAngle, int arcAngle) { + g2d.drawArc(x, y, width, height, startAngle, arcAngle); + } + public void drawString(String str, int x, int y) { + g2d.drawString(str, x, y); + } + public void fillArc(int x, int y, int width, int height, int startAngle, int arcAngle) { + g2d.fillArc(x, y, width, height, startAngle, arcAngle); + } + public void drawString(String str, float x, float y) { + g2d.drawString(str, x, y); + } + public void drawString(AttributedCharacterIterator iterator, int x, int y) { + g2d.drawString(iterator, x, y); + } + public void drawPolyline(int[] xPoints, int[] yPoints, int nPoints) { + g2d.drawPolyline(xPoints, yPoints, nPoints); + } + public void drawPolygon(int[] xPoints, int[] yPoints, int nPoints) { + g2d.drawPolygon(xPoints, yPoints, nPoints); + } + public void drawString(AttributedCharacterIterator iterator, float x, float y) { + g2d.drawString(iterator, x, y); + } + public void drawPolygon(Polygon p) { + g2d.drawPolygon(p); + } + public void drawGlyphVector(GlyphVector g, float x, float y) { + g2d.drawGlyphVector(g, x, y); + } + public void fillPolygon(int[] xPoints, int[] yPoints, int nPoints) { + g2d.fillPolygon(xPoints, yPoints, nPoints); + } + public void fill(Shape s) { + g2d.fill(s); + } + public void fillPolygon(Polygon p) { + g2d.fillPolygon(p); + } + public boolean hit(Rectangle rect, Shape s, boolean onStroke) { + return g2d.hit(rect, s, onStroke); + } + public GraphicsConfiguration getDeviceConfiguration() { + return g2d.getDeviceConfiguration(); + } + public void setComposite(Composite comp) { + g2d.setComposite(comp); + } + public void drawChars(char[] data, int offset, int length, int x, int y) { + g2d.drawChars(data, offset, length, x, y); + } + public void drawBytes(byte[] data, int offset, int length, int x, int y) { + g2d.drawBytes(data, offset, length, x, y); + } + public void setStroke(Stroke s) { + g2d.setStroke(s); + } + public void setRenderingHint(Key hintKey, Object hintValue) { + g2d.setRenderingHint(hintKey, hintValue); + } + public boolean drawImage(Image img, int x, int y, ImageObserver observer) { + return g2d.drawImage(img, x, y, observer); + } + public Object getRenderingHint(Key hintKey) { + return g2d.getRenderingHint(hintKey); + } + public void setRenderingHints(Map hints) { + g2d.setRenderingHints(hints); + } + public boolean drawImage(Image img, int x, int y, int width, int height, ImageObserver observer) { + return g2d.drawImage(img, x, y, width, height, observer); + } + public void addRenderingHints(Map hints) { + g2d.addRenderingHints(hints); + } + public RenderingHints getRenderingHints() { + return g2d.getRenderingHints(); + } + public void translate(int x, int y) { + g2d.translate(x, y); + } + public boolean drawImage(Image img, int x, int y, Color bgcolor, ImageObserver observer) { + return g2d.drawImage(img, x, y, bgcolor, observer); + } + public void translate(double tx, double ty) { + g2d.translate(tx, ty); + } + public void rotate(double theta) { + g2d.rotate(theta); + } + public void rotate(double theta, double x, double y) { + g2d.rotate(theta, x, y); + } + public boolean drawImage(Image img, int x, int y, int width, int height, Color bgcolor, ImageObserver observer) { + return g2d.drawImage(img, x, y, width, height, bgcolor, observer); + } + public void scale(double sx, double sy) { + g2d.scale(sx, sy); + } + public void shear(double shx, double shy) { + g2d.shear(shx, shy); + } + public void transform(AffineTransform Tx) { + g2d.transform(Tx); + } + public boolean drawImage(Image img, int dx1, int dy1, int dx2, int dy2, int sx1, int sy1, int sx2, int sy2, + ImageObserver observer) { + return g2d.drawImage(img, dx1, dy1, dx2, dy2, sx1, sy1, sx2, sy2, observer); + } + public void setTransform(AffineTransform Tx) { + g2d.setTransform(Tx); + } + public AffineTransform getTransform() { + return g2d.getTransform(); + } + public Paint getPaint() { + return g2d.getPaint(); + } + public Composite getComposite() { + return g2d.getComposite(); + } + public void setBackground(Color color) { + g2d.setBackground(color); + } + public boolean drawImage(Image img, int dx1, int dy1, int dx2, int dy2, int sx1, int sy1, int sx2, int sy2, + Color bgcolor, ImageObserver observer) { + return g2d.drawImage(img, dx1, dy1, dx2, dy2, sx1, sy1, sx2, sy2, bgcolor, observer); + } + public Color getBackground() { + return g2d.getBackground(); + } + public Stroke getStroke() { + return g2d.getStroke(); + } + public void clip(Shape s) { + g2d.clip(s); + } + public FontRenderContext getFontRenderContext() { + return g2d.getFontRenderContext(); + } + public void dispose() { + g2d.dispose(); + } + @SuppressWarnings("deprecation") + public void finalize() { + g2d.finalize(); + } + public String toString() { + return g2d.toString(); + } + @SuppressWarnings("deprecation") + public Rectangle getClipRect() { + return g2d.getClipRect(); + } + public boolean hitClip(int x, int y, int width, int height) { + return g2d.hitClip(x, y, width, height); + } + public Rectangle getClipBounds(Rectangle r) { + return g2d.getClipBounds(r); + } +} diff --git a/bundles/org.simantics.scenegraph/src/org/simantics/scenegraph/g2d/nodes/SingleElementNode.java b/bundles/org.simantics.scenegraph/src/org/simantics/scenegraph/g2d/nodes/SingleElementNode.java index 29ef9954e..b45e7493a 100644 --- a/bundles/org.simantics.scenegraph/src/org/simantics/scenegraph/g2d/nodes/SingleElementNode.java +++ b/bundles/org.simantics.scenegraph/src/org/simantics/scenegraph/g2d/nodes/SingleElementNode.java @@ -21,6 +21,8 @@ import java.util.Map; import org.simantics.scenegraph.INode; import org.simantics.scenegraph.g2d.G2DRenderingHints; import org.simantics.scenegraph.g2d.IG2DNode; +import org.simantics.scenegraph.g2d.color.ColorFilter; +import org.simantics.scenegraph.g2d.color.Graphics2DWithColorFilter; import org.simantics.scenegraph.g2d.events.EventTypes; import org.simantics.scenegraph.g2d.events.MouseEvent; import org.simantics.scenegraph.g2d.events.MouseEvent.MouseDragBegin; @@ -33,6 +35,7 @@ public class SingleElementNode extends TransformNode implements InitValueSupport private TransferableProvider transferableProvider; protected Composite composite; + private ColorFilter colorFilter; protected Boolean visible = Boolean.TRUE; protected Boolean hidden = Boolean.FALSE; private transient Object key; @@ -91,6 +94,10 @@ public class SingleElementNode extends TransformNode implements InitValueSupport public void setComposite(Composite composite) { this.composite = composite; } + + public void setColorFilter(ColorFilter colorFilter) { + this.colorFilter = colorFilter; + } @SyncField("visible") public void setVisible(Boolean visible) { @@ -111,10 +118,18 @@ public class SingleElementNode extends TransformNode implements InitValueSupport } @Override - public void render(Graphics2D g) { + public void render(Graphics2D g2d) { if (!visible || hidden) return; + Graphics2D g; + if (colorFilter != null) { + g = new Graphics2DWithColorFilter(g2d, colorFilter); + g.setRenderingHint(G2DRenderingHints.KEY_COLOR_FILTER, colorFilter); + } else { + g = g2d; + } + Composite oldComposite = null; if(composite != null) { oldComposite = g.getComposite(); @@ -130,6 +145,10 @@ public class SingleElementNode extends TransformNode implements InitValueSupport if (oldComposite != null) g.setComposite(oldComposite); + + if (colorFilter != null) { + g.setRenderingHint(G2DRenderingHints.KEY_COLOR_FILTER, null); + } } @Override diff --git a/bundles/org.simantics.scenegraph/src/org/simantics/scenegraph/utils/BufferedImage.java b/bundles/org.simantics.scenegraph/src/org/simantics/scenegraph/utils/BufferedImage.java index 5401fa757..7904c3f4a 100644 --- a/bundles/org.simantics.scenegraph/src/org/simantics/scenegraph/utils/BufferedImage.java +++ b/bundles/org.simantics.scenegraph/src/org/simantics/scenegraph/utils/BufferedImage.java @@ -17,6 +17,11 @@ import java.awt.Point; import java.awt.RenderingHints; import java.awt.geom.AffineTransform; import java.awt.geom.Rectangle2D; +import java.util.Objects; + +import org.simantics.scenegraph.g2d.G2DRenderingHints; +import org.simantics.scenegraph.g2d.color.ColorFilter; +import org.simantics.scenegraph.g2d.color.Graphics2DWithColorFilter; import com.kitfox.svg.SVGDiagram; import com.kitfox.svg.SVGException; @@ -31,6 +36,7 @@ public class BufferedImage { java.awt.image.BufferedImage buffer = null; AffineTransform previousTransform = null; + ColorFilter previousColorFilter = null; /** * @param original @@ -70,7 +76,7 @@ public class BufferedImage { } } - public void paintToBuffer(AffineTransform transform, float margin) throws SVGException { + public void paintToBuffer(AffineTransform transform, ColorFilter colorFilter, float margin) throws SVGException { int w = (int)((imageBounds.getWidth()+margin*2) * transform.getScaleX()); int h = (int)((imageBounds.getHeight()+margin*2) * transform.getScaleY()); @@ -90,18 +96,24 @@ public class BufferedImage { b2g.translate(margin, margin); b2g.scale(transform.getScaleX(), transform.getScaleY()); b2g.translate(-imageBounds.getMinX(), -imageBounds.getMinY()); - source.render(b2g); + if (colorFilter != null) { + source.render(new Graphics2DWithColorFilter(b2g, colorFilter)); + } else { + source.render(b2g); + } } public void paint(Graphics2D g) { float margin = 5; - if(previousTransform == null || previousTransform.getScaleX() != g.getTransform().getScaleX() || previousTransform.getScaleY() != g.getTransform().getScaleY()) { + ColorFilter colorFilter = (ColorFilter) g.getRenderingHint(G2DRenderingHints.KEY_COLOR_FILTER); + if(previousTransform == null || previousTransform.getScaleX() != g.getTransform().getScaleX() || previousTransform.getScaleY() != g.getTransform().getScaleY() || !Objects.equals(colorFilter, previousColorFilter)) { // setupSourceRender(g); try { + previousColorFilter = colorFilter; previousTransform = (AffineTransform)g.getTransform().clone(); - paintToBuffer(previousTransform, margin); + paintToBuffer(previousTransform, previousColorFilter, margin); } catch (SVGException e) { // TODO Auto-generated catch block e.printStackTrace(); diff --git a/bundles/org.simantics.scenegraph/src/org/simantics/scenegraph/utils/MipMapBufferedImage.java b/bundles/org.simantics.scenegraph/src/org/simantics/scenegraph/utils/MipMapBufferedImage.java index 7e5f09949..b24927554 100644 --- a/bundles/org.simantics.scenegraph/src/org/simantics/scenegraph/utils/MipMapBufferedImage.java +++ b/bundles/org.simantics.scenegraph/src/org/simantics/scenegraph/utils/MipMapBufferedImage.java @@ -22,7 +22,11 @@ import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Objects; +import org.simantics.scenegraph.g2d.G2DRenderingHints; +import org.simantics.scenegraph.g2d.color.ColorFilter; +import org.simantics.scenegraph.g2d.color.Graphics2DWithColorFilter; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -166,12 +170,18 @@ public class MipMapBufferedImage extends BufferedImage { @Override public void paint(Graphics2D g) { + ColorFilter colorFilter = (ColorFilter) g.getRenderingHint(G2DRenderingHints.KEY_COLOR_FILTER); + // Quality rendering requested, do not render from cache //QualityHints.HIGH_QUALITY_HINTS.setQuality(g); if (g.getRenderingHint(RenderingHints.KEY_RENDERING) == RenderingHints.VALUE_RENDER_QUALITY) { try { - source.render(g); + if (colorFilter != null) { + source.render(new Graphics2DWithColorFilter(g, colorFilter)); + } else { + source.render(g); + } } catch (Exception e) { // NOTE: Catching Exception instead of SVGException due to an // NPE when encountering invalid color SVG definitions (e.g. @@ -192,7 +202,11 @@ public class MipMapBufferedImage extends BufferedImage { Graphics2D g2d = (Graphics2D) g.create(); setupSourceRender(g2d); try { - source.render(g2d); + if (colorFilter != null) { + source.render(new Graphics2DWithColorFilter(g, colorFilter)); + } else { + source.render(g); + } } catch (Exception e) { // NOTE: Catching Exception instead of SVGException due to an // NPE when encountering invalid color SVG definitions (e.g. @@ -271,6 +285,7 @@ public class MipMapBufferedImage extends BufferedImage { java.awt.image.BufferedImage image; //int widMargin, heiMargin; int wid, hei; + private ColorFilter previousColorFilter = null; BufferedRaster(double resolution) { super(resolution); @@ -282,8 +297,12 @@ public class MipMapBufferedImage extends BufferedImage { // heiMargin = (int) (hei * resolution * (MARGIN_PERCENT/100)) +1; } - synchronized java.awt.image.BufferedImage getOrCreate() + synchronized java.awt.image.BufferedImage getOrCreate(ColorFilter colorFilter) { + if (!Objects.equals(colorFilter, previousColorFilter)) { + previousColorFilter = colorFilter; + image = null; + } if (image!=null) return image; image = new java.awt.image.BufferedImage( (wid+0*2+1), @@ -303,7 +322,11 @@ public class MipMapBufferedImage extends BufferedImage { target.scale(resolution, resolution); target.translate(-imageBounds.getMinX(), -imageBounds.getMinY()); try { - source.render(target); + if (colorFilter != null) { + source.render(new Graphics2DWithColorFilter(target, colorFilter)); + } else { + source.render(target); + } } catch (Exception e) { // TODO Auto-generated catch block // NOTE: Catching Exception instead of SVGException due to an @@ -320,11 +343,12 @@ public class MipMapBufferedImage extends BufferedImage { } public void paint(Graphics2D g) { - java.awt.image.BufferedImage image = getOrCreate(); + ColorFilter colorFilter = (ColorFilter) g.getRenderingHint(G2DRenderingHints.KEY_COLOR_FILTER); + java.awt.image.BufferedImage image = getOrCreate(colorFilter); if (image==null) { try { - source.render(g); + source.render(new Graphics2DWithColorFilter(g, colorFilter)); } catch (Exception e) { // TODO Auto-generated catch block // NOTE: Catching Exception instead of SVGException due to an diff --git a/bundles/org.simantics.scenegraph/src/org/simantics/scenegraph/utils/MipMapVRamBufferedImage.java b/bundles/org.simantics.scenegraph/src/org/simantics/scenegraph/utils/MipMapVRamBufferedImage.java index 54323097b..69b4e337b 100644 --- a/bundles/org.simantics.scenegraph/src/org/simantics/scenegraph/utils/MipMapVRamBufferedImage.java +++ b/bundles/org.simantics.scenegraph/src/org/simantics/scenegraph/utils/MipMapVRamBufferedImage.java @@ -19,8 +19,12 @@ import java.awt.RenderingHints; import java.awt.geom.AffineTransform; import java.awt.geom.Rectangle2D; import java.awt.image.VolatileImage; +import java.util.Objects; import java.util.concurrent.atomic.AtomicInteger; +import org.simantics.scenegraph.g2d.G2DRenderingHints; +import org.simantics.scenegraph.g2d.color.ColorFilter; +import org.simantics.scenegraph.g2d.color.Graphics2DWithColorFilter; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -99,7 +103,7 @@ public class MipMapVRamBufferedImage extends MipMapBufferedImage { * @param image * @return */ - private VolatileImage sourceRender(VolatileImage image) { + private VolatileImage sourceRender(VolatileImage image, ColorFilter colorFilter) { Graphics2D target = image.createGraphics(); target.setBackground(new Color(255,255,255,0)); target.clearRect(0, 0, image.getWidth(), image.getHeight()); @@ -113,7 +117,11 @@ public class MipMapVRamBufferedImage extends MipMapBufferedImage { target.scale(resolution, resolution); target.translate(-imageBounds.getMinX(), -imageBounds.getMinY()); try { - source.render(target); + if (colorFilter != null) { + source.render(new Graphics2DWithColorFilter(target, colorFilter)); + } else { + source.render(target); + } } catch (Exception e) { e.printStackTrace(); target.dispose(); @@ -125,7 +133,7 @@ public class MipMapVRamBufferedImage extends MipMapBufferedImage { return image; } - synchronized VolatileImage restore(GraphicsConfiguration gc2) { + synchronized VolatileImage restore(GraphicsConfiguration gc2, ColorFilter colorFilter) { //System.out.println("restoring provider " + imageProvider); VolatileImage image = imageProvider.get(gc2, this.validateResult); @@ -144,12 +152,15 @@ public class MipMapVRamBufferedImage extends MipMapBufferedImage { // } boolean contentsLost = validateResult != VolatileImage.IMAGE_OK || image.contentsLost(); - return contentsLost ? sourceRender(image) : image; + boolean changed = !Objects.equals(colorFilter, previousColorFilter); + previousColorFilter = colorFilter; + return contentsLost || changed ? sourceRender(image, colorFilter) : image; //return contentsLost ? sourceRender(image) : sourceRender(image); } public void paint(Graphics2D g) { - VolatileImage image = restore(g.getDeviceConfiguration()); + ColorFilter colorFilter = (ColorFilter) g.getRenderingHint(G2DRenderingHints.KEY_COLOR_FILTER); + VolatileImage image = restore(g.getDeviceConfiguration(), colorFilter); if (image==null) { g.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_SPEED); @@ -158,7 +169,7 @@ public class MipMapVRamBufferedImage extends MipMapBufferedImage { g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR); try { - source.render(g); + source.render(new Graphics2DWithColorFilter(g, colorFilter)); } catch (Exception e) { // TODO Auto-generated catch block // NOTE: Catching Exception instead of SVGException due to an -- 2.45.2