Cyberpython Online

Αντικατοπτρισμός & Java

Posted in java, programming, Προγραμματισμός by cyberpython on Σεπτεμβρίου 20, 2008

Πριν από λίγο καιρό χρειάστηκε να δημιουργήσω στο GIMP μία εικόνα κάτω από την οποία εμφανιζόταν ο αντικατοπτρισμός της. Η διαδικασία ήταν η εξής:

  • Δημιούργησα την εικόνα
  • Έκανα ένα αντίγραφό της ακριβώς κάτω από αυτή (σε νέο layer) αφήνοντας διάστημα ~ 2 pixels
  • Άλλαξα τη διαφάνεια του νέου layer σε 50%
  • “Αναποδογύρισα” (flip vertically) το layer του αντικατοπτρισμού
  • Έφτιαξα ακόμη ένα layer πάνω από αυτό του αντικατοπτρισμού, επέλεξα την περιοχή πάνω από τον αντικατοπτρισμό και τη γέμισα με ένα gradient που “έσβηνε” περίπου μέχρι τη μέση
  • “Έπαιξα” με τα blending modes του gradient layer μέχρι να βρω αυτό που θα έκανε τον αντικατοπτρισμό να φαίνεται οτι εξαφανίζεται από ένα σημείο και έπειτα.

Αφού είχα φτάσει το τελικό αποτέλεσμα, σκέφτηκα οτι δεν πρέπει να είναι ιδιαίτερα δύσκολο να γραφτεί μία μέθοδος σε Java που θα κάνει κάτι ανάλογο.

Πρώτα απ’ όλα θα πρέπει να δίνουμε στη μέθοδο την εικόνα της οποίας θέλουμε να δημιουργήσουμε τον αντικατοπτρισμό (ένα BufferedImage), το μέγεθος (ΠxΥ – ιnt) που επιθυμούμε για το αποτέλεσμα, το χρώμα/gradient που θα μπεί στο φόντο (2 αντικείμενα Color), το πόσο “θολός” θα είναι ο αντικατοπτρισμός (float) και πόσο γρήγορα θα “ξεθωριάζει”(float).

Άρα η υπογραφή της μεθόδου μας θα είναι κάπως έτσι:

public BufferedImage renderEffect(BufferedImage src, int width, int height, int space,
                                  Color GradientStart, Color GradientEnd, float opacity,
                                  float fadeFactor)

Πρώτο βήμα είναι να δημιουργήσουμε ένα BufferedImage όπου θα σχεδιάσουμε το αποτέλεσμα:

BufferedImage result = new BufferedImage(width, height,
                                         BufferedImage.TYPE_INT_ARGB );

Στη συνέχεια δημιουργούμε ένα αντικείμενο Graphics2D για το result:

Graphics2D g2d = result.createGraphics();

Αν κανένα από τα GradientStart, GradientEnd δεν είναι null τότε γεμίζουμε το result με το αντίστοιχο gradient, αλλιώς αν μόνο ένα είναι null γεμίζουμε με το αντίστοιχο χρώμα, αλλιώς αφήνουμε το φόντο διαφανές :

    if((GradientStart!=null)&&(GradientEnd!=null)){
     g2d.setPaint( new GradientPaint( 0, 0, GradientStart,
                                      0,height, GradientEnd ) );
     g2d.fillRect( 0, 0, width, height );
    }
    else if((GradientStart!=null)&&(GradientEnd==null)){
     g2d.setPaint( GradientStart );
     g2d.fillRect( 0, 0, width, height );
    }
    else if((GradientStart==null)&&(GradientEnd!=null)){
     g2d.setPaint(  GradientEnd  );
     g2d.fillRect( 0, 0, width, height );
    }

Στη συνέχεια σχεδιάζουμε την αρχική εικόνα (src),  κεντραρισμένη οριζοντίως ((width-srcWidth)/2) και με την κάτω βάση της στο κατακόρυφο μέσο του result((height/2)-srcHeight):

    int srcWidth = src.getWidth();
    int srcHeight = src.getHeight();

    g2d.translate( (width-srcWidth)/2, (height/2)-srcHeight );
    g2d.drawRenderedImage( src, null );

Σειρά έχει ο αντικατοπτρισμός. Φτιάχνουμε ένα νέο BufferedImage με την αρχική εικόνα:

    BufferedImage reflection = new BufferedImage( srcWidth, srcHeight,
                                                  BufferedImage.TYPE_INT_ARGB );
    Graphics2D tmp = reflection.createGraphics();
    tmp.drawRenderedImage( src, null );

Για να έχουμε το “εφέ” του ξεθωριάσματος θα γεμίσουμε την εικόνα αυτή με ένα gradient που θα ξεκινά εντελώς διάφανο από το σημείο (0, srcHeight*fadeFactor) και θα φτάνει μέχρι το τέλος της εικόνας καθώς θα γίνεται σταδιακά λιγότερο διαφανές μέχρι και την τιμή opacity:

    tmp.setComposite( AlphaComposite.getInstance( AlphaComposite.DST_IN ) );
    tmp.setPaint(
      new GradientPaint(
         0, srcHeight*fadeFactor, new Color( 0.0f, 0.0f, 0.0f, 0.0f ),
         0, srcHeight, new Color( 0.0f, 0.0f, 0.0f, opacity )
      )
    );
    tmp.fillRect( 0, 0, srcWidth, srcHeight );
    tmp.dispose();

Ο αντικατοπτρισμός είναι έτοιμος και το μόνο που μένει είναι να τον σχεδιάσουμε από κάτω προς τα πάνω κάτω από την αρχική εικόνα.

Μεταφερόμαστε στο κάτω αριστερό σημείο όπου τελειώνει η αρχική εικόνα και αφήνουμε και μερικά (space) pixels κενό:

g2d.translate( 0, 2*srcHeight+space );

Σχεδιάζουμε τον αντικατοπτρισμό:

g2d.scale( 1, -1 );
g2d.drawRenderedImage( reflection, null );

Και τέλος επιστρέφουμε το τελικό αποτέλεσμα:

return  result;

Η παρακάτω τάξη Reflection συνοψίζει τα παραπάνω :

import java.awt.image.*;
import javax.imageio.*;
import java.awt.*;
import java.io.File;

public class Reflection {

    public BufferedImage renderEffect(BufferedImage src, int width, int height, int space,
                                      Color GradientStart, Color GradientEnd, float opacity,
                                      float fadeFactor){

        BufferedImage result = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB );
        Graphics2D g2d = result.createGraphics();
        if((GradientStart!=null)&&(GradientEnd!=null)){
            g2d.setPaint( new GradientPaint( 0, 0, GradientStart, 0, height, GradientEnd ) );
            g2d.fillRect( 0, 0, width, height );
        }
        else if((GradientStart!=null)&&(GradientEnd==null)){
            g2d.setPaint( GradientStart );
            g2d.fillRect( 0, 0, width, height );
        }
        else if((GradientStart==null)&&(GradientEnd!=null)){
            g2d.setPaint(  GradientEnd  );
            g2d.fillRect( 0, 0, width, height );
        }

        int srcWidth = src.getWidth();
        int srcHeight = src.getHeight();

        g2d.translate( (width-srcWidth)/2, (height/2)-srcHeight );
        g2d.drawRenderedImage( src, null );

        BufferedImage reflection = new BufferedImage( srcWidth, srcHeight,
                                                      BufferedImage.TYPE_INT_ARGB );
        Graphics2D tmp = reflection.createGraphics();
        tmp.drawRenderedImage( src, null );

        tmp.setComposite( AlphaComposite.getInstance( AlphaComposite.DST_IN ) );
        tmp.setPaint(
        new GradientPaint(
            0, srcHeight*fadeFactor, new Color( 0.0f, 0.0f, 0.0f, 0.0f ),
            0, srcHeight, new Color( 0.0f, 0.0f, 0.0f, opacity )
            )
        );
        tmp.fillRect( 0, 0, srcWidth, srcHeight );
        tmp.dispose();

        g2d.translate( 0, 2*srcHeight+space );

        g2d.scale( 1, -1 );
        g2d.drawRenderedImage( reflection, null );

        return  result;

    }

    public static void main(String args[]){

        try {
            BufferedImage image = ImageIO.read( new File( args[0] ) );

            Reflection r = new Reflection();

            //BufferedImage res = r.renderEffect(image, image.getWidth()*2, image.getHeight()*2+50, 2, new Color(0.0f,0.0f,0.2f), new Color(0.0f,0.0f,0.5f), 0.5f, 0.5f);
            BufferedImage res = r.renderEffect(image, image.getWidth()*2, image.getHeight()*2+50, 2, Color.white, null, 0.5f, 0.5f);

            ImageIO.write(res, “png”, new File(args[1]));

        }
        catch ( Exception e ) {
         e.printStackTrace();
        }

    }

}

Η μέθοδος main της παραπάνω τάξης δέχεται από τις παραμέτρος εκτέλεσης το όνομα του αρχείου που περιέχει την αρχική εικόνα και το όνομα του αρχείου png στο οποίο θα αποθηκεύσει το τελικό αποτέλεσμα. Αν δώσουμε την παρακάτω εικόνα σαν είσοδο στο πρόγραμμά μας :

το αποτέλεσμα θα είναι αυτό:

Μία Απάντηση

Subscribe to comments with RSS.

  1. sandra said, on Σεπτεμβρίου 21, 2008 at 5:39 μμ

    Ότι θα πήγαινε το μυαλό σου από το GIMP σε συνάρτηση στην Java :D


Υποβολή απάντησης