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.prism.sw; 27 28 import com.sun.javafx.geom.Path2D; 29 import com.sun.javafx.geom.Rectangle; 30 import com.sun.javafx.geom.Shape; 31 import com.sun.javafx.geom.transform.BaseTransform; 32 import com.sun.marlin.IntArrayCache; 33 import com.sun.marlin.MarlinAlphaConsumer; 34 import com.sun.marlin.MarlinConst; 35 import com.sun.marlin.MarlinRenderer; 36 import com.sun.marlin.MarlinRenderingEngine; 37 import com.sun.marlin.RendererContext; 38 import com.sun.openpisces.Renderer; 39 import com.sun.pisces.PiscesRenderer; 40 import com.sun.prism.BasicStroke; 41 import com.sun.prism.PixelFormat; 42 import com.sun.prism.ResourceFactory; 43 import com.sun.prism.Texture; 44 import com.sun.prism.impl.PrismSettings; 45 import com.sun.prism.impl.shape.MarlinPrismUtils; 46 import com.sun.prism.impl.shape.MaskData; 47 import com.sun.prism.impl.shape.OpenPiscesPrismUtils; 48 import com.sun.prism.impl.shape.ShapeUtil; 49 50 import java.lang.ref.SoftReference; 51 52 final class SWContext { 53 54 private final ResourceFactory factory; 55 private final ShapeRenderer shapeRenderer; 56 private SoftReference<SWRTTexture> readBackBufferRef; 57 private SoftReference<SWArgbPreTexture> imagePaintTextureRef; 58 59 interface ShapeRenderer { 60 void renderShape(PiscesRenderer pr, Shape shape, BasicStroke stroke, BaseTransform tr, Rectangle clip, boolean antialiasedShape); 61 void dispose(); 62 } 63 64 class NativeShapeRenderer implements ShapeRenderer { 65 private SoftReference<SWMaskTexture> maskTextureRef; 66 67 public void renderShape(PiscesRenderer pr, Shape shape, BasicStroke stroke, BaseTransform tr, Rectangle clip, boolean antialiasedShape) { 68 final MaskData mask = ShapeUtil.rasterizeShape(shape, stroke, clip.toRectBounds(), tr, true, antialiasedShape); 69 final SWMaskTexture tex = this.validateMaskTexture(mask.getWidth(), mask.getHeight()); 70 mask.uploadToTexture(tex, 0, 0, false); 71 pr.fillAlphaMask(tex.getDataNoClone(), mask.getOriginX(), mask.getOriginY(), 72 mask.getWidth(), mask.getHeight(), 0, tex.getPhysicalWidth()); 73 } 74 75 private SWMaskTexture initMaskTexture(int width, int height) { 76 final SWMaskTexture tex = (SWMaskTexture)factory.createMaskTexture(width, height, Texture.WrapMode.CLAMP_NOT_NEEDED); 77 maskTextureRef = new SoftReference<SWMaskTexture>(tex); 78 return tex; 79 } 80 81 private void disposeMaskTexture() { 82 if (maskTextureRef != null){ 83 maskTextureRef.clear(); 84 maskTextureRef = null; 85 } 86 } 87 88 private SWMaskTexture validateMaskTexture(int width, int height) { 89 SWMaskTexture tex; 90 if (maskTextureRef == null) { 91 tex = this.initMaskTexture(width, height); 92 } else { 93 tex = maskTextureRef.get(); 94 if (tex == null || 95 tex.getPhysicalWidth() < width || 96 tex.getPhysicalHeight() < height) 97 { 98 this.disposeMaskTexture(); 99 tex = this.initMaskTexture(width, height); 100 } 101 } 102 return tex; 103 } 104 105 public void dispose() { 106 this.disposeMaskTexture(); 107 } 108 } 109 110 class JavaShapeRenderer implements ShapeRenderer { 111 private final DirectRTPiscesAlphaConsumer alphaConsumer = new DirectRTPiscesAlphaConsumer(); 112 113 public void renderShape(PiscesRenderer pr, Shape shape, BasicStroke stroke, BaseTransform tr, Rectangle clip, boolean antialiasedShape) { 114 if (stroke != null && stroke.getType() != BasicStroke.TYPE_CENTERED) { 115 // RT-27427 116 // TODO: Optimize the combinatorial strokes for simple 117 // shapes and/or teach the rasterizer to be able to 118 // do a "differential fill" between two shapes. 119 // Note that most simple shapes will use a more optimized path 120 // than this method for the INNER/OUTER strokes anyway. 121 shape = stroke.createStrokedShape(shape); 122 stroke = null; 123 } 124 final Renderer r = OpenPiscesPrismUtils.setupRenderer(shape, stroke, tr, clip, antialiasedShape); 125 alphaConsumer.initConsumer(r, pr); 126 r.produceAlphas(alphaConsumer); 127 } 128 129 public void dispose() { } 130 } 131 132 static final class MarlinShapeRenderer implements ShapeRenderer { 133 private final DirectRTMarlinAlphaConsumer alphaConsumer = new DirectRTMarlinAlphaConsumer(); 134 135 @Override 136 public void renderShape(PiscesRenderer pr, Shape shape, BasicStroke stroke, BaseTransform tr, Rectangle clip, boolean antialiasedShape) { 137 if (stroke != null && stroke.getType() != BasicStroke.TYPE_CENTERED) { 138 // RT-27427 139 // TODO: Optimize the combinatorial strokes for simple 140 // shapes and/or teach the rasterizer to be able to 141 // do a "differential fill" between two shapes. 142 // Note that most simple shapes will use a more optimized path 143 // than this method for the INNER/OUTER strokes anyway. 144 shape = stroke.createStrokedShape(shape); 145 stroke = null; 146 } 147 final RendererContext rdrCtx = MarlinRenderingEngine.getRendererContext(); 148 MarlinRenderer renderer = null; 149 try { 150 if (shape instanceof Path2D) { 151 renderer = MarlinPrismUtils.setupRenderer(rdrCtx, (Path2D) shape, stroke, tr, clip, 152 antialiasedShape); 153 } 154 if (renderer == null) { 155 renderer = MarlinPrismUtils.setupRenderer(rdrCtx, shape, stroke, tr, clip, 156 antialiasedShape); 157 } 158 final int outpix_xmin = renderer.getOutpixMinX(); 159 final int outpix_xmax = renderer.getOutpixMaxX(); 160 final int outpix_ymin = renderer.getOutpixMinY(); 161 final int outpix_ymax = renderer.getOutpixMaxY(); 162 final int w = outpix_xmax - outpix_xmin; 163 final int h = outpix_ymax - outpix_ymin; 164 if ((w <= 0) || (h <= 0)) { 165 return; 166 } 167 alphaConsumer.initConsumer(outpix_xmin, outpix_ymin, w, h, pr); 168 renderer.produceAlphas(alphaConsumer); 169 } finally { 170 if (renderer != null) { 171 renderer.dispose(); 172 } 173 // recycle the RendererContext instance 174 MarlinRenderingEngine.returnRendererContext(rdrCtx); 175 } 176 } 177 178 @Override 179 public void dispose() { } 180 181 private static final class DirectRTMarlinAlphaConsumer implements MarlinAlphaConsumer { 182 private byte alpha_map[]; 183 private int x; 184 private int y; 185 private int w; 186 private int h; 187 private int rowNum; 188 189 private PiscesRenderer pr; 190 191 public void initConsumer(int x, int y, int w, int h, PiscesRenderer pr) { 192 this.x = x; 193 this.y = y; 194 this.w = w; 195 this.h = h; 196 rowNum = 0; 197 this.pr = pr; 198 } 199 200 @Override 201 public int getOriginX() { 202 return x; 203 } 204 205 @Override 206 public int getOriginY() { 207 return y; 208 } 209 210 @Override 211 public int getWidth() { 212 return w; 213 } 214 215 @Override 216 public int getHeight() { 217 return h; 218 } 219 220 @Override 221 public void setMaxAlpha(int maxalpha) { 222 if ((alpha_map == null) || (alpha_map.length != maxalpha+1)) { 223 alpha_map = new byte[maxalpha+1]; 224 for (int i = 0; i <= maxalpha; i++) { 225 alpha_map[i] = (byte) ((i*255 + maxalpha/2)/maxalpha); 226 } 227 } 228 } 229 230 @Override 231 public boolean supportBlockFlags() { 232 return false; 233 } 234 235 @Override 236 public void clearAlphas(final int pix_y) { 237 // noop 238 } 239 240 @Override 241 public void setAndClearRelativeAlphas(final int[] alphaDeltas, final int pix_y, 242 final int pix_from, final int pix_to) 243 { 244 // use x instead of pix_from as it cause artefacts: 245 // note: it would be more efficient to skip all those empty pixels [x to pix_from[ 246 // but the native implementation must be fixed too. 247 // pr.emitAndClearAlphaRow(alpha_map, alphaDeltas, pix_y, pix_from, pix_to, rowNum); 248 pr.emitAndClearAlphaRow(alpha_map, alphaDeltas, pix_y, x, pix_to, rowNum); 249 rowNum++; 250 251 // clear properly the end of the alphaDeltas: 252 final int to = pix_to - x; 253 if (to <= w) { 254 alphaDeltas[to] = 0; 255 } else { 256 alphaDeltas[w] = 0; 257 } 258 259 if (MarlinConst.DO_CHECKS) { 260 IntArrayCache.check(alphaDeltas, pix_from - x, to + 1, 0); 261 } 262 } 263 264 @Override 265 public void setAndClearRelativeAlphas(final int[] blkFlags, final int[] alphaDeltas, final int pix_y, 266 final int pix_from, final int pix_to) 267 { 268 throw new UnsupportedOperationException(); 269 } 270 } 271 } 272 273 SWContext(ResourceFactory factory) { 274 this.factory = factory; 275 if (PrismSettings.useMarlinRasterizer) { 276 this.shapeRenderer = new MarlinShapeRenderer(); 277 } else if (PrismSettings.doNativePisces) { 278 this.shapeRenderer = new NativeShapeRenderer(); 279 } else { 280 this.shapeRenderer = new JavaShapeRenderer(); 281 } 282 } 283 284 void renderShape(PiscesRenderer pr, Shape shape, BasicStroke stroke, BaseTransform tr, Rectangle clip, boolean antialiasedShape) { 285 this.shapeRenderer.renderShape(pr, shape, stroke, tr, clip, antialiasedShape); 286 } 287 288 private SWRTTexture initRBBuffer(int width, int height) { 289 final SWRTTexture tex = (SWRTTexture)factory.createRTTexture(width, height, Texture.WrapMode.CLAMP_NOT_NEEDED); 290 readBackBufferRef = new SoftReference<SWRTTexture>(tex); 291 return tex; 292 } 293 294 private void disposeRBBuffer() { 295 if (readBackBufferRef != null) { 296 readBackBufferRef.clear(); 297 readBackBufferRef = null; 298 } 299 } 300 301 SWRTTexture validateRBBuffer(int width, int height) { 302 SWRTTexture tex; 303 if (readBackBufferRef == null) { 304 tex = this.initRBBuffer(width, height); 305 } else { 306 tex = readBackBufferRef.get(); 307 if (tex == null || 308 tex.getPhysicalWidth() < width || 309 tex.getPhysicalHeight() < height) 310 { 311 this.disposeRBBuffer(); 312 tex = this.initRBBuffer(width, height); 313 } 314 tex.setContentWidth(width); 315 tex.setContentHeight(height); 316 } 317 return tex; 318 } 319 320 private SWArgbPreTexture initImagePaintTexture(int width, int height) { 321 final SWArgbPreTexture tex = (SWArgbPreTexture)factory.createTexture(PixelFormat.INT_ARGB_PRE, 322 Texture.Usage.DEFAULT, Texture.WrapMode.REPEAT, width, height); 323 imagePaintTextureRef = new SoftReference<SWArgbPreTexture>(tex); 324 return tex; 325 } 326 327 private void disposeImagePaintTexture() { 328 if (imagePaintTextureRef != null) { 329 imagePaintTextureRef.clear(); 330 imagePaintTextureRef = null; 331 } 332 } 333 334 SWArgbPreTexture validateImagePaintTexture(int width, int height) { 335 SWArgbPreTexture tex; 336 if (imagePaintTextureRef == null) { 337 tex = this.initImagePaintTexture(width, height); 338 } else { 339 tex = imagePaintTextureRef.get(); 340 if (tex == null || 341 tex.getPhysicalWidth() < width || 342 tex.getPhysicalHeight() < height) 343 { 344 this.disposeImagePaintTexture(); 345 tex = this.initImagePaintTexture(width, height); 346 } 347 tex.setContentWidth(width); 348 tex.setContentHeight(height); 349 } 350 return tex; 351 } 352 353 354 void dispose() { 355 this.disposeRBBuffer(); 356 this.disposeImagePaintTexture(); 357 this.shapeRenderer.dispose(); 358 } 359 }