diff --git a/examples/org.eclipse.swt.examples/src/examples_graphics.properties b/examples/org.eclipse.swt.examples/src/examples_graphics.properties index 2b76d314bd..10f3c72352 100644 --- a/examples/org.eclipse.swt.examples/src/examples_graphics.properties +++ b/examples/org.eclipse.swt.examples/src/examples_graphics.properties @@ -238,3 +238,21 @@ FlatTextDescription=This is a miscellaneous demonstration of an animated flat te Lens=Lens LensDescription=This is a miscellaneous demonstration of an animated spherical lens distortion effect that bounces across an image, magnifying the area beneath the lens. + +BlockEffect=Block Effect +BlockEffectDescription=This is a miscellaneous demonstration of an animated block pixelation effect that oscillates block size from single pixels to large blocks and back on an image. + +TwirlEffect=Twirl Effect +TwirlEffectDescription=This is a miscellaneous demonstration of an animated twirl distortion effect that rotates pixels around the center of an image with increasing and decreasing twirl angle. + +SineWave=Sine Wave +SineWaveDescription=This is a miscellaneous demonstration of an animated sine wave distortion effect that cycles through 12 different wave configurations applied to an image. + +Sky=Sky +SkyDescription=This is a miscellaneous demonstration of an animated sky/landscape effect using perspective-corrected texture mapping with scrolling sprites and shadows. + +UnlimitedBalls=Unlimited Balls +UnlimitedBallsDescription=This is a miscellaneous demonstration of an animated unlimited balls effect where ball sprites are drawn along Lissajous-like curves using multiple offscreen buffers. + +Warp=Warp +WarpDescription=This is a miscellaneous demonstration of an animated warp distortion effect that uses a precomputed distortion lookup table to render a texture with animated warping. diff --git a/examples/org.eclipse.swt.examples/src/org/eclipse/swt/examples/graphics/BlockEffectTab.java b/examples/org.eclipse.swt.examples/src/org/eclipse/swt/examples/graphics/BlockEffectTab.java new file mode 100644 index 0000000000..0a43d00066 --- /dev/null +++ b/examples/org.eclipse.swt.examples/src/org/eclipse/swt/examples/graphics/BlockEffectTab.java @@ -0,0 +1,183 @@ +/******************************************************************************* + * Copyright (c) 2018 Laurent Caron and others. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Jerry Huxtable - Original Version + * Laurent Caron (laurent.caron at gmail dot com) - Conversion to SWT + * IBM Corporation - adaptation to GraphicsExample + *******************************************************************************/ + +package org.eclipse.swt.examples.graphics; + +import org.eclipse.swt.graphics.GC; +import org.eclipse.swt.graphics.Image; +import org.eclipse.swt.graphics.ImageData; + +/** + * This tab displays an animated block pixelation effect that oscillates block + * size from single pixels to large blocks and back on an image. + */ +public class BlockEffectTab extends AnimatedGraphicsTab { + + private ImageData sourceImage; + private ImageData imageData; + private Image outputImage; + private int imgWidth, imgHeight; + private int blockSize; + private int direction; + + public BlockEffectTab(GraphicsExample example) { + super(example); + } + + @Override + public String getCategory() { + return GraphicsExample.getResourceString("Misc"); //$NON-NLS-1$ + } + + @Override + public String getText() { + return GraphicsExample.getResourceString("BlockEffect"); //$NON-NLS-1$ + } + + @Override + public String getDescription() { + return GraphicsExample.getResourceString("BlockEffectDescription"); //$NON-NLS-1$ + } + + @Override + public int getInitialAnimationTime() { + return 10; + } + + @Override + public void dispose() { + if (outputImage != null) { + outputImage.dispose(); + outputImage = null; + } + } + + @Override + public void next(int width, int height) { + if (sourceImage == null) { + return; + } + + imageData = filter(sourceImage, blockSize); + blockSize += direction; + + if (blockSize <= 1) { + direction = 1; + blockSize = 1; + } + + if (blockSize >= imgWidth / 4) { + direction = -1; + } + } + + @Override + public void paint(GC gc, int width, int height) { + if (!example.checkAdvancedGraphics()) return; + + if (sourceImage == null) { + Image loaded = example.loadImage(gc.getDevice(), "flower.jpg"); //$NON-NLS-1$ + if (loaded == null) return; + sourceImage = loaded.getImageData(); + imgWidth = sourceImage.width; + imgHeight = sourceImage.height; + + int[] pixels = new int[imgWidth * imgHeight]; + sourceImage.getPixels(0, 0, imgWidth * imgHeight, pixels, 0); + imageData = new ImageData(imgWidth, imgHeight, sourceImage.depth, sourceImage.palette); + imageData.setPixels(0, 0, imgWidth * imgHeight, pixels, 0); + + blockSize = 1; + direction = 1; + } + + if (imageData == null) { + return; + } + + if (outputImage != null) { + outputImage.dispose(); + } + outputImage = new Image(gc.getDevice(), imageData); + + int x = (width - imgWidth) / 2; + int y = (height - imgHeight) / 2; + gc.drawImage(outputImage, x, y); + } + + private ImageData filter(ImageData src, int size) { + if (size <= 1) { + int[] pixels = new int[imgWidth * imgHeight]; + src.getPixels(0, 0, imgWidth * imgHeight, pixels, 0); + ImageData copy = new ImageData(imgWidth, imgHeight, src.depth, src.palette); + copy.setPixels(0, 0, imgWidth * imgHeight, pixels, 0); + return copy; + } + + ImageData dst = new ImageData(imgWidth, imgHeight, src.depth, src.palette); + int[] pixels = new int[size * size]; + + for (int y = 0; y < imgHeight; y += size) { + for (int x = 0; x < imgWidth; x += size) { + int w = Math.min(size, imgWidth - x); + int h = Math.min(size, imgHeight - y); + int t = w * h; + getRGB(src, x, y, w, h, pixels, 0, w); + int r = 0, g = 0, b = 0; + int i = 0; + for (int by = 0; by < h; by++) { + for (int bx = 0; bx < w; bx++) { + int argb = pixels[i]; + r += (argb >> 16) & 0xff; + g += (argb >> 8) & 0xff; + b += argb & 0xff; + i++; + } + } + int argb = ((r / t) << 16) | ((g / t) << 8) | (b / t); + i = 0; + for (int by = 0; by < h; by++) { + for (int bx = 0; bx < w; bx++) { + pixels[i] = (pixels[i] & 0xff000000) | argb; + i++; + } + } + setRGB(dst, x, y, w, h, pixels, 0, w); + } + } + return dst; + } + + private static void getRGB(ImageData image, int startX, int startY, int w, int h, int[] pixels, int offset, int scansize) { + int yoff = offset; + for (int y = startY; y < startY + h; y++, yoff += scansize) { + int off = yoff; + for (int x = startX; x < startX + w; x++) { + pixels[off++] = image.getPixel(x, y); + } + } + } + + private static void setRGB(ImageData image, int startX, int startY, int w, int h, int[] pixels, int offset, int scansize) { + int yoff = offset; + for (int y = startY; y < startY + h; y++, yoff += scansize) { + int off = yoff; + for (int x = startX; x < startX + w; x++) { + image.setPixel(x, y, pixels[off++]); + } + } + } +} diff --git a/examples/org.eclipse.swt.examples/src/org/eclipse/swt/examples/graphics/DEMO2.png b/examples/org.eclipse.swt.examples/src/org/eclipse/swt/examples/graphics/DEMO2.png new file mode 100644 index 0000000000..b84c9f3246 Binary files /dev/null and b/examples/org.eclipse.swt.examples/src/org/eclipse/swt/examples/graphics/DEMO2.png differ diff --git a/examples/org.eclipse.swt.examples/src/org/eclipse/swt/examples/graphics/GraphicsExample.java b/examples/org.eclipse.swt.examples/src/org/eclipse/swt/examples/graphics/GraphicsExample.java index 67477fe2a2..b9f0965270 100644 --- a/examples/org.eclipse.swt.examples/src/org/eclipse/swt/examples/graphics/GraphicsExample.java +++ b/examples/org.eclipse.swt.examples/src/org/eclipse/swt/examples/graphics/GraphicsExample.java @@ -495,6 +495,12 @@ GraphicsTab[] createTabs() { new BumpMappingTab(this), new FlatTextTab(this), new LensTab(this), + new BlockEffectTab(this), + new TwirlEffectTab(this), + new SineWaveTab(this), + new SkyTab(this), + new UnlimitedBallsTab(this), + new WarpTab(this), }; } diff --git a/examples/org.eclipse.swt.examples/src/org/eclipse/swt/examples/graphics/SineWaveTab.java b/examples/org.eclipse.swt.examples/src/org/eclipse/swt/examples/graphics/SineWaveTab.java new file mode 100644 index 0000000000..0739d96cd2 --- /dev/null +++ b/examples/org.eclipse.swt.examples/src/org/eclipse/swt/examples/graphics/SineWaveTab.java @@ -0,0 +1,189 @@ +/******************************************************************************* + * Copyright (c) 2018 Laurent Caron and others. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * W.P. van Paassen - Original Version + * Laurent Caron (laurent.caron at gmail dot com) - Conversion to SWT + * IBM Corporation - adaptation to GraphicsExample + *******************************************************************************/ + +package org.eclipse.swt.examples.graphics; + +import org.eclipse.swt.graphics.GC; +import org.eclipse.swt.graphics.Image; +import org.eclipse.swt.graphics.ImageData; + +/** + * This tab displays an animated sine wave distortion effect that cycles + * through 12 different wave configurations applied to an image. + */ +public class SineWaveTab extends AnimatedGraphicsTab { + + private static final int MAX_EFFECTS = 12; + + private ImageData sourceImage; + private ImageData imageData; + private Image outputImage; + private int imgWidth, imgHeight; + private int sinIndex; + private SinEffect[] sineEffects; + private int currentEffect; + + public SineWaveTab(GraphicsExample example) { + super(example); + } + + @Override + public String getCategory() { + return GraphicsExample.getResourceString("Misc"); //$NON-NLS-1$ + } + + @Override + public String getText() { + return GraphicsExample.getResourceString("SineWave"); //$NON-NLS-1$ + } + + @Override + public String getDescription() { + return GraphicsExample.getResourceString("SineWaveDescription"); //$NON-NLS-1$ + } + + @Override + public int getInitialAnimationTime() { + return 10; + } + + @Override + public void dispose() { + if (outputImage != null) { + outputImage.dispose(); + outputImage = null; + } + } + + @Override + public void next(int width, int height) { + if (sourceImage == null) { + return; + } + + int sinBackup = sinIndex; + SinEffect effect = sineEffects[currentEffect]; + + if (effect.horizontal) { + for (int i = 0; i < imgHeight; i++) { + int offset = effect.sineTable[sinBackup]; + if (effect.horizontal && (i % 2 == 1)) { + offset = -offset; + } + copyRow(sourceImage, i, offset, imageData, i); + sinBackup += effect.indexAdd; + sinBackup &= 511; + } + } else { + for (int i = 0; i < imgHeight; i++) { + int offset = effect.sineTable[sinBackup]; + copyRow(sourceImage, i, offset, imageData, i); + sinBackup += effect.indexAdd; + sinBackup &= 511; + } + } + + sinIndex += 6; + if (sinIndex > 511) { + sinIndex = 0; + currentEffect++; + currentEffect %= MAX_EFFECTS; + } + } + + @Override + public void paint(GC gc, int width, int height) { + if (!example.checkAdvancedGraphics()) return; + + if (sourceImage == null) { + Image loaded = example.loadImage(gc.getDevice(), "tuxblackbg.png"); //$NON-NLS-1$ + if (loaded == null) return; + sourceImage = loaded.getImageData(); + imgWidth = sourceImage.width; + imgHeight = sourceImage.height; + + int[] pixels = new int[imgWidth * imgHeight]; + sourceImage.getPixels(0, 0, imgWidth * imgHeight, pixels, 0); + imageData = new ImageData(imgWidth, imgHeight, sourceImage.depth, sourceImage.palette); + imageData.setPixels(0, 0, imgWidth * imgHeight, pixels, 0); + + sinIndex = 0; + currentEffect = 0; + initEffects(); + } + + if (imageData == null) { + return; + } + + if (outputImage != null) { + outputImage.dispose(); + } + outputImage = new Image(gc.getDevice(), imageData); + + int x = (width - imgWidth) / 2; + int y = (height - imgHeight) / 2; + gc.drawImage(outputImage, x, y); + } + + private void initEffects() { + sineEffects = new SinEffect[MAX_EFFECTS]; + for (int i = 0; i < MAX_EFFECTS; i++) { + sineEffects[i] = new SinEffect(); + } + + double[] amplitudes = { 8, 16, 20, 32, 64, 128, 256, 128, 64, 44, 32, 8 }; + for (int i = 0; i < 512; i++) { + float rad = (float) (i * 0.0174532 * 0.703125); + for (int j = 0; j < MAX_EFFECTS; j++) { + sineEffects[j].sineTable[i] = (short) (Math.sin(rad) * amplitudes[j]); + } + } + + int[] indexAdds = { 2, 4, 3, 5, 2, 2, 4, 1, 2, 8, 3, 2 }; + boolean[] horizontals = { false, false, true, false, false, true, false, false, true, false, true, true }; + for (int i = 0; i < MAX_EFFECTS; i++) { + sineEffects[i].indexAdd = indexAdds[i]; + sineEffects[i].horizontal = horizontals[i]; + } + } + + private void copyRow(ImageData src, int srcY, int offset, ImageData dest, int destY) { + if (offset >= 0) { + int count = Math.min(imgWidth, imgWidth - offset); + if (count > 0) { + int[] pixels = new int[count]; + src.getPixels(offset, srcY, count, pixels, 0); + dest.setPixels(0, destY, count, pixels, 0); + } + } else { + int srcStart = 0; + int destStart = -offset; + int count = Math.min(imgWidth, imgWidth + offset); + if (count > 0 && destStart < imgWidth) { + int[] pixels = new int[count]; + src.getPixels(srcStart, srcY, count, pixels, 0); + dest.setPixels(destStart, destY, count, pixels, 0); + } + } + } + + private static class SinEffect { + short[] sineTable = new short[512]; + int indexAdd; + boolean horizontal; + } +} diff --git a/examples/org.eclipse.swt.examples/src/org/eclipse/swt/examples/graphics/SkyTab.java b/examples/org.eclipse.swt.examples/src/org/eclipse/swt/examples/graphics/SkyTab.java new file mode 100644 index 0000000000..e4923068c1 --- /dev/null +++ b/examples/org.eclipse.swt.examples/src/org/eclipse/swt/examples/graphics/SkyTab.java @@ -0,0 +1,209 @@ +/******************************************************************************* + * Copyright (c) 2018 Laurent Caron and others. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Josh83 - Original Version + * Laurent Caron (laurent.caron at gmail dot com) - Conversion to SWT + * IBM Corporation - adaptation to GraphicsExample + *******************************************************************************/ + +package org.eclipse.swt.examples.graphics; + +import org.eclipse.swt.graphics.Color; +import org.eclipse.swt.graphics.GC; +import org.eclipse.swt.graphics.Image; +import org.eclipse.swt.graphics.ImageData; + +/** + * This tab displays an animated sky/landscape effect using perspective-corrected + * texture mapping with scrolling sprites and shadows on a floor and ceiling. + */ +public class SkyTab extends AnimatedGraphicsTab { + + private static final int RENDER_WIDTH = 320; + private static final int RENDER_HEIGHT = 200; + + private ImageData sprite; + private ImageData imageData; + private Image outputImage; + private int t; + + public SkyTab(GraphicsExample example) { + super(example); + } + + @Override + public String getCategory() { + return GraphicsExample.getResourceString("Misc"); //$NON-NLS-1$ + } + + @Override + public String getText() { + return GraphicsExample.getResourceString("Sky"); //$NON-NLS-1$ + } + + @Override + public String getDescription() { + return GraphicsExample.getResourceString("SkyDescription"); //$NON-NLS-1$ + } + + @Override + public int getInitialAnimationTime() { + return 10; + } + + @Override + public void dispose() { + if (outputImage != null) { + outputImage.dispose(); + outputImage = null; + } + } + + @Override + public void next(int width, int height) { + if (sprite == null) { + return; + } + + // Clear with blue background + Color blue = new Color(0, 0, 255); + Image tmp = new Image(null, imageData); + GC tmpGC = new GC(tmp); + tmpGC.setBackground(blue); + tmpGC.fillRectangle(0, 0, RENDER_WIDTH, RENDER_HEIGHT); + tmpGC.dispose(); + imageData = tmp.getImageData(); + tmp.dispose(); + + t += 10; + + // Floor and ceiling textures + for (int z = 1; z < 91; z++) { + int u = 0; + int difx = 2 * z + 10; + int ustep = (160 << 7) / difx; + + for (int x = 0; x < 160; x++) { + int xnew = (u >> 7) - ((u >> 14) << 7); + int ynew = 7000 / z + t - ((7000 / z + t >> 7) << 7); + int xnew2 = (u >> 9) - ((u >> 16) << 7); + int ynew2 = 2048 / z + (t >> 5) - ((2048 / z + (t >> 5) >> 7) << 7); + + safeSetPixel(160 + x, 90 - z, safeGetPixel(sprite, 255 - xnew2, ynew2)); + safeSetPixel(160 - x, 90 - z, safeGetPixel(sprite, 128 + xnew2, ynew2)); + safeSetPixel(160 + x, 91 + z, safeGetPixel(sprite, 127 - xnew, ynew)); + safeSetPixel(160 - x, 91 + z, safeGetPixel(sprite, xnew, ynew)); + + u += ustep; + } + } + + // Sprites and shadows + for (int b = 0; b < 200; b += 10) { + int e = (int) (110 + 20 * Math.cos((b + t) * 0.01) + 15 * Math.sin((4 * b + t) * 0.01)); + int f = (int) (160 + 50 * Math.sin((2 * b + t) * 0.01) + 25 * Math.cos((2 * b + t) * 0.01)); + drawSprite(f, e, 127, 127, 0.1f, 0.1f); + drawShade(f, (e >> 2) + 135, 127, 127, 0.1f, 0.04f); + } + + // Scrolling text bar + for (int d = 0; d < 320; d++) { + int t2 = t / 9 + d - ((t / 9 + d >> 10) << 10); + int ys = t2 >> 7; + int xs = t2 - (ys << 7); + + for (int c = 0; c < 15; c++) { + int sy = ys * 16 + 128 + c; + int sx = xs + 128; + if (sx >= 0 && sx < sprite.width && sy >= 0 && sy < sprite.height) { + safeSetPixel(d, c + 184, sprite.getPixel(sx, sy)); + } + } + } + } + + @Override + public void paint(GC gc, int width, int height) { + if (!example.checkAdvancedGraphics()) return; + + if (sprite == null) { + Image loaded = example.loadImage(gc.getDevice(), "DEMO2.png"); //$NON-NLS-1$ + if (loaded == null) return; + sprite = loaded.getImageData(); + + imageData = new ImageData(RENDER_WIDTH, RENDER_HEIGHT, sprite.depth, sprite.palette); + t = 0; + } + + if (imageData == null) { + return; + } + + if (outputImage != null) { + outputImage.dispose(); + } + outputImage = new Image(gc.getDevice(), imageData); + + int x = (width - RENDER_WIDTH) / 2; + int y = (height - RENDER_HEIGHT) / 2; + gc.drawImage(outputImage, x, y); + } + + private int safeGetPixel(ImageData img, int x, int y) { + x = Math.max(0, Math.min(x, img.width - 1)); + y = Math.max(0, Math.min(y, img.height - 1)); + return img.getPixel(x, y); + } + + private void safeSetPixel(int x, int y, int pixel) { + if (x >= 0 && x < imageData.width && y >= 0 && y < imageData.height) { + imageData.setPixel(x, y, pixel); + } + } + + private void drawSprite(int x, int y, int xs, int ys, float rx, float ry) { + int xn = Math.max(2, (int) (xs * rx)); + int yn = Math.max(2, (int) (ys * ry)); + int an = (ys << 8) / yn; + int bn = (xs << 8) / xn; + int rn = x - (xn >> 1); + int tn = y - (yn >> 1); + for (int ut = 1; ut < xn; ut++) { + for (int vt = 1; vt < yn; vt++) { + int sx = (ut * bn) >> 8; + int sy = ((vt * an) >> 8) + 128; + int pixel = safeGetPixel(sprite, sx, sy); + if (pixel != 255) { + safeSetPixel(ut + rn, vt + tn, pixel); + } + } + } + } + + private void drawShade(int x, int y, int xs, int ys, float rx, float ry) { + int xn = Math.max(2, (int) (xs * rx)); + int yn = Math.max(2, (int) (ys * ry)); + int an = (ys << 8) / yn; + int bn = (xs << 8) / xn; + int rn = x - (xn >> 1); + int tn = y - (yn >> 1); + for (int ut = 1; ut < xn; ut++) { + for (int vt = 1; vt < yn; vt++) { + int sx = (ut * bn) >> 8; + int sy = ((vt * an) >> 8) + 128; + int pixel = safeGetPixel(sprite, sx, sy); + if (pixel != 255) { + safeSetPixel(ut + rn, vt + tn, pixel); + } + } + } + } +} diff --git a/examples/org.eclipse.swt.examples/src/org/eclipse/swt/examples/graphics/TwirlEffectTab.java b/examples/org.eclipse.swt.examples/src/org/eclipse/swt/examples/graphics/TwirlEffectTab.java new file mode 100644 index 0000000000..fbd0a6acfa --- /dev/null +++ b/examples/org.eclipse.swt.examples/src/org/eclipse/swt/examples/graphics/TwirlEffectTab.java @@ -0,0 +1,211 @@ +/******************************************************************************* + * Copyright (c) 2018 Laurent Caron and others. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Jerry Huxtable - Original Version + * Laurent Caron (laurent.caron at gmail dot com) - Conversion to SWT + * IBM Corporation - adaptation to GraphicsExample + *******************************************************************************/ + +package org.eclipse.swt.examples.graphics; + +import org.eclipse.swt.graphics.GC; +import org.eclipse.swt.graphics.Image; +import org.eclipse.swt.graphics.ImageData; + +/** + * This tab displays an animated twirl distortion effect that rotates pixels + * around the center of an image with increasing and decreasing twirl angle. + */ +public class TwirlEffectTab extends AnimatedGraphicsTab { + + private static final float STEP = 0.1f; + + private ImageData sourceImage; + private ImageData imageData; + private Image outputImage; + private int imgWidth, imgHeight; + private float angle; + private float icentreX, icentreY; + private float radius, radius2; + private float direction; + + public TwirlEffectTab(GraphicsExample example) { + super(example); + } + + @Override + public String getCategory() { + return GraphicsExample.getResourceString("Misc"); //$NON-NLS-1$ + } + + @Override + public String getText() { + return GraphicsExample.getResourceString("TwirlEffect"); //$NON-NLS-1$ + } + + @Override + public String getDescription() { + return GraphicsExample.getResourceString("TwirlEffectDescription"); //$NON-NLS-1$ + } + + @Override + public int getInitialAnimationTime() { + return 10; + } + + @Override + public void dispose() { + if (outputImage != null) { + outputImage.dispose(); + outputImage = null; + } + } + + @Override + public void next(int width, int height) { + if (sourceImage == null) { + return; + } + + imageData = filter(sourceImage); + angle += direction; + + if (angle < -15.7f) { + direction = STEP; + } + if (angle > 15.7f) { + direction = -STEP; + } + } + + @Override + public void paint(GC gc, int width, int height) { + if (!example.checkAdvancedGraphics()) return; + + if (sourceImage == null) { + Image loaded = example.loadImage(gc.getDevice(), "flower.jpg"); //$NON-NLS-1$ + if (loaded == null) return; + sourceImage = loaded.getImageData(); + imgWidth = sourceImage.width; + imgHeight = sourceImage.height; + + int[] pixels = new int[imgWidth * imgHeight]; + sourceImage.getPixels(0, 0, imgWidth * imgHeight, pixels, 0); + imageData = new ImageData(imgWidth, imgHeight, sourceImage.depth, sourceImage.palette); + imageData.setPixels(0, 0, imgWidth * imgHeight, pixels, 0); + + angle = 0; + direction = STEP; + icentreX = imgWidth * 0.5f; + icentreY = imgHeight * 0.5f; + radius = 100; + radius2 = radius * radius; + } + + if (imageData == null) { + return; + } + + if (outputImage != null) { + outputImage.dispose(); + } + outputImage = new Image(gc.getDevice(), imageData); + + int x = (width - imgWidth) / 2; + int y = (height - imgHeight) / 2; + gc.drawImage(outputImage, x, y); + } + + private ImageData filter(ImageData src) { + ImageData dst = new ImageData(imgWidth, imgHeight, src.depth, src.palette); + int[] inPixels = new int[imgWidth * imgHeight]; + src.getPixels(0, 0, imgWidth * imgHeight, inPixels, 0); + + int[] outPixels = new int[imgWidth]; + float[] out = new float[2]; + + for (int y = 0; y < imgHeight; y++) { + for (int x = 0; x < imgWidth; x++) { + transformInverse(x, y, out); + int srcX = (int) Math.floor(out[0]); + int srcY = (int) Math.floor(out[1]); + float xWeight = out[0] - srcX; + float yWeight = out[1] - srcY; + int nw, ne, sw, se; + + if (srcX >= 0 && srcX < imgWidth - 1 && srcY >= 0 && srcY < imgHeight - 1) { + int i = imgWidth * srcY + srcX; + nw = inPixels[i]; + ne = inPixels[i + 1]; + sw = inPixels[i + imgWidth]; + se = inPixels[i + imgWidth + 1]; + } else { + nw = getPixel(inPixels, srcX, srcY); + ne = getPixel(inPixels, srcX + 1, srcY); + sw = getPixel(inPixels, srcX, srcY + 1); + se = getPixel(inPixels, srcX + 1, srcY + 1); + } + outPixels[x] = bilinearInterpolate(xWeight, yWeight, nw, ne, sw, se); + } + setRGB(dst, 0, y, imgWidth, 1, outPixels, 0, imgWidth); + } + return dst; + } + + private int getPixel(int[] pixels, int x, int y) { + return pixels[clamp(y, 0, imgHeight - 1) * imgWidth + clamp(x, 0, imgWidth - 1)]; + } + + private void transformInverse(int x, int y, float[] out) { + float dx = x - icentreX; + float dy = y - icentreY; + float distance = dx * dx + dy * dy; + if (distance > radius2) { + out[0] = x; + out[1] = y; + } else { + distance = (float) Math.sqrt(distance); + float a = (float) Math.atan2(dy, dx) + angle * (radius - distance) / radius; + out[0] = icentreX + distance * (float) Math.cos(a); + out[1] = icentreY + distance * (float) Math.sin(a); + } + } + + private static int bilinearInterpolate(float x, float y, int nw, int ne, int sw, int se) { + float cx = 1.0f - x; + float cy = 1.0f - y; + + int r = (int) (cy * (cx * ((nw >> 16) & 0xff) + x * ((ne >> 16) & 0xff)) + + y * (cx * ((sw >> 16) & 0xff) + x * ((se >> 16) & 0xff))); + int g = (int) (cy * (cx * ((nw >> 8) & 0xff) + x * ((ne >> 8) & 0xff)) + + y * (cx * ((sw >> 8) & 0xff) + x * ((se >> 8) & 0xff))); + int b = (int) (cy * (cx * (nw & 0xff) + x * (ne & 0xff)) + + y * (cx * (sw & 0xff) + x * (se & 0xff))); + int a = (int) (cy * (cx * ((nw >> 24) & 0xff) + x * ((ne >> 24) & 0xff)) + + y * (cx * ((sw >> 24) & 0xff) + x * ((se >> 24) & 0xff))); + + return (a << 24) | (r << 16) | (g << 8) | b; + } + + private static int clamp(int x, int a, int b) { + return x < a ? a : x > b ? b : x; + } + + private static void setRGB(ImageData image, int startX, int startY, int w, int h, int[] pixels, int offset, int scansize) { + int yoff = offset; + for (int y = startY; y < startY + h; y++, yoff += scansize) { + int off = yoff; + for (int x = startX; x < startX + w; x++) { + image.setPixel(x, y, pixels[off++]); + } + } + } +} diff --git a/examples/org.eclipse.swt.examples/src/org/eclipse/swt/examples/graphics/UnlimitedBallsTab.java b/examples/org.eclipse.swt.examples/src/org/eclipse/swt/examples/graphics/UnlimitedBallsTab.java new file mode 100644 index 0000000000..023cf0b7ed --- /dev/null +++ b/examples/org.eclipse.swt.examples/src/org/eclipse/swt/examples/graphics/UnlimitedBallsTab.java @@ -0,0 +1,189 @@ +/******************************************************************************* + * Copyright (c) 2018 Laurent Caron and others. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Laurent Caron (laurent.caron at gmail dot com) - Conversion to SWT + * IBM Corporation - adaptation to GraphicsExample + *******************************************************************************/ + +package org.eclipse.swt.examples.graphics; + +import org.eclipse.swt.graphics.Color; +import org.eclipse.swt.graphics.GC; +import org.eclipse.swt.graphics.Image; + +/** + * This tab displays an animated unlimited balls effect where ball sprites are + * drawn along Lissajous-like curves using multiple offscreen buffers. + */ +public class UnlimitedBallsTab extends AnimatedGraphicsTab { + + private Image[] offscreenImages; + private Image ball; + private int xCenter, yCenter; + private float xRadius, yRadius; + private int count; + private int current; + private int xspeed, yspeed, xrspeed, yrspeed, xstart, ystart, xrstart, yrstart; + private int currentShape; + private int canvasWidth, canvasHeight; + + public UnlimitedBallsTab(GraphicsExample example) { + super(example); + } + + @Override + public String getCategory() { + return GraphicsExample.getResourceString("Misc"); //$NON-NLS-1$ + } + + @Override + public String getText() { + return GraphicsExample.getResourceString("UnlimitedBalls"); //$NON-NLS-1$ + } + + @Override + public String getDescription() { + return GraphicsExample.getResourceString("UnlimitedBallsDescription"); //$NON-NLS-1$ + } + + @Override + public int getInitialAnimationTime() { + return 10; + } + + @Override + public void dispose() { + if (offscreenImages != null) { + for (Image img : offscreenImages) { + if (img != null) { + img.dispose(); + } + } + offscreenImages = null; + } + if (ball != null) { + ball.dispose(); + ball = null; + } + } + + @Override + public void next(int width, int height) { + if (offscreenImages == null || ball == null) { + return; + } + + if (width != canvasWidth || height != canvasHeight) { + initBuffers(width, height); + } + + count = (count + 1) % 8; + for (int i = 0; i < 8; i++) { + double xr = Math.cos((current + xrstart) * Math.PI / xrspeed) * xRadius; + double yr = Math.cos((current + yrstart) * Math.PI / yrspeed) * yRadius; + double x = Math.cos((current + xstart) * Math.PI / xspeed) * xr; + double y = Math.sin((current + ystart) * Math.PI / yspeed) * yr; + GC tempGC = new GC(offscreenImages[i]); + tempGC.drawImage(ball, (int) (xCenter + Math.round(x)), (int) (yCenter + Math.round(y))); + tempGC.dispose(); + current++; + } + } + + @Override + public void paint(GC gc, int width, int height) { + if (!example.checkAdvancedGraphics()) return; + + if (offscreenImages == null) { + ball = example.loadImage(gc.getDevice(), "ball.gif"); //$NON-NLS-1$ + if (ball == null) return; + + currentShape = 0; + count = 0; + current = 0; + canvasWidth = width; + canvasHeight = height; + initBuffers(width, height); + } + + if (offscreenImages == null) { + return; + } + + int x = (width - canvasWidth) / 2; + int y = (height - canvasHeight) / 2; + gc.drawImage(offscreenImages[count], x, y); + } + + private void initBuffers(int width, int height) { + if (offscreenImages != null) { + for (Image img : offscreenImages) { + if (img != null) { + img.dispose(); + } + } + } + + canvasWidth = width; + canvasHeight = height; + xCenter = width / 2 - 8; + yCenter = height / 2 - 8; + xRadius = width / 2.5f; + yRadius = height / 2.5f; + count = 0; + current = 0; + + offscreenImages = new Image[8]; + for (int i = 0; i < 8; i++) { + Image image = new Image(null, width, height); + GC tempGC = new GC(image); + tempGC.setBackground(new Color(255, 255, 255)); + tempGC.fillRectangle(0, 0, width, height); + tempGC.dispose(); + offscreenImages[i] = image; + } + + switch (currentShape) { + case 0: + xspeed = 100; + yspeed = 100; + xrspeed = 4150; + yrspeed = 4150; + xstart = 0; + ystart = 0; + xrstart = 0; + yrstart = 4150; + break; + case 1: + xspeed = 199999; + yspeed = 100; + xrspeed = 550; + yrspeed = 950; + xstart = 0; + ystart = 50; + xrstart = 0; + yrstart = 0; + break; + default: + xspeed = 100; + yspeed = 150; + xrspeed = 2200; + yrspeed = 1100; + xstart = 0; + ystart = 150; + xrstart = 0; + yrstart = 1100; + break; + } + + currentShape = (currentShape + 1) % 3; + } +} diff --git a/examples/org.eclipse.swt.examples/src/org/eclipse/swt/examples/graphics/WarpTab.java b/examples/org.eclipse.swt.examples/src/org/eclipse/swt/examples/graphics/WarpTab.java new file mode 100644 index 0000000000..a2f055f205 --- /dev/null +++ b/examples/org.eclipse.swt.examples/src/org/eclipse/swt/examples/graphics/WarpTab.java @@ -0,0 +1,177 @@ +/******************************************************************************* + * Copyright (c) 2019 Laurent Caron and others. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Calogiuri Enzo Antonio (Insolit Dust) - Original Version + * Laurent Caron (laurent.caron at gmail dot com) - Conversion to SWT + * IBM Corporation - adaptation to GraphicsExample + *******************************************************************************/ + +package org.eclipse.swt.examples.graphics; + +import org.eclipse.swt.graphics.GC; +import org.eclipse.swt.graphics.Image; +import org.eclipse.swt.graphics.ImageData; + +/** + * This tab displays an animated warp distortion effect that uses a precomputed + * distortion lookup table to render a texture with animated warping. + */ +public class WarpTab extends AnimatedGraphicsTab { + + private static final int RENDER_WIDTH = 640; + private static final int RENDER_HEIGHT = 480; + private static final int TEXTURE_SIZE = 256; + + private ImageData imageData; + private Image outputImage; + private int[] texture; + private int[][] distortionX; + private int[][] distortionY; + private float alpha, beta, dz, dw; + + public WarpTab(GraphicsExample example) { + super(example); + } + + @Override + public String getCategory() { + return GraphicsExample.getResourceString("Misc"); //$NON-NLS-1$ + } + + @Override + public String getText() { + return GraphicsExample.getResourceString("Warp"); //$NON-NLS-1$ + } + + @Override + public String getDescription() { + return GraphicsExample.getResourceString("WarpDescription"); //$NON-NLS-1$ + } + + @Override + public int getInitialAnimationTime() { + return 30; + } + + @Override + public void dispose() { + if (outputImage != null) { + outputImage.dispose(); + outputImage = null; + } + } + + @Override + public void next(int width, int height) { + if (texture == null) { + return; + } + + alpha += 0.02f; + beta += 0.044f; + dz += (float) (Math.sin(alpha + beta) * 2 + Math.cos(beta) + 0.4); + dw += (float) (Math.cos(beta - alpha) * 3 + Math.sin(alpha) + 0.2); + int decz = (int) dz; + int decw = (int) dw; + + int halfW = RENDER_WIDTH / 2; + int halfH = RENDER_HEIGHT / 2; + + for (int j = 0; j < halfH; j++) { + int p1 = halfW + RENDER_WIDTH * (halfH - j); + int p2 = p1; + int p3 = halfW + RENDER_WIDTH * (halfH + j); + int p4 = p3; + + for (int i = 0; i < halfW; i++) { + int ddx = distortionX[j][i]; + int ddy = distortionY[j][i]; + + drawPixel(p1, (-ddx + decz) & 255, (-ddy + decw) & 255); + p1--; + drawPixel(p2, (-ddx + decz) & 255, (ddy + decw) & 255); + p2++; + drawPixel(p3, (ddx + decz) & 255, (-ddy + decw) & 255); + p3--; + drawPixel(p4, (ddx + decz) & 255, (ddy + decw) & 255); + p4++; + } + } + } + + @Override + public void paint(GC gc, int width, int height) { + if (!example.checkAdvancedGraphics()) return; + + if (texture == null) { + Image loaded = example.loadImage(gc.getDevice(), "texture.png"); //$NON-NLS-1$ + if (loaded == null) return; + ImageData texImage = loaded.getImageData(); + + imageData = new ImageData(RENDER_WIDTH, RENDER_HEIGHT, texImage.depth, texImage.palette); + + int index = 0; + texture = new int[TEXTURE_SIZE * TEXTURE_SIZE]; + for (int x = 0; x < TEXTURE_SIZE; x++) { + for (int y = 0; y < TEXTURE_SIZE; y++) { + texture[index++] = texImage.getPixel(x, y); + } + } + + initDistortionTable(); + alpha = 0; + beta = 0; + dz = 0; + dw = 0; + } + + if (imageData == null) { + return; + } + + if (outputImage != null) { + outputImage.dispose(); + } + outputImage = new Image(gc.getDevice(), imageData); + + int x = (width - RENDER_WIDTH) / 2; + int y = (height - RENDER_HEIGHT) / 2; + gc.drawImage(outputImage, x, y); + } + + private void initDistortionTable() { + int halfW = RENDER_WIDTH / 2; + int halfH = RENDER_HEIGHT / 2; + distortionX = new int[halfH][halfW]; + distortionY = new int[halfH][halfW]; + + for (int i = 0; i < halfH; i++) { + for (int j = 0; j < halfW; j++) { + double f = Math.pow(j * 1.2 / RENDER_WIDTH, 2); + double d = Math.log(1 + i / (RENDER_HEIGHT / 3.0)) / (3 * f + 1) * halfH; + distortionX[i][j] = (short) d; + + f = Math.pow(i * 1.5 / RENDER_WIDTH, 2); + d = Math.log(1 + j / (RENDER_WIDTH / 3.0)) / (3 * f + 1) * halfW; + distortionY[i][j] = (short) d; + } + } + } + + private void drawPixel(int index, int x, int y) { + int colorIndex = texture[(y << 8) + x]; + int posX = index % RENDER_WIDTH; + int posY = index / RENDER_WIDTH; + if (posX >= 0 && posX < RENDER_WIDTH && posY >= 0 && posY < RENDER_HEIGHT) { + imageData.setPixel(posX, posY, colorIndex); + } + } +} diff --git a/examples/org.eclipse.swt.examples/src/org/eclipse/swt/examples/graphics/ball.gif b/examples/org.eclipse.swt.examples/src/org/eclipse/swt/examples/graphics/ball.gif new file mode 100644 index 0000000000..53df11add4 Binary files /dev/null and b/examples/org.eclipse.swt.examples/src/org/eclipse/swt/examples/graphics/ball.gif differ diff --git a/examples/org.eclipse.swt.examples/src/org/eclipse/swt/examples/graphics/flower.jpg b/examples/org.eclipse.swt.examples/src/org/eclipse/swt/examples/graphics/flower.jpg new file mode 100644 index 0000000000..37cb0cd25e Binary files /dev/null and b/examples/org.eclipse.swt.examples/src/org/eclipse/swt/examples/graphics/flower.jpg differ diff --git a/examples/org.eclipse.swt.examples/src/org/eclipse/swt/examples/graphics/texture.png b/examples/org.eclipse.swt.examples/src/org/eclipse/swt/examples/graphics/texture.png new file mode 100644 index 0000000000..fb51b8598c Binary files /dev/null and b/examples/org.eclipse.swt.examples/src/org/eclipse/swt/examples/graphics/texture.png differ