1503 if (rt == null) {
1504 return;
1505 }
1506 Graphics g = rt.createGraphics();
1507 draw(g, x, y, w, h);
1508 int[] pixels = rt.getPixels();
1509 if (pixels != null) {
1510 pImage.setImage(com.sun.prism.Image.fromIntArgbPreData(pixels, w, h));
1511 } else {
1512 IntBuffer ib = IntBuffer.allocate(w * h);
1513 if (rt.readPixels(ib, rt.getContentX(), rt.getContentY(), w, h)) {
1514 pImage.setImage(com.sun.prism.Image.fromIntArgbPreData(ib, w, h));
1515 } else {
1516 pImage.dispose();
1517 pImage = null;
1518 }
1519 }
1520 rt.unlock();
1521 }
1522
1523 private int computeOptimumTileSize(int size, int maxSize) {
1524 return computeOptimumTileSize(size, maxSize, null);
1525 }
1526
1527 private int computeOptimumTileSize(int size, int maxSize, boolean[] isDivExact) {
1528 // This method attempts to find the smallest exact divider for the provided `size`
1529 // while the result of the division is less than `maxSize`.
1530 // It tests all potential dividers from 2 to 6 and returns the result of the division
1531 // if all conditions can be satisfied or, failing that, `maxSize`.
1532 // If non-null, the value for `isDivExact` is set so as to reflect whether or not
1533 // an exact divider could be found.
1534 for (int n = 2; n <= 6; n++) {
1535 int optimumSize = size / n;
1536 if (optimumSize <= maxSize && optimumSize * n == size) {
1537 if (isDivExact != null && isDivExact.length > 0) {
1538 isDivExact[0] = true;
1539 }
1540 return optimumSize;
1541 }
1542 }
1543 if (isDivExact != null && isDivExact.length > 0) {
1544 isDivExact[0]= false;
1545 }
1546 return maxSize;
1547 }
1548
1549 @Override
1550 public void run() {
1551
1552 ResourceFactory rf = GraphicsPipeline.getDefaultResourceFactory();
1553
1554 if (!rf.isDeviceReady()) {
1555 return;
1556 }
1557
1558 int x = params.x;
1559 int y = params.y;
1560 int w = params.width;
1561 int h = params.height;
1562
1563 if (w <= 0 || h <= 0) {
1564 return;
1565 }
1566
1567 boolean errored = false;
1568 // A temp QuantumImage used only as a RTT cache for rendering tiles.
1569 var tileRttCache = new QuantumImage((com.sun.prism.Image) null);
1570 try {
1571 QuantumImage pImage = (params.platformImage instanceof QuantumImage) ?
1572 (QuantumImage) params.platformImage : new QuantumImage((com.sun.prism.Image) null);
1573
1574 int maxTextureSize = rf.getMaximumTextureSize();
1575 if (h > maxTextureSize || w > maxTextureSize) {
1576 // The requested size for the screenshot is too big to fit a single texture,
1577 // so we need to take several snapshot tiles and merge them into pImage
1578 if (pImage.image == null) {
1579 pImage.setImage(com.sun.prism.Image.fromIntArgbPreData(IntBuffer.allocate(w * h), w, h));
1580 }
1581 // Find out if it is possible to divide up the image in tiles of the same size
1582 int tileWidth = computeOptimumTileSize(w, maxTextureSize);
1583 var exactHeightDivFound = new boolean[]{false};
1584 int tileHeight = computeOptimumTileSize(h, maxTextureSize, exactHeightDivFound);
1585 IntBuffer buffer = IntBuffer.allocate(tileWidth * tileHeight);
1586 // In order to minimize the number of time we have to resize the underlying
1587 // surface for capturing a tile, choose a dimension that has an exact divider
1588 // (if any) to be processed in the inner most loop.
1589 // E.g. looping on width then height in the example bellow requires four
1590 // surface resizing, whereas the opposite requires only two:
1591 //
1592 // for (w;;) for (h;;)
1593 // for(h;;) for(w;;)
1594 // ----------------- -----------------
1595 // | | | | | |
1596 // | 1 | 3 | | 1 | 2 |
1597 // h | | | h | | |
1598 // ----------------- -----------------
1599 // | 2 | 4 | | 3 | 4 |
1600 // ----------------- -----------------
1601 // w w
1602
1603
1604
1605 if (exactHeightDivFound[0]) {
1606 for (int xOffset = 0; xOffset < w; xOffset += tileWidth) {
1607 tileWidth = Math.min(tileWidth, w - xOffset);
1608 for (int yOffset = 0; yOffset < h; yOffset += tileHeight) {
1609 tileHeight = Math.min(tileHeight, h - yOffset);
1610 renderTile(x, xOffset, y, yOffset, tileWidth, tileHeight,
1611 buffer, rf, tileRttCache, pImage);
1612 }
1613 }
1614 } else {
1615 for (int yOffset = 0; yOffset < h; yOffset += tileHeight) {
1616 tileHeight = Math.min(tileHeight, h - yOffset);
1617 for (int xOffset = 0; xOffset < w; xOffset += tileWidth) {
1618 tileWidth = Math.min(tileWidth, w - xOffset);
1619 renderTile(x, xOffset, y, yOffset, tileWidth, tileHeight,
1620 buffer, rf, tileRttCache, pImage);
1621 }
1622 }
1623 }
1624 } else {
1625 // The requested size for the screenshot fits max texture size,
1626 // so we can directly render it in the target image.
1627 renderWholeImage(x, y, w, h, rf, pImage);
1628 }
1629 params.platformImage = pImage;
1630 } catch (Throwable t) {
1631 errored = true;
1632 t.printStackTrace(System.err);
1633 } finally {
1634 tileRttCache.dispose();
1635 Disposer.cleanUp();
1636 rf.getTextureResourcePool().freeDisposalRequestedAndCheckResources(errored);
1637 }
1638 }
1639 });
1640
1641 final CountDownLatch latch = new CountDownLatch(1);
1642 re.setCompletionListener(job -> latch.countDown());
1643 addRenderJob(re);
1644
|
1503 if (rt == null) {
1504 return;
1505 }
1506 Graphics g = rt.createGraphics();
1507 draw(g, x, y, w, h);
1508 int[] pixels = rt.getPixels();
1509 if (pixels != null) {
1510 pImage.setImage(com.sun.prism.Image.fromIntArgbPreData(pixels, w, h));
1511 } else {
1512 IntBuffer ib = IntBuffer.allocate(w * h);
1513 if (rt.readPixels(ib, rt.getContentX(), rt.getContentY(), w, h)) {
1514 pImage.setImage(com.sun.prism.Image.fromIntArgbPreData(ib, w, h));
1515 } else {
1516 pImage.dispose();
1517 pImage = null;
1518 }
1519 }
1520 rt.unlock();
1521 }
1522
1523
1524 private int computeTileSize(int size, int maxSize) {
1525 // If 'size' divided by either 2 or 3 produce an exact result
1526 // and is lesser that the specified maxSize, then use this value
1527 // as the tile size, as this makes the tiling process more efficient.
1528 for (int n = 1; n <= 3; n++) {
1529 int optimumSize = size / n;
1530 if (optimumSize <= maxSize && optimumSize * n == size) {
1531 return optimumSize;
1532 }
1533 }
1534 return maxSize;
1535 }
1536
1537 @Override
1538 public void run() {
1539
1540 ResourceFactory rf = GraphicsPipeline.getDefaultResourceFactory();
1541
1542 if (!rf.isDeviceReady()) {
1543 return;
1544 }
1545
1546 int x = params.x;
1547 int y = params.y;
1548 int w = params.width;
1549 int h = params.height;
1550
1551 if (w <= 0 || h <= 0) {
1552 return;
1553 }
1554
1555 boolean errored = false;
1556 // A temp QuantumImage used only as a RTT cache for rendering tiles.
1557 var tileRttCache = new QuantumImage((com.sun.prism.Image) null);
1558 try {
1559 QuantumImage pImage = (params.platformImage instanceof QuantumImage) ?
1560 (QuantumImage) params.platformImage : new QuantumImage((com.sun.prism.Image) null);
1561
1562 int maxTextureSize = rf.getMaximumTextureSize();
1563 if (h > maxTextureSize || w > maxTextureSize) {
1564 // The requested size for the screenshot is too big to fit a single texture,
1565 // so we need to take several snapshot tiles and merge them into pImage
1566 if (pImage.image == null) {
1567 pImage.setImage(com.sun.prism.Image.fromIntArgbPreData(IntBuffer.allocate(w * h), w, h));
1568 }
1569 // Find out if it is possible to divide up the image in tiles of the same size
1570 int tileWidth = computeTileSize(w, maxTextureSize);
1571 int tileHeight = computeTileSize(h, maxTextureSize);
1572 IntBuffer buffer = IntBuffer.allocate(tileWidth * tileHeight);
1573
1574 // M represents the middle set of tiles each with a size of tileW x tileH.
1575 // R is the right hand column of tiles,
1576 // B is the bottom row,
1577 // C is the corner:
1578 // +-----------+-----------+ . +-------+
1579 // | | | . | |
1580 // | M | M | . | R |
1581 // | | | . | |
1582 // +-----------+-----------+ . +-------+
1583 // | | | . | |
1584 // | M | M | . | R |
1585 // | | | . | |
1586 // +-----------+-----------+ . +-------+
1587 // . . . .
1588 // +-----------+-----------+ . +-------+
1589 // | B | B | . | C |
1590 // +-----------+-----------+ . +-------+
1591
1592 // Walk through all same-size "M" tiles
1593 int xOffset = 0;
1594 int yOffset = 0;
1595 var mTileWidth = tileWidth;
1596 var mTileHeight = tileHeight;
1597 while (mTileWidth == tileWidth && xOffset < w) {
1598 yOffset = 0;
1599 mTileHeight = tileHeight;
1600 while (mTileHeight == tileHeight && yOffset < h) {
1601 renderTile(x, xOffset, y, yOffset, mTileWidth, mTileHeight,
1602 buffer, rf, tileRttCache, pImage);
1603 yOffset += tileHeight;
1604 mTileHeight = Math.min(tileHeight, h - yOffset);
1605 }
1606 xOffset += tileWidth;
1607 mTileWidth = Math.min(tileWidth, w - xOffset);
1608 }
1609 // Walk through remaining same-width "B" tiles, if any
1610 int bOffset = 0;
1611 int bTileHeight = tileHeight;
1612 while (bTileHeight == tileHeight && bOffset < h) {
1613 renderTile(x, xOffset, y, bOffset, mTileWidth, bTileHeight, buffer, rf, tileRttCache, pImage);
1614 bOffset += tileHeight;
1615 bTileHeight = Math.min(tileHeight, h - bOffset);
1616 }
1617 // Walk through remaining same-height "R" tiles, if any
1618 int rOffset = 0;
1619 int rTileWidth = tileWidth;
1620 while (rTileWidth == tileWidth && rOffset < w) {
1621 renderTile(x, rOffset, y, yOffset, rTileWidth, mTileHeight, buffer, rf, tileRttCache, pImage);
1622 rOffset += tileWidth;
1623 rTileWidth = Math.min(tileWidth, w - rOffset);
1624 }
1625 // Render corner "C" tile if needed
1626 if (bOffset > 0 && rOffset > 0) {
1627 renderTile(x, rOffset, y, bOffset, rTileWidth, bTileHeight, buffer, rf, tileRttCache, pImage);
1628 }
1629 }
1630 else {
1631 // The requested size for the screenshot fits max texture size,
1632 // so we can directly render it in the target image.
1633 renderWholeImage(x, y, w, h, rf, pImage);
1634 }
1635 params.platformImage = pImage;
1636 } catch (Throwable t) {
1637 errored = true;
1638 t.printStackTrace(System.err);
1639 } finally {
1640 tileRttCache.dispose();
1641 Disposer.cleanUp();
1642 rf.getTextureResourcePool().freeDisposalRequestedAndCheckResources(errored);
1643 }
1644 }
1645 });
1646
1647 final CountDownLatch latch = new CountDownLatch(1);
1648 re.setCompletionListener(job -> latch.countDown());
1649 addRenderJob(re);
1650
|