paint中的Graphics从何而来?

时间:2021-12-03 11:56:33

Canvas中调用repaint后,系统会调用paint(Graphics g),而能给与程序员发挥的空间就在paint里,那么,paintGraphics从那里来的呢?换句话说,我们paint操作的东西到底是什么呢?他又是怎么反应到手机硬件屏幕上了呢?

 1。我们先来看看repaint是怎么实现的。

public final void repaint(int x, int y, int width, int height) {

        synchronized (Display.LCDUILock) {

            callRepaint(x + viewport[X], y + viewport[Y], width, height, null);

        }

    }

 

 2。那么callReapint是怎么实现的呢?

Displable里有一个方法(以final标识),此方法禁止任何子类重写,就是为了保证只有他来控制repaint事件的传递:

final void callRepaint(int x, int y, int width, int height, Object target) {

        if (currentDisplay != null) {

            // Note: Display will not let anyone but the current

            // Displayable schedule repaints

            currentDisplay.repaintImpl(paintDelegate, x, y, width, height, target);//targetNULL

        }

    }

3,Diaplay里,此方法的实现如下:

    void repaintImpl(Displayable d, int x, int y, int w, int h,

                     Object target) {

 

        synchronized (LCDUILock) {

            if (paintSuspended || !hasForeground || d != current) {

                return;

            }

        }

 

        eventHandler.scheduleRepaint(x, y, w, h, target);//Repaint的请求部署下去

    }

4.   /**

     * Called to schedule a repaint of the current Displayable

     * as soon as possible

     *

     * @param x     The x coordinate of the origin of the repaint rectangle

     * @param y     The y coordinate of the origin of the repaint rectangle

     * @param w     The width of the repaint rectangle

     * @param h     The height of the repaint rectangle

     * @param target An optional target Object, which may have been the

     *               original requestor for the repaint

     */

    public void scheduleRepaint(int x, int y, int w, int h, Object target) {

        eventQueue.push(x, y, w, h, target);//安排后的处理就是往事件队列里push一个消息

    }

5.

 

        /**

         * Push a repaint

         *

         * @param x The x origin coordinate

         * @param y The y origin coordinate

         * @param w The width

         * @param h The height

         * @param target The optional paint target

         */

        public void push(int x, int y, int w, int h, Object target) {

 

            try {

                w += x; // convert from width, height to absolute

                h += y; //  x2, y2

              if (x < 0) x = 0;

              if (y < 0) y = 0;

 

                synchronized (qLock) {

                    if (paintX1 == -1) {

                        // If we have no pending repaint

                        // just store the region

                     paintX1 = x;

                     paintY1 = y;

                     paintX2 = w;

                     paintY2 = h;

                     paintTarget = target;

                    } else {

                        // If there is a pending repaint

                        // union the dirty regions

                        if (paintX1 > x) {

                            paintX1 = x;

                        }

                        if (paintY1 > y) {

                            paintY1 = y;

                        }

                        if (paintX2 < w) {

                            paintX2 = w;

                        }

                        if (paintY2 < h) {

                            paintY2 = h;

                        }

                        paintTarget = null;

                    }

                } // synchronized

            } catch (Throwable t) {

                t.printStackTrace();

            }

            queuedEventHandler.process(); //push消息后,就要立即进行处理,

        }

6.  其实,一个事件队列就是一个无限循环的线程,不停的检查队列,看是否有需要处理的消息,有则处理,无则等待。现在既然已经push进去一个消息,就要立即唤醒他去处理这个消息。所以自然就notify了。

        /**

         * Signal this handler there is a need to process

         * a pending event.

         */

        public synchronized void process() {

            try {

                notify();

            } catch (Throwable t) {

                // TO DO: Do something more useful with this

                t.printStackTrace();

            }

        }

7. 我们来看看事件队列EventQuene的线程是如何定义的,其中repaintScreenEvent是来对repaint事件进行处理的。

/**

         * Process events from the EventQueue

         */

        public void run() {

            int x1, y1, x2, y2, type = 0;

            Display parentOfNextScreen = null;

            Displayable nextScreen = null;

            boolean call = false;

                    

                     ........

                      if (x1 != -1) {

                        repaintScreenEvent(x1, y1, x2, y2, target);

                        x1 = y1 = x2 = y2 = -1;

                        target = null;

                    }

8. 那么,我们再来看看是如何实现repaintScreenEvent的,

/**

     * Process a repaint event

     *

     * @param x1 The x origin coordinate

     * @param y1 The y origin coordinate

     * @param x2 The lower right x coordinate

     * @param y2 The lower right y coordinate

     * @param target The optional paint target

     */

    void repaintScreenEvent(int x1, int y1, int x2, int y2, Object target) {

        try {

            synchronized (eventLock) {

                displayManager.repaint(x1, y1, x2, y2, target);//调用了屏幕管理器的reapint,请注意,这是5个参数的repaint

            }

        } catch (Throwable t) {

            handleThrowable(t);

        }

    }

9.   这个5个参数的实现方法如下:

/*

     * SYNC NOTE: this method performs its own locking of

     * LCDUILock and calloutLock.  Therefore, callers

     * must not hold any locks when they call this method.

     */

    void repaint(int x1, int y1, int x2, int y2, Object target) {

        Displayable currentCopy = null;

 

        synchronized (LCDUILock) {

            if (paintSuspended || !hasForeground) {

                return;

            }

            currentCopy = current;

        }

 

        if (currentCopy == null) {

            return;

        }

 

        screenGraphics.reset(x1, y1, x2, y2);

        current.callPaint(screenGraphics, target);

        refresh(x1, y1, x2, y2);

    }

10. 可见,screenGraphcis当作了参数传递给了callPaint,在需要知道screenGraphics如何来的之前,我们先看看callPaint方法。

/**

     * Paint this Canvas

     *

     * @param g the Graphics to paint to

     * @param target the target Object of this repaint

     */

    void callPaint(Graphics g, Object target) {

        super.callPaint(g, target);

 

        if (g.getClipY() + g.getClipHeight() <= viewport[Y]) {

            return;

        }

 

        // We prevent the Canvas from drawing outside of the

        // allowable viewport - such as over the command labels

        g.clipRect(viewport[X], viewport[Y],

                   viewport[WIDTH],

                   viewport[HEIGHT]);

 

        synchronized (Display.calloutLock) {

            g.translate(viewport[X], viewport[Y]);

            try {

                paint(g); //这就是调用后repaint方法后,系统会调用到paint(g),这个g就是调用callPaint方法传递过来的screenGraphcis,请看下面分析

            } catch (Throwable t) {

                Display.handleThrowable(t);

            }

            g.translate(-viewport[X], -viewport[Y]);

        }

    }

11,现在我们来看看screenGraphics是从那里来的,在Display类里,有一段静态数据初始化的代码

./*

 * ************* Static initializer, constructor

 */

    static {

 

        /* done this way because native access to static fields is hard */

        DeviceCaps c = new DeviceCaps();

 

        WIDTH               = c.width;

        HEIGHT              = c.height;

        ADORNEDHEIGHT       = c.adornedHeight;

        ………

 

        c = null; // let the DeviceCaps instance be garbage collected

 

        /* Let com.sun.midp classes call in to this class. */

        displayManagerImpl = new DisplayManagerImpl();//初始化屏幕管理器

        DisplayManagerFactory.SetDisplayManagerImpl(displayManagerImpl); //得到屏幕管理器,这个一个工厂方法,为以后使用做准备,此处不赘叙

        deviceAccess = new DisplayDeviceAccess(); //猜测可能是KVMDEVICE的桥接器

        eventHandler = getEventHandler(); //事件处理器

 

        screenGraphics = Graphics.getGraphics(null);//哈哈,screenGraphics找到了

    }

      

       12. Graphics类里有个方法,getGraphics,但是,这个方法是默认的package级别的访问权限。所以我们不会看到。

    static Graphics getGraphics(Image img) {

        if (img == null) {

            return new Graphics(Display.WIDTH, Display.HEIGHT);

        } else {

            return new ImageGraphics(img); //猜猜它是为谁服务的?

        }

    }

13.  Graphics的构造方法如下:

 Graphics(int w, int h) {

        destination = null;

 

        maxWidth  = (short) (w & 0x7fff);

        maxHeight = (short) (h & 0x7fff);

 

        init();

        reset();

    }

14.   天哪,终于找到了,native的一个接口,init,是它来初始化了Graphics

        /**

     * Intialize the native peer of this Graphics context

     */

private native void init();

 

15.其实,找到这里,还不能说挖掘到了最根本的实现,但是,我们暂且就挖到这里,所以,总结一下,paint中的Graphics,其实是Display来提供的,只是,这个变量不会让我们看到。那么KVM的底层实现,也就是nativeinit(),会把这个Graphics给对接到某个具体的平台,比如win32GDISymbianGraphics GDI,还有LINUX常见的QT平台的GDI等等。

最后还望各位看官给多多指点错误。我去喝咖啡去了。。。