Avatar billede forceprogrammer Nybegynder
02. december 2008 - 19:32 Der er 5 kommentarer

Centrér på bestemt pixel ved zoom

Hej eksperter
Jeg er ved at implementere en zoom funktion hvor man i et jscrollpane kan zoome ind og ud på et billede. Når man foretager et zoom skal viewporten centreres på den pixel der blev klikket på, dvs. både ved zoom ind og ud.
Jeg har isoleret et eksempel her hvor zoom ind funktionen ser ud til at virke perfekt. Zoom ud funktionen derimod zoomer ud, men den hopper rundt på JPaneler istedet for at centrere omkring den rigtige pixel. Jeg forstår det ikke eftersom de er implementeret helt ens bare omvendt. Desuden synes jeg umiddelbart de koordinater jeg udregner er korrekte, men alligvel scrolles til forkert position når scrollRectToVisible(..) kaldes.

Jeg håber nogle af jer eksperter kan hjælpe mig.
Mange hilsner herfra og tak på forhånd.

Implementering følger: (Kan køres direkte, husk dog at ski sti og navn på billede)

import javax.swing.*;
import java.awt.*;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;

public class ScrollPaneBidule extends JPanel implements MouseListener{
   
    private java.awt.image.BufferedImage editI = null;
    private Graphics2D editg;     //graphics object for mask image
    private JScrollPane theScrollPane = null;
    private Rectangle rect = null;
    private JComponent component = null;
    private boolean firsttime=true;
    private double factor=1;
   
   
    public ScrollPaneBidule(Image I) {
        editI=new BufferedImage(I.getWidth(null),I.getHeight(null),BufferedImage.TYPE_INT_RGB);
        editg=editI.createGraphics();
        editg.drawImage(I, 0, 0, this); // Copy image to the buffered edit Image
        setPreferredSize(new Dimension(I.getWidth(null),I.getHeight(null)));
        addMouseListener(this);
       
    }
    public void registerScrollPanel(JScrollPane p){
        theScrollPane=p;
    }
    protected void paintComponent(Graphics g){
        //g.drawImage(editI, 0, 0, this);
        Graphics2D g2 = (Graphics2D)g;
        g2.drawImage(editI, new AffineTransform(factor,0,0,factor,0,0), null);//
    }
       
    public void mousePressed(MouseEvent arg0){
        int X=arg0.getPoint().x,Y=arg0.getPoint().y;
        if(firsttime)component = (JComponent)theScrollPane.getViewport().getView();
        firsttime=false;
        double newZoom=1.0;
        if(arg0.getButton()==MouseEvent.BUTTON1){      factor*=2;newZoom=2.0;}//Zoom in on left click
        else if(arg0.getButton()==MouseEvent.BUTTON3){ factor/=2;newZoom=1.0/2.0;}//Zoom out on right click
       
        rect = component.getVisibleRect();
       
        rect.x = (int)((((double)(getPreferredSize().width *newZoom))*(((double)X)/((double)getPreferredSize().width )))-((double)(rect.width/2 )));
        rect.y = (int)((((double)(getPreferredSize().height*newZoom))*(((double)Y)/((double)getPreferredSize().height)))-((double)(rect.height/2)));

        System.out.println("__________________________________________________________");
        System.out.println("PanelPreferredSize: {width: "+editI.getWidth()+", height: "+editI.getWidth()*factor+"} newZoom={"+newZoom+"}, X={"+X+"}, Y={"+Y+"} , rect: "+rect);
       
        System.out.println("Size of new image(editI scaled): width: "+editI.getWidth()+", height: "+editI.getWidth()*factor+", factor: "+factor);
       
        setPreferredSize(new Dimension((int)(((double)editI.getWidth())*factor),(int)(((double)editI.getHeight())*factor)));
        revalidate();
        repaint();
        component.scrollRectToVisible(rect);
        rect = component.getVisibleRect();
        System.out.println(" rect after scroll:"+rect);
        System.out.println("__________________________________________________________");
    }
    public void mouseClicked(MouseEvent e){}
    public void mouseEntered(MouseEvent e){}
    public void mouseExited(MouseEvent e){}
    public void mouseReleased(MouseEvent e){}
   
    public static void main(String[] args) {
        EventQueue.invokeLater(new Runnable() {
            public void run() {
                try {
                    JFrame frame = new JFrame(ScrollPaneBidule.class.getName());
                    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                   
                    ScrollPaneBidule s = new ScrollPaneBidule(new ImageIcon(java.awt.Toolkit.getDefaultToolkit().getImage("C:\\Users\\bdi\\workspace\\SpectrumAnnotater\\images\\testbillede.jpg")).getImage());
                    JScrollPane scrollPane = new JScrollPane(s);
                    s.registerScrollPanel(scrollPane);
                    scrollPane.setPreferredSize(new Dimension(900,700));
                    frame.setContentPane(scrollPane);
                    frame.pack();
                    frame.setVisible(true);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        });
    }
}
Avatar billede forceprogrammer Nybegynder
04. december 2008 - 02:15 #1
er der slet ikke nogle der har et bud på hvad der går galt? :(
Avatar billede aloman Nybegynder
06. december 2008 - 00:32 #2
Jeg er ikke helt sikker at jeg forstår alt hvad du beregner, men når du beregner

        rect.x = (int)((((double)(getPreferredSize().width *newZoom))*(((double)X)/((double)getPreferredSize().width )))-((double)(rect.width/2 )));
        rect.y = (int)((((double)(getPreferredSize().height*newZoom))*(((double)Y)/((double)getPreferredSize().height)))-((double)(rect.height/2)));

..., så dividerer du med 2 uanset om du zoomer ind eller ud. Det syntes jeg virker mærkeligt.

For mig ser det ud som om du finder det sted hvor musen ville ramme, hvis den pegede på samme sted efter billedet er zoomet.

Hvis rect er det område du kan se, skal det vel også skaleres op og ned, når resten af fladen bliver det, ellers regner du på 2 flader med hver sin skala i samme formel.

Bare en strøtanke.
Avatar billede forceprogrammer Nybegynder
06. december 2008 - 13:37 #3
nej du læser det forkert.
Det sidste led sørger bare for at centrere vinduet omkring musepointeren. som du ser bliver newZoom ændret til .5 el. 2 alt efter om man trykker højre el. venstre museknap. Dvs. man zoomer ind el. ud.
Avatar billede aloman Nybegynder
06. december 2008 - 22:06 #4
Jeg skal lige tænke:
Et billed1 er 600x400.
Du har zoomet ind så du kan se rektanglet(100,200 -> 200,400), størrelse (100x200)
du klikker på punktet (150,300).

Billeet bliver nu dobbelt så stort på begge leder. billed2: 1200x800
efter billedet er zoomet ind vil det punkt du klikkede på ligge på (300,600)
det rektangel du kunne se før er nu (200,400 -> 400,800)

Det rektangel du faktisk kan se er kun halvt så stort og skal ligge symmetrisk omkring der hvor klik-punktet er flyttet hen.
Det bliver... rektangel2 (250,500 -> 350,700) .

Her passer din beregningsmetode.

lad os nu zoome ud:

Vi vælger at klikke på (300,600)
Billedet bliver nu halvt så stort på begge leder altså, Billed3: 600x400
Efter zoom-ind vil det punkt vi har klikket på ligge på (150,300)

Det rektangel vi kunne se før zoom er nu på (125,250 -> 175,350), størrelse 50x100

Det område vi faktisk kan se er dobbelt så stort og symmetrisk omkring der hvor klik-punktet er efter zoom-ind,
(det er her jeg mener at rect.x og rect.y skal skaleres op sammen med (getPreferredSize()) fladen)

området vi kan se bliver som før (100,200 -> 200,400), størrelse (100x200)

Vi kan stadig være uenige, men jag kan vidst ikke komme nærmere på en forklaring af min synsvinkel.

Du skal også huske på at, hvis det rektangel du kan se overskrider kanten på billedet, vil det blive flyttet tilbage indenfor rammen, uanset hvad du beregner.
Avatar billede aloman Nybegynder
06. december 2008 - 22:13 #5
Fejlretning (hvorfor kan man ikke rediger orginalen)

Efter "lad os nu zoome ud" skal "zoom-ind" være "zoom-ud".
Avatar billede Ny bruger Nybegynder

Din løsning...

Tilladte BB-code-tags: [b]fed[/b] [i]kursiv[/i] [u]understreget[/u] Web- og emailadresser omdannes automatisk til links. Der sættes "nofollow" på alle links.

Loading billede Opret Preview
Kategori
Kurser inden for grundlæggende programmering

Log ind eller opret profil

Hov!

For at kunne deltage på Computerworld Eksperten skal du være logget ind.

Det er heldigvis nemt at oprette en bruger: Det tager to minutter og du kan vælge at bruge enten e-mail, Facebook eller Google som login.

Du kan også logge ind via nedenstående tjenester