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