1 /*
   2  * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  * 
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  * 
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  * 
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  * 
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package com.sun.javafx.sg.prism;
  27 
  28 import com.sun.javafx.geom.Rectangle;
  29 import com.sun.javafx.sg.PGExternalNode;
  30 
  31 import com.sun.prism.Graphics;
  32 import com.sun.prism.PixelFormat;
  33 import com.sun.prism.ResourceFactory;
  34 import com.sun.prism.Texture;
  35 
  36 import java.nio.Buffer;
  37 import java.util.concurrent.locks.Lock;
  38 import java.util.concurrent.locks.ReentrantLock;
  39 
  40 public class NGExternalNode extends NGNode implements PGExternalNode {
  41 
  42     // pixel buffer of the source image
  43     volatile private Buffer srcbuffer;
  44     
  45     // line stride of the source buffer
  46     volatile private int linestride;
  47 
  48     // source image bounds
  49     volatile private int srcx;
  50     volatile private int srcy;
  51     volatile private int srcwidth;
  52     volatile private int srcheight;
  53 
  54     // of the same size as the source image buffer
  55     private Texture dsttexture;
  56     
  57     // size of the source image buffer
  58     volatile private int dstwidth;
  59     volatile private int dstheight;
  60 
  61     // when the content shrinks, we need to clear the target
  62     volatile private boolean clearTarget = false;
  63     
  64     // relative to the [srcx, srcy]
  65     volatile private Rectangle dirtyRect;
  66     volatile private boolean isDirty;
  67     
  68     volatile private Lock paintLock;
  69 
  70     @Override
  71     protected void renderContent(Graphics g) {
  72         paintLock.lock();
  73         try {
  74             if (srcbuffer == null) {
  75                 // the buffer may be initialized with some delay, asynchronously
  76                 return;
  77             }
  78             if ((dsttexture == null) ||
  79                 (dsttexture.getContentWidth() != dstwidth) ||
  80                 (dsttexture.getContentHeight() != dstheight))
  81             {
  82                 ResourceFactory factory = g.getResourceFactory();
  83                 if (!factory.isDeviceReady()) {
  84                     System.err.println("NGExternalNode: graphics device is not ready");
  85                     return;
  86                 }
  87                 if (dsttexture != null) {
  88                     dsttexture.dispose();
  89                 }
  90                 dsttexture =
  91                     factory.createTexture(PixelFormat.INT_ARGB_PRE,
  92                                           Texture.Usage.DYNAMIC,
  93                                           Texture.WrapMode.CLAMP_NOT_NEEDED,
  94                                           dstwidth, dstheight);
  95                 
  96                 if (dsttexture == null) {
  97                     System.err.println("NGExternalNode: failed to create a texture");
  98                     return;
  99                 }
 100             }
 101             if (dirtyRect == null) return;
 102             
 103             dsttexture.update(srcbuffer,
 104                               PixelFormat.INT_ARGB_PRE,
 105                               srcx + dirtyRect.x, srcy + dirtyRect.y, // dst
 106                               srcx + dirtyRect.x, srcy + dirtyRect.y, dirtyRect.width, dirtyRect.height, // src
 107                               linestride * 4,
 108                               false);
 109             
 110             if (clearTarget) {
 111                 clearTarget = false;
 112                 g.clear();
 113             }
 114             g.drawTexture(dsttexture,
 115                           srcx, srcy, srcx + srcwidth, srcy + srcheight, // dst
 116                           srcx, srcy, srcx + srcwidth, srcy + srcheight); // src
 117             
 118             isDirty = false;
 119             
 120         } finally {
 121             paintLock.unlock();
 122         }
 123     }
 124     
 125     @Override
 126     public void setLock(ReentrantLock lock) {
 127         this.paintLock = lock;
 128     }
 129 
 130     @Override
 131     public void setImageBuffer(Buffer buffer, int x, int y, int width, int height, int linestride) {
 132         paintLock.lock();
 133         try {
 134             this.srcbuffer = buffer;
 135             
 136             this.srcx = x;
 137             this.srcy = y;
 138             this.srcwidth = width;
 139             this.srcheight = height;
 140             
 141             this.linestride = linestride;
 142             
 143             this.dstwidth = linestride;
 144             this.dstheight = buffer.capacity() / linestride;
 145             
 146             dirtyRect = new Rectangle(x, y, width, height);
 147             
 148         } finally {
 149             paintLock.unlock();
 150         }
 151     }
 152 
 153     @Override
 154     public void setImageBounds(int x, int y, int width, int height) {
 155         paintLock.lock();
 156         try {
 157             // content shrinked
 158             if (width < srcwidth || height < srcheight) {
 159                 clearTarget = true;
 160             }
 161             this.srcx = x;
 162             this.srcy = y;
 163             this.srcwidth = width;
 164             this.srcheight = height;
 165             
 166             dirtyRect.setBounds(x, y, width, height);
 167             
 168         } finally {
 169             paintLock.unlock();
 170         }
 171     }
 172     
 173     @Override
 174     public void repaintDirtyRegion(int dirtyX, int dirtyY, int dirtyWidth, int dirtyHeight) {
 175         paintLock.lock();
 176         try {
 177             if (isDirty) {
 178                 dirtyRect.add(new Rectangle(dirtyX, dirtyY, dirtyWidth, dirtyHeight));
 179             } else {
 180                 dirtyRect.setBounds(dirtyX, dirtyY, dirtyWidth, dirtyHeight);
 181             }
 182             // System.out.println("NGExternalNode.repaintDirtyRegion: " + dirtyRect);
 183             
 184             isDirty = true;
 185             visualsChanged();
 186 
 187         } finally {
 188             paintLock.unlock();
 189         }
 190     }
 191 
 192     @Override
 193     protected boolean hasOverlappingContents() {  return false; }
 194 }