Fixed <rdar://problem/3898708> REGRESSION (8A314-8A317): World Clock's short hand...
authorrjw <rjw@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 10 Dec 2004 20:23:37 +0000 (20:23 +0000)
committerrjw <rjw@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 10 Dec 2004 20:23:37 +0000 (20:23 +0000)
Fixed <rdar://problem/3914012> use CG directly for pdf images not ImageIO

Create a PDF document and draw that instead of using ImageIO to create a rasterized image.

        Reviewed by Maciej.

        * WebCoreSupport.subproj/WebImageData.h:
        * WebCoreSupport.subproj/WebImageData.m:
        (-[WebImageData setIsPDF:]):
        (-[WebImageData isPDF]):
        (-[WebImageData dealloc]):
        (-[WebImageData decodeData:isComplete:callback:]):
        (-[WebImageData incrementalLoadWithBytes:length:complete:callback:]):
        (-[WebImageData size]):
        (-[WebImageData animate]):
        (-[WebImageData _createPDFWithData:]):
        (-[WebImageData _PDFDocumentRef]):
        (-[WebImageData _PDFDrawInContext:]):
        (-[WebImageData _PDFDrawFromRect:toRect:operation:alpha:flipped:context:]):
        * WebCoreSupport.subproj/WebImageRenderer.h:
        * WebCoreSupport.subproj/WebImageRenderer.m:
        (-[WebImageRenderer size]):
        (-[WebImageRenderer incrementalLoadWithBytes:length:complete:callback:]):
        (-[WebImageRenderer drawImageInRect:fromRect:compositeOperator:context:]):
        (_createImageRef):

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

WebKit/ChangeLog
WebKit/WebCoreSupport.subproj/WebImageData.h
WebKit/WebCoreSupport.subproj/WebImageData.m
WebKit/WebCoreSupport.subproj/WebImageRenderer.h
WebKit/WebCoreSupport.subproj/WebImageRenderer.m

index c694aba46fecccdf0e86666f179bf07a1ea96507..a53e97de72610d65354fdc1d778b0654d3ad302c 100644 (file)
@@ -1,3 +1,32 @@
+2004-12-10  Richard Williamson   <rjw@apple.com>
+
+       Fixed <rdar://problem/3898708> REGRESSION (8A314-8A317): World Clock's short hand not displayed (ImageIO problem with PDF?)
+       Fixed <rdar://problem/3914012> use CG directly for pdf images not ImageIO
+
+       Create a PDF document and draw that instead of using ImageIO to create a rasterized image.
+
+        Reviewed by Maciej.
+
+        * WebCoreSupport.subproj/WebImageData.h:
+        * WebCoreSupport.subproj/WebImageData.m:
+        (-[WebImageData setIsPDF:]):
+        (-[WebImageData isPDF]):
+        (-[WebImageData dealloc]):
+        (-[WebImageData decodeData:isComplete:callback:]):
+        (-[WebImageData incrementalLoadWithBytes:length:complete:callback:]):
+        (-[WebImageData size]):
+        (-[WebImageData animate]):
+        (-[WebImageData _createPDFWithData:]):
+        (-[WebImageData _PDFDocumentRef]):
+        (-[WebImageData _PDFDrawInContext:]):
+        (-[WebImageData _PDFDrawFromRect:toRect:operation:alpha:flipped:context:]):
+        * WebCoreSupport.subproj/WebImageRenderer.h:
+        * WebCoreSupport.subproj/WebImageRenderer.m:
+        (-[WebImageRenderer size]):
+        (-[WebImageRenderer incrementalLoadWithBytes:length:complete:callback:]):
+        (-[WebImageRenderer drawImageInRect:fromRect:compositeOperator:context:]):
+        (_createImageRef):
+
 2004-12-10  John Sullivan  <sullivan@apple.com>
 
         Reviewed by Ken.
index 1527af91725a0eb0b633b21ca926c93d1451f1d7..fd99f8d5bc2c0b45f2c42d9537b2105dc8908fd4 100644 (file)
@@ -31,6 +31,9 @@
     BOOL animationFinished;
     
     NSLock *decodeLock;
+    
+    id _PDFDoc;
+    BOOL isPDF;
 }
 
 - (size_t)numberOfImages;
@@ -52,6 +55,9 @@
 
 - (void)decodeData:(CFDataRef)data isComplete:(BOOL)f callback:(id)c;
 
+- (void)setIsPDF:(BOOL)f;
+- (BOOL)isPDF;
+
 @end
 
 #endif
index 11f0243f64760ea571a17284021a723d4c4e307e..c464716e9256e3e497bbbf9fa7aa4a3941203e7e 100644 (file)
@@ -29,6 +29,9 @@ static CFDictionaryRef imageSourceOptions;
 - (void)_stopAnimation;
 - (void)_nextFrame;
 - (CFDictionaryRef)_imageSourceOptions;
+-(void)_createPDFWithData:(NSData *)data;
+- (CGPDFDocumentRef)_PDFDocumentRef;
+- (BOOL)_PDFDrawFromRect:(NSRect)srcRect toRect:(NSRect)dstRect operation:(CGCompositeOperation)op alpha:(float)alpha flipped:(BOOL)flipped context:(CGContextRef)context;
 @end
 
 
@@ -51,6 +54,15 @@ static CFDictionaryRef imageSourceOptions;
     return self;
 }
 
+- (void)setIsPDF:(BOOL)f
+{
+    isPDF = f;
+}
+
+- (BOOL)isPDF
+{
+    return isPDF;
+}
 
 - (void)_commonTermination
 {
@@ -69,6 +81,7 @@ static CFDictionaryRef imageSourceOptions;
 
 - (void)dealloc
 {
+    [_PDFDoc release];
     [decodeLock release];
 
     [self _commonTermination];
@@ -199,31 +212,49 @@ static CFDictionaryRef imageSourceOptions;
 }
 
 // Called from decoder thread.
-- (void)decodeData:(CFDataRef)data isComplete:(BOOL)f callback:(id)callback
+- (void)decodeData:(CFDataRef)data isComplete:(BOOL)isComplete callback:(id)callback
 {
     [decodeLock lock];
     
-    CGImageSourceUpdateData (imageSource, data, f);
-
-    // The work of decoding is actually triggered by image creation.
-    [self _createImages];
-
+    if (isPDF) {
+        if (isComplete) {
+            [self _createPDFWithData:(NSData *)data];
+        }
+    }
+    else {
+        // The work of decoding is actually triggered by image creation.
+        CGImageSourceUpdateData (imageSource, data, isComplete);
+        [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];
+    if (isPDF) {
+        if (isComplete && callback) {
+            if ([self _PDFDocumentRef]) {
+                [WebImageDecoder decodeComplete:callback status:kCGImageStatusComplete];
+            }
+            else {
+                [WebImageDecoder decodeComplete:callback status:kCGImageStatusInvalidData];
+            }
+        }
+    }
+    else {
+        // 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 (isComplete && imageStatus >= kCGImageStatusIncomplete)
+                imageStatus = kCGImageStatusComplete;
+            
+            // Only send bad status if we've read the whole image.
+            if (isComplete || (!isComplete && imageStatus >= kCGImageStatusIncomplete))
+                [WebImageDecoder decodeComplete:callback status:imageStatus];
+        }
     }
 }
 
@@ -235,8 +266,15 @@ static CFDictionaryRef imageSourceOptions;
         [WebImageDecoder performDecodeWithImage:self data:data isComplete:isComplete callback:callback];
     }
     else {
-        CGImageSourceUpdateData (imageSource, data, isComplete);
-        [self _createImages];
+        if (isPDF) {
+            if (isComplete) {
+                [self _createPDFWithData:(NSData *)data];
+            }
+        }
+        else {
+            CGImageSourceUpdateData (imageSource, data, isComplete);
+            [self _createImages];
+        }
     }
     
     CFRelease (data);
@@ -245,60 +283,70 @@ static CFDictionaryRef imageSourceOptions;
 }
 
 - (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) {
-        [decodeLock unlock];
-        return;
+{
+    if (isPDF) {
+        [self _PDFDrawFromRect:NSMakeRect(fr.origin.x, fr.origin.y, fr.size.width, fr.size.height)
+                toRect:NSMakeRect(ir.origin.x, ir.origin.y, ir.size.width, ir.size.height)
+                operation:op 
+                alpha:1.0 
+                flipped:YES
+                context:aContext];
     }
+    else {
+        [decodeLock lock];
+        
+        CGImageRef image = [self imageAtIndex:index];
+        
+        if (!image) {
+            [decodeLock unlock];
+            return;
+        }
 
-    CGContextSaveGState (aContext);
-
-    // Get the height of the portion of the image that is currently decoded.  This
-    // could be less that the actual height.
-    float h = CGImageGetHeight(image);
+        CGContextSaveGState (aContext);
 
-    // Is the amount of available bands less than what we need to draw?  If so,
-    // clip.
-    BOOL clipped = NO;
-    CGSize actualSize = [self size];
-    if (h != actualSize.height) {
-       float proportionLoaded = h/actualSize.height;
-       fr.size.height = fr.size.height * proportionLoaded;
-       ir.size.height = ir.size.height * proportionLoaded;
-       clipped = YES;
-    }
-    
-    // Flip the coords.
-    CGContextSetCompositeOperation (aContext, op);
-    CGContextTranslateCTM (aContext, ir.origin.x, ir.origin.y);
-    CGContextScaleCTM (aContext, 1, -1);
-    CGContextTranslateCTM (aContext, 0, -fr.size.height);
-    
-    // Translated to origin, now draw at 0,0.
-    ir.origin.x = ir.origin.y = 0;
-    
-    // If we're drawing a sub portion of the image then create
-    // a image for the sub portion and draw that.
-    // Test using example site at http://www.meyerweb.com/eric/css/edge/complexspiral/demo.html
-    if (clipped == NO && (fr.size.width != adjustedSize.width || fr.size.height != adjustedSize.height)) {
-        image = CGImageCreateWithImageInRect (image, fr);
-        if (image) {
+        // Get the height of the portion of the image that is currently decoded.  This
+        // could be less that the actual height.
+        float h = CGImageGetHeight(image);
+
+        // Is the amount of available bands less than what we need to draw?  If so,
+        // clip.
+        BOOL clipped = NO;
+        CGSize actualSize = [self size];
+        if (h != actualSize.height) {
+            float proportionLoaded = h/actualSize.height;
+            fr.size.height = fr.size.height * proportionLoaded;
+            ir.size.height = ir.size.height * proportionLoaded;
+            clipped = YES;
+        }
+        
+        // Flip the coords.
+        CGContextSetCompositeOperation (aContext, op);
+        CGContextTranslateCTM (aContext, ir.origin.x, ir.origin.y);
+        CGContextScaleCTM (aContext, 1, -1);
+        CGContextTranslateCTM (aContext, 0, -fr.size.height);
+        
+        // Translated to origin, now draw at 0,0.
+        ir.origin.x = ir.origin.y = 0;
+        
+        // If we're drawing a sub portion of the image then create
+        // a image for the sub portion and draw that.
+        // Test using example site at http://www.meyerweb.com/eric/css/edge/complexspiral/demo.html
+        if (clipped == NO && (fr.size.width != adjustedSize.width || fr.size.height != adjustedSize.height)) {
+            image = CGImageCreateWithImageInRect (image, fr);
+            if (image) {
             CGContextDrawImage (aContext, ir, image);
             CFRelease (image);
+            }
+        }
+        // otherwise draw the whole image.
+        else { 
+            CGContextDrawImage (aContext, ir, image);
         }
-    }
-    // otherwise draw the whole image.
-    else { 
-        CGContextDrawImage (aContext, ir, image);
-    }
 
-    CGContextRestoreGState (aContext);
+        CGContextRestoreGState (aContext);
 
-    [decodeLock unlock];
+        [decodeLock unlock];
+    }
 }
 
 - (void)drawImageAtIndex:(size_t)index inRect:(CGRect)ir fromRect:(CGRect)fr compositeOperation:(CGCompositeOperation)op context:(CGContextRef)aContext;
@@ -407,10 +455,17 @@ CGPatternCallbacks patternCallbacks = { 0, drawPattern, NULL };
 {
     float w = 0.f, h = 0.f;
 
-    if (!haveSize) {
-        [decodeLock lock];
-        CFDictionaryRef properties = [self propertiesAtIndex:0];
-        if (properties) {
+    if (isPDF) {
+        if (_PDFDoc) {
+            CGRect mediaBox = [_PDFDoc mediaBox];
+            return mediaBox.size;
+        }
+    }
+    else {
+        if (!haveSize) {
+            [decodeLock lock];
+            CFDictionaryRef properties = [self propertiesAtIndex:0];
+            if (properties) {
             CFNumberRef num = CFDictionaryGetValue (properties, kCGImagePropertyPixelWidth);
             if (num)
                 CFNumberGetValue (num, kCFNumberFloat32Type, &w);
@@ -422,8 +477,9 @@ CGPatternCallbacks patternCallbacks = { 0, drawPattern, NULL };
             size.height = h;
             
             haveSize = YES;
+            }
+            [decodeLock unlock];
         }
-        [decodeLock unlock];
     }
     
     return size;
@@ -641,6 +697,69 @@ static NSMutableSet *activeAnimations;
                                                    repeats:NO] retain];
 }
 
+-(void)_createPDFWithData:(NSData *)data
+{
+    if (!_PDFDoc) {
+        _PDFDoc = [[WebPDFDocument alloc] initWithData:data];
+    }
+}
+
+- (CGPDFDocumentRef)_PDFDocumentRef
+{
+    return [_PDFDoc documentRef];
+}
+
+- (void)_PDFDrawInContext:(CGContextRef)context
+{
+    CGPDFDocumentRef document = [self _PDFDocumentRef];
+    if (document != NULL) {
+        CGRect       mediaBox = [_PDFDoc mediaBox];
+        
+        CGContextSaveGState(context);
+        // Rotate translate image into position according to doc properties.
+        [_PDFDoc adjustCTM:context];   
+
+        // Media box may have non-zero origin which we ignore. CGPDFDocumentShowPage pages start
+        // at 1, not 0.
+        CGContextDrawPDFDocument(context, CGRectMake(0, 0, mediaBox.size.width, mediaBox.size.height), document, 1);
+
+        CGContextRestoreGState(context);
+    }
+}
+
+- (BOOL)_PDFDrawFromRect:(NSRect)srcRect toRect:(NSRect)dstRect operation:(CGCompositeOperation)op alpha:(float)alpha flipped:(BOOL)flipped context:(CGContextRef)context
+{
+    float hScale, vScale;
+
+    CGContextSaveGState(context);
+
+    CGContextSetCompositeOperation (context, op);
+
+    // Scale and translate so the document is rendered in the correct location.
+    hScale = dstRect.size.width  / srcRect.size.width;
+    vScale = dstRect.size.height / srcRect.size.height;
+
+    CGContextTranslateCTM (context, dstRect.origin.x - srcRect.origin.x * hScale, dstRect.origin.y - srcRect.origin.y * vScale);
+    CGContextScaleCTM (context, hScale, vScale);
+
+    // Reverse if flipped image.
+    if (flipped) {
+        CGContextScaleCTM(context, 1, -1);
+        CGContextTranslateCTM (context, 0, -(dstRect.origin.y + dstRect.size.height));
+    }
+
+    // Clip to destination in case we are imaging part of the source only
+    CGContextClipToRect(context, CGRectIntegral(*(CGRect*)&srcRect));
+
+    // and draw
+    [self _PDFDrawInContext:context];
+
+    // done with our fancy transforms
+    CGContextRestoreGState(context);
+
+    return YES;
+}
+
 @end
 
 #endif
index 571647b793bffc306e49755ff55abdc333dc0ad0..0f26ab9f39632e91e87f640b4bd5996b9d57736a 100644 (file)
 
 #endif
 
+@interface WebPDFDocument : NSObject
+{
+    CGPDFDocumentRef _document;
+    CGRect           _mediaBox;
+    NSRect           _cropBox;
+    float            _rotation;
+    int              _currentPage;
+}
+- (id)               initWithData:(NSData*)data;
+- (CGPDFDocumentRef) documentRef;
+- (CGRect)           mediaBox;
+- (NSRect)           bounds;   // adjust for rotation
+- (void)             setCurrentPage:(int)page;
+- (int)              currentPage;
+- (int)              pageCount;
+- (void)             adjustCTM:(CGContextRef)context;
+@end
+
 CGColorSpaceRef WebCGColorSpaceCreateRGB(void);
 CGColorSpaceRef WebCGColorSpaceCreateGray(void);
 CGColorSpaceRef WebCGColorSpaceCreateCMYK(void);
index 5571fd300505f04d9387549947fb19aad57430c3..c4f7f860aecbde02eb402fd8174a67ac824ed8d7 100644 (file)
         return adjustedSize;
         
     if (!imageData)
-       return NSZeroSize;
+        return NSZeroSize;
        
     CGSize sz = [imageData size];
     return NSMakeSize(sz.width, sz.height);
 
 - (BOOL)incrementalLoadWithBytes:(const void *)bytes length:(unsigned)length complete:(BOOL)isComplete callback:(id)c
 {
-    if (!imageData)
+    if (!imageData) {
         imageData = [[WebImageData alloc] init];
+        if ([MIMEType isEqual:@"application/pdf"]) {
+            [imageData setIsPDF:YES];
+        }
+    }
     return [imageData incrementalLoadWithBytes:bytes length:length complete:isComplete callback:c];
 }
 
         op = kCGCompositeSover;
         
     if (isSizeAdjusted) {
-       [imageData drawImageAtIndex:[imageData currentFrame] inRect:CGRectMake(ir.origin.x, ir.origin.y, ir.size.width, ir.size.height) 
-                       fromRect:CGRectMake(fr.origin.x, fr.origin.y, fr.size.width, fr.size.height) 
-                       adjustedSize:CGSizeMake(adjustedSize.width, adjustedSize.height)
-                       compositeOperation:op context:aContext];
+        [imageData drawImageAtIndex:[imageData currentFrame] inRect:CGRectMake(ir.origin.x, ir.origin.y, ir.size.width, ir.size.height) 
+                fromRect:CGRectMake(fr.origin.x, fr.origin.y, fr.size.width, fr.size.height) 
+                adjustedSize:CGSizeMake(adjustedSize.width, adjustedSize.height)
+                compositeOperation:op context:aContext];
     }
     else {
-       [imageData drawImageAtIndex:[imageData currentFrame] inRect:CGRectMake(ir.origin.x, ir.origin.y, ir.size.width, ir.size.height) 
-                       fromRect:CGRectMake(fr.origin.x, fr.origin.y, fr.size.width, fr.size.height) 
-                       compositeOperation:op context:aContext];
+        [imageData drawImageAtIndex:[imageData currentFrame] inRect:CGRectMake(ir.origin.x, ir.origin.y, ir.size.width, ir.size.height) 
+                fromRect:CGRectMake(fr.origin.x, fr.origin.y, fr.size.width, fr.size.height) 
+                compositeOperation:op context:aContext];
     }
 
     targetAnimationRect = ir;
@@ -355,23 +359,6 @@ static CGImageRef _createImageRef(NSBitmapImageRep *rep);
 - (NSRect)bounds;
 @end
 
-@interface WebPDFDocument : NSObject
-{
-    CGPDFDocumentRef _document;
-    CGRect           _mediaBox;
-    NSRect           _cropBox;
-    float            _rotation;
-    int              _currentPage;
-}
-- (id)               initWithData:(NSData*)data;
-- (CGPDFDocumentRef) documentRef;
-- (CGRect)           mediaBox;
-- (NSRect)           bounds;   // adjust for rotation
-- (void)             setCurrentPage:(int)page;
-- (int)              currentPage;
-- (int)              pageCount;
-- (void)             adjustCTM:(CGContextRef)context;
-@end
 
 @implementation WebImageContext
 
@@ -1336,6 +1323,36 @@ static NSMutableSet *activeImageRenderers;
 
 @end
 
+static CGImageRef _createImageRef(NSBitmapImageRep *rep) {
+    BOOL isPlanar = [rep isPlanar];
+    if (isPlanar)
+        return 0;
+        
+    const unsigned char *bitmapData = [rep bitmapData];
+    int pixelsWide = [rep pixelsWide];
+    int pixelsHigh = [rep pixelsHigh];
+    int bitsPerSample = [rep bitsPerSample];
+    int bitsPerPixel = [rep bitsPerPixel];
+    int bytesPerRow = [rep bytesPerRow];
+    BOOL hasAlpha = [rep hasAlpha];
+    CGImageRef image;
+    CGDataProviderRef dataProvider;
+
+    CGColorSpaceRef colorSpace = WebCGColorSpaceCreateRGB();
+    dataProvider = CGDataProviderCreateWithData(NULL, bitmapData, pixelsHigh * bytesPerRow, NULL);
+
+    image = CGImageCreate(pixelsWide, pixelsHigh, bitsPerSample, bitsPerPixel, bytesPerRow, colorSpace,
+                          hasAlpha ? kCGImageAlphaPremultipliedLast : kCGImageAlphaNone,
+                          dataProvider, NULL, false /*shouldInterpolate*/, kCGRenderingIntentDefault);
+
+    CGDataProviderRelease(dataProvider);
+    CGColorSpaceRelease(colorSpace);   
+
+    return image;
+}
+
+#endif
+
 //------------------------------------------------------------------------------------
 
 @implementation WebPDFDocument
@@ -1463,35 +1480,6 @@ static void ReleasePDFDocumentData(void *info, const void *data, size_t size) {
 
 @end
 
-static CGImageRef _createImageRef(NSBitmapImageRep *rep) {
-    BOOL isPlanar = [rep isPlanar];
-    if (isPlanar)
-        return 0;
-        
-    const unsigned char *bitmapData = [rep bitmapData];
-    int pixelsWide = [rep pixelsWide];
-    int pixelsHigh = [rep pixelsHigh];
-    int bitsPerSample = [rep bitsPerSample];
-    int bitsPerPixel = [rep bitsPerPixel];
-    int bytesPerRow = [rep bytesPerRow];
-    BOOL hasAlpha = [rep hasAlpha];
-    CGImageRef image;
-    CGDataProviderRef dataProvider;
-
-    CGColorSpaceRef colorSpace = WebCGColorSpaceCreateRGB();
-    dataProvider = CGDataProviderCreateWithData(NULL, bitmapData, pixelsHigh * bytesPerRow, NULL);
-
-    image = CGImageCreate(pixelsWide, pixelsHigh, bitsPerSample, bitsPerPixel, bytesPerRow, colorSpace,
-                          hasAlpha ? kCGImageAlphaPremultipliedLast : kCGImageAlphaNone,
-                          dataProvider, NULL, false /*shouldInterpolate*/, kCGRenderingIntentDefault);
-
-    CGDataProviderRelease(dataProvider);
-    CGColorSpaceRelease(colorSpace);   
-
-    return image;
-}
-#endif
-
 CGColorSpaceRef WebCGColorSpaceCreateRGB(void)
 {
 #ifdef COLORMATCH_EVERYTHING