/* * Created on 10/10/2006 * * TODO To change the template for this generated file go to * Window - Preferences - Java - Code Style - Code Templates */ /** * @author ting * * TODO To change the template for this generated type comment go to Window - * Preferences - Java - Code Style - Code Templates * * Este programa ilustra as operações lógico-aritméticas suportadas por OpenGL * e uma implementação de warping com as funções de textura de OpenGL. * São implementadas as seguintes * funções: * '+': soma * '-': subtração * '*': multiplicação * 'a': AND * 'o': OR * 'x': XOR * 'i': NOT * 'w': warping (baseado no código disponível no site * http://local.wasp.uwa.edu.au/~pbourke/projection/imagewarp/) * 'z': zoom (3x Imagem 1) * 'c': convolução * 's': filtragem + zoom (3x Imagem 1) * 'h': histograma (Imagem 1) * * As rotinas de leitura de imagens do formato bmp foram adaptadas de http:??? * * Ting (5/10/06) * */ import java.awt.BorderLayout; import java.awt.Dimension; import java.awt.Image; import java.awt.event.*; import javax.swing.*; import com.sun.opengl.util.*; import javax.media.opengl.*; import javax.media.opengl.glu.*; import java.io.InputStream; import java.io.Serializable; import java.net.URL; import java.nio.ByteBuffer; import java.nio.ShortBuffer; import java.nio.FloatBuffer; import com.sun.opengl.util.BufferUtil; public class Op_images extends JFrame implements GLEventListener, KeyListener { public static final Dimension PREFERRED_FRAME_SIZE = new Dimension(100, 100); static int n, m; static int opcode = GL.GL_OR; static float[][] box = { { 1.f, 1.f, 1.f }, { 1.f, 1.f, 1.f }, { 1.f, 1.f, 1.f } }; static float[][] gaussian = { { 1.f, 4.f, 8.f, 10.f, 8.f, 4.f, 1.f }, { 4.f, 12.f, 25.f, 29.f, 25.f, 12.f, 4.f }, { 8.f, 25.f, 49.f, 58.f, 49.f, 25.f, 8.f }, { 10.f, 29.f, 58.f, 67.f, 58.f, 29.f, 10.f }, { 8.f, 25.f, 49.f, 58.f, 49.f, 25.f, 8.f }, { 4.f, 12.f, 25.f, 29.f, 25.f, 12.f, 4.f }, { 1.f, 4.f, 8.f, 10.f, 8.f, 4.f, 1.f }, }; static int HISTOGRAM_SIZE = 256; private FloatBuffer boxBuf = BufferUtil.newFloatBuffer(3 * 3); private FloatBuffer gaussianBuf = BufferUtil.newFloatBuffer(7 * 7); { for (int i = 0; i < 3; i++) { for (int j = 0; j < 3; j++) boxBuf.put(box[i][j] / 9.f); } boxBuf.rewind(); } { for (int i = 0; i < 7; i++) { for (int j = 0; j < 7; j++) gaussianBuf.put(gaussian[i][j] / 1000.f); } gaussianBuf.rewind(); } static LoadImage image1, image2; /** * Constructor. */ public Op_images() { //init JFrame super("Op_Images - JMDM version "); System.out.println("Constructor"); } /** * We'd like to be 500x500, please. */ public Dimension getPreferredSize() { return PREFERRED_FRAME_SIZE; } /* * METHODS DEFINED BY GLEventListener */ /** * Called by drawable to initiate drawing. */ public void display(GLAutoDrawable drawable) { GL gl = drawable.getGL(); GLU glu = new GLU(); GLUT glut = new GLUT(); int i, j; float r, phi, nmax, mmax; System.out.println("Display"); // Desenhar os pixels resultantes da operação gl.glDisable(GL.GL_DITHER); // resetar as cores nos buffers de cor gl.glClear(GL.GL_COLOR_BUFFER_BIT); gl.glRasterPos2i(0, 0); if (opcode == GL.GL_FUNC_SUBTRACT || opcode == GL.GL_FUNC_ADD || opcode == 21) { switch (opcode) { case GL.GL_FUNC_SUBTRACT: case GL.GL_FUNC_ADD: /* Operações aritméticas: adição e subtração */ /* Acumular os pixels no framebuffer (destino) */ gl.glDrawPixels(n, m, GL.GL_RGB, GL.GL_UNSIGNED_BYTE, image1 .getData()); /* Habilitar operações de blending */ gl.glEnable(GL.GL_BLEND); /* * Selecionar o peso de ponderação para os pixels de fonte e de * destino */ gl.glBlendFunc(GL.GL_ONE, GL.GL_ONE); /* * Selecionar as operações aritméticas: GL_FUNC_ADD (default), * GL_FUNC_SUBTRACT, GL_FUNC_REVERSE_SUBTRACT, GL_MIN, GL_MAX */ gl.glBlendEquation(opcode); /* Desenhar os novos pixels (fonte) */ gl.glDrawPixels(n, m, GL.GL_RGB, GL.GL_UNSIGNED_BYTE, image2 .getData()); /* Desabilitar operações de blending */ gl.glDisable(GL.GL_BLEND); break; case 21: /* multiplicação */ byte data[] = new byte[n * m * 3]; for (i = 0; i < n * m * 3; i += 3) { /* * os componentes RGB são representados em 1 byte, variando * de 0 até 255, sem sinal. */ data[i] = (byte) (255. * (image1.data[i] / 255.) * (image2.data[i]) / 255.); data[i + 1] = (byte) (255. * (image1.data[i + 1] / 255.) * (image2.data[i + 1]) / 255.); data[i + 2] = (byte) (255. * (image1.data[i + 2] / 255.) * (image2.data[i + 2]) / 255.); } /* transferir dados para framebuffer */ gl.glDrawPixels(n, m, GL.GL_RGB, GL.GL_UNSIGNED_BYTE, ByteBuffer.wrap(data)); break; } } else if (opcode == GL.GL_AND || opcode == GL.GL_OR || opcode == GL.GL_XOR || opcode == GL.GL_INVERT) { /* operações lógicas */ /* transferir os pixels de destino para framebuffer */ gl.glDrawPixels(n, m, GL.GL_RGB, GL.GL_UNSIGNED_BYTE, image1 .getData()); /* Habilitar operações lógicas */ gl.glEnable(GL.GL_COLOR_LOGIC_OP); /* selecionar o tipo de operação lógica */ gl.glLogicOp(opcode); /* desenhar os novos pixels (fonte) */ gl.glDrawPixels(n, m, GL.GL_RGB, GL.GL_UNSIGNED_BYTE, image2 .getData()); /* desabilitar operações lógicas */ gl.glDisable(GL.GL_COLOR_LOGIC_OP); } else if (opcode == 11) { /* warping */ /* * definir o modo como os pixels são armazenados na memória do * processador */ gl.glPixelStorei(GL.GL_UNPACK_ALIGNMENT, 1); /* * definir forma de tratamento de efeitos "aliasing": * correspondências m:1 (minimification) 1:m (magnification) entre * pixels e texels. */ gl.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_MIN_FILTER, GL.GL_LINEAR); gl.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_MAG_FILTER, GL.GL_LINEAR); /* gerar mipmaps para atenuar distorções */ glu.gluBuild2DMipmaps(GL.GL_TEXTURE_2D, GL.GL_RGB, n, m, GL.GL_RGB, GL.GL_UNSIGNED_BYTE, image1.getData()); /* * setar o modo de texturização: GL_REPLACE (substitui os valores do * pixel), GL_DECAL (pondera com o parâmetro alfa da cor), * GL_MODULATE (multiplica o valor-destino com o valor-fonte) */ gl.glTexEnvf(GL.GL_TEXTURE_ENV, GL.GL_TEXTURE_ENV_MODE, GL.GL_REPLACE); /* habilitar a texturização */ gl.glEnable(GL.GL_TEXTURE_2D); r = (float) Math.sqrt(Math.sqrt(n * n + m * m)); phi = (float) Math.atan2(m, n); mmax = r * ((float) Math.sin(phi)); nmax = r * ((float) Math.cos(phi)); for (i = 0; i < n; i++) { for (j = 0; j < m; j++) { gl.glBegin(GL.GL_POLYGON); r = (float) Math.sqrt(Math.sqrt(i * i + j * j)); phi = (float) Math.atan2(j, i); gl.glTexCoord2f(r * ((float) Math.cos(phi) / nmax), r * ((float) Math.sin(phi) / mmax)); gl.glVertex2i(i, j); r = (float) Math.sqrt(Math.sqrt(((i + 1) * (i + 1)) + (j * j))); phi = (float) Math.atan2(j, i + 1); gl.glTexCoord2f(r * ((float) Math.cos(phi) / nmax), r * ((float) Math.sin(phi) / mmax)); gl.glVertex2i(i + 1, j); r = (float) Math.sqrt(Math.sqrt(((i + 1) * (i + 1)) + ((j + 1) * (j + 1)))); phi = (float) Math.atan2(j + 1, i + 1); gl.glTexCoord2f(r * ((float) Math.cos(phi) / nmax), r * ((float) Math.sin(phi) / mmax)); gl.glVertex2i(i + 1, j + 1); r = (float) Math.sqrt(Math.sqrt(((i) * (i)) + ((j + 1) * (j + 1)))); phi = (float) Math.atan2(j + 1, i); gl.glTexCoord2f(r * ((float) Math.cos(phi) / nmax), r * ((float) Math.sin(phi) / mmax)); gl.glVertex2i(i, j + 1); gl.glEnd(); } } gl.glDisable(GL.GL_TEXTURE_2D); } else if (opcode == 31) { /* zoom */ gl.glPixelZoom(3.0f, 3.0f); gl.glDrawPixels(n, m, GL.GL_RGB, GL.GL_UNSIGNED_BYTE, image1 .getData()); /* resetar o fator de zooming */ gl.glPixelZoom(1.0f, 1.0f); gl.glDisable(GL.GL_CONVOLUTION_2D); } else if (opcode == 41) { /* zoom */ gl.glEnable(GL.GL_CONVOLUTION_2D); // setar o modo de tratamento da borda: GL_REDUCE, GL_CONSTANT, // GL_REPLICATE_BORDER gl.glConvolutionParameteri(GL.GL_CONVOLUTION_2D, GL.GL_CONVOLUTION_BORDER_MODE, GL.GL_REDUCE); //gl.glConvolutionFilter2D(GL.GL_CONVOLUTION_2D, GL.GL_LUMINANCE, // 3, 3, GL.GL_LUMINANCE, GL.GL_FLOAT, boxBuf); gl.glConvolutionFilter2D(GL.GL_CONVOLUTION_2D, GL.GL_LUMINANCE, 7, 7, GL.GL_LUMINANCE, GL.GL_FLOAT, gaussianBuf); gl.glDrawPixels(n, m, GL.GL_RGB, GL.GL_UNSIGNED_BYTE, image1 .getData()); gl.glDisable(GL.GL_CONVOLUTION_2D); } else if (opcode == 51) { /* filtragem + zooming */ byte data[] = new byte[n * m * 3 * 3 * 3]; glu.gluScaleImage(GL.GL_RGB, n, m, GL.GL_UNSIGNED_BYTE, image1 .getData(), n * 3, m * 3, GL.GL_UNSIGNED_BYTE, ByteBuffer .wrap(data)); gl.glDrawPixels(n, m, GL.GL_RGB, GL.GL_UNSIGNED_BYTE, ByteBuffer .wrap(data)); } else if (opcode == 61) { /* histograma */ short data[] = new short[HISTOGRAM_SIZE * 3]; short values[] = new short[6]; short l = 0, k = 0; /* definir o formato e o tipo de dados do histograma */ gl.glHistogram(GL.GL_HISTOGRAM, HISTOGRAM_SIZE, GL.GL_RGB, false); gl.glEnable(GL.GL_HISTOGRAM); /* transferir os dados para framebuffer */ gl.glDrawPixels(n, m, GL.GL_RGB, GL.GL_UNSIGNED_BYTE, image1 .getData()); /* ler o histograma de cor */ gl.glGetHistogram(GL.GL_HISTOGRAM, true, GL.GL_RGB, GL.GL_UNSIGNED_SHORT, ShortBuffer.wrap(data)); gl.glDisable(GL.GL_HISTOGRAM); /* * plotar o histograma: normalizar os valores para ficarem dentro da * janela */ gl.glPointSize(10.0f); gl.glBegin(GL.GL_LINE_STRIP); gl.glColor3f(1.0f, 0.0f, 0.0f); for (i = 0; i < HISTOGRAM_SIZE; i++) gl.glVertex2s((short) i, (short) (data[i * 3] / 6)); gl.glEnd(); gl.glBegin(GL.GL_LINE_STRIP); gl.glColor3f(0.0f, 1.0f, 0.0f); for (i = 0; i < HISTOGRAM_SIZE; i++) gl.glVertex2s((short) i, (short) (data[i * 3 + 1] / 6)); gl.glEnd(); gl.glBegin(GL.GL_LINE_STRIP); gl.glColor3f(0.0f, 0.0f, 1.0f); for (i = 0; i < HISTOGRAM_SIZE; i++) gl.glVertex2s((short) i, (short) (data[i * 3 + 2] / 6)); gl.glEnd(); } /* plotar os pontos min-max */ /* setar o tamanho do ponto em pixels */ gl.glColor3f(0.0f, 0.0f, 1.0f); gl.glFlush(); } /** * Called by drawable to indicate mode or device has changed. */ public void displayChanged(GLAutoDrawable drawable, boolean modeChanged, boolean deviceChanged) { } /** * Called after OpenGL is init'ed */ public void init(GLAutoDrawable drawable) { System.out.println("init()"); GL gl = drawable.getGL(); System.err.println("INIT GL IS: " + gl.getClass().getName()); System.err.println("GL_VENDOR: " + gl.glGetString(GL.GL_VENDOR)); System.err.println("GL_RENDERER: " + gl.glGetString(GL.GL_RENDERER)); System.err.println("GL_VERSION: " + gl.glGetString(GL.GL_VERSION)); gl.glClearColor(0.0f, 0.0f, 0.0f, 0.0f); //drawable.addKeyListener(this); } /** * Called to indicate the drawing surface has been moved and/or resized. */ public void reshape(GLAutoDrawable drawable, int x, int y, int width, int height) { System.out.println("reshape()"); GL gl = drawable.getGL(); GLU glu = new GLU(); gl.glViewport(0, 0, width, height); gl.glMatrixMode(GL.GL_PROJECTION); gl.glLoadIdentity(); glu.gluOrtho2D(0.0, (float) n, 0.0, (float) m); gl.glMatrixMode(GL.GL_MODELVIEW); } /* * OUR HELPER METHODS */ // Methods required for the implementation of KeyListener public void keyPressed(KeyEvent e) { System.out.println("Key pressed"); switch (e.getKeyChar()) { case '+': opcode = GL.GL_FUNC_ADD; break; case '-': opcode = GL.GL_FUNC_SUBTRACT; break; case '*': opcode = 21; break; case 'a': opcode = GL.GL_AND; break; case 'o': opcode = GL.GL_OR; break; case 'x': opcode = GL.GL_XOR; break; case 'i': opcode = GL.GL_INVERT; break; case 'w': opcode = 11; break; case 'z': opcode = 31; break; case 'c': opcode = 41; break; case 's': opcode = 51; break; case 'h': opcode = 61; break; case 27: System.exit(0); return; } /* forçar o redesenho do conteudo */ //this.display(); ((GLCanvas) (this.getContentPane().getComponentAt(0, 0))).display(); } public void keyReleased(KeyEvent e) { } public void keyTyped(KeyEvent e) { } /** * main creates and shows a Op_images-JFrame */ public static void main(String[] args) { Op_images g = new Op_images(); //Carrega a primeira imagem image1 = new LoadImage(); image1.generateTextureInfo("photo1.bmp", false); //Carrega a segunda imagem image2 = new LoadImage(); image2.generateTextureInfo("photo2.bmp", false); // Atribui tamanho e local do frame n = image1.getWidth(); m = image1.getHeight(); //Set frame location g.setLocation(100, 100); g.setSize(new Dimension(n, m)); GLCapabilities gl_c = new GLCapabilities(); // Disable double buffer gl_c.setDoubleBuffered(false); // get a GLCanvas for result GLCanvas canvas = new GLCanvas(gl_c); // add a GLEventListener, which will get called when the // canvas is resized or needs a repaint canvas.addGLEventListener(g); // add a GLEventListener, which will get called when a // key is pressed canvas.addKeyListener(g); g.getContentPane().add(canvas, BorderLayout.CENTER); g.setVisible(true); } /* Adaptado pelo Paulo Aragão */ static class LoadImage extends JFrame implements Serializable { byte[] data; int width, height; LoadImage() { destroy(); } public int getWidth() { return width; } public int getHeight() { return height; } public ByteBuffer getData() { return ByteBuffer.wrap(data); } void generateTextureInfo(String textureName, boolean AlphaChannel) { Image textureFile = null; int pixels[]; URL url = LoadImage.class.getClassLoader().getResource(textureName); if (!textureName.endsWith("bmp")) { try { // Modificado aragao // textureFile = // java.awt.Toolkit.getDefaultToolkit().getImage( // textureName); textureFile = java.awt.Toolkit.getDefaultToolkit() .getImage(url); } catch (NullPointerException e) { System.out.println(e); } while (width <= 0 || height <= 0) { width = textureFile.getWidth(null); height = textureFile.getHeight(null); } pixels = new int[width * height]; try { new java.awt.image.PixelGrabber(textureFile, 0, 0, width, height, pixels, 0, width).grabPixels(); textureFile = null; } catch (InterruptedException e) { } } else // Modificado aragao pixels = loadBitmapPixels(textureName); if (!AlphaChannel) { data = new byte[pixels.length * 3]; for (int y = height - 1, pointer = 0; y >= 0; y--) for (int x = 0; x < width; x++, pointer += 3) { data[pointer + 0] = (byte) ((pixels[y * width + x] >> 16) & 0xFF); data[pointer + 1] = (byte) ((pixels[y * width + x] >> 8) & 0xFF); data[pointer + 2] = (byte) (pixels[y * width + x] & 0xFF); } } else { data = new byte[pixels.length * 4]; for (int y = height - 1, pointer = 0; y >= 0; y--) for (int x = 0; x < width; x++, pointer += 4) { data[pointer + 3] = (byte) ((pixels[y * width + x] >> 24) & 0xFF); data[pointer + 0] = (byte) ((pixels[y * width + x] >> 16) & 0xFF); data[pointer + 1] = (byte) ((pixels[y * width + x] >> 8) & 0xFF); data[pointer + 2] = (byte) (pixels[y * width + x] & 0xFF); } } pixels = null; } int[] loadBitmapPixels(String textureName/* String file */) { try { // Modificado Aragao // FileInputStream input = new FileInputStream(url/*file*/); InputStream input = LoadImage.class.getClassLoader() .getResourceAsStream(textureName); int bitmapFileHeaderLength = 14, bitmapInfoHeaderLength = 40; byte bitmapFileHeader[] = new byte[bitmapFileHeaderLength], bitmapInfoHeader[] = new byte[bitmapInfoHeaderLength]; input.read(bitmapFileHeader, 0, bitmapFileHeaderLength); input.read(bitmapInfoHeader, 0, bitmapInfoHeaderLength); int nSize = bytesToInt(bitmapFileHeader, 2), nWidth = bytesToInt( bitmapInfoHeader, 4), nHeight = bytesToInt( bitmapInfoHeader, 8), nBiSize = bytesToInt( bitmapInfoHeader, 0), nPlanes = bytesToShort( bitmapInfoHeader, 12), nBitCount = bytesToShort( bitmapInfoHeader, 14), nSizeImage = bytesToInt( bitmapInfoHeader, 20), nCompression = bytesToInt( bitmapInfoHeader, 16), nColoursUsed = bytesToInt( bitmapInfoHeader, 32), nXPixelsMeter = bytesToInt( bitmapInfoHeader, 24), nYPixelsMeter = bytesToInt( bitmapInfoHeader, 28), nImportantColours = bytesToInt( bitmapInfoHeader, 36); if (nBitCount == 24) { int npad = ((npad = (nSizeImage / nHeight) - nWidth * 3) == 4 || npad < 0) ? 0 : npad, nindex = 0, ndata[] = new int[nHeight * nWidth]; byte brgb[] = new byte[(nWidth + npad) * 3 * nHeight]; input.read(brgb, 0, (nWidth + npad) * 3 * nHeight); for (int j = 0; j < nHeight; j++) { for (int i = 0; i < nWidth; i++) { ndata[nWidth * (nHeight - j - 1) + i] = (255 & 0xff) << 24 | (brgb[nindex + 2] & 0xff) << 16 | (brgb[nindex + 1] & 0xff) << 8 | (brgb[nindex] & 0xff); nindex += 3; } nindex += npad; } brgb = null; input.close(); width = nWidth; height = nHeight; return ndata; } else if (nBitCount == 8) { int nNumColors = (nColoursUsed > 0) ? nColoursUsed : (1 & 0xff) << nBitCount; if (nSizeImage == 0) { nSizeImage = ((((nWidth * nBitCount) + 31) & ~31) >> 3); nSizeImage *= nHeight; } int npalette[] = new int[nNumColors]; byte bpalette[] = new byte[nNumColors * 4]; input.read(bpalette, 0, nNumColors * 4); int nindex8 = 0; for (int n = 0; n < nNumColors; n++) { npalette[n] = (255 & 0xff) << 24 | (bpalette[nindex8 + 2] & 0xff) << 16 | (bpalette[nindex8 + 1] & 0xff) << 8 | (bpalette[nindex8 + 0] & 0xff); nindex8 += 4; } int npad8 = (nSizeImage / nHeight) - nWidth, ndata8[] = new int[nWidth * nHeight]; byte bdata[] = new byte[(nWidth + npad8) * nHeight]; input.read(bdata, 0, (nWidth + npad8) * nHeight); nindex8 = 0; for (int j8 = 0; j8 < nHeight; j8++) { for (int i8 = 0; i8 < nWidth; i8++) { ndata8[nWidth * (nHeight - j8 - 1) + i8] = npalette[((int) bdata[nindex8] & 0xff)]; nindex8++; } nindex8 += npad8; } input.close(); width = nWidth; height = nHeight; return ndata8; } else System.out .println("Not a 24-bit or 8-bit Windows Bitmap, aborting..."); input.close(); } catch (Exception e) { System.out.println("Caught exception in loadbitmap!"); } return null; } int bytesToInt(byte[] bytes, int index) { return (bytes[index + 3] & 0xff) << 24 | (bytes[index + 2] & 0xff) << 16 | (bytes[index + 1] & 0xff) << 8 | bytes[index + 0] & 0xff; } short bytesToShort(byte[] bytes, int index) { return (short) ((bytes[index + 1] & 0xff) << 8 | (bytes[index + 0] & 0xff)); } void destroy() { data = null; width = -1; height = -1; } } /* * public class TextureMap { private ByteBuffer mData; * * private int mWidth; * * private int mHeight; * * public TextureMap(String filename) { URL url = * TextureMap.class.getClassLoader().getResource(filename); ImageIcon icon = * new ImageIcon(url); mWidth = icon.getIconWidth(); mHeight = * icon.getIconHeight(); * * mData = ByteBuffer.allocate(mWidth * mHeight * 4); BufferedImage bImage = * new BufferedImage(mWidth, mHeight, BufferedImage.TYPE_INT_ARGB); Graphics * g2 = bImage.getGraphics(); g2.drawImage(icon.getImage(), 0, 0, null); * * int k = 0; int c; for (int y = 0; y < mHeight; y++) for (int x = 0; x < * mWidth; x++) { c = bImage.getRGB(x, y); mData.put(k + 2, (byte) (c & * 0xFF)); c = c >> 8; mData.put(k + 1, (byte) (c & 0xFF)); c = c >> 8; * mData.put(k, (byte) (c & 0xFF)); c = c >> 8; mData.put(k + 3, (byte) * (0xFF)); k += 4; } } * * public int getWidth() { return mWidth; } * * public int getHeight() { return mHeight; } * * public ByteBuffer getData() { return mData; } } */ }