1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/image/src/Decoder.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,446 @@ 1.4 + 1.5 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.6 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.9 + 1.10 +#include "Decoder.h" 1.11 +#include "nsIConsoleService.h" 1.12 +#include "nsIScriptError.h" 1.13 +#include "GeckoProfiler.h" 1.14 +#include "nsServiceManagerUtils.h" 1.15 +#include "nsComponentManagerUtils.h" 1.16 + 1.17 +namespace mozilla { 1.18 +namespace image { 1.19 + 1.20 +Decoder::Decoder(RasterImage &aImage) 1.21 + : mImage(aImage) 1.22 + , mCurrentFrame(nullptr) 1.23 + , mImageData(nullptr) 1.24 + , mColormap(nullptr) 1.25 + , mDecodeFlags(0) 1.26 + , mDecodeDone(false) 1.27 + , mDataError(false) 1.28 + , mFrameCount(0) 1.29 + , mFailCode(NS_OK) 1.30 + , mNeedsNewFrame(false) 1.31 + , mInitialized(false) 1.32 + , mSizeDecode(false) 1.33 + , mInFrame(false) 1.34 + , mIsAnimated(false) 1.35 +{ 1.36 +} 1.37 + 1.38 +Decoder::~Decoder() 1.39 +{ 1.40 + mInitialized = false; 1.41 +} 1.42 + 1.43 +/* 1.44 + * Common implementation of the decoder interface. 1.45 + */ 1.46 + 1.47 +void 1.48 +Decoder::Init() 1.49 +{ 1.50 + // No re-initializing 1.51 + NS_ABORT_IF_FALSE(!mInitialized, "Can't re-initialize a decoder!"); 1.52 + NS_ABORT_IF_FALSE(mObserver, "Need an observer!"); 1.53 + 1.54 + // Fire OnStartDecode at init time to support bug 512435. 1.55 + if (!IsSizeDecode()) 1.56 + mObserver->OnStartDecode(); 1.57 + 1.58 + // Implementation-specific initialization 1.59 + InitInternal(); 1.60 + 1.61 + mInitialized = true; 1.62 +} 1.63 + 1.64 +// Initializes a decoder whose image and observer is already being used by a 1.65 +// parent decoder 1.66 +void 1.67 +Decoder::InitSharedDecoder(uint8_t* imageData, uint32_t imageDataLength, 1.68 + uint32_t* colormap, uint32_t colormapSize, 1.69 + imgFrame* currentFrame) 1.70 +{ 1.71 + // No re-initializing 1.72 + NS_ABORT_IF_FALSE(!mInitialized, "Can't re-initialize a decoder!"); 1.73 + NS_ABORT_IF_FALSE(mObserver, "Need an observer!"); 1.74 + 1.75 + mImageData = imageData; 1.76 + mImageDataLength = imageDataLength; 1.77 + mColormap = colormap; 1.78 + mColormapSize = colormapSize; 1.79 + mCurrentFrame = currentFrame; 1.80 + // We have all the frame data, so we've started the frame. 1.81 + if (!IsSizeDecode()) { 1.82 + PostFrameStart(); 1.83 + } 1.84 + 1.85 + // Implementation-specific initialization 1.86 + InitInternal(); 1.87 + mInitialized = true; 1.88 +} 1.89 + 1.90 +void 1.91 +Decoder::Write(const char* aBuffer, uint32_t aCount, DecodeStrategy aStrategy) 1.92 +{ 1.93 + PROFILER_LABEL("ImageDecoder", "Write"); 1.94 + MOZ_ASSERT(NS_IsMainThread() || aStrategy == DECODE_ASYNC); 1.95 + 1.96 + // We're strict about decoder errors 1.97 + NS_ABORT_IF_FALSE(!HasDecoderError(), 1.98 + "Not allowed to make more decoder calls after error!"); 1.99 + 1.100 + // If a data error occured, just ignore future data 1.101 + if (HasDataError()) 1.102 + return; 1.103 + 1.104 + if (IsSizeDecode() && HasSize()) { 1.105 + // More data came in since we found the size. We have nothing to do here. 1.106 + return; 1.107 + } 1.108 + 1.109 + // Pass the data along to the implementation 1.110 + WriteInternal(aBuffer, aCount, aStrategy); 1.111 + 1.112 + // If we're a synchronous decoder and we need a new frame to proceed, let's 1.113 + // create one and call it again. 1.114 + while (aStrategy == DECODE_SYNC && NeedsNewFrame() && !HasDataError()) { 1.115 + nsresult rv = AllocateFrame(); 1.116 + 1.117 + if (NS_SUCCEEDED(rv)) { 1.118 + // Tell the decoder to use the data it saved when it asked for a new frame. 1.119 + WriteInternal(nullptr, 0, aStrategy); 1.120 + } 1.121 + } 1.122 +} 1.123 + 1.124 +void 1.125 +Decoder::Finish(RasterImage::eShutdownIntent aShutdownIntent) 1.126 +{ 1.127 + MOZ_ASSERT(NS_IsMainThread()); 1.128 + 1.129 + // Implementation-specific finalization 1.130 + if (!HasError()) 1.131 + FinishInternal(); 1.132 + 1.133 + // If the implementation left us mid-frame, finish that up. 1.134 + if (mInFrame && !HasError()) 1.135 + PostFrameStop(); 1.136 + 1.137 + // If PostDecodeDone() has not been called, we need to sent teardown 1.138 + // notifications. 1.139 + if (!IsSizeDecode() && !mDecodeDone) { 1.140 + 1.141 + // Log data errors to the error console 1.142 + nsCOMPtr<nsIConsoleService> consoleService = 1.143 + do_GetService(NS_CONSOLESERVICE_CONTRACTID); 1.144 + nsCOMPtr<nsIScriptError> errorObject = 1.145 + do_CreateInstance(NS_SCRIPTERROR_CONTRACTID); 1.146 + 1.147 + if (consoleService && errorObject && !HasDecoderError()) { 1.148 + nsAutoString msg(NS_LITERAL_STRING("Image corrupt or truncated: ") + 1.149 + NS_ConvertUTF8toUTF16(mImage.GetURIString())); 1.150 + 1.151 + if (NS_SUCCEEDED(errorObject->InitWithWindowID( 1.152 + msg, 1.153 + NS_ConvertUTF8toUTF16(mImage.GetURIString()), 1.154 + EmptyString(), 0, 0, nsIScriptError::errorFlag, 1.155 + "Image", mImage.InnerWindowID() 1.156 + ))) { 1.157 + consoleService->LogMessage(errorObject); 1.158 + } 1.159 + } 1.160 + 1.161 + bool usable = !HasDecoderError(); 1.162 + if (aShutdownIntent != RasterImage::eShutdownIntent_NotNeeded && !HasDecoderError()) { 1.163 + // If we only have a data error, we're usable if we have at least one complete frame. 1.164 + if (GetCompleteFrameCount() == 0) { 1.165 + usable = false; 1.166 + } 1.167 + } 1.168 + 1.169 + // If we're usable, do exactly what we should have when the decoder 1.170 + // completed. 1.171 + if (usable) { 1.172 + if (mInFrame) { 1.173 + PostFrameStop(); 1.174 + } 1.175 + PostDecodeDone(); 1.176 + } else { 1.177 + if (mObserver) { 1.178 + mObserver->OnStopDecode(NS_ERROR_FAILURE); 1.179 + } 1.180 + } 1.181 + } 1.182 + 1.183 + // Set image metadata before calling DecodingComplete, because DecodingComplete calls Optimize(). 1.184 + mImageMetadata.SetOnImage(&mImage); 1.185 + 1.186 + if (mDecodeDone) { 1.187 + mImage.DecodingComplete(); 1.188 + } 1.189 +} 1.190 + 1.191 +void 1.192 +Decoder::FinishSharedDecoder() 1.193 +{ 1.194 + MOZ_ASSERT(NS_IsMainThread()); 1.195 + 1.196 + if (!HasError()) { 1.197 + FinishInternal(); 1.198 + } 1.199 +} 1.200 + 1.201 +nsresult 1.202 +Decoder::AllocateFrame() 1.203 +{ 1.204 + MOZ_ASSERT(mNeedsNewFrame); 1.205 + MOZ_ASSERT(NS_IsMainThread()); 1.206 + 1.207 + MarkFrameDirty(); 1.208 + 1.209 + nsresult rv; 1.210 + imgFrame* frame = nullptr; 1.211 + if (mNewFrameData.mPaletteDepth) { 1.212 + rv = mImage.EnsureFrame(mNewFrameData.mFrameNum, mNewFrameData.mOffsetX, 1.213 + mNewFrameData.mOffsetY, mNewFrameData.mWidth, 1.214 + mNewFrameData.mHeight, mNewFrameData.mFormat, 1.215 + mNewFrameData.mPaletteDepth, 1.216 + &mImageData, &mImageDataLength, 1.217 + &mColormap, &mColormapSize, &frame); 1.218 + } else { 1.219 + rv = mImage.EnsureFrame(mNewFrameData.mFrameNum, mNewFrameData.mOffsetX, 1.220 + mNewFrameData.mOffsetY, mNewFrameData.mWidth, 1.221 + mNewFrameData.mHeight, mNewFrameData.mFormat, 1.222 + &mImageData, &mImageDataLength, &frame); 1.223 + } 1.224 + 1.225 + if (NS_SUCCEEDED(rv)) { 1.226 + mCurrentFrame = frame; 1.227 + } else { 1.228 + mCurrentFrame = nullptr; 1.229 + } 1.230 + 1.231 + // Notify if appropriate 1.232 + if (NS_SUCCEEDED(rv) && mNewFrameData.mFrameNum == mFrameCount) { 1.233 + PostFrameStart(); 1.234 + } else if (NS_FAILED(rv)) { 1.235 + PostDataError(); 1.236 + } 1.237 + 1.238 + // Mark ourselves as not needing another frame before talking to anyone else 1.239 + // so they can tell us if they need yet another. 1.240 + mNeedsNewFrame = false; 1.241 + 1.242 + return rv; 1.243 +} 1.244 + 1.245 +void 1.246 +Decoder::FlushInvalidations() 1.247 +{ 1.248 + NS_ABORT_IF_FALSE(!HasDecoderError(), 1.249 + "Not allowed to make more decoder calls after error!"); 1.250 + 1.251 + // If we've got an empty invalidation rect, we have nothing to do 1.252 + if (mInvalidRect.IsEmpty()) 1.253 + return; 1.254 + 1.255 + if (mObserver) { 1.256 +#ifdef XP_MACOSX 1.257 + // Bug 703231 1.258 + // Because of high quality down sampling on mac we show scan lines while decoding. 1.259 + // Bypass this problem by redrawing the border. 1.260 + if (mImageMetadata.HasSize()) { 1.261 + nsIntRect mImageBound(0, 0, mImageMetadata.GetWidth(), mImageMetadata.GetHeight()); 1.262 + 1.263 + mInvalidRect.Inflate(1); 1.264 + mInvalidRect = mInvalidRect.Intersect(mImageBound); 1.265 + } 1.266 +#endif 1.267 + mObserver->FrameChanged(&mInvalidRect); 1.268 + } 1.269 + 1.270 + // Clear the invalidation rectangle 1.271 + mInvalidRect.SetEmpty(); 1.272 +} 1.273 + 1.274 +void 1.275 +Decoder::SetSizeOnImage() 1.276 +{ 1.277 + MOZ_ASSERT(mImageMetadata.HasSize(), "Should have size"); 1.278 + MOZ_ASSERT(mImageMetadata.HasOrientation(), "Should have orientation"); 1.279 + 1.280 + mImage.SetSize(mImageMetadata.GetWidth(), 1.281 + mImageMetadata.GetHeight(), 1.282 + mImageMetadata.GetOrientation()); 1.283 +} 1.284 + 1.285 +/* 1.286 + * Hook stubs. Override these as necessary in decoder implementations. 1.287 + */ 1.288 + 1.289 +void Decoder::InitInternal() { } 1.290 +void Decoder::WriteInternal(const char* aBuffer, uint32_t aCount, DecodeStrategy aStrategy) { } 1.291 +void Decoder::FinishInternal() { } 1.292 + 1.293 +/* 1.294 + * Progress Notifications 1.295 + */ 1.296 + 1.297 +void 1.298 +Decoder::PostSize(int32_t aWidth, 1.299 + int32_t aHeight, 1.300 + Orientation aOrientation /* = Orientation()*/) 1.301 +{ 1.302 + // Validate 1.303 + NS_ABORT_IF_FALSE(aWidth >= 0, "Width can't be negative!"); 1.304 + NS_ABORT_IF_FALSE(aHeight >= 0, "Height can't be negative!"); 1.305 + 1.306 + // Tell the image 1.307 + mImageMetadata.SetSize(aWidth, aHeight, aOrientation); 1.308 + 1.309 + // Notify the observer 1.310 + if (mObserver) 1.311 + mObserver->OnStartContainer(); 1.312 +} 1.313 + 1.314 +void 1.315 +Decoder::PostFrameStart() 1.316 +{ 1.317 + // We shouldn't already be mid-frame 1.318 + NS_ABORT_IF_FALSE(!mInFrame, "Starting new frame but not done with old one!"); 1.319 + 1.320 + // We should take care of any invalidation region when wrapping up the 1.321 + // previous frame 1.322 + NS_ABORT_IF_FALSE(mInvalidRect.IsEmpty(), 1.323 + "Start image frame with non-empty invalidation region!"); 1.324 + 1.325 + // Update our state to reflect the new frame 1.326 + mFrameCount++; 1.327 + mInFrame = true; 1.328 + 1.329 + // Decoder implementations should only call this method if they successfully 1.330 + // appended the frame to the image. So mFrameCount should always match that 1.331 + // reported by the Image. 1.332 + NS_ABORT_IF_FALSE(mFrameCount == mImage.GetNumFrames(), 1.333 + "Decoder frame count doesn't match image's!"); 1.334 + 1.335 + // Fire notifications 1.336 + if (mObserver) { 1.337 + mObserver->OnStartFrame(); 1.338 + } 1.339 +} 1.340 + 1.341 +void 1.342 +Decoder::PostFrameStop(FrameBlender::FrameAlpha aFrameAlpha /* = FrameBlender::kFrameHasAlpha */, 1.343 + FrameBlender::FrameDisposalMethod aDisposalMethod /* = FrameBlender::kDisposeKeep */, 1.344 + int32_t aTimeout /* = 0 */, 1.345 + FrameBlender::FrameBlendMethod aBlendMethod /* = FrameBlender::kBlendOver */) 1.346 +{ 1.347 + // We should be mid-frame 1.348 + NS_ABORT_IF_FALSE(mInFrame, "Stopping frame when we didn't start one!"); 1.349 + NS_ABORT_IF_FALSE(mCurrentFrame, "Stopping frame when we don't have one!"); 1.350 + 1.351 + // Update our state 1.352 + mInFrame = false; 1.353 + 1.354 + if (aFrameAlpha == FrameBlender::kFrameOpaque) { 1.355 + mCurrentFrame->SetHasNoAlpha(); 1.356 + } 1.357 + 1.358 + mCurrentFrame->SetFrameDisposalMethod(aDisposalMethod); 1.359 + mCurrentFrame->SetRawTimeout(aTimeout); 1.360 + mCurrentFrame->SetBlendMethod(aBlendMethod); 1.361 + mCurrentFrame->ImageUpdated(mCurrentFrame->GetRect()); 1.362 + 1.363 + // Flush any invalidations before we finish the frame 1.364 + FlushInvalidations(); 1.365 + 1.366 + // Fire notifications 1.367 + if (mObserver) { 1.368 + mObserver->OnStopFrame(); 1.369 + if (mFrameCount > 1 && !mIsAnimated) { 1.370 + mIsAnimated = true; 1.371 + mObserver->OnImageIsAnimated(); 1.372 + } 1.373 + } 1.374 +} 1.375 + 1.376 +void 1.377 +Decoder::PostInvalidation(nsIntRect& aRect) 1.378 +{ 1.379 + // We should be mid-frame 1.380 + NS_ABORT_IF_FALSE(mInFrame, "Can't invalidate when not mid-frame!"); 1.381 + NS_ABORT_IF_FALSE(mCurrentFrame, "Can't invalidate when not mid-frame!"); 1.382 + 1.383 + // Account for the new region 1.384 + mInvalidRect.UnionRect(mInvalidRect, aRect); 1.385 + mCurrentFrame->ImageUpdated(aRect); 1.386 +} 1.387 + 1.388 +void 1.389 +Decoder::PostDecodeDone(int32_t aLoopCount /* = 0 */) 1.390 +{ 1.391 + NS_ABORT_IF_FALSE(!IsSizeDecode(), "Can't be done with decoding with size decode!"); 1.392 + NS_ABORT_IF_FALSE(!mInFrame, "Can't be done decoding if we're mid-frame!"); 1.393 + NS_ABORT_IF_FALSE(!mDecodeDone, "Decode already done!"); 1.394 + mDecodeDone = true; 1.395 + 1.396 + mImageMetadata.SetLoopCount(aLoopCount); 1.397 + mImageMetadata.SetIsNonPremultiplied(GetDecodeFlags() & DECODER_NO_PREMULTIPLY_ALPHA); 1.398 + 1.399 + if (mObserver) { 1.400 + mObserver->OnStopDecode(NS_OK); 1.401 + } 1.402 +} 1.403 + 1.404 +void 1.405 +Decoder::PostDataError() 1.406 +{ 1.407 + mDataError = true; 1.408 +} 1.409 + 1.410 +void 1.411 +Decoder::PostDecoderError(nsresult aFailureCode) 1.412 +{ 1.413 + NS_ABORT_IF_FALSE(NS_FAILED(aFailureCode), "Not a failure code!"); 1.414 + 1.415 + mFailCode = aFailureCode; 1.416 + 1.417 + // XXXbholley - we should report the image URI here, but imgContainer 1.418 + // needs to know its URI first 1.419 + NS_WARNING("Image decoding error - This is probably a bug!"); 1.420 +} 1.421 + 1.422 +void 1.423 +Decoder::NeedNewFrame(uint32_t framenum, uint32_t x_offset, uint32_t y_offset, 1.424 + uint32_t width, uint32_t height, 1.425 + gfxImageFormat format, 1.426 + uint8_t palette_depth /* = 0 */) 1.427 +{ 1.428 + // Decoders should never call NeedNewFrame without yielding back to Write(). 1.429 + MOZ_ASSERT(!mNeedsNewFrame); 1.430 + 1.431 + // We don't want images going back in time or skipping frames. 1.432 + MOZ_ASSERT(framenum == mFrameCount || framenum == (mFrameCount - 1)); 1.433 + 1.434 + mNewFrameData = NewFrameData(framenum, x_offset, y_offset, width, height, format, palette_depth); 1.435 + mNeedsNewFrame = true; 1.436 +} 1.437 + 1.438 +void 1.439 +Decoder::MarkFrameDirty() 1.440 +{ 1.441 + MOZ_ASSERT(NS_IsMainThread()); 1.442 + 1.443 + if (mCurrentFrame) { 1.444 + mCurrentFrame->ApplyDirtToSurfaces(); 1.445 + } 1.446 +} 1.447 + 1.448 +} // namespace image 1.449 +} // namespace mozilla