Commit 6ca0edc2 authored by Stephen Pohl's avatar Stephen Pohl
Browse files

Bug 817700 - Make <canvas>.toBlob run asynchronously - canvas changes. r=roc,bz

parent 02e4a0f7
Loading
Loading
Loading
Loading
+5 −2
Original line number Diff line number Diff line
@@ -14,8 +14,8 @@
#include "mozilla/RefPtr.h"

#define NS_ICANVASRENDERINGCONTEXTINTERNAL_IID \
{ 0x8b8da863, 0xd151, 0x4014, \
  { 0x8b, 0xdc, 0x62, 0xb5, 0x0d, 0xc0, 0x2b, 0x62 } }
{ 0x9a6a5bdf, 0x1261, 0x4057, \
  { 0x85, 0xcc, 0xaf, 0x97, 0x6c, 0x36, 0x99, 0xa9 } }

class gfxContext;
class gfxASurface;
@@ -71,6 +71,9 @@ public:
                    GraphicsFilter aFilter,
                    uint32_t aFlags = RenderFlagPremultAlpha) = 0;

  // Creates an image buffer. Returns null on failure.
  virtual void GetImageBuffer(uint8_t** aImageBuffer, int32_t* aFormat) = 0;

  // Gives you a stream containing the image represented by this context.
  // The format is given in aMimeTime, for example "image/png".
  //
+43 −40
Original line number Diff line number Diff line
@@ -41,7 +41,7 @@

#include "nsTArray.h"

#include "imgIEncoder.h"
#include "ImageEncoder.h"

#include "gfxContext.h"
#include "gfxASurface.h"
@@ -1052,71 +1052,71 @@ CanvasRenderingContext2D::Render(gfxContext *ctx, GraphicsFilter aFilter, uint32
  return rv;
}

NS_IMETHODIMP
CanvasRenderingContext2D::GetInputStream(const char *aMimeType,
                                         const PRUnichar *aEncoderOptions,
                                         nsIInputStream **aStream)
void
CanvasRenderingContext2D::GetImageBuffer(uint8_t** aImageBuffer,
                                         int32_t* aFormat)
{
  EnsureTarget();
  if (!IsTargetValid()) {
    return NS_ERROR_FAILURE;
  }
  *aImageBuffer = nullptr;
  *aFormat = 0;

  nsRefPtr<gfxASurface> surface;

  if (NS_FAILED(GetThebesSurface(getter_AddRefs(surface)))) {
    return NS_ERROR_FAILURE;
  nsresult rv = GetThebesSurface(getter_AddRefs(surface));
  if (NS_FAILED(rv)) {
    return;
  }

  nsresult rv;
  const char encoderPrefix[] = "@mozilla.org/image/encoder;2?type=";
  static const fallible_t fallible = fallible_t();
  nsAutoArrayPtr<char> conid(new (fallible) char[strlen(encoderPrefix) + strlen(aMimeType) + 1]);

  if (!conid) {
    return NS_ERROR_OUT_OF_MEMORY;
  }

  strcpy(conid, encoderPrefix);
  strcat(conid, aMimeType);

  nsCOMPtr<imgIEncoder> encoder = do_CreateInstance(conid);
  if (!encoder) {
    return NS_ERROR_FAILURE;
  }

  nsAutoArrayPtr<uint8_t> imageBuffer(new (fallible) uint8_t[mWidth * mHeight * 4]);
  uint8_t* imageBuffer = new (fallible) uint8_t[mWidth * mHeight * 4];
  if (!imageBuffer) {
    return NS_ERROR_OUT_OF_MEMORY;
    return;
  }

  nsRefPtr<gfxImageSurface> imgsurf =
    new gfxImageSurface(imageBuffer.get(),
    new gfxImageSurface(imageBuffer,
                        gfxIntSize(mWidth, mHeight),
                        mWidth * 4,
                        gfxImageFormatARGB32);

  if (!imgsurf || imgsurf->CairoStatus()) {
    return NS_ERROR_FAILURE;
    delete[] imageBuffer;
    return;
  }

  nsRefPtr<gfxContext> ctx = new gfxContext(imgsurf);

  if (!ctx || ctx->HasError()) {
    return NS_ERROR_FAILURE;
    delete[] imageBuffer;
    return;
  }

  ctx->SetOperator(gfxContext::OPERATOR_SOURCE);
  ctx->SetSource(surface, gfxPoint(0, 0));
  ctx->Paint();

  rv = encoder->InitFromData(imageBuffer.get(),
                              mWidth * mHeight * 4, mWidth, mHeight, mWidth * 4,
                              imgIEncoder::INPUT_FORMAT_HOSTARGB,
                              nsDependentString(aEncoderOptions));
  NS_ENSURE_SUCCESS(rv, rv);
  *aImageBuffer = imageBuffer;
  *aFormat = imgIEncoder::INPUT_FORMAT_HOSTARGB;
}

NS_IMETHODIMP
CanvasRenderingContext2D::GetInputStream(const char *aMimeType,
                                         const PRUnichar *aEncoderOptions,
                                         nsIInputStream **aStream)
{
  uint8_t* imageBuffer = nullptr;
  int32_t format = 0;
  GetImageBuffer(&imageBuffer, &format);
  if (!imageBuffer) {
    return NS_ERROR_FAILURE;
  }

  nsCString enccid("@mozilla.org/image/encoder;2?type=");
  enccid += aMimeType;
  nsCOMPtr<imgIEncoder> encoder = do_CreateInstance(enccid.get());
  if (!encoder) {
    return NS_ERROR_FAILURE;
  }

  return CallQueryInterface(encoder, aStream);
  return ImageEncoder::GetInputStream(mWidth, mHeight, imageBuffer, format,
                                      encoder, aEncoderOptions, aStream);
}

SurfaceFormat
@@ -3817,6 +3817,9 @@ NS_IMETHODIMP
CanvasRenderingContext2D::GetThebesSurface(gfxASurface **surface)
{
  EnsureTarget();
  if (!IsTargetValid()) {
    return NS_ERROR_FAILURE;
  }

  nsRefPtr<gfxASurface> thebesSurface =
      gfxPlatform::GetPlatform()->GetThebesSurfaceForDrawTarget(mTarget);
+3 −0
Original line number Diff line number Diff line
@@ -22,6 +22,7 @@
#include "mozilla/gfx/Rect.h"
#include "mozilla/gfx/2D.h"
#include "gfx2DGlue.h"
#include "imgIEncoder.h"

class nsXULElement;

@@ -460,6 +461,8 @@ public:

  friend class CanvasRenderingContext2DUserData;

  virtual void GetImageBuffer(uint8_t** aImageBuffer, int32_t* aFormat);

protected:
  nsresult GetImageDataArray(JSContext* aCx, int32_t aX, int32_t aY,
                             uint32_t aWidth, uint32_t aHeight,
+1 −0
Original line number Diff line number Diff line
@@ -22,5 +22,6 @@ INCLUDES += \
		-I$(srcdir)/../../html/content/src \
		-I$(srcdir)/../../../js/xpconnect/src \
		-I$(srcdir)/../../../dom/base \
		-I$(srcdir)/../../../image/src \
		-I$(topsrcdir)/content/xul/content/src \
		$(NULL)
+58 −36
Original line number Diff line number Diff line
@@ -27,7 +27,7 @@

#include "nsIVariant.h"

#include "imgIEncoder.h"
#include "ImageEncoder.h"

#include "gfxContext.h"
#include "gfxPattern.h"
@@ -735,57 +735,79 @@ void WebGLContext::LoseOldestWebGLContextIfLimitExceeded()
    }
}

NS_IMETHODIMP
WebGLContext::GetInputStream(const char* aMimeType,
                             const PRUnichar* aEncoderOptions,
                             nsIInputStream **aStream)
void
WebGLContext::GetImageBuffer(uint8_t** aImageBuffer, int32_t* aFormat)
{
    NS_ASSERTION(gl, "GetInputStream on invalid context?");
    if (!gl)
        return NS_ERROR_FAILURE;
    *aImageBuffer = nullptr;
    *aFormat = 0;

    nsRefPtr<gfxImageSurface> surf = new gfxImageSurface(gfxIntSize(mWidth, mHeight),
    nsRefPtr<gfxImageSurface> imgsurf =
        new gfxImageSurface(gfxIntSize(mWidth, mHeight),
                            gfxImageFormatARGB32);
    if (surf->CairoStatus() != 0)
        return NS_ERROR_FAILURE;

    nsRefPtr<gfxContext> tmpcx = new gfxContext(surf);
    // Use Render() to make sure that appropriate y-flip gets applied
    uint32_t flags = mOptions.premultipliedAlpha ? RenderFlagPremultAlpha : 0;
    nsresult rv = Render(tmpcx, GraphicsFilter::FILTER_NEAREST, flags);
    if (NS_FAILED(rv))
        return rv;

    const char encoderPrefix[] = "@mozilla.org/image/encoder;2?type=";
    nsAutoArrayPtr<char> conid(new char[strlen(encoderPrefix) + strlen(aMimeType) + 1]);
    if (!imgsurf || imgsurf->CairoStatus()) {
        return;
    }

    strcpy(conid, encoderPrefix);
    strcat(conid, aMimeType);
    nsRefPtr<gfxContext> ctx = new gfxContext(imgsurf);
    if (!ctx || ctx->HasError()) {
        return;
    }

    nsCOMPtr<imgIEncoder> encoder = do_CreateInstance(conid);
    if (!encoder)
        return NS_ERROR_FAILURE;
    // Use Render() to make sure that appropriate y-flip gets applied
    uint32_t flags = mOptions.premultipliedAlpha ? RenderFlagPremultAlpha : 0;
    nsresult rv = Render(ctx, GraphicsFilter::FILTER_NEAREST, flags);
    if (NS_FAILED(rv)) {
        return;
    }

    int format = imgIEncoder::INPUT_FORMAT_HOSTARGB;
    int32_t format = imgIEncoder::INPUT_FORMAT_HOSTARGB;
    if (!mOptions.premultipliedAlpha) {
        // We need to convert to INPUT_FORMAT_RGBA, otherwise
        // we are automatically considered premult, and unpremult'd.
        // Yes, it is THAT silly.
        // Except for different lossy conversions by color,
        // we could probably just change the label, and not change the data.
        gfxUtils::ConvertBGRAtoRGBA(surf);
        gfxUtils::ConvertBGRAtoRGBA(imgsurf);
        format = imgIEncoder::INPUT_FORMAT_RGBA;
    }

    rv = encoder->InitFromData(surf->Data(),
                               mWidth * mHeight * 4,
                               mWidth, mHeight,
                               surf->Stride(),
                               format,
                               nsDependentString(aEncoderOptions));
    NS_ENSURE_SUCCESS(rv, rv);
    static const fallible_t fallible = fallible_t();
    uint8_t* imageBuffer = new (fallible) uint8_t[mWidth * mHeight * 4];
    if (!imageBuffer) {
        return;
    }
    memcpy(imageBuffer, imgsurf->Data(), mWidth * mHeight * 4);

    *aImageBuffer = imageBuffer;
    *aFormat = format;
}

NS_IMETHODIMP
WebGLContext::GetInputStream(const char* aMimeType,
                             const PRUnichar* aEncoderOptions,
                             nsIInputStream **aStream)
{
    NS_ASSERTION(gl, "GetInputStream on invalid context?");
    if (!gl)
        return NS_ERROR_FAILURE;

    uint8_t* imageBuffer = nullptr;
    int32_t format = 0;
    GetImageBuffer(&imageBuffer, &format);
    if (!imageBuffer) {
        return NS_ERROR_FAILURE;
    }

    nsCString enccid("@mozilla.org/image/encoder;2?type=");
    enccid += aMimeType;
    nsCOMPtr<imgIEncoder> encoder = do_CreateInstance(enccid.get());
    if (!encoder) {
        return NS_ERROR_FAILURE;
    }

    return CallQueryInterface(encoder, aStream);
    return ImageEncoder::GetInputStream(mWidth, mHeight, imageBuffer, format,
                                        encoder, aEncoderOptions, aStream);
}

NS_IMETHODIMP
Loading