Advertisement
Guest User

Example for a self-contained CPU-side shader without excess fat [Java, Swing]

a guest
Nov 23rd, 2024
27
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Java 5.88 KB | Source Code | 0 0
  1. import java.awt.Dimension;
  2. import java.awt.Graphics;
  3. import java.awt.event.ComponentAdapter;
  4. import java.awt.event.ComponentEvent;
  5. import java.awt.event.WindowAdapter;
  6. import java.awt.event.WindowEvent;
  7. import java.awt.image.BufferedImage;
  8. import java.awt.image.DataBufferInt;
  9. import java.util.concurrent.CountDownLatch;
  10.  
  11. import javax.swing.JFrame;
  12. import javax.swing.JPanel;
  13. import javax.swing.SwingUtilities;
  14.  
  15. public class CpuShader {
  16.  
  17.   public static float clamp (float value, float min, float max) {
  18.     if (value < min) { return min; }
  19.     if (value > max) { return max; }
  20.     return value;
  21.   }
  22.  
  23.   public static void main (String[] args) throws Exception {
  24.     ShaderRunner runner = run( (ctx, in, out) -> {
  25.       // "mango"
  26.       out.r = in.xN;
  27.       out.g = in.yN;
  28.     });
  29.     runner.execution.await();
  30.     SwingUtilities.invokeLater(runner.frame::dispose);
  31.   }
  32.  
  33.   public static float ratio (int a, int b) {
  34.     return (float) a / (float) b;
  35.   }
  36.  
  37.   public static ShaderRunner run (Shader shader) {
  38.     ShaderRunner result = new ShaderRunner(shader);
  39.     SwingUtilities.invokeLater( () -> {
  40.       result.ui();
  41.       result.clock.start();
  42.     });
  43.     return result;
  44.   }
  45.  
  46.   public static interface Shader {
  47.     public void apply (Context ctx, Input in, Output out);
  48.  
  49.     public static class Context {
  50.       public int width, height; // target dimensions
  51.       public float aspectWH; // aspect-ratio = width / height
  52.       public float aspectHW; // aspect-ratio = height / width
  53.     }
  54.  
  55.     public static class Input {
  56.       public int x, y;
  57.       public float xN, yN; // normalized to [0, 1]
  58.       public float time;
  59.     }
  60.  
  61.     public static class Output {
  62.       public float r, g, b, a;
  63.     }
  64.   }
  65.  
  66.   public static class ShaderPanel extends JPanel {
  67.     public Dimension targetDim;
  68.     public BufferedImage target;
  69.     public int[] targetBuffer;
  70.     public final ShaderRunner runner;
  71.     public final Shader.Context shaderCtx = new Shader.Context();
  72.     public final Shader.Input shaderIn = new Shader.Input();
  73.     public final Shader.Output shaderOut = new Shader.Output();
  74.  
  75.     public ShaderPanel (ShaderRunner runner) {
  76.       {
  77.         this.runner = runner;
  78.       }
  79.       {
  80.         Dimension initialDim = new Dimension(640, 480);
  81.         setPreferredSize(initialDim);
  82.         setSize(initialDim);
  83.       }
  84.       {
  85.         rebuildTarget();
  86.       }
  87.       {
  88.         addComponentListener(new ComponentAdapter() {
  89.           @Override public void componentResized (ComponentEvent e) {
  90.             rebuildTarget();
  91.           }
  92.         });
  93.       }
  94.     }
  95.  
  96.     @Override public void paintComponent (Graphics graphics) {
  97.       {
  98.         shaderCtx.width = targetDim.width;
  99.         shaderCtx.height = targetDim.height;
  100.         shaderCtx.aspectWH = ratio(shaderCtx.width, shaderCtx.height);
  101.         shaderCtx.aspectHW = ratio(shaderCtx.height, shaderCtx.width);
  102.       }
  103.       {
  104.         long t0 = System.nanoTime();
  105.         for (int y = 0; y < targetDim.height; y++) {
  106.           for (int x = 0; x < targetDim.width; x++) {
  107.             {
  108.               shaderIn.x = x;
  109.               shaderIn.y = y;
  110.               shaderIn.xN = ratio(x, targetDim.width);
  111.               shaderIn.yN = ratio(y, targetDim.height);
  112.               shaderIn.time = runner.time;
  113.             }
  114.             {
  115.               shaderOut.r = 0f;
  116.               shaderOut.g = 0f;
  117.               shaderOut.b = 0f;
  118.               shaderOut.a = 1f;
  119.             }
  120.             {
  121.               runner.shader.apply(shaderCtx, shaderIn, shaderOut);
  122.             }
  123.             {
  124.               shaderOut.r = clamp(shaderOut.r, 0f, 1f);
  125.               shaderOut.g = clamp(shaderOut.g, 0f, 1f);
  126.               shaderOut.b = clamp(shaderOut.b, 0f, 1f);
  127.               shaderOut.a = clamp(shaderOut.a, 0f, 1f);
  128.               int r = (int) (255 * shaderOut.r);
  129.               int g = (int) (255 * shaderOut.g);
  130.               int b = (int) (255 * shaderOut.b);
  131.               int a = (int) (255 * shaderOut.a);
  132.               int i = y * targetDim.width + x;
  133.               targetBuffer[i] = (a << 24) | (r << 16) | (g << 8) | (b << 0);
  134.             }
  135.           }
  136.         }
  137.         long t1 = System.nanoTime();
  138.         long dt = t1 - t0;
  139.         long dtMS = dt / 1_000_000L;
  140.         long fps = 1_000_000_000L / dt;
  141.         if (false) { System.out.println("frame: " + dtMS + "ms (" + fps + "fps)"); }
  142.       }
  143.       {
  144.         graphics.drawImage(target, 0, 0, null);
  145.       }
  146.     }
  147.  
  148.     public void rebuildTarget () {
  149.       targetDim = new Dimension(getWidth(), getHeight());
  150.       target = new BufferedImage(targetDim.width, targetDim.height, BufferedImage.TYPE_INT_ARGB);
  151.       targetBuffer = ((DataBufferInt) target.getRaster().getDataBuffer()).getData();
  152.     }
  153.   }
  154.  
  155.   public static class ShaderRunner {
  156.     public final Shader shader;
  157.     public final CountDownLatch execution = new CountDownLatch(1);
  158.     public final Thread clock = new Thread(this::clock, "shader-runner-clock");
  159.     public final JFrame frame;
  160.     public final ShaderPanel panel;
  161.     public float time;
  162.  
  163.     public ShaderRunner (Shader shader) {
  164.       this.shader = shader;
  165.       frame = new JFrame("ShaderRunner");
  166.       panel = new ShaderPanel(this);
  167.     }
  168.  
  169.     public void clock () {
  170.       while (true) {
  171.         time += ratio(1, 60);
  172.         SwingUtilities.invokeLater(panel::repaint);
  173.         try {
  174.           Thread.sleep(16);
  175.         } catch (InterruptedException e) {
  176.           break;
  177.         }
  178.       }
  179.     }
  180.  
  181.     public void ui () {
  182.       frame.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
  183.       frame.addWindowListener(new WindowAdapter() {
  184.         @Override public void windowClosing (WindowEvent e) {
  185.           clock.interrupt();
  186.           execution.countDown();
  187.         }
  188.       });
  189.       frame.add(panel);
  190.       frame.pack();
  191.       frame.setLocationRelativeTo(null);
  192.       frame.setVisible(true);
  193.     }
  194.   }
  195.  
  196. }
  197.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement