WebCore:
authorrjw <rjw@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 7 Dec 2004 23:06:24 +0000 (23:06 +0000)
committerrjw <rjw@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 7 Dec 2004 23:06:24 +0000 (23:06 +0000)
Support threaded image decoding on machines w/ > 2 CPUs.

        Reviewed by Maciej and Chris.

        * khtml/misc/loader.cpp:
        (CachedImageCallback::notifyUpdate):
        (CachedImageCallback::notifyFinished):
        (CachedImageCallback::notifyDecodingError):
        (CachedImageCallback::handleError):
        (CachedImageCallback::clear):
        (CachedImage::CachedImage):
        (CachedImage::clear):
        (CachedImage::data):
        (CachedImage::checkNotify):
        (Loader::servePendingRequests):
        (Loader::slotFinished):
        (Loader::numRequests):
        (Loader::cancelRequests):
        (Loader::removeBackgroundDecodingRequest):
        * khtml/misc/loader.h:
        (khtml::CachedImageCallback::CachedImageCallback):
        (khtml::CachedImageCallback::ref):
        (khtml::CachedImageCallback::deref):
        (khtml::CachedImage::decoderCallback):
        * khtml/rendering/render_object.cpp:
        (RenderObject::setPixmap):
        * kwq/KWQPixmap.h:
        * kwq/KWQPixmap.mm:
        (-[WebImageCallback initWithCallback:khtml::]):
        (-[WebImageCallback _commonTermination]):
        (-[WebImageCallback dealloc]):
        (-[WebImageCallback finalize]):
        (-[WebImageCallback notify]):
        (-[WebImageCallback setImageSourceStatus:]):
        (-[WebImageCallback status]):
        (QPixmap::shouldUseThreadedDecoding):
        (QPixmap::receivedData):
        * kwq/WebCoreImageRenderer.h:
        * kwq/WebCoreImageRendererFactory.h:
        * kwq/WebCoreImageRendererFactory.m:
        (+[WebCoreImageRendererFactory shouldUseThreadedDecoding]):
        (+[WebCoreImageRendererFactory setShouldUseThreadedDecoding:]):

WebKit:
Support threaded image decoding on machines w/ > 2 CPUs.

        Reviewed by Maciej and Chris.

        * Misc.subproj/WebKitSystemBits.h:
        * Misc.subproj/WebKitSystemBits.m:
        (WebSystemMainMemory):
        (WebNumberOfCPUs):
        * WebCoreSupport.subproj/WebImageData.h:
        * WebCoreSupport.subproj/WebImageData.m:
        (+[WebImageData initialize]):
        (-[WebImageData init]):
        (-[WebImageData _commonTermination]):
        (-[WebImageData dealloc]):
        (-[WebImageData _invalidateImages]):
        (-[WebImageData _imageSourceOptions]):
        (-[WebImageData imageAtIndex:]):
        (-[WebImageData propertiesAtIndex:]):
        (-[WebImageData _createImages]):
        (-[WebImageData decodeData:isComplete:callback:]):
        (-[WebImageData incrementalLoadWithBytes:length:complete:callback:]):
        (drawPattern):
        (-[WebImageData tileInRect:fromPoint:context:]):
        (-[WebImageData isNull]):
        (-[WebImageData size]):
        (-[WebImageData _frameDurationAt:]):
        (-[WebImageData _frameDuration]):
        (+[WebImageData stopAnimationsInView:]):
        (-[WebImageData addAnimatingRenderer:inView:]):
        (-[WebImageData removeAnimatingRenderer:]):
        * WebCoreSupport.subproj/WebImageDecodeItem.h: Added.
        * WebCoreSupport.subproj/WebImageDecodeItem.m: Added.
        (+[WebImageDecodeItem decodeItemWithImage:data:isComplete:callback:]):
        (-[WebImageDecodeItem initWithImage:data:isComplete:callback:]):
        (-[WebImageDecodeItem finalize]):
        (-[WebImageDecodeItem dealloc]):
        * WebCoreSupport.subproj/WebImageDecoder.h: Added.
        * WebCoreSupport.subproj/WebImageDecoder.m: Added.
        (decoderNotifications):
        (+[WebImageDecoder initialize]):
        (+[WebImageDecoder notifyMainThread]):
        (+[WebImageDecoder sharedDecoder]):
        (+[WebImageDecoder performDecodeWithImage:data:isComplete:callback:]):
        (+[WebImageDecoder imageDecodesPending]):
        (+[WebImageDecoder decodeComplete:status:]):
        (-[WebImageDecoder init]):
        (-[WebImageDecoder dealloc]):
        (-[WebImageDecoder finalize]):
        (-[WebImageDecoder removeItem]):
        (-[WebImageDecoder addItem:]):
        (-[WebImageDecoder decodeItem:]):
        (decoderThread):
        (startDecoderThread):
        * WebCoreSupport.subproj/WebImageRenderer.m:
        (-[WebImageRenderer initWithData:MIMEType:]):
        (-[WebImageRenderer initWithContentsOfFile:]):
        (-[WebImageRenderer incrementalLoadWithBytes:length:complete:callback:]):
        (-[WebInternalImage incrementalLoadWithBytes:length:complete:callback:]):
        * WebKit.pbproj/project.pbxproj:
        * WebView.subproj/WebImageRepresentation.m:
        (-[WebImageRepresentation receivedData:withDataSource:]):
        (-[WebImageRepresentation receivedError:withDataSource:]):
        (-[WebImageRepresentation finishedLoadingWithDataSource:]):

WebBrowser:
Added a menu item to enable/disable threaded image decoding.

        Reviewed by Maciej and Chris.

        * Debug/DebugUtilities.m:
        (-[DebugUtilities createDebugMenu]):
        (-[BrowserDocument toggleUseATSUForAllTextDrawing:]):
        (-[BrowserDocument toggleUseThreadedImageDecoding:]):
        (-[BrowserDocument validate_toggleUseThreadedImageDecoding:]):

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@8153 268f45cc-cd09-0410-ab3c-d52691b4dbfc

21 files changed:
WebCore/ChangeLog-2005-08-23
WebCore/khtml/misc/loader.cpp
WebCore/khtml/misc/loader.h
WebCore/khtml/rendering/render_object.cpp
WebCore/kwq/KWQPixmap.h
WebCore/kwq/KWQPixmap.mm
WebCore/kwq/WebCoreImageRenderer.h
WebCore/kwq/WebCoreImageRendererFactory.h
WebCore/kwq/WebCoreImageRendererFactory.m
WebKit/ChangeLog
WebKit/Misc.subproj/WebKitSystemBits.h
WebKit/Misc.subproj/WebKitSystemBits.m
WebKit/WebCoreSupport.subproj/WebImageData.h
WebKit/WebCoreSupport.subproj/WebImageData.m
WebKit/WebCoreSupport.subproj/WebImageDecodeItem.h [new file with mode: 0644]
WebKit/WebCoreSupport.subproj/WebImageDecodeItem.m [new file with mode: 0644]
WebKit/WebCoreSupport.subproj/WebImageDecoder.h [new file with mode: 0644]
WebKit/WebCoreSupport.subproj/WebImageDecoder.m [new file with mode: 0644]
WebKit/WebCoreSupport.subproj/WebImageRenderer.m
WebKit/WebKit.pbproj/project.pbxproj
WebKit/WebView.subproj/WebImageRepresentation.m

index c0e5ca9ab93085da4c4478d348da88f131526bae..5b57416c86e79d0ebf7053a4c991b72e9f4dd43f 100644 (file)
@@ -1,3 +1,48 @@
+2004-12-07  Richard Williamson   <rjw@apple.com>
+
+       Support threaded image decoding on machines w/ > 2 CPUs.
+
+        Reviewed by Maciej and Chris.
+
+        * khtml/misc/loader.cpp:
+        (CachedImageCallback::notifyUpdate):
+        (CachedImageCallback::notifyFinished):
+        (CachedImageCallback::notifyDecodingError):
+        (CachedImageCallback::handleError):
+        (CachedImageCallback::clear):
+        (CachedImage::CachedImage):
+        (CachedImage::clear):
+        (CachedImage::data):
+        (CachedImage::checkNotify):
+        (Loader::servePendingRequests):
+        (Loader::slotFinished):
+        (Loader::numRequests):
+        (Loader::cancelRequests):
+        (Loader::removeBackgroundDecodingRequest):
+        * khtml/misc/loader.h:
+        (khtml::CachedImageCallback::CachedImageCallback):
+        (khtml::CachedImageCallback::ref):
+        (khtml::CachedImageCallback::deref):
+        (khtml::CachedImage::decoderCallback):
+        * khtml/rendering/render_object.cpp:
+        (RenderObject::setPixmap):
+        * kwq/KWQPixmap.h:
+        * kwq/KWQPixmap.mm:
+        (-[WebImageCallback initWithCallback:khtml::]):
+        (-[WebImageCallback _commonTermination]):
+        (-[WebImageCallback dealloc]):
+        (-[WebImageCallback finalize]):
+        (-[WebImageCallback notify]):
+        (-[WebImageCallback setImageSourceStatus:]):
+        (-[WebImageCallback status]):
+        (QPixmap::shouldUseThreadedDecoding):
+        (QPixmap::receivedData):
+        * kwq/WebCoreImageRenderer.h:
+        * kwq/WebCoreImageRendererFactory.h:
+        * kwq/WebCoreImageRendererFactory.m:
+        (+[WebCoreImageRendererFactory shouldUseThreadedDecoding]):
+        (+[WebCoreImageRendererFactory setShouldUseThreadedDecoding:]):
+
 2004-12-07  Ken Kocienda  <kocienda@apple.com>
 
         Reviewed by John
index ff1d2dbc4cb809d7e1a60aa59b549483d0e68e14..f769dc64181e874435d83384c8fabf80976b8349 100644 (file)
@@ -498,6 +498,96 @@ static bool crossDomain(const QString &a, const QString &b)
 }
 
 // -------------------------------------------------------------------------------------
+#if APPLE_CHANGES
+void CachedImageCallback::notifyUpdate() 
+{ 
+    if (cachedImage) {
+        cachedImage->do_notify (cachedImage->pixmap(), cachedImage->pixmap().rect()); 
+        QSize s = cachedImage->pixmap_size();
+        cachedImage->setSize(s.width() * s.height() * 2);
+
+        // After receiving the image header we are guaranteed to know
+        // the image size.  Although all of the data may not have arrived or
+        // been decoded we can consider the image loaded for purposed of
+        // layout and dispatching the image's onload handler.  Removing the request from
+        // the list of background decoding requests will ensure that Loader::numRequests() 
+        // does not count this background request.  Further numRequests() can
+        // be correctly used by the part to determine if loading is sufficiently
+        // complete to dispatch the page's onload handler.
+        Request *r = cachedImage->m_request;
+        DocLoader *dl = r->m_docLoader;
+
+        khtml::Cache::loader()->removeBackgroundDecodingRequest(r);
+
+        // Poke the part to get it to do a checkCompleted().  Only do this for
+        // the first update to minimize work.  Note that we are guaranteed to have
+        // read the header when we received this first update, which is triggered
+        // by the first kCGImageStatusIncomplete status from CG. kCGImageStatusIncomplete
+        // really means that the CG decoder is waiting for more data, but has already
+        // read the header.
+        if (!headerReceived) {
+            emit khtml::Cache::loader()->requestDone( dl, cachedImage );
+            headerReceived = true;
+        }
+    }
+}
+
+void CachedImageCallback::notifyFinished()
+{
+    if (cachedImage) {
+        cachedImage->do_notify (cachedImage->pixmap(), cachedImage->pixmap().rect()); 
+        cachedImage->m_loading = false;
+        cachedImage->checkNotify();
+        QSize s = cachedImage->pixmap_size();
+        cachedImage->setSize(s.width() * s.height() * 2);
+       
+        Request *r = cachedImage->m_request;
+        DocLoader *dl = r->m_docLoader;
+
+        khtml::Cache::loader()->removeBackgroundDecodingRequest(r);
+
+        // Poke the part to get it to do a checkCompleted().
+        emit khtml::Cache::loader()->requestDone( dl, cachedImage );
+        
+        delete r;
+    }
+}
+
+void CachedImageCallback::notifyDecodingError()
+{
+    if (cachedImage) {
+        handleError();
+    }
+}
+
+void CachedImageCallback::handleError()
+{
+    if (cachedImage) {
+        cachedImage->errorOccured = true;
+        QPixmap ep = cachedImage->pixmap();
+        cachedImage->do_notify (ep, ep.rect());
+        Cache::removeCacheEntry (cachedImage);
+
+        clear();
+    }
+}
+
+void CachedImageCallback::clear() 
+{
+    if (cachedImage && cachedImage->m_request) {
+        Request *r = cachedImage->m_request;
+        DocLoader *dl = r->m_docLoader;
+
+        khtml::Cache::loader()->removeBackgroundDecodingRequest(r);
+
+        // Poke the part to get it to do a checkCompleted().
+        emit khtml::Cache::loader()->requestFailed( dl, cachedImage );
+
+        delete r;
+    }
+    cachedImage = 0;
+}
+#endif
 
 CachedImage::CachedImage(DocLoader* dl, const DOMString &url, KIO::CacheControl _cachePolicy, time_t _expireDate)
     : QObject(), CachedObject(url, Image, _cachePolicy, _expireDate)
@@ -528,6 +618,16 @@ CachedImage::CachedImage(DocLoader* dl, const DOMString &url, KIO::CacheControl
     setAccept( acceptHeader );
 #endif
     m_showAnimations = dl->showAnimations();
+#if APPLE_CHANGES
+#if BUILDING_ON_PANTHER
+    m_decoderCallback = 0;
+#else
+    if (QPixmap::shouldUseThreadedDecoding())
+        m_decoderCallback = new CachedImageCallback(this);
+    else
+        m_decoderCallback = 0;
+#endif
+#endif
 }
 
 CachedImage::~CachedImage()
@@ -852,6 +952,14 @@ void CachedImage::clear()
 
     // No need to delete imageSource - QMovie does it for us
     imgSource = 0;
+
+#if APPLE_CHANGES
+    if (m_decoderCallback) {
+        m_decoderCallback->clear();
+        m_decoderCallback->deref();
+        m_decoderCallback = 0;
+    }
+#endif
 }
 
 void CachedImage::data ( QBuffer &_buffer, bool eof )
@@ -923,29 +1031,30 @@ void CachedImage::data ( QBuffer &_buffer, bool eof )
         canDraw = true;
     } else {
         // Always attempt to load the image incrementally.
-        // If the AppKit is unable to decode incrementally this pixmap
-        // will not be renderable until all the data has been received.
         if (!p)
             p = new QPixmap(KWQResponseMIMEType(m_response));
-        canDraw = p->receivedData(_buffer.buffer(), eof);
+        canDraw = p->receivedData(_buffer.buffer(), eof, m_decoderCallback);
     }
     
-    if (canDraw || eof) {
-        if (p->isNull()) {
-            errorOccured = true;
-            QPixmap ep = pixmap();
-            do_notify (ep, ep.rect());
-            Cache::removeCacheEntry (this);
-        }
-        else
-            do_notify(*p, p->rect());
+    // If we have a decoder, we'll be notified when decoding has completed.
+    if (!m_decoderCallback) {
+        if (canDraw || eof) {
+            if (p->isNull()) {
+                errorOccured = true;
+                QPixmap ep = pixmap();
+                do_notify (ep, ep.rect());
+                Cache::removeCacheEntry (this);
+            }
+            else
+                do_notify(*p, p->rect());
 
-        QSize s = pixmap_size();
-        setSize(s.width() * s.height() * 2);
-    }
-    if (eof) {
-       m_loading = false;
-       checkNotify();
+            QSize s = pixmap_size();
+            setSize(s.width() * s.height() * 2);
+        }
+        if (eof) {
+            m_loading = false;
+            checkNotify();
+        }
     }
 #endif // APPLE_CHANGES
 }
@@ -973,8 +1082,9 @@ void CachedImage::checkNotify()
     if(m_loading) return;
 
     CachedObjectClientWalker w(m_clients);
-    while (CachedObjectClient *c = w.next())
+    while (CachedObjectClient *c = w.next()) {
         c->notifyFinished(this);
+    }
 }
 
 // -------------------------------------------------------------------------------------------
@@ -1427,8 +1537,15 @@ void Loader::servePendingRequests()
            SLOT( slotData( KIO::Job*, const char *, int)));
   connect( job, SIGNAL( receivedResponse( KIO::Job *, NSURLResponse *)), SLOT( slotReceivedResponse( KIO::Job *, NSURLResponse *)) );
 
-  if (KWQServeRequest(this, req, job))
+  if (KWQServeRequest(this, req, job)) {
+      if (req->object->type() == CachedObject::Image) {
+       CachedImage *ci = static_cast<CachedImage*>(req->object);
+       if (ci->decoderCallback()) {
+           m_requestsBackgroundDecoding.append(req);
+       }
+      }
       m_requestsLoading.insert(job, req);
+  }
 #else
   connect( job, SIGNAL( data( KIO::Job*, const QByteArray &)),
            SLOT( slotData( KIO::Job*, const QByteArray &)));
@@ -1440,11 +1557,8 @@ void Loader::servePendingRequests()
 #endif // APPLE_CHANGES
 }
 
-#if APPLE_CHANGES
+#if !defined(APPLE_CHANGES)
 void Loader::slotFinished( KIO::Job* job, NSData *allData)
-#else
-void Loader::slotFinished( KIO::Job* job )
-#endif
 {
   Request *r = m_requestsLoading.take( job );
   KIO::TransferJob* j = static_cast<KIO::TransferJob*>(job);
@@ -1461,23 +1575,13 @@ void Loader::slotFinished( KIO::Job* job )
   else
   {
       r->object->data(r->m_buffer, true);
-#if APPLE_CHANGES
-      r->object->setAllData(allData);
-#endif 
+
       emit requestDone( r->m_docLoader, r->object );
-#if !APPLE_CHANGES
       time_t expireDate = j->queryMetaData("expire-date").toLong();
 kdDebug(6060) << "Loader::slotFinished, url = " << j->url().url() << " expires " << ctime(&expireDate) << endl;
       r->object->setExpireDate(expireDate, false);
-#endif
   }
 
-#if APPLE_CHANGES
-  // We don't want to ever finish the load in the case of an error because we don't want them cached.
-  if (j->error())
-      Cache::removeCacheEntry( r->object );
-  else
-#endif
   r->object->finish();
 
 #ifdef CACHE_DEBUG
@@ -1485,8 +1589,57 @@ kdDebug(6060) << "Loader::slotFinished, url = " << j->url().url() << " expires "
 #endif
 
   delete r;
+
   servePendingRequests();
 }
+#else // APPLE_CHANGES
+void Loader::slotFinished( KIO::Job* job, NSData *allData)
+{
+    Request *r = m_requestsLoading.take( job );
+    KIO::TransferJob* j = static_cast<KIO::TransferJob*>(job);
+
+    if ( !r )
+        return;
+
+    CachedObject *object = r->object;
+    DocLoader *docLoader = r->m_docLoader;
+    
+    bool backgroundImageDecoding = (object->type() == CachedObject::Image && 
+       static_cast<CachedImage*>(object)->decoderCallback());
+       
+    if (j->error() || j->isErrorPage()) {
+        // Use the background image decoder's callback to handle the error.
+        if (backgroundImageDecoding) {
+            CachedImageCallback *callback = static_cast<CachedImage*>(object)->decoderCallback();
+            callback->handleError();
+        }
+        else {
+            r->object->error( job->error(), job->errorText().ascii() );
+            emit requestFailed( docLoader, object );
+            Cache::removeCacheEntry( object );
+        }
+    }
+    else {
+        object->data(r->m_buffer, true);
+
+        r->object->setAllData(allData);
+
+        // Let the background image decoder trigger the done signal.
+        if (!backgroundImageDecoding)
+            emit requestDone( docLoader, object );
+
+        object->finish();
+    }
+
+    // Let the background image decoder release the request when it is
+    // finished.
+    if (!backgroundImageDecoding) {
+        delete r;
+    }
+
+    servePendingRequests();
+}
+#endif
 
 #if APPLE_CHANGES
 
@@ -1540,6 +1693,13 @@ int Loader::numRequests( DocLoader* dl ) const
         if ( lIt.current()->m_docLoader == dl )
             res++;
 
+#if APPLE_CHANGES
+    QPtrListIterator<Request> bdIt( m_requestsBackgroundDecoding );
+    for (; bdIt.current(); ++bdIt )
+        if ( bdIt.current()->m_docLoader == dl )
+            res++;
+#endif
+
     return res;
 }
 
@@ -1578,8 +1738,34 @@ void Loader::cancelRequests( DocLoader* dl )
         else
             ++lIt;
     }
+
+#if APPLE_CHANGES
+    QPtrListIterator<Request> bdIt( m_requestsBackgroundDecoding );
+    while ( bdIt.current() )
+    {
+        if ( bdIt.current()->m_docLoader == dl )
+        {
+            kdDebug( 6060 ) << "cancelling pending request for " << bdIt.current()->object->url().string() << endl;
+            //emit requestFailed( dl, bdIt.current()->object );
+            Cache::removeCacheEntry( bdIt.current()->object );
+            m_requestsBackgroundDecoding.remove( bdIt );
+        }
+        else
+            ++bdIt;
+    }
+#endif
 }
 
+#if APPLE_CHANGES
+void Loader::removeBackgroundDecodingRequest (Request *r)
+{
+    bool present = m_requestsBackgroundDecoding.containsRef(r);
+    if (present) {
+       m_requestsBackgroundDecoding.remove (r);
+    }
+}
+#endif
+
 KIO::Job *Loader::jobForRequest( const DOM::DOMString &url ) const
 {
     QPtrDictIterator<Request> it( m_requestsLoading );
index a80c652198cb2d215ea845359ebb61f371897560..bb6df85f43167496a7ccbc39c5f5f39eb94ca938 100644 (file)
@@ -305,6 +305,30 @@ namespace khtml
 
     class ImageSource;
 
+#if APPLE_CHANGES    
+    class CachedImage;
+    
+    class CachedImageCallback
+    {
+    public:
+        CachedImageCallback (CachedImage *c) : cachedImage(c), refCount(1), headerReceived(false) {};
+
+        void ref() { refCount++; }
+        void deref() { if (--refCount == 0) delete this; }
+        
+        void notifyUpdate();
+        void notifyFinished();
+        void notifyDecodingError();
+        void clear();
+        void handleError();
+        
+    private:
+        CachedImage *cachedImage;
+        uint refCount;
+       bool headerReceived;
+    };
+#endif
+        
     /**
      * a cached image
      */
@@ -375,8 +399,12 @@ namespace khtml
 #if APPLE_CHANGES
     public:
         int dataSize() const { return m_dataSize; }
+       CachedImageCallback *decoderCallback() const { return m_decoderCallback; }
     private:
+        friend class CachedImageCallback;
+        
         int m_dataSize;
+        CachedImageCallback *m_decoderCallback;
 #endif
     };
 
@@ -501,7 +529,7 @@ protected:
     {
        Q_OBJECT
 
-    public:
+    public:    
        Loader();
        ~Loader();
 
@@ -510,6 +538,10 @@ protected:
         int numRequests( DocLoader* dl ) const;
         void cancelRequests( DocLoader* dl );
 
+#if APPLE_CHANGES
+       void removeBackgroundDecodingRequest (Request *r);
+#endif
+       
         // may return 0L
         KIO::Job *jobForRequest( const DOM::DOMString &url ) const;
 
@@ -518,6 +550,8 @@ protected:
 #endif
 
     signals:
+       friend class CachedImageCallback;
+
         void requestStarted( khtml::DocLoader* dl, khtml::CachedObject* obj );
        void requestDone( khtml::DocLoader* dl, khtml::CachedObject *obj );
        void requestFailed( khtml::DocLoader* dl, khtml::CachedObject *obj );
@@ -537,6 +571,11 @@ protected:
 
        QPtrList<Request> m_requestsPending;
        QPtrDict<Request> m_requestsLoading;
+
+#if APPLE_CHANGES
+       QPtrList<Request> m_requestsBackgroundDecoding;
+#endif
+
 #ifdef HAVE_LIBJPEG
         KJPEGFormatType m_jpegloader;
 #endif
index 7b661e21aa8981f992266005090b833d7b5c4a84..ea1448fa33707e2c52cb66ec4677533d2899308b 100644 (file)
@@ -2310,7 +2310,7 @@ void RenderObject::setPixmap(const QPixmap&, const QRect&, CachedImage *image)
     // would avoid putting this function and the CachedObjectClient base class into RenderObject.
 
     if (image && image->pixmap_size() == image->valid_rect().size() && parent()) {
-        if (element() && (element()->id() == ID_HTML || element()->id() == ID_BODY))
+        if (canvas() && element() && (element()->id() == ID_HTML || element()->id() == ID_BODY))
             canvas()->repaint();    // repaint the entire canvas since the background gets propagated up
         else
             repaint();              // repaint object, which is a box or a container with boxes inside it
index 481b7b77c9b3ad7df343d2aac6fedbad93d032d8..bc91637dda877cda2e4094b7165b84ceea8897af 100644 (file)
@@ -47,6 +47,11 @@ class NSString;
 
 class QWMatrix;
 
+namespace khtml
+{
+    class CachedImageCallback;
+}
+
 bool canRenderImageType(const QString &type);
 QPixmap *KWQLoadPixmap(const char *name);
 
@@ -78,7 +83,7 @@ public:
 
     QPixmap &operator=(const QPixmap &);
 
-    bool receivedData(const QByteArray &bytes, bool isComplete);
+    bool receivedData(const QByteArray &bytes, bool isComplete, khtml::CachedImageCallback *decoderCallback);
     void stopAnimations();
 
     WebCoreImageRendererPtr image() { return imageRenderer; };
@@ -90,6 +95,8 @@ public:
     
     CGImageRef imageRef();
     
+    static bool shouldUseThreadedDecoding();
+
 private:
 
     WebCoreImageRendererPtr imageRenderer;
index 082d434bec7e1951eae34cef8a980e3715f3ae14..5041698233cce498d58c6ec67a4e49e090c0a15e 100644 (file)
@@ -22,6 +22,7 @@
  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
  */
+#import "loader.h"
 
 #import "KWQPixmap.h"
 #import "KWQFoundationExtras.h"
@@ -108,12 +109,91 @@ CGImageRef QPixmap::imageRef()
     return [imageRenderer imageRef];
 }
 
-bool QPixmap::receivedData(const QByteArray &bytes, bool isComplete)
+#if !defined(BUILDING_ON_PANTHER)
+@interface WebImageCallback : NSObject
+{
+    khtml::CachedImageCallback *callback;
+    CGImageSourceStatus status;
+}
+- (void)notify;
+- (void)setImageSourceStatus:(CGImageSourceStatus)status;
+- (CGImageSourceStatus)status;
+@end
+@implementation WebImageCallback
+- initWithCallback:(khtml::CachedImageCallback *)c
+{
+    self = [super init];
+    callback = c;
+    c->ref();
+    return self;
+}
+
+- (void)_commonTermination
+{
+    callback->deref();
+}
+
+- (void)dealloc
+{
+    [self _commonTermination];
+    [super dealloc];
+}
+
+- (void)finalize
+{
+    [self _commonTermination];
+    [super finalize];
+}
+
+- (void)notify
+{
+    if (status < kCGImageStatusReadingHeader)
+        callback->notifyDecodingError();
+    else if (status == kCGImageStatusIncomplete) {
+        callback->notifyUpdate();
+    }
+    else if (status == kCGImageStatusComplete) {
+        callback->notifyFinished();
+    }
+}
+
+- (void)setImageSourceStatus:(CGImageSourceStatus)s
+{
+    status = s;
+}
+
+- (CGImageSourceStatus)status
+{
+    return status;
+}
+
+@end
+#endif
+
+bool QPixmap::shouldUseThreadedDecoding()
+{
+    return [WebCoreImageRendererFactory shouldUseThreadedDecoding] ? true : false;
+}
+
+bool QPixmap::receivedData(const QByteArray &bytes, bool isComplete, khtml::CachedImageCallback *decoderCallback)
 {
     if (imageRenderer == nil) {
         imageRenderer = KWQRetain([[WebCoreImageRendererFactory sharedFactory] imageRendererWithMIMEType:MIMEType]);
     }
-    return [imageRenderer incrementalLoadWithBytes:bytes.data() length:bytes.size() complete:isComplete];
+    
+#if !defined(BUILDING_ON_PANTHER)
+    WebImageCallback *callbackWrapper = 0;
+    if (decoderCallback)
+        callbackWrapper = [[WebImageCallback alloc] initWithCallback:decoderCallback];
+
+    bool result = [imageRenderer incrementalLoadWithBytes:bytes.data() length:bytes.size() complete:isComplete callback:callbackWrapper];
+
+    [callbackWrapper release];
+#else
+    bool result = [imageRenderer incrementalLoadWithBytes:bytes.data() length:bytes.size() complete:isComplete callback:0];
+#endif
+    
+    return result;
 }
 
 bool QPixmap::mask() const
index 4a9007b1534a9cc2b1386d1a5a69a816e5460b07..25eee8d254a3ebfe33d201785e55b81b0df24ee7 100644 (file)
@@ -27,7 +27,7 @@
 
 @protocol WebCoreImageRenderer <NSObject, NSCopying>
 
-- (BOOL)incrementalLoadWithBytes:(const void *)bytes length:(unsigned)length complete:(BOOL)isComplete;
+- (BOOL)incrementalLoadWithBytes:(const void *)bytes length:(unsigned)length complete:(BOOL)isComplete callback:(id)c;
 
 - (NSSize)size;
 - (void)resize:(NSSize)s;
index 72134c069bc4fd4d093e785bdaa4f70f82b998d8..5fd28c2fd2f2255c40cb61c5cfd43c79ff839762 100644 (file)
@@ -47,6 +47,8 @@
 }
 
 + (WebCoreImageRendererFactory *)sharedFactory;
++ (BOOL)shouldUseThreadedDecoding;
++ (void)setShouldUseThreadedDecoding:(BOOL)flag;
 @end
 
 @interface WebCoreImageRendererFactory (SubclassResponsibility) <WebCoreImageRendererFactory>
index e2a00055ca68267a74ce6aacf517d4390939ff79..2d848d2cabaf44f7e16eb1adba62df6aa8bb79f4 100644 (file)
@@ -35,6 +35,18 @@ static WebCoreImageRendererFactory *sharedFactory;
     return sharedFactory;
 }
 
+static BOOL shouldUseThreadedDecoding = NO;
+
++ (BOOL)shouldUseThreadedDecoding
+{
+    return shouldUseThreadedDecoding;
+}
+
++ (void)setShouldUseThreadedDecoding:(BOOL)flag
+{
+    shouldUseThreadedDecoding = flag;
+}
+
 - init
 {
     [super init];
index 56d252efb8b735d19c111758ea69898f02c23052..7bf678afdc9c5ee945d8a1b30b90d423de834156 100644 (file)
@@ -1,3 +1,69 @@
+2004-12-07  Richard Williamson   <rjw@apple.com>
+
+       Support threaded image decoding on machines w/ > 2 CPUs.
+
+        Reviewed by Maciej and Chris.
+
+        * Misc.subproj/WebKitSystemBits.h:
+        * Misc.subproj/WebKitSystemBits.m:
+        (WebSystemMainMemory):
+        (WebNumberOfCPUs):
+        * WebCoreSupport.subproj/WebImageData.h:
+        * WebCoreSupport.subproj/WebImageData.m:
+        (+[WebImageData initialize]):
+        (-[WebImageData init]):
+        (-[WebImageData _commonTermination]):
+        (-[WebImageData dealloc]):
+        (-[WebImageData _invalidateImages]):
+        (-[WebImageData _imageSourceOptions]):
+        (-[WebImageData imageAtIndex:]):
+        (-[WebImageData propertiesAtIndex:]):
+        (-[WebImageData _createImages]):
+        (-[WebImageData decodeData:isComplete:callback:]):
+        (-[WebImageData incrementalLoadWithBytes:length:complete:callback:]):
+        (drawPattern):
+        (-[WebImageData tileInRect:fromPoint:context:]):
+        (-[WebImageData isNull]):
+        (-[WebImageData size]):
+        (-[WebImageData _frameDurationAt:]):
+        (-[WebImageData _frameDuration]):
+        (+[WebImageData stopAnimationsInView:]):
+        (-[WebImageData addAnimatingRenderer:inView:]):
+        (-[WebImageData removeAnimatingRenderer:]):
+        * WebCoreSupport.subproj/WebImageDecodeItem.h: Added.
+        * WebCoreSupport.subproj/WebImageDecodeItem.m: Added.
+        (+[WebImageDecodeItem decodeItemWithImage:data:isComplete:callback:]):
+        (-[WebImageDecodeItem initWithImage:data:isComplete:callback:]):
+        (-[WebImageDecodeItem finalize]):
+        (-[WebImageDecodeItem dealloc]):
+        * WebCoreSupport.subproj/WebImageDecoder.h: Added.
+        * WebCoreSupport.subproj/WebImageDecoder.m: Added.
+        (decoderNotifications):
+        (+[WebImageDecoder initialize]):
+        (+[WebImageDecoder notifyMainThread]):
+        (+[WebImageDecoder sharedDecoder]):
+        (+[WebImageDecoder performDecodeWithImage:data:isComplete:callback:]):
+        (+[WebImageDecoder imageDecodesPending]):
+        (+[WebImageDecoder decodeComplete:status:]):
+        (-[WebImageDecoder init]):
+        (-[WebImageDecoder dealloc]):
+        (-[WebImageDecoder finalize]):
+        (-[WebImageDecoder removeItem]):
+        (-[WebImageDecoder addItem:]):
+        (-[WebImageDecoder decodeItem:]):
+        (decoderThread):
+        (startDecoderThread):
+        * WebCoreSupport.subproj/WebImageRenderer.m:
+        (-[WebImageRenderer initWithData:MIMEType:]):
+        (-[WebImageRenderer initWithContentsOfFile:]):
+        (-[WebImageRenderer incrementalLoadWithBytes:length:complete:callback:]):
+        (-[WebInternalImage incrementalLoadWithBytes:length:complete:callback:]):
+        * WebKit.pbproj/project.pbxproj:
+        * WebView.subproj/WebImageRepresentation.m:
+        (-[WebImageRepresentation receivedData:withDataSource:]):
+        (-[WebImageRepresentation receivedError:withDataSource:]):
+        (-[WebImageRepresentation finishedLoadingWithDataSource:]):
+
 2004-12-07  Chris Blumenberg  <cblu@apple.com>
 
        Fix for performance regression.
index d743b48e81bd2a43962b5dca0f0b93c9a868f8ad..f416d8d60424bbc2656c10658ff270324141d624 100644 (file)
@@ -5,4 +5,5 @@
 
 #import <Foundation/Foundation.h>
 
-vm_size_t WebSystemMainMemory(void);
\ No newline at end of file
+vm_size_t WebSystemMainMemory(void);
+int WebNumberOfCPUs(void);
index 79829378c807c1b73a37a61ec62886daa1e57ce2..13fc41c385aeab1225beac8888bd9215199afd29 100644 (file)
@@ -11,6 +11,9 @@
 #include <mach/host_info.h>
 #include <mach/mach_error.h>
 
+#include <sys/types.h>
+#include <sys/sysctl.h>
+
 static host_basic_info_data_t gHostBasicInfo;
 static pthread_once_t initControl = PTHREAD_ONCE_INIT;
 
@@ -35,3 +38,19 @@ vm_size_t WebSystemMainMemory(void)
     pthread_once(&initControl, initCapabilities);
     return gHostBasicInfo.memory_size;
 }
+
+int WebNumberOfCPUs(void)
+{
+    static int numCPUs = 0;
+    
+    if (numCPUs == 0) {
+        int mib[2];
+        size_t len;
+
+        mib[0] = CTL_HW;
+        mib[1] = HW_NCPU;
+        len = sizeof(numCPUs);
+        sysctl(mib, 2, &numCPUs, &len, NULL, 0);
+    }
+    return numCPUs;
+}
index aeecf94b60b5e513a78615fd4eaaebf77bb3c3a5..49b4d546f12b4bb2a4f884cd1a15d02ba4864de6 100644 (file)
@@ -14,6 +14,7 @@
 {
     size_t imagesSize;
     CGImageRef *images;
+    CFDictionaryRef *imageProperties;
     CGImageSourceRef imageSource;
 
     CGSize size;
     size_t frameDurationsSize;
     float *frameDurations;
     
-    size_t imagePropertiesSize;
-    CFDictionaryRef *imageProperties;
-
     size_t currentFrame;
     int repetitionsComplete;
     BOOL animationFinished;
+    
+    NSLock *decodeLock;
 }
 
 - (size_t)numberOfImages;
 - (CGImageRef)imageAtIndex:(size_t)index;
-- (BOOL)incrementalLoadWithBytes:(const void *)bytes length:(unsigned)length complete:(BOOL)isComplete;
-- (void)drawImageAtIndex:(size_t)index inRect:(CGRect)ir fromRect:(CGRect)fr adjustedSize:(CGSize)size compositeOperation:(CGCompositeOperation)op context:(CGContextRef)aContext;
+- (BOOL)incrementalLoadWithBytes:(const void *)bytes length:(unsigned)length complete:(BOOL)isComplete callback:(id)c;
 - (void)drawImageAtIndex:(size_t)index inRect:(CGRect)ir fromRect:(CGRect)fr compositeOperation:(CGCompositeOperation)op context:(CGContextRef)aContext;
+- (void)drawImageAtIndex:(size_t)index inRect:(CGRect)ir fromRect:(CGRect)fr adjustedSize:(CGSize)size compositeOperation:(CGCompositeOperation)op context:(CGContextRef)aContext;
 - (void)tileInRect:(CGRect)rect fromPoint:(CGPoint)point context:(CGContextRef)aContext;
 - (BOOL)isNull;
 - (CGSize)size;
@@ -49,6 +49,8 @@
 - (size_t)currentFrame;
 - (CFDictionaryRef)propertiesAtIndex:(size_t)index;
 
+- (void)decodeData:(CFDataRef)data isComplete:(BOOL)f callback:(id)c;
+
 @end
 
 #endif
index b70f269cfeabd65a06212e07a93edbd798241ef8..4aa599d8ba7892a9e3f06433cfbc698b5cbc14bf 100644 (file)
@@ -5,8 +5,10 @@
 #import <WebKit/WebAssertions.h>
 #import <WebKit/WebGraphicsBridge.h>
 #import <WebKit/WebImageData.h>
+#import <WebKit/WebImageDecoder.h>
 #import <WebKit/WebImageRenderer.h>
 #import <WebKit/WebImageRendererFactory.h>
+#import <WebKit/WebKitSystemBits.h>
 
 #import <WebCore/WebCoreImageRenderer.h>
 
@@ -22,24 +24,40 @@ static CFDictionaryRef imageSourceOptions;
 @interface WebImageData (WebInternal)
 - (void)_commonTermination;
 - (void)_invalidateImages;
-- (void)_invalidateImageProperties;
 - (int)_repetitionCount;
 - (float)_frameDuration;
 - (void)_stopAnimation;
 - (void)_nextFrame;
+- (CFDictionaryRef)_imageSourceOptions;
 @end
 
 
 @implementation WebImageData
 
++ (void)initialize
+{
+    [WebImageRendererFactory setShouldUseThreadedDecoding:(WebNumberOfCPUs() >= 2 ? YES : NO)];
+}
+
+- init
+{
+    self = [super init];
+    
+    if ([WebImageRendererFactory shouldUseThreadedDecoding])
+        decodeLock = [[NSLock alloc] init];
+
+    imageSource = CGImageSourceCreateIncremental ([self _imageSourceOptions]);
+    
+    return self;
+}
+
+
 - (void)_commonTermination
 {
     ASSERT (!frameTimer);
     
     [self _invalidateImages];
-    
-    [self _invalidateImageProperties];
-    
+        
     if (imageSource)
         CFRelease (imageSource); 
         
@@ -51,7 +69,10 @@ static CFDictionaryRef imageSourceOptions;
 
 - (void)dealloc
 {
+    [decodeLock release];
+
     [self _commonTermination];
+
     [super dealloc];
 }
 
@@ -92,27 +113,30 @@ static CFDictionaryRef imageSourceOptions;
         for (i = 0; i < imagesSize; i++) {
             if (images[i])
                 CFRelease (images[i]);
+
+            if (imageProperties[i])
+                CFRelease (imageProperties[i]);
         }
         free (images);
         images = 0;
+        free (imageProperties);
+        imageProperties = 0;
     }
 }
 
-- (void)_invalidateImageProperties
+- (CFDictionaryRef)_imageSourceOptions
 {
-    size_t i;
-    for (i = 0; i < imagePropertiesSize; i++) {
-       if (imageProperties[i])
-           CFRelease (imageProperties[i]);
+    if (!imageSourceOptions) {
+        CFStringRef keys[1] = { kCGImageSourceShouldCache };
+        CFBooleanRef values[1] = { kCFBooleanTrue };
+        imageSourceOptions = CFDictionaryCreate (NULL, (const void **)&keys, (const void **)&values, 1, 
+                &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
     }
-    free (imageProperties);
-    imageProperties = 0;
-    imagePropertiesSize = 0;
+    return imageSourceOptions;
 }
 
 - (CGImageRef)_noColorCorrectionImage:(CGImageRef)image withProperties:(CFDictionaryRef)props;
 {
-#if NO_COLOR_CORRECTION
     CGColorSpaceRef uncorrectedColorSpace = 0;
     CGImageRef noColorCorrectionImage = 0;
 
@@ -132,95 +156,104 @@ static CFDictionaryRef imageSourceOptions;
        }
     }
     return noColorCorrectionImage;
-#else
-    return 0;
-#endif
 }
            
 - (CGImageRef)imageAtIndex:(size_t)index
 {
-    size_t num = [self numberOfImages];
-    
-    if (imagesSize && num > imagesSize)
-        [self _invalidateImages];
-        
-    if (imageSource) {
-        if (index > [self numberOfImages])
-            return 0;
+    if (index >= imagesSize)
+        return 0;
 
-#ifndef NDEBUG
-        CGImageSourceStatus containerStatus = CGImageSourceGetStatus(imageSource);
-#endif
-       // Ignore status, just try to create the image!  Status reported from ImageIO 
-       // is bogus until the image is created.  See 3827851
-        //if (containerStatus < kCGImageStatusIncomplete)
-        //    return 0;
+    return images[index];
+}
 
-#ifndef NDEBUG
-        CGImageSourceStatus imageStatus = CGImageSourceGetStatusAtIndex(imageSource, index);
-#endif
-       // Ignore status.  Status is invalid until we create the image (and eventually try to display it).
-       // See 3827851
-        //if (imageStatus < kCGImageStatusIncomplete)
-        //    return 0;
+- (CFDictionaryRef)propertiesAtIndex:(size_t)index
+{
+    if (index >= imagesSize)
+        return 0;
+
+    return imageProperties[index];
+}
+
+- (void)_createImages
+{
+    size_t i;
 
-        imagesSize = [self numberOfImages];
+    [self _invalidateImages];
+    
+    imagesSize = [self numberOfImages];
+    for (i = 0; i < imagesSize; i++) {
         if (!images) {
-            images = (CGImageRef *)calloc ([self numberOfImages], sizeof(CGImageRef *));
+            images = (CGImageRef *)calloc (imagesSize, sizeof(CGImageRef *));
         }
             
-        if (!images[index]) {
-            if (!imageSourceOptions) {
-                CFStringRef keys[1] = { kCGImageSourceShouldCache };
-                CFBooleanRef values[1] = { kCFBooleanTrue };
-                imageSourceOptions = CFDictionaryCreate (NULL, (const void **)&keys, (const void **)&values, 1, 
-                            &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
-            }
-            images[index] = CGImageSourceCreateImageAtIndex (imageSource, index, imageSourceOptions);
-            if (images[index] == 0)
-                ERROR ("unable to create image at index %d, containerStatus %d, image status %d", (int)index, containerStatus, imageStatus);
+        images[i] = CGImageSourceCreateImageAtIndex (imageSource, i, [self _imageSourceOptions]);
+
+        if (!imageProperties) {
+            imageProperties = (CFDictionaryRef *)malloc (imagesSize * sizeof(CFDictionaryRef));
         }
-       
-#if NO_COLOR_CORRECTION
-       if (imageStatus >= kCGImageStatusIncomplete) {
-           CGImageRef noColorCorrectionImage = [self _noColorCorrectionImage:images[index]
-                                               withProperties:[self propertiesAtIndex:index]];
-           if (noColorCorrectionImage) {
-               CFRelease (images[index]);
-               images[index] = noColorCorrectionImage;
-           }
-       }
-#endif
-       
-        return images[index];
+        
+        imageProperties[i] = CGImageSourceGetPropertiesAtIndex (imageSource, i, 0);
+        if (imageProperties[i])
+            CFRetain (imageProperties[i]);
     }
-    return 0;
 }
 
-- (BOOL)incrementalLoadWithBytes:(const void *)bytes length:(unsigned)length complete:(BOOL)isComplete
+// Called from decoder thread.
+- (void)decodeData:(CFDataRef)data isComplete:(BOOL)f callback:(id)callback
 {
-    if (!imageSource)
-        imageSource = CGImageSourceCreateIncremental (imageSourceOptions);
+    [decodeLock lock];
+    
+    CGImageSourceUpdateData (imageSource, data, f);
 
-    [self _invalidateImages];
+    // The work of decoding is actually triggered by image creation.
+    [self _createImages];
 
+    [decodeLock unlock];
+
+    // Use status from first image to trigger appropriate notification back to WebCore
+    // on main thread.
+    if (callback) {
+        CGImageSourceStatus imageStatus = CGImageSourceGetStatusAtIndex(imageSource, 0);
+        
+        // Lie about status.  If we have all the data, go ahead and say we're complete
+        // as long we are have at least some valid bands (i.e. >= kCGImageStatusIncomplete).
+        // We have to lie because CG incorrectly reports the status.
+        if (f && imageStatus >= kCGImageStatusIncomplete)
+            imageStatus = kCGImageStatusComplete;
+        
+        // Only send bad status if we've read the whole image.
+        if (f || (!f && imageStatus >= kCGImageStatusIncomplete))
+            [WebImageDecoder decodeComplete:callback status:imageStatus];
+    }
+}
+
+- (BOOL)incrementalLoadWithBytes:(const void *)bytes length:(unsigned)length complete:(BOOL)isComplete callback:(id)callback
+{
     CFDataRef data = CFDataCreate (NULL, bytes, length);
-    CGImageSourceUpdateData (imageSource, data, isComplete);
+    
+    if (callback) {
+        [WebImageDecoder performDecodeWithImage:self data:data isComplete:isComplete callback:callback];
+    }
+    else {
+        CGImageSourceUpdateData (imageSource, data, isComplete);
+        [self _createImages];
+    }
+    
     CFRelease (data);
 
-    // Always returns YES because we can't rely on status.  See 3827851
-    //CGImageSourceStatus status = CGImageSourceGetStatus(imageSource);
-    //
-    //return status >= kCGImageStatusReadingHeader;
     return YES;
 }
 
 - (void)drawImageAtIndex:(size_t)index inRect:(CGRect)ir fromRect:(CGRect)fr adjustedSize:(CGSize)adjustedSize compositeOperation:(CGCompositeOperation)op context:(CGContextRef)aContext;
 {    
+    [decodeLock lock];
+    
     CGImageRef image = [self imageAtIndex:index];
     
-    if (!image)
+    if (!image) {
+        [decodeLock unlock];
         return;
+    }
 
     CGContextSaveGState (aContext);
 
@@ -232,9 +265,9 @@ static CFDictionaryRef imageSourceOptions;
     // clip.
     BOOL clipped = NO;
     if (h < fr.size.height) {
-       fr.size.height = h;
-       ir.size.height = h;
-       clipped = YES;
+        fr.size.height = h;
+        ir.size.height = h;
+        clipped = YES;
     }
     
     // Flip the coords.
@@ -262,6 +295,8 @@ static CFDictionaryRef imageSourceOptions;
     }
 
     CGContextRestoreGState (aContext);
+
+    [decodeLock unlock];
 }
 
 - (void)drawImageAtIndex:(size_t)index inRect:(CGRect)ir fromRect:(CGRect)fr compositeOperation:(CGCompositeOperation)op context:(CGContextRef)aContext;
@@ -271,7 +306,9 @@ static CFDictionaryRef imageSourceOptions;
 
 static void drawPattern (void * info, CGContextRef context)
 {
-    CGImageRef image = (CGImageRef)info;
+    WebImageData *data = (WebImageData *)info;
+    
+    CGImageRef image = (CGImageRef)[data imageAtIndex:[data currentFrame]];
     float w = CGImageGetWidth(image);
     float h = CGImageGetHeight(image);
     CGContextDrawImage (context, CGRectMake(0, 0, w, h), image);    
@@ -282,10 +319,13 @@ CGPatternCallbacks patternCallbacks = { 0, drawPattern, NULL };
 - (void)tileInRect:(CGRect)rect fromPoint:(CGPoint)point context:(CGContextRef)aContext
 {
     ASSERT (aContext);
+
+    [decodeLock lock];
     
     CGImageRef image = [self imageAtIndex:[self currentFrame]];
     if (!image) {
         ERROR ("unable to find image");
+        [decodeLock unlock];
         return;
     }
 
@@ -306,8 +346,11 @@ CGPatternCallbacks patternCallbacks = { 0, drawPattern, NULL };
         fromRect.origin.x = rect.origin.x - oneTileRect.origin.x;
         fromRect.origin.y = rect.origin.y - oneTileRect.origin.y;
         fromRect.size = rect.size;
+
+        [decodeLock unlock];
         
         [self drawImageAtIndex:[self currentFrame] inRect:rect fromRect:fromRect compositeOperation:kCGCompositeSover context:aContext];
+
         return;
     }
 
@@ -322,7 +365,7 @@ CGPatternCallbacks patternCallbacks = { 0, drawPattern, NULL };
     CGSize phase = CGSizeMake(fmodf(originInWindow.x, w), fmodf(originInWindow.y, h));
 
     // Possible optimization:  We may want to cache the CGPatternRef    
-    CGPatternRef pattern = CGPatternCreate(image, CGRectMake (0, 0, w, h), CGAffineTransformIdentity, w, h, 
+    CGPatternRef pattern = CGPatternCreate(self, CGRectMake (0, 0, w, h), CGAffineTransformIdentity, w, h, 
         kCGPatternTilingConstantSpacing, true, &patternCallbacks);
     if (pattern) {
         CGContextSaveGState (aContext);
@@ -347,12 +390,14 @@ CGPatternCallbacks patternCallbacks = { 0, drawPattern, NULL };
     else {
         ERROR ("unable to create pattern");
     }
+
+    [decodeLock unlock];
 }
 
 - (BOOL)isNull
 {
     if (imageSource)
-       return CGImageSourceGetStatus(imageSource) < kCGImageStatusReadingHeader;
+        return CGImageSourceGetStatus(imageSource) < kCGImageStatusReadingHeader;
     return YES;
 }
 
@@ -361,6 +406,7 @@ CGPatternCallbacks patternCallbacks = { 0, drawPattern, NULL };
     float w = 0.f, h = 0.f;
 
     if (!haveSize) {
+        [decodeLock lock];
         CFDictionaryRef properties = [self propertiesAtIndex:0];
         if (properties) {
             CFNumberRef num = CFDictionaryGetValue (properties, kCGImagePropertyPixelWidth);
@@ -375,65 +421,35 @@ CGPatternCallbacks patternCallbacks = { 0, drawPattern, NULL };
             
             haveSize = YES;
         }
+        [decodeLock unlock];
     }
     
     return size;
 }
 
-- (CFDictionaryRef)propertiesAtIndex:(size_t)index
-{
-    size_t num = [self numberOfImages];
-    
-    // Number of images changed!
-    if (imagePropertiesSize && num > imagePropertiesSize) {
-        // Clear cache.
-       [self _invalidateImageProperties];
-    }
-    
-    if (imageProperties == 0 && num) {
-        imageProperties = (CFDictionaryRef *)malloc (num * sizeof(CFDictionaryRef));
-        size_t i;
-        for (i = 0; i < num; i++) {
-            imageProperties[i] = CGImageSourceGetPropertiesAtIndex (imageSource, i, 0);
-            if (imageProperties[i])
-                CFRetain (imageProperties[i]);
-        }
-        imagePropertiesSize = num;
-    }
-    
-    if (index < num) {
-        // If image properties are nil, try to get them again.  May have attempted to
-        // get them before enough data was available in the header.
-        if (imageProperties[index] == 0) {
-            imageProperties[index] = CGImageSourceGetPropertiesAtIndex (imageSource, index, 0);
-            if (imageProperties[index])
-                CFRetain (imageProperties[index]);
-        }
-        
-        return imageProperties[index];
-    }
-        
-    return 0;
-}
-
 #define MINIMUM_DURATION (1.0/30.0)
 
 - (float)_frameDurationAt:(size_t)i
 {
+    [decodeLock lock];
+    
     CFDictionaryRef properties = [self propertiesAtIndex:i];
     if (!properties) {
+        [decodeLock unlock];
         return 0.f;
     }
     
     // FIXME:  Use constant instead of {GIF}
     CFDictionaryRef GIFProperties = CFDictionaryGetValue (properties, @"{GIF}");
     if (!GIFProperties) {
+        [decodeLock unlock];
         return 0.f;
     }
     
     // FIXME:  Use constant instead of DelayTime
     CFNumberRef num = CFDictionaryGetValue (GIFProperties, @"DelayTime");
     if (!num) {
+        [decodeLock unlock];
         return 0.f;
     }
     
@@ -452,6 +468,8 @@ CGPatternCallbacks patternCallbacks = { 0, drawPattern, NULL };
         */
         duration = MINIMUM_DURATION;
     }
+
+    [decodeLock unlock];
     
     return duration;
 }
@@ -466,7 +484,7 @@ CGPatternCallbacks patternCallbacks = { 0, drawPattern, NULL };
     }
     
     if (!frameDurations) {
-        size_t i, num = [self numberOfImages];
+        size_t i;
 
         frameDurations = (float *)malloc (sizeof(float) * num);
         for (i = 0; i < num; i++) {
@@ -517,24 +535,25 @@ static NSMutableSet *activeAnimations;
 
     // Now tell them all to stop drawing.
     if (renderersToStop) {
-       objectEnumerator = [renderersToStop objectEnumerator];
-       while ((renderersInView = [objectEnumerator nextObject])) {
-           [renderersInView makeObjectsPerformSelector:@selector(stopAnimation)];
-       }
-       
-       [renderersToStop release];
+        objectEnumerator = [renderersToStop objectEnumerator];
+        while ((renderersInView = [objectEnumerator nextObject])) {
+            [renderersInView makeObjectsPerformSelector:@selector(stopAnimation)];
+        }
+        
+        [renderersToStop release];
     }
 }
 
 - (void)addAnimatingRenderer:(WebImageRenderer *)r inView:(NSView *)view
 {
     if (!animatingRenderers)
-        animatingRenderers = CFDictionaryCreateMutable (NULL, 0, NULL, NULL);
+        animatingRenderers = CFDictionaryCreateMutable (NULL, 0, NULL, &kCFTypeDictionaryValueCallBacks);
     
     NSMutableSet *renderers = (NSMutableSet *)CFDictionaryGetValue (animatingRenderers, view);
     if (!renderers) {
         renderers = [[NSMutableSet alloc] init];
         CFDictionaryAddValue(animatingRenderers, view, renderers);
+       [renderers release];
     }
             
     [renderers addObject:r];
@@ -559,7 +578,7 @@ static NSMutableSet *activeAnimations;
     
     if (animatingRenderers && CFDictionaryGetCount(animatingRenderers) == 0) {
         [activeAnimations removeObject:self];
-       [self _stopAnimation];
+        [self _stopAnimation];
     }
 }
 
diff --git a/WebKit/WebCoreSupport.subproj/WebImageDecodeItem.h b/WebKit/WebCoreSupport.subproj/WebImageDecodeItem.h
new file mode 100644 (file)
index 0000000..4208abe
--- /dev/null
@@ -0,0 +1,21 @@
+/*     
+    WebImageDecodeItem.h
+       Copyright 2004, Apple, Inc. All rights reserved.
+*/
+#import <WebKit/WebImageData.h>
+
+#ifndef OMIT_TIGER_FEATURES
+
+@interface WebImageDecodeItem : NSObject
+{
+@public
+    WebImageData *imageData;
+    id callback;
+    CFDataRef data;
+    BOOL isComplete;
+}
++ decodeItemWithImage:(WebImageData *)img data:(CFDataRef)d isComplete:(BOOL)f callback:(id)c;
+- initWithImage:(WebImageData *)img data:(CFDataRef)d isComplete:(BOOL)f callback:(id)c;
+@end
+
+#endif
\ No newline at end of file
diff --git a/WebKit/WebCoreSupport.subproj/WebImageDecodeItem.m b/WebKit/WebCoreSupport.subproj/WebImageDecodeItem.m
new file mode 100644 (file)
index 0000000..8eadad0
--- /dev/null
@@ -0,0 +1,42 @@
+/*     
+    WebImageDecodeItem.m
+       Copyright 2004, Apple, Inc. All rights reserved.
+*/
+#import <WebKit/WebImageDecodeItem.h>
+
+#ifndef OMIT_TIGER_FEATURES
+
+@implementation WebImageDecodeItem
+
++ decodeItemWithImage:(WebImageData *)img data:(CFDataRef)d isComplete:(BOOL)f callback:(id)c
+{
+    return [[[WebImageDecodeItem alloc] initWithImage:img data:d isComplete:f callback:c] autorelease];
+}
+
+- initWithImage:(WebImageData *)img data:(CFDataRef)d isComplete:(BOOL)f callback:(id)c
+{
+    self = [super init];
+    imageData = [img retain];
+    callback = [c retain];
+    data = CFRetain (d);
+    isComplete = f;
+    return self;
+}
+
+- (void)finalize
+{
+    CFRelease (data);
+    [super finalize];
+}
+
+- (void)dealloc
+{
+    [imageData release];
+    [callback release];
+    CFRelease (data);
+    [super dealloc];
+}
+
+@end
+
+#endif
\ No newline at end of file
diff --git a/WebKit/WebCoreSupport.subproj/WebImageDecoder.h b/WebKit/WebCoreSupport.subproj/WebImageDecoder.h
new file mode 100644 (file)
index 0000000..300b7d1
--- /dev/null
@@ -0,0 +1,27 @@
+/*     
+    WebImageDecoder.h
+       Copyright 2004, Apple, Inc. All rights reserved.
+*/
+#import <WebKit/WebImageData.h>
+#import <WebKit/WebImageDecodeItem.h>
+
+#ifndef OMIT_TIGER_FEATURES
+
+@interface WebImageDecoder : NSObject
+{
+@public
+    NSConditionLock *itemLock;
+    CFMutableDictionaryRef items;
+    NSLock *completedItemsLock;
+    NSMutableArray *completedItems;
+}
++ (WebImageDecoder *)sharedDecoder;
++ (void)performDecodeWithImage:(WebImageData *)img data:(CFDataRef)d isComplete:(BOOL)f callback:(id)c;
++ (void)decodeComplete:(id)callback status:(CGImageSourceStatus)imageStatus;
++ (BOOL)isImageDecodePending:(WebImageData *)img;
+- (WebImageDecodeItem *)removeItem;
+- (void)addItem:(WebImageDecodeItem *)item;
+- (void)decodeItem:(WebImageDecodeItem *)item;
+@end
+
+#endif
\ No newline at end of file
diff --git a/WebKit/WebCoreSupport.subproj/WebImageDecoder.m b/WebKit/WebCoreSupport.subproj/WebImageDecoder.m
new file mode 100644 (file)
index 0000000..1a277e8
--- /dev/null
@@ -0,0 +1,285 @@
+/*     
+    WebImageDecoder.m
+       Copyright 2004, Apple, Inc. All rights reserved.
+*/
+#import <WebKit/WebAssertions.h>
+#import <WebKit/WebImageDecoder.h>
+
+#ifndef OMIT_TIGER_FEATURES
+
+@interface WebImageCallback : NSObject
+- (void)notify;
+- (void)setImageSourceStatus:(CGImageSourceStatus)status;
+- (CGImageSourceStatus)status;
+@end
+
+
+@implementation WebImageDecoder
+
+#define CONDITION_NEW_ITEM_TO_DECODE 1
+#define CONDITION_CHECKED_FOR_ITEMS 2
+
+static void startDecoderThread(void);
+static void *decoderThread(void *arg);
+
+static CFRunLoopSourceRef decoderMainThreadSource;
+static CFRunLoopRef mainRunLoop;
+
+// Don't send partial image update notifications more than
+// every PARTIAL_IMAGE_UPDATE_INTERVAL seconds.  The is
+// a global update interval for all pages in all windows.
+// The update notification will trigger a paint, so governing
+// the notification limits the number of intermediate paints
+// for progressive images.  Not sending a partial
+// update is harmless.
+//
+// Note the completed notifications are always sent immediately,
+// so, the PARTIAL_IMAGE_UPDATE_INTERVAL will not ever delay
+// drawing of the completely decoded image.
+//
+// We may want to make this interval dynamic based on speed of the 
+// machine.  
+#define PARTIAL_IMAGE_UPDATE_INTERVAL 0.2
+
+static double lastPartialImageUpdate;
+
+static void decoderNotifications(void *info);
+static void decoderNotifications(void *info)
+{
+    WebImageDecoder *decoder = [WebImageDecoder sharedDecoder];
+    ASSERT (mainRunLoop == CFRunLoopGetCurrent());
+   
+    [decoder->completedItemsLock lock];
+    
+    unsigned i, count = [decoder->completedItems count];
+    WebImageCallback *callback;
+    for (i = 0; i < count; i++) {
+        callback = [decoder->completedItems objectAtIndex:i];
+        if ([callback status] <= kCGImageStatusIncomplete) {
+            double now = CFAbsoluteTimeGetCurrent();
+            if (lastPartialImageUpdate != 0 && lastPartialImageUpdate + PARTIAL_IMAGE_UPDATE_INTERVAL > now)
+                continue;
+            lastPartialImageUpdate = now;
+        }
+        [callback notify];
+    }
+    [decoder->completedItems removeAllObjects];
+    
+    [decoder->completedItemsLock unlock];
+}
+
++ (void)initialize
+{
+    // Assumes we are being called from main thread.
+
+    // Create shared decoder.
+    [WebImageDecoder sharedDecoder];
+    
+    // Add run loop source to receive notifications when decoding has completed.
+    mainRunLoop = CFRunLoopGetCurrent();
+    CFRunLoopSourceContext sourceContext = {0, self, NULL, NULL, NULL, NULL, NULL, NULL, NULL, decoderNotifications};
+    decoderMainThreadSource = CFRunLoopSourceCreate(NULL, 0, &sourceContext);
+    CFRunLoopAddSource(mainRunLoop, decoderMainThreadSource, kCFRunLoopDefaultMode);
+    
+    startDecoderThread();
+}
+
++ (void)notifyMainThread
+{
+    CFRunLoopSourceSignal(decoderMainThreadSource);
+    if (CFRunLoopIsWaiting(mainRunLoop))
+        CFRunLoopWakeUp(mainRunLoop);
+}
+
++ (WebImageDecoder *)sharedDecoder
+{
+    static WebImageDecoder *sharedDecoder = 0;
+    
+    if (!sharedDecoder)
+       sharedDecoder = [[WebImageDecoder alloc] init];
+    
+    return sharedDecoder;
+}
+
++ (void)performDecodeWithImage:(WebImageData *)img data:(CFDataRef)d isComplete:(BOOL)f callback:(id)c
+{
+    WebImageDecodeItem *item = [WebImageDecodeItem decodeItemWithImage:img data:d isComplete:(BOOL)f callback:c];
+    [[WebImageDecoder sharedDecoder] addItem:item];
+}
+
++ (BOOL)isImageDecodePending:(WebImageData *)img;
+{
+    WebImageDecoder *decoder = [WebImageDecoder sharedDecoder];
+    
+    [decoder->itemLock lock];
+    BOOL result = CFDictionaryGetValue (decoder->items, img) ? YES : NO;
+    [decoder->itemLock unlock];
+    
+    return result;
+}
+
++ (BOOL)imageDecodesPending
+{
+    WebImageDecoder *decoder = [WebImageDecoder sharedDecoder];
+
+    [decoder->itemLock lock];
+    BOOL result = CFDictionaryGetCount(decoder->items) > 0 ? YES : NO;
+    [decoder->itemLock unlock];
+    
+    return result;
+}
+
++ (void)decodeComplete:(id)c status:(CGImageSourceStatus)imageStatus
+{
+    WebImageDecoder *decoder = [WebImageDecoder sharedDecoder];
+    WebImageCallback *callback = (WebImageCallback *)c;
+    [c setImageSourceStatus:imageStatus];
+    
+    [decoder->completedItemsLock lock];
+    [decoder->completedItems addObject:callback];
+    [decoder->completedItemsLock unlock];
+
+    [WebImageDecoder notifyMainThread];
+}
+
+
+- init
+{
+    self = [super init];
+    items = CFDictionaryCreateMutable(NULL, 0, NULL, &kCFTypeDictionaryValueCallBacks);
+    itemLock = [[NSConditionLock alloc] init];
+    completedItems = [[NSMutableArray alloc] init];
+    completedItemsLock = [[NSLock alloc] init];
+    return self;
+}
+
+- (void)dealloc
+{
+    CFRelease (items);
+    [itemLock release];
+    [completedItems release];
+    [completedItemsLock release];
+    [super dealloc];
+}
+
+- (void)finalize
+{
+    CFRelease (items);
+    [super finalize];
+}
+
+#define ITEM_STACK_BUFFER_SIZE 128
+
+- (WebImageDecodeItem *)removeItem
+{
+    ASSERT (mainRunLoop != CFRunLoopGetCurrent());
+
+    WebImageDecodeItem *item = 0;
+    
+    [itemLock lock];
+    
+    CFIndex count = CFDictionaryGetCount(items);
+    if (count) {
+        WebImageData *_keys[ITEM_STACK_BUFFER_SIZE], **keys;
+        WebImageDecodeItem *_values[ITEM_STACK_BUFFER_SIZE], **values;
+        if (count > ITEM_STACK_BUFFER_SIZE) {
+            keys = (WebImageData **)malloc(count * sizeof(WebImageData *));
+            values = (WebImageDecodeItem **)malloc(count * sizeof(WebImageDecodeItem *));
+        }
+        else {
+            keys = _keys;
+            values = _values;
+        }
+        CFDictionaryGetKeysAndValues (items, (const void **)keys, (const void **)values);
+        
+        item = _values[0];
+
+        CFDictionaryRemoveValue (items, _keys[0]);
+        
+        if (keys != _keys) {
+            free (keys);
+            free (values);
+        }
+    }
+    [itemLock unlock];
+    
+    // If we have no item block until we have a new item.
+    if (!item) {
+        [itemLock lockWhenCondition:CONDITION_NEW_ITEM_TO_DECODE];
+        [itemLock unlockWithCondition:CONDITION_CHECKED_FOR_ITEMS];
+    }
+    
+    return item;
+}
+
+- (void)addItem:(WebImageDecodeItem *)item
+{
+    ASSERT (mainRunLoop == CFRunLoopGetCurrent());
+
+    [itemLock lock];
+    
+    // Add additional reference here to ensure that the main thread's pool
+    // doesn't release the item.  It has to stick around long enough for
+    // the decode thread to process it.  The decoder thread will remove
+    // the extra reference.
+    [item retain];
+    
+    // If a prior decode item was already requested
+    // for the image, remove it.  The new item will have more
+    // data.  Necessary because CFDictionaryAddValue has a 
+    // "add if absent" policy.
+    WebImageDecodeItem *replacementItem = (WebImageDecodeItem *)CFDictionaryGetValue (items, item->imageData);
+    if (replacementItem) {
+        CFDictionaryRemoveValue (items, item->imageData);
+        // Remove additional reference increment when item was added.
+        [replacementItem release];
+    }
+
+    CFDictionaryAddValue (items, item->imageData, item);
+        
+    [itemLock unlockWithCondition:CONDITION_NEW_ITEM_TO_DECODE];
+}
+
+- (void)decodeItem:(WebImageDecodeItem *)item
+{
+    ASSERT (mainRunLoop != CFRunLoopGetCurrent());
+
+    [item->imageData decodeData:item->data isComplete:item->isComplete callback:item->callback];
+
+    // Remove additional reference incremented when item was added.
+    [item release];
+}
+
+@end
+
+static void *decoderThread(void *arg)
+{    
+    WebImageDecoder *decoder = [WebImageDecoder sharedDecoder];
+    WebImageDecodeItem *item;
+    
+    while (true) {
+        NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+
+        item = [decoder removeItem];
+        if (item) {
+            [decoder decodeItem:item];
+        }
+
+        [pool release];
+    };
+    
+    return 0;
+}
+
+static void startDecoderThread() {
+    pthread_attr_t attr;
+    pthread_t tid;
+    pthread_attr_init(&attr);
+    pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM);
+    pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
+    pthread_create(&tid, &attr, decoderThread, 0);
+    pthread_attr_destroy(&attr);
+}
+
+#endif
\ No newline at end of file
index ed9455d2e04df3d6b0dfffdacd7ab99d75abefbf..6573928306d05a57005b49e03d34d2f5ec33634c 100644 (file)
@@ -42,7 +42,7 @@
     if (self != nil) {
         MIMEType = [MIME copy];
         imageData = [[WebImageData alloc] init];
-        [imageData incrementalLoadWithBytes:[data bytes] length:[data length] complete:YES];
+        [imageData incrementalLoadWithBytes:[data bytes] length:[data length] complete:YES callback:0];
     }
     return self;
 }
@@ -56,7 +56,7 @@
 
     imageData = [[WebImageData alloc] init];
     NSData *data = [NSData dataWithContentsOfFile:imagePath];
-    [imageData incrementalLoadWithBytes:[data bytes] length:[data length] complete:YES];
+    [imageData incrementalLoadWithBytes:[data bytes] length:[data length] complete:YES callback:0];
         
 
     return self;
     return [imageData isNull];
 }
 
-- (BOOL)incrementalLoadWithBytes:(const void *)bytes length:(unsigned)length complete:(BOOL)isComplete
+- (BOOL)incrementalLoadWithBytes:(const void *)bytes length:(unsigned)length complete:(BOOL)isComplete callback:(id)c
 {
     if (!imageData)
         imageData = [[WebImageData alloc] init];
-    return [imageData incrementalLoadWithBytes:bytes length:length complete:isComplete];
+    return [imageData incrementalLoadWithBytes:bytes length:length complete:isComplete callback:c];
 }
 
 - (void)drawImageInRect:(NSRect)ir fromRect:(NSRect)fr
 - (NSString *)MIMEType;
 - (int)frameCount;
 
-- (BOOL)incrementalLoadWithBytes:(const void *)bytes length:(unsigned)length complete:(BOOL)isComplete;
+- (BOOL)incrementalLoadWithBytes:(const void *)bytes length:(unsigned)length complete:(BOOL)isComplete callback:(id)c;
 - (void)resize:(NSSize)s;
 - (void)drawImageInRect:(NSRect)ir fromRect:(NSRect)fr;
 - (void)drawImageInRect:(NSRect)ir fromRect:(NSRect)fr compositeOperator:(NSCompositingOperation)compsiteOperator context:(CGContextRef)context;
@@ -605,7 +605,7 @@ static NSMutableSet *activeImageRenderers;
     [self setSize:size];
 }
 
-- (BOOL)incrementalLoadWithBytes:(const void *)bytes length:(unsigned)length complete:(BOOL)isComplete
+- (BOOL)incrementalLoadWithBytes:(const void *)bytes length:(unsigned)length complete:(BOOL)isComplete callback:(id)c
 {
     NSArray *reps = [self representations];
     NSBitmapImageRep *imageRep = [reps count] > 0 ? [[self representations] objectAtIndex:0] : nil;
@@ -1251,9 +1251,9 @@ static NSMutableSet *activeImageRenderers;
     [WebInternalImage stopAnimationsInView:aView];
 }
 
-- (BOOL)incrementalLoadWithBytes:(const void *)bytes length:(unsigned)length complete:(BOOL)isComplete
+- (BOOL)incrementalLoadWithBytes:(const void *)bytes length:(unsigned)length complete:(BOOL)isComplete callback:(id)c
 {
-    return [image incrementalLoadWithBytes:bytes length:length complete:isComplete];
+    return [image incrementalLoadWithBytes:bytes length:length complete:isComplete callback:c];
 }
 
 - (NSSize)size
index 29a5238a4f65b5a14770105980fd1e9ad79786c1..1dc1171e2ac5d9fb46729347e531dedb796339e3 100644 (file)
                                9305892B070868B300E79D96,
                                832D7E050709D8FB00F49B61,
                                83E679790726D7CF006C7A36,
+                               514C4C2E075E7DE500B89CAD,
+                               514C4C30075E7DE500B89CAD,
                        );
                        isa = PBXHeadersBuildPhase;
                        runOnlyForDeploymentPostprocessing = 0;
                                93B641FB06E292BC0055F610,
                                51C6513906EFCD9300969825,
                                832D7E060709D8FB00F49B61,
+                               514C4C2F075E7DE500B89CAD,
+                               514C4C31075E7DE500B89CAD,
                        );
                        isa = PBXSourcesBuildPhase;
                        runOnlyForDeploymentPostprocessing = 0;
                                );
                        };
                };
+               514C4C2A075E7DE500B89CAD = {
+                       fileEncoding = 30;
+                       isa = PBXFileReference;
+                       lastKnownFileType = sourcecode.c.h;
+                       path = WebImageDecodeItem.h;
+                       refType = 4;
+                       sourceTree = "<group>";
+               };
+               514C4C2B075E7DE500B89CAD = {
+                       fileEncoding = 30;
+                       isa = PBXFileReference;
+                       lastKnownFileType = sourcecode.c.objc;
+                       path = WebImageDecodeItem.m;
+                       refType = 4;
+                       sourceTree = "<group>";
+               };
+               514C4C2C075E7DE500B89CAD = {
+                       fileEncoding = 30;
+                       isa = PBXFileReference;
+                       lastKnownFileType = sourcecode.c.h;
+                       path = WebImageDecoder.h;
+                       refType = 4;
+                       sourceTree = "<group>";
+               };
+               514C4C2D075E7DE500B89CAD = {
+                       fileEncoding = 30;
+                       isa = PBXFileReference;
+                       lastKnownFileType = sourcecode.c.objc;
+                       path = WebImageDecoder.m;
+                       refType = 4;
+                       sourceTree = "<group>";
+               };
+               514C4C2E075E7DE500B89CAD = {
+                       fileRef = 514C4C2A075E7DE500B89CAD;
+                       isa = PBXBuildFile;
+                       settings = {
+                       };
+               };
+               514C4C2F075E7DE500B89CAD = {
+                       fileRef = 514C4C2B075E7DE500B89CAD;
+                       isa = PBXBuildFile;
+                       settings = {
+                       };
+               };
+               514C4C30075E7DE500B89CAD = {
+                       fileRef = 514C4C2C075E7DE500B89CAD;
+                       isa = PBXBuildFile;
+                       settings = {
+                       };
+               };
+               514C4C31075E7DE500B89CAD = {
+                       fileRef = 514C4C2D075E7DE500B89CAD;
+                       isa = PBXBuildFile;
+                       settings = {
+                       };
+               };
                5152FADD033FC50400CA2ACD = {
                        fileEncoding = 4;
                        isa = PBXFileReference;
                                F5E7B24603025CE801A80180,
                                BE26F18D05517E0800BFA0C3,
                                BE26F18E05517E0800BFA0C3,
+                               514C4C2A075E7DE500B89CAD,
+                               514C4C2B075E7DE500B89CAD,
+                               514C4C2C075E7DE500B89CAD,
+                               514C4C2D075E7DE500B89CAD,
                                51C6513606EFCD9300969825,
                                51C6513706EFCD9300969825,
                                9CE1F8A002A5C6F30ECA2ACD,
index ad9b9fc2100bc0394e7157ec112206a3f4dd5c8c..eba43535a2a034959ca8075aa00db003eab9edcb 100644 (file)
 - (void)receivedData:(NSData *)data withDataSource:(WebDataSource *)theDataSource
 {
     NSData *allData = [dataSource data];
-    [image incrementalLoadWithBytes:[allData bytes] length:[allData length] complete:NO];
+    [image incrementalLoadWithBytes:[allData bytes] length:[allData length] complete:NO callback:0];
 }
 
 - (void)receivedError:(NSError *)error withDataSource:(WebDataSource *)theDataSource
 {
     NSData *allData = [dataSource data];
     if ([allData length] > 0) {
-        [image incrementalLoadWithBytes:[allData bytes] length:[allData length] complete:YES];
+        [image incrementalLoadWithBytes:[allData bytes] length:[allData length] complete:YES callback:0];
     }
     doneLoading = YES;
 }
@@ -63,7 +63,7 @@
 - (void)finishedLoadingWithDataSource:(WebDataSource *)theDataSource
 {
     NSData *allData = [dataSource data];
-    [image incrementalLoadWithBytes:[allData bytes] length:[allData length] complete:YES];
+    [image incrementalLoadWithBytes:[allData bytes] length:[allData length] complete:YES callback:0];
     doneLoading = YES;
 }