Exemple #1
0
BitmapImage::BitmapImage(PassRefPtr<cairo_surface_t> nativeImage, ImageObserver* observer)
    : Image(observer)
    , m_size(cairoSurfaceSize(nativeImage.get()))
    , m_minimumSubsamplingLevel(0)
    , m_currentFrame(0)
    , m_repetitionCount(cAnimationNone)
    , m_repetitionCountStatus(Unknown)
    , m_repetitionsComplete(0)
    , m_decodedSize(m_size.width() * m_size.height() * 4)
    , m_frameCount(1)
    , m_isSolidColor(false)
    , m_checkedForSolidColor(false)
    , m_animationFinished(true)
    , m_allDataReceived(true)
    , m_haveSize(true)
    , m_sizeAvailable(true)
    , m_haveFrameCount(true)
{
    m_frames.grow(1);
    m_frames[0].m_hasAlpha = cairo_surface_get_content(nativeImage.get()) != CAIRO_CONTENT_COLOR;
    m_frames[0].m_frame = nativeImage;
    m_frames[0].m_haveMetadata = true;

    checkForSolidColor();
}
Exemple #2
0
void BitmapImage::checkForSolidColor()
{
    m_isSolidColor = false;
    m_checkedForSolidColor = true;

    if (frameCount() > 1)
        return;

    RefPtr<cairo_surface_t> surface = frameAtIndex(m_currentFrame);
    if (!surface) // If it's too early we won't have an image yet.
        return;

    if (cairo_surface_get_type(surface.get()) != CAIRO_SURFACE_TYPE_IMAGE)
        return;

    IntSize size = cairoSurfaceSize(surface.get());

    if (size.width() != 1 || size.height() != 1)
        return;

    unsigned* pixelColor = reinterpret_cast_ptr<unsigned*>(cairo_image_surface_get_data(surface.get()));
    m_solidColor = colorFromPremultipliedARGB(*pixelColor);

    m_isSolidColor = true;
}
void BitmapImage::draw(GraphicsContext& context, const FloatRect& dst, const FloatRect& src, CompositeOperator op,
    BlendMode blendMode, ImageOrientationDescription description)
{
    if (!dst.width() || !dst.height() || !src.width() || !src.height())
        return;

    startAnimation();

    auto surface = frameImageAtIndex(m_currentFrame);
    if (!surface) // If it's too early we won't have an image yet.
        return;

    Color color = singlePixelSolidColor();
    if (color.isValid()) {
        fillWithSolidColor(context, dst, color, op);
        return;
    }

    context.save();

    // Set the compositing operation.
    if (op == CompositeSourceOver && blendMode == BlendModeNormal && !frameHasAlphaAtIndex(m_currentFrame))
        context.setCompositeOperation(CompositeCopy);
    else
        context.setCompositeOperation(op, blendMode);

#if ENABLE(IMAGE_DECODER_DOWN_SAMPLING)
    IntSize scaledSize = cairoSurfaceSize(surface.get());
    FloatRect adjustedSrcRect = adjustSourceRectForDownSampling(src, scaledSize);
#else
    FloatRect adjustedSrcRect(src);
#endif

    ImageOrientation frameOrientation(description.imageOrientation());
    if (description.respectImageOrientation() == RespectImageOrientation)
        frameOrientation = frameOrientationAtIndex(m_currentFrame);

    FloatRect dstRect = dst;

    if (frameOrientation != DefaultImageOrientation) {
        // ImageOrientation expects the origin to be at (0, 0).
        context.translate(dstRect.x(), dstRect.y());
        dstRect.setLocation(FloatPoint());
        context.concatCTM(frameOrientation.transformFromDefault(dstRect.size()));
        if (frameOrientation.usesWidthAsHeight()) {
            // The destination rectangle will have it's width and height already reversed for the orientation of
            // the image, as it was needed for page layout, so we need to reverse it back here.
            dstRect = FloatRect(dstRect.x(), dstRect.y(), dstRect.height(), dstRect.width());
        }
    }

    context.platformContext()->drawSurfaceToContext(surface.get(), dstRect, adjustedSrcRect, context);

    context.restore();

    if (imageObserver())
        imageObserver()->didDraw(this);
}
Exemple #4
0
static bool encodeImage(cairo_surface_t* surface, const String& mimeType, std::optional<double> quality, GUniqueOutPtr<gchar>& buffer, gsize& bufferSize)
{
    // List of supported image encoding types comes from the GdkPixbuf documentation.
    // http://developer.gnome.org/gdk-pixbuf/stable/gdk-pixbuf-File-saving.html#gdk-pixbuf-save-to-bufferv

    String type = mimeType.substring(sizeof "image");
    if (type != "jpeg" && type != "png" && type != "tiff" && type != "ico" && type != "bmp")
        return false;

    GRefPtr<GdkPixbuf> pixbuf;
    if (type == "jpeg") {
        // JPEG doesn't support alpha channel. The <canvas> spec states that toDataURL() must encode a Porter-Duff
        // composite source-over black for image types that do not support alpha.
        RefPtr<cairo_surface_t> newSurface;
        if (cairo_surface_get_type(surface) == CAIRO_SURFACE_TYPE_IMAGE) {
            newSurface = adoptRef(cairo_image_surface_create_for_data(cairo_image_surface_get_data(surface),
                CAIRO_FORMAT_RGB24,
                cairo_image_surface_get_width(surface),
                cairo_image_surface_get_height(surface),
                cairo_image_surface_get_stride(surface)));
        } else {
            IntSize size = cairoSurfaceSize(surface);
            newSurface = adoptRef(cairo_image_surface_create(CAIRO_FORMAT_RGB24, size.width(), size.height()));
            RefPtr<cairo_t> cr = adoptRef(cairo_create(newSurface.get()));
            cairo_set_source_surface(cr.get(), surface, 0, 0);
            cairo_paint(cr.get());
        }
        pixbuf = adoptGRef(cairoSurfaceToGdkPixbuf(newSurface.get()));
    } else
        pixbuf = adoptGRef(cairoSurfaceToGdkPixbuf(surface));
    if (!pixbuf)
        return false;

    GUniqueOutPtr<GError> error;
    if (type == "jpeg" && quality && *quality >= 0.0 && *quality <= 1.0) {
        String qualityString = String::format("%d", static_cast<int>(*quality * 100.0 + 0.5));
        gdk_pixbuf_save_to_buffer(pixbuf.get(), &buffer.outPtr(), &bufferSize, type.utf8().data(), &error.outPtr(), "quality", qualityString.utf8().data(), NULL);
    } else
        gdk_pixbuf_save_to_buffer(pixbuf.get(), &buffer.outPtr(), &bufferSize, type.utf8().data(), &error.outPtr(), NULL);

    return !error;
}
IntSize size(const RefPtr<cairo_surface_t>& image)
{
    return cairoSurfaceSize(image.get());
}
Exemple #6
0
bool GraphicsContext3D::ImageExtractor::extractImage(bool premultiplyAlpha, bool ignoreGammaAndColorProfile)
{
    if (!m_image)
        return false;
    // We need this to stay in scope because the native image is just a shallow copy of the data.
    m_decoder = new ImageSource(premultiplyAlpha ? ImageSource::AlphaPremultiplied : ImageSource::AlphaNotPremultiplied, ignoreGammaAndColorProfile ? ImageSource::GammaAndColorProfileIgnored : ImageSource::GammaAndColorProfileApplied);
    if (!m_decoder)
        return false;
    ImageSource& decoder = *m_decoder;

    m_alphaOp = AlphaDoNothing;
    if (m_image->data()) {
        decoder.setData(m_image->data(), true);
        if (!decoder.frameCount() || !decoder.frameIsCompleteAtIndex(0))
            return false;
        m_imageSurface = decoder.createFrameAtIndex(0);
    } else {
        m_imageSurface = m_image->nativeImageForCurrentFrame();
        // 1. For texImage2D with HTMLVideoElment input, assume no PremultiplyAlpha had been applied and the alpha value is 0xFF for each pixel,
        // which is true at present and may be changed in the future and needs adjustment accordingly.
        // 2. For texImage2D with HTMLCanvasElement input in which Alpha is already Premultiplied in this port, 
        // do AlphaDoUnmultiply if UNPACK_PREMULTIPLY_ALPHA_WEBGL is set to false.
        if (!premultiplyAlpha && m_imageHtmlDomSource != HtmlDomVideo)
            m_alphaOp = AlphaDoUnmultiply;

        // if m_imageSurface is not an image, extract a copy of the surface
        if (m_imageSurface && cairo_surface_get_type(m_imageSurface.get()) != CAIRO_SURFACE_TYPE_IMAGE) {
            RefPtr<cairo_surface_t> tmpSurface = adoptRef(cairo_image_surface_create(CAIRO_FORMAT_ARGB32, m_imageWidth, m_imageHeight));
            copyRectFromOneSurfaceToAnother(m_imageSurface.get(), tmpSurface.get(), IntSize(), IntRect(0, 0, m_imageWidth, m_imageHeight), IntSize(), CAIRO_OPERATOR_SOURCE);
            m_imageSurface = tmpSurface.release();
        }
    }

    if (!m_imageSurface)
        return false;

    ASSERT(cairo_surface_get_type(m_imageSurface.get()) == CAIRO_SURFACE_TYPE_IMAGE);

    IntSize imageSize = cairoSurfaceSize(m_imageSurface.get());
    m_imageWidth = imageSize.width();
    m_imageHeight = imageSize.height();
    if (!m_imageWidth || !m_imageHeight)
        return false;

    if (cairo_image_surface_get_format(m_imageSurface.get()) != CAIRO_FORMAT_ARGB32)
        return false;

    unsigned int srcUnpackAlignment = 1;
    size_t bytesPerRow = cairo_image_surface_get_stride(m_imageSurface.get());
    size_t bitsPerPixel = 32;
    unsigned padding = bytesPerRow - bitsPerPixel / 8 * m_imageWidth;
    if (padding) {
        srcUnpackAlignment = padding + 1;
        while (bytesPerRow % srcUnpackAlignment)
            ++srcUnpackAlignment;
    }

    m_imagePixelData = cairo_image_surface_get_data(m_imageSurface.get());
    m_imageSourceFormat = DataFormatBGRA8;
    m_imageSourceUnpackAlignment = srcUnpackAlignment;
    return true;
}
void PlatformContextCairo::drawSurfaceToContext(cairo_surface_t* surface, const FloatRect& destRect, const FloatRect& originalSrcRect, GraphicsContext& context)
{
    // Avoid invalid cairo matrix with small values.
    if (std::fabs(destRect.width()) < 0.5f || std::fabs(destRect.height()) < 0.5f)
        return;

    FloatRect srcRect = originalSrcRect;

    // We need to account for negative source dimensions by flipping the rectangle.
    if (originalSrcRect.width() < 0) {
        srcRect.setX(originalSrcRect.x() + originalSrcRect.width());
        srcRect.setWidth(std::fabs(originalSrcRect.width()));
    }
    if (originalSrcRect.height() < 0) {
        srcRect.setY(originalSrcRect.y() + originalSrcRect.height());
        srcRect.setHeight(std::fabs(originalSrcRect.height()));
    }

    RefPtr<cairo_surface_t> patternSurface = surface;
    float leftPadding = 0;
    float topPadding = 0;
    if (srcRect.x() || srcRect.y() || srcRect.size() != cairoSurfaceSize(surface)) {
        // Cairo subsurfaces don't support floating point boundaries well, so we expand the rectangle.
        IntRect expandedSrcRect(enclosingIntRect(srcRect));

        // We use a subsurface here so that we don't end up sampling outside the originalSrcRect rectangle.
        // See https://bugs.webkit.org/show_bug.cgi?id=58309
        patternSurface = adoptRef(cairo_surface_create_for_rectangle(surface, expandedSrcRect.x(),
            expandedSrcRect.y(), expandedSrcRect.width(), expandedSrcRect.height()));

        leftPadding = static_cast<float>(expandedSrcRect.x()) - floorf(srcRect.x());
        topPadding = static_cast<float>(expandedSrcRect.y()) - floorf(srcRect.y());
    }

    RefPtr<cairo_pattern_t> pattern = adoptRef(cairo_pattern_create_for_surface(patternSurface.get()));

    ASSERT(m_state);
    switch (m_state->m_imageInterpolationQuality) {
    case InterpolationNone:
    case InterpolationLow:
        cairo_pattern_set_filter(pattern.get(), CAIRO_FILTER_FAST);
        break;
    case InterpolationMedium:
    case InterpolationDefault:
        cairo_pattern_set_filter(pattern.get(), CAIRO_FILTER_GOOD);
        break;
    case InterpolationHigh:
        cairo_pattern_set_filter(pattern.get(), CAIRO_FILTER_BEST);
        break;
    }
    cairo_pattern_set_extend(pattern.get(), CAIRO_EXTEND_PAD);

    // The pattern transformation properly scales the pattern for when the source rectangle is a
    // different size than the destination rectangle. We also account for any offset we introduced
    // by expanding floating point source rectangle sizes. It's important to take the absolute value
    // of the scale since the original width and height might be negative.
    float scaleX = std::fabs(srcRect.width() / destRect.width());
    float scaleY = std::fabs(srcRect.height() / destRect.height());
    cairo_matrix_t matrix = { scaleX, 0, 0, scaleY, leftPadding, topPadding };
    cairo_pattern_set_matrix(pattern.get(), &matrix);

    ShadowBlur& shadow = context.platformContext()->shadowBlur();
    if (shadow.type() != ShadowBlur::NoShadow) {
        if (GraphicsContext* shadowContext = shadow.beginShadowLayer(context, destRect)) {
            drawPatternToCairoContext(shadowContext->platformContext()->cr(), pattern.get(), destRect, 1);
            shadow.endShadowLayer(context);
        }
    }

    cairo_save(m_cr.get());
    drawPatternToCairoContext(m_cr.get(), pattern.get(), destRect, globalAlpha());
    cairo_restore(m_cr.get());
}
Exemple #8
0
IntSize nativeImageSize(const NativeImagePtr& image)
{
    return image ? cairoSurfaceSize(image.get()) : IntSize();
}