class ScreenViewLayer extends Layer {

  @Override
  public void paint(@NotNull Graphics2D graphics2D) {
    myScreenView.getSize(myScreenViewSize);
    // Calculate the portion of the screen view that it's visible
    myScreenViewVisibleRect.setBounds(myScreenView.getX(), myScreenView.getY(),
                                      myScreenViewSize.width, myScreenViewSize.height);
    Rectangle2D clipBounds = graphics2D.getClipBounds();
    if (!myScreenViewVisibleRect.intersects(clipBounds)) {
      return;
    }

    // When the screen view visible rect is bigger than the current viewport, we limit the visible rect to the viewport.
    // We will never be painting an image bigger than the viewport.
    if (myScreenViewVisibleRect.width > clipBounds.getWidth() || myScreenViewVisibleRect.height > clipBounds.getHeight()) {
      Rectangle2D.intersect(myScreenViewVisibleRect, clipBounds, myScreenViewVisibleRect);
    }

    // In some cases, we will try to re-use the previous image to paint on top of it, assuming that it still matches the right dimensions.
    // This way we can save the allocation.
    BufferedImage previousVisibleImage = myCachedVisibleImage;
    RenderResult renderResult = myScreenView.getResult();
    boolean drawNewImg = false;
    if (newRenderImageAvailable(renderResult)) {
      setLastRenderResult(renderResult);
      myScreenView.getScene().needsRebuildList();
      drawNewImg = true;
    }

    Graphics2D g = (Graphics2D) graphics2D.create();
    BufferedImage cachedVisibleImage = drawNewImg ? null : previousVisibleImage;
    double currentScale = myScreenView.getScale();
    //noinspection FloatingPointEquality
    if (drawNewImg || currentScale != myLastScale || !myScreenViewVisibleRect.equals(myCachedScreenViewDisplayRect)) {
      if (myLastRenderResult != null && myLastRenderResult.hasImage()) {
        BufferedImage renderedImage = myLastRenderResult.getRenderedImage().getCopy();
        if (renderedImage != null) {
          int resultImageWidth = renderedImage.getWidth();
          int resultImageHeight = renderedImage.getHeight();

          myCachedScreenViewDisplayRect.setBounds(myScreenViewVisibleRect);
          // Obtain the factors to convert from screen view coordinates to our result image coordinates
          double xScaleFactor = (double)resultImageWidth / myScreenViewSize.width;
          double yScaleFactor = (double)resultImageHeight / myScreenViewSize.height;
          cancelHighQualityScaleRequests();
          if (xScaleFactor > 1.2 && yScaleFactor > 1.2) {
            // This means that the result image is bigger than the ScreenView by more than a 20%. For this cases, we need to scale down the
            // result image to make it fit in the ScreenView and we use a higher quality (but slow) process. We will issue a request to obtain
            // the high quality version but paint the low quality version below. Once it's ready, we'll repaint.

            requestHighQualityScaledImage(ScaleContext.create(g));
          }

          cachedVisibleImage = getPreviewImage(g.getDeviceConfiguration(), renderedImage,
                                               myScreenView.getX(), myScreenView.getY(),
                                               myScreenViewVisibleRect, xScaleFactor, yScaleFactor,
                                               previousVisibleImage, myScreenView.hasBorderLayer());
          myCachedVisibleImage = cachedVisibleImage;
          myLastScale = currentScale;
        }
      }
    }

    if (cachedVisibleImage != null) {
      Shape screenShape = myScreenView.getScreenShape();
      if (screenShape != null) {
        g.clip(screenShape);
      }
      UIUtil.drawImage(g, cachedVisibleImage, myScreenViewVisibleRect.x, myScreenViewVisibleRect.y, null);
    }
    g.dispose();
  }

  public  ScreenViewLayer(@NotNull ScreenView screenView);
  @VisibleForTesting  ScreenViewLayer(@NotNull ScreenView screenView, @Nullable ScheduledExecutorService executor);

}