예제 #1
0
// Computes the smallest rectangle that contains both aRect1 and aRect2 and
// fills 'this' with the result. Returns FALSE if both aRect1 and aRect2 are
// empty and TRUE otherwise
PRBool nsIntRect::UnionRect(const nsIntRect &aRect1, const nsIntRect &aRect2)
{
  PRBool  result = PR_TRUE;

  // Is aRect1 empty?
  if (aRect1.IsEmpty()) {
    if (aRect2.IsEmpty()) {
      // Both rectangles are empty which is an error
      Empty();
      result = PR_FALSE;
    } else {
      // aRect1 is empty so set the result to aRect2
      *this = aRect2;
    }
  } else if (aRect2.IsEmpty()) {
    // aRect2 is empty so set the result to aRect1
    *this = aRect1;
  } else {
    PRInt32 xmost1 = aRect1.XMost();
    PRInt32 xmost2 = aRect2.XMost();
    PRInt32 ymost1 = aRect1.YMost();
    PRInt32 ymost2 = aRect2.YMost();

    // Compute the origin
    x = PR_MIN(aRect1.x, aRect2.x);
    y = PR_MIN(aRect1.y, aRect2.y);

    // Compute the size
    width = PR_MAX(xmost1, xmost2) - x;
    height = PR_MAX(ymost1, ymost2) - y;
  }

  return result;
}
예제 #2
0
static inline void
TilePixels(uint8_t *aTargetData,
           const uint8_t *aSourceData,
           const nsIntRect &targetRegion,
           const nsIntRect &aTile,
           uint32_t aStride)
{
  if (targetRegion.IsEmpty()) {
    return;
  }

  uint32_t tileRowCopyMemSize = aTile.width * 4;
  uint32_t numTimesToCopyTileRows = targetRegion.width / aTile.width;

  uint8_t *targetFirstRowOffset = aTargetData + 4 * targetRegion.x;
  const uint8_t *tileFirstRowOffset = aSourceData + 4 * aTile.x;

  int32_t tileYOffset = 0;
  for (int32_t targetY = targetRegion.y;
       targetY < targetRegion.YMost();
       ++targetY) {
    uint8_t *targetRowOffset = targetFirstRowOffset + aStride * targetY;
    const uint8_t *tileRowOffset = tileFirstRowOffset +
                                             aStride * (aTile.y + tileYOffset);

    for (uint32_t i = 0; i < numTimesToCopyTileRows; ++i) {
      memcpy(targetRowOffset + i * tileRowCopyMemSize,
             tileRowOffset,
             tileRowCopyMemSize);
    }

    tileYOffset = (tileYOffset + 1) % aTile.height;
  }
}
예제 #3
0
/* static */ void
ProgressTracker::SyncNotifyInternal(ObserverArray& aObservers,
                                    bool aHasImage,
                                    Progress aProgress,
                                    const nsIntRect& aDirtyRect)
{
  MOZ_ASSERT(NS_IsMainThread());

  typedef imgINotificationObserver I;

  if (aProgress & FLAG_SIZE_AVAILABLE) {
    NOTIFY_IMAGE_OBSERVERS(aObservers, Notify(I::SIZE_AVAILABLE));
  }

  if (aProgress & FLAG_ONLOAD_BLOCKED) {
    NOTIFY_IMAGE_OBSERVERS(aObservers, BlockOnload());
  }

  if (aHasImage) {
    // OnFrameUpdate
    // If there's any content in this frame at all (always true for
    // vector images, true for raster images that have decoded at
    // least one frame) then send OnFrameUpdate.
    if (!aDirtyRect.IsEmpty()) {
      NOTIFY_IMAGE_OBSERVERS(aObservers, Notify(I::FRAME_UPDATE, &aDirtyRect));
    }

    if (aProgress & FLAG_FRAME_COMPLETE) {
      NOTIFY_IMAGE_OBSERVERS(aObservers, Notify(I::FRAME_COMPLETE));
    }

    if (aProgress & FLAG_HAS_TRANSPARENCY) {
      NOTIFY_IMAGE_OBSERVERS(aObservers, Notify(I::HAS_TRANSPARENCY));
    }

    if (aProgress & FLAG_IS_ANIMATED) {
      NOTIFY_IMAGE_OBSERVERS(aObservers, Notify(I::IS_ANIMATED));
    }
  }

  // Send UnblockOnload before OnStopDecode and OnStopRequest. This allows
  // observers that can fire events when they receive those notifications to do
  // so then, instead of being forced to wait for UnblockOnload.
  if (aProgress & FLAG_ONLOAD_UNBLOCKED) {
    NOTIFY_IMAGE_OBSERVERS(aObservers, UnblockOnload());
  }

  if (aProgress & FLAG_DECODE_COMPLETE) {
    MOZ_ASSERT(aHasImage, "Stopped decoding without ever having an image?");
    NOTIFY_IMAGE_OBSERVERS(aObservers, Notify(I::DECODE_COMPLETE));
  }

  if (aProgress & FLAG_LOAD_COMPLETE) {
    NOTIFY_IMAGE_OBSERVERS(aObservers,
                           OnLoadComplete(aProgress & FLAG_LAST_PART_COMPLETE));
  }
}
예제 #4
0
/* static */ void
ProgressTracker::SyncNotifyInternal(ProxyArray& aProxies,
                                    bool aHasImage,
                                    Progress aProgress,
                                    const nsIntRect& aDirtyRect)
{
  MOZ_ASSERT(NS_IsMainThread());
  // OnStartRequest
  if (aProgress & FLAG_REQUEST_STARTED)
    NOTIFY_IMAGE_OBSERVERS(aProxies, OnStartRequest());

  // OnStartContainer
  if (aProgress & FLAG_HAS_SIZE)
    NOTIFY_IMAGE_OBSERVERS(aProxies, OnStartContainer());

  // OnStartDecode
  if (aProgress & FLAG_DECODE_STARTED)
    NOTIFY_IMAGE_OBSERVERS(aProxies, OnStartDecode());

  // BlockOnload
  if (aProgress & FLAG_ONLOAD_BLOCKED)
    NOTIFY_IMAGE_OBSERVERS(aProxies, BlockOnload());

  if (aHasImage) {
    // OnFrameUpdate
    // If there's any content in this frame at all (always true for
    // vector images, true for raster images that have decoded at
    // least one frame) then send OnFrameUpdate.
    if (!aDirtyRect.IsEmpty())
      NOTIFY_IMAGE_OBSERVERS(aProxies, OnFrameUpdate(&aDirtyRect));

    if (aProgress & FLAG_FRAME_STOPPED)
      NOTIFY_IMAGE_OBSERVERS(aProxies, OnStopFrame());

    // OnImageIsAnimated
    if (aProgress & FLAG_IS_ANIMATED)
      NOTIFY_IMAGE_OBSERVERS(aProxies, OnImageIsAnimated());
  }

  // Send UnblockOnload before OnStopDecode and OnStopRequest. This allows
  // observers that can fire events when they receive those notifications to do
  // so then, instead of being forced to wait for UnblockOnload.
  if (aProgress & FLAG_ONLOAD_UNBLOCKED) {
    NOTIFY_IMAGE_OBSERVERS(aProxies, UnblockOnload());
  }

  if (aProgress & FLAG_DECODE_STOPPED) {
    MOZ_ASSERT(aHasImage, "Stopped decoding without ever having an image?");
    NOTIFY_IMAGE_OBSERVERS(aProxies, OnStopDecode());
  }

  if (aProgress & FLAG_REQUEST_STOPPED) {
    NOTIFY_IMAGE_OBSERVERS(aProxies,
                           OnStopRequest(aProgress & FLAG_MULTIPART_STOPPED));
  }
}
예제 #5
0
nsRect
nsFilterInstance::FilterSpaceToFrameSpace(const nsIntRect& aRect) const
{
  if (aRect.IsEmpty()) {
    return nsRect();
  }
  gfxRect r(aRect.x, aRect.y, aRect.width, aRect.height);
  r = mFilterSpaceToFrameSpaceInCSSPxTransform.TransformBounds(r);
  return nsLayoutUtils::RoundGfxRectToAppRect(r, mAppUnitsPerCSSPx);
}
예제 #6
0
bool imgFrame::Draw(gfxContext *aContext, GraphicsFilter aFilter,
                    const gfxMatrix &aUserSpaceToImageSpace, const gfxRect& aFill,
                    const nsIntMargin &aPadding, const nsIntRect &aSubimage,
                    uint32_t aImageFlags)
{
  PROFILER_LABEL("image", "imgFrame::Draw");
  NS_ASSERTION(!aFill.IsEmpty(), "zero dest size --- fix caller");
  NS_ASSERTION(!aSubimage.IsEmpty(), "zero source size --- fix caller");
  NS_ASSERTION(!mPalettedImageData, "Directly drawing a paletted image!");

  bool doPadding = aPadding != nsIntMargin(0,0,0,0);
  bool doPartialDecode = !ImageComplete();

  if (mSinglePixel && !doPadding && !doPartialDecode) {
    DoSingleColorFastPath(aContext, mSinglePixelColor, aFill);
    return true;
  }

  gfxMatrix userSpaceToImageSpace = aUserSpaceToImageSpace;
  gfxRect sourceRect = userSpaceToImageSpace.TransformBounds(aFill);
  gfxRect imageRect(0, 0, mSize.width + aPadding.LeftRight(),
                    mSize.height + aPadding.TopBottom());
  gfxRect subimage(aSubimage.x, aSubimage.y, aSubimage.width, aSubimage.height);
  gfxRect fill = aFill;

  NS_ASSERTION(!sourceRect.Intersect(subimage).IsEmpty(),
               "We must be allowed to sample *some* source pixels!");

  nsRefPtr<gfxASurface> surf;
  if (!mSinglePixel) {
    surf = ThebesSurface();
    if (!surf)
      return false;
  }

  bool doTile = !imageRect.Contains(sourceRect) &&
                !(aImageFlags & imgIContainer::FLAG_CLAMP);
  SurfaceWithFormat surfaceResult =
    SurfaceForDrawing(doPadding, doPartialDecode, doTile, aPadding,
                      userSpaceToImageSpace, fill, subimage, sourceRect,
                      imageRect, surf);

  if (surfaceResult.IsValid()) {
    gfxUtils::DrawPixelSnapped(aContext, surfaceResult.mDrawable,
                               userSpaceToImageSpace,
                               subimage, sourceRect, imageRect, fill,
                               surfaceResult.mFormat, aFilter, aImageFlags);
  }
  return true;
}
void
GrallocTextureHostBasic::SetCropRect(nsIntRect aCropRect)
{
  MOZ_ASSERT(aCropRect.TopLeft() == gfx::IntPoint(0, 0));
  MOZ_ASSERT(!aCropRect.IsEmpty());
  MOZ_ASSERT(aCropRect.width <= mSize.width);
  MOZ_ASSERT(aCropRect.height <= mSize.height);

  gfx::IntSize cropSize(aCropRect.width, aCropRect.height);
  if (mCropSize == cropSize) {
    return;
  }

  mCropSize = cropSize;
  ClearTextureSource();
}
static nsIntRect
TransformRect(const nsIntRect& aRect, const gfx3DMatrix& aTransform)
{
    if (aRect.IsEmpty()) {
        return nsIntRect();
    }

    gfxRect rect(aRect.x, aRect.y, aRect.width, aRect.height);
    rect = aTransform.TransformBounds(rect);
    rect.RoundOut();

    nsIntRect intRect;
    if (!gfxUtils::GfxRectToIntRect(rect, &intRect)) {
        return nsIntRect();
    }

    return intRect;
}
예제 #9
0
void imgFrame::Draw(gfxContext *aContext, gfxPattern::GraphicsFilter aFilter,
                    const gfxMatrix &aUserSpaceToImageSpace, const gfxRect& aFill,
                    const nsIntMargin &aPadding, const nsIntRect &aSubimage)
{
  NS_ASSERTION(!aFill.IsEmpty(), "zero dest size --- fix caller");
  NS_ASSERTION(!aSubimage.IsEmpty(), "zero source size --- fix caller");
  NS_ASSERTION(!mPalettedImageData, "Directly drawing a paletted image!");

  bool doPadding = aPadding != nsIntMargin(0,0,0,0);
  bool doPartialDecode = !ImageComplete();

  if (mSinglePixel && !doPadding && !doPartialDecode) {
    DoSingleColorFastPath(aContext, mSinglePixelColor, aFill);
    return;
  }

  gfxMatrix userSpaceToImageSpace = aUserSpaceToImageSpace;
  gfxRect sourceRect = userSpaceToImageSpace.Transform(aFill);
  gfxRect imageRect(0, 0, mSize.width + aPadding.LeftRight(),
                    mSize.height + aPadding.TopBottom());
  gfxRect subimage(aSubimage.x, aSubimage.y, aSubimage.width, aSubimage.height);
  gfxRect fill = aFill;

  NS_ASSERTION(!sourceRect.Intersect(subimage).IsEmpty(),
               "We must be allowed to sample *some* source pixels!");

  bool doTile = !imageRect.Contains(sourceRect);
  SurfaceWithFormat surfaceResult =
    SurfaceForDrawing(doPadding, doPartialDecode, doTile, aPadding,
                      userSpaceToImageSpace, fill, subimage, sourceRect,
                      imageRect);

  if (surfaceResult.IsValid()) {
    gfxUtils::DrawPixelSnapped(aContext, surfaceResult.mDrawable,
                               userSpaceToImageSpace,
                               subimage, sourceRect, imageRect, fill,
                               surfaceResult.mFormat, aFilter);
  }
}
예제 #10
0
void
GLBlitTextureImageHelper::BlitTextureImage(TextureImage *aSrc, const nsIntRect& aSrcRect,
                                           TextureImage *aDst, const nsIntRect& aDstRect)
{
    GLContext *gl = mCompositor->gl();
    NS_ASSERTION(!aSrc->InUpdate(), "Source texture is in update!");
    NS_ASSERTION(!aDst->InUpdate(), "Destination texture is in update!");

    if (!aSrc || !aDst || aSrcRect.IsEmpty() || aDstRect.IsEmpty())
        return;

    int savedFb = 0;
    gl->fGetIntegerv(LOCAL_GL_FRAMEBUFFER_BINDING, &savedFb);

    ScopedGLState scopedScissorTestState(gl, LOCAL_GL_SCISSOR_TEST, false);
    ScopedGLState scopedBlendState(gl, LOCAL_GL_BLEND, false);

    // 2.0 means scale up by two
    float blitScaleX = float(aDstRect.width) / float(aSrcRect.width);
    float blitScaleY = float(aDstRect.height) / float(aSrcRect.height);

    // We start iterating over all destination tiles
    aDst->BeginBigImageIteration();
    do {
        // calculate portion of the tile that is going to be painted to
        nsIntRect dstSubRect;
        nsIntRect dstTextureRect = ThebesIntRect(aDst->GetTileRect());
        dstSubRect.IntersectRect(aDstRect, dstTextureRect);

        // this tile is not part of the destination rectangle aDstRect
        if (dstSubRect.IsEmpty())
            continue;

        // (*) transform the rect of this tile into the rectangle defined by aSrcRect...
        nsIntRect dstInSrcRect(dstSubRect);
        dstInSrcRect.MoveBy(-aDstRect.TopLeft());
        // ...which might be of different size, hence scale accordingly
        dstInSrcRect.ScaleRoundOut(1.0f / blitScaleX, 1.0f / blitScaleY);
        dstInSrcRect.MoveBy(aSrcRect.TopLeft());

        SetBlitFramebufferForDestTexture(aDst->GetTextureID());
        UseBlitProgram();

        aSrc->BeginBigImageIteration();
        // now iterate over all tiles in the source Image...
        do {
            // calculate portion of the source tile that is in the source rect
            nsIntRect srcSubRect;
            nsIntRect srcTextureRect = ThebesIntRect(aSrc->GetTileRect());
            srcSubRect.IntersectRect(aSrcRect, srcTextureRect);

            // this tile is not part of the source rect
            if (srcSubRect.IsEmpty()) {
                continue;
            }
            // calculate intersection of source rect with destination rect
            srcSubRect.IntersectRect(srcSubRect, dstInSrcRect);
            // this tile does not overlap the current destination tile
            if (srcSubRect.IsEmpty()) {
                continue;
            }
            // We now have the intersection of
            //     the current source tile
            // and the desired source rectangle
            // and the destination tile
            // and the desired destination rectange
            // in destination space.
            // We need to transform this back into destination space, inverting the transform from (*)
            nsIntRect srcSubInDstRect(srcSubRect);
            srcSubInDstRect.MoveBy(-aSrcRect.TopLeft());
            srcSubInDstRect.ScaleRoundOut(blitScaleX, blitScaleY);
            srcSubInDstRect.MoveBy(aDstRect.TopLeft());

            // we transform these rectangles to be relative to the current src and dst tiles, respectively
            nsIntSize srcSize = srcTextureRect.Size();
            nsIntSize dstSize = dstTextureRect.Size();
            srcSubRect.MoveBy(-srcTextureRect.x, -srcTextureRect.y);
            srcSubInDstRect.MoveBy(-dstTextureRect.x, -dstTextureRect.y);

            float dx0 = 2.0f * float(srcSubInDstRect.x) / float(dstSize.width) - 1.0f;
            float dy0 = 2.0f * float(srcSubInDstRect.y) / float(dstSize.height) - 1.0f;
            float dx1 = 2.0f * float(srcSubInDstRect.x + srcSubInDstRect.width) / float(dstSize.width) - 1.0f;
            float dy1 = 2.0f * float(srcSubInDstRect.y + srcSubInDstRect.height) / float(dstSize.height) - 1.0f;
            ScopedViewportRect autoViewportRect(gl, 0, 0, dstSize.width, dstSize.height);

            RectTriangles rects;

            nsIntSize realTexSize = srcSize;
            if (!CanUploadNonPowerOfTwo(gl)) {
                realTexSize = nsIntSize(gfx::NextPowerOfTwo(srcSize.width),
                                        gfx::NextPowerOfTwo(srcSize.height));
            }

            if (aSrc->GetWrapMode() == LOCAL_GL_REPEAT) {
                rects.addRect(/* dest rectangle */
                        dx0, dy0, dx1, dy1,
                        /* tex coords */
                        srcSubRect.x / float(realTexSize.width),
                        srcSubRect.y / float(realTexSize.height),
                        srcSubRect.XMost() / float(realTexSize.width),
                        srcSubRect.YMost() / float(realTexSize.height));
            } else {
                DecomposeIntoNoRepeatTriangles(srcSubRect, realTexSize, rects);

                // now put the coords into the d[xy]0 .. d[xy]1 coordinate space
                // from the 0..1 that it comes out of decompose
                InfallibleTArray<RectTriangles::coord>& coords = rects.vertCoords();

                for (unsigned int i = 0; i < coords.Length(); ++i) {
                    coords[i].x = (coords[i].x * (dx1 - dx0)) + dx0;
                    coords[i].y = (coords[i].y * (dy1 - dy0)) + dy0;
                }
            }

            ScopedBindTextureUnit autoTexUnit(gl, LOCAL_GL_TEXTURE0);
            ScopedBindTexture autoTex(gl, aSrc->GetTextureID());
            ScopedVertexAttribPointer autoAttrib0(gl, 0, 2, LOCAL_GL_FLOAT, LOCAL_GL_FALSE, 0, 0, rects.vertCoords().Elements());
            ScopedVertexAttribPointer autoAttrib1(gl, 1, 2, LOCAL_GL_FLOAT, LOCAL_GL_FALSE, 0, 0, rects.texCoords().Elements());

            gl->fDrawArrays(LOCAL_GL_TRIANGLES, 0, rects.elements());

        } while (aSrc->NextTile());
    } while (aDst->NextTile());

    // unbind the previous texture from the framebuffer
    SetBlitFramebufferForDestTexture(0);

    gl->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, savedFb);
}
예제 #11
0
void
gfxImageSurface::MovePixels(const nsIntRect& aSourceRect,
                            const nsIntPoint& aDestTopLeft)
{
    const nsIntRect bounds(0, 0, mSize.width, mSize.height);
    nsIntPoint offset = aDestTopLeft - aSourceRect.TopLeft(); 
    nsIntRect clippedSource = aSourceRect;
    clippedSource.IntersectRect(clippedSource, bounds);
    nsIntRect clippedDest = clippedSource + offset;
    clippedDest.IntersectRect(clippedDest, bounds);
    const nsIntRect dest = clippedDest;
    const nsIntRect source = dest - offset;
    // NB: this relies on IntersectRect() and operator+/- preserving
    // x/y for empty rectangles
    NS_ABORT_IF_FALSE(bounds.Contains(dest) && bounds.Contains(source) &&
                      aSourceRect.Contains(source) &&
                      nsIntRect(aDestTopLeft, aSourceRect.Size()).Contains(dest) &&
                      source.Size() == dest.Size() &&
                      offset == (dest.TopLeft() - source.TopLeft()),
                      "Messed up clipping, crash or corruption will follow");
    if (source.IsEmpty() || source.IsEqualInterior(dest)) {
        return;
    }

    long naturalStride = ComputeStride(mSize, mFormat);
    if (mStride == naturalStride && dest.width == bounds.width) {
        // Fast path: this is a vertical shift of some rows in a
        // "normal" image surface.  We can directly memmove and
        // hopefully stay in SIMD land.
        unsigned char* dst = mData + dest.y * mStride;
        const unsigned char* src = mData + source.y * mStride;
        size_t nBytes = dest.height * mStride;
        memmove(dst, src, nBytes);
        return;
    }

    // Slow(er) path: have to move row-by-row.
    const int32_t bpp = BytePerPixelFromFormat(mFormat);
    const size_t nRowBytes = dest.width * bpp;
    // dstRow points at the first pixel within the current destination
    // row, and similarly for srcRow.  endSrcRow is one row beyond the
    // last row we need to copy.  stride is either +mStride or
    // -mStride, depending on which direction we're copying.
    unsigned char* dstRow;
    unsigned char* srcRow;
    unsigned char* endSrcRow;   // NB: this may point outside the image
    long stride;
    if (dest.y > source.y) {
        // We're copying down from source to dest, so walk backwards
        // starting from the last rows to avoid stomping pixels we
        // need.
        stride = -mStride;
        dstRow = mData + dest.x * bpp + (dest.YMost() - 1) * mStride;
        srcRow = mData + source.x * bpp + (source.YMost() - 1) * mStride;
        endSrcRow = mData + source.x * bpp + (source.y - 1) * mStride;
    } else {
        stride = mStride;
        dstRow = mData + dest.x * bpp + dest.y * mStride;
        srcRow = mData + source.x * bpp + source.y * mStride;
        endSrcRow = mData + source.x * bpp + source.YMost() * mStride;
    }

    for (; srcRow != endSrcRow; dstRow += stride, srcRow += stride) {
        memmove(dstRow, srcRow, nRowBytes);
    }
}
static bool ValidatePictureRect(const mozilla::gfx::IntSize& aSize,
                                const nsIntRect& aPictureRect)
{
  return nsIntRect(0, 0, aSize.width, aSize.height).Contains(aPictureRect) &&
      !aPictureRect.IsEmpty();
}
예제 #13
0
bool imgFrame::Draw(gfxContext *aContext, GraphicsFilter aFilter,
                    const gfxMatrix &aUserSpaceToImageSpace, const gfxRect& aFill,
                    const nsIntMargin &aPadding, const nsIntRect &aSubimage,
                    uint32_t aImageFlags)
{
  PROFILER_LABEL("imgFrame", "Draw",
    js::ProfileEntry::Category::GRAPHICS);

  NS_ASSERTION(!aFill.IsEmpty(), "zero dest size --- fix caller");
  NS_ASSERTION(!aSubimage.IsEmpty(), "zero source size --- fix caller");
  NS_ASSERTION(!mPalettedImageData, "Directly drawing a paletted image!");

  bool doPadding = aPadding != nsIntMargin(0,0,0,0);
  bool doPartialDecode = !ImageComplete();

  if (mSinglePixel && !doPadding && !doPartialDecode) {
    DoSingleColorFastPath(aContext, mSinglePixelColor, aFill);
    return true;
  }

  gfxMatrix userSpaceToImageSpace = aUserSpaceToImageSpace;
  gfxRect sourceRect = userSpaceToImageSpace.TransformBounds(aFill);
  gfxRect imageRect(0, 0, mSize.width + aPadding.LeftRight(),
                    mSize.height + aPadding.TopBottom());
  gfxRect subimage(aSubimage.x, aSubimage.y, aSubimage.width, aSubimage.height);
  gfxRect fill = aFill;

  NS_ASSERTION(!sourceRect.Intersect(subimage).IsEmpty(),
               "We must be allowed to sample *some* source pixels!");

  nsRefPtr<gfxASurface> surf = CachedThebesSurface();
  VolatileBufferPtr<unsigned char> ref(mVBuf);
  if (!mSinglePixel && !surf) {
    if (ref.WasBufferPurged()) {
      return false;
    }

    surf = mDrawSurface;
    if (!surf) {
      long stride = gfxImageSurface::ComputeStride(mSize, mFormat);
      nsRefPtr<gfxImageSurface> imgSurf =
        new gfxImageSurface(ref, mSize, stride, mFormat);
#if defined(XP_MACOSX)
      surf = mDrawSurface = new gfxQuartzImageSurface(imgSurf);
#else
      surf = mDrawSurface = imgSurf;
#endif
    }
    if (!surf || surf->CairoStatus()) {
      mDrawSurface = nullptr;
      return true;
    }
  }

  bool doTile = !imageRect.Contains(sourceRect) &&
                !(aImageFlags & imgIContainer::FLAG_CLAMP);
  SurfaceWithFormat surfaceResult =
    SurfaceForDrawing(doPadding, doPartialDecode, doTile, aPadding,
                      userSpaceToImageSpace, fill, subimage, sourceRect,
                      imageRect, surf);

  if (surfaceResult.IsValid()) {
    gfxUtils::DrawPixelSnapped(aContext, surfaceResult.mDrawable,
                               userSpaceToImageSpace,
                               subimage, sourceRect, imageRect, fill,
                               surfaceResult.mFormat, aFilter, aImageFlags);
  }
  return true;
}