/* * @(#)SubpixAA.java 1.0 2009-12-11 * * Copyright (c) 2009 Werner Randelshofer, Goldau, Switzerland. * All rights reserved. * * You may not use, copy or modify this file, except in compliance with the * license agreement you entered into with Werner Randelshofer. * For details see accompanying license terms. */ package org.monte.media.image; import java.awt.*; import java.awt.image.BufferedImage; import java.awt.image.DataBufferInt; import java.awt.image.ImageObserver; import java.awt.image.WritableRaster; import java.util.logging.Level; import java.util.logging.Logger; /** * Scales an image down and applies Subpixel antialiasing to it. * * @author Werner Randelshofer * @version 1.0 2009-12-11 Created. */ public class SubpixAA { public final static Object HBGR; public final static Object VBGR; public final static Object HRGB; public final static Object VRGB; static { Object hbgr, vbgr, hrgb, vrgb; try { vbgr = RenderingHints.class.getField("VALUE_TEXT_ANTIALIAS_LCD_VBGR").get(null); vrgb = RenderingHints.class.getField("VALUE_TEXT_ANTIALIAS_LCD_VRGB").get(null); hbgr = RenderingHints.class.getField("VALUE_TEXT_ANTIALIAS_LCD_HBGR").get(null); hrgb = RenderingHints.class.getField("VALUE_TEXT_ANTIALIAS_LCD_HRGB").get(null); } catch (Exception ex) { hrgb = "HRGB"; hbgr = "HBGR"; vrgb = "VRGB"; vbgr = "VBGR"; } HBGR = hbgr; HRGB = hrgb; VBGR = vrgb; VRGB = vbgr; } /** Source image buffer. */ private BufferedImage sBuf; /** Destination image buffer. */ private BufferedImage dBuf; /* private static void setup(Graphics gr) { Graphics2D g = (Graphics2D) gr; g.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_LCD_HRGB); g.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_OFF); } private static void setup2(Graphics gr) { Graphics2D g = (Graphics2D) gr; g.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_OFF); g.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_OFF); } public static void main(String[] args) { System.setProperty("apple.awt.graphics.UseQuartz", "false"); GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().getDisplayMode(); Map<RenderingHints.Key, Object> map = (Map<RenderingHints.Key, Object>) Toolkit.getDefaultToolkit().getDesktopProperty("awt.font.desktophints"); System.out.println(map); SwingUtilities.invokeLater(new Runnable() { public void run() { JFrame f = new JFrame(); f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); f.setSize(430, 340); f.setLocation(0, 180); f.setTitle("SubpixAA"); JPanel p = new JPanel() { public void update(Graphics g) { paint(g); } public void paint(Graphics gr) { Graphics2D g = (Graphics2D) gr; setup(g); Font f = new Font("Times New Roman", Font.PLAIN, 18); g.setFont(f); g.setColor(Color.white); g.fillRect(0, 0, size().width, size().height); g.setColor(Color.black); int y = 5; g.drawString("Sample Text String", 5, y + getFontMetrics(f).getAscent()); y += getFontMetrics(f).getHeight(); drawClearType(g, "Sample Text String", 5, y += getFontMetrics(f).getHeight(), RenderingHints.VALUE_TEXT_ANTIALIAS_OFF); drawClearType(g, "Sample Text String", 5, y += getFontMetrics(f).getHeight(), RenderingHints.VALUE_TEXT_ANTIALIAS_ON); drawClearType(g, "Sample Text String", 5, y += getFontMetrics(f).getHeight(), RenderingHints.VALUE_TEXT_ANTIALIAS_LCD_HRGB); drawClearType(g, "Sample Text String", 5, y += getFontMetrics(f).getHeight() * 2, RenderingHints.VALUE_TEXT_ANTIALIAS_LCD_HBGR); drawClearType(g, "Sample Text String", 5, y += getFontMetrics(f).getHeight() * 2, RenderingHints.VALUE_TEXT_ANTIALIAS_LCD_VRGB); drawClearType(g, "Sample Text String", 5, y += getFontMetrics(f).getHeight() * 3, RenderingHints.VALUE_TEXT_ANTIALIAS_LCD_VBGR); } public void drawClearType(Graphics g, String s, int x, int y, Object method) { Font f = g.getFont(); FontMetrics fm = getFontMetrics(f); int sw = fm.stringWidth(s); int sh = fm.getHeight(); Dimension dim = getSourceDimension(sw, sh, method); int width = dim.width; int height = dim.height; BufferedImage img = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); Graphics2D grph = img.createGraphics(); setup2(grph); grph.setColor(Color.white); grph.fillRect(0, 0, width, height); grph.setColor(Color.black); grph.setFont(f.deriveFont(AffineTransform.getScaleInstance(dim.width / sw, dim.height / sh))); grph.drawString(s, 0, grph.getFontMetrics().getMaxAscent()); grph.dispose(); SubpixAA aa = new SubpixAA(); aa.drawAA(g, img, x, y - sh, sw, sh, method, this); g.drawImage(img, x, y, this); } }; f.setContentPane(p); f.setVisible(true); } }); }*/ /** Renders an image with subpixel antialiasing. * * This method uses caching to improve the performance of subsequent calls. * * The image must be larger than the destination width and height. */ public void drawAA(Graphics gr, BufferedImage img, int x, int y, int width, int height, ImageObserver observer) { Graphics2D g = (Graphics2D) gr; drawAA(gr, img, x, y, width, height, g.getRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING), observer); } /** Renders an image with subpixel antialiasing. * * This method uses caching to improve the performance of subsequent calls. * * The image must be larger than the destination width and height. */ public void drawAA(Graphics gr, BufferedImage img, int x, int y, int width, int height, Object method, ImageObserver observer) { // If the image dimension matches width and height, draw it as is. if (img.getWidth() == width && img.getHeight() == height) { gr.drawImage(img, x, y, observer); return; } Graphics2D g = (Graphics2D) gr; // Rescale the image if it does not have the right size for the // subpixel antialiasing method. Dimension sdim = getSourceDimension(width, height, method); if (img.getWidth() != sdim.width || img.getHeight() != sdim.height) { if (sBuf == null || sBuf.getWidth() != sdim.width || sBuf.getHeight() != sdim.height) { sBuf = new BufferedImage(sdim.width, sdim.height, BufferedImage.TYPE_INT_RGB); } Graphics2D sg = sBuf.createGraphics(); setupRendering(sg); sg.drawImage(img, 0, 0, sdim.width, sdim.height, null); //System.out.println("rescale " + img.getWidth() + "," + img.getHeight() + " to " + sBuf.getWidth() + "," + sBuf.getHeight()); sg.dispose(); img = sBuf; } if (dBuf == null || dBuf.getWidth() != width || dBuf.getHeight() != height) { dBuf = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); } aa(img, dBuf, method); g.drawImage(dBuf, x, y, observer); } /** Returns the dimensions needed of the source image for the desired * destination image size. * * @param width The desired width of the destination image. * @param height The desired height of the destination image. * @param method The Antialiasing method to be used must be one of * {@code RenderingHints.VALUE_TEXT_ANTIALIAS_LCD_HRGB}, {@code ..._HBGR}, */ public static Dimension getSourceDimension(int width, int height, Object method) { Dimension dim; if (method == HRGB ||// method == HBGR) { dim = new Dimension(width * 3, height); } else if (method == VRGB ||// method == VBGR) { dim = new Dimension(width, height * 3); } else { dim = new Dimension(width, height); } return dim; } /** Scales down an image using the specified antialiasing method. * <p> * For methods {@code HBGR} and {@code HRGB}, the image width is scaled down * by factor 3. * For methods {@code VBGR} and {@code VRGB}, the image height is scaled down * by factor 3. * * @param src The source image. * @param dst The destination image. * @param m The method. */ public static void aa(BufferedImage src, BufferedImage dst, Object m) { if (m == HRGB) { aaHRGB(src, dst); } else if (m == HBGR) { aaHBGR(src, dst); } else if (m == VRGB) { aaVRGB(src, dst); } else if (m == VBGR) { aaVBGR(src, dst); } else { Graphics2D g = dst.createGraphics(); setupRendering(g); g.drawImage(src, 0, 0, dst.getWidth(), dst.getHeight(), null); g.dispose(); } } /** * Set up the graphics transform to match the clip region * to the image size. */ private static void setupRendering(Graphics2D g) { g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC); g.setRenderingHint(RenderingHints.KEY_ALPHA_INTERPOLATION, RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY); g.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY); //getToolkit(). } /** Scales down an image using HRGB antia-aliasing. * <p> * Source and destination image must be of type BufferedImage.TYPE_RGB * and have a data buffer of type DataBufferInt. * <p> * The height of source and destination image must be the same. * The width of the source image must be 3 times the width of the * destination image. * <p> * Intensity weights: * <pre> * * 1 * | * +----+----+ * | | | * 1/3 1/3 1/3 * | * +----+----+----+----+ * | | | | | * 1/9 2/9 3/9 2/9 1/9 * </pre> * * @param src The source image. * @param dst The destination image. */ public static void aaHRGB(BufferedImage src, BufferedImage dst) { WritableRaster sras = src.getRaster(); WritableRaster dras = dst.getRaster(); int[] s = ((DataBufferInt) sras.getDataBuffer()).getData(); int[] d = ((DataBufferInt) dras.getDataBuffer()).getData(); int sw = src.getWidth(); int sh = src.getHeight(); int dw = dst.getWidth(); int dh = dst.getHeight(); // weights:left-left,left,center,right,right-right,total //final int wll = 0, wl = 0, wc = 1, wr = 0, wrr = 0, tw = wll + wl + wc + wr + wrr; //final int wll = 0, wl = 1, wc = 1, wr = 1, wrr = 0, tw = wll+wl+wc+wr+wrr; final int wll = 1, wl = 2, wc = 3, wr = 2, wrr = 1, tw = wll + wl + wc + wr + wrr; for (int y = 0; y < dh; y++) { int sxy = y * sw; int dxy = y * dw; int dxyeol = dxy + dw - 1; // Process the first pixel of the line. d[dxy] =// // Red component: ((// (s[sxy + 0] & 0xff0000) * (wll + wl + wc) +// (s[sxy + 1] & 0xff0000) * wr +// (s[sxy + 2] & 0xff0000) * wrr// ) / tw & 0xff0000) | // // Green component: ((// (s[sxy + 0] & 0x00ff00) * (wll + wl) + // (s[sxy + 1] & 0x00ff00) * wc + // (s[sxy + 2] & 0x00ff00) * wr + // (s[sxy + 3] & 0x00ff00) * wrr// ) / tw & 0x00ff00) | // // Blue component: ((// (s[sxy + 0] & 0x0000ff) * wll + // (s[sxy + 1] & 0x0000ff) * wl + // (s[sxy + 2] & 0x0000ff) * wc + // (s[sxy + 3] & 0x0000ff) * wr +// (s[sxy + 4] & 0x0000ff) * wrr// ) / tw); sxy += 3; dxy++; // Process the pixels in the middle of the line. for (; dxy < dxyeol; sxy += 3, dxy++) { d[dxy] =// // Red component: ((// (s[sxy - 2] & 0xff0000) * wll + // (s[sxy - 1] & 0xff0000) * wl + // (s[sxy + 0] & 0xff0000) * wc + // (s[sxy + 1] & 0xff0000) * wr +// (s[sxy + 2] & 0xff0000) * wrr// ) / tw & 0xff0000) | // // Green component: ((// (s[sxy - 1] & 0x00ff00) * wll +// (s[sxy - 0] & 0x00ff00) * wl + // (s[sxy + 1] & 0x00ff00) * wc + // (s[sxy + 2] & 0x00ff00) * wr + // (s[sxy + 3] & 0x00ff00) * wrr// ) / tw & 0x00ff00) | // // Blue component: ((// (s[sxy + 0] & 0x0000ff) * wll + // (s[sxy + 1] & 0x0000ff) * wl + // (s[sxy + 2] & 0x0000ff) * wc + // (s[sxy + 3] & 0x0000ff) * wr +// (s[sxy + 4] & 0x0000ff) * wrr// ) / tw); /* if (x == 2) { System.out.println("y:" + y + " sxy:" + sxy + " :" + Integer.toHexString(s[sxy]).substring(2) + " " + Integer.toHexString(s[sxy + 1]).substring(2) + " " + Integer.toHexString(s[sxy + 2]).substring(2) + ":" + Integer.toHexString(0xff00000 | d[dxy]).substring(2)); }*/ } // Process the last pixel on the line d[dxy] =// // Red component: ((// (s[sxy - 2] & 0xff0000) * wll + // (s[sxy - 1] & 0xff0000) * wl + // (s[sxy + 0] & 0xff0000) * wc + // (s[sxy + 1] & 0xff0000) * wr +// (s[sxy + 2] & 0xff0000) * wrr// ) / tw & 0xff0000) | // // Green component: ((// (s[sxy - 1] & 0x00ff00) * wll +// (s[sxy + 0] & 0x00ff00) * wl + // (s[sxy + 1] & 0x00ff00) * wc + // (s[sxy + 2] & 0x00ff00) * (wr + wrr) // ) / tw & 0x00ff00) | // // Blue component: ((// (s[sxy + 0] & 0x0000ff) * wll + // (s[sxy + 1] & 0x0000ff) * wl + // (s[sxy + 2] & 0x0000ff) * (wc + wr + wrr) // ) / tw); } } /** Scales down an image using HBGR antia-aliasing. * <p> * Source and destination image must be of type BufferedImage.TYPE_RGB * and have a data buffer of type DataBufferInt. * <p> * The height of source and destination image must be the same. * The width of the source image must be 3 times the width of the * destination image. * <p> * Intensity weights: * <pre> * * 1 * | * +----+----+ * | | | * 1/3 1/3 1/3 * | * +----+----+----+----+ * | | | | | * 1/9 2/9 3/9 2/9 1/9 * </pre> * * @param src The source image. * @param dst The destination image. */ public static void aaHBGR(BufferedImage src, BufferedImage dst) { WritableRaster sras = src.getRaster(); WritableRaster dras = dst.getRaster(); int[] s = ((DataBufferInt) sras.getDataBuffer()).getData(); int[] d = ((DataBufferInt) dras.getDataBuffer()).getData(); int sw = src.getWidth(); int sh = src.getHeight(); int dw = dst.getWidth(); int dh = dst.getHeight(); // weights:left-left,left,center,right,right-right,total //final int wll = 0, wl = 0, wc = 1, wr = 0, wrr = 0, tw = wll + wl + wc + wr + wrr; final int wll = 1, wl = 2, wc = 3, wr = 2, wrr = 1, tw = wll + wl + wc + wr + wrr; for (int y = 0; y < dh; y++) { int sxy = y * sw; int dxy = y * dw; int dxyeol = dxy + dw - 1; // Process the first pixel of the line. d[dxy] =// // Blue component: ((// (s[sxy + 0] & 0x0000ff) * (wll + wl + wc) +// (s[sxy + 1] & 0x0000ff) * wr +// (s[sxy + 2] & 0x0000ff) * wrr// ) / tw) | // // Green component: ((// (s[sxy + 0] & 0x00ff00) * (wll + wl) + // (s[sxy + 1] & 0x00ff00) * wc + // (s[sxy + 2] & 0x00ff00) * wr + // (s[sxy + 3] & 0x00ff00) * wrr// ) / tw & 0x00ff00) | // // Red component: ((// (s[sxy + 0] & 0xff0000) * wll + // (s[sxy + 1] & 0xff0000) * wl + // (s[sxy + 2] & 0xff0000) * wc + // (s[sxy + 3] & 0xff0000) * wr +// (s[sxy + 4] & 0xff0000) * wrr// ) / tw & 0xff0000); sxy += 3; dxy++; // Process the pixels in the middle of the line. for (; dxy < dxyeol; sxy += 3, dxy++) { d[dxy] =// // Blue component: ((// (s[sxy - 2] & 0x0000ff) * wll + // (s[sxy - 1] & 0x0000ff) * wl + // (s[sxy + 0] & 0x0000ff) * wc + // (s[sxy + 1] & 0x0000ff) * wr +// (s[sxy + 2] & 0x0000ff) * wrr// ) / tw) | // // Green component: ((// (s[sxy - 1] & 0x00ff00) * wll +// (s[sxy + 0] & 0x00ff00) * wl + // (s[sxy + 1] & 0x00ff00) * wc + // (s[sxy + 2] & 0x00ff00) * wr + // (s[sxy + 3] & 0x00ff00) * wrr// ) / tw & 0x00ff00) | // // Red component: ((// (s[sxy + 0] & 0xff0000) * wll + // (s[sxy + 1] & 0xff0000) * wl + // (s[sxy + 2] & 0xff0000) * wc + // (s[sxy + 3] & 0xff0000) * wr +// (s[sxy + 4] & 0xff0000) * wrr// ) / tw & 0xff0000); /* if (x == 2) { System.out.println("y:" + y + " sxy:" + sxy + " :" + Integer.toHexString(s[sxy]).substring(2) + " " + Integer.toHexString(s[sxy + 1]).substring(2) + " " + Integer.toHexString(s[sxy + 2]).substring(2) + ":" + Integer.toHexString(0xff00000 | d[dxy]).substring(2)); }*/ } // Process the last pixel on the line d[dxy] =// // Blue component: ((// (s[sxy - 2] & 0x0000ff) * wll + // (s[sxy - 1] & 0x0000ff) * wl + // (s[sxy + 0] & 0x0000ff) * wc + // (s[sxy + 1] & 0x0000ff) * wr +// (s[sxy + 2] & 0x0000ff) * wrr// ) / tw) | // // Green component: ((// (s[sxy - 1] & 0x00ff00) * wll +// (s[sxy + 0] & 0x00ff00) * wl + // (s[sxy + 1] & 0x00ff00) * wc + // (s[sxy + 2] & 0x00ff00) * (wr + wrr) // ) / tw & 0x00ff00) | // // Red component: ((// (s[sxy + 0] & 0xff0000) * wll + // (s[sxy + 1] & 0xff0000) * wl + // (s[sxy + 2] & 0xff0000) * (wc + wr + wrr) // ) / tw & 0xff0000); } } /** Scales down an image using VRGB antia-aliasing. * <p> * Source and destination image must be of type BufferedImage.TYPE_RGB * and have a data buffer of type DataBufferInt. * <p> * The width of source and destination image must be the same. * The height of the source image must be 3 times the height of the * destination image. * <p> * Intensity weights: * <pre> * * 1 * | * +----+----+ * | | | * 1/3 1/3 1/3 * | * +----+----+----+----+ * | | | | | * 1/9 2/9 3/9 2/9 1/9 * </pre> * * @param src The source image. * @param dst The destination image. */ public static void aaVRGB(BufferedImage src, BufferedImage dst) { WritableRaster sras = src.getRaster(); WritableRaster dras = dst.getRaster(); int[] s = ((DataBufferInt) sras.getDataBuffer()).getData(); int[] d = ((DataBufferInt) dras.getDataBuffer()).getData(); int sw = src.getWidth(); int sh = src.getHeight(); int dw = dst.getWidth(); int dh = dst.getHeight(); // weights:left-left,left,center,right,right-right,total //final int wll = 0, wl = 0, wc = 1, wr = 0, wrr = 0, tw = wll + wl + wc + wr + wrr; //final int wll = 0, wl = 1, wc = 1, wr = 1, wrr = 0, tw = wll+wl+wc+wr+wrr; final int wll = 1, wl = 2, wc = 3, wr = 2, wrr = 1, tw = wll + wl + wc + wr + wrr; for (int x = 0; x < dw; x++) { int sxy = x; int dxy = x; int dxyeol = dxy + dw * (dh - 1); // Process the first pixel of the line. d[dxy] =// // Red component: ((// (s[sxy + 0] & 0xff0000) * (wll + wl + wc) +// (s[sxy + sw] & 0xff0000) * wr +// (s[sxy + sw * 2] & 0xff0000) * wrr// ) / tw & 0xff0000) | // // Green component: ((// (s[sxy + 0] & 0x00ff00) * (wll + wl) + // (s[sxy + sw] & 0x00ff00) * wc + // (s[sxy + sw * 2] & 0x00ff00) * wr + // (s[sxy + sw * 3] & 0x00ff00) * wrr// ) / tw & 0x00ff00) | // // Blue component: ((// (s[sxy + 0] & 0x0000ff) * wll + // (s[sxy + sw] & 0x0000ff) * wl + // (s[sxy + sw * 2] & 0x0000ff) * wc + // (s[sxy + sw * 3] & 0x0000ff) * wr +// (s[sxy + sw * 4] & 0x0000ff) * wrr// ) / tw); sxy += 3 * sw; dxy += dw; // Process the pixels in the middle of the line. for (; dxy < dxyeol; sxy += 3 * sw, dxy += dw) { d[dxy] =// // Red component: ((// (s[sxy - sw * 2] & 0xff0000) * wll + // (s[sxy - sw] & 0xff0000) * wl + // (s[sxy + 0] & 0xff0000) * wc + // (s[sxy + sw] & 0xff0000) * wr +// (s[sxy + sw * 2] & 0xff0000) * wrr// ) / tw & 0xff0000) | // // Green component: ((// (s[sxy - sw] & 0x00ff00) * wll +// (s[sxy - 0] & 0x00ff00) * wl + // (s[sxy + sw] & 0x00ff00) * wc + // (s[sxy + sw * 2] & 0x00ff00) * wr + // (s[sxy + sw * 3] & 0x00ff00) * wrr// ) / tw & 0x00ff00) | // // Blue component: ((// (s[sxy + 0] & 0x0000ff) * wll + // (s[sxy + sw] & 0x0000ff) * wl + // (s[sxy + sw * 2] & 0x0000ff) * wc + // (s[sxy + sw * 3] & 0x0000ff) * wr +// (s[sxy + sw * 4] & 0x0000ff) * wrr// ) / tw); /* if (dxy <= dw*2) { System.out.println("x:" + x + " sxy:" + sxy + " :" + Integer.toHexString(s[sxy]).substring(2) + " " + Integer.toHexString(s[sxy + 1]).substring(2) + " " + Integer.toHexString(s[sxy + 2]).substring(2) + ":" + Integer.toHexString(0xff00000 | d[dxy]).substring(2)); }*/ } // Process the last pixel on the line d[dxy] =// // Red component: ((// (s[sxy - sw * 2] & 0xff0000) * wll + // (s[sxy - sw] & 0xff0000) * wl + // (s[sxy + 0] & 0xff0000) * wc + // (s[sxy + sw] & 0xff0000) * wr +// (s[sxy + sw * 2] & 0xff0000) * wrr// ) / tw & 0xff0000) | // // Green component: ((// (s[sxy - sw] & 0x00ff00) * wll +// (s[sxy + 0] & 0x00ff00) * wl + // (s[sxy + sw] & 0x00ff00) * wc + // (s[sxy + sw * 2] & 0x00ff00) * (wr + wrr) // ) / tw & 0x00ff00) | // // Blue component: ((// (s[sxy + 0] & 0x0000ff) * wll + // (s[sxy + sw] & 0x0000ff) * wl + // (s[sxy + sw * 2] & 0x0000ff) * (wc + wr + wrr) // ) / tw); } } /** Scales down an image using VBGR antia-aliasing. * <p> * Source and destination image must be of type BufferedImage.TYPE_RGB * and have a data buffer of type DataBufferInt. * <p> * The width of source and destination image must be the same. * The height of the source image must be 3 times the height of the * destination image. * <p> * Intensity weights: * <pre> * * 1 * | * +----+----+ * | | | * 1/3 1/3 1/3 * | * +----+----+----+----+ * | | | | | * 1/9 2/9 3/9 2/9 1/9 * </pre> * * @param src The source image. * @param dst The destination image. */ public static void aaVBGR(BufferedImage src, BufferedImage dst) { WritableRaster sras = src.getRaster(); WritableRaster dras = dst.getRaster(); int[] s = ((DataBufferInt) sras.getDataBuffer()).getData(); int[] d = ((DataBufferInt) dras.getDataBuffer()).getData(); int sw = src.getWidth(); int sh = src.getHeight(); int dw = dst.getWidth(); int dh = dst.getHeight(); // weights:left-left,left,center,right,right-right,total //final int wll = 0, wl = 0, wc = 1, wr = 0, wrr = 0, tw = wll + wl + wc + wr + wrr; //final int wll = 0, wl = 1, wc = 1, wr = 1, wrr = 0, tw = wll+wl+wc+wr+wrr; final int wll = 1, wl = 2, wc = 3, wr = 2, wrr = 1, tw = wll + wl + wc + wr + wrr; for (int x = 0; x < dw; x++) { int sxy = x; int dxy = x; int dxyeol = dxy + dw * (dh - 1); // Process the first pixel of the line. d[dxy] =// // Blue component: ((// (s[sxy + 0] & 0x0000ff) * (wll + wl + wc) +// (s[sxy + sw] & 0x0000ff) * wr +// (s[sxy + sw * 2] & 0x0000ff) * wrr// ) / tw) | // // Green component: ((// (s[sxy + 0] & 0x00ff00) * (wll + wl) + // (s[sxy + sw] & 0x00ff00) * wc + // (s[sxy + sw * 2] & 0x00ff00) * wr + // (s[sxy + sw * 3] & 0x00ff00) * wrr// ) / tw & 0x00ff00) | // // Red component: ((// (s[sxy + 0] & 0xff0000) * wll + // (s[sxy + sw] & 0xff0000) * wl + // (s[sxy + sw * 2] & 0xff0000) * wc + // (s[sxy + sw * 3] & 0xff0000) * wr +// (s[sxy + sw * 4] & 0xff0000) * wrr// ) / tw & 0xff0000); sxy += 3 * sw; dxy += dw; // Process the pixels in the middle of the line. for (; dxy < dxyeol; sxy += 3 * sw, dxy += dw) { d[dxy] =// // Blue component: ((// (s[sxy - sw * 2] & 0x0000ff) * wll + // (s[sxy - sw] & 0x0000ff) * wl + // (s[sxy + 0] & 0x0000ff) * wc + // (s[sxy + sw] & 0x0000ff) * wr +// (s[sxy + sw * 2] & 0x0000ff) * wrr// ) / tw) | // // Green component: ((// (s[sxy - sw] & 0x00ff00) * wll +// (s[sxy - 0] & 0x00ff00) * wl + // (s[sxy + sw] & 0x00ff00) * wc + // (s[sxy + sw * 2] & 0x00ff00) * wr + // (s[sxy + sw * 3] & 0x00ff00) * wrr// ) / tw & 0x00ff00) | // // Red component: ((// (s[sxy + 0] & 0xff0000) * wll + // (s[sxy + sw] & 0xff0000) * wl + // (s[sxy + sw * 2] & 0xff0000) * wc + // (s[sxy + sw * 3] & 0xff0000) * wr +// (s[sxy + sw * 4] & 0xff0000) * wrr// ) / tw & 0xff0000); /* if (dxy <= dw*2) { System.out.println("x:" + x + " sxy:" + sxy + " :" + Integer.toHexString(s[sxy]).substring(2) + " " + Integer.toHexString(s[sxy + 1]).substring(2) + " " + Integer.toHexString(s[sxy + 2]).substring(2) + ":" + Integer.toHexString(0xff00000 | d[dxy]).substring(2)); }*/ } // Process the last pixel on the line d[dxy] =// // Blue component: ((// (s[sxy - sw * 2] & 0x0000ff) * wll + // (s[sxy - sw] & 0x0000ff) * wl + // (s[sxy + 0] & 0x0000ff) * wc + // (s[sxy + sw] & 0x0000ff) * wr +// (s[sxy + sw * 2] & 0x0000ff) * wrr// ) / tw) | // // Green component: ((// (s[sxy - sw] & 0x00ff00) * wll +// (s[sxy + 0] & 0x00ff00) * wl + // (s[sxy + sw] & 0x00ff00) * wc + // (s[sxy + sw * 2] & 0x00ff00) * (wr + wrr) // ) / tw & 0x00ff00) | // // Red component: ((// (s[sxy + 0] & 0xff0000) * wll + // (s[sxy + sw] & 0xff0000) * wl + // (s[sxy + sw * 2] & 0xff0000) * (wc + wr + wrr) // ) / tw & 0xff0000); } } }