示例#1
0
RotatedContentBuffer::PaintState
ContentClientIncremental::BeginPaintBuffer(ThebesLayer* aLayer,
        uint32_t aFlags)
{
    mTextureInfo.mDeprecatedTextureHostFlags = 0;
    PaintState result;
    // We need to disable rotation if we're going to be resampled when
    // drawing, because we might sample across the rotation boundary.
    bool canHaveRotation =  !(aFlags & RotatedContentBuffer::PAINT_WILL_RESAMPLE);

    nsIntRegion validRegion = aLayer->GetValidRegion();

    bool canUseOpaqueSurface = aLayer->CanUseOpaqueSurface();
    ContentType contentType =
        canUseOpaqueSurface ? gfxContentType::COLOR :
        gfxContentType::COLOR_ALPHA;

    SurfaceMode mode;
    nsIntRegion neededRegion;
    bool canReuseBuffer;
    nsIntRect destBufferRect;

    while (true) {
        mode = aLayer->GetSurfaceMode();
        neededRegion = aLayer->GetVisibleRegion();
        // If we're going to resample, we need a buffer that's in clamp mode.
        canReuseBuffer = neededRegion.GetBounds().Size() <= mBufferRect.Size() &&
                         mHasBuffer &&
                         (!(aFlags & RotatedContentBuffer::PAINT_WILL_RESAMPLE) ||
                          !(mTextureInfo.mTextureFlags & TEXTURE_ALLOW_REPEAT));

        if (canReuseBuffer) {
            if (mBufferRect.Contains(neededRegion.GetBounds())) {
                // We don't need to adjust mBufferRect.
                destBufferRect = mBufferRect;
            } else {
                // The buffer's big enough but doesn't contain everything that's
                // going to be visible. We'll move it.
                destBufferRect = nsIntRect(neededRegion.GetBounds().TopLeft(), mBufferRect.Size());
            }
        } else {
            destBufferRect = neededRegion.GetBounds();
        }

        if (mode == SurfaceMode::SURFACE_COMPONENT_ALPHA) {
            if (!gfxPrefs::ComponentAlphaEnabled() ||
                    !aLayer->GetParent() ||
                    !aLayer->GetParent()->SupportsComponentAlphaChildren()) {
                mode = SurfaceMode::SURFACE_SINGLE_CHANNEL_ALPHA;
            } else {
                contentType = gfxContentType::COLOR;
            }
        }

        if ((aFlags & RotatedContentBuffer::PAINT_WILL_RESAMPLE) &&
                (!neededRegion.GetBounds().IsEqualInterior(destBufferRect) ||
                 neededRegion.GetNumRects() > 1)) {
            // The area we add to neededRegion might not be painted opaquely
            if (mode == SurfaceMode::SURFACE_OPAQUE) {
                contentType = gfxContentType::COLOR_ALPHA;
                mode = SurfaceMode::SURFACE_SINGLE_CHANNEL_ALPHA;
            }
            // For component alpha layers, we leave contentType as gfxContentType::COLOR.

            // We need to validate the entire buffer, to make sure that only valid
            // pixels are sampled
            neededRegion = destBufferRect;
        }

        if (mHasBuffer &&
                (mContentType != contentType ||
                 (mode == SurfaceMode::SURFACE_COMPONENT_ALPHA) != mHasBufferOnWhite)) {
            // We're effectively clearing the valid region, so we need to draw
            // the entire needed region now.
            result.mRegionToInvalidate = aLayer->GetValidRegion();
            validRegion.SetEmpty();
            mHasBuffer = false;
            mHasBufferOnWhite = false;
            mBufferRect.SetRect(0, 0, 0, 0);
            mBufferRotation.MoveTo(0, 0);
            // Restart decision process with the cleared buffer. We can only go
            // around the loop one more iteration, since mTexImage is null now.
            continue;
        }

        break;
    }

    result.mRegionToDraw.Sub(neededRegion, validRegion);
    if (result.mRegionToDraw.IsEmpty())
        return result;

    if (destBufferRect.width > mForwarder->GetMaxTextureSize() ||
            destBufferRect.height > mForwarder->GetMaxTextureSize()) {
        return result;
    }

    // BlitTextureImage depends on the FBO texture target being
    // TEXTURE_2D.  This isn't the case on some older X1600-era Radeons.
    if (!mForwarder->SupportsTextureBlitting() ||
            !mForwarder->SupportsPartialUploads()) {
        result.mRegionToDraw = neededRegion;
        validRegion.SetEmpty();
        mHasBuffer = false;
        mHasBufferOnWhite = false;
        mBufferRect.SetRect(0, 0, 0, 0);
        mBufferRotation.MoveTo(0, 0);
        canReuseBuffer = false;
    }

    nsIntRect drawBounds = result.mRegionToDraw.GetBounds();
    bool createdBuffer = false;

    uint32_t bufferFlags = canHaveRotation ? TEXTURE_ALLOW_REPEAT : 0;
    if (mode == SurfaceMode::SURFACE_COMPONENT_ALPHA) {
        bufferFlags |= TEXTURE_COMPONENT_ALPHA;
    }
    if (canReuseBuffer) {
        nsIntRect keepArea;
        if (keepArea.IntersectRect(destBufferRect, mBufferRect)) {
            // Set mBufferRotation so that the pixels currently in mBuffer
            // will still be rendered in the right place when mBufferRect
            // changes to destBufferRect.
            nsIntPoint newRotation = mBufferRotation +
                                     (destBufferRect.TopLeft() - mBufferRect.TopLeft());
            WrapRotationAxis(&newRotation.x, mBufferRect.width);
            WrapRotationAxis(&newRotation.y, mBufferRect.height);
            NS_ASSERTION(nsIntRect(nsIntPoint(0,0), mBufferRect.Size()).Contains(newRotation),
                         "newRotation out of bounds");
            int32_t xBoundary = destBufferRect.XMost() - newRotation.x;
            int32_t yBoundary = destBufferRect.YMost() - newRotation.y;
            if ((drawBounds.x < xBoundary && xBoundary < drawBounds.XMost()) ||
                    (drawBounds.y < yBoundary && yBoundary < drawBounds.YMost()) ||
                    (newRotation != nsIntPoint(0,0) && !canHaveRotation)) {
                // The stuff we need to redraw will wrap around an edge of the
                // buffer, so we will need to do a self-copy
                // If mBufferRotation == nsIntPoint(0,0) we could do a real
                // self-copy but we're not going to do that in GL yet.
                // We can't do a real self-copy because the buffer is rotated.
                // So allocate a new buffer for the destination.
                destBufferRect = neededRegion.GetBounds();
                createdBuffer = true;
            } else {
                mBufferRect = destBufferRect;
                mBufferRotation = newRotation;
            }
        } else {
            // No pixels are going to be kept. The whole visible region
            // will be redrawn, so we don't need to copy anything, so we don't
            // set destBuffer.
            mBufferRect = destBufferRect;
            mBufferRotation = nsIntPoint(0,0);
        }
    } else {
        // The buffer's not big enough, so allocate a new one
        createdBuffer = true;
    }
    NS_ASSERTION(!(aFlags & RotatedContentBuffer::PAINT_WILL_RESAMPLE) ||
                 destBufferRect == neededRegion.GetBounds(),
                 "If we're resampling, we need to validate the entire buffer");

    if (!createdBuffer && !mHasBuffer) {
        return result;
    }

    if (createdBuffer) {
        if (mHasBuffer &&
                (mode != SurfaceMode::SURFACE_COMPONENT_ALPHA || mHasBufferOnWhite)) {
            mTextureInfo.mDeprecatedTextureHostFlags = TEXTURE_HOST_COPY_PREVIOUS;
        }

        mHasBuffer = true;
        if (mode == SurfaceMode::SURFACE_COMPONENT_ALPHA) {
            mHasBufferOnWhite = true;
        }
        mBufferRect = destBufferRect;
        mBufferRotation = nsIntPoint(0,0);
        NotifyBufferCreated(contentType, bufferFlags);
    }

    NS_ASSERTION(canHaveRotation || mBufferRotation == nsIntPoint(0,0),
                 "Rotation disabled, but we have nonzero rotation?");

    nsIntRegion invalidate;
    invalidate.Sub(aLayer->GetValidRegion(), destBufferRect);
    result.mRegionToInvalidate.Or(result.mRegionToInvalidate, invalidate);

    // If we do partial updates, we have to clip drawing to the regionToDraw.
    // If we don't clip, background images will be fillrect'd to the region correctly,
    // while text or lines will paint outside of the regionToDraw. This becomes apparent
    // with concave regions. Right now the scrollbars invalidate a narrow strip of the bar
    // although they never cover it. This leads to two draw rects, the narow strip and the actually
    // newly exposed area. It would be wise to fix this glitch in any way to have simpler
    // clip and draw regions.
    result.mClip = DrawRegionClip::DRAW;
    result.mMode = mode;

    return result;
}
ThebesLayerBuffer::PaintState
ContentClientIncremental::BeginPaintBuffer(ThebesLayer* aLayer,
                                           ThebesLayerBuffer::ContentType aContentType,
                                           uint32_t aFlags)
{
  mTextureInfo.mDeprecatedTextureHostFlags = 0;
  PaintState result;
  // We need to disable rotation if we're going to be resampled when
  // drawing, because we might sample across the rotation boundary.
  bool canHaveRotation =  !(aFlags & ThebesLayerBuffer::PAINT_WILL_RESAMPLE);

  nsIntRegion validRegion = aLayer->GetValidRegion();

  Layer::SurfaceMode mode;
  ContentType contentType;
  nsIntRegion neededRegion;
  bool canReuseBuffer;
  nsIntRect destBufferRect;

  while (true) {
    mode = aLayer->GetSurfaceMode();
    contentType = aContentType;
    neededRegion = aLayer->GetVisibleRegion();
    // If we're going to resample, we need a buffer that's in clamp mode.
    canReuseBuffer = neededRegion.GetBounds().Size() <= mBufferRect.Size() &&
      mHasBuffer &&
      (!(aFlags & ThebesLayerBuffer::PAINT_WILL_RESAMPLE) ||
       !(mTextureInfo.mTextureFlags & TEXTURE_ALLOW_REPEAT));

    if (canReuseBuffer) {
      if (mBufferRect.Contains(neededRegion.GetBounds())) {
        // We don't need to adjust mBufferRect.
        destBufferRect = mBufferRect;
      } else {
        // The buffer's big enough but doesn't contain everything that's
        // going to be visible. We'll move it.
        destBufferRect = nsIntRect(neededRegion.GetBounds().TopLeft(), mBufferRect.Size());
      }
    } else {
      destBufferRect = neededRegion.GetBounds();
    }

    if (mode == Layer::SURFACE_COMPONENT_ALPHA) {
      if (!gfxPlatform::ComponentAlphaEnabled() ||
          !aLayer->GetParent() ||
          !aLayer->GetParent()->SupportsComponentAlphaChildren()) {
        mode = Layer::SURFACE_SINGLE_CHANNEL_ALPHA;
      } else {
        contentType = gfxASurface::CONTENT_COLOR;
      }
    }

    if ((aFlags & ThebesLayerBuffer::PAINT_WILL_RESAMPLE) &&
        (!neededRegion.GetBounds().IsEqualInterior(destBufferRect) ||
         neededRegion.GetNumRects() > 1)) {
      // The area we add to neededRegion might not be painted opaquely
      if (mode == Layer::SURFACE_OPAQUE) {
        contentType = gfxASurface::CONTENT_COLOR_ALPHA;
        mode = Layer::SURFACE_SINGLE_CHANNEL_ALPHA;
      }
      // For component alpha layers, we leave contentType as CONTENT_COLOR.

      // We need to validate the entire buffer, to make sure that only valid
      // pixels are sampled
      neededRegion = destBufferRect;
    }

    if (mHasBuffer &&
        (mContentType != contentType ||
         (mode == Layer::SURFACE_COMPONENT_ALPHA) != mHasBufferOnWhite)) {
      // We're effectively clearing the valid region, so we need to draw
      // the entire needed region now.
      result.mRegionToInvalidate = aLayer->GetValidRegion();
      validRegion.SetEmpty();
      mHasBuffer = false;
      mHasBufferOnWhite = false;
      mBufferRect.SetRect(0, 0, 0, 0);
      mBufferRotation.MoveTo(0, 0);
      // Restart decision process with the cleared buffer. We can only go
      // around the loop one more iteration, since mTexImage is null now.
      continue;
    }

    break;
  }

  result.mRegionToDraw.Sub(neededRegion, validRegion);
  if (result.mRegionToDraw.IsEmpty())
    return result;

  if (destBufferRect.width > mForwarder->GetMaxTextureSize() ||
      destBufferRect.height > mForwarder->GetMaxTextureSize()) {
    return result;
  }

  // BlitTextureImage depends on the FBO texture target being
  // TEXTURE_2D.  This isn't the case on some older X1600-era Radeons.
  if (!mForwarder->SupportsTextureBlitting() ||
      !mForwarder->SupportsPartialUploads()) {
    result.mRegionToDraw = neededRegion;
    validRegion.SetEmpty();
    mHasBuffer = false;
    mHasBufferOnWhite = false;
    mBufferRect.SetRect(0, 0, 0, 0);
    mBufferRotation.MoveTo(0, 0);
    canReuseBuffer = false;
  }

  nsIntRect drawBounds = result.mRegionToDraw.GetBounds();
  bool createdBuffer = false;

  uint32_t bufferFlags = canHaveRotation ? TEXTURE_ALLOW_REPEAT : 0;
  if (mode == Layer::SURFACE_COMPONENT_ALPHA) {
    bufferFlags |= TEXTURE_COMPONENT_ALPHA;
  }
  if (canReuseBuffer) {
    nsIntRect keepArea;
    if (keepArea.IntersectRect(destBufferRect, mBufferRect)) {
      // Set mBufferRotation so that the pixels currently in mBuffer
      // will still be rendered in the right place when mBufferRect
      // changes to destBufferRect.
      nsIntPoint newRotation = mBufferRotation +
        (destBufferRect.TopLeft() - mBufferRect.TopLeft());
      WrapRotationAxis(&newRotation.x, mBufferRect.width);
      WrapRotationAxis(&newRotation.y, mBufferRect.height);
      NS_ASSERTION(nsIntRect(nsIntPoint(0,0), mBufferRect.Size()).Contains(newRotation),
                   "newRotation out of bounds");
      int32_t xBoundary = destBufferRect.XMost() - newRotation.x;
      int32_t yBoundary = destBufferRect.YMost() - newRotation.y;
      if ((drawBounds.x < xBoundary && xBoundary < drawBounds.XMost()) ||
          (drawBounds.y < yBoundary && yBoundary < drawBounds.YMost()) ||
          (newRotation != nsIntPoint(0,0) && !canHaveRotation)) {
        // The stuff we need to redraw will wrap around an edge of the
        // buffer, so we will need to do a self-copy
        // If mBufferRotation == nsIntPoint(0,0) we could do a real
        // self-copy but we're not going to do that in GL yet.
        // We can't do a real self-copy because the buffer is rotated.
        // So allocate a new buffer for the destination.
        destBufferRect = neededRegion.GetBounds();
        createdBuffer = true;
      } else {
        mBufferRect = destBufferRect;
        mBufferRotation = newRotation;
      }
    } else {
      // No pixels are going to be kept. The whole visible region
      // will be redrawn, so we don't need to copy anything, so we don't
      // set destBuffer.
      mBufferRect = destBufferRect;
      mBufferRotation = nsIntPoint(0,0);
    }
  } else {
    // The buffer's not big enough, so allocate a new one
    createdBuffer = true;
  }
  NS_ASSERTION(!(aFlags & ThebesLayerBuffer::PAINT_WILL_RESAMPLE) ||
               destBufferRect == neededRegion.GetBounds(),
               "If we're resampling, we need to validate the entire buffer");

  if (!createdBuffer && !mHasBuffer) {
    return result;
  }

  if (createdBuffer) {
    if (mHasBuffer &&
        (mode != Layer::SURFACE_COMPONENT_ALPHA || mHasBufferOnWhite)) {
      mTextureInfo.mDeprecatedTextureHostFlags = TEXTURE_HOST_COPY_PREVIOUS;
    }

    mHasBuffer = true;
    if (mode == Layer::SURFACE_COMPONENT_ALPHA) {
      mHasBufferOnWhite = true;
    }
    mBufferRect = destBufferRect;
    mBufferRotation = nsIntPoint(0,0);
    NotifyBufferCreated(contentType, bufferFlags);
  }

  NS_ASSERTION(canHaveRotation || mBufferRotation == nsIntPoint(0,0),
               "Rotation disabled, but we have nonzero rotation?");

  nsIntRegion invalidate;
  invalidate.Sub(aLayer->GetValidRegion(), destBufferRect);
  result.mRegionToInvalidate.Or(result.mRegionToInvalidate, invalidate);

  // BeginUpdate is allowed to modify the given region,
  // if it wants more to be repainted than we request.
  if (mode == Layer::SURFACE_COMPONENT_ALPHA) {
    nsIntRegion drawRegionCopy = result.mRegionToDraw;
    nsRefPtr<gfxASurface> onBlack = GetUpdateSurface(BUFFER_BLACK, drawRegionCopy);
    nsRefPtr<gfxASurface> onWhite = GetUpdateSurface(BUFFER_WHITE, result.mRegionToDraw);
    if (onBlack && onWhite) {
      NS_ASSERTION(result.mRegionToDraw == drawRegionCopy,
                   "BeginUpdate should always modify the draw region in the same way!");
      FillSurface(onBlack, result.mRegionToDraw, nsIntPoint(drawBounds.x, drawBounds.y), gfxRGBA(0.0, 0.0, 0.0, 1.0));
      FillSurface(onWhite, result.mRegionToDraw, nsIntPoint(drawBounds.x, drawBounds.y), gfxRGBA(1.0, 1.0, 1.0, 1.0));
      if (RefPtr<DrawTarget> onBlackDT = gfxPlatform::GetPlatform()->CreateDrawTargetForUpdateSurface(onBlack, onBlack->GetSize())) {
        RefPtr<DrawTarget> onWhiteDT = gfxPlatform::GetPlatform()->CreateDrawTargetForUpdateSurface(onWhite, onWhite->GetSize());
        RefPtr<DrawTarget> dt = Factory::CreateDualDrawTarget(onBlackDT, onWhiteDT);
        result.mContext = new gfxContext(dt);
      } else {
        gfxASurface* surfaces[2] = { onBlack.get(), onWhite.get() };
        nsRefPtr<gfxTeeSurface> surf = new gfxTeeSurface(surfaces, ArrayLength(surfaces));

        // XXX If the device offset is set on the individual surfaces instead of on
        // the tee surface, we render in the wrong place. Why?
        gfxPoint deviceOffset = onBlack->GetDeviceOffset();
        onBlack->SetDeviceOffset(gfxPoint(0, 0));
        onWhite->SetDeviceOffset(gfxPoint(0, 0));
        surf->SetDeviceOffset(deviceOffset);

        // Using this surface as a source will likely go horribly wrong, since
        // only the onBlack surface will really be used, so alpha information will
        // be incorrect.
        surf->SetAllowUseAsSource(false);
        result.mContext = new gfxContext(surf);
      }
    } else {
      result.mContext = nullptr;
    }
  } else {
    nsRefPtr<gfxASurface> surf = GetUpdateSurface(BUFFER_BLACK, result.mRegionToDraw);
    if (RefPtr<DrawTarget> dt = gfxPlatform::GetPlatform()->CreateDrawTargetForUpdateSurface(surf, surf->GetSize())) {
      result.mContext = new gfxContext(dt);
    } else {
      result.mContext = new gfxContext(surf);
    }
  }
  if (!result.mContext) {
    NS_WARNING("unable to get context for update");
    return result;
  }
  result.mContext->Translate(-gfxPoint(drawBounds.x, drawBounds.y));

  // If we do partial updates, we have to clip drawing to the regionToDraw.
  // If we don't clip, background images will be fillrect'd to the region correctly,
  // while text or lines will paint outside of the regionToDraw. This becomes apparent
  // with concave regions. Right now the scrollbars invalidate a narrow strip of the bar
  // although they never cover it. This leads to two draw rects, the narow strip and the actually
  // newly exposed area. It would be wise to fix this glitch in any way to have simpler
  // clip and draw regions.
  gfxUtils::ClipToRegion(result.mContext, result.mRegionToDraw);

  if (mContentType == gfxASurface::CONTENT_COLOR_ALPHA) {
    result.mContext->SetOperator(gfxContext::OPERATOR_CLEAR);
    result.mContext->Paint();
    result.mContext->SetOperator(gfxContext::OPERATOR_OVER);
  }

  return result;
}