001/* 002 * FrameBufferPanel. The MIT License. 003 * Copyright (c) 2022 rlkraft@pnw.edu 004 * See LICENSE for details. 005*/ 006 007package renderer.framebuffer; 008 009import renderer.framebuffer.FrameBuffer; 010 011import java.awt.Color; 012import java.awt.Graphics; 013import java.awt.Graphics2D; 014import java.awt.Dimension; 015import java.awt.Image; 016import java.awt.image.MemoryImageSource; 017import javax.swing.JPanel; 018 019/** 020 This class is an interface between our renderer and the Java GUI system. 021 This class allows our rendering code to be used as the primary renderer 022 for Java programs. That is, this class allows us to write Java programs 023 that use our renderer instead of the renderer built into Java's GUI 024 library. Of course, our renderer will be much slower than the one built 025 into Java (which uses the computer's GPU). 026<p> 027 A {@code FrameBufferPanel} "is a" {@link javax.swing.JPanel}. A 028 {@code FrameBufferPanel} "has a" {@link FrameBuffer}. And a 029 {@link FrameBuffer} "has a" array of pixel data. The pixel data in a 030 {@link FrameBuffer} is put there by calling our rendering algorithms. 031<p> 032 Each instance of {@code FrameBufferPanel} has a reference to a 033 {@link FrameBuffer} object and the {@link FrameBuffer} object 034 determines the (preferred) dimensions of the {@code FrameBufferPanel}. 035<p> 036 A {@code FrameBufferPanel} should be instantiated by an application that 037 uses our renderer. The application should initialize a {@link renderer.scene.Scene} 038 object with appropriate models and geometry. The application should then 039 render the {@link renderer.scene.Scene} into the {@link FrameBuffer} object 040 contained in the {@code FrameBufferPanel}. 041<p> 042 This class is meant to be instantiated as a sub-panel of a 043 {@link javax.swing.JFrame}. The {@link javax.swing.JFrame} may or may 044 not implement event listeners. If the {@link javax.swing.JFrame} does 045 implement event listeners, then the event listeners can make our 046 renderer interactive. 047<p> 048 When a GUI event happens, any implemented event listener should update 049 this {@code FrameBufferPanel} by modifying a {@link renderer.scene.Scene} 050 object appropriately and then having our renderer render the 051 {@link renderer.scene.Scene} object into this object's {@link FrameBuffer}. 052 When the renderer is done updating the {@link FrameBuffer}, the event 053 listener should call this object's {@link java.awt.Component#repaint} 054 method, which will lead to the calling of this object's {@link #paintComponent} 055 method, which will pass the {@link FrameBuffer}'s pixel data to an 056 {@link java.awt.Image} that will be drawn on the {@link java.awt.Graphics} 057 context of the {@code FrameBufferPanel} (which is a {@link JPanel}). 058 This will display the {@link java.awt.Image}, which holds the 059 {@link FrameBuffer}'s contents, in the {@link JPanel} within a 060 {@link javax.swing.JFrame}'s window. 061<p> 062 This panel may be resizeable. When this panel resizes, its 063 {@link FrameBuffer} object will also need to resize. But 064 {@link FrameBuffer} objects cannot be resized. So each time this 065 panel resizes, a new {@link FrameBuffer} object needs to be created. 066 The {@link java.awt.event.ComponentListener#componentResized} event 067 handler from the {@link java.awt.event.ComponentListener} interface 068 should instantiate a new {@link FrameBuffer} object with the 069 appropriate dimensions and then call this object's {@link #setFrameBuffer} 070 method and pass it a reference to the new {@link FrameBuffer} object. 071*/ 072@SuppressWarnings("serial") 073public final class FrameBufferPanel extends JPanel 074{ 075 private FrameBuffer fb; 076 private MemoryImageSource source; 077 078 /** 079 @param fbWidth width for the initial {@link FrameBuffer} used by this {@link JPanel} 080 @param fbHeight height for the initial {@link FrameBuffer} used by this {@link JPanel} 081 */ 082 public FrameBufferPanel(final int fbWidth, final int fbHeight) 083 { 084 // Create the initial FrameBuffer for the FrameBufferPanel. 085 this.setFrameBuffer( new FrameBuffer(fbWidth, fbHeight) ); 086 } 087 088 089 /** 090 @param fbWidth width for the initial {@link FrameBuffer} used by this {@link JPanel} 091 @param fbHeight height for the initial {@link FrameBuffer} used by this {@link JPanel} 092 @param bgColor background {@link Color} for the initial {@link FrameBuffer} used by this {@link JPanel} 093 */ 094 public FrameBufferPanel(final int fbWidth, final int fbHeight, final Color bgColor) 095 { 096 // Create the initial FrameBuffer for the FrameBufferPanel. 097 this.setFrameBuffer( new FrameBuffer(fbWidth, fbHeight, bgColor) ); 098 } 099 100 101 @Override 102 public Dimension getPreferredSize() 103 { 104 return new Dimension(fb.width, fb.height); 105 } 106 107 108 @Override 109 protected void paintComponent(Graphics g) 110 { 111 super.paintComponent(g); 112 113 final Graphics2D g2 = (Graphics2D)g.create(); 114 final Image img = this.createImage(this.source); 115 g2.drawImage(img, 0, 0, this); 116 g2.dispose(); 117 } 118 119 120 /** 121 Accessor method for the {@link FrameBuffer} currently being used as 122 the source for the {@link java.awt.Image} painted on this {@link JPanel}. 123 124 @return a reference to the {@link FrameBuffer} owned by this {@link JPanel} 125 */ 126 public FrameBuffer getFrameBuffer() 127 { 128 return fb; 129 } 130 131 132 /** 133 Change the {@link FrameBuffer} being used as the source for 134 the {@link java.awt.Image} painted on this {@link JPanel}. 135 <p> 136 This will usually be in response to a call to the 137 componentResized() event handler. 138 139 @param fb new {@link FrameBuffer} object for this {@link JPanel} 140 */ 141 public void setFrameBuffer(final FrameBuffer fb) 142 { 143 this.fb = fb; 144 145 // Use the framebuffer as the source for an Image. 146 this.source = new MemoryImageSource(fb.width, fb.height, 147 fb.pixel_buffer, 148 0, fb.width); 149 } 150}