image/src/Decoder.cpp

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

michael@0 1
michael@0 2 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
michael@0 3 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 4 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 6
michael@0 7 #include "Decoder.h"
michael@0 8 #include "nsIConsoleService.h"
michael@0 9 #include "nsIScriptError.h"
michael@0 10 #include "GeckoProfiler.h"
michael@0 11 #include "nsServiceManagerUtils.h"
michael@0 12 #include "nsComponentManagerUtils.h"
michael@0 13
michael@0 14 namespace mozilla {
michael@0 15 namespace image {
michael@0 16
michael@0 17 Decoder::Decoder(RasterImage &aImage)
michael@0 18 : mImage(aImage)
michael@0 19 , mCurrentFrame(nullptr)
michael@0 20 , mImageData(nullptr)
michael@0 21 , mColormap(nullptr)
michael@0 22 , mDecodeFlags(0)
michael@0 23 , mDecodeDone(false)
michael@0 24 , mDataError(false)
michael@0 25 , mFrameCount(0)
michael@0 26 , mFailCode(NS_OK)
michael@0 27 , mNeedsNewFrame(false)
michael@0 28 , mInitialized(false)
michael@0 29 , mSizeDecode(false)
michael@0 30 , mInFrame(false)
michael@0 31 , mIsAnimated(false)
michael@0 32 {
michael@0 33 }
michael@0 34
michael@0 35 Decoder::~Decoder()
michael@0 36 {
michael@0 37 mInitialized = false;
michael@0 38 }
michael@0 39
michael@0 40 /*
michael@0 41 * Common implementation of the decoder interface.
michael@0 42 */
michael@0 43
michael@0 44 void
michael@0 45 Decoder::Init()
michael@0 46 {
michael@0 47 // No re-initializing
michael@0 48 NS_ABORT_IF_FALSE(!mInitialized, "Can't re-initialize a decoder!");
michael@0 49 NS_ABORT_IF_FALSE(mObserver, "Need an observer!");
michael@0 50
michael@0 51 // Fire OnStartDecode at init time to support bug 512435.
michael@0 52 if (!IsSizeDecode())
michael@0 53 mObserver->OnStartDecode();
michael@0 54
michael@0 55 // Implementation-specific initialization
michael@0 56 InitInternal();
michael@0 57
michael@0 58 mInitialized = true;
michael@0 59 }
michael@0 60
michael@0 61 // Initializes a decoder whose image and observer is already being used by a
michael@0 62 // parent decoder
michael@0 63 void
michael@0 64 Decoder::InitSharedDecoder(uint8_t* imageData, uint32_t imageDataLength,
michael@0 65 uint32_t* colormap, uint32_t colormapSize,
michael@0 66 imgFrame* currentFrame)
michael@0 67 {
michael@0 68 // No re-initializing
michael@0 69 NS_ABORT_IF_FALSE(!mInitialized, "Can't re-initialize a decoder!");
michael@0 70 NS_ABORT_IF_FALSE(mObserver, "Need an observer!");
michael@0 71
michael@0 72 mImageData = imageData;
michael@0 73 mImageDataLength = imageDataLength;
michael@0 74 mColormap = colormap;
michael@0 75 mColormapSize = colormapSize;
michael@0 76 mCurrentFrame = currentFrame;
michael@0 77 // We have all the frame data, so we've started the frame.
michael@0 78 if (!IsSizeDecode()) {
michael@0 79 PostFrameStart();
michael@0 80 }
michael@0 81
michael@0 82 // Implementation-specific initialization
michael@0 83 InitInternal();
michael@0 84 mInitialized = true;
michael@0 85 }
michael@0 86
michael@0 87 void
michael@0 88 Decoder::Write(const char* aBuffer, uint32_t aCount, DecodeStrategy aStrategy)
michael@0 89 {
michael@0 90 PROFILER_LABEL("ImageDecoder", "Write");
michael@0 91 MOZ_ASSERT(NS_IsMainThread() || aStrategy == DECODE_ASYNC);
michael@0 92
michael@0 93 // We're strict about decoder errors
michael@0 94 NS_ABORT_IF_FALSE(!HasDecoderError(),
michael@0 95 "Not allowed to make more decoder calls after error!");
michael@0 96
michael@0 97 // If a data error occured, just ignore future data
michael@0 98 if (HasDataError())
michael@0 99 return;
michael@0 100
michael@0 101 if (IsSizeDecode() && HasSize()) {
michael@0 102 // More data came in since we found the size. We have nothing to do here.
michael@0 103 return;
michael@0 104 }
michael@0 105
michael@0 106 // Pass the data along to the implementation
michael@0 107 WriteInternal(aBuffer, aCount, aStrategy);
michael@0 108
michael@0 109 // If we're a synchronous decoder and we need a new frame to proceed, let's
michael@0 110 // create one and call it again.
michael@0 111 while (aStrategy == DECODE_SYNC && NeedsNewFrame() && !HasDataError()) {
michael@0 112 nsresult rv = AllocateFrame();
michael@0 113
michael@0 114 if (NS_SUCCEEDED(rv)) {
michael@0 115 // Tell the decoder to use the data it saved when it asked for a new frame.
michael@0 116 WriteInternal(nullptr, 0, aStrategy);
michael@0 117 }
michael@0 118 }
michael@0 119 }
michael@0 120
michael@0 121 void
michael@0 122 Decoder::Finish(RasterImage::eShutdownIntent aShutdownIntent)
michael@0 123 {
michael@0 124 MOZ_ASSERT(NS_IsMainThread());
michael@0 125
michael@0 126 // Implementation-specific finalization
michael@0 127 if (!HasError())
michael@0 128 FinishInternal();
michael@0 129
michael@0 130 // If the implementation left us mid-frame, finish that up.
michael@0 131 if (mInFrame && !HasError())
michael@0 132 PostFrameStop();
michael@0 133
michael@0 134 // If PostDecodeDone() has not been called, we need to sent teardown
michael@0 135 // notifications.
michael@0 136 if (!IsSizeDecode() && !mDecodeDone) {
michael@0 137
michael@0 138 // Log data errors to the error console
michael@0 139 nsCOMPtr<nsIConsoleService> consoleService =
michael@0 140 do_GetService(NS_CONSOLESERVICE_CONTRACTID);
michael@0 141 nsCOMPtr<nsIScriptError> errorObject =
michael@0 142 do_CreateInstance(NS_SCRIPTERROR_CONTRACTID);
michael@0 143
michael@0 144 if (consoleService && errorObject && !HasDecoderError()) {
michael@0 145 nsAutoString msg(NS_LITERAL_STRING("Image corrupt or truncated: ") +
michael@0 146 NS_ConvertUTF8toUTF16(mImage.GetURIString()));
michael@0 147
michael@0 148 if (NS_SUCCEEDED(errorObject->InitWithWindowID(
michael@0 149 msg,
michael@0 150 NS_ConvertUTF8toUTF16(mImage.GetURIString()),
michael@0 151 EmptyString(), 0, 0, nsIScriptError::errorFlag,
michael@0 152 "Image", mImage.InnerWindowID()
michael@0 153 ))) {
michael@0 154 consoleService->LogMessage(errorObject);
michael@0 155 }
michael@0 156 }
michael@0 157
michael@0 158 bool usable = !HasDecoderError();
michael@0 159 if (aShutdownIntent != RasterImage::eShutdownIntent_NotNeeded && !HasDecoderError()) {
michael@0 160 // If we only have a data error, we're usable if we have at least one complete frame.
michael@0 161 if (GetCompleteFrameCount() == 0) {
michael@0 162 usable = false;
michael@0 163 }
michael@0 164 }
michael@0 165
michael@0 166 // If we're usable, do exactly what we should have when the decoder
michael@0 167 // completed.
michael@0 168 if (usable) {
michael@0 169 if (mInFrame) {
michael@0 170 PostFrameStop();
michael@0 171 }
michael@0 172 PostDecodeDone();
michael@0 173 } else {
michael@0 174 if (mObserver) {
michael@0 175 mObserver->OnStopDecode(NS_ERROR_FAILURE);
michael@0 176 }
michael@0 177 }
michael@0 178 }
michael@0 179
michael@0 180 // Set image metadata before calling DecodingComplete, because DecodingComplete calls Optimize().
michael@0 181 mImageMetadata.SetOnImage(&mImage);
michael@0 182
michael@0 183 if (mDecodeDone) {
michael@0 184 mImage.DecodingComplete();
michael@0 185 }
michael@0 186 }
michael@0 187
michael@0 188 void
michael@0 189 Decoder::FinishSharedDecoder()
michael@0 190 {
michael@0 191 MOZ_ASSERT(NS_IsMainThread());
michael@0 192
michael@0 193 if (!HasError()) {
michael@0 194 FinishInternal();
michael@0 195 }
michael@0 196 }
michael@0 197
michael@0 198 nsresult
michael@0 199 Decoder::AllocateFrame()
michael@0 200 {
michael@0 201 MOZ_ASSERT(mNeedsNewFrame);
michael@0 202 MOZ_ASSERT(NS_IsMainThread());
michael@0 203
michael@0 204 MarkFrameDirty();
michael@0 205
michael@0 206 nsresult rv;
michael@0 207 imgFrame* frame = nullptr;
michael@0 208 if (mNewFrameData.mPaletteDepth) {
michael@0 209 rv = mImage.EnsureFrame(mNewFrameData.mFrameNum, mNewFrameData.mOffsetX,
michael@0 210 mNewFrameData.mOffsetY, mNewFrameData.mWidth,
michael@0 211 mNewFrameData.mHeight, mNewFrameData.mFormat,
michael@0 212 mNewFrameData.mPaletteDepth,
michael@0 213 &mImageData, &mImageDataLength,
michael@0 214 &mColormap, &mColormapSize, &frame);
michael@0 215 } else {
michael@0 216 rv = mImage.EnsureFrame(mNewFrameData.mFrameNum, mNewFrameData.mOffsetX,
michael@0 217 mNewFrameData.mOffsetY, mNewFrameData.mWidth,
michael@0 218 mNewFrameData.mHeight, mNewFrameData.mFormat,
michael@0 219 &mImageData, &mImageDataLength, &frame);
michael@0 220 }
michael@0 221
michael@0 222 if (NS_SUCCEEDED(rv)) {
michael@0 223 mCurrentFrame = frame;
michael@0 224 } else {
michael@0 225 mCurrentFrame = nullptr;
michael@0 226 }
michael@0 227
michael@0 228 // Notify if appropriate
michael@0 229 if (NS_SUCCEEDED(rv) && mNewFrameData.mFrameNum == mFrameCount) {
michael@0 230 PostFrameStart();
michael@0 231 } else if (NS_FAILED(rv)) {
michael@0 232 PostDataError();
michael@0 233 }
michael@0 234
michael@0 235 // Mark ourselves as not needing another frame before talking to anyone else
michael@0 236 // so they can tell us if they need yet another.
michael@0 237 mNeedsNewFrame = false;
michael@0 238
michael@0 239 return rv;
michael@0 240 }
michael@0 241
michael@0 242 void
michael@0 243 Decoder::FlushInvalidations()
michael@0 244 {
michael@0 245 NS_ABORT_IF_FALSE(!HasDecoderError(),
michael@0 246 "Not allowed to make more decoder calls after error!");
michael@0 247
michael@0 248 // If we've got an empty invalidation rect, we have nothing to do
michael@0 249 if (mInvalidRect.IsEmpty())
michael@0 250 return;
michael@0 251
michael@0 252 if (mObserver) {
michael@0 253 #ifdef XP_MACOSX
michael@0 254 // Bug 703231
michael@0 255 // Because of high quality down sampling on mac we show scan lines while decoding.
michael@0 256 // Bypass this problem by redrawing the border.
michael@0 257 if (mImageMetadata.HasSize()) {
michael@0 258 nsIntRect mImageBound(0, 0, mImageMetadata.GetWidth(), mImageMetadata.GetHeight());
michael@0 259
michael@0 260 mInvalidRect.Inflate(1);
michael@0 261 mInvalidRect = mInvalidRect.Intersect(mImageBound);
michael@0 262 }
michael@0 263 #endif
michael@0 264 mObserver->FrameChanged(&mInvalidRect);
michael@0 265 }
michael@0 266
michael@0 267 // Clear the invalidation rectangle
michael@0 268 mInvalidRect.SetEmpty();
michael@0 269 }
michael@0 270
michael@0 271 void
michael@0 272 Decoder::SetSizeOnImage()
michael@0 273 {
michael@0 274 MOZ_ASSERT(mImageMetadata.HasSize(), "Should have size");
michael@0 275 MOZ_ASSERT(mImageMetadata.HasOrientation(), "Should have orientation");
michael@0 276
michael@0 277 mImage.SetSize(mImageMetadata.GetWidth(),
michael@0 278 mImageMetadata.GetHeight(),
michael@0 279 mImageMetadata.GetOrientation());
michael@0 280 }
michael@0 281
michael@0 282 /*
michael@0 283 * Hook stubs. Override these as necessary in decoder implementations.
michael@0 284 */
michael@0 285
michael@0 286 void Decoder::InitInternal() { }
michael@0 287 void Decoder::WriteInternal(const char* aBuffer, uint32_t aCount, DecodeStrategy aStrategy) { }
michael@0 288 void Decoder::FinishInternal() { }
michael@0 289
michael@0 290 /*
michael@0 291 * Progress Notifications
michael@0 292 */
michael@0 293
michael@0 294 void
michael@0 295 Decoder::PostSize(int32_t aWidth,
michael@0 296 int32_t aHeight,
michael@0 297 Orientation aOrientation /* = Orientation()*/)
michael@0 298 {
michael@0 299 // Validate
michael@0 300 NS_ABORT_IF_FALSE(aWidth >= 0, "Width can't be negative!");
michael@0 301 NS_ABORT_IF_FALSE(aHeight >= 0, "Height can't be negative!");
michael@0 302
michael@0 303 // Tell the image
michael@0 304 mImageMetadata.SetSize(aWidth, aHeight, aOrientation);
michael@0 305
michael@0 306 // Notify the observer
michael@0 307 if (mObserver)
michael@0 308 mObserver->OnStartContainer();
michael@0 309 }
michael@0 310
michael@0 311 void
michael@0 312 Decoder::PostFrameStart()
michael@0 313 {
michael@0 314 // We shouldn't already be mid-frame
michael@0 315 NS_ABORT_IF_FALSE(!mInFrame, "Starting new frame but not done with old one!");
michael@0 316
michael@0 317 // We should take care of any invalidation region when wrapping up the
michael@0 318 // previous frame
michael@0 319 NS_ABORT_IF_FALSE(mInvalidRect.IsEmpty(),
michael@0 320 "Start image frame with non-empty invalidation region!");
michael@0 321
michael@0 322 // Update our state to reflect the new frame
michael@0 323 mFrameCount++;
michael@0 324 mInFrame = true;
michael@0 325
michael@0 326 // Decoder implementations should only call this method if they successfully
michael@0 327 // appended the frame to the image. So mFrameCount should always match that
michael@0 328 // reported by the Image.
michael@0 329 NS_ABORT_IF_FALSE(mFrameCount == mImage.GetNumFrames(),
michael@0 330 "Decoder frame count doesn't match image's!");
michael@0 331
michael@0 332 // Fire notifications
michael@0 333 if (mObserver) {
michael@0 334 mObserver->OnStartFrame();
michael@0 335 }
michael@0 336 }
michael@0 337
michael@0 338 void
michael@0 339 Decoder::PostFrameStop(FrameBlender::FrameAlpha aFrameAlpha /* = FrameBlender::kFrameHasAlpha */,
michael@0 340 FrameBlender::FrameDisposalMethod aDisposalMethod /* = FrameBlender::kDisposeKeep */,
michael@0 341 int32_t aTimeout /* = 0 */,
michael@0 342 FrameBlender::FrameBlendMethod aBlendMethod /* = FrameBlender::kBlendOver */)
michael@0 343 {
michael@0 344 // We should be mid-frame
michael@0 345 NS_ABORT_IF_FALSE(mInFrame, "Stopping frame when we didn't start one!");
michael@0 346 NS_ABORT_IF_FALSE(mCurrentFrame, "Stopping frame when we don't have one!");
michael@0 347
michael@0 348 // Update our state
michael@0 349 mInFrame = false;
michael@0 350
michael@0 351 if (aFrameAlpha == FrameBlender::kFrameOpaque) {
michael@0 352 mCurrentFrame->SetHasNoAlpha();
michael@0 353 }
michael@0 354
michael@0 355 mCurrentFrame->SetFrameDisposalMethod(aDisposalMethod);
michael@0 356 mCurrentFrame->SetRawTimeout(aTimeout);
michael@0 357 mCurrentFrame->SetBlendMethod(aBlendMethod);
michael@0 358 mCurrentFrame->ImageUpdated(mCurrentFrame->GetRect());
michael@0 359
michael@0 360 // Flush any invalidations before we finish the frame
michael@0 361 FlushInvalidations();
michael@0 362
michael@0 363 // Fire notifications
michael@0 364 if (mObserver) {
michael@0 365 mObserver->OnStopFrame();
michael@0 366 if (mFrameCount > 1 && !mIsAnimated) {
michael@0 367 mIsAnimated = true;
michael@0 368 mObserver->OnImageIsAnimated();
michael@0 369 }
michael@0 370 }
michael@0 371 }
michael@0 372
michael@0 373 void
michael@0 374 Decoder::PostInvalidation(nsIntRect& aRect)
michael@0 375 {
michael@0 376 // We should be mid-frame
michael@0 377 NS_ABORT_IF_FALSE(mInFrame, "Can't invalidate when not mid-frame!");
michael@0 378 NS_ABORT_IF_FALSE(mCurrentFrame, "Can't invalidate when not mid-frame!");
michael@0 379
michael@0 380 // Account for the new region
michael@0 381 mInvalidRect.UnionRect(mInvalidRect, aRect);
michael@0 382 mCurrentFrame->ImageUpdated(aRect);
michael@0 383 }
michael@0 384
michael@0 385 void
michael@0 386 Decoder::PostDecodeDone(int32_t aLoopCount /* = 0 */)
michael@0 387 {
michael@0 388 NS_ABORT_IF_FALSE(!IsSizeDecode(), "Can't be done with decoding with size decode!");
michael@0 389 NS_ABORT_IF_FALSE(!mInFrame, "Can't be done decoding if we're mid-frame!");
michael@0 390 NS_ABORT_IF_FALSE(!mDecodeDone, "Decode already done!");
michael@0 391 mDecodeDone = true;
michael@0 392
michael@0 393 mImageMetadata.SetLoopCount(aLoopCount);
michael@0 394 mImageMetadata.SetIsNonPremultiplied(GetDecodeFlags() & DECODER_NO_PREMULTIPLY_ALPHA);
michael@0 395
michael@0 396 if (mObserver) {
michael@0 397 mObserver->OnStopDecode(NS_OK);
michael@0 398 }
michael@0 399 }
michael@0 400
michael@0 401 void
michael@0 402 Decoder::PostDataError()
michael@0 403 {
michael@0 404 mDataError = true;
michael@0 405 }
michael@0 406
michael@0 407 void
michael@0 408 Decoder::PostDecoderError(nsresult aFailureCode)
michael@0 409 {
michael@0 410 NS_ABORT_IF_FALSE(NS_FAILED(aFailureCode), "Not a failure code!");
michael@0 411
michael@0 412 mFailCode = aFailureCode;
michael@0 413
michael@0 414 // XXXbholley - we should report the image URI here, but imgContainer
michael@0 415 // needs to know its URI first
michael@0 416 NS_WARNING("Image decoding error - This is probably a bug!");
michael@0 417 }
michael@0 418
michael@0 419 void
michael@0 420 Decoder::NeedNewFrame(uint32_t framenum, uint32_t x_offset, uint32_t y_offset,
michael@0 421 uint32_t width, uint32_t height,
michael@0 422 gfxImageFormat format,
michael@0 423 uint8_t palette_depth /* = 0 */)
michael@0 424 {
michael@0 425 // Decoders should never call NeedNewFrame without yielding back to Write().
michael@0 426 MOZ_ASSERT(!mNeedsNewFrame);
michael@0 427
michael@0 428 // We don't want images going back in time or skipping frames.
michael@0 429 MOZ_ASSERT(framenum == mFrameCount || framenum == (mFrameCount - 1));
michael@0 430
michael@0 431 mNewFrameData = NewFrameData(framenum, x_offset, y_offset, width, height, format, palette_depth);
michael@0 432 mNeedsNewFrame = true;
michael@0 433 }
michael@0 434
michael@0 435 void
michael@0 436 Decoder::MarkFrameDirty()
michael@0 437 {
michael@0 438 MOZ_ASSERT(NS_IsMainThread());
michael@0 439
michael@0 440 if (mCurrentFrame) {
michael@0 441 mCurrentFrame->ApplyDirtToSurfaces();
michael@0 442 }
michael@0 443 }
michael@0 444
michael@0 445 } // namespace image
michael@0 446 } // namespace mozilla

mercurial