| /******************************************************************************* |
| * Copyright (c) 2008, 2015 Angelo Zerr and others. |
| * 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: |
| * Angelo Zerr <angelo.zerr@gmail.com> - initial API and implementation |
| * IBM Corporation |
| * Kai Toedter - added radial gradient support |
| * Lars Vogel <Lars.Vogel@vogella.com> - Bug 461688 |
| * Robert Roth <robert.roth.off@gmail.com> - Bug 283255 |
| * Simon Scholz <simon.scholz@vogella.com> - Bug 466646, 461690 |
| *******************************************************************************/ |
| package org.eclipse.e4.ui.css.swt.properties; |
| |
| import java.awt.Graphics2D; |
| import java.awt.MultipleGradientPaint.CycleMethod; |
| import java.awt.RadialGradientPaint; |
| import java.awt.geom.Point2D; |
| import java.awt.image.BufferedImage; |
| import java.awt.image.DataBufferInt; |
| import java.util.ArrayList; |
| import java.util.HashMap; |
| import java.util.List; |
| import java.util.Map; |
| import org.eclipse.e4.ui.css.core.dom.properties.Gradient; |
| import org.eclipse.e4.ui.css.swt.helpers.CSSSWTColorHelper; |
| import org.eclipse.swt.SWT; |
| import org.eclipse.swt.events.DisposeEvent; |
| import org.eclipse.swt.events.DisposeListener; |
| import org.eclipse.swt.graphics.Color; |
| import org.eclipse.swt.graphics.GC; |
| import org.eclipse.swt.graphics.Image; |
| import org.eclipse.swt.graphics.ImageData; |
| import org.eclipse.swt.graphics.PaletteData; |
| import org.eclipse.swt.graphics.Point; |
| import org.eclipse.swt.graphics.RGB; |
| import org.eclipse.swt.graphics.RGBA; |
| import org.eclipse.swt.graphics.Rectangle; |
| import org.eclipse.swt.widgets.Control; |
| import org.eclipse.swt.widgets.Event; |
| import org.eclipse.swt.widgets.Listener; |
| |
| public class GradientBackgroundListener implements Listener { |
| private static Map<Control, GradientBackgroundListener> handlers = new HashMap<Control, GradientBackgroundListener>(); |
| |
| private Gradient grad; |
| private final Control control; |
| private boolean radialGradient; |
| Image gradientImage; |
| |
| private DisposeListener disposeListener = new DisposeListener() { |
| @Override |
| public void widgetDisposed(DisposeEvent e) { |
| GradientBackgroundListener.remove(control); |
| } |
| }; |
| |
| private GradientBackgroundListener(Control control, Gradient grad) { |
| this.grad = grad; |
| this.control = control; |
| control.addListener(SWT.Resize, this); |
| control.addDisposeListener(disposeListener); |
| } |
| |
| public void dispose() { |
| grad = null; |
| if (control != null && !control.isDisposed()) { |
| control.removeListener(SWT.Resize, this); |
| control.removeDisposeListener(disposeListener); |
| if (control.getBackgroundImage() == gradientImage) { |
| control.setBackgroundImage(null); |
| } |
| } |
| if (gradientImage != null && !gradientImage.isDisposed()) { |
| gradientImage.dispose(); |
| } |
| gradientImage = null; |
| } |
| |
| public static void handle(Control control, Gradient grad) { |
| GradientBackgroundListener handler = handlers.get(control); |
| if (handler == null) { |
| handler = new GradientBackgroundListener(control, grad); |
| handlers.put(control, handler); |
| handler.handleEvent(null); |
| } else { |
| handler.grad = grad; |
| handler.handleEvent(null); |
| } |
| } |
| |
| public static void remove(Control control) { |
| GradientBackgroundListener handler = handlers.remove(control); |
| if (handler != null) { |
| handler.dispose(); |
| } |
| } |
| |
| @Override |
| public void handleEvent(Event event) { |
| Point size = control.getSize(); |
| if (size.x <= 0 || size.y <= 0) { |
| return; |
| } |
| |
| // hold onto our old image for disposal, if necessary |
| Image oldImage = control.getBackgroundImage(); |
| if(oldImage != gradientImage) { |
| oldImage = null; |
| } |
| |
| /* |
| * Draw the new background. Radial backgrounds have to be generated |
| * for the full size of the control's size; linear backgrounds are |
| * just a slice for the control's height that is then repeated. |
| */ |
| if (grad.isRadial()) { |
| List<java.awt.Color> colors = new ArrayList<java.awt.Color>(); |
| for (Object rgbObj : grad.getRGBs()) { |
| if (rgbObj instanceof RGBA) { |
| RGBA rgba = (RGBA) rgbObj; |
| java.awt.Color color = new java.awt.Color(rgba.rgb.red, rgba.rgb.green, rgba.rgb.blue, rgba.alpha); |
| colors.add(color); |
| } else if (rgbObj instanceof RGB) { |
| RGB rgb = (RGB) rgbObj; |
| java.awt.Color color = new java.awt.Color(rgb.red, rgb.green, rgb.blue); |
| colors.add(color); |
| } |
| } |
| |
| BufferedImage image = getBufferedImage(size.x, size.y, colors, |
| CSSSWTColorHelper.getPercents(grad)); |
| // long startTime = System.currentTimeMillis(); |
| ImageData imagedata = convertToSWT(image); |
| // System.out.println("Conversion took " |
| // + (System.currentTimeMillis() - startTime) + " ms"); |
| gradientImage = new Image(control.getDisplay(), imagedata); |
| radialGradient = true; |
| } else if (oldImage == null || oldImage.isDisposed() |
| || oldImage.getBounds().height != size.y || radialGradient |
| || event == null) { |
| radialGradient = false; |
| boolean verticalGradient = grad.getVerticalGradient(); |
| int x = verticalGradient? 2 : size.x; |
| int y = verticalGradient ? size.y : 2; |
| gradientImage = new Image(control.getDisplay(), x, y); |
| GC gc = new GC(gradientImage); |
| List<Color> colors = new ArrayList<Color>(); |
| for (Object rgbObj : grad.getRGBs()) { |
| if (rgbObj instanceof RGBA) { |
| RGBA rgba = (RGBA) rgbObj; |
| Color color = new Color(control.getDisplay(), rgba); |
| colors.add(color); |
| } else if (rgbObj instanceof RGB) { |
| RGB rgb = (RGB) rgbObj; |
| Color color = new Color(control.getDisplay(), rgb); |
| colors.add(color); |
| } |
| } |
| fillGradient(gc, new Rectangle(0, 0, x, y), colors, |
| CSSSWTColorHelper.getPercents(grad), grad.getVerticalGradient()); |
| gc.dispose(); |
| for (Color c : colors) { |
| c.dispose(); // Dispose colors too. |
| } |
| } |
| if (gradientImage != null) { |
| control.setBackgroundImage(gradientImage); |
| } |
| if (oldImage != null && oldImage != gradientImage) { |
| oldImage.dispose(); |
| } |
| } |
| |
| /* |
| * Fills a gradient rectangle in the specified gc with the specified colors |
| * and percentages. |
| * |
| * @param gc @param rect @param gradientColors @param gradientPercents |
| * |
| * @param gradientVertical |
| */ |
| private static void fillGradient(GC gc, Rectangle rect, |
| List<Color> gradientColors, int[] gradientPercents, |
| boolean gradientVertical) { |
| Color background = gradientColors.get(gradientColors.size() - 1); |
| if (gradientColors.size() == 1) { |
| if (gradientColors.get(0) != null) { |
| gc.setBackground(gradientColors.get(0)); |
| } |
| gc.fillRectangle(rect.x, rect.y, rect.width, rect.height); |
| } else { |
| Color lastColor = gradientColors.get(0); |
| int pos = (gradientVertical) ? rect.y : rect.x; |
| int loopCount = Math.min(gradientColors.size() - 1, |
| gradientPercents.length); |
| for (int i = 0; i < loopCount; ++i) { |
| gc.setForeground(lastColor); |
| lastColor = gradientColors.get(i + 1); |
| if (lastColor == null) { |
| lastColor = background; |
| } |
| gc.setBackground(lastColor); |
| int grpercent = ((Integer) gradientPercents[i]).intValue(); |
| if (gradientVertical) { |
| final int gradientHeight = (grpercent * rect.height / 100) |
| - (pos - rect.y); |
| gc.fillGradientRectangle(rect.x, pos, rect.width, |
| gradientHeight, true); |
| pos += gradientHeight; |
| } else { |
| final int gradientWidth = (grpercent * rect.width / 100) |
| - (pos - rect.x); |
| gc.fillGradientRectangle(pos, rect.y, gradientWidth, |
| rect.height, false); |
| pos += gradientWidth; |
| } |
| } |
| if (gradientVertical && pos < rect.height) { |
| gc.setBackground(background); |
| gc.fillRectangle(rect.x, pos, rect.width, rect.height - pos); |
| } |
| if (!gradientVertical && pos < rect.width) { |
| gc.setBackground(background); |
| gc.fillRectangle(pos, rect.y, rect.width - pos, rect.height); |
| } |
| } |
| } |
| |
| /** |
| * Returns a BufferedImage that renders a radial gradient. This is a |
| * workaround since SWT does not support radial gradients yet. |
| * |
| * @param width |
| * image width |
| * @param height |
| * image height |
| * @param colors |
| * a list of colors that define the gradients |
| * @param percents |
| * a list of percents that define the percents of above colors |
| * @return the image |
| */ |
| private BufferedImage getBufferedImage(int width, int height, |
| List<java.awt.Color> colors, int[] percents) { |
| java.awt.Color[] colorArray = colors.toArray(new java.awt.Color[] {}); |
| float[] fractions = new float[percents.length + 1]; |
| fractions[0] = 0.0f; |
| for (int i = 1; i <= percents.length; i++) { |
| fractions[i] = percents[i - 1] / 100.0f; |
| } |
| BufferedImage image = new BufferedImage(width, height, |
| BufferedImage.TYPE_INT_RGB); |
| Graphics2D g2 = (Graphics2D) image.getGraphics(); |
| |
| RadialGradientPaint radialGradientPaint = new RadialGradientPaint(new Point2D.Double(width / 2.0, 0), width, |
| new Point2D.Double(width / 2.0, 0.0), fractions, colorArray, CycleMethod.NO_CYCLE); |
| g2.setPaint(radialGradientPaint); |
| |
| g2.fillRect(0, 0, width, height); |
| return image; |
| } |
| |
| /** |
| * Converts a AWT BufferedImage to an SWT ImageData. This is a workaround |
| * since SWT does not support radial gradients yet. |
| * |
| * @param bufferedImage |
| * the source AWT BufferedImage |
| * @return the converted SWT ImageData |
| */ |
| private ImageData convertToSWT(BufferedImage bufferedImage) { |
| int[] bufferedImageData = ((DataBufferInt) bufferedImage.getData() |
| .getDataBuffer()).getData(); |
| ImageData imageData = new ImageData(bufferedImage.getWidth(), |
| bufferedImage.getHeight(), 24, new PaletteData(0xff0000, |
| 0x00ff00, 0x0000ff)); |
| imageData.setPixels(0, 0, bufferedImageData.length, bufferedImageData, |
| 0); |
| return imageData; |
| } |
| } |