void PDFDocumentImage::draw(GraphicsContext* context, const FloatRect& dstRect, const FloatRect& srcRect, CompositeOperator op)
{
    if (!m_document || m_currentPage == -1)
        return;

    context->save();

    context->setCompositeOperation(op);

    float hScale = dstRect.width() / srcRect.width();
    float vScale = dstRect.height() / srcRect.height();

    // Scale and translate so the document is rendered in the correct location,
    // including accounting for the fact that a GraphicsContext is always flipped
    // and doing appropriate flipping.
    CGContextTranslateCTM(context->platformContext(), dstRect.x() - srcRect.x() * hScale, dstRect.y() - srcRect.y() * vScale);
    CGContextScaleCTM(context->platformContext(), hScale, vScale);
    CGContextScaleCTM(context->platformContext(), 1, -1);
    CGContextTranslateCTM(context->platformContext(), 0, -srcRect.height());
    CGContextClipToRect(context->platformContext(), CGRectIntegral(srcRect));

    // Rotate translate image into position according to doc properties.
    adjustCTM(context);

    // Media box may have non-zero origin which we ignore. Pass 1 for the page number.
    CGContextDrawPDFDocument(context->platformContext(), FloatRect(FloatPoint(), m_mediaBox.size()),
        m_document, m_currentPage + 1);
    
    context->restore();

    if (imageObserver())
        imageObserver()->didDraw(this);
}
void PDFDocumentImage::draw(GraphicsContext* context, const FloatRect& dstRect, const FloatRect& srcRect, ColorSpace, CompositeOperator op, BlendMode)
{
    if (!m_document || m_currentPage == -1)
        return;

    {
        GraphicsContextStateSaver stateSaver(*context);

        context->setCompositeOperation(op);

        float hScale = dstRect.width() / srcRect.width();
        float vScale = dstRect.height() / srcRect.height();

        // Scale and translate so the document is rendered in the correct location,
        // including accounting for the fact that a GraphicsContext is always flipped
        // and doing appropriate flipping.
        CGContextTranslateCTM(context->platformContext(), dstRect.x() - srcRect.x() * hScale, dstRect.y() - srcRect.y() * vScale);
        CGContextScaleCTM(context->platformContext(), hScale, vScale);
        CGContextScaleCTM(context->platformContext(), 1, -1);
        CGContextTranslateCTM(context->platformContext(), 0, -srcRect.height());
        CGContextClipToRect(context->platformContext(), CGRectIntegral(srcRect));

        // Rotate translate image into position according to doc properties.
        adjustCTM(context);

        CGContextTranslateCTM(context->platformContext(), -m_mediaBox.x(), -m_mediaBox.y());
        CGContextDrawPDFPage(context->platformContext(), CGPDFDocumentGetPage(m_document, m_currentPage + 1));
    }

    if (imageObserver())
        imageObserver()->didDraw(this);
}
Exemple #3
0
void BitmapImage::draw(GraphicsContext* ctxt, const FloatRect& destRect, const FloatRect& srcRect, ColorSpace styleColorSpace, CompositeOperator compositeOp)
{
    startAnimation();

    RetainPtr<CGImageRef> image = frameAtIndex(m_currentFrame);
    if (!image) // If it's too early we won't have an image yet.
        return;
    
    if (mayFillWithSolidColor()) {
        fillWithSolidColor(ctxt, destRect, solidColor(), styleColorSpace, compositeOp);
        return;
    }

    float currHeight = CGImageGetHeight(image.get());
    if (currHeight <= srcRect.y())
        return;

    CGContextRef context = ctxt->platformContext();
    ctxt->save();

    bool shouldUseSubimage = false;

    // If the source rect is a subportion of the image, then we compute an inflated destination rect that will hold the entire image
    // and then set a clip to the portion that we want to display.
    FloatRect adjustedDestRect = destRect;
    FloatSize selfSize = currentFrameSize();
    if (srcRect.size() != selfSize) {
        CGInterpolationQuality interpolationQuality = CGContextGetInterpolationQuality(context);
        // When the image is scaled using high-quality interpolation, we create a temporary CGImage
        // containing only the portion we want to display. We need to do this because high-quality
        // interpolation smoothes sharp edges, causing pixels from outside the source rect to bleed
        // into the destination rect. See <rdar://problem/6112909>.
        shouldUseSubimage = (interpolationQuality == kCGInterpolationHigh || interpolationQuality == kCGInterpolationDefault) && (srcRect.size() != destRect.size() || !ctxt->getCTM().isIdentityOrTranslationOrFlipped());
        float xScale = srcRect.width() / destRect.width();
        float yScale = srcRect.height() / destRect.height();
        if (shouldUseSubimage) {
            FloatRect subimageRect = srcRect;
            float leftPadding = srcRect.x() - floorf(srcRect.x());
            float topPadding = srcRect.y() - floorf(srcRect.y());

            subimageRect.move(-leftPadding, -topPadding);
            adjustedDestRect.move(-leftPadding / xScale, -topPadding / yScale);

            subimageRect.setWidth(ceilf(subimageRect.width() + leftPadding));
            adjustedDestRect.setWidth(subimageRect.width() / xScale);

            subimageRect.setHeight(ceilf(subimageRect.height() + topPadding));
            adjustedDestRect.setHeight(subimageRect.height() / yScale);

            image.adoptCF(CGImageCreateWithImageInRect(image.get(), subimageRect));
            if (currHeight < srcRect.bottom()) {
                ASSERT(CGImageGetHeight(image.get()) == currHeight - CGRectIntegral(srcRect).origin.y);
                adjustedDestRect.setHeight(CGImageGetHeight(image.get()) / yScale);
            }
        } else {
            adjustedDestRect.setLocation(FloatPoint(destRect.x() - srcRect.x() / xScale, destRect.y() - srcRect.y() / yScale));
            adjustedDestRect.setSize(FloatSize(selfSize.width() / xScale, selfSize.height() / yScale));
        }

        CGContextClipToRect(context, destRect);
    }

    // If the image is only partially loaded, then shrink the destination rect that we're drawing into accordingly.
    if (!shouldUseSubimage && currHeight < selfSize.height())
        adjustedDestRect.setHeight(adjustedDestRect.height() * currHeight / selfSize.height());

    ctxt->setCompositeOperation(compositeOp);

    // Flip the coords.
    CGContextScaleCTM(context, 1, -1);
    adjustedDestRect.setY(-adjustedDestRect.bottom());

    // Adjust the color space.
    image = imageWithColorSpace(image.get(), styleColorSpace);

    // Draw the image.
    CGContextDrawImage(context, adjustedDestRect, image.get());

    ctxt->restore();

    if (imageObserver())
        imageObserver()->didDraw(this);
}
static cairo_int_status_t
_cairo_quartz_init_glyph_surface (cairo_quartz_scaled_font_t *font,
				  cairo_scaled_glyph_t *scaled_glyph)
{
    cairo_int_status_t status = CAIRO_STATUS_SUCCESS;

    cairo_quartz_font_face_t *font_face = _cairo_quartz_scaled_to_face(font);

    cairo_image_surface_t *surface = NULL;

    CGGlyph glyph = _cairo_quartz_scaled_glyph_index (scaled_glyph);

    int advance;
    CGRect bbox;
    double width, height;
    double emscale = CGFontGetUnitsPerEmPtr (font_face->cgFont);

    CGContextRef cgContext = NULL;
    CGAffineTransform textMatrix;
    CGRect glyphRect, glyphRectInt;
    CGPoint glyphOrigin;

    //fprintf (stderr, "scaled_glyph: %p surface: %p\n", scaled_glyph, scaled_glyph->surface);

    /* Create blank 2x2 image if we don't have this character.
     * Maybe we should draw a better missing-glyph slug or something,
     * but this is ok for now.
     */
    if (glyph == INVALID_GLYPH) {
	surface = (cairo_image_surface_t*) cairo_image_surface_create (CAIRO_FORMAT_A8, 2, 2);
	status = cairo_surface_status ((cairo_surface_t *) surface);
	if (status)
	    return status;

	_cairo_scaled_glyph_set_surface (scaled_glyph,
					 &font->base,
					 surface);
	return CAIRO_STATUS_SUCCESS;
    }

    if (!CGFontGetGlyphAdvancesPtr (font_face->cgFont, &glyph, 1, &advance) ||
	!CGFontGetGlyphBBoxesPtr (font_face->cgFont, &glyph, 1, &bbox))
    {
	return CAIRO_INT_STATUS_UNSUPPORTED;
    }

    /* scale(1,-1) * font->base.scale * scale(1,-1) */
    textMatrix = CGAffineTransformMake (font->base.scale.xx,
					-font->base.scale.yx,
					-font->base.scale.xy,
					font->base.scale.yy,
					0, -0);
    glyphRect = CGRectMake (bbox.origin.x / emscale,
			    bbox.origin.y / emscale,
			    bbox.size.width / emscale,
			    bbox.size.height / emscale);

    glyphRect = CGRectApplyAffineTransform (glyphRect, textMatrix);

    /* Round the rectangle outwards, so that we don't have to deal
     * with non-integer-pixel origins or dimensions.
     */
    glyphRectInt = CGRectIntegral (glyphRect);

#if 0
    fprintf (stderr, "glyphRect[o]: %f %f %f %f\n",
	     glyphRect.origin.x, glyphRect.origin.y, glyphRect.size.width, glyphRect.size.height);
    fprintf (stderr, "glyphRectInt: %f %f %f %f\n",
	     glyphRectInt.origin.x, glyphRectInt.origin.y, glyphRectInt.size.width, glyphRectInt.size.height);
#endif

    glyphOrigin = glyphRectInt.origin;

    //textMatrix = CGAffineTransformConcat (textMatrix, CGAffineTransformInvert (ctm));

    width = glyphRectInt.size.width;
    height = glyphRectInt.size.height;

    //fprintf (stderr, "glyphRect[n]: %f %f %f %f\n", glyphRect.origin.x, glyphRect.origin.y, glyphRect.size.width, glyphRect.size.height);

    surface = (cairo_image_surface_t*) cairo_image_surface_create (CAIRO_FORMAT_A8, width, height);
    if (surface->base.status)
	return surface->base.status;

    if (surface->width != 0 && surface->height != 0) {
	cgContext = CGBitmapContextCreate (surface->data,
					   surface->width,
					   surface->height,
					   8,
					   surface->stride,
					   NULL,
					   kCGImageAlphaOnly);

	if (cgContext == NULL) {
	    cairo_surface_destroy (&surface->base);
	    return _cairo_error (CAIRO_STATUS_NO_MEMORY);
	}

	CGContextSetFont (cgContext, font_face->cgFont);
	CGContextSetFontSize (cgContext, 1.0);
	CGContextSetTextMatrix (cgContext, textMatrix);

	switch (font->base.options.antialias) {
	case CAIRO_ANTIALIAS_SUBPIXEL:
	case CAIRO_ANTIALIAS_BEST:
	    CGContextSetShouldAntialias (cgContext, TRUE);
	    CGContextSetShouldSmoothFonts (cgContext, TRUE);
	    if (CGContextSetAllowsFontSmoothingPtr &&
		!CGContextGetAllowsFontSmoothingPtr (cgContext))
		CGContextSetAllowsFontSmoothingPtr (cgContext, TRUE);
	    break;
	case CAIRO_ANTIALIAS_NONE:
	    CGContextSetShouldAntialias (cgContext, FALSE);
	    break;
	case CAIRO_ANTIALIAS_GRAY:
	case CAIRO_ANTIALIAS_GOOD:
	case CAIRO_ANTIALIAS_FAST:
	    CGContextSetShouldAntialias (cgContext, TRUE);
	    CGContextSetShouldSmoothFonts (cgContext, FALSE);
	    break;
	case CAIRO_ANTIALIAS_DEFAULT:
	default:
	    /* Don't do anything */
	    break;
	}

	CGContextSetAlpha (cgContext, 1.0);
	CGContextShowGlyphsAtPoint (cgContext, - glyphOrigin.x, - glyphOrigin.y, &glyph, 1);

	CGContextRelease (cgContext);
    }

    cairo_surface_set_device_offset (&surface->base,
				     - glyphOrigin.x,
				     height + glyphOrigin.y);

    _cairo_scaled_glyph_set_surface (scaled_glyph, &font->base, surface);

    return status;
}
static cairo_int_status_t
_cairo_quartz_init_glyph_metrics (cairo_quartz_scaled_font_t *font,
				  cairo_scaled_glyph_t *scaled_glyph)
{
    cairo_int_status_t status = CAIRO_STATUS_SUCCESS;

    cairo_quartz_font_face_t *font_face = _cairo_quartz_scaled_to_face(font);
    cairo_text_extents_t extents = {0, 0, 0, 0, 0, 0};
    CGGlyph glyph = _cairo_quartz_scaled_glyph_index (scaled_glyph);
    int advance;
    CGRect bbox;
    double emscale = CGFontGetUnitsPerEmPtr (font_face->cgFont);
    double xmin, ymin, xmax, ymax;

    if (glyph == INVALID_GLYPH)
	goto FAIL;

    if (!CGFontGetGlyphAdvancesPtr (font_face->cgFont, &glyph, 1, &advance) ||
	!CGFontGetGlyphBBoxesPtr (font_face->cgFont, &glyph, 1, &bbox))
	goto FAIL;

    /* broken fonts like Al Bayan return incorrect bounds for some null characters,
       see https://bugzilla.mozilla.org/show_bug.cgi?id=534260 */
    if (unlikely (bbox.origin.x == -32767 &&
                  bbox.origin.y == -32767 &&
                  bbox.size.width == 65534 &&
                  bbox.size.height == 65534)) {
        bbox.origin.x = bbox.origin.y = 0;
        bbox.size.width = bbox.size.height = 0;
    }

    bbox = CGRectMake (bbox.origin.x / emscale,
		       bbox.origin.y / emscale,
		       bbox.size.width / emscale,
		       bbox.size.height / emscale);

    /* Should we want to always integer-align glyph extents, we can do so in this way */
#if 0
    {
	CGAffineTransform textMatrix;
	textMatrix = CGAffineTransformMake (font->base.scale.xx,
					    -font->base.scale.yx,
					    -font->base.scale.xy,
					    font->base.scale.yy,
					    0.0f, 0.0f);

	bbox = CGRectApplyAffineTransform (bbox, textMatrix);
	bbox = CGRectIntegral (bbox);
	bbox = CGRectApplyAffineTransform (bbox, CGAffineTransformInvert (textMatrix));
    }
#endif

#if 0
    fprintf (stderr, "[0x%04x] bbox: %f %f %f %f\n", glyph,
	     bbox.origin.x / emscale, bbox.origin.y / emscale,
	     bbox.size.width / emscale, bbox.size.height / emscale);
#endif

    xmin = CGRectGetMinX(bbox);
    ymin = CGRectGetMinY(bbox);
    xmax = CGRectGetMaxX(bbox);
    ymax = CGRectGetMaxY(bbox);

    extents.x_bearing = xmin;
    extents.y_bearing = - ymax;
    extents.width = xmax - xmin;
    extents.height = ymax - ymin;

    extents.x_advance = (double) advance / emscale;
    extents.y_advance = 0.0;

#if 0
    fprintf (stderr, "[0x%04x] extents: bearings: %f %f dim: %f %f adv: %f\n\n", glyph,
	     extents.x_bearing, extents.y_bearing, extents.width, extents.height, extents.x_advance);
#endif

  FAIL:
    _cairo_scaled_glyph_set_metrics (scaled_glyph,
				     &font->base,
				     &extents);

    return status;
}
static cairo_int_status_t
_cairo_quartz_init_glyph_surface (cairo_quartz_scaled_font_t *font,
				  cairo_scaled_glyph_t *scaled_glyph)
{
    cairo_int_status_t status = CAIRO_STATUS_SUCCESS;

    cairo_quartz_font_face_t *font_face = _cairo_quartz_scaled_to_face(font);

    cairo_image_surface_t *surface = NULL;

    CGGlyph glyph = _cairo_quartz_scaled_glyph_index (scaled_glyph);

    int advance;
    CGRect bbox;
    double width, height;
    double xscale, yscale;
    double emscale = CGFontGetUnitsPerEmPtr (font_face->cgFont);

    CGColorSpaceRef gray;
    CGContextRef cgContext = NULL;
    CGAffineTransform textMatrix;
    CGRect glyphRect, glyphRectInt;
    CGPoint glyphOrigin;

    //fprintf (stderr, "scaled_glyph: %p surface: %p\n", scaled_glyph, scaled_glyph->surface);

    /* Create blank 2x2 image if we don't have this character.
     * Maybe we should draw a better missing-glyph slug or something,
     * but this is ok for now.
     */
    if (glyph == INVALID_GLYPH) {
	surface = (cairo_image_surface_t*) cairo_image_surface_create (CAIRO_FORMAT_A8, 2, 2);
	status = cairo_surface_status ((cairo_surface_t *) surface);
	if (status)
	    return status;

	_cairo_scaled_glyph_set_surface (scaled_glyph,
					 &font->base,
					 surface);
	return CAIRO_STATUS_SUCCESS;
    }

    if (!CGFontGetGlyphAdvancesPtr (font_face->cgFont, &glyph, 1, &advance) ||
	!CGFontGetGlyphBBoxesPtr (font_face->cgFont, &glyph, 1, &bbox))
    {
	return CAIRO_INT_STATUS_UNSUPPORTED;
    }

    status = _cairo_matrix_compute_basis_scale_factors (&font->base.scale,
						  &xscale, &yscale, 1);
    if (status)
	return status;

    textMatrix = CGAffineTransformMake (font->base.scale.xx,
					-font->base.scale.yx,
					-font->base.scale.xy,
					font->base.scale.yy,
					0.0f, 0.0f);
    glyphRect = CGRectMake (bbox.origin.x / emscale,
			    bbox.origin.y / emscale,
			    bbox.size.width / emscale,
			    bbox.size.height / emscale);

    glyphRect = CGRectApplyAffineTransform (glyphRect, textMatrix);

    /* Round the rectangle outwards, so that we don't have to deal
     * with non-integer-pixel origins or dimensions.
     */
    glyphRectInt = CGRectIntegral (glyphRect);

#if 0
    fprintf (stderr, "glyphRect[o]: %f %f %f %f\n",
	     glyphRect.origin.x, glyphRect.origin.y, glyphRect.size.width, glyphRect.size.height);
    fprintf (stderr, "glyphRectInt: %f %f %f %f\n",
	     glyphRectInt.origin.x, glyphRectInt.origin.y, glyphRectInt.size.width, glyphRectInt.size.height);
#endif

    glyphOrigin = glyphRectInt.origin;

    //textMatrix = CGAffineTransformConcat (textMatrix, CGAffineTransformInvert (ctm));

    width = glyphRectInt.size.width;
    height = glyphRectInt.size.height;

    //fprintf (stderr, "glyphRect[n]: %f %f %f %f\n", glyphRect.origin.x, glyphRect.origin.y, glyphRect.size.width, glyphRect.size.height);

    surface = (cairo_image_surface_t*) cairo_image_surface_create (CAIRO_FORMAT_A8, width, height);
    if (surface->base.status)
	return surface->base.status;

    gray = CGColorSpaceCreateDeviceGray ();
    cgContext = CGBitmapContextCreate (surface->data,
				       surface->width,
				       surface->height,
				       8,
				       surface->stride,
				       gray,
				       kCGImageAlphaNone);
    CGColorSpaceRelease (gray);

    CGContextSetFont (cgContext, font_face->cgFont);
    CGContextSetFontSize (cgContext, 1.0);
    CGContextSetTextMatrix (cgContext, textMatrix);

    CGContextClearRect (cgContext, CGRectMake (0.0f, 0.0f, width, height));

    if (font->base.options.antialias == CAIRO_ANTIALIAS_NONE)
	CGContextSetShouldAntialias (cgContext, false);

    CGContextSetRGBFillColor (cgContext, 1.0, 1.0, 1.0, 1.0);
    CGContextShowGlyphsAtPoint (cgContext, - glyphOrigin.x, - glyphOrigin.y, &glyph, 1);

    CGContextRelease (cgContext);

    cairo_surface_set_device_offset (&surface->base,
				     - glyphOrigin.x,
				     height + glyphOrigin.y);

    _cairo_scaled_glyph_set_surface (scaled_glyph, &font->base, surface);

    return status;
}
static cairo_int_status_t
_cairo_quartz_init_glyph_metrics (cairo_quartz_scaled_font_t *font,
				  cairo_scaled_glyph_t *scaled_glyph)
{
    cairo_int_status_t status = CAIRO_STATUS_SUCCESS;

    cairo_quartz_font_face_t *font_face = _cairo_quartz_scaled_to_face(font);
    cairo_text_extents_t extents = {0, 0, 0, 0, 0, 0};
    CGAffineTransform textMatrix;
    CGGlyph glyph = _cairo_quartz_scaled_glyph_index (scaled_glyph);
    int advance;
    CGRect bbox;
    double emscale = CGFontGetUnitsPerEmPtr (font_face->cgFont);
    double xscale, yscale;
    double xmin, ymin, xmax, ymax;

    if (glyph == INVALID_GLYPH)
	goto FAIL;

    if (!CGFontGetGlyphAdvancesPtr (font_face->cgFont, &glyph, 1, &advance) ||
	!CGFontGetGlyphBBoxesPtr (font_face->cgFont, &glyph, 1, &bbox))
	goto FAIL;

    status = _cairo_matrix_compute_basis_scale_factors (&font->base.scale,
						  &xscale, &yscale, 1);
    if (status)
	goto FAIL;

    bbox = CGRectMake (bbox.origin.x / emscale,
		       bbox.origin.y / emscale,
		       bbox.size.width / emscale,
		       bbox.size.height / emscale);

    /* Should we want to always integer-align glyph extents, we can do so in this way */
#if 0
    {
	CGAffineTransform textMatrix;
	textMatrix = CGAffineTransformMake (font->base.scale.xx,
					    -font->base.scale.yx,
					    -font->base.scale.xy,
					    font->base.scale.yy,
					    0.0f, 0.0f);

	bbox = CGRectApplyAffineTransform (bbox, textMatrix);
	bbox = CGRectIntegral (bbox);
	bbox = CGRectApplyAffineTransform (bbox, CGAffineTransformInvert (textMatrix));
    }
#endif

#if 0
    fprintf (stderr, "[0x%04x] bbox: %f %f %f %f\n", glyph,
	     bbox.origin.x / emscale, bbox.origin.y / emscale,
	     bbox.size.width / emscale, bbox.size.height / emscale);
#endif

    xmin = CGRectGetMinX(bbox);
    ymin = CGRectGetMinY(bbox);
    xmax = CGRectGetMaxX(bbox);
    ymax = CGRectGetMaxY(bbox);

    extents.x_bearing = xmin;
    extents.y_bearing = - ymax;
    extents.width = xmax - xmin;
    extents.height = ymax - ymin;

    extents.x_advance = (double) advance / emscale;
    extents.y_advance = 0.0;

#if 0
    fprintf (stderr, "[0x%04x] extents: bearings: %f %f dim: %f %f adv: %f\n\n", glyph,
	     extents.x_bearing, extents.y_bearing, extents.width, extents.height, extents.x_advance);
#endif

  FAIL:
    _cairo_scaled_glyph_set_metrics (scaled_glyph,
				     &font->base,
				     &extents);

    return status;
}