|
| 1 | +/******************************************************************************* |
| 2 | + * Copyright (c) 2026 Laurent Caron and others. |
| 3 | + * |
| 4 | + * This program and the accompanying materials |
| 5 | + * are made available under the terms of the Eclipse Public License 2.0 |
| 6 | + * which accompanies this distribution, and is available at |
| 7 | + * https://www.eclipse.org/legal/epl-2.0/ |
| 8 | + * |
| 9 | + * SPDX-License-Identifier: EPL-2.0 |
| 10 | + * |
| 11 | + * Contributors: |
| 12 | + * Neil Wallis (http://www.neilwallis.com/java/water.html) - Original Version |
| 13 | + * Laurent Caron (laurent.caron at gmail dot com) - Conversion to SWT |
| 14 | + * IBM Corporation - adaptation to GraphicsExample |
| 15 | + *******************************************************************************/ |
| 16 | + |
| 17 | +package org.eclipse.swt.examples.graphics; |
| 18 | + |
| 19 | +import org.eclipse.swt.graphics.GC; |
| 20 | +import org.eclipse.swt.graphics.Image; |
| 21 | +import org.eclipse.swt.graphics.ImageData; |
| 22 | +import org.eclipse.swt.graphics.Rectangle; |
| 23 | +import org.eclipse.swt.widgets.Composite; |
| 24 | + |
| 25 | +/** |
| 26 | + * This tab displays a ripple effect on an image. |
| 27 | + */ |
| 28 | +public class RippleTab extends AnimatedGraphicsTab { |
| 29 | + |
| 30 | + private Image sourceImage; |
| 31 | + private ImageData offImageData; |
| 32 | + private int width, height; |
| 33 | + private int hwidth, hheight; |
| 34 | + private int riprad; |
| 35 | + private int size; |
| 36 | + private short[] ripplemap; |
| 37 | + private int[] ripple; |
| 38 | + private int[] texture; |
| 39 | + private int oldind; |
| 40 | + private int newind; |
| 41 | + private Image outputImage; |
| 42 | + |
| 43 | + public RippleTab(GraphicsExample example) { |
| 44 | + super(example); |
| 45 | + } |
| 46 | + |
| 47 | + @Override |
| 48 | + public String getCategory() { |
| 49 | + return GraphicsExample.getResourceString("Misc"); //$NON-NLS-1$ |
| 50 | + } |
| 51 | + |
| 52 | + @Override |
| 53 | + public String getText() { |
| 54 | + return GraphicsExample.getResourceString("Ripple"); //$NON-NLS-1$ |
| 55 | + } |
| 56 | + |
| 57 | + @Override |
| 58 | + public String getDescription() { |
| 59 | + return GraphicsExample.getResourceString("RippleDescription"); //$NON-NLS-1$ |
| 60 | + } |
| 61 | + |
| 62 | + @Override |
| 63 | + public int getInitialAnimationTime() { |
| 64 | + return 30; |
| 65 | + } |
| 66 | + |
| 67 | + @Override |
| 68 | + public void createControlPanel(Composite parent) { |
| 69 | + super.createControlPanel(parent); |
| 70 | + // Add mouse listener to the canvas to disturb the water |
| 71 | + example.canvas.addMouseMoveListener(e -> { |
| 72 | + if (example.getTab() == RippleTab.this) { |
| 73 | + Rectangle rect = example.canvas.getClientArea(); |
| 74 | + disturb(e.x, e.y, rect.width, rect.height); |
| 75 | + } |
| 76 | + }); |
| 77 | + } |
| 78 | + |
| 79 | + @Override |
| 80 | + public void dispose() { |
| 81 | + if (sourceImage != null) { |
| 82 | + sourceImage.dispose(); |
| 83 | + sourceImage = null; |
| 84 | + } |
| 85 | + if (outputImage != null) { |
| 86 | + outputImage.dispose(); |
| 87 | + outputImage = null; |
| 88 | + } |
| 89 | + } |
| 90 | + |
| 91 | + @Override |
| 92 | + public void next(int width, int height) { |
| 93 | + if (texture == null) return; |
| 94 | + if (Math.random() > 0.98) { |
| 95 | + disturb((int)(Math.random() * width), (int)(Math.random() * height), width, height); |
| 96 | + } |
| 97 | + newframe(); |
| 98 | + } |
| 99 | + |
| 100 | + @Override |
| 101 | + public void paint(GC gc, int width, int height) { |
| 102 | + if (!example.checkAdvancedGraphics()) return; |
| 103 | + |
| 104 | + if (sourceImage == null) { |
| 105 | + sourceImage = example.loadImage(gc.getDevice(), "ocean.jpg"); |
| 106 | + if (sourceImage == null) return; |
| 107 | + |
| 108 | + ImageData imgData = sourceImage.getImageData(); |
| 109 | + this.width = imgData.width; |
| 110 | + this.height = imgData.height; |
| 111 | + hwidth = this.width >> 1; |
| 112 | + hheight = this.height >> 1; |
| 113 | + riprad = 3; |
| 114 | + |
| 115 | + size = this.width * (this.height + 2) * 2; |
| 116 | + ripplemap = new short[size]; |
| 117 | + ripple = new int[this.width * this.height]; |
| 118 | + texture = new int[this.width * this.height]; |
| 119 | + oldind = this.width; |
| 120 | + newind = this.width * (this.height + 3); |
| 121 | + |
| 122 | + imgData.getPixels(0, 0, this.width * this.height, texture, 0); |
| 123 | + offImageData = new ImageData(this.width, this.height, imgData.depth, imgData.palette); |
| 124 | + } |
| 125 | + |
| 126 | + offImageData.setPixels(0, 0, this.width * this.height, ripple, 0); |
| 127 | + |
| 128 | + if (outputImage != null) outputImage.dispose(); |
| 129 | + outputImage = new Image(gc.getDevice(), offImageData); |
| 130 | + |
| 131 | + int x = (width - this.width) / 2; |
| 132 | + int y = (height - this.height) / 2; |
| 133 | + gc.drawImage(outputImage, x, y); |
| 134 | + } |
| 135 | + |
| 136 | + private void newframe() { |
| 137 | + int i, a, b; |
| 138 | + // Toggle maps each frame |
| 139 | + i = oldind; |
| 140 | + oldind = newind; |
| 141 | + newind = i; |
| 142 | + |
| 143 | + i = 0; |
| 144 | + int mapind = oldind; |
| 145 | + for (int y = 0; y < height; y++) { |
| 146 | + for (int x = 0; x < width; x++) { |
| 147 | + short data = (short) (ripplemap[mapind - width] + ripplemap[mapind + width] + ripplemap[mapind - 1] + ripplemap[mapind + 1] >> 1); |
| 148 | + data -= ripplemap[newind + i]; |
| 149 | + data -= data >> 5; |
| 150 | + ripplemap[newind + i] = data; |
| 151 | + |
| 152 | + // where data=0 then still, where data>0 then wave |
| 153 | + data = (short) (1024 - data); |
| 154 | + |
| 155 | + // offsets |
| 156 | + a = (x - hwidth) * data / 1024 + hwidth; |
| 157 | + b = (y - hheight) * data / 1024 + hheight; |
| 158 | + |
| 159 | + // bounds check |
| 160 | + if (a >= width) { |
| 161 | + a = width - 1; |
| 162 | + } |
| 163 | + if (a < 0) { |
| 164 | + a = 0; |
| 165 | + } |
| 166 | + if (b >= height) { |
| 167 | + b = height - 1; |
| 168 | + } |
| 169 | + if (b < 0) { |
| 170 | + b = 0; |
| 171 | + } |
| 172 | + |
| 173 | + ripple[i] = texture[a + b * width]; |
| 174 | + mapind++; |
| 175 | + i++; |
| 176 | + } |
| 177 | + } |
| 178 | + } |
| 179 | + |
| 180 | + private void disturb(int dx, int dy, int canvasWidth, int canvasHeight) { |
| 181 | + if (texture == null) return; |
| 182 | + |
| 183 | + // Adjust dx, dy based on center alignment in paint() |
| 184 | + int xOffset = (canvasWidth - width) / 2; |
| 185 | + int yOffset = (canvasHeight - height) / 2; |
| 186 | + |
| 187 | + int x = dx - xOffset; |
| 188 | + int y = dy - yOffset; |
| 189 | + |
| 190 | + for (int j = y - riprad; j < y + riprad; j++) { |
| 191 | + for (int k = x - riprad; k < x + riprad; k++) { |
| 192 | + if (j >= 0 && j < height && k >= 0 && k < width) { |
| 193 | + ripplemap[oldind + j * width + k] += 512; |
| 194 | + } |
| 195 | + } |
| 196 | + } |
| 197 | + } |
| 198 | +} |
0 commit comments