content/canvas/src/ImageEncoder.cpp

Thu, 15 Jan 2015 21:03:48 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 15 Jan 2015 21:03:48 +0100
branch
TOR_BUG_9701
changeset 11
deefc01c0e14
permissions
-rw-r--r--

Integrate friendly tips from Tor colleagues to make (or not) 4.5 alpha 3;
This includes removal of overloaded (but unused) methods, and addition of
a overlooked call to DataStruct::SetData(nsISupports, uint32_t, bool.)

michael@0 1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
michael@0 2 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 3 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 5
michael@0 6 #include "gfxImageSurface.h"
michael@0 7 #include "ImageEncoder.h"
michael@0 8 #include "mozilla/dom/CanvasRenderingContext2D.h"
michael@0 9
michael@0 10 namespace mozilla {
michael@0 11 namespace dom {
michael@0 12
michael@0 13 class EncodingCompleteEvent : public nsRunnable
michael@0 14 {
michael@0 15 public:
michael@0 16 NS_DECL_THREADSAFE_ISUPPORTS
michael@0 17
michael@0 18 EncodingCompleteEvent(nsIScriptContext* aScriptContext,
michael@0 19 nsIThread* aEncoderThread,
michael@0 20 FileCallback& aCallback)
michael@0 21 : mImgSize(0)
michael@0 22 , mType()
michael@0 23 , mImgData(nullptr)
michael@0 24 , mScriptContext(aScriptContext)
michael@0 25 , mEncoderThread(aEncoderThread)
michael@0 26 , mCallback(&aCallback)
michael@0 27 , mFailed(false)
michael@0 28 {}
michael@0 29 virtual ~EncodingCompleteEvent() {}
michael@0 30
michael@0 31 NS_IMETHOD Run()
michael@0 32 {
michael@0 33 MOZ_ASSERT(NS_IsMainThread());
michael@0 34
michael@0 35 mozilla::ErrorResult rv;
michael@0 36
michael@0 37 if (!mFailed) {
michael@0 38 nsRefPtr<nsDOMMemoryFile> blob =
michael@0 39 new nsDOMMemoryFile(mImgData, mImgSize, mType);
michael@0 40
michael@0 41 if (mScriptContext) {
michael@0 42 JSContext* jsContext = mScriptContext->GetNativeContext();
michael@0 43 if (jsContext) {
michael@0 44 JS_updateMallocCounter(jsContext, mImgSize);
michael@0 45 }
michael@0 46 }
michael@0 47
michael@0 48 mCallback->Call(blob, rv);
michael@0 49 }
michael@0 50
michael@0 51 // These members aren't thread-safe. We're making sure that they're being
michael@0 52 // released on the main thread here. Otherwise, they could be getting
michael@0 53 // released by EncodingRunnable's destructor on the encoding thread
michael@0 54 // (bug 916128).
michael@0 55 mScriptContext = nullptr;
michael@0 56 mCallback = nullptr;
michael@0 57
michael@0 58 mEncoderThread->Shutdown();
michael@0 59 return rv.ErrorCode();
michael@0 60 }
michael@0 61
michael@0 62 void SetMembers(void* aImgData, uint64_t aImgSize, const nsAutoString& aType)
michael@0 63 {
michael@0 64 mImgData = aImgData;
michael@0 65 mImgSize = aImgSize;
michael@0 66 mType = aType;
michael@0 67 }
michael@0 68
michael@0 69 void SetFailed()
michael@0 70 {
michael@0 71 mFailed = true;
michael@0 72 }
michael@0 73
michael@0 74 private:
michael@0 75 uint64_t mImgSize;
michael@0 76 nsAutoString mType;
michael@0 77 void* mImgData;
michael@0 78 nsCOMPtr<nsIScriptContext> mScriptContext;
michael@0 79 nsCOMPtr<nsIThread> mEncoderThread;
michael@0 80 nsRefPtr<FileCallback> mCallback;
michael@0 81 bool mFailed;
michael@0 82 };
michael@0 83
michael@0 84 NS_IMPL_ISUPPORTS(EncodingCompleteEvent, nsIRunnable);
michael@0 85
michael@0 86 class EncodingRunnable : public nsRunnable
michael@0 87 {
michael@0 88 public:
michael@0 89 NS_DECL_THREADSAFE_ISUPPORTS
michael@0 90
michael@0 91 EncodingRunnable(const nsAString& aType,
michael@0 92 const nsAString& aOptions,
michael@0 93 uint8_t* aImageBuffer,
michael@0 94 imgIEncoder* aEncoder,
michael@0 95 EncodingCompleteEvent* aEncodingCompleteEvent,
michael@0 96 int32_t aFormat,
michael@0 97 const nsIntSize aSize,
michael@0 98 bool aUsePlaceholder,
michael@0 99 bool aUsingCustomOptions)
michael@0 100 : mType(aType)
michael@0 101 , mOptions(aOptions)
michael@0 102 , mImageBuffer(aImageBuffer)
michael@0 103 , mEncoder(aEncoder)
michael@0 104 , mEncodingCompleteEvent(aEncodingCompleteEvent)
michael@0 105 , mFormat(aFormat)
michael@0 106 , mSize(aSize)
michael@0 107 , mUsePlaceholder(aUsePlaceholder)
michael@0 108 , mUsingCustomOptions(aUsingCustomOptions)
michael@0 109 {}
michael@0 110 virtual ~EncodingRunnable() {}
michael@0 111
michael@0 112 nsresult ProcessImageData(uint64_t* aImgSize, void** aImgData)
michael@0 113 {
michael@0 114 nsCOMPtr<nsIInputStream> stream;
michael@0 115 nsresult rv = ImageEncoder::ExtractDataInternal(mType,
michael@0 116 mOptions,
michael@0 117 mImageBuffer,
michael@0 118 mFormat,
michael@0 119 mSize,
michael@0 120 mUsePlaceholder,
michael@0 121 nullptr,
michael@0 122 getter_AddRefs(stream),
michael@0 123 mEncoder);
michael@0 124
michael@0 125 // If there are unrecognized custom parse options, we should fall back to
michael@0 126 // the default values for the encoder without any options at all.
michael@0 127 if (rv == NS_ERROR_INVALID_ARG && mUsingCustomOptions) {
michael@0 128 rv = ImageEncoder::ExtractDataInternal(mType,
michael@0 129 EmptyString(),
michael@0 130 mImageBuffer,
michael@0 131 mFormat,
michael@0 132 mSize,
michael@0 133 mUsePlaceholder,
michael@0 134 nullptr,
michael@0 135 getter_AddRefs(stream),
michael@0 136 mEncoder);
michael@0 137 }
michael@0 138 NS_ENSURE_SUCCESS(rv, rv);
michael@0 139
michael@0 140 rv = stream->Available(aImgSize);
michael@0 141 NS_ENSURE_SUCCESS(rv, rv);
michael@0 142 NS_ENSURE_TRUE(*aImgSize <= UINT32_MAX, NS_ERROR_FILE_TOO_BIG);
michael@0 143
michael@0 144 rv = NS_ReadInputStreamToBuffer(stream, aImgData, *aImgSize);
michael@0 145 NS_ENSURE_SUCCESS(rv, rv);
michael@0 146
michael@0 147 return rv;
michael@0 148 }
michael@0 149
michael@0 150 NS_IMETHOD Run()
michael@0 151 {
michael@0 152 uint64_t imgSize;
michael@0 153 void* imgData = nullptr;
michael@0 154
michael@0 155 nsresult rv = ProcessImageData(&imgSize, &imgData);
michael@0 156 if (NS_FAILED(rv)) {
michael@0 157 mEncodingCompleteEvent->SetFailed();
michael@0 158 } else {
michael@0 159 mEncodingCompleteEvent->SetMembers(imgData, imgSize, mType);
michael@0 160 }
michael@0 161 rv = NS_DispatchToMainThread(mEncodingCompleteEvent, NS_DISPATCH_NORMAL);
michael@0 162 if (NS_FAILED(rv)) {
michael@0 163 // Better to leak than to crash.
michael@0 164 mEncodingCompleteEvent.forget();
michael@0 165 return rv;
michael@0 166 }
michael@0 167
michael@0 168 return rv;
michael@0 169 }
michael@0 170
michael@0 171 private:
michael@0 172 nsAutoString mType;
michael@0 173 nsAutoString mOptions;
michael@0 174 nsAutoArrayPtr<uint8_t> mImageBuffer;
michael@0 175 nsCOMPtr<imgIEncoder> mEncoder;
michael@0 176 nsRefPtr<EncodingCompleteEvent> mEncodingCompleteEvent;
michael@0 177 int32_t mFormat;
michael@0 178 const nsIntSize mSize;
michael@0 179 bool mUsePlaceholder;
michael@0 180 bool mUsingCustomOptions;
michael@0 181 };
michael@0 182
michael@0 183 NS_IMPL_ISUPPORTS(EncodingRunnable, nsIRunnable)
michael@0 184
michael@0 185 /* static */
michael@0 186 nsresult
michael@0 187 ImageEncoder::ExtractData(nsAString& aType,
michael@0 188 const nsAString& aOptions,
michael@0 189 const nsIntSize aSize,
michael@0 190 bool aUsePlaceholder,
michael@0 191 nsICanvasRenderingContextInternal* aContext,
michael@0 192 nsIInputStream** aStream)
michael@0 193 {
michael@0 194 nsCOMPtr<imgIEncoder> encoder = ImageEncoder::GetImageEncoder(aType);
michael@0 195 if (!encoder) {
michael@0 196 return NS_IMAGELIB_ERROR_NO_ENCODER;
michael@0 197 }
michael@0 198
michael@0 199 return ExtractDataInternal(aType, aOptions, nullptr, 0, aSize,
michael@0 200 aUsePlaceholder, aContext, aStream, encoder);
michael@0 201 }
michael@0 202
michael@0 203 /* static */
michael@0 204 nsresult
michael@0 205 ImageEncoder::ExtractDataAsync(nsAString& aType,
michael@0 206 const nsAString& aOptions,
michael@0 207 bool aUsingCustomOptions,
michael@0 208 uint8_t* aImageBuffer,
michael@0 209 int32_t aFormat,
michael@0 210 const nsIntSize aSize,
michael@0 211 bool aUsePlaceholder,
michael@0 212 nsICanvasRenderingContextInternal* aContext,
michael@0 213 nsIScriptContext* aScriptContext,
michael@0 214 FileCallback& aCallback)
michael@0 215 {
michael@0 216 nsCOMPtr<imgIEncoder> encoder = ImageEncoder::GetImageEncoder(aType);
michael@0 217 if (!encoder) {
michael@0 218 return NS_IMAGELIB_ERROR_NO_ENCODER;
michael@0 219 }
michael@0 220
michael@0 221 nsCOMPtr<nsIThread> encoderThread;
michael@0 222 nsresult rv = NS_NewThread(getter_AddRefs(encoderThread), nullptr);
michael@0 223 NS_ENSURE_SUCCESS(rv, rv);
michael@0 224
michael@0 225 nsRefPtr<EncodingCompleteEvent> completeEvent =
michael@0 226 new EncodingCompleteEvent(aScriptContext, encoderThread, aCallback);
michael@0 227
michael@0 228 nsCOMPtr<nsIRunnable> event = new EncodingRunnable(aType,
michael@0 229 aOptions,
michael@0 230 aImageBuffer,
michael@0 231 encoder,
michael@0 232 completeEvent,
michael@0 233 aFormat,
michael@0 234 aSize,
michael@0 235 aUsePlaceholder,
michael@0 236 aUsingCustomOptions);
michael@0 237 return encoderThread->Dispatch(event, NS_DISPATCH_NORMAL);
michael@0 238 }
michael@0 239
michael@0 240 /*static*/ nsresult
michael@0 241 ImageEncoder::GetInputStream(int32_t aWidth,
michael@0 242 int32_t aHeight,
michael@0 243 uint8_t* aImageBuffer,
michael@0 244 int32_t aFormat,
michael@0 245 imgIEncoder* aEncoder,
michael@0 246 const char16_t* aEncoderOptions,
michael@0 247 nsIInputStream** aStream)
michael@0 248 {
michael@0 249 nsresult rv =
michael@0 250 aEncoder->InitFromData(aImageBuffer,
michael@0 251 aWidth * aHeight * 4, aWidth, aHeight, aWidth * 4,
michael@0 252 aFormat,
michael@0 253 nsDependentString(aEncoderOptions));
michael@0 254 NS_ENSURE_SUCCESS(rv, rv);
michael@0 255
michael@0 256 return CallQueryInterface(aEncoder, aStream);
michael@0 257 }
michael@0 258
michael@0 259 /* static */
michael@0 260 nsresult
michael@0 261 ImageEncoder::ExtractDataInternal(const nsAString& aType,
michael@0 262 const nsAString& aOptions,
michael@0 263 uint8_t* aImageBuffer,
michael@0 264 int32_t aFormat,
michael@0 265 const nsIntSize aSize,
michael@0 266 bool aUsePlaceholder,
michael@0 267 nsICanvasRenderingContextInternal* aContext,
michael@0 268 nsIInputStream** aStream,
michael@0 269 imgIEncoder* aEncoder)
michael@0 270 {
michael@0 271 nsCOMPtr<nsIInputStream> imgStream;
michael@0 272
michael@0 273 // get image bytes
michael@0 274 nsresult rv;
michael@0 275 if (aImageBuffer && !aUsePlaceholder) {
michael@0 276 rv = ImageEncoder::GetInputStream(
michael@0 277 aSize.width,
michael@0 278 aSize.height,
michael@0 279 aImageBuffer,
michael@0 280 aFormat,
michael@0 281 aEncoder,
michael@0 282 nsPromiseFlatString(aOptions).get(),
michael@0 283 getter_AddRefs(imgStream));
michael@0 284 } else if (aContext && !aUsePlaceholder) {
michael@0 285 NS_ConvertUTF16toUTF8 encoderType(aType);
michael@0 286 rv = aContext->GetInputStream(encoderType.get(),
michael@0 287 nsPromiseFlatString(aOptions).get(),
michael@0 288 getter_AddRefs(imgStream));
michael@0 289 } else {
michael@0 290 // If placeholder data requested or no context, encode an empty image.
michael@0 291 // note that if we didn't have a current context, the spec says we're
michael@0 292 // supposed to just return transparent black pixels of the canvas
michael@0 293 // dimensions.
michael@0 294 nsRefPtr<gfxImageSurface> emptyCanvas =
michael@0 295 new gfxImageSurface(gfxIntSize(aSize.width, aSize.height),
michael@0 296 gfxImageFormat::ARGB32);
michael@0 297 if (emptyCanvas->CairoStatus()) {
michael@0 298 return NS_ERROR_INVALID_ARG;
michael@0 299 }
michael@0 300 if (aUsePlaceholder) {
michael@0 301 // If placeholder data was requested, return all-white, opaque image data.
michael@0 302 int32_t dataSize = emptyCanvas->GetDataSize();
michael@0 303 memset(emptyCanvas->Data(), 0xFF, dataSize);
michael@0 304 }
michael@0 305 rv = aEncoder->InitFromData(emptyCanvas->Data(),
michael@0 306 aSize.width * aSize.height * 4,
michael@0 307 aSize.width,
michael@0 308 aSize.height,
michael@0 309 aSize.width * 4,
michael@0 310 imgIEncoder::INPUT_FORMAT_HOSTARGB,
michael@0 311 aOptions);
michael@0 312 if (NS_SUCCEEDED(rv)) {
michael@0 313 imgStream = do_QueryInterface(aEncoder);
michael@0 314 }
michael@0 315 }
michael@0 316 NS_ENSURE_SUCCESS(rv, rv);
michael@0 317
michael@0 318 imgStream.forget(aStream);
michael@0 319 return rv;
michael@0 320 }
michael@0 321
michael@0 322 /* static */
michael@0 323 already_AddRefed<imgIEncoder>
michael@0 324 ImageEncoder::GetImageEncoder(nsAString& aType)
michael@0 325 {
michael@0 326 // Get an image encoder for the media type.
michael@0 327 nsCString encoderCID("@mozilla.org/image/encoder;2?type=");
michael@0 328 NS_ConvertUTF16toUTF8 encoderType(aType);
michael@0 329 encoderCID += encoderType;
michael@0 330 nsCOMPtr<imgIEncoder> encoder = do_CreateInstance(encoderCID.get());
michael@0 331
michael@0 332 if (!encoder && aType != NS_LITERAL_STRING("image/png")) {
michael@0 333 // Unable to create an encoder instance of the specified type. Falling back
michael@0 334 // to PNG.
michael@0 335 aType.AssignLiteral("image/png");
michael@0 336 nsCString PNGEncoderCID("@mozilla.org/image/encoder;2?type=image/png");
michael@0 337 encoder = do_CreateInstance(PNGEncoderCID.get());
michael@0 338 }
michael@0 339
michael@0 340 return encoder.forget();
michael@0 341 }
michael@0 342
michael@0 343 } // namespace dom
michael@0 344 } // namespace mozilla

mercurial