2016-01-09 5 views
5

Graphics2d का उपयोग करके एक छवि खींचें, मैं पृष्ठभूमि छवि के शीर्ष पर BufferedImage खींचने की कोशिश कर रहा हूं। इस छवि में मनमाने ढंग से बिंदु पर, मैं पृष्ठभूमि को दिखाने के लिए खींची गई छवि में "गोलाकार छेद काटना" चाहता हूं।जावा ग्राफिक्स 2 डी - ग्रेडिएंट अस्पष्टता

मैं चाहता हूं कि छेद एक ठोस आकार न हो, बल्कि एक ढाल। दूसरे शब्दों में, BufferedImage में प्रत्येक पिक्सेल में छेद के केंद्र से इसकी दूरी के लिए आनुपातिक अल्फा/अस्पष्ट होना चाहिए।

मैं Graphics2d ग्रेडियेंट और AlphaComposite के साथ कुछ हद तक परिचित हूं, लेकिन क्या इन्हें गठबंधन करने का कोई तरीका है?

क्या इस प्रभाव को प्राप्त करने के लिए कोई (अत्यधिक महंगा नहीं) तरीका है?

उत्तर

6

यह एक RadialGradientPaint और उचित AlphaComposite के साथ हल किया जा सकता है।

निम्नलिखित MCVE है जो दिखाता है कि यह कैसे किया जा सकता है। यह user1803551 used in his answer के समान छवियों का उपयोग करता है, इसलिए एक स्क्रीनशॉट (लगभग) समान दिखाई देगा।

  • यह पहली बार साथ छवि भरता है: लेकिन यह एक एक MouseMotionListener कि आप के चारों ओर ले जाने के लिए छेद, updateGradientAt विधि, जहां वांछित छवि के वास्तविक निर्माण होता है करने के लिए वर्तमान माउस स्थिति पारित करके की अनुमति देता है कहते हैं मूल छवि
  • फिर यह RadialGradientPaint बनाता है, जिसमें केंद्र में एक पूरी तरह से अपारदर्शी रंग है, और सीमा (!) पर एक पूरी तरह से पारदर्शी रंग है। यह counterintuitive देखा जा सकता है, लेकिन इरादा एक मौजूदा छवि के छेद को "काट" करना है, जो अगले चरण के साथ किया जाता है:
  • AlphaComposite.DstOutGraphics2D को सौंपा गया है। यह एक, एक अल्फा मूल्यों की "उलट" का कारण बनता है सूत्र में के रूप में

    Ar = Ad*(1-As) 
    Cr = Cd*(1-As) 
    

    जहां "परिणाम", "स्रोत" के लिए s खड़ा है, और "गंतव्य के लिए" d खड़ा

के लिए r खड़ा

परिणाम एक ऐसी छवि है जिसमें वांछित स्थान पर रेडियल ग्रेडियेंट पारदर्शिता है, जो केंद्र में पूरी तरह पारदर्शी है और सीमा (!) पर पूरी तरह से अपारदर्शी है। Paint और Composite का यह संयोजन तब छेद के आकार और निर्देशांक के साथ अंडाकार भरने के लिए उपयोग किया जाता है। (कोई भी fillRect कॉल भी कर सकता है, पूरी छवि भर रहा है - यह परिणाम नहीं बदलेगा)।

import java.awt.AlphaComposite; 
import java.awt.Color; 
import java.awt.Graphics; 
import java.awt.Graphics2D; 
import java.awt.Point; 
import java.awt.RadialGradientPaint; 
import java.awt.event.MouseAdapter; 
import java.awt.event.MouseEvent; 
import java.awt.image.BufferedImage; 
import java.io.File; 
import java.io.IOException; 

import javax.imageio.ImageIO; 
import javax.swing.JFrame; 
import javax.swing.JPanel; 
import javax.swing.SwingUtilities; 

public class TransparentGradientInImage 
{ 
    public static void main(String[] args) 
    { 
     SwingUtilities.invokeLater(new Runnable() 
     { 
      @Override 
      public void run() 
      { 
       createAndShowGUI(); 
      } 
     }); 
    } 

    private static void createAndShowGUI() 
    { 
     JFrame f = new JFrame(); 
     f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 

     TransparentGradientInImagePanel p = 
      new TransparentGradientInImagePanel(); 
     f.getContentPane().add(p); 
     f.setSize(800, 600); 
     f.setLocationRelativeTo(null); 
     f.setVisible(true); 
    } 

} 

class TransparentGradientInImagePanel extends JPanel 
{ 
    private BufferedImage background; 
    private BufferedImage originalImage; 
    private BufferedImage imageWithGradient; 

    TransparentGradientInImagePanel() 
    { 
     try 
     { 
      background = ImageIO.read(
       new File("night-sky-astrophotography-1.jpg")); 
      originalImage = convertToARGB(ImageIO.read(new File("7bI1Y.jpg"))); 
      imageWithGradient = convertToARGB(originalImage); 
     } 
     catch (IOException e) 
     { 
      e.printStackTrace(); 
     } 

     addMouseMotionListener(new MouseAdapter() 
     { 
      @Override 
      public void mouseMoved(MouseEvent e) 
      { 
       updateGradientAt(e.getPoint()); 
      } 
     }); 
    } 


    private void updateGradientAt(Point point) 
    { 
     Graphics2D g = imageWithGradient.createGraphics(); 
     g.drawImage(originalImage, 0, 0, null); 

     int radius = 100; 
     float fractions[] = { 0.0f, 1.0f }; 
     Color colors[] = { new Color(0,0,0,255), new Color(0,0,0,0) }; 
     RadialGradientPaint paint = 
      new RadialGradientPaint(point, radius, fractions, colors); 
     g.setPaint(paint); 

     g.setComposite(AlphaComposite.DstOut); 
     g.fillOval(point.x - radius, point.y - radius, radius * 2, radius * 2); 
     g.dispose(); 
     repaint(); 
    } 

    private static BufferedImage convertToARGB(BufferedImage image) 
    { 
     BufferedImage newImage = 
      new BufferedImage(image.getWidth(), image.getHeight(), 
       BufferedImage.TYPE_INT_ARGB); 
     Graphics2D g = newImage.createGraphics(); 
     g.drawImage(image, 0, 0, null); 
     g.dispose(); 
     return newImage; 
    } 

    @Override 
    protected void paintComponent(Graphics g) 
    { 
     super.paintComponent(g); 
     g.drawImage(background, 0, 0, null); 
     g.drawImage(imageWithGradient, 0, 0, null); 
    } 
} 

आप अलग अलग प्रभाव को प्राप्त करने fractions और RadialGradientPaint की colors साथ निभा सकते हैं। उदाहरण के लिए, इन मूल्यों ...

float fractions[] = { 0.0f, 0.1f, 1.0f }; 
Color colors[] = { 
    new Color(0,0,0,255), 
    new Color(0,0,0,255), 
    new Color(0,0,0,0) 
}; 

कारण एक छोटी सी, पारदर्शी छेद, एक बड़े, सॉफ्ट "प्रभामंडल" के साथ:

TransparentGradientInImage02.png

इन मूल्यों जबकि

float fractions[] = { 0.0f, 0.9f, 1.0f }; 
Color colors[] = { 
    new Color(0,0,0,255), 
    new Color(0,0,0,255), 
    new Color(0,0,0,0) 
}; 

एक छोटे, कोरोना "के साथ एक बड़ा, तेज़ पारदर्शी केंद्र का कारण बनता है:

TransparentGradientInImage01.png

RadialGradientPaint JavaDocs कुछ उदाहरण हैं जो वांछित मूल्यों को खोजने में मदद कर सकते हैं।


कुछ संबंधित प्रश्नों जहाँ मैं पोस्ट (समान) उत्तर:


संपादित प्रदर्शन के बारे में प्रश्न के उत्तर में कि कॉम में पूछा गया था एनटीएस

कैसे Paint/Composite दृष्टिकोण का प्रदर्शन getRGB/setRGB दृष्टिकोण की तुलना का सवाल वास्तव में दिलचस्प है। मेरे पिछले अनुभव से, मेरा आंत महसूस होता कि पहला व्यक्ति दूसरे की तुलना में बहुत तेज होता है, क्योंकि, सामान्यतः getRGB/setRGB धीमा हो जाता है, और अंतर्निर्मित तंत्र अत्यधिक अनुकूलित होते हैं (और, कुछ मामलों में , हार्डवेयर भी तेज हो सकता है)।

वास्तव में, Paint/Composite दृष्टिकोण getRGB/setRGB दृष्टिकोण की तुलना में तेजी है, लेकिन के रूप में ज्यादा नहीं के रूप में मैं उम्मीद है। निम्नलिखित नहीं एक बहुत गहरा "बेंचमार्क" (मैं इस बात के लिए कैलिपर या JMH को रोजगार नहीं था) निश्चित रूप से है, लेकिन वास्तविक प्रदर्शन के बारे में एक अच्छा अनुमान देना चाहिए:

// NOTE: This is not really a sophisticated "Benchmark", 
// but gives a rough estimate about the performance 

import java.awt.AlphaComposite; 
import java.awt.Color; 
import java.awt.Graphics2D; 
import java.awt.Point; 
import java.awt.RadialGradientPaint; 
import java.awt.image.BufferedImage; 

public class TransparentGradientInImagePerformance 
{ 
    public static void main(String[] args) 
    { 
     int w = 1000; 
     int h = 1000; 
     BufferedImage image0 = new BufferedImage(w, h, 
      BufferedImage.TYPE_INT_ARGB); 
     BufferedImage image1 = new BufferedImage(w, h, 
      BufferedImage.TYPE_INT_ARGB); 

     long before = 0; 
     long after = 0; 
     int runs = 100; 
     for (int radius = 100; radius <=400; radius += 10) 
     { 
      before = System.nanoTime(); 
      for (int i=0; i<runs; i++) 
      { 
       transparitize(image0, w/2, h/2, radius); 
      } 
      after = System.nanoTime(); 
      System.out.println(
       "Radius "+radius+" with getRGB/setRGB: "+(after-before)/1e6); 

      before = System.nanoTime(); 
      for (int i=0; i<runs; i++) 
      { 
       updateGradientAt(image0, image1, new Point(w/2, h/2), radius); 
      } 
      after = System.nanoTime(); 
      System.out.println(
       "Radius "+radius+" with paint   "+(after-before)/1e6); 
     } 
    } 

    private static void transparitize(
     BufferedImage imgA, int centerX, int centerY, int r) 
    { 

     for (int x = centerX - r; x < centerX + r; x++) 
     { 
      for (int y = centerY - r; y < centerY + r; y++) 
      { 
       double distance = Math.sqrt(
        Math.pow(Math.abs(centerX - x), 2) + 
        Math.pow(Math.abs(centerY - y), 2)); 
       if (distance > r) 
        continue; 
       int argb = imgA.getRGB(x, y); 
       int a = (argb >> 24) & 255; 
       double factor = distance/r; 
       argb = (argb - (a << 24) + ((int) (a * factor) << 24)); 
       imgA.setRGB(x, y, argb); 
      } 
     } 
    } 

    private static void updateGradientAt(BufferedImage originalImage, 
     BufferedImage imageWithGradient, Point point, int radius) 
    { 
     Graphics2D g = imageWithGradient.createGraphics(); 
     g.drawImage(originalImage, 0, 0, null); 

     float fractions[] = { 0.0f, 1.0f }; 
     Color colors[] = { new Color(0, 0, 0, 255), new Color(0, 0, 0, 0) }; 
     RadialGradientPaint paint = new RadialGradientPaint(point, radius, 
      fractions, colors); 
     g.setPaint(paint); 

     g.setComposite(AlphaComposite.DstOut); 
     g.fillOval(point.x - radius, point.y - radius, radius * 2, radius * 2); 
     g.dispose(); 
    } 
} 

मेरे पीसी पर समय के साथ कर रहे हैं

... 
Radius 390 with getRGB/setRGB: 1518.224404 
Radius 390 with paint   764.11017 
Radius 400 with getRGB/setRGB: 1612.854049 
Radius 400 with paint   794.695199 

की तर्ज दिखा रहा है कि Paint/Composite विधि मोटे तौर पर के रूप में तेजी से getRGB/setRGB पद्धति के रूप में दो बार है। प्रदर्शन के अलावा, Paint/Composite में कुछ अन्य फायदे हैं, मुख्य रूप से ऊपर वर्णित RadialGradientPaint के संभावित पैरामीट्रिजेशन, जो कारण हैं कि मैं इस समाधान को क्यों पसंद करूंगा।

+0

बहुत अच्छा, क्या यह मेरे समाधान से बेहतर प्रदर्शन देता है? – user1803551

+1

@ user1803551 यह एक बेहतर प्रदर्शन (इसके लिए एक संपादन जोड़ा गया) प्रतीत होता है, लेकिन जितना मैंने अपेक्षा की थी उतनी नहीं। (यह जटिलता/लचीलापन के कारण हो सकता है ' RadialGradientPaint' - आप इसके साथ कुछ निफ्टी प्रभाव प्राप्त कर सकते हैं, और बस "छेद काटने" इस वर्ग के लिए सबसे आसान संभव मामलों में से एक है) – Marco13

2

मुझे नहीं पता कि क्या आप इस पारदर्शी "छेद" को गतिशील रूप से बनाना चाहते हैं या यदि यह एक बार की बात है। मुझे यकीन है कि आप जो चाहते हैं उसे पूरा करने के लिए कई तरीके हैं और मैं उनमें से एक को पिक्सेल को सीधे बदलने के साथ दिखा रहा हूं, जो शायद सर्वश्रेष्ठ प्रदर्शन-वार नहीं हो सकता है (मैं बस अन्य तरीकों से तुलना नहीं करता और मुझे लगता है कि यह इस बात पर निर्भर करेगा कि आप वास्तव में क्या करते हैं)।

यहाँ मैं ऑस्ट्रेलिया के ऊपर ओजोन परत में छेद को दर्शाती:

enter image description here

public class Paint extends JPanel { 

    BufferedImage imgA; 
    BufferedImage bck; 

    Paint() { 

     BufferedImage img = null; 
     try { 
      img = ImageIO.read(getClass().getResource("img.jpg")); // images linked below 
      bck = ImageIO.read(getClass().getResource("bck.jpg")); 
     } catch (IOException e) { 
      e.printStackTrace(); 
     } 
     imgA = new BufferedImage(img.getWidth(), img.getHeight(), BufferedImage.TYPE_INT_ARGB); 
     Graphics2D g2d = imgA.createGraphics(); 
     g2d.drawImage(img, 0, 0, null); 
     g2d.dispose(); 

     transparitize(200, 100, 80); 
    } 

    private void transparitize(int centerX, int centerY, int r) { 

     for (int x = centerX - r; x < centerX + r; x++) { 
      for (int y = centerY - r; y < centerY + r; y++) { 
       double distance = Math.sqrt(Math.pow(Math.abs(centerX - x), 2) 
              + Math.pow(Math.abs(centerY - y), 2)); 
       if (distance > r) 
        continue; 
       int argb = imgA.getRGB(x, y); 
       int a = (argb >> 24) & 255; 
       double factor = distance/r; 
       argb = (argb - (a << 24) + ((int) (a * factor) << 24)); 
       imgA.setRGB(x, y, argb); 
      } 
     } 
    } 

    @Override 
    protected void paintComponent(Graphics g) { 

     super.paintComponent(g); 
     g.drawImage(bck, 0, 0, null); 
     g.drawImage(imgA, 0, 0, null); 
    } 

    @Override 
    public Dimension getPreferredSize() { 

     return new Dimension(bck.getWidth(), bck.getHeight()); // because bck is larger than imgA, otherwise use Math.max 
    } 
} 

विचार getRGB साथ पिक्सेल के ARGB मूल्य प्राप्त करने, अल्फा (या कुछ और) बदल जाता है, और इसे setRGB के साथ सेट करें। मैंने एक विधि बनाई है जो एक रेडियल ढाल बनाता है जो एक केंद्र और त्रिज्या देता है। यह निश्चित रूप से सुधार किया जा सकता है, मैं इसे आपके पास छोड़ दूंगा (संकेत: centerX - r सीमा से बाहर हो सकता है; distance > r के साथ पिक्सेल पूरी तरह से पुनरावृत्ति से हटाया जा सकता है)।

नोट्स:

  • मैं पृष्ठभूमि छवि चित्रित और यह की चोटी पर छोटे से अधिक की छवि अभी स्पष्ट रूप से दिखाने के लिए पृष्ठभूमि कैसा दिखता है।
  • int के अल्फा मान को पढ़ने और बदलने के कुछ तरीके हैं, इस साइट को खोजें और आपको कम से कम 2-3 और तरीके मिलेंगे।
  • अपने पसंदीदा शीर्ष स्तर के कंटेनर में जोड़ें और चलाएं।

सूत्रों का कहना है:

+0

धन्यवाद, अगर मैं एक और समाधान नहीं ढूंढ पा रहा था तो यह कम या कम है जो मैं करने की योजना बना रहा था। मैं बस सोच रहा था कि ऐसा करने के लिए कुछ अंतर्निहित, अनुकूलित फ़ंक्शन था या नहीं। – B1CL0PS

+1

कुछ सीमाएं जांच भी आवश्यक हो सकती हैं, उदा। इस मामले के लिए विधि (100,100) के साथ एक छवि पर पैरामीटर (99,99,10) के साथ विधि कहा जाता है। – Marco13

+0

@ मार्को 13 मैंने विधि के बारे में बात करते समय सीमाओं का उल्लेख किया है ("* यह निश्चित रूप से सुधार किया जा सकता है, मैं इसे आपको छोड़ दूंगा (संकेत:' centerX - r' सीमाओं से बाहर हो सकता है *) – user1803551

संबंधित मुद्दे