Bug 661529: Make <canvas>.toDataURL more efficient by avoiding unnecessary copying/allocation. r=vlad
authorKyle Huey <khuey@kylehuey.com>
Wed, 08 Jun 2011 11:12:18 -0700
changeset 70755 dd0a133916c7f6b9fd89bc2855ac607d9d0e1945
parent 70754 edaa20bfaffe35ec6abec28b1654eb2ff7b8828b
child 70756 4e2b86b6025c899c9904e1cf8b43059a89f12a02
push id20406
push usereakhgari@mozilla.com
push dateWed, 08 Jun 2011 21:46:52 +0000
treeherdermozilla-central@3a509617644e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersvlad
bugs661529
milestone7.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 661529: Make <canvas>.toDataURL more efficient by avoiding unnecessary copying/allocation. r=vlad
content/html/content/public/nsHTMLCanvasElement.h
content/html/content/src/nsHTMLCanvasElement.cpp
--- a/content/html/content/public/nsHTMLCanvasElement.h
+++ b/content/html/content/public/nsHTMLCanvasElement.h
@@ -172,18 +172,17 @@ public:
 
   virtual nsXPCClassInfo* GetClassInfo();
 protected:
   nsIntSize GetWidthHeight();
 
   nsresult UpdateContext(nsIPropertyBag *aNewContextOptions = nsnull);
   nsresult ExtractData(const nsAString& aType,
                        const nsAString& aOptions,
-                       char*& aData,
-                       PRUint32& aSize,
+                       nsIInputStream** aStream,
                        bool& aFellBackToPNG);
   nsresult ToDataURLImpl(const nsAString& aMimeType,
                          nsIVariant* aEncoderOptions,
                          nsAString& aDataURL);
   nsresult MozGetAsFileImpl(const nsAString& aName,
                             const nsAString& aType,
                             nsIDOMFile** aResult);
   nsresult GetContextHelper(const nsAString& aContextId,
--- a/content/html/content/src/nsHTMLCanvasElement.cpp
+++ b/content/html/content/src/nsHTMLCanvasElement.cpp
@@ -32,17 +32,17 @@
  * and other provisions required by the GPL or the LGPL. If you do not delete
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #include "nsHTMLCanvasElement.h"
 
-#include "plbase64.h"
+#include "mozilla/Base64.h"
 #include "nsNetUtil.h"
 #include "prmem.h"
 #include "nsDOMFile.h"
 #include "CheckedInt.h"
 
 #include "nsIScriptSecurityManager.h"
 #include "nsIXPConnect.h"
 #include "jsapi.h"
@@ -206,18 +206,17 @@ nsHTMLCanvasElement::ToDataURL(const nsA
   }
 
   return ToDataURLImpl(aType, aParams, aDataURL);
 }
 
 nsresult
 nsHTMLCanvasElement::ExtractData(const nsAString& aType,
                                  const nsAString& aOptions,
-                                 char*& aResult,
-                                 PRUint32& aSize,
+                                 nsIInputStream** aStream,
                                  bool& aFellBackToPNG)
 {
   // note that if we don't have a current context, the spec says we're
   // supposed to just return transparent black pixels of the canvas
   // dimensions.
   nsRefPtr<gfxImageSurface> emptyCanvas;
   nsIntSize size = GetWidthHeight();
   if (!mCurrentContext) {
@@ -260,55 +259,19 @@ nsHTMLCanvasElement::ExtractData(const n
   if (NS_FAILED(rv) && !aFellBackToPNG) {
     // Try image/png instead.
     // XXX ERRMSG we need to report an error to developers here! (bug 329026)
     aFellBackToPNG = true;
     encoderType.AssignLiteral("image/png");
     goto try_again;
   }
 
-  // at this point, we either need to succeed or bail.
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  // Generally, there will be only one chunk of data, and it will be available
-  // for us to read right away, so optimize this case.
-  PRUint32 bufSize;
-  rv = imgStream->Available(&bufSize);
-  CheckedUint32 safeBufSize(bufSize);
   NS_ENSURE_SUCCESS(rv, rv);
 
-  // ...leave a little extra room so we can call read again and make sure we
-  // got everything. 16 bytes for better padding (maybe)
-  safeBufSize += 16;
-  NS_ENSURE_TRUE(safeBufSize.valid(), NS_ERROR_FAILURE);
-  aSize = 0;
-  aResult = (char*)PR_Malloc(safeBufSize.value());
-  if (!aResult)
-    return NS_ERROR_OUT_OF_MEMORY;
-  PRUint32 numReadThisTime = 0;
-  while ((rv = imgStream->Read(&aResult[aSize], safeBufSize.value() - aSize,
-                               &numReadThisTime)) == NS_OK &&
-         numReadThisTime > 0) {
-    aSize += numReadThisTime;
-    if (aSize == safeBufSize.value()) {
-      // need a bigger buffer, just double
-      safeBufSize *= 2;
-      if (!safeBufSize.valid()) {
-        PR_Free(aResult);
-        return NS_ERROR_FAILURE;
-      }
-      char* newImgData = (char*)PR_Realloc(aResult, safeBufSize.value());
-      if (! newImgData) {
-        PR_Free(aResult);
-        return NS_ERROR_OUT_OF_MEMORY;
-      }
-      aResult = newImgData;
-    }
-  }
-
+  return CallQueryInterface(imgStream, aStream);
   return NS_OK;
 }
 
 nsresult
 nsHTMLCanvasElement::ToDataURLImpl(const nsAString& aMimeType,
                                    nsIVariant* aEncoderOptions,
                                    nsAString& aDataURL)
 {
@@ -338,39 +301,33 @@ nsHTMLCanvasElement::ToDataURLImpl(const
       if (NS_SUCCEEDED(aEncoderOptions->GetAsDouble(&quality)) &&
           quality >= 0.0 && quality <= 1.0) {
         params.AppendLiteral("quality=");
         params.AppendInt(NS_lround(quality * 100.0));
       }
     }
   }
 
-  PRUint32 imgSize = 0;
-  char* imgData;
-
-  nsresult rv = ExtractData(type, params, imgData, imgSize, fallbackToPNG);
+  nsCOMPtr<nsIInputStream> stream;
+  nsresult rv = ExtractData(type, params, getter_AddRefs(stream),
+                            fallbackToPNG);
   NS_ENSURE_SUCCESS(rv, rv);
 
-  // base 64, result will be NULL terminated
-  char* encodedImg = PL_Base64Encode(imgData, imgSize, nsnull);
-  PR_Free(imgData);
-  if (!encodedImg) // not sure why this would fail
-    return NS_ERROR_OUT_OF_MEMORY;
-
   // build data URL string
   if (fallbackToPNG)
-    aDataURL = NS_LITERAL_STRING("data:image/png;base64,") +
-      NS_ConvertUTF8toUTF16(encodedImg);
+    aDataURL = NS_LITERAL_STRING("data:image/png;base64,");
   else
     aDataURL = NS_LITERAL_STRING("data:") + type +
-      NS_LITERAL_STRING(";base64,") + NS_ConvertUTF8toUTF16(encodedImg);
+      NS_LITERAL_STRING(";base64,");
 
-  PR_Free(encodedImg);
+  PRUint32 count;
+  rv = stream->Available(&count);
+  NS_ENSURE_SUCCESS(rv, rv);
 
-  return NS_OK;
+  return Base64EncodeInputStream(stream, aDataURL, count, aDataURL.Length());
 }
 
 NS_IMETHODIMP
 nsHTMLCanvasElement::MozGetAsFile(const nsAString& aName,
                                   const nsAString& aType,
                                   PRUint8 optional_argc,
                                   nsIDOMFile** aResult)
 {
@@ -384,31 +341,38 @@ nsHTMLCanvasElement::MozGetAsFile(const 
 }
 
 nsresult
 nsHTMLCanvasElement::MozGetAsFileImpl(const nsAString& aName,
                                       const nsAString& aType,
                                       nsIDOMFile** aResult)
 {
   bool fallbackToPNG = false;
-  PRUint32 imgSize = 0;
-  char* imgData;
 
-  nsresult rv = ExtractData(aType, EmptyString(), imgData,
-                            imgSize, fallbackToPNG);
+  nsCOMPtr<nsIInputStream> stream;
+  nsresult rv = ExtractData(aType, EmptyString(), getter_AddRefs(stream),
+                            fallbackToPNG);
   NS_ENSURE_SUCCESS(rv, rv);
 
   nsAutoString type(aType);
   if (fallbackToPNG) {
     type.AssignLiteral("image/png");
   }
 
+  PRUint32 imgSize;
+  rv = stream->Available(&imgSize);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  void* imgData = nsnull;
+  rv = NS_ReadInputStreamToBuffer(stream, &imgData, imgSize);
+  NS_ENSURE_SUCCESS(rv, rv);
+
   // The DOMFile takes ownership of the buffer
   nsRefPtr<nsDOMMemoryFile> file =
-    new nsDOMMemoryFile((void*)imgData, imgSize, aName, type);
+    new nsDOMMemoryFile(imgData, imgSize, aName, type);
 
   return CallQueryInterface(file, aResult);
 }
 
 nsresult
 nsHTMLCanvasElement::GetContextHelper(const nsAString& aContextId,
                                       nsICanvasRenderingContextInternal **aContext)
 {