1 :
2 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
3 : /* ***** BEGIN LICENSE BLOCK *****
4 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 : *
6 : * The contents of this file are subject to the Mozilla Public License Version
7 : * 1.1 (the "License"); you may not use this file except in compliance with
8 : * the License. You may obtain a copy of the License at
9 : * http://www.mozilla.org/MPL/
10 : *
11 : * Software distributed under the License is distributed on an "AS IS" basis,
12 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13 : * for the specific language governing rights and limitations under the
14 : * License.
15 : *
16 : * The Original Code is mozilla.org code.
17 : *
18 : * The Initial Developer of the Original Code is
19 : * the Mozilla Foundation.
20 : * Portions created by the Initial Developer are Copyright (C) 2010.
21 : * the Initial Developer. All Rights Reserved.
22 : *
23 : * Contributor(s):
24 : *
25 : * Alternatively, the contents of this file may be used under the terms of
26 : * either the GNU General Public License Version 2 or later (the "GPL"), or
27 : * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
28 : * in which case the provisions of the GPL or the LGPL are applicable instead
29 : * of those above. If you wish to allow use of your version of this file only
30 : * under the terms of either the GPL or the LGPL, and not to allow others to
31 : * use your version of this file under the terms of the MPL, indicate your
32 : * decision by deleting the provisions above and replace them with the notice
33 : * and other provisions required by the GPL or the LGPL. If you do not delete
34 : * the provisions above, a recipient may use your version of this file under
35 : * the terms of any one of the MPL, the GPL or the LGPL.
36 : *
37 : * ***** END LICENSE BLOCK ***** */
38 :
39 : #include "Decoder.h"
40 : #include "nsIServiceManager.h"
41 : #include "nsIConsoleService.h"
42 : #include "nsIScriptError.h"
43 :
44 : namespace mozilla {
45 : namespace image {
46 :
47 24 : Decoder::Decoder(RasterImage &aImage, imgIDecoderObserver* aObserver)
48 : : mImage(aImage)
49 : , mObserver(aObserver)
50 : , mDecodeFlags(0)
51 : , mDecodeDone(false)
52 : , mDataError(false)
53 : , mFrameCount(0)
54 : , mFailCode(NS_OK)
55 : , mInitialized(false)
56 : , mSizeDecode(false)
57 : , mInFrame(false)
58 24 : , mIsAnimated(false)
59 : {
60 24 : }
61 :
62 48 : Decoder::~Decoder()
63 : {
64 24 : NS_WARN_IF_FALSE(!mInFrame, "Shutting down decoder mid-frame!");
65 24 : mInitialized = false;
66 48 : }
67 :
68 : /*
69 : * Common implementation of the decoder interface.
70 : */
71 :
72 : void
73 20 : Decoder::Init()
74 : {
75 : // No re-initializing
76 20 : NS_ABORT_IF_FALSE(!mInitialized, "Can't re-initialize a decoder!");
77 :
78 : // Fire OnStartDecode at init time to support bug 512435
79 20 : if (!IsSizeDecode() && mObserver)
80 2 : mObserver->OnStartDecode(nsnull);
81 :
82 : // Implementation-specific initialization
83 20 : InitInternal();
84 20 : mInitialized = true;
85 20 : }
86 :
87 : // Initializes a decoder whose aImage and aObserver is already being used by a
88 : // parent decoder
89 : void
90 4 : Decoder::InitSharedDecoder()
91 : {
92 : // No re-initializing
93 4 : NS_ABORT_IF_FALSE(!mInitialized, "Can't re-initialize a decoder!");
94 :
95 : // Implementation-specific initialization
96 4 : InitInternal();
97 4 : mInitialized = true;
98 4 : }
99 :
100 : void
101 73 : Decoder::Write(const char* aBuffer, PRUint32 aCount)
102 : {
103 : // We're strict about decoder errors
104 73 : NS_ABORT_IF_FALSE(!HasDecoderError(),
105 : "Not allowed to make more decoder calls after error!");
106 :
107 : // If a data error occured, just ignore future data
108 73 : if (HasDataError())
109 5 : return;
110 :
111 : // Pass the data along to the implementation
112 68 : WriteInternal(aBuffer, aCount);
113 : }
114 :
115 : void
116 20 : Decoder::Finish()
117 : {
118 : // Implementation-specific finalization
119 20 : if (!HasError())
120 19 : FinishInternal();
121 :
122 : // If the implementation left us mid-frame, finish that up.
123 20 : if (mInFrame && !HasDecoderError())
124 0 : PostFrameStop();
125 :
126 : // If PostDecodeDone() has not been called, we need to sent teardown
127 : // notifications.
128 20 : if (!IsSizeDecode() && !mDecodeDone) {
129 :
130 : // Log data errors to the error console
131 : nsCOMPtr<nsIConsoleService> consoleService =
132 2 : do_GetService(NS_CONSOLESERVICE_CONTRACTID);
133 : nsCOMPtr<nsIScriptError> errorObject =
134 2 : do_CreateInstance(NS_SCRIPTERROR_CONTRACTID);
135 :
136 1 : if (consoleService && errorObject && !HasDecoderError()) {
137 1 : nsAutoString msg(NS_LITERAL_STRING("Image corrupt or truncated: ") +
138 3 : NS_ConvertASCIItoUTF16(mImage.GetURIString()));
139 :
140 1 : if (NS_SUCCEEDED(errorObject->InitWithWindowID(
141 : msg.get(),
142 : NS_ConvertUTF8toUTF16(mImage.GetURIString()).get(),
143 : nsnull, 0, 0, nsIScriptError::errorFlag,
144 : "Image", mImage.InnerWindowID()
145 : ))) {
146 1 : consoleService->LogMessage(errorObject);
147 : }
148 : }
149 :
150 : // If we only have a data error, see if things are worth salvaging
151 1 : bool salvage = !HasDecoderError() && mImage.GetNumFrames();
152 :
153 : // If we're salvaging, say we finished decoding
154 1 : if (salvage)
155 0 : mImage.DecodingComplete();
156 :
157 : // Fire teardown notifications
158 1 : if (mObserver) {
159 0 : mObserver->OnStopContainer(nsnull, &mImage);
160 0 : mObserver->OnStopDecode(nsnull, salvage ? NS_OK : NS_ERROR_FAILURE, nsnull);
161 : }
162 : }
163 20 : }
164 :
165 : void
166 4 : Decoder::FinishSharedDecoder()
167 : {
168 4 : if (!HasError()) {
169 4 : FinishInternal();
170 : }
171 4 : }
172 :
173 : void
174 72 : Decoder::FlushInvalidations()
175 : {
176 72 : NS_ABORT_IF_FALSE(!HasDecoderError(),
177 : "Not allowed to make more decoder calls after error!");
178 :
179 : // If we've got an empty invalidation rect, we have nothing to do
180 72 : if (mInvalidRect.IsEmpty())
181 40 : return;
182 :
183 : // Tell the image that it's been updated
184 32 : mImage.FrameUpdated(mFrameCount - 1, mInvalidRect);
185 :
186 : // Fire OnDataAvailable
187 32 : if (mObserver) {
188 : #ifdef XP_MACOSX
189 : // Bug 703231
190 : // Because of high quality down sampling on mac we show scan lines while decoding.
191 : // Bypass this problem by redrawing the border.
192 : PRInt32 width;
193 : PRInt32 height;
194 :
195 : mImage.GetWidth(&width);
196 : mImage.GetHeight(&height);
197 : nsIntRect mImageBound(0, 0, width, height);
198 :
199 : mInvalidRect.Inflate(1);
200 : mInvalidRect = mInvalidRect.Intersect(mImageBound);
201 : #endif
202 2 : bool isCurrentFrame = mImage.GetCurrentFrameIndex() == (mFrameCount - 1);
203 2 : mObserver->OnDataAvailable(nsnull, isCurrentFrame, &mInvalidRect);
204 : }
205 :
206 : // Clear the invalidation rectangle
207 32 : mInvalidRect.SetEmpty();
208 : }
209 :
210 : /*
211 : * Hook stubs. Override these as necessary in decoder implementations.
212 : */
213 :
214 13 : void Decoder::InitInternal() { }
215 0 : void Decoder::WriteInternal(const char* aBuffer, PRUint32 aCount) { }
216 6 : void Decoder::FinishInternal() { }
217 :
218 : /*
219 : * Progress Notifications
220 : */
221 :
222 : void
223 19 : Decoder::PostSize(PRInt32 aWidth, PRInt32 aHeight)
224 : {
225 : // Validate
226 19 : NS_ABORT_IF_FALSE(aWidth >= 0, "Width can't be negative!");
227 19 : NS_ABORT_IF_FALSE(aHeight >= 0, "Height can't be negative!");
228 :
229 : // Tell the image
230 19 : mImage.SetSize(aWidth, aHeight);
231 :
232 : // Notify the observer
233 19 : if (mObserver)
234 6 : mObserver->OnStartContainer(nsnull, &mImage);
235 19 : }
236 :
237 : void
238 17 : Decoder::PostFrameStart()
239 : {
240 : // We shouldn't already be mid-frame
241 17 : NS_ABORT_IF_FALSE(!mInFrame, "Starting new frame but not done with old one!");
242 :
243 : // We should take care of any invalidation region when wrapping up the
244 : // previous frame
245 17 : NS_ABORT_IF_FALSE(mInvalidRect.IsEmpty(),
246 : "Start image frame with non-empty invalidation region!");
247 :
248 : // Update our state to reflect the new frame
249 17 : mFrameCount++;
250 17 : mInFrame = true;
251 :
252 : // Decoder implementations should only call this method if they successfully
253 : // appended the frame to the image. So mFrameCount should always match that
254 : // reported by the Image.
255 17 : NS_ABORT_IF_FALSE(mFrameCount == mImage.GetNumFrames(),
256 : "Decoder frame count doesn't match image's!");
257 :
258 : // Fire notification
259 17 : if (mObserver)
260 4 : mObserver->OnStartFrame(nsnull, mFrameCount - 1); // frame # is zero-indexed
261 17 : }
262 :
263 : void
264 17 : Decoder::PostFrameStop()
265 : {
266 : // We should be mid-frame
267 17 : NS_ABORT_IF_FALSE(mInFrame, "Stopping frame when we didn't start one!");
268 :
269 : // Update our state
270 17 : mInFrame = false;
271 :
272 : // Flush any invalidations before we finish the frame
273 17 : FlushInvalidations();
274 :
275 : // Fire notifications
276 17 : if (mObserver) {
277 4 : mObserver->OnStopFrame(nsnull, mFrameCount - 1); // frame # is zero-indexed
278 4 : if (mFrameCount > 1 && !mIsAnimated) {
279 1 : mIsAnimated = true;
280 1 : mObserver->OnImageIsAnimated(nsnull);
281 : }
282 : }
283 17 : }
284 :
285 : void
286 211 : Decoder::PostInvalidation(nsIntRect& aRect)
287 : {
288 : // We should be mid-frame
289 211 : NS_ABORT_IF_FALSE(mInFrame, "Can't invalidate when not mid-frame!");
290 :
291 : // Account for the new region
292 211 : mInvalidRect.UnionRect(mInvalidRect, aRect);
293 211 : }
294 :
295 : void
296 15 : Decoder::PostDecodeDone()
297 : {
298 15 : NS_ABORT_IF_FALSE(!IsSizeDecode(), "Can't be done with decoding with size decode!");
299 15 : NS_ABORT_IF_FALSE(!mInFrame, "Can't be done decoding if we're mid-frame!");
300 15 : NS_ABORT_IF_FALSE(!mDecodeDone, "Decode already done!");
301 15 : mDecodeDone = true;
302 :
303 : // Notify
304 15 : mImage.DecodingComplete();
305 15 : if (mObserver) {
306 2 : mObserver->OnStopContainer(nsnull, &mImage);
307 2 : mObserver->OnStopDecode(nsnull, NS_OK, nsnull);
308 : }
309 15 : }
310 :
311 : void
312 1 : Decoder::PostDataError()
313 : {
314 1 : mDataError = true;
315 1 : }
316 :
317 : void
318 0 : Decoder::PostDecoderError(nsresult aFailureCode)
319 : {
320 0 : NS_ABORT_IF_FALSE(NS_FAILED(aFailureCode), "Not a failure code!");
321 :
322 0 : mFailCode = aFailureCode;
323 :
324 : // XXXbholley - we should report the image URI here, but imgContainer
325 : // needs to know its URI first
326 0 : NS_WARNING("Image decoding error - This is probably a bug!");
327 0 : }
328 :
329 : } // namespace image
330 : } // namespace mozilla
|