1 /*
   2  * Copyright (c) 2011, 2016, 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.webkit.graphics;
  27 
  28 import java.lang.annotation.Native;
  29 import com.sun.javafx.logging.PlatformLogger;
  30 import com.sun.javafx.logging.PlatformLogger.Level;
  31 import com.sun.webkit.Invoker;
  32 import java.nio.ByteBuffer;
  33 import java.util.HashMap;
  34 import java.util.LinkedList;
  35 import java.util.concurrent.atomic.AtomicInteger;
  36 
  37 public abstract class WCRenderQueue extends Ref {
  38     private final static AtomicInteger idCountObj = new AtomicInteger(0);
  39     private final static PlatformLogger log =
  40             PlatformLogger.getLogger(WCRenderQueue.class.getName());
  41     @Native public final static int MAX_QUEUE_SIZE = 0x80000;
  42 
  43     private final LinkedList<BufferData> buffers = new LinkedList<BufferData>();
  44     private BufferData currentBuffer = new BufferData();
  45     private final WCRectangle clip;
  46     private int size = 0;
  47     private final boolean opaque;
  48 
  49     // Associated graphics context (currently used to draw to a buffered image).
  50     protected final WCGraphicsContext gc;
  51 
  52     protected WCRenderQueue(WCGraphicsContext gc) {
  53         this.clip = null;
  54         this.opaque = false;
  55         this.gc = gc;
  56     }
  57 
  58     protected WCRenderQueue(WCRectangle clip, boolean opaque) {
  59         this.clip = clip;
  60         this.opaque = opaque;
  61         this.gc = null;
  62     }
  63 
  64     public synchronized int getSize() {
  65         return size;
  66     }
  67 
  68     public synchronized void addBuffer(ByteBuffer buffer) {
  69         if (log.isLoggable(Level.FINE) && buffers.isEmpty()) {
  70             log.fine("'{'WCRenderQueue{0}[{1}]",
  71                     new Object[]{hashCode(), idCountObj.incrementAndGet()});
  72         }
  73         currentBuffer.setBuffer(buffer);
  74         buffers.addLast(currentBuffer);
  75         currentBuffer = new BufferData();
  76         size += buffer.capacity();
  77         if (size > MAX_QUEUE_SIZE && gc!=null) {
  78             // It is isolated queue over the canvas image [image-gc!=null].
  79             // We need to flush the changes periodically
  80             // by the same reason as in [WebPage.addLastRQ].
  81             flush();
  82         }
  83     }
  84 
  85     public synchronized boolean isEmpty() {
  86         return buffers.isEmpty();
  87     }
  88 
  89     public synchronized void decode(WCGraphicsContext gc) {
  90         for (BufferData bdata : buffers) {
  91             try {
  92                 GraphicsDecoder.decode(
  93                     WCGraphicsManager.getGraphicsManager(), gc, bdata);
  94             } catch (RuntimeException e) {
  95                 e.printStackTrace(System.err);
  96             }
  97         }
  98         dispose();
  99     }
 100 
 101     public synchronized void decode() {
 102         assert (gc != null);
 103         decode(gc);
 104         gc.flush();
 105     }
 106 
 107     public synchronized void decode(int fontSmoothingType) {
 108         assert (gc != null);
 109         gc.setFontSmoothingType(fontSmoothingType);
 110         decode();
 111     }
 112 
 113     protected abstract void flush();
 114 
 115     private void fwkFlush() {
 116         flush();
 117     }
 118 
 119     private void fwkAddBuffer(ByteBuffer buffer) {
 120         addBuffer(buffer);
 121     }
 122 
 123     public WCRectangle getClip() {
 124         return clip;
 125     }
 126 
 127     public synchronized void dispose() {
 128         int n = buffers.size();
 129         if (n > 0) {
 130             int i = 0;
 131             final Object[] arr = new Object[n];
 132             for (BufferData bdata: buffers) {
 133                 arr[i++] = bdata.getBuffer();
 134             }
 135             buffers.clear();
 136             Invoker.getInvoker().invokeOnEventThread(() -> {
 137                 twkRelease(arr);
 138             });
 139             size = 0;
 140             if (log.isLoggable(Level.FINE)) {
 141                 log.fine("'}'WCRenderQueue{0}[{1}]",
 142                         new Object[]{hashCode(), idCountObj.decrementAndGet()});
 143             }
 144         }
 145     }
 146 
 147     protected abstract void disposeGraphics();
 148 
 149     private void fwkDisposeGraphics() {
 150         disposeGraphics();
 151     }
 152 
 153     private native void twkRelease(Object[] bufs);
 154 
 155     /*is called from native*/
 156     private int refString(String str) {
 157         return currentBuffer.addString(str);
 158     }
 159 
 160     /*is called from native*/
 161     private int refIntArr(int[] arr) {
 162         return currentBuffer.addIntArray(arr);
 163     }
 164 
 165     /*is called from native*/
 166     private int refFloatArr(float[] arr) {
 167         return currentBuffer.addFloatArray(arr);
 168     }
 169 
 170     public boolean isOpaque() {
 171         return opaque;
 172     }
 173 
 174     @Override public synchronized String toString() {
 175         return "WCRenderQueue{"
 176                 + "clip=" + clip + ", "
 177                 + "size=" + size + ", "
 178                 + "opaque=" + opaque
 179                 + "}";
 180     }
 181 }
 182 
 183 final class BufferData {
 184     /* For passing data that does not fit into the queue */
 185     private final AtomicInteger idCount = new AtomicInteger(0);
 186     private final HashMap<Integer,String> strMap =
 187             new HashMap<Integer,String>();
 188     private final HashMap<Integer,int[]> intArrMap =
 189             new HashMap<Integer,int[]>();
 190     private final HashMap<Integer,float[]> floatArrMap =
 191             new HashMap<Integer,float[]>();
 192 
 193     private ByteBuffer buffer;
 194 
 195     private int createID() {
 196         return idCount.incrementAndGet();
 197     }
 198 
 199     int addIntArray(int[] a) {
 200         int id = createID();
 201         intArrMap.put(id, a);
 202         return id;
 203     }
 204 
 205     int[] getIntArray(int id) {
 206         return intArrMap.get(id);
 207     }
 208 
 209     int addFloatArray(float[] a) {
 210         int id = createID();
 211         floatArrMap.put(id, a);
 212         return id;
 213     }
 214 
 215     float[] getFloatArray(int id) {
 216         return floatArrMap.get(id);
 217     }
 218 
 219     int addString(String s) {
 220         int id = createID();
 221         strMap.put(id, s);
 222         return id;
 223     }
 224 
 225     String getString(int id) {
 226         return strMap.get(id);
 227     }
 228 
 229     ByteBuffer getBuffer() {
 230         return buffer;
 231     }
 232 
 233     void setBuffer(ByteBuffer buffer) {
 234         this.buffer = buffer;
 235     }
 236 }