summaryrefslogtreecommitdiff
path: root/Source/WebCore/platform/graphics
diff options
context:
space:
mode:
Diffstat (limited to 'Source/WebCore/platform/graphics')
-rw-r--r--Source/WebCore/platform/graphics/ANGLEWebKitBridge.cpp218
-rw-r--r--Source/WebCore/platform/graphics/ANGLEWebKitBridge.h53
-rw-r--r--Source/WebCore/platform/graphics/AudioTrackPrivate.h35
-rw-r--r--Source/WebCore/platform/graphics/BitmapImage.cpp839
-rw-r--r--Source/WebCore/platform/graphics/BitmapImage.h364
-rw-r--r--Source/WebCore/platform/graphics/Color.cpp403
-rw-r--r--Source/WebCore/platform/graphics/Color.h400
-rw-r--r--Source/WebCore/platform/graphics/ColorHash.h51
-rw-r--r--Source/WebCore/platform/graphics/ColorSpace.h3
-rw-r--r--Source/WebCore/platform/graphics/ComplexTextController.cpp893
-rw-r--r--Source/WebCore/platform/graphics/ComplexTextController.h231
-rw-r--r--Source/WebCore/platform/graphics/CrossfadeGeneratedImage.cpp83
-rw-r--r--Source/WebCore/platform/graphics/CrossfadeGeneratedImage.h39
-rw-r--r--Source/WebCore/platform/graphics/DashArray.h4
-rw-r--r--Source/WebCore/platform/graphics/DisplayRefreshMonitor.cpp154
-rw-r--r--Source/WebCore/platform/graphics/DisplayRefreshMonitor.h128
-rw-r--r--Source/WebCore/platform/graphics/DisplayRefreshMonitorClient.cpp56
-rw-r--r--Source/WebCore/platform/graphics/DisplayRefreshMonitorClient.h64
-rw-r--r--Source/WebCore/platform/graphics/DisplayRefreshMonitorManager.cpp130
-rw-r--r--Source/WebCore/platform/graphics/DisplayRefreshMonitorManager.h66
-rw-r--r--Source/WebCore/platform/graphics/ExtendedColor.cpp78
-rw-r--r--Source/WebCore/platform/graphics/ExtendedColor.h66
-rw-r--r--Source/WebCore/platform/graphics/Extensions3D.h47
-rw-r--r--Source/WebCore/platform/graphics/FloatPoint.cpp58
-rw-r--r--Source/WebCore/platform/graphics/FloatPoint.h100
-rw-r--r--Source/WebCore/platform/graphics/FloatPoint3D.cpp7
-rw-r--r--Source/WebCore/platform/graphics/FloatPoint3D.h2
-rw-r--r--Source/WebCore/platform/graphics/FloatPolygon.cpp4
-rw-r--r--Source/WebCore/platform/graphics/FloatPolygon.h11
-rw-r--r--Source/WebCore/platform/graphics/FloatQuad.cpp12
-rw-r--r--Source/WebCore/platform/graphics/FloatQuad.h16
-rw-r--r--Source/WebCore/platform/graphics/FloatRect.cpp57
-rw-r--r--Source/WebCore/platform/graphics/FloatRect.h77
-rw-r--r--Source/WebCore/platform/graphics/FloatRoundedRect.cpp98
-rw-r--r--Source/WebCore/platform/graphics/FloatRoundedRect.h63
-rw-r--r--Source/WebCore/platform/graphics/FloatSize.cpp24
-rw-r--r--Source/WebCore/platform/graphics/FloatSize.h82
-rw-r--r--Source/WebCore/platform/graphics/FloatSizeHash.h54
-rw-r--r--Source/WebCore/platform/graphics/Font.cpp1223
-rw-r--r--Source/WebCore/platform/graphics/Font.h553
-rw-r--r--Source/WebCore/platform/graphics/FontBaseline.h4
-rw-r--r--Source/WebCore/platform/graphics/FontCache.cpp561
-rw-r--r--Source/WebCore/platform/graphics/FontCache.h267
-rw-r--r--Source/WebCore/platform/graphics/FontCascade.cpp1491
-rw-r--r--Source/WebCore/platform/graphics/FontCascade.h373
-rw-r--r--Source/WebCore/platform/graphics/FontCascadeFonts.cpp463
-rw-r--r--Source/WebCore/platform/graphics/FontCascadeFonts.h139
-rw-r--r--Source/WebCore/platform/graphics/FontData.cpp35
-rw-r--r--Source/WebCore/platform/graphics/FontDescription.cpp171
-rw-r--r--Source/WebCore/platform/graphics/FontDescription.h361
-rw-r--r--Source/WebCore/platform/graphics/FontFastPath.cpp375
-rw-r--r--Source/WebCore/platform/graphics/FontFeatureSettings.cpp46
-rw-r--r--Source/WebCore/platform/graphics/FontFeatureSettings.h68
-rw-r--r--Source/WebCore/platform/graphics/FontGenericFamilies.cpp33
-rw-r--r--Source/WebCore/platform/graphics/FontGenericFamilies.h2
-rw-r--r--Source/WebCore/platform/graphics/FontGlyphs.cpp434
-rw-r--r--Source/WebCore/platform/graphics/FontGlyphs.h133
-rw-r--r--Source/WebCore/platform/graphics/FontMetrics.h71
-rw-r--r--Source/WebCore/platform/graphics/FontOrientation.h35
-rw-r--r--Source/WebCore/platform/graphics/FontPlatformData.cpp91
-rw-r--r--Source/WebCore/platform/graphics/FontPlatformData.h326
-rw-r--r--Source/WebCore/platform/graphics/FontRanges.cpp116
-rw-r--r--Source/WebCore/platform/graphics/FontRanges.h93
-rw-r--r--Source/WebCore/platform/graphics/FontSelector.h42
-rw-r--r--Source/WebCore/platform/graphics/FontSelectorClient.h (renamed from Source/WebCore/platform/graphics/FontSmoothingMode.h)20
-rw-r--r--Source/WebCore/platform/graphics/FontTaggedSettings.cpp81
-rw-r--r--Source/WebCore/platform/graphics/FontTaggedSettings.h153
-rw-r--r--Source/WebCore/platform/graphics/FontTraitsMask.h70
-rw-r--r--Source/WebCore/platform/graphics/FormatConverter.cpp12
-rw-r--r--Source/WebCore/platform/graphics/FormatConverter.h10
-rw-r--r--Source/WebCore/platform/graphics/GLContext.cpp209
-rw-r--r--Source/WebCore/platform/graphics/GLContext.h41
-rw-r--r--Source/WebCore/platform/graphics/GeneratedImage.cpp1
-rw-r--r--Source/WebCore/platform/graphics/GeneratedImage.h38
-rw-r--r--Source/WebCore/platform/graphics/GeometryUtilities.cpp163
-rw-r--r--Source/WebCore/platform/graphics/GeometryUtilities.h54
-rw-r--r--Source/WebCore/platform/graphics/Glyph.h2
-rw-r--r--Source/WebCore/platform/graphics/GlyphBuffer.h105
-rw-r--r--Source/WebCore/platform/graphics/GlyphMetricsMap.h68
-rw-r--r--Source/WebCore/platform/graphics/GlyphPage.h160
-rw-r--r--Source/WebCore/platform/graphics/GlyphPageTreeNode.cpp440
-rw-r--r--Source/WebCore/platform/graphics/GlyphPageTreeNode.h141
-rw-r--r--Source/WebCore/platform/graphics/Gradient.cpp12
-rw-r--r--Source/WebCore/platform/graphics/Gradient.h36
-rw-r--r--Source/WebCore/platform/graphics/GradientImage.cpp60
-rw-r--r--Source/WebCore/platform/graphics/GradientImage.h47
-rw-r--r--Source/WebCore/platform/graphics/GraphicsContext.cpp1086
-rw-r--r--Source/WebCore/platform/graphics/GraphicsContext.h1034
-rw-r--r--Source/WebCore/platform/graphics/GraphicsContext3D.cpp240
-rw-r--r--Source/WebCore/platform/graphics/GraphicsContext3D.h590
-rw-r--r--Source/WebCore/platform/graphics/GraphicsContext3DAttributes.h57
-rw-r--r--Source/WebCore/platform/graphics/GraphicsContext3DPrivate.cpp106
-rw-r--r--Source/WebCore/platform/graphics/GraphicsContext3DPrivate.h25
-rw-r--r--Source/WebCore/platform/graphics/GraphicsLayer.cpp371
-rw-r--r--Source/WebCore/platform/graphics/GraphicsLayer.h398
-rw-r--r--Source/WebCore/platform/graphics/GraphicsLayerClient.h64
-rw-r--r--Source/WebCore/platform/graphics/GraphicsLayerFactory.h10
-rw-r--r--Source/WebCore/platform/graphics/GraphicsLayerTransform.cpp14
-rw-r--r--Source/WebCore/platform/graphics/GraphicsLayerTransform.h10
-rw-r--r--Source/WebCore/platform/graphics/GraphicsLayerUpdater.cpp43
-rw-r--r--Source/WebCore/platform/graphics/GraphicsLayerUpdater.h32
-rw-r--r--Source/WebCore/platform/graphics/GraphicsTypes.cpp111
-rw-r--r--Source/WebCore/platform/graphics/GraphicsTypes.h163
-rw-r--r--Source/WebCore/platform/graphics/GraphicsTypes3D.h6
-rw-r--r--Source/WebCore/platform/graphics/ISOVTTCue.cpp153
-rw-r--r--Source/WebCore/platform/graphics/ISOVTTCue.h94
-rw-r--r--Source/WebCore/platform/graphics/Icon.h31
-rw-r--r--Source/WebCore/platform/graphics/Image.cpp238
-rw-r--r--Source/WebCore/platform/graphics/Image.h141
-rw-r--r--Source/WebCore/platform/graphics/ImageBackingStore.h224
-rw-r--r--Source/WebCore/platform/graphics/ImageBuffer.cpp147
-rw-r--r--Source/WebCore/platform/graphics/ImageBuffer.h238
-rw-r--r--Source/WebCore/platform/graphics/ImageBufferData.h8
-rw-r--r--Source/WebCore/platform/graphics/ImageFrame.cpp175
-rw-r--r--Source/WebCore/platform/graphics/ImageFrame.h164
-rw-r--r--Source/WebCore/platform/graphics/ImageFrameCache.cpp541
-rw-r--r--Source/WebCore/platform/graphics/ImageFrameCache.h168
-rw-r--r--Source/WebCore/platform/graphics/ImageObserver.h18
-rw-r--r--Source/WebCore/platform/graphics/ImageOrientation.h13
-rw-r--r--Source/WebCore/platform/graphics/ImageRenderingMode.h34
-rw-r--r--Source/WebCore/platform/graphics/ImageSource.cpp227
-rw-r--r--Source/WebCore/platform/graphics/ImageSource.h216
-rw-r--r--Source/WebCore/platform/graphics/InbandTextTrackPrivate.h14
-rw-r--r--Source/WebCore/platform/graphics/InbandTextTrackPrivateClient.h158
-rw-r--r--Source/WebCore/platform/graphics/IntPoint.cpp25
-rw-r--r--Source/WebCore/platform/graphics/IntPoint.h71
-rw-r--r--Source/WebCore/platform/graphics/IntRect.cpp18
-rw-r--r--Source/WebCore/platform/graphics/IntRect.h81
-rw-r--r--Source/WebCore/platform/graphics/IntRectExtent.h4
-rw-r--r--Source/WebCore/platform/graphics/IntRectHash.h59
-rw-r--r--Source/WebCore/platform/graphics/IntSize.cpp25
-rw-r--r--Source/WebCore/platform/graphics/IntSize.h60
-rw-r--r--Source/WebCore/platform/graphics/Latin1TextIterator.h20
-rw-r--r--Source/WebCore/platform/graphics/LayoutBoxExtent.cpp196
-rw-r--r--Source/WebCore/platform/graphics/LayoutBoxExtent.h86
-rw-r--r--Source/WebCore/platform/graphics/LayoutPoint.cpp49
-rw-r--r--Source/WebCore/platform/graphics/LayoutPoint.h69
-rw-r--r--Source/WebCore/platform/graphics/LayoutRect.cpp54
-rw-r--r--Source/WebCore/platform/graphics/LayoutRect.h80
-rw-r--r--Source/WebCore/platform/graphics/LayoutSize.cpp (renamed from Source/WebCore/platform/graphics/cairo/DrawingBufferCairo.cpp)28
-rw-r--r--Source/WebCore/platform/graphics/LayoutSize.h13
-rw-r--r--Source/WebCore/platform/graphics/LegacyCDMSession.h77
-rw-r--r--Source/WebCore/platform/graphics/MediaPlaybackTarget.h66
-rw-r--r--Source/WebCore/platform/graphics/MediaPlaybackTargetClient.h51
-rw-r--r--Source/WebCore/platform/graphics/MediaPlaybackTargetContext.h108
-rw-r--r--Source/WebCore/platform/graphics/MediaPlaybackTargetPicker.cpp95
-rw-r--r--Source/WebCore/platform/graphics/MediaPlaybackTargetPicker.h89
-rw-r--r--Source/WebCore/platform/graphics/MediaPlayer.cpp952
-rw-r--r--Source/WebCore/platform/graphics/MediaPlayer.h371
-rw-r--r--Source/WebCore/platform/graphics/MediaPlayerEnums.h (renamed from Source/WebCore/platform/graphics/FontWidthVariant.h)33
-rw-r--r--Source/WebCore/platform/graphics/MediaPlayerPrivate.h145
-rw-r--r--Source/WebCore/platform/graphics/MediaSourcePrivate.h (renamed from Source/WebCore/platform/graphics/gstreamer/SourceBufferPrivateGStreamer.h)48
-rw-r--r--Source/WebCore/platform/graphics/MediaSourcePrivateClient.h (renamed from Source/WebCore/platform/graphics/win/SharedGDIObject.h)53
-rw-r--r--Source/WebCore/platform/graphics/NamedImageGeneratedImage.cpp94
-rw-r--r--Source/WebCore/platform/graphics/NamedImageGeneratedImage.h57
-rw-r--r--Source/WebCore/platform/graphics/NativeImage.h (renamed from Source/WebCore/platform/graphics/NativeImagePtr.h)45
-rw-r--r--Source/WebCore/platform/graphics/OpenGLESShims.h12
-rw-r--r--Source/WebCore/platform/graphics/OpenGLShims.cpp34
-rw-r--r--Source/WebCore/platform/graphics/OpenGLShims.h19
-rw-r--r--Source/WebCore/platform/graphics/Path.cpp179
-rw-r--r--Source/WebCore/platform/graphics/Path.h123
-rw-r--r--Source/WebCore/platform/graphics/PathTraversalState.cpp176
-rw-r--r--Source/WebCore/platform/graphics/PathTraversalState.h66
-rw-r--r--Source/WebCore/platform/graphics/PathUtilities.cpp596
-rw-r--r--Source/WebCore/platform/graphics/PathUtilities.h46
-rw-r--r--Source/WebCore/platform/graphics/Pattern.cpp10
-rw-r--r--Source/WebCore/platform/graphics/Pattern.h22
-rw-r--r--Source/WebCore/platform/graphics/PlatformDisplay.cpp241
-rw-r--r--Source/WebCore/platform/graphics/PlatformDisplay.h107
-rw-r--r--Source/WebCore/platform/graphics/PlatformLayer.h25
-rw-r--r--Source/WebCore/platform/graphics/PlatformMediaResourceLoader.h92
-rw-r--r--Source/WebCore/platform/graphics/PlatformTextTrack.h131
-rw-r--r--Source/WebCore/platform/graphics/PlatformTimeRanges.cpp240
-rw-r--r--Source/WebCore/platform/graphics/PlatformTimeRanges.h119
-rw-r--r--Source/WebCore/platform/graphics/Region.cpp30
-rw-r--r--Source/WebCore/platform/graphics/Region.h63
-rw-r--r--Source/WebCore/platform/graphics/RoundedRect.cpp118
-rw-r--r--Source/WebCore/platform/graphics/RoundedRect.h69
-rw-r--r--Source/WebCore/platform/graphics/SVGGlyph.cpp189
-rw-r--r--Source/WebCore/platform/graphics/SVGGlyph.h113
-rw-r--r--Source/WebCore/platform/graphics/SegmentedFontData.cpp99
-rw-r--r--Source/WebCore/platform/graphics/SegmentedFontData.h85
-rw-r--r--Source/WebCore/platform/graphics/ShadowBlur.cpp338
-rw-r--r--Source/WebCore/platform/graphics/ShadowBlur.h36
-rw-r--r--Source/WebCore/platform/graphics/SimpleFontData.cpp305
-rw-r--r--Source/WebCore/platform/graphics/SimpleFontData.h373
-rw-r--r--Source/WebCore/platform/graphics/SourceBufferPrivate.h (renamed from Source/WebCore/platform/graphics/gstreamer/MediaSourceGStreamer.h)52
-rw-r--r--Source/WebCore/platform/graphics/SourceBufferPrivateClient.h83
-rw-r--r--Source/WebCore/platform/graphics/SpringSolver.h71
-rw-r--r--Source/WebCore/platform/graphics/StringTruncator.cpp113
-rw-r--r--Source/WebCore/platform/graphics/StringTruncator.h28
-rw-r--r--Source/WebCore/platform/graphics/SurrogatePairAwareTextIterator.cpp12
-rw-r--r--Source/WebCore/platform/graphics/SurrogatePairAwareTextIterator.h18
-rw-r--r--Source/WebCore/platform/graphics/TextRun.cpp25
-rw-r--r--Source/WebCore/platform/graphics/TextRun.h183
-rw-r--r--Source/WebCore/platform/graphics/TextTrackRepresentation.cpp20
-rw-r--r--Source/WebCore/platform/graphics/TextTrackRepresentation.h10
-rw-r--r--Source/WebCore/platform/graphics/TiledBacking.h85
-rw-r--r--Source/WebCore/platform/graphics/TrackPrivateBase.h41
-rw-r--r--Source/WebCore/platform/graphics/VideoTrackPrivate.h39
-rw-r--r--Source/WebCore/platform/graphics/WOFFFileFormat.cpp103
-rw-r--r--Source/WebCore/platform/graphics/WOFFFileFormat.h4
-rw-r--r--Source/WebCore/platform/graphics/WidthCache.h40
-rw-r--r--Source/WebCore/platform/graphics/WidthIterator.cpp324
-rw-r--r--Source/WebCore/platform/graphics/WidthIterator.h69
-rw-r--r--Source/WebCore/platform/graphics/WindRule.h4
-rw-r--r--Source/WebCore/platform/graphics/cairo/BackingStoreBackendCairo.h59
-rw-r--r--Source/WebCore/platform/graphics/cairo/BackingStoreBackendCairoImpl.cpp63
-rw-r--r--Source/WebCore/platform/graphics/cairo/BackingStoreBackendCairoImpl.h43
-rw-r--r--Source/WebCore/platform/graphics/cairo/BackingStoreBackendCairoX11.cpp74
-rw-r--r--Source/WebCore/platform/graphics/cairo/BackingStoreBackendCairoX11.h46
-rw-r--r--Source/WebCore/platform/graphics/cairo/BitmapImageCairo.cpp161
-rw-r--r--Source/WebCore/platform/graphics/cairo/CairoUniquePtr.h (renamed from Source/WebCore/platform/graphics/TextRenderingMode.h)28
-rw-r--r--Source/WebCore/platform/graphics/cairo/CairoUtilities.cpp130
-rw-r--r--Source/WebCore/platform/graphics/cairo/CairoUtilities.h50
-rw-r--r--Source/WebCore/platform/graphics/cairo/DrawErrorUnderline.h8
-rw-r--r--Source/WebCore/platform/graphics/cairo/FloatRectCairo.cpp8
-rw-r--r--Source/WebCore/platform/graphics/cairo/FontCairo.cpp252
-rw-r--r--Source/WebCore/platform/graphics/cairo/FontCairoHarfbuzzNG.cpp53
-rw-r--r--Source/WebCore/platform/graphics/cairo/FontCustomPlatformData.h16
-rw-r--r--Source/WebCore/platform/graphics/cairo/GradientCairo.cpp12
-rw-r--r--Source/WebCore/platform/graphics/cairo/GraphicsContext3DCairo.cpp194
-rw-r--r--Source/WebCore/platform/graphics/cairo/GraphicsContextCairo.cpp708
-rw-r--r--Source/WebCore/platform/graphics/cairo/GraphicsContextPlatformPrivateCairo.h22
-rw-r--r--Source/WebCore/platform/graphics/cairo/ImageBufferCairo.cpp370
-rw-r--r--Source/WebCore/platform/graphics/cairo/ImageBufferDataCairo.h37
-rw-r--r--Source/WebCore/platform/graphics/cairo/ImageCairo.cpp23
-rw-r--r--Source/WebCore/platform/graphics/cairo/IntRectCairo.cpp6
-rw-r--r--Source/WebCore/platform/graphics/cairo/NativeImageCairo.cpp105
-rw-r--r--Source/WebCore/platform/graphics/cairo/OwnPtrCairo.cpp52
-rw-r--r--Source/WebCore/platform/graphics/cairo/OwnPtrCairo.h44
-rw-r--r--Source/WebCore/platform/graphics/cairo/PathCairo.cpp62
-rw-r--r--Source/WebCore/platform/graphics/cairo/PatternCairo.cpp10
-rw-r--r--Source/WebCore/platform/graphics/cairo/PlatformContextCairo.cpp34
-rw-r--r--Source/WebCore/platform/graphics/cairo/PlatformContextCairo.h10
-rw-r--r--Source/WebCore/platform/graphics/cairo/PlatformPathCairo.cpp15
-rw-r--r--Source/WebCore/platform/graphics/cairo/PlatformPathCairo.h4
-rw-r--r--Source/WebCore/platform/graphics/cairo/RefPtrCairo.cpp45
-rw-r--r--Source/WebCore/platform/graphics/cairo/RefPtrCairo.h10
-rw-r--r--Source/WebCore/platform/graphics/cairo/TransformationMatrixCairo.cpp13
-rw-r--r--Source/WebCore/platform/graphics/cpu/arm/filters/FEBlendNEON.h49
-rw-r--r--Source/WebCore/platform/graphics/cpu/arm/filters/FECompositeArithmeticNEON.h14
-rw-r--r--Source/WebCore/platform/graphics/cpu/arm/filters/FEGaussianBlurNEON.h4
-rw-r--r--Source/WebCore/platform/graphics/cpu/arm/filters/FELightingNEON.cpp4
-rw-r--r--Source/WebCore/platform/graphics/cpu/arm/filters/FELightingNEON.h4
-rw-r--r--Source/WebCore/platform/graphics/cpu/arm/filters/NEONHelpers.h4
-rw-r--r--Source/WebCore/platform/graphics/cv/PixelBufferConformerCV.cpp102
-rw-r--r--Source/WebCore/platform/graphics/cv/PixelBufferConformerCV.h50
-rw-r--r--Source/WebCore/platform/graphics/cv/TextureCacheCV.h69
-rw-r--r--Source/WebCore/platform/graphics/cv/VideoTextureCopierCV.cpp212
-rw-r--r--Source/WebCore/platform/graphics/cv/VideoTextureCopierCV.h59
-rw-r--r--Source/WebCore/platform/graphics/displaylists/DisplayList.cpp131
-rw-r--r--Source/WebCore/platform/graphics/displaylists/DisplayList.h111
-rw-r--r--Source/WebCore/platform/graphics/displaylists/DisplayListItems.cpp1151
-rw-r--r--Source/WebCore/platform/graphics/displaylists/DisplayListItems.h1387
-rw-r--r--Source/WebCore/platform/graphics/displaylists/DisplayListRecorder.cpp465
-rw-r--r--Source/WebCore/platform/graphics/displaylists/DisplayListRecorder.h174
-rw-r--r--Source/WebCore/platform/graphics/displaylists/DisplayListReplayer.cpp79
-rw-r--r--Source/WebCore/platform/graphics/displaylists/DisplayListReplayer.h56
-rw-r--r--Source/WebCore/platform/graphics/egl/GLContextEGL.cpp198
-rw-r--r--Source/WebCore/platform/graphics/egl/GLContextEGL.h87
-rw-r--r--Source/WebCore/platform/graphics/egl/GLContextEGLWayland.cpp90
-rw-r--r--Source/WebCore/platform/graphics/egl/GLContextEGLX11.cpp104
-rw-r--r--Source/WebCore/platform/graphics/filters/DistantLightSource.cpp4
-rw-r--r--Source/WebCore/platform/graphics/filters/DistantLightSource.h18
-rw-r--r--Source/WebCore/platform/graphics/filters/FEBlend.cpp159
-rw-r--r--Source/WebCore/platform/graphics/filters/FEBlend.h29
-rw-r--r--Source/WebCore/platform/graphics/filters/FEColorMatrix.cpp25
-rw-r--r--Source/WebCore/platform/graphics/filters/FEColorMatrix.h16
-rw-r--r--Source/WebCore/platform/graphics/filters/FEComponentTransfer.cpp10
-rw-r--r--Source/WebCore/platform/graphics/filters/FEComponentTransfer.h15
-rw-r--r--Source/WebCore/platform/graphics/filters/FEComposite.cpp60
-rw-r--r--Source/WebCore/platform/graphics/filters/FEComposite.h22
-rw-r--r--Source/WebCore/platform/graphics/filters/FEConvolveMatrix.cpp108
-rw-r--r--Source/WebCore/platform/graphics/filters/FEConvolveMatrix.h30
-rw-r--r--Source/WebCore/platform/graphics/filters/FEDiffuseLighting.cpp12
-rw-r--r--Source/WebCore/platform/graphics/filters/FEDiffuseLighting.h13
-rw-r--r--Source/WebCore/platform/graphics/filters/FEDisplacementMap.cpp16
-rw-r--r--Source/WebCore/platform/graphics/filters/FEDisplacementMap.h17
-rw-r--r--Source/WebCore/platform/graphics/filters/FEDropShadow.cpp62
-rw-r--r--Source/WebCore/platform/graphics/filters/FEDropShadow.h18
-rw-r--r--Source/WebCore/platform/graphics/filters/FEFlood.cpp16
-rw-r--r--Source/WebCore/platform/graphics/filters/FEFlood.h26
-rw-r--r--Source/WebCore/platform/graphics/filters/FEGaussianBlur.cpp412
-rw-r--r--Source/WebCore/platform/graphics/filters/FEGaussianBlur.h50
-rw-r--r--Source/WebCore/platform/graphics/filters/FELighting.cpp8
-rw-r--r--Source/WebCore/platform/graphics/filters/FELighting.h9
-rw-r--r--Source/WebCore/platform/graphics/filters/FEMerge.cpp16
-rw-r--r--Source/WebCore/platform/graphics/filters/FEMerge.h17
-rw-r--r--Source/WebCore/platform/graphics/filters/FEMorphology.cpp30
-rw-r--r--Source/WebCore/platform/graphics/filters/FEMorphology.h15
-rw-r--r--Source/WebCore/platform/graphics/filters/FEOffset.cpp23
-rw-r--r--Source/WebCore/platform/graphics/filters/FEOffset.h15
-rw-r--r--Source/WebCore/platform/graphics/filters/FESpecularLighting.cpp12
-rw-r--r--Source/WebCore/platform/graphics/filters/FESpecularLighting.h13
-rw-r--r--Source/WebCore/platform/graphics/filters/FETile.cpp45
-rw-r--r--Source/WebCore/platform/graphics/filters/FETile.h17
-rw-r--r--Source/WebCore/platform/graphics/filters/FETurbulence.cpp18
-rw-r--r--Source/WebCore/platform/graphics/filters/FETurbulence.h18
-rw-r--r--Source/WebCore/platform/graphics/filters/Filter.h35
-rw-r--r--Source/WebCore/platform/graphics/filters/FilterEffect.cpp291
-rw-r--r--Source/WebCore/platform/graphics/filters/FilterEffect.h29
-rw-r--r--Source/WebCore/platform/graphics/filters/FilterOperation.cpp190
-rw-r--r--Source/WebCore/platform/graphics/filters/FilterOperation.h234
-rw-r--r--Source/WebCore/platform/graphics/filters/FilterOperations.cpp123
-rw-r--r--Source/WebCore/platform/graphics/filters/FilterOperations.h44
-rw-r--r--Source/WebCore/platform/graphics/filters/LightSource.h4
-rw-r--r--Source/WebCore/platform/graphics/filters/PointLightSource.cpp10
-rw-r--r--Source/WebCore/platform/graphics/filters/PointLightSource.h20
-rw-r--r--Source/WebCore/platform/graphics/filters/SourceAlpha.cpp37
-rw-r--r--Source/WebCore/platform/graphics/filters/SourceAlpha.h23
-rw-r--r--Source/WebCore/platform/graphics/filters/SourceGraphic.cpp25
-rw-r--r--Source/WebCore/platform/graphics/filters/SourceGraphic.h22
-rw-r--r--Source/WebCore/platform/graphics/filters/SpotLightSource.cpp10
-rw-r--r--Source/WebCore/platform/graphics/filters/SpotLightSource.h30
-rw-r--r--Source/WebCore/platform/graphics/freetype/FcUniquePtr.h76
-rw-r--r--Source/WebCore/platform/graphics/freetype/FontCacheFreeType.cpp307
-rw-r--r--Source/WebCore/platform/graphics/freetype/FontCustomPlatformDataFreeType.cpp34
-rw-r--r--Source/WebCore/platform/graphics/freetype/FontPlatformData.h124
-rw-r--r--Source/WebCore/platform/graphics/freetype/FontPlatformDataFreeType.cpp340
-rw-r--r--Source/WebCore/platform/graphics/freetype/GlyphPageTreeNodeFreeType.cpp22
-rw-r--r--Source/WebCore/platform/graphics/freetype/SimpleFontDataFreeType.cpp94
-rw-r--r--Source/WebCore/platform/graphics/glx/GLContextGLX.cpp312
-rw-r--r--Source/WebCore/platform/graphics/glx/GLContextGLX.h63
-rw-r--r--Source/WebCore/platform/graphics/gpu/DrawingBuffer.cpp466
-rw-r--r--Source/WebCore/platform/graphics/gpu/DrawingBuffer.h173
-rw-r--r--Source/WebCore/platform/graphics/gpu/Texture.cpp10
-rw-r--r--Source/WebCore/platform/graphics/gpu/Texture.h6
-rw-r--r--Source/WebCore/platform/graphics/gpu/TilingData.cpp4
-rw-r--r--Source/WebCore/platform/graphics/gstreamer/AudioTrackPrivateGStreamer.cpp4
-rw-r--r--Source/WebCore/platform/graphics/gstreamer/AudioTrackPrivateGStreamer.h14
-rw-r--r--Source/WebCore/platform/graphics/gstreamer/GRefPtrGStreamer.cpp142
-rw-r--r--Source/WebCore/platform/graphics/gstreamer/GRefPtrGStreamer.h39
-rw-r--r--Source/WebCore/platform/graphics/gstreamer/GStreamerUtilities.cpp127
-rw-r--r--Source/WebCore/platform/graphics/gstreamer/GStreamerUtilities.h33
-rw-r--r--Source/WebCore/platform/graphics/gstreamer/GUniquePtrGStreamer.h36
-rw-r--r--Source/WebCore/platform/graphics/gstreamer/ImageGStreamer.h8
-rw-r--r--Source/WebCore/platform/graphics/gstreamer/ImageGStreamerCairo.cpp10
-rw-r--r--Source/WebCore/platform/graphics/gstreamer/InbandMetadataTextTrackPrivateGStreamer.h33
-rw-r--r--Source/WebCore/platform/graphics/gstreamer/InbandTextTrackPrivateGStreamer.cpp78
-rw-r--r--Source/WebCore/platform/graphics/gstreamer/InbandTextTrackPrivateGStreamer.h20
-rw-r--r--Source/WebCore/platform/graphics/gstreamer/MainThreadNotifier.h105
-rw-r--r--Source/WebCore/platform/graphics/gstreamer/MediaPlayerPrivateGStreamer.cpp1511
-rw-r--r--Source/WebCore/platform/graphics/gstreamer/MediaPlayerPrivateGStreamer.h238
-rw-r--r--Source/WebCore/platform/graphics/gstreamer/MediaPlayerPrivateGStreamerBase.cpp1118
-rw-r--r--Source/WebCore/platform/graphics/gstreamer/MediaPlayerPrivateGStreamerBase.h218
-rw-r--r--Source/WebCore/platform/graphics/gstreamer/MediaPlayerPrivateGStreamerOwr.cpp501
-rw-r--r--Source/WebCore/platform/graphics/gstreamer/MediaPlayerPrivateGStreamerOwr.h128
-rw-r--r--Source/WebCore/platform/graphics/gstreamer/MediaPlayerRequestInstallMissingPluginsCallback.h61
-rw-r--r--Source/WebCore/platform/graphics/gstreamer/MediaSourceGStreamer.cpp84
-rw-r--r--Source/WebCore/platform/graphics/gstreamer/SourceBufferPrivateGStreamer.cpp68
-rw-r--r--Source/WebCore/platform/graphics/gstreamer/TextCombinerGStreamer.cpp8
-rw-r--r--Source/WebCore/platform/graphics/gstreamer/TextSinkGStreamer.cpp2
-rw-r--r--Source/WebCore/platform/graphics/gstreamer/TrackPrivateBaseGStreamer.cpp123
-rw-r--r--Source/WebCore/platform/graphics/gstreamer/TrackPrivateBaseGStreamer.h34
-rw-r--r--Source/WebCore/platform/graphics/gstreamer/VideoSinkGStreamer.cpp340
-rw-r--r--Source/WebCore/platform/graphics/gstreamer/VideoTextureCopierGStreamer.cpp190
-rw-r--r--Source/WebCore/platform/graphics/gstreamer/VideoTextureCopierGStreamer.h59
-rw-r--r--Source/WebCore/platform/graphics/gstreamer/VideoTrackPrivateGStreamer.cpp4
-rw-r--r--Source/WebCore/platform/graphics/gstreamer/VideoTrackPrivateGStreamer.h14
-rw-r--r--Source/WebCore/platform/graphics/gstreamer/WebKitMediaSourceGStreamer.cpp846
-rw-r--r--Source/WebCore/platform/graphics/gstreamer/WebKitWebSourceGStreamer.cpp1083
-rw-r--r--Source/WebCore/platform/graphics/gstreamer/eme/WebKitClearKeyDecryptorGStreamer.cpp260
-rw-r--r--Source/WebCore/platform/graphics/gstreamer/eme/WebKitClearKeyDecryptorGStreamer.h57
-rw-r--r--Source/WebCore/platform/graphics/gstreamer/eme/WebKitCommonEncryptionDecryptorGStreamer.cpp362
-rw-r--r--Source/WebCore/platform/graphics/gstreamer/eme/WebKitCommonEncryptionDecryptorGStreamer.h64
-rw-r--r--Source/WebCore/platform/graphics/gstreamer/mse/AppendPipeline.cpp1188
-rw-r--r--Source/WebCore/platform/graphics/gstreamer/mse/AppendPipeline.h165
-rw-r--r--Source/WebCore/platform/graphics/gstreamer/mse/GStreamerMediaDescription.cpp75
-rw-r--r--Source/WebCore/platform/graphics/gstreamer/mse/GStreamerMediaDescription.h58
-rw-r--r--Source/WebCore/platform/graphics/gstreamer/mse/GStreamerMediaSample.cpp120
-rw-r--r--Source/WebCore/platform/graphics/gstreamer/mse/GStreamerMediaSample.h76
-rw-r--r--Source/WebCore/platform/graphics/gstreamer/mse/MediaPlayerPrivateGStreamerMSE.cpp860
-rw-r--r--Source/WebCore/platform/graphics/gstreamer/mse/MediaPlayerPrivateGStreamerMSE.h132
-rw-r--r--Source/WebCore/platform/graphics/gstreamer/mse/MediaSourceClientGStreamerMSE.cpp216
-rw-r--r--Source/WebCore/platform/graphics/gstreamer/mse/MediaSourceClientGStreamerMSE.h70
-rw-r--r--Source/WebCore/platform/graphics/gstreamer/mse/MediaSourceGStreamer.cpp139
-rw-r--r--Source/WebCore/platform/graphics/gstreamer/mse/MediaSourceGStreamer.h88
-rw-r--r--Source/WebCore/platform/graphics/gstreamer/mse/PlaybackPipeline.cpp449
-rw-r--r--Source/WebCore/platform/graphics/gstreamer/mse/PlaybackPipeline.h79
-rw-r--r--Source/WebCore/platform/graphics/gstreamer/mse/SourceBufferPrivateGStreamer.cpp177
-rw-r--r--Source/WebCore/platform/graphics/gstreamer/mse/SourceBufferPrivateGStreamer.h94
-rw-r--r--Source/WebCore/platform/graphics/gstreamer/mse/WebKitMediaSourceGStreamer.cpp776
-rw-r--r--Source/WebCore/platform/graphics/gstreamer/mse/WebKitMediaSourceGStreamer.h (renamed from Source/WebCore/platform/graphics/gstreamer/WebKitMediaSourceGStreamer.h)46
-rw-r--r--Source/WebCore/platform/graphics/gstreamer/mse/WebKitMediaSourceGStreamerPrivate.h143
-rw-r--r--Source/WebCore/platform/graphics/gtk/ColorGtk.cpp18
-rw-r--r--Source/WebCore/platform/graphics/gtk/GdkCairoUtilities.cpp24
-rw-r--r--Source/WebCore/platform/graphics/gtk/GdkCairoUtilities.h11
-rw-r--r--Source/WebCore/platform/graphics/gtk/IconGtk.cpp12
-rw-r--r--Source/WebCore/platform/graphics/gtk/ImageBufferGtk.cpp6
-rw-r--r--Source/WebCore/platform/graphics/gtk/ImageGtk.cpp78
-rw-r--r--Source/WebCore/platform/graphics/gtk/IntPointGtk.cpp41
-rw-r--r--Source/WebCore/platform/graphics/gtk/IntRectGtk.cpp41
-rw-r--r--Source/WebCore/platform/graphics/harfbuzz/ComplexTextControllerHarfBuzz.cpp36
-rw-r--r--Source/WebCore/platform/graphics/harfbuzz/HarfBuzzFace.cpp6
-rw-r--r--Source/WebCore/platform/graphics/harfbuzz/HarfBuzzFace.h1
-rw-r--r--Source/WebCore/platform/graphics/harfbuzz/HarfBuzzFaceCairo.cpp25
-rw-r--r--Source/WebCore/platform/graphics/harfbuzz/HarfBuzzShaper.cpp150
-rw-r--r--Source/WebCore/platform/graphics/harfbuzz/HarfBuzzShaper.h39
-rw-r--r--Source/WebCore/platform/graphics/opengl/Extensions3DOpenGL.cpp124
-rw-r--r--Source/WebCore/platform/graphics/opengl/Extensions3DOpenGL.h41
-rw-r--r--Source/WebCore/platform/graphics/opengl/Extensions3DOpenGLCommon.cpp112
-rw-r--r--Source/WebCore/platform/graphics/opengl/Extensions3DOpenGLCommon.h52
-rw-r--r--Source/WebCore/platform/graphics/opengl/Extensions3DOpenGLES.cpp74
-rw-r--r--Source/WebCore/platform/graphics/opengl/Extensions3DOpenGLES.h30
-rw-r--r--Source/WebCore/platform/graphics/opengl/GLDefs.h74
-rw-r--r--Source/WebCore/platform/graphics/opengl/GLPlatformContext.cpp273
-rw-r--r--Source/WebCore/platform/graphics/opengl/GLPlatformContext.h94
-rw-r--r--Source/WebCore/platform/graphics/opengl/GLPlatformSurface.h93
-rw-r--r--Source/WebCore/platform/graphics/opengl/GraphicsContext3DOpenGL.cpp172
-rw-r--r--Source/WebCore/platform/graphics/opengl/GraphicsContext3DOpenGLCommon.cpp446
-rw-r--r--Source/WebCore/platform/graphics/opengl/GraphicsContext3DOpenGLES.cpp275
-rw-r--r--Source/WebCore/platform/graphics/opengl/TemporaryOpenGLSetting.cpp4
-rw-r--r--Source/WebCore/platform/graphics/opengl/TemporaryOpenGLSetting.h2
-rw-r--r--Source/WebCore/platform/graphics/opentype/OpenTypeCG.cpp83
-rw-r--r--Source/WebCore/platform/graphics/opentype/OpenTypeCG.h44
-rw-r--r--Source/WebCore/platform/graphics/opentype/OpenTypeMathData.cpp397
-rw-r--r--Source/WebCore/platform/graphics/opentype/OpenTypeMathData.h146
-rw-r--r--Source/WebCore/platform/graphics/opentype/OpenTypeTypes.h85
-rw-r--r--Source/WebCore/platform/graphics/opentype/OpenTypeUtilities.cpp429
-rw-r--r--Source/WebCore/platform/graphics/opentype/OpenTypeUtilities.h (renamed from Source/WebCore/platform/graphics/win/DIBPixelData.h)64
-rw-r--r--Source/WebCore/platform/graphics/opentype/OpenTypeVerticalData.cpp79
-rw-r--r--Source/WebCore/platform/graphics/opentype/OpenTypeVerticalData.h22
-rw-r--r--Source/WebCore/platform/graphics/texmap/BitmapTexture.cpp64
-rw-r--r--Source/WebCore/platform/graphics/texmap/BitmapTexture.h98
-rw-r--r--Source/WebCore/platform/graphics/texmap/BitmapTextureGL.cpp347
-rw-r--r--Source/WebCore/platform/graphics/texmap/BitmapTextureGL.h114
-rw-r--r--Source/WebCore/platform/graphics/texmap/BitmapTexturePool.cpp114
-rw-r--r--Source/WebCore/platform/graphics/texmap/BitmapTexturePool.h80
-rw-r--r--Source/WebCore/platform/graphics/texmap/ClipStack.cpp90
-rw-r--r--Source/WebCore/platform/graphics/texmap/ClipStack.h74
-rw-r--r--Source/WebCore/platform/graphics/texmap/GraphicsLayerTextureMapper.cpp274
-rw-r--r--Source/WebCore/platform/graphics/texmap/GraphicsLayerTextureMapper.h136
-rw-r--r--Source/WebCore/platform/graphics/texmap/TextureMapper.cpp135
-rw-r--r--Source/WebCore/platform/graphics/texmap/TextureMapper.h101
-rw-r--r--Source/WebCore/platform/graphics/texmap/TextureMapperAnimation.cpp (renamed from Source/WebCore/platform/graphics/GraphicsLayerAnimation.cpp)260
-rw-r--r--Source/WebCore/platform/graphics/texmap/TextureMapperAnimation.h (renamed from Source/WebCore/platform/graphics/GraphicsLayerAnimation.h)76
-rw-r--r--Source/WebCore/platform/graphics/texmap/TextureMapperBackingStore.cpp7
-rw-r--r--Source/WebCore/platform/graphics/texmap/TextureMapperBackingStore.h13
-rw-r--r--Source/WebCore/platform/graphics/texmap/TextureMapperFPSCounter.cpp8
-rw-r--r--Source/WebCore/platform/graphics/texmap/TextureMapperFPSCounter.h7
-rw-r--r--Source/WebCore/platform/graphics/texmap/TextureMapperGC3DPlatformLayer.cpp131
-rw-r--r--Source/WebCore/platform/graphics/texmap/TextureMapperGC3DPlatformLayer.h64
-rw-r--r--Source/WebCore/platform/graphics/texmap/TextureMapperGL.cpp742
-rw-r--r--Source/WebCore/platform/graphics/texmap/TextureMapperGL.h164
-rw-r--r--Source/WebCore/platform/graphics/texmap/TextureMapperImageBuffer.cpp162
-rw-r--r--Source/WebCore/platform/graphics/texmap/TextureMapperImageBuffer.h82
-rw-r--r--Source/WebCore/platform/graphics/texmap/TextureMapperLayer.cpp225
-rw-r--r--Source/WebCore/platform/graphics/texmap/TextureMapperLayer.h59
-rw-r--r--Source/WebCore/platform/graphics/texmap/TextureMapperPlatformLayer.h18
-rw-r--r--Source/WebCore/platform/graphics/texmap/TextureMapperPlatformLayerBuffer.cpp67
-rw-r--r--Source/WebCore/platform/graphics/texmap/TextureMapperPlatformLayerBuffer.h80
-rw-r--r--Source/WebCore/platform/graphics/texmap/TextureMapperPlatformLayerProxy.cpp197
-rw-r--r--Source/WebCore/platform/graphics/texmap/TextureMapperPlatformLayerProxy.h111
-rw-r--r--Source/WebCore/platform/graphics/texmap/TextureMapperShaderProgram.cpp193
-rw-r--r--Source/WebCore/platform/graphics/texmap/TextureMapperShaderProgram.h32
-rw-r--r--Source/WebCore/platform/graphics/texmap/TextureMapperSurfaceBackingStore.cpp54
-rw-r--r--Source/WebCore/platform/graphics/texmap/TextureMapperSurfaceBackingStore.h55
-rw-r--r--Source/WebCore/platform/graphics/texmap/TextureMapperTile.cpp17
-rw-r--r--Source/WebCore/platform/graphics/texmap/TextureMapperTile.h11
-rw-r--r--Source/WebCore/platform/graphics/texmap/TextureMapperTiledBackingStore.cpp92
-rw-r--r--Source/WebCore/platform/graphics/texmap/TextureMapperTiledBackingStore.h31
-rw-r--r--Source/WebCore/platform/graphics/texmap/coordinated/CoordinatedGraphicsLayer.cpp1182
-rw-r--r--Source/WebCore/platform/graphics/texmap/coordinated/CoordinatedGraphicsLayer.h246
-rw-r--r--Source/WebCore/platform/graphics/texmap/coordinated/CoordinatedGraphicsState.h195
-rw-r--r--Source/WebCore/platform/graphics/texmap/coordinated/CoordinatedImageBacking.cpp182
-rw-r--r--Source/WebCore/platform/graphics/texmap/coordinated/CoordinatedImageBacking.h95
-rw-r--r--Source/WebCore/platform/graphics/texmap/coordinated/CoordinatedSurface.cpp (renamed from Source/WebCore/platform/graphics/TypesettingFeatures.h)34
-rw-r--r--Source/WebCore/platform/graphics/texmap/coordinated/CoordinatedSurface.h76
-rw-r--r--Source/WebCore/platform/graphics/texmap/coordinated/SurfaceUpdateInfo.h48
-rw-r--r--Source/WebCore/platform/graphics/texmap/coordinated/Tile.cpp116
-rw-r--r--Source/WebCore/platform/graphics/texmap/coordinated/Tile.h72
-rw-r--r--Source/WebCore/platform/graphics/texmap/coordinated/TiledBackingStore.cpp425
-rw-r--r--Source/WebCore/platform/graphics/texmap/coordinated/TiledBackingStore.h109
-rw-r--r--Source/WebCore/platform/graphics/texmap/coordinated/TiledBackingStoreClient.h49
-rw-r--r--Source/WebCore/platform/graphics/transforms/AffineTransform.cpp71
-rw-r--r--Source/WebCore/platform/graphics/transforms/AffineTransform.h99
-rw-r--r--Source/WebCore/platform/graphics/transforms/IdentityTransformOperation.h32
-rw-r--r--Source/WebCore/platform/graphics/transforms/Matrix3DTransformOperation.cpp27
-rw-r--r--Source/WebCore/platform/graphics/transforms/Matrix3DTransformOperation.h39
-rw-r--r--Source/WebCore/platform/graphics/transforms/MatrixTransformOperation.cpp41
-rw-r--r--Source/WebCore/platform/graphics/transforms/MatrixTransformOperation.h42
-rw-r--r--Source/WebCore/platform/graphics/transforms/PerspectiveTransformOperation.cpp21
-rw-r--r--Source/WebCore/platform/graphics/transforms/PerspectiveTransformOperation.h38
-rw-r--r--Source/WebCore/platform/graphics/transforms/RotateTransformOperation.cpp20
-rw-r--r--Source/WebCore/platform/graphics/transforms/RotateTransformOperation.h41
-rw-r--r--Source/WebCore/platform/graphics/transforms/ScaleTransformOperation.cpp20
-rw-r--r--Source/WebCore/platform/graphics/transforms/ScaleTransformOperation.h41
-rw-r--r--Source/WebCore/platform/graphics/transforms/SkewTransformOperation.cpp20
-rw-r--r--Source/WebCore/platform/graphics/transforms/SkewTransformOperation.h37
-rw-r--r--Source/WebCore/platform/graphics/transforms/TransformOperation.cpp75
-rw-r--r--Source/WebCore/platform/graphics/transforms/TransformOperation.h39
-rw-r--r--Source/WebCore/platform/graphics/transforms/TransformOperations.cpp23
-rw-r--r--Source/WebCore/platform/graphics/transforms/TransformOperations.h4
-rw-r--r--Source/WebCore/platform/graphics/transforms/TransformState.cpp83
-rw-r--r--Source/WebCore/platform/graphics/transforms/TransformState.h55
-rw-r--r--Source/WebCore/platform/graphics/transforms/TransformationMatrix.cpp51
-rw-r--r--Source/WebCore/platform/graphics/transforms/TransformationMatrix.h136
-rw-r--r--Source/WebCore/platform/graphics/transforms/TranslateTransformOperation.cpp25
-rw-r--r--Source/WebCore/platform/graphics/transforms/TranslateTransformOperation.h42
-rw-r--r--Source/WebCore/platform/graphics/wayland/PlatformDisplayWayland.cpp117
-rw-r--r--Source/WebCore/platform/graphics/wayland/PlatformDisplayWayland.h (renamed from Source/WebCore/platform/graphics/FontData.h)64
-rw-r--r--Source/WebCore/platform/graphics/wayland/WlUniquePtr.h (renamed from Source/WebCore/platform/graphics/FontRenderingMode.h)40
-rw-r--r--Source/WebCore/platform/graphics/win/DIBPixelData.cpp124
-rw-r--r--Source/WebCore/platform/graphics/win/GraphicsContextCairoWin.cpp183
-rw-r--r--Source/WebCore/platform/graphics/win/GraphicsContextWin.cpp211
-rw-r--r--Source/WebCore/platform/graphics/win/LocalWindowsContext.h62
-rw-r--r--Source/WebCore/platform/graphics/win/TransformationMatrixWin.cpp46
-rw-r--r--Source/WebCore/platform/graphics/x11/PlatformDisplayX11.cpp125
-rw-r--r--Source/WebCore/platform/graphics/x11/PlatformDisplayX11.h68
-rw-r--r--Source/WebCore/platform/graphics/x11/XErrorTrapper.cpp101
-rw-r--r--Source/WebCore/platform/graphics/x11/XErrorTrapper.h54
-rw-r--r--Source/WebCore/platform/graphics/x11/XUniquePtr.h82
-rw-r--r--Source/WebCore/platform/graphics/x11/XUniqueResource.cpp91
-rw-r--r--Source/WebCore/platform/graphics/x11/XUniqueResource.h110
515 files changed, 47246 insertions, 21830 deletions
diff --git a/Source/WebCore/platform/graphics/ANGLEWebKitBridge.cpp b/Source/WebCore/platform/graphics/ANGLEWebKitBridge.cpp
index abfb06dc6..3c0d36740 100644
--- a/Source/WebCore/platform/graphics/ANGLEWebKitBridge.cpp
+++ b/Source/WebCore/platform/graphics/ANGLEWebKitBridge.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2010 Apple Inc. All rights reserved.
+ * Copyright (C) 2010, 2016 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -10,10 +10,10 @@
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
- * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
@@ -25,7 +25,7 @@
#include "config.h"
-#if USE(3D_GRAPHICS)
+#if ENABLE(GRAPHICS_CONTEXT_3D)
#include "ANGLEWebKitBridge.h"
#include "Logging.h"
@@ -33,114 +33,94 @@
namespace WebCore {
-// Temporary typedef to support an incompatible change in the ANGLE API.
-#if !defined(ANGLE_SH_VERSION) || ANGLE_SH_VERSION < 108
-typedef int ANGLEGetInfoType;
-#else
-typedef size_t ANGLEGetInfoType;
-#endif
+// FIXME: This is awful. Get rid of ANGLEWebKitBridge completely and call the libANGLE API directly to validate shaders.
-inline static ANGLEGetInfoType getValidationResultValue(const ShHandle compiler, ShShaderInfo shaderInfo)
+static void appendSymbol(const sh::ShaderVariable& variable, ANGLEShaderSymbolType symbolType, Vector<std::pair<ANGLEShaderSymbolType, sh::ShaderVariable>>& symbols, const std::string& name, const std::string& mappedName)
{
- ANGLEGetInfoType value = 0;
- ShGetInfo(compiler, shaderInfo, &value);
- return value;
+ LOG(WebGL, "Map shader symbol %s -> %s\n", name.c_str(), mappedName.c_str());
+
+ sh::ShaderVariable variableToAppend = variable;
+ variableToAppend.name = name;
+ variableToAppend.mappedName = mappedName;
+ symbols.append(std::make_pair(symbolType, variableToAppend));
+
+ if (variable.isArray()) {
+ for (unsigned i = 0; i < variable.elementCount(); i++) {
+ std::string arrayBrackets = "[" + std::to_string(i) + "]";
+ std::string arrayName = name + arrayBrackets;
+ std::string arrayMappedName = mappedName + arrayBrackets;
+ LOG(WebGL, "Map shader symbol %s -> %s\n", arrayName.c_str(), arrayMappedName.c_str());
+ variableToAppend.name = arrayName;
+ variableToAppend.mappedName = arrayMappedName;
+ symbols.append(std::make_pair(symbolType, variableToAppend));
+ }
+ }
+}
+
+static void getStructInfo(const sh::ShaderVariable& field, ANGLEShaderSymbolType symbolType, Vector<std::pair<ANGLEShaderSymbolType, sh::ShaderVariable>>& symbols, const std::string& namePrefix, const std::string& mappedNamePrefix)
+{
+ std::string name = namePrefix + '.' + field.name;
+ std::string mappedName = mappedNamePrefix + '.' + field.mappedName;
+
+ if (field.isStruct()) {
+ for (const auto& subfield : field.fields) {
+ // ANGLE restricts the depth of structs, which prevents stack overflow errors in this recursion.
+ getStructInfo(subfield, symbolType, symbols, name, mappedName);
+ }
+ } else
+ appendSymbol(field, symbolType, symbols, name, mappedName);
}
-static bool getSymbolInfo(ShHandle compiler, ShShaderInfo symbolType, Vector<ANGLEShaderSymbol>& symbols)
+static void getSymbolInfo(const sh::ShaderVariable& variable, ANGLEShaderSymbolType symbolType, Vector<std::pair<ANGLEShaderSymbolType, sh::ShaderVariable>>& symbols)
{
- ShShaderInfo symbolMaxNameLengthType;
+ if (variable.isStruct()) {
+ if (variable.isArray()) {
+ for (unsigned i = 0; i < variable.elementCount(); i++) {
+ std::string arrayBrackets = "[" + std::to_string(i) + "]";
+ std::string arrayName = variable.name + arrayBrackets;
+ std::string arrayMappedName = variable.mappedName + arrayBrackets;
+ for (const auto& field : variable.fields)
+ getStructInfo(field, symbolType, symbols, arrayName, arrayMappedName);
+ }
+ } else {
+ for (const auto& field : variable.fields)
+ getStructInfo(field, symbolType, symbols, variable.name, variable.mappedName);
+ }
+ } else
+ appendSymbol(variable, symbolType, symbols, variable.name, variable.mappedName);
+}
+static bool getSymbolInfo(ShHandle compiler, ANGLEShaderSymbolType symbolType, Vector<std::pair<ANGLEShaderSymbolType, sh::ShaderVariable>>& symbols)
+{
switch (symbolType) {
- case SH_ACTIVE_ATTRIBUTES:
- symbolMaxNameLengthType = SH_ACTIVE_ATTRIBUTE_MAX_LENGTH;
+ case SHADER_SYMBOL_TYPE_UNIFORM: {
+ auto uniforms = ShGetUniforms(compiler);
+ if (!uniforms)
+ return false;
+ for (const auto& uniform : *uniforms)
+ getSymbolInfo(uniform, symbolType, symbols);
break;
- case SH_ACTIVE_UNIFORMS:
- symbolMaxNameLengthType = SH_ACTIVE_UNIFORM_MAX_LENGTH;
+ }
+ case SHADER_SYMBOL_TYPE_VARYING: {
+ auto varyings = ShGetVaryings(compiler);
+ if (!varyings)
+ return false;
+ for (const auto& varying : *varyings)
+ getSymbolInfo(varying, symbolType, symbols);
break;
- case SH_VARYINGS:
- symbolMaxNameLengthType = SH_VARYING_MAX_LENGTH;
+ }
+ case SHADER_SYMBOL_TYPE_ATTRIBUTE: {
+ auto attributes = ShGetAttributes(compiler);
+ if (!attributes)
+ return false;
+ for (const auto& attribute : *attributes)
+ getSymbolInfo(attribute, symbolType, symbols);
break;
+ }
default:
ASSERT_NOT_REACHED();
return false;
}
-
- ANGLEGetInfoType numSymbols = getValidationResultValue(compiler, symbolType);
-
- ANGLEGetInfoType maxNameLength = getValidationResultValue(compiler, symbolMaxNameLengthType);
- if (maxNameLength <= 1)
- return false;
-
- ANGLEGetInfoType maxMappedNameLength = getValidationResultValue(compiler, SH_MAPPED_NAME_MAX_LENGTH);
- if (maxMappedNameLength <= 1)
- return false;
-
- // The maximum allowed symbol name length is 256 characters.
- Vector<char, 256> nameBuffer(maxNameLength);
- Vector<char, 256> mappedNameBuffer(maxMappedNameLength);
-
- for (ANGLEGetInfoType i = 0; i < numSymbols; ++i) {
- ANGLEShaderSymbol symbol;
- ANGLEGetInfoType nameLength = 0;
- ShPrecisionType precision;
- int staticUse;
- switch (symbolType) {
- case SH_ACTIVE_ATTRIBUTES:
- symbol.symbolType = SHADER_SYMBOL_TYPE_ATTRIBUTE;
- ShGetVariableInfo(compiler, symbolType, i, &nameLength, &symbol.size, &symbol.dataType, &precision, &staticUse, nameBuffer.data(), mappedNameBuffer.data());
- break;
- case SH_ACTIVE_UNIFORMS:
- symbol.symbolType = SHADER_SYMBOL_TYPE_UNIFORM;
- ShGetVariableInfo(compiler, symbolType, i, &nameLength, &symbol.size, &symbol.dataType, &precision, &staticUse, nameBuffer.data(), mappedNameBuffer.data());
- break;
- case SH_VARYINGS:
- symbol.symbolType = SHADER_SYMBOL_TYPE_VARYING;
- ShGetVariableInfo(compiler, symbolType, i, &nameLength, &symbol.size, &symbol.dataType, &precision, &staticUse, nameBuffer.data(), mappedNameBuffer.data());
- break;
- default:
- ASSERT_NOT_REACHED();
- return false;
- }
- if (!nameLength)
- return false;
-
- // The ShGetActive* calls above are guaranteed to produce null-terminated strings for
- // nameBuffer and mappedNameBuffer. Also, the character set for symbol names
- // is a subset of Latin-1 as specified by the OpenGL ES Shading Language, Section 3.1 and
- // WebGL, Section "Characters Outside the GLSL Source Character Set".
-
- String name = String(nameBuffer.data());
- String mappedName = String(mappedNameBuffer.data());
- LOG(WebGL, "Map shader symbol %s -> %s\n", name.utf8().data(), mappedName.utf8().data());
-
- // ANGLE returns array names in the format "array[0]".
- // The only way to know if a symbol is an array is to check if it ends with "[0]".
- // We can't check the size because regular symbols and arrays of length 1 both have a size of 1.
- symbol.isArray = name.endsWith("[0]") && mappedName.endsWith("[0]");
- if (symbol.isArray) {
- // Add a symbol for the array name without the "[0]" suffix.
- name.truncate(name.length() - 3);
- mappedName.truncate(mappedName.length() - 3);
- }
-
- symbol.name = name;
- symbol.mappedName = mappedName;
- symbol.precision = precision;
- symbol.staticUse = staticUse;
- symbols.append(symbol);
-
- if (symbol.isArray) {
- // Add symbols for each array element.
- symbol.isArray = false;
- for (int i = 0; i < symbol.size; i++) {
- String arrayBrackets = "[" + String::number(i) + "]";
- symbol.name = name + arrayBrackets;
- symbol.mappedName = mappedName + arrayBrackets;
- symbols.append(symbol);
- }
- }
- }
return true;
}
@@ -164,15 +144,15 @@ void ANGLEWebKitBridge::cleanupCompilers()
{
if (m_fragmentCompiler)
ShDestruct(m_fragmentCompiler);
- m_fragmentCompiler = 0;
+ m_fragmentCompiler = nullptr;
if (m_vertexCompiler)
ShDestruct(m_vertexCompiler);
- m_vertexCompiler = 0;
+ m_vertexCompiler = nullptr;
builtCompilers = false;
}
-void ANGLEWebKitBridge::setResources(ShBuiltInResources resources)
+void ANGLEWebKitBridge::setResources(const ShBuiltInResources& resources)
{
// Resources are (possibly) changing - cleanup compilers if we had them already
cleanupCompilers();
@@ -180,11 +160,11 @@ void ANGLEWebKitBridge::setResources(ShBuiltInResources resources)
m_resources = resources;
}
-bool ANGLEWebKitBridge::compileShaderSource(const char* shaderSource, ANGLEShaderType shaderType, String& translatedShaderSource, String& shaderValidationLog, Vector<ANGLEShaderSymbol>& symbols, int extraCompileOptions)
+bool ANGLEWebKitBridge::compileShaderSource(const char* shaderSource, ANGLEShaderType shaderType, String& translatedShaderSource, String& shaderValidationLog, Vector<std::pair<ANGLEShaderSymbolType, sh::ShaderVariable>>& symbols, int extraCompileOptions)
{
if (!builtCompilers) {
- m_fragmentCompiler = ShConstructCompiler(SH_FRAGMENT_SHADER, m_shaderSpec, m_shaderOutput, &m_resources);
- m_vertexCompiler = ShConstructCompiler(SH_VERTEX_SHADER, m_shaderSpec, m_shaderOutput, &m_resources);
+ m_fragmentCompiler = ShConstructCompiler(GL_FRAGMENT_SHADER, m_shaderSpec, m_shaderOutput, &m_resources);
+ m_vertexCompiler = ShConstructCompiler(GL_VERTEX_SHADER, m_shaderSpec, m_shaderOutput, &m_resources);
if (!m_fragmentCompiler || !m_vertexCompiler) {
cleanupCompilers();
return false;
@@ -204,31 +184,21 @@ bool ANGLEWebKitBridge::compileShaderSource(const char* shaderSource, ANGLEShade
bool validateSuccess = ShCompile(compiler, shaderSourceStrings, 1, SH_OBJECT_CODE | SH_VARIABLES | extraCompileOptions);
if (!validateSuccess) {
- int logSize = getValidationResultValue(compiler, SH_INFO_LOG_LENGTH);
- if (logSize > 1) {
- auto logBuffer = std::make_unique<char[]>(logSize);
- if (logBuffer) {
- ShGetInfoLog(compiler, logBuffer.get());
- shaderValidationLog = logBuffer.get();
- }
- }
+ const std::string& log = ShGetInfoLog(compiler);
+ if (log.length())
+ shaderValidationLog = log.c_str();
return false;
}
- int translationLength = getValidationResultValue(compiler, SH_OBJECT_CODE_LENGTH);
- if (translationLength > 1) {
- auto translationBuffer = std::make_unique<char[]>(translationLength);
- if (!translationBuffer)
- return false;
- ShGetObjectCode(compiler, translationBuffer.get());
- translatedShaderSource = translationBuffer.get();
- }
+ const std::string& objectCode = ShGetObjectCode(compiler);
+ if (objectCode.length())
+ translatedShaderSource = objectCode.c_str();
- if (!getSymbolInfo(compiler, SH_ACTIVE_ATTRIBUTES, symbols))
+ if (!getSymbolInfo(compiler, SHADER_SYMBOL_TYPE_ATTRIBUTE, symbols))
return false;
- if (!getSymbolInfo(compiler, SH_ACTIVE_UNIFORMS, symbols))
+ if (!getSymbolInfo(compiler, SHADER_SYMBOL_TYPE_UNIFORM, symbols))
return false;
- if (!getSymbolInfo(compiler, SH_VARYINGS, symbols))
+ if (!getSymbolInfo(compiler, SHADER_SYMBOL_TYPE_VARYING, symbols))
return false;
return true;
@@ -236,4 +206,4 @@ bool ANGLEWebKitBridge::compileShaderSource(const char* shaderSource, ANGLEShade
}
-#endif // USE(3D_GRAPHICS)
+#endif // ENABLE(GRAPHICS_CONTEXT_3D)
diff --git a/Source/WebCore/platform/graphics/ANGLEWebKitBridge.h b/Source/WebCore/platform/graphics/ANGLEWebKitBridge.h
index 449ab453b..9114e76c7 100644
--- a/Source/WebCore/platform/graphics/ANGLEWebKitBridge.h
+++ b/Source/WebCore/platform/graphics/ANGLEWebKitBridge.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2010 Apple Inc. All rights reserved.
+ * Copyright (C) 2010, 2016 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -10,10 +10,10 @@
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
- * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
@@ -26,22 +26,29 @@
#ifndef ANGLEWebKitBridge_h
#define ANGLEWebKitBridge_h
+#include <ANGLE/ShaderLang.h>
#include <wtf/text/CString.h>
#include <wtf/text/WTFString.h>
-#if !PLATFORM(GTK) && !PLATFORM(EFL) && !PLATFORM(WIN)
-#include "ANGLE/ShaderLang.h"
+#if PLATFORM(IOS)
+#import <OpenGLES/ES2/glext.h>
+#elif PLATFORM(MAC)
+#include <OpenGL/gl.h>
#elif PLATFORM(WIN)
-#include "GLSLANG/ShaderLang.h"
+#include "OpenGLESShims.h"
+#elif PLATFORM(GTK)
+#if USE(OPENGL_ES_2)
+#include <GLES2/gl2.h>
#else
-#include "ShaderLang.h"
+#include "OpenGLShims.h"
+#endif
#endif
namespace WebCore {
enum ANGLEShaderType {
- SHADER_TYPE_VERTEX = SH_VERTEX_SHADER,
- SHADER_TYPE_FRAGMENT = SH_FRAGMENT_SHADER,
+ SHADER_TYPE_VERTEX = GL_VERTEX_SHADER,
+ SHADER_TYPE_FRAGMENT = GL_FRAGMENT_SHADER,
};
enum ANGLEShaderSymbolType {
@@ -50,36 +57,16 @@ enum ANGLEShaderSymbolType {
SHADER_SYMBOL_TYPE_VARYING
};
-struct ANGLEShaderSymbol {
- ANGLEShaderSymbolType symbolType;
- String name;
- String mappedName;
- ShDataType dataType;
- int size;
- bool isArray;
- ShPrecisionType precision;
- int staticUse;
-
- bool isSampler() const
- {
- return symbolType == SHADER_SYMBOL_TYPE_UNIFORM
- && (dataType == SH_SAMPLER_2D
- || dataType == SH_SAMPLER_CUBE
- || dataType == SH_SAMPLER_2D_RECT_ARB
- || dataType == SH_SAMPLER_EXTERNAL_OES);
- }
-};
-
class ANGLEWebKitBridge {
public:
- ANGLEWebKitBridge(ShShaderOutput = SH_GLSL_OUTPUT, ShShaderSpec = SH_WEBGL_SPEC);
+ ANGLEWebKitBridge(ShShaderOutput = SH_GLSL_COMPATIBILITY_OUTPUT, ShShaderSpec = SH_WEBGL_SPEC);
~ANGLEWebKitBridge();
- ShBuiltInResources getResources() { return m_resources; }
- void setResources(ShBuiltInResources);
+ const ShBuiltInResources& getResources() { return m_resources; }
+ void setResources(const ShBuiltInResources&);
- bool compileShaderSource(const char* shaderSource, ANGLEShaderType, String& translatedShaderSource, String& shaderValidationLog, Vector<ANGLEShaderSymbol>& symbols, int extraCompileOptions = 0);
+ bool compileShaderSource(const char* shaderSource, ANGLEShaderType, String& translatedShaderSource, String& shaderValidationLog, Vector<std::pair<ANGLEShaderSymbolType, sh::ShaderVariable>>& symbols, int extraCompileOptions = 0);
private:
diff --git a/Source/WebCore/platform/graphics/AudioTrackPrivate.h b/Source/WebCore/platform/graphics/AudioTrackPrivate.h
index ed0212924..710d17020 100644
--- a/Source/WebCore/platform/graphics/AudioTrackPrivate.h
+++ b/Source/WebCore/platform/graphics/AudioTrackPrivate.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2012 Apple Inc. All rights reserved.
+ * Copyright (C) 2012-2017 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -10,10 +10,10 @@
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
- * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
@@ -23,8 +23,7 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef AudioTrackPrivate_h
-#define AudioTrackPrivate_h
+#pragma once
#include "TrackPrivateBase.h"
@@ -36,18 +35,18 @@ class AudioTrackPrivate;
class AudioTrackPrivateClient : public TrackPrivateBaseClient {
public:
- virtual void enabledChanged(AudioTrackPrivate*, bool) = 0;
+ virtual void enabledChanged(bool) = 0;
};
class AudioTrackPrivate : public TrackPrivateBase {
public:
- static PassRefPtr<AudioTrackPrivate> create()
+ static Ref<AudioTrackPrivate> create()
{
- return adoptRef(new AudioTrackPrivate());
+ return adoptRef(*new AudioTrackPrivate);
}
void setClient(AudioTrackPrivateClient* client) { m_client = client; }
- virtual AudioTrackPrivateClient* client() const override { return m_client; }
+ AudioTrackPrivateClient* client() const override { return m_client; }
virtual void setEnabled(bool enabled)
{
@@ -55,26 +54,22 @@ public:
return;
m_enabled = enabled;
if (m_client)
- m_client->enabledChanged(this, enabled);
- };
- virtual bool enabled() const { return m_enabled; }
+ m_client->enabledChanged(enabled);
+ }
+
+ bool enabled() const { return m_enabled; }
enum Kind { Alternative, Description, Main, MainDesc, Translation, Commentary, None };
virtual Kind kind() const { return None; }
protected:
- AudioTrackPrivate()
- : m_client(0)
- , m_enabled(false)
- {
- }
+ AudioTrackPrivate() = default;
private:
- AudioTrackPrivateClient* m_client;
- bool m_enabled;
+ AudioTrackPrivateClient* m_client { nullptr };
+ bool m_enabled { false };
};
} // namespace WebCore
#endif
-#endif
diff --git a/Source/WebCore/platform/graphics/BitmapImage.cpp b/Source/WebCore/platform/graphics/BitmapImage.cpp
index 29f9d5c1f..d7de7f828 100644
--- a/Source/WebCore/platform/graphics/BitmapImage.cpp
+++ b/Source/WebCore/platform/graphics/BitmapImage.cpp
@@ -1,6 +1,6 @@
/*
* Copyright (C) 2006 Samuel Weinig (sam.weinig@gmail.com)
- * Copyright (C) 2004, 2005, 2006, 2008 Apple Inc. All rights reserved.
+ * Copyright (C) 2004, 2005, 2006, 2008, 2015 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -11,27 +11,29 @@
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
- * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "config.h"
#include "BitmapImage.h"
#include "FloatRect.h"
+#include "GraphicsContext.h"
#include "ImageBuffer.h"
#include "ImageObserver.h"
#include "IntRect.h"
-#include "MIMETypeRegistry.h"
+#include "Logging.h"
+#include "TextStream.h"
#include "Timer.h"
#include <wtf/CurrentTime.h>
#include <wtf/Vector.h>
@@ -43,730 +45,373 @@
namespace WebCore {
-// FIXME: We should better integrate the iOS and non-iOS code in this class. Unlike other ports, the
-// iOS port caches the metadata for a frame without decoding the image.
BitmapImage::BitmapImage(ImageObserver* observer)
: Image(observer)
- , m_currentFrame(0)
- , m_frames(0)
- , m_repetitionCount(cAnimationNone)
- , m_repetitionCountStatus(Unknown)
- , m_repetitionsComplete(0)
- , m_desiredFrameStartTime(0)
- , m_decodedSize(0)
- , m_decodedPropertiesSize(0)
- , m_frameCount(0)
-#if PLATFORM(IOS)
- // FIXME: We should expose a setting to enable/disable progressive loading remove the PLATFORM(IOS)-guard.
- , m_progressiveLoadChunkTime(0)
- , m_progressiveLoadChunkCount(0)
-#endif
- , m_isSolidColor(false)
- , m_checkedForSolidColor(false)
- , m_animationFinished(false)
- , m_allDataReceived(false)
- , m_haveSize(false)
- , m_sizeAvailable(false)
- , m_hasUniformFrameSize(true)
- , m_haveFrameCount(false)
- , m_cachedImage(0)
+ , m_source(this)
{
}
-BitmapImage::~BitmapImage()
+BitmapImage::BitmapImage(NativeImagePtr&& image, ImageObserver* observer)
+ : Image(observer)
+ , m_source(WTFMove(image))
{
- invalidatePlatformData();
- stopAnimation();
}
-bool BitmapImage::hasSingleSecurityOrigin() const
+BitmapImage::~BitmapImage()
{
- return true;
+ invalidatePlatformData();
+ stopAnimation();
}
void BitmapImage::destroyDecodedData(bool destroyAll)
{
- unsigned frameBytesCleared = 0;
- const size_t clearBeforeFrame = destroyAll ? m_frames.size() : m_currentFrame;
-
- // Because we can advance frames without always needing to decode the actual
- // bitmap data, |m_currentFrame| may be larger than m_frames.size();
- // make sure not to walk off the end of the container in this case.
- for (size_t i = 0; i < std::min(clearBeforeFrame, m_frames.size()); ++i) {
- // The underlying frame isn't actually changing (we're just trying to
- // save the memory for the framebuffer data), so we don't need to clear
- // the metadata.
- unsigned frameBytes = m_frames[i].m_frameBytes;
- if (m_frames[i].clear(false))
- frameBytesCleared += frameBytes;
- }
+ if (!destroyAll)
+ m_source.destroyDecodedDataBeforeFrame(m_currentFrame);
+ else if (m_source.hasDecodingQueue())
+ m_source.destroyAllDecodedDataExcludeFrame(m_currentFrame);
+ else
+ m_source.destroyAllDecodedData();
- destroyMetadataAndNotify(frameBytesCleared);
+ // There's no need to throw away the decoder unless we're explicitly asked
+ // to destroy all of the frames.
+ if (!destroyAll || m_source.hasDecodingQueue())
+ m_source.clearFrameBufferCache(m_currentFrame);
+ else
+ m_source.clear(data());
- m_source.clear(destroyAll, clearBeforeFrame, data(), m_allDataReceived);
- return;
+ invalidatePlatformData();
}
void BitmapImage::destroyDecodedDataIfNecessary(bool destroyAll)
{
- // Animated images >5MB are considered large enough that we'll only hang on
- // to one frame at a time.
-#if PLATFORM(IOS)
- static const unsigned cLargeAnimationCutoff = 2097152;
-
- // If we have decoded frames but there is no encoded data, we shouldn't destroy
- // the decoded image since we won't be able to reconstruct it later.
- if (!data() && m_frames.size())
- return;
-#else
- static const unsigned cLargeAnimationCutoff = 5242880;
-#endif
-
// If we have decoded frames but there is no encoded data, we shouldn't destroy
// the decoded image since we won't be able to reconstruct it later.
- if (!data() && m_frames.size())
+ if (!data() && frameCount())
return;
- unsigned allFrameBytes = 0;
- for (size_t i = 0; i < m_frames.size(); ++i)
- allFrameBytes += m_frames[i].m_frameBytes;
+ if (m_source.decodedSize() < LargeAnimationCutoff)
+ return;
- if (allFrameBytes > cLargeAnimationCutoff)
- destroyDecodedData(destroyAll);
+ destroyDecodedData(destroyAll);
}
-void BitmapImage::destroyMetadataAndNotify(unsigned frameBytesCleared)
+bool BitmapImage::dataChanged(bool allDataReceived)
{
- m_isSolidColor = false;
- m_checkedForSolidColor = false;
- invalidatePlatformData();
-
- ASSERT(m_decodedSize >= frameBytesCleared);
- m_decodedSize -= frameBytesCleared;
- if (frameBytesCleared > 0) {
- frameBytesCleared += m_decodedPropertiesSize;
- m_decodedPropertiesSize = 0;
- }
- if (frameBytesCleared && imageObserver())
- imageObserver()->decodedSizeChanged(this, -safeCast<int>(frameBytesCleared));
+ return m_source.dataChanged(data(), allDataReceived);
}
-#if PLATFORM(IOS)
-void BitmapImage::cacheFrame(size_t index, float scaleHint)
-#else
-void BitmapImage::cacheFrame(size_t index)
-#endif
+NativeImagePtr BitmapImage::frameImageAtIndex(size_t index, const std::optional<SubsamplingLevel>& subsamplingLevel, const std::optional<IntSize>& sizeForDrawing, const GraphicsContext* targetContext)
{
- size_t numFrames = frameCount();
- ASSERT(m_decodedSize == 0 || numFrames > 1);
-
- if (m_frames.size() < numFrames)
- m_frames.grow(numFrames);
-
-#if PLATFORM(IOS)
- m_frames[index].m_frame = m_source.createFrameAtIndex(index, &scaleHint);
- m_frames[index].m_scale = scaleHint;
-#else
- m_frames[index].m_frame = m_source.createFrameAtIndex(index);
-#endif
- if (numFrames == 1 && m_frames[index].m_frame)
- checkForSolidColor();
-
- m_frames[index].m_orientation = m_source.orientationAtIndex(index);
- m_frames[index].m_haveMetadata = true;
- m_frames[index].m_isComplete = m_source.frameIsCompleteAtIndex(index);
- if (repetitionCount(false) != cAnimationNone)
- m_frames[index].m_duration = m_source.frameDurationAtIndex(index);
- m_frames[index].m_hasAlpha = m_source.frameHasAlphaAtIndex(index);
- m_frames[index].m_frameBytes = m_source.frameBytesAtIndex(index);
-
- const IntSize frameSize(index ? m_source.frameSizeAtIndex(index) : m_size);
- if (frameSize != m_size)
- m_hasUniformFrameSize = false;
- if (m_frames[index].m_frame) {
- int deltaBytes = safeCast<int>(m_frames[index].m_frameBytes);
- m_decodedSize += deltaBytes;
- // The fully-decoded frame will subsume the partially decoded data used
- // to determine image properties.
- deltaBytes -= m_decodedPropertiesSize;
- m_decodedPropertiesSize = 0;
- if (imageObserver())
- imageObserver()->decodedSizeChanged(this, deltaBytes);
+ if (!frameHasValidNativeImageAtIndex(index, subsamplingLevel, sizeForDrawing)) {
+ LOG(Images, "BitmapImage::%s - %p - url: %s [subsamplingLevel was %d, resampling]", __FUNCTION__, this, sourceURL().characters8(), static_cast<int>(frameSubsamplingLevelAtIndex(index)));
+ invalidatePlatformData();
}
+
+ return m_source.frameImageAtIndex(index, subsamplingLevel, sizeForDrawing, targetContext);
}
-#if PLATFORM(IOS)
-void BitmapImage::cacheFrameInfo(size_t index)
+NativeImagePtr BitmapImage::nativeImage(const GraphicsContext* targetContext)
{
- size_t numFrames = frameCount();
-
- if (m_frames.size() < numFrames)
- m_frames.resize(numFrames);
-
- ASSERT(!m_frames[index].m_haveInfo);
-
- if (shouldAnimate())
- m_frames[index].m_duration = m_source.frameDurationAtIndex(index);
- m_frames[index].m_hasAlpha = m_source.frameHasAlphaAtIndex(index);
- m_frames[index].m_haveInfo = true;
+ return frameImageAtIndex(0, SubsamplingLevel::Default, { }, targetContext);
}
-#endif
-void BitmapImage::didDecodeProperties() const
+NativeImagePtr BitmapImage::nativeImageForCurrentFrame(const GraphicsContext* targetContext)
{
- if (m_decodedSize)
- return;
- size_t updatedSize = m_source.bytesDecodedToDetermineProperties();
- if (m_decodedPropertiesSize == updatedSize)
- return;
- int deltaBytes = updatedSize - m_decodedPropertiesSize;
-#if !ASSERT_DISABLED
- bool overflow = updatedSize > m_decodedPropertiesSize && deltaBytes < 0;
- bool underflow = updatedSize < m_decodedPropertiesSize && deltaBytes > 0;
- ASSERT(!overflow && !underflow);
-#endif
- m_decodedPropertiesSize = updatedSize;
- if (imageObserver())
- imageObserver()->decodedSizeChanged(this, deltaBytes);
+ return frameImageAtIndex(m_currentFrame, SubsamplingLevel::Default, { }, targetContext);
}
-void BitmapImage::updateSize(ImageOrientationDescription description) const
+#if USE(CG)
+NativeImagePtr BitmapImage::nativeImageOfSize(const IntSize& size, const GraphicsContext* targetContext)
{
- if (!m_sizeAvailable || m_haveSize)
- return;
+ size_t count = frameCount();
- m_size = m_source.size(description);
- m_sizeRespectingOrientation = m_source.size(ImageOrientationDescription(RespectImageOrientation, description.imageOrientation()));
- m_imageOrientation = static_cast<unsigned>(description.imageOrientation());
- m_shouldRespectImageOrientation = static_cast<unsigned>(description.respectImageOrientation());
-#if PLATFORM(IOS)
- m_originalSize = m_source.originalSize();
- m_originalSizeRespectingOrientation = m_source.originalSize(RespectImageOrientation);
-#endif
- m_haveSize = true;
- didDecodeProperties();
-}
+ for (size_t i = 0; i < count; ++i) {
+ auto image = frameImageAtIndex(i, SubsamplingLevel::Default, { }, targetContext);
+ if (image && nativeImageSize(image) == size)
+ return image;
+ }
-IntSize BitmapImage::size() const
-{
- updateSize();
- return m_size;
+ // Fallback to the first frame image if we can't find the right size
+ return frameImageAtIndex(0, SubsamplingLevel::Default, { }, targetContext);
}
-IntSize BitmapImage::sizeRespectingOrientation(ImageOrientationDescription description) const
+Vector<NativeImagePtr> BitmapImage::framesNativeImages()
{
- updateSize(description);
- return m_sizeRespectingOrientation;
-}
+ Vector<NativeImagePtr> images;
+ size_t count = frameCount();
-#if PLATFORM(IOS)
-IntSize BitmapImage::originalSize() const
-{
- updateSize();
- return m_originalSize;
-}
+ for (size_t i = 0; i < count; ++i) {
+ if (auto image = frameImageAtIndex(i))
+ images.append(image);
+ }
-IntSize BitmapImage::originalSizeRespectingOrientation() const
-{
- updateSize();
- return m_originalSizeRespectingOrientation;
+ return images;
}
#endif
-IntSize BitmapImage::currentFrameSize() const
+#if !ASSERT_DISABLED
+bool BitmapImage::notSolidColor()
{
- if (!m_currentFrame || m_hasUniformFrameSize)
- return size();
- IntSize frameSize = m_source.frameSizeAtIndex(m_currentFrame);
- didDecodeProperties();
- return frameSize;
+ return size().width() != 1 || size().height() != 1 || frameCount() > 1;
}
+#endif
-bool BitmapImage::getHotSpot(IntPoint& hotSpot) const
+void BitmapImage::draw(GraphicsContext& context, const FloatRect& destRect, const FloatRect& srcRect, CompositeOperator op, BlendMode mode, ImageOrientationDescription description)
{
- bool result = m_source.getHotSpot(hotSpot);
- didDecodeProperties();
- return result;
-}
+ if (destRect.isEmpty() || srcRect.isEmpty())
+ return;
-bool BitmapImage::dataChanged(bool allDataReceived)
-{
- // Because we're modifying the current frame, clear its (now possibly
- // inaccurate) metadata as well.
-#if !PLATFORM(IOS)
- // Clear all partially-decoded frames. For most image formats, there is only
- // one frame, but at least GIF and ICO can have more. With GIFs, the frames
- // come in order and we ask to decode them in order, waiting to request a
- // subsequent frame until the prior one is complete. Given that we clear
- // incomplete frames here, this means there is at most one incomplete frame
- // (even if we use destroyDecodedData() -- since it doesn't reset the
- // metadata), and it is after all the complete frames.
- //
- // With ICOs, on the other hand, we may ask for arbitrary frames at
- // different times (e.g. because we're displaying a higher-resolution image
- // in the content area and using a lower-resolution one for the favicon),
- // and the frames aren't even guaranteed to appear in the file in the same
- // order as in the directory, so an arbitrary number of the frames might be
- // incomplete (if we ask for frames for which we've not yet reached the
- // start of the frame data), and any or none of them might be the particular
- // frame affected by appending new data here. Thus we have to clear all the
- // incomplete frames to be safe.
- unsigned frameBytesCleared = 0;
- for (size_t i = 0; i < m_frames.size(); ++i) {
- // NOTE: Don't call frameIsCompleteAtIndex() here, that will try to
- // decode any uncached (i.e. never-decoded or
- // cleared-on-a-previous-pass) frames!
- unsigned frameBytes = m_frames[i].m_frameBytes;
- if (m_frames[i].m_haveMetadata && !m_frames[i].m_isComplete)
- frameBytesCleared += (m_frames[i].clear(true) ? frameBytes : 0);
- }
- destroyMetadataAndNotify(frameBytesCleared);
-#else
- int deltaBytes = 0;
- if (!m_frames.isEmpty()) {
- int bytes = m_frames[m_frames.size() - 1].m_frameBytes;
- if (m_frames[m_frames.size() - 1].clear(true)) {
- deltaBytes += bytes;
- deltaBytes += m_decodedPropertiesSize;
- m_decodedPropertiesSize = 0;
- }
- }
- destroyMetadataAndNotify(deltaBytes);
-#endif
-
- // Feed all the data we've seen so far to the image decoder.
- m_allDataReceived = allDataReceived;
-#if PLATFORM(IOS)
- // FIXME: We should expose a setting to enable/disable progressive loading and make this
- // code conditional on it. Then we can remove the PLATFORM(IOS)-guard.
- static const double chunkLoadIntervals[] = {0, 1, 3, 6, 15};
- double interval = chunkLoadIntervals[std::min(m_progressiveLoadChunkCount, static_cast<uint16_t>(4))];
-
- bool needsUpdate = false;
- if (currentTime() - m_progressiveLoadChunkTime > interval) { // The first time through, the chunk time will be 0 and the image will get an update.
- needsUpdate = true;
- m_progressiveLoadChunkTime = currentTime();
- ASSERT(m_progressiveLoadChunkCount <= std::numeric_limits<uint16_t>::max());
- ++m_progressiveLoadChunkCount;
+ m_sizeForDrawing = enclosingIntRect(destRect).size();
+ StartAnimationResult result = internalStartAnimation();
+
+ Color color;
+ if (result == StartAnimationResult::DecodingActive && showDebugBackground())
+ color = Color::yellow;
+ else
+ color = singlePixelSolidColor();
+
+ if (color.isValid()) {
+ fillWithSolidColor(context, destRect, color, op);
+ return;
}
- if (needsUpdate || allDataReceived)
- m_source.setData(data(), allDataReceived);
-#else
- m_source.setData(data(), allDataReceived);
-#endif
- m_haveFrameCount = false;
- m_hasUniformFrameSize = true;
- return isSizeAvailable();
-}
+ float scale = subsamplingScale(context, destRect, srcRect);
+ m_currentSubsamplingLevel = allowSubsampling() ? m_source.subsamplingLevelForScale(scale) : SubsamplingLevel::Default;
+ LOG(Images, "BitmapImage::%s - %p - url: %s [m_currentFrame = %ld subsamplingLevel = %d scale = %.4f]", __FUNCTION__, this, sourceURL().characters8(), m_currentFrame, static_cast<int>(m_currentSubsamplingLevel), scale);
-String BitmapImage::filenameExtension() const
-{
- return m_source.filenameExtension();
+ ASSERT_IMPLIES(result == StartAnimationResult::DecodingActive, m_source.frameHasValidNativeImageAtIndex(m_currentFrame, m_currentSubsamplingLevel, m_sizeForDrawing));
+ auto image = frameImageAtIndex(m_currentFrame, m_currentSubsamplingLevel, m_sizeForDrawing, &context);
+ if (!image) // If it's too early we won't have an image yet.
+ return;
+
+ ImageOrientation orientation(description.imageOrientation());
+ if (description.respectImageOrientation() == RespectImageOrientation)
+ orientation = frameOrientationAtIndex(m_currentFrame);
+
+ drawNativeImage(image, context, destRect, srcRect, IntSize(size()), op, mode, orientation);
+
+ if (imageObserver())
+ imageObserver()->didDraw(this);
}
-size_t BitmapImage::frameCount()
+void BitmapImage::drawPattern(GraphicsContext& ctxt, const FloatRect& destRect, const FloatRect& tileRect, const AffineTransform& transform, const FloatPoint& phase, const FloatSize& spacing, CompositeOperator op, BlendMode blendMode)
{
- if (!m_haveFrameCount) {
- m_frameCount = m_source.frameCount();
- // If decoder is not initialized yet, m_source.frameCount() returns 0.
- if (m_frameCount) {
- didDecodeProperties();
- m_haveFrameCount = true;
- }
+ if (tileRect.isEmpty())
+ return;
+
+ if (!ctxt.drawLuminanceMask()) {
+ Image::drawPattern(ctxt, destRect, tileRect, transform, phase, spacing, op, blendMode);
+ return;
}
- return m_frameCount;
-}
-bool BitmapImage::isSizeAvailable()
-{
- if (m_sizeAvailable)
- return true;
+ if (!m_cachedImage) {
+ auto buffer = ImageBuffer::createCompatibleBuffer(expandedIntSize(tileRect.size()), ColorSpaceSRGB, ctxt);
+ if (!buffer)
+ return;
- m_sizeAvailable = m_source.isSizeAvailable();
- didDecodeProperties();
+ ImageObserver* observer = imageObserver();
- return m_sizeAvailable;
-}
+ // Temporarily reset image observer, we don't want to receive any changeInRect() calls due to this relayout.
+ setImageObserver(nullptr);
-#if !PLATFORM(IOS)
-bool BitmapImage::ensureFrameIsCached(size_t index)
-{
- if (index >= frameCount())
- return false;
+ draw(buffer->context(), tileRect, tileRect, op, blendMode, ImageOrientationDescription());
- if (index >= m_frames.size() || !m_frames[index].m_frame)
- cacheFrame(index);
- return true;
-}
-#else
-bool BitmapImage::ensureFrameInfoIsCached(size_t index)
-{
- if (index >= frameCount())
- return false;
+ setImageObserver(observer);
+ buffer->convertToLuminanceMask();
- if (index >= m_frames.size() || !m_frames[index].m_haveInfo)
- cacheFrameInfo(index);
- return true;
-}
-#endif
+ m_cachedImage = buffer->copyImage(DontCopyBackingStore, Unscaled);
+ if (!m_cachedImage)
+ return;
+ }
-PassNativeImagePtr BitmapImage::frameAtIndex(size_t index)
-{
-#if PLATFORM(IOS)
- return frameAtIndex(index, 1.0f);
-#else
- if (!ensureFrameIsCached(index))
- return nullptr;
- return m_frames[index].m_frame;
-#endif
+ ctxt.setDrawLuminanceMask(false);
+ m_cachedImage->drawPattern(ctxt, destRect, tileRect, transform, phase, spacing, op, blendMode);
}
-#if PLATFORM(IOS)
-PassNativeImagePtr BitmapImage::frameAtIndex(size_t index, float scaleHint)
+bool BitmapImage::shouldAnimate()
{
- if (index >= frameCount())
- return nullptr;
-
- if (index >= m_frames.size() || !m_frames[index].m_frame)
- cacheFrame(index, scaleHint);
- else if (std::min(1.0f, scaleHint) > m_frames[index].m_scale) {
- // If the image is already cached, but at too small a size, re-decode a larger version.
- int sizeChange = -m_frames[index].m_frameBytes;
- ASSERT(static_cast<int>(m_decodedSize) + sizeChange >= 0);
- m_frames[index].clear(true);
- invalidatePlatformData();
- m_decodedSize += sizeChange;
- if (imageObserver())
- imageObserver()->decodedSizeChanged(this, sizeChange);
-
- cacheFrame(index, scaleHint);
- }
- return m_frames[index].m_frame;
+ return repetitionCount() && !m_animationFinished && imageObserver();
}
-#endif
-bool BitmapImage::frameIsCompleteAtIndex(size_t index)
+bool BitmapImage::canAnimate()
{
-#if PLATFORM(IOS)
- // FIXME: cacheFrameInfo does not set m_isComplete. Should it?
- if (!ensureFrameInfoIsCached(index))
- return false;
-#else
- if (!ensureFrameIsCached(index))
- return false;
-#endif
- return m_frames[index].m_isComplete;
+ return shouldAnimate() && frameCount() > 1;
}
-float BitmapImage::frameDurationAtIndex(size_t index)
+bool BitmapImage::isLargeImageAsyncDecodingRequired()
{
-#if PLATFORM(IOS)
- if (!ensureFrameInfoIsCached(index))
- return 0;
-#else
- if (!ensureFrameIsCached(index))
- return 0;
-#endif
- return m_frames[index].m_duration;
+ return !canAnimate() && allowLargeImageAsyncDecoding() && (isAsyncDecodingForcedForTesting() || m_source.isAsyncDecodingRequired());
}
-PassNativeImagePtr BitmapImage::nativeImageForCurrentFrame()
+bool BitmapImage::isAnimatedImageAsyncDecodingRequired()
{
- return frameAtIndex(currentFrame());
+ return canAnimate() && allowAnimatedImageAsyncDecoding() && (isAsyncDecodingForcedForTesting() || m_source.isAsyncDecodingRequired());
}
-bool BitmapImage::frameHasAlphaAtIndex(size_t index)
+void BitmapImage::clearTimer()
{
-#if PLATFORM(IOS)
- if (!ensureFrameInfoIsCached(index))
- return true; // FIXME: Why would an invalid index return true here?
-#else
- if (m_frames.size() <= index)
- return true;
-#endif
- if (m_frames[index].m_haveMetadata)
- return m_frames[index].m_hasAlpha;
-
- return m_source.frameHasAlphaAtIndex(index);
+ m_frameTimer = nullptr;
}
-bool BitmapImage::currentFrameKnownToBeOpaque()
+void BitmapImage::startTimer(double delay)
{
- return !frameHasAlphaAtIndex(currentFrame());
+ ASSERT(!m_frameTimer);
+ m_frameTimer = std::make_unique<Timer>(*this, &BitmapImage::advanceAnimation);
+ m_frameTimer->startOneShot(delay);
}
-ImageOrientation BitmapImage::frameOrientationAtIndex(size_t index)
+BitmapImage::StartAnimationResult BitmapImage::internalStartAnimation()
{
-#if PLATFORM(IOS)
- // FIXME: cacheFrameInfo does not set m_orientation. Should it?
- if (!ensureFrameInfoIsCached(index))
- return DefaultImageOrientation;
-#else
- if (!ensureFrameIsCached(index))
- return DefaultImageOrientation;
-#endif
+ if (!canAnimate())
+ return StartAnimationResult::CannotStart;
- if (m_frames[index].m_haveMetadata)
- return m_frames[index].m_orientation;
+ if (m_frameTimer)
+ return StartAnimationResult::TimerActive;
+
+ // Don't start a new animation until we draw the frame that is currently being decoded.
+ size_t nextFrame = (m_currentFrame + 1) % frameCount();
+ if (frameIsBeingDecodedAtIndex(nextFrame, m_sizeForDrawing)) {
+ LOG(Images, "BitmapImage::%s - %p - url: %s [nextFrame = %ld is being decoded]", __FUNCTION__, this, sourceURL().characters8(), nextFrame);
+ return StartAnimationResult::DecodingActive;
+ }
- return m_source.orientationAtIndex(index);
-}
+ if (m_currentFrame >= frameCount() - 1) {
+ // Don't advance past the last frame if we haven't decoded the whole image
+ // yet and our repetition count is potentially unset. The repetition count
+ // in a GIF can potentially come after all the rest of the image data, so
+ // wait on it.
+ if (!m_source.isAllDataReceived() && repetitionCount() == RepetitionCountOnce)
+ return StartAnimationResult::IncompleteData;
-#if !ASSERT_DISABLED
-bool BitmapImage::notSolidColor()
-{
- return size().width() != 1 || size().height() != 1 || frameCount() > 1;
-}
-#endif
+ ++m_repetitionsComplete;
-int BitmapImage::repetitionCount(bool imageKnownToBeComplete)
-{
- if ((m_repetitionCountStatus == Unknown) || ((m_repetitionCountStatus == Uncertain) && imageKnownToBeComplete)) {
- // Snag the repetition count. If |imageKnownToBeComplete| is false, the
- // repetition count may not be accurate yet for GIFs; in this case the
- // decoder will default to cAnimationLoopOnce, and we'll try and read
- // the count again once the whole image is decoded.
- m_repetitionCount = m_source.repetitionCount();
- didDecodeProperties();
- m_repetitionCountStatus = (imageKnownToBeComplete || m_repetitionCount == cAnimationNone) ? Certain : Uncertain;
+ // Check for the end of animation.
+ if (repetitionCount() != RepetitionCountInfinite && m_repetitionsComplete >= repetitionCount()) {
+ m_animationFinished = true;
+ destroyDecodedDataIfNecessary(false);
+ return StartAnimationResult::CannotStart;
+ }
+
+ destroyDecodedDataIfNecessary(true);
}
- return m_repetitionCount;
-}
-bool BitmapImage::shouldAnimate()
-{
- return (repetitionCount(false) != cAnimationNone && !m_animationFinished && imageObserver());
-}
+ // Don't advance the animation to an incomplete frame.
+ if (!m_source.isAllDataReceived() && !frameIsCompleteAtIndex(nextFrame))
+ return StartAnimationResult::IncompleteData;
-void BitmapImage::startAnimation(bool catchUpIfNecessary)
-{
- if (m_frameTimer || !shouldAnimate() || frameCount() <= 1)
- return;
+ double time = monotonicallyIncreasingTime();
- // If we aren't already animating, set now as the animation start time.
- const double time = monotonicallyIncreasingTime();
+ // Handle initial state.
if (!m_desiredFrameStartTime)
m_desiredFrameStartTime = time;
- // Don't advance the animation to an incomplete frame.
- size_t nextFrame = (m_currentFrame + 1) % frameCount();
- if (!m_allDataReceived && !frameIsCompleteAtIndex(nextFrame))
- return;
-
- // Don't advance past the last frame if we haven't decoded the whole image
- // yet and our repetition count is potentially unset. The repetition count
- // in a GIF can potentially come after all the rest of the image data, so
- // wait on it.
- if (!m_allDataReceived && repetitionCount(false) == cAnimationLoopOnce && m_currentFrame >= (frameCount() - 1))
- return;
-
- // Determine time for next frame to start. By ignoring paint and timer lag
- // in this calculation, we make the animation appear to run at its desired
- // rate regardless of how fast it's being repainted.
- const double currentDuration = frameDurationAtIndex(m_currentFrame);
- m_desiredFrameStartTime += currentDuration;
-
-#if !PLATFORM(IOS)
- // When an animated image is more than five minutes out of date, the
- // user probably doesn't care about resyncing and we could burn a lot of
- // time looping through frames below. Just reset the timings.
- const double cAnimationResyncCutoff = 5 * 60;
- if ((time - m_desiredFrameStartTime) > cAnimationResyncCutoff)
- m_desiredFrameStartTime = time + currentDuration;
+ // Setting 'm_desiredFrameStartTime' to 'time' means we are late; otherwise we are early.
+ m_desiredFrameStartTime = std::max(time, m_desiredFrameStartTime + frameDurationAtIndex(m_currentFrame));
+
+ // Request async decoding for nextFrame only if this is required. If nextFrame is not in the frameCache,
+ // it will be decoded on a separate work queue. When decoding nextFrame finishes, we will be notified
+ // through the callback newFrameNativeImageAvailableAtIndex(). Otherwise, advanceAnimation() will be called
+ // when the timer fires and m_currentFrame will be advanced to nextFrame since it is not being decoded.
+ if (m_sizeForDrawing && isAnimatedImageAsyncDecodingRequired()) {
+ bool isAsyncDecode = m_source.requestFrameAsyncDecodingAtIndex(nextFrame, m_currentSubsamplingLevel, *m_sizeForDrawing);
+
+#if !LOG_DISABLED
+ if (isAsyncDecode)
+ LOG(Images, "BitmapImage::%s - %p - url: %s [requesting async decoding for nextFrame = %ld]", __FUNCTION__, this, sourceURL().characters8(), nextFrame);
+ else
+ LOG(Images, "BitmapImage::%s - %p - url: %s [cachedFrameCount = %ld nextFrame = %ld]", __FUNCTION__, this, sourceURL().characters8(), ++m_cachedFrameCount, nextFrame);
#else
- // Maintaining frame-to-frame delays is more important than
- // maintaining absolute animation timing, so reset the timings each frame.
- m_desiredFrameStartTime = time + currentDuration;
+ UNUSED_PARAM(isAsyncDecode);
#endif
- // The image may load more slowly than it's supposed to animate, so that by
- // the time we reach the end of the first repetition, we're well behind.
- // Clamp the desired frame start time in this case, so that we don't skip
- // frames (or whole iterations) trying to "catch up". This is a tradeoff:
- // It guarantees users see the whole animation the second time through and
- // don't miss any repetitions, and is closer to what other browsers do; on
- // the other hand, it makes animations "less accurate" for pages that try to
- // sync an image and some other resource (e.g. audio), especially if users
- // switch tabs (and thus stop drawing the animation, which will pause it)
- // during that initial loop, then switch back later.
- if (nextFrame == 0 && m_repetitionsComplete == 0 && m_desiredFrameStartTime < time)
- m_desiredFrameStartTime = time;
+ m_desiredFrameDecodeTimeForTesting = time + std::max(m_frameDecodingDurationForTesting, 0.0f);
+ }
- if (!catchUpIfNecessary || time < m_desiredFrameStartTime) {
- // Haven't yet reached time for next frame to start; delay until then.
- m_frameTimer = std::make_unique<Timer<BitmapImage>>(this, &BitmapImage::advanceAnimation);
- m_frameTimer->startOneShot(std::max(m_desiredFrameStartTime - time, 0.));
- } else {
- // We've already reached or passed the time for the next frame to start.
- // See if we've also passed the time for frames after that to start, in
- // case we need to skip some frames entirely. Remember not to advance
- // to an incomplete frame.
- for (size_t frameAfterNext = (nextFrame + 1) % frameCount(); frameIsCompleteAtIndex(frameAfterNext); frameAfterNext = (nextFrame + 1) % frameCount()) {
- // Should we skip the next frame?
- double frameAfterNextStartTime = m_desiredFrameStartTime + frameDurationAtIndex(nextFrame);
- if (time < frameAfterNextStartTime)
- break;
-
- // Yes; skip over it without notifying our observers.
- if (!internalAdvanceAnimation(true))
- return;
- m_desiredFrameStartTime = frameAfterNextStartTime;
- nextFrame = frameAfterNext;
- }
+ ASSERT(!m_frameTimer);
+ startTimer(m_desiredFrameStartTime - time);
+ return StartAnimationResult::Started;
+}
- // Draw the next frame immediately. Note that m_desiredFrameStartTime
- // may be in the past, meaning the next time through this function we'll
- // kick off the next advancement sooner than this frame's duration would
- // suggest.
- if (internalAdvanceAnimation(false)) {
- // The image region has been marked dirty, but once we return to our
- // caller, draw() will clear it, and nothing will cause the
- // animation to advance again. We need to start the timer for the
- // next frame running, or the animation can hang. (Compare this
- // with when advanceAnimation() is called, and the region is dirtied
- // while draw() is not in the callstack, meaning draw() gets called
- // to update the region and thus startAnimation() is reached again.)
- // NOTE: For large images with slow or heavily-loaded systems,
- // throwing away data as we go (see destroyDecodedData()) means we
- // can spend so much time re-decoding data above that by the time we
- // reach here we're behind again. If we let startAnimation() run
- // the catch-up code again, we can get long delays without painting
- // as we race the timer, or even infinite recursion. In this
- // situation the best we can do is to simply change frames as fast
- // as possible, so force startAnimation() to set a zero-delay timer
- // and bail out if we're not caught up.
- startAnimation(false);
+void BitmapImage::advanceAnimation()
+{
+ clearTimer();
+
+ // Pretend as if decoding nextFrame has taken m_frameDecodingDurationForTesting from
+ // the time this decoding was requested.
+ if (isAsyncDecodingForcedForTesting()) {
+ double time = monotonicallyIncreasingTime();
+ // Start a timer with the remaining time from now till the m_desiredFrameDecodeTime.
+ if (m_desiredFrameDecodeTimeForTesting > std::max(time, m_desiredFrameStartTime)) {
+ startTimer(m_desiredFrameDecodeTimeForTesting - time);
+ return;
}
}
+
+ // Don't advance to nextFrame unless its decoding has finished or was not required.
+ size_t nextFrame = (m_currentFrame + 1) % frameCount();
+ if (!frameIsBeingDecodedAtIndex(nextFrame, m_sizeForDrawing))
+ internalAdvanceAnimation();
+ else {
+ // Force repaint if showDebugBackground() is on.
+ if (showDebugBackground())
+ imageObserver()->changedInRect(this);
+ LOG(Images, "BitmapImage::%s - %p - url: %s [lateFrameCount = %ld nextFrame = %ld]", __FUNCTION__, this, sourceURL().characters8(), ++m_lateFrameCount, nextFrame);
+ }
+}
+
+void BitmapImage::internalAdvanceAnimation()
+{
+ m_currentFrame = (m_currentFrame + 1) % frameCount();
+ ASSERT(!frameIsBeingDecodedAtIndex(m_currentFrame, m_sizeForDrawing));
+
+ destroyDecodedDataIfNecessary(false);
+
+ if (imageObserver())
+ imageObserver()->animationAdvanced(this);
+
+ LOG(Images, "BitmapImage::%s - %p - url: %s [m_currentFrame = %ld]", __FUNCTION__, this, sourceURL().characters8(), m_currentFrame);
}
void BitmapImage::stopAnimation()
{
- // This timer is used to animate all occurrences of this image. Don't invalidate
+ // This timer is used to animate all occurrences of this image. Don't invalidate
// the timer unless all renderers have stopped drawing.
- m_frameTimer = nullptr;
+ clearTimer();
+ m_source.stopAsyncDecodingQueue();
}
void BitmapImage::resetAnimation()
{
stopAnimation();
m_currentFrame = 0;
- m_repetitionsComplete = 0;
+ m_repetitionsComplete = RepetitionCountNone;
m_desiredFrameStartTime = 0;
m_animationFinished = false;
-
+
// For extremely large animations, when the animation is reset, we just throw everything away.
destroyDecodedDataIfNecessary(true);
}
-void BitmapImage::drawPattern(GraphicsContext* ctxt, const FloatRect& tileRect, const AffineTransform& transform,
- const FloatPoint& phase, ColorSpace styleColorSpace, CompositeOperator op, const FloatRect& destRect, BlendMode blendMode)
+void BitmapImage::newFrameNativeImageAvailableAtIndex(size_t index)
{
- if (tileRect.isEmpty())
- return;
-
- if (!ctxt->drawLuminanceMask()) {
- Image::drawPattern(ctxt, tileRect, transform, phase, styleColorSpace, op, destRect, blendMode);
- return;
- }
- if (!m_cachedImage) {
- std::unique_ptr<ImageBuffer> buffer = ImageBuffer::create(expandedIntSize(tileRect.size()));
- ASSERT(buffer.get());
-
- ImageObserver* observer = imageObserver();
- ASSERT(observer);
-
- // Temporarily reset image observer, we don't want to receive any changeInRect() calls due to this relayout.
- setImageObserver(0);
-
- draw(buffer->context(), tileRect, tileRect, styleColorSpace, op, blendMode, ImageOrientationDescription());
+ UNUSED_PARAM(index);
+ ASSERT(index == (m_currentFrame + 1) % frameCount());
- setImageObserver(observer);
- buffer->convertToLuminanceMask();
-
- m_cachedImage = buffer->copyImage(DontCopyBackingStore, Unscaled);
- m_cachedImage->setSpaceSize(spaceSize());
-
- setImageObserver(observer);
- }
-
- ctxt->setDrawLuminanceMask(false);
- m_cachedImage->drawPattern(ctxt, tileRect, transform, phase, styleColorSpace, op, destRect, blendMode);
+ // Don't advance to nextFrame unless the timer was fired before its decoding finishes.
+ if (canAnimate() && !m_frameTimer)
+ internalAdvanceAnimation();
+ else
+ LOG(Images, "BitmapImage::%s - %p - url: %s [earlyFrameCount = %ld nextFrame = %ld]", __FUNCTION__, this, sourceURL().characters8(), ++m_earlyFrameCount, index);
}
-
-void BitmapImage::advanceAnimation(Timer<BitmapImage>&)
+void BitmapImage::dump(TextStream& ts) const
{
- internalAdvanceAnimation(false);
- // At this point the image region has been marked dirty, and if it's
- // onscreen, we'll soon make a call to draw(), which will call
- // startAnimation() again to keep the animation moving.
-}
-
-bool BitmapImage::internalAdvanceAnimation(bool skippingFrames)
-{
- // Stop the animation.
- stopAnimation();
+ Image::dump(ts);
-#if !PLATFORM(IOS)
- // See if anyone is still paying attention to this animation. If not, we don't
- // advance and will remain suspended at the current frame until the animation is resumed.
- if (!skippingFrames && imageObserver()->shouldPauseAnimation(this))
- return false;
-#endif
-
- ++m_currentFrame;
- bool advancedAnimation = true;
- bool destroyAll = false;
- if (m_currentFrame >= frameCount()) {
- ++m_repetitionsComplete;
-
- // Get the repetition count again. If we weren't able to get a
- // repetition count before, we should have decoded the whole image by
- // now, so it should now be available.
- // Note that we don't need to special-case cAnimationLoopOnce here
- // because it is 0 (see comments on its declaration in ImageSource.h).
- if (repetitionCount(true) != cAnimationLoopInfinite && m_repetitionsComplete > m_repetitionCount) {
- m_animationFinished = true;
- m_desiredFrameStartTime = 0;
- --m_currentFrame;
- advancedAnimation = false;
- } else {
- m_currentFrame = 0;
- destroyAll = true;
- }
- }
- destroyDecodedDataIfNecessary(destroyAll);
-
- // We need to draw this frame if we advanced to it while not skipping, or if
- // while trying to skip frames we hit the last frame and thus had to stop.
- if (skippingFrames != advancedAnimation)
- imageObserver()->animationAdvanced(this);
- return advancedAnimation;
-}
-
-bool BitmapImage::mayFillWithSolidColor()
-{
- if (!m_checkedForSolidColor && frameCount() > 0) {
- checkForSolidColor();
- // WINCE PORT: checkForSolidColor() doesn't set m_checkedForSolidColor until
- // it gets enough information to make final decision.
-#if !OS(WINCE)
- ASSERT(m_checkedForSolidColor);
-#endif
- }
- return m_isSolidColor && !m_currentFrame;
-}
-
-Color BitmapImage::solidColor() const
-{
- return m_solidColor;
-}
+ if (isAnimated())
+ ts.dumpProperty("current-frame", m_currentFrame);
-bool BitmapImage::canAnimate()
-{
- return shouldAnimate() && frameCount() > 1;
+ m_source.dump(ts);
}
}
diff --git a/Source/WebCore/platform/graphics/BitmapImage.h b/Source/WebCore/platform/graphics/BitmapImage.h
index c2161b61a..30add02af 100644
--- a/Source/WebCore/platform/graphics/BitmapImage.h
+++ b/Source/WebCore/platform/graphics/BitmapImage.h
@@ -1,6 +1,6 @@
/*
* Copyright (C) 2006 Samuel Weinig (sam.weinig@gmail.com)
- * Copyright (C) 2004, 2005, 2006, 2013 Apple Computer, Inc. All rights reserved.
+ * Copyright (C) 2004, 2005, 2006, 2013 Apple Inc. All rights reserved.
* Copyright (C) 2008-2009 Torch Mobile, Inc.
*
* Redistribution and use in source and binary forms, with or without
@@ -12,10 +12,10 @@
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
- * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
@@ -25,16 +25,17 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef BitmapImage_h
-#define BitmapImage_h
+#pragma once
#include "Image.h"
#include "Color.h"
+#include "ImageObserver.h"
#include "ImageOrientation.h"
#include "ImageSource.h"
#include "IntSize.h"
+#include "URL.h"
-#if PLATFORM(MAC)
+#if USE(CG) || USE(APPKIT)
#include <wtf/RetainPtr.h>
#endif
@@ -47,310 +48,183 @@ typedef struct HBITMAP__ *HBITMAP;
#endif
namespace WebCore {
- struct FrameData;
-}
-namespace WTF {
- template<> struct VectorTraits<WebCore::FrameData> : public SimpleClassVectorTraits {
- static const bool canInitializeWithMemset = false; // Not all FrameData members initialize to 0.
- };
-}
-
-namespace WebCore {
-
-template <typename T> class Timer;
-
-// ================================================
-// FrameData Class
-// ================================================
-
-struct FrameData {
- WTF_MAKE_NONCOPYABLE(FrameData);
-public:
- FrameData()
- : m_frame(0)
- , m_orientation(DefaultImageOrientation)
-#if PLATFORM(IOS)
- , m_scale(0)
- , m_haveInfo(false)
-#endif
- , m_duration(0)
- , m_haveMetadata(false)
- , m_isComplete(false)
- , m_hasAlpha(true)
- , m_frameBytes(0)
- {
- }
-
- ~FrameData()
- {
- clear(true);
- }
-
- // Clear the cached image data on the frame, and (optionally) the metadata.
- // Returns whether there was cached image data to clear.
- bool clear(bool clearMetadata);
-
- NativeImagePtr m_frame;
- ImageOrientation m_orientation;
-#if PLATFORM(IOS)
- float m_scale;
- bool m_haveInfo;
-#endif
- float m_duration;
- bool m_haveMetadata : 1;
- bool m_isComplete : 1;
- bool m_hasAlpha : 1;
- unsigned m_frameBytes;
-};
-
-// =================================================
-// BitmapImage Class
-// =================================================
-
-// FIXME: We should better integrate the iOS and non-iOS code in this class. Unlike other ports, the
-// iOS port caches the metadata for a frame without decoding the image.
+class Timer;
class BitmapImage final : public Image {
- friend class GeneratedImage;
- friend class CrossfadeGeneratedImage;
- friend class GradientImage;
- friend class GraphicsContext;
public:
- static PassRefPtr<BitmapImage> create(PassNativeImagePtr nativeImage, ImageObserver* observer = 0)
+ static Ref<BitmapImage> create(NativeImagePtr&& nativeImage, ImageObserver* observer = nullptr)
{
- return adoptRef(new BitmapImage(nativeImage, observer));
+ return adoptRef(*new BitmapImage(WTFMove(nativeImage), observer));
}
- static PassRefPtr<BitmapImage> create(ImageObserver* observer = 0)
+ static Ref<BitmapImage> create(ImageObserver* observer = nullptr)
{
- return adoptRef(new BitmapImage(observer));
+ return adoptRef(*new BitmapImage(observer));
}
#if PLATFORM(WIN)
- static PassRefPtr<BitmapImage> create(HBITMAP);
+ WEBCORE_EXPORT static RefPtr<BitmapImage> create(HBITMAP);
#endif
virtual ~BitmapImage();
- virtual bool isBitmapImage() const override { return true; }
+ bool hasSingleSecurityOrigin() const override { return true; }
- virtual bool hasSingleSecurityOrigin() const override;
+ bool dataChanged(bool allDataReceived) override;
+ unsigned decodedSize() const { return m_source.decodedSize(); }
- virtual IntSize size() const override;
- IntSize sizeRespectingOrientation(ImageOrientationDescription = ImageOrientationDescription()) const;
-#if PLATFORM(IOS)
- virtual IntSize originalSize() const;
- IntSize originalSizeRespectingOrientation() const;
-#endif
- IntSize currentFrameSize() const;
- virtual bool getHotSpot(IntPoint&) const override;
+ bool isSizeAvailable() const { return m_source.isSizeAvailable(); }
+ size_t frameCount() const { return m_source.frameCount(); }
+ RepetitionCount repetitionCount() const { return m_source.repetitionCount(); }
+ String filenameExtension() const override { return m_source.filenameExtension(); }
+ std::optional<IntPoint> hotSpot() const override { return m_source.hotSpot(); }
- unsigned decodedSize() const { return m_decodedSize; }
+ // FloatSize due to override.
+ FloatSize size() const override { return m_source.size(); }
+ IntSize sizeRespectingOrientation() const { return m_source.sizeRespectingOrientation(); }
+ Color singlePixelSolidColor() const override { return m_source.singlePixelSolidColor(); }
- virtual bool dataChanged(bool allDataReceived) override;
- virtual String filenameExtension() const override;
+ bool frameIsBeingDecodedAtIndex(size_t index, const std::optional<IntSize>& sizeForDrawing) const { return m_source.frameIsBeingDecodedAtIndex(index, sizeForDrawing); }
+ bool frameHasDecodedNativeImage(size_t index) const { return m_source.frameHasDecodedNativeImage(index); }
+ bool frameIsCompleteAtIndex(size_t index) const { return m_source.frameIsCompleteAtIndex(index); }
+ bool frameHasAlphaAtIndex(size_t index) const { return m_source.frameHasAlphaAtIndex(index); }
+ bool frameHasValidNativeImageAtIndex(size_t index, const std::optional<SubsamplingLevel>& subsamplingLevel, const std::optional<IntSize>& sizeForDrawing) const { return m_source.frameHasValidNativeImageAtIndex(index, subsamplingLevel, sizeForDrawing); }
+ SubsamplingLevel frameSubsamplingLevelAtIndex(size_t index) const { return m_source.frameSubsamplingLevelAtIndex(index); }
- // It may look unusual that there is no start animation call as public API. This is because
- // we start and stop animating lazily. Animation begins whenever someone draws the image. It will
- // automatically pause once all observers no longer want to render the image anywhere.
- virtual void stopAnimation() override;
- virtual void resetAnimation() override;
+ float frameDurationAtIndex(size_t index) const { return m_source.frameDurationAtIndex(index); }
+ ImageOrientation frameOrientationAtIndex(size_t index) const { return m_source.frameOrientationAtIndex(index); }
- virtual void drawPattern(GraphicsContext*, const FloatRect& srcRect, const AffineTransform& patternTransform,
- const FloatPoint& phase, ColorSpace styleColorSpace, CompositeOperator, const FloatRect& destRect, BlendMode = BlendModeNormal) override;
+ size_t currentFrame() const { return m_currentFrame; }
+ bool currentFrameKnownToBeOpaque() const override { return !frameHasAlphaAtIndex(currentFrame()); }
+ ImageOrientation orientationForCurrentFrame() const override { return frameOrientationAtIndex(currentFrame()); }
- // Accessors for native image formats.
+ bool isAsyncDecodingForcedForTesting() const { return m_frameDecodingDurationForTesting > 0; }
+ void setFrameDecodingDurationForTesting(float duration) { m_frameDecodingDurationForTesting = duration; }
+ bool isLargeImageAsyncDecodingRequired();
+ bool isAnimatedImageAsyncDecodingRequired();
+ // Accessors for native image formats.
#if USE(APPKIT)
- virtual NSImage* getNSImage() override;
-#endif
-
-#if PLATFORM(MAC)
- virtual CFDataRef getTIFFRepresentation() override;
+ NSImage *nsImage() override;
+ RetainPtr<NSImage> snapshotNSImage() override;
#endif
-#if USE(CG)
- virtual CGImageRef getCGImageRef() override;
- virtual CGImageRef getFirstCGImageRefOfSize(const IntSize&) override;
- virtual RetainPtr<CFArrayRef> getCGImageArray() override;
+#if PLATFORM(COCOA)
+ CFDataRef tiffRepresentation() override;
#endif
#if PLATFORM(WIN)
- virtual bool getHBITMAP(HBITMAP) override;
- virtual bool getHBITMAPOfSize(HBITMAP, const IntSize*) override;
+ bool getHBITMAP(HBITMAP) override;
+ bool getHBITMAPOfSize(HBITMAP, const IntSize*) override;
#endif
#if PLATFORM(GTK)
- virtual GdkPixbuf* getGdkPixbuf() override;
+ GdkPixbuf* getGdkPixbuf() override;
#endif
-#if PLATFORM(EFL)
- virtual Evas_Object* getEvasObject(Evas*) override;
+ WEBCORE_EXPORT NativeImagePtr nativeImage(const GraphicsContext* = nullptr) override;
+ NativeImagePtr nativeImageForCurrentFrame(const GraphicsContext* = nullptr) override;
+#if USE(CG)
+ NativeImagePtr nativeImageOfSize(const IntSize&, const GraphicsContext* = nullptr) override;
+ Vector<NativeImagePtr> framesNativeImages() override;
#endif
- virtual PassNativeImagePtr nativeImageForCurrentFrame() override;
- virtual ImageOrientation orientationForCurrentFrame() override { return frameOrientationAtIndex(currentFrame()); }
-
- virtual bool currentFrameKnownToBeOpaque() override;
-
- bool canAnimate();
-
-private:
- void updateSize(ImageOrientationDescription = ImageOrientationDescription()) const;
-
protected:
- enum RepetitionCountStatus {
- Unknown, // We haven't checked the source's repetition count.
- Uncertain, // We have a repetition count, but it might be wrong (some GIFs have a count after the image data, and will report "loop once" until all data has been decoded).
- Certain // The repetition count is known to be correct.
- };
+ WEBCORE_EXPORT BitmapImage(NativeImagePtr&&, ImageObserver* = nullptr);
+ WEBCORE_EXPORT BitmapImage(ImageObserver* = nullptr);
- BitmapImage(PassNativeImagePtr, ImageObserver* = 0);
- BitmapImage(ImageObserver* = 0);
+ NativeImagePtr frameImageAtIndex(size_t, const std::optional<SubsamplingLevel>& = { }, const std::optional<IntSize>& sizeForDrawing = { }, const GraphicsContext* = nullptr);
-#if PLATFORM(WIN)
- virtual void drawFrameMatchingSourceSize(GraphicsContext*, const FloatRect& dstRect, const IntSize& srcSize, ColorSpace styleColorSpace, CompositeOperator) override;
-#endif
- virtual void draw(GraphicsContext*, const FloatRect& dstRect, const FloatRect& srcRect, ColorSpace styleColorSpace, CompositeOperator, BlendMode, ImageOrientationDescription) override;
-
-#if USE(WINGDI)
- virtual void drawPattern(GraphicsContext*, const FloatRect& srcRect, const AffineTransform& patternTransform,
- const FloatPoint& phase, ColorSpace styleColorSpace, CompositeOperator, const FloatRect& destRect);
-#endif
-
- size_t currentFrame() const { return m_currentFrame; }
-#if PLATFORM(IOS)
- PassNativeImagePtr frameAtIndex(size_t, float scaleHint);
- PassNativeImagePtr copyUnscaledFrameAtIndex(size_t);
-#endif
- size_t frameCount();
- PassNativeImagePtr frameAtIndex(size_t);
- bool frameIsCompleteAtIndex(size_t);
- float frameDurationAtIndex(size_t);
- bool frameHasAlphaAtIndex(size_t);
- ImageOrientation frameOrientationAtIndex(size_t);
-
- // Decodes and caches a frame. Never accessed except internally.
-#if PLATFORM(IOS)
- void cacheFrame(size_t index, float scaleHint);
-
- // Cache frame metadata without decoding image.
- void cacheFrameInfo(size_t index);
- // Called before accessing m_frames[index] for info without decoding. Returns false on index out of bounds.
- bool ensureFrameInfoIsCached(size_t index);
-#else
- void cacheFrame(size_t index);
- // Called before accessing m_frames[index]. Returns false on index out of bounds.
- bool ensureFrameIsCached(size_t index);
-#endif
+ String sourceURL() const { return imageObserver() ? imageObserver()->sourceUrl().string() : emptyString(); }
+ bool allowSubsampling() const { return imageObserver() && imageObserver()->allowSubsampling(); }
+ bool allowLargeImageAsyncDecoding() const { return imageObserver() && imageObserver()->allowLargeImageAsyncDecoding(); }
+ bool allowAnimatedImageAsyncDecoding() const { return imageObserver() && imageObserver()->allowAnimatedImageAsyncDecoding(); }
+ bool showDebugBackground() const { return imageObserver() && imageObserver()->showDebugBackground(); }
- // Called to invalidate cached data. When |destroyAll| is true, we wipe out
+ // Called to invalidate cached data. When |destroyAll| is true, we wipe out
// the entire frame buffer cache and tell the image source to destroy
// everything; this is used when e.g. we want to free some room in the image
- // cache. If |destroyAll| is false, we only delete frames up to the current
+ // cache. If |destroyAll| is false, we only delete frames up to the current
// one; this is used while animating large images to keep memory footprint
// low without redecoding the whole image on every frame.
- virtual void destroyDecodedData(bool destroyAll = true) override;
+ void destroyDecodedData(bool destroyAll = true) override;
// If the image is large enough, calls destroyDecodedData() and passes
// |destroyAll| along.
- void destroyDecodedDataIfNecessary(bool destroyAll);
+ void destroyDecodedDataIfNecessary(bool destroyAll = true);
- // Generally called by destroyDecodedData(), destroys whole-image metadata
- // and notifies observers that the memory footprint has (hopefully)
- // decreased by |frameBytesCleared|.
- void destroyMetadataAndNotify(unsigned frameBytesCleared);
-
- // Whether or not size is available yet.
- bool isSizeAvailable();
-
- // Called after asking the source for any information that may require
- // decoding part of the image (e.g., the image size). We need to report
- // the partially decoded data to our observer so it has an accurate
- // account of the BitmapImage's memory usage.
- void didDecodeProperties() const;
+ void draw(GraphicsContext&, const FloatRect& dstRect, const FloatRect& srcRect, CompositeOperator, BlendMode, ImageOrientationDescription) override;
+ void drawPattern(GraphicsContext&, const FloatRect& destRect, const FloatRect& srcRect, const AffineTransform& patternTransform, const FloatPoint& phase, const FloatSize& spacing, CompositeOperator, BlendMode = BlendModeNormal) override;
+#if PLATFORM(WIN)
+ void drawFrameMatchingSourceSize(GraphicsContext&, const FloatRect& dstRect, const IntSize& srcSize, CompositeOperator) override;
+#endif
// Animation.
- int repetitionCount(bool imageKnownToBeComplete); // |imageKnownToBeComplete| should be set if the caller knows the entire image has been decoded.
+ enum class StartAnimationResult { CannotStart, IncompleteData, TimerActive, DecodingActive, Started };
+ bool isAnimated() const override { return m_source.frameCount() > 1; }
bool shouldAnimate();
- virtual void startAnimation(bool catchUpIfNecessary = true) override;
- void advanceAnimation(Timer<BitmapImage>&);
+ bool canAnimate();
+ void startAnimation() override { internalStartAnimation(); }
+ StartAnimationResult internalStartAnimation();
+ void advanceAnimation();
+ void internalAdvanceAnimation();
- // Function that does the real work of advancing the animation. When
- // skippingFrames is true, we're in the middle of a loop trying to skip over
- // a bunch of animation frames, so we should not do things like decode each
- // one or notify our observers.
- // Returns whether the animation was advanced.
- bool internalAdvanceAnimation(bool skippingFrames);
+ // It may look unusual that there is no start animation call as public API. This is because
+ // we start and stop animating lazily. Animation begins whenever someone draws the image. It will
+ // automatically pause once all observers no longer want to render the image anywhere.
+ void stopAnimation() override;
+ void resetAnimation() override;
+ void newFrameNativeImageAvailableAtIndex(size_t) override;
// Handle platform-specific data
void invalidatePlatformData();
- // Checks to see if the image is a 1x1 solid color. We optimize these images and just do a fill rect instead.
- // This check should happen regardless whether m_checkedForSolidColor is already set, as the frame may have
- // changed.
- void checkForSolidColor();
-
- virtual bool mayFillWithSolidColor() override;
- virtual Color solidColor() const override;
-
#if !ASSERT_DISABLED
- virtual bool notSolidColor() override;
+ bool notSolidColor() override;
#endif
-private:
- ImageSource m_source;
- mutable IntSize m_size; // The size to use for the overall image (will just be the size of the first image).
- mutable IntSize m_sizeRespectingOrientation;
- mutable unsigned m_imageOrientation : 4; // ImageOrientationEnum
- mutable unsigned m_shouldRespectImageOrientation : 1; // RespectImageOrientationEnum
-
-#if PLATFORM(IOS)
- mutable IntSize m_originalSize; // The size of the unsubsampled image.
- mutable IntSize m_originalSizeRespectingOrientation;
+#if PLATFORM(COCOA)
+ RetainPtr<CFDataRef> tiffRepresentation(const Vector<NativeImagePtr>&);
#endif
- size_t m_currentFrame; // The index of the current frame of animation.
- Vector<FrameData, 1> m_frames; // An array of the cached frames of the animation. We have to ref frames to pin them in the cache.
-
- std::unique_ptr<Timer<BitmapImage>> m_frameTimer;
- int m_repetitionCount; // How many total animation loops we should do. This will be cAnimationNone if this image type is incapable of animation.
- RepetitionCountStatus m_repetitionCountStatus;
- int m_repetitionsComplete; // How many repetitions we've finished.
- double m_desiredFrameStartTime; // The system time at which we hope to see the next call to startAnimation().
-#if USE(APPKIT)
- mutable RetainPtr<NSImage> m_nsImage; // A cached NSImage of frame 0. Only built lazily if someone actually queries for one.
-#endif
-#if USE(CG)
- mutable RetainPtr<CFDataRef> m_tiffRep; // Cached TIFF rep for frame 0. Only built lazily if someone queries for one.
+private:
+ void clearTimer();
+ void startTimer(double delay);
+ bool isBitmapImage() const override { return true; }
+ void dump(TextStream&) const override;
+
+ // Animated images over a certain size are considered large enough that we'll only hang on to one frame at a time.
+#if !PLATFORM(IOS)
+ static const unsigned LargeAnimationCutoff = 5242880;
+#else
+ static const unsigned LargeAnimationCutoff = 2097152;
#endif
- Color m_solidColor; // If we're a 1x1 solid color, this is the color to use to fill.
+ mutable ImageSource m_source;
- unsigned m_decodedSize; // The current size of all decoded frames.
- mutable unsigned m_decodedPropertiesSize; // The size of data decoded by the source to determine image properties (e.g. size, frame count, etc).
- size_t m_frameCount;
+ size_t m_currentFrame { 0 }; // The index of the current frame of animation.
+ SubsamplingLevel m_currentSubsamplingLevel { SubsamplingLevel::Default };
+ std::optional<IntSize> m_sizeForDrawing;
+ std::unique_ptr<Timer> m_frameTimer;
+ RepetitionCount m_repetitionsComplete { RepetitionCountNone }; // How many repetitions we've finished.
+ double m_desiredFrameStartTime { 0 }; // The system time at which we hope to see the next call to startAnimation().
+ bool m_animationFinished { false };
-#if PLATFORM(IOS)
- // FIXME: We should expose a setting to enable/disable progressive loading remove the PLATFORM(IOS)-guard.
- double m_progressiveLoadChunkTime;
- uint16_t m_progressiveLoadChunkCount;
+ float m_frameDecodingDurationForTesting { 0 };
+ double m_desiredFrameDecodeTimeForTesting { 0 };
+#if !LOG_DISABLED
+ size_t m_lateFrameCount { 0 };
+ size_t m_earlyFrameCount { 0 };
+ size_t m_cachedFrameCount { 0 };
#endif
- bool m_isSolidColor : 1; // Whether or not we are a 1x1 solid image.
- bool m_checkedForSolidColor : 1; // Whether we've checked the frame for solid color.
-
- bool m_animationFinished : 1; // Whether or not we've completed the entire animation.
-
- bool m_allDataReceived : 1; // Whether or not we've received all our data.
- mutable bool m_haveSize : 1; // Whether or not our |m_size| member variable has the final overall image size yet.
- bool m_sizeAvailable : 1; // Whether or not we can obtain the size of the first image frame yet from ImageIO.
- mutable bool m_hasUniformFrameSize : 1;
- mutable bool m_haveFrameCount : 1;
-
+#if USE(APPKIT)
+ mutable RetainPtr<NSImage> m_nsImage; // A cached NSImage of all the frames. Only built lazily if someone actually queries for one.
+#endif
+#if USE(CG)
+ mutable RetainPtr<CFDataRef> m_tiffRep; // Cached TIFF rep for all the frames. Only built lazily if someone queries for one.
+#endif
RefPtr<Image> m_cachedImage;
};
-IMAGE_TYPE_CASTS(BitmapImage)
+} // namespace WebCore
-}
-
-#endif
+SPECIALIZE_TYPE_TRAITS_IMAGE(BitmapImage)
diff --git a/Source/WebCore/platform/graphics/Color.cpp b/Source/WebCore/platform/graphics/Color.cpp
index 7de24ebdc..4c0d15606 100644
--- a/Source/WebCore/platform/graphics/Color.cpp
+++ b/Source/WebCore/platform/graphics/Color.cpp
@@ -10,10 +10,10 @@
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
- * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
@@ -26,7 +26,9 @@
#include "config.h"
#include "Color.h"
+#include "AnimationUtilities.h"
#include "HashTools.h"
+#include "TextStream.h"
#include <wtf/Assertions.h>
#include <wtf/DecimalNumber.h>
#include <wtf/HexNumber.h>
@@ -47,6 +49,16 @@ const RGBA32 Color::transparent;
static const RGBA32 lightenedBlack = 0xFF545454;
static const RGBA32 darkenedWhite = 0xFFABABAB;
+static inline unsigned premultipliedChannel(unsigned c, unsigned a, bool ceiling = true)
+{
+ return fastDivideBy255(ceiling ? c * a + 254 : c * a);
+}
+
+static inline unsigned unpremultipliedChannel(unsigned c, unsigned a)
+{
+ return (fastMultiplyBy255(c) + a - 1) / a;
+}
+
RGBA32 makeRGB(int r, int g, int b)
{
return 0xFF000000 | std::max(0, std::min(r, 255)) << 16 | std::max(0, std::min(g, 255)) << 8 | std::max(0, std::min(b, 255));
@@ -57,6 +69,16 @@ RGBA32 makeRGBA(int r, int g, int b, int a)
return std::max(0, std::min(a, 255)) << 24 | std::max(0, std::min(r, 255)) << 16 | std::max(0, std::min(g, 255)) << 8 | std::max(0, std::min(b, 255));
}
+RGBA32 makePremultipliedRGBA(int r, int g, int b, int a, bool ceiling)
+{
+ return makeRGBA(premultipliedChannel(r, a, ceiling), premultipliedChannel(g, a, ceiling), premultipliedChannel(b, a, ceiling), a);
+}
+
+RGBA32 makeUnPremultipliedRGBA(int r, int g, int b, int a)
+{
+ return makeRGBA(unpremultipliedChannel(r, a), unpremultipliedChannel(g, a), unpremultipliedChannel(b, a), a);
+}
+
static int colorFloatToRGBAByte(float f)
{
// We use lroundf and 255 instead of nextafterf(256, 0) to match CG's rounding
@@ -126,7 +148,7 @@ RGBA32 makeRGBAFromCMYKA(float c, float m, float y, float k, float a)
template <typename CharacterType>
static inline bool parseHexColorInternal(const CharacterType* name, unsigned length, RGBA32& rgb)
{
- if (length != 3 && length != 6)
+ if (length != 3 && length != 4 && length != 6 && length != 8)
return false;
unsigned value = 0;
for (unsigned i = 0; i < length; ++i) {
@@ -139,6 +161,20 @@ static inline bool parseHexColorInternal(const CharacterType* name, unsigned len
rgb = 0xFF000000 | value;
return true;
}
+ if (length == 8) {
+ // We parsed the values into RGBA order, but the RGBA32 type
+ // expects them to be in ARGB order, so we right rotate eight bits.
+ rgb = value << 24 | value >> 8;
+ return true;
+ }
+ if (length == 4) {
+ // #abcd converts to ddaabbcc in RGBA32.
+ rgb = (value & 0xF) << 28 | (value & 0xF) << 24
+ | (value & 0xF000) << 8 | (value & 0xF000) << 4
+ | (value & 0xF00) << 4 | (value & 0xF00)
+ | (value & 0xF0) | (value & 0xF0) >> 4;
+ return true;
+ }
// #abc converts to #aabbcc
rgb = 0xFF000000
| (value & 0xF00) << 12 | (value & 0xF00) << 8
@@ -165,42 +201,153 @@ bool Color::parseHexColor(const String& name, RGBA32& rgb)
return false;
if (name.is8Bit())
return parseHexColor(name.characters8(), name.length(), rgb);
- return parseHexColor(name.deprecatedCharacters(), name.length(), rgb);
+ return parseHexColor(name.characters16(), name.length(), rgb);
+}
+
+bool Color::parseHexColor(const StringView& name, RGBA32& rgb)
+{
+ unsigned length = name.length();
+ if (!length)
+ return false;
+ if (name.is8Bit())
+ return parseHexColor(name.characters8(), name.length(), rgb);
+ return parseHexColor(name.characters16(), name.length(), rgb);
}
int differenceSquared(const Color& c1, const Color& c2)
{
- int dR = c1.red() - c2.red();
- int dG = c1.green() - c2.green();
- int dB = c1.blue() - c2.blue();
+ // FIXME: This is assuming that the colors are in the same colorspace.
+ // FIXME: This should probably return a floating point number, but many of the call
+ // sites have picked comparison values based on feel. We'd need to break out
+ // our logarithm tables to change them :)
+ int c1Red = c1.isExtended() ? c1.asExtended().red() * 255 : c1.red();
+ int c1Green = c1.isExtended() ? c1.asExtended().green() * 255 : c1.green();
+ int c1Blue = c1.isExtended() ? c1.asExtended().blue() * 255 : c1.blue();
+ int c2Red = c2.isExtended() ? c2.asExtended().red() * 255 : c2.red();
+ int c2Green = c2.isExtended() ? c2.asExtended().green() * 255 : c2.green();
+ int c2Blue = c2.isExtended() ? c2.asExtended().blue() * 255 : c2.blue();
+ int dR = c1Red - c2Red;
+ int dG = c1Green - c2Green;
+ int dB = c1Blue - c2Blue;
return dR * dR + dG * dG + dB * dB;
}
+static inline const NamedColor* findNamedColor(const String& name)
+{
+ char buffer[64]; // easily big enough for the longest color name
+ unsigned length = name.length();
+ if (length > sizeof(buffer) - 1)
+ return nullptr;
+ for (unsigned i = 0; i < length; ++i) {
+ UChar c = name[i];
+ if (!c || !WTF::isASCII(c))
+ return nullptr;
+ buffer[i] = toASCIILower(static_cast<char>(c));
+ }
+ buffer[length] = '\0';
+ return findColor(buffer, length);
+}
+
Color::Color(const String& name)
{
if (name[0] == '#') {
+ RGBA32 color;
+ bool valid;
+
if (name.is8Bit())
- m_valid = parseHexColor(name.characters8() + 1, name.length() - 1, m_color);
+ valid = parseHexColor(name.characters8() + 1, name.length() - 1, color);
else
- m_valid = parseHexColor(name.deprecatedCharacters() + 1, name.length() - 1, m_color);
- } else
- setNamedColor(name);
+ valid = parseHexColor(name.characters16() + 1, name.length() - 1, color);
+
+ if (valid)
+ setRGB(color);
+ } else {
+ if (auto* foundColor = findNamedColor(name))
+ setRGB(foundColor->ARGBValue);
+ else
+ m_colorData.rgbaAndFlags = invalidRGBAColor;
+ }
}
Color::Color(const char* name)
{
+ RGBA32 color;
+ bool valid;
if (name[0] == '#')
- m_valid = parseHexColor(&name[1], m_color);
+ valid = parseHexColor((String)&name[1], color);
else {
const NamedColor* foundColor = findColor(name, strlen(name));
- m_color = foundColor ? foundColor->ARGBValue : 0;
- m_valid = foundColor;
+ color = foundColor ? foundColor->ARGBValue : 0;
+ valid = foundColor;
}
+
+ if (valid)
+ setRGB(color);
+}
+
+Color::Color(const Color& other)
+ : m_colorData(other.m_colorData)
+{
+ if (isExtended())
+ m_colorData.extendedColor->ref();
+}
+
+Color::Color(Color&& other)
+{
+ *this = WTFMove(other);
+}
+
+Color::Color(float r, float g, float b, float a, ColorSpace colorSpace)
+{
+ // Zero the union, just in case a 32-bit system only assigns the
+ // top 32 bits when copying the extendedColor pointer below.
+ m_colorData.rgbaAndFlags = 0;
+ auto extendedColorRef = ExtendedColor::create(r, g, b, a, colorSpace);
+ m_colorData.extendedColor = &extendedColorRef.leakRef();
+ ASSERT(isExtended());
+}
+
+Color::~Color()
+{
+ if (isExtended())
+ m_colorData.extendedColor->deref();
+}
+
+Color& Color::operator=(const Color& other)
+{
+ if (*this == other)
+ return *this;
+
+ if (isExtended())
+ m_colorData.extendedColor->deref();
+
+ m_colorData = other.m_colorData;
+
+ if (isExtended())
+ m_colorData.extendedColor->ref();
+ return *this;
+}
+
+Color& Color::operator=(Color&& other)
+{
+ if (*this == other)
+ return *this;
+
+ if (isExtended())
+ m_colorData.extendedColor->deref();
+
+ m_colorData = other.m_colorData;
+ other.m_colorData.rgbaAndFlags = invalidRGBAColor;
+
+ return *this;
}
String Color::serialized() const
{
- if (!hasAlpha()) {
+ if (isExtended())
+ return asExtended().cssText();
+
+ if (isOpaque()) {
StringBuilder builder;
builder.reserveCapacity(7);
builder.append('#');
@@ -210,65 +357,54 @@ String Color::serialized() const
return builder.toString();
}
- Vector<LChar> result;
- result.reserveInitialCapacity(28);
- const char commaSpace[] = ", ";
- const char rgbaParen[] = "rgba(";
-
- result.append(rgbaParen, 5);
- appendNumber(result, red());
- result.append(commaSpace, 2);
- appendNumber(result, green());
- result.append(commaSpace, 2);
- appendNumber(result, blue());
- result.append(commaSpace, 2);
-
- if (!alpha())
- result.append('0');
- else {
- NumberToLStringBuffer buffer;
- unsigned length = DecimalNumber(alpha() / 255.0).toStringDecimal(buffer, WTF::NumberToStringBufferLength);
- result.append(buffer, length);
- }
-
- result.append(')');
- return String::adopt(result);
+ return cssText();
}
-String Color::nameForRenderTreeAsText() const
+String Color::cssText() const
{
- if (alpha() < 0xFF)
- return String::format("#%02X%02X%02X%02X", red(), green(), blue(), alpha());
- return String::format("#%02X%02X%02X", red(), green(), blue());
-}
+ if (isExtended())
+ return asExtended().cssText();
+
+ StringBuilder builder;
+ builder.reserveCapacity(28);
+ bool colorHasAlpha = !isOpaque();
+ if (colorHasAlpha)
+ builder.appendLiteral("rgba(");
+ else
+ builder.appendLiteral("rgb(");
-static inline const NamedColor* findNamedColor(const String& name)
-{
- char buffer[64]; // easily big enough for the longest color name
- unsigned length = name.length();
- if (length > sizeof(buffer) - 1)
- return 0;
- for (unsigned i = 0; i < length; ++i) {
- UChar c = name[i];
- if (!c || c > 0x7F)
- return 0;
- buffer[i] = toASCIILower(static_cast<char>(c));
+ builder.appendNumber(static_cast<unsigned char>(red()));
+ builder.appendLiteral(", ");
+
+ builder.appendNumber(static_cast<unsigned char>(green()));
+ builder.appendLiteral(", ");
+
+
+ builder.appendNumber(static_cast<unsigned char>(blue()));
+ if (colorHasAlpha) {
+ builder.appendLiteral(", ");
+
+ NumberToStringBuffer buffer;
+ bool shouldTruncateTrailingZeros = true;
+ builder.append(numberToFixedPrecisionString(alpha() / 255.0f, 6, buffer, shouldTruncateTrailingZeros));
}
- buffer[length] = '\0';
- return findColor(buffer, length);
+
+ builder.append(')');
+ return builder.toString();
}
-void Color::setNamedColor(const String& name)
+String Color::nameForRenderTreeAsText() const
{
- const NamedColor* foundColor = findNamedColor(name);
- m_color = foundColor ? foundColor->ARGBValue : 0;
- m_valid = foundColor;
+ // FIXME: Handle ExtendedColors.
+ if (alpha() < 0xFF)
+ return String::format("#%02X%02X%02X%02X", red(), green(), blue(), alpha());
+ return String::format("#%02X%02X%02X", red(), green(), blue());
}
Color Color::light() const
{
// Hardcode this common case for speed.
- if (m_color == black)
+ if (rgb() == black)
return lightenedBlack;
const float scaleFactor = nextafterf(256.0f, 0.0f);
@@ -293,7 +429,7 @@ Color Color::light() const
Color Color::dark() const
{
// Hardcode this common case for speed.
- if (m_color == white)
+ if (rgb() == white)
return darkenedWhite;
const float scaleFactor = nextafterf(256.0f, 0.0f);
@@ -336,7 +472,7 @@ const int cAlphaIncrement = 17; // Increments in between.
Color Color::blend(const Color& source) const
{
- if (!alpha() || !source.hasAlpha())
+ if (!isVisible() || source.isOpaque())
return source;
if (!source.alpha())
@@ -353,7 +489,7 @@ Color Color::blend(const Color& source) const
Color Color::blendWithWhite() const
{
// If the color contains alpha already, we leave it alone.
- if (hasAlpha())
+ if (!isOpaque())
return *this;
Color newColor;
@@ -372,6 +508,21 @@ Color Color::blendWithWhite() const
return newColor;
}
+Color Color::colorWithAlphaMultipliedBy(float amount) const
+{
+ float newAlpha = amount * (isExtended() ? m_colorData.extendedColor->alpha() : static_cast<float>(alpha()) / 255);
+ return colorWithAlpha(newAlpha);
+}
+
+Color Color::colorWithAlpha(float alpha) const
+{
+ if (isExtended())
+ return Color { m_colorData.extendedColor->red(), m_colorData.extendedColor->green(), m_colorData.extendedColor->blue(), alpha, m_colorData.extendedColor->colorSpace() };
+
+ int newAlpha = alpha * 255;
+ return Color { red(), green(), blue(), newAlpha };
+}
+
void Color::getRGBA(float& r, float& g, float& b, float& a) const
{
r = red() / 255.0f;
@@ -398,15 +549,16 @@ void Color::getHSL(double& hue, double& saturation, double& lightness) const
double b = static_cast<double>(blue()) / 255.0;
double max = std::max(std::max(r, g), b);
double min = std::min(std::min(r, g), b);
+ double chroma = max - min;
- if (max == min)
+ if (!chroma)
hue = 0.0;
else if (max == r)
- hue = (60.0 * ((g - b) / (max - min))) + 360.0;
+ hue = (60.0 * ((g - b) / chroma)) + 360.0;
else if (max == g)
- hue = (60.0 * ((b - r) / (max - min))) + 120.0;
+ hue = (60.0 * ((b - r) / chroma)) + 120.0;
else
- hue = (60.0 * ((r - g) / (max - min))) + 240.0;
+ hue = (60.0 * ((r - g) / chroma)) + 240.0;
if (hue >= 360.0)
hue -= 360.0;
@@ -415,42 +567,113 @@ void Color::getHSL(double& hue, double& saturation, double& lightness) const
hue /= 360.0;
lightness = 0.5 * (max + min);
- if (max == min)
+ if (!chroma)
saturation = 0.0;
else if (lightness <= 0.5)
- saturation = ((max - min) / (max + min));
+ saturation = (chroma / (max + min));
else
- saturation = ((max - min) / (2.0 - (max + min)));
+ saturation = (chroma / (2.0 - (max + min)));
+}
+
+void Color::getHSV(double& hue, double& saturation, double& value) const
+{
+ double r = static_cast<double>(red()) / 255.0;
+ double g = static_cast<double>(green()) / 255.0;
+ double b = static_cast<double>(blue()) / 255.0;
+ double max = std::max(std::max(r, g), b);
+ double min = std::min(std::min(r, g), b);
+ double chroma = max - min;
+
+ if (!chroma)
+ hue = 0.0;
+ else if (max == r)
+ hue = (60.0 * ((g - b) / chroma)) + 360.0;
+ else if (max == g)
+ hue = (60.0 * ((b - r) / chroma)) + 120.0;
+ else
+ hue = (60.0 * ((r - g) / chroma)) + 240.0;
+
+ if (hue >= 360.0)
+ hue -= 360.0;
+
+ hue /= 360.0;
+
+ if (!max)
+ saturation = 0;
+ else
+ saturation = chroma / max;
+
+ value = max;
}
Color colorFromPremultipliedARGB(RGBA32 pixelColor)
{
int alpha = alphaChannel(pixelColor);
- if (alpha && alpha < 255) {
- return Color::createUnchecked(
- redChannel(pixelColor) * 255 / alpha,
- greenChannel(pixelColor) * 255 / alpha,
- blueChannel(pixelColor) * 255 / alpha,
- alpha);
- } else
- return Color(pixelColor);
+ if (alpha && alpha < 255)
+ pixelColor = makeUnPremultipliedRGBA(redChannel(pixelColor), greenChannel(pixelColor), blueChannel(pixelColor), alpha);
+ return Color(pixelColor);
}
RGBA32 premultipliedARGBFromColor(const Color& color)
{
- unsigned pixelColor;
+ if (color.isOpaque()) {
+ if (color.isExtended())
+ return makeRGB(color.asExtended().red() * 255, color.asExtended().green() * 255, color.asExtended().blue() * 255);
+ return color.rgb();
+ }
- unsigned alpha = color.alpha();
- if (alpha < 255) {
- pixelColor = Color::createUnchecked(
- fastDivideBy255(color.red() * alpha + 254),
- fastDivideBy255(color.green() * alpha + 254),
- fastDivideBy255(color.blue() * alpha + 254),
- alpha).rgb();
- } else
- pixelColor = color.rgb();
+ if (color.isExtended())
+ return makePremultipliedRGBA(color.asExtended().red() * 255, color.asExtended().green() * 255, color.asExtended().blue() * 255, color.asExtended().alpha() * 255);
- return pixelColor;
+ return makePremultipliedRGBA(color.red(), color.green(), color.blue(), color.alpha());
+}
+
+Color blend(const Color& from, const Color& to, double progress, bool blendPremultiplied)
+{
+ // FIXME: ExtendedColor - needs to handle color spaces.
+ // We need to preserve the state of the valid flag at the end of the animation
+ if (progress == 1 && !to.isValid())
+ return Color();
+
+ if (blendPremultiplied) {
+ // Contrary to the name, RGBA32 actually stores ARGB, so we can initialize Color directly from premultipliedARGBFromColor().
+ // Also, premultipliedARGBFromColor() bails on zero alpha, so special-case that.
+ Color premultFrom = from.alpha() ? premultipliedARGBFromColor(from) : 0;
+ Color premultTo = to.alpha() ? premultipliedARGBFromColor(to) : 0;
+
+ Color premultBlended(blend(premultFrom.red(), premultTo.red(), progress),
+ blend(premultFrom.green(), premultTo.green(), progress),
+ blend(premultFrom.blue(), premultTo.blue(), progress),
+ blend(premultFrom.alpha(), premultTo.alpha(), progress));
+
+ return Color(colorFromPremultipliedARGB(premultBlended.rgb()));
+ }
+
+ return Color(blend(from.red(), to.red(), progress),
+ blend(from.green(), to.green(), progress),
+ blend(from.blue(), to.blue(), progress),
+ blend(from.alpha(), to.alpha(), progress));
+}
+
+TextStream& operator<<(TextStream& ts, const Color& color)
+{
+ return ts << color.nameForRenderTreeAsText();
+}
+
+void Color::tagAsValid()
+{
+ m_colorData.rgbaAndFlags |= validRGBAColor;
+}
+
+bool Color::isExtended() const
+{
+ return !(m_colorData.rgbaAndFlags & invalidRGBAColor);
+}
+
+ExtendedColor& Color::asExtended() const
+{
+ ASSERT(isExtended());
+ return *m_colorData.extendedColor;
}
} // namespace WebCore
diff --git a/Source/WebCore/platform/graphics/Color.h b/Source/WebCore/platform/graphics/Color.h
index 2047ad24c..83b2a3945 100644
--- a/Source/WebCore/platform/graphics/Color.h
+++ b/Source/WebCore/platform/graphics/Color.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003, 2004, 2005, 2006, 2010 Apple Inc. All rights reserved.
+ * Copyright (C) 2003-2016 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -10,10 +10,10 @@
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
- * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
@@ -23,20 +23,30 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef Color_h
-#define Color_h
+#pragma once
-#include "AnimationUtilities.h"
-#include <wtf/FastMalloc.h>
+#include "ColorSpace.h"
+#include "ExtendedColor.h"
+#include "PlatformExportMacros.h"
+#include <algorithm>
+#include <cmath>
+#include <unicode/uchar.h>
#include <wtf/Forward.h>
-#include <wtf/unicode/Unicode.h>
+#include <wtf/HashFunctions.h>
+#include <wtf/Optional.h>
+#include <wtf/text/LChar.h>
#if USE(CG)
-#include "ColorSpace.h"
typedef struct CGColor* CGColorRef;
-#if PLATFORM(IOS)
-typedef struct CGColorSpace* CGColorSpaceRef;
-#endif // PLATFORM(IOS)
+#endif
+
+#if PLATFORM(WIN)
+struct _D3DCOLORVALUE;
+typedef _D3DCOLORVALUE D3DCOLORVALUE;
+typedef D3DCOLORVALUE D2D_COLOR_F;
+typedef D2D_COLOR_F D2D1_COLOR_F;
+struct D2D_VECTOR_4F;
+typedef D2D_VECTOR_4F D2D1_VECTOR_4F;
#endif
#if PLATFORM(GTK)
@@ -48,39 +58,123 @@ typedef struct _GdkRGBA GdkRGBA;
namespace WebCore {
-class Color;
+class TextStream;
-typedef unsigned RGBA32; // RGBA quadruplet
+typedef unsigned RGBA32; // Deprecated: Type for an RGBA quadruplet. Use RGBA class instead.
-RGBA32 makeRGB(int r, int g, int b);
-RGBA32 makeRGBA(int r, int g, int b, int a);
+WEBCORE_EXPORT RGBA32 makeRGB(int r, int g, int b);
+WEBCORE_EXPORT RGBA32 makeRGBA(int r, int g, int b, int a);
-RGBA32 colorWithOverrideAlpha(RGBA32 color, float overrideAlpha);
-RGBA32 makeRGBA32FromFloats(float r, float g, float b, float a);
+RGBA32 makePremultipliedRGBA(int r, int g, int b, int a, bool ceiling = true);
+RGBA32 makeUnPremultipliedRGBA(int r, int g, int b, int a);
+
+WEBCORE_EXPORT RGBA32 colorWithOverrideAlpha(RGBA32 color, float overrideAlpha);
+RGBA32 colorWithOverrideAlpha(RGBA32 color, std::optional<float> overrideAlpha);
+
+WEBCORE_EXPORT RGBA32 makeRGBA32FromFloats(float r, float g, float b, float a);
RGBA32 makeRGBAFromHSLA(double h, double s, double l, double a);
RGBA32 makeRGBAFromCMYKA(float c, float m, float y, float k, float a);
-int differenceSquared(const Color&, const Color&);
-
inline int redChannel(RGBA32 color) { return (color >> 16) & 0xFF; }
inline int greenChannel(RGBA32 color) { return (color >> 8) & 0xFF; }
inline int blueChannel(RGBA32 color) { return color & 0xFF; }
inline int alphaChannel(RGBA32 color) { return (color >> 24) & 0xFF; }
+uint8_t roundAndClampColorChannel(int);
+uint8_t roundAndClampColorChannel(float);
+
+class RGBA {
+public:
+ RGBA(); // all channels zero, including alpha
+ RGBA(uint8_t red, uint8_t green, uint8_t blue, uint8_t alpha);
+ RGBA(uint8_t red, uint8_t green, uint8_t blue); // opaque, alpha of 1
+
+ uint8_t red() const;
+ uint8_t green() const;
+ uint8_t blue() const;
+ uint8_t alpha() const;
+
+ bool hasAlpha() const;
+
+private:
+ friend class Color;
+
+ unsigned m_integer { 0 };
+};
+
+bool operator==(const RGBA&, const RGBA&);
+bool operator!=(const RGBA&, const RGBA&);
+
class Color {
WTF_MAKE_FAST_ALLOCATED;
public:
- Color() : m_color(0), m_valid(false) { }
- Color(RGBA32 color, bool valid = true) : m_color(color), m_valid(valid) { ASSERT(!m_color || m_valid); }
- Color(int r, int g, int b) : m_color(makeRGB(r, g, b)), m_valid(true) { }
- Color(int r, int g, int b, int a) : m_color(makeRGBA(r, g, b, a)), m_valid(true) { }
- // Color is currently limited to 32bit RGBA, perhaps some day we'll support better colors
- Color(float r, float g, float b, float a) : m_color(makeRGBA32FromFloats(r, g, b, a)), m_valid(true) { }
+ Color() { }
+
+ // FIXME: Remove all these constructors and creation functions and replace the ones that are still needed with free functions.
+
+ Color(RGBA32 color, bool valid = true)
+ {
+ if (valid)
+ setRGB(color);
+ }
+
+ Color(int r, int g, int b)
+ {
+ setRGB(r, g, b);
+ }
+
+ Color(int r, int g, int b, int a)
+ {
+ setRGB(makeRGBA(r, g, b, a));
+ }
+
+ Color(float r, float g, float b, float a)
+ {
+ setRGB(makeRGBA32FromFloats(r, g, b, a));
+ }
+
// Creates a new color from the specific CMYK and alpha values.
- Color(float c, float m, float y, float k, float a) : m_color(makeRGBAFromCMYKA(c, m, y, k, a)), m_valid(true) { }
- explicit Color(const String&);
+ Color(float c, float m, float y, float k, float a)
+ {
+ setRGB(makeRGBAFromCMYKA(c, m, y, k, a));
+ }
+
+ WEBCORE_EXPORT explicit Color(const String&);
explicit Color(const char*);
+ explicit Color(WTF::HashTableDeletedValueType)
+ {
+ static_assert(deletedHashValue & invalidRGBAColor, "Color's deleted hash value must not look like an ExtendedColor");
+ static_assert(!(deletedHashValue & validRGBAColorBit), "Color's deleted hash value must not look like a valid RGBA32 Color");
+ static_assert(deletedHashValue & (1 << 4), "Color's deleted hash value must have some bits set that an RGBA32 Color wouldn't have");
+ m_colorData.rgbaAndFlags = deletedHashValue;
+ ASSERT(!isExtended());
+ }
+
+ bool isHashTableDeletedValue() const
+ {
+ return m_colorData.rgbaAndFlags == deletedHashValue;
+ }
+
+ explicit Color(WTF::HashTableEmptyValueType)
+ {
+ static_assert(emptyHashValue & invalidRGBAColor, "Color's empty hash value must not look like an ExtendedColor");
+ static_assert(emptyHashValue & (1 << 4), "Color's deleted hash value must have some bits set that an RGBA32 Color wouldn't have");
+ m_colorData.rgbaAndFlags = emptyHashValue;
+ ASSERT(!isExtended());
+ }
+
+ // This creates an ExtendedColor.
+ // FIXME: If the colorSpace is sRGB and the values can all be
+ // converted exactly to integers, we should make a normal Color.
+ WEBCORE_EXPORT Color(float r, float g, float b, float a, ColorSpace colorSpace);
+
+ Color(RGBA, ColorSpace);
+ WEBCORE_EXPORT Color(const Color&);
+ WEBCORE_EXPORT Color(Color&&);
+
+ WEBCORE_EXPORT ~Color();
+
static Color createUnchecked(int r, int g, int b)
{
RGBA32 color = 0xFF000000 | r << 16 | g << 8 | b;
@@ -93,30 +187,36 @@ public:
}
// Returns the color serialized according to HTML5
- // - http://www.whatwg.org/specs/web-apps/current-work/#serialization-of-a-color
- String serialized() const;
+ // <https://html.spec.whatwg.org/multipage/scripting.html#fill-and-stroke-styles> (10 September 2015)
+ WEBCORE_EXPORT String serialized() const;
+
+ WEBCORE_EXPORT String cssText() const;
// Returns the color serialized as either #RRGGBB or #RRGGBBAA
- // The latter format is not a valid CSS color, and should only be seen in DRT dumps.
String nameForRenderTreeAsText() const;
- void setNamedColor(const String&);
+ bool isValid() const { return isExtended() || (m_colorData.rgbaAndFlags & validRGBAColorBit); }
- bool isValid() const { return m_valid; }
+ bool isOpaque() const { return isValid() && (isExtended() ? asExtended().alpha() == 1.0 : alpha() == 255); }
+ bool isVisible() const { return isValid() && (isExtended() ? asExtended().alpha() > 0.0 : alpha() > 0); }
- bool hasAlpha() const { return alpha() < 255; }
+ int red() const { return redChannel(rgb()); }
+ int green() const { return greenChannel(rgb()); }
+ int blue() const { return blueChannel(rgb()); }
+ int alpha() const { return alphaChannel(rgb()); }
- int red() const { return redChannel(m_color); }
- int green() const { return greenChannel(m_color); }
- int blue() const { return blueChannel(m_color); }
- int alpha() const { return alphaChannel(m_color); }
-
- RGBA32 rgb() const { return m_color; } // Preserve the alpha.
- void setRGB(int r, int g, int b) { m_color = makeRGB(r, g, b); m_valid = true; }
- void setRGB(RGBA32 rgb) { m_color = rgb; m_valid = true; }
- void getRGBA(float& r, float& g, float& b, float& a) const;
- void getRGBA(double& r, double& g, double& b, double& a) const;
- void getHSL(double& h, double& s, double& l) const;
+ float alphaAsFloat() const { return isExtended() ? asExtended().alpha() : static_cast<float>(alphaChannel(rgb())) / 255; }
+
+ RGBA32 rgb() const;
+
+ // FIXME: Like operator==, this will give different values for ExtendedColors that
+ // should be identical, since the respective pointer will be different.
+ unsigned hash() const { return WTF::intHash(m_colorData.rgbaAndFlags); }
+
+ WEBCORE_EXPORT void getRGBA(float& r, float& g, float& b, float& a) const;
+ WEBCORE_EXPORT void getRGBA(double& r, double& g, double& b, double& a) const;
+ WEBCORE_EXPORT void getHSL(double& h, double& s, double& l) const;
+ WEBCORE_EXPORT void getHSV(double& h, double& s, double& v) const;
Color light() const;
Color dark() const;
@@ -127,6 +227,12 @@ public:
Color blend(const Color&) const;
Color blendWithWhite() const;
+ Color colorWithAlphaMultipliedBy(float) const;
+
+ // Returns a color that has the same RGB values, but with the given A.
+ Color colorWithAlpha(float) const;
+ Color opaqueColor() const { return colorWithAlpha(1.0f); }
+
#if PLATFORM(GTK)
Color(const GdkColor&);
// We can't sensibly go back to GdkColor without losing the alpha value
@@ -137,37 +243,136 @@ public:
#endif
#if USE(CG)
- Color(CGColorRef);
+ WEBCORE_EXPORT Color(CGColorRef);
+#endif
+
+#if PLATFORM(WIN)
+ WEBCORE_EXPORT Color(D2D1_COLOR_F);
+ WEBCORE_EXPORT operator D2D1_COLOR_F() const;
+ WEBCORE_EXPORT operator D2D1_VECTOR_4F() const;
#endif
static bool parseHexColor(const String&, RGBA32&);
+ static bool parseHexColor(const StringView&, RGBA32&);
static bool parseHexColor(const LChar*, unsigned, RGBA32&);
static bool parseHexColor(const UChar*, unsigned, RGBA32&);
static const RGBA32 black = 0xFF000000;
- static const RGBA32 white = 0xFFFFFFFF;
+ WEBCORE_EXPORT static const RGBA32 white = 0xFFFFFFFF;
static const RGBA32 darkGray = 0xFF808080;
static const RGBA32 gray = 0xFFA0A0A0;
static const RGBA32 lightGray = 0xFFC0C0C0;
- static const RGBA32 transparent = 0x00000000;
+ WEBCORE_EXPORT static const RGBA32 transparent = 0x00000000;
static const RGBA32 cyan = 0xFF00FFFF;
+ static const RGBA32 yellow = 0xFFFFFF00;
#if PLATFORM(IOS)
- static const RGBA32 tap = 0x4D1A1A1A;
-
- // FIXME: This color shouldn't be iOS-specific. Once we fix up its usage in InlineTextBox::paintCompositionBackground()
- // we should move it outside the PLATFORM(IOS)-guard. See <https://bugs.webkit.org/show_bug.cgi?id=126296>.
static const RGBA32 compositionFill = 0x3CAFC0E3;
+#else
+ static const RGBA32 compositionFill = 0xFFE1DD55;
#endif
+ WEBCORE_EXPORT bool isExtended() const;
+ WEBCORE_EXPORT ExtendedColor& asExtended() const;
+
+ WEBCORE_EXPORT Color& operator=(const Color&);
+ WEBCORE_EXPORT Color& operator=(Color&&);
+
+ friend bool operator==(const Color& a, const Color& b);
+
+ static bool isBlackColor(const Color&);
+ static bool isWhiteColor(const Color&);
+
private:
- RGBA32 m_color;
- bool m_valid;
+ void setRGB(int r, int g, int b) { setRGB(makeRGB(r, g, b)); }
+ void setRGB(RGBA32);
+
+ // 0x_______00 is an ExtendedColor pointer.
+ // 0x_______01 is an invalid RGBA32.
+ // 0x_______11 is a valid RGBA32.
+ static const uint64_t extendedColor = 0x0;
+ static const uint64_t invalidRGBAColor = 0x1;
+ static const uint64_t validRGBAColorBit = 0x2;
+ static const uint64_t validRGBAColor = 0x3;
+
+ static const uint64_t deletedHashValue = 0xFFFFFFFFFFFFFFFD;
+ static const uint64_t emptyHashValue = 0xFFFFFFFFFFFFFFFB;
+
+ WEBCORE_EXPORT void tagAsValid();
+
+ union {
+ uint64_t rgbaAndFlags { invalidRGBAColor };
+ ExtendedColor* extendedColor;
+ } m_colorData;
};
+// FIXME: These do not work for ExtendedColor because
+// they become just pointer comparison.
+bool operator==(const Color&, const Color&);
+bool operator!=(const Color&, const Color&);
+
+Color colorFromPremultipliedARGB(RGBA32);
+RGBA32 premultipliedARGBFromColor(const Color&);
+
+Color blend(const Color& from, const Color& to, double progress, bool blendPremultiplied = true);
+
+int differenceSquared(const Color&, const Color&);
+
+uint16_t fastMultiplyBy255(uint16_t value);
+uint16_t fastDivideBy255(uint16_t);
+
+#if USE(CG)
+WEBCORE_EXPORT CGColorRef cachedCGColor(const Color&);
+#endif
+
+inline RGBA::RGBA()
+{
+}
+
+inline RGBA::RGBA(uint8_t red, uint8_t green, uint8_t blue, uint8_t alpha)
+ : m_integer(alpha << 24 | red << 16 | green << 8 | blue)
+{
+}
+
+inline RGBA::RGBA(uint8_t red, uint8_t green, uint8_t blue)
+ : m_integer(0xFF000000 | red << 16 | green << 8 | blue)
+{
+}
+
+inline uint8_t RGBA::red() const
+{
+ return m_integer >> 16;
+}
+
+inline uint8_t RGBA::green() const
+{
+ return m_integer >> 8;
+}
+
+inline uint8_t RGBA::blue() const
+{
+ return m_integer;
+}
+
+inline uint8_t RGBA::alpha() const
+{
+ return m_integer >> 24;
+}
+
+inline bool RGBA::hasAlpha() const
+{
+ return (m_integer & 0xFF000000) != 0xFF000000;
+}
+
+inline Color::Color(RGBA color, ColorSpace space)
+{
+ setRGB(color.m_integer);
+ ASSERT_UNUSED(space, space == ColorSpaceSRGB);
+}
+
inline bool operator==(const Color& a, const Color& b)
{
- return a.rgb() == b.rgb() && a.isValid() == b.isValid();
+ return a.m_colorData.rgbaAndFlags == b.m_colorData.rgbaAndFlags;
}
inline bool operator!=(const Color& a, const Color& b)
@@ -175,50 +380,69 @@ inline bool operator!=(const Color& a, const Color& b)
return !(a == b);
}
-Color colorFromPremultipliedARGB(RGBA32);
-RGBA32 premultipliedARGBFromColor(const Color&);
+inline uint8_t roundAndClampColorChannel(int value)
+{
+ return std::max(0, std::min(255, value));
+}
-inline Color blend(const Color& from, const Color& to, double progress, bool blendPremultiplied = true)
+inline uint8_t roundAndClampColorChannel(float value)
{
- // We need to preserve the state of the valid flag at the end of the animation
- if (progress == 1 && !to.isValid())
- return Color();
-
- if (blendPremultiplied) {
- // Contrary to the name, RGBA32 actually stores ARGB, so we can initialize Color directly from premultipliedARGBFromColor().
- // Also, premultipliedARGBFromColor() bails on zero alpha, so special-case that.
- Color premultFrom = from.alpha() ? premultipliedARGBFromColor(from) : 0;
- Color premultTo = to.alpha() ? premultipliedARGBFromColor(to) : 0;
-
- Color premultBlended(blend(premultFrom.red(), premultTo.red(), progress),
- blend(premultFrom.green(), premultTo.green(), progress),
- blend(premultFrom.blue(), premultTo.blue(), progress),
- blend(premultFrom.alpha(), premultTo.alpha(), progress));
-
- return Color(colorFromPremultipliedARGB(premultBlended.rgb()));
- }
+ return std::max(0.f, std::min(255.f, std::round(value)));
+}
- return Color(blend(from.red(), to.red(), progress),
- blend(from.green(), to.green(), progress),
- blend(from.blue(), to.blue(), progress),
- blend(from.alpha(), to.alpha(), progress));
+inline uint16_t fastMultiplyBy255(uint16_t value)
+{
+ return (value << 8) - value;
}
inline uint16_t fastDivideBy255(uint16_t value)
{
- // This is an approximate algorithm for division by 255, but it gives accurate results for 16bit values.
+ // While this is an approximate algorithm for division by 255, it gives perfectly accurate results for 16-bit values.
+ // FIXME: Since this gives accurate results for 16-bit values, we should get this optimization into compilers like clang.
uint16_t approximation = value >> 8;
uint16_t remainder = value - (approximation * 255) + 1;
return approximation + (remainder >> 8);
}
-#if USE(CG)
-CGColorRef cachedCGColor(const Color&, ColorSpace);
-#if PLATFORM(IOS)
-CGColorRef createCGColorWithDeviceWhite(CGFloat white, CGFloat alpha);
-#endif // PLATFORM(IOS)
-#endif
+inline RGBA32 colorWithOverrideAlpha(RGBA32 color, std::optional<float> overrideAlpha)
+{
+ return overrideAlpha ? colorWithOverrideAlpha(color, overrideAlpha.value()) : color;
+}
-} // namespace WebCore
+inline RGBA32 Color::rgb() const
+{
+ // FIXME: We should ASSERT(!isExtended()) here, or produce
+ // an RGBA32 equivalent for an ExtendedColor. Ideally the former,
+ // so we can audit all the rgb() call sites to handle extended.
+ return static_cast<RGBA32>(m_colorData.rgbaAndFlags >> 32);
+}
+
+inline void Color::setRGB(RGBA32 rgb)
+{
+ m_colorData.rgbaAndFlags = static_cast<uint64_t>(rgb) << 32;
+ tagAsValid();
+}
+
+WEBCORE_EXPORT TextStream& operator<<(TextStream&, const Color&);
+
+inline bool Color::isBlackColor(const Color& color)
+{
+ if (color.isExtended()) {
+ const ExtendedColor& extendedColor = color.asExtended();
+ return !extendedColor.red() && !extendedColor.green() && !extendedColor.blue() && extendedColor.alpha() == 1;
+ }
-#endif // Color_h
+ return color.isValid() && color.rgb() == Color::black;
+}
+
+inline bool Color::isWhiteColor(const Color& color)
+{
+ if (color.isExtended()) {
+ const ExtendedColor& extendedColor = color.asExtended();
+ return extendedColor.red() == 1 && extendedColor.green() == 1 && extendedColor.blue() == 1 && extendedColor.alpha() == 1;
+ }
+
+ return color.isValid() && color.rgb() == Color::white;
+}
+
+} // namespace WebCore
diff --git a/Source/WebCore/platform/graphics/ColorHash.h b/Source/WebCore/platform/graphics/ColorHash.h
new file mode 100644
index 000000000..f08119351
--- /dev/null
+++ b/Source/WebCore/platform/graphics/ColorHash.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2016 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include "Color.h"
+#include <wtf/HashTraits.h>
+
+namespace WTF {
+
+struct ColorHash {
+ static unsigned hash(const WebCore::Color& key) { return key.hash(); }
+ static bool equal(const WebCore::Color& a, const WebCore::Color& b) { return a == b; }
+ static const bool safeToCompareToEmptyOrDeleted = true;
+};
+
+template<> struct DefaultHash<WebCore::Color> {
+ using Hash = ColorHash;
+};
+
+template<> struct HashTraits<WebCore::Color> : GenericHashTraits<WebCore::Color> {
+ static const bool emptyValueIsZero = false;
+ static WebCore::Color emptyValue() { return WebCore::Color(HashTableEmptyValue); }
+
+ static void constructDeletedValue(WebCore::Color& slot) { new (NotNull, &slot) WebCore::Color(HashTableDeletedValue); }
+ static bool isDeletedValue(const WebCore::Color& value) { return value.isHashTableDeletedValue(); }
+};
+
+} // namespace WTF
diff --git a/Source/WebCore/platform/graphics/ColorSpace.h b/Source/WebCore/platform/graphics/ColorSpace.h
index 7622c47b2..e12c40adc 100644
--- a/Source/WebCore/platform/graphics/ColorSpace.h
+++ b/Source/WebCore/platform/graphics/ColorSpace.h
@@ -31,7 +31,8 @@ namespace WebCore {
enum ColorSpace {
ColorSpaceDeviceRGB,
ColorSpaceSRGB,
- ColorSpaceLinearRGB
+ ColorSpaceLinearRGB,
+ ColorSpaceDisplayP3
};
} // namespace WebCore
diff --git a/Source/WebCore/platform/graphics/ComplexTextController.cpp b/Source/WebCore/platform/graphics/ComplexTextController.cpp
new file mode 100644
index 000000000..507765f42
--- /dev/null
+++ b/Source/WebCore/platform/graphics/ComplexTextController.cpp
@@ -0,0 +1,893 @@
+/*
+ * Copyright (C) 2007, 2008, 2009, 2010, 2011 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "ComplexTextController.h"
+
+#include "CharacterProperties.h"
+#include "FloatSize.h"
+#include "FontCascade.h"
+#include "RenderBlock.h"
+#include "RenderText.h"
+#include "TextRun.h"
+#include <unicode/ubrk.h>
+#include <wtf/Optional.h>
+#include <wtf/StdLibExtras.h>
+#include <wtf/text/TextBreakIterator.h>
+#include <wtf/unicode/CharacterNames.h>
+
+#if PLATFORM(IOS)
+#include <CoreText/CoreText.h>
+#endif
+
+#if PLATFORM(MAC)
+#include <ApplicationServices/ApplicationServices.h>
+#endif
+
+namespace WebCore {
+
+class TextLayout {
+ WTF_MAKE_FAST_ALLOCATED;
+public:
+ static bool isNeeded(RenderText& text, const FontCascade& font)
+ {
+ TextRun run = RenderBlock::constructTextRun(text, text.style());
+ return font.codePath(run) == FontCascade::Complex;
+ }
+
+ TextLayout(RenderText& text, const FontCascade& font, float xPos)
+ : m_font(font)
+ , m_run(constructTextRun(text, xPos))
+ , m_controller(std::make_unique<ComplexTextController>(m_font, m_run, true))
+ {
+ }
+
+ float width(unsigned from, unsigned len, HashSet<const Font*>* fallbackFonts)
+ {
+ m_controller->advance(from, 0, ByWholeGlyphs, fallbackFonts);
+ float beforeWidth = m_controller->runWidthSoFar();
+ if (m_font.wordSpacing() && from && FontCascade::treatAsSpace(m_run[from]))
+ beforeWidth += m_font.wordSpacing();
+ m_controller->advance(from + len, 0, ByWholeGlyphs, fallbackFonts);
+ float afterWidth = m_controller->runWidthSoFar();
+ return afterWidth - beforeWidth;
+ }
+
+private:
+ static TextRun constructTextRun(RenderText& text, float xPos)
+ {
+ TextRun run = RenderBlock::constructTextRun(text, text.style());
+ run.setCharactersLength(text.textLength());
+ ASSERT(run.charactersLength() >= run.length());
+ run.setXPos(xPos);
+ return run;
+ }
+
+ // ComplexTextController has only references to its FontCascade and TextRun so they must be kept alive here.
+ FontCascade m_font;
+ TextRun m_run;
+ std::unique_ptr<ComplexTextController> m_controller;
+};
+
+void TextLayoutDeleter::operator()(TextLayout* layout) const
+{
+#if PLATFORM(COCOA)
+ delete layout;
+#else
+ ASSERT_UNUSED(layout, !layout);
+#endif
+}
+
+std::unique_ptr<TextLayout, TextLayoutDeleter> FontCascade::createLayout(RenderText& text, float xPos, bool collapseWhiteSpace) const
+{
+#if PLATFORM(COCOA)
+ if (!collapseWhiteSpace || !TextLayout::isNeeded(text, *this))
+ return nullptr;
+ return std::unique_ptr<TextLayout, TextLayoutDeleter>(new TextLayout(text, *this, xPos));
+#else
+ UNUSED_PARAM(text);
+ UNUSED_PARAM(xPos);
+ UNUSED_PARAM(collapseWhiteSpace);
+ return nullptr;
+#endif
+}
+
+float FontCascade::width(TextLayout& layout, unsigned from, unsigned len, HashSet<const Font*>* fallbackFonts)
+{
+#if PLATFORM(COCOA)
+ return layout.width(from, len, fallbackFonts);
+#else
+ UNUSED_PARAM(layout);
+ UNUSED_PARAM(from);
+ UNUSED_PARAM(len);
+ UNUSED_PARAM(fallbackFonts);
+ ASSERT_NOT_REACHED();
+ return 0;
+#endif
+}
+
+void ComplexTextController::computeExpansionOpportunity()
+{
+ if (!m_expansion)
+ m_expansionPerOpportunity = 0;
+ else {
+ unsigned expansionOpportunityCount = FontCascade::expansionOpportunityCount(m_run.text(), m_run.ltr() ? LTR : RTL, m_run.expansionBehavior()).first;
+
+ if (!expansionOpportunityCount)
+ m_expansionPerOpportunity = 0;
+ else
+ m_expansionPerOpportunity = m_expansion / expansionOpportunityCount;
+ }
+}
+
+ComplexTextController::ComplexTextController(const FontCascade& font, const TextRun& run, bool mayUseNaturalWritingDirection, HashSet<const Font*>* fallbackFonts, bool forTextEmphasis)
+ : m_fallbackFonts(fallbackFonts)
+ , m_font(font)
+ , m_run(run)
+ , m_end(run.length())
+ , m_expansion(run.expansion())
+ , m_mayUseNaturalWritingDirection(mayUseNaturalWritingDirection)
+ , m_forTextEmphasis(forTextEmphasis)
+{
+ computeExpansionOpportunity();
+
+ collectComplexTextRuns();
+
+ finishConstruction();
+}
+
+ComplexTextController::ComplexTextController(const FontCascade& font, const TextRun& run, Vector<Ref<ComplexTextRun>>& runs)
+ : m_font(font)
+ , m_run(run)
+ , m_end(run.length())
+ , m_expansion(run.expansion())
+{
+ computeExpansionOpportunity();
+
+ for (auto& run : runs)
+ m_complexTextRuns.append(run.ptr());
+
+ finishConstruction();
+}
+
+void ComplexTextController::finishConstruction()
+{
+ adjustGlyphsAndAdvances();
+
+ if (!m_isLTROnly) {
+ m_runIndices.reserveInitialCapacity(m_complexTextRuns.size());
+
+ m_glyphCountFromStartToIndex.reserveInitialCapacity(m_complexTextRuns.size());
+ unsigned glyphCountSoFar = 0;
+ for (unsigned i = 0; i < m_complexTextRuns.size(); ++i) {
+ m_glyphCountFromStartToIndex.uncheckedAppend(glyphCountSoFar);
+ glyphCountSoFar += m_complexTextRuns[i]->glyphCount();
+ }
+ }
+}
+
+unsigned ComplexTextController::offsetForPosition(float h, bool includePartialGlyphs)
+{
+ if (h >= m_totalWidth)
+ return m_run.ltr() ? m_end : 0;
+
+ if (h < 0)
+ return m_run.ltr() ? 0 : m_end;
+
+ float x = h;
+
+ size_t runCount = m_complexTextRuns.size();
+ unsigned offsetIntoAdjustedGlyphs = 0;
+
+ for (size_t r = 0; r < runCount; ++r) {
+ const ComplexTextRun& complexTextRun = *m_complexTextRuns[r];
+ for (unsigned j = 0; j < complexTextRun.glyphCount(); ++j) {
+ unsigned index = offsetIntoAdjustedGlyphs + j;
+ float adjustedAdvance = m_adjustedBaseAdvances[index].width();
+ if (x < adjustedAdvance) {
+ unsigned hitGlyphStart = complexTextRun.indexAt(j);
+ unsigned hitGlyphEnd;
+ if (m_run.ltr())
+ hitGlyphEnd = std::max(hitGlyphStart, j + 1 < complexTextRun.glyphCount() ? complexTextRun.indexAt(j + 1) : complexTextRun.indexEnd());
+ else
+ hitGlyphEnd = std::max(hitGlyphStart, j > 0 ? complexTextRun.indexAt(j - 1) : complexTextRun.indexEnd());
+
+ // FIXME: Instead of dividing the glyph's advance equally between the characters, this
+ // could use the glyph's "ligature carets". This is available in CoreText via CTFontGetLigatureCaretPositions().
+ unsigned hitIndex = hitGlyphStart + (hitGlyphEnd - hitGlyphStart) * (m_run.ltr() ? x / adjustedAdvance : 1 - x / adjustedAdvance);
+ unsigned stringLength = complexTextRun.stringLength();
+ UBreakIterator* cursorPositionIterator = cursorMovementIterator(StringView(complexTextRun.characters(), stringLength));
+ unsigned clusterStart;
+ if (ubrk_isBoundary(cursorPositionIterator, hitIndex))
+ clusterStart = hitIndex;
+ else {
+ int preceeding = ubrk_preceding(cursorPositionIterator, hitIndex);
+ clusterStart = preceeding == UBRK_DONE ? 0 : preceeding;
+ }
+
+ if (!includePartialGlyphs)
+ return complexTextRun.stringLocation() + clusterStart;
+
+ int following = ubrk_following(cursorPositionIterator, hitIndex);
+ unsigned clusterEnd = following == UBRK_DONE ? stringLength : following;
+
+ float clusterWidth;
+ // FIXME: The search stops at the boundaries of complexTextRun. In theory, it should go on into neighboring ComplexTextRuns
+ // derived from the same CTLine. In practice, we do not expect there to be more than one CTRun in a CTLine, as no
+ // reordering and no font fallback should occur within a CTLine.
+ if (clusterEnd - clusterStart > 1) {
+ clusterWidth = adjustedAdvance;
+ if (j) {
+ unsigned firstGlyphBeforeCluster = j - 1;
+ while (complexTextRun.indexAt(firstGlyphBeforeCluster) >= clusterStart && complexTextRun.indexAt(firstGlyphBeforeCluster) < clusterEnd) {
+ float width = m_adjustedBaseAdvances[offsetIntoAdjustedGlyphs + firstGlyphBeforeCluster].width();
+ clusterWidth += width;
+ x += width;
+ if (!firstGlyphBeforeCluster)
+ break;
+ firstGlyphBeforeCluster--;
+ }
+ }
+ unsigned firstGlyphAfterCluster = j + 1;
+ while (firstGlyphAfterCluster < complexTextRun.glyphCount() && complexTextRun.indexAt(firstGlyphAfterCluster) >= clusterStart && complexTextRun.indexAt(firstGlyphAfterCluster) < clusterEnd) {
+ clusterWidth += m_adjustedBaseAdvances[offsetIntoAdjustedGlyphs + firstGlyphAfterCluster].width();
+ firstGlyphAfterCluster++;
+ }
+ } else {
+ clusterWidth = adjustedAdvance / (hitGlyphEnd - hitGlyphStart);
+ x -= clusterWidth * (m_run.ltr() ? hitIndex - hitGlyphStart : hitGlyphEnd - hitIndex - 1);
+ }
+ if (x <= clusterWidth / 2)
+ return complexTextRun.stringLocation() + (m_run.ltr() ? clusterStart : clusterEnd);
+ return complexTextRun.stringLocation() + (m_run.ltr() ? clusterEnd : clusterStart);
+ }
+ x -= adjustedAdvance;
+ }
+ offsetIntoAdjustedGlyphs += complexTextRun.glyphCount();
+ }
+
+ ASSERT_NOT_REACHED();
+ return 0;
+}
+
+// FIXME: We should consider reimplementing this function using ICU to advance by grapheme.
+// The current implementation only considers explicitly emoji sequences and emoji variations.
+static bool advanceByCombiningCharacterSequence(const UChar*& iterator, const UChar* end, UChar32& baseCharacter, unsigned& markCount)
+{
+ ASSERT(iterator < end);
+
+ markCount = 0;
+
+ unsigned i = 0;
+ unsigned remainingCharacters = end - iterator;
+ U16_NEXT(iterator, i, remainingCharacters, baseCharacter);
+ iterator = iterator + i;
+ if (U_IS_SURROGATE(baseCharacter))
+ return false;
+
+ // Consume marks.
+ bool sawEmojiGroupCandidate = isEmojiGroupCandidate(baseCharacter);
+ bool sawJoiner = false;
+ while (iterator < end) {
+ UChar32 nextCharacter;
+ unsigned markLength = 0;
+ bool shouldContinue = false;
+ ASSERT(end >= iterator);
+ U16_NEXT(iterator, markLength, static_cast<unsigned>(end - iterator), nextCharacter);
+
+ if (isVariationSelector(nextCharacter) || isEmojiFitzpatrickModifier(nextCharacter))
+ shouldContinue = true;
+
+ if (sawJoiner && isEmojiGroupCandidate(nextCharacter))
+ shouldContinue = true;
+
+ sawJoiner = false;
+ if (sawEmojiGroupCandidate && nextCharacter == zeroWidthJoiner) {
+ sawJoiner = true;
+ shouldContinue = true;
+ }
+
+ if (!shouldContinue && !(U_GET_GC_MASK(nextCharacter) & U_GC_M_MASK))
+ break;
+
+ markCount += markLength;
+ iterator += markLength;
+ }
+
+ return true;
+}
+
+// FIXME: Capitalization is language-dependent and context-dependent and should operate on grapheme clusters instead of codepoints.
+static inline std::optional<UChar32> capitalized(UChar32 baseCharacter)
+{
+ if (U_GET_GC_MASK(baseCharacter) & U_GC_M_MASK)
+ return std::nullopt;
+
+ UChar32 uppercaseCharacter = u_toupper(baseCharacter);
+ ASSERT(uppercaseCharacter == baseCharacter || (U_IS_BMP(baseCharacter) == U_IS_BMP(uppercaseCharacter)));
+ if (uppercaseCharacter != baseCharacter)
+ return uppercaseCharacter;
+ return std::nullopt;
+}
+
+static bool shouldSynthesize(bool dontSynthesizeSmallCaps, const Font* nextFont, UChar32 baseCharacter, std::optional<UChar32> capitalizedBase, FontVariantCaps fontVariantCaps, bool engageAllSmallCapsProcessing)
+{
+ if (dontSynthesizeSmallCaps)
+ return false;
+ if (!nextFont || nextFont == Font::systemFallback())
+ return false;
+ if (engageAllSmallCapsProcessing && isASCIISpace(baseCharacter))
+ return false;
+ if (!engageAllSmallCapsProcessing && !capitalizedBase)
+ return false;
+ return !nextFont->variantCapsSupportsCharacterForSynthesis(fontVariantCaps, baseCharacter);
+}
+
+void ComplexTextController::collectComplexTextRuns()
+{
+ if (!m_end)
+ return;
+
+ // We break up glyph run generation for the string by Font.
+ const UChar* cp;
+
+ if (m_run.is8Bit()) {
+ String stringFor8BitRun = String::make16BitFrom8BitSource(m_run.characters8(), m_run.length());
+ m_stringsFor8BitRuns.append(WTFMove(stringFor8BitRun));
+ cp = m_stringsFor8BitRuns.last().characters16();
+ } else
+ cp = m_run.characters16();
+
+ auto fontVariantCaps = m_font.fontDescription().variantCaps();
+ bool dontSynthesizeSmallCaps = !static_cast<bool>(m_font.fontDescription().fontSynthesis() & FontSynthesisSmallCaps);
+ bool engageAllSmallCapsProcessing = fontVariantCaps == FontVariantCaps::AllSmall || fontVariantCaps == FontVariantCaps::AllPetite;
+ bool engageSmallCapsProcessing = engageAllSmallCapsProcessing || fontVariantCaps == FontVariantCaps::Small || fontVariantCaps == FontVariantCaps::Petite;
+
+ if (engageAllSmallCapsProcessing || engageSmallCapsProcessing)
+ m_smallCapsBuffer.resize(m_end);
+
+ unsigned indexOfFontTransition = 0;
+ const UChar* curr = cp;
+ const UChar* end = cp + m_end;
+
+ const Font* font;
+ const Font* nextFont;
+ const Font* synthesizedFont = nullptr;
+ const Font* smallSynthesizedFont = nullptr;
+
+ unsigned markCount;
+ UChar32 baseCharacter;
+ if (!advanceByCombiningCharacterSequence(curr, end, baseCharacter, markCount))
+ return;
+
+ nextFont = m_font.fontForCombiningCharacterSequence(cp, curr - cp);
+
+ bool isSmallCaps = false;
+ bool nextIsSmallCaps = false;
+
+ auto capitalizedBase = capitalized(baseCharacter);
+ if (shouldSynthesize(dontSynthesizeSmallCaps, nextFont, baseCharacter, capitalizedBase, fontVariantCaps, engageAllSmallCapsProcessing)) {
+ synthesizedFont = &nextFont->noSynthesizableFeaturesFont();
+ smallSynthesizedFont = synthesizedFont->smallCapsFont(m_font.fontDescription());
+ UChar32 characterToWrite = capitalizedBase ? capitalizedBase.value() : cp[0];
+ unsigned characterIndex = 0;
+ U16_APPEND_UNSAFE(m_smallCapsBuffer, characterIndex, characterToWrite);
+ for (unsigned i = characterIndex; cp + i < curr; ++i)
+ m_smallCapsBuffer[i] = cp[i];
+ nextIsSmallCaps = true;
+ }
+
+ while (curr < end) {
+ font = nextFont;
+ isSmallCaps = nextIsSmallCaps;
+ unsigned index = curr - cp;
+
+ if (!advanceByCombiningCharacterSequence(curr, end, baseCharacter, markCount))
+ return;
+
+ if (synthesizedFont) {
+ if (auto capitalizedBase = capitalized(baseCharacter)) {
+ unsigned characterIndex = index;
+ U16_APPEND_UNSAFE(m_smallCapsBuffer, characterIndex, capitalizedBase.value());
+ for (unsigned i = 0; i < markCount; ++i)
+ m_smallCapsBuffer[i + characterIndex] = cp[i + characterIndex];
+ nextIsSmallCaps = true;
+ } else {
+ if (engageAllSmallCapsProcessing) {
+ for (unsigned i = 0; i < curr - cp - index; ++i)
+ m_smallCapsBuffer[index + i] = cp[index + i];
+ }
+ nextIsSmallCaps = engageAllSmallCapsProcessing;
+ }
+ }
+
+ if (baseCharacter == zeroWidthJoiner)
+ nextFont = font;
+ else
+ nextFont = m_font.fontForCombiningCharacterSequence(cp + index, curr - cp - index);
+
+ capitalizedBase = capitalized(baseCharacter);
+ if (!synthesizedFont && shouldSynthesize(dontSynthesizeSmallCaps, nextFont, baseCharacter, capitalizedBase, fontVariantCaps, engageAllSmallCapsProcessing)) {
+ // Rather than synthesize each character individually, we should synthesize the entire "run" if any character requires synthesis.
+ synthesizedFont = &nextFont->noSynthesizableFeaturesFont();
+ smallSynthesizedFont = synthesizedFont->smallCapsFont(m_font.fontDescription());
+ nextIsSmallCaps = true;
+ curr = cp + indexOfFontTransition;
+ continue;
+ }
+
+ if (nextFont != font || nextIsSmallCaps != isSmallCaps) {
+ unsigned itemLength = index - indexOfFontTransition;
+ if (itemLength) {
+ unsigned itemStart = indexOfFontTransition;
+ if (synthesizedFont) {
+ if (isSmallCaps)
+ collectComplexTextRunsForCharacters(m_smallCapsBuffer.data() + itemStart, itemLength, itemStart, smallSynthesizedFont);
+ else
+ collectComplexTextRunsForCharacters(cp + itemStart, itemLength, itemStart, synthesizedFont);
+ } else
+ collectComplexTextRunsForCharacters(cp + itemStart, itemLength, itemStart, font);
+ if (nextFont != font) {
+ synthesizedFont = nullptr;
+ smallSynthesizedFont = nullptr;
+ nextIsSmallCaps = false;
+ }
+ }
+ indexOfFontTransition = index;
+ }
+ }
+
+ ASSERT(m_end >= indexOfFontTransition);
+ unsigned itemLength = m_end - indexOfFontTransition;
+ if (itemLength) {
+ unsigned itemStart = indexOfFontTransition;
+ if (synthesizedFont) {
+ if (nextIsSmallCaps)
+ collectComplexTextRunsForCharacters(m_smallCapsBuffer.data() + itemStart, itemLength, itemStart, smallSynthesizedFont);
+ else
+ collectComplexTextRunsForCharacters(cp + itemStart, itemLength, itemStart, synthesizedFont);
+ } else
+ collectComplexTextRunsForCharacters(cp + itemStart, itemLength, itemStart, nextFont);
+ }
+
+ if (!m_run.ltr())
+ m_complexTextRuns.reverse();
+}
+
+unsigned ComplexTextController::ComplexTextRun::indexAt(unsigned i) const
+{
+ ASSERT(i < m_glyphCount);
+
+ return m_coreTextIndices[i];
+}
+
+void ComplexTextController::ComplexTextRun::setIsNonMonotonic()
+{
+ ASSERT(m_isMonotonic);
+ m_isMonotonic = false;
+
+ Vector<bool, 64> mappedIndices(m_stringLength, false);
+ for (unsigned i = 0; i < m_glyphCount; ++i) {
+ ASSERT(indexAt(i) < m_stringLength);
+ mappedIndices[indexAt(i)] = true;
+ }
+
+ m_glyphEndOffsets.grow(m_glyphCount);
+ for (unsigned i = 0; i < m_glyphCount; ++i) {
+ unsigned nextMappedIndex = m_indexEnd;
+ for (unsigned j = indexAt(i) + 1; j < m_stringLength; ++j) {
+ if (mappedIndices[j]) {
+ nextMappedIndex = j;
+ break;
+ }
+ }
+ m_glyphEndOffsets[i] = nextMappedIndex;
+ }
+}
+
+unsigned ComplexTextController::indexOfCurrentRun(unsigned& leftmostGlyph)
+{
+ leftmostGlyph = 0;
+
+ size_t runCount = m_complexTextRuns.size();
+ if (m_currentRun >= runCount)
+ return runCount;
+
+ if (m_isLTROnly) {
+ for (unsigned i = 0; i < m_currentRun; ++i)
+ leftmostGlyph += m_complexTextRuns[i]->glyphCount();
+ return m_currentRun;
+ }
+
+ if (m_runIndices.isEmpty()) {
+ unsigned firstRun = 0;
+ unsigned firstRunOffset = stringBegin(*m_complexTextRuns[0]);
+ for (unsigned i = 1; i < runCount; ++i) {
+ unsigned offset = stringBegin(*m_complexTextRuns[i]);
+ if (offset < firstRunOffset) {
+ firstRun = i;
+ firstRunOffset = offset;
+ }
+ }
+ m_runIndices.uncheckedAppend(firstRun);
+ }
+
+ while (m_runIndices.size() <= m_currentRun) {
+ unsigned offset = stringEnd(*m_complexTextRuns[m_runIndices.last()]);
+
+ for (unsigned i = 0; i < runCount; ++i) {
+ if (offset == stringBegin(*m_complexTextRuns[i])) {
+ m_runIndices.uncheckedAppend(i);
+ break;
+ }
+ }
+ }
+
+ unsigned currentRunIndex = m_runIndices[m_currentRun];
+ leftmostGlyph = m_glyphCountFromStartToIndex[currentRunIndex];
+ return currentRunIndex;
+}
+
+unsigned ComplexTextController::incrementCurrentRun(unsigned& leftmostGlyph)
+{
+ if (m_isLTROnly) {
+ leftmostGlyph += m_complexTextRuns[m_currentRun++]->glyphCount();
+ return m_currentRun;
+ }
+
+ m_currentRun++;
+ leftmostGlyph = 0;
+ return indexOfCurrentRun(leftmostGlyph);
+}
+
+float ComplexTextController::runWidthSoFarFraction(unsigned glyphStartOffset, unsigned glyphEndOffset, unsigned oldCharacterInCurrentGlyph, GlyphIterationStyle iterationStyle) const
+{
+ // FIXME: Instead of dividing the glyph's advance equally between the characters, this
+ // could use the glyph's "ligature carets". This is available in CoreText via CTFontGetLigatureCaretPositions().
+ if (glyphStartOffset == glyphEndOffset) {
+ // When there are multiple glyphs per character we need to advance by the full width of the glyph.
+ ASSERT(m_characterInCurrentGlyph == oldCharacterInCurrentGlyph);
+ return 1;
+ }
+
+ if (iterationStyle == ByWholeGlyphs) {
+ if (!oldCharacterInCurrentGlyph)
+ return 1;
+ return 0;
+ }
+
+ return static_cast<float>(m_characterInCurrentGlyph - oldCharacterInCurrentGlyph) / (glyphEndOffset - glyphStartOffset);
+}
+
+void ComplexTextController::advance(unsigned offset, GlyphBuffer* glyphBuffer, GlyphIterationStyle iterationStyle, HashSet<const Font*>* fallbackFonts)
+{
+ if (offset > m_end)
+ offset = m_end;
+
+ if (offset <= m_currentCharacter) {
+ m_runWidthSoFar = 0;
+ m_numGlyphsSoFar = 0;
+ m_currentRun = 0;
+ m_glyphInCurrentRun = 0;
+ m_characterInCurrentGlyph = 0;
+ }
+
+ m_currentCharacter = offset;
+
+ size_t runCount = m_complexTextRuns.size();
+
+ unsigned indexOfLeftmostGlyphInCurrentRun = 0; // Relative to the beginning of ComplexTextController.
+ unsigned currentRunIndex = indexOfCurrentRun(indexOfLeftmostGlyphInCurrentRun);
+ while (m_currentRun < runCount) {
+ const ComplexTextRun& complexTextRun = *m_complexTextRuns[currentRunIndex];
+ bool ltr = complexTextRun.isLTR();
+ unsigned glyphCount = complexTextRun.glyphCount();
+ unsigned glyphIndexIntoCurrentRun = ltr ? m_glyphInCurrentRun : glyphCount - 1 - m_glyphInCurrentRun;
+ unsigned glyphIndexIntoComplexTextController = indexOfLeftmostGlyphInCurrentRun + glyphIndexIntoCurrentRun;
+ if (fallbackFonts && &complexTextRun.font() != &m_font.primaryFont())
+ fallbackFonts->add(&complexTextRun.font());
+
+ // We must store the initial advance for the first glyph we are going to draw.
+ // When leftmostGlyph is 0, it represents the first glyph to draw, taking into
+ // account the text direction.
+ if (!indexOfLeftmostGlyphInCurrentRun && glyphBuffer)
+ glyphBuffer->setInitialAdvance(GlyphBufferAdvance(complexTextRun.initialAdvance().width(), complexTextRun.initialAdvance().height()));
+
+ while (m_glyphInCurrentRun < glyphCount) {
+ unsigned glyphStartOffset = complexTextRun.indexAt(glyphIndexIntoCurrentRun);
+ unsigned glyphEndOffset;
+ if (complexTextRun.isMonotonic()) {
+ if (ltr)
+ glyphEndOffset = std::max(glyphStartOffset, glyphIndexIntoCurrentRun + 1 < glyphCount ? complexTextRun.indexAt(glyphIndexIntoCurrentRun + 1) : complexTextRun.indexEnd());
+ else
+ glyphEndOffset = std::max(glyphStartOffset, glyphIndexIntoCurrentRun > 0 ? complexTextRun.indexAt(glyphIndexIntoCurrentRun - 1) : complexTextRun.indexEnd());
+ } else
+ glyphEndOffset = complexTextRun.endOffsetAt(glyphIndexIntoCurrentRun);
+
+ FloatSize adjustedBaseAdvance = m_adjustedBaseAdvances[glyphIndexIntoComplexTextController];
+
+ if (glyphStartOffset + complexTextRun.stringLocation() >= m_currentCharacter)
+ return;
+
+ if (glyphBuffer && !m_characterInCurrentGlyph) {
+ auto currentGlyphOrigin = glyphOrigin(glyphIndexIntoComplexTextController);
+ GlyphBufferAdvance paintAdvance(adjustedBaseAdvance);
+ if (!glyphIndexIntoCurrentRun) {
+ // The first layout advance of every run includes the "initial layout advance." However, here, we need
+ // paint advances, so subtract it out before transforming the layout advance into a paint advance.
+ paintAdvance.setWidth(paintAdvance.width() - (complexTextRun.initialAdvance().width() - currentGlyphOrigin.x()));
+ paintAdvance.setHeight(paintAdvance.height() - (complexTextRun.initialAdvance().height() - currentGlyphOrigin.y()));
+ }
+ paintAdvance.setWidth(paintAdvance.width() + glyphOrigin(glyphIndexIntoComplexTextController + 1).x() - currentGlyphOrigin.x());
+ paintAdvance.setHeight(paintAdvance.height() + glyphOrigin(glyphIndexIntoComplexTextController + 1).y() - currentGlyphOrigin.y());
+ if (glyphIndexIntoCurrentRun == glyphCount - 1 && currentRunIndex + 1 < runCount) {
+ // Our paint advance points to the end of the run. However, the next run may have an
+ // initial advance, and our paint advance needs to point to the location of the next
+ // glyph. So, we need to add in the next run's initial advance.
+ paintAdvance.setWidth(paintAdvance.width() - glyphOrigin(glyphIndexIntoComplexTextController + 1).x() + m_complexTextRuns[currentRunIndex + 1]->initialAdvance().width());
+ paintAdvance.setHeight(paintAdvance.height() - glyphOrigin(glyphIndexIntoComplexTextController + 1).y() + m_complexTextRuns[currentRunIndex + 1]->initialAdvance().height());
+ }
+ paintAdvance.setHeight(-paintAdvance.height()); // Increasing y points down
+ glyphBuffer->add(m_adjustedGlyphs[glyphIndexIntoComplexTextController], &complexTextRun.font(), paintAdvance, complexTextRun.indexAt(m_glyphInCurrentRun));
+ }
+
+ unsigned oldCharacterInCurrentGlyph = m_characterInCurrentGlyph;
+ m_characterInCurrentGlyph = std::min(m_currentCharacter - complexTextRun.stringLocation(), glyphEndOffset) - glyphStartOffset;
+ m_runWidthSoFar += adjustedBaseAdvance.width() * runWidthSoFarFraction(glyphStartOffset, glyphEndOffset, oldCharacterInCurrentGlyph, iterationStyle);
+
+ if (glyphEndOffset + complexTextRun.stringLocation() > m_currentCharacter)
+ return;
+
+ m_numGlyphsSoFar++;
+ m_glyphInCurrentRun++;
+ m_characterInCurrentGlyph = 0;
+ if (ltr) {
+ glyphIndexIntoCurrentRun++;
+ glyphIndexIntoComplexTextController++;
+ } else {
+ glyphIndexIntoCurrentRun--;
+ glyphIndexIntoComplexTextController--;
+ }
+ }
+ currentRunIndex = incrementCurrentRun(indexOfLeftmostGlyphInCurrentRun);
+ m_glyphInCurrentRun = 0;
+ }
+}
+
+static inline std::pair<bool, bool> expansionLocation(bool ideograph, bool treatAsSpace, bool ltr, bool isAfterExpansion, bool forbidLeadingExpansion, bool forbidTrailingExpansion, bool forceLeadingExpansion, bool forceTrailingExpansion)
+{
+ bool expandLeft = ideograph;
+ bool expandRight = ideograph;
+ if (treatAsSpace) {
+ if (ltr)
+ expandRight = true;
+ else
+ expandLeft = true;
+ }
+ if (isAfterExpansion)
+ expandLeft = false;
+ ASSERT(!forbidLeadingExpansion || !forceLeadingExpansion);
+ ASSERT(!forbidTrailingExpansion || !forceTrailingExpansion);
+ if (forbidLeadingExpansion)
+ expandLeft = false;
+ if (forbidTrailingExpansion)
+ expandRight = false;
+ if (forceLeadingExpansion)
+ expandLeft = true;
+ if (forceTrailingExpansion)
+ expandRight = true;
+ return std::make_pair(expandLeft, expandRight);
+}
+
+void ComplexTextController::adjustGlyphsAndAdvances()
+{
+ bool afterExpansion = (m_run.expansionBehavior() & LeadingExpansionMask) == ForbidLeadingExpansion;
+ size_t runCount = m_complexTextRuns.size();
+ bool hasExtraSpacing = (m_font.letterSpacing() || m_font.wordSpacing() || m_expansion) && !m_run.spacingDisabled();
+ bool runForcesLeadingExpansion = (m_run.expansionBehavior() & LeadingExpansionMask) == ForceLeadingExpansion;
+ bool runForcesTrailingExpansion = (m_run.expansionBehavior() & TrailingExpansionMask) == ForceTrailingExpansion;
+ bool runForbidsLeadingExpansion = (m_run.expansionBehavior() & LeadingExpansionMask) == ForbidLeadingExpansion;
+ bool runForbidsTrailingExpansion = (m_run.expansionBehavior() & TrailingExpansionMask) == ForbidTrailingExpansion;
+
+ // We are iterating in glyph order, not string order. Compare this to WidthIterator::advanceInternal()
+ for (size_t runIndex = 0; runIndex < runCount; ++runIndex) {
+ ComplexTextRun& complexTextRun = *m_complexTextRuns[runIndex];
+ unsigned glyphCount = complexTextRun.glyphCount();
+ const Font& font = complexTextRun.font();
+
+ if (!complexTextRun.isLTR())
+ m_isLTROnly = false;
+
+ const CGGlyph* glyphs = complexTextRun.glyphs();
+ const FloatSize* advances = complexTextRun.baseAdvances();
+
+ float spaceWidth = font.spaceWidth() - font.syntheticBoldOffset();
+ const UChar* cp = complexTextRun.characters();
+ FloatPoint glyphOrigin;
+ unsigned lastCharacterIndex = m_run.ltr() ? std::numeric_limits<unsigned>::min() : std::numeric_limits<unsigned>::max();
+ bool isMonotonic = true;
+
+ for (unsigned i = 0; i < glyphCount; i++) {
+ unsigned characterIndex = complexTextRun.indexAt(i);
+ if (m_run.ltr()) {
+ if (characterIndex < lastCharacterIndex)
+ isMonotonic = false;
+ } else {
+ if (characterIndex > lastCharacterIndex)
+ isMonotonic = false;
+ }
+ UChar ch = *(cp + characterIndex);
+
+ bool treatAsSpace = FontCascade::treatAsSpace(ch);
+ CGGlyph glyph = treatAsSpace ? font.spaceGlyph() : glyphs[i];
+ FloatSize advance = treatAsSpace ? FloatSize(spaceWidth, advances[i].height()) : advances[i];
+
+ if (ch == '\t' && m_run.allowTabs())
+ advance.setWidth(m_font.tabWidth(font, m_run.tabSize(), m_run.xPos() + m_totalWidth));
+ else if (FontCascade::treatAsZeroWidthSpace(ch) && !treatAsSpace) {
+ advance.setWidth(0);
+ glyph = font.spaceGlyph();
+ }
+
+ if (!i) {
+ advance.expand(complexTextRun.initialAdvance().width(), complexTextRun.initialAdvance().height());
+ if (auto* origins = complexTextRun.glyphOrigins())
+ advance.expand(-origins[0].x(), -origins[0].y());
+ }
+
+ advance.expand(font.syntheticBoldOffset(), 0);
+
+ if (hasExtraSpacing) {
+ // If we're a glyph with an advance, add in letter-spacing.
+ // That way we weed out zero width lurkers. This behavior matches the fast text code path.
+ if (advance.width())
+ advance.expand(m_font.letterSpacing(), 0);
+
+ unsigned characterIndexInRun = characterIndex + complexTextRun.stringLocation();
+ bool isFirstCharacter = !(characterIndex + complexTextRun.stringLocation());
+ bool isLastCharacter = characterIndexInRun + 1 == m_run.length() || (U16_IS_LEAD(ch) && characterIndexInRun + 2 == m_run.length() && U16_IS_TRAIL(*(cp + characterIndex + 1)));
+
+ bool forceLeadingExpansion = false; // On the left, regardless of m_run.ltr()
+ bool forceTrailingExpansion = false; // On the right, regardless of m_run.ltr()
+ bool forbidLeadingExpansion = false;
+ bool forbidTrailingExpansion = false;
+ if (runForcesLeadingExpansion)
+ forceLeadingExpansion = m_run.ltr() ? isFirstCharacter : isLastCharacter;
+ if (runForcesTrailingExpansion)
+ forceTrailingExpansion = m_run.ltr() ? isLastCharacter : isFirstCharacter;
+ if (runForbidsLeadingExpansion)
+ forbidLeadingExpansion = m_run.ltr() ? isFirstCharacter : isLastCharacter;
+ if (runForbidsTrailingExpansion)
+ forbidTrailingExpansion = m_run.ltr() ? isLastCharacter : isFirstCharacter;
+ // Handle justification and word-spacing.
+ bool ideograph = FontCascade::isCJKIdeographOrSymbol(ch);
+ if (treatAsSpace || ideograph || forceLeadingExpansion || forceTrailingExpansion) {
+ // Distribute the run's total expansion evenly over all expansion opportunities in the run.
+ if (m_expansion) {
+ bool expandLeft, expandRight;
+ std::tie(expandLeft, expandRight) = expansionLocation(ideograph, treatAsSpace, m_run.ltr(), afterExpansion, forbidLeadingExpansion, forbidTrailingExpansion, forceLeadingExpansion, forceTrailingExpansion);
+ if (expandLeft) {
+ m_expansion -= m_expansionPerOpportunity;
+ // Increase previous width
+ if (m_adjustedBaseAdvances.isEmpty()) {
+ advance.expand(m_expansionPerOpportunity, 0);
+ complexTextRun.growInitialAdvanceHorizontally(m_expansionPerOpportunity);
+ } else {
+ m_adjustedBaseAdvances.last().expand(m_expansionPerOpportunity, 0);
+ m_totalWidth += m_expansionPerOpportunity;
+ }
+ }
+ if (expandRight) {
+ m_expansion -= m_expansionPerOpportunity;
+ advance.expand(m_expansionPerOpportunity, 0);
+ afterExpansion = true;
+ }
+ } else
+ afterExpansion = false;
+
+ // Account for word-spacing.
+ if (treatAsSpace && (ch != '\t' || !m_run.allowTabs()) && (characterIndex > 0 || runIndex > 0) && m_font.wordSpacing())
+ advance.expand(m_font.wordSpacing(), 0);
+ } else
+ afterExpansion = false;
+ }
+
+ m_totalWidth += advance.width();
+
+ // FIXME: Combining marks should receive a text emphasis mark if they are combine with a space.
+ if (m_forTextEmphasis && (!FontCascade::canReceiveTextEmphasis(ch) || (U_GET_GC_MASK(ch) & U_GC_M_MASK)))
+ glyph = 0;
+
+ m_adjustedBaseAdvances.append(advance);
+ if (auto* origins = complexTextRun.glyphOrigins()) {
+ ASSERT(m_glyphOrigins.size() < m_adjustedBaseAdvances.size());
+ m_glyphOrigins.grow(m_adjustedBaseAdvances.size());
+ m_glyphOrigins[m_glyphOrigins.size() - 1] = origins[i];
+ ASSERT(m_glyphOrigins.size() == m_adjustedBaseAdvances.size());
+ }
+ m_adjustedGlyphs.append(glyph);
+
+ FloatRect glyphBounds = font.boundsForGlyph(glyph);
+ glyphBounds.move(glyphOrigin.x(), glyphOrigin.y());
+ m_minGlyphBoundingBoxX = std::min(m_minGlyphBoundingBoxX, glyphBounds.x());
+ m_maxGlyphBoundingBoxX = std::max(m_maxGlyphBoundingBoxX, glyphBounds.maxX());
+ m_minGlyphBoundingBoxY = std::min(m_minGlyphBoundingBoxY, glyphBounds.y());
+ m_maxGlyphBoundingBoxY = std::max(m_maxGlyphBoundingBoxY, glyphBounds.maxY());
+ glyphOrigin.move(advance);
+
+ lastCharacterIndex = characterIndex;
+ }
+ if (!isMonotonic)
+ complexTextRun.setIsNonMonotonic();
+ }
+}
+
+// Missing glyphs run constructor. Core Text will not generate a run of missing glyphs, instead falling back on
+// glyphs from LastResort. We want to use the primary font's missing glyph in order to match the fast text code path.
+ComplexTextController::ComplexTextRun::ComplexTextRun(const Font& font, const UChar* characters, unsigned stringLocation, unsigned stringLength, unsigned indexBegin, unsigned indexEnd, bool ltr)
+ : m_font(font)
+ , m_characters(characters)
+ , m_stringLength(stringLength)
+ , m_indexBegin(indexBegin)
+ , m_indexEnd(indexEnd)
+ , m_stringLocation(stringLocation)
+ , m_isLTR(ltr)
+{
+ auto runLengthInCodeUnits = m_indexEnd - m_indexBegin;
+ m_coreTextIndices.reserveInitialCapacity(runLengthInCodeUnits);
+ unsigned r = m_indexBegin;
+ while (r < m_indexEnd) {
+ m_coreTextIndices.uncheckedAppend(r);
+ UChar32 character;
+ U16_NEXT(m_characters, r, m_stringLength, character);
+ }
+ m_glyphCount = m_coreTextIndices.size();
+ if (!ltr) {
+ for (unsigned r = 0, end = m_glyphCount - 1; r < m_glyphCount / 2; ++r, --end)
+ std::swap(m_coreTextIndices[r], m_coreTextIndices[end]);
+ }
+
+ // Synthesize a run of missing glyphs.
+ m_glyphs.fill(0, m_glyphCount);
+ m_baseAdvances.fill(FloatSize(m_font.widthForGlyph(0), 0), m_glyphCount);
+}
+
+ComplexTextController::ComplexTextRun::ComplexTextRun(const Vector<FloatSize>& advances, const Vector<FloatPoint>& origins, const Vector<Glyph>& glyphs, const Vector<unsigned>& stringIndices, FloatSize initialAdvance, const Font& font, const UChar* characters, unsigned stringLocation, unsigned stringLength, unsigned indexBegin, unsigned indexEnd, bool ltr)
+ : m_baseAdvances(advances)
+ , m_glyphOrigins(origins)
+ , m_glyphs(glyphs)
+ , m_coreTextIndices(stringIndices)
+ , m_initialAdvance(initialAdvance)
+ , m_font(font)
+ , m_characters(characters)
+ , m_stringLength(stringLength)
+ , m_indexBegin(indexBegin)
+ , m_indexEnd(indexEnd)
+ , m_glyphCount(glyphs.size())
+ , m_stringLocation(stringLocation)
+ , m_isLTR(ltr)
+{
+}
+
+} // namespace WebCore
diff --git a/Source/WebCore/platform/graphics/ComplexTextController.h b/Source/WebCore/platform/graphics/ComplexTextController.h
new file mode 100644
index 000000000..169d891b2
--- /dev/null
+++ b/Source/WebCore/platform/graphics/ComplexTextController.h
@@ -0,0 +1,231 @@
+/*
+ * Copyright (C) 2007, 2008, 2009, 2011 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include "FloatPoint.h"
+#include "GlyphBuffer.h"
+#include <wtf/HashSet.h>
+#include <wtf/RefCounted.h>
+#include <wtf/RetainPtr.h>
+#include <wtf/Vector.h>
+#include <wtf/text/WTFString.h>
+
+#define USE_LAYOUT_SPECIFIC_ADVANCES ((PLATFORM(MAC) && __MAC_OS_X_VERSION_MIN_REQUIRED >= 101100) || (PLATFORM(IOS) && __IPHONE_OS_VERSION_MIN_REQUIRED >= 90000))
+
+typedef unsigned short CGGlyph;
+
+typedef const struct __CTRun * CTRunRef;
+typedef const struct __CTLine * CTLineRef;
+
+namespace WebCore {
+
+class FontCascade;
+class Font;
+class TextRun;
+
+enum GlyphIterationStyle { IncludePartialGlyphs, ByWholeGlyphs };
+
+// See https://trac.webkit.org/wiki/ComplexTextController for more information about ComplexTextController.
+class ComplexTextController {
+ WTF_MAKE_FAST_ALLOCATED;
+public:
+ ComplexTextController(const FontCascade&, const TextRun&, bool mayUseNaturalWritingDirection = false, HashSet<const Font*>* fallbackFonts = 0, bool forTextEmphasis = false);
+
+ class ComplexTextRun;
+ WEBCORE_EXPORT ComplexTextController(const FontCascade&, const TextRun&, Vector<Ref<ComplexTextRun>>&);
+
+ // Advance and emit glyphs up to the specified character.
+ WEBCORE_EXPORT void advance(unsigned to, GlyphBuffer* = nullptr, GlyphIterationStyle = IncludePartialGlyphs, HashSet<const Font*>* fallbackFonts = nullptr);
+
+ // Compute the character offset for a given x coordinate.
+ unsigned offsetForPosition(float x, bool includePartialGlyphs);
+
+ // Returns the width of everything we've consumed so far.
+ float runWidthSoFar() const { return m_runWidthSoFar; }
+
+ float totalWidth() const { return m_totalWidth; }
+
+ float minGlyphBoundingBoxX() const { return m_minGlyphBoundingBoxX; }
+ float maxGlyphBoundingBoxX() const { return m_maxGlyphBoundingBoxX; }
+ float minGlyphBoundingBoxY() const { return m_minGlyphBoundingBoxY; }
+ float maxGlyphBoundingBoxY() const { return m_maxGlyphBoundingBoxY; }
+
+ class ComplexTextRun : public RefCounted<ComplexTextRun> {
+ public:
+ static Ref<ComplexTextRun> create(CTRunRef ctRun, const Font& font, const UChar* characters, unsigned stringLocation, unsigned stringLength, unsigned indexBegin, unsigned indexEnd)
+ {
+ return adoptRef(*new ComplexTextRun(ctRun, font, characters, stringLocation, stringLength, indexBegin, indexEnd));
+ }
+
+ static Ref<ComplexTextRun> create(const Font& font, const UChar* characters, unsigned stringLocation, unsigned stringLength, unsigned indexBegin, unsigned indexEnd, bool ltr)
+ {
+ return adoptRef(*new ComplexTextRun(font, characters, stringLocation, stringLength, indexBegin, indexEnd, ltr));
+ }
+
+ static Ref<ComplexTextRun> create(const Vector<FloatSize>& advances, const Vector<FloatPoint>& origins, const Vector<Glyph>& glyphs, const Vector<unsigned>& stringIndices, FloatSize initialAdvance, const Font& font, const UChar* characters, unsigned stringLocation, unsigned stringLength, unsigned indexBegin, unsigned indexEnd, bool ltr)
+ {
+ return adoptRef(*new ComplexTextRun(advances, origins, glyphs, stringIndices, initialAdvance, font, characters, stringLocation, stringLength, indexBegin, indexEnd, ltr));
+ }
+
+ unsigned glyphCount() const { return m_glyphCount; }
+ const Font& font() const { return m_font; }
+ const UChar* characters() const { return m_characters; }
+ unsigned stringLocation() const { return m_stringLocation; }
+ unsigned stringLength() const { return m_stringLength; }
+ ALWAYS_INLINE unsigned indexAt(unsigned) const;
+ unsigned indexBegin() const { return m_indexBegin; }
+ unsigned indexEnd() const { return m_indexEnd; }
+ unsigned endOffsetAt(unsigned i) const { ASSERT(!m_isMonotonic); return m_glyphEndOffsets[i]; }
+ const CGGlyph* glyphs() const { return m_glyphs.data(); }
+
+ /*
+ * This is the format of the information CoreText gives us about each run:
+ *
+ * ----->X (Paint glyph position) X (Paint glyph position) X (Paint glyph position)
+ * / 7 7 7
+ * / / / /
+ * (Initial advance) / / (Glyph origin) / (Glyph origin) / (Glyph origin)
+ * ------------------- / / /
+ * / / / /
+ * X X--------------------------X--------------------------X--------------------------X
+ * (text position ^) (base advance) (base advance) (base advance)
+ *
+ *
+ *
+ *
+ *
+ * And here is the output we transform this into (for each run):
+ *
+ * ----->X------------------------->X------------------------->X
+ * / (Paint advance) (Paint advance) \
+ * / \
+ * (Initial advance) / \ (Paint advance)
+ * ------------------- ----------------
+ * / \
+ * X--------------------------------------------------X--------------------------X--------------------------X
+ * (text position ^) (layout advance) (layout advance) (layout advance)
+ */
+ void growInitialAdvanceHorizontally(float delta) { m_initialAdvance.expand(delta, 0); }
+ FloatSize initialAdvance() const { return m_initialAdvance; }
+ const FloatSize* baseAdvances() const { return m_baseAdvances.data(); }
+ const FloatPoint* glyphOrigins() const { return m_glyphOrigins.size() == glyphCount() ? m_glyphOrigins.data() : nullptr; }
+ bool isLTR() const { return m_isLTR; }
+ bool isMonotonic() const { return m_isMonotonic; }
+ void setIsNonMonotonic();
+
+ private:
+ ComplexTextRun(CTRunRef, const Font&, const UChar* characters, unsigned stringLocation, unsigned stringLength, unsigned indexBegin, unsigned indexEnd);
+ ComplexTextRun(const Font&, const UChar* characters, unsigned stringLocation, unsigned stringLength, unsigned indexBegin, unsigned indexEnd, bool ltr);
+ WEBCORE_EXPORT ComplexTextRun(const Vector<FloatSize>& advances, const Vector<FloatPoint>& origins, const Vector<Glyph>& glyphs, const Vector<unsigned>& stringIndices, FloatSize initialAdvance, const Font&, const UChar* characters, unsigned stringLocation, unsigned stringLength, unsigned indexBegin, unsigned indexEnd, bool ltr);
+
+ Vector<FloatSize, 64> m_baseAdvances;
+ Vector<FloatPoint, 64> m_glyphOrigins;
+ Vector<CGGlyph, 64> m_glyphs;
+ Vector<unsigned, 64> m_glyphEndOffsets;
+ Vector<unsigned, 64> m_coreTextIndices;
+ FloatSize m_initialAdvance;
+ const Font& m_font;
+ const UChar* m_characters;
+ unsigned m_stringLength;
+ unsigned m_indexBegin;
+ unsigned m_indexEnd;
+ unsigned m_glyphCount;
+ unsigned m_stringLocation;
+ bool m_isLTR;
+ bool m_isMonotonic { true };
+ };
+private:
+ void computeExpansionOpportunity();
+ void finishConstruction();
+
+ static unsigned stringBegin(const ComplexTextRun& run) { return run.stringLocation() + run.indexBegin(); }
+ static unsigned stringEnd(const ComplexTextRun& run) { return run.stringLocation() + run.indexEnd(); }
+
+ void collectComplexTextRuns();
+
+ void collectComplexTextRunsForCharacters(const UChar*, unsigned length, unsigned stringLocation, const Font*);
+ void adjustGlyphsAndAdvances();
+
+ unsigned indexOfCurrentRun(unsigned& leftmostGlyph);
+ unsigned incrementCurrentRun(unsigned& leftmostGlyph);
+
+ float runWidthSoFarFraction(unsigned glyphStartOffset, unsigned glyphEndOffset, unsigned oldCharacterInCurrentGlyph, GlyphIterationStyle) const;
+
+ FloatPoint glyphOrigin(unsigned index) const { return index < m_glyphOrigins.size() ? m_glyphOrigins[index] : FloatPoint(); }
+
+ Vector<FloatSize, 256> m_adjustedBaseAdvances;
+ Vector<FloatPoint, 256> m_glyphOrigins;
+ Vector<CGGlyph, 256> m_adjustedGlyphs;
+
+ Vector<UChar, 256> m_smallCapsBuffer;
+
+ // There is a 3-level hierarchy here. At the top, we are interested in m_run.string(). We partition that string
+ // into Lines, each of which is a sequence of characters which should use the same Font. Core Text then partitions
+ // the Line into ComplexTextRuns.
+ // ComplexTextRun::stringLocation() and ComplexTextRun::stringLength() refer to the offset and length of the Line
+ // relative to m_run.string(). ComplexTextRun::indexAt() returns to the offset of a codepoint relative to
+ // its Line. ComplexTextRun::glyphs() and ComplexTextRun::advances() refer to glyphs relative to the ComplexTextRun.
+ // The length of the entire TextRun is m_run.length()
+ Vector<RefPtr<ComplexTextRun>, 16> m_complexTextRuns;
+
+ // The initial capacity of these vectors was selected as being the smallest power of two greater than
+ // the average (3.5) plus one standard deviation (7.5) of nonzero sizes used on Arabic Wikipedia.
+ Vector<unsigned, 16> m_runIndices;
+ Vector<unsigned, 16> m_glyphCountFromStartToIndex;
+
+#if PLATFORM(COCOA)
+ Vector<RetainPtr<CTLineRef>> m_coreTextLines;
+#endif
+
+ Vector<String> m_stringsFor8BitRuns;
+
+ HashSet<const Font*>* m_fallbackFonts { nullptr };
+
+ const FontCascade& m_font;
+ const TextRun& m_run;
+
+ unsigned m_currentCharacter { 0 };
+ unsigned m_end { 0 };
+
+ float m_totalWidth { 0 };
+ float m_runWidthSoFar { 0 };
+ unsigned m_numGlyphsSoFar { 0 };
+ unsigned m_currentRun { 0 };
+ unsigned m_glyphInCurrentRun { 0 };
+ unsigned m_characterInCurrentGlyph { 0 };
+ float m_expansion { 0 };
+ float m_expansionPerOpportunity { 0 };
+
+ float m_minGlyphBoundingBoxX { std::numeric_limits<float>::max() };
+ float m_maxGlyphBoundingBoxX { std::numeric_limits<float>::min() };
+ float m_minGlyphBoundingBoxY { std::numeric_limits<float>::max() };
+ float m_maxGlyphBoundingBoxY { std::numeric_limits<float>::min() };
+
+ bool m_isLTROnly { true };
+ bool m_mayUseNaturalWritingDirection { false };
+ bool m_forTextEmphasis { false };
+};
+
+} // namespace WebCore
diff --git a/Source/WebCore/platform/graphics/CrossfadeGeneratedImage.cpp b/Source/WebCore/platform/graphics/CrossfadeGeneratedImage.cpp
index bf44bffab..3a229c61b 100644
--- a/Source/WebCore/platform/graphics/CrossfadeGeneratedImage.cpp
+++ b/Source/WebCore/platform/graphics/CrossfadeGeneratedImage.cpp
@@ -10,10 +10,10 @@
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
- * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
@@ -29,10 +29,11 @@
#include "FloatRect.h"
#include "GraphicsContext.h"
#include "ImageBuffer.h"
+#include "TextStream.h"
namespace WebCore {
-CrossfadeGeneratedImage::CrossfadeGeneratedImage(Image* fromImage, Image* toImage, float percentage, IntSize crossfadeSize, const IntSize& size)
+CrossfadeGeneratedImage::CrossfadeGeneratedImage(Image& fromImage, Image& toImage, float percentage, const FloatSize& crossfadeSize, const FloatSize& size)
: m_fromImage(fromImage)
, m_toImage(toImage)
, m_percentage(percentage)
@@ -41,73 +42,83 @@ CrossfadeGeneratedImage::CrossfadeGeneratedImage(Image* fromImage, Image* toImag
setContainerSize(size);
}
-static void drawCrossfadeSubimage(GraphicsContext* context, Image* image, CompositeOperator operation, float opacity, IntSize targetSize)
+static void drawCrossfadeSubimage(GraphicsContext& context, Image& image, CompositeOperator operation, float opacity, const FloatSize& targetSize)
{
- IntSize imageSize = image->size();
+ FloatSize imageSize = image.size();
// SVGImage resets the opacity when painting, so we have to use transparency layers to accurately paint one at a given opacity.
- bool useTransparencyLayer = image->isSVGImage();
+ bool useTransparencyLayer = image.isSVGImage();
- GraphicsContextStateSaver stateSaver(*context);
-
- context->setCompositeOperation(operation);
+ GraphicsContextStateSaver stateSaver(context);
+
+ CompositeOperator drawImageOperation = operation;
- if (useTransparencyLayer)
- context->beginTransparencyLayer(opacity);
- else
- context->setAlpha(opacity);
+ if (useTransparencyLayer) {
+ context.setCompositeOperation(operation);
+ context.beginTransparencyLayer(opacity);
+ drawImageOperation = CompositeSourceOver;
+ } else
+ context.setAlpha(opacity);
if (targetSize != imageSize)
- context->scale(FloatSize(static_cast<float>(targetSize.width()) / imageSize.width(),
- static_cast<float>(targetSize.height()) / imageSize.height()));
- context->drawImage(image, ColorSpaceDeviceRGB, IntPoint());
+ context.scale(FloatSize(targetSize.width() / imageSize.width(), targetSize.height() / imageSize.height()));
+
+ context.drawImage(image, IntPoint(), ImagePaintingOptions(drawImageOperation));
if (useTransparencyLayer)
- context->endTransparencyLayer();
+ context.endTransparencyLayer();
}
-void CrossfadeGeneratedImage::drawCrossfade(GraphicsContext* context)
+void CrossfadeGeneratedImage::drawCrossfade(GraphicsContext& context)
{
// Draw nothing if either of the images hasn't loaded yet.
- if (m_fromImage == Image::nullImage() || m_toImage == Image::nullImage())
+ if (m_fromImage.ptr() == Image::nullImage() || m_toImage.ptr() == Image::nullImage())
return;
- GraphicsContextStateSaver stateSaver(*context);
+ GraphicsContextStateSaver stateSaver(context);
- context->clip(IntRect(IntPoint(), m_crossfadeSize));
- context->beginTransparencyLayer(1);
+ context.clip(FloatRect(FloatPoint(), m_crossfadeSize));
+ context.beginTransparencyLayer(1);
- drawCrossfadeSubimage(context, m_fromImage, CompositeSourceOver, 1 - m_percentage, m_crossfadeSize);
- drawCrossfadeSubimage(context, m_toImage, CompositePlusLighter, m_percentage, m_crossfadeSize);
+ drawCrossfadeSubimage(context, m_fromImage.get(), CompositeSourceOver, 1 - m_percentage, m_crossfadeSize);
+ drawCrossfadeSubimage(context, m_toImage.get(), CompositePlusLighter, m_percentage, m_crossfadeSize);
- context->endTransparencyLayer();
+ context.endTransparencyLayer();
}
-void CrossfadeGeneratedImage::draw(GraphicsContext* context, const FloatRect& dstRect, const FloatRect& srcRect, ColorSpace, CompositeOperator compositeOp, BlendMode blendMode, ImageOrientationDescription)
+void CrossfadeGeneratedImage::draw(GraphicsContext& context, const FloatRect& dstRect, const FloatRect& srcRect, CompositeOperator compositeOp, BlendMode blendMode, ImageOrientationDescription)
{
- GraphicsContextStateSaver stateSaver(*context);
- context->setCompositeOperation(compositeOp, blendMode);
- context->clip(dstRect);
- context->translate(dstRect.x(), dstRect.y());
+ GraphicsContextStateSaver stateSaver(context);
+ context.setCompositeOperation(compositeOp, blendMode);
+ context.clip(dstRect);
+ context.translate(dstRect.x(), dstRect.y());
if (dstRect.size() != srcRect.size())
- context->scale(FloatSize(dstRect.width() / srcRect.width(), dstRect.height() / srcRect.height()));
- context->translate(-srcRect.x(), -srcRect.y());
+ context.scale(FloatSize(dstRect.width() / srcRect.width(), dstRect.height() / srcRect.height()));
+ context.translate(-srcRect.x(), -srcRect.y());
drawCrossfade(context);
}
-void CrossfadeGeneratedImage::drawPattern(GraphicsContext* context, const FloatRect& srcRect, const AffineTransform& patternTransform, const FloatPoint& phase, ColorSpace styleColorSpace, CompositeOperator compositeOp, const FloatRect& dstRect, BlendMode blendMode)
+void CrossfadeGeneratedImage::drawPattern(GraphicsContext& context, const FloatRect& dstRect, const FloatRect& srcRect, const AffineTransform& patternTransform, const FloatPoint& phase, const FloatSize& spacing, CompositeOperator compositeOp, BlendMode blendMode)
{
- std::unique_ptr<ImageBuffer> imageBuffer = ImageBuffer::create(size(), 1, ColorSpaceDeviceRGB, context->isAcceleratedContext() ? Accelerated : Unaccelerated);
+ std::unique_ptr<ImageBuffer> imageBuffer = ImageBuffer::create(size(), context.renderingMode());
if (!imageBuffer)
return;
// Fill with the cross-faded image.
- GraphicsContext* graphicsContext = imageBuffer->context();
+ GraphicsContext& graphicsContext = imageBuffer->context();
drawCrossfade(graphicsContext);
// Tile the image buffer into the context.
- imageBuffer->drawPattern(context, srcRect, patternTransform, phase, styleColorSpace, compositeOp, dstRect, blendMode);
+ imageBuffer->drawPattern(context, dstRect, srcRect, patternTransform, phase, spacing, compositeOp, blendMode);
+}
+
+void CrossfadeGeneratedImage::dump(TextStream& ts) const
+{
+ GeneratedImage::dump(ts);
+ ts.dumpProperty("from-image", m_fromImage.get());
+ ts.dumpProperty("to-image", m_toImage.get());
+ ts.dumpProperty("percentage", m_percentage);
}
}
diff --git a/Source/WebCore/platform/graphics/CrossfadeGeneratedImage.h b/Source/WebCore/platform/graphics/CrossfadeGeneratedImage.h
index ee965d446..c7849a715 100644
--- a/Source/WebCore/platform/graphics/CrossfadeGeneratedImage.h
+++ b/Source/WebCore/platform/graphics/CrossfadeGeneratedImage.h
@@ -10,10 +10,10 @@
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
- * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
@@ -26,44 +26,45 @@
#ifndef CrossfadeGeneratedImage_h
#define CrossfadeGeneratedImage_h
+#include "FloatSize.h"
#include "GeneratedImage.h"
#include "Image.h"
#include "ImageObserver.h"
-#include "IntSize.h"
#include <wtf/RefPtr.h>
namespace WebCore {
-class CSSCrossfadeValue;
-
class CrossfadeGeneratedImage final : public GeneratedImage {
public:
- static PassRefPtr<CrossfadeGeneratedImage> create(Image* fromImage, Image* toImage, float percentage, IntSize crossfadeSize, const IntSize& size)
+ static Ref<CrossfadeGeneratedImage> create(Image& fromImage, Image& toImage, float percentage, const FloatSize& crossfadeSize, const FloatSize& size)
{
- return adoptRef(new CrossfadeGeneratedImage(fromImage, toImage, percentage, crossfadeSize, size));
+ return adoptRef(*new CrossfadeGeneratedImage(fromImage, toImage, percentage, crossfadeSize, size));
}
- virtual void setContainerSize(const IntSize&) override { }
- virtual bool usesContainerSize() const override { return false; }
- virtual bool hasRelativeWidth() const override { return false; }
- virtual bool hasRelativeHeight() const override { return false; }
+ void setContainerSize(const FloatSize&) override { }
+ bool usesContainerSize() const override { return false; }
+ bool hasRelativeWidth() const override { return false; }
+ bool hasRelativeHeight() const override { return false; }
- virtual IntSize size() const override { return m_crossfadeSize; }
+ FloatSize size() const override { return m_crossfadeSize; }
protected:
- virtual void draw(GraphicsContext*, const FloatRect& dstRect, const FloatRect& srcRect, ColorSpace styleColorSpace, CompositeOperator, BlendMode, ImageOrientationDescription) override;
- virtual void drawPattern(GraphicsContext*, const FloatRect& srcRect, const AffineTransform& patternTransform, const FloatPoint& phase, ColorSpace styleColorSpace, CompositeOperator, const FloatRect& dstRect, BlendMode) override;
+ void draw(GraphicsContext&, const FloatRect& dstRect, const FloatRect& srcRect, CompositeOperator, BlendMode, ImageOrientationDescription) override;
+ void drawPattern(GraphicsContext&, const FloatRect& dstRect, const FloatRect& srcRect, const AffineTransform& patternTransform, const FloatPoint& phase, const FloatSize& spacing, CompositeOperator, BlendMode) override;
- CrossfadeGeneratedImage(Image* fromImage, Image* toImage, float percentage, IntSize crossfadeSize, const IntSize&);
+ CrossfadeGeneratedImage(Image& fromImage, Image& toImage, float percentage, const FloatSize& crossfadeSize, const FloatSize&);
private:
- void drawCrossfade(GraphicsContext*);
+ bool isCrossfadeGeneratedImage() const override { return true; }
+ void dump(TextStream&) const override;
+
+ void drawCrossfade(GraphicsContext&);
- Image* m_fromImage;
- Image* m_toImage;
+ Ref<Image> m_fromImage;
+ Ref<Image> m_toImage;
float m_percentage;
- IntSize m_crossfadeSize;
+ FloatSize m_crossfadeSize;
};
}
diff --git a/Source/WebCore/platform/graphics/DashArray.h b/Source/WebCore/platform/graphics/DashArray.h
index 39596d423..5a1b31733 100644
--- a/Source/WebCore/platform/graphics/DashArray.h
+++ b/Source/WebCore/platform/graphics/DashArray.h
@@ -10,10 +10,10 @@
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
- * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
diff --git a/Source/WebCore/platform/graphics/DisplayRefreshMonitor.cpp b/Source/WebCore/platform/graphics/DisplayRefreshMonitor.cpp
index 5641864a6..2cbefcc95 100644
--- a/Source/WebCore/platform/graphics/DisplayRefreshMonitor.cpp
+++ b/Source/WebCore/platform/graphics/DisplayRefreshMonitor.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2010 Apple Inc. All rights reserved.
+ * Copyright (C) 2010, 2014 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -13,7 +13,7 @@
* THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
@@ -24,45 +24,48 @@
*/
#include "config.h"
+#include "DisplayRefreshMonitor.h"
#if USE(REQUEST_ANIMATION_FRAME_DISPLAY_MONITOR)
-#include "DisplayRefreshMonitor.h"
-#include <wtf/CurrentTime.h>
-#include <wtf/Ref.h>
+#include "DisplayRefreshMonitorClient.h"
+#include "DisplayRefreshMonitorManager.h"
-namespace WebCore {
+#if PLATFORM(IOS)
+#include "DisplayRefreshMonitorIOS.h"
+#else
+#include "DisplayRefreshMonitorMac.h"
+#endif
-DisplayRefreshMonitorClient::DisplayRefreshMonitorClient()
- : m_scheduled(false)
- , m_displayIDIsSet(false)
-{
-}
+namespace WebCore {
-DisplayRefreshMonitorClient::~DisplayRefreshMonitorClient()
+RefPtr<DisplayRefreshMonitor> DisplayRefreshMonitor::createDefaultDisplayRefreshMonitor(PlatformDisplayID displayID)
{
- DisplayRefreshMonitorManager::sharedManager()->unregisterClient(this);
+#if PLATFORM(MAC)
+ return DisplayRefreshMonitorMac::create(displayID);
+#endif
+#if PLATFORM(IOS)
+ return DisplayRefreshMonitorIOS::create(displayID);
+#endif
+ return nullptr;
}
-void DisplayRefreshMonitorClient::fireDisplayRefreshIfNeeded(double timestamp)
+RefPtr<DisplayRefreshMonitor> DisplayRefreshMonitor::create(DisplayRefreshMonitorClient& client)
{
- if (m_scheduled) {
- m_scheduled = false;
- displayRefreshFired(timestamp);
- }
+ return client.createDisplayRefreshMonitor(client.displayID());
}
DisplayRefreshMonitor::DisplayRefreshMonitor(PlatformDisplayID displayID)
- : m_monotonicAnimationStartTime(0)
- , m_active(true)
+ : m_active(true)
, m_scheduled(false)
, m_previousFrameDone(true)
, m_unscheduledFireCount(0)
, m_displayID(displayID)
, m_clientsToBeNotified(nullptr)
-#if PLATFORM(MAC)
- , m_displayLink(0)
-#endif
+{
+}
+
+DisplayRefreshMonitor::~DisplayRefreshMonitor()
{
}
@@ -72,138 +75,57 @@ void DisplayRefreshMonitor::handleDisplayRefreshedNotificationOnMainThread(void*
monitor->displayDidRefresh();
}
-void DisplayRefreshMonitor::addClient(DisplayRefreshMonitorClient* client)
+void DisplayRefreshMonitor::addClient(DisplayRefreshMonitorClient& client)
{
- m_clients.add(client);
+ m_clients.add(&client);
}
-bool DisplayRefreshMonitor::removeClient(DisplayRefreshMonitorClient* client)
+bool DisplayRefreshMonitor::removeClient(DisplayRefreshMonitorClient& client)
{
if (m_clientsToBeNotified)
- m_clientsToBeNotified->remove(client);
- return m_clients.remove(client);
+ m_clientsToBeNotified->remove(&client);
+ return m_clients.remove(&client);
}
void DisplayRefreshMonitor::displayDidRefresh()
{
- double monotonicAnimationStartTime;
{
- MutexLocker lock(m_mutex);
+ LockHolder lock(m_mutex);
if (!m_scheduled)
++m_unscheduledFireCount;
else
m_unscheduledFireCount = 0;
m_scheduled = false;
- monotonicAnimationStartTime = m_monotonicAnimationStartTime;
}
// The call back can cause all our clients to be unregistered, so we need to protect
// against deletion until the end of the method.
- Ref<DisplayRefreshMonitor> protect(*this);
+ Ref<DisplayRefreshMonitor> protectedThis(*this);
// Copy the hash table and remove clients from it one by one so we don't notify
// any client twice, but can respond to removal of clients during the delivery process.
HashSet<DisplayRefreshMonitorClient*> clientsToBeNotified = m_clients;
m_clientsToBeNotified = &clientsToBeNotified;
while (!clientsToBeNotified.isEmpty()) {
- // Take a random client out of the set. Ordering doesn't matter.
- // FIXME: Would read more cleanly if HashSet had a take function.
- auto it = clientsToBeNotified.begin();
- DisplayRefreshMonitorClient* client = *it;
- clientsToBeNotified.remove(it);
-
- client->fireDisplayRefreshIfNeeded(monotonicAnimationStartTime);
+ DisplayRefreshMonitorClient* client = clientsToBeNotified.takeAny();
+ client->fireDisplayRefreshIfNeeded();
// This checks if this function was reentered. In that case, stop iterating
// since it's not safe to use the set any more.
if (m_clientsToBeNotified != &clientsToBeNotified)
break;
}
+
if (m_clientsToBeNotified == &clientsToBeNotified)
m_clientsToBeNotified = nullptr;
{
- MutexLocker lock(m_mutex);
+ LockHolder lock(m_mutex);
m_previousFrameDone = true;
}
- DisplayRefreshMonitorManager::sharedManager()->displayDidRefresh(this);
-}
-
-DisplayRefreshMonitorManager* DisplayRefreshMonitorManager::sharedManager()
-{
- DEFINE_STATIC_LOCAL(DisplayRefreshMonitorManager, manager, ());
- return &manager;
-}
-
-DisplayRefreshMonitor* DisplayRefreshMonitorManager::ensureMonitorForClient(DisplayRefreshMonitorClient* client)
-{
- DisplayRefreshMonitorMap::iterator it = m_monitors.find(client->m_displayID);
- if (it == m_monitors.end()) {
- RefPtr<DisplayRefreshMonitor> monitor = DisplayRefreshMonitor::create(client->m_displayID);
- monitor->addClient(client);
- DisplayRefreshMonitor* result = monitor.get();
- m_monitors.add(client->m_displayID, monitor.release());
- return result;
- }
- it->value->addClient(client);
- return it->value.get();
-}
-
-void DisplayRefreshMonitorManager::registerClient(DisplayRefreshMonitorClient* client)
-{
- if (!client->m_displayIDIsSet)
- return;
-
- ensureMonitorForClient(client);
-}
-
-void DisplayRefreshMonitorManager::unregisterClient(DisplayRefreshMonitorClient* client)
-{
- if (!client->m_displayIDIsSet)
- return;
-
- DisplayRefreshMonitorMap::iterator it = m_monitors.find(client->m_displayID);
- if (it == m_monitors.end())
- return;
-
- DisplayRefreshMonitor* monitor = it->value.get();
- if (monitor->removeClient(client)) {
- if (!monitor->hasClients())
- m_monitors.remove(it);
- }
-}
-
-bool DisplayRefreshMonitorManager::scheduleAnimation(DisplayRefreshMonitorClient* client)
-{
- if (!client->m_displayIDIsSet)
- return false;
-
- DisplayRefreshMonitor* monitor = ensureMonitorForClient(client);
-
- client->m_scheduled = true;
- return monitor->requestRefreshCallback();
-}
-
-void DisplayRefreshMonitorManager::displayDidRefresh(DisplayRefreshMonitor* monitor)
-{
- if (monitor->shouldBeTerminated()) {
- ASSERT(m_monitors.contains(monitor->displayID()));
- m_monitors.remove(monitor->displayID());
- }
-}
-
-void DisplayRefreshMonitorManager::windowScreenDidChange(PlatformDisplayID displayID, DisplayRefreshMonitorClient* client)
-{
- if (client->m_displayIDIsSet && client->m_displayID == displayID)
- return;
-
- unregisterClient(client);
- client->setDisplayID(displayID);
- registerClient(client);
- if (client->m_scheduled)
- scheduleAnimation(client);
+ DisplayRefreshMonitorManager::sharedManager().displayDidRefresh(*this);
}
}
diff --git a/Source/WebCore/platform/graphics/DisplayRefreshMonitor.h b/Source/WebCore/platform/graphics/DisplayRefreshMonitor.h
index 648a19119..12183267b 100644
--- a/Source/WebCore/platform/graphics/DisplayRefreshMonitor.h
+++ b/Source/WebCore/platform/graphics/DisplayRefreshMonitor.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2010 Apple Inc. All rights reserved.
+ * Copyright (C) 2010, 2014, 2015 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -13,7 +13,7 @@
* THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
@@ -29,70 +29,29 @@
#if USE(REQUEST_ANIMATION_FRAME_DISPLAY_MONITOR)
#include "PlatformScreen.h"
-#include <wtf/HashMap.h>
#include <wtf/HashSet.h>
+#include <wtf/Lock.h>
#include <wtf/RefCounted.h>
#include <wtf/RefPtr.h>
-#include <wtf/Threading.h>
-
-#if PLATFORM(MAC)
-typedef struct __CVDisplayLink *CVDisplayLinkRef;
-#endif
namespace WebCore {
class DisplayAnimationClient;
-class DisplayRefreshMonitor;
-class DisplayRefreshMonitorManager;
-
-//
-// Abstract virtual client which receives refresh fired messages on the main thread
-//
-class DisplayRefreshMonitorClient {
- friend class DisplayRefreshMonitor;
- friend class DisplayRefreshMonitorManager;
-
-public:
- DisplayRefreshMonitorClient();
- virtual ~DisplayRefreshMonitorClient();
-
- virtual void displayRefreshFired(double timestamp) = 0;
-
-private:
- void fireDisplayRefreshIfNeeded(double timestamp);
-
- void setDisplayID(PlatformDisplayID displayID)
- {
- m_displayID = displayID;
- m_displayIDIsSet = true;
- }
-
- bool m_scheduled;
- bool m_displayIDIsSet;
- PlatformDisplayID m_displayID;
-};
-
-//
-// Monitor for display refresh messages for a given screen
-//
+class DisplayRefreshMonitorClient;
class DisplayRefreshMonitor : public RefCounted<DisplayRefreshMonitor> {
public:
- static PassRefPtr<DisplayRefreshMonitor> create(PlatformDisplayID displayID)
- {
- return adoptRef(new DisplayRefreshMonitor(displayID));
- }
-
- ~DisplayRefreshMonitor();
+ static RefPtr<DisplayRefreshMonitor> create(DisplayRefreshMonitorClient&);
+ WEBCORE_EXPORT virtual ~DisplayRefreshMonitor();
// Return true if callback request was scheduled, false if it couldn't be
// (e.g., hardware refresh is not available)
- bool requestRefreshCallback();
+ virtual bool requestRefreshCallback() = 0;
void windowScreenDidChange(PlatformDisplayID);
bool hasClients() const { return m_clients.size(); }
- void addClient(DisplayRefreshMonitorClient*);
- bool removeClient(DisplayRefreshMonitorClient*);
+ void addClient(DisplayRefreshMonitorClient&);
+ bool removeClient(DisplayRefreshMonitorClient&);
PlatformDisplayID displayID() const { return m_displayID; }
@@ -101,67 +60,36 @@ public:
const int maxInactiveFireCount = 10;
return !m_scheduled && m_unscheduledFireCount > maxInactiveFireCount;
}
-
-private:
- explicit DisplayRefreshMonitor(PlatformDisplayID);
+ bool isActive() const { return m_active; }
+ void setIsActive(bool active) { m_active = active; }
+
+ bool isScheduled() const { return m_scheduled; }
+ void setIsScheduled(bool scheduled) { m_scheduled = scheduled; }
+
+ bool isPreviousFrameDone() const { return m_previousFrameDone; }
+ void setIsPreviousFrameDone(bool done) { m_previousFrameDone = done; }
+
+ Lock& mutex() { return m_mutex; }
+
+ static RefPtr<DisplayRefreshMonitor> createDefaultDisplayRefreshMonitor(PlatformDisplayID);
+
+protected:
+ WEBCORE_EXPORT explicit DisplayRefreshMonitor(PlatformDisplayID);
+ WEBCORE_EXPORT static void handleDisplayRefreshedNotificationOnMainThread(void* data);
+
+private:
void displayDidRefresh();
- static void handleDisplayRefreshedNotificationOnMainThread(void* data);
- double m_monotonicAnimationStartTime;
bool m_active;
bool m_scheduled;
bool m_previousFrameDone;
int m_unscheduledFireCount; // Number of times the display link has fired with no clients.
PlatformDisplayID m_displayID;
- Mutex m_mutex;
+ Lock m_mutex;
HashSet<DisplayRefreshMonitorClient*> m_clients;
HashSet<DisplayRefreshMonitorClient*>* m_clientsToBeNotified;
-
-#if PLATFORM(MAC) && !PLATFORM(IOS)
-public:
- void displayLinkFired(double nowSeconds, double outputTimeSeconds);
-private:
- CVDisplayLinkRef m_displayLink;
-#endif
-
-#if PLATFORM(IOS)
-public:
- void displayLinkFired(double nowSeconds);
-private:
- void* m_displayLink;
-#endif
-};
-
-//
-// Singleton manager for all the DisplayRefreshMonitors. This is the interface to the
-// outside world. It distributes requests to the appropriate monitor. When the display
-// refresh event fires, the passed DisplayRefreshMonitorClient is called directly on
-// the main thread.
-//
-class DisplayRefreshMonitorManager {
-public:
- static DisplayRefreshMonitorManager* sharedManager();
-
- void registerClient(DisplayRefreshMonitorClient*);
- void unregisterClient(DisplayRefreshMonitorClient*);
-
- bool scheduleAnimation(DisplayRefreshMonitorClient*);
- void windowScreenDidChange(PlatformDisplayID, DisplayRefreshMonitorClient*);
-
-private:
- friend class DisplayRefreshMonitor;
- void displayDidRefresh(DisplayRefreshMonitor*);
-
- DisplayRefreshMonitorManager() { }
- DisplayRefreshMonitor* ensureMonitorForClient(DisplayRefreshMonitorClient*);
-
- // We know nothing about the values of PlatformDisplayIDs, so use UnsignedWithZeroKeyHashTraits.
- // FIXME: Since we know nothing about these values, this is not sufficient.
- // Even with UnsignedWithZeroKeyHashTraits, there are still two special values used for empty and deleted hash table slots.
- typedef HashMap<uint64_t, RefPtr<DisplayRefreshMonitor>, WTF::IntHash<uint64_t>, WTF::UnsignedWithZeroKeyHashTraits<uint64_t>> DisplayRefreshMonitorMap;
- DisplayRefreshMonitorMap m_monitors;
};
}
diff --git a/Source/WebCore/platform/graphics/DisplayRefreshMonitorClient.cpp b/Source/WebCore/platform/graphics/DisplayRefreshMonitorClient.cpp
new file mode 100644
index 000000000..b08a3a898
--- /dev/null
+++ b/Source/WebCore/platform/graphics/DisplayRefreshMonitorClient.cpp
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2010, 2014 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "DisplayRefreshMonitorClient.h"
+
+#if USE(REQUEST_ANIMATION_FRAME_DISPLAY_MONITOR)
+
+#include "DisplayRefreshMonitor.h"
+#include "DisplayRefreshMonitorManager.h"
+
+namespace WebCore {
+
+DisplayRefreshMonitorClient::DisplayRefreshMonitorClient()
+{
+}
+
+DisplayRefreshMonitorClient::~DisplayRefreshMonitorClient()
+{
+ DisplayRefreshMonitorManager::sharedManager().unregisterClient(*this);
+}
+
+void DisplayRefreshMonitorClient::fireDisplayRefreshIfNeeded()
+{
+ if (!m_scheduled)
+ return;
+
+ m_scheduled = false;
+ displayRefreshFired();
+}
+
+}
+
+#endif // USE(REQUEST_ANIMATION_FRAME_DISPLAY_MONITOR)
diff --git a/Source/WebCore/platform/graphics/DisplayRefreshMonitorClient.h b/Source/WebCore/platform/graphics/DisplayRefreshMonitorClient.h
new file mode 100644
index 000000000..8fa114c45
--- /dev/null
+++ b/Source/WebCore/platform/graphics/DisplayRefreshMonitorClient.h
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2010, 2014 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#if USE(REQUEST_ANIMATION_FRAME_DISPLAY_MONITOR)
+
+#include "PlatformScreen.h"
+#include <wtf/Forward.h>
+#include <wtf/Optional.h>
+
+namespace WebCore {
+
+class DisplayRefreshMonitor;
+
+class DisplayRefreshMonitorClient {
+public:
+ DisplayRefreshMonitorClient();
+ virtual ~DisplayRefreshMonitorClient();
+
+ // Always called on the main thread.
+ virtual void displayRefreshFired() = 0;
+
+ virtual RefPtr<DisplayRefreshMonitor> createDisplayRefreshMonitor(PlatformDisplayID) const = 0;
+
+ PlatformDisplayID displayID() const { return m_displayID.value(); }
+ bool hasDisplayID() const { return !!m_displayID; }
+ void setDisplayID(PlatformDisplayID displayID) { m_displayID = displayID; }
+
+ void setIsScheduled(bool isScheduled) { m_scheduled = isScheduled; }
+ bool isScheduled() const { return m_scheduled; }
+
+ void fireDisplayRefreshIfNeeded();
+
+private:
+ bool m_scheduled { false };
+ std::optional<PlatformDisplayID> m_displayID;
+};
+
+}
+
+#endif // USE(REQUEST_ANIMATION_FRAME_DISPLAY_MONITOR)
diff --git a/Source/WebCore/platform/graphics/DisplayRefreshMonitorManager.cpp b/Source/WebCore/platform/graphics/DisplayRefreshMonitorManager.cpp
new file mode 100644
index 000000000..f08aebe72
--- /dev/null
+++ b/Source/WebCore/platform/graphics/DisplayRefreshMonitorManager.cpp
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2010, 2014 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "DisplayRefreshMonitorManager.h"
+
+#if USE(REQUEST_ANIMATION_FRAME_DISPLAY_MONITOR)
+
+#include "DisplayRefreshMonitor.h"
+#include "DisplayRefreshMonitorClient.h"
+#include <wtf/CurrentTime.h>
+
+namespace WebCore {
+
+DisplayRefreshMonitorManager::~DisplayRefreshMonitorManager()
+{
+}
+
+DisplayRefreshMonitorManager& DisplayRefreshMonitorManager::sharedManager()
+{
+ static NeverDestroyed<DisplayRefreshMonitorManager> manager;
+ return manager.get();
+}
+
+DisplayRefreshMonitor* DisplayRefreshMonitorManager::createMonitorForClient(DisplayRefreshMonitorClient& client)
+{
+ PlatformDisplayID clientDisplayID = client.displayID();
+ for (const RefPtr<DisplayRefreshMonitor>& monitor : m_monitors) {
+ if (monitor->displayID() != clientDisplayID)
+ continue;
+ monitor->addClient(client);
+ return monitor.get();
+ }
+
+ auto monitor = DisplayRefreshMonitor::create(client);
+ if (!monitor)
+ return nullptr;
+
+ monitor->addClient(client);
+ DisplayRefreshMonitor* result = monitor.get();
+ m_monitors.append(WTFMove(monitor));
+ return result;
+}
+
+void DisplayRefreshMonitorManager::registerClient(DisplayRefreshMonitorClient& client)
+{
+ if (!client.hasDisplayID())
+ return;
+
+ createMonitorForClient(client);
+}
+
+void DisplayRefreshMonitorManager::unregisterClient(DisplayRefreshMonitorClient& client)
+{
+ if (!client.hasDisplayID())
+ return;
+
+ PlatformDisplayID clientDisplayID = client.displayID();
+ for (size_t i = 0; i < m_monitors.size(); ++i) {
+ RefPtr<DisplayRefreshMonitor> monitor = m_monitors[i];
+ if (monitor->displayID() != clientDisplayID)
+ continue;
+ if (monitor->removeClient(client)) {
+ if (!monitor->hasClients())
+ m_monitors.remove(i);
+ }
+ return;
+ }
+}
+
+bool DisplayRefreshMonitorManager::scheduleAnimation(DisplayRefreshMonitorClient& client)
+{
+ if (!client.hasDisplayID())
+ return false;
+
+ DisplayRefreshMonitor* monitor = createMonitorForClient(client);
+ if (!monitor)
+ return false;
+
+ client.setIsScheduled(true);
+ return monitor->requestRefreshCallback();
+}
+
+void DisplayRefreshMonitorManager::displayDidRefresh(DisplayRefreshMonitor& monitor)
+{
+ if (!monitor.shouldBeTerminated())
+ return;
+
+ size_t monitorIndex = m_monitors.find(&monitor);
+ if (monitorIndex != notFound)
+ m_monitors.remove(monitorIndex);
+}
+
+void DisplayRefreshMonitorManager::windowScreenDidChange(PlatformDisplayID displayID, DisplayRefreshMonitorClient& client)
+{
+ if (client.hasDisplayID() && client.displayID() == displayID)
+ return;
+
+ unregisterClient(client);
+ client.setDisplayID(displayID);
+ registerClient(client);
+ if (client.isScheduled())
+ scheduleAnimation(client);
+}
+
+}
+
+#endif // USE(REQUEST_ANIMATION_FRAME_DISPLAY_MONITOR)
diff --git a/Source/WebCore/platform/graphics/DisplayRefreshMonitorManager.h b/Source/WebCore/platform/graphics/DisplayRefreshMonitorManager.h
new file mode 100644
index 000000000..801811bbc
--- /dev/null
+++ b/Source/WebCore/platform/graphics/DisplayRefreshMonitorManager.h
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2010, 2014 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef DisplayRefreshMonitorManager_h
+#define DisplayRefreshMonitorManager_h
+
+#if USE(REQUEST_ANIMATION_FRAME_DISPLAY_MONITOR)
+
+#include "DisplayRefreshMonitor.h"
+#include "PlatformScreen.h"
+#include <wtf/NeverDestroyed.h>
+#include <wtf/RefPtr.h>
+#include <wtf/Vector.h>
+
+namespace WebCore {
+
+class DisplayRefreshMonitorManager {
+ friend class NeverDestroyed<DisplayRefreshMonitorManager>;
+public:
+ static DisplayRefreshMonitorManager& sharedManager();
+
+ void registerClient(DisplayRefreshMonitorClient&);
+ void unregisterClient(DisplayRefreshMonitorClient&);
+
+ bool scheduleAnimation(DisplayRefreshMonitorClient&);
+ void windowScreenDidChange(PlatformDisplayID, DisplayRefreshMonitorClient&);
+
+private:
+ friend class DisplayRefreshMonitor;
+ void displayDidRefresh(DisplayRefreshMonitor&);
+
+ DisplayRefreshMonitorManager() { }
+ virtual ~DisplayRefreshMonitorManager();
+
+ DisplayRefreshMonitor* createMonitorForClient(DisplayRefreshMonitorClient&);
+
+ Vector<RefPtr<DisplayRefreshMonitor>> m_monitors;
+};
+
+}
+
+#endif // USE(REQUEST_ANIMATION_FRAME_DISPLAY_MONITOR)
+
+#endif
diff --git a/Source/WebCore/platform/graphics/ExtendedColor.cpp b/Source/WebCore/platform/graphics/ExtendedColor.cpp
new file mode 100644
index 000000000..50e3c5c8a
--- /dev/null
+++ b/Source/WebCore/platform/graphics/ExtendedColor.cpp
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2016 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "ExtendedColor.h"
+
+#include "ColorSpace.h"
+#include <wtf/MathExtras.h>
+#include <wtf/dtoa.h>
+#include <wtf/text/StringBuilder.h>
+
+namespace WebCore {
+
+Ref<ExtendedColor> ExtendedColor::create(float r, float g, float b, float a, ColorSpace colorSpace)
+{
+ return adoptRef(*new ExtendedColor(r, g, b, a, colorSpace));
+}
+
+String ExtendedColor::cssText() const
+{
+ StringBuilder builder;
+ builder.reserveCapacity(40);
+ builder.appendLiteral("color(");
+
+ switch (m_colorSpace) {
+ case ColorSpaceSRGB:
+ builder.appendLiteral("srgb ");
+ break;
+ case ColorSpaceDisplayP3:
+ builder.appendLiteral("display-p3 ");
+ break;
+ default:
+ ASSERT_NOT_REACHED();
+ return WTF::emptyString();
+ }
+
+ NumberToStringBuffer buffer;
+ bool shouldTruncateTrailingZeros = true;
+
+ builder.append(numberToFixedPrecisionString(red(), 6, buffer, shouldTruncateTrailingZeros));
+ builder.append(' ');
+
+ builder.append(numberToFixedPrecisionString(green(), 6, buffer, shouldTruncateTrailingZeros));
+ builder.append(' ');
+
+ builder.append(numberToFixedPrecisionString(blue(), 6, buffer, shouldTruncateTrailingZeros));
+ if (!WTF::areEssentiallyEqual(alpha(), 1.0f)) {
+ builder.appendLiteral(" / ");
+ builder.append(numberToFixedPrecisionString(alpha(), 6, buffer, shouldTruncateTrailingZeros));
+ }
+ builder.append(')');
+
+ return builder.toString();
+}
+
+}
diff --git a/Source/WebCore/platform/graphics/ExtendedColor.h b/Source/WebCore/platform/graphics/ExtendedColor.h
new file mode 100644
index 000000000..37c68edc3
--- /dev/null
+++ b/Source/WebCore/platform/graphics/ExtendedColor.h
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2016 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include "ColorSpace.h"
+
+#include <wtf/Ref.h>
+#include <wtf/RefCounted.h>
+#include <wtf/text/WTFString.h>
+
+namespace WebCore {
+
+class ExtendedColor : public RefCounted<ExtendedColor> {
+public:
+ static Ref<ExtendedColor> create(float r, float g, float b, float a, ColorSpace = ColorSpace::ColorSpaceSRGB);
+
+ float red() const { return m_red; }
+ float green() const { return m_green; }
+ float blue() const { return m_blue; }
+ float alpha() const { return m_alpha; }
+
+ ColorSpace colorSpace() const { return m_colorSpace; }
+
+ WEBCORE_EXPORT String cssText() const;
+
+private:
+ ExtendedColor(float r, float g, float b, float a, ColorSpace colorSpace)
+ : m_red(r)
+ , m_green(g)
+ , m_blue(b)
+ , m_alpha(a)
+ , m_colorSpace(colorSpace)
+ { }
+
+ float m_red { 0 };
+ float m_green { 0 };
+ float m_blue { 0 };
+ float m_alpha { 0 };
+
+ ColorSpace m_colorSpace { ColorSpace::ColorSpaceSRGB };
+};
+
+}
diff --git a/Source/WebCore/platform/graphics/Extensions3D.h b/Source/WebCore/platform/graphics/Extensions3D.h
index 2454e40ea..87ee55d7e 100644
--- a/Source/WebCore/platform/graphics/Extensions3D.h
+++ b/Source/WebCore/platform/graphics/Extensions3D.h
@@ -52,6 +52,7 @@ public:
// GL_ARB_texture_non_power_of_two / GL_OES_texture_npot
// GL_EXT_packed_depth_stencil / GL_OES_packed_depth_stencil
// GL_ANGLE_framebuffer_blit / GL_ANGLE_framebuffer_multisample
+ // GL_IMG_multisampled_render_to_texture
// GL_OES_texture_float
// GL_OES_texture_float_linear
// GL_OES_texture_half_float
@@ -71,9 +72,8 @@ public:
// GL_IMG_texture_compression_pvrtc
// EXT_texture_filter_anisotropic
// GL_EXT_debug_marker
- // GL_CHROMIUM_copy_texture
- // GL_CHROMIUM_flipy
// GL_ARB_draw_buffers / GL_EXT_draw_buffers
+ // GL_ANGLE_instanced_arrays
// Takes full name of extension; for example,
// "GL_EXT_texture_format_BGRA8888".
@@ -90,6 +90,16 @@ public:
virtual bool isEnabled(const String&) = 0;
enum ExtensionsEnumType {
+ // EXT_sRGB formats
+ SRGB_EXT = 0x8C40,
+ SRGB_ALPHA_EXT = 0x8C42,
+ SRGB8_ALPHA8_EXT = 0x8C43,
+ FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING_EXT = 0x8210,
+
+ // EXT_blend_minmax enums
+ MIN_EXT = 0x8007,
+ MAX_EXT = 0x8008,
+
// GL_EXT_texture_format_BGRA8888 enums
BGRA_EXT = 0x80E1,
@@ -112,6 +122,12 @@ public:
FRAMEBUFFER_INCOMPLETE_MULTISAMPLE = 0x8D56,
MAX_SAMPLES = 0x8D57,
+ // GL_IMG_multisampled_render_to_texture
+ RENDERBUFFER_SAMPLES_IMG = 0x9133,
+ FRAMEBUFFER_INCOMPLETE_MULTISAMPLE_IMG = 0x9134,
+ MAX_SAMPLES_IMG = 0x9135,
+ TEXTURE_SAMPLES_IMG = 0x9136,
+
// GL_OES_standard_derivatives names
FRAGMENT_SHADER_DERIVATIVE_HINT_OES = 0x8B8B,
@@ -154,13 +170,6 @@ public:
TEXTURE_MAX_ANISOTROPY_EXT = 0x84FE,
MAX_TEXTURE_MAX_ANISOTROPY_EXT = 0x84FF,
- // GL_CHROMIUM_flipy
- UNPACK_FLIP_Y_CHROMIUM = 0x9240,
-
- // GL_CHROMIUM_copy_texture
- UNPACK_PREMULTIPLY_ALPHA_CHROMIUM = 0x9241,
- UNPACK_UNPREMULTIPLY_ALPHA_CHROMIUM = 0x9242,
-
// GL_ARB_draw_buffers / GL_EXT_draw_buffers
MAX_DRAW_BUFFERS_EXT = 0x8824,
DRAW_BUFFER0_EXT = 0x8825,
@@ -222,9 +231,6 @@ public:
// GL_ANGLE_translated_shader_source
virtual String getTranslatedShaderSourceANGLE(Platform3DObject) = 0;
- // GL_CHROMIUM_copy_texture
- virtual void copyTextureCHROMIUM(GC3Denum, Platform3DObject, Platform3DObject, GC3Dint, GC3Denum) = 0;
-
// EXT Robustness - uses getGraphicsResetStatusARB
virtual void readnPixelsEXT(int x, int y, GC3Dsizei width, GC3Dsizei height, GC3Denum format, GC3Denum type, GC3Dsizei bufSize, void *data) = 0;
virtual void getnUniformfvEXT(GC3Duint program, int location, GC3Dsizei bufSize, float *params) = 0;
@@ -238,21 +244,22 @@ public:
// GL_ARB_draw_buffers / GL_EXT_draw_buffers
virtual void drawBuffersEXT(GC3Dsizei n, const GC3Denum* bufs) = 0;
+ // GL_ANGLE_instanced_arrays
+ virtual void drawArraysInstanced(GC3Denum mode, GC3Dint first, GC3Dsizei count, GC3Dsizei primcount) = 0;
+ virtual void drawElementsInstanced(GC3Denum mode, GC3Dsizei count, GC3Denum type, long long offset, GC3Dsizei primcount) = 0;
+ virtual void vertexAttribDivisor(GC3Duint index, GC3Duint divisor) = 0;
+
virtual bool isNVIDIA() = 0;
virtual bool isAMD() = 0;
virtual bool isIntel() = 0;
+ virtual bool isImagination() = 0;
virtual String vendor() = 0;
- // If this method returns false then the system *definitely* does not support multisampling.
- // It does not necessarily say the system does support it - callers must attempt to construct
- // multisampled renderbuffers and check framebuffer completeness.
- // Ports should implement this to return false on configurations where it is known
- // that multisampling is not available.
- virtual bool maySupportMultisampling() = 0;
-
// Some configurations have bugs regarding built-in functions in their OpenGL drivers
- // that must be avoided. Ports should implement this flag such configurations.
+ // that must be avoided. Ports should implement these flags on such configurations.
virtual bool requiresBuiltInFunctionEmulation() = 0;
+ virtual bool requiresRestrictedMaximumTextureSize() = 0;
+
};
} // namespace WebCore
diff --git a/Source/WebCore/platform/graphics/FloatPoint.cpp b/Source/WebCore/platform/graphics/FloatPoint.cpp
index ca1b8786b..7a1343c72 100644
--- a/Source/WebCore/platform/graphics/FloatPoint.cpp
+++ b/Source/WebCore/platform/graphics/FloatPoint.cpp
@@ -11,10 +11,10 @@
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
- * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
@@ -30,10 +30,10 @@
#include "AffineTransform.h"
#include "FloatConversion.h"
#include "IntPoint.h"
+#include "TextStream.h"
#include "TransformationMatrix.h"
#include <limits>
#include <math.h>
-#include <wtf/PrintStream.h>
namespace WebCore {
@@ -41,6 +41,14 @@ FloatPoint::FloatPoint(const IntPoint& p) : m_x(p.x()), m_y(p.y())
{
}
+FloatPoint FloatPoint::constrainedBetween(const FloatPoint& min, const FloatPoint& max) const
+{
+ return {
+ std::max(min.x(), std::min(max.x(), m_x)),
+ std::max(min.y(), std::min(max.y(), m_y))
+ };
+}
+
void FloatPoint::normalize()
{
float tempLength = length();
@@ -80,48 +88,10 @@ FloatPoint FloatPoint::narrowPrecision(double x, double y)
return FloatPoint(narrowPrecisionToFloat(x), narrowPrecisionToFloat(y));
}
-float findSlope(const FloatPoint& p1, const FloatPoint& p2, float& c)
-{
- if (p2.x() == p1.x())
- return std::numeric_limits<float>::infinity();
-
- // y = mx + c
- float slope = (p2.y() - p1.y()) / (p2.x() - p1.x());
- c = p1.y() - slope * p1.x();
- return slope;
-}
-
-bool findIntersection(const FloatPoint& p1, const FloatPoint& p2, const FloatPoint& d1, const FloatPoint& d2, FloatPoint& intersection)
-{
- float pOffset = 0;
- float pSlope = findSlope(p1, p2, pOffset);
-
- float dOffset = 0;
- float dSlope = findSlope(d1, d2, dOffset);
-
- if (dSlope == pSlope)
- return false;
-
- if (pSlope == std::numeric_limits<float>::infinity()) {
- intersection.setX(p1.x());
- intersection.setY(dSlope * intersection.x() + dOffset);
- return true;
- }
- if (dSlope == std::numeric_limits<float>::infinity()) {
- intersection.setX(d1.x());
- intersection.setY(pSlope * intersection.x() + pOffset);
- return true;
- }
-
- // Find x at intersection, where ys overlap; x = (c' - c) / (m - m')
- intersection.setX((dOffset - pOffset) / (pSlope - dSlope));
- intersection.setY(pSlope * intersection.x() + pOffset);
- return true;
-}
-
-void FloatPoint::dump(PrintStream& out) const
+TextStream& operator<<(TextStream& ts, const FloatPoint& p)
{
- out.printf("(%f, %f)", x(), y());
+ // FIXME: callers should use the NumberRespectingIntegers flag.
+ return ts << "(" << TextStream::FormatNumberRespectingIntegers(p.x()) << "," << TextStream::FormatNumberRespectingIntegers(p.y()) << ")";
}
}
diff --git a/Source/WebCore/platform/graphics/FloatPoint.h b/Source/WebCore/platform/graphics/FloatPoint.h
index 62c37bd36..7a417b4b1 100644
--- a/Source/WebCore/platform/graphics/FloatPoint.h
+++ b/Source/WebCore/platform/graphics/FloatPoint.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2004, 2006, 2007 Apple Inc. All rights reserved.
+ * Copyright (C) 2004-2016 Apple Inc. All rights reserved.
* Copyright (C) 2005 Nokia. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -11,10 +11,10 @@
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
- * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
@@ -31,17 +31,26 @@
#include "IntPoint.h"
#include <wtf/MathExtras.h>
+#if PLATFORM(MAC) && defined __OBJC__
+#import <Foundation/NSGeometry.h>
+#endif
+
#if USE(CG)
typedef struct CGPoint CGPoint;
#endif
-#if PLATFORM(MAC) && !PLATFORM(IOS)
+#if PLATFORM(MAC)
#ifdef NSGEOMETRY_TYPES_SAME_AS_CGGEOMETRY_TYPES
typedef struct CGPoint NSPoint;
#else
typedef struct _NSPoint NSPoint;
#endif
-#endif // PLATFORM(MAC) && !PLATFORM(IOS)
+#endif // PLATFORM(MAC)
+
+#if PLATFORM(WIN)
+struct D2D_POINT_2F;
+typedef D2D_POINT_2F D2D1_POINT_2F;
+#endif
namespace WebCore {
@@ -49,12 +58,13 @@ class AffineTransform;
class TransformationMatrix;
class IntPoint;
class IntSize;
+class TextStream;
class FloatPoint {
public:
- FloatPoint() : m_x(0), m_y(0) { }
+ FloatPoint() { }
FloatPoint(float x, float y) : m_x(x), m_y(y) { }
- FloatPoint(const IntPoint&);
+ WEBCORE_EXPORT FloatPoint(const IntPoint&);
explicit FloatPoint(const FloatSize& size) : m_x(size.width()), m_y(size.height()) { }
static FloatPoint zero() { return FloatPoint(); }
@@ -66,43 +76,56 @@ public:
void setX(float x) { m_x = x; }
void setY(float y) { m_y = y; }
+
void set(float x, float y)
{
m_x = x;
m_y = y;
}
+
void move(float dx, float dy)
{
m_x += dx;
m_y += dy;
}
+
void move(const IntSize& a)
{
m_x += a.width();
m_y += a.height();
}
+
void move(const FloatSize& a)
{
m_x += a.width();
m_y += a.height();
}
+
void moveBy(const IntPoint& a)
{
m_x += a.x();
m_y += a.y();
}
+
void moveBy(const FloatPoint& a)
{
m_x += a.x();
m_y += a.y();
}
+
+ void scale(float scale)
+ {
+ m_x *= scale;
+ m_y *= scale;
+ }
+
void scale(float sx, float sy)
{
m_x *= sx;
m_y *= sy;
}
- void normalize();
+ WEBCORE_EXPORT void normalize();
float dot(const FloatPoint& a) const
{
@@ -111,40 +134,50 @@ public:
float slopeAngleRadians() const;
float length() const;
+
float lengthSquared() const
{
return m_x * m_x + m_y * m_y;
}
+ WEBCORE_EXPORT FloatPoint constrainedBetween(const FloatPoint& min, const FloatPoint& max) const;
+
+ FloatPoint shrunkTo(const FloatPoint& other) const
+ {
+ return { std::min(m_x, other.m_x), std::min(m_y, other.m_y) };
+ }
+
FloatPoint expandedTo(const FloatPoint& other) const
{
- return FloatPoint(std::max(m_x, other.m_x), std::max(m_y, other.m_y));
+ return { std::max(m_x, other.m_x), std::max(m_y, other.m_y) };
}
FloatPoint transposedPoint() const
{
- return FloatPoint(m_y, m_x);
+ return { m_y, m_x };
}
#if USE(CG)
- FloatPoint(const CGPoint&);
- operator CGPoint() const;
+ WEBCORE_EXPORT FloatPoint(const CGPoint&);
+ WEBCORE_EXPORT operator CGPoint() const;
#endif
-#if !PLATFORM(IOS)
#if PLATFORM(MAC) && !defined(NSGEOMETRY_TYPES_SAME_AS_CGGEOMETRY_TYPES)
- FloatPoint(const NSPoint&);
- operator NSPoint() const;
+ WEBCORE_EXPORT FloatPoint(const NSPoint&);
+ WEBCORE_EXPORT operator NSPoint() const;
#endif
-#endif // !PLATFORM(IOS)
- FloatPoint matrixTransform(const TransformationMatrix&) const;
- FloatPoint matrixTransform(const AffineTransform&) const;
+#if PLATFORM(WIN)
+ WEBCORE_EXPORT FloatPoint(const D2D1_POINT_2F&);
+ WEBCORE_EXPORT operator D2D1_POINT_2F() const;
+#endif
- void dump(PrintStream& out) const;
+ WEBCORE_EXPORT FloatPoint matrixTransform(const TransformationMatrix&) const;
+ WEBCORE_EXPORT FloatPoint matrixTransform(const AffineTransform&) const;
private:
- float m_x, m_y;
+ float m_x { 0 };
+ float m_y { 0 };
};
@@ -207,6 +240,11 @@ inline float operator*(const FloatPoint& a, const FloatPoint& b)
return a.dot(b);
}
+inline IntSize flooredIntSize(const FloatPoint& p)
+{
+ return IntSize(clampToInteger(floorf(p.x())), clampToInteger(floorf(p.y())));
+}
+
inline IntPoint roundedIntPoint(const FloatPoint& p)
{
return IntPoint(clampToInteger(roundf(p.x())), clampToInteger(roundf(p.y())));
@@ -222,9 +260,14 @@ inline IntPoint ceiledIntPoint(const FloatPoint& p)
return IntPoint(clampToInteger(ceilf(p.x())), clampToInteger(ceilf(p.y())));
}
-inline IntSize flooredIntSize(const FloatPoint& p)
+inline FloatPoint floorPointToDevicePixels(const FloatPoint& p, float deviceScaleFactor)
{
- return IntSize(clampToInteger(floorf(p.x())), clampToInteger(floorf(p.y())));
+ return FloatPoint(floorf(p.x() * deviceScaleFactor) / deviceScaleFactor, floorf(p.y() * deviceScaleFactor) / deviceScaleFactor);
+}
+
+inline FloatPoint ceilPointToDevicePixels(const FloatPoint& p, float deviceScaleFactor)
+{
+ return FloatPoint(ceilf(p.x() * deviceScaleFactor) / deviceScaleFactor, ceilf(p.y() * deviceScaleFactor) / deviceScaleFactor);
}
inline FloatSize toFloatSize(const FloatPoint& a)
@@ -232,10 +275,17 @@ inline FloatSize toFloatSize(const FloatPoint& a)
return FloatSize(a.x(), a.y());
}
-float findSlope(const FloatPoint& p1, const FloatPoint& p2, float& c);
+inline FloatPoint toFloatPoint(const FloatSize& a)
+{
+ return FloatPoint(a.width(), a.height());
+}
+
+inline bool areEssentiallyEqual(const FloatPoint& a, const FloatPoint& b)
+{
+ return WTF::areEssentiallyEqual(a.x(), b.x()) && WTF::areEssentiallyEqual(a.y(), b.y());
+}
-// Find point where lines through the two pairs of points intersect. Returns false if the lines don't intersect.
-bool findIntersection(const FloatPoint& p1, const FloatPoint& p2, const FloatPoint& d1, const FloatPoint& d2, FloatPoint& intersection);
+WEBCORE_EXPORT TextStream& operator<<(TextStream&, const FloatPoint&);
}
diff --git a/Source/WebCore/platform/graphics/FloatPoint3D.cpp b/Source/WebCore/platform/graphics/FloatPoint3D.cpp
index ed1816bdd..c6fbdab72 100644
--- a/Source/WebCore/platform/graphics/FloatPoint3D.cpp
+++ b/Source/WebCore/platform/graphics/FloatPoint3D.cpp
@@ -20,9 +20,9 @@
*/
#include "config.h"
-
#include "FloatPoint3D.h"
+#include "TextStream.h"
#include <math.h>
namespace WebCore {
@@ -38,5 +38,10 @@ void FloatPoint3D::normalize()
}
}
+TextStream& operator<<(TextStream& ts, const FloatPoint3D& point)
+{
+ return ts << point.x() << " " << point.y() << " " << point.z();
+}
+
} // namespace WebCore
diff --git a/Source/WebCore/platform/graphics/FloatPoint3D.h b/Source/WebCore/platform/graphics/FloatPoint3D.h
index ba0ee9d94..3c6e2a77c 100644
--- a/Source/WebCore/platform/graphics/FloatPoint3D.h
+++ b/Source/WebCore/platform/graphics/FloatPoint3D.h
@@ -182,6 +182,8 @@ inline float FloatPoint3D::distanceTo(const FloatPoint3D& a) const
return (*this - a).length();
}
+WEBCORE_EXPORT TextStream& operator<<(TextStream&, const FloatPoint3D&);
+
} // namespace WebCore
#endif // FloatPoint3D_h
diff --git a/Source/WebCore/platform/graphics/FloatPolygon.cpp b/Source/WebCore/platform/graphics/FloatPolygon.cpp
index c39e285ab..d91359111 100644
--- a/Source/WebCore/platform/graphics/FloatPolygon.cpp
+++ b/Source/WebCore/platform/graphics/FloatPolygon.cpp
@@ -79,8 +79,8 @@ static unsigned findNextEdgeVertexIndex(const FloatPolygon& polygon, unsigned ve
return vertexIndex2;
}
-FloatPolygon::FloatPolygon(PassOwnPtr<Vector<FloatPoint>> vertices, WindRule fillRule)
- : m_vertices(vertices)
+FloatPolygon::FloatPolygon(std::unique_ptr<Vector<FloatPoint>> vertices, WindRule fillRule)
+ : m_vertices(WTFMove(vertices))
, m_fillRule(fillRule)
{
unsigned nVertices = numberOfVertices();
diff --git a/Source/WebCore/platform/graphics/FloatPolygon.h b/Source/WebCore/platform/graphics/FloatPolygon.h
index 5666574b0..aeaef25d6 100644
--- a/Source/WebCore/platform/graphics/FloatPolygon.h
+++ b/Source/WebCore/platform/graphics/FloatPolygon.h
@@ -35,8 +35,7 @@
#include "PODIntervalTree.h"
#include "ValueToString.h"
#include "WindRule.h"
-#include <wtf/OwnPtr.h>
-#include <wtf/PassOwnPtr.h>
+#include <memory>
#include <wtf/Vector.h>
namespace WebCore {
@@ -45,7 +44,7 @@ class FloatPolygonEdge;
class FloatPolygon {
public:
- FloatPolygon(PassOwnPtr<Vector<FloatPoint>> vertices, WindRule fillRule);
+ FloatPolygon(std::unique_ptr<Vector<FloatPoint>> vertices, WindRule fillRule);
const FloatPoint& vertexAt(unsigned index) const { return (*m_vertices)[index]; }
unsigned numberOfVertices() const { return m_vertices->size(); }
@@ -67,7 +66,7 @@ private:
bool containsNonZero(const FloatPoint&) const;
bool containsEvenOdd(const FloatPoint&) const;
- OwnPtr<Vector<FloatPoint>> m_vertices;
+ std::unique_ptr<Vector<FloatPoint>> m_vertices;
WindRule m_fillRule;
FloatRect m_boundingBox;
bool m_empty;
@@ -95,13 +94,13 @@ public:
class FloatPolygonEdge : public VertexPair {
friend class FloatPolygon;
public:
- virtual const FloatPoint& vertex1() const override
+ const FloatPoint& vertex1() const override
{
ASSERT(m_polygon);
return m_polygon->vertexAt(m_vertexIndex1);
}
- virtual const FloatPoint& vertex2() const override
+ const FloatPoint& vertex2() const override
{
ASSERT(m_polygon);
return m_polygon->vertexAt(m_vertexIndex2);
diff --git a/Source/WebCore/platform/graphics/FloatQuad.cpp b/Source/WebCore/platform/graphics/FloatQuad.cpp
index 655beabf3..c5687767c 100644
--- a/Source/WebCore/platform/graphics/FloatQuad.cpp
+++ b/Source/WebCore/platform/graphics/FloatQuad.cpp
@@ -12,7 +12,7 @@
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
- * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ * 3. Neither the name of Apple Inc. ("Apple") nor the names of
* its contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
@@ -33,6 +33,7 @@
#include <algorithm>
#include <limits>
+#include <wtf/MathExtras.h>
namespace WebCore {
@@ -90,15 +91,10 @@ FloatRect FloatQuad::boundingBox() const
return FloatRect(left, top, right - left, bottom - top);
}
-static inline bool withinEpsilon(float a, float b)
-{
- return fabs(a - b) < std::numeric_limits<float>::epsilon();
-}
-
bool FloatQuad::isRectilinear() const
{
- return (withinEpsilon(m_p1.x(), m_p2.x()) && withinEpsilon(m_p2.y(), m_p3.y()) && withinEpsilon(m_p3.x(), m_p4.x()) && withinEpsilon(m_p4.y(), m_p1.y()))
- || (withinEpsilon(m_p1.y(), m_p2.y()) && withinEpsilon(m_p2.x(), m_p3.x()) && withinEpsilon(m_p3.y(), m_p4.y()) && withinEpsilon(m_p4.x(), m_p1.x()));
+ return (WTF::areEssentiallyEqual(m_p1.x(), m_p2.x()) && WTF::areEssentiallyEqual(m_p2.y(), m_p3.y()) && WTF::areEssentiallyEqual(m_p3.x(), m_p4.x()) && WTF::areEssentiallyEqual(m_p4.y(), m_p1.y()))
+ || (WTF::areEssentiallyEqual(m_p1.y(), m_p2.y()) && WTF::areEssentiallyEqual(m_p2.x(), m_p3.x()) && WTF::areEssentiallyEqual(m_p3.y(), m_p4.y()) && WTF::areEssentiallyEqual(m_p4.x(), m_p1.x()));
}
bool FloatQuad::containsPoint(const FloatPoint& p) const
diff --git a/Source/WebCore/platform/graphics/FloatQuad.h b/Source/WebCore/platform/graphics/FloatQuad.h
index a9d9096f2..793eb34c1 100644
--- a/Source/WebCore/platform/graphics/FloatQuad.h
+++ b/Source/WebCore/platform/graphics/FloatQuad.h
@@ -10,7 +10,7 @@
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
- * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ * 3. Neither the name of Apple Inc. ("Apple") nor the names of
* its contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
@@ -39,6 +39,7 @@ namespace WebCore {
// mapping a rectangle through transforms. When initialized from a rect, the
// points are in clockwise order from top left.
class FloatQuad {
+ WTF_MAKE_FAST_ALLOCATED;
public:
FloatQuad()
{
@@ -78,15 +79,15 @@ public:
// that is, if two edges are parallel to the x-axis and the other two
// are parallel to the y-axis. If this method returns true, the
// corresponding FloatRect can be retrieved with boundingBox().
- bool isRectilinear() const;
+ WEBCORE_EXPORT bool isRectilinear() const;
// Tests whether the given point is inside, or on an edge or corner of this quad.
- bool containsPoint(const FloatPoint&) const;
+ WEBCORE_EXPORT bool containsPoint(const FloatPoint&) const;
// Tests whether the four corners of other are inside, or coincident with the sides of this quad.
// Note that this only works for convex quads, but that includes all quads that originate
// from transformed rects.
- bool containsQuad(const FloatQuad&) const;
+ WEBCORE_EXPORT bool containsQuad(const FloatQuad&) const;
// Tests whether any part of the rectangle intersects with this quad.
// This only works for convex quads.
@@ -104,7 +105,7 @@ public:
(m_p1.y() + m_p2.y() + m_p3.y() + m_p4.y()) / 4.0);
}
- FloatRect boundingBox() const;
+ WEBCORE_EXPORT FloatRect boundingBox() const;
IntRect enclosingBoundingBox() const
{
return enclosingIntRect(boundingBox());
@@ -125,6 +126,11 @@ public:
m_p3.move(dx, dy);
m_p4.move(dx, dy);
}
+
+ void scale(float s)
+ {
+ scale(s, s);
+ }
void scale(float dx, float dy)
{
diff --git a/Source/WebCore/platform/graphics/FloatRect.cpp b/Source/WebCore/platform/graphics/FloatRect.cpp
index 2fae4e7b4..5556c28c4 100644
--- a/Source/WebCore/platform/graphics/FloatRect.cpp
+++ b/Source/WebCore/platform/graphics/FloatRect.cpp
@@ -11,10 +11,10 @@
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
- * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
@@ -29,10 +29,10 @@
#include "FloatConversion.h"
#include "IntRect.h"
+#include "TextStream.h"
#include <algorithm>
#include <math.h>
#include <wtf/MathExtras.h>
-#include <wtf/PrintStream.h>
namespace WebCore {
@@ -147,17 +147,6 @@ void FloatRect::scale(float sx, float sy)
m_size.setHeight(height() * sy);
}
-FloatRect unionRect(const Vector<FloatRect>& rects)
-{
- FloatRect result;
-
- size_t count = rects.size();
- for (size_t i = 0; i < count; ++i)
- result.unite(rects[i]);
-
- return result;
-}
-
void FloatRect::fitToPoints(const FloatPoint& p0, const FloatPoint& p1)
{
float left = std::min(p0.x(), p1.x());
@@ -217,22 +206,18 @@ void FloatRect::fitToPoints(const FloatPoint& p0, const FloatPoint& p1, const Fl
setLocationAndSizeFromEdges(left, top, right, bottom);
}
-IntRect enclosingIntRect(const FloatRect& rect)
+FloatRect encloseRectToDevicePixels(const FloatRect& rect, float deviceScaleFactor)
{
- IntPoint location = flooredIntPoint(rect.minXMinYCorner());
- IntPoint maxPoint = ceiledIntPoint(rect.maxXMaxYCorner());
-
- return IntRect(location, maxPoint - location);
+ FloatPoint location = floorPointToDevicePixels(rect.minXMinYCorner(), deviceScaleFactor);
+ FloatPoint maxPoint = ceilPointToDevicePixels(rect.maxXMaxYCorner(), deviceScaleFactor);
+ return FloatRect(location, maxPoint - location);
}
-IntRect enclosedIntRect(const FloatRect& rect)
+IntRect enclosingIntRect(const FloatRect& rect)
{
- IntPoint location = ceiledIntPoint(rect.minXMinYCorner());
- IntPoint maxPoint = flooredIntPoint(rect.maxXMaxYCorner());
- IntSize size = maxPoint - location;
- size.clampNegativeToZero();
-
- return IntRect(location, size);
+ FloatPoint location = flooredIntPoint(rect.minXMinYCorner());
+ FloatPoint maxPoint = ceiledIntPoint(rect.maxXMaxYCorner());
+ return IntRect(IntPoint(location), IntSize(maxPoint - location));
}
IntRect roundedIntRect(const FloatRect& rect)
@@ -240,21 +225,15 @@ IntRect roundedIntRect(const FloatRect& rect)
return IntRect(roundedIntPoint(rect.location()), roundedIntSize(rect.size()));
}
-FloatRect mapRect(const FloatRect& r, const FloatRect& srcRect, const FloatRect& destRect)
+TextStream& operator<<(TextStream& ts, const FloatRect &r)
{
- if (srcRect.width() == 0 || srcRect.height() == 0)
- return FloatRect();
-
- float widthScale = destRect.width() / srcRect.width();
- float heightScale = destRect.height() / srcRect.height();
- return FloatRect(destRect.x() + (r.x() - srcRect.x()) * widthScale,
- destRect.y() + (r.y() - srcRect.y()) * heightScale,
- r.width() * widthScale, r.height() * heightScale);
-}
+ if (ts.hasFormattingFlag(TextStream::Formatting::SVGStyleRect)) {
+ // FIXME: callers should use the NumberRespectingIntegers flag.
+ return ts << "at (" << TextStream::FormatNumberRespectingIntegers(r.x()) << "," << TextStream::FormatNumberRespectingIntegers(r.y())
+ << ") size " << TextStream::FormatNumberRespectingIntegers(r.width()) << "x" << TextStream::FormatNumberRespectingIntegers(r.height());
+ }
-void FloatRect::dump(PrintStream& out) const
-{
- out.print(location(), " ", size());
+ return ts << r.location() << " " << r.size();
}
}
diff --git a/Source/WebCore/platform/graphics/FloatRect.h b/Source/WebCore/platform/graphics/FloatRect.h
index 8056686d8..cf6dccfd9 100644
--- a/Source/WebCore/platform/graphics/FloatRect.h
+++ b/Source/WebCore/platform/graphics/FloatRect.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003, 2006, 2007 Apple Inc. All rights reserved.
+ * Copyright (C) 2003-2016 Apple Inc. All rights reserved.
* Copyright (C) 2005 Nokia. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -11,10 +11,10 @@
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
- * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
@@ -28,32 +28,33 @@
#define FloatRect_h
#include "FloatPoint.h"
-#include <wtf/Vector.h>
-
-#if PLATFORM(IOS)
-#include <CoreGraphics/CGGeometry.h>
-#endif
#if USE(CG)
typedef struct CGRect CGRect;
#endif
-#if PLATFORM(MAC) && !PLATFORM(IOS)
+#if PLATFORM(MAC)
#ifdef NSGEOMETRY_TYPES_SAME_AS_CGGEOMETRY_TYPES
typedef struct CGRect NSRect;
#else
typedef struct _NSRect NSRect;
#endif
-#endif // PLATFORM(MAC) && !PLATFORM(IOS)
+#endif // PLATFORM(MAC)
#if USE(CAIRO)
typedef struct _cairo_rectangle cairo_rectangle_t;
#endif
+#if PLATFORM(WIN)
+struct D2D_RECT_F;
+typedef D2D_RECT_F D2D1_RECT_F;
+#endif
+
namespace WebCore {
class IntRect;
class IntPoint;
+class TextStream;
class FloatRect {
public:
@@ -67,7 +68,9 @@ public:
: m_location(location), m_size(size) { }
FloatRect(float x, float y, float width, float height)
: m_location(FloatPoint(x, y)), m_size(FloatSize(width, height)) { }
- FloatRect(const IntRect&);
+ FloatRect(const FloatPoint& topLeft, const FloatPoint& bottomRight)
+ : m_location(topLeft), m_size(FloatSize(bottomRight.x() - topLeft.x(), bottomRight.y() - topLeft.y())) { }
+ WEBCORE_EXPORT FloatRect(const IntRect&);
static FloatRect narrowPrecision(double x, double y, double width, double height);
@@ -132,21 +135,24 @@ public:
FloatPoint minXMaxYCorner() const { return FloatPoint(m_location.x(), m_location.y() + m_size.height()); } // typically bottomLeft
FloatPoint maxXMaxYCorner() const { return FloatPoint(m_location.x() + m_size.width(), m_location.y() + m_size.height()); } // typically bottomRight
- bool intersects(const FloatRect&) const;
- bool contains(const FloatRect&) const;
- bool contains(const FloatPoint&, ContainsMode = InsideOrOnStroke) const;
+ WEBCORE_EXPORT bool intersects(const FloatRect&) const;
+ WEBCORE_EXPORT bool contains(const FloatRect&) const;
+ WEBCORE_EXPORT bool contains(const FloatPoint&, ContainsMode = InsideOrOnStroke) const;
- void intersect(const FloatRect&);
- void unite(const FloatRect&);
+ WEBCORE_EXPORT void intersect(const FloatRect&);
+ WEBCORE_EXPORT void unite(const FloatRect&);
void uniteEvenIfEmpty(const FloatRect&);
void uniteIfNonZero(const FloatRect&);
- void extend(const FloatPoint&);
+ WEBCORE_EXPORT void extend(const FloatPoint&);
// Note, this doesn't match what IntRect::contains(IntPoint&) does; the int version
// is really checking for containment of 1x1 rect, but that doesn't make sense with floats.
bool contains(float px, float py) const
{ return px >= x() && px <= maxX() && py >= y() && py <= maxY(); }
+ bool overlapsYRange(float y1, float y2) const { return !isEmpty() && y2 >= y1 && y2 >= y() && y1 <= maxY(); }
+ bool overlapsXRange(float x1, float x2) const { return !isEmpty() && x2 >= x1 && x2 >= x() && x1 <= maxX(); }
+
void inflateX(float dx) {
m_location.setX(m_location.x() - dx);
m_size.setWidth(m_size.width() + dx + dx);
@@ -157,33 +163,34 @@ public:
}
void inflate(float d) { inflateX(d); inflateY(d); }
void scale(float s) { scale(s, s); }
- void scale(float sx, float sy);
+ WEBCORE_EXPORT void scale(float sx, float sy);
FloatRect transposedRect() const { return FloatRect(m_location.transposedPoint(), m_size.transposedSize()); }
// Re-initializes this rectangle to fit the sets of passed points.
- void fitToPoints(const FloatPoint& p0, const FloatPoint& p1);
- void fitToPoints(const FloatPoint& p0, const FloatPoint& p1, const FloatPoint& p2);
- void fitToPoints(const FloatPoint& p0, const FloatPoint& p1, const FloatPoint& p2, const FloatPoint& p3);
+ WEBCORE_EXPORT void fitToPoints(const FloatPoint& p0, const FloatPoint& p1);
+ WEBCORE_EXPORT void fitToPoints(const FloatPoint& p0, const FloatPoint& p1, const FloatPoint& p2);
+ WEBCORE_EXPORT void fitToPoints(const FloatPoint& p0, const FloatPoint& p1, const FloatPoint& p2, const FloatPoint& p3);
#if USE(CG)
- FloatRect(const CGRect&);
- operator CGRect() const;
+ WEBCORE_EXPORT FloatRect(const CGRect&);
+ WEBCORE_EXPORT operator CGRect() const;
#endif
-#if !PLATFORM(IOS)
#if PLATFORM(MAC) && !defined(NSGEOMETRY_TYPES_SAME_AS_CGGEOMETRY_TYPES)
- FloatRect(const NSRect&);
- operator NSRect() const;
+ WEBCORE_EXPORT FloatRect(const NSRect&);
+ WEBCORE_EXPORT operator NSRect() const;
#endif
-#endif // !PLATFORM(IOS)
#if USE(CAIRO)
FloatRect(const cairo_rectangle_t&);
operator cairo_rectangle_t() const;
#endif
- void dump(PrintStream& out) const;
+#if PLATFORM(WIN)
+ WEBCORE_EXPORT FloatRect(const D2D1_RECT_F&);
+ WEBCORE_EXPORT operator D2D1_RECT_F() const;
+#endif
static FloatRect infiniteRect();
bool isInfinite() const;
@@ -214,8 +221,6 @@ inline FloatRect unionRect(const FloatRect& a, const FloatRect& b)
return c;
}
-FloatRect unionRect(const Vector<FloatRect>&);
-
inline FloatRect& operator+=(FloatRect& a, const FloatRect& b)
{
a.move(b.x(), b.y());
@@ -252,15 +257,11 @@ inline bool FloatRect::isInfinite() const
return *this == infiniteRect();
}
-IntRect enclosingIntRect(const FloatRect&);
-
-// Returns a valid IntRect contained within the given FloatRect.
-IntRect enclosedIntRect(const FloatRect&);
-
-IntRect roundedIntRect(const FloatRect&);
+WEBCORE_EXPORT FloatRect encloseRectToDevicePixels(const FloatRect&, float deviceScaleFactor);
+WEBCORE_EXPORT IntRect enclosingIntRect(const FloatRect&);
+WEBCORE_EXPORT IntRect roundedIntRect(const FloatRect&);
-// Map rect r from srcRect to an equivalent rect in destRect.
-FloatRect mapRect(const FloatRect& r, const FloatRect& srcRect, const FloatRect& destRect);
+WEBCORE_EXPORT TextStream& operator<<(TextStream&, const FloatRect&);
}
diff --git a/Source/WebCore/platform/graphics/FloatRoundedRect.cpp b/Source/WebCore/platform/graphics/FloatRoundedRect.cpp
index 19ac93186..20a88b8b6 100644
--- a/Source/WebCore/platform/graphics/FloatRoundedRect.cpp
+++ b/Source/WebCore/platform/graphics/FloatRoundedRect.cpp
@@ -30,10 +30,17 @@
#include "config.h"
#include "FloatRoundedRect.h"
+#include "TextStream.h"
#include <algorithm>
namespace WebCore {
+FloatRoundedRect::FloatRoundedRect(const RoundedRect& rect)
+ : m_rect(rect.rect())
+ , m_radii(rect.radii())
+{
+}
+
FloatRoundedRect::FloatRoundedRect(float x, float y, float width, float height)
: m_rect(x, y, width, height)
{
@@ -56,25 +63,37 @@ bool FloatRoundedRect::Radii::isZero() const
return m_topLeft.isZero() && m_topRight.isZero() && m_bottomLeft.isZero() && m_bottomRight.isZero();
}
+bool FloatRoundedRect::Radii::isUniformCornerRadius() const
+{
+ return WTF::areEssentiallyEqual(m_topLeft.width(), m_topLeft.height())
+ && areEssentiallyEqual(m_topLeft, m_topRight)
+ && areEssentiallyEqual(m_topLeft, m_bottomLeft)
+ && areEssentiallyEqual(m_topLeft, m_bottomRight);
+}
+
void FloatRoundedRect::Radii::scale(float factor)
{
- if (factor == 1)
+ scale(factor, factor);
+}
+
+void FloatRoundedRect::Radii::scale(float horizontalFactor, float verticalFactor)
+{
+ if (horizontalFactor == 1 && verticalFactor == 1)
return;
// If either radius on a corner becomes zero, reset both radii on that corner.
- m_topLeft.scale(factor);
+ m_topLeft.scale(horizontalFactor, verticalFactor);
if (!m_topLeft.width() || !m_topLeft.height())
m_topLeft = FloatSize();
- m_topRight.scale(factor);
+ m_topRight.scale(horizontalFactor, verticalFactor);
if (!m_topRight.width() || !m_topRight.height())
m_topRight = FloatSize();
- m_bottomLeft.scale(factor);
+ m_bottomLeft.scale(horizontalFactor, verticalFactor);
if (!m_bottomLeft.width() || !m_bottomLeft.height())
m_bottomLeft = FloatSize();
- m_bottomRight.scale(factor);
+ m_bottomRight.scale(horizontalFactor, verticalFactor);
if (!m_bottomRight.width() || !m_bottomRight.height())
m_bottomRight = FloatSize();
-
}
void FloatRoundedRect::Radii::expand(float topWidth, float bottomWidth, float leftWidth, float rightWidth)
@@ -137,4 +156,71 @@ bool FloatRoundedRect::xInterceptsAtY(float y, float& minXIntercept, float& maxX
return true;
}
+bool FloatRoundedRect::isRenderable() const
+{
+ return m_radii.topLeft().width() >= 0 && m_radii.topLeft().height() >= 0
+ && m_radii.bottomLeft().width() >= 0 && m_radii.bottomLeft().height() >= 0
+ && m_radii.topRight().width() >= 0 && m_radii.topRight().height() >= 0
+ && m_radii.bottomRight().width() >= 0 && m_radii.bottomRight().height() >= 0
+ && m_radii.topLeft().width() + m_radii.topRight().width() <= m_rect.width()
+ && m_radii.bottomLeft().width() + m_radii.bottomRight().width() <= m_rect.width()
+ && m_radii.topLeft().height() + m_radii.bottomLeft().height() <= m_rect.height()
+ && m_radii.topRight().height() + m_radii.bottomRight().height() <= m_rect.height();
+}
+
+void FloatRoundedRect::inflateWithRadii(float size)
+{
+ FloatRect old = m_rect;
+
+ m_rect.inflate(size);
+ // Considering the inflation factor of shorter size to scale the radii seems appropriate here
+ float factor;
+ if (m_rect.width() < m_rect.height())
+ factor = old.width() ? m_rect.width() / old.width() : 0;
+ else
+ factor = old.height() ? m_rect.height() / old.height() : 0;
+
+ m_radii.scale(factor);
+}
+
+void FloatRoundedRect::adjustRadii()
+{
+ float maxRadiusWidth = std::max(m_radii.topLeft().width() + m_radii.topRight().width(), m_radii.bottomLeft().width() + m_radii.bottomRight().width());
+ float maxRadiusHeight = std::max(m_radii.topLeft().height() + m_radii.bottomLeft().height(), m_radii.topRight().height() + m_radii.bottomRight().height());
+
+ if (maxRadiusWidth <= 0 || maxRadiusHeight <= 0) {
+ m_radii.scale(0.0f);
+ return;
+ }
+ float widthRatio = m_rect.width() / maxRadiusWidth;
+ float heightRatio = m_rect.height() / maxRadiusHeight;
+ m_radii.scale(widthRatio < heightRatio ? widthRatio : heightRatio);
+}
+
+// This is conservative; it does not test intrusion into the corner rects.
+bool FloatRoundedRect::intersectionIsRectangular(const FloatRect& rect) const
+{
+ return !(rect.intersects(topLeftCorner()) || rect.intersects(topRightCorner()) || rect.intersects(bottomLeftCorner()) || rect.intersects(bottomRightCorner()));
+}
+
+TextStream& operator<<(TextStream& ts, const FloatRoundedRect& roundedRect)
+{
+ ts << roundedRect.rect().x() << " " << roundedRect.rect().y() << " " << roundedRect.rect().width() << " " << roundedRect.rect().height() << "\n";
+
+ ts.increaseIndent();
+ ts.writeIndent();
+ ts << "topLeft=" << roundedRect.topLeftCorner().width() << " " << roundedRect.topLeftCorner().height() << "\n";
+ ts.writeIndent();
+ ts << "topRight=" << roundedRect.topRightCorner().width() << " " << roundedRect.topRightCorner().height() << "\n";
+ ts.writeIndent();
+ ts << "bottomLeft=" << roundedRect.bottomLeftCorner().width() << " " << roundedRect.bottomLeftCorner().height() << "\n";
+ ts.writeIndent();
+ ts << "bottomRight=" << roundedRect.bottomRightCorner().width() << " " << roundedRect.bottomRightCorner().height();
+ ts.decreaseIndent();
+
+ return ts;
+}
+
+
+
} // namespace WebCore
diff --git a/Source/WebCore/platform/graphics/FloatRoundedRect.h b/Source/WebCore/platform/graphics/FloatRoundedRect.h
index fbd449be8..eed0e355f 100644
--- a/Source/WebCore/platform/graphics/FloatRoundedRect.h
+++ b/Source/WebCore/platform/graphics/FloatRoundedRect.h
@@ -37,6 +37,7 @@
namespace WebCore {
class FloatRoundedRect {
+ WTF_MAKE_FAST_ALLOCATED;
public:
class Radii {
public:
@@ -57,6 +58,14 @@ public:
{
}
+ explicit Radii(float uniformRadius)
+ : m_topLeft(uniformRadius, uniformRadius)
+ , m_topRight(uniformRadius, uniformRadius)
+ , m_bottomLeft(uniformRadius, uniformRadius)
+ , m_bottomRight(uniformRadius, uniformRadius)
+ {
+ }
+
void setTopLeft(const FloatSize& size) { m_topLeft = size; }
void setTopRight(const FloatSize& size) { m_topRight = size; }
void setBottomLeft(const FloatSize& size) { m_bottomLeft = size; }
@@ -67,8 +76,10 @@ public:
const FloatSize& bottomRight() const { return m_bottomRight; }
bool isZero() const;
+ bool isUniformCornerRadius() const; // Including no radius.
void scale(float factor);
+ void scale(float horizontalFactor, float verticalFactor);
void expand(float topWidth, float bottomWidth, float leftWidth, float rightWidth);
void expand(float size) { expand(size, size, size, size); }
void shrink(float topWidth, float bottomWidth, float leftWidth, float rightWidth) { expand(-topWidth, -bottomWidth, -leftWidth, -rightWidth); }
@@ -81,7 +92,8 @@ public:
FloatSize m_bottomRight;
};
- explicit FloatRoundedRect(const FloatRect&, const Radii& = Radii());
+ WEBCORE_EXPORT explicit FloatRoundedRect(const FloatRect& = FloatRect(), const Radii& = Radii());
+ explicit FloatRoundedRect(const RoundedRect&);
FloatRoundedRect(float x, float y, float width, float height);
FloatRoundedRect(const FloatRect&, const FloatSize& topLeft, const FloatSize& topRight, const FloatSize& bottomLeft, const FloatSize& bottomRight);
@@ -97,6 +109,8 @@ public:
void inflate(float size) { m_rect.inflate(size); }
void expandRadii(float size) { m_radii.expand(size); }
void shrinkRadii(float size) { m_radii.shrink(size); }
+ void inflateWithRadii(float size);
+ void adjustRadii();
FloatRect topLeftCorner() const
{
@@ -115,8 +129,11 @@ public:
return FloatRect(m_rect.maxX() - m_radii.bottomRight().width(), m_rect.maxY() - m_radii.bottomRight().height(), m_radii.bottomRight().width(), m_radii.bottomRight().height());
}
+ bool isRenderable() const;
bool xInterceptsAtY(float y, float& minXIntercept, float& maxXIntercept) const;
+ bool intersectionIsRectangular(const FloatRect&) const;
+
private:
FloatRect m_rect;
Radii m_radii;
@@ -127,11 +144,55 @@ inline bool operator==(const FloatRoundedRect::Radii& a, const FloatRoundedRect:
return a.topLeft() == b.topLeft() && a.topRight() == b.topRight() && a.bottomLeft() == b.bottomLeft() && a.bottomRight() == b.bottomRight();
}
+inline bool operator!=(const FloatRoundedRect::Radii& a, const FloatRoundedRect::Radii& b)
+{
+ return !(a == b);
+}
+
inline bool operator==(const FloatRoundedRect& a, const FloatRoundedRect& b)
{
return a.rect() == b.rect() && a.radii() == b.radii();
}
+inline bool operator!=(const FloatRoundedRect& a, const FloatRoundedRect& b)
+{
+ return !(a == b);
+}
+
+inline float calcBorderRadiiConstraintScaleFor(const FloatRect& rect, const FloatRoundedRect::Radii& radii)
+{
+ // Constrain corner radii using CSS3 rules:
+ // http://www.w3.org/TR/css3-background/#the-border-radius
+
+ float factor = 1;
+ float radiiSum;
+
+ // top
+ radiiSum = radii.topLeft().width() + radii.topRight().width(); // Casts to avoid integer overflow.
+ if (radiiSum > rect.width())
+ factor = std::min(rect.width() / radiiSum, factor);
+
+ // bottom
+ radiiSum = radii.bottomLeft().width() + radii.bottomRight().width();
+ if (radiiSum > rect.width())
+ factor = std::min(rect.width() / radiiSum, factor);
+
+ // left
+ radiiSum = radii.topLeft().height() + radii.bottomLeft().height();
+ if (radiiSum > rect.height())
+ factor = std::min(rect.height() / radiiSum, factor);
+
+ // right
+ radiiSum = radii.topRight().height() + radii.bottomRight().height();
+ if (radiiSum > rect.height())
+ factor = std::min(rect.height() / radiiSum, factor);
+
+ ASSERT(factor <= 1);
+ return factor;
+}
+
+WEBCORE_EXPORT TextStream& operator<<(TextStream&, const FloatRoundedRect&);
+
} // namespace WebCore
#endif // FloatRoundedRect_h
diff --git a/Source/WebCore/platform/graphics/FloatSize.cpp b/Source/WebCore/platform/graphics/FloatSize.cpp
index 60ae586a2..73eed5440 100644
--- a/Source/WebCore/platform/graphics/FloatSize.cpp
+++ b/Source/WebCore/platform/graphics/FloatSize.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003, 2006 Apple Computer, Inc. All rights reserved.
+ * Copyright (C) 2003, 2006 Apple Inc. All rights reserved.
* Copyright (C) 2005 Nokia. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -11,10 +11,10 @@
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
- * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
@@ -29,15 +29,26 @@
#include "FloatConversion.h"
#include "IntSize.h"
+#include "TextStream.h"
#include <limits>
#include <math.h>
namespace WebCore {
-FloatSize::FloatSize(const IntSize& size) : m_width(size.width()), m_height(size.height())
+FloatSize::FloatSize(const IntSize& size)
+ : m_width(size.width())
+ , m_height(size.height())
{
}
+FloatSize FloatSize::constrainedBetween(const FloatSize& min, const FloatSize& max) const
+{
+ return {
+ std::max(min.width(), std::min(max.width(), m_width)),
+ std::max(min.height(), std::min(max.height(), m_height))
+ };
+}
+
float FloatSize::diagonalLength() const
{
return sqrtf(diagonalLengthSquared());
@@ -58,9 +69,10 @@ FloatSize FloatSize::narrowPrecision(double width, double height)
return FloatSize(narrowPrecisionToFloat(width), narrowPrecisionToFloat(height));
}
-void FloatSize::dump(PrintStream& out) const
+TextStream& operator<<(TextStream& ts, const FloatSize& size)
{
- out.printf("(%f x %f)", width(), height());
+ return ts << "width=" << TextStream::FormatNumberRespectingIntegers(size.width())
+ << " height=" << TextStream::FormatNumberRespectingIntegers(size.height());
}
}
diff --git a/Source/WebCore/platform/graphics/FloatSize.h b/Source/WebCore/platform/graphics/FloatSize.h
index d7aa6df75..e48d0805b 100644
--- a/Source/WebCore/platform/graphics/FloatSize.h
+++ b/Source/WebCore/platform/graphics/FloatSize.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003, 2006 Apple Computer, Inc. All rights reserved.
+ * Copyright (C) 2003-2016 Apple Inc. All rights reserved.
* Copyright (C) 2005 Nokia. All rights reserved.
* 2008 Eric Seidel <eric@webkit.org>
*
@@ -12,10 +12,10 @@
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
- * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
@@ -39,23 +39,29 @@
typedef struct CGSize CGSize;
#endif
-#if PLATFORM(MAC) && !PLATFORM(IOS)
+#if PLATFORM(MAC)
#ifdef NSGEOMETRY_TYPES_SAME_AS_CGGEOMETRY_TYPES
typedef struct CGSize NSSize;
#else
typedef struct _NSSize NSSize;
#endif
-#endif // PLATFORM(MAC) && !PLATFORM(IOS)
+#endif // PLATFORM(MAC)
+
+#if PLATFORM(WIN)
+struct D2D_SIZE_F;
+typedef D2D_SIZE_F D2D1_SIZE_F;
+#endif
namespace WebCore {
class IntSize;
+class TextStream;
class FloatSize {
public:
- FloatSize() : m_width(0), m_height(0) { }
+ FloatSize() { }
FloatSize(float width, float height) : m_width(width), m_height(height) { }
- FloatSize(const IntSize&);
+ WEBCORE_EXPORT FloatSize(const IntSize&);
static FloatSize narrowPrecision(double width, double height);
@@ -66,7 +72,7 @@ public:
void setHeight(float height) { m_height = height; }
bool isEmpty() const { return m_width <= 0 || m_height <= 0; }
- bool isZero() const;
+ WEBCORE_EXPORT bool isZero() const;
bool isExpressibleAsIntSize() const;
float aspectRatio() const { return m_width / m_height; }
@@ -77,7 +83,11 @@ public:
m_height += height;
}
- void scale(float s) { scale(s, s); }
+ void scale(float s)
+ {
+ m_width *= s;
+ m_height *= s;
+ }
void scale(float scaleX, float scaleY)
{
@@ -85,6 +95,8 @@ public:
m_height *= scaleY;
}
+ WEBCORE_EXPORT FloatSize constrainedBetween(const FloatSize& min, const FloatSize& max) const;
+
FloatSize expandedTo(const FloatSize& other) const
{
return FloatSize(m_width > other.m_width ? m_width : other.m_width,
@@ -97,11 +109,17 @@ public:
m_height < other.m_height ? m_height : other.m_height);
}
- float diagonalLength() const;
+ WEBCORE_EXPORT float diagonalLength() const;
+
float diagonalLengthSquared() const
{
return m_width * m_width + m_height * m_height;
}
+
+ float area() const
+ {
+ return m_width * m_height;
+ }
FloatSize transposedSize() const
{
@@ -109,21 +127,23 @@ public:
}
#if USE(CG)
- explicit FloatSize(const CGSize&); // don't do this implicitly since it's lossy
- operator CGSize() const;
+ WEBCORE_EXPORT explicit FloatSize(const CGSize&); // don't do this implicitly since it's lossy
+ WEBCORE_EXPORT operator CGSize() const;
#endif
-#if !PLATFORM(IOS)
-#if (PLATFORM(MAC) && !defined(NSGEOMETRY_TYPES_SAME_AS_CGGEOMETRY_TYPES))
- explicit FloatSize(const NSSize &); // don't do this implicitly since it's lossy
+#if PLATFORM(MAC) && !defined(NSGEOMETRY_TYPES_SAME_AS_CGGEOMETRY_TYPES)
+ WEBCORE_EXPORT explicit FloatSize(const NSSize&); // don't do this implicitly since it's lossy
operator NSSize() const;
#endif
-#endif // !PLATFORM(IOS)
- void dump(PrintStream& out) const;
+#if PLATFORM(WIN)
+ WEBCORE_EXPORT FloatSize(const D2D1_SIZE_F&);
+ operator D2D1_SIZE_F() const;
+#endif
private:
- float m_width, m_height;
+ float m_width { 0 };
+ float m_height { 0 };
};
inline FloatSize& operator+=(FloatSize& a, const FloatSize& b)
@@ -155,21 +175,41 @@ inline FloatSize operator-(const FloatSize& size)
return FloatSize(-size.width(), -size.height());
}
-inline FloatSize operator*(const FloatSize& a, const float b)
+inline FloatSize operator*(const FloatSize& a, float b)
{
return FloatSize(a.width() * b, a.height() * b);
}
-inline FloatSize operator*(const float a, const FloatSize& b)
+inline FloatSize operator*(float a, const FloatSize& b)
{
return FloatSize(a * b.width(), a * b.height());
}
+inline FloatSize operator*(const FloatSize& a, const FloatSize& b)
+{
+ return FloatSize(a.width() * b.width(), a.height() * b.height());
+}
+
+inline FloatSize operator/(const FloatSize& a, float b)
+{
+ return FloatSize(a.width() / b, a.height() / b);
+}
+
+inline FloatSize operator/(float a, const FloatSize& b)
+{
+ return FloatSize(a / b.width(), a / b.height());
+}
+
inline bool operator==(const FloatSize& a, const FloatSize& b)
{
return a.width() == b.width() && a.height() == b.height();
}
+inline bool areEssentiallyEqual(const FloatSize& a, const FloatSize& b)
+{
+ return WTF::areEssentiallyEqual(a.width(), b.width()) && WTF::areEssentiallyEqual(a.height(), b.height());
+}
+
inline bool operator!=(const FloatSize& a, const FloatSize& b)
{
return a.width() != b.width() || a.height() != b.height();
@@ -195,6 +235,8 @@ inline IntPoint flooredIntPoint(const FloatSize& p)
return IntPoint(clampToInteger(floorf(p.width())), clampToInteger(floorf(p.height())));
}
+WEBCORE_EXPORT TextStream& operator<<(TextStream&, const FloatSize&);
+
} // namespace WebCore
#endif // FloatSize_h
diff --git a/Source/WebCore/platform/graphics/FloatSizeHash.h b/Source/WebCore/platform/graphics/FloatSizeHash.h
new file mode 100644
index 000000000..c5a3966b1
--- /dev/null
+++ b/Source/WebCore/platform/graphics/FloatSizeHash.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2014 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef FloatSizeHash_h
+#define FloatSizeHash_h
+
+#include "FloatSize.h"
+#include <wtf/HashMap.h>
+#include <wtf/HashSet.h>
+
+namespace WTF {
+
+template<> struct FloatHash<WebCore::FloatSize> {
+ static unsigned hash(const WebCore::FloatSize& key) { return pairIntHash(DefaultHash<float>::Hash::hash(key.width()), DefaultHash<float>::Hash::hash(key.height())); }
+ static bool equal(const WebCore::FloatSize& a, const WebCore::FloatSize& b) { return a == b; }
+ static const bool safeToCompareToEmptyOrDeleted = true;
+};
+
+template<> struct DefaultHash<WebCore::FloatSize> {
+ typedef FloatHash<WebCore::FloatSize> Hash;
+};
+
+template<> struct HashTraits<WebCore::FloatSize> : GenericHashTraits<WebCore::FloatSize> {
+ static const bool emptyValueIsZero = true;
+ static const bool needsDestruction = false;
+ static void constructDeletedValue(WebCore::FloatSize& slot) { new (NotNull, &slot) WebCore::FloatSize(-1, -1); }
+ static bool isDeletedValue(const WebCore::FloatSize& value) { return value.width() == -1 && value.height() == -1; }
+};
+
+} // namespace WTF
+
+#endif
diff --git a/Source/WebCore/platform/graphics/Font.cpp b/Source/WebCore/platform/graphics/Font.cpp
index 456f26c0d..ac8fa39c5 100644
--- a/Source/WebCore/platform/graphics/Font.cpp
+++ b/Source/WebCore/platform/graphics/Font.cpp
@@ -1,960 +1,517 @@
/*
- * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
- * (C) 1999 Antti Koivisto (koivisto@kde.org)
- * (C) 2000 Dirk Mueller (mueller@kde.org)
- * Copyright (C) 2003, 2006, 2010, 2011 Apple Inc. All rights reserved.
+ * Copyright (C) 2005, 2008, 2010, 2015 Apple Inc. All rights reserved.
+ * Copyright (C) 2006 Alexey Proskuryakov
*
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
*
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public License
- * along with this library; see the file COPYING.LIB. If not, write to
- * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
- * Boston, MA 02110-1301, USA.
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Apple Inc. ("Apple") nor the names of
+ * its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
*
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "config.h"
#include "Font.h"
-#include "FloatRect.h"
+#if PLATFORM(COCOA)
+#include "CoreTextSPI.h"
+#endif
#include "FontCache.h"
-#include "IntPoint.h"
-#include "GlyphBuffer.h"
-#include "TextRun.h"
-#include "WidthIterator.h"
-#include <wtf/MainThread.h>
+#include "FontCascade.h"
+#include "OpenTypeMathData.h"
#include <wtf/MathExtras.h>
+#include <wtf/NeverDestroyed.h>
#include <wtf/text/AtomicStringHash.h>
-#include <wtf/text/StringBuilder.h>
-
-using namespace WTF;
-using namespace Unicode;
-namespace WTF {
-
-// allow compilation of OwnPtr<TextLayout> in source files that don't have access to the TextLayout class definition
-template <> void deleteOwnedPtr<WebCore::TextLayout>(WebCore::TextLayout* ptr)
-{
- WebCore::Font::deleteLayout(ptr);
-}
+#if ENABLE(OPENTYPE_VERTICAL)
+#include "OpenTypeVerticalData.h"
+#endif
-}
+#if USE(DIRECT2D)
+#include <dwrite.h>
+#endif
namespace WebCore {
-static PassRef<FontGlyphs> retrieveOrAddCachedFontGlyphs(const FontDescription&, PassRefPtr<FontSelector>);
-
-const uint8_t Font::s_roundingHackCharacterTable[256] = {
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 /*\t*/, 1 /*\n*/, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 1 /*space*/, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 /*-*/, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 /*?*/,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 1 /*no-break space*/, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
-};
-
-static bool useBackslashAsYenSignForFamily(const AtomicString& family)
-{
- if (family.isEmpty())
- return false;
- static HashSet<AtomicString>* set;
- if (!set) {
- set = new HashSet<AtomicString>;
- set->add("MS PGothic");
- UChar unicodeNameMSPGothic[] = {0xFF2D, 0xFF33, 0x0020, 0xFF30, 0x30B4, 0x30B7, 0x30C3, 0x30AF};
- set->add(AtomicString(unicodeNameMSPGothic, WTF_ARRAY_LENGTH(unicodeNameMSPGothic)));
-
- set->add("MS PMincho");
- UChar unicodeNameMSPMincho[] = {0xFF2D, 0xFF33, 0x0020, 0xFF30, 0x660E, 0x671D};
- set->add(AtomicString(unicodeNameMSPMincho, WTF_ARRAY_LENGTH(unicodeNameMSPMincho)));
-
- set->add("MS Gothic");
- UChar unicodeNameMSGothic[] = {0xFF2D, 0xFF33, 0x0020, 0x30B4, 0x30B7, 0x30C3, 0x30AF};
- set->add(AtomicString(unicodeNameMSGothic, WTF_ARRAY_LENGTH(unicodeNameMSGothic)));
-
- set->add("MS Mincho");
- UChar unicodeNameMSMincho[] = {0xFF2D, 0xFF33, 0x0020, 0x660E, 0x671D};
- set->add(AtomicString(unicodeNameMSMincho, WTF_ARRAY_LENGTH(unicodeNameMSMincho)));
-
- set->add("Meiryo");
- UChar unicodeNameMeiryo[] = {0x30E1, 0x30A4, 0x30EA, 0x30AA};
- set->add(AtomicString(unicodeNameMeiryo, WTF_ARRAY_LENGTH(unicodeNameMeiryo)));
- }
- return set->contains(family);
-}
-
-Font::CodePath Font::s_codePath = Auto;
-
-TypesettingFeatures Font::s_defaultTypesettingFeatures = 0;
-
-// ============================================================================================
-// Font Implementation (Cross-Platform Portion)
-// ============================================================================================
-
-Font::Font()
- : m_letterSpacing(0)
- , m_wordSpacing(0)
- , m_useBackslashAsYenSymbol(false)
- , m_typesettingFeatures(0)
-{
-}
-
-Font::Font(const FontDescription& fd, float letterSpacing, float wordSpacing)
- : m_fontDescription(fd)
- , m_letterSpacing(letterSpacing)
- , m_wordSpacing(wordSpacing)
- , m_useBackslashAsYenSymbol(useBackslashAsYenSignForFamily(fd.firstFamily()))
- , m_typesettingFeatures(computeTypesettingFeatures())
-{
-}
-
-// FIXME: We should make this constructor platform-independent.
-Font::Font(const FontPlatformData& fontData, bool isPrinterFont, FontSmoothingMode fontSmoothingMode)
- : m_glyphs(FontGlyphs::createForPlatformFont(fontData))
- , m_letterSpacing(0)
- , m_wordSpacing(0)
- , m_useBackslashAsYenSymbol(false)
- , m_typesettingFeatures(computeTypesettingFeatures())
-{
- m_fontDescription.setUsePrinterFont(isPrinterFont);
- m_fontDescription.setFontSmoothing(fontSmoothingMode);
+unsigned GlyphPage::s_count = 0;
+
+const float smallCapsFontSizeMultiplier = 0.7f;
+const float emphasisMarkFontSizeMultiplier = 0.5f;
+
+Font::Font(const FontPlatformData& platformData, bool isCustomFont, bool isLoading, bool isTextOrientationFallback)
+ : m_maxCharWidth(-1)
+ , m_avgCharWidth(-1)
+ , m_platformData(platformData)
+ , m_mathData(nullptr)
+ , m_treatAsFixedPitch(false)
+ , m_isCustomFont(isCustomFont)
+ , m_isLoading(isLoading)
+ , m_isTextOrientationFallback(isTextOrientationFallback)
+ , m_isBrokenIdeographFallback(false)
+ , m_hasVerticalGlyphs(false)
+ , m_isUsedInSystemFallbackCache(false)
#if PLATFORM(IOS)
- m_fontDescription.setSpecifiedSize(CTFontGetSize(fontData.font()));
- m_fontDescription.setComputedSize(CTFontGetSize(fontData.font()));
- m_fontDescription.setItalic(CTFontGetSymbolicTraits(fontData.font()) & kCTFontTraitItalic);
- m_fontDescription.setWeight((CTFontGetSymbolicTraits(fontData.font()) & kCTFontTraitBold) ? FontWeightBold : FontWeightNormal);
+ , m_shouldNotBeUsedForArabic(false)
#endif
-}
-
-// FIXME: We should make this constructor platform-independent.
-#if PLATFORM(IOS)
-Font::Font(const FontPlatformData& fontData, PassRefPtr<FontSelector> fontSelector)
- : m_glyphs(FontGlyphs::createForPlatformFont(fontData))
- , m_letterSpacing(0)
- , m_wordSpacing(0)
- , m_typesettingFeatures(computeTypesettingFeatures())
{
- CTFontRef primaryFont = fontData.font();
- m_fontDescription.setSpecifiedSize(CTFontGetSize(primaryFont));
- m_fontDescription.setComputedSize(CTFontGetSize(primaryFont));
- m_fontDescription.setItalic(CTFontGetSymbolicTraits(primaryFont) & kCTFontTraitItalic);
- m_fontDescription.setWeight((CTFontGetSymbolicTraits(primaryFont) & kCTFontTraitBold) ? FontWeightBold : FontWeightNormal);
- m_fontDescription.setUsePrinterFont(fontData.isPrinterFont());
- m_glyphs = retrieveOrAddCachedFontGlyphs(m_fontDescription, fontSelector.get());
-}
+ platformInit();
+ platformGlyphInit();
+ platformCharWidthInit();
+#if ENABLE(OPENTYPE_VERTICAL)
+ if (platformData.orientation() == Vertical && !isTextOrientationFallback) {
+ m_verticalData = FontCache::singleton().verticalData(platformData);
+ m_hasVerticalGlyphs = m_verticalData.get() && m_verticalData->hasVerticalMetrics();
+ }
#endif
-
-Font::Font(const Font& other)
- : m_fontDescription(other.m_fontDescription)
- , m_glyphs(other.m_glyphs)
- , m_letterSpacing(other.m_letterSpacing)
- , m_wordSpacing(other.m_wordSpacing)
- , m_useBackslashAsYenSymbol(other.m_useBackslashAsYenSymbol)
- , m_typesettingFeatures(computeTypesettingFeatures())
-{
}
-Font& Font::operator=(const Font& other)
+// Estimates of avgCharWidth and maxCharWidth for platforms that don't support accessing these values from the font.
+void Font::initCharWidths()
{
- m_fontDescription = other.m_fontDescription;
- m_glyphs = other.m_glyphs;
- m_letterSpacing = other.m_letterSpacing;
- m_wordSpacing = other.m_wordSpacing;
- m_useBackslashAsYenSymbol = other.m_useBackslashAsYenSymbol;
- m_typesettingFeatures = other.m_typesettingFeatures;
- return *this;
-}
-
-bool Font::operator==(const Font& other) const
-{
- // Our FontData don't have to be checked, since checking the font description will be fine.
- // FIXME: This does not work if the font was made with the FontPlatformData constructor.
- if (loadingCustomFonts() || other.loadingCustomFonts())
- return false;
-
- if (m_fontDescription != other.m_fontDescription || m_letterSpacing != other.m_letterSpacing || m_wordSpacing != other.m_wordSpacing)
- return false;
- if (m_glyphs == other.m_glyphs)
- return true;
- if (!m_glyphs || !other.m_glyphs)
- return false;
- if (m_glyphs->fontSelector() != other.m_glyphs->fontSelector())
- return false;
- // Can these cases actually somehow occur? All fonts should get wiped out by full style recalc.
- if (m_glyphs->fontSelectorVersion() != other.m_glyphs->fontSelectorVersion())
- return false;
- if (m_glyphs->generation() != other.m_glyphs->generation())
- return false;
- return true;
-}
-
-struct FontGlyphsCacheKey {
- // This part of the key is shared with the lower level FontCache (caching FontData objects).
- FontDescriptionFontDataCacheKey fontDescriptionCacheKey;
- Vector<AtomicString, 3> families;
- unsigned fontSelectorId;
- unsigned fontSelectorVersion;
- unsigned fontSelectorFlags;
-};
-
-struct FontGlyphsCacheEntry {
- WTF_MAKE_FAST_ALLOCATED;
-public:
- FontGlyphsCacheEntry(FontGlyphsCacheKey&& k, PassRef<FontGlyphs> g) : key(k), glyphs(std::move(g)) { }
- FontGlyphsCacheKey key;
- Ref<FontGlyphs> glyphs;
-};
+ auto* glyphPageZero = glyphPage(GlyphPage::pageNumberForCodePoint('0'));
-typedef HashMap<unsigned, OwnPtr<FontGlyphsCacheEntry>, AlreadyHashed> FontGlyphsCache;
-
-static bool operator==(const FontGlyphsCacheKey& a, const FontGlyphsCacheKey& b)
-{
- if (a.fontDescriptionCacheKey != b.fontDescriptionCacheKey)
- return false;
- if (a.fontSelectorId != b.fontSelectorId || a.fontSelectorVersion != b.fontSelectorVersion || a.fontSelectorFlags != b.fontSelectorFlags)
- return false;
- if (a.families.size() != b.families.size())
- return false;
- for (unsigned i = 0; i < a.families.size(); ++i) {
- if (!equalIgnoringCase(a.families[i].impl(), b.families[i].impl()))
- return false;
+ // Treat the width of a '0' as the avgCharWidth.
+ if (m_avgCharWidth <= 0.f && glyphPageZero) {
+ Glyph digitZeroGlyph = glyphPageZero->glyphDataForCharacter('0').glyph;
+ if (digitZeroGlyph)
+ m_avgCharWidth = widthForGlyph(digitZeroGlyph);
}
- return true;
-}
-static FontGlyphsCache& fontGlyphsCache()
-{
- DEFINE_STATIC_LOCAL(FontGlyphsCache, cache, ());
- return cache;
-}
+ // If we can't retrieve the width of a '0', fall back to the x height.
+ if (m_avgCharWidth <= 0.f)
+ m_avgCharWidth = m_fontMetrics.xHeight();
-void invalidateFontGlyphsCache()
-{
- fontGlyphsCache().clear();
+ if (m_maxCharWidth <= 0.f)
+ m_maxCharWidth = std::max(m_avgCharWidth, m_fontMetrics.floatAscent());
}
-void clearWidthCaches()
+void Font::platformGlyphInit()
{
- for (auto it = fontGlyphsCache().begin(), end = fontGlyphsCache().end(); it != end; ++it)
- it->value->glyphs.get().widthCache().clear();
-}
+ auto* glyphPageZero = glyphPage(0);
+ auto* glyphPageCharacterZero = glyphPage(GlyphPage::pageNumberForCodePoint('0'));
+ auto* glyphPageSpace = glyphPage(GlyphPage::pageNumberForCodePoint(space));
-static unsigned makeFontSelectorFlags(const FontDescription& description)
-{
- return static_cast<unsigned>(description.script()) << 1 | static_cast<unsigned>(description.smallCaps());
-}
+ // Ask for the glyph for 0 to avoid paging in ZERO WIDTH SPACE. Control characters, including 0,
+ // are mapped to the ZERO WIDTH SPACE glyph.
+ if (glyphPageZero)
+ m_zeroWidthSpaceGlyph = glyphPageZero->glyphDataForCharacter(0).glyph;
-static void makeFontGlyphsCacheKey(FontGlyphsCacheKey& key, const FontDescription& description, FontSelector* fontSelector)
-{
- key.fontDescriptionCacheKey = FontDescriptionFontDataCacheKey(description);
- for (unsigned i = 0; i < description.familyCount(); ++i)
- key.families.append(description.familyAt(i));
- key.fontSelectorId = fontSelector ? fontSelector->uniqueId() : 0;
- key.fontSelectorVersion = fontSelector ? fontSelector->version() : 0;
- key.fontSelectorFlags = fontSelector && fontSelector->resolvesFamilyFor(description) ? makeFontSelectorFlags(description) : 0;
-}
+ // Nasty hack to determine if we should round or ceil space widths.
+ // If the font is monospace or fake monospace we ceil to ensure that
+ // every character and the space are the same width. Otherwise we round.
+ if (glyphPageSpace)
+ m_spaceGlyph = glyphPageSpace->glyphDataForCharacter(space).glyph;
+ float width = widthForGlyph(m_spaceGlyph);
+ m_spaceWidth = width;
+ if (glyphPageCharacterZero)
+ m_zeroGlyph = glyphPageCharacterZero->glyphDataForCharacter('0').glyph;
+ m_fontMetrics.setZeroWidth(widthForGlyph(m_zeroGlyph));
+ determinePitch();
+ m_adjustedSpaceWidth = m_treatAsFixedPitch ? ceilf(width) : roundf(width);
-static unsigned computeFontGlyphsCacheHash(const FontGlyphsCacheKey& key)
-{
- Vector<unsigned, 7> hashCodes;
- hashCodes.reserveInitialCapacity(4 + key.families.size());
-
- hashCodes.uncheckedAppend(key.fontDescriptionCacheKey.computeHash());
- hashCodes.uncheckedAppend(key.fontSelectorId);
- hashCodes.uncheckedAppend(key.fontSelectorVersion);
- hashCodes.uncheckedAppend(key.fontSelectorFlags);
- for (unsigned i = 0; i < key.families.size(); ++i)
- hashCodes.uncheckedAppend(key.families[i].impl() ? CaseFoldingHash::hash(key.families[i]) : 0);
-
- return StringHasher::hashMemory(hashCodes.data(), hashCodes.size() * sizeof(unsigned));
-}
-
-void pruneUnreferencedEntriesFromFontGlyphsCache()
-{
- Vector<unsigned, 50> toRemove;
- FontGlyphsCache::iterator end = fontGlyphsCache().end();
- for (FontGlyphsCache::iterator it = fontGlyphsCache().begin(); it != end; ++it) {
- if (it->value->glyphs.get().hasOneRef())
- toRemove.append(it->key);
- }
- for (unsigned i = 0; i < toRemove.size(); ++i)
- fontGlyphsCache().remove(toRemove[i]);
+ // Force the glyph for ZERO WIDTH SPACE to have zero width, unless it is shared with SPACE.
+ // Helvetica is an example of a non-zero width ZERO WIDTH SPACE glyph.
+ // See <http://bugs.webkit.org/show_bug.cgi?id=13178> and Font::isZeroWidthSpaceGlyph()
+ if (m_zeroWidthSpaceGlyph == m_spaceGlyph)
+ m_zeroWidthSpaceGlyph = 0;
}
-static PassRef<FontGlyphs> retrieveOrAddCachedFontGlyphs(const FontDescription& fontDescription, PassRefPtr<FontSelector> fontSelector)
+Font::~Font()
{
- FontGlyphsCacheKey key;
- makeFontGlyphsCacheKey(key, fontDescription, fontSelector.get());
-
- unsigned hash = computeFontGlyphsCacheHash(key);
- FontGlyphsCache::AddResult addResult = fontGlyphsCache().add(hash, PassOwnPtr<FontGlyphsCacheEntry>());
- if (!addResult.isNewEntry && addResult.iterator->value->key == key)
- return addResult.iterator->value->glyphs.get();
-
- OwnPtr<FontGlyphsCacheEntry>& newEntry = addResult.iterator->value;
- newEntry = adoptPtr(new FontGlyphsCacheEntry(std::move(key), FontGlyphs::create(fontSelector)));
- PassRef<FontGlyphs> glyphs = newEntry->glyphs.get();
-
- static const unsigned unreferencedPruneInterval = 50;
- static const int maximumEntries = 400;
- static unsigned pruneCounter;
- // Referenced FontGlyphs would exist anyway so pruning them saves little memory.
- if (!(++pruneCounter % unreferencedPruneInterval))
- pruneUnreferencedEntriesFromFontGlyphsCache();
- // Prevent pathological growth.
- if (fontGlyphsCache().size() > maximumEntries)
- fontGlyphsCache().remove(fontGlyphsCache().begin());
- return glyphs;
+ removeFromSystemFallbackCache();
}
-void Font::update(PassRefPtr<FontSelector> fontSelector) const
+static bool fillGlyphPage(GlyphPage& pageToFill, UChar* buffer, unsigned bufferLength, const Font& font)
{
- m_glyphs = retrieveOrAddCachedFontGlyphs(m_fontDescription, fontSelector.get());
- m_useBackslashAsYenSymbol = useBackslashAsYenSignForFamily(firstFamily());
- m_typesettingFeatures = computeTypesettingFeatures();
+ bool hasGlyphs = pageToFill.fill(buffer, bufferLength);
+#if ENABLE(OPENTYPE_VERTICAL)
+ if (hasGlyphs && font.verticalData())
+ font.verticalData()->substituteWithVerticalGlyphs(&font, &pageToFill);
+#else
+ UNUSED_PARAM(font);
+#endif
+ return hasGlyphs;
}
-float Font::drawText(GraphicsContext* context, const TextRun& run, const FloatPoint& point, int from, int to, CustomFontNotReadyAction customFontNotReadyAction) const
+static RefPtr<GlyphPage> createAndFillGlyphPage(unsigned pageNumber, const Font& font)
{
- // Don't draw anything while we are using custom fonts that are in the process of loading,
- // except if the 'force' argument is set to true (in which case it will use a fallback
- // font).
- if (loadingCustomFonts() && customFontNotReadyAction == DoNotPaintIfFontNotReady)
- return 0;
-
- to = (to == -1 ? run.length() : to);
-
- if (codePath(run) != Complex)
- return drawSimpleText(context, run, point, from, to);
+#if PLATFORM(IOS)
+ // FIXME: Times New Roman contains Arabic glyphs, but Core Text doesn't know how to shape them. See <rdar://problem/9823975>.
+ // Once we have the fix for <rdar://problem/9823975> then remove this code together with Font::shouldNotBeUsedForArabic()
+ // in <rdar://problem/12096835>.
+ if (GlyphPage::pageNumberIsUsedForArabic(pageNumber) && font.shouldNotBeUsedForArabic())
+ return nullptr;
+#endif
- return drawComplexText(context, run, point, from, to);
-}
+ unsigned glyphPageSize = GlyphPage::sizeForPageNumber(pageNumber);
+
+ unsigned start = GlyphPage::startingCodePointInPageNumber(pageNumber);
+ unsigned end = start + glyphPageSize;
+ Vector<UChar> buffer(glyphPageSize * 2 + 2);
+ unsigned bufferLength;
+ // Fill in a buffer with the entire "page" of characters that we want to look up glyphs for.
+ if (U_IS_BMP(start)) {
+ bufferLength = glyphPageSize;
+ for (unsigned i = 0; i < bufferLength; i++)
+ buffer[i] = start + i;
+
+ // Code points 0x0 - 0x20 and 0x7F - 0xA0 are control character and shouldn't render. Map them to ZERO WIDTH SPACE.
+ auto overwriteCodePoints = [&](unsigned minimum, unsigned maximum, UChar newCodePoint) {
+ unsigned begin = std::max(start, minimum);
+ unsigned complete = std::min(end, maximum);
+ for (unsigned i = begin; i < complete; ++i)
+ buffer[i - start] = newCodePoint;
+ };
+
+ auto overwriteCodePoint = [&](UChar codePoint, UChar newCodePoint) {
+ if (codePoint >= start && codePoint < end)
+ buffer[codePoint - start] = newCodePoint;
+ };
+
+ overwriteCodePoints(0x0, 0x20, zeroWidthSpace);
+ overwriteCodePoints(0x7F, 0xA0, zeroWidthSpace);
+ overwriteCodePoint(softHyphen, zeroWidthSpace);
+ overwriteCodePoint('\n', space);
+ overwriteCodePoint('\t', space);
+ overwriteCodePoint(noBreakSpace, space);
+ overwriteCodePoint(narrowNoBreakSpace, zeroWidthSpace);
+ overwriteCodePoint(leftToRightMark, zeroWidthSpace);
+ overwriteCodePoint(rightToLeftMark, zeroWidthSpace);
+ overwriteCodePoint(leftToRightEmbed, zeroWidthSpace);
+ overwriteCodePoint(rightToLeftEmbed, zeroWidthSpace);
+ overwriteCodePoint(leftToRightOverride, zeroWidthSpace);
+ overwriteCodePoint(rightToLeftOverride, zeroWidthSpace);
+ overwriteCodePoint(leftToRightIsolate, zeroWidthSpace);
+ overwriteCodePoint(rightToLeftIsolate, zeroWidthSpace);
+ overwriteCodePoint(zeroWidthNonJoiner, zeroWidthSpace);
+ overwriteCodePoint(zeroWidthJoiner, zeroWidthSpace);
+ overwriteCodePoint(popDirectionalFormatting, zeroWidthSpace);
+ overwriteCodePoint(popDirectionalIsolate, zeroWidthSpace);
+ overwriteCodePoint(firstStrongIsolate, zeroWidthSpace);
+ overwriteCodePoint(objectReplacementCharacter, zeroWidthSpace);
+ overwriteCodePoint(zeroWidthNoBreakSpace, zeroWidthSpace);
+ } else {
+ bufferLength = glyphPageSize * 2;
+ for (unsigned i = 0; i < glyphPageSize; i++) {
+ int c = i + start;
+ buffer[i * 2] = U16_LEAD(c);
+ buffer[i * 2 + 1] = U16_TRAIL(c);
+ }
+ }
-void Font::drawEmphasisMarks(GraphicsContext* context, const TextRun& run, const AtomicString& mark, const FloatPoint& point, int from, int to) const
-{
- if (loadingCustomFonts())
- return;
+ // Now that we have a buffer full of characters, we want to get back an array
+ // of glyph indices. This part involves calling into the platform-specific
+ // routine of our glyph map for actually filling in the page with the glyphs.
+ // Success is not guaranteed. For example, Times fails to fill page 260, giving glyph data
+ // for only 128 out of 256 characters.
+ Ref<GlyphPage> glyphPage = GlyphPage::create(font);
- if (to < 0)
- to = run.length();
+ bool haveGlyphs = fillGlyphPage(glyphPage, buffer.data(), bufferLength, font);
+ if (!haveGlyphs)
+ return nullptr;
- if (codePath(run) != Complex)
- drawEmphasisMarksForSimpleText(context, run, mark, point, from, to);
- else
- drawEmphasisMarksForComplexText(context, run, mark, point, from, to);
+ return WTFMove(glyphPage);
}
-float Font::width(const TextRun& run, HashSet<const SimpleFontData*>* fallbackFonts, GlyphOverflow* glyphOverflow) const
+const GlyphPage* Font::glyphPage(unsigned pageNumber) const
{
- CodePath codePathToUse = codePath(run);
- if (codePathToUse != Complex) {
- // The complex path is more restrictive about returning fallback fonts than the simple path, so we need an explicit test to make their behaviors match.
- if (!canReturnFallbackFontsForComplexText())
- fallbackFonts = 0;
- // The simple path can optimize the case where glyph overflow is not observable.
- if (codePathToUse != SimpleWithGlyphOverflow && (glyphOverflow && !glyphOverflow->computeBounds))
- glyphOverflow = 0;
+ if (!pageNumber) {
+ if (!m_glyphPageZero)
+ m_glyphPageZero = createAndFillGlyphPage(0, *this);
+ return m_glyphPageZero.get();
}
+ auto addResult = m_glyphPages.add(pageNumber, nullptr);
+ if (addResult.isNewEntry)
+ addResult.iterator->value = createAndFillGlyphPage(pageNumber, *this);
- bool hasKerningOrLigatures = typesettingFeatures() & (Kerning | Ligatures);
- bool hasWordSpacingOrLetterSpacing = wordSpacing() || letterSpacing();
- float* cacheEntry = m_glyphs->widthCache().add(run, std::numeric_limits<float>::quiet_NaN(), hasKerningOrLigatures, hasWordSpacingOrLetterSpacing, glyphOverflow);
- if (cacheEntry && !std::isnan(*cacheEntry))
- return *cacheEntry;
-
- HashSet<const SimpleFontData*> localFallbackFonts;
- if (!fallbackFonts)
- fallbackFonts = &localFallbackFonts;
-
- float result;
- if (codePathToUse == Complex)
- result = floatWidthForComplexText(run, fallbackFonts, glyphOverflow);
- else
- result = floatWidthForSimpleText(run, fallbackFonts, glyphOverflow);
-
- if (cacheEntry && fallbackFonts->isEmpty())
- *cacheEntry = result;
- return result;
+ return addResult.iterator->value.get();
}
-float Font::width(const TextRun& run, int& charsConsumed, String& glyphName) const
+Glyph Font::glyphForCharacter(UChar32 character) const
{
-#if ENABLE(SVG_FONTS)
- if (TextRun::RenderingContext* renderingContext = run.renderingContext())
- return renderingContext->floatWidthUsingSVGFont(*this, run, charsConsumed, glyphName);
-#endif
-
- charsConsumed = run.length();
- glyphName = "";
- return width(run);
-}
-
-#if !PLATFORM(MAC)
-PassOwnPtr<TextLayout> Font::createLayout(RenderText*, float, bool) const
-{
- return nullptr;
+ auto* page = glyphPage(GlyphPage::pageNumberForCodePoint(character));
+ if (!page)
+ return 0;
+ return page->glyphForCharacter(character);
}
-void Font::deleteLayout(TextLayout*)
+GlyphData Font::glyphDataForCharacter(UChar32 character) const
{
+ auto* page = glyphPage(GlyphPage::pageNumberForCodePoint(character));
+ if (!page)
+ return GlyphData();
+ return page->glyphDataForCharacter(character);
}
-float Font::width(TextLayout&, unsigned, unsigned, HashSet<const SimpleFontData*>*)
+const Font& Font::verticalRightOrientationFont() const
{
- ASSERT_NOT_REACHED();
- return 0;
+ if (!m_derivedFontData)
+ m_derivedFontData = std::make_unique<DerivedFonts>(isCustomFont());
+ if (!m_derivedFontData->verticalRightOrientation) {
+ auto verticalRightPlatformData = FontPlatformData::cloneWithOrientation(m_platformData, Horizontal);
+ m_derivedFontData->verticalRightOrientation = create(verticalRightPlatformData, isCustomFont(), false, true);
+ }
+ ASSERT(m_derivedFontData->verticalRightOrientation != this);
+ return *m_derivedFontData->verticalRightOrientation;
}
-#endif
-FloatRect Font::selectionRectForText(const TextRun& run, const FloatPoint& point, int h, int from, int to) const
+const Font& Font::uprightOrientationFont() const
{
- to = (to == -1 ? run.length() : to);
-
- if (codePath(run) != Complex)
- return selectionRectForSimpleText(run, point, h, from, to);
-
- return selectionRectForComplexText(run, point, h, from, to);
+ if (!m_derivedFontData)
+ m_derivedFontData = std::make_unique<DerivedFonts>(isCustomFont());
+ if (!m_derivedFontData->uprightOrientation)
+ m_derivedFontData->uprightOrientation = create(m_platformData, isCustomFont(), false, true);
+ ASSERT(m_derivedFontData->uprightOrientation != this);
+ return *m_derivedFontData->uprightOrientation;
}
-int Font::offsetForPosition(const TextRun& run, float x, bool includePartialGlyphs) const
+const Font* Font::smallCapsFont(const FontDescription& fontDescription) const
{
- if (codePath(run) != Complex)
- return offsetForPositionForSimpleText(run, x, includePartialGlyphs);
-
- return offsetForPositionForComplexText(run, x, includePartialGlyphs);
+ if (!m_derivedFontData)
+ m_derivedFontData = std::make_unique<DerivedFonts>(isCustomFont());
+ if (!m_derivedFontData->smallCaps)
+ m_derivedFontData->smallCaps = createScaledFont(fontDescription, smallCapsFontSizeMultiplier);
+ ASSERT(m_derivedFontData->smallCaps != this);
+ return m_derivedFontData->smallCaps.get();
}
-template <typename CharacterType>
-static inline String normalizeSpacesInternal(const CharacterType* characters, unsigned length)
+const Font& Font::noSynthesizableFeaturesFont() const
{
- StringBuilder normalized;
- normalized.reserveCapacity(length);
-
- for (unsigned i = 0; i < length; ++i)
- normalized.append(Font::normalizeSpaces(characters[i]));
-
- return normalized.toString();
+#if PLATFORM(COCOA)
+ if (!m_derivedFontData)
+ m_derivedFontData = std::make_unique<DerivedFonts>(isCustomFont());
+ if (!m_derivedFontData->noSynthesizableFeatures)
+ m_derivedFontData->noSynthesizableFeatures = createFontWithoutSynthesizableFeatures();
+ ASSERT(m_derivedFontData->noSynthesizableFeatures != this);
+ return *m_derivedFontData->noSynthesizableFeatures;
+#else
+ return *this;
+#endif
}
-String Font::normalizeSpaces(const LChar* characters, unsigned length)
+const Font* Font::emphasisMarkFont(const FontDescription& fontDescription) const
{
- return normalizeSpacesInternal(characters, length);
+ if (!m_derivedFontData)
+ m_derivedFontData = std::make_unique<DerivedFonts>(isCustomFont());
+ if (!m_derivedFontData->emphasisMark)
+ m_derivedFontData->emphasisMark = createScaledFont(fontDescription, emphasisMarkFontSizeMultiplier);
+ ASSERT(m_derivedFontData->emphasisMark != this);
+ return m_derivedFontData->emphasisMark.get();
}
-String Font::normalizeSpaces(const UChar* characters, unsigned length)
+const Font& Font::brokenIdeographFont() const
{
- return normalizeSpacesInternal(characters, length);
+ if (!m_derivedFontData)
+ m_derivedFontData = std::make_unique<DerivedFonts>(isCustomFont());
+ if (!m_derivedFontData->brokenIdeograph) {
+ m_derivedFontData->brokenIdeograph = create(m_platformData, isCustomFont(), false);
+ m_derivedFontData->brokenIdeograph->m_isBrokenIdeographFallback = true;
+ }
+ ASSERT(m_derivedFontData->brokenIdeograph != this);
+ return *m_derivedFontData->brokenIdeograph;
}
-static bool shouldUseFontSmoothing = true;
-
-void Font::setShouldUseSmoothing(bool shouldUseSmoothing)
+const Font& Font::nonSyntheticItalicFont() const
{
- ASSERT(isMainThread());
- shouldUseFontSmoothing = shouldUseSmoothing;
+ if (!m_derivedFontData)
+ m_derivedFontData = std::make_unique<DerivedFonts>(isCustomFont());
+ if (!m_derivedFontData->nonSyntheticItalic) {
+#if PLATFORM(COCOA) || USE(CAIRO)
+ FontPlatformData nonSyntheticItalicFontPlatformData = FontPlatformData::cloneWithSyntheticOblique(m_platformData, false);
+#else
+ FontPlatformData nonSyntheticItalicFontPlatformData(m_platformData);
+#endif
+ m_derivedFontData->nonSyntheticItalic = create(nonSyntheticItalicFontPlatformData, isCustomFont());
+ }
+ ASSERT(m_derivedFontData->nonSyntheticItalic != this);
+ return *m_derivedFontData->nonSyntheticItalic;
}
-bool Font::shouldUseSmoothing()
+#ifndef NDEBUG
+String Font::description() const
{
- return shouldUseFontSmoothing;
-}
+ if (isCustomFont())
+ return "[custom font]";
-void Font::setCodePath(CodePath p)
-{
- s_codePath = p;
+ return platformData().description();
}
+#endif
-Font::CodePath Font::codePath()
+const OpenTypeMathData* Font::mathData() const
{
- return s_codePath;
+ if (m_isLoading)
+ return nullptr;
+ if (!m_mathData) {
+ m_mathData = OpenTypeMathData::create(m_platformData);
+ if (!m_mathData->hasMathData())
+ m_mathData = nullptr;
+ }
+ return m_mathData.get();
}
-void Font::setDefaultTypesettingFeatures(TypesettingFeatures typesettingFeatures)
+Font::DerivedFonts::~DerivedFonts()
{
- s_defaultTypesettingFeatures = typesettingFeatures;
}
-TypesettingFeatures Font::defaultTypesettingFeatures()
+RefPtr<Font> Font::createScaledFont(const FontDescription& fontDescription, float scaleFactor) const
{
- return s_defaultTypesettingFeatures;
+ return platformCreateScaledFont(fontDescription, scaleFactor);
}
-Font::CodePath Font::codePath(const TextRun& run) const
+bool Font::applyTransforms(GlyphBufferGlyph* glyphs, GlyphBufferAdvance* advances, size_t glyphCount, bool enableKerning, bool requiresShaping) const
{
- if (s_codePath != Auto)
- return s_codePath;
-
-#if ENABLE(SVG_FONTS)
- if (run.renderingContext())
- return Simple;
+ // We need to handle transforms on SVG fonts internally, since they are rendered internally.
+#if PLATFORM(COCOA)
+ CTFontTransformOptions options = (enableKerning ? kCTFontTransformApplyPositioning : 0) | (requiresShaping ? kCTFontTransformApplyShaping : 0);
+ return CTFontTransformGlyphs(m_platformData.ctFont(), glyphs, reinterpret_cast<CGSize*>(advances), glyphCount, options);
+#else
+ UNUSED_PARAM(glyphs);
+ UNUSED_PARAM(advances);
+ UNUSED_PARAM(glyphCount);
+ UNUSED_PARAM(enableKerning);
+ UNUSED_PARAM(requiresShaping);
+ return false;
#endif
-
- if (m_fontDescription.featureSettings() && m_fontDescription.featureSettings()->size() > 0)
- return Complex;
-
- if (run.length() > 1 && !WidthIterator::supportsTypesettingFeatures(*this))
- return Complex;
-
- if (!run.characterScanForCodePath())
- return Simple;
-
- if (run.is8Bit())
- return Simple;
-
- // Start from 0 since drawing and highlighting also measure the characters before run->from.
- return characterRangeCodePath(run.characters16(), run.length());
}
-Font::CodePath Font::characterRangeCodePath(const UChar* characters, unsigned len)
-{
- // FIXME: Should use a UnicodeSet in ports where ICU is used. Note that we
- // can't simply use UnicodeCharacter Property/class because some characters
- // are not 'combining', but still need to go to the complex path.
- // Alternatively, we may as well consider binary search over a sorted
- // list of ranges.
- CodePath result = Simple;
- for (unsigned i = 0; i < len; i++) {
- const UChar c = characters[i];
- if (c < 0x2E5) // U+02E5 through U+02E9 (Modifier Letters : Tone letters)
- continue;
- if (c <= 0x2E9)
- return Complex;
-
- if (c < 0x300) // U+0300 through U+036F Combining diacritical marks
- continue;
- if (c <= 0x36F)
- return Complex;
-
- if (c < 0x0591 || c == 0x05BE) // U+0591 through U+05CF excluding U+05BE Hebrew combining marks, Hebrew punctuation Paseq, Sof Pasuq and Nun Hafukha
- continue;
- if (c <= 0x05CF)
- return Complex;
-
- // U+0600 through U+109F Arabic, Syriac, Thaana, NKo, Samaritan, Mandaic,
- // Devanagari, Bengali, Gurmukhi, Gujarati, Oriya, Tamil, Telugu, Kannada,
- // Malayalam, Sinhala, Thai, Lao, Tibetan, Myanmar
- if (c < 0x0600)
- continue;
- if (c <= 0x109F)
- return Complex;
-
- // U+1100 through U+11FF Hangul Jamo (only Ancient Korean should be left here if you precompose;
- // Modern Korean will be precomposed as a result of step A)
- if (c < 0x1100)
- continue;
- if (c <= 0x11FF)
- return Complex;
-
- if (c < 0x135D) // U+135D through U+135F Ethiopic combining marks
- continue;
- if (c <= 0x135F)
- return Complex;
-
- if (c < 0x1700) // U+1780 through U+18AF Tagalog, Hanunoo, Buhid, Taghanwa,Khmer, Mongolian
- continue;
- if (c <= 0x18AF)
- return Complex;
-
- if (c < 0x1900) // U+1900 through U+194F Limbu (Unicode 4.0)
- continue;
- if (c <= 0x194F)
- return Complex;
-
- if (c < 0x1980) // U+1980 through U+19DF New Tai Lue
- continue;
- if (c <= 0x19DF)
- return Complex;
-
- if (c < 0x1A00) // U+1A00 through U+1CFF Buginese, Tai Tham, Balinese, Batak, Lepcha, Vedic
- continue;
- if (c <= 0x1CFF)
- return Complex;
-
- if (c < 0x1DC0) // U+1DC0 through U+1DFF Comining diacritical mark supplement
- continue;
- if (c <= 0x1DFF)
- return Complex;
-
- // U+1E00 through U+2000 characters with diacritics and stacked diacritics
- if (c <= 0x2000) {
- result = SimpleWithGlyphOverflow;
- continue;
- }
-
- if (c < 0x20D0) // U+20D0 through U+20FF Combining marks for symbols
- continue;
- if (c <= 0x20FF)
- return Complex;
-
- if (c < 0x2CEF) // U+2CEF through U+2CF1 Combining marks for Coptic
- continue;
- if (c <= 0x2CF1)
- return Complex;
-
- if (c < 0x302A) // U+302A through U+302F Ideographic and Hangul Tone marks
- continue;
- if (c <= 0x302F)
- return Complex;
-
- if (c < 0xA67C) // U+A67C through U+A67D Combining marks for old Cyrillic
- continue;
- if (c <= 0xA67D)
- return Complex;
-
- if (c < 0xA6F0) // U+A6F0 through U+A6F1 Combining mark for Bamum
- continue;
- if (c <= 0xA6F1)
- return Complex;
-
- // U+A800 through U+ABFF Nagri, Phags-pa, Saurashtra, Devanagari Extended,
- // Hangul Jamo Ext. A, Javanese, Myanmar Extended A, Tai Viet, Meetei Mayek,
- if (c < 0xA800)
- continue;
- if (c <= 0xABFF)
- return Complex;
-
- if (c < 0xD7B0) // U+D7B0 through U+D7FF Hangul Jamo Ext. B
- continue;
- if (c <= 0xD7FF)
- return Complex;
-
- if (c <= 0xDBFF) {
- // High surrogate
-
- if (i == len - 1)
- continue;
-
- UChar next = characters[++i];
- if (!U16_IS_TRAIL(next))
- continue;
-
- UChar32 supplementaryCharacter = U16_GET_SUPPLEMENTARY(c, next);
-
- if (supplementaryCharacter < 0x1F1E6) // U+1F1E6 through U+1F1FF Regional Indicator Symbols
- continue;
- if (supplementaryCharacter <= 0x1F1FF)
- return Complex;
-
- if (supplementaryCharacter < 0xE0100) // U+E0100 through U+E01EF Unicode variation selectors.
- continue;
- if (supplementaryCharacter <= 0xE01EF)
- return Complex;
-
- // FIXME: Check for Brahmi (U+11000 block), Kaithi (U+11080 block) and other complex scripts
- // in plane 1 or higher.
-
- continue;
- }
-
- if (c < 0xFE00) // U+FE00 through U+FE0F Unicode variation selectors
- continue;
- if (c <= 0xFE0F)
- return Complex;
-
- if (c < 0xFE20) // U+FE20 through U+FE2F Combining half marks
- continue;
- if (c <= 0xFE2F)
- return Complex;
+class CharacterFallbackMapKey {
+public:
+ CharacterFallbackMapKey()
+ {
}
- return result;
-}
-
-bool Font::isCJKIdeograph(UChar32 c)
-{
- // The basic CJK Unified Ideographs block.
- if (c >= 0x4E00 && c <= 0x9FFF)
- return true;
-
- // CJK Unified Ideographs Extension A.
- if (c >= 0x3400 && c <= 0x4DBF)
- return true;
-
- // CJK Radicals Supplement.
- if (c >= 0x2E80 && c <= 0x2EFF)
- return true;
-
- // Kangxi Radicals.
- if (c >= 0x2F00 && c <= 0x2FDF)
- return true;
-
- // CJK Strokes.
- if (c >= 0x31C0 && c <= 0x31EF)
- return true;
-
- // CJK Compatibility Ideographs.
- if (c >= 0xF900 && c <= 0xFAFF)
- return true;
-
- // CJK Unified Ideographs Extension B.
- if (c >= 0x20000 && c <= 0x2A6DF)
- return true;
-
- // CJK Unified Ideographs Extension C.
- if (c >= 0x2A700 && c <= 0x2B73F)
- return true;
-
- // CJK Unified Ideographs Extension D.
- if (c >= 0x2B740 && c <= 0x2B81F)
- return true;
-
- // CJK Compatibility Ideographs Supplement.
- if (c >= 0x2F800 && c <= 0x2FA1F)
- return true;
-
- return false;
-}
-
-bool Font::isCJKIdeographOrSymbol(UChar32 c)
-{
- // 0x2C7 Caron, Mandarin Chinese 3rd Tone
- // 0x2CA Modifier Letter Acute Accent, Mandarin Chinese 2nd Tone
- // 0x2CB Modifier Letter Grave Access, Mandarin Chinese 4th Tone
- // 0x2D9 Dot Above, Mandarin Chinese 5th Tone
- if ((c == 0x2C7) || (c == 0x2CA) || (c == 0x2CB) || (c == 0x2D9))
- return true;
-
- if ((c == 0x2020) || (c == 0x2021) || (c == 0x2030) || (c == 0x203B) || (c == 0x203C)
- || (c == 0x2042) || (c == 0x2047) || (c == 0x2048) || (c == 0x2049) || (c == 0x2051)
- || (c == 0x20DD) || (c == 0x20DE) || (c == 0x2100) || (c == 0x2103) || (c == 0x2105)
- || (c == 0x2109) || (c == 0x210A) || (c == 0x2113) || (c == 0x2116) || (c == 0x2121)
- || (c == 0x212B) || (c == 0x213B) || (c == 0x2150) || (c == 0x2151) || (c == 0x2152))
- return true;
-
- if (c >= 0x2156 && c <= 0x215A)
- return true;
-
- if (c >= 0x2160 && c <= 0x216B)
- return true;
-
- if (c >= 0x2170 && c <= 0x217B)
- return true;
-
- if ((c == 0x217F) || (c == 0x2189) || (c == 0x2307) || (c == 0x2312) || (c == 0x23BE) || (c == 0x23BF))
- return true;
-
- if (c >= 0x23C0 && c <= 0x23CC)
- return true;
-
- if ((c == 0x23CE) || (c == 0x2423))
- return true;
-
- if (c >= 0x2460 && c <= 0x2492)
- return true;
-
- if (c >= 0x249C && c <= 0x24FF)
- return true;
-
- if ((c == 0x25A0) || (c == 0x25A1) || (c == 0x25A2) || (c == 0x25AA) || (c == 0x25AB))
- return true;
- if ((c == 0x25B1) || (c == 0x25B2) || (c == 0x25B3) || (c == 0x25B6) || (c == 0x25B7) || (c == 0x25BC) || (c == 0x25BD))
- return true;
-
- if ((c == 0x25C0) || (c == 0x25C1) || (c == 0x25C6) || (c == 0x25C7) || (c == 0x25C9) || (c == 0x25CB) || (c == 0x25CC))
- return true;
-
- if (c >= 0x25CE && c <= 0x25D3)
- return true;
-
- if (c >= 0x25E2 && c <= 0x25E6)
- return true;
-
- if (c == 0x25EF)
- return true;
-
- if (c >= 0x2600 && c <= 0x2603)
- return true;
-
- if ((c == 0x2605) || (c == 0x2606) || (c == 0x260E) || (c == 0x2616) || (c == 0x2617) || (c == 0x2640) || (c == 0x2642))
- return true;
-
- if (c >= 0x2660 && c <= 0x266F)
- return true;
-
- if (c >= 0x2672 && c <= 0x267D)
- return true;
-
- if ((c == 0x26A0) || (c == 0x26BD) || (c == 0x26BE) || (c == 0x2713) || (c == 0x271A) || (c == 0x273F) || (c == 0x2740) || (c == 0x2756))
- return true;
-
- if (c >= 0x2776 && c <= 0x277F)
- return true;
-
- if (c == 0x2B1A)
- return true;
-
- // Ideographic Description Characters.
- if (c >= 0x2FF0 && c <= 0x2FFF)
- return true;
-
- // CJK Symbols and Punctuation, excluding 0x3030.
- if (c >= 0x3000 && c < 0x3030)
- return true;
-
- if (c > 0x3030 && c <= 0x303F)
- return true;
-
- // Hiragana
- if (c >= 0x3040 && c <= 0x309F)
- return true;
+ CharacterFallbackMapKey(const AtomicString& locale, UChar32 character, bool isForPlatformFont)
+ : locale(locale)
+ , character(character)
+ , isForPlatformFont(isForPlatformFont)
+ {
+ }
- // Katakana
- if (c >= 0x30A0 && c <= 0x30FF)
- return true;
+ CharacterFallbackMapKey(WTF::HashTableDeletedValueType)
+ : character(-1)
+ {
+ }
- // Bopomofo
- if (c >= 0x3100 && c <= 0x312F)
- return true;
+ bool isHashTableDeletedValue() const { return character == -1; }
- if (c >= 0x3190 && c <= 0x319F)
- return true;
+ bool operator==(const CharacterFallbackMapKey& other) const
+ {
+ return locale == other.locale && character == other.character && isForPlatformFont == other.isForPlatformFont;
+ }
- // Bopomofo Extended
- if (c >= 0x31A0 && c <= 0x31BF)
- return true;
-
- // Enclosed CJK Letters and Months.
- if (c >= 0x3200 && c <= 0x32FF)
- return true;
-
- // CJK Compatibility.
- if (c >= 0x3300 && c <= 0x33FF)
- return true;
+ static const bool emptyValueIsZero = true;
- if (c >= 0xF860 && c <= 0xF862)
- return true;
+private:
+ friend struct CharacterFallbackMapKeyHash;
- // CJK Compatibility Forms.
- if (c >= 0xFE30 && c <= 0xFE4F)
- return true;
+ AtomicString locale;
+ UChar32 character { 0 };
+ bool isForPlatformFont { false };
+};
- if ((c == 0xFE10) || (c == 0xFE11) || (c == 0xFE12) || (c == 0xFE19))
- return true;
+struct CharacterFallbackMapKeyHash {
+ static unsigned hash(const CharacterFallbackMapKey& key)
+ {
+ IntegerHasher hasher;
+ hasher.add(key.character);
+ hasher.add(key.isForPlatformFont);
+ hasher.add(key.locale.existingHash());
+ return hasher.hash();
+ }
- if ((c == 0xFF0D) || (c == 0xFF1B) || (c == 0xFF1C) || (c == 0xFF1E))
- return false;
+ static bool equal(const CharacterFallbackMapKey& a, const CharacterFallbackMapKey& b)
+ {
+ return a == b;
+ }
- // Halfwidth and Fullwidth Forms
- // Usually only used in CJK
- if (c >= 0xFF00 && c <= 0xFFEF)
- return true;
+ static const bool safeToCompareToEmptyOrDeleted = true;
+};
- // Emoji.
- if (c == 0x1F100)
- return true;
+// Fonts are not ref'd to avoid cycles.
+// FIXME: Shouldn't these be WeakPtrs?
+typedef HashMap<CharacterFallbackMapKey, Font*, CharacterFallbackMapKeyHash, WTF::SimpleClassHashTraits<CharacterFallbackMapKey>> CharacterFallbackMap;
+typedef HashMap<const Font*, CharacterFallbackMap> SystemFallbackCache;
- if (c >= 0x1F110 && c <= 0x1F129)
- return true;
+static SystemFallbackCache& systemFallbackCache()
+{
+ static NeverDestroyed<SystemFallbackCache> map;
+ return map.get();
+}
- if (c >= 0x1F130 && c <= 0x1F149)
- return true;
+RefPtr<Font> Font::systemFallbackFontForCharacter(UChar32 character, const FontDescription& description, bool isForPlatformFont) const
+{
+ auto fontAddResult = systemFallbackCache().add(this, CharacterFallbackMap());
- if (c >= 0x1F150 && c <= 0x1F169)
- return true;
+ if (!character) {
+ UChar codeUnit = 0;
+ return FontCache::singleton().systemFallbackForCharacters(description, this, isForPlatformFont, &codeUnit, 1);
+ }
- if (c >= 0x1F170 && c <= 0x1F189)
- return true;
+ auto key = CharacterFallbackMapKey(description.locale(), character, isForPlatformFont);
+ auto characterAddResult = fontAddResult.iterator->value.add(WTFMove(key), nullptr);
+
+ Font*& fallbackFont = characterAddResult.iterator->value;
+
+ if (!fallbackFont) {
+ UChar codeUnits[2];
+ unsigned codeUnitsLength;
+ if (U_IS_BMP(character)) {
+ codeUnits[0] = FontCascade::normalizeSpaces(character);
+ codeUnitsLength = 1;
+ } else {
+ codeUnits[0] = U16_LEAD(character);
+ codeUnits[1] = U16_TRAIL(character);
+ codeUnitsLength = 2;
+ }
- if (c >= 0x1F200 && c <= 0x1F6C5)
- return true;
+ fallbackFont = FontCache::singleton().systemFallbackForCharacters(description, this, isForPlatformFont, codeUnits, codeUnitsLength).get();
+ if (fallbackFont)
+ fallbackFont->m_isUsedInSystemFallbackCache = true;
+ }
- return isCJKIdeograph(c);
+ return fallbackFont;
}
-unsigned Font::expansionOpportunityCount(const LChar* characters, size_t length, TextDirection direction, bool& isAfterExpansion)
+void Font::removeFromSystemFallbackCache()
{
- unsigned count = 0;
- if (direction == LTR) {
- for (size_t i = 0; i < length; ++i) {
- if (treatAsSpace(characters[i])) {
- count++;
- isAfterExpansion = true;
- } else
- isAfterExpansion = false;
- }
- } else {
- for (size_t i = length; i > 0; --i) {
- if (treatAsSpace(characters[i - 1])) {
- count++;
- isAfterExpansion = true;
- } else
- isAfterExpansion = false;
- }
- }
- return count;
-}
+ systemFallbackCache().remove(this);
-unsigned Font::expansionOpportunityCount(const UChar* characters, size_t length, TextDirection direction, bool& isAfterExpansion)
-{
- static bool expandAroundIdeographs = canExpandAroundIdeographsInComplexText();
- unsigned count = 0;
- if (direction == LTR) {
- for (size_t i = 0; i < length; ++i) {
- UChar32 character = characters[i];
- if (treatAsSpace(character)) {
- count++;
- isAfterExpansion = true;
- continue;
- }
- if (U16_IS_LEAD(character) && i + 1 < length && U16_IS_TRAIL(characters[i + 1])) {
- character = U16_GET_SUPPLEMENTARY(character, characters[i + 1]);
- i++;
- }
- if (expandAroundIdeographs && isCJKIdeographOrSymbol(character)) {
- if (!isAfterExpansion)
- count++;
- count++;
- isAfterExpansion = true;
- continue;
- }
- isAfterExpansion = false;
- }
- } else {
- for (size_t i = length; i > 0; --i) {
- UChar32 character = characters[i - 1];
- if (treatAsSpace(character)) {
- count++;
- isAfterExpansion = true;
- continue;
- }
- if (U16_IS_TRAIL(character) && i > 1 && U16_IS_LEAD(characters[i - 2])) {
- character = U16_GET_SUPPLEMENTARY(characters[i - 2], character);
- i--;
- }
- if (expandAroundIdeographs && isCJKIdeographOrSymbol(character)) {
- if (!isAfterExpansion)
- count++;
- count++;
- isAfterExpansion = true;
- continue;
- }
- isAfterExpansion = false;
+ if (!m_isUsedInSystemFallbackCache)
+ return;
+
+ for (auto& characterMap : systemFallbackCache().values()) {
+ Vector<CharacterFallbackMapKey, 512> toRemove;
+ for (auto& entry : characterMap) {
+ if (entry.value == this)
+ toRemove.append(entry.key);
}
+ for (auto& key : toRemove)
+ characterMap.remove(key);
}
- return count;
}
-bool Font::canReceiveTextEmphasis(UChar32 c)
+#if !PLATFORM(COCOA)
+bool Font::variantCapsSupportsCharacterForSynthesis(FontVariantCaps, UChar32) const
{
- if (U_GET_GC_MASK(c) & (U_GC_Z_MASK | U_GC_CN_MASK | U_GC_CC_MASK | U_GC_CF_MASK))
- return false;
-
- // Additional word-separator characters listed in CSS Text Level 3 Editor's Draft 3 November 2010.
- if (c == ethiopicWordspace || c == aegeanWordSeparatorLine || c == aegeanWordSeparatorDot
- || c == ugariticWordDivider || c == tibetanMarkIntersyllabicTsheg || c == tibetanMarkDelimiterTshegBstar)
- return false;
-
- return true;
+ return false;
}
+#endif
-}
+} // namespace WebCore
diff --git a/Source/WebCore/platform/graphics/Font.h b/Source/WebCore/platform/graphics/Font.h
index 35151f127..83a20d5e7 100644
--- a/Source/WebCore/platform/graphics/Font.h
+++ b/Source/WebCore/platform/graphics/Font.h
@@ -1,9 +1,8 @@
/*
- * Copyright (C) 2000 Lars Knoll (knoll@kde.org)
- * (C) 2000 Antti Koivisto (koivisto@kde.org)
- * (C) 2000 Dirk Mueller (mueller@kde.org)
- * Copyright (C) 2003, 2006, 2007, 2010, 2011 Apple Inc. All rights reserved.
- * Copyright (C) 2008 Holger Hans Peter Freyther
+ * This file is part of the internal font implementation.
+ *
+ * Copyright (C) 2006, 2008, 2010, 2015-2016 Apple Inc. All rights reserved.
+ * Copyright (C) 2007-2008 Torch Mobile, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
@@ -25,341 +24,347 @@
#ifndef Font_h
#define Font_h
-#include "DashArray.h"
-#include "FontDescription.h"
-#include "FontGlyphs.h"
-#include "SimpleFontData.h"
-#include "TextDirection.h"
-#include "TypesettingFeatures.h"
-#include <wtf/HashMap.h>
-#include <wtf/HashSet.h>
-#include <wtf/MathExtras.h>
-#include <wtf/unicode/CharacterNames.h>
-
-// "X11/X.h" defines Complex to 0 and conflicts
-// with Complex value in CodePath enum.
-#ifdef Complex
-#undef Complex
+#include "FloatRect.h"
+#include "FontBaseline.h"
+#include "FontMetrics.h"
+#include "FontPlatformData.h"
+#include "GlyphBuffer.h"
+#include "GlyphMetricsMap.h"
+#include "GlyphPage.h"
+#include "OpenTypeMathData.h"
+#if ENABLE(OPENTYPE_VERTICAL)
+#include "OpenTypeVerticalData.h"
+#endif
+#include <wtf/BitVector.h>
+#include <wtf/Optional.h>
+#include <wtf/TypeCasts.h>
+#include <wtf/text/StringHash.h>
+
+#if PLATFORM(COCOA)
+#include "WebCoreSystemInterface.h"
+#include <wtf/RetainPtr.h>
#endif
-namespace WebCore {
+#if PLATFORM(WIN)
+#include <usp10.h>
+#endif
+
+#if USE(CAIRO)
+#include <cairo.h>
+#endif
-class FloatPoint;
-class FloatRect;
-class FontData;
-class FontMetrics;
-class FontPlatformData;
-class FontSelector;
-class GlyphBuffer;
-class GraphicsContext;
-class RenderText;
-class TextLayout;
-class TextRun;
+#if USE(CG)
+#include "CoreGraphicsSPI.h"
+#endif
+
+#if USE(DIRECT2D)
+interface IDWriteFactory;
+interface IDWriteGdiInterop;
+#endif
+namespace WebCore {
+
+class GlyphPage;
+class FontDescription;
+class SharedBuffer;
struct GlyphData;
+struct WidthIterator;
+
+enum FontVariant { AutoVariant, NormalVariant, SmallCapsVariant, EmphasisMarkVariant, BrokenIdeographVariant };
+enum Pitch { UnknownPitch, FixedPitch, VariablePitch };
-struct GlyphOverflow {
- GlyphOverflow()
- : left(0)
- , right(0)
- , top(0)
- , bottom(0)
- , computeBounds(false)
+class Font : public RefCounted<Font> {
+public:
+ // Used to create platform fonts.
+ static Ref<Font> create(const FontPlatformData& platformData, bool isCustomFont = false, bool isLoading = false, bool isTextOrientationFallback = false)
{
+ return adoptRef(*new Font(platformData, isCustomFont, isLoading, isTextOrientationFallback));
}
- int left;
- int right;
- int top;
- int bottom;
- bool computeBounds;
-};
-
+ WEBCORE_EXPORT ~Font();
-class Font {
-public:
- Font();
- Font(const FontDescription&, float letterSpacing, float wordSpacing);
- // This constructor is only used if the platform wants to start with a native font.
- Font(const FontPlatformData&, bool isPrinting, FontSmoothingMode = AutoSmoothing);
+ static const Font* systemFallback() { return reinterpret_cast<const Font*>(-1); }
- // FIXME: We should make this constructor platform-independent.
-#if PLATFORM(IOS)
- Font(const FontPlatformData&, PassRefPtr<FontSelector>);
+ const FontPlatformData& platformData() const { return m_platformData; }
+ const OpenTypeMathData* mathData() const;
+#if ENABLE(OPENTYPE_VERTICAL)
+ const OpenTypeVerticalData* verticalData() const { return m_verticalData.get(); }
#endif
- ~Font();
-
- Font(const Font&);
- Font& operator=(const Font&);
- bool operator==(const Font& other) const;
- bool operator!=(const Font& other) const { return !(*this == other); }
+ const Font* smallCapsFont(const FontDescription&) const;
+ const Font& noSynthesizableFeaturesFont() const;
+ const Font* emphasisMarkFont(const FontDescription&) const;
+ const Font& brokenIdeographFont() const;
+ const Font& nonSyntheticItalicFont() const;
- const FontDescription& fontDescription() const { return m_fontDescription; }
+ const Font* variantFont(const FontDescription& description, FontVariant variant) const
+ {
+#if PLATFORM(COCOA)
+ ASSERT(variant != SmallCapsVariant);
+#endif
+ switch (variant) {
+ case SmallCapsVariant:
+ return smallCapsFont(description);
+ case EmphasisMarkVariant:
+ return emphasisMarkFont(description);
+ case BrokenIdeographVariant:
+ return &brokenIdeographFont();
+ case AutoVariant:
+ case NormalVariant:
+ break;
+ }
+ ASSERT_NOT_REACHED();
+ return const_cast<Font*>(this);
+ }
- int pixelSize() const { return fontDescription().computedPixelSize(); }
- float size() const { return fontDescription().computedSize(); }
+ bool variantCapsSupportsCharacterForSynthesis(FontVariantCaps, UChar32) const;
- void update(PassRefPtr<FontSelector>) const;
+ const Font& verticalRightOrientationFont() const;
+ const Font& uprightOrientationFont() const;
- enum CustomFontNotReadyAction { DoNotPaintIfFontNotReady, UseFallbackIfFontNotReady };
- float drawText(GraphicsContext*, const TextRun&, const FloatPoint&, int from = 0, int to = -1, CustomFontNotReadyAction = DoNotPaintIfFontNotReady) const;
- void drawEmphasisMarks(GraphicsContext*, const TextRun&, const AtomicString& mark, const FloatPoint&, int from = 0, int to = -1) const;
+ bool hasVerticalGlyphs() const { return m_hasVerticalGlyphs; }
+ bool isTextOrientationFallback() const { return m_isTextOrientationFallback; }
- DashArray dashesForIntersectionsWithRect(const TextRun&, const FloatPoint& textOrigin, const FloatRect& lineExtents) const;
+ FontMetrics& fontMetrics() { return m_fontMetrics; }
+ const FontMetrics& fontMetrics() const { return m_fontMetrics; }
+ float sizePerUnit() const { return platformData().size() / (fontMetrics().unitsPerEm() ? fontMetrics().unitsPerEm() : 1); }
- float width(const TextRun&, HashSet<const SimpleFontData*>* fallbackFonts = 0, GlyphOverflow* = 0) const;
- float width(const TextRun&, int& charsConsumed, String& glyphName) const;
+ float maxCharWidth() const { return m_maxCharWidth; }
+ void setMaxCharWidth(float maxCharWidth) { m_maxCharWidth = maxCharWidth; }
- PassOwnPtr<TextLayout> createLayout(RenderText*, float xPos, bool collapseWhiteSpace) const;
- static void deleteLayout(TextLayout*);
- static float width(TextLayout&, unsigned from, unsigned len, HashSet<const SimpleFontData*>* fallbackFonts = 0);
+ float avgCharWidth() const { return m_avgCharWidth; }
+ void setAvgCharWidth(float avgCharWidth) { m_avgCharWidth = avgCharWidth; }
- int offsetForPosition(const TextRun&, float position, bool includePartialGlyphs) const;
- FloatRect selectionRectForText(const TextRun&, const FloatPoint&, int h, int from = 0, int to = -1) const;
+ FloatRect boundsForGlyph(Glyph) const;
+ float widthForGlyph(Glyph) const;
+ FloatRect platformBoundsForGlyph(Glyph) const;
+ float platformWidthForGlyph(Glyph) const;
- bool isSmallCaps() const { return m_fontDescription.smallCaps(); }
+ float spaceWidth() const { return m_spaceWidth; }
+ float adjustedSpaceWidth() const { return m_adjustedSpaceWidth; }
+ void setSpaceWidths(float spaceWidth)
+ {
+ m_spaceWidth = spaceWidth;
+ m_adjustedSpaceWidth = spaceWidth;
+ }
- float wordSpacing() const { return m_wordSpacing; }
- float letterSpacing() const { return m_letterSpacing; }
- void setWordSpacing(float s) { m_wordSpacing = s; }
- void setLetterSpacing(float s) { m_letterSpacing = s; }
- bool isFixedPitch() const;
- bool isPrinterFont() const { return m_fontDescription.usePrinterFont(); }
- bool isSVGFont() const { return primaryFont()->isSVGFont(); }
-
- FontRenderingMode renderingMode() const { return m_fontDescription.renderingMode(); }
+#if USE(CG) || USE(DIRECT2D) || USE(CAIRO)
+ float syntheticBoldOffset() const { return m_syntheticBoldOffset; }
+#endif
- TypesettingFeatures typesettingFeatures() const { return static_cast<TypesettingFeatures>(m_typesettingFeatures); }
+ Glyph spaceGlyph() const { return m_spaceGlyph; }
+ void setSpaceGlyph(Glyph spaceGlyph) { m_spaceGlyph = spaceGlyph; }
+ Glyph zeroWidthSpaceGlyph() const { return m_zeroWidthSpaceGlyph; }
+ void setZeroWidthSpaceGlyph(Glyph spaceGlyph) { m_zeroWidthSpaceGlyph = spaceGlyph; }
+ bool isZeroWidthSpaceGlyph(Glyph glyph) const { return glyph == m_zeroWidthSpaceGlyph && glyph; }
+ Glyph zeroGlyph() const { return m_zeroGlyph; }
+ void setZeroGlyph(Glyph zeroGlyph) { m_zeroGlyph = zeroGlyph; }
- const AtomicString& firstFamily() const { return m_fontDescription.firstFamily(); }
- unsigned familyCount() const { return m_fontDescription.familyCount(); }
- const AtomicString& familyAt(unsigned i) const { return m_fontDescription.familyAt(i); }
+ GlyphData glyphDataForCharacter(UChar32) const;
+ Glyph glyphForCharacter(UChar32) const;
- FontItalic italic() const { return m_fontDescription.italic(); }
- FontWeight weight() const { return m_fontDescription.weight(); }
- FontWidthVariant widthVariant() const { return m_fontDescription.widthVariant(); }
+ RefPtr<Font> systemFallbackFontForCharacter(UChar32, const FontDescription&, bool isForPlatformFont) const;
- bool isPlatformFont() const { return m_glyphs->isForPlatformFont(); }
+ const GlyphPage* glyphPage(unsigned pageNumber) const;
- const FontMetrics& fontMetrics() const { return primaryFont()->fontMetrics(); }
- float spaceWidth() const { return primaryFont()->spaceWidth() + m_letterSpacing; }
- float tabWidth(const SimpleFontData&, unsigned tabSize, float position) const;
- float tabWidth(unsigned tabSize, float position) const { return tabWidth(*primaryFont(), tabSize, position); }
+ void determinePitch();
+ Pitch pitch() const { return m_treatAsFixedPitch ? FixedPitch : VariablePitch; }
- int emphasisMarkAscent(const AtomicString&) const;
- int emphasisMarkDescent(const AtomicString&) const;
- int emphasisMarkHeight(const AtomicString&) const;
+ bool isCustomFont() const { return m_isCustomFont; }
+ bool isLoading() const { return m_isLoading; }
- const SimpleFontData* primaryFont() const;
- const FontData* fontDataAt(unsigned) const;
- GlyphData glyphDataForCharacter(UChar32 c, bool mirror, FontDataVariant variant = AutoVariant) const
- {
- return glyphDataAndPageForCharacter(c, mirror, variant).first;
- }
-#if PLATFORM(MAC)
- const SimpleFontData* fontDataForCombiningCharacterSequence(const UChar*, size_t length, FontDataVariant) const;
+#ifndef NDEBUG
+ String description() const;
#endif
- std::pair<GlyphData, GlyphPage*> glyphDataAndPageForCharacter(UChar32 c, bool mirror, FontDataVariant variant) const
- {
- return m_glyphs->glyphDataAndPageForCharacter(m_fontDescription, c, mirror, variant);
- }
- bool primaryFontHasGlyphForCharacter(UChar32) const;
-
- static bool isCJKIdeograph(UChar32);
- static bool isCJKIdeographOrSymbol(UChar32);
- static unsigned expansionOpportunityCount(const LChar*, size_t length, TextDirection, bool& isAfterExpansion);
- static unsigned expansionOpportunityCount(const UChar*, size_t length, TextDirection, bool& isAfterExpansion);
+#if PLATFORM(IOS)
+ bool shouldNotBeUsedForArabic() const { return m_shouldNotBeUsedForArabic; };
+#endif
+#if PLATFORM(COCOA)
+ CTFontRef getCTFont() const { return m_platformData.font(); }
+ CFDictionaryRef getCFStringAttributes(bool enableKerning, FontOrientation) const;
+ const BitVector& glyphsSupportedBySmallCaps() const;
+ const BitVector& glyphsSupportedByAllSmallCaps() const;
+ const BitVector& glyphsSupportedByPetiteCaps() const;
+ const BitVector& glyphsSupportedByAllPetiteCaps() const;
+#endif
- static void setShouldUseSmoothing(bool);
- static bool shouldUseSmoothing();
+#if PLATFORM(COCOA) || USE(HARFBUZZ)
+ bool canRenderCombiningCharacterSequence(const UChar*, size_t) const;
+#endif
- enum CodePath { Auto, Simple, Complex, SimpleWithGlyphOverflow };
- CodePath codePath(const TextRun&) const;
- static CodePath characterRangeCodePath(const LChar*, unsigned) { return Simple; }
- static CodePath characterRangeCodePath(const UChar*, unsigned len);
+ bool applyTransforms(GlyphBufferGlyph*, GlyphBufferAdvance*, size_t glyphCount, bool enableKerning, bool requiresShaping) const;
-private:
- enum ForTextEmphasisOrNot { NotForTextEmphasis, ForTextEmphasis };
-
- // Returns the initial in-stream advance.
- float getGlyphsAndAdvancesForSimpleText(const TextRun&, int from, int to, GlyphBuffer&, ForTextEmphasisOrNot = NotForTextEmphasis) const;
- float drawSimpleText(GraphicsContext*, const TextRun&, const FloatPoint&, int from, int to) const;
- void drawEmphasisMarksForSimpleText(GraphicsContext*, const TextRun&, const AtomicString& mark, const FloatPoint&, int from, int to) const;
- void drawGlyphs(GraphicsContext*, const SimpleFontData*, const GlyphBuffer&, int from, int to, const FloatPoint&) const;
- void drawGlyphBuffer(GraphicsContext*, const TextRun&, const GlyphBuffer&, FloatPoint&) const;
- void drawEmphasisMarks(GraphicsContext*, const TextRun&, const GlyphBuffer&, const AtomicString&, const FloatPoint&) const;
- float floatWidthForSimpleText(const TextRun&, HashSet<const SimpleFontData*>* fallbackFonts = 0, GlyphOverflow* = 0) const;
- int offsetForPositionForSimpleText(const TextRun&, float position, bool includePartialGlyphs) const;
- FloatRect selectionRectForSimpleText(const TextRun&, const FloatPoint&, int h, int from, int to) const;
-
- bool getEmphasisMarkGlyphData(const AtomicString&, GlyphData&) const;
-
- static bool canReturnFallbackFontsForComplexText();
- static bool canExpandAroundIdeographsInComplexText();
-
- // Returns the initial in-stream advance.
- float getGlyphsAndAdvancesForComplexText(const TextRun&, int from, int to, GlyphBuffer&, ForTextEmphasisOrNot = NotForTextEmphasis) const;
- float drawComplexText(GraphicsContext*, const TextRun&, const FloatPoint&, int from, int to) const;
- void drawEmphasisMarksForComplexText(GraphicsContext*, const TextRun&, const AtomicString& mark, const FloatPoint&, int from, int to) const;
- float floatWidthForComplexText(const TextRun&, HashSet<const SimpleFontData*>* fallbackFonts = 0, GlyphOverflow* = 0) const;
- int offsetForPositionForComplexText(const TextRun&, float position, bool includePartialGlyphs) const;
- FloatRect selectionRectForComplexText(const TextRun&, const FloatPoint&, int h, int from, int to) const;
-
- friend struct WidthIterator;
- friend class SVGTextRunRenderingContext;
+#if PLATFORM(WIN)
+ SCRIPT_FONTPROPERTIES* scriptFontProperties() const;
+ SCRIPT_CACHE* scriptCache() const { return &m_scriptCache; }
+ static void setShouldApplyMacAscentHack(bool);
+ static bool shouldApplyMacAscentHack();
+ static float ascentConsideringMacAscentHack(const WCHAR*, float ascent, float descent);
+#endif
-public:
-#if ENABLE(IOS_TEXT_AUTOSIZING)
- bool equalForTextAutoSizing(const Font& other) const
- {
- return m_fontDescription.equalForTextAutoSizing(other.m_fontDescription)
- && m_letterSpacing == other.m_letterSpacing
- && m_wordSpacing == other.m_wordSpacing;
- }
+#if USE(DIRECT2D)
+ WEBCORE_EXPORT static IDWriteFactory* systemDWriteFactory();
+ WEBCORE_EXPORT static IDWriteGdiInterop* systemDWriteGdiInterop();
#endif
- // Useful for debugging the different font rendering code paths.
- static void setCodePath(CodePath);
- static CodePath codePath();
- static CodePath s_codePath;
+private:
+ Font(const FontPlatformData&, bool isCustomFont = false, bool isLoading = false, bool isTextOrientationFallback = false);
- static void setDefaultTypesettingFeatures(TypesettingFeatures);
- static TypesettingFeatures defaultTypesettingFeatures();
+ void platformInit();
+ void platformGlyphInit();
+ void platformCharWidthInit();
+ void platformDestroy();
- static const uint8_t s_roundingHackCharacterTable[256];
- static bool isRoundingHackCharacter(UChar32 c)
- {
- return !(c & ~0xFF) && s_roundingHackCharacterTable[c];
- }
+ void initCharWidths();
- FontSelector* fontSelector() const;
- static bool treatAsSpace(UChar c) { return c == ' ' || c == '\t' || c == '\n' || c == noBreakSpace; }
- static bool treatAsZeroWidthSpace(UChar c) { return treatAsZeroWidthSpaceInComplexScript(c) || c == 0x200c || c == 0x200d; }
- static bool treatAsZeroWidthSpaceInComplexScript(UChar c) { return c < 0x20 || (c >= 0x7F && c < 0xA0) || c == softHyphen || c == zeroWidthSpace || (c >= 0x200e && c <= 0x200f) || (c >= 0x202a && c <= 0x202e) || c == zeroWidthNoBreakSpace || c == objectReplacementCharacter; }
- static bool canReceiveTextEmphasis(UChar32 c);
+ RefPtr<Font> createFontWithoutSynthesizableFeatures() const;
+ RefPtr<Font> createScaledFont(const FontDescription&, float scaleFactor) const;
+ RefPtr<Font> platformCreateScaledFont(const FontDescription&, float scaleFactor) const;
- static inline UChar normalizeSpaces(UChar character)
- {
- if (treatAsSpace(character))
- return space;
+ void removeFromSystemFallbackCache();
- if (treatAsZeroWidthSpace(character))
- return zeroWidthSpace;
+#if PLATFORM(WIN)
+ void initGDIFont();
+ void platformCommonDestroy();
+ FloatRect boundsForGDIGlyph(Glyph) const;
+ float widthForGDIGlyph(Glyph) const;
+#endif
- return character;
- }
+ FontMetrics m_fontMetrics;
+ float m_maxCharWidth;
+ float m_avgCharWidth;
- static String normalizeSpaces(const LChar*, unsigned length);
- static String normalizeSpaces(const UChar*, unsigned length);
+ const FontPlatformData m_platformData;
- bool useBackslashAsYenSymbol() const { return m_useBackslashAsYenSymbol; }
- FontGlyphs* glyphs() const { return m_glyphs.get(); }
+ mutable RefPtr<GlyphPage> m_glyphPageZero;
+ mutable HashMap<unsigned, RefPtr<GlyphPage>> m_glyphPages;
+ mutable std::unique_ptr<GlyphMetricsMap<FloatRect>> m_glyphToBoundsMap;
+ mutable GlyphMetricsMap<float> m_glyphToWidthMap;
-private:
- bool loadingCustomFonts() const
- {
- return m_glyphs && m_glyphs->loadingCustomFonts();
- }
+ mutable RefPtr<OpenTypeMathData> m_mathData;
+#if ENABLE(OPENTYPE_VERTICAL)
+ RefPtr<OpenTypeVerticalData> m_verticalData;
+#endif
- TypesettingFeatures computeTypesettingFeatures() const
- {
- TextRenderingMode textRenderingMode = m_fontDescription.textRenderingMode();
- TypesettingFeatures features = s_defaultTypesettingFeatures;
+ Glyph m_spaceGlyph { 0 };
+ float m_spaceWidth { 0 };
+ Glyph m_zeroGlyph { 0 };
+ float m_adjustedSpaceWidth { 0 };
- switch (textRenderingMode) {
- case AutoTextRendering:
- break;
- case OptimizeSpeed:
- features &= ~(Kerning | Ligatures);
- break;
- case GeometricPrecision:
- case OptimizeLegibility:
- features |= Kerning | Ligatures;
- break;
- }
+ Glyph m_zeroWidthSpaceGlyph { 0 };
- switch (m_fontDescription.kerning()) {
- case FontDescription::NoneKerning:
- features &= ~Kerning;
- break;
- case FontDescription::NormalKerning:
- features |= Kerning;
- break;
- case FontDescription::AutoKerning:
- break;
+ struct DerivedFonts {
+#if !COMPILER(MSVC)
+ WTF_MAKE_FAST_ALLOCATED;
+#endif
+ public:
+ explicit DerivedFonts(bool custom)
+ : forCustomFont(custom)
+ {
}
+ ~DerivedFonts();
+
+ bool forCustomFont;
+ RefPtr<Font> smallCaps;
+ RefPtr<Font> noSynthesizableFeatures;
+ RefPtr<Font> emphasisMark;
+ RefPtr<Font> brokenIdeograph;
+ RefPtr<Font> verticalRightOrientation;
+ RefPtr<Font> uprightOrientation;
+ RefPtr<Font> nonSyntheticItalic;
+ };
+
+ mutable std::unique_ptr<DerivedFonts> m_derivedFontData;
+
+#if USE(CG) || USE(DIRECT2D) || USE(CAIRO)
+ float m_syntheticBoldOffset;
+#endif
- switch (m_fontDescription.commonLigaturesState()) {
- case FontDescription::DisabledLigaturesState:
- features &= ~Ligatures;
- break;
- case FontDescription::EnabledLigaturesState:
- features |= Ligatures;
- break;
- case FontDescription::NormalLigaturesState:
- break;
- }
+#if PLATFORM(COCOA)
+ mutable RetainPtr<CFDictionaryRef> m_nonKernedCFStringAttributes;
+ mutable RetainPtr<CFDictionaryRef> m_kernedCFStringAttributes;
+ mutable std::optional<BitVector> m_glyphsSupportedBySmallCaps;
+ mutable std::optional<BitVector> m_glyphsSupportedByAllSmallCaps;
+ mutable std::optional<BitVector> m_glyphsSupportedByPetiteCaps;
+ mutable std::optional<BitVector> m_glyphsSupportedByAllPetiteCaps;
+#endif
- return features;
- }
+#if PLATFORM(COCOA) || USE(HARFBUZZ)
+ mutable std::unique_ptr<HashMap<String, bool>> m_combiningCharacterSequenceSupport;
+#endif
- static TypesettingFeatures s_defaultTypesettingFeatures;
+#if PLATFORM(WIN)
+ mutable SCRIPT_CACHE m_scriptCache;
+ mutable SCRIPT_FONTPROPERTIES* m_scriptFontProperties;
+#endif
- FontDescription m_fontDescription;
- mutable RefPtr<FontGlyphs> m_glyphs;
- float m_letterSpacing;
- float m_wordSpacing;
- mutable bool m_useBackslashAsYenSymbol;
- mutable unsigned m_typesettingFeatures : 2; // (TypesettingFeatures) Caches values computed from m_fontDescription.
-};
+ unsigned m_treatAsFixedPitch : 1;
+ unsigned m_isCustomFont : 1; // Whether or not we are custom font loaded via @font-face
+ unsigned m_isLoading : 1; // Whether or not this custom font is still in the act of loading.
-void invalidateFontGlyphsCache();
-void pruneUnreferencedEntriesFromFontGlyphsCache();
-void clearWidthCaches();
+ unsigned m_isTextOrientationFallback : 1;
+ unsigned m_isBrokenIdeographFallback : 1;
+ unsigned m_hasVerticalGlyphs : 1;
-inline Font::~Font()
-{
-}
+ unsigned m_isUsedInSystemFallbackCache : 1;
-inline const SimpleFontData* Font::primaryFont() const
-{
- ASSERT(m_glyphs);
- return m_glyphs->primarySimpleFontData(m_fontDescription);
-}
+#if PLATFORM(IOS)
+ unsigned m_shouldNotBeUsedForArabic : 1;
+#endif
+};
-inline const FontData* Font::fontDataAt(unsigned index) const
-{
- ASSERT(m_glyphs);
- return m_glyphs->realizeFontDataAt(m_fontDescription, index);
-}
+#if PLATFORM(IOS)
+bool fontFamilyShouldNotBeUsedForArabic(CFStringRef);
+#endif
-inline bool Font::isFixedPitch() const
+ALWAYS_INLINE FloatRect Font::boundsForGlyph(Glyph glyph) const
{
- ASSERT(m_glyphs);
- return m_glyphs->isFixedPitch(m_fontDescription);
-}
+ if (isZeroWidthSpaceGlyph(glyph))
+ return FloatRect();
+
+ FloatRect bounds;
+ if (m_glyphToBoundsMap) {
+ bounds = m_glyphToBoundsMap->metricsForGlyph(glyph);
+ if (bounds.width() != cGlyphSizeUnknown)
+ return bounds;
+ }
-inline FontSelector* Font::fontSelector() const
-{
- return m_glyphs ? m_glyphs->fontSelector() : 0;
+ bounds = platformBoundsForGlyph(glyph);
+ if (!m_glyphToBoundsMap)
+ m_glyphToBoundsMap = std::make_unique<GlyphMetricsMap<FloatRect>>();
+ m_glyphToBoundsMap->setMetricsForGlyph(glyph, bounds);
+ return bounds;
}
-inline float Font::tabWidth(const SimpleFontData& fontData, unsigned tabSize, float position) const
+ALWAYS_INLINE float Font::widthForGlyph(Glyph glyph) const
{
- if (!tabSize)
- return letterSpacing();
- float tabWidth = tabSize * fontData.spaceWidth() + letterSpacing();
- return tabWidth - fmodf(position, tabWidth);
-}
+ // The optimization of returning 0 for the zero-width-space glyph is incorrect for the LastResort font,
+ // used in place of the actual font when isLoading() is true on both macOS and iOS.
+ // The zero-width-space glyph in that font does not have a width of zero and, further, that glyph is used
+ // for many other characters and must not be zero width when used for them.
+ if (isZeroWidthSpaceGlyph(glyph) && !isLoading())
+ return 0;
+
+ float width = m_glyphToWidthMap.metricsForGlyph(glyph);
+ if (width != cGlyphSizeUnknown)
+ return width;
+
+#if ENABLE(OPENTYPE_VERTICAL)
+ if (m_verticalData) {
+#if USE(CG) || USE(DIRECT2D) || USE(CAIRO)
+ width = m_verticalData->advanceHeight(this, glyph) + m_syntheticBoldOffset;
+#else
+ width = m_verticalData->advanceHeight(this, glyph);
+#endif
+ } else
+#endif
+ width = platformWidthForGlyph(glyph);
+ m_glyphToWidthMap.setMetricsForGlyph(glyph, width);
+ return width;
}
-namespace WTF {
-
-template <> void deleteOwnedPtr<WebCore::TextLayout>(WebCore::TextLayout*);
+} // namespace WebCore
-}
-
-#endif
+#endif // Font_h
diff --git a/Source/WebCore/platform/graphics/FontBaseline.h b/Source/WebCore/platform/graphics/FontBaseline.h
index f7d256d58..586baafb2 100644
--- a/Source/WebCore/platform/graphics/FontBaseline.h
+++ b/Source/WebCore/platform/graphics/FontBaseline.h
@@ -10,10 +10,10 @@
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
- * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
diff --git a/Source/WebCore/platform/graphics/FontCache.cpp b/Source/WebCore/platform/graphics/FontCache.cpp
index df26e68db..c1c6069f0 100644
--- a/Source/WebCore/platform/graphics/FontCache.cpp
+++ b/Source/WebCore/platform/graphics/FontCache.cpp
@@ -11,7 +11,7 @@
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
- * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ * 3. Neither the name of Apple Inc. ("Apple") nor the names of
* its contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
@@ -30,13 +30,13 @@
#include "config.h"
#include "FontCache.h"
-#include "Font.h"
-#include "FontGlyphs.h"
+#include "FontCascade.h"
#include "FontPlatformData.h"
#include "FontSelector.h"
+#include "MemoryPressureHandler.h"
#include "WebKitFontFamilyNames.h"
#include <wtf/HashMap.h>
-#include <wtf/ListHashSet.h>
+#include <wtf/NeverDestroyed.h>
#include <wtf/StdLibExtras.h>
#include <wtf/text/AtomicStringHash.h>
#include <wtf/text/StringHash.h>
@@ -45,19 +45,22 @@
#include "OpenTypeVerticalData.h"
#endif
+#if USE(DIRECT2D)
+#include <dwrite.h>
+#endif
+
#if PLATFORM(IOS)
-#include "MemoryPressureHandler.h"
#include <wtf/Noncopyable.h>
// FIXME: We may be able to simplify this code using C++11 threading primitives, including std::call_once().
-static pthread_mutex_t fontDataLock;
+static pthread_mutex_t fontLock;
static void initFontCacheLockOnce()
{
pthread_mutexattr_t mutexAttribute;
pthread_mutexattr_init(&mutexAttribute);
pthread_mutexattr_settype(&mutexAttribute, PTHREAD_MUTEX_RECURSIVE);
- pthread_mutex_init(&fontDataLock, &mutexAttribute);
+ pthread_mutex_init(&fontLock, &mutexAttribute);
pthread_mutexattr_destroy(&mutexAttribute);
}
@@ -69,13 +72,13 @@ public:
FontLocker()
{
pthread_once(&initFontLockControl, initFontCacheLockOnce);
- int lockcode = pthread_mutex_lock(&fontDataLock);
- ASSERT_WITH_MESSAGE_UNUSED(lockcode, !lockcode, "fontDataLock lock failed with code:%d", lockcode);
+ int lockcode = pthread_mutex_lock(&fontLock);
+ ASSERT_WITH_MESSAGE_UNUSED(lockcode, !lockcode, "fontLock lock failed with code:%d", lockcode);
}
~FontLocker()
{
- int lockcode = pthread_mutex_unlock(&fontDataLock);
- ASSERT_WITH_MESSAGE_UNUSED(lockcode, !lockcode, "fontDataLock unlock failed with code:%d", lockcode);
+ int lockcode = pthread_mutex_unlock(&fontLock);
+ ASSERT_WITH_MESSAGE_UNUSED(lockcode, !lockcode, "fontLock unlock failed with code:%d", lockcode);
}
};
#endif // PLATFORM(IOS)
@@ -84,15 +87,14 @@ using namespace WTF;
namespace WebCore {
-// FIXME: We should return a reference instead of a pointer since we never return a nullptr.
-FontCache* fontCache()
+FontCache& FontCache::singleton()
{
- DEFINE_STATIC_LOCAL(FontCache, globalFontCache, ());
- return &globalFontCache;
+ static NeverDestroyed<FontCache> globalFontCache;
+ return globalFontCache;
}
FontCache::FontCache()
- : m_purgePreventCount(0)
+ : m_purgeTimer(*this, &FontCache::purgeInactiveFontDataIfNeeded)
{
}
@@ -100,35 +102,47 @@ struct FontPlatformDataCacheKey {
WTF_MAKE_FAST_ALLOCATED;
public:
FontPlatformDataCacheKey() { }
- FontPlatformDataCacheKey(const AtomicString& family, const FontDescription& description)
+ FontPlatformDataCacheKey(const AtomicString& family, const FontDescription& description, const FontFeatureSettings* fontFaceFeatures, const FontVariantSettings* fontFaceVariantSettings)
: m_fontDescriptionKey(description)
, m_family(family)
+ , m_fontFaceFeatures(fontFaceFeatures ? *fontFaceFeatures : FontFeatureSettings())
+ , m_fontFaceVariantSettings(fontFaceVariantSettings ? *fontFaceVariantSettings : FontVariantSettings())
{ }
- FontPlatformDataCacheKey(HashTableDeletedValueType) : m_fontDescriptionKey(hashTableDeletedSize()) { }
- bool isHashTableDeletedValue() const { return m_fontDescriptionKey.size == hashTableDeletedSize(); }
+ explicit FontPlatformDataCacheKey(HashTableDeletedValueType t)
+ : m_fontDescriptionKey(t)
+ { }
+
+ bool isHashTableDeletedValue() const { return m_fontDescriptionKey.isHashTableDeletedValue(); }
bool operator==(const FontPlatformDataCacheKey& other) const
{
- return equalIgnoringCase(m_family, other.m_family) && m_fontDescriptionKey == other.m_fontDescriptionKey;
+ if (m_fontDescriptionKey != other.m_fontDescriptionKey
+ || m_fontFaceFeatures != other.m_fontFaceFeatures
+ || m_fontFaceVariantSettings != other.m_fontFaceVariantSettings)
+ return false;
+ if (m_family.impl() == other.m_family.impl())
+ return true;
+ if (m_family.isNull() || other.m_family.isNull())
+ return false;
+ return ASCIICaseInsensitiveHash::equal(m_family, other.m_family);
}
- FontDescriptionFontDataCacheKey m_fontDescriptionKey;
+ FontDescriptionKey m_fontDescriptionKey;
AtomicString m_family;
-
-private:
- static unsigned hashTableDeletedSize() { return 0xFFFFFFFFU; }
+ FontFeatureSettings m_fontFaceFeatures;
+ FontVariantSettings m_fontFaceVariantSettings;
};
-inline unsigned computeHash(const FontPlatformDataCacheKey& fontKey)
-{
- return pairIntHash(CaseFoldingHash::hash(fontKey.m_family), fontKey.m_fontDescriptionKey.computeHash());
-}
-
struct FontPlatformDataCacheKeyHash {
- static unsigned hash(const FontPlatformDataCacheKey& font)
+ static unsigned hash(const FontPlatformDataCacheKey& fontKey)
{
- return computeHash(font);
+ IntegerHasher hasher;
+ hasher.add(ASCIICaseInsensitiveHash::hash(fontKey.m_family));
+ hasher.add(fontKey.m_fontDescriptionKey.computeHash());
+ hasher.add(fontKey.m_fontFaceFeatures.hash());
+ hasher.add(fontKey.m_fontFaceVariantSettings.uniqueValue());
+ return hasher.hash();
}
static bool equal(const FontPlatformDataCacheKey& a, const FontPlatformDataCacheKey& b)
@@ -139,86 +153,64 @@ struct FontPlatformDataCacheKeyHash {
static const bool safeToCompareToEmptyOrDeleted = true;
};
-struct FontPlatformDataCacheKeyTraits : WTF::SimpleClassHashTraits<FontPlatformDataCacheKey> { };
-
-typedef HashMap<FontPlatformDataCacheKey, OwnPtr<FontPlatformData>, FontPlatformDataCacheKeyHash, FontPlatformDataCacheKeyTraits> FontPlatformDataCache;
-
-static FontPlatformDataCache* gFontPlatformDataCache = 0;
+typedef HashMap<FontPlatformDataCacheKey, std::unique_ptr<FontPlatformData>, FontPlatformDataCacheKeyHash, WTF::SimpleClassHashTraits<FontPlatformDataCacheKey>> FontPlatformDataCache;
-static bool familyNameEqualIgnoringCase(const AtomicString& familyName, const char* reference, unsigned length)
+static FontPlatformDataCache& fontPlatformDataCache()
{
- ASSERT(length > 0);
- ASSERT(familyName.length() == length);
- ASSERT(strlen(reference) == length);
- const AtomicStringImpl* familyNameImpl = familyName.impl();
- if (familyNameImpl->is8Bit())
- return equalIgnoringCase(familyNameImpl->characters8(), reinterpret_cast<const LChar*>(reference), length);
- return equalIgnoringCase(familyNameImpl->characters16(), reinterpret_cast<const LChar*>(reference), length);
+ static NeverDestroyed<FontPlatformDataCache> cache;
+ return cache;
}
-template<size_t length>
-static inline bool familyNameEqualIgnoringCase(const AtomicString& familyName, const char (&reference)[length])
+const AtomicString& FontCache::alternateFamilyName(const AtomicString& familyName)
{
- return familyNameEqualIgnoringCase(familyName, reference, length - 1);
-}
+ static NeverDestroyed<AtomicString> arial("Arial", AtomicString::ConstructFromLiteral);
+ static NeverDestroyed<AtomicString> courier("Courier", AtomicString::ConstructFromLiteral);
+ static NeverDestroyed<AtomicString> courierNew("Courier New", AtomicString::ConstructFromLiteral);
+ static NeverDestroyed<AtomicString> helvetica("Helvetica", AtomicString::ConstructFromLiteral);
+ static NeverDestroyed<AtomicString> times("Times", AtomicString::ConstructFromLiteral);
+ static NeverDestroyed<AtomicString> timesNewRoman("Times New Roman", AtomicString::ConstructFromLiteral);
+
+ const AtomicString& platformSpecificAlternate = platformAlternateFamilyName(familyName);
+ if (!platformSpecificAlternate.isNull())
+ return platformSpecificAlternate;
-static const AtomicString alternateFamilyName(const AtomicString& familyName)
-{
- // Alias Courier and Courier New.
- // Alias Times and Times New Roman.
- // Alias Arial and Helvetica.
switch (familyName.length()) {
case 5:
- if (familyNameEqualIgnoringCase(familyName, "Arial"))
- return AtomicString("Helvetica", AtomicString::ConstructFromLiteral);
- if (familyNameEqualIgnoringCase(familyName, "Times"))
- return AtomicString("Times New Roman", AtomicString::ConstructFromLiteral);
+ if (equalLettersIgnoringASCIICase(familyName, "arial"))
+ return helvetica;
+ if (equalLettersIgnoringASCIICase(familyName, "times"))
+ return timesNewRoman;
break;
case 7:
- if (familyNameEqualIgnoringCase(familyName, "Courier"))
- return AtomicString("Courier New", AtomicString::ConstructFromLiteral);
+ if (equalLettersIgnoringASCIICase(familyName, "courier"))
+ return courierNew;
break;
case 9:
- if (familyNameEqualIgnoringCase(familyName, "Helvetica"))
- return AtomicString("Arial", AtomicString::ConstructFromLiteral);
+ if (equalLettersIgnoringASCIICase(familyName, "helvetica"))
+ return arial;
break;
#if !OS(WINDOWS)
- // On Windows, Courier New (truetype font) is always present and
- // Courier is a bitmap font. So, we don't want to map Courier New to
- // Courier.
+ // On Windows, Courier New is a TrueType font that is always present and
+ // Courier is a bitmap font that we do not support. So, we don't want to map
+ // Courier New to Courier.
+ // FIXME: Not sure why this is harmful on Windows, since the alternative will
+ // only be tried if Courier New is not found.
case 11:
- if (familyNameEqualIgnoringCase(familyName, "Courier New"))
- return AtomicString("Courier", AtomicString::ConstructFromLiteral);
+ if (equalLettersIgnoringASCIICase(familyName, "courier new"))
+ return courier;
break;
-#endif // !OS(WINDOWS)
+#endif
case 15:
- if (familyNameEqualIgnoringCase(familyName, "Times New Roman"))
- return AtomicString("Times", AtomicString::ConstructFromLiteral);
- break;
-#if OS(WINDOWS)
- // On Windows, bitmap fonts are blocked altogether so that we have to
- // alias MS Sans Serif (bitmap font) -> Microsoft Sans Serif (truetype font)
- case 13:
- if (familyNameEqualIgnoringCase(familyName, "MS Sans Serif"))
- return AtomicString("Microsoft Sans Serif", AtomicString::ConstructFromLiteral);
- break;
-
- // Alias MS Serif (bitmap) -> Times New Roman (truetype font). There's no
- // 'Microsoft Sans Serif-equivalent' for Serif.
- case 8:
- if (familyNameEqualIgnoringCase(familyName, "MS Serif"))
- return AtomicString("Times New Roman", AtomicString::ConstructFromLiteral);
+ if (equalLettersIgnoringASCIICase(familyName, "times new roman"))
+ return times;
break;
-#endif // OS(WINDOWS)
-
}
return nullAtom;
}
-FontPlatformData* FontCache::getCachedFontPlatformData(const FontDescription& fontDescription,
- const AtomicString& passedFamilyName,
- bool checkingAlternateName)
+FontPlatformData* FontCache::getCachedFontPlatformData(const FontDescription& fontDescription, const AtomicString& passedFamilyName,
+ const FontFeatureSettings* fontFaceFeatures, const FontVariantSettings* fontFaceVariantSettings, bool checkingAlternateName)
{
#if PLATFORM(IOS)
FontLocker fontLocker;
@@ -234,30 +226,31 @@ FontPlatformData* FontCache::getCachedFontPlatformData(const FontDescription& fo
const AtomicString& familyName = passedFamilyName;
#endif
- if (!gFontPlatformDataCache) {
- gFontPlatformDataCache = new FontPlatformDataCache;
+ static bool initialized;
+ if (!initialized) {
platformInit();
+ initialized = true;
}
- FontPlatformDataCacheKey key(familyName, fontDescription);
+ FontPlatformDataCacheKey key(familyName, fontDescription, fontFaceFeatures, fontFaceVariantSettings);
- FontPlatformDataCache::AddResult result = gFontPlatformDataCache->add(key, nullptr);
- FontPlatformDataCache::iterator it = result.iterator;
- if (result.isNewEntry) {
- it->value = createFontPlatformData(fontDescription, familyName);
+ auto addResult = fontPlatformDataCache().add(key, nullptr);
+ FontPlatformDataCache::iterator it = addResult.iterator;
+ if (addResult.isNewEntry) {
+ it->value = createFontPlatformData(fontDescription, familyName, fontFaceFeatures, fontFaceVariantSettings);
if (!it->value && !checkingAlternateName) {
// We were unable to find a font. We have a small set of fonts that we alias to other names,
// e.g., Arial/Helvetica, Courier/Courier New, etc. Try looking up the font under the aliased name.
- const AtomicString alternateName = alternateFamilyName(familyName);
+ const AtomicString& alternateName = alternateFamilyName(familyName);
if (!alternateName.isNull()) {
- FontPlatformData* fontPlatformDataForAlternateName = getCachedFontPlatformData(fontDescription, alternateName, true);
+ FontPlatformData* fontPlatformDataForAlternateName = getCachedFontPlatformData(fontDescription, alternateName, fontFaceFeatures, fontFaceVariantSettings, true);
// Lookup the key in the hash table again as the previous iterator may have
// been invalidated by the recursive call to getCachedFontPlatformData().
- it = gFontPlatformDataCache->find(key);
- ASSERT(it != gFontPlatformDataCache->end());
+ it = fontPlatformDataCache().find(key);
+ ASSERT(it != fontPlatformDataCache().end());
if (fontPlatformDataForAlternateName)
- it->value = adoptPtr(new FontPlatformData(*fontPlatformDataForAlternateName));
+ it->value = std::make_unique<FontPlatformData>(*fontPlatformDataForAlternateName);
}
}
}
@@ -265,62 +258,6 @@ FontPlatformData* FontCache::getCachedFontPlatformData(const FontDescription& fo
return it->value.get();
}
-#if ENABLE(OPENTYPE_VERTICAL)
-struct FontVerticalDataCacheKeyHash {
- static unsigned hash(const FontCache::FontFileKey& fontFileKey)
- {
- return PtrHash<const FontCache::FontFileKey*>::hash(&fontFileKey);
- }
-
- static bool equal(const FontCache::FontFileKey& a, const FontCache::FontFileKey& b)
- {
- return a == b;
- }
-
- static const bool safeToCompareToEmptyOrDeleted = true;
-};
-
-struct FontVerticalDataCacheKeyTraits : WTF::GenericHashTraits<FontCache::FontFileKey> {
- static const bool emptyValueIsZero = true;
- static const bool needsDestruction = true;
- static const FontCache::FontFileKey& emptyValue()
- {
- DEFINE_STATIC_LOCAL(FontCache::FontFileKey, key, (nullAtom));
- return key;
- }
- static void constructDeletedValue(FontCache::FontFileKey& slot)
- {
- new (NotNull, &slot) FontCache::FontFileKey(HashTableDeletedValue);
- }
- static bool isDeletedValue(const FontCache::FontFileKey& value)
- {
- return value.isHashTableDeletedValue();
- }
-};
-
-typedef HashMap<FontCache::FontFileKey, RefPtr<OpenTypeVerticalData>, FontVerticalDataCacheKeyHash, FontVerticalDataCacheKeyTraits> FontVerticalDataCache;
-
-FontVerticalDataCache& fontVerticalDataCacheInstance()
-{
- DEFINE_STATIC_LOCAL(FontVerticalDataCache, fontVerticalDataCache, ());
- return fontVerticalDataCache;
-}
-
-PassRefPtr<OpenTypeVerticalData> FontCache::getVerticalData(const FontFileKey& key, const FontPlatformData& platformData)
-{
- FontVerticalDataCache& fontVerticalDataCache = fontVerticalDataCacheInstance();
- FontVerticalDataCache::iterator result = fontVerticalDataCache.find(key);
- if (result != fontVerticalDataCache.end())
- return result.get()->value;
-
- RefPtr<OpenTypeVerticalData> verticalData = OpenTypeVerticalData::create(platformData);
- if (!verticalData->isOpenType())
- verticalData.clear();
- fontVerticalDataCache.set(key, verticalData);
- return verticalData;
-}
-#endif
-
struct FontDataCacheKeyHash {
static unsigned hash(const FontPlatformData& platformData)
{
@@ -337,10 +274,9 @@ struct FontDataCacheKeyHash {
struct FontDataCacheKeyTraits : WTF::GenericHashTraits<FontPlatformData> {
static const bool emptyValueIsZero = true;
- static const bool needsDestruction = true;
static const FontPlatformData& emptyValue()
{
- DEFINE_STATIC_LOCAL(FontPlatformData, key, (0.f, false, false));
+ static NeverDestroyed<FontPlatformData> key(0.f, false, false);
return key;
}
static void constructDeletedValue(FontPlatformData& slot)
@@ -353,266 +289,162 @@ struct FontDataCacheKeyTraits : WTF::GenericHashTraits<FontPlatformData> {
}
};
-typedef HashMap<FontPlatformData, std::pair<RefPtr<SimpleFontData>, unsigned>, FontDataCacheKeyHash, FontDataCacheKeyTraits> FontDataCache;
+typedef HashMap<FontPlatformData, RefPtr<Font>, FontDataCacheKeyHash, FontDataCacheKeyTraits> FontDataCache;
-static FontDataCache* gFontDataCache = 0;
+static FontDataCache& cachedFonts()
+{
+ static NeverDestroyed<FontDataCache> cache;
+ return cache;
+}
-#if PLATFORM(IOS)
-const int cMaxInactiveFontData = 120;
-const int cTargetInactiveFontData = 100;
-const int cMaxUnderMemoryPressureInactiveFontData = 50;
-const int cTargetUnderMemoryPressureInactiveFontData = 30;
-#else
-const int cMaxInactiveFontData = 225;
-const int cTargetInactiveFontData = 200;
-#endif
-static ListHashSet<RefPtr<SimpleFontData>>* gInactiveFontData = 0;
+#if ENABLE(OPENTYPE_VERTICAL)
+typedef HashMap<FontPlatformData, RefPtr<OpenTypeVerticalData>, FontDataCacheKeyHash, FontDataCacheKeyTraits> FontVerticalDataCache;
-PassRefPtr<SimpleFontData> FontCache::getCachedFontData(const FontDescription& fontDescription, const AtomicString& family, bool checkingAlternateName, ShouldRetain shouldRetain)
+FontVerticalDataCache& fontVerticalDataCache()
{
- FontPlatformData* platformData = getCachedFontPlatformData(fontDescription, family, checkingAlternateName);
- if (!platformData)
- return 0;
-
- return getCachedFontData(platformData, shouldRetain);
+ static NeverDestroyed<FontVerticalDataCache> fontVerticalDataCache;
+ return fontVerticalDataCache;
}
-PassRefPtr<SimpleFontData> FontCache::getCachedFontData(const FontPlatformData* platformData, ShouldRetain shouldRetain)
+RefPtr<OpenTypeVerticalData> FontCache::verticalData(const FontPlatformData& platformData)
{
- if (!platformData)
- return 0;
-
-#if !ASSERT_DISABLED
- if (shouldRetain == DoNotRetain)
- ASSERT(m_purgePreventCount);
+ auto addResult = fontVerticalDataCache().ensure(platformData, [&platformData] {
+ return OpenTypeVerticalData::create(platformData);
+ });
+ return addResult.iterator->value;
+}
#endif
#if PLATFORM(IOS)
- FontLocker fontLocker;
+const unsigned cMaxInactiveFontData = 120;
+const unsigned cTargetInactiveFontData = 100;
+#else
+const unsigned cMaxInactiveFontData = 225;
+const unsigned cTargetInactiveFontData = 200;
#endif
-
- if (!gFontDataCache) {
- gFontDataCache = new FontDataCache;
- gInactiveFontData = new ListHashSet<RefPtr<SimpleFontData>>;
- }
-
- FontDataCache::iterator result = gFontDataCache->find(*platformData);
- if (result == gFontDataCache->end()) {
- std::pair<RefPtr<SimpleFontData>, unsigned> newValue(SimpleFontData::create(*platformData), shouldRetain == Retain ? 1 : 0);
- gFontDataCache->set(*platformData, newValue);
- if (shouldRetain == DoNotRetain)
- gInactiveFontData->add(newValue.first);
- return newValue.first.release();
- }
- if (!result.get()->value.second) {
- ASSERT(gInactiveFontData->contains(result.get()->value.first));
- gInactiveFontData->remove(result.get()->value.first);
- }
+const unsigned cMaxUnderMemoryPressureInactiveFontData = 50;
+const unsigned cTargetUnderMemoryPressureInactiveFontData = 30;
- if (shouldRetain == Retain)
- result.get()->value.second++;
- else if (!result.get()->value.second) {
- // If shouldRetain is DoNotRetain and count is 0, we want to remove the fontData from
- // gInactiveFontData (above) and re-add here to update LRU position.
- gInactiveFontData->add(result.get()->value.first);
- }
+RefPtr<Font> FontCache::fontForFamily(const FontDescription& fontDescription, const AtomicString& family, const FontFeatureSettings* fontFaceFeatures, const FontVariantSettings* fontFaceVariantSettings, bool checkingAlternateName)
+{
+ if (!m_purgeTimer.isActive())
+ m_purgeTimer.startOneShot(0ms);
- return result.get()->value.first;
-}
+ FontPlatformData* platformData = getCachedFontPlatformData(fontDescription, family, fontFaceFeatures, fontFaceVariantSettings, checkingAlternateName);
+ if (!platformData)
+ return nullptr;
-SimpleFontData* FontCache::getNonRetainedLastResortFallbackFont(const FontDescription& fontDescription)
-{
- return getLastResortFallbackFont(fontDescription, DoNotRetain).leakRef();
+ return fontForPlatformData(*platformData);
}
-void FontCache::releaseFontData(const SimpleFontData* fontData)
+Ref<Font> FontCache::fontForPlatformData(const FontPlatformData& platformData)
{
- ASSERT(gFontDataCache);
- ASSERT(!fontData->isCustomFont());
-
#if PLATFORM(IOS)
FontLocker fontLocker;
#endif
- FontDataCache::iterator it = gFontDataCache->find(fontData->platformData());
- ASSERT(it != gFontDataCache->end());
- if (it == gFontDataCache->end())
- return;
+ auto addResult = cachedFonts().add(platformData, nullptr);
+ if (addResult.isNewEntry)
+ addResult.iterator->value = Font::create(platformData);
- ASSERT(it->value.second);
- if (!--it->value.second)
- gInactiveFontData->add(it->value.first);
-}
-
-void FontCache::purgeInactiveFontDataIfNeeded()
-{
-#if PLATFORM(IOS)
- bool underMemoryPressure = memoryPressureHandler().hasReceivedMemoryPressure();
- int inactiveFontDataLimit = underMemoryPressure ? cMaxUnderMemoryPressureInactiveFontData : cMaxInactiveFontData;
- int targetFontDataLimit = underMemoryPressure ? cTargetUnderMemoryPressureInactiveFontData : cTargetInactiveFontData;
+ ASSERT(addResult.iterator->value->platformData() == platformData);
- if (gInactiveFontData && !m_purgePreventCount && gInactiveFontData->size() > inactiveFontDataLimit)
- purgeInactiveFontData(gInactiveFontData->size() - targetFontDataLimit);
-#else
- if (gInactiveFontData && !m_purgePreventCount && gInactiveFontData->size() > cMaxInactiveFontData)
- purgeInactiveFontData(gInactiveFontData->size() - cTargetInactiveFontData);
-#endif
+ return *addResult.iterator->value;
}
-void FontCache::purgeInactiveFontData(int count)
+void FontCache::purgeInactiveFontDataIfNeeded()
{
- pruneUnreferencedEntriesFromFontGlyphsCache();
+ bool underMemoryPressure = MemoryPressureHandler::singleton().isUnderMemoryPressure();
+ unsigned inactiveFontDataLimit = underMemoryPressure ? cMaxUnderMemoryPressureInactiveFontData : cMaxInactiveFontData;
- if (!gInactiveFontData || m_purgePreventCount)
+ if (cachedFonts().size() < inactiveFontDataLimit)
return;
-
- static bool isPurging; // Guard against reentry when e.g. a deleted FontData releases its small caps FontData.
- if (isPurging)
+ unsigned inactiveCount = inactiveFontCount();
+ if (inactiveCount <= inactiveFontDataLimit)
return;
- isPurging = true;
+ unsigned targetFontDataLimit = underMemoryPressure ? cTargetUnderMemoryPressureInactiveFontData : cTargetInactiveFontData;
+ purgeInactiveFontData(inactiveCount - targetFontDataLimit);
+}
+
+void FontCache::purgeInactiveFontData(unsigned purgeCount)
+{
+ pruneUnreferencedEntriesFromFontCascadeCache();
+ pruneSystemFallbackFonts();
#if PLATFORM(IOS)
FontLocker fontLocker;
#endif
- Vector<RefPtr<SimpleFontData>, 20> fontDataToDelete;
- ListHashSet<RefPtr<SimpleFontData>>::iterator end = gInactiveFontData->end();
- ListHashSet<RefPtr<SimpleFontData>>::iterator it = gInactiveFontData->begin();
- for (int i = 0; i < count && it != end; ++it, ++i) {
- RefPtr<SimpleFontData>& fontData = *it.get();
- gFontDataCache->remove(fontData->platformData());
- // We should not delete SimpleFontData here because deletion can modify gInactiveFontData. See http://trac.webkit.org/changeset/44011
- fontDataToDelete.append(fontData);
- }
-
- if (it == end) {
- // Removed everything
- gInactiveFontData->clear();
- } else {
- for (int i = 0; i < count; ++i)
- gInactiveFontData->remove(gInactiveFontData->begin());
- }
-
- fontDataToDelete.clear();
-
- if (gFontPlatformDataCache) {
- Vector<FontPlatformDataCacheKey> keysToRemove;
- keysToRemove.reserveInitialCapacity(gFontPlatformDataCache->size());
- FontPlatformDataCache::iterator platformDataEnd = gFontPlatformDataCache->end();
- for (FontPlatformDataCache::iterator platformData = gFontPlatformDataCache->begin(); platformData != platformDataEnd; ++platformData) {
- if (platformData->value && !gFontDataCache->contains(*platformData->value))
- keysToRemove.append(platformData->key);
+ while (purgeCount) {
+ Vector<RefPtr<Font>, 20> fontsToDelete;
+ for (auto& font : cachedFonts().values()) {
+ if (!font->hasOneRef())
+ continue;
+ fontsToDelete.append(WTFMove(font));
+ if (!--purgeCount)
+ break;
}
-
- size_t keysToRemoveCount = keysToRemove.size();
- for (size_t i = 0; i < keysToRemoveCount; ++i)
- gFontPlatformDataCache->remove(keysToRemove[i]);
- }
-
+ // Fonts may ref other fonts so we loop until there are no changes.
+ if (fontsToDelete.isEmpty())
+ break;
+ for (auto& font : fontsToDelete) {
+ bool success = cachedFonts().remove(font->platformData());
+ ASSERT_UNUSED(success, success);
#if ENABLE(OPENTYPE_VERTICAL)
- FontVerticalDataCache& fontVerticalDataCache = fontVerticalDataCacheInstance();
- if (!fontVerticalDataCache.isEmpty()) {
- // Mark & sweep unused verticalData
- FontVerticalDataCache::iterator verticalDataEnd = fontVerticalDataCache.end();
- for (FontVerticalDataCache::iterator verticalData = fontVerticalDataCache.begin(); verticalData != verticalDataEnd; ++verticalData) {
- if (verticalData->value)
- verticalData->value->m_inFontCache = false;
- }
- FontDataCache::iterator fontDataEnd = gFontDataCache->end();
- for (FontDataCache::iterator fontData = gFontDataCache->begin(); fontData != fontDataEnd; ++fontData) {
- OpenTypeVerticalData* verticalData = const_cast<OpenTypeVerticalData*>(fontData->value.first->verticalData());
- if (verticalData)
- verticalData->m_inFontCache = true;
- }
- Vector<FontFileKey> keysToRemove;
- keysToRemove.reserveInitialCapacity(fontVerticalDataCache.size());
- for (FontVerticalDataCache::iterator verticalData = fontVerticalDataCache.begin(); verticalData != verticalDataEnd; ++verticalData) {
- if (!verticalData->value || !verticalData->value->m_inFontCache)
- keysToRemove.append(verticalData->key);
- }
- for (size_t i = 0, count = keysToRemove.size(); i < count; ++i)
- fontVerticalDataCache.take(keysToRemove[i]);
- }
+ fontVerticalDataCache().remove(font->platformData());
#endif
+ }
+ };
- isPurging = false;
-}
+ Vector<FontPlatformDataCacheKey> keysToRemove;
+ keysToRemove.reserveInitialCapacity(fontPlatformDataCache().size());
+ for (auto& entry : fontPlatformDataCache()) {
+ if (entry.value && !cachedFonts().contains(*entry.value))
+ keysToRemove.uncheckedAppend(entry.key);
+ }
+ for (auto& key : keysToRemove)
+ fontPlatformDataCache().remove(key);
-size_t FontCache::fontDataCount()
-{
- if (gFontDataCache)
- return gFontDataCache->size();
- return 0;
+ platformPurgeInactiveFontData();
}
-size_t FontCache::inactiveFontDataCount()
+size_t FontCache::fontCount()
{
- if (gInactiveFontData)
- return gInactiveFontData->size();
- return 0;
+ return cachedFonts().size();
}
-PassRefPtr<FontData> FontCache::getFontData(const FontDescription& description, int& familyIndex, FontSelector* fontSelector)
+size_t FontCache::inactiveFontCount()
{
- ASSERT(familyIndex != cAllFamiliesScanned);
- RefPtr<FontData> result;
-
- bool isFirst = !familyIndex;
- int familyCount = description.familyCount();
- for (;familyIndex < familyCount && !result; ++familyIndex) {
- const AtomicString& family = description.familyAt(familyIndex);
- if (family.isEmpty())
- continue;
- if (fontSelector)
- result = fontSelector->getFontData(description, family);
- if (!result)
- result = getCachedFontData(description, family);
- }
- if (familyIndex == familyCount)
- familyIndex = cAllFamiliesScanned;
-
-#if PLATFORM(MAC)
- if (!result) {
- // We didn't find a font. Try to find a similar font using our own specific knowledge about our platform.
- // For example on OS X, we know to map any families containing the words Arabic, Pashto, or Urdu to the
- // Geeza Pro font.
- result = similarFontPlatformData(description);
- }
+#if PLATFORM(IOS)
+ FontLocker fontLocker;
#endif
-
- if (!result && isFirst) {
- // If it's the primary font that we couldn't find, we try the following. In all other cases, we will
- // just use per-character system fallback.
- if (fontSelector) {
- // Try the user's preferred standard font.
- if (RefPtr<FontData> data = fontSelector->getFontData(description, standardFamily))
- return data.release();
- }
- // Still no result. Hand back our last resort fallback font.
- result = getLastResortFallbackFont(description);
+ unsigned count = 0;
+ for (auto& font : cachedFonts().values()) {
+ if (font->hasOneRef())
+ ++count;
}
- return result.release();
+ return count;
}
static HashSet<FontSelector*>* gClients;
-void FontCache::addClient(FontSelector* client)
+void FontCache::addClient(FontSelector& client)
{
if (!gClients)
gClients = new HashSet<FontSelector*>;
- ASSERT(!gClients->contains(client));
- gClients->add(client);
+ ASSERT(!gClients->contains(&client));
+ gClients->add(&client);
}
-void FontCache::removeClient(FontSelector* client)
+void FontCache::removeClient(FontSelector& client)
{
ASSERT(gClients);
- ASSERT(gClients->contains(client));
+ ASSERT(gClients->contains(&client));
- gClients->remove(client);
+ gClients->remove(&client);
}
static unsigned short gGeneration = 0;
@@ -625,13 +457,15 @@ unsigned short FontCache::generation()
void FontCache::invalidate()
{
if (!gClients) {
- ASSERT(!gFontPlatformDataCache);
+ ASSERT(fontPlatformDataCache().isEmpty());
return;
}
- if (gFontPlatformDataCache)
- gFontPlatformDataCache->clear();
- invalidateFontGlyphsCache();
+ fontPlatformDataCache().clear();
+#if ENABLE(OPENTYPE_VERTICAL)
+ fontVerticalDataCache().clear();
+#endif
+ invalidateFontCascadeCache();
gGeneration++;
@@ -646,4 +480,11 @@ void FontCache::invalidate()
purgeInactiveFontData();
}
+#if !PLATFORM(COCOA)
+RefPtr<Font> FontCache::similarFont(const FontDescription&, const AtomicString&)
+{
+ return nullptr;
+}
+#endif
+
} // namespace WebCore
diff --git a/Source/WebCore/platform/graphics/FontCache.h b/Source/WebCore/platform/graphics/FontCache.h
index 83ec1246d..e5a3f19cc 100644
--- a/Source/WebCore/platform/graphics/FontCache.h
+++ b/Source/WebCore/platform/graphics/FontCache.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2006, 2008 Apple Computer, Inc. All rights reserved.
+ * Copyright (C) 2006, 2008 Apple Inc. All rights reserved.
* Copyright (C) 2007-2008 Torch Mobile, Inc.
*
* Redistribution and use in source and binary forms, with or without
@@ -11,7 +11,7 @@
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
- * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ * 3. Neither the name of Apple Inc. ("Apple") nor the names of
* its contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
@@ -27,19 +27,19 @@
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef FontCache_h
-#define FontCache_h
+#pragma once
#include "FontDescription.h"
+#include "Timer.h"
+#include <array>
#include <limits.h>
#include <wtf/Forward.h>
-#include <wtf/PassRefPtr.h>
#include <wtf/RefPtr.h>
#include <wtf/Vector.h>
+#include <wtf/text/AtomicStringHash.h>
#include <wtf/text/WTFString.h>
-#include <wtf/unicode/Unicode.h>
-#if PLATFORM(IOS)
+#if PLATFORM(COCOA)
#include <CoreText/CTFont.h>
#endif
@@ -51,12 +51,11 @@
namespace WebCore {
-class Font;
+class FontCascade;
class FontPlatformData;
-class FontData;
class FontSelector;
class OpenTypeVerticalData;
-class SimpleFontData;
+class Font;
#if PLATFORM(WIN)
#if USE(IMLANG_FONT_LINK2)
@@ -67,63 +66,129 @@ typedef IMLangFontLink IMLangFontLinkType;
#endif
// This key contains the FontDescription fields other than family that matter when fetching FontDatas (platform fonts).
-struct FontDescriptionFontDataCacheKey {
- explicit FontDescriptionFontDataCacheKey(unsigned size = 0)
- : size(size)
- , weight(0)
- , flags(0)
+struct FontDescriptionKey {
+ FontDescriptionKey() = default;
+
+ FontDescriptionKey(const FontDescription& description)
+ : m_size(description.computedPixelSize())
+ , m_weight(description.weight())
+ , m_flags(makeFlagsKey(description))
+ , m_featureSettings(description.featureSettings())
+#if ENABLE(VARIATION_FONTS)
+ , m_variationSettings(description.variationSettings())
+#endif
{ }
- FontDescriptionFontDataCacheKey(const FontDescription& description)
- : size(description.computedPixelSize())
- , weight(description.weight())
- , flags(makeFlagKey(description))
+
+ explicit FontDescriptionKey(WTF::HashTableDeletedValueType)
+ : m_size(cHashTableDeletedSize)
{ }
- static unsigned makeFlagKey(const FontDescription& description)
- {
- return static_cast<unsigned>(description.widthVariant()) << 4
- | static_cast<unsigned>(description.orientation()) << 3
- | static_cast<unsigned>(description.italic()) << 2
- | static_cast<unsigned>(description.usePrinterFont()) << 1
- | static_cast<unsigned>(description.renderingMode());
- }
- bool operator==(const FontDescriptionFontDataCacheKey& other) const
+
+ bool operator==(const FontDescriptionKey& other) const
{
- return size == other.size && weight == other.weight && flags == other.flags;
+ return m_size == other.m_size
+ && m_weight == other.m_weight
+ && m_flags == other.m_flags
+#if ENABLE(VARIATION_FONTS)
+ && m_variationSettings == other.m_variationSettings
+#endif
+ && m_featureSettings == other.m_featureSettings;
}
- bool operator!=(const FontDescriptionFontDataCacheKey& other) const
+
+ bool operator!=(const FontDescriptionKey& other) const
{
return !(*this == other);
}
+
+ bool isHashTableDeletedValue() const { return m_size == cHashTableDeletedSize; }
+
inline unsigned computeHash() const
{
- return StringHasher::hashMemory<sizeof(FontDescriptionFontDataCacheKey)>(this);
+ IntegerHasher hasher;
+ hasher.add(m_size);
+ hasher.add(m_weight);
+ for (unsigned flagItem : m_flags)
+ hasher.add(flagItem);
+ hasher.add(m_featureSettings.hash());
+#if ENABLE(VARIATION_FONTS)
+ hasher.add(m_variationSettings.hash());
+#endif
+ return hasher.hash();
+ }
+
+private:
+ static std::array<unsigned, 2> makeFlagsKey(const FontDescription& description)
+ {
+ static_assert(USCRIPT_CODE_LIMIT < 0x1000, "Script code must fit in an unsigned along with the other flags");
+ unsigned first = static_cast<unsigned>(description.script()) << 11
+ | static_cast<unsigned>(description.textRenderingMode()) << 9
+ | static_cast<unsigned>(description.fontSynthesis()) << 6
+ | static_cast<unsigned>(description.widthVariant()) << 4
+ | static_cast<unsigned>(description.nonCJKGlyphOrientation()) << 3
+ | static_cast<unsigned>(description.orientation()) << 2
+ | static_cast<unsigned>(description.italic()) << 1
+ | static_cast<unsigned>(description.renderingMode());
+ unsigned second = static_cast<unsigned>(description.variantEastAsianRuby()) << 27
+ | static_cast<unsigned>(description.variantEastAsianWidth()) << 25
+ | static_cast<unsigned>(description.variantEastAsianVariant()) << 22
+ | static_cast<unsigned>(description.variantAlternates()) << 21
+ | static_cast<unsigned>(description.variantNumericSlashedZero()) << 20
+ | static_cast<unsigned>(description.variantNumericOrdinal()) << 19
+ | static_cast<unsigned>(description.variantNumericFraction()) << 17
+ | static_cast<unsigned>(description.variantNumericSpacing()) << 15
+ | static_cast<unsigned>(description.variantNumericFigure()) << 13
+ | static_cast<unsigned>(description.variantCaps()) << 10
+ | static_cast<unsigned>(description.variantPosition()) << 8
+ | static_cast<unsigned>(description.variantContextualAlternates()) << 6
+ | static_cast<unsigned>(description.variantHistoricalLigatures()) << 4
+ | static_cast<unsigned>(description.variantDiscretionaryLigatures()) << 2
+ | static_cast<unsigned>(description.variantCommonLigatures());
+ return {{ first, second }};
}
- unsigned size;
- unsigned weight;
- unsigned flags;
+
+ static const unsigned cHashTableDeletedSize = 0xFFFFFFFFU;
+
+ // FontCascade::locale() is explicitly not included in this struct.
+ unsigned m_size { 0 };
+ unsigned m_weight { 0 };
+ std::array<unsigned, 2> m_flags {{ 0, 0 }};
+ FontFeatureSettings m_featureSettings;
+#if ENABLE(VARIATION_FONTS)
+ FontVariationSettings m_variationSettings;
+#endif
};
-class FontCache {
- friend class FontCachePurgePreventer;
+struct FontDescriptionKeyHash {
+ static unsigned hash(const FontDescriptionKey& key)
+ {
+ return key.computeHash();
+ }
- WTF_MAKE_NONCOPYABLE(FontCache); WTF_MAKE_FAST_ALLOCATED;
-public:
- friend FontCache* fontCache();
+ static bool equal(const FontDescriptionKey& a, const FontDescriptionKey& b)
+ {
+ return a == b;
+ }
- enum ShouldRetain { Retain, DoNotRetain };
+ static const bool safeToCompareToEmptyOrDeleted = true;
+};
- PassRefPtr<FontData> getFontData(const FontDescription&, int& familyIndex, FontSelector*);
- void releaseFontData(const SimpleFontData*);
+class FontCache {
+ friend class WTF::NeverDestroyed<FontCache>;
- // This method is implemented by the platform.
- PassRefPtr<SimpleFontData> systemFallbackForCharacters(const FontDescription&, const SimpleFontData* originalFontData, bool isPlatformFont, const UChar* characters, int length);
+ WTF_MAKE_NONCOPYABLE(FontCache); WTF_MAKE_FAST_ALLOCATED;
+public:
+ WEBCORE_EXPORT static FontCache& singleton();
- // Also implemented by the platform.
+ // These methods are implemented by the platform.
+ RefPtr<Font> systemFallbackForCharacters(const FontDescription&, const Font* originalFontData, bool isPlatformFont, const UChar* characters, unsigned length);
+ Vector<String> systemFontFamilies();
void platformInit();
#if PLATFORM(IOS)
static float weightOfCTFont(CTFontRef);
#endif
+#if PLATFORM(COCOA)
+ WEBCORE_EXPORT static void setFontWhitelist(const Vector<String>&);
+#endif
#if PLATFORM(WIN)
IMLangFontLinkType* getFontLinkInterface();
static void comInitialize();
@@ -131,89 +196,95 @@ public:
static IMultiLanguage* getMultiLanguageInterface();
#endif
- void getTraitsInFamily(const AtomicString&, Vector<unsigned>&);
+ // This function exists so CSSFontSelector can have a unified notion of preinstalled fonts and @font-face.
+ // It comes into play when you create an @font-face which shares a family name as a preinstalled font.
+ Vector<FontTraitsMask> getTraitsInFamily(const AtomicString&);
- PassRefPtr<SimpleFontData> getCachedFontData(const FontDescription&, const AtomicString&, bool checkingAlternateName = false, ShouldRetain = Retain);
- PassRefPtr<SimpleFontData> getLastResortFallbackFont(const FontDescription&, ShouldRetain = Retain);
- SimpleFontData* getNonRetainedLastResortFallbackFont(const FontDescription&);
+ WEBCORE_EXPORT RefPtr<Font> fontForFamily(const FontDescription&, const AtomicString&, const FontFeatureSettings* fontFaceFeatures = nullptr, const FontVariantSettings* fontFaceVariantSettings = nullptr, bool checkingAlternateName = false);
+ WEBCORE_EXPORT Ref<Font> lastResortFallbackFont(const FontDescription&);
+ Ref<Font> lastResortFallbackFontForEveryCharacter(const FontDescription&);
+ WEBCORE_EXPORT Ref<Font> fontForPlatformData(const FontPlatformData&);
+ RefPtr<Font> similarFont(const FontDescription&, const AtomicString& family);
- void addClient(FontSelector*);
- void removeClient(FontSelector*);
+ void addClient(FontSelector&);
+ void removeClient(FontSelector&);
unsigned short generation();
- void invalidate();
+ WEBCORE_EXPORT void invalidate();
- size_t fontDataCount();
- size_t inactiveFontDataCount();
- void purgeInactiveFontData(int count = INT_MAX);
+ WEBCORE_EXPORT size_t fontCount();
+ WEBCORE_EXPORT size_t inactiveFontCount();
+ WEBCORE_EXPORT void purgeInactiveFontData(unsigned count = UINT_MAX);
+ void platformPurgeInactiveFontData();
#if PLATFORM(WIN)
- PassRefPtr<SimpleFontData> fontDataFromDescriptionAndLogFont(const FontDescription&, ShouldRetain, const LOGFONT&, AtomicString& outFontFamilyName);
+ RefPtr<Font> fontFromDescriptionAndLogFont(const FontDescription&, const LOGFONT&, AtomicString& outFontFamilyName);
#endif
#if ENABLE(OPENTYPE_VERTICAL)
- typedef AtomicString FontFileKey;
- PassRefPtr<OpenTypeVerticalData> getVerticalData(const FontFileKey&, const FontPlatformData&);
+ RefPtr<OpenTypeVerticalData> verticalData(const FontPlatformData&);
#endif
- struct SimpleFontFamily {
- String name;
- bool isBold;
- bool isItalic;
- };
- static void getFontFamilyForCharacters(const UChar* characters, size_t numCharacters, const char* preferredLocale, SimpleFontFamily*);
-
private:
FontCache();
- ~FontCache();
+ ~FontCache() = delete;
- void disablePurging() { m_purgePreventCount++; }
- void enablePurging()
- {
- ASSERT(m_purgePreventCount);
- if (!--m_purgePreventCount)
- purgeInactiveFontDataIfNeeded();
- }
-
- void purgeInactiveFontDataIfNeeded();
+ WEBCORE_EXPORT void purgeInactiveFontDataIfNeeded();
// FIXME: This method should eventually be removed.
- FontPlatformData* getCachedFontPlatformData(const FontDescription&, const AtomicString& family, bool checkingAlternateName = false);
+ FontPlatformData* getCachedFontPlatformData(const FontDescription&, const AtomicString& family, const FontFeatureSettings* fontFaceFeatures = nullptr, const FontVariantSettings* fontFaceVariantSettings = nullptr, bool checkingAlternateName = false);
// These methods are implemented by each platform.
-#if PLATFORM(IOS)
+#if PLATFORM(COCOA)
FontPlatformData* getCustomFallbackFont(const UInt32, const FontDescription&);
- PassRefPtr<SimpleFontData> getSystemFontFallbackForCharacters(const FontDescription&, const SimpleFontData*, const UChar* characters, int length);
#endif
- PassOwnPtr<FontPlatformData> createFontPlatformData(const FontDescription&, const AtomicString& family);
-#if PLATFORM(MAC)
- PassRefPtr<SimpleFontData> similarFontPlatformData(const FontDescription&);
-#endif
-
- PassRefPtr<SimpleFontData> getCachedFontData(const FontPlatformData*, ShouldRetain = Retain);
+ std::unique_ptr<FontPlatformData> createFontPlatformData(const FontDescription&, const AtomicString& family, const FontFeatureSettings* fontFaceFeatures, const FontVariantSettings* fontFaceVariantSettings);
+
+ static const AtomicString& alternateFamilyName(const AtomicString&);
+ static const AtomicString& platformAlternateFamilyName(const AtomicString&);
- // Don't purge if this count is > 0;
- int m_purgePreventCount;
+ Timer m_purgeTimer;
-#if PLATFORM(MAC)
- friend class ComplexTextController;
-#endif
- friend class SimpleFontData; // For getCachedFontData(const FontPlatformData*)
- friend class FontGlyphs;
-#if PLATFORM(IOS)
+#if PLATFORM(COCOA)
friend class ComplexTextController;
#endif
+ friend class Font;
};
-// Get the global fontCache.
-FontCache* fontCache();
+#if PLATFORM(COCOA)
-class FontCachePurgePreventer {
-public:
- FontCachePurgePreventer() { fontCache()->disablePurging(); }
- ~FontCachePurgePreventer() { fontCache()->enablePurging(); }
+struct SynthesisPair {
+ SynthesisPair(bool needsSyntheticBold, bool needsSyntheticOblique)
+ : needsSyntheticBold(needsSyntheticBold)
+ , needsSyntheticOblique(needsSyntheticOblique)
+ {
+ }
+
+ std::pair<bool, bool> boldObliquePair() const
+ {
+ return std::make_pair(needsSyntheticBold, needsSyntheticOblique);
+ }
+
+ bool needsSyntheticBold;
+ bool needsSyntheticOblique;
};
+RetainPtr<CTFontRef> preparePlatformFont(CTFontRef, TextRenderingMode, const FontFeatureSettings* fontFaceFeatures, const FontVariantSettings* fontFaceVariantSettings, const FontFeatureSettings& features, const FontVariantSettings&, const FontVariationSettings&);
+FontWeight fontWeightFromCoreText(CGFloat weight);
+uint16_t toCoreTextFontWeight(FontWeight);
+bool isFontWeightBold(FontWeight);
+void platformInvalidateFontCache();
+SynthesisPair computeNecessarySynthesis(CTFontRef, const FontDescription&, bool isPlatformFont = false);
+RetainPtr<CTFontRef> platformFontWithFamilySpecialCase(const AtomicString& family, FontWeight, CTFontSymbolicTraits, float size);
+RetainPtr<CTFontRef> platformFontWithFamily(const AtomicString& family, CTFontSymbolicTraits, FontWeight, TextRenderingMode, float size);
+bool requiresCustomFallbackFont(UChar32 character);
+
+#else
+
+inline void FontCache::platformPurgeInactiveFontData()
+{
}
#endif
+
+}
diff --git a/Source/WebCore/platform/graphics/FontCascade.cpp b/Source/WebCore/platform/graphics/FontCascade.cpp
new file mode 100644
index 000000000..0fac64331
--- /dev/null
+++ b/Source/WebCore/platform/graphics/FontCascade.cpp
@@ -0,0 +1,1491 @@
+/*
+ * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
+ * (C) 1999 Antti Koivisto (koivisto@kde.org)
+ * (C) 2000 Dirk Mueller (mueller@kde.org)
+ * Copyright (C) 2003, 2006, 2010, 2011 Apple Inc. All rights reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "config.h"
+#include "FontCascade.h"
+
+#include "CharacterProperties.h"
+#include "FloatRect.h"
+#include "FontCache.h"
+#include "GlyphBuffer.h"
+#include "GraphicsContext.h"
+#include "LayoutRect.h"
+#include "SurrogatePairAwareTextIterator.h"
+#include "TextRun.h"
+#include "WidthIterator.h"
+#if USE(CAIRO)
+#include <cairo.h>
+#endif
+#include <wtf/MainThread.h>
+#include <wtf/MathExtras.h>
+#include <wtf/NeverDestroyed.h>
+#include <wtf/text/AtomicStringHash.h>
+#include <wtf/text/StringBuilder.h>
+
+using namespace WTF;
+using namespace Unicode;
+
+namespace WebCore {
+
+static Ref<FontCascadeFonts> retrieveOrAddCachedFonts(const FontCascadeDescription&, RefPtr<FontSelector>&&);
+
+static bool useBackslashAsYenSignForFamily(const AtomicString& family)
+{
+ if (family.isEmpty())
+ return false;
+ static HashSet<AtomicString>* set;
+ if (!set) {
+ set = new HashSet<AtomicString>;
+ set->add("MS PGothic");
+ UChar unicodeNameMSPGothic[] = {0xFF2D, 0xFF33, 0x0020, 0xFF30, 0x30B4, 0x30B7, 0x30C3, 0x30AF};
+ set->add(AtomicString(unicodeNameMSPGothic, WTF_ARRAY_LENGTH(unicodeNameMSPGothic)));
+
+ set->add("MS PMincho");
+ UChar unicodeNameMSPMincho[] = {0xFF2D, 0xFF33, 0x0020, 0xFF30, 0x660E, 0x671D};
+ set->add(AtomicString(unicodeNameMSPMincho, WTF_ARRAY_LENGTH(unicodeNameMSPMincho)));
+
+ set->add("MS Gothic");
+ UChar unicodeNameMSGothic[] = {0xFF2D, 0xFF33, 0x0020, 0x30B4, 0x30B7, 0x30C3, 0x30AF};
+ set->add(AtomicString(unicodeNameMSGothic, WTF_ARRAY_LENGTH(unicodeNameMSGothic)));
+
+ set->add("MS Mincho");
+ UChar unicodeNameMSMincho[] = {0xFF2D, 0xFF33, 0x0020, 0x660E, 0x671D};
+ set->add(AtomicString(unicodeNameMSMincho, WTF_ARRAY_LENGTH(unicodeNameMSMincho)));
+
+ set->add("Meiryo");
+ UChar unicodeNameMeiryo[] = {0x30E1, 0x30A4, 0x30EA, 0x30AA};
+ set->add(AtomicString(unicodeNameMeiryo, WTF_ARRAY_LENGTH(unicodeNameMeiryo)));
+ }
+ return set->contains(family);
+}
+
+FontCascade::CodePath FontCascade::s_codePath = Auto;
+
+// ============================================================================================
+// FontCascade Implementation (Cross-Platform Portion)
+// ============================================================================================
+
+FontCascade::FontCascade()
+ : m_weakPtrFactory(this)
+ , m_letterSpacing(0)
+ , m_wordSpacing(0)
+ , m_useBackslashAsYenSymbol(false)
+ , m_enableKerning(false)
+ , m_requiresShaping(false)
+{
+}
+
+FontCascade::FontCascade(const FontCascadeDescription& fd, float letterSpacing, float wordSpacing)
+ : m_fontDescription(fd)
+ , m_weakPtrFactory(this)
+ , m_letterSpacing(letterSpacing)
+ , m_wordSpacing(wordSpacing)
+ , m_useBackslashAsYenSymbol(useBackslashAsYenSignForFamily(fd.firstFamily()))
+ , m_enableKerning(computeEnableKerning())
+ , m_requiresShaping(computeRequiresShaping())
+{
+}
+
+// FIXME: We should make this constructor platform-independent.
+FontCascade::FontCascade(const FontPlatformData& fontData, FontSmoothingMode fontSmoothingMode)
+ : m_fonts(FontCascadeFonts::createForPlatformFont(fontData))
+ , m_weakPtrFactory(this)
+ , m_letterSpacing(0)
+ , m_wordSpacing(0)
+ , m_useBackslashAsYenSymbol(false)
+ , m_enableKerning(computeEnableKerning())
+ , m_requiresShaping(computeRequiresShaping())
+{
+ m_fontDescription.setFontSmoothing(fontSmoothingMode);
+#if PLATFORM(IOS)
+ m_fontDescription.setSpecifiedSize(CTFontGetSize(fontData.font()));
+ m_fontDescription.setComputedSize(CTFontGetSize(fontData.font()));
+ m_fontDescription.setIsItalic(CTFontGetSymbolicTraits(fontData.font()) & kCTFontTraitItalic);
+ m_fontDescription.setWeight((CTFontGetSymbolicTraits(fontData.font()) & kCTFontTraitBold) ? FontWeightBold : FontWeightNormal);
+#endif
+}
+
+FontCascade::FontCascade(const FontCascade& other)
+ : m_fontDescription(other.m_fontDescription)
+ , m_fonts(other.m_fonts)
+ , m_weakPtrFactory(this)
+ , m_letterSpacing(other.m_letterSpacing)
+ , m_wordSpacing(other.m_wordSpacing)
+ , m_useBackslashAsYenSymbol(other.m_useBackslashAsYenSymbol)
+ , m_enableKerning(computeEnableKerning())
+ , m_requiresShaping(computeRequiresShaping())
+{
+}
+
+FontCascade& FontCascade::operator=(const FontCascade& other)
+{
+ m_fontDescription = other.m_fontDescription;
+ m_fonts = other.m_fonts;
+ m_letterSpacing = other.m_letterSpacing;
+ m_wordSpacing = other.m_wordSpacing;
+ m_useBackslashAsYenSymbol = other.m_useBackslashAsYenSymbol;
+ m_enableKerning = other.m_enableKerning;
+ m_requiresShaping = other.m_requiresShaping;
+ return *this;
+}
+
+bool FontCascade::operator==(const FontCascade& other) const
+{
+ if (isLoadingCustomFonts() || other.isLoadingCustomFonts())
+ return false;
+
+ if (m_fontDescription != other.m_fontDescription || m_letterSpacing != other.m_letterSpacing || m_wordSpacing != other.m_wordSpacing)
+ return false;
+ if (m_fonts == other.m_fonts)
+ return true;
+ if (!m_fonts || !other.m_fonts)
+ return false;
+ if (m_fonts->fontSelector() != other.m_fonts->fontSelector())
+ return false;
+ // Can these cases actually somehow occur? All fonts should get wiped out by full style recalc.
+ if (m_fonts->fontSelectorVersion() != other.m_fonts->fontSelectorVersion())
+ return false;
+ if (m_fonts->generation() != other.m_fonts->generation())
+ return false;
+ return true;
+}
+
+struct FontCascadeCacheKey {
+ FontDescriptionKey fontDescriptionKey; // Shared with the lower level FontCache (caching Font objects)
+ Vector<AtomicString, 3> families;
+ unsigned fontSelectorId;
+ unsigned fontSelectorVersion;
+};
+
+struct FontCascadeCacheEntry {
+ WTF_MAKE_FAST_ALLOCATED;
+public:
+ FontCascadeCacheEntry(FontCascadeCacheKey&& key, Ref<FontCascadeFonts>&& fonts)
+ : key(WTFMove(key))
+ , fonts(WTFMove(fonts))
+ { }
+ FontCascadeCacheKey key;
+ Ref<FontCascadeFonts> fonts;
+};
+
+// FIXME: Should make hash traits for FontCascadeCacheKey instead of using a hash as the key (so we hash a hash).
+typedef HashMap<unsigned, std::unique_ptr<FontCascadeCacheEntry>, AlreadyHashed> FontCascadeCache;
+
+static bool keysMatch(const FontCascadeCacheKey& a, const FontCascadeCacheKey& b)
+{
+ if (a.fontDescriptionKey != b.fontDescriptionKey)
+ return false;
+ if (a.fontSelectorId != b.fontSelectorId || a.fontSelectorVersion != b.fontSelectorVersion)
+ return false;
+ unsigned size = a.families.size();
+ if (size != b.families.size())
+ return false;
+ for (unsigned i = 0; i < size; ++i) {
+ if (!equalIgnoringASCIICase(a.families[i], b.families[i]))
+ return false;
+ }
+ return true;
+}
+
+static FontCascadeCache& fontCascadeCache()
+{
+ static NeverDestroyed<FontCascadeCache> cache;
+ return cache.get();
+}
+
+void invalidateFontCascadeCache()
+{
+ fontCascadeCache().clear();
+}
+
+void clearWidthCaches()
+{
+ for (auto& value : fontCascadeCache().values())
+ value->fonts.get().widthCache().clear();
+}
+
+static FontCascadeCacheKey makeFontCascadeCacheKey(const FontCascadeDescription& description, FontSelector* fontSelector)
+{
+ FontCascadeCacheKey key;
+ key.fontDescriptionKey = FontDescriptionKey(description);
+ unsigned familyCount = description.familyCount();
+ key.families.reserveInitialCapacity(familyCount);
+ for (unsigned i = 0; i < familyCount; ++i)
+ key.families.uncheckedAppend(description.familyAt(i));
+ key.fontSelectorId = fontSelector ? fontSelector->uniqueId() : 0;
+ key.fontSelectorVersion = fontSelector ? fontSelector->version() : 0;
+ return key;
+}
+
+static unsigned computeFontCascadeCacheHash(const FontCascadeCacheKey& key)
+{
+ // FIXME: Should hash the key and the family name characters rather than making a hash out of other hashes.
+ IntegerHasher hasher;
+ hasher.add(key.fontDescriptionKey.computeHash());
+ hasher.add(key.fontSelectorId);
+ hasher.add(key.fontSelectorVersion);
+ for (unsigned i = 0; i < key.families.size(); ++i) {
+ StringImpl* family = key.families[i].impl();
+ hasher.add(family ? ASCIICaseInsensitiveHash::hash(family) : 0);
+ }
+ return hasher.hash();
+}
+
+void pruneUnreferencedEntriesFromFontCascadeCache()
+{
+ fontCascadeCache().removeIf([](auto& entry) {
+ return entry.value->fonts.get().hasOneRef();
+ });
+}
+
+void pruneSystemFallbackFonts()
+{
+ for (auto& entry : fontCascadeCache().values())
+ entry->fonts->pruneSystemFallbacks();
+}
+
+static Ref<FontCascadeFonts> retrieveOrAddCachedFonts(const FontCascadeDescription& fontDescription, RefPtr<FontSelector>&& fontSelector)
+{
+ auto key = makeFontCascadeCacheKey(fontDescription, fontSelector.get());
+
+ unsigned hash = computeFontCascadeCacheHash(key);
+ auto addResult = fontCascadeCache().add(hash, nullptr);
+ if (!addResult.isNewEntry && keysMatch(addResult.iterator->value->key, key))
+ return addResult.iterator->value->fonts.get();
+
+ auto& newEntry = addResult.iterator->value;
+ newEntry = std::make_unique<FontCascadeCacheEntry>(WTFMove(key), FontCascadeFonts::create(WTFMove(fontSelector)));
+ Ref<FontCascadeFonts> glyphs = newEntry->fonts.get();
+
+ static const unsigned unreferencedPruneInterval = 50;
+ static const int maximumEntries = 400;
+ static unsigned pruneCounter;
+ // Referenced FontCascadeFonts would exist anyway so pruning them saves little memory.
+ if (!(++pruneCounter % unreferencedPruneInterval))
+ pruneUnreferencedEntriesFromFontCascadeCache();
+ // Prevent pathological growth.
+ if (fontCascadeCache().size() > maximumEntries)
+ fontCascadeCache().remove(fontCascadeCache().begin());
+ return glyphs;
+}
+
+void FontCascade::update(RefPtr<FontSelector>&& fontSelector) const
+{
+ m_fonts = retrieveOrAddCachedFonts(m_fontDescription, WTFMove(fontSelector));
+ m_useBackslashAsYenSymbol = useBackslashAsYenSignForFamily(firstFamily());
+ m_enableKerning = computeEnableKerning();
+ m_requiresShaping = computeRequiresShaping();
+}
+
+float FontCascade::glyphBufferForTextRun(CodePath codePathToUse, const TextRun& run, unsigned from, unsigned to, GlyphBuffer& glyphBuffer) const
+{
+ if (codePathToUse != Complex)
+ return getGlyphsAndAdvancesForSimpleText(run, from, to, glyphBuffer);
+ return getGlyphsAndAdvancesForComplexText(run, from, to, glyphBuffer);
+}
+
+float FontCascade::drawText(GraphicsContext& context, const TextRun& run, const FloatPoint& point, unsigned from, std::optional<unsigned> to, CustomFontNotReadyAction customFontNotReadyAction) const
+{
+ // Don't draw anything while we are using custom fonts that are in the process of loading,
+ // except if the 'force' argument is set to true (in which case it will use a fallback
+ // font).
+ if (isLoadingCustomFonts() && customFontNotReadyAction == DoNotPaintIfFontNotReady)
+ return 0;
+
+ unsigned destination = to.value_or(run.length());
+
+ CodePath codePathToUse = codePath(run);
+ // FIXME: Use the fast code path once it handles partial runs with kerning and ligatures. See http://webkit.org/b/100050
+ if (codePathToUse != Complex && (enableKerning() || requiresShaping()) && (from || destination != run.length()))
+ codePathToUse = Complex;
+
+ GlyphBuffer glyphBuffer;
+ float startX = point.x() + glyphBufferForTextRun(codePathToUse, run, from, destination, glyphBuffer);
+ // We couldn't generate any glyphs for the run. Give up.
+ if (glyphBuffer.isEmpty())
+ return 0;
+ // Draw the glyph buffer now at the starting point returned in startX.
+ FloatPoint startPoint(startX, point.y());
+ drawGlyphBuffer(context, glyphBuffer, startPoint);
+ return startPoint.x() - startX;
+}
+
+void FontCascade::drawEmphasisMarks(GraphicsContext& context, const TextRun& run, const AtomicString& mark, const FloatPoint& point, unsigned from, std::optional<unsigned> to) const
+{
+ if (isLoadingCustomFonts())
+ return;
+
+ unsigned destination = to.value_or(run.length());
+
+ CodePath codePathToUse = codePath(run);
+ // FIXME: Use the fast code path once it handles partial runs with kerning and ligatures. See http://webkit.org/b/100050
+ if (codePathToUse != Complex && (enableKerning() || requiresShaping()) && (from || destination != run.length()))
+ codePathToUse = Complex;
+
+ if (codePathToUse != Complex)
+ drawEmphasisMarksForSimpleText(context, run, mark, point, from, destination);
+ else
+ drawEmphasisMarksForComplexText(context, run, mark, point, from, destination);
+}
+
+float FontCascade::width(const TextRun& run, HashSet<const Font*>* fallbackFonts, GlyphOverflow* glyphOverflow) const
+{
+ CodePath codePathToUse = codePath(run);
+ if (codePathToUse != Complex) {
+ // The complex path is more restrictive about returning fallback fonts than the simple path, so we need an explicit test to make their behaviors match.
+ if (!canReturnFallbackFontsForComplexText())
+ fallbackFonts = nullptr;
+ // The simple path can optimize the case where glyph overflow is not observable.
+ if (codePathToUse != SimpleWithGlyphOverflow && (glyphOverflow && !glyphOverflow->computeBounds))
+ glyphOverflow = nullptr;
+ }
+
+ bool hasWordSpacingOrLetterSpacing = wordSpacing() || letterSpacing();
+ float* cacheEntry = m_fonts->widthCache().add(run, std::numeric_limits<float>::quiet_NaN(), enableKerning() || requiresShaping(), hasWordSpacingOrLetterSpacing, glyphOverflow);
+ if (cacheEntry && !std::isnan(*cacheEntry))
+ return *cacheEntry;
+
+ HashSet<const Font*> localFallbackFonts;
+ if (!fallbackFonts)
+ fallbackFonts = &localFallbackFonts;
+
+ float result;
+ if (codePathToUse == Complex)
+ result = floatWidthForComplexText(run, fallbackFonts, glyphOverflow);
+ else
+ result = floatWidthForSimpleText(run, fallbackFonts, glyphOverflow);
+
+ if (cacheEntry && fallbackFonts->isEmpty())
+ *cacheEntry = result;
+ return result;
+}
+
+float FontCascade::widthForSimpleText(StringView text) const
+{
+ if (text.isNull() || text.isEmpty())
+ return 0;
+ ASSERT(codePath(TextRun(text)) != FontCascade::Complex);
+ float* cacheEntry = m_fonts->widthCache().add(text, std::numeric_limits<float>::quiet_NaN());
+ if (cacheEntry && !std::isnan(*cacheEntry))
+ return *cacheEntry;
+
+ Vector<GlyphBufferGlyph, 16> glyphs;
+ Vector<GlyphBufferAdvance, 16> advances;
+ bool hasKerningOrLigatures = enableKerning() || requiresShaping();
+ float runWidth = 0;
+ auto& font = primaryFont();
+ for (unsigned i = 0; i < text.length(); ++i) {
+ auto glyph = glyphDataForCharacter(text[i], false).glyph;
+ auto glyphWidth = font.widthForGlyph(glyph);
+ runWidth += glyphWidth;
+ if (!hasKerningOrLigatures)
+ continue;
+#if USE(CAIRO)
+ cairo_glyph_t cairoGlyph;
+ cairoGlyph.index = glyph;
+ glyphs.append(cairoGlyph);
+#else
+ glyphs.append(glyph);
+#endif
+ advances.append(FloatSize(glyphWidth, 0));
+ }
+ if (hasKerningOrLigatures) {
+ font.applyTransforms(&glyphs[0], &advances[0], glyphs.size(), enableKerning(), requiresShaping());
+ // This is needed only to match the result of the slow path. Same glyph widths but different floating point arithmentics can
+ // produce different run width.
+ float runWidthDifferenceWithTransformApplied = -runWidth;
+ for (auto& advance : advances)
+ runWidthDifferenceWithTransformApplied += advance.width();
+ runWidth += runWidthDifferenceWithTransformApplied;
+ }
+
+ if (cacheEntry)
+ *cacheEntry = runWidth;
+ return runWidth;
+}
+
+GlyphData FontCascade::glyphDataForCharacter(UChar32 c, bool mirror, FontVariant variant) const
+{
+ if (variant == AutoVariant) {
+ if (m_fontDescription.variantCaps() == FontVariantCaps::Small) {
+ UChar32 upperC = u_toupper(c);
+ if (upperC != c) {
+ c = upperC;
+ variant = SmallCapsVariant;
+ } else
+ variant = NormalVariant;
+ } else
+ variant = NormalVariant;
+ }
+
+ if (mirror)
+ c = u_charMirror(c);
+
+ return m_fonts->glyphDataForCharacter(c, m_fontDescription, variant);
+}
+
+static const char* fontFamiliesWithInvalidCharWidth[] = {
+ "American Typewriter",
+ "Arial Hebrew",
+ "Chalkboard",
+ "Cochin",
+ "Corsiva Hebrew",
+ "Courier",
+ "Euphemia UCAS",
+ "Geneva",
+ "Gill Sans",
+ "Hei",
+ "Helvetica",
+ "Hoefler Text",
+ "InaiMathi",
+ "Kai",
+ "Lucida Grande",
+ "Marker Felt",
+ "Monaco",
+ "Mshtakan",
+ "New Peninim MT",
+ "Osaka",
+ "Raanana",
+ "STHeiti",
+ "Symbol",
+ "Times",
+ "Apple Braille",
+ "Apple LiGothic",
+ "Apple LiSung",
+ "Apple Symbols",
+ "AppleGothic",
+ "AppleMyungjo",
+ "#GungSeo",
+ "#HeadLineA",
+ "#PCMyungjo",
+ "#PilGi",
+};
+
+// For font families where any of the fonts don't have a valid entry in the OS/2 table
+// for avgCharWidth, fallback to the legacy webkit behavior of getting the avgCharWidth
+// from the width of a '0'. This only seems to apply to a fixed number of Mac fonts,
+// but, in order to get similar rendering across platforms, we do this check for
+// all platforms.
+bool FontCascade::hasValidAverageCharWidth() const
+{
+ AtomicString family = firstFamily();
+ if (family.isEmpty())
+ return false;
+
+#if PLATFORM(MAC) || PLATFORM(IOS)
+ // Internal fonts on OS X and iOS also have an invalid entry in the table for avgCharWidth.
+ if (primaryFontIsSystemFont())
+ return false;
+#endif
+
+ static HashSet<AtomicString>* fontFamiliesWithInvalidCharWidthMap = 0;
+
+ if (!fontFamiliesWithInvalidCharWidthMap) {
+ fontFamiliesWithInvalidCharWidthMap = new HashSet<AtomicString>;
+
+ for (size_t i = 0; i < WTF_ARRAY_LENGTH(fontFamiliesWithInvalidCharWidth); ++i)
+ fontFamiliesWithInvalidCharWidthMap->add(AtomicString(fontFamiliesWithInvalidCharWidth[i]));
+ }
+
+ return !fontFamiliesWithInvalidCharWidthMap->contains(family);
+}
+
+bool FontCascade::fastAverageCharWidthIfAvailable(float& width) const
+{
+ bool success = hasValidAverageCharWidth();
+ if (success)
+ width = roundf(primaryFont().avgCharWidth()); // FIXME: primaryFont() might not correspond to firstFamily().
+ return success;
+}
+
+void FontCascade::adjustSelectionRectForText(const TextRun& run, LayoutRect& selectionRect, unsigned from, std::optional<unsigned> to) const
+{
+ unsigned destination = to.value_or(run.length());
+
+ CodePath codePathToUse = codePath(run);
+ // FIXME: Use the fast code path once it handles partial runs with kerning and ligatures. See http://webkit.org/b/100050
+ if (codePathToUse != Complex && (enableKerning() || requiresShaping()) && (from || destination != run.length()))
+ codePathToUse = Complex;
+
+ if (codePathToUse != Complex)
+ return adjustSelectionRectForSimpleText(run, selectionRect, from, destination);
+
+ return adjustSelectionRectForComplexText(run, selectionRect, from, destination);
+}
+
+int FontCascade::offsetForPosition(const TextRun& run, float x, bool includePartialGlyphs) const
+{
+ // FIXME: Use the fast code path once it handles partial runs with kerning and ligatures. See http://webkit.org/b/100050
+ if (codePath(run) != Complex && (!(enableKerning() || requiresShaping())))
+ return offsetForPositionForSimpleText(run, x, includePartialGlyphs);
+
+ return offsetForPositionForComplexText(run, x, includePartialGlyphs);
+}
+
+template <typename CharacterType>
+static inline String normalizeSpacesInternal(const CharacterType* characters, unsigned length)
+{
+ StringBuilder normalized;
+ normalized.reserveCapacity(length);
+
+ for (unsigned i = 0; i < length; ++i)
+ normalized.append(FontCascade::normalizeSpaces(characters[i]));
+
+ return normalized.toString();
+}
+
+String FontCascade::normalizeSpaces(const LChar* characters, unsigned length)
+{
+ return normalizeSpacesInternal(characters, length);
+}
+
+String FontCascade::normalizeSpaces(const UChar* characters, unsigned length)
+{
+ return normalizeSpacesInternal(characters, length);
+}
+
+static bool shouldUseFontSmoothing = true;
+
+void FontCascade::setShouldUseSmoothing(bool shouldUseSmoothing)
+{
+ ASSERT(isMainThread());
+ shouldUseFontSmoothing = shouldUseSmoothing;
+}
+
+bool FontCascade::shouldUseSmoothing()
+{
+ return shouldUseFontSmoothing;
+}
+
+void FontCascade::setCodePath(CodePath p)
+{
+ s_codePath = p;
+}
+
+FontCascade::CodePath FontCascade::codePath()
+{
+ return s_codePath;
+}
+
+FontCascade::CodePath FontCascade::codePath(const TextRun& run) const
+{
+ if (s_codePath != Auto)
+ return s_codePath;
+
+#if PLATFORM(COCOA)
+ // Because Font::applyTransforms() doesn't know which features to enable/disable in the simple code path, it can't properly handle feature or variant settings.
+ // FIXME: https://bugs.webkit.org/show_bug.cgi?id=150791: @font-face features should also cause this to be complex.
+ if (m_fontDescription.featureSettings().size() > 0 || !m_fontDescription.variantSettings().isAllNormal())
+ return Complex;
+
+#else
+
+ if (run.length() > 1 && (enableKerning() || requiresShaping()))
+ return Complex;
+#endif
+
+ if (!run.characterScanForCodePath())
+ return Simple;
+
+ if (run.is8Bit())
+ return Simple;
+
+ // Start from 0 since drawing and highlighting also measure the characters before run->from.
+ return characterRangeCodePath(run.characters16(), run.length());
+}
+
+FontCascade::CodePath FontCascade::characterRangeCodePath(const UChar* characters, unsigned len)
+{
+ // FIXME: Should use a UnicodeSet in ports where ICU is used. Note that we
+ // can't simply use UnicodeCharacter Property/class because some characters
+ // are not 'combining', but still need to go to the complex path.
+ // Alternatively, we may as well consider binary search over a sorted
+ // list of ranges.
+ CodePath result = Simple;
+ bool previousCharacterIsEmojiGroupCandidate = false;
+ for (unsigned i = 0; i < len; i++) {
+ const UChar c = characters[i];
+ if (c == zeroWidthJoiner && previousCharacterIsEmojiGroupCandidate)
+ return Complex;
+
+ previousCharacterIsEmojiGroupCandidate = false;
+ if (c < 0x2E5) // U+02E5 through U+02E9 (Modifier Letters : Tone letters)
+ continue;
+ if (c <= 0x2E9)
+ return Complex;
+
+ if (c < 0x300) // U+0300 through U+036F Combining diacritical marks
+ continue;
+ if (c <= 0x36F)
+ return Complex;
+
+ if (c < 0x0591 || c == 0x05BE) // U+0591 through U+05CF excluding U+05BE Hebrew combining marks, Hebrew punctuation Paseq, Sof Pasuq and Nun Hafukha
+ continue;
+ if (c <= 0x05CF)
+ return Complex;
+
+ // U+0600 through U+109F Arabic, Syriac, Thaana, NKo, Samaritan, Mandaic,
+ // Devanagari, Bengali, Gurmukhi, Gujarati, Oriya, Tamil, Telugu, Kannada,
+ // Malayalam, Sinhala, Thai, Lao, Tibetan, Myanmar
+ if (c < 0x0600)
+ continue;
+ if (c <= 0x109F)
+ return Complex;
+
+ // U+1100 through U+11FF Hangul Jamo (only Ancient Korean should be left here if you precompose;
+ // Modern Korean will be precomposed as a result of step A)
+ if (c < 0x1100)
+ continue;
+ if (c <= 0x11FF)
+ return Complex;
+
+ if (c < 0x135D) // U+135D through U+135F Ethiopic combining marks
+ continue;
+ if (c <= 0x135F)
+ return Complex;
+
+ if (c < 0x1700) // U+1780 through U+18AF Tagalog, Hanunoo, Buhid, Taghanwa,Khmer, Mongolian
+ continue;
+ if (c <= 0x18AF)
+ return Complex;
+
+ if (c < 0x1900) // U+1900 through U+194F Limbu (Unicode 4.0)
+ continue;
+ if (c <= 0x194F)
+ return Complex;
+
+ if (c < 0x1980) // U+1980 through U+19DF New Tai Lue
+ continue;
+ if (c <= 0x19DF)
+ return Complex;
+
+ if (c < 0x1A00) // U+1A00 through U+1CFF Buginese, Tai Tham, Balinese, Batak, Lepcha, Vedic
+ continue;
+ if (c <= 0x1CFF)
+ return Complex;
+
+ if (c < 0x1DC0) // U+1DC0 through U+1DFF Comining diacritical mark supplement
+ continue;
+ if (c <= 0x1DFF)
+ return Complex;
+
+ // U+1E00 through U+2000 characters with diacritics and stacked diacritics
+ if (c <= 0x2000) {
+ result = SimpleWithGlyphOverflow;
+ continue;
+ }
+
+ if (c < 0x20D0) // U+20D0 through U+20FF Combining marks for symbols
+ continue;
+ if (c <= 0x20FF)
+ return Complex;
+
+ if (c < 0x26F9)
+ continue;
+ if (c < 0x26FA)
+ return Complex;
+
+ if (c < 0x2CEF) // U+2CEF through U+2CF1 Combining marks for Coptic
+ continue;
+ if (c <= 0x2CF1)
+ return Complex;
+
+ if (c < 0x302A) // U+302A through U+302F Ideographic and Hangul Tone marks
+ continue;
+ if (c <= 0x302F)
+ return Complex;
+
+ if (c < 0xA67C) // U+A67C through U+A67D Combining marks for old Cyrillic
+ continue;
+ if (c <= 0xA67D)
+ return Complex;
+
+ if (c < 0xA6F0) // U+A6F0 through U+A6F1 Combining mark for Bamum
+ continue;
+ if (c <= 0xA6F1)
+ return Complex;
+
+ // U+A800 through U+ABFF Nagri, Phags-pa, Saurashtra, Devanagari Extended,
+ // Hangul Jamo Ext. A, Javanese, Myanmar Extended A, Tai Viet, Meetei Mayek,
+ if (c < 0xA800)
+ continue;
+ if (c <= 0xABFF)
+ return Complex;
+
+ if (c < 0xD7B0) // U+D7B0 through U+D7FF Hangul Jamo Ext. B
+ continue;
+ if (c <= 0xD7FF)
+ return Complex;
+
+ if (c <= 0xDBFF) {
+ // High surrogate
+
+ if (i == len - 1)
+ continue;
+
+ UChar next = characters[++i];
+ if (!U16_IS_TRAIL(next))
+ continue;
+
+ UChar32 supplementaryCharacter = U16_GET_SUPPLEMENTARY(c, next);
+
+ if (supplementaryCharacter < 0x1F1E6) // U+1F1E6 through U+1F1FF Regional Indicator Symbols
+ continue;
+ if (supplementaryCharacter <= 0x1F1FF)
+ return Complex;
+
+ if (isEmojiGroupCandidate(supplementaryCharacter)) {
+ previousCharacterIsEmojiGroupCandidate = true;
+ continue;
+ }
+ if (isEmojiFitzpatrickModifier(supplementaryCharacter))
+ return Complex;
+ if (supplementaryCharacter < 0xE0100) // U+E0100 through U+E01EF Unicode variation selectors.
+ continue;
+ if (supplementaryCharacter <= 0xE01EF)
+ return Complex;
+
+ // FIXME: Check for Brahmi (U+11000 block), Kaithi (U+11080 block) and other complex scripts
+ // in plane 1 or higher.
+
+ continue;
+ }
+
+ if (c < 0xFE00) // U+FE00 through U+FE0F Unicode variation selectors
+ continue;
+ if (c <= 0xFE0F)
+ return Complex;
+
+ if (c < 0xFE20) // U+FE20 through U+FE2F Combining half marks
+ continue;
+ if (c <= 0xFE2F)
+ return Complex;
+ }
+ return result;
+}
+
+bool FontCascade::isCJKIdeograph(UChar32 c)
+{
+ // The basic CJK Unified Ideographs block.
+ if (c >= 0x4E00 && c <= 0x9FFF)
+ return true;
+
+ // CJK Unified Ideographs Extension A.
+ if (c >= 0x3400 && c <= 0x4DBF)
+ return true;
+
+ // CJK Radicals Supplement.
+ if (c >= 0x2E80 && c <= 0x2EFF)
+ return true;
+
+ // Kangxi Radicals.
+ if (c >= 0x2F00 && c <= 0x2FDF)
+ return true;
+
+ // CJK Strokes.
+ if (c >= 0x31C0 && c <= 0x31EF)
+ return true;
+
+ // CJK Compatibility Ideographs.
+ if (c >= 0xF900 && c <= 0xFAFF)
+ return true;
+
+ // CJK Unified Ideographs Extension B.
+ if (c >= 0x20000 && c <= 0x2A6DF)
+ return true;
+
+ // CJK Unified Ideographs Extension C.
+ if (c >= 0x2A700 && c <= 0x2B73F)
+ return true;
+
+ // CJK Unified Ideographs Extension D.
+ if (c >= 0x2B740 && c <= 0x2B81F)
+ return true;
+
+ // CJK Compatibility Ideographs Supplement.
+ if (c >= 0x2F800 && c <= 0x2FA1F)
+ return true;
+
+ return false;
+}
+
+bool FontCascade::isCJKIdeographOrSymbol(UChar32 c)
+{
+ // 0x2C7 Caron, Mandarin Chinese 3rd Tone
+ // 0x2CA Modifier Letter Acute Accent, Mandarin Chinese 2nd Tone
+ // 0x2CB Modifier Letter Grave Access, Mandarin Chinese 4th Tone
+ // 0x2D9 Dot Above, Mandarin Chinese 5th Tone
+ if ((c == 0x2C7) || (c == 0x2CA) || (c == 0x2CB) || (c == 0x2D9))
+ return true;
+
+ if ((c == 0x2020) || (c == 0x2021) || (c == 0x2030) || (c == 0x203B) || (c == 0x203C)
+ || (c == 0x2042) || (c == 0x2047) || (c == 0x2048) || (c == 0x2049) || (c == 0x2051)
+ || (c == 0x20DD) || (c == 0x20DE) || (c == 0x2100) || (c == 0x2103) || (c == 0x2105)
+ || (c == 0x2109) || (c == 0x210A) || (c == 0x2113) || (c == 0x2116) || (c == 0x2121)
+ || (c == 0x212B) || (c == 0x213B) || (c == 0x2150) || (c == 0x2151) || (c == 0x2152))
+ return true;
+
+ if (c >= 0x2156 && c <= 0x215A)
+ return true;
+
+ if (c >= 0x2160 && c <= 0x216B)
+ return true;
+
+ if (c >= 0x2170 && c <= 0x217B)
+ return true;
+
+ if ((c == 0x217F) || (c == 0x2189) || (c == 0x2307) || (c == 0x2312) || (c == 0x23BE) || (c == 0x23BF))
+ return true;
+
+ if (c >= 0x23C0 && c <= 0x23CC)
+ return true;
+
+ if ((c == 0x23CE) || (c == 0x2423))
+ return true;
+
+ if (c >= 0x2460 && c <= 0x2492)
+ return true;
+
+ if (c >= 0x249C && c <= 0x24FF)
+ return true;
+
+ if ((c == 0x25A0) || (c == 0x25A1) || (c == 0x25A2) || (c == 0x25AA) || (c == 0x25AB))
+ return true;
+
+ if ((c == 0x25B1) || (c == 0x25B2) || (c == 0x25B3) || (c == 0x25B6) || (c == 0x25B7) || (c == 0x25BC) || (c == 0x25BD))
+ return true;
+
+ if ((c == 0x25C0) || (c == 0x25C1) || (c == 0x25C6) || (c == 0x25C7) || (c == 0x25C9) || (c == 0x25CB) || (c == 0x25CC))
+ return true;
+
+ if (c >= 0x25CE && c <= 0x25D3)
+ return true;
+
+ if (c >= 0x25E2 && c <= 0x25E6)
+ return true;
+
+ if (c == 0x25EF)
+ return true;
+
+ if (c >= 0x2600 && c <= 0x2603)
+ return true;
+
+ if ((c == 0x2605) || (c == 0x2606) || (c == 0x260E) || (c == 0x2616) || (c == 0x2617) || (c == 0x2640) || (c == 0x2642))
+ return true;
+
+ if (c >= 0x2660 && c <= 0x266F)
+ return true;
+
+ if (c >= 0x2672 && c <= 0x267D)
+ return true;
+
+ if ((c == 0x26A0) || (c == 0x26BD) || (c == 0x26BE) || (c == 0x2713) || (c == 0x271A) || (c == 0x273F) || (c == 0x2740) || (c == 0x2756))
+ return true;
+
+ if (c >= 0x2776 && c <= 0x277F)
+ return true;
+
+ if (c == 0x2B1A)
+ return true;
+
+ // Ideographic Description Characters.
+ if (c >= 0x2FF0 && c <= 0x2FFF)
+ return true;
+
+ // CJK Symbols and Punctuation, excluding 0x3030.
+ if (c >= 0x3000 && c < 0x3030)
+ return true;
+
+ if (c > 0x3030 && c <= 0x303F)
+ return true;
+
+ // Hiragana
+ if (c >= 0x3040 && c <= 0x309F)
+ return true;
+
+ // Katakana
+ if (c >= 0x30A0 && c <= 0x30FF)
+ return true;
+
+ // Bopomofo
+ if (c >= 0x3100 && c <= 0x312F)
+ return true;
+
+ if (c >= 0x3190 && c <= 0x319F)
+ return true;
+
+ // Bopomofo Extended
+ if (c >= 0x31A0 && c <= 0x31BF)
+ return true;
+
+ // Enclosed CJK Letters and Months.
+ if (c >= 0x3200 && c <= 0x32FF)
+ return true;
+
+ // CJK Compatibility.
+ if (c >= 0x3300 && c <= 0x33FF)
+ return true;
+
+ if (c >= 0xF860 && c <= 0xF862)
+ return true;
+
+ // CJK Compatibility Forms.
+ if (c >= 0xFE30 && c <= 0xFE4F)
+ return true;
+
+ if ((c == 0xFE10) || (c == 0xFE11) || (c == 0xFE12) || (c == 0xFE19))
+ return true;
+
+ if ((c == 0xFF0D) || (c == 0xFF1B) || (c == 0xFF1C) || (c == 0xFF1E))
+ return false;
+
+ // Halfwidth and Fullwidth Forms
+ // Usually only used in CJK
+ if (c >= 0xFF00 && c <= 0xFFEF)
+ return true;
+
+ // Emoji.
+ if (c == 0x1F100)
+ return true;
+
+ if (c >= 0x1F110 && c <= 0x1F129)
+ return true;
+
+ if (c >= 0x1F130 && c <= 0x1F149)
+ return true;
+
+ if (c >= 0x1F150 && c <= 0x1F169)
+ return true;
+
+ if (c >= 0x1F170 && c <= 0x1F189)
+ return true;
+
+ if (c >= 0x1F200 && c <= 0x1F6C5)
+ return true;
+
+ return isCJKIdeograph(c);
+}
+
+std::pair<unsigned, bool> FontCascade::expansionOpportunityCountInternal(const LChar* characters, unsigned length, TextDirection direction, ExpansionBehavior expansionBehavior)
+{
+ unsigned count = 0;
+ bool isAfterExpansion = (expansionBehavior & LeadingExpansionMask) == ForbidLeadingExpansion;
+ if ((expansionBehavior & LeadingExpansionMask) == ForceLeadingExpansion) {
+ ++count;
+ isAfterExpansion = true;
+ }
+ if (direction == LTR) {
+ for (unsigned i = 0; i < length; ++i) {
+ if (treatAsSpace(characters[i])) {
+ count++;
+ isAfterExpansion = true;
+ } else
+ isAfterExpansion = false;
+ }
+ } else {
+ for (unsigned i = length; i > 0; --i) {
+ if (treatAsSpace(characters[i - 1])) {
+ count++;
+ isAfterExpansion = true;
+ } else
+ isAfterExpansion = false;
+ }
+ }
+ if (!isAfterExpansion && (expansionBehavior & TrailingExpansionMask) == ForceTrailingExpansion) {
+ ++count;
+ isAfterExpansion = true;
+ } else if (isAfterExpansion && (expansionBehavior & TrailingExpansionMask) == ForbidTrailingExpansion) {
+ ASSERT(count);
+ --count;
+ isAfterExpansion = false;
+ }
+ return std::make_pair(count, isAfterExpansion);
+}
+
+std::pair<unsigned, bool> FontCascade::expansionOpportunityCountInternal(const UChar* characters, unsigned length, TextDirection direction, ExpansionBehavior expansionBehavior)
+{
+ static bool expandAroundIdeographs = canExpandAroundIdeographsInComplexText();
+ unsigned count = 0;
+ bool isAfterExpansion = (expansionBehavior & LeadingExpansionMask) == ForbidLeadingExpansion;
+ if ((expansionBehavior & LeadingExpansionMask) == ForceLeadingExpansion) {
+ ++count;
+ isAfterExpansion = true;
+ }
+ if (direction == LTR) {
+ for (unsigned i = 0; i < length; ++i) {
+ UChar32 character = characters[i];
+ if (treatAsSpace(character)) {
+ count++;
+ isAfterExpansion = true;
+ continue;
+ }
+ if (U16_IS_LEAD(character) && i + 1 < length && U16_IS_TRAIL(characters[i + 1])) {
+ character = U16_GET_SUPPLEMENTARY(character, characters[i + 1]);
+ i++;
+ }
+ if (expandAroundIdeographs && isCJKIdeographOrSymbol(character)) {
+ if (!isAfterExpansion)
+ count++;
+ count++;
+ isAfterExpansion = true;
+ continue;
+ }
+ isAfterExpansion = false;
+ }
+ } else {
+ for (unsigned i = length; i > 0; --i) {
+ UChar32 character = characters[i - 1];
+ if (treatAsSpace(character)) {
+ count++;
+ isAfterExpansion = true;
+ continue;
+ }
+ if (U16_IS_TRAIL(character) && i > 1 && U16_IS_LEAD(characters[i - 2])) {
+ character = U16_GET_SUPPLEMENTARY(characters[i - 2], character);
+ i--;
+ }
+ if (expandAroundIdeographs && isCJKIdeographOrSymbol(character)) {
+ if (!isAfterExpansion)
+ count++;
+ count++;
+ isAfterExpansion = true;
+ continue;
+ }
+ isAfterExpansion = false;
+ }
+ }
+ if (!isAfterExpansion && (expansionBehavior & TrailingExpansionMask) == ForceTrailingExpansion) {
+ ++count;
+ isAfterExpansion = true;
+ } else if (isAfterExpansion && (expansionBehavior & TrailingExpansionMask) == ForbidTrailingExpansion) {
+ ASSERT(count);
+ --count;
+ isAfterExpansion = false;
+ }
+ return std::make_pair(count, isAfterExpansion);
+}
+
+std::pair<unsigned, bool> FontCascade::expansionOpportunityCount(const StringView& stringView, TextDirection direction, ExpansionBehavior expansionBehavior)
+{
+ // For each character, iterating from left to right:
+ // If it is recognized as a space, insert an opportunity after it
+ // If it is an ideograph, insert one opportunity before it and one opportunity after it
+ // Do this such a way so that there are not two opportunities next to each other.
+ if (stringView.is8Bit())
+ return expansionOpportunityCountInternal(stringView.characters8(), stringView.length(), direction, expansionBehavior);
+ return expansionOpportunityCountInternal(stringView.characters16(), stringView.length(), direction, expansionBehavior);
+}
+
+bool FontCascade::leadingExpansionOpportunity(const StringView& stringView, TextDirection direction)
+{
+ if (!stringView.length())
+ return false;
+
+ UChar32 initialCharacter;
+ if (direction == LTR) {
+ initialCharacter = stringView[0];
+ if (U16_IS_LEAD(initialCharacter) && stringView.length() > 1 && U16_IS_TRAIL(stringView[1]))
+ initialCharacter = U16_GET_SUPPLEMENTARY(initialCharacter, stringView[1]);
+ } else {
+ initialCharacter = stringView[stringView.length() - 1];
+ if (U16_IS_TRAIL(initialCharacter) && stringView.length() > 1 && U16_IS_LEAD(stringView[stringView.length() - 2]))
+ initialCharacter = U16_GET_SUPPLEMENTARY(stringView[stringView.length() - 2], initialCharacter);
+ }
+
+ return canExpandAroundIdeographsInComplexText() && isCJKIdeographOrSymbol(initialCharacter);
+}
+
+bool FontCascade::trailingExpansionOpportunity(const StringView& stringView, TextDirection direction)
+{
+ if (!stringView.length())
+ return false;
+
+ UChar32 finalCharacter;
+ if (direction == LTR) {
+ finalCharacter = stringView[stringView.length() - 1];
+ if (U16_IS_TRAIL(finalCharacter) && stringView.length() > 1 && U16_IS_LEAD(stringView[stringView.length() - 2]))
+ finalCharacter = U16_GET_SUPPLEMENTARY(stringView[stringView.length() - 2], finalCharacter);
+ } else {
+ finalCharacter = stringView[0];
+ if (U16_IS_LEAD(finalCharacter) && stringView.length() > 1 && U16_IS_TRAIL(stringView[1]))
+ finalCharacter = U16_GET_SUPPLEMENTARY(finalCharacter, stringView[1]);
+ }
+
+ return treatAsSpace(finalCharacter) || (canExpandAroundIdeographsInComplexText() && isCJKIdeographOrSymbol(finalCharacter));
+}
+
+bool FontCascade::canReceiveTextEmphasis(UChar32 c)
+{
+ if (U_GET_GC_MASK(c) & (U_GC_Z_MASK | U_GC_CN_MASK | U_GC_CC_MASK | U_GC_CF_MASK))
+ return false;
+
+ // Additional word-separator characters listed in CSS Text Level 3 Editor's Draft 3 November 2010.
+ if (c == ethiopicWordspace || c == aegeanWordSeparatorLine || c == aegeanWordSeparatorDot
+ || c == ugariticWordDivider || c == tibetanMarkIntersyllabicTsheg || c == tibetanMarkDelimiterTshegBstar)
+ return false;
+
+ return true;
+}
+
+bool FontCascade::isLoadingCustomFonts() const
+{
+ return m_fonts && m_fonts->isLoadingCustomFonts();
+}
+
+GlyphToPathTranslator::GlyphUnderlineType computeUnderlineType(const TextRun& textRun, const GlyphBuffer& glyphBuffer, unsigned index)
+{
+ // In general, we want to skip descenders. However, skipping descenders on CJK characters leads to undesirable renderings,
+ // so we want to draw through CJK characters (on a character-by-character basis).
+ UChar32 baseCharacter;
+ unsigned offsetInString = glyphBuffer.offsetInString(index);
+
+ if (offsetInString == GlyphBuffer::noOffset || offsetInString >= textRun.length()) {
+ // We have no idea which character spawned this glyph. Bail.
+ ASSERT_WITH_SECURITY_IMPLICATION(offsetInString < textRun.length());
+ return GlyphToPathTranslator::GlyphUnderlineType::DrawOverGlyph;
+ }
+
+ if (textRun.is8Bit())
+ baseCharacter = textRun.characters8()[offsetInString];
+ else
+ U16_NEXT(textRun.characters16(), offsetInString, textRun.length(), baseCharacter);
+
+ // u_getIntPropertyValue with UCHAR_IDEOGRAPHIC doesn't return true for Japanese or Korean codepoints.
+ // Instead, we can use the "Unicode allocation block" for the character.
+ UBlockCode blockCode = ublock_getCode(baseCharacter);
+ switch (blockCode) {
+ case UBLOCK_CJK_RADICALS_SUPPLEMENT:
+ case UBLOCK_CJK_SYMBOLS_AND_PUNCTUATION:
+ case UBLOCK_ENCLOSED_CJK_LETTERS_AND_MONTHS:
+ case UBLOCK_CJK_COMPATIBILITY:
+ case UBLOCK_CJK_UNIFIED_IDEOGRAPHS_EXTENSION_A:
+ case UBLOCK_CJK_UNIFIED_IDEOGRAPHS:
+ case UBLOCK_CJK_COMPATIBILITY_IDEOGRAPHS:
+ case UBLOCK_CJK_COMPATIBILITY_FORMS:
+ case UBLOCK_CJK_UNIFIED_IDEOGRAPHS_EXTENSION_B:
+ case UBLOCK_CJK_COMPATIBILITY_IDEOGRAPHS_SUPPLEMENT:
+ case UBLOCK_CJK_STROKES:
+ case UBLOCK_CJK_UNIFIED_IDEOGRAPHS_EXTENSION_C:
+ case UBLOCK_CJK_UNIFIED_IDEOGRAPHS_EXTENSION_D:
+ case UBLOCK_IDEOGRAPHIC_DESCRIPTION_CHARACTERS:
+ case UBLOCK_LINEAR_B_IDEOGRAMS:
+ case UBLOCK_ENCLOSED_IDEOGRAPHIC_SUPPLEMENT:
+ case UBLOCK_HIRAGANA:
+ case UBLOCK_KATAKANA:
+ case UBLOCK_BOPOMOFO:
+ case UBLOCK_BOPOMOFO_EXTENDED:
+ case UBLOCK_HANGUL_JAMO:
+ case UBLOCK_HANGUL_COMPATIBILITY_JAMO:
+ case UBLOCK_HANGUL_SYLLABLES:
+ case UBLOCK_HANGUL_JAMO_EXTENDED_A:
+ case UBLOCK_HANGUL_JAMO_EXTENDED_B:
+ return GlyphToPathTranslator::GlyphUnderlineType::DrawOverGlyph;
+ default:
+ return GlyphToPathTranslator::GlyphUnderlineType::SkipDescenders;
+ }
+}
+
+// FIXME: This function may not work if the emphasis mark uses a complex script, but none of the
+// standard emphasis marks do so.
+std::optional<GlyphData> FontCascade::getEmphasisMarkGlyphData(const AtomicString& mark) const
+{
+ if (mark.isEmpty())
+ return std::nullopt;
+
+ UChar32 character;
+ if (!mark.is8Bit()) {
+ SurrogatePairAwareTextIterator iterator(mark.characters16(), 0, mark.length(), mark.length());
+ unsigned clusterLength;
+ if (!iterator.consume(character, clusterLength))
+ return std::nullopt;
+ } else
+ character = mark[0];
+
+ std::optional<GlyphData> glyphData(glyphDataForCharacter(character, false, EmphasisMarkVariant));
+ return glyphData.value().isValid() ? glyphData : std::nullopt;
+}
+
+int FontCascade::emphasisMarkAscent(const AtomicString& mark) const
+{
+ std::optional<GlyphData> markGlyphData = getEmphasisMarkGlyphData(mark);
+ if (!markGlyphData)
+ return 0;
+
+ const Font* markFontData = markGlyphData.value().font;
+ ASSERT(markFontData);
+ if (!markFontData)
+ return 0;
+
+ return markFontData->fontMetrics().ascent();
+}
+
+int FontCascade::emphasisMarkDescent(const AtomicString& mark) const
+{
+ std::optional<GlyphData> markGlyphData = getEmphasisMarkGlyphData(mark);
+ if (!markGlyphData)
+ return 0;
+
+ const Font* markFontData = markGlyphData.value().font;
+ ASSERT(markFontData);
+ if (!markFontData)
+ return 0;
+
+ return markFontData->fontMetrics().descent();
+}
+
+int FontCascade::emphasisMarkHeight(const AtomicString& mark) const
+{
+ std::optional<GlyphData> markGlyphData = getEmphasisMarkGlyphData(mark);
+ if (!markGlyphData)
+ return 0;
+
+ const Font* markFontData = markGlyphData.value().font;
+ ASSERT(markFontData);
+ if (!markFontData)
+ return 0;
+
+ return markFontData->fontMetrics().height();
+}
+
+float FontCascade::getGlyphsAndAdvancesForSimpleText(const TextRun& run, unsigned from, unsigned to, GlyphBuffer& glyphBuffer, ForTextEmphasisOrNot forTextEmphasis) const
+{
+ float initialAdvance;
+
+ WidthIterator it(this, run, 0, false, forTextEmphasis);
+ // FIXME: Using separate glyph buffers for the prefix and the suffix is incorrect when kerning or
+ // ligatures are enabled.
+ GlyphBuffer localGlyphBuffer;
+ it.advance(from, &localGlyphBuffer);
+ float beforeWidth = it.m_runWidthSoFar;
+ it.advance(to, &glyphBuffer);
+
+ if (glyphBuffer.isEmpty())
+ return 0;
+
+ float afterWidth = it.m_runWidthSoFar;
+
+ if (run.rtl()) {
+ float finalRoundingWidth = it.m_finalRoundingWidth;
+ it.advance(run.length(), &localGlyphBuffer);
+ initialAdvance = finalRoundingWidth + it.m_runWidthSoFar - afterWidth;
+ } else
+ initialAdvance = beforeWidth;
+
+ if (run.rtl())
+ glyphBuffer.reverse(0, glyphBuffer.size());
+
+ return initialAdvance;
+}
+
+void FontCascade::drawEmphasisMarksForSimpleText(GraphicsContext& context, const TextRun& run, const AtomicString& mark, const FloatPoint& point, unsigned from, unsigned to) const
+{
+ GlyphBuffer glyphBuffer;
+ float initialAdvance = getGlyphsAndAdvancesForSimpleText(run, from, to, glyphBuffer, ForTextEmphasis);
+
+ if (glyphBuffer.isEmpty())
+ return;
+
+ drawEmphasisMarks(context, glyphBuffer, mark, FloatPoint(point.x() + initialAdvance, point.y()));
+}
+
+void FontCascade::drawGlyphBuffer(GraphicsContext& context, const GlyphBuffer& glyphBuffer, FloatPoint& point) const
+{
+ // Draw each contiguous run of glyphs that use the same font data.
+ const Font* fontData = glyphBuffer.fontAt(0);
+ FloatSize offset = glyphBuffer.offsetAt(0);
+ FloatPoint startPoint(point.x(), point.y() - glyphBuffer.initialAdvance().height());
+ float nextX = startPoint.x() + glyphBuffer.advanceAt(0).width();
+ float nextY = startPoint.y() + glyphBuffer.advanceAt(0).height();
+ unsigned lastFrom = 0;
+ unsigned nextGlyph = 1;
+ while (nextGlyph < glyphBuffer.size()) {
+ const Font* nextFontData = glyphBuffer.fontAt(nextGlyph);
+ FloatSize nextOffset = glyphBuffer.offsetAt(nextGlyph);
+
+ if (nextFontData != fontData || nextOffset != offset) {
+ context.drawGlyphs(*this, *fontData, glyphBuffer, lastFrom, nextGlyph - lastFrom, startPoint);
+
+ lastFrom = nextGlyph;
+ fontData = nextFontData;
+ offset = nextOffset;
+ startPoint.setX(nextX);
+ startPoint.setY(nextY);
+ }
+ nextX += glyphBuffer.advanceAt(nextGlyph).width();
+ nextY += glyphBuffer.advanceAt(nextGlyph).height();
+ nextGlyph++;
+ }
+
+ context.drawGlyphs(*this, *fontData, glyphBuffer, lastFrom, nextGlyph - lastFrom, startPoint);
+ point.setX(nextX);
+}
+
+inline static float offsetToMiddleOfGlyph(const Font* fontData, Glyph glyph)
+{
+ if (fontData->platformData().orientation() == Horizontal) {
+ FloatRect bounds = fontData->boundsForGlyph(glyph);
+ return bounds.x() + bounds.width() / 2;
+ }
+ // FIXME: Use glyph bounds once they make sense for vertical fonts.
+ return fontData->widthForGlyph(glyph) / 2;
+}
+
+inline static float offsetToMiddleOfGlyphAtIndex(const GlyphBuffer& glyphBuffer, unsigned i)
+{
+ return offsetToMiddleOfGlyph(glyphBuffer.fontAt(i), glyphBuffer.glyphAt(i));
+}
+
+void FontCascade::drawEmphasisMarks(GraphicsContext& context, const GlyphBuffer& glyphBuffer, const AtomicString& mark, const FloatPoint& point) const
+{
+ std::optional<GlyphData> markGlyphData = getEmphasisMarkGlyphData(mark);
+ if (!markGlyphData)
+ return;
+
+ const Font* markFontData = markGlyphData.value().font;
+ ASSERT(markFontData);
+ if (!markFontData)
+ return;
+
+ Glyph markGlyph = markGlyphData.value().glyph;
+ Glyph spaceGlyph = markFontData->spaceGlyph();
+
+ float middleOfLastGlyph = offsetToMiddleOfGlyphAtIndex(glyphBuffer, 0);
+ FloatPoint startPoint(point.x() + middleOfLastGlyph - offsetToMiddleOfGlyph(markFontData, markGlyph), point.y());
+
+ GlyphBuffer markBuffer;
+ for (unsigned i = 0; i + 1 < glyphBuffer.size(); ++i) {
+ float middleOfNextGlyph = offsetToMiddleOfGlyphAtIndex(glyphBuffer, i + 1);
+ float advance = glyphBuffer.advanceAt(i).width() - middleOfLastGlyph + middleOfNextGlyph;
+ markBuffer.add(glyphBuffer.glyphAt(i) ? markGlyph : spaceGlyph, markFontData, advance);
+ middleOfLastGlyph = middleOfNextGlyph;
+ }
+ markBuffer.add(glyphBuffer.glyphAt(glyphBuffer.size() - 1) ? markGlyph : spaceGlyph, markFontData, 0);
+
+ drawGlyphBuffer(context, markBuffer, startPoint);
+}
+
+float FontCascade::floatWidthForSimpleText(const TextRun& run, HashSet<const Font*>* fallbackFonts, GlyphOverflow* glyphOverflow) const
+{
+ WidthIterator it(this, run, fallbackFonts, glyphOverflow);
+ GlyphBuffer glyphBuffer;
+ it.advance(run.length(), (enableKerning() || requiresShaping()) ? &glyphBuffer : nullptr);
+
+ if (glyphOverflow) {
+ glyphOverflow->top = std::max<int>(glyphOverflow->top, ceilf(-it.minGlyphBoundingBoxY()) - (glyphOverflow->computeBounds ? 0 : fontMetrics().ascent()));
+ glyphOverflow->bottom = std::max<int>(glyphOverflow->bottom, ceilf(it.maxGlyphBoundingBoxY()) - (glyphOverflow->computeBounds ? 0 : fontMetrics().descent()));
+ glyphOverflow->left = ceilf(it.firstGlyphOverflow());
+ glyphOverflow->right = ceilf(it.lastGlyphOverflow());
+ }
+
+ return it.m_runWidthSoFar;
+}
+
+void FontCascade::adjustSelectionRectForSimpleText(const TextRun& run, LayoutRect& selectionRect, unsigned from, unsigned to) const
+{
+ GlyphBuffer glyphBuffer;
+ WidthIterator it(this, run);
+ it.advance(from, &glyphBuffer);
+ float beforeWidth = it.m_runWidthSoFar;
+ it.advance(to, &glyphBuffer);
+ float afterWidth = it.m_runWidthSoFar;
+ float totalWidth = -1;
+
+ if (run.rtl()) {
+ it.advance(run.length(), &glyphBuffer);
+ totalWidth = it.m_runWidthSoFar;
+ selectionRect.move(totalWidth - afterWidth, 0);
+ } else
+ selectionRect.move(beforeWidth, 0);
+ selectionRect.setWidth(LayoutUnit::fromFloatCeil(afterWidth - beforeWidth));
+}
+
+int FontCascade::offsetForPositionForSimpleText(const TextRun& run, float x, bool includePartialGlyphs) const
+{
+ float delta = x;
+
+ WidthIterator it(this, run);
+ GlyphBuffer localGlyphBuffer;
+ unsigned offset;
+ if (run.rtl()) {
+ delta -= floatWidthForSimpleText(run);
+ while (1) {
+ offset = it.m_currentCharacter;
+ float w;
+ if (!it.advanceOneCharacter(w, localGlyphBuffer))
+ break;
+ delta += w;
+ if (includePartialGlyphs) {
+ if (delta - w / 2 >= 0)
+ break;
+ } else {
+ if (delta >= 0)
+ break;
+ }
+ }
+ } else {
+ while (1) {
+ offset = it.m_currentCharacter;
+ float w;
+ if (!it.advanceOneCharacter(w, localGlyphBuffer))
+ break;
+ delta -= w;
+ if (includePartialGlyphs) {
+ if (delta + w / 2 <= 0)
+ break;
+ } else {
+ if (delta <= 0)
+ break;
+ }
+ }
+ }
+
+ return offset;
+}
+
+#if !PLATFORM(COCOA)
+// FIXME: Unify this with the macOS and iOS implementation.
+const Font* FontCascade::fontForCombiningCharacterSequence(const UChar* characters, size_t length) const
+{
+ UChar32 baseCharacter;
+ size_t baseCharacterLength = 0;
+ U16_NEXT(characters, baseCharacterLength, length, baseCharacter);
+ GlyphData baseCharacterGlyphData = glyphDataForCharacter(baseCharacter, false, NormalVariant);
+
+ if (!baseCharacterGlyphData.glyph)
+ return nullptr;
+ return baseCharacterGlyphData.font;
+}
+#endif
+
+void FontCascade::drawEmphasisMarksForComplexText(GraphicsContext& context, const TextRun& run, const AtomicString& mark, const FloatPoint& point, unsigned from, unsigned to) const
+{
+ GlyphBuffer glyphBuffer;
+ float initialAdvance = getGlyphsAndAdvancesForComplexText(run, from, to, glyphBuffer, ForTextEmphasis);
+
+ if (glyphBuffer.isEmpty())
+ return;
+
+ drawEmphasisMarks(context, glyphBuffer, mark, FloatPoint(point.x() + initialAdvance, point.y()));
+}
+
+}
diff --git a/Source/WebCore/platform/graphics/FontCascade.h b/Source/WebCore/platform/graphics/FontCascade.h
new file mode 100644
index 000000000..92d878620
--- /dev/null
+++ b/Source/WebCore/platform/graphics/FontCascade.h
@@ -0,0 +1,373 @@
+/*
+ * Copyright (C) 2000 Lars Knoll (knoll@kde.org)
+ * (C) 2000 Antti Koivisto (koivisto@kde.org)
+ * (C) 2000 Dirk Mueller (mueller@kde.org)
+ * Copyright (C) 2003, 2006, 2007, 2010, 2011-2016 Apple Inc. All rights reserved.
+ * Copyright (C) 2008 Holger Hans Peter Freyther
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef FontCascade_h
+#define FontCascade_h
+
+#include "DashArray.h"
+#include "Font.h"
+#include "FontCascadeFonts.h"
+#include "FontDescription.h"
+#include "Path.h"
+#include "TextFlags.h"
+#include <wtf/HashSet.h>
+#include <wtf/Optional.h>
+#include <wtf/WeakPtr.h>
+#include <wtf/unicode/CharacterNames.h>
+
+// "X11/X.h" defines Complex to 0 and conflicts
+// with Complex value in CodePath enum.
+#ifdef Complex
+#undef Complex
+#endif
+
+namespace WebCore {
+
+class FloatPoint;
+class FloatRect;
+class FontData;
+class FontMetrics;
+class FontPlatformData;
+class FontSelector;
+class GlyphBuffer;
+class GraphicsContext;
+class LayoutRect;
+class RenderText;
+class TextLayout;
+class TextRun;
+
+struct GlyphData;
+
+struct GlyphOverflow {
+ GlyphOverflow()
+ : left(0)
+ , right(0)
+ , top(0)
+ , bottom(0)
+ , computeBounds(false)
+ {
+ }
+
+ inline bool isEmpty()
+ {
+ return !left && !right && !top && !bottom;
+ }
+
+ inline void extendTo(const GlyphOverflow& other)
+ {
+ left = std::max(left, other.left);
+ right = std::max(right, other.right);
+ top = std::max(top, other.top);
+ bottom = std::max(bottom, other.bottom);
+ }
+
+ bool operator!=(const GlyphOverflow& other)
+ {
+ return left != other.left || right != other.right || top != other.top || bottom != other.bottom;
+ }
+
+ int left;
+ int right;
+ int top;
+ int bottom;
+ bool computeBounds;
+};
+
+class GlyphToPathTranslator {
+public:
+ enum class GlyphUnderlineType {SkipDescenders, SkipGlyph, DrawOverGlyph};
+ virtual bool containsMorePaths() = 0;
+ virtual Path path() = 0;
+ virtual std::pair<float, float> extents() = 0;
+ virtual GlyphUnderlineType underlineType() = 0;
+ virtual void advance() = 0;
+ virtual ~GlyphToPathTranslator() { }
+};
+GlyphToPathTranslator::GlyphUnderlineType computeUnderlineType(const TextRun&, const GlyphBuffer&, unsigned index);
+
+class TextLayoutDeleter {
+public:
+ void operator()(TextLayout*) const;
+};
+
+class FontCascade {
+public:
+ WEBCORE_EXPORT FontCascade();
+ WEBCORE_EXPORT FontCascade(const FontCascadeDescription&, float letterSpacing = 0, float wordSpacing = 0);
+ // This constructor is only used if the platform wants to start with a native font.
+ WEBCORE_EXPORT FontCascade(const FontPlatformData&, FontSmoothingMode = AutoSmoothing);
+
+ FontCascade(const FontCascade&);
+ WEBCORE_EXPORT FontCascade& operator=(const FontCascade&);
+
+ WEBCORE_EXPORT bool operator==(const FontCascade& other) const;
+ bool operator!=(const FontCascade& other) const { return !(*this == other); }
+
+ const FontCascadeDescription& fontDescription() const { return m_fontDescription; }
+
+ int pixelSize() const { return fontDescription().computedPixelSize(); }
+ float size() const { return fontDescription().computedSize(); }
+
+ WEBCORE_EXPORT void update(RefPtr<FontSelector>&& = nullptr) const;
+
+ enum CustomFontNotReadyAction { DoNotPaintIfFontNotReady, UseFallbackIfFontNotReady };
+ WEBCORE_EXPORT float drawText(GraphicsContext&, const TextRun&, const FloatPoint&, unsigned from = 0, std::optional<unsigned> to = std::nullopt, CustomFontNotReadyAction = DoNotPaintIfFontNotReady) const;
+ static void drawGlyphs(GraphicsContext&, const Font&, const GlyphBuffer&, unsigned from, unsigned numGlyphs, const FloatPoint&, FontSmoothingMode);
+ void drawEmphasisMarks(GraphicsContext&, const TextRun&, const AtomicString& mark, const FloatPoint&, unsigned from = 0, std::optional<unsigned> to = std::nullopt) const;
+
+ DashArray dashesForIntersectionsWithRect(const TextRun&, const FloatPoint& textOrigin, const FloatRect& lineExtents) const;
+
+ WEBCORE_EXPORT float width(const TextRun&, HashSet<const Font*>* fallbackFonts = 0, GlyphOverflow* = 0) const;
+ float widthForSimpleText(StringView text) const;
+
+ std::unique_ptr<TextLayout, TextLayoutDeleter> createLayout(RenderText&, float xPos, bool collapseWhiteSpace) const;
+ static float width(TextLayout&, unsigned from, unsigned len, HashSet<const Font*>* fallbackFonts = 0);
+
+ int offsetForPosition(const TextRun&, float position, bool includePartialGlyphs) const;
+ void adjustSelectionRectForText(const TextRun&, LayoutRect& selectionRect, unsigned from = 0, std::optional<unsigned> to = std::nullopt) const;
+
+ bool isSmallCaps() const { return m_fontDescription.variantCaps() == FontVariantCaps::Small; }
+
+ float wordSpacing() const { return m_wordSpacing; }
+ float letterSpacing() const { return m_letterSpacing; }
+ void setWordSpacing(float s) { m_wordSpacing = s; }
+ void setLetterSpacing(float s) { m_letterSpacing = s; }
+ bool isFixedPitch() const;
+
+ FontRenderingMode renderingMode() const { return m_fontDescription.renderingMode(); }
+
+ bool enableKerning() const { return m_enableKerning; }
+ bool requiresShaping() const { return m_requiresShaping; }
+
+ const AtomicString& firstFamily() const { return m_fontDescription.firstFamily(); }
+ unsigned familyCount() const { return m_fontDescription.familyCount(); }
+ const AtomicString& familyAt(unsigned i) const { return m_fontDescription.familyAt(i); }
+
+ FontItalic italic() const { return m_fontDescription.italic(); }
+ FontWeight weight() const { return m_fontDescription.weight(); }
+ FontWidthVariant widthVariant() const { return m_fontDescription.widthVariant(); }
+
+ bool isPlatformFont() const { return m_fonts->isForPlatformFont(); }
+
+ const FontMetrics& fontMetrics() const { return primaryFont().fontMetrics(); }
+ float spaceWidth() const { return primaryFont().spaceWidth() + m_letterSpacing; }
+ float tabWidth(const Font&, unsigned tabSize, float position) const;
+ float tabWidth(unsigned tabSize, float position) const { return tabWidth(primaryFont(), tabSize, position); }
+ bool hasValidAverageCharWidth() const;
+ bool fastAverageCharWidthIfAvailable(float &width) const; // returns true on success
+
+ int emphasisMarkAscent(const AtomicString&) const;
+ int emphasisMarkDescent(const AtomicString&) const;
+ int emphasisMarkHeight(const AtomicString&) const;
+
+ const Font& primaryFont() const;
+ const FontRanges& fallbackRangesAt(unsigned) const;
+ GlyphData glyphDataForCharacter(UChar32, bool mirror, FontVariant = AutoVariant) const;
+
+ const Font* fontForCombiningCharacterSequence(const UChar*, size_t length) const;
+
+ static bool isCJKIdeograph(UChar32);
+ static bool isCJKIdeographOrSymbol(UChar32);
+
+ // Returns (the number of opportunities, whether the last expansion is a trailing expansion)
+ // If there are no opportunities, the bool will be true iff we are forbidding leading expansions.
+ static std::pair<unsigned, bool> expansionOpportunityCount(const StringView&, TextDirection, ExpansionBehavior);
+
+ // Whether or not there is an expansion opportunity just before the first character
+ // Note that this does not take a isAfterExpansion flag; this assumes that isAfterExpansion is false
+ // Here, "Leading" and "Trailing" are relevant after the line has been rearranged for bidi.
+ // ("Leading" means "left" and "Trailing" means "right.")
+ static bool leadingExpansionOpportunity(const StringView&, TextDirection);
+ static bool trailingExpansionOpportunity(const StringView&, TextDirection);
+
+ WEBCORE_EXPORT static void setShouldUseSmoothing(bool);
+ WEBCORE_EXPORT static bool shouldUseSmoothing();
+
+ enum CodePath { Auto, Simple, Complex, SimpleWithGlyphOverflow };
+ CodePath codePath(const TextRun&) const;
+ static CodePath characterRangeCodePath(const LChar*, unsigned) { return Simple; }
+ static CodePath characterRangeCodePath(const UChar*, unsigned len);
+
+ bool primaryFontIsSystemFont() const;
+
+ WeakPtr<FontCascade> createWeakPtr() const { return m_weakPtrFactory.createWeakPtr(); }
+
+private:
+ enum ForTextEmphasisOrNot { NotForTextEmphasis, ForTextEmphasis };
+
+ float glyphBufferForTextRun(CodePath, const TextRun&, unsigned from, unsigned to, GlyphBuffer&) const;
+ // Returns the initial in-stream advance.
+ float getGlyphsAndAdvancesForSimpleText(const TextRun&, unsigned from, unsigned to, GlyphBuffer&, ForTextEmphasisOrNot = NotForTextEmphasis) const;
+ void drawEmphasisMarksForSimpleText(GraphicsContext&, const TextRun&, const AtomicString& mark, const FloatPoint&, unsigned from, unsigned to) const;
+ void drawGlyphBuffer(GraphicsContext&, const GlyphBuffer&, FloatPoint&) const;
+ void drawEmphasisMarks(GraphicsContext&, const GlyphBuffer&, const AtomicString&, const FloatPoint&) const;
+ float floatWidthForSimpleText(const TextRun&, HashSet<const Font*>* fallbackFonts = 0, GlyphOverflow* = 0) const;
+ int offsetForPositionForSimpleText(const TextRun&, float position, bool includePartialGlyphs) const;
+ void adjustSelectionRectForSimpleText(const TextRun&, LayoutRect& selectionRect, unsigned from, unsigned to) const;
+
+ std::optional<GlyphData> getEmphasisMarkGlyphData(const AtomicString&) const;
+
+ static bool canReturnFallbackFontsForComplexText();
+ static bool canExpandAroundIdeographsInComplexText();
+
+ // Returns the initial in-stream advance.
+ float getGlyphsAndAdvancesForComplexText(const TextRun&, unsigned from, unsigned to, GlyphBuffer&, ForTextEmphasisOrNot = NotForTextEmphasis) const;
+ void drawEmphasisMarksForComplexText(GraphicsContext&, const TextRun&, const AtomicString& mark, const FloatPoint&, unsigned from, unsigned to) const;
+ float floatWidthForComplexText(const TextRun&, HashSet<const Font*>* fallbackFonts = 0, GlyphOverflow* = 0) const;
+ int offsetForPositionForComplexText(const TextRun&, float position, bool includePartialGlyphs) const;
+ void adjustSelectionRectForComplexText(const TextRun&, LayoutRect& selectionRect, unsigned from, unsigned to) const;
+
+ static std::pair<unsigned, bool> expansionOpportunityCountInternal(const LChar*, unsigned length, TextDirection, ExpansionBehavior);
+ static std::pair<unsigned, bool> expansionOpportunityCountInternal(const UChar*, unsigned length, TextDirection, ExpansionBehavior);
+
+ friend struct WidthIterator;
+
+public:
+#if ENABLE(TEXT_AUTOSIZING)
+ bool equalForTextAutoSizing(const FontCascade& other) const
+ {
+ return m_fontDescription.equalForTextAutoSizing(other.m_fontDescription)
+ && m_letterSpacing == other.m_letterSpacing
+ && m_wordSpacing == other.m_wordSpacing;
+ }
+#endif
+
+ // Useful for debugging the different font rendering code paths.
+ WEBCORE_EXPORT static void setCodePath(CodePath);
+ static CodePath codePath();
+ static CodePath s_codePath;
+
+ FontSelector* fontSelector() const;
+ static bool treatAsSpace(UChar c) { return c == ' ' || c == '\t' || c == '\n' || c == noBreakSpace; }
+ static bool treatAsZeroWidthSpace(UChar c) { return treatAsZeroWidthSpaceInComplexScript(c) || c == 0x200c || c == 0x200d; }
+ static bool treatAsZeroWidthSpaceInComplexScript(UChar c) { return c < 0x20 || (c >= 0x7F && c < 0xA0) || c == softHyphen || c == zeroWidthSpace || (c >= 0x200e && c <= 0x200f) || (c >= 0x202a && c <= 0x202e) || c == zeroWidthNoBreakSpace || c == objectReplacementCharacter; }
+ static bool canReceiveTextEmphasis(UChar32);
+
+ static inline UChar normalizeSpaces(UChar character)
+ {
+ if (treatAsSpace(character))
+ return space;
+
+ if (treatAsZeroWidthSpace(character))
+ return zeroWidthSpace;
+
+ return character;
+ }
+
+ static String normalizeSpaces(const LChar*, unsigned length);
+ static String normalizeSpaces(const UChar*, unsigned length);
+
+ bool useBackslashAsYenSymbol() const { return m_useBackslashAsYenSymbol; }
+ FontCascadeFonts* fonts() const { return m_fonts.get(); }
+
+private:
+ bool isLoadingCustomFonts() const;
+
+ bool advancedTextRenderingMode() const
+ {
+ auto textRenderingMode = m_fontDescription.textRenderingMode();
+ if (textRenderingMode == GeometricPrecision || textRenderingMode == OptimizeLegibility)
+ return true;
+ if (textRenderingMode == OptimizeSpeed)
+ return false;
+#if PLATFORM(COCOA)
+ return true;
+#else
+ return false;
+#endif
+ }
+
+ bool computeEnableKerning() const
+ {
+ auto kerning = m_fontDescription.kerning();
+ if (kerning == Kerning::Normal)
+ return true;
+ if (kerning == Kerning::NoShift)
+ return false;
+ return advancedTextRenderingMode();
+ }
+
+ bool computeRequiresShaping() const
+ {
+#if PLATFORM(COCOA)
+ if (!m_fontDescription.variantSettings().isAllNormal())
+ return true;
+ if (m_fontDescription.featureSettings().size())
+ return true;
+#endif
+ return advancedTextRenderingMode();
+ }
+
+ static int syntheticObliqueAngle() { return 14; }
+
+ FontCascadeDescription m_fontDescription;
+ mutable RefPtr<FontCascadeFonts> m_fonts;
+ WeakPtrFactory<FontCascade> m_weakPtrFactory;
+ float m_letterSpacing;
+ float m_wordSpacing;
+ mutable bool m_useBackslashAsYenSymbol;
+ mutable unsigned m_enableKerning : 1; // Computed from m_fontDescription.
+ mutable unsigned m_requiresShaping : 1; // Computed from m_fontDescription.
+};
+
+void invalidateFontCascadeCache();
+void pruneUnreferencedEntriesFromFontCascadeCache();
+void pruneSystemFallbackFonts();
+void clearWidthCaches();
+
+inline const Font& FontCascade::primaryFont() const
+{
+ ASSERT(m_fonts);
+ return m_fonts->primaryFont(m_fontDescription);
+}
+
+inline const FontRanges& FontCascade::fallbackRangesAt(unsigned index) const
+{
+ ASSERT(m_fonts);
+ return m_fonts->realizeFallbackRangesAt(m_fontDescription, index);
+}
+
+inline bool FontCascade::isFixedPitch() const
+{
+ ASSERT(m_fonts);
+ return m_fonts->isFixedPitch(m_fontDescription);
+}
+
+inline FontSelector* FontCascade::fontSelector() const
+{
+ return m_fonts ? m_fonts->fontSelector() : nullptr;
+}
+
+inline float FontCascade::tabWidth(const Font& font, unsigned tabSize, float position) const
+{
+ if (!tabSize)
+ return letterSpacing();
+ float tabWidth = tabSize * font.spaceWidth() + letterSpacing();
+ float tabDeltaWidth = tabWidth - fmodf(position, tabWidth);
+ return (tabDeltaWidth < font.spaceWidth() / 2) ? tabWidth : tabDeltaWidth;
+}
+
+}
+
+#endif
diff --git a/Source/WebCore/platform/graphics/FontCascadeFonts.cpp b/Source/WebCore/platform/graphics/FontCascadeFonts.cpp
new file mode 100644
index 000000000..3d5455b2f
--- /dev/null
+++ b/Source/WebCore/platform/graphics/FontCascadeFonts.cpp
@@ -0,0 +1,463 @@
+/*
+ * Copyright (C) 2006, 2013-2015 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Apple Inc. ("Apple") nor the names of
+ * its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "FontCascadeFonts.h"
+
+#include "FontCache.h"
+#include "FontCascade.h"
+#include "GlyphPage.h"
+
+namespace WebCore {
+
+class MixedFontGlyphPage {
+ WTF_MAKE_FAST_ALLOCATED;
+public:
+ MixedFontGlyphPage(const GlyphPage* initialPage)
+ {
+ if (initialPage) {
+ for (unsigned i = 0; i < GlyphPage::size; ++i)
+ setGlyphDataForIndex(i, initialPage->glyphDataForIndex(i));
+ }
+ }
+
+ GlyphData glyphDataForCharacter(UChar32 c) const
+ {
+ unsigned index = GlyphPage::indexForCodePoint(c);
+ ASSERT_WITH_SECURITY_IMPLICATION(index < GlyphPage::size);
+ return { m_glyphs[index], m_fonts[index] };
+ }
+
+ void setGlyphDataForCharacter(UChar32 c, GlyphData glyphData)
+ {
+ setGlyphDataForIndex(GlyphPage::indexForCodePoint(c), glyphData);
+ }
+
+private:
+ void setGlyphDataForIndex(unsigned index, const GlyphData& glyphData)
+ {
+ ASSERT_WITH_SECURITY_IMPLICATION(index < GlyphPage::size);
+ m_glyphs[index] = glyphData.glyph;
+ m_fonts[index] = glyphData.font;
+ }
+
+ Glyph m_glyphs[GlyphPage::size] { };
+ const Font* m_fonts[GlyphPage::size] { };
+};
+
+GlyphData FontCascadeFonts::GlyphPageCacheEntry::glyphDataForCharacter(UChar32 character)
+{
+ ASSERT(!(m_singleFont && m_mixedFont));
+ if (m_singleFont)
+ return m_singleFont->glyphDataForCharacter(character);
+ if (m_mixedFont)
+ return m_mixedFont->glyphDataForCharacter(character);
+ return 0;
+}
+
+void FontCascadeFonts::GlyphPageCacheEntry::setGlyphDataForCharacter(UChar32 character, GlyphData glyphData)
+{
+ ASSERT(!glyphDataForCharacter(character).glyph);
+ if (!m_mixedFont) {
+ m_mixedFont = std::make_unique<MixedFontGlyphPage>(m_singleFont.get());
+ m_singleFont = nullptr;
+ }
+ m_mixedFont->setGlyphDataForCharacter(character, glyphData);
+}
+
+void FontCascadeFonts::GlyphPageCacheEntry::setSingleFontPage(RefPtr<GlyphPage>&& page)
+{
+ ASSERT(isNull());
+ m_singleFont = page;
+}
+
+FontCascadeFonts::FontCascadeFonts(RefPtr<FontSelector>&& fontSelector)
+ : m_cachedPrimaryFont(nullptr)
+ , m_fontSelector(fontSelector)
+ , m_fontSelectorVersion(m_fontSelector ? m_fontSelector->version() : 0)
+ , m_generation(FontCache::singleton().generation())
+{
+}
+
+FontCascadeFonts::FontCascadeFonts(const FontPlatformData& platformData)
+ : m_cachedPrimaryFont(nullptr)
+ , m_fontSelectorVersion(0)
+ , m_generation(FontCache::singleton().generation())
+ , m_isForPlatformFont(true)
+{
+ m_realizedFallbackRanges.append(FontRanges(FontCache::singleton().fontForPlatformData(platformData)));
+}
+
+FontCascadeFonts::~FontCascadeFonts()
+{
+}
+
+void FontCascadeFonts::determinePitch(const FontCascadeDescription& description)
+{
+ auto& primaryRanges = realizeFallbackRangesAt(description, 0);
+ unsigned numRanges = primaryRanges.size();
+ if (numRanges == 1)
+ m_pitch = primaryRanges.fontForFirstRange().pitch();
+ else
+ m_pitch = VariablePitch;
+}
+
+bool FontCascadeFonts::isLoadingCustomFonts() const
+{
+ for (auto& fontRanges : m_realizedFallbackRanges) {
+ if (fontRanges.isLoading())
+ return true;
+ }
+ return false;
+}
+
+static FontRanges realizeNextFallback(const FontCascadeDescription& description, unsigned& index, FontSelector* fontSelector)
+{
+ ASSERT(index < description.familyCount());
+
+ auto& fontCache = FontCache::singleton();
+ while (index < description.familyCount()) {
+ const AtomicString& family = description.familyAt(index++);
+ if (family.isEmpty())
+ continue;
+ if (fontSelector) {
+ auto ranges = fontSelector->fontRangesForFamily(description, family);
+ if (!ranges.isNull())
+ return ranges;
+ }
+ if (auto font = fontCache.fontForFamily(description, family))
+ return FontRanges(WTFMove(font));
+ }
+ // We didn't find a font. Try to find a similar font using our own specific knowledge about our platform.
+ // For example on OS X, we know to map any families containing the words Arabic, Pashto, or Urdu to the
+ // Geeza Pro font.
+ for (auto& family : description.families()) {
+ if (auto font = fontCache.similarFont(description, family))
+ return FontRanges(WTFMove(font));
+ }
+ return { };
+}
+
+const FontRanges& FontCascadeFonts::realizeFallbackRangesAt(const FontCascadeDescription& description, unsigned index)
+{
+ if (index < m_realizedFallbackRanges.size())
+ return m_realizedFallbackRanges[index];
+
+ ASSERT(index == m_realizedFallbackRanges.size());
+ ASSERT(FontCache::singleton().generation() == m_generation);
+
+ m_realizedFallbackRanges.append(FontRanges());
+ auto& fontRanges = m_realizedFallbackRanges.last();
+
+ if (!index) {
+ fontRanges = realizeNextFallback(description, m_lastRealizedFallbackIndex, m_fontSelector.get());
+ if (fontRanges.isNull() && m_fontSelector)
+ fontRanges = m_fontSelector->fontRangesForFamily(description, standardFamily);
+ if (fontRanges.isNull())
+ fontRanges = FontRanges(FontCache::singleton().lastResortFallbackFont(description));
+ return fontRanges;
+ }
+
+ if (m_lastRealizedFallbackIndex < description.familyCount())
+ fontRanges = realizeNextFallback(description, m_lastRealizedFallbackIndex, m_fontSelector.get());
+
+ if (fontRanges.isNull() && m_fontSelector) {
+ ASSERT(m_lastRealizedFallbackIndex >= description.familyCount());
+
+ unsigned fontSelectorFallbackIndex = m_lastRealizedFallbackIndex - description.familyCount();
+ if (fontSelectorFallbackIndex == m_fontSelector->fallbackFontCount())
+ return fontRanges;
+ ++m_lastRealizedFallbackIndex;
+ fontRanges = FontRanges(m_fontSelector->fallbackFontAt(description, fontSelectorFallbackIndex));
+ }
+
+ return fontRanges;
+}
+
+static inline bool isInRange(UChar32 character, UChar32 lowerBound, UChar32 upperBound)
+{
+ return character >= lowerBound && character <= upperBound;
+}
+
+static bool shouldIgnoreRotation(UChar32 character)
+{
+ if (character == 0x000A7 || character == 0x000A9 || character == 0x000AE)
+ return true;
+
+ if (character == 0x000B6 || character == 0x000BC || character == 0x000BD || character == 0x000BE)
+ return true;
+
+ if (isInRange(character, 0x002E5, 0x002EB))
+ return true;
+
+ if (isInRange(character, 0x01100, 0x011FF) || isInRange(character, 0x01401, 0x0167F) || isInRange(character, 0x01800, 0x018FF))
+ return true;
+
+ if (character == 0x02016 || character == 0x02020 || character == 0x02021 || character == 0x2030 || character == 0x02031)
+ return true;
+
+ if (isInRange(character, 0x0203B, 0x0203D) || character == 0x02042 || character == 0x02044 || character == 0x02047
+ || character == 0x02048 || character == 0x02049 || character == 0x2051)
+ return true;
+
+ if (isInRange(character, 0x02065, 0x02069) || isInRange(character, 0x020DD, 0x020E0)
+ || isInRange(character, 0x020E2, 0x020E4) || isInRange(character, 0x02100, 0x02117)
+ || isInRange(character, 0x02119, 0x02131) || isInRange(character, 0x02133, 0x0213F))
+ return true;
+
+ if (isInRange(character, 0x02145, 0x0214A) || character == 0x0214C || character == 0x0214D
+ || isInRange(character, 0x0214F, 0x0218F))
+ return true;
+
+ if (isInRange(character, 0x02300, 0x02307) || isInRange(character, 0x0230C, 0x0231F)
+ || isInRange(character, 0x02322, 0x0232B) || isInRange(character, 0x0237D, 0x0239A)
+ || isInRange(character, 0x023B4, 0x023B6) || isInRange(character, 0x023BA, 0x023CF)
+ || isInRange(character, 0x023D1, 0x023DB) || isInRange(character, 0x023E2, 0x024FF))
+ return true;
+
+ if (isInRange(character, 0x025A0, 0x02619) || isInRange(character, 0x02620, 0x02767)
+ || isInRange(character, 0x02776, 0x02793) || isInRange(character, 0x02B12, 0x02B2F)
+ || isInRange(character, 0x02B4D, 0x02BFF) || isInRange(character, 0x02E80, 0x03007))
+ return true;
+
+ if (character == 0x03012 || character == 0x03013 || isInRange(character, 0x03020, 0x0302F)
+ || isInRange(character, 0x03031, 0x0309F) || isInRange(character, 0x030A1, 0x030FB)
+ || isInRange(character, 0x030FD, 0x0A4CF))
+ return true;
+
+ if (isInRange(character, 0x0A840, 0x0A87F) || isInRange(character, 0x0A960, 0x0A97F)
+ || isInRange(character, 0x0AC00, 0x0D7FF) || isInRange(character, 0x0E000, 0x0FAFF))
+ return true;
+
+ if (isInRange(character, 0x0FE10, 0x0FE1F) || isInRange(character, 0x0FE30, 0x0FE48)
+ || isInRange(character, 0x0FE50, 0x0FE57) || isInRange(character, 0x0FE5F, 0x0FE62)
+ || isInRange(character, 0x0FE67, 0x0FE6F))
+ return true;
+
+ if (isInRange(character, 0x0FF01, 0x0FF07) || isInRange(character, 0x0FF0A, 0x0FF0C)
+ || isInRange(character, 0x0FF0E, 0x0FF19) || character == 0x0FF1B || isInRange(character, 0x0FF1F, 0x0FF3A))
+ return true;
+
+ if (character == 0x0FF3C || character == 0x0FF3E)
+ return true;
+
+ if (isInRange(character, 0x0FF40, 0x0FF5A) || isInRange(character, 0x0FFE0, 0x0FFE2)
+ || isInRange(character, 0x0FFE4, 0x0FFE7) || isInRange(character, 0x0FFF0, 0x0FFF8)
+ || character == 0x0FFFD)
+ return true;
+
+ if (isInRange(character, 0x13000, 0x1342F) || isInRange(character, 0x1B000, 0x1B0FF)
+ || isInRange(character, 0x1D000, 0x1D1FF) || isInRange(character, 0x1D300, 0x1D37F)
+ || isInRange(character, 0x1F000, 0x1F64F) || isInRange(character, 0x1F680, 0x1F77F))
+ return true;
+
+ if (isInRange(character, 0x20000, 0x2FFFD) || isInRange(character, 0x30000, 0x3FFFD))
+ return true;
+
+ return false;
+}
+
+#if PLATFORM(COCOA) || USE(CAIRO)
+static GlyphData glyphDataForCJKCharacterWithoutSyntheticItalic(UChar32 character, GlyphData& data)
+{
+ GlyphData nonItalicData = data.font->nonSyntheticItalicFont().glyphDataForCharacter(character);
+ if (nonItalicData.font)
+ return nonItalicData;
+ return data;
+}
+#endif
+
+static GlyphData glyphDataForNonCJKCharacterWithGlyphOrientation(UChar32 character, NonCJKGlyphOrientation orientation, const GlyphData& data)
+{
+ if (orientation == NonCJKGlyphOrientation::Upright || shouldIgnoreRotation(character)) {
+ GlyphData uprightData = data.font->uprightOrientationFont().glyphDataForCharacter(character);
+ // If the glyphs are the same, then we know we can just use the horizontal glyph rotated vertically to be upright.
+ if (data.glyph == uprightData.glyph)
+ return data;
+ // The glyphs are distinct, meaning that the font has a vertical-right glyph baked into it. We can't use that
+ // glyph, so we fall back to the upright data and use the horizontal glyph.
+ if (uprightData.font)
+ return uprightData;
+ } else if (orientation == NonCJKGlyphOrientation::Mixed) {
+ GlyphData verticalRightData = data.font->verticalRightOrientationFont().glyphDataForCharacter(character);
+ // If the glyphs are distinct, we will make the assumption that the font has a vertical-right glyph baked
+ // into it.
+ if (data.glyph != verticalRightData.glyph)
+ return data;
+ // The glyphs are identical, meaning that we should just use the horizontal glyph.
+ if (verticalRightData.font)
+ return verticalRightData;
+ }
+ return data;
+}
+
+GlyphData FontCascadeFonts::glyphDataForSystemFallback(UChar32 c, const FontCascadeDescription& description, FontVariant variant)
+{
+ // System fallback is character-dependent.
+ auto& primaryRanges = realizeFallbackRangesAt(description, 0);
+ auto* originalFont = primaryRanges.fontForCharacter(c);
+ if (!originalFont)
+ originalFont = &primaryRanges.fontForFirstRange();
+
+ auto systemFallbackFont = originalFont->systemFallbackFontForCharacter(c, description, m_isForPlatformFont);
+ if (!systemFallbackFont)
+ return GlyphData();
+
+ if (systemFallbackFont->platformData().orientation() == Vertical && !systemFallbackFont->hasVerticalGlyphs() && FontCascade::isCJKIdeographOrSymbol(c))
+ variant = BrokenIdeographVariant;
+
+ GlyphData fallbackGlyphData;
+ if (variant == NormalVariant)
+ fallbackGlyphData = systemFallbackFont->glyphDataForCharacter(c);
+ else
+ fallbackGlyphData = systemFallbackFont->variantFont(description, variant)->glyphDataForCharacter(c);
+
+ if (fallbackGlyphData.font && fallbackGlyphData.font->platformData().orientation() == Vertical && !fallbackGlyphData.font->isTextOrientationFallback()) {
+ if (variant == NormalVariant && !FontCascade::isCJKIdeographOrSymbol(c))
+ fallbackGlyphData = glyphDataForNonCJKCharacterWithGlyphOrientation(c, description.nonCJKGlyphOrientation(), fallbackGlyphData);
+#if PLATFORM(COCOA) || USE(CAIRO)
+ if (fallbackGlyphData.font->platformData().syntheticOblique() && FontCascade::isCJKIdeographOrSymbol(c))
+ fallbackGlyphData = glyphDataForCJKCharacterWithoutSyntheticItalic(c, fallbackGlyphData);
+#endif
+ }
+
+ // Keep the system fallback fonts we use alive.
+ if (fallbackGlyphData.glyph)
+ m_systemFallbackFontSet.add(WTFMove(systemFallbackFont));
+
+ return fallbackGlyphData;
+}
+
+GlyphData FontCascadeFonts::glyphDataForVariant(UChar32 c, const FontCascadeDescription& description, FontVariant variant, unsigned fallbackIndex)
+{
+ while (true) {
+ auto& fontRanges = realizeFallbackRangesAt(description, fallbackIndex++);
+ if (fontRanges.isNull())
+ break;
+ GlyphData data = fontRanges.glyphDataForCharacter(c);
+ if (!data.font)
+ continue;
+ // The variantFont function should not normally return 0.
+ // But if it does, we will just render the capital letter big.
+ if (const Font* variantFont = data.font->variantFont(description, variant))
+ return variantFont->glyphDataForCharacter(c);
+ return data;
+ }
+
+ return glyphDataForSystemFallback(c, description, variant);
+}
+
+GlyphData FontCascadeFonts::glyphDataForNormalVariant(UChar32 c, const FontCascadeDescription& description)
+{
+ for (unsigned fallbackIndex = 0; ; ++fallbackIndex) {
+ auto& fontRanges = realizeFallbackRangesAt(description, fallbackIndex);
+ if (fontRanges.isNull())
+ break;
+ GlyphData data = fontRanges.glyphDataForCharacter(c);
+ if (!data.font)
+ continue;
+ if (data.font->platformData().orientation() == Vertical && !data.font->isTextOrientationFallback()) {
+ if (!FontCascade::isCJKIdeographOrSymbol(c))
+ return glyphDataForNonCJKCharacterWithGlyphOrientation(c, description.nonCJKGlyphOrientation(), data);
+
+ if (!data.font->hasVerticalGlyphs()) {
+ // Use the broken ideograph font data. The broken ideograph font will use the horizontal width of glyphs
+ // to make sure you get a square (even for broken glyphs like symbols used for punctuation).
+ return glyphDataForVariant(c, description, BrokenIdeographVariant, fallbackIndex);
+ }
+#if PLATFORM(COCOA) || USE(CAIRO)
+ if (data.font->platformData().syntheticOblique())
+ return glyphDataForCJKCharacterWithoutSyntheticItalic(c, data);
+#endif
+ }
+ return data;
+ }
+
+ return glyphDataForSystemFallback(c, description, NormalVariant);
+}
+
+static RefPtr<GlyphPage> glyphPageFromFontRanges(unsigned pageNumber, const FontRanges& fontRanges)
+{
+ const Font* font = nullptr;
+ UChar32 pageRangeFrom = pageNumber * GlyphPage::size;
+ UChar32 pageRangeTo = pageRangeFrom + GlyphPage::size - 1;
+ for (unsigned i = 0; i < fontRanges.size(); ++i) {
+ auto& range = fontRanges.rangeAt(i);
+ if (range.to()) {
+ if (range.from() <= pageRangeFrom && pageRangeTo <= range.to())
+ font = range.font();
+ break;
+ }
+ }
+ if (!font || font->platformData().orientation() == Vertical)
+ return nullptr;
+
+ return const_cast<GlyphPage*>(font->glyphPage(pageNumber));
+}
+
+GlyphData FontCascadeFonts::glyphDataForCharacter(UChar32 c, const FontCascadeDescription& description, FontVariant variant)
+{
+ ASSERT(isMainThread());
+ ASSERT(variant != AutoVariant);
+
+ if (variant != NormalVariant)
+ return glyphDataForVariant(c, description, variant, 0);
+
+ const unsigned pageNumber = GlyphPage::pageNumberForCodePoint(c);
+
+ auto& cacheEntry = pageNumber ? m_cachedPages.add(pageNumber, GlyphPageCacheEntry()).iterator->value : m_cachedPageZero;
+
+ // Initialize cache with a full page of glyph mappings from a single font.
+ if (cacheEntry.isNull())
+ cacheEntry.setSingleFontPage(glyphPageFromFontRanges(pageNumber, realizeFallbackRangesAt(description, 0)));
+
+ GlyphData glyphData = cacheEntry.glyphDataForCharacter(c);
+ if (!glyphData.glyph) {
+ // No glyph, resolve per-character.
+ glyphData = glyphDataForNormalVariant(c, description);
+ // Cache the results.
+ cacheEntry.setGlyphDataForCharacter(c, glyphData);
+ }
+
+ return glyphData;
+}
+
+void FontCascadeFonts::pruneSystemFallbacks()
+{
+ if (m_systemFallbackFontSet.isEmpty())
+ return;
+ // Mutable glyph pages may reference fallback fonts.
+ if (m_cachedPageZero.isMixedFont())
+ m_cachedPageZero = { };
+ m_cachedPages.removeIf([](auto& keyAndValue) {
+ return keyAndValue.value.isMixedFont();
+ });
+ m_systemFallbackFontSet.clear();
+}
+
+}
diff --git a/Source/WebCore/platform/graphics/FontCascadeFonts.h b/Source/WebCore/platform/graphics/FontCascadeFonts.h
new file mode 100644
index 000000000..fcc1c37a9
--- /dev/null
+++ b/Source/WebCore/platform/graphics/FontCascadeFonts.h
@@ -0,0 +1,139 @@
+/*
+ * Copyright (C) 2006, 2010, 2013-2015 Apple Inc. All rights reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef FontCascadeFonts_h
+#define FontCascadeFonts_h
+
+#include "Font.h"
+#include "FontRanges.h"
+#include "FontSelector.h"
+#include "GlyphPage.h"
+#include "WidthCache.h"
+#include <wtf/Forward.h>
+#include <wtf/MainThread.h>
+
+#if PLATFORM(IOS)
+#include "WebCoreThread.h"
+#endif
+
+namespace WebCore {
+
+class FontCascadeDescription;
+class FontPlatformData;
+class FontSelector;
+class GraphicsContext;
+class IntRect;
+class MixedFontGlyphPage;
+
+class FontCascadeFonts : public RefCounted<FontCascadeFonts> {
+ WTF_MAKE_NONCOPYABLE(FontCascadeFonts);
+public:
+ static Ref<FontCascadeFonts> create(RefPtr<FontSelector>&& fontSelector) { return adoptRef(*new FontCascadeFonts(WTFMove(fontSelector))); }
+ static Ref<FontCascadeFonts> createForPlatformFont(const FontPlatformData& platformData) { return adoptRef(*new FontCascadeFonts(platformData)); }
+
+ WEBCORE_EXPORT ~FontCascadeFonts();
+
+ bool isForPlatformFont() const { return m_isForPlatformFont; }
+
+ GlyphData glyphDataForCharacter(UChar32, const FontCascadeDescription&, FontVariant);
+
+ bool isFixedPitch(const FontCascadeDescription&);
+ void determinePitch(const FontCascadeDescription&);
+
+ bool isLoadingCustomFonts() const;
+
+ FontSelector* fontSelector() { return m_fontSelector.get(); }
+ // FIXME: It should be possible to combine fontSelectorVersion and generation.
+ unsigned fontSelectorVersion() const { return m_fontSelectorVersion; }
+ unsigned generation() const { return m_generation; }
+
+ WidthCache& widthCache() { return m_widthCache; }
+ const WidthCache& widthCache() const { return m_widthCache; }
+
+ const Font& primaryFont(const FontCascadeDescription&);
+ WEBCORE_EXPORT const FontRanges& realizeFallbackRangesAt(const FontCascadeDescription&, unsigned fallbackIndex);
+
+ void pruneSystemFallbacks();
+
+private:
+ FontCascadeFonts(RefPtr<FontSelector>&&);
+ FontCascadeFonts(const FontPlatformData&);
+
+ GlyphData glyphDataForSystemFallback(UChar32, const FontCascadeDescription&, FontVariant);
+ GlyphData glyphDataForNormalVariant(UChar32, const FontCascadeDescription&);
+ GlyphData glyphDataForVariant(UChar32, const FontCascadeDescription&, FontVariant, unsigned fallbackIndex);
+
+ Vector<FontRanges, 1> m_realizedFallbackRanges;
+ unsigned m_lastRealizedFallbackIndex { 0 };
+
+ class GlyphPageCacheEntry {
+ public:
+ GlyphData glyphDataForCharacter(UChar32);
+
+ void setSingleFontPage(RefPtr<GlyphPage>&&);
+ void setGlyphDataForCharacter(UChar32, GlyphData);
+
+ bool isNull() const { return !m_singleFont && !m_mixedFont; }
+ bool isMixedFont() const { return !!m_mixedFont; }
+
+ private:
+ // Only one of these is non-null.
+ RefPtr<GlyphPage> m_singleFont;
+ std::unique_ptr<MixedFontGlyphPage> m_mixedFont;
+ };
+
+ GlyphPageCacheEntry m_cachedPageZero;
+ HashMap<int, GlyphPageCacheEntry> m_cachedPages;
+
+ HashSet<RefPtr<Font>> m_systemFallbackFontSet;
+
+ const Font* m_cachedPrimaryFont;
+ RefPtr<FontSelector> m_fontSelector;
+
+ WidthCache m_widthCache;
+
+ unsigned m_fontSelectorVersion;
+ unsigned short m_generation;
+ Pitch m_pitch { UnknownPitch };
+ bool m_isForPlatformFont { false };
+};
+
+inline bool FontCascadeFonts::isFixedPitch(const FontCascadeDescription& description)
+{
+ if (m_pitch == UnknownPitch)
+ determinePitch(description);
+ return m_pitch == FixedPitch;
+};
+
+inline const Font& FontCascadeFonts::primaryFont(const FontCascadeDescription& description)
+{
+ ASSERT(isMainThread());
+ if (!m_cachedPrimaryFont) {
+ auto& primaryRanges = realizeFallbackRangesAt(description, 0);
+ m_cachedPrimaryFont = primaryRanges.fontForCharacter(' ');
+ if (!m_cachedPrimaryFont)
+ m_cachedPrimaryFont = &primaryRanges.fontForFirstRange();
+ }
+ return *m_cachedPrimaryFont;
+}
+
+}
+
+#endif
diff --git a/Source/WebCore/platform/graphics/FontData.cpp b/Source/WebCore/platform/graphics/FontData.cpp
deleted file mode 100644
index c40e13cbd..000000000
--- a/Source/WebCore/platform/graphics/FontData.cpp
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * Copyright (C) 2008 Apple Inc. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
- * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
- * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#include "config.h"
-#include "FontData.h"
-
-namespace WebCore {
-
-FontData::~FontData()
-{
-}
-
-} // namespace WebCore
diff --git a/Source/WebCore/platform/graphics/FontDescription.cpp b/Source/WebCore/platform/graphics/FontDescription.cpp
index 84bb5af16..0c03e9a3f 100644
--- a/Source/WebCore/platform/graphics/FontDescription.cpp
+++ b/Source/WebCore/platform/graphics/FontDescription.cpp
@@ -11,7 +11,7 @@
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
- * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ * 3. Neither the name of Apple Inc. ("Apple") nor the names of
* its contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
@@ -30,93 +30,125 @@
#include "config.h"
#include "FontDescription.h"
+#include "LocaleToScriptMapping.h"
+
namespace WebCore {
-struct SameSizeAsFontDescription {
- void* pointers[2];
- float sizes[2];
- // FXIME: Make them fit into one word.
- uint32_t bitfields;
- uint32_t bitfields2 : 8;
+struct SameSizeAsFontCascadeDescription {
+ Vector<void*> vector;
+#if ENABLE(VARIATION_FONTS)
+ Vector<void*> vector2;
+#else
+ char c;
+#endif
+ AtomicString string;
+ float size;
+ unsigned bitfields1;
+ unsigned bitfields2 : 22;
+ void* array;
+ float size2;
+ unsigned bitfields3 : 10;
};
-COMPILE_ASSERT(sizeof(FontDescription) == sizeof(SameSizeAsFontDescription), FontDescription_should_stay_small);
-
-FontWeight FontDescription::lighterWeight(void) const
+COMPILE_ASSERT(sizeof(FontCascadeDescription) == sizeof(SameSizeAsFontCascadeDescription), FontCascadeDescription_should_stay_small);
+
+FontDescription::FontDescription()
+ : m_orientation(Horizontal)
+ , m_nonCJKGlyphOrientation(static_cast<unsigned>(NonCJKGlyphOrientation::Mixed))
+ , m_widthVariant(RegularWidth)
+ , m_italic(FontItalicOff)
+ , m_weight(FontWeightNormal)
+ , m_renderingMode(static_cast<unsigned>(FontRenderingMode::Normal))
+ , m_textRendering(AutoTextRendering)
+ , m_script(USCRIPT_COMMON)
+ , m_fontSynthesis(FontSynthesisWeight | FontSynthesisStyle | FontSynthesisSmallCaps)
+ , m_variantCommonLigatures(static_cast<unsigned>(FontVariantLigatures::Normal))
+ , m_variantDiscretionaryLigatures(static_cast<unsigned>(FontVariantLigatures::Normal))
+ , m_variantHistoricalLigatures(static_cast<unsigned>(FontVariantLigatures::Normal))
+ , m_variantContextualAlternates(static_cast<unsigned>(FontVariantLigatures::Normal))
+ , m_variantPosition(static_cast<unsigned>(FontVariantPosition::Normal))
+ , m_variantCaps(static_cast<unsigned>(FontVariantCaps::Normal))
+ , m_variantNumericFigure(static_cast<unsigned>(FontVariantNumericFigure::Normal))
+ , m_variantNumericSpacing(static_cast<unsigned>(FontVariantNumericSpacing::Normal))
+ , m_variantNumericFraction(static_cast<unsigned>(FontVariantNumericFraction::Normal))
+ , m_variantNumericOrdinal(static_cast<unsigned>(FontVariantNumericOrdinal::Normal))
+ , m_variantNumericSlashedZero(static_cast<unsigned>(FontVariantNumericSlashedZero::Normal))
+ , m_variantAlternates(static_cast<unsigned>(FontVariantAlternates::Normal))
+ , m_variantEastAsianVariant(static_cast<unsigned>(FontVariantEastAsianVariant::Normal))
+ , m_variantEastAsianWidth(static_cast<unsigned>(FontVariantEastAsianWidth::Normal))
+ , m_variantEastAsianRuby(static_cast<unsigned>(FontVariantEastAsianRuby::Normal))
{
- // FIXME: Should actually return the CSS weight corresponding to next lightest
- // weight of the currently used font family.
- switch (m_weight) {
- case FontWeight100:
- case FontWeight200:
- return FontWeight100;
-
- case FontWeight300:
- return FontWeight200;
+}
- case FontWeight400:
- case FontWeight500:
- return FontWeight300;
+FontTraitsMask FontDescription::traitsMask() const
+{
+ return static_cast<FontTraitsMask>((m_italic ? FontStyleItalicMask : FontStyleNormalMask)
+ | (FontWeight100Mask << (m_weight - FontWeight100)));
+
+}
- case FontWeight600:
- case FontWeight700:
- return FontWeight400;
+void FontDescription::setLocale(const AtomicString& locale)
+{
+ m_locale = locale;
+ m_script = localeToScriptCodeForFontSelection(m_locale);
+}
- case FontWeight800:
- return FontWeight500;
+FontCascadeDescription::FontCascadeDescription()
+ : m_isAbsoluteSize(false)
+ , m_kerning(static_cast<unsigned>(Kerning::Auto))
+ , m_keywordSize(0)
+ , m_fontSmoothing(AutoSmoothing)
+ , m_isSpecifiedFont(false)
+{
+}
- case FontWeight900:
- return FontWeight700;
+FontWeight FontCascadeDescription::lighterWeight(void) const
+{
+ switch (weight()) {
+ case FontWeight100:
+ case FontWeight200:
+ case FontWeight300:
+ case FontWeight400:
+ case FontWeight500:
+ return FontWeight100;
+
+ case FontWeight600:
+ case FontWeight700:
+ return FontWeight400;
+
+ case FontWeight800:
+ case FontWeight900:
+ return FontWeight700;
}
ASSERT_NOT_REACHED();
return FontWeightNormal;
}
-FontWeight FontDescription::bolderWeight(void) const
+FontWeight FontCascadeDescription::bolderWeight(void) const
{
- // FIXME: Should actually return the CSS weight corresponding to next heaviest
- // weight of the currently used font family.
- switch (m_weight) {
- case FontWeight100:
- case FontWeight200:
- return FontWeight300;
-
- case FontWeight300:
- return FontWeight400;
-
- case FontWeight400:
- case FontWeight500:
- return FontWeight700;
-
- case FontWeight600:
- case FontWeight700:
- return FontWeight800;
-
- case FontWeight800:
- case FontWeight900:
- return FontWeight900;
+ switch (weight()) {
+ case FontWeight100:
+ case FontWeight200:
+ case FontWeight300:
+ return FontWeight400;
+
+ case FontWeight400:
+ case FontWeight500:
+ return FontWeight700;
+
+ case FontWeight600:
+ case FontWeight700:
+ case FontWeight800:
+ case FontWeight900:
+ return FontWeight900;
}
ASSERT_NOT_REACHED();
return FontWeightNormal;
}
-FontTraitsMask FontDescription::traitsMask() const
-{
- return static_cast<FontTraitsMask>((m_italic ? FontStyleItalicMask : FontStyleNormalMask)
- | (m_smallCaps ? FontVariantSmallCapsMask : FontVariantNormalMask)
- | (FontWeight100Mask << (m_weight - FontWeight100)));
-
-}
+#if ENABLE(TEXT_AUTOSIZING)
-FontDescription FontDescription::makeNormalFeatureSettings() const
-{
- FontDescription normalDescription(*this);
- normalDescription.setFeatureSettings(0);
- return normalDescription;
-}
-
-#if ENABLE(IOS_TEXT_AUTOSIZING)
-bool FontDescription::familiesEqualForTextAutoSizing(const FontDescription& other) const
+bool FontCascadeDescription::familiesEqualForTextAutoSizing(const FontCascadeDescription& other) const
{
unsigned thisFamilyCount = familyCount();
unsigned otherFamilyCount = other.familyCount();
@@ -125,12 +157,13 @@ bool FontDescription::familiesEqualForTextAutoSizing(const FontDescription& othe
return false;
for (unsigned i = 0; i < thisFamilyCount; ++i) {
- if (!equalIgnoringCase(familyAt(i), other.familyAt(i)))
+ if (!equalIgnoringASCIICase(familyAt(i), other.familyAt(i)))
return false;
}
return true;
}
-#endif // ENABLE(IOS_TEXT_AUTOSIZING)
+
+#endif // ENABLE(TEXT_AUTOSIZING)
} // namespace WebCore
diff --git a/Source/WebCore/platform/graphics/FontDescription.h b/Source/WebCore/platform/graphics/FontDescription.h
index 3ff17909c..a7fda4012 100644
--- a/Source/WebCore/platform/graphics/FontDescription.h
+++ b/Source/WebCore/platform/graphics/FontDescription.h
@@ -25,84 +25,182 @@
#ifndef FontDescription_h
#define FontDescription_h
-#include "FontFeatureSettings.h"
-#include "FontOrientation.h"
-#include "FontRenderingMode.h"
-#include "FontSmoothingMode.h"
-#include "FontTraitsMask.h"
-#include "FontWidthVariant.h"
-#include "NonCJKGlyphOrientation.h"
-#include "TextRenderingMode.h"
+#include "CSSValueKeywords.h"
+#include "FontTaggedSettings.h"
+#include "TextFlags.h"
#include "WebKitFontFamilyNames.h"
+#include <unicode/uscript.h>
#include <wtf/MathExtras.h>
#include <wtf/RefCountedArray.h>
-#include <wtf/RefPtr.h>
namespace WebCore {
using namespace WebKitFontFamilyNames;
-enum FontWeight {
- FontWeight100,
- FontWeight200,
- FontWeight300,
- FontWeight400,
- FontWeight500,
- FontWeight600,
- FontWeight700,
- FontWeight800,
- FontWeight900,
- FontWeightNormal = FontWeight400,
- FontWeightBold = FontWeight700
-};
+class FontDescription {
+public:
+ WEBCORE_EXPORT FontDescription();
-enum FontItalic {
- FontItalicOff = 0,
- FontItalicOn = 1
-};
+ bool operator==(const FontDescription&) const;
+ bool operator!=(const FontDescription& other) const { return !(*this == other); }
-enum FontSmallCaps {
- FontSmallCapsOff = 0,
- FontSmallCapsOn = 1
-};
+ float computedSize() const { return m_computedSize; }
+ FontItalic italic() const { return static_cast<FontItalic>(m_italic); }
+ int computedPixelSize() const { return int(m_computedSize + 0.5f); }
+ FontWeight weight() const { return static_cast<FontWeight>(m_weight); }
+ FontWeight lighterWeight() const;
+ FontWeight bolderWeight() const;
+ FontRenderingMode renderingMode() const { return static_cast<FontRenderingMode>(m_renderingMode); }
+ TextRenderingMode textRenderingMode() const { return static_cast<TextRenderingMode>(m_textRendering); }
+ UScriptCode script() const { return static_cast<UScriptCode>(m_script); }
+ const AtomicString& locale() const { return m_locale; }
-class FontDescription {
-public:
- enum GenericFamilyType { NoFamily, StandardFamily, SerifFamily, SansSerifFamily,
- MonospaceFamily, CursiveFamily, FantasyFamily, PictographFamily };
-
- enum Kerning { AutoKerning, NormalKerning, NoneKerning };
-
- enum LigaturesState { NormalLigaturesState, DisabledLigaturesState, EnabledLigaturesState };
-
- FontDescription()
- : m_families(1)
- , m_specifiedSize(0)
- , m_computedSize(0)
- , m_orientation(Horizontal)
- , m_nonCJKGlyphOrientation(NonCJKGlyphOrientationVerticalRight)
- , m_widthVariant(RegularWidth)
- , m_italic(FontItalicOff)
- , m_smallCaps(FontSmallCapsOff)
- , m_isAbsoluteSize(false)
- , m_weight(FontWeightNormal)
- , m_genericFamily(NoFamily)
- , m_usePrinterFont(false)
- , m_renderingMode(NormalRenderingMode)
- , m_kerning(AutoKerning)
- , m_commonLigaturesState(NormalLigaturesState)
- , m_discretionaryLigaturesState(NormalLigaturesState)
- , m_historicalLigaturesState(NormalLigaturesState)
- , m_keywordSize(0)
- , m_fontSmoothing(AutoSmoothing)
- , m_textRendering(AutoTextRendering)
- , m_isSpecifiedFont(false)
- , m_script(USCRIPT_COMMON)
+ FontOrientation orientation() const { return static_cast<FontOrientation>(m_orientation); }
+ NonCJKGlyphOrientation nonCJKGlyphOrientation() const { return static_cast<NonCJKGlyphOrientation>(m_nonCJKGlyphOrientation); }
+ FontWidthVariant widthVariant() const { return static_cast<FontWidthVariant>(m_widthVariant); }
+ const FontFeatureSettings& featureSettings() const { return m_featureSettings; }
+ const FontVariationSettings& variationSettings() const { return m_variationSettings; }
+ FontSynthesis fontSynthesis() const { return static_cast<FontSynthesis>(m_fontSynthesis); }
+ FontVariantLigatures variantCommonLigatures() const { return static_cast<FontVariantLigatures>(m_variantCommonLigatures); }
+ FontVariantLigatures variantDiscretionaryLigatures() const { return static_cast<FontVariantLigatures>(m_variantDiscretionaryLigatures); }
+ FontVariantLigatures variantHistoricalLigatures() const { return static_cast<FontVariantLigatures>(m_variantHistoricalLigatures); }
+ FontVariantLigatures variantContextualAlternates() const { return static_cast<FontVariantLigatures>(m_variantContextualAlternates); }
+ FontVariantPosition variantPosition() const { return static_cast<FontVariantPosition>(m_variantPosition); }
+ FontVariantCaps variantCaps() const { return static_cast<FontVariantCaps>(m_variantCaps); }
+ FontVariantNumericFigure variantNumericFigure() const { return static_cast<FontVariantNumericFigure>(m_variantNumericFigure); }
+ FontVariantNumericSpacing variantNumericSpacing() const { return static_cast<FontVariantNumericSpacing>(m_variantNumericSpacing); }
+ FontVariantNumericFraction variantNumericFraction() const { return static_cast<FontVariantNumericFraction>(m_variantNumericFraction); }
+ FontVariantNumericOrdinal variantNumericOrdinal() const { return static_cast<FontVariantNumericOrdinal>(m_variantNumericOrdinal); }
+ FontVariantNumericSlashedZero variantNumericSlashedZero() const { return static_cast<FontVariantNumericSlashedZero>(m_variantNumericSlashedZero); }
+ FontVariantAlternates variantAlternates() const { return static_cast<FontVariantAlternates>(m_variantAlternates); }
+ FontVariantEastAsianVariant variantEastAsianVariant() const { return static_cast<FontVariantEastAsianVariant>(m_variantEastAsianVariant); }
+ FontVariantEastAsianWidth variantEastAsianWidth() const { return static_cast<FontVariantEastAsianWidth>(m_variantEastAsianWidth); }
+ FontVariantEastAsianRuby variantEastAsianRuby() const { return static_cast<FontVariantEastAsianRuby>(m_variantEastAsianRuby); }
+ FontVariantSettings variantSettings() const
{
+ return { variantCommonLigatures(),
+ variantDiscretionaryLigatures(),
+ variantHistoricalLigatures(),
+ variantContextualAlternates(),
+ variantPosition(),
+ variantCaps(),
+ variantNumericFigure(),
+ variantNumericSpacing(),
+ variantNumericFraction(),
+ variantNumericOrdinal(),
+ variantNumericSlashedZero(),
+ variantAlternates(),
+ variantEastAsianVariant(),
+ variantEastAsianWidth(),
+ variantEastAsianRuby() };
}
- bool operator==(const FontDescription&) const;
- bool operator!=(const FontDescription& other) const { return !(*this == other); }
+ void setComputedSize(float s) { m_computedSize = clampToFloat(s); }
+ void setItalic(FontItalic i) { m_italic = i; }
+ void setIsItalic(bool i) { setItalic(i ? FontItalicOn : FontItalicOff); }
+ void setWeight(FontWeight w) { m_weight = w; }
+ void setRenderingMode(FontRenderingMode mode) { m_renderingMode = static_cast<unsigned>(mode); }
+ void setTextRenderingMode(TextRenderingMode rendering) { m_textRendering = rendering; }
+ void setOrientation(FontOrientation orientation) { m_orientation = orientation; }
+ void setNonCJKGlyphOrientation(NonCJKGlyphOrientation orientation) { m_nonCJKGlyphOrientation = static_cast<unsigned>(orientation); }
+ void setWidthVariant(FontWidthVariant widthVariant) { m_widthVariant = widthVariant; } // Make sure new callers of this sync with FontPlatformData::isForTextCombine()!
+ void setLocale(const AtomicString&);
+ void setFeatureSettings(FontFeatureSettings&& settings) { m_featureSettings = WTFMove(settings); }
+#if ENABLE(VARIATION_FONTS)
+ void setVariationSettings(FontVariationSettings&& settings) { m_variationSettings = WTFMove(settings); }
+#endif
+ void setFontSynthesis(FontSynthesis fontSynthesis) { m_fontSynthesis = fontSynthesis; }
+ void setVariantCommonLigatures(FontVariantLigatures variant) { m_variantCommonLigatures = static_cast<unsigned>(variant); }
+ void setVariantDiscretionaryLigatures(FontVariantLigatures variant) { m_variantDiscretionaryLigatures = static_cast<unsigned>(variant); }
+ void setVariantHistoricalLigatures(FontVariantLigatures variant) { m_variantHistoricalLigatures = static_cast<unsigned>(variant); }
+ void setVariantContextualAlternates(FontVariantLigatures variant) { m_variantContextualAlternates = static_cast<unsigned>(variant); }
+ void setVariantPosition(FontVariantPosition variant) { m_variantPosition = static_cast<unsigned>(variant); }
+ void setVariantCaps(FontVariantCaps variant) { m_variantCaps = static_cast<unsigned>(variant); }
+ void setVariantNumericFigure(FontVariantNumericFigure variant) { m_variantNumericFigure = static_cast<unsigned>(variant); }
+ void setVariantNumericSpacing(FontVariantNumericSpacing variant) { m_variantNumericSpacing = static_cast<unsigned>(variant); }
+ void setVariantNumericFraction(FontVariantNumericFraction variant) { m_variantNumericFraction = static_cast<unsigned>(variant); }
+ void setVariantNumericOrdinal(FontVariantNumericOrdinal variant) { m_variantNumericOrdinal = static_cast<unsigned>(variant); }
+ void setVariantNumericSlashedZero(FontVariantNumericSlashedZero variant) { m_variantNumericSlashedZero = static_cast<unsigned>(variant); }
+ void setVariantAlternates(FontVariantAlternates variant) { m_variantAlternates = static_cast<unsigned>(variant); }
+ void setVariantEastAsianVariant(FontVariantEastAsianVariant variant) { m_variantEastAsianVariant = static_cast<unsigned>(variant); }
+ void setVariantEastAsianWidth(FontVariantEastAsianWidth variant) { m_variantEastAsianWidth = static_cast<unsigned>(variant); }
+ void setVariantEastAsianRuby(FontVariantEastAsianRuby variant) { m_variantEastAsianRuby = static_cast<unsigned>(variant); }
+
+ FontTraitsMask traitsMask() const;
+
+private:
+ // FIXME: Investigate moving these into their own object on the heap (to save memory).
+ FontFeatureSettings m_featureSettings;
+ FontVariationSettings m_variationSettings;
+ AtomicString m_locale;
+
+ float m_computedSize { 0 }; // Computed size adjusted for the minimum font size and the zoom factor.
+ unsigned m_orientation : 1; // FontOrientation - Whether the font is rendering on a horizontal line or a vertical line.
+ unsigned m_nonCJKGlyphOrientation : 1; // NonCJKGlyphOrientation - Only used by vertical text. Determines the default orientation for non-ideograph glyphs.
+ unsigned m_widthVariant : 2; // FontWidthVariant
+ unsigned m_italic : 1; // FontItalic
+ unsigned m_weight : 8; // FontWeight
+ unsigned m_renderingMode : 1; // Used to switch between CG and GDI text on Windows.
+ unsigned m_textRendering : 2; // TextRenderingMode
+ unsigned m_script : 7; // Used to help choose an appropriate font for generic font families.
+ unsigned m_fontSynthesis : 3; // FontSynthesis type
+ unsigned m_variantCommonLigatures : 2; // FontVariantLigatures
+ unsigned m_variantDiscretionaryLigatures : 2; // FontVariantLigatures
+ unsigned m_variantHistoricalLigatures : 2; // FontVariantLigatures
+ unsigned m_variantContextualAlternates : 2; // FontVariantLigatures
+ unsigned m_variantPosition : 2; // FontVariantPosition
+ unsigned m_variantCaps : 3; // FontVariantCaps
+ unsigned m_variantNumericFigure : 2; // FontVariantNumericFigure
+ unsigned m_variantNumericSpacing : 2; // FontVariantNumericSpacing
+ unsigned m_variantNumericFraction : 2; // FontVariantNumericFraction
+ unsigned m_variantNumericOrdinal : 1; // FontVariantNumericOrdinal
+ unsigned m_variantNumericSlashedZero : 1; // FontVariantNumericSlashedZero
+ unsigned m_variantAlternates : 1; // FontVariantAlternates
+ unsigned m_variantEastAsianVariant : 3; // FontVariantEastAsianVariant
+ unsigned m_variantEastAsianWidth : 2; // FontVariantEastAsianWidth
+ unsigned m_variantEastAsianRuby : 1; // FontVariantEastAsianRuby
+};
+
+inline bool FontDescription::operator==(const FontDescription& other) const
+{
+ return m_computedSize == other.m_computedSize
+ && m_italic == other.m_italic
+ && m_weight == other.m_weight
+ && m_renderingMode == other.m_renderingMode
+ && m_textRendering == other.m_textRendering
+ && m_orientation == other.m_orientation
+ && m_nonCJKGlyphOrientation == other.m_nonCJKGlyphOrientation
+ && m_widthVariant == other.m_widthVariant
+ && m_locale == other.m_locale
+ && m_featureSettings == other.m_featureSettings
+#if ENABLE(VARIATION_FONTS)
+ && m_variationSettings == other.m_variationSettings
+#endif
+ && m_fontSynthesis == other.m_fontSynthesis
+ && m_variantCommonLigatures == other.m_variantCommonLigatures
+ && m_variantDiscretionaryLigatures == other.m_variantDiscretionaryLigatures
+ && m_variantHistoricalLigatures == other.m_variantHistoricalLigatures
+ && m_variantContextualAlternates == other.m_variantContextualAlternates
+ && m_variantPosition == other.m_variantPosition
+ && m_variantCaps == other.m_variantCaps
+ && m_variantNumericFigure == other.m_variantNumericFigure
+ && m_variantNumericSpacing == other.m_variantNumericSpacing
+ && m_variantNumericFraction == other.m_variantNumericFraction
+ && m_variantNumericOrdinal == other.m_variantNumericOrdinal
+ && m_variantNumericSlashedZero == other.m_variantNumericSlashedZero
+ && m_variantAlternates == other.m_variantAlternates
+ && m_variantEastAsianVariant == other.m_variantEastAsianVariant
+ && m_variantEastAsianWidth == other.m_variantEastAsianWidth
+ && m_variantEastAsianRuby == other.m_variantEastAsianRuby;
+}
+
+// FIXME: Move to a file of its own.
+class FontCascadeDescription : public FontDescription {
+public:
+ WEBCORE_EXPORT FontCascadeDescription();
+
+ bool operator==(const FontCascadeDescription&) const;
+ bool operator!=(const FontCascadeDescription& other) const { return !(*this == other); }
unsigned familyCount() const { return m_families.size(); }
const AtomicString& firstFamily() const { return familyAt(0); }
@@ -110,141 +208,96 @@ public:
const RefCountedArray<AtomicString>& families() const { return m_families; }
float specifiedSize() const { return m_specifiedSize; }
- float computedSize() const { return m_computedSize; }
- FontItalic italic() const { return static_cast<FontItalic>(m_italic); }
- int computedPixelSize() const { return int(m_computedSize + 0.5f); }
- FontSmallCaps smallCaps() const { return static_cast<FontSmallCaps>(m_smallCaps); }
bool isAbsoluteSize() const { return m_isAbsoluteSize; }
- FontWeight weight() const { return static_cast<FontWeight>(m_weight); }
FontWeight lighterWeight() const;
FontWeight bolderWeight() const;
- GenericFamilyType genericFamily() const { return static_cast<GenericFamilyType>(m_genericFamily); }
- bool usePrinterFont() const { return m_usePrinterFont; }
+
// only use fixed default size when there is only one font family, and that family is "monospace"
- bool useFixedDefaultSize() const { return genericFamily() == MonospaceFamily && familyCount() == 1 && firstFamily() == monospaceFamily; }
- FontRenderingMode renderingMode() const { return static_cast<FontRenderingMode>(m_renderingMode); }
+ bool useFixedDefaultSize() const { return familyCount() == 1 && firstFamily() == monospaceFamily; }
+
Kerning kerning() const { return static_cast<Kerning>(m_kerning); }
- LigaturesState commonLigaturesState() const { return static_cast<LigaturesState>(m_commonLigaturesState); }
- LigaturesState discretionaryLigaturesState() const { return static_cast<LigaturesState>(m_discretionaryLigaturesState); }
- LigaturesState historicalLigaturesState() const { return static_cast<LigaturesState>(m_historicalLigaturesState); }
unsigned keywordSize() const { return m_keywordSize; }
+ CSSValueID keywordSizeAsIdentifier() const
+ {
+ CSSValueID identifier = m_keywordSize ? static_cast<CSSValueID>(CSSValueXxSmall + m_keywordSize - 1) : CSSValueInvalid;
+ ASSERT(identifier == CSSValueInvalid || (identifier >= CSSValueXxSmall && identifier <= CSSValueWebkitXxxLarge));
+ return identifier;
+ }
FontSmoothingMode fontSmoothing() const { return static_cast<FontSmoothingMode>(m_fontSmoothing); }
- TextRenderingMode textRenderingMode() const { return static_cast<TextRenderingMode>(m_textRendering); }
- UScriptCode script() const { return static_cast<UScriptCode>(m_script); }
-
- FontTraitsMask traitsMask() const;
bool isSpecifiedFont() const { return m_isSpecifiedFont; }
- FontOrientation orientation() const { return static_cast<FontOrientation>(m_orientation); }
- NonCJKGlyphOrientation nonCJKGlyphOrientation() const { return static_cast<NonCJKGlyphOrientation>(m_nonCJKGlyphOrientation); }
- FontWidthVariant widthVariant() const { return static_cast<FontWidthVariant>(m_widthVariant); }
- FontFeatureSettings* featureSettings() const { return m_featureSettings.get(); }
- FontDescription makeNormalFeatureSettings() const;
void setOneFamily(const AtomicString& family) { ASSERT(m_families.size() == 1); m_families[0] = family; }
void setFamilies(const Vector<AtomicString>& families) { m_families = RefCountedArray<AtomicString>(families); }
void setFamilies(const RefCountedArray<AtomicString>& families) { m_families = families; }
- void setComputedSize(float s) { m_computedSize = clampToFloat(s); }
void setSpecifiedSize(float s) { m_specifiedSize = clampToFloat(s); }
- void setItalic(FontItalic i) { m_italic = i; }
- void setItalic(bool i) { setItalic(i ? FontItalicOn : FontItalicOff); }
- void setSmallCaps(FontSmallCaps c) { m_smallCaps = c; }
- void setSmallCaps(bool c) { setSmallCaps(c ? FontSmallCapsOn : FontSmallCapsOff); }
void setIsAbsoluteSize(bool s) { m_isAbsoluteSize = s; }
- void setWeight(FontWeight w) { m_weight = w; }
- void setGenericFamily(GenericFamilyType genericFamily) { m_genericFamily = genericFamily; }
- void setUsePrinterFont(bool p) { m_usePrinterFont = p; }
- void setRenderingMode(FontRenderingMode mode) { m_renderingMode = mode; }
- void setKerning(Kerning kerning) { m_kerning = kerning; }
- void setCommonLigaturesState(LigaturesState commonLigaturesState) { m_commonLigaturesState = commonLigaturesState; }
- void setDiscretionaryLigaturesState(LigaturesState discretionaryLigaturesState) { m_discretionaryLigaturesState = discretionaryLigaturesState; }
- void setHistoricalLigaturesState(LigaturesState historicalLigaturesState) { m_historicalLigaturesState = historicalLigaturesState; }
- void setKeywordSize(unsigned s) { m_keywordSize = s; }
+ void setKerning(Kerning kerning) { m_kerning = static_cast<unsigned>(kerning); }
+ void setKeywordSize(unsigned size)
+ {
+ ASSERT(size <= 8);
+ m_keywordSize = size;
+ ASSERT(m_keywordSize == size); // Make sure it fits in the bitfield.
+ }
+ void setKeywordSizeFromIdentifier(CSSValueID identifier)
+ {
+ ASSERT(!identifier || (identifier >= CSSValueXxSmall && identifier <= CSSValueWebkitXxxLarge));
+ static_assert(CSSValueWebkitXxxLarge - CSSValueXxSmall + 1 == 8, "Maximum keyword size should be 8.");
+ setKeywordSize(identifier ? identifier - CSSValueXxSmall + 1 : 0);
+ }
void setFontSmoothing(FontSmoothingMode smoothing) { m_fontSmoothing = smoothing; }
- void setTextRenderingMode(TextRenderingMode rendering) { m_textRendering = rendering; }
void setIsSpecifiedFont(bool isSpecifiedFont) { m_isSpecifiedFont = isSpecifiedFont; }
- void setOrientation(FontOrientation orientation) { m_orientation = orientation; }
- void setNonCJKGlyphOrientation(NonCJKGlyphOrientation orientation) { m_nonCJKGlyphOrientation = orientation; }
- void setWidthVariant(FontWidthVariant widthVariant) { m_widthVariant = widthVariant; }
- void setScript(UScriptCode s) { m_script = s; }
- void setFeatureSettings(PassRefPtr<FontFeatureSettings> settings) { m_featureSettings = settings; }
-#if ENABLE(IOS_TEXT_AUTOSIZING)
- bool familiesEqualForTextAutoSizing(const FontDescription& other) const;
+#if ENABLE(TEXT_AUTOSIZING)
+ bool familiesEqualForTextAutoSizing(const FontCascadeDescription& other) const;
- bool equalForTextAutoSizing(const FontDescription& other) const
+ bool equalForTextAutoSizing(const FontCascadeDescription& other) const
{
return familiesEqualForTextAutoSizing(other)
&& m_specifiedSize == other.m_specifiedSize
- && m_smallCaps == other.m_smallCaps
- && m_isAbsoluteSize == other.m_isAbsoluteSize
- && m_genericFamily == other.m_genericFamily
- && m_usePrinterFont == other.m_usePrinterFont;
+ && variantSettings() == other.variantSettings()
+ && m_isAbsoluteSize == other.m_isAbsoluteSize;
}
#endif
+ // Initial values for font properties.
+ static FontItalic initialItalic() { return FontItalicOff; }
+ static FontSmallCaps initialSmallCaps() { return FontSmallCapsOff; }
+ static Kerning initialKerning() { return Kerning::Auto; }
+ static FontSmoothingMode initialFontSmoothing() { return AutoSmoothing; }
+ static TextRenderingMode initialTextRenderingMode() { return AutoTextRendering; }
+ static FontSynthesis initialFontSynthesis() { return FontSynthesisWeight | FontSynthesisStyle | FontSynthesisSmallCaps; }
+ static FontVariantPosition initialVariantPosition() { return FontVariantPosition::Normal; }
+ static FontVariantCaps initialVariantCaps() { return FontVariantCaps::Normal; }
+ static FontVariantAlternates initialVariantAlternates() { return FontVariantAlternates::Normal; }
+ static const AtomicString& initialLocale() { return nullAtom; }
+
private:
- RefCountedArray<AtomicString> m_families;
- RefPtr<FontFeatureSettings> m_featureSettings;
+ RefCountedArray<AtomicString> m_families { 1 };
- float m_specifiedSize; // Specified CSS value. Independent of rendering issues such as integer
+ float m_specifiedSize { 0 }; // Specified CSS value. Independent of rendering issues such as integer
// rounding, minimum font sizes, and zooming.
- float m_computedSize; // Computed size adjusted for the minimum font size and the zoom factor.
-
- unsigned m_orientation : 1; // FontOrientation - Whether the font is rendering on a horizontal line or a vertical line.
- unsigned m_nonCJKGlyphOrientation : 1; // NonCJKGlyphOrientation - Only used by vertical text. Determines the default orientation for non-ideograph glyphs.
-
- unsigned m_widthVariant : 2; // FontWidthVariant
-
- unsigned m_italic : 1; // FontItalic
- unsigned m_smallCaps : 1; // FontSmallCaps
unsigned m_isAbsoluteSize : 1; // Whether or not CSS specified an explicit size
// (logical sizes like "medium" don't count).
- unsigned m_weight : 8; // FontWeight
- unsigned m_genericFamily : 3; // GenericFamilyType
- unsigned m_usePrinterFont : 1;
-
- unsigned m_renderingMode : 1; // Used to switch between CG and GDI text on Windows.
unsigned m_kerning : 2; // Kerning
- unsigned m_commonLigaturesState : 2;
- unsigned m_discretionaryLigaturesState : 2;
- unsigned m_historicalLigaturesState : 2;
-
unsigned m_keywordSize : 4; // We cache whether or not a font is currently represented by a CSS keyword (e.g., medium). If so,
// then we can accurately translate across different generic families to adjust for different preference settings
// (e.g., 13px monospace vs. 16px everything else). Sizes are 1-8 (like the HTML size values for <font>).
unsigned m_fontSmoothing : 2; // FontSmoothingMode
- unsigned m_textRendering : 2; // TextRenderingMode
unsigned m_isSpecifiedFont : 1; // True if a web page specifies a non-generic font family as the first font family.
- unsigned m_script : 7; // Used to help choose an appropriate font for generic font families.
};
-inline bool FontDescription::operator==(const FontDescription& other) const
+inline bool FontCascadeDescription::operator==(const FontCascadeDescription& other) const
{
- return m_families == other.m_families
+ return FontDescription::operator==(other)
+ && m_families == other.m_families
&& m_specifiedSize == other.m_specifiedSize
- && m_computedSize == other.m_computedSize
- && m_italic == other.m_italic
- && m_smallCaps == other.m_smallCaps
&& m_isAbsoluteSize == other.m_isAbsoluteSize
- && m_weight == other.m_weight
- && m_genericFamily == other.m_genericFamily
- && m_usePrinterFont == other.m_usePrinterFont
- && m_renderingMode == other.m_renderingMode
&& m_kerning == other.m_kerning
- && m_commonLigaturesState == other.m_commonLigaturesState
- && m_discretionaryLigaturesState == other.m_discretionaryLigaturesState
- && m_historicalLigaturesState == other.m_historicalLigaturesState
&& m_keywordSize == other.m_keywordSize
&& m_fontSmoothing == other.m_fontSmoothing
- && m_textRendering == other.m_textRendering
- && m_isSpecifiedFont == other.m_isSpecifiedFont
- && m_orientation == other.m_orientation
- && m_nonCJKGlyphOrientation == other.m_nonCJKGlyphOrientation
- && m_widthVariant == other.m_widthVariant
- && m_script == other.m_script
- && m_featureSettings == other.m_featureSettings;
+ && m_isSpecifiedFont == other.m_isSpecifiedFont;
}
}
diff --git a/Source/WebCore/platform/graphics/FontFastPath.cpp b/Source/WebCore/platform/graphics/FontFastPath.cpp
deleted file mode 100644
index 6602c3b66..000000000
--- a/Source/WebCore/platform/graphics/FontFastPath.cpp
+++ /dev/null
@@ -1,375 +0,0 @@
-/**
- * Copyright (C) 2003, 2006, 2010 Apple Inc. All rights reserved.
- * Copyright (C) 2008 Holger Hans Peter Freyther
- * Copyright (C) 2009 Torch Mobile, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public License
- * along with this library; see the file COPYING.LIB. If not, write to
- * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
- * Boston, MA 02110-1301, USA.
- *
- */
-
-#include "config.h"
-#include "Font.h"
-
-#include "FloatRect.h"
-#include "FontCache.h"
-#include "FontGlyphs.h"
-#include "GlyphBuffer.h"
-#include "GlyphPageTreeNode.h"
-#include "SimpleFontData.h"
-#include "TextRun.h"
-#include "WidthIterator.h"
-#include <wtf/MainThread.h>
-#include <wtf/MathExtras.h>
-#include <wtf/unicode/CharacterNames.h>
-#include <wtf/unicode/Unicode.h>
-
-using namespace WTF;
-using namespace Unicode;
-
-namespace WebCore {
-
-bool Font::primaryFontHasGlyphForCharacter(UChar32 character) const
-{
- unsigned pageNumber = (character / GlyphPage::size);
-
- GlyphPageTreeNode* node = GlyphPageTreeNode::getRootChild(primaryFont(), pageNumber);
- GlyphPage* page = node->page();
-
- return page && page->fontDataForCharacter(character);
-}
-
-// FIXME: This function may not work if the emphasis mark uses a complex script, but none of the
-// standard emphasis marks do so.
-bool Font::getEmphasisMarkGlyphData(const AtomicString& mark, GlyphData& glyphData) const
-{
- if (mark.isEmpty())
- return false;
-
- UChar32 character = mark[0];
-
- if (U16_IS_SURROGATE(character)) {
- if (!U16_IS_SURROGATE_LEAD(character))
- return false;
-
- if (mark.length() < 2)
- return false;
-
- UChar low = mark[1];
- if (!U16_IS_TRAIL(low))
- return false;
-
- character = U16_GET_SUPPLEMENTARY(character, low);
- }
-
- glyphData = glyphDataForCharacter(character, false, EmphasisMarkVariant);
- return true;
-}
-
-int Font::emphasisMarkAscent(const AtomicString& mark) const
-{
- FontCachePurgePreventer purgePreventer;
-
- GlyphData markGlyphData;
- if (!getEmphasisMarkGlyphData(mark, markGlyphData))
- return 0;
-
- const SimpleFontData* markFontData = markGlyphData.fontData;
- ASSERT(markFontData);
- if (!markFontData)
- return 0;
-
- return markFontData->fontMetrics().ascent();
-}
-
-int Font::emphasisMarkDescent(const AtomicString& mark) const
-{
- FontCachePurgePreventer purgePreventer;
-
- GlyphData markGlyphData;
- if (!getEmphasisMarkGlyphData(mark, markGlyphData))
- return 0;
-
- const SimpleFontData* markFontData = markGlyphData.fontData;
- ASSERT(markFontData);
- if (!markFontData)
- return 0;
-
- return markFontData->fontMetrics().descent();
-}
-
-int Font::emphasisMarkHeight(const AtomicString& mark) const
-{
- FontCachePurgePreventer purgePreventer;
-
- GlyphData markGlyphData;
- if (!getEmphasisMarkGlyphData(mark, markGlyphData))
- return 0;
-
- const SimpleFontData* markFontData = markGlyphData.fontData;
- ASSERT(markFontData);
- if (!markFontData)
- return 0;
-
- return markFontData->fontMetrics().height();
-}
-
-float Font::getGlyphsAndAdvancesForSimpleText(const TextRun& run, int from, int to, GlyphBuffer& glyphBuffer, ForTextEmphasisOrNot forTextEmphasis) const
-{
- WidthIterator it(this, run, 0, false, forTextEmphasis);
- GlyphBuffer localGlyphBuffer;
- it.advance(run.length(), &localGlyphBuffer);
-
- if (localGlyphBuffer.isEmpty())
- return 0;
-
- float totalWidth = it.m_runWidthSoFar;
- float beforeWidth = 0;
- int glyphPos = 0;
- for (; glyphPos < localGlyphBuffer.size() && it.m_characterIndexOfGlyph[glyphPos] < from; ++glyphPos)
- beforeWidth += localGlyphBuffer.advanceAt(glyphPos).width();
- int glyphFrom = glyphPos;
-
- float afterWidth = totalWidth;
- glyphPos = localGlyphBuffer.size() - 1;
- for (; glyphPos >= glyphFrom && it.m_characterIndexOfGlyph[glyphPos] >= to; --glyphPos)
- afterWidth -= localGlyphBuffer.advanceAt(glyphPos).width();
- int glyphTo = glyphPos + 1;
-
- glyphBuffer.add(&localGlyphBuffer, glyphFrom, glyphTo - glyphFrom);
-
- if (run.rtl()) {
- glyphBuffer.reverse(0, glyphBuffer.size());
- return totalWidth - afterWidth;
- }
-
- return beforeWidth;
-}
-
-float Font::drawSimpleText(GraphicsContext* context, const TextRun& run, const FloatPoint& point, int from, int to) const
-{
- // This glyph buffer holds our glyphs+advances+font data for each glyph.
- GlyphBuffer glyphBuffer;
-
- float startX = point.x() + getGlyphsAndAdvancesForSimpleText(run, from, to, glyphBuffer);
-
- if (glyphBuffer.isEmpty())
- return 0;
-
- FloatPoint startPoint(startX, point.y());
- drawGlyphBuffer(context, run, glyphBuffer, startPoint);
-
- return startPoint.x() - startX;
-}
-
-void Font::drawEmphasisMarksForSimpleText(GraphicsContext* context, const TextRun& run, const AtomicString& mark, const FloatPoint& point, int from, int to) const
-{
- GlyphBuffer glyphBuffer;
- float initialAdvance = getGlyphsAndAdvancesForSimpleText(run, from, to, glyphBuffer, ForTextEmphasis);
-
- if (glyphBuffer.isEmpty())
- return;
-
- drawEmphasisMarks(context, run, glyphBuffer, mark, FloatPoint(point.x() + initialAdvance, point.y()));
-}
-
-void Font::drawGlyphBuffer(GraphicsContext* context, const TextRun& run, const GlyphBuffer& glyphBuffer, FloatPoint& point) const
-{
-#if !ENABLE(SVG_FONTS)
- UNUSED_PARAM(run);
-#endif
-
- // Draw each contiguous run of glyphs that use the same font data.
- const SimpleFontData* fontData = glyphBuffer.fontDataAt(0);
- FloatSize offset = glyphBuffer.offsetAt(0);
- FloatPoint startPoint(point.x(), point.y() - glyphBuffer.initialAdvance().height());
- float nextX = startPoint.x() + glyphBuffer.advanceAt(0).width();
- float nextY = startPoint.y() + glyphBuffer.advanceAt(0).height();
- int lastFrom = 0;
- int nextGlyph = 1;
-#if ENABLE(SVG_FONTS)
- TextRun::RenderingContext* renderingContext = run.renderingContext();
-#endif
- while (nextGlyph < glyphBuffer.size()) {
- const SimpleFontData* nextFontData = glyphBuffer.fontDataAt(nextGlyph);
- FloatSize nextOffset = glyphBuffer.offsetAt(nextGlyph);
-
- if (nextFontData != fontData || nextOffset != offset) {
-#if ENABLE(SVG_FONTS)
- if (renderingContext && fontData->isSVGFont())
- renderingContext->drawSVGGlyphs(context, fontData, glyphBuffer, lastFrom, nextGlyph - lastFrom, startPoint);
- else
-#endif
- drawGlyphs(context, fontData, glyphBuffer, lastFrom, nextGlyph - lastFrom, startPoint);
-
- lastFrom = nextGlyph;
- fontData = nextFontData;
- offset = nextOffset;
- startPoint.setX(nextX);
- startPoint.setY(nextY);
- }
- nextX += glyphBuffer.advanceAt(nextGlyph).width();
- nextY += glyphBuffer.advanceAt(nextGlyph).height();
- nextGlyph++;
- }
-
-#if ENABLE(SVG_FONTS)
- if (renderingContext && fontData->isSVGFont())
- renderingContext->drawSVGGlyphs(context, fontData, glyphBuffer, lastFrom, nextGlyph - lastFrom, startPoint);
- else
-#endif
- {
- drawGlyphs(context, fontData, glyphBuffer, lastFrom, nextGlyph - lastFrom, startPoint);
- point.setX(nextX);
- }
-}
-
-inline static float offsetToMiddleOfGlyph(const SimpleFontData* fontData, Glyph glyph)
-{
- if (fontData->platformData().orientation() == Horizontal) {
- FloatRect bounds = fontData->boundsForGlyph(glyph);
- return bounds.x() + bounds.width() / 2;
- }
- // FIXME: Use glyph bounds once they make sense for vertical fonts.
- return fontData->widthForGlyph(glyph) / 2;
-}
-
-inline static float offsetToMiddleOfGlyphAtIndex(const GlyphBuffer& glyphBuffer, size_t i)
-{
- return offsetToMiddleOfGlyph(glyphBuffer.fontDataAt(i), glyphBuffer.glyphAt(i));
-}
-
-void Font::drawEmphasisMarks(GraphicsContext* context, const TextRun& run, const GlyphBuffer& glyphBuffer, const AtomicString& mark, const FloatPoint& point) const
-{
- FontCachePurgePreventer purgePreventer;
-
- GlyphData markGlyphData;
- if (!getEmphasisMarkGlyphData(mark, markGlyphData))
- return;
-
- const SimpleFontData* markFontData = markGlyphData.fontData;
- ASSERT(markFontData);
- if (!markFontData)
- return;
-
- Glyph markGlyph = markGlyphData.glyph;
- Glyph spaceGlyph = markFontData->spaceGlyph();
-
- float middleOfLastGlyph = offsetToMiddleOfGlyphAtIndex(glyphBuffer, 0);
- FloatPoint startPoint(point.x() + middleOfLastGlyph - offsetToMiddleOfGlyph(markFontData, markGlyph), point.y());
-
- GlyphBuffer markBuffer;
- for (int i = 0; i + 1 < glyphBuffer.size(); ++i) {
- float middleOfNextGlyph = offsetToMiddleOfGlyphAtIndex(glyphBuffer, i + 1);
- float advance = glyphBuffer.advanceAt(i).width() - middleOfLastGlyph + middleOfNextGlyph;
- markBuffer.add(glyphBuffer.glyphAt(i) ? markGlyph : spaceGlyph, markFontData, advance);
- middleOfLastGlyph = middleOfNextGlyph;
- }
- markBuffer.add(glyphBuffer.glyphAt(glyphBuffer.size() - 1) ? markGlyph : spaceGlyph, markFontData, 0);
-
- drawGlyphBuffer(context, run, markBuffer, startPoint);
-}
-
-float Font::floatWidthForSimpleText(const TextRun& run, HashSet<const SimpleFontData*>* fallbackFonts, GlyphOverflow* glyphOverflow) const
-{
- WidthIterator it(this, run, fallbackFonts, glyphOverflow);
- GlyphBuffer glyphBuffer;
- it.advance(run.length(), (typesettingFeatures() & (Kerning | Ligatures)) ? &glyphBuffer : 0);
-
- if (glyphOverflow) {
- glyphOverflow->top = std::max<int>(glyphOverflow->top, ceilf(-it.minGlyphBoundingBoxY()) - (glyphOverflow->computeBounds ? 0 : fontMetrics().ascent()));
- glyphOverflow->bottom = std::max<int>(glyphOverflow->bottom, ceilf(it.maxGlyphBoundingBoxY()) - (glyphOverflow->computeBounds ? 0 : fontMetrics().descent()));
- glyphOverflow->left = ceilf(it.firstGlyphOverflow());
- glyphOverflow->right = ceilf(it.lastGlyphOverflow());
- }
-
- return it.m_runWidthSoFar;
-}
-
-FloatRect Font::selectionRectForSimpleText(const TextRun& run, const FloatPoint& point, int h, int from, int to) const
-{
- GlyphBuffer glyphBuffer;
- WidthIterator it(this, run);
- it.advance(run.length(), &glyphBuffer);
-
- float totalWidth = it.m_runWidthSoFar;
- float beforeWidth = 0;
- int glyphPos = 0;
- for (; glyphPos < glyphBuffer.size() && it.m_characterIndexOfGlyph[glyphPos] < from; ++glyphPos)
- beforeWidth += glyphBuffer.advanceAt(glyphPos).width();
- int glyphFrom = glyphPos;
-
- float afterWidth = totalWidth;
- glyphPos = glyphBuffer.size() - 1;
- for (; glyphPos >= glyphFrom && it.m_characterIndexOfGlyph[glyphPos] >= to; --glyphPos)
- afterWidth -= glyphBuffer.advanceAt(glyphPos).width();
-
- // Using roundf() rather than ceilf() for the right edge as a compromise to ensure correct caret positioning.
- if (run.rtl()) {
- return FloatRect(floorf(point.x() + totalWidth - afterWidth), point.y(), roundf(point.x() + totalWidth - beforeWidth) - floorf(point.x() + totalWidth - afterWidth), h);
- }
-
- return FloatRect(floorf(point.x() + beforeWidth), point.y(), roundf(point.x() + afterWidth) - floorf(point.x() + beforeWidth), h);
-}
-
-int Font::offsetForPositionForSimpleText(const TextRun& run, float x, bool includePartialGlyphs) const
-{
- GlyphBuffer glyphBuffer;
- WidthIterator it(this, run);
- it.advance(run.length(), &glyphBuffer);
-
- int characterOffset = 0;
- if (run.rtl()) {
- float currentX = it.m_runWidthSoFar;
- for (int glyphPosition = 0; glyphPosition <= glyphBuffer.size(); ++glyphPosition) {
- if (glyphPosition == glyphBuffer.size()) {
- characterOffset = run.length();
- break;
- }
- characterOffset = it.m_characterIndexOfGlyph[glyphPosition];
- float glyphWidth = glyphBuffer.advanceAt(glyphPosition).width();
- if (includePartialGlyphs) {
- if (currentX - glyphWidth / 2.0f <= x)
- break;
- } else {
- if (currentX - glyphWidth <= x)
- break;
- }
- currentX -= glyphWidth;
- }
- } else {
- float currentX = 0;
- for (int glyphPosition = 0; glyphPosition <= glyphBuffer.size(); ++glyphPosition) {
- if (glyphPosition == glyphBuffer.size()) {
- characterOffset = run.length();
- break;
- }
- characterOffset = it.m_characterIndexOfGlyph[glyphPosition];
- float glyphWidth = glyphBuffer.advanceAt(glyphPosition).width();
- if (includePartialGlyphs) {
- if (currentX + glyphWidth / 2.0f >= x)
- break;
- } else {
- if (currentX + glyphWidth >= x)
- break;
- }
- currentX += glyphWidth;
- }
- }
-
- return characterOffset;
-}
-
-} // namespace WebCore
diff --git a/Source/WebCore/platform/graphics/FontFeatureSettings.cpp b/Source/WebCore/platform/graphics/FontFeatureSettings.cpp
deleted file mode 100644
index 3713322f4..000000000
--- a/Source/WebCore/platform/graphics/FontFeatureSettings.cpp
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Copyright (C) 2011 Google Inc. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
- * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
- * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#include "config.h"
-#include "FontFeatureSettings.h"
-
-namespace WebCore {
-
-FontFeature::FontFeature(const AtomicString& tag, int value)
- : m_tag(tag)
- , m_value(value)
-{
-}
-
-bool FontFeature::operator==(const FontFeature& other)
-{
- return m_tag == other.m_tag && m_value == other.m_value;
-}
-
-FontFeatureSettings::FontFeatureSettings()
-{
-}
-
-}
diff --git a/Source/WebCore/platform/graphics/FontFeatureSettings.h b/Source/WebCore/platform/graphics/FontFeatureSettings.h
deleted file mode 100644
index 1bd9b339e..000000000
--- a/Source/WebCore/platform/graphics/FontFeatureSettings.h
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * Copyright (C) 2011 Google Inc. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
- * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
- * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#ifndef FontFeatureSettings_h
-#define FontFeatureSettings_h
-
-#include <wtf/PassRefPtr.h>
-#include <wtf/RefCounted.h>
-#include <wtf/RefPtr.h>
-#include <wtf/Vector.h>
-#include <wtf/text/AtomicString.h>
-
-namespace WebCore {
-
-class FontFeature {
-public:
- FontFeature(const AtomicString& tag, int value);
- bool operator==(const FontFeature&);
-
- const AtomicString& tag() const { return m_tag; }
- int value() const { return m_value; }
-
-private:
- AtomicString m_tag;
- const int m_value;
-};
-
-class FontFeatureSettings : public RefCounted<FontFeatureSettings> {
-public:
- static PassRefPtr<FontFeatureSettings> create()
- {
- return adoptRef(new FontFeatureSettings());
- }
- void append(const FontFeature& feature) { m_list.append(feature); }
- size_t size() const { return m_list.size(); }
- const FontFeature& operator[](int index) const { return m_list[index]; }
- const FontFeature& at(size_t index) const { return m_list.at(index); }
-
-private:
- FontFeatureSettings();
- Vector<FontFeature> m_list;
-};
-
-}
-
-#endif // FontFeatureSettings_h
diff --git a/Source/WebCore/platform/graphics/FontGenericFamilies.cpp b/Source/WebCore/platform/graphics/FontGenericFamilies.cpp
index e08255347..655eee3a4 100644
--- a/Source/WebCore/platform/graphics/FontGenericFamilies.cpp
+++ b/Source/WebCore/platform/graphics/FontGenericFamilies.cpp
@@ -25,6 +25,7 @@
#include "config.h"
#include "FontGenericFamilies.h"
+#include "Language.h"
namespace WebCore {
@@ -46,11 +47,41 @@ static bool setGenericFontFamilyForScript(ScriptFontFamilyMap& fontMap, const At
return true;
}
+static inline bool computeUserPrefersSimplified()
+{
+ const Vector<String>& preferredLanguages = userPreferredLanguages();
+ for (auto& language : preferredLanguages) {
+ if (equalIgnoringASCIICase(language, "zh-tw"))
+ return false;
+ if (equalIgnoringASCIICase(language, "zh-cn"))
+ return true;
+ }
+ return true;
+}
+
+static bool& cachedUserPrefersSimplified()
+{
+ static bool cached = true;
+ return cached;
+}
+
+static void languageChanged(void*)
+{
+ cachedUserPrefersSimplified() = computeUserPrefersSimplified();
+}
+
static const AtomicString& genericFontFamilyForScript(const ScriptFontFamilyMap& fontMap, UScriptCode script)
{
ScriptFontFamilyMap::const_iterator it = fontMap.find(static_cast<int>(script));
if (it != fontMap.end())
return it->value;
+ // Content using USCRIPT_HAN doesn't tell us if we should be using Simplified Chinese or Traditional Chinese. In the
+ // absence of all other signals, we consult with the user's system preferences.
+ if (script == USCRIPT_HAN) {
+ it = fontMap.find(static_cast<int>(cachedUserPrefersSimplified() ? USCRIPT_SIMPLIFIED_HAN : USCRIPT_TRADITIONAL_HAN));
+ if (it != fontMap.end())
+ return it->value;
+ }
if (script != USCRIPT_COMMON)
return genericFontFamilyForScript(fontMap, USCRIPT_COMMON);
return emptyAtom;
@@ -58,6 +89,8 @@ static const AtomicString& genericFontFamilyForScript(const ScriptFontFamilyMap&
FontGenericFamilies::FontGenericFamilies()
{
+ addLanguageChangeObserver(this, &languageChanged);
+ languageChanged(nullptr);
}
const AtomicString& FontGenericFamilies::standardFontFamily(UScriptCode script) const
diff --git a/Source/WebCore/platform/graphics/FontGenericFamilies.h b/Source/WebCore/platform/graphics/FontGenericFamilies.h
index d0d1dabab..f762bae2f 100644
--- a/Source/WebCore/platform/graphics/FontGenericFamilies.h
+++ b/Source/WebCore/platform/graphics/FontGenericFamilies.h
@@ -26,6 +26,7 @@
#ifndef FontGenericFamilies_h
#define FontGenericFamilies_h
+#include <unicode/uscript.h>
#include <wtf/HashMap.h>
#include <wtf/text/AtomicString.h>
@@ -43,6 +44,7 @@ struct UScriptCodeHashTraits : WTF::GenericHashTraits<int> {
typedef HashMap<int, AtomicString, DefaultHash<int>::Hash, UScriptCodeHashTraits> ScriptFontFamilyMap;
class FontGenericFamilies {
+ WTF_MAKE_FAST_ALLOCATED;
public:
FontGenericFamilies();
diff --git a/Source/WebCore/platform/graphics/FontGlyphs.cpp b/Source/WebCore/platform/graphics/FontGlyphs.cpp
deleted file mode 100644
index 819e371ce..000000000
--- a/Source/WebCore/platform/graphics/FontGlyphs.cpp
+++ /dev/null
@@ -1,434 +0,0 @@
-/*
- * Copyright (C) 2006, 2013 Apple Inc. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
- * its contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
- * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
- * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
- * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#include "config.h"
-#include "FontGlyphs.h"
-
-#include "Font.h"
-#include "FontCache.h"
-#include "SegmentedFontData.h"
-#include <wtf/unicode/Unicode.h>
-
-namespace WebCore {
-
-
-FontGlyphs::FontGlyphs(PassRefPtr<FontSelector> fontSelector)
- : m_pageZero(0)
- , m_cachedPrimarySimpleFontData(0)
- , m_fontSelector(fontSelector)
- , m_fontSelectorVersion(m_fontSelector ? m_fontSelector->version() : 0)
- , m_familyIndex(0)
- , m_generation(fontCache()->generation())
- , m_pitch(UnknownPitch)
- , m_loadingCustomFonts(false)
- , m_isForPlatformFont(false)
-{
-}
-
-FontGlyphs::FontGlyphs(const FontPlatformData& platformData)
- : m_pageZero(0)
- , m_cachedPrimarySimpleFontData(0)
- , m_fontSelector(0)
- , m_fontSelectorVersion(0)
- , m_familyIndex(cAllFamiliesScanned)
- , m_generation(fontCache()->generation())
- , m_pitch(UnknownPitch)
- , m_loadingCustomFonts(false)
- , m_isForPlatformFont(true)
-{
- RefPtr<FontData> fontData = fontCache()->getCachedFontData(&platformData);
- m_realizedFontData.append(fontData.release());
-}
-
-void FontGlyphs::releaseFontData()
-{
- unsigned numFonts = m_realizedFontData.size();
- for (unsigned i = 0; i < numFonts; ++i) {
- if (m_realizedFontData[i]->isCustomFont())
- continue;
- ASSERT(!m_realizedFontData[i]->isSegmented());
- fontCache()->releaseFontData(static_cast<const SimpleFontData*>(m_realizedFontData[i].get()));
- }
-}
-
-void FontGlyphs::determinePitch(const FontDescription& description) const
-{
- const FontData* fontData = primaryFontData(description);
- if (!fontData->isSegmented())
- m_pitch = static_cast<const SimpleFontData*>(fontData)->pitch();
- else {
- const SegmentedFontData* segmentedFontData = static_cast<const SegmentedFontData*>(fontData);
- unsigned numRanges = segmentedFontData->numRanges();
- if (numRanges == 1)
- m_pitch = segmentedFontData->rangeAt(0).fontData()->pitch();
- else
- m_pitch = VariablePitch;
- }
-}
-
-const FontData* FontGlyphs::realizeFontDataAt(const FontDescription& description, unsigned realizedFontIndex) const
-{
- if (realizedFontIndex < m_realizedFontData.size())
- return m_realizedFontData[realizedFontIndex].get(); // This fallback font is already in our list.
-
- // Make sure we're not passing in some crazy value here.
- ASSERT(realizedFontIndex == m_realizedFontData.size());
-
- if (m_familyIndex <= cAllFamiliesScanned) {
- if (!m_fontSelector)
- return 0;
-
- size_t index = cAllFamiliesScanned - m_familyIndex;
- if (index == m_fontSelector->fallbackFontDataCount())
- return 0;
-
- m_familyIndex--;
- RefPtr<FontData> fallback = m_fontSelector->getFallbackFontData(description, index);
- if (fallback)
- m_realizedFontData.append(fallback);
- return fallback.get();
- }
-
- // Ask the font cache for the font data.
- // We are obtaining this font for the first time. We keep track of the families we've looked at before
- // in |m_familyIndex|, so that we never scan the same spot in the list twice. getFontData will adjust our
- // |m_familyIndex| as it scans for the right font to make.
- ASSERT(fontCache()->generation() == m_generation);
- RefPtr<FontData> result = fontCache()->getFontData(description, m_familyIndex, m_fontSelector.get());
- if (result) {
- m_realizedFontData.append(result);
- if (result->isLoading())
- m_loadingCustomFonts = true;
- }
- return result.get();
-}
-
-static inline bool isInRange(UChar32 character, UChar32 lowerBound, UChar32 upperBound)
-{
- return character >= lowerBound && character <= upperBound;
-}
-
-static bool shouldIgnoreRotation(UChar32 character)
-{
- if (character == 0x000A7 || character == 0x000A9 || character == 0x000AE)
- return true;
-
- if (character == 0x000B6 || character == 0x000BC || character == 0x000BD || character == 0x000BE)
- return true;
-
- if (isInRange(character, 0x002E5, 0x002EB))
- return true;
-
- if (isInRange(character, 0x01100, 0x011FF) || isInRange(character, 0x01401, 0x0167F) || isInRange(character, 0x01800, 0x018FF))
- return true;
-
- if (character == 0x02016 || character == 0x02018 || character == 0x02019 || character == 0x02020 || character == 0x02021
- || character == 0x2030 || character == 0x02031)
- return true;
-
- if (isInRange(character, 0x0203B, 0x0203D) || character == 0x02042 || character == 0x02044 || character == 0x02047
- || character == 0x02048 || character == 0x02049 || character == 0x2051)
- return true;
-
- if (isInRange(character, 0x02065, 0x02069) || isInRange(character, 0x020DD, 0x020E0)
- || isInRange(character, 0x020E2, 0x020E4) || isInRange(character, 0x02100, 0x02117)
- || isInRange(character, 0x02119, 0x02131) || isInRange(character, 0x02133, 0x0213F))
- return true;
-
- if (isInRange(character, 0x02145, 0x0214A) || character == 0x0214C || character == 0x0214D
- || isInRange(character, 0x0214F, 0x0218F))
- return true;
-
- if (isInRange(character, 0x02300, 0x02307) || isInRange(character, 0x0230C, 0x0231F)
- || isInRange(character, 0x02322, 0x0232B) || isInRange(character, 0x0237D, 0x0239A)
- || isInRange(character, 0x023B4, 0x023B6) || isInRange(character, 0x023BA, 0x023CF)
- || isInRange(character, 0x023D1, 0x023DB) || isInRange(character, 0x023E2, 0x024FF))
- return true;
-
- if (isInRange(character, 0x025A0, 0x02619) || isInRange(character, 0x02620, 0x02767)
- || isInRange(character, 0x02776, 0x02793) || isInRange(character, 0x02B12, 0x02B2F)
- || isInRange(character, 0x02B4D, 0x02BFF) || isInRange(character, 0x02E80, 0x03007))
- return true;
-
- if (character == 0x03012 || character == 0x03013 || isInRange(character, 0x03020, 0x0302F)
- || isInRange(character, 0x03031, 0x0309F) || isInRange(character, 0x030A1, 0x030FB)
- || isInRange(character, 0x030FD, 0x0A4CF))
- return true;
-
- if (isInRange(character, 0x0A840, 0x0A87F) || isInRange(character, 0x0A960, 0x0A97F)
- || isInRange(character, 0x0AC00, 0x0D7FF) || isInRange(character, 0x0E000, 0x0FAFF))
- return true;
-
- if (isInRange(character, 0x0FE10, 0x0FE1F) || isInRange(character, 0x0FE30, 0x0FE48)
- || isInRange(character, 0x0FE50, 0x0FE57) || isInRange(character, 0x0FE5F, 0x0FE62)
- || isInRange(character, 0x0FE67, 0x0FE6F))
- return true;
-
- if (isInRange(character, 0x0FF01, 0x0FF07) || isInRange(character, 0x0FF0A, 0x0FF0C)
- || isInRange(character, 0x0FF0E, 0x0FF19) || character == 0x0FF1B || isInRange(character, 0x0FF1F, 0x0FF3A))
- return true;
-
- if (character == 0x0FF3C || character == 0x0FF3E)
- return true;
-
- if (isInRange(character, 0x0FF40, 0x0FF5A) || isInRange(character, 0x0FFE0, 0x0FFE2)
- || isInRange(character, 0x0FFE4, 0x0FFE7) || isInRange(character, 0x0FFF0, 0x0FFF8)
- || character == 0x0FFFD)
- return true;
-
- if (isInRange(character, 0x13000, 0x1342F) || isInRange(character, 0x1B000, 0x1B0FF)
- || isInRange(character, 0x1D000, 0x1D1FF) || isInRange(character, 0x1D300, 0x1D37F)
- || isInRange(character, 0x1F000, 0x1F64F) || isInRange(character, 0x1F680, 0x1F77F))
- return true;
-
- if (isInRange(character, 0x20000, 0x2FFFD) || isInRange(character, 0x30000, 0x3FFFD))
- return true;
-
- return false;
-}
-
-static inline std::pair<GlyphData, GlyphPage*> glyphDataAndPageForCJKCharacterWithoutSyntheticItalic(UChar32 character, GlyphData& data, GlyphPage* page, unsigned pageNumber)
-{
- RefPtr<SimpleFontData> nonItalicFontData = data.fontData->nonSyntheticItalicFontData();
- GlyphPageTreeNode* nonItalicNode = GlyphPageTreeNode::getRootChild(nonItalicFontData.get(), pageNumber);
- GlyphPage* nonItalicPage = nonItalicNode->page();
- if (nonItalicPage) {
- GlyphData nonItalicData = nonItalicPage->glyphDataForCharacter(character);
- if (nonItalicData.fontData)
- return std::make_pair(nonItalicData, nonItalicPage);
- }
- return std::make_pair(data, page);
-}
-
-static inline std::pair<GlyphData, GlyphPage*> glyphDataAndPageForNonCJKCharacterWithGlyphOrientation(UChar32 character, NonCJKGlyphOrientation orientation, GlyphData& data, GlyphPage* page, unsigned pageNumber)
-{
- if (orientation == NonCJKGlyphOrientationUpright || shouldIgnoreRotation(character)) {
- RefPtr<SimpleFontData> uprightFontData = data.fontData->uprightOrientationFontData();
- GlyphPageTreeNode* uprightNode = GlyphPageTreeNode::getRootChild(uprightFontData.get(), pageNumber);
- GlyphPage* uprightPage = uprightNode->page();
- if (uprightPage) {
- GlyphData uprightData = uprightPage->glyphDataForCharacter(character);
- // If the glyphs are the same, then we know we can just use the horizontal glyph rotated vertically to be upright.
- if (data.glyph == uprightData.glyph)
- return std::make_pair(data, page);
- // The glyphs are distinct, meaning that the font has a vertical-right glyph baked into it. We can't use that
- // glyph, so we fall back to the upright data and use the horizontal glyph.
- if (uprightData.fontData)
- return std::make_pair(uprightData, uprightPage);
- }
- } else if (orientation == NonCJKGlyphOrientationVerticalRight) {
- RefPtr<SimpleFontData> verticalRightFontData = data.fontData->verticalRightOrientationFontData();
- GlyphPageTreeNode* verticalRightNode = GlyphPageTreeNode::getRootChild(verticalRightFontData.get(), pageNumber);
- GlyphPage* verticalRightPage = verticalRightNode->page();
- if (verticalRightPage) {
- GlyphData verticalRightData = verticalRightPage->glyphDataForCharacter(character);
- // If the glyphs are distinct, we will make the assumption that the font has a vertical-right glyph baked
- // into it.
- if (data.glyph != verticalRightData.glyph)
- return std::make_pair(data, page);
- // The glyphs are identical, meaning that we should just use the horizontal glyph.
- if (verticalRightData.fontData)
- return std::make_pair(verticalRightData, verticalRightPage);
- }
- }
- return std::make_pair(data, page);
-}
-
-std::pair<GlyphData, GlyphPage*> FontGlyphs::glyphDataAndPageForCharacter(const FontDescription& description, UChar32 c, bool mirror, FontDataVariant variant) const
-{
- ASSERT(isMainThread());
-
- if (variant == AutoVariant) {
- if (description.smallCaps() && !primarySimpleFontData(description)->isSVGFont()) {
- UChar32 upperC = u_toupper(c);
- if (upperC != c) {
- c = upperC;
- variant = SmallCapsVariant;
- } else
- variant = NormalVariant;
- } else
- variant = NormalVariant;
- }
-
- if (mirror)
- c = u_charMirror(c);
-
- unsigned pageNumber = (c / GlyphPage::size);
-
- GlyphPageTreeNode* node = pageNumber ? m_pages.get(pageNumber) : m_pageZero;
- if (!node) {
- node = GlyphPageTreeNode::getRootChild(realizeFontDataAt(description, 0), pageNumber);
- if (pageNumber)
- m_pages.set(pageNumber, node);
- else
- m_pageZero = node;
- }
-
- GlyphPage* page = 0;
- if (variant == NormalVariant) {
- // Fastest loop, for the common case (normal variant).
- while (true) {
- page = node->page();
- if (page) {
- GlyphData data = page->glyphDataForCharacter(c);
- if (data.fontData && (data.fontData->platformData().orientation() == Horizontal || data.fontData->isTextOrientationFallback()))
- return std::make_pair(data, page);
-
- if (data.fontData) {
- if (Font::isCJKIdeographOrSymbol(c)) {
- if (!data.fontData->hasVerticalGlyphs()) {
- // Use the broken ideograph font data. The broken ideograph font will use the horizontal width of glyphs
- // to make sure you get a square (even for broken glyphs like symbols used for punctuation).
- variant = BrokenIdeographVariant;
- break;
- }
-#if PLATFORM(MAC)
- else if (data.fontData->platformData().syntheticOblique())
- return glyphDataAndPageForCJKCharacterWithoutSyntheticItalic(c, data, page, pageNumber);
-#endif
- } else
- return glyphDataAndPageForNonCJKCharacterWithGlyphOrientation(c, description.nonCJKGlyphOrientation(), data, page, pageNumber);
-
- return std::make_pair(data, page);
- }
-
- if (node->isSystemFallback())
- break;
- }
-
- node = node->getChild(realizeFontDataAt(description, node->level()), pageNumber);
- if (pageNumber)
- m_pages.set(pageNumber, node);
- else
- m_pageZero = node;
- }
- }
- if (variant != NormalVariant) {
- while (true) {
- page = node->page();
- if (page) {
- GlyphData data = page->glyphDataForCharacter(c);
- if (data.fontData) {
- // The variantFontData function should not normally return 0.
- // But if it does, we will just render the capital letter big.
- RefPtr<SimpleFontData> variantFontData = data.fontData->variantFontData(description, variant);
- if (!variantFontData)
- return std::make_pair(data, page);
-
- GlyphPageTreeNode* variantNode = GlyphPageTreeNode::getRootChild(variantFontData.get(), pageNumber);
- GlyphPage* variantPage = variantNode->page();
- if (variantPage) {
- GlyphData data = variantPage->glyphDataForCharacter(c);
- if (data.fontData)
- return std::make_pair(data, variantPage);
- }
-
- // Do not attempt system fallback off the variantFontData. This is the very unlikely case that
- // a font has the lowercase character but the small caps font does not have its uppercase version.
- return std::make_pair(variantFontData->missingGlyphData(), page);
- }
-
- if (node->isSystemFallback())
- break;
- }
-
- node = node->getChild(realizeFontDataAt(description, node->level()), pageNumber);
- if (pageNumber)
- m_pages.set(pageNumber, node);
- else
- m_pageZero = node;
- }
- }
-
- ASSERT(page);
- ASSERT(node->isSystemFallback());
-
- // System fallback is character-dependent. When we get here, we
- // know that the character in question isn't in the system fallback
- // font's glyph page. Try to lazily create it here.
- UChar codeUnits[2];
- int codeUnitsLength;
- if (c <= 0xFFFF) {
- codeUnits[0] = Font::normalizeSpaces(c);
- codeUnitsLength = 1;
- } else {
- codeUnits[0] = U16_LEAD(c);
- codeUnits[1] = U16_TRAIL(c);
- codeUnitsLength = 2;
- }
- const SimpleFontData* originalFontData = primaryFontData(description)->fontDataForCharacter(c);
- RefPtr<SimpleFontData> characterFontData = fontCache()->systemFallbackForCharacters(description, originalFontData, m_isForPlatformFont, codeUnits, codeUnitsLength);
- if (characterFontData) {
- if (characterFontData->platformData().orientation() == Vertical && !characterFontData->hasVerticalGlyphs() && Font::isCJKIdeographOrSymbol(c))
- variant = BrokenIdeographVariant;
- if (variant != NormalVariant)
- characterFontData = characterFontData->variantFontData(description, variant);
- }
- if (characterFontData) {
- // Got the fallback glyph and font.
- GlyphPage* fallbackPage = GlyphPageTreeNode::getRootChild(characterFontData.get(), pageNumber)->page();
- GlyphData data = fallbackPage && fallbackPage->fontDataForCharacter(c) ? fallbackPage->glyphDataForCharacter(c) : characterFontData->missingGlyphData();
- // Cache it so we don't have to do system fallback again next time.
- if (variant == NormalVariant) {
-#if OS(WINCE)
- // missingGlyphData returns a null character, which is not suitable for GDI to display.
- // Also, sometimes we cannot map a font for the character on WINCE, but GDI can still
- // display the character, probably because the font package is not installed correctly.
- // So we just always set the glyph to be same as the character, and let GDI solve it.
- page->setGlyphDataForCharacter(c, c, characterFontData.get());
- characterFontData->setMaxGlyphPageTreeLevel(std::max(characterFontData->maxGlyphPageTreeLevel(), node->level()));
- return std::make_pair(page->glyphDataForCharacter(c), page);
-#else
- page->setGlyphDataForCharacter(c, data.glyph, data.fontData);
- data.fontData->setMaxGlyphPageTreeLevel(std::max(data.fontData->maxGlyphPageTreeLevel(), node->level()));
- if (!Font::isCJKIdeographOrSymbol(c) && data.fontData->platformData().orientation() != Horizontal && !data.fontData->isTextOrientationFallback())
- return glyphDataAndPageForNonCJKCharacterWithGlyphOrientation(c, description.nonCJKGlyphOrientation(), data, fallbackPage, pageNumber);
-#endif
- }
- return std::make_pair(data, page);
- }
-
- // Even system fallback can fail; use the missing glyph in that case.
- // FIXME: It would be nicer to use the missing glyph from the last resort font instead.
- GlyphData data = primarySimpleFontData(description)->missingGlyphData();
- if (variant == NormalVariant) {
-#if OS(WINCE)
- // See comment about WINCE GDI handling near setGlyphDataForCharacter above.
- page->setGlyphDataForCharacter(c, c, data.fontData);
- data.fontData->setMaxGlyphPageTreeLevel(std::max(data.fontData->maxGlyphPageTreeLevel(), node->level()));
- return std::make_pair(page->glyphDataForCharacter(c), page);
-#else
- page->setGlyphDataForCharacter(c, data.glyph, data.fontData);
- data.fontData->setMaxGlyphPageTreeLevel(std::max(data.fontData->maxGlyphPageTreeLevel(), node->level()));
-#endif
- }
- return std::make_pair(data, page);
-}
-
-
-}
diff --git a/Source/WebCore/platform/graphics/FontGlyphs.h b/Source/WebCore/platform/graphics/FontGlyphs.h
deleted file mode 100644
index 4c70d7df2..000000000
--- a/Source/WebCore/platform/graphics/FontGlyphs.h
+++ /dev/null
@@ -1,133 +0,0 @@
-/*
- * Copyright (C) 2006, 2010, 2013 Apple Inc. All rights reserved.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public License
- * along with this library; see the file COPYING.LIB. If not, write to
- * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
- * Boston, MA 02110-1301, USA.
- *
- */
-
-#ifndef FontGlyphs_h
-#define FontGlyphs_h
-
-#include "FontSelector.h"
-#include "SimpleFontData.h"
-#include "WidthCache.h"
-#include <wtf/Forward.h>
-#include <wtf/MainThread.h>
-
-#if PLATFORM(IOS)
-#include "WebCoreThread.h"
-#endif
-
-namespace WebCore {
-
-class GlyphPageTreeNode;
-class GraphicsContext;
-class IntRect;
-class FontDescription;
-class FontPlatformData;
-class FontSelector;
-
-const int cAllFamiliesScanned = -1;
-
-class FontGlyphs : public RefCounted<FontGlyphs> {
- WTF_MAKE_NONCOPYABLE(FontGlyphs);
-public:
- typedef HashMap<int, GlyphPageTreeNode*, DefaultHash<int>::Hash> GlyphPages;
-
- class GlyphPagesStateSaver {
- public:
- GlyphPagesStateSaver(FontGlyphs& glyphs)
- : m_glyphs(glyphs)
- , m_pages(glyphs.m_pages)
- , m_pageZero(glyphs.m_pageZero)
- {
- }
-
- ~GlyphPagesStateSaver()
- {
- m_glyphs.m_pages = m_pages;
- m_glyphs.m_pageZero = m_pageZero;
- }
-
- private:
- FontGlyphs& m_glyphs;
- GlyphPages& m_pages;
- GlyphPageTreeNode* m_pageZero;
- };
-
- static PassRef<FontGlyphs> create(PassRefPtr<FontSelector> fontSelector) { return adoptRef(*new FontGlyphs(fontSelector)); }
- static PassRef<FontGlyphs> createForPlatformFont(const FontPlatformData& platformData) { return adoptRef(*new FontGlyphs(platformData)); }
-
- ~FontGlyphs() { releaseFontData(); }
-
- bool isForPlatformFont() const { return m_isForPlatformFont; }
-
- std::pair<GlyphData, GlyphPage*> glyphDataAndPageForCharacter(const FontDescription&, UChar32, bool mirror, FontDataVariant) const;
-
- bool isFixedPitch(const FontDescription&) const;
- void determinePitch(const FontDescription&) const;
-
- bool loadingCustomFonts() const { return m_loadingCustomFonts; }
-
- FontSelector* fontSelector() const { return m_fontSelector.get(); }
- // FIXME: It should be possible to combine fontSelectorVersion and generation.
- unsigned fontSelectorVersion() const { return m_fontSelectorVersion; }
- unsigned generation() const { return m_generation; }
-
- WidthCache& widthCache() const { return m_widthCache; }
-
- const SimpleFontData* primarySimpleFontData(const FontDescription&) const;
- const FontData* primaryFontData(const FontDescription& description) const { return realizeFontDataAt(description, 0); }
- const FontData* realizeFontDataAt(const FontDescription&, unsigned index) const;
-
-private:
- FontGlyphs(PassRefPtr<FontSelector>);
- FontGlyphs(const FontPlatformData&);
-
- void releaseFontData();
-
- mutable Vector<RefPtr<FontData>, 1> m_realizedFontData;
- mutable GlyphPages m_pages;
- mutable GlyphPageTreeNode* m_pageZero;
- mutable const SimpleFontData* m_cachedPrimarySimpleFontData;
- RefPtr<FontSelector> m_fontSelector;
- mutable WidthCache m_widthCache;
- unsigned m_fontSelectorVersion;
- mutable int m_familyIndex;
- unsigned short m_generation;
- mutable unsigned m_pitch : 3; // Pitch
- mutable bool m_loadingCustomFonts : 1;
- bool m_isForPlatformFont : 1;
-};
-
-inline bool FontGlyphs::isFixedPitch(const FontDescription& description) const
-{
- if (m_pitch == UnknownPitch)
- determinePitch(description);
- return m_pitch == FixedPitch;
-};
-
-inline const SimpleFontData* FontGlyphs::primarySimpleFontData(const FontDescription& description) const
-{
- ASSERT(isMainThread());
- if (!m_cachedPrimarySimpleFontData)
- m_cachedPrimarySimpleFontData = primaryFontData(description)->fontDataForCharacter(' ');
- return m_cachedPrimarySimpleFontData;
-}
-
-}
-
-#endif
diff --git a/Source/WebCore/platform/graphics/FontMetrics.h b/Source/WebCore/platform/graphics/FontMetrics.h
index 5142ffe25..090d5ca9c 100644
--- a/Source/WebCore/platform/graphics/FontMetrics.h
+++ b/Source/WebCore/platform/graphics/FontMetrics.h
@@ -22,25 +22,13 @@
#include "FontBaseline.h"
#include <wtf/MathExtras.h>
+#include <wtf/Optional.h>
namespace WebCore {
-const unsigned gDefaultUnitsPerEm = 1000;
-
class FontMetrics {
public:
- FontMetrics()
- : m_unitsPerEm(gDefaultUnitsPerEm)
- , m_ascent(0)
- , m_descent(0)
- , m_lineGap(0)
- , m_lineSpacing(0)
- , m_xHeight(0)
- , m_zeroWidth(0)
- , m_hasXHeight(false)
- , m_hasZeroWidth(false)
- {
- }
+ static const unsigned defaultUnitsPerEm = 1000;
unsigned unitsPerEm() const { return m_unitsPerEm; }
void setUnitsPerEm(unsigned unitsPerEm) { m_unitsPerEm = unitsPerEm; }
@@ -75,15 +63,13 @@ public:
void setLineSpacing(float lineSpacing) { m_lineSpacing = lineSpacing; }
float xHeight() const { return m_xHeight; }
- void setXHeight(float xHeight)
- {
- m_xHeight = xHeight;
- m_hasXHeight = true;
- }
-
- bool hasXHeight() const { return m_hasXHeight && m_xHeight > 0; }
- void setHasXHeight(bool hasXHeight) { m_hasXHeight = hasXHeight; }
-
+ void setXHeight(float xHeight) { m_xHeight = xHeight; }
+ bool hasXHeight() const { return m_xHeight > 0; }
+
+ bool hasCapHeight() const { return m_capHeight > 0; }
+ float floatCapHeight() const { return m_capHeight; }
+ void setCapHeight(float capHeight) { m_capHeight = capHeight; }
+
// Integer variants of certain metrics, used for HTML rendering.
int ascent(FontBaseline baselineType = AlphabeticBaseline) const
{
@@ -91,7 +77,7 @@ public:
return lroundf(m_ascent);
return height() - height() / 2;
}
-
+
int descent(FontBaseline baselineType = AlphabeticBaseline) const
{
if (baselineType == AlphabeticBaseline)
@@ -106,45 +92,40 @@ public:
int lineGap() const { return lroundf(m_lineGap); }
int lineSpacing() const { return lroundf(m_lineSpacing); }
-
+
+ int capHeight() const { return lroundf(m_capHeight); }
+
bool hasIdenticalAscentDescentAndLineGap(const FontMetrics& other) const
{
return ascent() == other.ascent() && descent() == other.descent() && lineGap() == other.lineGap();
}
float zeroWidth() const { return m_zeroWidth; }
- void setZeroWidth(float zeroWidth)
- {
- m_zeroWidth = zeroWidth;
- m_hasZeroWidth = true;
- }
-
- bool hasZeroWidth() const { return m_hasZeroWidth; }
- void setHasZeroWidth(bool hasZeroWidth) { m_hasZeroWidth = hasZeroWidth; }
+ void setZeroWidth(float zeroWidth) { m_zeroWidth = zeroWidth; }
private:
- friend class SimpleFontData;
+ friend class Font;
void reset()
{
- m_unitsPerEm = gDefaultUnitsPerEm;
+ m_unitsPerEm = defaultUnitsPerEm;
m_ascent = 0;
m_descent = 0;
m_lineGap = 0;
m_lineSpacing = 0;
m_xHeight = 0;
- m_hasXHeight = false;
+ m_capHeight = 0;
+ m_zeroWidth = 0;
}
- unsigned m_unitsPerEm;
- float m_ascent;
- float m_descent;
- float m_lineGap;
- float m_lineSpacing;
- float m_xHeight;
- float m_zeroWidth;
- bool m_hasXHeight;
- bool m_hasZeroWidth;
+ unsigned m_unitsPerEm { defaultUnitsPerEm };
+ float m_ascent { 0 };
+ float m_descent { 0 };
+ float m_lineGap { 0 };
+ float m_lineSpacing { 0 };
+ float m_zeroWidth { 0 };
+ float m_xHeight { 0 };
+ float m_capHeight { 0 };
};
static inline float scaleEmToUnits(float x, unsigned unitsPerEm)
diff --git a/Source/WebCore/platform/graphics/FontOrientation.h b/Source/WebCore/platform/graphics/FontOrientation.h
deleted file mode 100644
index 12cf5c1b6..000000000
--- a/Source/WebCore/platform/graphics/FontOrientation.h
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * Copyright (C) 2010 Apple Inc. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
- * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
- * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#ifndef FontOrientation_h
-#define FontOrientation_h
-
-namespace WebCore {
-
-enum FontOrientation { Horizontal, Vertical };
-
-} // namespace WebCore
-
-#endif // FontOrientation_h
diff --git a/Source/WebCore/platform/graphics/FontPlatformData.cpp b/Source/WebCore/platform/graphics/FontPlatformData.cpp
new file mode 100644
index 000000000..e41902cf6
--- /dev/null
+++ b/Source/WebCore/platform/graphics/FontPlatformData.cpp
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2011 Brent Fulgham
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "config.h"
+#include "FontPlatformData.h"
+
+#include <wtf/HashMap.h>
+#include <wtf/RetainPtr.h>
+#include <wtf/text/StringHash.h>
+#include <wtf/text/WTFString.h>
+
+#if OS(DARWIN) && USE(CG)
+#include "SharedBuffer.h"
+#include <CoreGraphics/CGFont.h>
+#endif
+
+#if USE(DIRECT2D)
+#include <dwrite.h>
+#endif
+
+namespace WebCore {
+
+FontPlatformData::FontPlatformData(WTF::HashTableDeletedValueType)
+ : m_isHashTableDeletedValue(true)
+{
+}
+
+FontPlatformData::FontPlatformData()
+{
+}
+
+FontPlatformData::FontPlatformData(float size, bool syntheticBold, bool syntheticOblique, FontOrientation orientation, FontWidthVariant widthVariant, TextRenderingMode textRenderingMode)
+ : m_size(size)
+ , m_orientation(orientation)
+ , m_widthVariant(widthVariant)
+ , m_textRenderingMode(textRenderingMode)
+ , m_syntheticBold(syntheticBold)
+ , m_syntheticOblique(syntheticOblique)
+{
+}
+
+#if USE(CG) && (PLATFORM(WIN) || (PLATFORM(MAC) && __MAC_OS_X_VERSION_MIN_REQUIRED < 101200) || (PLATFORM(IOS) && __IPHONE_OS_VERSION_MIN_REQUIRED < 100000))
+FontPlatformData::FontPlatformData(CGFontRef cgFont, float size, bool syntheticBold, bool syntheticOblique, FontOrientation orientation, FontWidthVariant widthVariant, TextRenderingMode textRenderingMode)
+ : FontPlatformData(size, syntheticBold, syntheticOblique, orientation, widthVariant, textRenderingMode)
+{
+ m_cgFont = cgFont;
+ ASSERT(m_cgFont);
+}
+#endif
+
+#if !USE(FREETYPE)
+FontPlatformData FontPlatformData::cloneWithOrientation(const FontPlatformData& source, FontOrientation orientation)
+{
+ FontPlatformData copy(source);
+ copy.m_orientation = orientation;
+ return copy;
+}
+
+FontPlatformData FontPlatformData::cloneWithSyntheticOblique(const FontPlatformData& source, bool syntheticOblique)
+{
+ FontPlatformData copy(source);
+ copy.m_syntheticOblique = syntheticOblique;
+ return copy;
+}
+
+FontPlatformData FontPlatformData::cloneWithSize(const FontPlatformData& source, float size)
+{
+ FontPlatformData copy(source);
+ copy.m_size = size;
+ return copy;
+}
+#endif
+
+}
diff --git a/Source/WebCore/platform/graphics/FontPlatformData.h b/Source/WebCore/platform/graphics/FontPlatformData.h
index bfa57095e..b154b65d4 100644
--- a/Source/WebCore/platform/graphics/FontPlatformData.h
+++ b/Source/WebCore/platform/graphics/FontPlatformData.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2006, 2007, 2008, 2010, 2013 Apple Inc.
+ * Copyright (C) 2006-2017 Apple Inc. All rights reserved.
* Copyright (C) 2006 Michael Emmel mike.emmel@gmail.com
* Copyright (C) 2007 Holger Hans Peter Freyther
* Copyright (C) 2007 Pioneer Research Center USA, Inc.
@@ -22,58 +22,50 @@
*
*/
-// FIXME: This is temporary until all ports switch to using this file.
-#if USE(WINGDI)
-#include "wince/FontPlatformData.h"
-#elif PLATFORM(EFL) || PLATFORM(GTK)
-#include "freetype/FontPlatformData.h"
-#else
+#pragma once
-#ifndef FontPlatformData_h
-#define FontPlatformData_h
+#include "TextFlags.h"
+#include <wtf/Forward.h>
+#include <wtf/PassRefPtr.h>
+#include <wtf/RefCounted.h>
+#include <wtf/RetainPtr.h>
+#include <wtf/text/StringImpl.h>
-#include "FontOrientation.h"
-#include "FontWidthVariant.h"
#if PLATFORM(WIN)
+#include "COMPtr.h"
#include "SharedGDIObject.h"
#endif
#if USE(CAIRO)
+#include "RefPtrCairo.h"
#include <wtf/HashFunctions.h>
#include <cairo.h>
#endif
-#if OS(DARWIN)
-OBJC_CLASS NSFont;
+#if USE(FREETYPE)
+#include "FcUniquePtr.h"
+#include "HarfBuzzFace.h"
+#include "OpenTypeVerticalData.h"
+#endif
-#if PLATFORM(IOS)
-#import <CoreGraphics/CoreGraphics.h>
+#if USE(APPKIT)
+OBJC_CLASS NSFont;
#endif
-typedef struct CGFont* CGFontRef;
+#if PLATFORM(COCOA)
typedef const struct __CTFont* CTFontRef;
#endif
-#include <wtf/Forward.h>
-#include <wtf/PassRefPtr.h>
-#include <wtf/RefCounted.h>
-#include <wtf/RetainPtr.h>
-#include <wtf/text/StringImpl.h>
+#if USE(CG)
+#include <CoreGraphics/CoreGraphics.h>
+#endif
#if PLATFORM(WIN)
#include <wtf/win/GDIObject.h>
typedef struct HFONT__* HFONT;
-#endif
-
-#if USE(CG)
-typedef struct CGFont* CGFontRef;
-#if OS(DARWIN)
-typedef const struct __CTFont* CTFontRef;
-typedef UInt32 FMFont;
-typedef FMFont ATSUFontID;
-typedef UInt32 ATSFontRef;
-#endif
+interface IDWriteFont;
+interface IDWriteFontFace;
#endif
namespace WebCore {
@@ -81,143 +73,136 @@ namespace WebCore {
class FontDescription;
class SharedBuffer;
-#if OS(DARWIN) && USE(APPKIT)
-inline CTFontRef toCTFontRef(NSFont *nsFont) { return reinterpret_cast<CTFontRef>(nsFont); }
-#endif
-
+// This class is conceptually immutable. Once created, no instances should ever change (in an observable way).
class FontPlatformData {
+ WTF_MAKE_FAST_ALLOCATED;
public:
FontPlatformData(WTF::HashTableDeletedValueType);
FontPlatformData();
- FontPlatformData(const FontPlatformData&);
+
FontPlatformData(const FontDescription&, const AtomicString& family);
- FontPlatformData(float size, bool syntheticBold, bool syntheticOblique, FontOrientation = Horizontal, FontWidthVariant = RegularWidth);
+ FontPlatformData(float size, bool syntheticBold, bool syntheticOblique, FontOrientation = Horizontal, FontWidthVariant = RegularWidth, TextRenderingMode = AutoTextRendering);
-#if OS(DARWIN)
-#if USE(APPKIT)
- FontPlatformData(NSFont*, float size, bool isPrinterFont = false, bool syntheticBold = false, bool syntheticOblique = false,
- FontOrientation = Horizontal, FontWidthVariant = RegularWidth);
-#else
- FontPlatformData(CTFontRef, float size, bool isPrinterFont = false, bool syntheticBold = false, bool syntheticOblique = false,
- FontOrientation = Horizontal, FontWidthVariant = RegularWidth);
+#if PLATFORM(COCOA)
+ WEBCORE_EXPORT FontPlatformData(CTFontRef, float size, bool syntheticBold = false, bool syntheticOblique = false, FontOrientation = Horizontal, FontWidthVariant = RegularWidth, TextRenderingMode = AutoTextRendering);
#endif
-#if USE(CG)
- FontPlatformData(CGFontRef, float size, bool syntheticBold, bool syntheticOblique, FontOrientation, FontWidthVariant);
-#endif
+ static FontPlatformData cloneWithOrientation(const FontPlatformData&, FontOrientation);
+ static FontPlatformData cloneWithSyntheticOblique(const FontPlatformData&, bool);
+ static FontPlatformData cloneWithSize(const FontPlatformData&, float);
+
+#if USE(CG) && (PLATFORM(WIN) || (PLATFORM(MAC) && __MAC_OS_X_VERSION_MIN_REQUIRED < 101200) || (PLATFORM(IOS) && __IPHONE_OS_VERSION_MIN_REQUIRED < 100000))
+ FontPlatformData(CGFontRef, float size, bool syntheticBold, bool syntheticOblique, FontOrientation, FontWidthVariant, TextRenderingMode);
#endif
+
#if PLATFORM(WIN)
FontPlatformData(GDIObject<HFONT>, float size, bool syntheticBold, bool syntheticOblique, bool useGDI);
-#if USE(CG)
+#endif
+
+#if PLATFORM(WIN) && USE(CG)
FontPlatformData(GDIObject<HFONT>, CGFontRef, float size, bool syntheticBold, bool syntheticOblique, bool useGDI);
-#elif USE(CAIRO)
- FontPlatformData(GDIObject<HFONT>, cairo_font_face_t*, float size, bool bold, bool italic);
#endif
+
+#if PLATFORM(WIN) && USE(DIRECT2D)
+ FontPlatformData(GDIObject<HFONT>, IDWriteFont*, float size, bool syntheticBold, bool syntheticOblique, bool useGDI);
#endif
-#if PLATFORM(IOS)
- FontPlatformData(CTFontRef, float size, bool syntheticBold = false, bool syntheticOblique = false, FontOrientation = Horizontal, FontWidthVariant = RegularWidth);
+#if PLATFORM(WIN) && USE(CAIRO)
+ FontPlatformData(GDIObject<HFONT>, cairo_font_face_t*, float size, bool bold, bool italic);
#endif
+#if USE(FREETYPE)
+ FontPlatformData(FcPattern*, const FontDescription&);
+ FontPlatformData(cairo_font_face_t*, const FontDescription&, bool syntheticBold, bool syntheticOblique);
+ FontPlatformData(const FontPlatformData&);
+ FontPlatformData(FontPlatformData&&) = default;
+ FontPlatformData& operator=(const FontPlatformData&);
+ FontPlatformData& operator=(FontPlatformData&&) = default;
~FontPlatformData();
+#endif
#if PLATFORM(WIN)
HFONT hfont() const { return m_font ? m_font->get() : 0; }
bool useGDI() const { return m_useGDI; }
-#elif PLATFORM(IOS)
- CTFontRef font() const { return m_font; }
- void setFont(CTFontRef);
-#elif OS(DARWIN)
- NSFont* font() const { return m_font; }
- void setFont(NSFont*);
#endif
-#if USE(CG)
-#if OS(DARWIN)
- CGFontRef cgFont() const { return m_cgFont.get(); }
+#if PLATFORM(COCOA)
+ CTFontRef font() const { return m_font.get(); }
+ WEBCORE_EXPORT CTFontRef registeredFont() const; // Returns nullptr iff the font is not registered, such as web fonts (otherwise returns font()).
+
CTFontRef ctFont() const;
+ static RetainPtr<CFTypeRef> objectForEqualityCheck(CTFontRef);
+ RetainPtr<CFTypeRef> objectForEqualityCheck() const;
-#if !PLATFORM(IOS)
- bool roundsGlyphAdvances() const;
-#else
- bool roundsGlyphAdvances() const { return false; }
-#endif // !PLATFORM(IOS)
+ bool hasCustomTracking() const { return isSystemFont(); }
+#endif
- bool allowsLigatures() const;
-#else
+#if PLATFORM(WIN) || PLATFORM(COCOA)
+ bool isSystemFont() const { return m_isSystemFont; }
+#endif
+
+ bool hasVariations() const { return m_hasVariations; }
+
+#if USE(CG) && (PLATFORM(WIN) || (PLATFORM(MAC) && __MAC_OS_X_VERSION_MIN_REQUIRED < 101200) || (PLATFORM(IOS) && __IPHONE_OS_VERSION_MIN_REQUIRED < 100000))
CGFontRef cgFont() const { return m_cgFont.get(); }
#endif
+
+#if USE(DIRECT2D)
+ IDWriteFont* dwFont() const { return m_dwFont.get(); }
+ IDWriteFontFace* dwFontFace() const { return m_dwFontFace.get(); }
#endif
bool isFixedPitch() const;
float size() const { return m_size; }
- void setSize(float size) { m_size = size; }
bool syntheticBold() const { return m_syntheticBold; }
bool syntheticOblique() const { return m_syntheticOblique; }
bool isColorBitmapFont() const { return m_isColorBitmapFont; }
- bool isCompositeFontReference() const { return m_isCompositeFontReference; }
-#if OS(DARWIN)
- bool isPrinterFont() const { return m_isPrinterFont; }
-#endif
FontOrientation orientation() const { return m_orientation; }
FontWidthVariant widthVariant() const { return m_widthVariant; }
-
- void setOrientation(FontOrientation orientation) { m_orientation = orientation; }
+ TextRenderingMode textRenderingMode() const { return m_textRenderingMode; }
+ bool isForTextCombine() const { return widthVariant() != RegularWidth; } // Keep in sync with callers of FontDescription::setWidthVariant().
#if USE(CAIRO)
- cairo_scaled_font_t* scaledFont() const { return m_scaledFont; }
+ cairo_scaled_font_t* scaledFont() const { return m_scaledFont.get(); }
#endif
- unsigned hash() const
- {
-#if PLATFORM(WIN) && !USE(CAIRO)
- return m_font ? m_font->hash() : 0;
-#elif OS(DARWIN)
-#if !PLATFORM(IOS)
-#if USE(CG)
- ASSERT(m_font || !m_cgFont);
+#if USE(FREETYPE)
+ HarfBuzzFace* harfBuzzFace() const;
+ bool hasCompatibleCharmap() const;
+ FcFontSet* fallbacks() const;
#endif
- uintptr_t hashCodes[3] = { (uintptr_t)m_font, m_widthVariant, static_cast<uintptr_t>(m_isPrinterFont << 3 | m_orientation << 2 | m_syntheticBold << 1 | m_syntheticOblique) };
-#else
- ASSERT(m_font || !m_cgFont || m_isEmoji);
- uintptr_t hashCodes[3] = { static_cast<uintptr_t>(CFHash(m_font)), m_widthVariant, static_cast<uintptr_t>(m_isEmoji << 4 | m_isPrinterFont << 3 | m_orientation << 2 | m_syntheticBold << 1 | m_syntheticOblique) };
-#endif // !PLATFORM(IOS)
- return StringHasher::hashMemory<sizeof(hashCodes)>(hashCodes);
-#elif USE(CAIRO)
- return PtrHash<cairo_scaled_font_t*>::hash(m_scaledFont);
-#endif
- }
- const FontPlatformData& operator=(const FontPlatformData&);
+ unsigned hash() const;
bool operator==(const FontPlatformData& other) const
{
return platformIsEqual(other)
+ && m_isHashTableDeletedValue == other.m_isHashTableDeletedValue
&& m_size == other.m_size
&& m_syntheticBold == other.m_syntheticBold
&& m_syntheticOblique == other.m_syntheticOblique
&& m_isColorBitmapFont == other.m_isColorBitmapFont
- && m_isCompositeFontReference == other.m_isCompositeFontReference
-#if OS(DARWIN)
- && m_isPrinterFont == other.m_isPrinterFont
-#endif
&& m_orientation == other.m_orientation
- && m_widthVariant == other.m_widthVariant;
+ && m_widthVariant == other.m_widthVariant
+ && m_textRenderingMode == other.m_textRenderingMode;
}
bool isHashTableDeletedValue() const
{
-#if PLATFORM(WIN) && !USE(CAIRO)
- return m_font.isHashTableDeletedValue();
-#elif OS(DARWIN)
- return m_font == hashTableDeletedFontValue();
-#elif USE(CAIRO)
- return m_scaledFont == hashTableDeletedFontValue();
+ return m_isHashTableDeletedValue;
+ }
+
+ bool isEmoji() const
+ {
+#if PLATFORM(IOS)
+ return m_isEmoji;
+#else
+ return false;
#endif
}
-#if PLATFORM(WIN) && (USE(CG) || USE(CAIRO))
- PassRefPtr<SharedBuffer> openTypeTable(uint32_t table) const;
+#if PLATFORM(COCOA) || PLATFORM(WIN) || USE(FREETYPE)
+ RefPtr<SharedBuffer> openTypeTable(uint32_t table) const;
#endif
#ifndef NDEBUG
@@ -226,67 +211,116 @@ public:
private:
bool platformIsEqual(const FontPlatformData&) const;
- void platformDataInit(const FontPlatformData&);
- const FontPlatformData& platformDataAssign(const FontPlatformData&);
-#if PLATFORM(IOS)
- static CTFontRef hashTableDeletedFontValue() { return reinterpret_cast<CTFontRef>(-1); }
-#elif OS(DARWIN)
- // Load various data about the font specified by |nsFont| with the size fontSize into the following output paramters:
- void loadFont(NSFont*, float fontSize, NSFont*& outNSFont, CGFontRef&);
- static NSFont* hashTableDeletedFontValue() { return reinterpret_cast<NSFont *>(-1); }
-#elif PLATFORM(WIN)
- void platformDataInit(HFONT, float size, HDC, WCHAR* faceName);
+
+#if PLATFORM(COCOA)
+ CGFloat ctFontSize() const;
#endif
-#if USE(CAIRO)
- static cairo_scaled_font_t* hashTableDeletedFontValue() { return reinterpret_cast<cairo_scaled_font_t*>(-1); }
+#if PLATFORM(WIN)
+ void platformDataInit(HFONT, float size, HDC, WCHAR* faceName);
#endif
-public:
- bool m_syntheticBold;
- bool m_syntheticOblique;
- FontOrientation m_orientation;
-#if PLATFORM(IOS)
- bool m_isEmoji;
+#if USE(FREETYPE)
+ void buildScaledFont(cairo_font_face_t*);
#endif
- float m_size;
- FontWidthVariant m_widthVariant;
-private:
-#if PLATFORM(IOS)
- CTFontRef m_font;
-#elif OS(DARWIN)
- NSFont* m_font;
+#if PLATFORM(COCOA)
+ // FIXME: Get rid of one of these. These two fonts are subtly different, and it is not obvious which one to use where.
+ RetainPtr<CTFontRef> m_font;
+ mutable RetainPtr<CTFontRef> m_ctFont;
#elif PLATFORM(WIN)
RefPtr<SharedGDIObject<HFONT>> m_font;
#endif
-#if USE(CG)
-#if PLATFORM(WIN)
- RetainPtr<CGFontRef> m_cgFont;
-#else
+#if USE(CG) && (PLATFORM(WIN) || (PLATFORM(MAC) && __MAC_OS_X_VERSION_MIN_REQUIRED < 101200) || (PLATFORM(IOS) && __IPHONE_OS_VERSION_MIN_REQUIRED < 100000))
RetainPtr<CGFontRef> m_cgFont;
- mutable RetainPtr<CTFontRef> m_CTFont;
#endif
+
+#if USE(DIRECT2D)
+ COMPtr<IDWriteFont> m_dwFont;
+ COMPtr<IDWriteFontFace> m_dwFontFace;
#endif
#if USE(CAIRO)
- cairo_scaled_font_t* m_scaledFont;
+ RefPtr<cairo_scaled_font_t> m_scaledFont;
#endif
- bool m_isColorBitmapFont;
- bool m_isCompositeFontReference;
-#if OS(DARWIN)
- bool m_isPrinterFont;
+#if USE(FREETYPE)
+ RefPtr<FcPattern> m_pattern;
+ mutable FcUniquePtr<FcFontSet> m_fallbacks;
+ mutable RefPtr<HarfBuzzFace> m_harfBuzzFace;
+#endif
+
+ // The values below are common to all ports
+ // FIXME: If they're common to all ports, they should move to Font
+ float m_size { 0 };
+
+ FontOrientation m_orientation { Horizontal };
+ FontWidthVariant m_widthVariant { RegularWidth };
+ TextRenderingMode m_textRenderingMode { AutoTextRendering };
+
+ bool m_syntheticBold { false };
+ bool m_syntheticOblique { false };
+ bool m_isColorBitmapFont { false };
+ bool m_isHashTableDeletedValue { false };
+ bool m_isSystemFont { false };
+ bool m_hasVariations { false };
+ // The values above are common to all ports
+
+#if PLATFORM(IOS)
+ bool m_isEmoji { false };
#endif
#if PLATFORM(WIN)
- bool m_useGDI;
+ bool m_useGDI { false };
+#endif
+
+#if USE(FREETYPE)
+ bool m_fixedWidth { false };
#endif
};
-} // namespace WebCore
+#if USE(APPKIT)
-#endif // FontPlatformData_h
+// NSFonts and CTFontRefs are toll-free-bridged.
+inline CTFontRef toCTFont(NSFont *font)
+{
+ return (CTFontRef)font;
+}
+
+inline NSFont *toNSFont(CTFontRef font)
+{
+ return (NSFont *)font;
+}
#endif
+
+#if USE(CG)
+
+class ScopedTextMatrix {
+public:
+ ScopedTextMatrix(CGAffineTransform newMatrix, CGContextRef context)
+ : m_context(context)
+ , m_textMatrix(CGContextGetTextMatrix(context))
+ {
+ CGContextSetTextMatrix(m_context, newMatrix);
+ }
+
+ ~ScopedTextMatrix()
+ {
+ CGContextSetTextMatrix(m_context, m_textMatrix);
+ }
+
+ CGAffineTransform savedMatrix() const
+ {
+ return m_textMatrix;
+ }
+
+private:
+ CGContextRef m_context;
+ CGAffineTransform m_textMatrix;
+};
+
+#endif
+
+} // namespace WebCore
diff --git a/Source/WebCore/platform/graphics/FontRanges.cpp b/Source/WebCore/platform/graphics/FontRanges.cpp
new file mode 100644
index 000000000..d1b791761
--- /dev/null
+++ b/Source/WebCore/platform/graphics/FontRanges.cpp
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2008, 2009, 2015 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "FontRanges.h"
+
+#include "Font.h"
+#include "FontSelector.h"
+#include <wtf/Assertions.h>
+#include <wtf/text/WTFString.h>
+
+namespace WebCore {
+
+const Font* FontRanges::Range::font() const
+{
+ return m_fontAccessor->font();
+}
+
+FontRanges::FontRanges()
+{
+}
+
+class TrivialFontAccessor final : public FontAccessor {
+public:
+ static Ref<TrivialFontAccessor> create(Ref<Font>&& font)
+ {
+ return adoptRef(*new TrivialFontAccessor(WTFMove(font)));
+ }
+
+private:
+ TrivialFontAccessor(RefPtr<Font>&& font)
+ : m_font(WTFMove(font))
+ {
+ }
+
+ const Font* font() const final
+ {
+ return m_font.get();
+ }
+
+ bool isLoading() const final
+ {
+ return m_font->isLoading();
+ }
+
+ RefPtr<Font> m_font;
+};
+
+FontRanges::FontRanges(RefPtr<Font>&& font)
+{
+ if (font)
+ m_ranges.append(Range { 0, 0x7FFFFFFF, TrivialFontAccessor::create(font.releaseNonNull()) });
+}
+
+FontRanges::~FontRanges()
+{
+}
+
+GlyphData FontRanges::glyphDataForCharacter(UChar32 character) const
+{
+ for (auto& range : m_ranges) {
+ if (range.from() <= character && character <= range.to()) {
+ if (auto* font = range.font()) {
+ auto glyphData = font->glyphDataForCharacter(character);
+ if (glyphData.glyph)
+ return glyphData;
+ }
+ }
+ }
+ return GlyphData();
+}
+
+const Font* FontRanges::fontForCharacter(UChar32 character) const
+{
+ return glyphDataForCharacter(character).font;
+}
+
+const Font& FontRanges::fontForFirstRange() const
+{
+ auto* font = m_ranges[0].font();
+ ASSERT(font);
+ return *font;
+}
+
+bool FontRanges::isLoading() const
+{
+ for (auto& range : m_ranges) {
+ if (range.fontAccessor().isLoading())
+ return true;
+ }
+ return false;
+}
+
+} // namespace WebCore
diff --git a/Source/WebCore/platform/graphics/FontRanges.h b/Source/WebCore/platform/graphics/FontRanges.h
new file mode 100644
index 000000000..4fe0bec47
--- /dev/null
+++ b/Source/WebCore/platform/graphics/FontRanges.h
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2008, 2009, 2015 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef FontRanges_h
+#define FontRanges_h
+
+#include "Font.h"
+#include <wtf/TypeCasts.h>
+#include <wtf/Vector.h>
+
+namespace WebCore {
+
+class FontAccessor;
+
+class FontRanges {
+public:
+ struct Range {
+ Range(UChar32 from, UChar32 to, Ref<FontAccessor>&& fontAccessor)
+ : m_from(from)
+ , m_to(to)
+ , m_fontAccessor(WTFMove(fontAccessor))
+ {
+ }
+
+ Range(const Range& range)
+ : m_from(range.m_from)
+ , m_to(range.m_to)
+ , m_fontAccessor(range.m_fontAccessor.copyRef())
+ {
+ }
+
+ Range(Range&&) = default;
+ Range& operator=(const Range&) = delete;
+ Range& operator=(Range&&) = default;
+
+ UChar32 from() const { return m_from; }
+ UChar32 to() const { return m_to; }
+ const Font* font() const;
+ const FontAccessor& fontAccessor() const { return m_fontAccessor; }
+
+ private:
+ UChar32 m_from;
+ UChar32 m_to;
+ Ref<FontAccessor> m_fontAccessor;
+ };
+
+ FontRanges();
+ explicit FontRanges(RefPtr<Font>&&);
+ ~FontRanges();
+
+ FontRanges(const FontRanges&) = default;
+ FontRanges& operator=(FontRanges&&) = default;
+
+ bool isNull() const { return m_ranges.isEmpty(); }
+
+ void appendRange(Range&& range) { m_ranges.append(WTFMove(range)); }
+ unsigned size() const { return m_ranges.size(); }
+ const Range& rangeAt(unsigned i) const { return m_ranges[i]; }
+
+ GlyphData glyphDataForCharacter(UChar32) const;
+ WEBCORE_EXPORT const Font* fontForCharacter(UChar32) const;
+ WEBCORE_EXPORT const Font& fontForFirstRange() const;
+ bool isLoading() const;
+
+private:
+ Vector<Range, 1> m_ranges;
+};
+
+}
+
+#endif
diff --git a/Source/WebCore/platform/graphics/FontSelector.h b/Source/WebCore/platform/graphics/FontSelector.h
index 68a22de1a..7f5f3230f 100644
--- a/Source/WebCore/platform/graphics/FontSelector.h
+++ b/Source/WebCore/platform/graphics/FontSelector.h
@@ -10,10 +10,10 @@
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
- * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
@@ -23,46 +23,42 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef FontSelector_h
-#define FontSelector_h
+#pragma once
+#include "FontRanges.h"
#include <wtf/Forward.h>
-#include <wtf/PassRefPtr.h>
#include <wtf/RefCounted.h>
namespace WebCore {
-class FontData;
+class FontCascadeDescription;
class FontDescription;
class FontSelectorClient;
+class FontAccessor : public RefCounted<FontAccessor> {
+public:
+ virtual ~FontAccessor() { }
+
+ virtual const Font* font() const = 0;
+ virtual bool isLoading() const = 0;
+};
+
class FontSelector : public RefCounted<FontSelector> {
public:
virtual ~FontSelector() { }
- // FIXME: Remove the "get" prefix from these two member functions
- virtual PassRefPtr<FontData> getFontData(const FontDescription&, const AtomicString& familyName) = 0;
- virtual PassRefPtr<FontData> getFallbackFontData(const FontDescription&, size_t) = 0;
+ virtual FontRanges fontRangesForFamily(const FontDescription&, const AtomicString&) = 0;
+ virtual RefPtr<Font> fallbackFontAt(const FontDescription&, size_t) = 0;
- virtual size_t fallbackFontDataCount() = 0;
- virtual bool resolvesFamilyFor(const FontDescription&) const = 0;
+ virtual size_t fallbackFontCount() = 0;
virtual void fontCacheInvalidated() { }
- virtual void registerForInvalidationCallbacks(FontSelectorClient*) = 0;
- virtual void unregisterForInvalidationCallbacks(FontSelectorClient*) = 0;
+ virtual void registerForInvalidationCallbacks(FontSelectorClient&) = 0;
+ virtual void unregisterForInvalidationCallbacks(FontSelectorClient&) = 0;
virtual unsigned uniqueId() const = 0;
virtual unsigned version() const = 0;
};
-class FontSelectorClient {
-public:
- virtual ~FontSelectorClient() { }
-
- virtual void fontsNeedUpdate(FontSelector*) = 0;
-};
-
-} // namespace WebCore
-
-#endif // FontSelector_h
+}
diff --git a/Source/WebCore/platform/graphics/FontSmoothingMode.h b/Source/WebCore/platform/graphics/FontSelectorClient.h
index 7c23394f1..a83468580 100644
--- a/Source/WebCore/platform/graphics/FontSmoothingMode.h
+++ b/Source/WebCore/platform/graphics/FontSelectorClient.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009 Apple Inc. All rights reserved.
+ * Copyright (C) 2007, 2008, 2016 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -20,16 +20,20 @@
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef FontSmoothingMode_h
-#define FontSmoothingMode_h
+#pragma once
namespace WebCore {
- enum FontSmoothingMode { AutoSmoothing, NoSmoothing, Antialiased, SubpixelAntialiased };
-
-} // namespace WebCore
+class FontSelector;
-#endif // FontSmoothingMode_h
+class FontSelectorClient {
+public:
+ virtual ~FontSelectorClient() { }
+
+ virtual void fontsNeedUpdate(FontSelector&) = 0;
+};
+
+}
diff --git a/Source/WebCore/platform/graphics/FontTaggedSettings.cpp b/Source/WebCore/platform/graphics/FontTaggedSettings.cpp
new file mode 100644
index 000000000..3aaad5af4
--- /dev/null
+++ b/Source/WebCore/platform/graphics/FontTaggedSettings.cpp
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2011 Google Inc. All rights reserved.
+ * Copyright (C) 2016 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "FontTaggedSettings.h"
+
+#include "TextStream.h"
+
+#include <wtf/text/AtomicStringHash.h>
+
+namespace WebCore {
+
+template <>
+unsigned FontFeatureSettings::hash() const
+{
+ IntegerHasher hasher;
+ for (auto& feature : m_list) {
+ hasher.add(FourCharacterTagHash::hash(feature.tag()));
+ hasher.add(feature.value());
+ }
+ return hasher.hash();
+}
+
+#if ENABLE(VARIATION_FONTS)
+template <>
+unsigned FontVariationSettings::hash() const
+{
+ static_assert(sizeof(float) == sizeof(int), "IntegerHasher needs to accept floats too");
+ union {
+ float f;
+ int i;
+ } floatToInt;
+
+ IntegerHasher hasher;
+ for (auto& variation : m_list) {
+ hasher.add(FourCharacterTagHash::hash(variation.tag()));
+ floatToInt.f = variation.value();
+ hasher.add(floatToInt.i);
+ }
+ return hasher.hash();
+}
+
+TextStream& operator<<(TextStream& ts, const FontVariationSettings& item)
+{
+ for (unsigned i = 0; i < item.size(); ++i) {
+ auto& variation = item.at(i);
+ StringBuilder s;
+ s.append(variation.tag()[0]);
+ s.append(variation.tag()[1]);
+ s.append(variation.tag()[2]);
+ s.append(variation.tag()[3]);
+ ts.dumpProperty(s.toString(), item.at(i).value());
+ }
+ return ts;
+}
+#endif
+
+}
diff --git a/Source/WebCore/platform/graphics/FontTaggedSettings.h b/Source/WebCore/platform/graphics/FontTaggedSettings.h
new file mode 100644
index 000000000..7553c4f91
--- /dev/null
+++ b/Source/WebCore/platform/graphics/FontTaggedSettings.h
@@ -0,0 +1,153 @@
+/*
+ * Copyright (C) 2011 Google Inc. All rights reserved.
+ * Copyright (C) 2016 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <array>
+#include <wtf/Vector.h>
+#include <wtf/text/AtomicString.h>
+
+namespace WebCore {
+
+class TextStream;
+
+typedef std::array<char, 4> FontTag;
+
+inline FontTag fontFeatureTag(const char arr[4]) { return {{ arr[0], arr[1], arr[2], arr[3] }}; }
+
+struct FourCharacterTagHash {
+ static unsigned hash(const FontTag& characters) { return (characters[0] << 24) | (characters[1] << 16) | (characters[2] << 8) | characters[3]; }
+ static bool equal(const FontTag& a, const FontTag& b) { return a == b; }
+ static const bool safeToCompareToEmptyOrDeleted = true;
+};
+
+struct FourCharacterTagHashTraits : WTF::GenericHashTraits<FontTag> {
+ static const bool emptyValueIsZero = true;
+ static void constructDeletedValue(FontTag& slot) { new (NotNull, std::addressof(slot)) FontTag({{ ff, ff, ff, ff }}); }
+ static bool isDeletedValue(const FontTag& value) { return value == FontTag({{ ff, ff, ff, ff }}); }
+
+private:
+ const static char ff = static_cast<char>(0xFF);
+};
+
+template <typename T>
+class FontTaggedSetting {
+public:
+ FontTaggedSetting() = delete;
+ FontTaggedSetting(const FontTag&, T value);
+ FontTaggedSetting(FontTag&&, T value);
+
+ bool operator==(const FontTaggedSetting<T>& other) const;
+ bool operator!=(const FontTaggedSetting<T>& other) const { return !(*this == other); }
+ bool operator<(const FontTaggedSetting<T>& other) const;
+
+ const FontTag& tag() const { return m_tag; }
+ T value() const { return m_value; }
+ bool enabled() const { return value(); }
+
+private:
+ FontTag m_tag;
+ T m_value;
+};
+
+template <typename T>
+FontTaggedSetting<T>::FontTaggedSetting(const FontTag& tag, T value)
+ : m_tag(tag)
+ , m_value(value)
+{
+}
+
+template <typename T>
+FontTaggedSetting<T>::FontTaggedSetting(FontTag&& tag, T value)
+ : m_tag(WTFMove(tag))
+ , m_value(value)
+{
+}
+
+template <typename T>
+bool FontTaggedSetting<T>::operator==(const FontTaggedSetting<T>& other) const
+{
+ return m_tag == other.m_tag && m_value == other.m_value;
+}
+
+template <typename T>
+bool FontTaggedSetting<T>::operator<(const FontTaggedSetting<T>& other) const
+{
+ return (m_tag < other.m_tag) || (m_tag == other.m_tag && m_value < other.m_value);
+}
+
+template <typename T>
+class FontTaggedSettings {
+public:
+ void insert(FontTaggedSetting<T>&&);
+ bool operator==(const FontTaggedSettings<T>& other) const { return m_list == other.m_list; }
+ bool operator!=(const FontTaggedSettings<T>& other) const { return !(*this == other); }
+
+ bool isEmpty() const { return !size(); }
+ size_t size() const { return m_list.size(); }
+ const FontTaggedSetting<T>& operator[](int index) const { return m_list[index]; }
+ const FontTaggedSetting<T>& at(size_t index) const { return m_list.at(index); }
+
+ typename Vector<FontTaggedSetting<T>>::const_iterator begin() const { return m_list.begin(); }
+ typename Vector<FontTaggedSetting<T>>::const_iterator end() const { return m_list.end(); }
+
+ unsigned hash() const;
+
+private:
+ Vector<FontTaggedSetting<T>> m_list;
+};
+
+template <typename T>
+void FontTaggedSettings<T>::insert(FontTaggedSetting<T>&& feature)
+{
+ // This vector will almost always have 0 or 1 items in it. Don't bother with the overhead of a binary search or a hash set.
+ size_t i;
+ for (i = 0; i < m_list.size(); ++i) {
+ if (!(feature < m_list[i]))
+ break;
+ }
+ if (i < m_list.size() && feature.tag() == m_list[i].tag())
+ m_list.remove(i);
+ m_list.insert(i, WTFMove(feature));
+}
+
+typedef FontTaggedSetting<int> FontFeature;
+typedef FontTaggedSettings<int> FontFeatureSettings;
+
+#if ENABLE(VARIATION_FONTS)
+
+typedef FontTaggedSettings<float> FontVariationSettings;
+TextStream& operator<<(TextStream&, const FontVariationSettings&);
+
+#else
+
+struct FontVariationSettings {
+ bool isEmpty() const { return true; }
+};
+
+#endif
+
+}
diff --git a/Source/WebCore/platform/graphics/FontTraitsMask.h b/Source/WebCore/platform/graphics/FontTraitsMask.h
deleted file mode 100644
index 686c30cda..000000000
--- a/Source/WebCore/platform/graphics/FontTraitsMask.h
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * Copyright (C) 2008 Apple Inc. All Rights Reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
- * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
- * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#ifndef FontTraitsMask_h
-#define FontTraitsMask_h
-
-namespace WebCore {
-
- enum {
- FontStyleNormalBit = 0,
- FontStyleItalicBit,
- FontVariantNormalBit,
- FontVariantSmallCapsBit,
- FontWeight100Bit,
- FontWeight200Bit,
- FontWeight300Bit,
- FontWeight400Bit,
- FontWeight500Bit,
- FontWeight600Bit,
- FontWeight700Bit,
- FontWeight800Bit,
- FontWeight900Bit,
- FontTraitsMaskWidth
- };
-
- enum FontTraitsMask {
- FontStyleNormalMask = 1 << FontStyleNormalBit,
- FontStyleItalicMask = 1 << FontStyleItalicBit,
- FontStyleMask = FontStyleNormalMask | FontStyleItalicMask,
-
- FontVariantNormalMask = 1 << FontVariantNormalBit,
- FontVariantSmallCapsMask = 1 << FontVariantSmallCapsBit,
- FontVariantMask = FontVariantNormalMask | FontVariantSmallCapsMask,
-
- FontWeight100Mask = 1 << FontWeight100Bit,
- FontWeight200Mask = 1 << FontWeight200Bit,
- FontWeight300Mask = 1 << FontWeight300Bit,
- FontWeight400Mask = 1 << FontWeight400Bit,
- FontWeight500Mask = 1 << FontWeight500Bit,
- FontWeight600Mask = 1 << FontWeight600Bit,
- FontWeight700Mask = 1 << FontWeight700Bit,
- FontWeight800Mask = 1 << FontWeight800Bit,
- FontWeight900Mask = 1 << FontWeight900Bit,
- FontWeightMask = FontWeight100Mask | FontWeight200Mask | FontWeight300Mask | FontWeight400Mask | FontWeight500Mask | FontWeight600Mask | FontWeight700Mask | FontWeight800Mask | FontWeight900Mask
- };
-
-} // namespace WebCore
-#endif // FontTraitsMask_h
diff --git a/Source/WebCore/platform/graphics/FormatConverter.cpp b/Source/WebCore/platform/graphics/FormatConverter.cpp
index c4a2f11cd..7f0263740 100644
--- a/Source/WebCore/platform/graphics/FormatConverter.cpp
+++ b/Source/WebCore/platform/graphics/FormatConverter.cpp
@@ -12,10 +12,10 @@
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
- * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
@@ -27,7 +27,7 @@
#include "config.h"
-#if USE(3D_GRAPHICS)
+#if ENABLE(GRAPHICS_CONTEXT_3D)
#include "FormatConverter.h"
@@ -91,7 +91,7 @@ void generatetables(){
}
*/
-unsigned short baseTable[512] = {
+static const unsigned short baseTable[512] = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
@@ -126,7 +126,7 @@ unsigned short baseTable[512] = {
64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512
};
-unsigned char shiftTable[512] = {
+static const unsigned char shiftTable[512] = {
24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
@@ -1273,4 +1273,4 @@ ALWAYS_INLINE_EXCEPT_MSVC void FormatConverter::convert()
} // namespace WebCore
-#endif // USE(3D_GRAPHICS)
+#endif // ENABLE(GRAPHICS_CONTEXT_3D)
diff --git a/Source/WebCore/platform/graphics/FormatConverter.h b/Source/WebCore/platform/graphics/FormatConverter.h
index b7b0b1428..dd97b37c7 100644
--- a/Source/WebCore/platform/graphics/FormatConverter.h
+++ b/Source/WebCore/platform/graphics/FormatConverter.h
@@ -12,10 +12,10 @@
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
- * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
@@ -25,9 +25,7 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#include "config.h"
-
-#if USE(3D_GRAPHICS)
+#if ENABLE(GRAPHICS_CONTEXT_3D)
#include "GraphicsContext3D.h"
#include <wtf/StdLibExtras.h>
@@ -75,4 +73,4 @@ private:
} // namespace WebCore
-#endif // USE(3D_GRAPHICS)
+#endif // ENABLE(GRAPHICS_CONTEXT_3D)
diff --git a/Source/WebCore/platform/graphics/GLContext.cpp b/Source/WebCore/platform/graphics/GLContext.cpp
index 82752ae9d..67d60ce83 100644
--- a/Source/WebCore/platform/graphics/GLContext.cpp
+++ b/Source/WebCore/platform/graphics/GLContext.cpp
@@ -17,29 +17,22 @@
*/
#include "config.h"
-#include "GLContext.h"
-#if USE(OPENGL)
+#if ENABLE(GRAPHICS_CONTEXT_3D)
+#include "GLContext.h"
+#include <wtf/ThreadSpecific.h>
#if USE(EGL)
#include "GLContextEGL.h"
#endif
-#if USE(GLX)
-#include "GLContextGLX.h"
+#if USE(OPENGL_ES_2)
+#include <GLES2/gl2.h>
+#include <GLES3/gl3.h>
#endif
-#include <wtf/ThreadSpecific.h>
-
-#if PLATFORM(X11)
-#include <X11/Xlib.h>
-#endif
-
-#if PLATFORM(GTK)
-#include <gdk/gdk.h>
-#if PLATFORM(WAYLAND) && !defined(GTK_API_VERSION_2) && defined(GDK_WINDOWING_WAYLAND)
-#include <gdk/gdkwayland.h>
-#endif
+#if USE(GLX)
+#include "GLContextGLX.h"
#endif
using WTF::ThreadSpecific;
@@ -66,124 +59,83 @@ inline ThreadGlobalGLContext* currentContext()
return *ThreadGlobalGLContext::staticGLContext;
}
-GLContext* GLContext::sharingContext()
-{
- DEFINE_STATIC_LOCAL(OwnPtr<GLContext>, sharing, (createOffscreenContext()));
- return sharing.get();
-}
-
-#if PLATFORM(X11)
-// We do not want to call glXMakeContextCurrent using different Display pointers,
-// because it might lead to crashes in some drivers (fglrx). We use a shared display
-// pointer here.
-static Display* gSharedX11Display = 0;
-Display* GLContext::sharedX11Display()
+static bool initializeOpenGLShimsIfNeeded()
{
- if (!gSharedX11Display)
- gSharedX11Display = XOpenDisplay(0);
- return gSharedX11Display;
-}
-
-void GLContext::cleanupSharedX11Display()
-{
- if (!gSharedX11Display)
- return;
- XCloseDisplay(gSharedX11Display);
- gSharedX11Display = 0;
+#if USE(OPENGL_ES_2)
+ return true;
+#else
+ static bool initialized = false;
+ static bool success = true;
+ if (!initialized) {
+ success = initializeOpenGLShims();
+ initialized = true;
+ }
+ return success;
+#endif
}
-// Because of driver bugs, exiting the program when there are active pbuffers
-// can crash the X server (this has been observed with the official Nvidia drivers).
-// We need to ensure that we clean everything up on exit. There are several reasons
-// that GraphicsContext3Ds will still be alive at exit, including user error (memory
-// leaks) and the page cache. In any case, we don't want the X server to crash.
-typedef Vector<GLContext*> ActiveContextList;
-static ActiveContextList& activeContextList()
+std::unique_ptr<GLContext> GLContext::createContextForWindow(GLNativeWindowType windowHandle, PlatformDisplay* platformDisplay)
{
- DEFINE_STATIC_LOCAL(ActiveContextList, activeContexts, ());
- return activeContexts;
-}
+ if (!initializeOpenGLShimsIfNeeded())
+ return nullptr;
-void GLContext::addActiveContext(GLContext* context)
-{
- static bool addedAtExitHandler = false;
- if (!addedAtExitHandler) {
- atexit(&GLContext::cleanupActiveContextsAtExit);
- addedAtExitHandler = true;
+ PlatformDisplay& display = platformDisplay ? *platformDisplay : PlatformDisplay::sharedDisplay();
+#if PLATFORM(WAYLAND)
+ if (display.type() == PlatformDisplay::Type::Wayland) {
+ if (auto eglContext = GLContextEGL::createContext(windowHandle, display))
+ return WTFMove(eglContext);
+ return nullptr;
}
- activeContextList().append(context);
-}
-
-static bool gCleaningUpAtExit = false;
+#endif
-void GLContext::removeActiveContext(GLContext* context)
-{
- // If we are cleaning up the context list at exit, don't bother removing the context
- // from the list, since we don't want to modify the list while it's being iterated.
- if (gCleaningUpAtExit)
- return;
-
- ActiveContextList& contextList = activeContextList();
- size_t i = contextList.find(context);
- if (i != notFound)
- contextList.remove(i);
+#if USE(GLX)
+ if (auto glxContext = GLContextGLX::createContext(windowHandle, display))
+ return WTFMove(glxContext);
+#endif
+#if USE(EGL)
+ if (auto eglContext = GLContextEGL::createContext(windowHandle, display))
+ return WTFMove(eglContext);
+#endif
+ return nullptr;
}
-void GLContext::cleanupActiveContextsAtExit()
+std::unique_ptr<GLContext> GLContext::createOffscreenContext(PlatformDisplay* platformDisplay)
{
- gCleaningUpAtExit = true;
-
- ActiveContextList& contextList = activeContextList();
- for (size_t i = 0; i < contextList.size(); ++i)
- delete contextList[i];
+ if (!initializeOpenGLShimsIfNeeded())
+ return nullptr;
- cleanupSharedX11Display();
+ return createContextForWindow(0, platformDisplay ? platformDisplay : &PlatformDisplay::sharedDisplay());
}
-#endif // PLATFORM(X11)
-
-PassOwnPtr<GLContext> GLContext::createContextForWindow(GLNativeWindowType windowHandle, GLContext* sharingContext)
+std::unique_ptr<GLContext> GLContext::createSharingContext(PlatformDisplay& display)
{
-#if PLATFORM(GTK) && PLATFORM(WAYLAND) && !defined(GTK_API_VERSION_2) && defined(GDK_WINDOWING_WAYLAND) && USE(EGL)
- GdkDisplay* display = gdk_display_manager_get_default_display(gdk_display_manager_get());
-
- if (GDK_IS_WAYLAND_DISPLAY(display)) {
- if (OwnPtr<GLContext> eglContext = GLContextEGL::createContext(windowHandle, sharingContext))
- return eglContext.release();
+ if (!initializeOpenGLShimsIfNeeded())
return nullptr;
- }
-#endif
#if USE(GLX)
- if (OwnPtr<GLContext> glxContext = GLContextGLX::createContext(windowHandle, sharingContext))
- return glxContext.release();
-#endif
-#if USE(EGL)
- if (OwnPtr<GLContext> eglContext = GLContextEGL::createContext(windowHandle, sharingContext))
- return eglContext.release();
+ if (display.type() == PlatformDisplay::Type::X11) {
+ if (auto glxContext = GLContextGLX::createSharingContext(display))
+ return WTFMove(glxContext);
+ }
#endif
- return nullptr;
-}
-GLContext::GLContext()
-{
-#if PLATFORM(X11)
- addActiveContext(this);
+#if USE(EGL) || PLATFORM(WAYLAND)
+ if (auto eglContext = GLContextEGL::createSharingContext(display))
+ return WTFMove(eglContext);
#endif
+
+ return nullptr;
}
-PassOwnPtr<GLContext> GLContext::createOffscreenContext(GLContext* sharingContext)
+GLContext::GLContext(PlatformDisplay& display)
+ : m_display(display)
{
- return createContextForWindow(0, sharingContext);
}
GLContext::~GLContext()
{
if (this == currentContext()->context())
- currentContext()->setContext(0);
-#if PLATFORM(X11)
- removeActiveContext(this);
-#endif
+ currentContext()->setContext(nullptr);
}
bool GLContext::makeContextCurrent()
@@ -192,12 +144,53 @@ bool GLContext::makeContextCurrent()
return true;
}
-GLContext* GLContext::getCurrent()
+GLContext* GLContext::current()
{
return currentContext()->context();
}
-} // namespace WebCore
+bool GLContext::isExtensionSupported(const char* extensionList, const char* extension)
+{
+ if (!extensionList)
+ return false;
+
+ ASSERT(extension);
+ int extensionLen = strlen(extension);
+ const char* extensionListPtr = extensionList;
+ while ((extensionListPtr = strstr(extensionListPtr, extension))) {
+ if (extensionListPtr[extensionLen] == ' ' || extensionListPtr[extensionLen] == '\0')
+ return true;
+ extensionListPtr += extensionLen;
+ }
+ return false;
+}
-#endif // USE(OPENGL)
+unsigned GLContext::version()
+{
+ if (!m_version) {
+ // Version string can start with the version number (all versions except GLES 1 and 2) or with
+ // "OpenGL". Different fields inside the version string are separated by spaces.
+ String versionString = String(reinterpret_cast<const char*>(::glGetString(GL_VERSION)));
+ Vector<String> versionStringComponents;
+ versionString.split(' ', versionStringComponents);
+
+ Vector<String> versionDigits;
+ if (versionStringComponents[0] == "OpenGL") {
+ // If the version string starts with "OpenGL" it can be GLES 1 or 2. In GLES1 version string starts
+ // with "OpenGL ES-<profile> major.minor" and in GLES2 with "OpenGL ES major.minor". Version is the
+ // third component in both cases.
+ versionStringComponents[2].split('.', versionDigits);
+ } else {
+ // Version is the first component. The version number is always "major.minor" or
+ // "major.minor.release". Ignore the release number.
+ versionStringComponents[0].split('.', versionDigits);
+ }
+
+ m_version = versionDigits[0].toUInt() * 100 + versionDigits[1].toUInt() * 10;
+ }
+ return m_version;
+}
+} // namespace WebCore
+
+#endif
diff --git a/Source/WebCore/platform/graphics/GLContext.h b/Source/WebCore/platform/graphics/GLContext.h
index 03b57b5e8..2c0ea27e1 100644
--- a/Source/WebCore/platform/graphics/GLContext.h
+++ b/Source/WebCore/platform/graphics/GLContext.h
@@ -21,9 +21,8 @@
#define GLContext_h
#include "GraphicsContext3D.h"
-#include "Widget.h"
+#include "PlatformDisplay.h"
#include <wtf/Noncopyable.h>
-#include <wtf/PassOwnPtr.h>
#if USE(EGL) && !PLATFORM(GTK)
#include "eglplatform.h"
@@ -36,44 +35,50 @@ typedef uint64_t GLNativeWindowType;
typedef struct _cairo_device cairo_device_t;
#endif
-#if PLATFORM(X11)
-typedef struct _XDisplay Display;
-#endif
-
namespace WebCore {
class GLContext {
- WTF_MAKE_NONCOPYABLE(GLContext);
+ WTF_MAKE_NONCOPYABLE(GLContext); WTF_MAKE_FAST_ALLOCATED;
public:
- static PassOwnPtr<GLContext> createContextForWindow(GLNativeWindowType windowHandle, GLContext* sharingContext);
- static PassOwnPtr<GLContext> createOffscreenContext(GLContext* sharing = 0);
- static GLContext* getCurrent();
- static GLContext* sharingContext();
+ static std::unique_ptr<GLContext> createContextForWindow(GLNativeWindowType windowHandle, PlatformDisplay* = nullptr);
+ static std::unique_ptr<GLContext> createOffscreenContext(PlatformDisplay* = nullptr);
+ static std::unique_ptr<GLContext> createSharingContext(PlatformDisplay&);
+ static GLContext* current();
+ static bool isExtensionSupported(const char* extensionList, const char* extension);
+
+ PlatformDisplay& display() const { return m_display; }
+ unsigned version();
- GLContext();
virtual ~GLContext();
virtual bool makeContextCurrent();
virtual void swapBuffers() = 0;
virtual void waitNative() = 0;
virtual bool canRenderToDefaultFramebuffer() = 0;
virtual IntSize defaultFrameBufferSize() = 0;
+ virtual void swapInterval(int) = 0;
+
+ virtual bool isEGLContext() const = 0;
#if USE(CAIRO)
virtual cairo_device_t* cairoDevice() = 0;
#endif
-#if PLATFORM(X11)
- static Display* sharedX11Display();
- static void cleanupSharedX11Display();
+#if ENABLE(GRAPHICS_CONTEXT_3D)
+ virtual PlatformGraphicsContext3D platformContext() = 0;
#endif
+#if PLATFORM(X11)
+private:
static void addActiveContext(GLContext*);
static void removeActiveContext(GLContext*);
static void cleanupActiveContextsAtExit();
-
-#if USE(3D_GRAPHICS)
- virtual PlatformGraphicsContext3D platformContext() = 0;
#endif
+
+protected:
+ GLContext(PlatformDisplay&);
+
+ PlatformDisplay& m_display;
+ unsigned m_version { 0 };
};
} // namespace WebCore
diff --git a/Source/WebCore/platform/graphics/GeneratedImage.cpp b/Source/WebCore/platform/graphics/GeneratedImage.cpp
index b250cb1c2..08921c957 100644
--- a/Source/WebCore/platform/graphics/GeneratedImage.cpp
+++ b/Source/WebCore/platform/graphics/GeneratedImage.cpp
@@ -33,7 +33,6 @@
#include "FloatSize.h"
-
namespace WebCore {
void GeneratedImage::computeIntrinsicDimensions(Length& intrinsicWidth, Length& intrinsicHeight, FloatSize& intrinsicRatio)
diff --git a/Source/WebCore/platform/graphics/GeneratedImage.h b/Source/WebCore/platform/graphics/GeneratedImage.h
index 446388beb..7d9fe358d 100644
--- a/Source/WebCore/platform/graphics/GeneratedImage.h
+++ b/Source/WebCore/platform/graphics/GeneratedImage.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2008, 2013 Apple Computer, Inc. All rights reserved.
+ * Copyright (C) 2008, 2013 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -10,10 +10,10 @@
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
- * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
@@ -26,40 +26,40 @@
#ifndef GeneratedImage_h
#define GeneratedImage_h
+#include "FloatSize.h"
#include "Image.h"
-#include "IntSize.h"
-#include <wtf/RefPtr.h>
-
namespace WebCore {
class GeneratedImage : public Image {
public:
- virtual bool hasSingleSecurityOrigin() const override { return true; }
+ bool hasSingleSecurityOrigin() const override { return true; }
- virtual void setContainerSize(const IntSize& size) override { m_size = size; }
- virtual bool usesContainerSize() const override { return true; }
- virtual bool hasRelativeWidth() const override { return true; }
- virtual bool hasRelativeHeight() const override { return true; }
- virtual void computeIntrinsicDimensions(Length& intrinsicWidth, Length& intrinsicHeight, FloatSize& intrinsicRatio) override;
+ void setContainerSize(const FloatSize& size) override { m_size = size; }
+ bool usesContainerSize() const override { return true; }
+ bool hasRelativeWidth() const override { return true; }
+ bool hasRelativeHeight() const override { return true; }
+ void computeIntrinsicDimensions(Length& intrinsicWidth, Length& intrinsicHeight, FloatSize& intrinsicRatio) override;
- virtual IntSize size() const override { return m_size; }
+ FloatSize size() const override { return m_size; }
// Assume that generated content has no decoded data we need to worry about
- virtual void destroyDecodedData(bool /*destroyAll*/ = true) override { }
+ void destroyDecodedData(bool /*destroyAll*/ = true) override { }
protected:
- virtual void draw(GraphicsContext*, const FloatRect& dstRect, const FloatRect& srcRect, ColorSpace styleColorSpace, CompositeOperator, BlendMode, ImageOrientationDescription) override = 0;
- virtual void drawPattern(GraphicsContext*, const FloatRect& srcRect, const AffineTransform& patternTransform,
- const FloatPoint& phase, ColorSpace styleColorSpace, CompositeOperator, const FloatRect& destRect, BlendMode) override = 0;
+ void draw(GraphicsContext&, const FloatRect& dstRect, const FloatRect& srcRect, CompositeOperator, BlendMode, ImageOrientationDescription) override = 0;
+ void drawPattern(GraphicsContext&, const FloatRect& destRect, const FloatRect& srcRect, const AffineTransform& patternTransform,
+ const FloatPoint& phase, const FloatSize& spacing, CompositeOperator, BlendMode) override = 0;
// FIXME: Implement this to be less conservative.
- virtual bool currentFrameKnownToBeOpaque() override { return false; }
+ bool currentFrameKnownToBeOpaque() const override { return false; }
GeneratedImage() { }
private:
- IntSize m_size;
+ bool isGeneratedImage() const override { return true; }
+
+ FloatSize m_size;
};
}
diff --git a/Source/WebCore/platform/graphics/GeometryUtilities.cpp b/Source/WebCore/platform/graphics/GeometryUtilities.cpp
new file mode 100644
index 000000000..5f296ac74
--- /dev/null
+++ b/Source/WebCore/platform/graphics/GeometryUtilities.cpp
@@ -0,0 +1,163 @@
+/*
+ * Copyright (C) 2014 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "GeometryUtilities.h"
+
+namespace WebCore {
+
+float euclidianDistance(const FloatPoint& p1, const FloatPoint& p2)
+{
+ FloatSize delta = p1 - p2;
+ return sqrt(delta.width() * delta.width() + delta.height() * delta.height());
+}
+
+float findSlope(const FloatPoint& p1, const FloatPoint& p2, float& c)
+{
+ if (p2.x() == p1.x())
+ return std::numeric_limits<float>::infinity();
+
+ // y = mx + c
+ float slope = (p2.y() - p1.y()) / (p2.x() - p1.x());
+ c = p1.y() - slope * p1.x();
+ return slope;
+}
+
+bool findIntersection(const FloatPoint& p1, const FloatPoint& p2, const FloatPoint& d1, const FloatPoint& d2, FloatPoint& intersection)
+{
+ float pOffset = 0;
+ float pSlope = findSlope(p1, p2, pOffset);
+
+ float dOffset = 0;
+ float dSlope = findSlope(d1, d2, dOffset);
+
+ if (dSlope == pSlope)
+ return false;
+
+ if (pSlope == std::numeric_limits<float>::infinity()) {
+ intersection.setX(p1.x());
+ intersection.setY(dSlope * intersection.x() + dOffset);
+ return true;
+ }
+ if (dSlope == std::numeric_limits<float>::infinity()) {
+ intersection.setX(d1.x());
+ intersection.setY(pSlope * intersection.x() + pOffset);
+ return true;
+ }
+
+ // Find x at intersection, where ys overlap; x = (c' - c) / (m - m')
+ intersection.setX((dOffset - pOffset) / (pSlope - dSlope));
+ intersection.setY(pSlope * intersection.x() + pOffset);
+ return true;
+}
+
+IntRect unionRect(const Vector<IntRect>& rects)
+{
+ IntRect result;
+
+ size_t count = rects.size();
+ for (size_t i = 0; i < count; ++i)
+ result.unite(rects[i]);
+
+ return result;
+}
+
+FloatRect unionRect(const Vector<FloatRect>& rects)
+{
+ FloatRect result;
+
+ size_t count = rects.size();
+ for (size_t i = 0; i < count; ++i)
+ result.unite(rects[i]);
+
+ return result;
+}
+
+FloatRect mapRect(const FloatRect& r, const FloatRect& srcRect, const FloatRect& destRect)
+{
+ if (!srcRect.width() || !srcRect.height())
+ return FloatRect();
+
+ float widthScale = destRect.width() / srcRect.width();
+ float heightScale = destRect.height() / srcRect.height();
+ return FloatRect(destRect.x() + (r.x() - srcRect.x()) * widthScale,
+ destRect.y() + (r.y() - srcRect.y()) * heightScale,
+ r.width() * widthScale, r.height() * heightScale);
+}
+
+FloatRect largestRectWithAspectRatioInsideRect(float aspectRatio, const FloatRect& srcRect)
+{
+ FloatRect destRect = srcRect;
+
+ if (aspectRatio > srcRect.size().aspectRatio()) {
+ float dy = destRect.width() / aspectRatio - destRect.height();
+ destRect.inflateY(dy / 2);
+ } else {
+ float dx = destRect.height() * aspectRatio - destRect.width();
+ destRect.inflateX(dx / 2);
+ }
+ return destRect;
+}
+
+FloatRect boundsOfRotatingRect(const FloatRect& r)
+{
+ // Compute the furthest corner from the origin.
+ float maxCornerDistance = euclidianDistance(FloatPoint(), r.minXMinYCorner());
+ maxCornerDistance = std::max(maxCornerDistance, euclidianDistance(FloatPoint(), r.maxXMinYCorner()));
+ maxCornerDistance = std::max(maxCornerDistance, euclidianDistance(FloatPoint(), r.minXMaxYCorner()));
+ maxCornerDistance = std::max(maxCornerDistance, euclidianDistance(FloatPoint(), r.maxXMaxYCorner()));
+
+ return FloatRect(-maxCornerDistance, -maxCornerDistance, 2 * maxCornerDistance, 2 * maxCornerDistance);
+}
+
+FloatRect smallestRectWithAspectRatioAroundRect(float aspectRatio, const FloatRect& srcRect)
+{
+ FloatRect destRect = srcRect;
+
+ if (aspectRatio < srcRect.size().aspectRatio()) {
+ float dy = destRect.width() / aspectRatio - destRect.height();
+ destRect.inflateY(dy / 2);
+ } else {
+ float dx = destRect.height() * aspectRatio - destRect.width();
+ destRect.inflateX(dx / 2);
+ }
+ return destRect;
+}
+
+bool ellipseContainsPoint(const FloatPoint& center, const FloatSize& radii, const FloatPoint& point)
+{
+ FloatPoint transformedPoint(point);
+ transformedPoint.move(-center.x(), -center.y());
+ transformedPoint.scale(radii.height(), radii.width());
+ float radius = radii.width() * radii.height();
+
+ if (transformedPoint.x() > radius || transformedPoint.y() > radius)
+ return false;
+ if (transformedPoint.x() + transformedPoint.y() <= radius)
+ return true;
+ return (transformedPoint.lengthSquared() <= radius * radius);
+}
+
+}
diff --git a/Source/WebCore/platform/graphics/GeometryUtilities.h b/Source/WebCore/platform/graphics/GeometryUtilities.h
new file mode 100644
index 000000000..86ca19720
--- /dev/null
+++ b/Source/WebCore/platform/graphics/GeometryUtilities.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2014 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include "FloatRect.h"
+#include "IntRect.h"
+#include <wtf/Vector.h>
+
+namespace WebCore {
+
+float euclidianDistance(const FloatPoint&, const FloatPoint&);
+
+float findSlope(const FloatPoint& p1, const FloatPoint& p2, float& c);
+
+// Find point where lines through the two pairs of points intersect. Returns false if the lines don't intersect.
+WEBCORE_EXPORT bool findIntersection(const FloatPoint& p1, const FloatPoint& p2, const FloatPoint& d1, const FloatPoint& d2, FloatPoint& intersection);
+
+IntRect unionRect(const Vector<IntRect>&);
+WEBCORE_EXPORT FloatRect unionRect(const Vector<FloatRect>&);
+
+// Map rect r from srcRect to an equivalent rect in destRect.
+FloatRect mapRect(const FloatRect&, const FloatRect& srcRect, const FloatRect& destRect);
+
+WEBCORE_EXPORT FloatRect largestRectWithAspectRatioInsideRect(float aspectRatio, const FloatRect&);
+WEBCORE_EXPORT FloatRect smallestRectWithAspectRatioAroundRect(float aspectRatio, const FloatRect&);
+
+// Compute a rect that encloses all points covered by the given rect if it were rotated a full turn around (0,0).
+FloatRect boundsOfRotatingRect(const FloatRect&);
+
+bool ellipseContainsPoint(const FloatPoint& center, const FloatSize& radii, const FloatPoint&);
+}
diff --git a/Source/WebCore/platform/graphics/Glyph.h b/Source/WebCore/platform/graphics/Glyph.h
index 309463f49..2af8b33d6 100644
--- a/Source/WebCore/platform/graphics/Glyph.h
+++ b/Source/WebCore/platform/graphics/Glyph.h
@@ -11,7 +11,7 @@
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
- * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ * 3. Neither the name of Apple Inc. ("Apple") nor the names of
* its contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
diff --git a/Source/WebCore/platform/graphics/GlyphBuffer.h b/Source/WebCore/platform/graphics/GlyphBuffer.h
index d72634cfd..61f3f050b 100644
--- a/Source/WebCore/platform/graphics/GlyphBuffer.h
+++ b/Source/WebCore/platform/graphics/GlyphBuffer.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2006, 2009, 2011 Apple Inc. All rights reserved.
+ * Copyright (C) 2006, 2009, 2011, 2016 Apple Inc. All rights reserved.
* Copyright (C) 2007-2008 Torch Mobile Inc.
*
* Redistribution and use in source and binary forms, with or without
@@ -11,7 +11,7 @@
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
- * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ * 3. Neither the name of Apple Inc. ("Apple") nor the names of
* its contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
@@ -32,6 +32,7 @@
#include "FloatSize.h"
#include "Glyph.h"
+#include <climits>
#include <wtf/Vector.h>
#if USE(CG)
@@ -44,7 +45,7 @@
namespace WebCore {
-class SimpleFontData;
+class Font;
#if USE(CAIRO)
// FIXME: Why does Cairo use such a huge struct instead of just an offset into an array?
@@ -61,11 +62,17 @@ typedef Glyph GlyphBufferGlyph;
struct GlyphBufferAdvance : CGSize {
public:
GlyphBufferAdvance() : CGSize(CGSizeZero) { }
- GlyphBufferAdvance(CGSize size) : CGSize(size)
+ GlyphBufferAdvance(CGSize size)
+ : CGSize(size)
+ {
+ }
+ GlyphBufferAdvance(float width, float height)
+ : CGSize(CGSizeMake(width, height))
{
}
void setWidth(CGFloat width) { this->CGSize::width = width; }
+ void setHeight(CGFloat height) { this->CGSize::height = height; }
CGFloat width() const { return this->CGSize::width; }
CGFloat height() const { return this->CGSize::height; }
};
@@ -75,30 +82,33 @@ typedef FloatSize GlyphBufferAdvance;
class GlyphBuffer {
public:
- bool isEmpty() const { return m_fontData.isEmpty(); }
- int size() const { return m_fontData.size(); }
+ bool isEmpty() const { return m_font.isEmpty(); }
+ unsigned size() const { return m_font.size(); }
void clear()
{
- m_fontData.clear();
+ m_font.clear();
m_glyphs.clear();
m_advances.clear();
+ if (m_offsetsInString)
+ m_offsetsInString->clear();
#if PLATFORM(WIN)
m_offsets.clear();
#endif
}
- GlyphBufferGlyph* glyphs(int from) { return m_glyphs.data() + from; }
- GlyphBufferAdvance* advances(int from) { return m_advances.data() + from; }
- const GlyphBufferGlyph* glyphs(int from) const { return m_glyphs.data() + from; }
- const GlyphBufferAdvance* advances(int from) const { return m_advances.data() + from; }
+ GlyphBufferGlyph* glyphs(unsigned from) { return m_glyphs.data() + from; }
+ GlyphBufferAdvance* advances(unsigned from) { return m_advances.data() + from; }
+ const GlyphBufferGlyph* glyphs(unsigned from) const { return m_glyphs.data() + from; }
+ const GlyphBufferAdvance* advances(unsigned from) const { return m_advances.data() + from; }
+ size_t advancesCount() const { return m_advances.size(); }
- const SimpleFontData* fontDataAt(int index) const { return m_fontData[index]; }
+ const Font* fontAt(unsigned index) const { return m_font[index]; }
void setInitialAdvance(GlyphBufferAdvance initialAdvance) { m_initialAdvance = initialAdvance; }
const GlyphBufferAdvance& initialAdvance() const { return m_initialAdvance; }
- Glyph glyphAt(int index) const
+ Glyph glyphAt(unsigned index) const
{
#if USE(CAIRO)
return m_glyphs[index].index;
@@ -107,12 +117,12 @@ public:
#endif
}
- GlyphBufferAdvance advanceAt(int index) const
+ GlyphBufferAdvance advanceAt(unsigned index) const
{
return m_advances[index];
}
- FloatSize offsetAt(int index) const
+ FloatSize offsetAt(unsigned index) const
{
#if PLATFORM(WIN)
return m_offsets[index];
@@ -121,20 +131,11 @@ public:
return FloatSize();
#endif
}
-
- void add(const GlyphBuffer* glyphBuffer, int from, int len)
- {
- m_glyphs.append(glyphBuffer->glyphs(from), len);
- m_advances.append(glyphBuffer->advances(from), len);
- m_fontData.append(glyphBuffer->m_fontData.data() + from, len);
-#if PLATFORM(WIN)
- m_offsets.append(glyphBuffer->m_offsets.data() + from, len);
-#endif
- }
-
- void add(Glyph glyph, const SimpleFontData* font, float width, const FloatSize* offset = 0)
+
+ static const unsigned noOffset = UINT_MAX;
+ void add(Glyph glyph, const Font* font, float width, unsigned offsetInString = noOffset, const FloatSize* offset = 0)
{
- m_fontData.append(font);
+ m_font.append(font);
#if USE(CAIRO)
cairo_glyph_t cairoGlyph;
@@ -159,12 +160,15 @@ public:
#else
UNUSED_PARAM(offset);
#endif
+
+ if (offsetInString != noOffset && m_offsetsInString)
+ m_offsetsInString->append(offsetInString);
}
#if !USE(WINGDI)
- void add(Glyph glyph, const SimpleFontData* font, GlyphBufferAdvance advance)
+ void add(Glyph glyph, const Font* font, GlyphBufferAdvance advance, unsigned offsetInString = noOffset)
{
- m_fontData.append(font);
+ m_font.append(font);
#if USE(CAIRO)
cairo_glyph_t cairoGlyph;
cairoGlyph.index = glyph;
@@ -174,12 +178,15 @@ public:
#endif
m_advances.append(advance);
+
+ if (offsetInString != noOffset && m_offsetsInString)
+ m_offsetsInString->append(offsetInString);
}
#endif
- void reverse(int from, int length)
+ void reverse(unsigned from, unsigned length)
{
- for (int i = from, end = from + length - 1; i < end; ++i, --end)
+ for (unsigned i = from, end = from + length - 1; i < end; ++i, --end)
swap(i, end);
}
@@ -189,13 +196,36 @@ public:
GlyphBufferAdvance& lastAdvance = m_advances.last();
lastAdvance.setWidth(lastAdvance.width() + width);
}
+
+ void saveOffsetsInString()
+ {
+ m_offsetsInString.reset(new Vector<unsigned, 2048>());
+ }
+
+ int offsetInString(unsigned index) const
+ {
+ ASSERT(m_offsetsInString);
+ return (*m_offsetsInString)[index];
+ }
+
+ void shrink(unsigned truncationPoint)
+ {
+ m_font.shrink(truncationPoint);
+ m_glyphs.shrink(truncationPoint);
+ m_advances.shrink(truncationPoint);
+ if (m_offsetsInString)
+ m_offsetsInString->shrink(truncationPoint);
+#if PLATFORM(WIN)
+ m_offsets.shrink(truncationPoint);
+#endif
+ }
private:
- void swap(int index1, int index2)
+ void swap(unsigned index1, unsigned index2)
{
- const SimpleFontData* f = m_fontData[index1];
- m_fontData[index1] = m_fontData[index2];
- m_fontData[index2] = f;
+ const Font* f = m_font[index1];
+ m_font[index1] = m_font[index2];
+ m_font[index2] = f;
GlyphBufferGlyph g = m_glyphs[index1];
m_glyphs[index1] = m_glyphs[index2];
@@ -212,10 +242,11 @@ private:
#endif
}
- Vector<const SimpleFontData*, 2048> m_fontData;
+ Vector<const Font*, 2048> m_font;
Vector<GlyphBufferGlyph, 2048> m_glyphs;
Vector<GlyphBufferAdvance, 2048> m_advances;
GlyphBufferAdvance m_initialAdvance;
+ std::unique_ptr<Vector<unsigned, 2048>> m_offsetsInString;
#if PLATFORM(WIN)
Vector<FloatSize, 2048> m_offsets;
#endif
diff --git a/Source/WebCore/platform/graphics/GlyphMetricsMap.h b/Source/WebCore/platform/graphics/GlyphMetricsMap.h
index 61c5fb09c..4f0f58a58 100644
--- a/Source/WebCore/platform/graphics/GlyphMetricsMap.h
+++ b/Source/WebCore/platform/graphics/GlyphMetricsMap.h
@@ -10,7 +10,7 @@
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
- * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ * 3. Neither the name of Apple Inc. ("Apple") nor the names of
* its contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
@@ -32,58 +32,70 @@
#include "Glyph.h"
#include <array>
#include <wtf/HashMap.h>
-#include <wtf/OwnPtr.h>
-#include <wtf/PassOwnPtr.h>
-#include <wtf/unicode/Unicode.h>
namespace WebCore {
const float cGlyphSizeUnknown = -1;
template<class T> class GlyphMetricsMap {
- WTF_MAKE_NONCOPYABLE(GlyphMetricsMap);
+ WTF_MAKE_FAST_ALLOCATED;
public:
- GlyphMetricsMap() : m_filledPrimaryPage(false) { }
T metricsForGlyph(Glyph glyph)
{
- return locatePage(glyph / GlyphMetricsPage::size)->metricsForGlyph(glyph);
+ return locatePage(glyph / GlyphMetricsPage::size).metricsForGlyph(glyph);
}
void setMetricsForGlyph(Glyph glyph, const T& metrics)
{
- locatePage(glyph / GlyphMetricsPage::size)->setMetricsForGlyph(glyph, metrics);
+ locatePage(glyph / GlyphMetricsPage::size).setMetricsForGlyph(glyph, metrics);
}
private:
- struct GlyphMetricsPage {
- static const size_t size = 256; // Usually covers Latin-1 in a single page.
- std::array<T, size> m_metrics;
+ class GlyphMetricsPage {
+ WTF_MAKE_FAST_ALLOCATED;
+ public:
+ static const size_t size = 16;
+
+ GlyphMetricsPage() = default;
+ explicit GlyphMetricsPage(const T& initialValue)
+ {
+ fill(initialValue);
+ }
+
+ void fill(const T& value)
+ {
+ m_metrics.fill(value);
+ }
T metricsForGlyph(Glyph glyph) const { return m_metrics[glyph % size]; }
void setMetricsForGlyph(Glyph glyph, const T& metrics)
{
setMetricsForIndex(glyph % size, metrics);
}
+
+ private:
void setMetricsForIndex(unsigned index, const T& metrics)
{
m_metrics[index] = metrics;
}
+
+ std::array<T, size> m_metrics;
};
- GlyphMetricsPage* locatePage(unsigned pageNumber)
+ GlyphMetricsPage& locatePage(unsigned pageNumber)
{
if (!pageNumber && m_filledPrimaryPage)
- return &m_primaryPage;
+ return m_primaryPage;
return locatePageSlowCase(pageNumber);
}
- GlyphMetricsPage* locatePageSlowCase(unsigned pageNumber);
+ GlyphMetricsPage& locatePageSlowCase(unsigned pageNumber);
static T unknownMetrics();
- bool m_filledPrimaryPage;
+ bool m_filledPrimaryPage { false };
GlyphMetricsPage m_primaryPage; // We optimize for the page that contains glyph indices 0-255.
- OwnPtr<HashMap<int, OwnPtr<GlyphMetricsPage>>> m_pages;
+ std::unique_ptr<HashMap<int, std::unique_ptr<GlyphMetricsPage>>> m_pages;
};
template<> inline float GlyphMetricsMap<float>::unknownMetrics()
@@ -96,28 +108,22 @@ template<> inline FloatRect GlyphMetricsMap<FloatRect>::unknownMetrics()
return FloatRect(0, 0, cGlyphSizeUnknown, cGlyphSizeUnknown);
}
-template<class T> typename GlyphMetricsMap<T>::GlyphMetricsPage* GlyphMetricsMap<T>::locatePageSlowCase(unsigned pageNumber)
+template<class T> typename GlyphMetricsMap<T>::GlyphMetricsPage& GlyphMetricsMap<T>::locatePageSlowCase(unsigned pageNumber)
{
- GlyphMetricsPage* page;
if (!pageNumber) {
ASSERT(!m_filledPrimaryPage);
- page = &m_primaryPage;
+ m_primaryPage.fill(unknownMetrics());
m_filledPrimaryPage = true;
- } else {
- if (m_pages) {
- if ((page = m_pages->get(pageNumber)))
- return page;
- } else
- m_pages = adoptPtr(new HashMap<int, OwnPtr<GlyphMetricsPage>>);
- page = new GlyphMetricsPage;
- m_pages->set(pageNumber, adoptPtr(page));
+ return m_primaryPage;
}
- // Fill in the whole page with the unknown glyph information.
- for (unsigned i = 0; i < GlyphMetricsPage::size; i++)
- page->setMetricsForIndex(i, unknownMetrics());
+ if (!m_pages)
+ m_pages = std::make_unique<HashMap<int, std::unique_ptr<GlyphMetricsPage>>>();
- return page;
+ auto& page = m_pages->ensure(pageNumber, [] {
+ return std::make_unique<GlyphMetricsPage>(unknownMetrics());
+ }).iterator->value;
+ return *page;
}
} // namespace WebCore
diff --git a/Source/WebCore/platform/graphics/GlyphPage.h b/Source/WebCore/platform/graphics/GlyphPage.h
index 53c200955..cd0dcd46f 100644
--- a/Source/WebCore/platform/graphics/GlyphPage.h
+++ b/Source/WebCore/platform/graphics/GlyphPage.h
@@ -11,7 +11,7 @@
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
- * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ * 3. Neither the name of Apple Inc. ("Apple") nor the names of
* its contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
@@ -31,174 +31,104 @@
#define GlyphPage_h
#include "Glyph.h"
-#include <string.h>
-#include <wtf/PassRefPtr.h>
+#include <unicode/utypes.h>
#include <wtf/RefCounted.h>
-#include <wtf/RefPtr.h>
-#include <wtf/unicode/Unicode.h>
+#include <wtf/Ref.h>
namespace WebCore {
-class SimpleFontData;
-class GlyphPageTreeNode;
+class Font;
-// Holds the glyph index and the corresponding SimpleFontData information for a given
+// Holds the glyph index and the corresponding Font information for a given
// character.
struct GlyphData {
- GlyphData(Glyph g = 0, const SimpleFontData* f = 0)
- : glyph(g)
- , fontData(f)
+ GlyphData(Glyph glyph = 0, const Font* font = nullptr)
+ : glyph(glyph)
+ , font(font)
{
}
+
+ bool isValid() const { return glyph || font; }
+
Glyph glyph;
- const SimpleFontData* fontData;
+ const Font* font;
};
-#if COMPILER(MSVC)
-#pragma warning(push)
-#pragma warning(disable: 4200) // Disable "zero-sized array in struct/union" warning
-#endif
-
// A GlyphPage contains a fixed-size set of GlyphData mappings for a contiguous
// range of characters in the Unicode code space. GlyphPages are indexed
-// starting from 0 and incrementing for each 256 glyphs.
-//
-// One page may actually include glyphs from other fonts if the characters are
-// missing in the primary font. It is owned by exactly one GlyphPageTreeNode,
-// although multiple nodes may reference it as their "page" if they are supposed
-// to be overriding the parent's node, but provide no additional information.
+// starting from 0 and incrementing for each "size" number of glyphs.
class GlyphPage : public RefCounted<GlyphPage> {
public:
- static PassRefPtr<GlyphPage> createForMixedFontData(GlyphPageTreeNode* owner)
+ static Ref<GlyphPage> create(const Font& font)
{
- void* slot = fastMalloc(sizeof(GlyphPage) + sizeof(SimpleFontData*) * GlyphPage::size);
- return adoptRef(new (NotNull, slot) GlyphPage(owner));
+ return adoptRef(*new GlyphPage(font));
}
- static PassRefPtr<GlyphPage> createForSingleFontData(GlyphPageTreeNode* owner, const SimpleFontData* fontData)
+ ~GlyphPage()
{
- ASSERT(fontData);
- return adoptRef(new GlyphPage(owner, fontData));
+ --s_count;
}
- PassRefPtr<GlyphPage> createCopiedSystemFallbackPage(GlyphPageTreeNode* owner) const
- {
- RefPtr<GlyphPage> page = GlyphPage::createForMixedFontData(owner);
- memcpy(page->m_glyphs, m_glyphs, sizeof(m_glyphs));
- if (hasPerGlyphFontData())
- memcpy(page->m_perGlyphFontData, m_perGlyphFontData, sizeof(SimpleFontData*) * GlyphPage::size);
- else {
- for (size_t i = 0; i < GlyphPage::size; ++i) {
- page->m_perGlyphFontData[i] = m_glyphs[i] ? m_fontDataForAllGlyphs : 0;
- }
- }
- return page.release();
- }
+ static unsigned count() { return s_count; }
- ~GlyphPage() { }
+ static const unsigned size = 16;
- static const size_t size = 256; // Covers Latin-1 in a single page.
- static unsigned indexForCharacter(UChar32 c) { return c % GlyphPage::size; }
+ static unsigned sizeForPageNumber(unsigned) { return 16; }
+ static unsigned indexForCodePoint(UChar32 c) { return c % size; }
+ static unsigned pageNumberForCodePoint(UChar32 c) { return c / size; }
+ static UChar32 startingCodePointInPageNumber(unsigned pageNumber) { return pageNumber * size; }
+ static bool pageNumberIsUsedForArabic(unsigned pageNumber) { return startingCodePointInPageNumber(pageNumber) >= 0x600 && startingCodePointInPageNumber(pageNumber) + sizeForPageNumber(pageNumber) < 0x700; }
- ALWAYS_INLINE GlyphData glyphDataForCharacter(UChar32 c) const
+ GlyphData glyphDataForCharacter(UChar32 c) const
{
- return glyphDataForIndex(indexForCharacter(c));
+ return glyphDataForIndex(indexForCodePoint(c));
}
- ALWAYS_INLINE GlyphData glyphDataForIndex(unsigned index) const
+ Glyph glyphForCharacter(UChar32 c) const
{
- ASSERT_WITH_SECURITY_IMPLICATION(index < size);
- Glyph glyph = m_glyphs[index];
- if (hasPerGlyphFontData())
- return GlyphData(glyph, m_perGlyphFontData[index]);
- return GlyphData(glyph, glyph ? m_fontDataForAllGlyphs : 0);
- }
-
- ALWAYS_INLINE Glyph glyphAt(unsigned index) const
- {
- ASSERT_WITH_SECURITY_IMPLICATION(index < size);
- return m_glyphs[index];
+ return glyphForIndex(indexForCodePoint(c));
}
- ALWAYS_INLINE const SimpleFontData* fontDataForCharacter(UChar32 c) const
+ GlyphData glyphDataForIndex(unsigned index) const
{
- unsigned index = indexForCharacter(c);
- if (hasPerGlyphFontData())
- return m_perGlyphFontData[index];
- return m_glyphs[index] ? m_fontDataForAllGlyphs : 0;
+ Glyph glyph = glyphForIndex(index);
+ return GlyphData(glyph, glyph ? &m_font : nullptr);
}
- void setGlyphDataForCharacter(UChar32 c, Glyph g, const SimpleFontData* f)
+ Glyph glyphForIndex(unsigned index) const
{
- setGlyphDataForIndex(indexForCharacter(c), g, f);
+ ASSERT_WITH_SECURITY_IMPLICATION(index < size);
+ return m_glyphs[index];
}
- void setGlyphDataForIndex(unsigned index, Glyph glyph, const SimpleFontData* fontData)
+ // FIXME: Pages are immutable after initialization. This should be private.
+ void setGlyphForIndex(unsigned index, Glyph glyph)
{
ASSERT_WITH_SECURITY_IMPLICATION(index < size);
m_glyphs[index] = glyph;
-
- // GlyphPage getters will always return a null SimpleFontData* for glyph #0 if there's no per-glyph font array.
- if (hasPerGlyphFontData()) {
- m_perGlyphFontData[index] = glyph ? fontData : 0;
- return;
- }
-
- // A single-font GlyphPage already assigned m_fontDataForAllGlyphs in the constructor.
- ASSERT(!glyph || fontData == m_fontDataForAllGlyphs);
- }
-
- void setGlyphDataForIndex(unsigned index, const GlyphData& glyphData)
- {
- setGlyphDataForIndex(index, glyphData.glyph, glyphData.fontData);
}
- void removeFontDataFromSystemFallbackPage(const SimpleFontData* fontData)
+ const Font& font() const
{
- // This method should only be called on the system fallback page, which is never single-font.
- ASSERT(hasPerGlyphFontData());
- for (size_t i = 0; i < size; ++i) {
- if (m_perGlyphFontData[i] == fontData) {
- m_glyphs[i] = 0;
- m_perGlyphFontData[i] = 0;
- }
- }
+ return m_font;
}
- GlyphPageTreeNode* owner() const { return m_owner; }
-
// Implemented by the platform.
- bool fill(unsigned offset, unsigned length, UChar* characterBuffer, unsigned bufferLength, const SimpleFontData*);
-#if PLATFORM(MAC)
- static bool mayUseMixedFontDataWhenFilling(const UChar* characterBuffer, unsigned bufferLength, const SimpleFontData*);
-#else
- static bool mayUseMixedFontDataWhenFilling(const UChar*, unsigned, const SimpleFontData*) { return false; }
-#endif
+ bool fill(UChar* characterBuffer, unsigned bufferLength);
private:
- explicit GlyphPage(GlyphPageTreeNode* owner, const SimpleFontData* fontDataForAllGlyphs = 0)
- : m_fontDataForAllGlyphs(fontDataForAllGlyphs)
- , m_owner(owner)
+ explicit GlyphPage(const Font& font)
+ : m_font(font)
{
- memset(m_glyphs, 0, sizeof(m_glyphs));
- if (hasPerGlyphFontData())
- memset(m_perGlyphFontData, 0, sizeof(SimpleFontData*) * GlyphPage::size);
+ ++s_count;
}
- bool hasPerGlyphFontData() const { return !m_fontDataForAllGlyphs; }
+ const Font& m_font;
+ Glyph m_glyphs[size] { };
- const SimpleFontData* m_fontDataForAllGlyphs;
- GlyphPageTreeNode* m_owner;
- Glyph m_glyphs[size];
-
- // NOTE: This array has (GlyphPage::size) elements if m_fontDataForAllGlyphs is null.
- const SimpleFontData* m_perGlyphFontData[0];
+ WEBCORE_EXPORT static unsigned s_count;
};
-#if COMPILER(MSVC)
-#pragma warning(pop)
-#endif
-
} // namespace WebCore
#endif // GlyphPage_h
diff --git a/Source/WebCore/platform/graphics/GlyphPageTreeNode.cpp b/Source/WebCore/platform/graphics/GlyphPageTreeNode.cpp
deleted file mode 100644
index caef5e9d6..000000000
--- a/Source/WebCore/platform/graphics/GlyphPageTreeNode.cpp
+++ /dev/null
@@ -1,440 +0,0 @@
-/*
- * Copyright (C) 2006, 2007, 2008 Apple Inc. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
- * its contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
- * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
- * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
- * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#include "config.h"
-#include "GlyphPageTreeNode.h"
-
-#include "SegmentedFontData.h"
-#include "SimpleFontData.h"
-#include <stdio.h>
-#include <wtf/text/CString.h>
-#include <wtf/text/WTFString.h>
-#include <wtf/unicode/CharacterNames.h>
-#include <wtf/unicode/Unicode.h>
-
-#if ENABLE(OPENTYPE_VERTICAL)
-#include "OpenTypeVerticalData.h"
-#endif
-
-namespace WebCore {
-
-HashMap<int, GlyphPageTreeNode*>* GlyphPageTreeNode::roots = 0;
-GlyphPageTreeNode* GlyphPageTreeNode::pageZeroRoot = 0;
-
-GlyphPageTreeNode* GlyphPageTreeNode::getRoot(unsigned pageNumber)
-{
- static bool initialized;
- if (!initialized) {
- initialized = true;
- roots = new HashMap<int, GlyphPageTreeNode*>;
- pageZeroRoot = new GlyphPageTreeNode;
- }
-
- if (!pageNumber)
- return pageZeroRoot;
-
- if (GlyphPageTreeNode* foundNode = roots->get(pageNumber))
- return foundNode;
-
- GlyphPageTreeNode* node = new GlyphPageTreeNode;
-#ifndef NDEBUG
- node->m_pageNumber = pageNumber;
-#endif
- roots->set(pageNumber, node);
- return node;
-}
-
-size_t GlyphPageTreeNode::treeGlyphPageCount()
-{
- size_t count = 0;
- if (roots) {
- HashMap<int, GlyphPageTreeNode*>::iterator end = roots->end();
- for (HashMap<int, GlyphPageTreeNode*>::iterator it = roots->begin(); it != end; ++it)
- count += it->value->pageCount();
- }
-
- if (pageZeroRoot)
- count += pageZeroRoot->pageCount();
-
- return count;
-}
-
-size_t GlyphPageTreeNode::pageCount() const
-{
- size_t count = m_page && m_page->owner() == this ? 1 : 0;
- GlyphPageTreeNodeMap::const_iterator end = m_children.end();
- for (GlyphPageTreeNodeMap::const_iterator it = m_children.begin(); it != end; ++it)
- count += it->value->pageCount();
-
- return count;
-}
-
-void GlyphPageTreeNode::pruneTreeCustomFontData(const FontData* fontData)
-{
- // Enumerate all the roots and prune any tree that contains our custom font data.
- if (roots) {
- HashMap<int, GlyphPageTreeNode*>::iterator end = roots->end();
- for (HashMap<int, GlyphPageTreeNode*>::iterator it = roots->begin(); it != end; ++it)
- it->value->pruneCustomFontData(fontData);
- }
-
- if (pageZeroRoot)
- pageZeroRoot->pruneCustomFontData(fontData);
-}
-
-void GlyphPageTreeNode::pruneTreeFontData(const SimpleFontData* fontData)
-{
- if (roots) {
- HashMap<int, GlyphPageTreeNode*>::iterator end = roots->end();
- for (HashMap<int, GlyphPageTreeNode*>::iterator it = roots->begin(); it != end; ++it)
- it->value->pruneFontData(fontData);
- }
-
- if (pageZeroRoot)
- pageZeroRoot->pruneFontData(fontData);
-}
-
-static bool fill(GlyphPage* pageToFill, unsigned offset, unsigned length, UChar* buffer, unsigned bufferLength, const SimpleFontData* fontData)
-{
-#if ENABLE(SVG_FONTS)
- if (SimpleFontData::AdditionalFontData* additionalFontData = fontData->fontData())
- return additionalFontData->fillSVGGlyphPage(pageToFill, offset, length, buffer, bufferLength, fontData);
-#endif
- bool hasGlyphs = pageToFill->fill(offset, length, buffer, bufferLength, fontData);
-#if ENABLE(OPENTYPE_VERTICAL)
- if (hasGlyphs && fontData->verticalData())
- fontData->verticalData()->substituteWithVerticalGlyphs(fontData, pageToFill, offset, length);
-#endif
- return hasGlyphs;
-}
-
-void GlyphPageTreeNode::initializePage(const FontData* fontData, unsigned pageNumber)
-{
- ASSERT(!m_page);
-
- // This function must not be called for the root of the tree, because that
- // level does not contain any glyphs.
- ASSERT(m_level > 0 && m_parent);
-
- // The parent's page will be 0 if we are level one or the parent's font data
- // did not contain any glyphs for that page.
- GlyphPage* parentPage = m_parent->page();
-
- // NULL FontData means we're being asked for the system fallback font.
- if (fontData) {
- if (m_level == 1) {
- // Children of the root hold pure pages. These will cover only one
- // font data's glyphs, and will have glyph index 0 if the font data does not
- // contain the glyph.
- unsigned start = pageNumber * GlyphPage::size;
- UChar buffer[GlyphPage::size * 2 + 2];
- unsigned bufferLength;
- unsigned i;
-
- // Fill in a buffer with the entire "page" of characters that we want to look up glyphs for.
- if (start < 0x10000) {
- bufferLength = GlyphPage::size;
- for (i = 0; i < GlyphPage::size; i++)
- buffer[i] = start + i;
-
- if (start == 0) {
- // Control characters must not render at all.
- for (i = 0; i < 0x20; ++i)
- buffer[i] = zeroWidthSpace;
- for (i = 0x7F; i < 0xA0; i++)
- buffer[i] = zeroWidthSpace;
- buffer[softHyphen] = zeroWidthSpace;
-
- // \n, \t, and nonbreaking space must render as a space.
- buffer[(int)'\n'] = ' ';
- buffer[(int)'\t'] = ' ';
- buffer[noBreakSpace] = ' ';
- } else if (start == (leftToRightMark & ~(GlyphPage::size - 1))) {
- // LRM, RLM, LRE, RLE, ZWNJ, ZWJ, and PDF must not render at all.
- buffer[leftToRightMark - start] = zeroWidthSpace;
- buffer[rightToLeftMark - start] = zeroWidthSpace;
- buffer[leftToRightEmbed - start] = zeroWidthSpace;
- buffer[rightToLeftEmbed - start] = zeroWidthSpace;
- buffer[leftToRightOverride - start] = zeroWidthSpace;
- buffer[rightToLeftOverride - start] = zeroWidthSpace;
- buffer[zeroWidthNonJoiner - start] = zeroWidthSpace;
- buffer[zeroWidthJoiner - start] = zeroWidthSpace;
- buffer[popDirectionalFormatting - start] = zeroWidthSpace;
- } else if (start == (objectReplacementCharacter & ~(GlyphPage::size - 1))) {
- // Object replacement character must not render at all.
- buffer[objectReplacementCharacter - start] = zeroWidthSpace;
- } else if (start == (zeroWidthNoBreakSpace & ~(GlyphPage::size - 1))) {
- // ZWNBS/BOM must not render at all.
- buffer[zeroWidthNoBreakSpace - start] = zeroWidthSpace;
- }
- } else {
- bufferLength = GlyphPage::size * 2;
- for (i = 0; i < GlyphPage::size; i++) {
- int c = i + start;
- buffer[i * 2] = U16_LEAD(c);
- buffer[i * 2 + 1] = U16_TRAIL(c);
- }
- }
-
- // Now that we have a buffer full of characters, we want to get back an array
- // of glyph indices. This part involves calling into the platform-specific
- // routine of our glyph map for actually filling in the page with the glyphs.
- // Success is not guaranteed. For example, Times fails to fill page 260, giving glyph data
- // for only 128 out of 256 characters.
- bool haveGlyphs;
- if (!fontData->isSegmented()) {
- if (GlyphPage::mayUseMixedFontDataWhenFilling(buffer, bufferLength, static_cast<const SimpleFontData*>(fontData)))
- m_page = GlyphPage::createForMixedFontData(this);
- else
- m_page = GlyphPage::createForSingleFontData(this, static_cast<const SimpleFontData*>(fontData));
-#if PLATFORM(IOS)
- // FIXME: Times New Roman contains Arabic glyphs, but Core Text doesn't know how to shape them. See <rdar://problem/9823975>.
- // Once we have the fix for <rdar://problem/9823975> then remove this code together with SimpleFontData::shouldNotBeUsedForArabic()
- // in <rdar://problem/12096835>.
- if (pageNumber == 6 && static_cast<const SimpleFontData*>(fontData)->shouldNotBeUsedForArabic())
- haveGlyphs = false;
- else
-#endif
- haveGlyphs = fill(m_page.get(), 0, GlyphPage::size, buffer, bufferLength, static_cast<const SimpleFontData*>(fontData));
- } else {
- m_page = GlyphPage::createForMixedFontData(this);
- haveGlyphs = false;
-
- const SegmentedFontData* segmentedFontData = static_cast<const SegmentedFontData*>(fontData);
- unsigned numRanges = segmentedFontData->numRanges();
- bool zeroFilled = false;
- RefPtr<GlyphPage> scratchPage;
- GlyphPage* pageToFill = m_page.get();
- for (unsigned i = 0; i < numRanges; i++) {
- const FontDataRange& range = segmentedFontData->rangeAt(i);
- // all this casting is to ensure all the parameters to min and max have the same type,
- // to avoid ambiguous template parameter errors on Windows
- int from = std::max(0, static_cast<int>(range.from()) - static_cast<int>(start));
- int to = 1 + std::min(static_cast<int>(range.to()) - static_cast<int>(start), static_cast<int>(GlyphPage::size) - 1);
- if (from < static_cast<int>(GlyphPage::size) && to > 0) {
- if (haveGlyphs && !scratchPage) {
- scratchPage = GlyphPage::createForMixedFontData(this);
- pageToFill = scratchPage.get();
- }
-
- if (!zeroFilled) {
- if (from > 0 || to < static_cast<int>(GlyphPage::size)) {
- for (unsigned i = 0; i < GlyphPage::size; i++)
- pageToFill->setGlyphDataForIndex(i, 0, 0);
- }
- zeroFilled = true;
- }
- haveGlyphs |= fill(pageToFill, from, to - from, buffer + from * (start < 0x10000 ? 1 : 2), (to - from) * (start < 0x10000 ? 1 : 2), range.fontData().get());
- if (scratchPage) {
- ASSERT_WITH_SECURITY_IMPLICATION(to <= static_cast<int>(GlyphPage::size));
- for (int j = from; j < to; j++) {
- if (!m_page->glyphAt(j) && pageToFill->glyphAt(j))
- m_page->setGlyphDataForIndex(j, pageToFill->glyphDataForIndex(j));
- }
- }
- }
- }
- }
-
- if (!haveGlyphs)
- m_page = 0;
- } else if (parentPage && parentPage->owner() != m_parent) {
- // The page we're overriding may not be owned by our parent node.
- // This happens when our parent node provides no useful overrides
- // and just copies the pointer to an already-existing page (see
- // below).
- //
- // We want our override to be shared by all nodes that reference
- // that page to avoid duplication, and so standardize on having the
- // page's owner collect all the overrides. Call getChild on the
- // page owner with the desired font data (this will populate
- // the page) and then reference it.
- m_page = parentPage->owner()->getChild(fontData, pageNumber)->page();
- } else {
- // Get the pure page for the fallback font (at level 1 with no
- // overrides). getRootChild will always create a page if one
- // doesn't exist, but the page doesn't necessarily have glyphs
- // (this pointer may be 0).
- GlyphPage* fallbackPage = getRootChild(fontData, pageNumber)->page();
- if (!parentPage) {
- // When the parent has no glyphs for this page, we can easily
- // override it just by supplying the glyphs from our font.
- m_page = fallbackPage;
- } else if (!fallbackPage) {
- // When our font has no glyphs for this page, we can just reference the
- // parent page.
- m_page = parentPage;
- } else {
- // Combine the parent's glyphs and ours to form a new more complete page.
- m_page = GlyphPage::createForMixedFontData(this);
-
- // Overlay the parent page on the fallback page. Check if the fallback font
- // has added anything.
- bool newGlyphs = false;
- for (unsigned i = 0; i < GlyphPage::size; i++) {
- if (parentPage->glyphAt(i))
- m_page->setGlyphDataForIndex(i, parentPage->glyphDataForIndex(i));
- else if (fallbackPage->glyphAt(i)) {
- m_page->setGlyphDataForIndex(i, fallbackPage->glyphDataForIndex(i));
- newGlyphs = true;
- } else
- m_page->setGlyphDataForIndex(i, 0, 0);
- }
-
- if (!newGlyphs)
- // We didn't override anything, so our override is just the parent page.
- m_page = parentPage;
- }
- }
- } else {
- // System fallback. Initialized with the parent's page here, as individual
- // entries may use different fonts depending on character. If the Font
- // ever finds it needs a glyph out of the system fallback page, it will
- // ask the system for the best font to use and fill that glyph in for us.
- if (parentPage)
- m_page = parentPage->createCopiedSystemFallbackPage(this);
- else
- m_page = GlyphPage::createForMixedFontData(this);
- }
-}
-
-GlyphPageTreeNode* GlyphPageTreeNode::getChild(const FontData* fontData, unsigned pageNumber)
-{
- ASSERT(fontData || !m_isSystemFallback);
- ASSERT(pageNumber == m_pageNumber);
-
- if (GlyphPageTreeNode* foundChild = fontData ? m_children.get(fontData) : m_systemFallbackChild.get())
- return foundChild;
-
- GlyphPageTreeNode* child = new GlyphPageTreeNode;
- child->m_parent = this;
- child->m_level = m_level + 1;
- if (fontData && fontData->isCustomFont()) {
- for (GlyphPageTreeNode* curr = this; curr; curr = curr->m_parent)
- curr->m_customFontCount++;
- }
-
-#ifndef NDEBUG
- child->m_pageNumber = m_pageNumber;
-#endif
- if (fontData) {
- m_children.set(fontData, adoptPtr(child));
- fontData->setMaxGlyphPageTreeLevel(std::max(fontData->maxGlyphPageTreeLevel(), child->m_level));
- } else {
- m_systemFallbackChild = adoptPtr(child);
- child->m_isSystemFallback = true;
- }
- child->initializePage(fontData, pageNumber);
- return child;
-}
-
-void GlyphPageTreeNode::pruneCustomFontData(const FontData* fontData)
-{
- if (!fontData || !m_customFontCount)
- return;
-
- // Prune any branch that contains this FontData.
- if (OwnPtr<GlyphPageTreeNode> node = m_children.take(fontData)) {
- if (unsigned customFontCount = node->m_customFontCount + 1) {
- for (GlyphPageTreeNode* curr = this; curr; curr = curr->m_parent)
- curr->m_customFontCount -= customFontCount;
- }
- }
-
- // Check any branches that remain that still have custom fonts underneath them.
- if (!m_customFontCount)
- return;
-
- GlyphPageTreeNodeMap::iterator end = m_children.end();
- for (GlyphPageTreeNodeMap::iterator it = m_children.begin(); it != end; ++it)
- it->value->pruneCustomFontData(fontData);
-}
-
-void GlyphPageTreeNode::pruneFontData(const SimpleFontData* fontData, unsigned level)
-{
- ASSERT(fontData);
-
- // Prune fall back child (if any) of this font.
- if (m_systemFallbackChild && m_systemFallbackChild->m_page)
- m_systemFallbackChild->m_page->removeFontDataFromSystemFallbackPage(fontData);
-
- // Prune any branch that contains this FontData.
- if (OwnPtr<GlyphPageTreeNode> node = m_children.take(fontData)) {
- if (unsigned customFontCount = node->m_customFontCount) {
- for (GlyphPageTreeNode* curr = this; curr; curr = curr->m_parent)
- curr->m_customFontCount -= customFontCount;
- }
- }
-
- level++;
- if (level > fontData->maxGlyphPageTreeLevel())
- return;
-
- GlyphPageTreeNodeMap::iterator end = m_children.end();
- for (GlyphPageTreeNodeMap::iterator it = m_children.begin(); it != end; ++it)
- it->value->pruneFontData(fontData, level);
-}
-
-#ifndef NDEBUG
- void GlyphPageTreeNode::showSubtree()
- {
- Vector<char> indent(level());
- indent.fill('\t', level());
- indent.append(0);
-
- GlyphPageTreeNodeMap::iterator end = m_children.end();
- for (GlyphPageTreeNodeMap::iterator it = m_children.begin(); it != end; ++it) {
- printf("%s\t%p %s\n", indent.data(), it->key, it->key->description().utf8().data());
- it->value->showSubtree();
- }
- if (m_systemFallbackChild) {
- printf("%s\t* fallback\n", indent.data());
- m_systemFallbackChild->showSubtree();
- }
- }
-#endif
-
-}
-
-#ifndef NDEBUG
-void showGlyphPageTrees()
-{
- printf("Page 0:\n");
- showGlyphPageTree(0);
- HashMap<int, WebCore::GlyphPageTreeNode*>::iterator end = WebCore::GlyphPageTreeNode::roots->end();
- for (HashMap<int, WebCore::GlyphPageTreeNode*>::iterator it = WebCore::GlyphPageTreeNode::roots->begin(); it != end; ++it) {
- printf("\nPage %d:\n", it->key);
- showGlyphPageTree(it->key);
- }
-}
-
-void showGlyphPageTree(unsigned pageNumber)
-{
- WebCore::GlyphPageTreeNode::getRoot(pageNumber)->showSubtree();
-}
-#endif
diff --git a/Source/WebCore/platform/graphics/GlyphPageTreeNode.h b/Source/WebCore/platform/graphics/GlyphPageTreeNode.h
deleted file mode 100644
index b68772922..000000000
--- a/Source/WebCore/platform/graphics/GlyphPageTreeNode.h
+++ /dev/null
@@ -1,141 +0,0 @@
-/*
- * Copyright (C) 2006, 2007, 2008 Apple Inc. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
- * its contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
- * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
- * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
- * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#ifndef GlyphPageTreeNode_h
-#define GlyphPageTreeNode_h
-
-#include "GlyphPage.h"
-#include <string.h>
-#include <wtf/HashMap.h>
-#include <wtf/OwnPtr.h>
-#include <wtf/PassRefPtr.h>
-#include <wtf/RefCounted.h>
-#include <wtf/unicode/Unicode.h>
-
-#ifndef NDEBUG
-void showGlyphPageTrees();
-void showGlyphPageTree(unsigned pageNumber);
-#endif
-
-namespace WebCore {
-
-class FontData;
-class SimpleFontData;
-
-// The glyph page tree is a data structure that maps (FontData, glyph page number)
-// to a GlyphPage. Level 0 (the "root") is special. There is one root
-// GlyphPageTreeNode for each glyph page number. The roots do not have a
-// GlyphPage associated with them, and their initializePage() function is never
-// called to fill the glyphs.
-//
-// Each root node maps a FontData pointer to another GlyphPageTreeNode at
-// level 1 (the "root child") that stores the actual glyphs for a specific font data.
-// These nodes will only have a GlyphPage if they have glyphs for that range.
-//
-// Levels greater than one correspond to subsequent levels of the fallback list
-// for that font. These levels override their parent's page of glyphs by
-// filling in holes with the new font (thus making a more complete page).
-//
-// A NULL FontData pointer corresponds to the system fallback
-// font. It is tracked separately from the regular pages and overrides so that
-// the glyph pages do not get polluted with these last-resort glyphs. The
-// system fallback page is not populated at construction like the other pages,
-// but on demand for each glyph, because the system may need to use different
-// fallback fonts for each. This lazy population is done by the Font.
-class GlyphPageTreeNode {
- WTF_MAKE_FAST_ALLOCATED;
-public:
- static GlyphPageTreeNode* getRootChild(const FontData* fontData, unsigned pageNumber)
- {
- return getRoot(pageNumber)->getChild(fontData, pageNumber);
- }
-
- static void pruneTreeCustomFontData(const FontData*);
- static void pruneTreeFontData(const SimpleFontData*);
-
- void pruneCustomFontData(const FontData*);
- void pruneFontData(const SimpleFontData*, unsigned level = 0);
-
- GlyphPageTreeNode* parent() const { return m_parent; }
- GlyphPageTreeNode* getChild(const FontData*, unsigned pageNumber);
-
- // Returns a page of glyphs (or NULL if there are no glyphs in this page's character range).
- GlyphPage* page() const { return m_page.get(); }
-
- // Returns the level of this node. See class-level comment.
- unsigned level() const { return m_level; }
-
- // The system fallback font has special rules (see above).
- bool isSystemFallback() const { return m_isSystemFallback; }
-
- static size_t treeGlyphPageCount();
- size_t pageCount() const;
-
-private:
- GlyphPageTreeNode()
- : m_parent(0)
- , m_level(0)
- , m_isSystemFallback(false)
- , m_customFontCount(0)
-#ifndef NDEBUG
- , m_pageNumber(0)
-#endif
- {
- }
-
- static GlyphPageTreeNode* getRoot(unsigned pageNumber);
- void initializePage(const FontData*, unsigned pageNumber);
-
-#ifndef NDEBUG
- void showSubtree();
-#endif
-
- static HashMap<int, GlyphPageTreeNode*>* roots;
- static GlyphPageTreeNode* pageZeroRoot;
-
- typedef HashMap<const FontData*, OwnPtr<GlyphPageTreeNode>> GlyphPageTreeNodeMap;
-
- GlyphPageTreeNodeMap m_children;
- GlyphPageTreeNode* m_parent;
- RefPtr<GlyphPage> m_page;
- unsigned m_level : 31;
- bool m_isSystemFallback : 1;
- unsigned m_customFontCount;
- OwnPtr<GlyphPageTreeNode> m_systemFallbackChild;
-
-#ifndef NDEBUG
- unsigned m_pageNumber;
-
- friend void ::showGlyphPageTrees();
- friend void ::showGlyphPageTree(unsigned pageNumber);
-#endif
-};
-
-} // namespace WebCore
-
-#endif // GlyphPageTreeNode_h
diff --git a/Source/WebCore/platform/graphics/Gradient.cpp b/Source/WebCore/platform/graphics/Gradient.cpp
index 67cd6075c..b0d1f6e97 100644
--- a/Source/WebCore/platform/graphics/Gradient.cpp
+++ b/Source/WebCore/platform/graphics/Gradient.cpp
@@ -11,10 +11,10 @@
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
- * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
@@ -30,7 +30,7 @@
#include "Color.h"
#include "FloatRect.h"
#include <wtf/HashFunctions.h>
-#include <wtf/StringHasher.h>
+#include <wtf/Hasher.h>
using WTF::pairIntHash;
@@ -69,7 +69,7 @@ Gradient::~Gradient()
platformDestroy();
}
-void Gradient::adjustParametersForTiledDrawing(IntSize& size, FloatRect& srcRect)
+void Gradient::adjustParametersForTiledDrawing(FloatSize& size, FloatRect& srcRect, const FloatSize& spacing)
{
if (m_radial)
return;
@@ -77,6 +77,9 @@ void Gradient::adjustParametersForTiledDrawing(IntSize& size, FloatRect& srcRect
if (srcRect.isEmpty())
return;
+ if (!spacing.isZero())
+ return;
+
if (m_p0.x() == m_p1.x()) {
size.setWidth(1);
srcRect.setWidth(1);
@@ -93,6 +96,7 @@ void Gradient::adjustParametersForTiledDrawing(IntSize& size, FloatRect& srcRect
void Gradient::addColorStop(float value, const Color& color)
{
+ // FIXME: ExtendedColor - update this to support colors with color spaces.
float r;
float g;
float b;
diff --git a/Source/WebCore/platform/graphics/Gradient.h b/Source/WebCore/platform/graphics/Gradient.h
index 8d19e3325..ccb018819 100644
--- a/Source/WebCore/platform/graphics/Gradient.h
+++ b/Source/WebCore/platform/graphics/Gradient.h
@@ -12,10 +12,10 @@
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
- * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
@@ -31,7 +31,6 @@
#include "AffineTransform.h"
#include "FloatPoint.h"
#include "GraphicsTypes.h"
-#include <wtf/PassRefPtr.h>
#include <wtf/RefCounted.h>
#include <wtf/Vector.h>
@@ -42,6 +41,10 @@ typedef struct CGContext* CGContextRef;
typedef struct CGGradient* CGGradientRef;
typedef CGGradientRef PlatformGradient;
+#elif USE(DIRECT2D)
+interface ID2D1Brush;
+interface ID2D1RenderTarget;
+typedef ID2D1Brush* PlatformGradient;
#elif USE(CAIRO)
typedef struct _cairo_pattern cairo_pattern_t;
typedef cairo_pattern_t* PlatformGradient;
@@ -57,19 +60,19 @@ namespace WebCore {
class Gradient : public RefCounted<Gradient> {
public:
- static PassRefPtr<Gradient> create(const FloatPoint& p0, const FloatPoint& p1)
+ static Ref<Gradient> create(const FloatPoint& p0, const FloatPoint& p1)
{
- return adoptRef(new Gradient(p0, p1));
+ return adoptRef(*new Gradient(p0, p1));
}
- static PassRefPtr<Gradient> create(const FloatPoint& p0, float r0, const FloatPoint& p1, float r1, float aspectRatio = 1)
+ static Ref<Gradient> create(const FloatPoint& p0, float r0, const FloatPoint& p1, float r1, float aspectRatio = 1)
{
- return adoptRef(new Gradient(p0, r0, p1, r1, aspectRatio));
+ return adoptRef(*new Gradient(p0, r0, p1, r1, aspectRatio));
}
- ~Gradient();
+ WEBCORE_EXPORT ~Gradient();
struct ColorStop;
- void addColorStop(const ColorStop&);
- void addColorStop(float, const Color&);
+ WEBCORE_EXPORT void addColorStop(const ColorStop&);
+ WEBCORE_EXPORT void addColorStop(float, const Color&);
bool hasAlpha() const;
@@ -130,6 +133,7 @@ namespace WebCore {
PlatformGradient platformGradient();
#endif
+ // FIXME: ExtendedColor - A color stop needs a notion of color space.
struct ColorStop {
float stop;
float red;
@@ -150,7 +154,7 @@ namespace WebCore {
AffineTransform gradientSpaceTransform() { return m_gradientSpaceTransformation; }
void fill(GraphicsContext*, const FloatRect&);
- void adjustParametersForTiledDrawing(IntSize&, FloatRect&);
+ void adjustParametersForTiledDrawing(FloatSize&, FloatRect&, const FloatSize& spacing);
void setPlatformGradientSpaceTransform(const AffineTransform& gradientSpaceTransformation);
@@ -160,19 +164,25 @@ namespace WebCore {
#if USE(CG)
void paint(CGContextRef);
void paint(GraphicsContext*);
+#elif USE(DIRECT2D)
+ PlatformGradient createPlatformGradientIfNecessary(ID2D1RenderTarget*);
#elif USE(CAIRO)
PlatformGradient platformGradient(float globalAlpha);
#endif
private:
- Gradient(const FloatPoint& p0, const FloatPoint& p1);
+ WEBCORE_EXPORT Gradient(const FloatPoint& p0, const FloatPoint& p1);
Gradient(const FloatPoint& p0, float r0, const FloatPoint& p1, float r1, float aspectRatio);
- void platformInit() { m_gradient = 0; }
+ void platformInit() { m_gradient = nullptr; }
void platformDestroy();
void sortStopsIfNecessary();
+#if USE(DIRECT2D)
+ void generateGradient(ID2D1RenderTarget*);
+#endif
+
// Keep any parameters relevant to rendering in sync with the structure in Gradient::hash().
bool m_radial;
FloatPoint m_p0;
diff --git a/Source/WebCore/platform/graphics/GradientImage.cpp b/Source/WebCore/platform/graphics/GradientImage.cpp
index e8c420365..50b9d77cf 100644
--- a/Source/WebCore/platform/graphics/GradientImage.cpp
+++ b/Source/WebCore/platform/graphics/GradientImage.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2008, 2009, 2010, 2012, 2013 Apple Inc. All rights reserved.
+ * Copyright (C) 2008-2016 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -26,34 +26,43 @@
#include "config.h"
#include "GradientImage.h"
-#include "FloatRect.h"
#include "GraphicsContext.h"
-#include "Length.h"
+#include "ImageBuffer.h"
namespace WebCore {
-void GradientImage::draw(GraphicsContext* destContext, const FloatRect& destRect, const FloatRect& srcRect, ColorSpace, CompositeOperator compositeOp, BlendMode blendMode, ImageOrientationDescription)
+GradientImage::GradientImage(Gradient& generator, const FloatSize& size)
+ : m_gradient(generator)
{
- GraphicsContextStateSaver stateSaver(*destContext);
- destContext->setCompositeOperation(compositeOp, blendMode);
- destContext->clip(destRect);
- destContext->translate(destRect.x(), destRect.y());
+ setContainerSize(size);
+}
+
+GradientImage::~GradientImage()
+{
+}
+
+void GradientImage::draw(GraphicsContext& destContext, const FloatRect& destRect, const FloatRect& srcRect, CompositeOperator compositeOp, BlendMode blendMode, ImageOrientationDescription)
+{
+ GraphicsContextStateSaver stateSaver(destContext);
+ destContext.setCompositeOperation(compositeOp, blendMode);
+ destContext.clip(destRect);
+ destContext.translate(destRect.x(), destRect.y());
if (destRect.size() != srcRect.size())
- destContext->scale(FloatSize(destRect.width() / srcRect.width(), destRect.height() / srcRect.height()));
- destContext->translate(-srcRect.x(), -srcRect.y());
- destContext->fillRect(FloatRect(FloatPoint(), size()), *m_gradient.get());
+ destContext.scale(FloatSize(destRect.width() / srcRect.width(), destRect.height() / srcRect.height()));
+ destContext.translate(-srcRect.x(), -srcRect.y());
+ destContext.fillRect(FloatRect(FloatPoint(), size()), m_gradient.get());
}
-void GradientImage::drawPattern(GraphicsContext* destContext, const FloatRect& srcRect, const AffineTransform& patternTransform,
- const FloatPoint& phase, ColorSpace styleColorSpace, CompositeOperator compositeOp, const FloatRect& destRect, BlendMode blendMode)
+void GradientImage::drawPattern(GraphicsContext& destContext, const FloatRect& destRect, const FloatRect& srcRect, const AffineTransform& patternTransform,
+ const FloatPoint& phase, const FloatSize& spacing, CompositeOperator compositeOp, BlendMode blendMode)
{
// Allow the generator to provide visually-equivalent tiling parameters for better performance.
- IntSize adjustedSize = size();
+ FloatSize adjustedSize = size();
FloatRect adjustedSrcRect = srcRect;
- m_gradient->adjustParametersForTiledDrawing(adjustedSize, adjustedSrcRect);
+ m_gradient->adjustParametersForTiledDrawing(adjustedSize, adjustedSrcRect, spacing);
// Factor in the destination context's scale to generate at the best resolution
- AffineTransform destContextCTM = destContext->getCTM(GraphicsContext::DefinitelyIncludeDeviceScale);
+ AffineTransform destContextCTM = destContext.getCTM(GraphicsContext::DefinitelyIncludeDeviceScale);
double xScale = fabs(destContextCTM.xScale());
double yScale = fabs(destContextCTM.yScale());
AffineTransform adjustedPatternCTM = patternTransform;
@@ -62,26 +71,31 @@ void GradientImage::drawPattern(GraphicsContext* destContext, const FloatRect& s
unsigned generatorHash = m_gradient->hash();
- if (!m_cachedImageBuffer || m_cachedGeneratorHash != generatorHash || m_cachedAdjustedSize != adjustedSize || !destContext->isCompatibleWithBuffer(m_cachedImageBuffer.get())) {
- m_cachedImageBuffer = destContext->createCompatibleBuffer(adjustedSize, m_gradient->hasAlpha());
+ if (!m_cachedImageBuffer || m_cachedGeneratorHash != generatorHash || m_cachedAdjustedSize != adjustedSize || !m_cachedImageBuffer->isCompatibleWithContext(destContext)) {
+ m_cachedImageBuffer = ImageBuffer::createCompatibleBuffer(adjustedSize, ColorSpaceSRGB, destContext);
if (!m_cachedImageBuffer)
return;
// Fill with the generated image.
- m_cachedImageBuffer->context()->fillRect(FloatRect(FloatPoint(), adjustedSize), *m_gradient);
+ m_cachedImageBuffer->context().fillRect(FloatRect(FloatPoint(), adjustedSize), m_gradient.get());
m_cachedGeneratorHash = generatorHash;
m_cachedAdjustedSize = adjustedSize;
- if (destContext->drawLuminanceMask())
+ if (destContext.drawLuminanceMask())
m_cachedImageBuffer->convertToLuminanceMask();
}
- m_cachedImageBuffer->setSpaceSize(spaceSize());
- destContext->setDrawLuminanceMask(false);
+ destContext.setDrawLuminanceMask(false);
// Tile the image buffer into the context.
- m_cachedImageBuffer->drawPattern(destContext, adjustedSrcRect, adjustedPatternCTM, phase, styleColorSpace, compositeOp, destRect, blendMode);
+ m_cachedImageBuffer->drawPattern(destContext, destRect, adjustedSrcRect, adjustedPatternCTM, phase, spacing, compositeOp, blendMode);
+}
+
+void GradientImage::dump(TextStream& ts) const
+{
+ GeneratedImage::dump(ts);
+ // FIXME: dump the gradient.
}
}
diff --git a/Source/WebCore/platform/graphics/GradientImage.h b/Source/WebCore/platform/graphics/GradientImage.h
index 0182dc1d7..c49463951 100644
--- a/Source/WebCore/platform/graphics/GradientImage.h
+++ b/Source/WebCore/platform/graphics/GradientImage.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2008, 2012, 2013 Apple Computer, Inc. All rights reserved.
+ * Copyright (C) 2008-2016 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -10,10 +10,10 @@
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
- * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
@@ -23,45 +23,36 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef GradientImage_h
-#define GradientImage_h
+#pragma once
#include "GeneratedImage.h"
-#include "Gradient.h"
-#include "Image.h"
-#include "ImageBuffer.h"
-#include "IntSize.h"
-#include <wtf/RefPtr.h>
namespace WebCore {
+class Gradient;
+class ImageBuffer;
+
class GradientImage final : public GeneratedImage {
public:
- static PassRefPtr<GradientImage> create(PassRefPtr<Gradient> generator, const IntSize& size)
+ static Ref<GradientImage> create(Gradient& generator, const FloatSize& size)
{
- return adoptRef(new GradientImage(generator, size));
+ return adoptRef(*new GradientImage(generator, size));
}
- virtual ~GradientImage() { }
-
-protected:
- virtual void draw(GraphicsContext*, const FloatRect& dstRect, const FloatRect& srcRect, ColorSpace styleColorSpace, CompositeOperator, BlendMode, ImageOrientationDescription) override;
- virtual void drawPattern(GraphicsContext*, const FloatRect& srcRect, const AffineTransform& patternTransform,
- const FloatPoint& phase, ColorSpace styleColorSpace, CompositeOperator, const FloatRect& destRect, BlendMode) override;
-
- GradientImage(PassRefPtr<Gradient> generator, const IntSize& size)
- : m_gradient(generator)
- {
- setContainerSize(size);
- }
+ virtual ~GradientImage();
private:
- RefPtr<Gradient> m_gradient;
+ GradientImage(Gradient&, const FloatSize&);
+
+ void draw(GraphicsContext&, const FloatRect& dstRect, const FloatRect& srcRect, CompositeOperator, BlendMode, ImageOrientationDescription) final;
+ void drawPattern(GraphicsContext&, const FloatRect& destRect, const FloatRect& srcRect, const AffineTransform& patternTransform, const FloatPoint& phase, const FloatSize& spacing, CompositeOperator, BlendMode) final;
+ bool isGradientImage() const final { return true; }
+ void dump(TextStream&) const final;
+
+ Ref<Gradient> m_gradient;
std::unique_ptr<ImageBuffer> m_cachedImageBuffer;
- IntSize m_cachedAdjustedSize;
+ FloatSize m_cachedAdjustedSize;
unsigned m_cachedGeneratorHash;
};
}
-
-#endif
diff --git a/Source/WebCore/platform/graphics/GraphicsContext.cpp b/Source/WebCore/platform/graphics/GraphicsContext.cpp
index f12f02a6f..1bf59c30e 100644
--- a/Source/WebCore/platform/graphics/GraphicsContext.cpp
+++ b/Source/WebCore/platform/graphics/GraphicsContext.cpp
@@ -28,13 +28,14 @@
#include "BidiResolver.h"
#include "BitmapImage.h"
+#include "DisplayListRecorder.h"
+#include "FloatRoundedRect.h"
#include "Gradient.h"
#include "ImageBuffer.h"
#include "IntRect.h"
#include "RoundedRect.h"
#include "TextRun.h"
-
-#include "stdio.h"
+#include "TextStream.h"
namespace WebCore {
@@ -73,24 +74,267 @@ public:
private:
const TextRun* m_textRun;
- int m_offset;
+ unsigned m_offset;
};
-#if !PLATFORM(IOS)
-GraphicsContext::GraphicsContext(PlatformGraphicsContext* platformGraphicsContext)
- : m_updatingControlTints(false)
- , m_transparencyCount(0)
+#define CHECK_FOR_CHANGED_PROPERTY(flag, property) \
+ if ((m_changeFlags & GraphicsContextState::flag) && (m_state.property != state.property)) \
+ changeFlags |= GraphicsContextState::flag;
+
+GraphicsContextState::StateChangeFlags GraphicsContextStateChange::changesFromState(const GraphicsContextState& state) const
{
- platformInit(platformGraphicsContext);
+ GraphicsContextState::StateChangeFlags changeFlags = GraphicsContextState::NoChange;
+
+ CHECK_FOR_CHANGED_PROPERTY(StrokeGradientChange, strokeGradient);
+ CHECK_FOR_CHANGED_PROPERTY(StrokePatternChange, strokePattern);
+ CHECK_FOR_CHANGED_PROPERTY(FillGradientChange, fillGradient);
+ CHECK_FOR_CHANGED_PROPERTY(FillPatternChange, fillPattern);
+
+ if ((m_changeFlags & GraphicsContextState::ShadowChange)
+ && (m_state.shadowOffset != state.shadowOffset
+ || m_state.shadowBlur != state.shadowBlur
+ || m_state.shadowColor != state.shadowColor))
+ changeFlags |= GraphicsContextState::ShadowChange;
+
+ CHECK_FOR_CHANGED_PROPERTY(StrokeThicknessChange, strokeThickness);
+ CHECK_FOR_CHANGED_PROPERTY(TextDrawingModeChange, textDrawingMode);
+ CHECK_FOR_CHANGED_PROPERTY(StrokeColorChange, strokeColor);
+ CHECK_FOR_CHANGED_PROPERTY(FillColorChange, fillColor);
+ CHECK_FOR_CHANGED_PROPERTY(StrokeStyleChange, strokeStyle);
+ CHECK_FOR_CHANGED_PROPERTY(FillRuleChange, fillRule);
+ CHECK_FOR_CHANGED_PROPERTY(AlphaChange, alpha);
+
+ if ((m_changeFlags & (GraphicsContextState::CompositeOperationChange | GraphicsContextState::BlendModeChange))
+ && (m_state.compositeOperator != state.compositeOperator || m_state.blendMode != state.blendMode))
+ changeFlags |= (GraphicsContextState::CompositeOperationChange | GraphicsContextState::BlendModeChange);
+
+ CHECK_FOR_CHANGED_PROPERTY(ShouldAntialiasChange, shouldAntialias);
+ CHECK_FOR_CHANGED_PROPERTY(ShouldSmoothFontsChange, shouldSmoothFonts);
+ CHECK_FOR_CHANGED_PROPERTY(ShouldSubpixelQuantizeFontsChange, shouldSubpixelQuantizeFonts);
+ CHECK_FOR_CHANGED_PROPERTY(ShadowsIgnoreTransformsChange, shadowsIgnoreTransforms);
+ CHECK_FOR_CHANGED_PROPERTY(DrawLuminanceMaskChange, drawLuminanceMask);
+ CHECK_FOR_CHANGED_PROPERTY(ImageInterpolationQualityChange, imageInterpolationQuality);
+
+ return changeFlags;
}
-#else
-GraphicsContext::GraphicsContext(PlatformGraphicsContext* platformGraphicsContext, bool shouldUseContextColors)
- : m_updatingControlTints(false)
- , m_transparencyCount(0)
+
+void GraphicsContextStateChange::accumulate(const GraphicsContextState& state, GraphicsContextState::StateChangeFlags flags)
{
- platformInit(platformGraphicsContext, shouldUseContextColors);
+ // FIXME: This code should move to GraphicsContextState.
+ if (flags & GraphicsContextState::StrokeGradientChange)
+ m_state.strokeGradient = state.strokeGradient;
+
+ if (flags & GraphicsContextState::StrokePatternChange)
+ m_state.strokePattern = state.strokePattern;
+
+ if (flags & GraphicsContextState::FillGradientChange)
+ m_state.fillGradient = state.fillGradient;
+
+ if (flags & GraphicsContextState::FillPatternChange)
+ m_state.fillPattern = state.fillPattern;
+
+ if (flags & GraphicsContextState::ShadowChange) {
+ // FIXME: Deal with state.shadowsUseLegacyRadius.
+ m_state.shadowOffset = state.shadowOffset;
+ m_state.shadowBlur = state.shadowBlur;
+ m_state.shadowColor = state.shadowColor;
+ }
+
+ if (flags & GraphicsContextState::StrokeThicknessChange)
+ m_state.strokeThickness = state.strokeThickness;
+
+ if (flags & GraphicsContextState::TextDrawingModeChange)
+ m_state.textDrawingMode = state.textDrawingMode;
+
+ if (flags & GraphicsContextState::StrokeColorChange)
+ m_state.strokeColor = state.strokeColor;
+
+ if (flags & GraphicsContextState::FillColorChange)
+ m_state.fillColor = state.fillColor;
+
+ if (flags & GraphicsContextState::StrokeStyleChange)
+ m_state.strokeStyle = state.strokeStyle;
+
+ if (flags & GraphicsContextState::FillRuleChange)
+ m_state.fillRule = state.fillRule;
+
+ if (flags & GraphicsContextState::AlphaChange)
+ m_state.alpha = state.alpha;
+
+ if (flags & (GraphicsContextState::CompositeOperationChange | GraphicsContextState::BlendModeChange)) {
+ m_state.compositeOperator = state.compositeOperator;
+ m_state.blendMode = state.blendMode;
+ }
+
+ if (flags & GraphicsContextState::ShouldAntialiasChange)
+ m_state.shouldAntialias = state.shouldAntialias;
+
+ if (flags & GraphicsContextState::ShouldSmoothFontsChange)
+ m_state.shouldSmoothFonts = state.shouldSmoothFonts;
+
+ if (flags & GraphicsContextState::ShouldSubpixelQuantizeFontsChange)
+ m_state.shouldSubpixelQuantizeFonts = state.shouldSubpixelQuantizeFonts;
+
+ if (flags & GraphicsContextState::ShadowsIgnoreTransformsChange)
+ m_state.shadowsIgnoreTransforms = state.shadowsIgnoreTransforms;
+
+ if (flags & GraphicsContextState::DrawLuminanceMaskChange)
+ m_state.drawLuminanceMask = state.drawLuminanceMask;
+
+ if (flags & GraphicsContextState::ImageInterpolationQualityChange)
+ m_state.imageInterpolationQuality = state.imageInterpolationQuality;
+
+ m_changeFlags |= flags;
}
+
+void GraphicsContextStateChange::apply(GraphicsContext& context) const
+{
+ if (m_changeFlags & GraphicsContextState::StrokeGradientChange)
+ context.setStrokeGradient(*m_state.strokeGradient);
+
+ if (m_changeFlags & GraphicsContextState::StrokePatternChange)
+ context.setStrokePattern(*m_state.strokePattern);
+
+ if (m_changeFlags & GraphicsContextState::FillGradientChange)
+ context.setFillGradient(*m_state.fillGradient);
+
+ if (m_changeFlags & GraphicsContextState::FillPatternChange)
+ context.setFillPattern(*m_state.fillPattern);
+
+ if (m_changeFlags & GraphicsContextState::ShadowChange) {
+#if USE(CG)
+ if (m_state.shadowsUseLegacyRadius)
+ context.setLegacyShadow(m_state.shadowOffset, m_state.shadowBlur, m_state.shadowColor);
+ else
#endif
+ context.setShadow(m_state.shadowOffset, m_state.shadowBlur, m_state.shadowColor);
+ }
+
+ if (m_changeFlags & GraphicsContextState::StrokeThicknessChange)
+ context.setStrokeThickness(m_state.strokeThickness);
+
+ if (m_changeFlags & GraphicsContextState::TextDrawingModeChange)
+ context.setTextDrawingMode(m_state.textDrawingMode);
+
+ if (m_changeFlags & GraphicsContextState::StrokeColorChange)
+ context.setStrokeColor(m_state.strokeColor);
+
+ if (m_changeFlags & GraphicsContextState::FillColorChange)
+ context.setFillColor(m_state.fillColor);
+
+ if (m_changeFlags & GraphicsContextState::StrokeStyleChange)
+ context.setStrokeStyle(m_state.strokeStyle);
+
+ if (m_changeFlags & GraphicsContextState::FillRuleChange)
+ context.setFillRule(m_state.fillRule);
+
+ if (m_changeFlags & GraphicsContextState::AlphaChange)
+ context.setAlpha(m_state.alpha);
+
+ if (m_changeFlags & (GraphicsContextState::CompositeOperationChange | GraphicsContextState::BlendModeChange))
+ context.setCompositeOperation(m_state.compositeOperator, m_state.blendMode);
+
+ if (m_changeFlags & GraphicsContextState::ShouldAntialiasChange)
+ context.setShouldAntialias(m_state.shouldAntialias);
+
+ if (m_changeFlags & GraphicsContextState::ShouldSmoothFontsChange)
+ context.setShouldSmoothFonts(m_state.shouldSmoothFonts);
+
+ if (m_changeFlags & GraphicsContextState::ShouldSubpixelQuantizeFontsChange)
+ context.setShouldSubpixelQuantizeFonts(m_state.shouldSubpixelQuantizeFonts);
+
+ if (m_changeFlags & GraphicsContextState::ShadowsIgnoreTransformsChange)
+ context.setShadowsIgnoreTransforms(m_state.shadowsIgnoreTransforms);
+
+ if (m_changeFlags & GraphicsContextState::DrawLuminanceMaskChange)
+ context.setDrawLuminanceMask(m_state.drawLuminanceMask);
+
+ if (m_changeFlags & GraphicsContextState::ImageInterpolationQualityChange)
+ context.setImageInterpolationQuality(m_state.imageInterpolationQuality);
+}
+
+void GraphicsContextStateChange::dump(TextStream& ts) const
+{
+ ts.dumpProperty("change-flags", m_changeFlags);
+
+ if (m_changeFlags & GraphicsContextState::StrokeGradientChange)
+ ts.dumpProperty("stroke-gradient", m_state.strokeGradient.get());
+
+ if (m_changeFlags & GraphicsContextState::StrokePatternChange)
+ ts.dumpProperty("stroke-pattern", m_state.strokePattern.get());
+
+ if (m_changeFlags & GraphicsContextState::FillGradientChange)
+ ts.dumpProperty("fill-gradient", m_state.fillGradient.get());
+
+ if (m_changeFlags & GraphicsContextState::FillPatternChange)
+ ts.dumpProperty("fill-pattern", m_state.fillPattern.get());
+
+ if (m_changeFlags & GraphicsContextState::ShadowChange) {
+ ts.dumpProperty("shadow-blur", m_state.shadowBlur);
+ ts.dumpProperty("shadow-offset", m_state.shadowOffset);
+#if USE(CG)
+ ts.dumpProperty("shadows-use-legacy-radius", m_state.shadowsUseLegacyRadius);
+#endif
+ }
+
+ if (m_changeFlags & GraphicsContextState::StrokeThicknessChange)
+ ts.dumpProperty("stroke-thickness", m_state.strokeThickness);
+
+ if (m_changeFlags & GraphicsContextState::TextDrawingModeChange)
+ ts.dumpProperty("text-drawing-mode", m_state.textDrawingMode);
+
+ if (m_changeFlags & GraphicsContextState::StrokeColorChange)
+ ts.dumpProperty("stroke-color", m_state.strokeColor);
+
+ if (m_changeFlags & GraphicsContextState::FillColorChange)
+ ts.dumpProperty("fill-color", m_state.fillColor);
+
+ if (m_changeFlags & GraphicsContextState::StrokeStyleChange)
+ ts.dumpProperty("stroke-style", m_state.strokeStyle);
+
+ if (m_changeFlags & GraphicsContextState::FillRuleChange)
+ ts.dumpProperty("fill-rule", m_state.fillRule);
+
+ if (m_changeFlags & GraphicsContextState::AlphaChange)
+ ts.dumpProperty("alpha", m_state.alpha);
+
+ if (m_changeFlags & GraphicsContextState::CompositeOperationChange)
+ ts.dumpProperty("composite-operator", m_state.compositeOperator);
+
+ if (m_changeFlags & GraphicsContextState::BlendModeChange)
+ ts.dumpProperty("blend-mode", m_state.blendMode);
+
+ if (m_changeFlags & GraphicsContextState::ShouldAntialiasChange)
+ ts.dumpProperty("should-antialias", m_state.shouldAntialias);
+
+ if (m_changeFlags & GraphicsContextState::ShouldSmoothFontsChange)
+ ts.dumpProperty("should-smooth-fonts", m_state.shouldSmoothFonts);
+
+ if (m_changeFlags & GraphicsContextState::ShouldSubpixelQuantizeFontsChange)
+ ts.dumpProperty("should-subpixel-quantize-fonts", m_state.shouldSubpixelQuantizeFonts);
+
+ if (m_changeFlags & GraphicsContextState::ShadowsIgnoreTransformsChange)
+ ts.dumpProperty("shadows-ignore-transforms", m_state.shadowsIgnoreTransforms);
+
+ if (m_changeFlags & GraphicsContextState::DrawLuminanceMaskChange)
+ ts.dumpProperty("draw-luminance-mask", m_state.drawLuminanceMask);
+}
+
+TextStream& operator<<(TextStream& ts, const GraphicsContextStateChange& stateChange)
+{
+ stateChange.dump(ts);
+ return ts;
+}
+
+GraphicsContext::GraphicsContext(NonPaintingReasons nonPaintingReasons)
+ : m_nonPaintingReasons(nonPaintingReasons)
+{
+}
+
+GraphicsContext::GraphicsContext(PlatformGraphicsContext* platformGraphicsContext)
+{
+ platformInit(platformGraphicsContext);
+}
GraphicsContext::~GraphicsContext()
{
@@ -106,6 +350,11 @@ void GraphicsContext::save()
m_stack.append(m_state);
+ if (isRecording()) {
+ m_displayListRecorder->save();
+ return;
+ }
+
savePlatformState();
}
@@ -118,74 +367,104 @@ void GraphicsContext::restore()
LOG_ERROR("ERROR void GraphicsContext::restore() stack is empty");
return;
}
+
m_state = m_stack.last();
m_stack.removeLast();
+ // Make sure we deallocate the state stack buffer when it goes empty.
+ // Canvas elements will immediately save() again, but that goes into inline capacity.
+ if (m_stack.isEmpty())
+ m_stack.clear();
+
+ if (isRecording()) {
+ m_displayListRecorder->restore();
+ return;
+ }
+
restorePlatformState();
}
-#if PLATFORM(IOS)
-void GraphicsContext::drawRaisedEllipse(const FloatRect& rect, const Color& ellipseColor, ColorSpace ellipseColorSpace, const Color& shadowColor, ColorSpace shadowColorSpace)
+void GraphicsContext::drawRaisedEllipse(const FloatRect& rect, const Color& ellipseColor, const Color& shadowColor)
{
if (paintingDisabled())
return;
save();
- setStrokeColor(shadowColor, shadowColorSpace);
- setFillColor(shadowColor, shadowColorSpace);
+ setStrokeColor(shadowColor);
+ setFillColor(shadowColor);
drawEllipse(FloatRect(rect.x(), rect.y() + 1, rect.width(), rect.height()));
- setStrokeColor(ellipseColor, ellipseColorSpace);
- setFillColor(ellipseColor, ellipseColorSpace);
+ setStrokeColor(ellipseColor);
+ setFillColor(ellipseColor);
drawEllipse(rect);
restore();
}
-#endif
void GraphicsContext::setStrokeThickness(float thickness)
{
m_state.strokeThickness = thickness;
+ if (isRecording()) {
+ m_displayListRecorder->updateState(m_state, GraphicsContextState::StrokeThicknessChange);
+ return;
+ }
+
setPlatformStrokeThickness(thickness);
}
void GraphicsContext::setStrokeStyle(StrokeStyle style)
{
m_state.strokeStyle = style;
+ if (isRecording()) {
+ m_displayListRecorder->updateState(m_state, GraphicsContextState::StrokeStyleChange);
+ return;
+ }
setPlatformStrokeStyle(style);
}
-void GraphicsContext::setStrokeColor(const Color& color, ColorSpace colorSpace)
+void GraphicsContext::setStrokeColor(const Color& color)
{
m_state.strokeColor = color;
- m_state.strokeColorSpace = colorSpace;
- m_state.strokeGradient.clear();
- m_state.strokePattern.clear();
- setPlatformStrokeColor(color, colorSpace);
+ m_state.strokeGradient = nullptr;
+ m_state.strokePattern = nullptr;
+ if (isRecording()) {
+ m_displayListRecorder->updateState(m_state, GraphicsContextState::StrokeColorChange);
+ return;
+ }
+ setPlatformStrokeColor(color);
}
-void GraphicsContext::setShadow(const FloatSize& offset, float blur, const Color& color, ColorSpace colorSpace)
+void GraphicsContext::setShadow(const FloatSize& offset, float blur, const Color& color)
{
m_state.shadowOffset = offset;
m_state.shadowBlur = blur;
m_state.shadowColor = color;
- m_state.shadowColorSpace = colorSpace;
- setPlatformShadow(offset, blur, color, colorSpace);
+#if USE(CG)
+ m_state.shadowsUseLegacyRadius = false;
+#endif
+ if (isRecording()) {
+ m_displayListRecorder->updateState(m_state, GraphicsContextState::ShadowChange);
+ return;
+ }
+ setPlatformShadow(offset, blur, color);
}
-void GraphicsContext::setLegacyShadow(const FloatSize& offset, float blur, const Color& color, ColorSpace colorSpace)
+void GraphicsContext::setLegacyShadow(const FloatSize& offset, float blur, const Color& color)
{
m_state.shadowOffset = offset;
m_state.shadowBlur = blur;
m_state.shadowColor = color;
- m_state.shadowColorSpace = colorSpace;
#if USE(CG)
m_state.shadowsUseLegacyRadius = true;
#endif
- setPlatformShadow(offset, blur, color, colorSpace);
+ if (isRecording()) {
+ m_displayListRecorder->updateState(m_state, GraphicsContextState::ShadowChange);
+ return;
+ }
+ setPlatformShadow(offset, blur, color);
}
void GraphicsContext::clearShadow()
@@ -193,31 +472,26 @@ void GraphicsContext::clearShadow()
m_state.shadowOffset = FloatSize();
m_state.shadowBlur = 0;
m_state.shadowColor = Color();
- m_state.shadowColorSpace = ColorSpaceDeviceRGB;
- clearPlatformShadow();
-}
+#if USE(CG)
+ m_state.shadowsUseLegacyRadius = false;
+#endif
-bool GraphicsContext::hasShadow() const
-{
- return m_state.shadowColor.isValid() && m_state.shadowColor.alpha()
- && (m_state.shadowBlur || m_state.shadowOffset.width() || m_state.shadowOffset.height());
+ if (isRecording()) {
+ m_displayListRecorder->clearShadow();
+ return;
+ }
+ clearPlatformShadow();
}
-bool GraphicsContext::getShadow(FloatSize& offset, float& blur, Color& color, ColorSpace& colorSpace) const
+bool GraphicsContext::getShadow(FloatSize& offset, float& blur, Color& color) const
{
offset = m_state.shadowOffset;
blur = m_state.shadowBlur;
color = m_state.shadowColor;
- colorSpace = m_state.shadowColorSpace;
return hasShadow();
}
-bool GraphicsContext::hasBlurredShadow() const
-{
- return m_state.shadowColor.isValid() && m_state.shadowColor.alpha() && m_state.shadowBlur;
-}
-
#if USE(CAIRO)
bool GraphicsContext::mustUseShadowBlur() const
{
@@ -236,273 +510,179 @@ bool GraphicsContext::mustUseShadowBlur() const
}
#endif
-float GraphicsContext::strokeThickness() const
-{
- return m_state.strokeThickness;
-}
-
-StrokeStyle GraphicsContext::strokeStyle() const
-{
- return m_state.strokeStyle;
-}
-
-Color GraphicsContext::strokeColor() const
-{
- return m_state.strokeColor;
-}
-
-ColorSpace GraphicsContext::strokeColorSpace() const
-{
- return m_state.strokeColorSpace;
-}
-
-WindRule GraphicsContext::fillRule() const
-{
- return m_state.fillRule;
-}
-
-void GraphicsContext::setFillRule(WindRule fillRule)
-{
- m_state.fillRule = fillRule;
-}
-
-void GraphicsContext::setFillColor(const Color& color, ColorSpace colorSpace)
+void GraphicsContext::setFillColor(const Color& color)
{
m_state.fillColor = color;
- m_state.fillColorSpace = colorSpace;
- m_state.fillGradient.clear();
- m_state.fillPattern.clear();
- setPlatformFillColor(color, colorSpace);
-}
+ m_state.fillGradient = nullptr;
+ m_state.fillPattern = nullptr;
-Color GraphicsContext::fillColor() const
-{
- return m_state.fillColor;
-}
-
-ColorSpace GraphicsContext::fillColorSpace() const
-{
- return m_state.fillColorSpace;
-}
-
-void GraphicsContext::setShouldAntialias(bool b)
-{
- m_state.shouldAntialias = b;
- setPlatformShouldAntialias(b);
-}
-
-bool GraphicsContext::shouldAntialias() const
-{
- return m_state.shouldAntialias;
-}
+ if (isRecording()) {
+ m_displayListRecorder->updateState(m_state, GraphicsContextState::FillColorChange);
+ return;
+ }
-void GraphicsContext::setShouldSmoothFonts(bool b)
-{
- m_state.shouldSmoothFonts = b;
- setPlatformShouldSmoothFonts(b);
+ setPlatformFillColor(color);
}
-bool GraphicsContext::shouldSmoothFonts() const
+void GraphicsContext::setShadowsIgnoreTransforms(bool shadowsIgnoreTransforms)
{
- return m_state.shouldSmoothFonts;
+ m_state.shadowsIgnoreTransforms = shadowsIgnoreTransforms;
+ if (isRecording())
+ m_displayListRecorder->updateState(m_state, GraphicsContextState::ShadowsIgnoreTransformsChange);
}
-void GraphicsContext::setShouldSubpixelQuantizeFonts(bool b)
+void GraphicsContext::setShouldAntialias(bool shouldAntialias)
{
- m_state.shouldSubpixelQuantizeFonts = b;
-}
+ m_state.shouldAntialias = shouldAntialias;
-bool GraphicsContext::shouldSubpixelQuantizeFonts() const
-{
- return m_state.shouldSubpixelQuantizeFonts;
-}
+ if (isRecording()) {
+ m_displayListRecorder->updateState(m_state, GraphicsContextState::ShouldAntialiasChange);
+ return;
+ }
-const GraphicsContextState& GraphicsContext::state() const
-{
- return m_state;
+ setPlatformShouldAntialias(shouldAntialias);
}
-void GraphicsContext::setStrokePattern(PassRefPtr<Pattern> pattern)
+void GraphicsContext::setShouldSmoothFonts(bool shouldSmoothFonts)
{
- ASSERT(pattern);
- if (!pattern) {
- setStrokeColor(Color::black, ColorSpaceDeviceRGB);
+ m_state.shouldSmoothFonts = shouldSmoothFonts;
+
+ if (isRecording()) {
+ m_displayListRecorder->updateState(m_state, GraphicsContextState::ShouldSmoothFontsChange);
return;
}
- m_state.strokeGradient.clear();
- m_state.strokePattern = pattern;
+
+ setPlatformShouldSmoothFonts(shouldSmoothFonts);
}
-void GraphicsContext::setFillPattern(PassRefPtr<Pattern> pattern)
+void GraphicsContext::setShouldSubpixelQuantizeFonts(bool shouldSubpixelQuantizeFonts)
{
- ASSERT(pattern);
- if (!pattern) {
- setFillColor(Color::black, ColorSpaceDeviceRGB);
- return;
- }
- m_state.fillGradient.clear();
- m_state.fillPattern = pattern;
+ m_state.shouldSubpixelQuantizeFonts = shouldSubpixelQuantizeFonts;
+ if (isRecording())
+ m_displayListRecorder->updateState(m_state, GraphicsContextState::ShouldSubpixelQuantizeFontsChange);
}
-void GraphicsContext::setStrokeGradient(PassRefPtr<Gradient> gradient)
+void GraphicsContext::setImageInterpolationQuality(InterpolationQuality imageInterpolationQuality)
{
- ASSERT(gradient);
- if (!gradient) {
- setStrokeColor(Color::black, ColorSpaceDeviceRGB);
+ m_state.imageInterpolationQuality = imageInterpolationQuality;
+
+ if (paintingDisabled())
return;
- }
- m_state.strokeGradient = gradient;
- m_state.strokePattern.clear();
-}
-void GraphicsContext::setFillGradient(PassRefPtr<Gradient> gradient)
-{
- ASSERT(gradient);
- if (!gradient) {
- setFillColor(Color::black, ColorSpaceDeviceRGB);
+ if (isRecording()) {
+ m_displayListRecorder->updateState(m_state, GraphicsContextState::ImageInterpolationQualityChange);
return;
}
- m_state.fillGradient = gradient;
- m_state.fillPattern.clear();
-}
-Gradient* GraphicsContext::fillGradient() const
-{
- return m_state.fillGradient.get();
+ setPlatformImageInterpolationQuality(imageInterpolationQuality);
}
-Gradient* GraphicsContext::strokeGradient() const
+void GraphicsContext::setStrokePattern(Ref<Pattern>&& pattern)
{
- return m_state.strokeGradient.get();
+ m_state.strokeGradient = nullptr;
+ m_state.strokePattern = WTFMove(pattern);
+ if (isRecording())
+ m_displayListRecorder->updateState(m_state, GraphicsContextState::StrokePatternChange);
}
-Pattern* GraphicsContext::fillPattern() const
+void GraphicsContext::setFillPattern(Ref<Pattern>&& pattern)
{
- return m_state.fillPattern.get();
+ m_state.fillGradient = nullptr;
+ m_state.fillPattern = WTFMove(pattern);
+ if (isRecording())
+ m_displayListRecorder->updateState(m_state, GraphicsContextState::FillPatternChange);
}
-Pattern* GraphicsContext::strokePattern() const
+void GraphicsContext::setStrokeGradient(Ref<Gradient>&& gradient)
{
- return m_state.strokePattern.get();
+ m_state.strokeGradient = WTFMove(gradient);
+ m_state.strokePattern = nullptr;
+ if (isRecording())
+ m_displayListRecorder->updateState(m_state, GraphicsContextState::StrokeGradientChange);
}
-void GraphicsContext::setShadowsIgnoreTransforms(bool ignoreTransforms)
+void GraphicsContext::setFillRule(WindRule fillRule)
{
- m_state.shadowsIgnoreTransforms = ignoreTransforms;
+ m_state.fillRule = fillRule;
+ if (isRecording())
+ m_displayListRecorder->updateState(m_state, GraphicsContextState::FillRuleChange);
}
-bool GraphicsContext::shadowsIgnoreTransforms() const
+void GraphicsContext::setFillGradient(Ref<Gradient>&& gradient)
{
- return m_state.shadowsIgnoreTransforms;
+ m_state.fillGradient = WTFMove(gradient);
+ m_state.fillPattern = nullptr;
+ if (isRecording())
+ m_displayListRecorder->updateState(m_state, GraphicsContextState::FillGradientChange); // FIXME: also fill pattern?
}
void GraphicsContext::beginTransparencyLayer(float opacity)
{
+ if (isRecording()) {
+ m_displayListRecorder->beginTransparencyLayer(opacity);
+ return;
+ }
beginPlatformTransparencyLayer(opacity);
++m_transparencyCount;
}
void GraphicsContext::endTransparencyLayer()
{
+ if (isRecording()) {
+ m_displayListRecorder->endTransparencyLayer();
+ return;
+ }
endPlatformTransparencyLayer();
ASSERT(m_transparencyCount > 0);
--m_transparencyCount;
}
-bool GraphicsContext::isInTransparencyLayer() const
-{
- return (m_transparencyCount > 0) && supportsTransparencyLayers();
-}
-
-bool GraphicsContext::updatingControlTints() const
-{
- return m_updatingControlTints;
-}
-
-void GraphicsContext::setUpdatingControlTints(bool b)
-{
- setPaintingDisabled(b);
- m_updatingControlTints = b;
-}
-
-void GraphicsContext::setPaintingDisabled(bool f)
+float GraphicsContext::drawText(const FontCascade& font, const TextRun& run, const FloatPoint& point, unsigned from, std::optional<unsigned> to)
{
- m_state.paintingDisabled = f;
-}
+ if (paintingDisabled())
+ return 0;
-bool GraphicsContext::paintingDisabled() const
-{
- return m_state.paintingDisabled;
+ // Display list recording for text content is done at glyphs level. See GraphicsContext::drawGlyphs.
+ return font.drawText(*this, run, point, from, to);
}
-// FIXME: Replace the non-iOS implementation with the iOS implementation since the latter computes returns
-// the width of the drawn text. Ensure that there aren't noticeable differences in layout.
-#if !PLATFORM(IOS)
-#if !USE(WINGDI)
-void GraphicsContext::drawText(const Font& font, const TextRun& run, const FloatPoint& point, int from, int to)
+void GraphicsContext::drawGlyphs(const FontCascade& fontCascade, const Font& font, const GlyphBuffer& buffer, unsigned from, unsigned numGlyphs, const FloatPoint& point)
{
if (paintingDisabled())
return;
- font.drawText(this, run, point, from, to);
-}
-#endif
-#else
-float GraphicsContext::drawText(const Font& font, const TextRun& run, const FloatPoint& point, int from, int to)
-{
- if (paintingDisabled())
- return 0;
+ if (isRecording()) {
+ m_displayListRecorder->drawGlyphs(font, buffer, from, numGlyphs, point, fontCascade.fontDescription().fontSmoothing());
+ return;
+ }
- return font.drawText(this, run, point, from, to);
+ fontCascade.drawGlyphs(*this, font, buffer, from, numGlyphs, point, fontCascade.fontDescription().fontSmoothing());
}
-#endif // !PLATFORM(IOS)
-void GraphicsContext::drawEmphasisMarks(const Font& font, const TextRun& run, const AtomicString& mark, const FloatPoint& point, int from, int to)
+void GraphicsContext::drawEmphasisMarks(const FontCascade& font, const TextRun& run, const AtomicString& mark, const FloatPoint& point, unsigned from, std::optional<unsigned> to)
{
if (paintingDisabled())
return;
- font.drawEmphasisMarks(this, run, mark, point, from, to);
+ font.drawEmphasisMarks(*this, run, mark, point, from, to);
}
-// FIXME: Better merge the iOS and non-iOS differences. In particular, make this method use the
-// returned width of the drawn text, Font::drawText(), instead of computing it. Ensure that there
-// aren't noticeable differences in layout with such a change.
-#if !PLATFORM(IOS)
-void GraphicsContext::drawBidiText(const Font& font, const TextRun& run, const FloatPoint& point, Font::CustomFontNotReadyAction customFontNotReadyAction)
-#else
-float GraphicsContext::drawBidiText(const Font& font, const TextRun& run, const FloatPoint& point, Font::CustomFontNotReadyAction customFontNotReadyAction, BidiStatus* status, int length)
-#endif
+void GraphicsContext::drawBidiText(const FontCascade& font, const TextRun& run, const FloatPoint& point, FontCascade::CustomFontNotReadyAction customFontNotReadyAction)
{
if (paintingDisabled())
-#if !PLATFORM(IOS)
return;
-#else
- return 0;
-#endif
BidiResolver<TextRunIterator, BidiCharacterRun> bidiResolver;
-#if !PLATFORM(IOS)
bidiResolver.setStatus(BidiStatus(run.direction(), run.directionalOverride()));
-#else
- bidiResolver.setStatus(status ? *status : BidiStatus(run.direction(), run.directionalOverride()));
-#endif
bidiResolver.setPositionIgnoringNestedIsolates(TextRunIterator(&run, 0));
// FIXME: This ownership should be reversed. We should pass BidiRunList
// to BidiResolver in createBidiRunsForLine.
BidiRunList<BidiCharacterRun>& bidiRuns = bidiResolver.runs();
-#if !PLATFORM(IOS)
bidiResolver.createBidiRunsForLine(TextRunIterator(&run, run.length()));
-#else
- bidiResolver.createBidiRunsForLine(TextRunIterator(&run, length < 0 ? run.length() : length));
-#endif
+
if (!bidiRuns.runCount())
-#if !PLATFORM(IOS)
return;
-#else
- return 0;
-#endif
FloatPoint currPoint = point;
BidiCharacterRun* bidiRun = bidiRuns.firstRun();
@@ -512,203 +692,135 @@ float GraphicsContext::drawBidiText(const Font& font, const TextRun& run, const
subrun.setDirection(isRTL ? RTL : LTR);
subrun.setDirectionalOverride(bidiRun->dirOverride(false));
-#if !PLATFORM(IOS)
- font.drawText(this, subrun, currPoint, 0, -1, customFontNotReadyAction);
-
- bidiRun = bidiRun->next();
- // FIXME: Have Font::drawText return the width of what it drew so that we don't have to re-measure here.
- if (bidiRun)
- currPoint.move(font.width(subrun), 0);
-#else
- float width = font.drawText(this, subrun, currPoint, 0, -1, customFontNotReadyAction);
+ float width = font.drawText(*this, subrun, currPoint, 0, std::nullopt, customFontNotReadyAction);
currPoint.move(width, 0);
bidiRun = bidiRun->next();
-#endif
}
-#if PLATFORM(IOS)
- if (status)
- *status = bidiResolver.status();
-#endif
- bidiRuns.deleteRuns();
-
-#if PLATFORM(IOS)
- return currPoint.x() - static_cast<float>(point.x());
-#endif
+ bidiRuns.clear();
}
-void GraphicsContext::drawHighlightForText(const Font& font, const TextRun& run, const FloatPoint& point, int h, const Color& backgroundColor, ColorSpace colorSpace, int from, int to)
+void GraphicsContext::drawImage(Image& image, const FloatPoint& destination, const ImagePaintingOptions& imagePaintingOptions)
{
- if (paintingDisabled())
- return;
-
- fillRect(font.selectionRectForText(run, point, h, from, to), backgroundColor, colorSpace);
+ drawImage(image, FloatRect(destination, image.size()), FloatRect(FloatPoint(), image.size()), imagePaintingOptions);
}
-void GraphicsContext::drawImage(Image* image, ColorSpace styleColorSpace, const IntPoint& p, CompositeOperator op, ImageOrientationDescription description)
+void GraphicsContext::drawImage(Image& image, const FloatRect& destination, const ImagePaintingOptions& imagePaintingOptions)
{
- if (!image)
- return;
- drawImage(image, styleColorSpace, FloatRect(IntRect(p, image->size())), FloatRect(FloatPoint(), FloatSize(image->size())), op, description);
+#if PLATFORM(IOS)
+ FloatRect srcRect(FloatPoint(), image.originalSize());
+#else
+ FloatRect srcRect(FloatPoint(), image.size());
+#endif
+
+ drawImage(image, destination, srcRect, imagePaintingOptions);
}
-void GraphicsContext::drawImage(Image* image, ColorSpace styleColorSpace, const IntRect& r, CompositeOperator op, ImageOrientationDescription description, bool useLowQualityScale)
+void GraphicsContext::drawImage(Image& image, const FloatRect& destination, const FloatRect& source, const ImagePaintingOptions& imagePaintingOptions)
{
- if (!image)
+ if (paintingDisabled())
return;
- drawImage(image, styleColorSpace, FloatRect(r), FloatRect(FloatPoint(), FloatSize(image->size())), op, description, useLowQualityScale);
-}
-void GraphicsContext::drawImage(Image* image, ColorSpace styleColorSpace, const IntPoint& dest, const IntRect& srcRect, CompositeOperator op, ImageOrientationDescription description)
-{
- drawImage(image, styleColorSpace, FloatRect(IntRect(dest, srcRect.size())), FloatRect(srcRect), op, description);
-}
+ if (isRecording()) {
+ m_displayListRecorder->drawImage(image, destination, source, imagePaintingOptions);
+ return;
+ }
-void GraphicsContext::drawImage(Image* image, ColorSpace styleColorSpace, const FloatRect& dest, const FloatRect& src, CompositeOperator op, ImageOrientationDescription description, bool useLowQualityScale)
-{
- drawImage(image, styleColorSpace, dest, src, op, BlendModeNormal, description, useLowQualityScale);
+ InterpolationQualityMaintainer interpolationQualityForThisScope(*this, imagePaintingOptions.m_interpolationQuality);
+ image.draw(*this, destination, source, imagePaintingOptions.m_compositeOperator, imagePaintingOptions.m_blendMode, imagePaintingOptions.m_orientationDescription);
}
-void GraphicsContext::drawImage(Image* image, ColorSpace styleColorSpace, const FloatRect& dest)
+void GraphicsContext::drawTiledImage(Image& image, const FloatRect& destination, const FloatPoint& source, const FloatSize& tileSize, const FloatSize& spacing, const ImagePaintingOptions& imagePaintingOptions)
{
- if (!image)
+ if (paintingDisabled())
return;
- drawImage(image, styleColorSpace, dest, FloatRect(IntRect(IntPoint(), image->size())));
-}
-void GraphicsContext::drawImage(Image* image, ColorSpace styleColorSpace, const FloatRect& dest, const FloatRect& src, CompositeOperator op, BlendMode blendMode, ImageOrientationDescription description, bool useLowQualityScale)
-{ if (paintingDisabled() || !image)
+ if (isRecording()) {
+ m_displayListRecorder->drawTiledImage(image, destination, source, tileSize, spacing, imagePaintingOptions);
return;
-
- InterpolationQuality previousInterpolationQuality = InterpolationDefault;
-
- if (useLowQualityScale) {
- previousInterpolationQuality = imageInterpolationQuality();
- // FIXME (49002): Should be InterpolationLow
- setImageInterpolationQuality(InterpolationNone);
}
- image->draw(this, dest, src, styleColorSpace, op, blendMode, description);
-
- if (useLowQualityScale)
- setImageInterpolationQuality(previousInterpolationQuality);
+ InterpolationQualityMaintainer interpolationQualityForThisScope(*this, imagePaintingOptions.m_interpolationQuality);
+ image.drawTiled(*this, destination, source, tileSize, spacing, imagePaintingOptions.m_compositeOperator, imagePaintingOptions.m_blendMode);
}
-void GraphicsContext::drawTiledImage(Image* image, ColorSpace styleColorSpace, const IntRect& destRect, const IntPoint& srcPoint, const IntSize& tileSize, CompositeOperator op, bool useLowQualityScale, BlendMode blendMode)
+void GraphicsContext::drawTiledImage(Image& image, const FloatRect& destination, const FloatRect& source, const FloatSize& tileScaleFactor,
+ Image::TileRule hRule, Image::TileRule vRule, const ImagePaintingOptions& imagePaintingOptions)
{
- if (paintingDisabled() || !image)
+ if (paintingDisabled())
return;
- if (useLowQualityScale) {
- InterpolationQuality previousInterpolationQuality = imageInterpolationQuality();
- setImageInterpolationQuality(InterpolationLow);
- image->drawTiled(this, destRect, srcPoint, tileSize, styleColorSpace, op, blendMode);
- setImageInterpolationQuality(previousInterpolationQuality);
- } else
- image->drawTiled(this, destRect, srcPoint, tileSize, styleColorSpace, op, blendMode);
-}
-
-void GraphicsContext::drawTiledImage(Image* image, ColorSpace styleColorSpace, const IntRect& dest, const IntRect& srcRect,
- const FloatSize& tileScaleFactor, Image::TileRule hRule, Image::TileRule vRule, CompositeOperator op, bool useLowQualityScale)
-{
- if (paintingDisabled() || !image)
+ if (isRecording()) {
+ m_displayListRecorder->drawTiledImage(image, destination, source, tileScaleFactor, hRule, vRule, imagePaintingOptions);
return;
+ }
if (hRule == Image::StretchTile && vRule == Image::StretchTile) {
// Just do a scale.
- drawImage(image, styleColorSpace, dest, srcRect, op);
+ drawImage(image, destination, source, imagePaintingOptions);
return;
}
- if (useLowQualityScale) {
- InterpolationQuality previousInterpolationQuality = imageInterpolationQuality();
- setImageInterpolationQuality(InterpolationLow);
- image->drawTiled(this, dest, srcRect, tileScaleFactor, hRule, vRule, styleColorSpace, op);
- setImageInterpolationQuality(previousInterpolationQuality);
- } else
- image->drawTiled(this, dest, srcRect, tileScaleFactor, hRule, vRule, styleColorSpace, op);
+ InterpolationQualityMaintainer interpolationQualityForThisScope(*this, imagePaintingOptions.m_interpolationQuality);
+ image.drawTiled(*this, destination, source, tileScaleFactor, hRule, vRule, imagePaintingOptions.m_compositeOperator);
}
-void GraphicsContext::drawImageBuffer(ImageBuffer* image, ColorSpace styleColorSpace, const IntPoint& p, CompositeOperator op, BlendMode blendMode)
+void GraphicsContext::drawImageBuffer(ImageBuffer& image, const FloatPoint& destination, const ImagePaintingOptions& imagePaintingOptions)
{
- if (!image)
- return;
- drawImageBuffer(image, styleColorSpace, FloatRect(IntRect(p, image->logicalSize())), FloatRect(FloatPoint(), FloatSize(image->logicalSize())), op, blendMode);
+ drawImageBuffer(image, FloatRect(destination, image.logicalSize()), FloatRect(FloatPoint(), image.logicalSize()), imagePaintingOptions);
}
-void GraphicsContext::drawImageBuffer(ImageBuffer* image, ColorSpace styleColorSpace, const IntRect& r, CompositeOperator op, BlendMode blendMode, bool useLowQualityScale)
+void GraphicsContext::drawImageBuffer(ImageBuffer& image, const FloatRect& destination, const ImagePaintingOptions& imagePaintingOptions)
{
- if (!image)
- return;
- drawImageBuffer(image, styleColorSpace, FloatRect(r), FloatRect(FloatPoint(), FloatSize(image->logicalSize())), op, blendMode, useLowQualityScale);
+ drawImageBuffer(image, destination, FloatRect(FloatPoint(), FloatSize(image.logicalSize())), imagePaintingOptions);
}
-void GraphicsContext::drawImageBuffer(ImageBuffer* image, ColorSpace styleColorSpace, const IntPoint& dest, const IntRect& srcRect, CompositeOperator op, BlendMode blendMode)
+void GraphicsContext::drawImageBuffer(ImageBuffer& image, const FloatRect& destination, const FloatRect& source, const ImagePaintingOptions& imagePaintingOptions)
{
- drawImageBuffer(image, styleColorSpace, FloatRect(IntRect(dest, srcRect.size())), FloatRect(srcRect), op, blendMode);
-}
+ if (paintingDisabled())
+ return;
-void GraphicsContext::drawImageBuffer(ImageBuffer* image, ColorSpace styleColorSpace, const IntRect& dest, const IntRect& srcRect, CompositeOperator op, BlendMode blendMode, bool useLowQualityScale)
-{
- drawImageBuffer(image, styleColorSpace, FloatRect(dest), FloatRect(srcRect), op, blendMode, useLowQualityScale);
+ InterpolationQualityMaintainer interpolationQualityForThisScope(*this, imagePaintingOptions.m_interpolationQuality);
+ image.draw(*this, destination, source, imagePaintingOptions.m_compositeOperator, imagePaintingOptions.m_blendMode);
}
-void GraphicsContext::drawImageBuffer(ImageBuffer* image, ColorSpace styleColorSpace, const FloatRect& dest)
+void GraphicsContext::drawConsumingImageBuffer(std::unique_ptr<ImageBuffer> image, const FloatPoint& destination, const ImagePaintingOptions& imagePaintingOptions)
{
if (!image)
return;
- drawImageBuffer(image, styleColorSpace, dest, FloatRect(IntRect(IntPoint(), image->logicalSize())));
+ IntSize imageLogicalSize = image->logicalSize();
+ drawConsumingImageBuffer(WTFMove(image), FloatRect(destination, imageLogicalSize), FloatRect(FloatPoint(), imageLogicalSize), imagePaintingOptions);
}
-void GraphicsContext::drawImageBuffer(ImageBuffer* image, ColorSpace styleColorSpace, const FloatRect& dest, const FloatRect& src, CompositeOperator op, BlendMode blendMode, bool useLowQualityScale)
+void GraphicsContext::drawConsumingImageBuffer(std::unique_ptr<ImageBuffer> image, const FloatRect& destination, const ImagePaintingOptions& imagePaintingOptions)
{
- if (paintingDisabled() || !image)
+ if (!image)
return;
-
- if (useLowQualityScale) {
- InterpolationQuality previousInterpolationQuality = imageInterpolationQuality();
- // FIXME (49002): Should be InterpolationLow
- setImageInterpolationQuality(InterpolationNone);
- image->draw(this, styleColorSpace, dest, src, op, blendMode, useLowQualityScale);
- setImageInterpolationQuality(previousInterpolationQuality);
- } else
- image->draw(this, styleColorSpace, dest, src, op, blendMode, useLowQualityScale);
-}
-
-void GraphicsContext::clip(const IntRect& rect)
-{
- clip(FloatRect(rect));
+ IntSize imageLogicalSize = image->logicalSize();
+ drawConsumingImageBuffer(WTFMove(image), destination, FloatRect(FloatPoint(), FloatSize(imageLogicalSize)), imagePaintingOptions);
}
-void GraphicsContext::clipRoundedRect(const RoundedRect& rect)
+void GraphicsContext::drawConsumingImageBuffer(std::unique_ptr<ImageBuffer> image, const FloatRect& destination, const FloatRect& source, const ImagePaintingOptions& imagePaintingOptions)
{
- if (paintingDisabled())
- return;
-
- if (!rect.isRounded()) {
- clip(rect.rect());
+ if (paintingDisabled() || !image)
return;
- }
-
- Path path;
- path.addRoundedRect(rect);
- clip(path);
+
+ InterpolationQualityMaintainer interpolationQualityForThisScope(*this, imagePaintingOptions.m_interpolationQuality);
+ ImageBuffer::drawConsuming(WTFMove(image), *this, destination, source, imagePaintingOptions.m_compositeOperator, imagePaintingOptions.m_blendMode);
}
-// FIXME: Consider writing this in terms of a specialized RoundedRect that uses FloatRect and FloatSize radii.
-void GraphicsContext::clipRoundedRect(const FloatRect& rect, const FloatSize& topLeft, const FloatSize& topRight,
- const FloatSize& bottomLeft, const FloatSize& bottomRight)
+void GraphicsContext::clipRoundedRect(const FloatRoundedRect& rect)
{
if (paintingDisabled())
return;
Path path;
- path.addRoundedRect(rect, topLeft, topRight, bottomLeft, bottomRight);
- clip(path);
+ path.addRoundedRect(rect);
+ clipPath(path);
}
-void GraphicsContext::clipOutRoundedRect(const RoundedRect& rect)
+void GraphicsContext::clipOutRoundedRect(const FloatRoundedRect& rect)
{
if (paintingDisabled())
return;
@@ -723,14 +835,7 @@ void GraphicsContext::clipOutRoundedRect(const RoundedRect& rect)
clipOut(path);
}
-void GraphicsContext::clipToImageBuffer(ImageBuffer* buffer, const FloatRect& rect)
-{
- if (paintingDisabled())
- return;
- buffer->clip(this, rect);
-}
-
-#if !USE(CG) && !USE(CAIRO)
+#if !USE(CG) && !USE(DIRECT2D) && !USE(CAIRO)
IntRect GraphicsContext::clipBounds() const
{
ASSERT_NOT_REACHED();
@@ -738,16 +843,16 @@ IntRect GraphicsContext::clipBounds() const
}
#endif
-TextDrawingModeFlags GraphicsContext::textDrawingMode() const
-{
- return m_state.textDrawingMode;
-}
-
void GraphicsContext::setTextDrawingMode(TextDrawingModeFlags mode)
{
m_state.textDrawingMode = mode;
if (paintingDisabled())
return;
+
+ if (isRecording()) {
+ m_displayListRecorder->updateState(m_state, GraphicsContextState::TextDrawingModeChange);
+ return;
+ }
setPlatformTextDrawingMode(mode);
}
@@ -755,33 +860,51 @@ void GraphicsContext::fillRect(const FloatRect& rect, Gradient& gradient)
{
if (paintingDisabled())
return;
+
+ if (isRecording()) {
+ m_displayListRecorder->fillRect(rect, gradient);
+ return;
+ }
+
gradient.fill(this, rect);
}
-void GraphicsContext::fillRect(const FloatRect& rect, const Color& color, ColorSpace styleColorSpace, CompositeOperator op, BlendMode blendMode)
+void GraphicsContext::fillRect(const FloatRect& rect, const Color& color, CompositeOperator op, BlendMode blendMode)
{
if (paintingDisabled())
return;
+ if (isRecording()) {
+ m_displayListRecorder->fillRect(rect, color, op, blendMode);
+ return;
+ }
+
CompositeOperator previousOperator = compositeOperation();
setCompositeOperation(op, blendMode);
- fillRect(rect, color, styleColorSpace);
+ fillRect(rect, color);
setCompositeOperation(previousOperator);
}
-void GraphicsContext::fillRoundedRect(const RoundedRect& rect, const Color& color, ColorSpace colorSpace, BlendMode blendMode)
+void GraphicsContext::fillRoundedRect(const FloatRoundedRect& rect, const Color& color, BlendMode blendMode)
{
+ if (paintingDisabled())
+ return;
+
+ if (isRecording()) {
+ m_displayListRecorder->fillRoundedRect(rect, color, blendMode);
+ return;
+ }
if (rect.isRounded()) {
setCompositeOperation(compositeOperation(), blendMode);
- fillRoundedRect(rect.rect(), rect.radii().topLeft(), rect.radii().topRight(), rect.radii().bottomLeft(), rect.radii().bottomRight(), color, colorSpace);
+ platformFillRoundedRect(rect, color);
setCompositeOperation(compositeOperation());
} else
- fillRect(rect.rect(), color, colorSpace, compositeOperation(), blendMode);
+ fillRect(rect.rect(), color, compositeOperation(), blendMode);
}
-#if !USE(CG) && !USE(CAIRO)
-void GraphicsContext::fillRectWithRoundedHole(const IntRect& rect, const RoundedRect& roundedHoleRect, const Color& color, ColorSpace colorSpace)
+#if !USE(CG) && !USE(DIRECT2D) && !USE(CAIRO)
+void GraphicsContext::fillRectWithRoundedHole(const IntRect& rect, const FloatRoundedRect& roundedHoleRect, const Color& color)
{
if (paintingDisabled())
return;
@@ -796,78 +919,65 @@ void GraphicsContext::fillRectWithRoundedHole(const IntRect& rect, const Rounded
WindRule oldFillRule = fillRule();
Color oldFillColor = fillColor();
- ColorSpace oldFillColorSpace = fillColorSpace();
setFillRule(RULE_EVENODD);
- setFillColor(color, colorSpace);
+ setFillColor(color);
fillPath(path);
setFillRule(oldFillRule);
- setFillColor(oldFillColor, oldFillColorSpace);
+ setFillColor(oldFillColor);
}
#endif
+void GraphicsContext::setAlpha(float alpha)
+{
+ m_state.alpha = alpha;
+ if (isRecording()) {
+ m_displayListRecorder->updateState(m_state, GraphicsContextState::AlphaChange);
+ return;
+ }
+ setPlatformAlpha(alpha);
+}
+
void GraphicsContext::setCompositeOperation(CompositeOperator compositeOperation, BlendMode blendMode)
{
m_state.compositeOperator = compositeOperation;
m_state.blendMode = blendMode;
+ if (isRecording()) {
+ m_displayListRecorder->updateState(m_state, GraphicsContextState::CompositeOperationChange);
+ return;
+ }
setPlatformCompositeOperation(compositeOperation, blendMode);
}
-CompositeOperator GraphicsContext::compositeOperation() const
-{
- return m_state.compositeOperator;
-}
-
-BlendMode GraphicsContext::blendModeOperation() const
-{
- return m_state.blendMode;
-}
-
-#if PLATFORM(IOS)
-bool GraphicsContext::emojiDrawingEnabled()
-{
- return m_state.emojiDrawingEnabled;
-}
-
-void GraphicsContext::setEmojiDrawingEnabled(bool emojiDrawingEnabled)
-{
- m_state.emojiDrawingEnabled = emojiDrawingEnabled;
-}
-#endif
-
void GraphicsContext::setDrawLuminanceMask(bool drawLuminanceMask)
{
m_state.drawLuminanceMask = drawLuminanceMask;
+ if (isRecording())
+ m_displayListRecorder->updateState(m_state, GraphicsContextState::DrawLuminanceMaskChange);
}
-bool GraphicsContext::drawLuminanceMask() const
-{
- return m_state.drawLuminanceMask;
-}
-
-#if !USE(CG)
-// Implement this if you want to go ahead and push the drawing mode into your native context
-// immediately.
+#if !USE(CG) && !USE(DIRECT2D)
+// Implement this if you want to go push the drawing mode into your native context immediately.
void GraphicsContext::setPlatformTextDrawingMode(TextDrawingModeFlags)
{
}
#endif
-#if !USE(CAIRO)
+#if !USE(CAIRO) && !USE(DIRECT2D)
void GraphicsContext::setPlatformStrokeStyle(StrokeStyle)
{
}
#endif
-#if !USE(CG)
+#if !USE(CG) && !USE(DIRECT2D)
void GraphicsContext::setPlatformShouldSmoothFonts(bool)
{
}
#endif
-#if !USE(CG) && !USE(CAIRO)
+#if !USE(CG) && !USE(DIRECT2D) && !USE(CAIRO)
bool GraphicsContext::isAcceleratedContext() const
{
return false;
@@ -903,38 +1013,7 @@ void GraphicsContext::adjustLineToPixelBoundaries(FloatPoint& p1, FloatPoint& p2
}
}
-static bool scalesMatch(AffineTransform a, AffineTransform b)
-{
- return a.xScale() == b.xScale() && a.yScale() == b.yScale();
-}
-
-std::unique_ptr<ImageBuffer> GraphicsContext::createCompatibleBuffer(const IntSize& size, bool hasAlpha) const
-{
- // Make the buffer larger if the context's transform is scaling it so we need a higher
- // resolution than one pixel per unit. Also set up a corresponding scale factor on the
- // graphics context.
-
- AffineTransform transform = getCTM(DefinitelyIncludeDeviceScale);
- IntSize scaledSize(static_cast<int>(ceil(size.width() * transform.xScale())), static_cast<int>(ceil(size.height() * transform.yScale())));
-
- std::unique_ptr<ImageBuffer> buffer = ImageBuffer::createCompatibleBuffer(scaledSize, 1, ColorSpaceDeviceRGB, this, hasAlpha);
- if (!buffer)
- return nullptr;
-
- buffer->context()->scale(FloatSize(static_cast<float>(scaledSize.width()) / size.width(),
- static_cast<float>(scaledSize.height()) / size.height()));
-
- return buffer;
-}
-
-bool GraphicsContext::isCompatibleWithBuffer(ImageBuffer* buffer) const
-{
- GraphicsContext* bufferContext = buffer->context();
-
- return scalesMatch(getCTM(), bufferContext->getCTM()) && isAcceleratedContext() == bufferContext->isAcceleratedContext();
-}
-
-#if !USE(CG)
+#if !USE(CG) && !USE(DIRECT2D)
void GraphicsContext::platformApplyDeviceScaleFactor(float)
{
}
@@ -942,9 +1021,21 @@ void GraphicsContext::platformApplyDeviceScaleFactor(float)
void GraphicsContext::applyDeviceScaleFactor(float deviceScaleFactor)
{
- scale(FloatSize(deviceScaleFactor, deviceScaleFactor));
+ scale(deviceScaleFactor);
+
+ if (isRecording()) {
+ m_displayListRecorder->applyDeviceScaleFactor(deviceScaleFactor);
+ return;
+ }
+
platformApplyDeviceScaleFactor(deviceScaleFactor);
}
+
+FloatSize GraphicsContext::scaleFactor() const
+{
+ AffineTransform transform = getCTM(GraphicsContext::DefinitelyIncludeDeviceScale);
+ return FloatSize(transform.xScale(), transform.yScale());
+}
void GraphicsContext::fillEllipse(const FloatRect& ellipse)
{
@@ -970,7 +1061,7 @@ void GraphicsContext::strokeEllipseAsPath(const FloatRect& ellipse)
strokePath(path);
}
-#if !USE(CG)
+#if !USE(CG) && !USE(DIRECT2D)
void GraphicsContext::platformFillEllipse(const FloatRect& ellipse)
{
if (paintingDisabled())
@@ -988,4 +1079,117 @@ void GraphicsContext::platformStrokeEllipse(const FloatRect& ellipse)
}
#endif
+FloatRect GraphicsContext::computeUnderlineBoundsForText(const FloatPoint& point, float width, bool printing)
+{
+ Color dummyColor;
+ return computeLineBoundsAndAntialiasingModeForText(point, width, printing, dummyColor);
+}
+
+FloatRect GraphicsContext::computeLineBoundsAndAntialiasingModeForText(const FloatPoint& point, float width, bool printing, Color& color)
+{
+ FloatPoint origin = point;
+ float thickness = std::max(strokeThickness(), 0.5f);
+ if (printing)
+ return FloatRect(origin, FloatSize(width, thickness));
+
+ AffineTransform transform = getCTM(GraphicsContext::DefinitelyIncludeDeviceScale);
+ // Just compute scale in x dimension, assuming x and y scales are equal.
+ float scale = transform.b() ? sqrtf(transform.a() * transform.a() + transform.b() * transform.b()) : transform.a();
+ if (scale < 1.0) {
+ // This code always draws a line that is at least one-pixel line high,
+ // which tends to visually overwhelm text at small scales. To counter this
+ // effect, an alpha is applied to the underline color when text is at small scales.
+ static const float minimumUnderlineAlpha = 0.4f;
+ float shade = scale > minimumUnderlineAlpha ? scale : minimumUnderlineAlpha;
+ color = color.colorWithAlphaMultipliedBy(shade);
+ }
+
+ FloatPoint devicePoint = transform.mapPoint(point);
+ // Visual overflow might occur here due to integral roundf/ceilf. visualOverflowForDecorations adjusts the overflow value for underline decoration.
+ FloatPoint deviceOrigin = FloatPoint(roundf(devicePoint.x()), ceilf(devicePoint.y()));
+ if (auto inverse = transform.inverse())
+ origin = inverse.value().mapPoint(deviceOrigin);
+ return FloatRect(origin, FloatSize(width, thickness));
+}
+
+void GraphicsContext::applyState(const GraphicsContextState& state)
+{
+ setPlatformShadow(state.shadowOffset, state.shadowBlur, state.shadowColor);
+ setPlatformStrokeThickness(state.strokeThickness);
+ setPlatformTextDrawingMode(state.textDrawingMode);
+ setPlatformStrokeColor(state.strokeColor);
+ setPlatformFillColor(state.fillColor);
+ setPlatformStrokeStyle(state.strokeStyle);
+ setPlatformAlpha(state.alpha);
+ setPlatformCompositeOperation(state.compositeOperator, state.blendMode);
+ setPlatformShouldAntialias(state.shouldAntialias);
+ setPlatformShouldSmoothFonts(state.shouldSmoothFonts);
+}
+
+float GraphicsContext::dashedLineCornerWidthForStrokeWidth(float strokeWidth) const
+{
+ float thickness = strokeThickness();
+ return strokeStyle() == DottedStroke ? thickness : std::min(2.0f * thickness, std::max(thickness, strokeWidth / 3.0f));
+}
+
+float GraphicsContext::dashedLinePatternWidthForStrokeWidth(float strokeWidth) const
+{
+ float thickness = strokeThickness();
+ return strokeStyle() == DottedStroke ? thickness : std::min(3.0f * thickness, std::max(thickness, strokeWidth / 3.0f));
+}
+
+float GraphicsContext::dashedLinePatternOffsetForPatternAndStrokeWidth(float patternWidth, float strokeWidth) const
+{
+ // Pattern starts with full fill and ends with the empty fill.
+ // 1. Let's start with the empty phase after the corner.
+ // 2. Check if we've got odd or even number of patterns and whether they fully cover the line.
+ // 3. In case of even number of patterns and/or remainder, move the pattern start position
+ // so that the pattern is balanced between the corners.
+ float patternOffset = patternWidth;
+ int numberOfSegments = std::floor(strokeWidth / patternWidth);
+ bool oddNumberOfSegments = numberOfSegments % 2;
+ float remainder = strokeWidth - (numberOfSegments * patternWidth);
+ if (oddNumberOfSegments && remainder)
+ patternOffset -= remainder / 2.0f;
+ else if (!oddNumberOfSegments) {
+ if (remainder)
+ patternOffset += patternOffset - (patternWidth + remainder) / 2.0f;
+ else
+ patternOffset += patternWidth / 2.0f;
+ }
+
+ return patternOffset;
+}
+
+Vector<FloatPoint> GraphicsContext::centerLineAndCutOffCorners(bool isVerticalLine, float cornerWidth, FloatPoint point1, FloatPoint point2) const
+{
+ // Center line and cut off corners for pattern painting.
+ if (isVerticalLine) {
+ float centerOffset = (point2.x() - point1.x()) / 2.0f;
+ point1.move(centerOffset, cornerWidth);
+ point2.move(-centerOffset, -cornerWidth);
+ } else {
+ float centerOffset = (point2.y() - point1.y()) / 2.0f;
+ point1.move(cornerWidth, centerOffset);
+ point2.move(-cornerWidth, -centerOffset);
+ }
+
+ return { point1, point2 };
+}
+
+#if !USE(CG)
+bool GraphicsContext::supportsInternalLinks() const
+{
+ return false;
+}
+
+void GraphicsContext::setDestinationForRect(const String&, const FloatRect&)
+{
+}
+
+void GraphicsContext::addDestinationAtPoint(const String&, const FloatPoint&)
+{
+}
+#endif
+
}
diff --git a/Source/WebCore/platform/graphics/GraphicsContext.h b/Source/WebCore/platform/graphics/GraphicsContext.h
index a437827ac..82b96126c 100644
--- a/Source/WebCore/platform/graphics/GraphicsContext.h
+++ b/Source/WebCore/platform/graphics/GraphicsContext.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003, 2006, 2007, 2008, 2009, 2013 Apple Inc. All rights reserved.
+ * Copyright (C) 2003-2016 Apple Inc. All rights reserved.
* Copyright (C) 2008-2009 Torch Mobile, Inc.
*
* Redistribution and use in source and binary forms, with or without
@@ -11,10 +11,10 @@
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
- * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
@@ -24,23 +24,26 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef GraphicsContext_h
-#define GraphicsContext_h
+#pragma once
-#include "ColorSpace.h"
#include "DashArray.h"
#include "FloatRect.h"
-#include "Font.h"
+#include "FontCascade.h"
#include "Gradient.h"
+#include "GraphicsTypes.h"
#include "Image.h"
#include "ImageOrientation.h"
-#include "Path.h"
#include "Pattern.h"
#include <wtf/Noncopyable.h>
-#include <wtf/PassOwnPtr.h>
#if USE(CG)
typedef struct CGContext PlatformGraphicsContext;
+#elif USE(DIRECT2D)
+interface ID2D1DCRenderTarget;
+interface ID2D1RenderTarget;
+interface ID2D1Factory;
+interface ID2D1SolidColorBrush;
+typedef ID2D1RenderTarget PlatformGraphicsContext;
#elif USE(CAIRO)
namespace WebCore {
class PlatformContextCairo;
@@ -64,563 +67,652 @@ typedef unsigned char UInt8;
namespace WebCore {
#if USE(WINGDI)
- class SharedBitmap;
- class SimpleFontData;
- class GlyphBuffer;
+class SharedBitmap;
+class Font;
+class GlyphBuffer;
#endif
- const int cMisspellingLineThickness = 3;
- const int cMisspellingLinePatternWidth = 4;
- const int cMisspellingLinePatternGapWidth = 1;
-
- class AffineTransform;
- class DrawingBuffer;
- class Gradient;
- class GraphicsContextPlatformPrivate;
- class ImageBuffer;
- class IntRect;
- class RoundedRect;
- class URL;
- class GraphicsContext3D;
- class TextRun;
- class TransformationMatrix;
-#if PLATFORM(IOS)
- struct BidiStatus;
+const int cMisspellingLineThickness = 3;
+const int cMisspellingLinePatternWidth = 4;
+const int cMisspellingLinePatternGapWidth = 1;
+
+class AffineTransform;
+class FloatRoundedRect;
+class Gradient;
+class GraphicsContextPlatformPrivate;
+class ImageBuffer;
+class IntRect;
+class RoundedRect;
+class URL;
+class GraphicsContext3D;
+class Path;
+class TextRun;
+class TransformationMatrix;
+
+enum TextDrawingMode {
+ TextModeFill = 1 << 0,
+ TextModeStroke = 1 << 1,
+#if ENABLE(LETTERPRESS)
+ TextModeLetterpress = 1 << 2,
#endif
+};
+typedef unsigned TextDrawingModeFlags;
+
+enum StrokeStyle {
+ NoStroke,
+ SolidStroke,
+ DottedStroke,
+ DashedStroke,
+ DoubleStroke,
+ WavyStroke,
+};
+
+namespace DisplayList {
+class Recorder;
+}
- enum TextDrawingMode {
- TextModeFill = 1 << 0,
- TextModeStroke = 1 << 1,
-#if ENABLE(LETTERPRESS)
- TextModeLetterpress = 1 << 2,
+struct GraphicsContextState {
+ GraphicsContextState()
+ : shouldAntialias(true)
+ , shouldSmoothFonts(true)
+ , shouldSubpixelQuantizeFonts(true)
+ , shadowsIgnoreTransforms(false)
+#if USE(CG)
+ // Core Graphics incorrectly renders shadows with radius > 8px (<rdar://problem/8103442>),
+ // but we need to preserve this buggy behavior for canvas and -webkit-box-shadow.
+ , shadowsUseLegacyRadius(false)
#endif
+ , drawLuminanceMask(false)
+ {
+ }
+
+ enum Change : uint32_t {
+ NoChange = 0,
+ StrokeGradientChange = 1 << 1,
+ StrokePatternChange = 1 << 2,
+ FillGradientChange = 1 << 3,
+ FillPatternChange = 1 << 4,
+ StrokeThicknessChange = 1 << 5,
+ StrokeColorChange = 1 << 6,
+ StrokeStyleChange = 1 << 7,
+ FillColorChange = 1 << 8,
+ FillRuleChange = 1 << 9,
+ ShadowChange = 1 << 10,
+ ShadowColorChange = 1 << 11,
+ ShadowsIgnoreTransformsChange = 1 << 12,
+ AlphaChange = 1 << 13,
+ CompositeOperationChange = 1 << 14,
+ BlendModeChange = 1 << 15,
+ TextDrawingModeChange = 1 << 16,
+ ShouldAntialiasChange = 1 << 17,
+ ShouldSmoothFontsChange = 1 << 18,
+ ShouldSubpixelQuantizeFontsChange = 1 << 19,
+ DrawLuminanceMaskChange = 1 << 20,
+ ImageInterpolationQualityChange = 1 << 21,
};
- typedef unsigned TextDrawingModeFlags;
-
- enum StrokeStyle {
- NoStroke,
- SolidStroke,
- DottedStroke,
- DashedStroke,
- DoubleStroke,
- WavyStroke,
- };
+ typedef uint32_t StateChangeFlags;
- enum InterpolationQuality {
- InterpolationDefault,
- InterpolationNone,
- InterpolationLow,
- InterpolationMedium,
- InterpolationHigh
- };
+ RefPtr<Gradient> strokeGradient;
+ RefPtr<Pattern> strokePattern;
+
+ RefPtr<Gradient> fillGradient;
+ RefPtr<Pattern> fillPattern;
- struct GraphicsContextState {
- GraphicsContextState()
- : strokeThickness(0)
- , shadowBlur(0)
- , textDrawingMode(TextModeFill)
- , strokeColor(Color::black)
- , fillColor(Color::black)
- , strokeStyle(SolidStroke)
- , fillRule(RULE_NONZERO)
- , strokeColorSpace(ColorSpaceDeviceRGB)
- , fillColorSpace(ColorSpaceDeviceRGB)
- , shadowColorSpace(ColorSpaceDeviceRGB)
- , compositeOperator(CompositeSourceOver)
- , blendMode(BlendModeNormal)
-#if PLATFORM(IOS)
- , emojiDrawingEnabled(true)
- , shouldUseContextColors(true)
-#endif
- , shouldAntialias(true)
- , shouldSmoothFonts(true)
- , shouldSubpixelQuantizeFonts(true)
- , paintingDisabled(false)
- , shadowsIgnoreTransforms(false)
+ FloatSize shadowOffset;
+
+ float strokeThickness { 0 };
+ float shadowBlur { 0 };
+
+ TextDrawingModeFlags textDrawingMode { TextModeFill };
+
+ Color strokeColor { Color::black };
+ Color fillColor { Color::black };
+ Color shadowColor;
+
+ StrokeStyle strokeStyle { SolidStroke };
+ WindRule fillRule { RULE_NONZERO };
+
+ float alpha { 1 };
+ CompositeOperator compositeOperator { CompositeSourceOver };
+ BlendMode blendMode { BlendModeNormal };
+ InterpolationQuality imageInterpolationQuality { InterpolationDefault };
+
+ bool shouldAntialias : 1;
+ bool shouldSmoothFonts : 1;
+ bool shouldSubpixelQuantizeFonts : 1;
+ bool shadowsIgnoreTransforms : 1;
#if USE(CG)
- // Core Graphics incorrectly renders shadows with radius > 8px (<rdar://problem/8103442>),
- // but we need to preserve this buggy behavior for canvas and -webkit-box-shadow.
- , shadowsUseLegacyRadius(false)
+ bool shadowsUseLegacyRadius : 1;
#endif
- , drawLuminanceMask(false)
- {
- }
+ bool drawLuminanceMask : 1;
+};
+
+struct ImagePaintingOptions {
+ ImagePaintingOptions(CompositeOperator compositeOperator = CompositeSourceOver, BlendMode blendMode = BlendModeNormal, ImageOrientationDescription orientationDescription = ImageOrientationDescription(), InterpolationQuality interpolationQuality = InterpolationDefault)
+ : m_compositeOperator(compositeOperator)
+ , m_blendMode(blendMode)
+ , m_orientationDescription(orientationDescription)
+ , m_interpolationQuality(interpolationQuality)
+ {
+ }
+
+ ImagePaintingOptions(ImageOrientationDescription orientationDescription, InterpolationQuality interpolationQuality = InterpolationDefault, CompositeOperator compositeOperator = CompositeSourceOver, BlendMode blendMode = BlendModeNormal)
+ : m_compositeOperator(compositeOperator)
+ , m_blendMode(blendMode)
+ , m_orientationDescription(orientationDescription)
+ , m_interpolationQuality(interpolationQuality)
+ {
+ }
+
+ ImagePaintingOptions(InterpolationQuality interpolationQuality, ImageOrientationDescription orientationDescription = ImageOrientationDescription(), CompositeOperator compositeOperator = CompositeSourceOver, BlendMode blendMode = BlendModeNormal)
+ : m_compositeOperator(compositeOperator)
+ , m_blendMode(blendMode)
+ , m_orientationDescription(orientationDescription)
+ , m_interpolationQuality(interpolationQuality)
+ {
+ }
+
+ bool usesDefaultInterpolation() const { return m_interpolationQuality == InterpolationDefault; }
+
+ CompositeOperator m_compositeOperator;
+ BlendMode m_blendMode;
+ ImageOrientationDescription m_orientationDescription;
+ InterpolationQuality m_interpolationQuality;
+};
+
+struct GraphicsContextStateChange {
+ GraphicsContextStateChange() = default;
+ GraphicsContextStateChange(const GraphicsContextState& state, GraphicsContextState::StateChangeFlags flags)
+ : m_state(state)
+ , m_changeFlags(flags)
+ {
+ }
+
+ GraphicsContextState::StateChangeFlags changesFromState(const GraphicsContextState&) const;
+
+ void accumulate(const GraphicsContextState&, GraphicsContextState::StateChangeFlags);
+ void apply(GraphicsContext&) const;
+
+ void dump(TextStream&) const;
+
+ GraphicsContextState m_state;
+ GraphicsContextState::StateChangeFlags m_changeFlags { GraphicsContextState::NoChange };
+};
+
+TextStream& operator<<(TextStream&, const GraphicsContextStateChange&);
+
+
+class GraphicsContext {
+ WTF_MAKE_NONCOPYABLE(GraphicsContext); WTF_MAKE_FAST_ALLOCATED;
+public:
+ WEBCORE_EXPORT GraphicsContext(PlatformGraphicsContext*);
+ GraphicsContext() = default;
+ WEBCORE_EXPORT ~GraphicsContext();
+
+ enum class NonPaintingReasons {
+ NoReasons,
+ UpdatingControlTints
+ };
+ GraphicsContext(NonPaintingReasons);
- RefPtr<Gradient> strokeGradient;
- RefPtr<Pattern> strokePattern;
-
- RefPtr<Gradient> fillGradient;
- RefPtr<Pattern> fillPattern;
+ WEBCORE_EXPORT PlatformGraphicsContext* platformContext() const;
- FloatSize shadowOffset;
+ bool paintingDisabled() const { return !m_data && !isRecording(); }
+ bool updatingControlTints() const { return m_nonPaintingReasons == NonPaintingReasons::UpdatingControlTints; }
- float strokeThickness;
- float shadowBlur;
+ void setDisplayListRecorder(DisplayList::Recorder* recorder) { m_displayListRecorder = recorder; }
+ bool isRecording() const { return m_displayListRecorder; }
- TextDrawingModeFlags textDrawingMode;
+ void setStrokeThickness(float);
+ float strokeThickness() const { return m_state.strokeThickness; }
- Color strokeColor;
- Color fillColor;
- Color shadowColor;
+ void setStrokeStyle(StrokeStyle);
+ StrokeStyle strokeStyle() const { return m_state.strokeStyle; }
- StrokeStyle strokeStyle;
- WindRule fillRule;
+ WEBCORE_EXPORT void setStrokeColor(const Color&);
+ const Color& strokeColor() const { return m_state.strokeColor; }
- ColorSpace strokeColorSpace;
- ColorSpace fillColorSpace;
- ColorSpace shadowColorSpace;
+ void setStrokePattern(Ref<Pattern>&&);
+ Pattern* strokePattern() const { return m_state.strokePattern.get(); }
- CompositeOperator compositeOperator;
- BlendMode blendMode;
+ void setStrokeGradient(Ref<Gradient>&&);
+ Gradient* strokeGradient() const { return m_state.strokeGradient.get(); }
-#if PLATFORM(IOS)
- bool emojiDrawingEnabled : 1;
- bool shouldUseContextColors : 1;
-#endif
- bool shouldAntialias : 1;
- bool shouldSmoothFonts : 1;
- bool shouldSubpixelQuantizeFonts : 1;
- bool paintingDisabled : 1;
- bool shadowsIgnoreTransforms : 1;
-#if USE(CG)
- bool shadowsUseLegacyRadius : 1;
-#endif
- bool drawLuminanceMask : 1;
- };
+ void setFillRule(WindRule);
+ WindRule fillRule() const { return m_state.fillRule; }
-#if PLATFORM(IOS)
- void setStrokeAndFillColor(PlatformGraphicsContext*, CGColorRef);
+ WEBCORE_EXPORT void setFillColor(const Color&);
+ const Color& fillColor() const { return m_state.fillColor; }
+
+ void setFillPattern(Ref<Pattern>&&);
+ Pattern* fillPattern() const { return m_state.fillPattern.get(); }
+
+ WEBCORE_EXPORT void setFillGradient(Ref<Gradient>&&);
+ Gradient* fillGradient() const { return m_state.fillGradient.get(); }
+
+ void setShadowsIgnoreTransforms(bool);
+ bool shadowsIgnoreTransforms() const { return m_state.shadowsIgnoreTransforms; }
+
+ WEBCORE_EXPORT void setShouldAntialias(bool);
+ bool shouldAntialias() const { return m_state.shouldAntialias; }
+
+ WEBCORE_EXPORT void setShouldSmoothFonts(bool);
+ bool shouldSmoothFonts() const { return m_state.shouldSmoothFonts; }
+
+ // Normally CG enables subpixel-quantization because it improves the performance of aligning glyphs.
+ // In some cases we have to disable to to ensure a high-quality output of the glyphs.
+ void setShouldSubpixelQuantizeFonts(bool);
+ bool shouldSubpixelQuantizeFonts() const { return m_state.shouldSubpixelQuantizeFonts; }
+
+ const GraphicsContextState& state() const { return m_state; }
+
+#if USE(CG) || USE(DIRECT2D) || USE(CAIRO)
+ WEBCORE_EXPORT void drawNativeImage(const NativeImagePtr&, const FloatSize& selfSize, const FloatRect& destRect, const FloatRect& srcRect, CompositeOperator = CompositeSourceOver, BlendMode = BlendModeNormal, ImageOrientation = ImageOrientation());
#endif
- class GraphicsContext {
- WTF_MAKE_NONCOPYABLE(GraphicsContext); WTF_MAKE_FAST_ALLOCATED;
- public:
-#if !PLATFORM(IOS)
- GraphicsContext(PlatformGraphicsContext*);
-#else
- GraphicsContext(PlatformGraphicsContext*, bool shouldUseContextColors = true);
+#if USE(CG) || USE(DIRECT2D)
+ void applyStrokePattern();
+ void applyFillPattern();
+ void drawPath(const Path&);
+
+ WEBCORE_EXPORT void setIsCALayerContext(bool);
+ bool isCALayerContext() const;
+
+ WEBCORE_EXPORT void setIsAcceleratedContext(bool);
#endif
- ~GraphicsContext();
+ bool isAcceleratedContext() const;
+ RenderingMode renderingMode() const { return isAcceleratedContext() ? Accelerated : Unaccelerated; }
- PlatformGraphicsContext* platformContext() const;
+ WEBCORE_EXPORT void save();
+ WEBCORE_EXPORT void restore();
- float strokeThickness() const;
- void setStrokeThickness(float);
- StrokeStyle strokeStyle() const;
- void setStrokeStyle(StrokeStyle);
- Color strokeColor() const;
- ColorSpace strokeColorSpace() const;
- void setStrokeColor(const Color&, ColorSpace);
+ // These draw methods will do both stroking and filling.
+ // FIXME: ...except drawRect(), which fills properly but always strokes
+ // using a 1-pixel stroke inset from the rect borders (of the correct
+ // stroke color).
+ void drawRect(const FloatRect&, float borderThickness = 1);
+ void drawLine(const FloatPoint&, const FloatPoint&);
- void setStrokePattern(PassRefPtr<Pattern>);
- Pattern* strokePattern() const;
+ void drawEllipse(const FloatRect&);
+ void drawRaisedEllipse(const FloatRect&, const Color& ellipseColor, const Color& shadowColor);
- void setStrokeGradient(PassRefPtr<Gradient>);
- Gradient* strokeGradient() const;
+ WEBCORE_EXPORT void fillPath(const Path&);
+ void strokePath(const Path&);
- WindRule fillRule() const;
- void setFillRule(WindRule);
- Color fillColor() const;
- ColorSpace fillColorSpace() const;
- void setFillColor(const Color&, ColorSpace);
+ void fillEllipse(const FloatRect&);
+ void strokeEllipse(const FloatRect&);
- void setFillPattern(PassRefPtr<Pattern>);
- Pattern* fillPattern() const;
+ WEBCORE_EXPORT void fillRect(const FloatRect&);
+ WEBCORE_EXPORT void fillRect(const FloatRect&, const Color&);
+ void fillRect(const FloatRect&, Gradient&);
+ void fillRect(const FloatRect&, const Color&, CompositeOperator, BlendMode = BlendModeNormal);
+ void fillRoundedRect(const FloatRoundedRect&, const Color&, BlendMode = BlendModeNormal);
+ void fillRectWithRoundedHole(const FloatRect&, const FloatRoundedRect& roundedHoleRect, const Color&);
- void setFillGradient(PassRefPtr<Gradient>);
- Gradient* fillGradient() const;
+ WEBCORE_EXPORT void clearRect(const FloatRect&);
- void setShadowsIgnoreTransforms(bool);
- bool shadowsIgnoreTransforms() const;
+ WEBCORE_EXPORT void strokeRect(const FloatRect&, float lineWidth);
- void setShouldAntialias(bool);
- bool shouldAntialias() const;
+ WEBCORE_EXPORT void drawImage(Image&, const FloatPoint& destination, const ImagePaintingOptions& = ImagePaintingOptions());
+ WEBCORE_EXPORT void drawImage(Image&, const FloatRect& destination, const ImagePaintingOptions& = ImagePaintingOptions());
+ void drawImage(Image&, const FloatRect& destination, const FloatRect& source, const ImagePaintingOptions& = ImagePaintingOptions());
- void setShouldSmoothFonts(bool);
- bool shouldSmoothFonts() const;
+ void drawTiledImage(Image&, const FloatRect& destination, const FloatPoint& source, const FloatSize& tileSize, const FloatSize& spacing, const ImagePaintingOptions& = ImagePaintingOptions());
+ void drawTiledImage(Image&, const FloatRect& destination, const FloatRect& source, const FloatSize& tileScaleFactor,
+ Image::TileRule, Image::TileRule, const ImagePaintingOptions& = ImagePaintingOptions());
- // Normally CG enables subpixel-quantization because it improves the performance of aligning glyphs.
- // In some cases we have to disable to to ensure a high-quality output of the glyphs.
- void setShouldSubpixelQuantizeFonts(bool);
- bool shouldSubpixelQuantizeFonts() const;
+ WEBCORE_EXPORT void drawImageBuffer(ImageBuffer&, const FloatPoint& destination, const ImagePaintingOptions& = ImagePaintingOptions());
+ void drawImageBuffer(ImageBuffer&, const FloatRect& destination, const ImagePaintingOptions& = ImagePaintingOptions());
+ void drawImageBuffer(ImageBuffer&, const FloatRect& destination, const FloatRect& source, const ImagePaintingOptions& = ImagePaintingOptions());
- const GraphicsContextState& state() const;
+ void drawPattern(Image&, const FloatRect& destRect, const FloatRect& srcRect, const AffineTransform&, const FloatPoint& phase, const FloatSize& spacing, CompositeOperator, BlendMode = BlendModeNormal);
-#if USE(CG)
- void applyStrokePattern();
- void applyFillPattern();
- void drawPath(const Path&);
+ WEBCORE_EXPORT void drawConsumingImageBuffer(std::unique_ptr<ImageBuffer>, const FloatPoint& destination, const ImagePaintingOptions& = ImagePaintingOptions());
+ void drawConsumingImageBuffer(std::unique_ptr<ImageBuffer>, const FloatRect& destination, const ImagePaintingOptions& = ImagePaintingOptions());
+ void drawConsumingImageBuffer(std::unique_ptr<ImageBuffer>, const FloatRect& destination, const FloatRect& source, const ImagePaintingOptions& = ImagePaintingOptions());
- void drawNativeImage(PassNativeImagePtr, const FloatSize& selfSize, ColorSpace styleColorSpace, const FloatRect& destRect, const FloatRect& srcRect, float scale = 1, CompositeOperator = CompositeSourceOver, BlendMode = BlendModeNormal, ImageOrientation = DefaultImageOrientation);
+ WEBCORE_EXPORT void setImageInterpolationQuality(InterpolationQuality);
+ InterpolationQuality imageInterpolationQuality() const { return m_state.imageInterpolationQuality; }
- // Allow font smoothing (LCD antialiasing). Not part of the graphics state.
- void setAllowsFontSmoothing(bool);
-
- void setIsCALayerContext(bool);
- bool isCALayerContext() const;
+ WEBCORE_EXPORT void clip(const FloatRect&);
+ void clipRoundedRect(const FloatRoundedRect&);
- void setIsAcceleratedContext(bool);
-#endif
- bool isAcceleratedContext() const;
+ void clipOut(const FloatRect&);
+ void clipOutRoundedRect(const FloatRoundedRect&);
+ void clipPath(const Path&, WindRule = RULE_EVENODD);
+ void clipToImageBuffer(ImageBuffer&, const FloatRect&);
+
+ IntRect clipBounds() const;
- void save();
- void restore();
+ void setTextDrawingMode(TextDrawingModeFlags);
+ TextDrawingModeFlags textDrawingMode() const { return m_state.textDrawingMode; }
- // These draw methods will do both stroking and filling.
- // FIXME: ...except drawRect(), which fills properly but always strokes
- // using a 1-pixel stroke inset from the rect borders (of the correct
- // stroke color).
- void drawRect(const IntRect&);
- void drawLine(const IntPoint&, const IntPoint&);
+ float drawText(const FontCascade&, const TextRun&, const FloatPoint&, unsigned from = 0, std::optional<unsigned> to = std::nullopt);
+ void drawGlyphs(const FontCascade&, const Font&, const GlyphBuffer&, unsigned from, unsigned numGlyphs, const FloatPoint&);
+ void drawEmphasisMarks(const FontCascade&, const TextRun&, const AtomicString& mark, const FloatPoint&, unsigned from = 0, std::optional<unsigned> to = std::nullopt);
+ void drawBidiText(const FontCascade&, const TextRun&, const FloatPoint&, FontCascade::CustomFontNotReadyAction = FontCascade::DoNotPaintIfFontNotReady);
-#if PLATFORM(IOS)
- void drawJoinedLines(CGPoint points[], unsigned count, bool antialias, CGLineCap = kCGLineCapButt);
-#endif
+ void applyState(const GraphicsContextState&);
- void drawEllipse(const IntRect&);
-#if PLATFORM(IOS)
- void drawEllipse(const FloatRect&);
- void drawRaisedEllipse(const FloatRect&, const Color& ellipseColor, ColorSpace ellipseColorSpace, const Color& shadowColor, ColorSpace shadowColorSpace);
-#endif
- void drawConvexPolygon(size_t numPoints, const FloatPoint*, bool shouldAntialias = false);
-
- void fillPath(const Path&);
- void strokePath(const Path&);
-
- void fillEllipse(const FloatRect&);
- void strokeEllipse(const FloatRect&);
-
- void fillRect(const FloatRect&);
- void fillRect(const FloatRect&, const Color&, ColorSpace);
- void fillRect(const FloatRect&, Gradient&);
- void fillRect(const FloatRect&, const Color&, ColorSpace, CompositeOperator, BlendMode = BlendModeNormal);
- void fillRoundedRect(const IntRect&, const IntSize& topLeft, const IntSize& topRight, const IntSize& bottomLeft, const IntSize& bottomRight, const Color&, ColorSpace);
- void fillRoundedRect(const RoundedRect&, const Color&, ColorSpace, BlendMode = BlendModeNormal);
- void fillRectWithRoundedHole(const IntRect&, const RoundedRect& roundedHoleRect, const Color&, ColorSpace);
-
- void clearRect(const FloatRect&);
-
- void strokeRect(const FloatRect&, float lineWidth);
-
- void drawImage(Image*, ColorSpace styleColorSpace, const IntPoint&, CompositeOperator = CompositeSourceOver, ImageOrientationDescription = ImageOrientationDescription());
- void drawImage(Image*, ColorSpace styleColorSpace, const IntRect&, CompositeOperator = CompositeSourceOver, ImageOrientationDescription = ImageOrientationDescription(), bool useLowQualityScale = false);
- void drawImage(Image*, ColorSpace styleColorSpace, const IntPoint& destPoint, const IntRect& srcRect, CompositeOperator = CompositeSourceOver, ImageOrientationDescription = ImageOrientationDescription());
- void drawImage(Image*, ColorSpace styleColorSpace, const FloatRect& destRect);
- void drawImage(Image*, ColorSpace styleColorSpace, const FloatRect& destRect, const FloatRect& srcRect, CompositeOperator = CompositeSourceOver, ImageOrientationDescription = ImageOrientationDescription(), bool useLowQualityScale = false);
- void drawImage(Image*, ColorSpace styleColorSpace, const FloatRect& destRect, const FloatRect& srcRect, CompositeOperator, BlendMode, ImageOrientationDescription = ImageOrientationDescription(), bool useLowQualityScale = false);
-
- void drawTiledImage(Image*, ColorSpace styleColorSpace, const IntRect& destRect, const IntPoint& srcPoint, const IntSize& tileSize,
- CompositeOperator = CompositeSourceOver, bool useLowQualityScale = false, BlendMode = BlendModeNormal);
- void drawTiledImage(Image*, ColorSpace styleColorSpace, const IntRect& destRect, const IntRect& srcRect,
- const FloatSize& tileScaleFactor, Image::TileRule hRule = Image::StretchTile, Image::TileRule vRule = Image::StretchTile,
- CompositeOperator = CompositeSourceOver, bool useLowQualityScale = false);
-
- void drawImageBuffer(ImageBuffer*, ColorSpace styleColorSpace, const IntPoint&, CompositeOperator = CompositeSourceOver, BlendMode = BlendModeNormal);
- void drawImageBuffer(ImageBuffer*, ColorSpace styleColorSpace, const IntRect&, CompositeOperator = CompositeSourceOver, BlendMode = BlendModeNormal, bool useLowQualityScale = false);
- void drawImageBuffer(ImageBuffer*, ColorSpace styleColorSpace, const IntPoint& destPoint, const IntRect& srcRect, CompositeOperator = CompositeSourceOver, BlendMode = BlendModeNormal);
- void drawImageBuffer(ImageBuffer*, ColorSpace styleColorSpace, const IntRect& destRect, const IntRect& srcRect, CompositeOperator = CompositeSourceOver, BlendMode = BlendModeNormal, bool useLowQualityScale = false);
- void drawImageBuffer(ImageBuffer*, ColorSpace styleColorSpace, const FloatRect& destRect);
- void drawImageBuffer(ImageBuffer*, ColorSpace styleColorSpace, const FloatRect& destRect, const FloatRect& srcRect, CompositeOperator = CompositeSourceOver, BlendMode = BlendModeNormal, bool useLowQualityScale = false);
-
- void setImageInterpolationQuality(InterpolationQuality);
- InterpolationQuality imageInterpolationQuality() const;
-
- void clip(const IntRect&);
- void clip(const FloatRect&);
- void clipRoundedRect(const RoundedRect&);
-
- // FIXME: Consider writing this in terms of a specialized RoundedRect that uses FloatRect and FloatSize radii.
- void clipRoundedRect(const FloatRect&, const FloatSize& topLeft, const FloatSize& topRight, const FloatSize& bottomLeft, const FloatSize& bottomRight);
-
- void clipOut(const IntRect&);
- void clipOutRoundedRect(const RoundedRect&);
- void clipPath(const Path&, WindRule);
- void clipConvexPolygon(size_t numPoints, const FloatPoint*, bool antialias = true);
- void clipToImageBuffer(ImageBuffer*, const FloatRect&);
-
- IntRect clipBounds() const;
-
- TextDrawingModeFlags textDrawingMode() const;
- void setTextDrawingMode(TextDrawingModeFlags);
+ enum RoundingMode {
+ RoundAllSides,
+ RoundOriginAndDimensions
+ };
+ FloatRect roundToDevicePixels(const FloatRect&, RoundingMode = RoundAllSides);
+ FloatRect computeUnderlineBoundsForText(const FloatPoint&, float width, bool printing);
+ WEBCORE_EXPORT void drawLineForText(const FloatPoint&, float width, bool printing, bool doubleLines = false, StrokeStyle = SolidStroke);
+ void drawLinesForText(const FloatPoint&, const DashArray& widths, bool printing, bool doubleLines = false, StrokeStyle = SolidStroke);
+ enum DocumentMarkerLineStyle {
#if PLATFORM(IOS)
- bool emojiDrawingEnabled();
- void setEmojiDrawingEnabled(bool);
+ TextCheckingDictationPhraseWithAlternativesLineStyle,
#endif
-
-#if !PLATFORM(IOS)
- void drawText(const Font&, const TextRun&, const FloatPoint&, int from = 0, int to = -1);
-#else
- float drawText(const Font&, const TextRun&, const FloatPoint&, int from = 0, int to = -1);
-#endif
- void drawEmphasisMarks(const Font&, const TextRun& , const AtomicString& mark, const FloatPoint&, int from = 0, int to = -1);
-#if !PLATFORM(IOS)
- void drawBidiText(const Font&, const TextRun&, const FloatPoint&, Font::CustomFontNotReadyAction = Font::DoNotPaintIfFontNotReady);
-#else
- float drawBidiText(const Font&, const TextRun&, const FloatPoint&, Font::CustomFontNotReadyAction = Font::DoNotPaintIfFontNotReady, BidiStatus* = 0, int length = -1);
-#endif
- void drawHighlightForText(const Font&, const TextRun&, const FloatPoint&, int h, const Color& backgroundColor, ColorSpace, int from = 0, int to = -1);
-
- enum RoundingMode {
- RoundAllSides,
- RoundOriginAndDimensions
- };
- FloatRect roundToDevicePixels(const FloatRect&, RoundingMode = RoundAllSides);
-
- FloatRect computeLineBoundsForText(const FloatPoint&, float width, bool printing);
- void drawLineForText(const FloatPoint&, float width, bool printing);
- void drawLinesForText(const FloatPoint&, const DashArray& widths, bool printing);
- enum DocumentMarkerLineStyle {
-#if PLATFORM(IOS)
- TextCheckingDictationPhraseWithAlternativesLineStyle,
-#endif
- DocumentMarkerSpellingLineStyle,
- DocumentMarkerGrammarLineStyle,
- DocumentMarkerAutocorrectionReplacementLineStyle,
- DocumentMarkerDictationAlternativesLineStyle
- };
- static void updateDocumentMarkerResources();
- void drawLineForDocumentMarker(const FloatPoint&, float width, DocumentMarkerLineStyle);
-
- bool paintingDisabled() const;
- void setPaintingDisabled(bool);
-
- bool updatingControlTints() const;
- void setUpdatingControlTints(bool);
-
- void beginTransparencyLayer(float opacity);
- void endTransparencyLayer();
- bool isInTransparencyLayer() const;
-
- bool hasShadow() const;
- void setShadow(const FloatSize&, float blur, const Color&, ColorSpace);
- // Legacy shadow blur radius is used for canvas, and -webkit-box-shadow.
- // It has different treatment of radii > 8px.
- void setLegacyShadow(const FloatSize&, float blur, const Color&, ColorSpace);
-
- bool getShadow(FloatSize&, float&, Color&, ColorSpace&) const;
- void clearShadow();
-
- bool hasBlurredShadow() const;
+ DocumentMarkerSpellingLineStyle,
+ DocumentMarkerGrammarLineStyle,
+ DocumentMarkerAutocorrectionReplacementLineStyle,
+ DocumentMarkerDictationAlternativesLineStyle
+ };
+ static void updateDocumentMarkerResources();
+ void drawLineForDocumentMarker(const FloatPoint&, float width, DocumentMarkerLineStyle);
+
+ WEBCORE_EXPORT void beginTransparencyLayer(float opacity);
+ WEBCORE_EXPORT void endTransparencyLayer();
+ bool isInTransparencyLayer() const { return (m_transparencyCount > 0) && supportsTransparencyLayers(); }
+
+ WEBCORE_EXPORT void setShadow(const FloatSize&, float blur, const Color&);
+ // Legacy shadow blur radius is used for canvas, and -webkit-box-shadow.
+ // It has different treatment of radii > 8px.
+ void setLegacyShadow(const FloatSize&, float blur, const Color&);
+
+ WEBCORE_EXPORT void clearShadow();
+ bool getShadow(FloatSize&, float&, Color&) const;
+
+ bool hasVisibleShadow() const { return m_state.shadowColor.isVisible(); }
+ bool hasShadow() const { return hasVisibleShadow() && (m_state.shadowBlur || m_state.shadowOffset.width() || m_state.shadowOffset.height()); }
+ bool hasBlurredShadow() const { return hasVisibleShadow() && m_state.shadowBlur; }
+
#if USE(CAIRO)
- bool mustUseShadowBlur() const;
+ bool mustUseShadowBlur() const;
#endif
- void drawFocusRing(const Vector<IntRect>&, int width, int offset, const Color&);
- void drawFocusRing(const Path&, int width, int offset, const Color&);
+ void drawFocusRing(const Vector<FloatRect>&, float width, float offset, const Color&);
+ void drawFocusRing(const Path&, float width, float offset, const Color&);
+#if PLATFORM(MAC)
+ void drawFocusRing(const Path&, double timeOffset, bool& needsRedraw);
+ void drawFocusRing(const Vector<FloatRect>&, double timeOffset, bool& needsRedraw);
+#endif
- void setLineCap(LineCap);
- void setLineDash(const DashArray&, float dashOffset);
- void setLineJoin(LineJoin);
- void setMiterLimit(float);
+ void setLineCap(LineCap);
+ void setLineDash(const DashArray&, float dashOffset);
+ void setLineJoin(LineJoin);
+ void setMiterLimit(float);
- void setAlpha(float);
+ void setAlpha(float);
+ float alpha() const { return m_state.alpha; }
- void setCompositeOperation(CompositeOperator, BlendMode = BlendModeNormal);
- CompositeOperator compositeOperation() const;
- BlendMode blendModeOperation() const;
+ WEBCORE_EXPORT void setCompositeOperation(CompositeOperator, BlendMode = BlendModeNormal);
+ CompositeOperator compositeOperation() const { return m_state.compositeOperator; }
+ BlendMode blendModeOperation() const { return m_state.blendMode; }
- void setDrawLuminanceMask(bool);
- bool drawLuminanceMask() const;
+ void setDrawLuminanceMask(bool);
+ bool drawLuminanceMask() const { return m_state.drawLuminanceMask; }
- void clip(const Path&, WindRule = RULE_EVENODD);
+ // This clip function is used only by <canvas> code. It allows
+ // implementations to handle clipping on the canvas differently since
+ // the discipline is different.
+ void canvasClip(const Path&, WindRule = RULE_EVENODD);
+ void clipOut(const Path&);
- // This clip function is used only by <canvas> code. It allows
- // implementations to handle clipping on the canvas differently since
- // the discipline is different.
- void canvasClip(const Path&, WindRule = RULE_EVENODD);
- void clipOut(const Path&);
+ void scale(float s)
+ {
+ scale({ s, s });
+ }
+ WEBCORE_EXPORT void scale(const FloatSize&);
+ void rotate(float angleInRadians);
+ void translate(const FloatSize& size) { translate(size.width(), size.height()); }
+ WEBCORE_EXPORT void translate(float x, float y);
- void scale(const FloatSize&);
- void rotate(float angleInRadians);
- void translate(const FloatSize& size) { translate(size.width(), size.height()); }
- void translate(float x, float y);
+ void setURLForRect(const URL&, const FloatRect&);
- void setURLForRect(const URL&, const IntRect&);
+ void setDestinationForRect(const String& name, const FloatRect&);
+ void addDestinationAtPoint(const String& name, const FloatPoint&);
- void concatCTM(const AffineTransform&);
- void setCTM(const AffineTransform&);
+ void concatCTM(const AffineTransform&);
+ void setCTM(const AffineTransform&);
- enum IncludeDeviceScale { DefinitelyIncludeDeviceScale, PossiblyIncludeDeviceScale };
- AffineTransform getCTM(IncludeDeviceScale includeScale = PossiblyIncludeDeviceScale) const;
+ enum IncludeDeviceScale { DefinitelyIncludeDeviceScale, PossiblyIncludeDeviceScale };
+ AffineTransform getCTM(IncludeDeviceScale includeScale = PossiblyIncludeDeviceScale) const;
-#if ENABLE(3D_RENDERING) && USE(TEXTURE_MAPPER)
- // This is needed when using accelerated-compositing in software mode, like in TextureMapper.
- void concat3DTransform(const TransformationMatrix&);
- void set3DTransform(const TransformationMatrix&);
- TransformationMatrix get3DTransform() const;
+#if ENABLE(3D_TRANSFORMS) && USE(TEXTURE_MAPPER)
+ // This is needed when using accelerated-compositing in software mode, like in TextureMapper.
+ void concat3DTransform(const TransformationMatrix&);
+ void set3DTransform(const TransformationMatrix&);
+ TransformationMatrix get3DTransform() const;
#endif
- // Create an image buffer compatible with this context, with suitable resolution
- // for drawing into the buffer and then into this context.
- std::unique_ptr<ImageBuffer> createCompatibleBuffer(const IntSize&, bool hasAlpha = true) const;
- bool isCompatibleWithBuffer(ImageBuffer*) const;
- // This function applies the device scale factor to the context, making the context capable of
- // acting as a base-level context for a HiDPI environment.
- void applyDeviceScaleFactor(float);
- void platformApplyDeviceScaleFactor(float);
+ // This function applies the device scale factor to the context, making the context capable of
+ // acting as a base-level context for a HiDPI environment.
+ WEBCORE_EXPORT void applyDeviceScaleFactor(float);
+ void platformApplyDeviceScaleFactor(float);
+ FloatSize scaleFactor() const;
#if OS(WINDOWS)
- HDC getWindowsContext(const IntRect&, bool supportAlphaBlend, bool mayCreateBitmap); // The passed in rect is used to create a bitmap for compositing inside transparency layers.
- void releaseWindowsContext(HDC, const IntRect&, bool supportAlphaBlend, bool mayCreateBitmap); // The passed in HDC should be the one handed back by getWindowsContext.
+ HDC getWindowsContext(const IntRect&, bool supportAlphaBlend, bool mayCreateBitmap); // The passed in rect is used to create a bitmap for compositing inside transparency layers.
+ void releaseWindowsContext(HDC, const IntRect&, bool supportAlphaBlend, bool mayCreateBitmap); // The passed in HDC should be the one handed back by getWindowsContext.
+ HDC hdc() const;
#if PLATFORM(WIN)
#if USE(WINGDI)
- void setBitmap(PassRefPtr<SharedBitmap>);
- const AffineTransform& affineTransform() const;
- AffineTransform& affineTransform();
- void resetAffineTransform();
- void fillRect(const FloatRect&, const Gradient*);
- void drawText(const SimpleFontData* fontData, const GlyphBuffer& glyphBuffer, int from, int numGlyphs, const FloatPoint& point);
- void drawFrameControl(const IntRect& rect, unsigned type, unsigned state);
- void drawFocusRect(const IntRect& rect);
- void paintTextField(const IntRect& rect, unsigned state);
- void drawBitmap(SharedBitmap*, const IntRect& dstRect, const IntRect& srcRect, ColorSpace styleColorSpace, CompositeOperator compositeOp, BlendMode blendMode);
- void drawBitmapPattern(SharedBitmap*, const FloatRect& tileRectIn, const AffineTransform& patternTransform, const FloatPoint& phase, ColorSpace styleColorSpace, CompositeOperator op, const FloatRect& destRect, const IntSize& origSourceSize);
- void drawIcon(HICON icon, const IntRect& dstRect, UINT flags);
- void drawRoundCorner(bool newClip, RECT clipRect, RECT rectWin, HDC dc, int width, int height);
+ void setBitmap(PassRefPtr<SharedBitmap>);
+ const AffineTransform& affineTransform() const;
+ AffineTransform& affineTransform();
+ void resetAffineTransform();
+ void fillRect(const FloatRect&, const Gradient*);
+ void drawText(const Font&, const GlyphBuffer&, int from, int numGlyphs, const FloatPoint&);
+ void drawFrameControl(const IntRect& rect, unsigned type, unsigned state);
+ void drawFocusRect(const IntRect& rect);
+ void paintTextField(const IntRect& rect, unsigned state);
+ void drawBitmap(SharedBitmap*, const IntRect& dstRect, const IntRect& srcRect, CompositeOperator, BlendMode);
+ void drawBitmapPattern(SharedBitmap*, const FloatRect& tileRectIn, const AffineTransform& patternTransform, const FloatPoint& phase, CompositeOperator, const FloatRect& destRect, const IntSize& origSourceSize);
+ void drawIcon(HICON icon, const IntRect& dstRect, UINT flags);
+ void drawRoundCorner(bool newClip, RECT clipRect, RECT rectWin, HDC dc, int width, int height);
#else
- GraphicsContext(HDC, bool hasAlpha = false); // FIXME: To be removed.
-
- // When set to true, child windows should be rendered into this context
- // rather than allowing them just to render to the screen. Defaults to
- // false.
- // FIXME: This is a layering violation. GraphicsContext shouldn't know
- // what a "window" is. It would be much more appropriate for this flag
- // to be passed as a parameter alongside the GraphicsContext, but doing
- // that would require lots of changes in cross-platform code that we
- // aren't sure we want to make.
- void setShouldIncludeChildWindows(bool);
- bool shouldIncludeChildWindows() const;
-
- class WindowsBitmap {
- WTF_MAKE_NONCOPYABLE(WindowsBitmap);
- public:
- WindowsBitmap(HDC, const IntSize&);
- ~WindowsBitmap();
-
- HDC hdc() const { return m_hdc; }
- UInt8* buffer() const { return m_pixelData.buffer(); }
- unsigned bufferLength() const { return m_pixelData.bufferLength(); }
- const IntSize& size() const { return m_pixelData.size(); }
- unsigned bytesPerRow() const { return m_pixelData.bytesPerRow(); }
- unsigned short bitsPerPixel() const { return m_pixelData.bitsPerPixel(); }
- const DIBPixelData& windowsDIB() const { return m_pixelData; }
-
- private:
- HDC m_hdc;
- HBITMAP m_bitmap;
- DIBPixelData m_pixelData;
- };
-
- PassOwnPtr<WindowsBitmap> createWindowsBitmap(const IntSize&);
- // The bitmap should be non-premultiplied.
- void drawWindowsBitmap(WindowsBitmap*, const IntPoint&);
+ GraphicsContext(HDC, bool hasAlpha = false); // FIXME: To be removed.
+
+ // When set to true, child windows should be rendered into this context
+ // rather than allowing them just to render to the screen. Defaults to
+ // false.
+ // FIXME: This is a layering violation. GraphicsContext shouldn't know
+ // what a "window" is. It would be much more appropriate for this flag
+ // to be passed as a parameter alongside the GraphicsContext, but doing
+ // that would require lots of changes in cross-platform code that we
+ // aren't sure we want to make.
+ void setShouldIncludeChildWindows(bool);
+ bool shouldIncludeChildWindows() const;
+
+ class WindowsBitmap {
+ WTF_MAKE_NONCOPYABLE(WindowsBitmap);
+ public:
+ WindowsBitmap(HDC, const IntSize&);
+ ~WindowsBitmap();
+
+ HDC hdc() const { return m_hdc; }
+ UInt8* buffer() const { return m_pixelData.buffer(); }
+ unsigned bufferLength() const { return m_pixelData.bufferLength(); }
+ const IntSize& size() const { return m_pixelData.size(); }
+ unsigned bytesPerRow() const { return m_pixelData.bytesPerRow(); }
+ unsigned short bitsPerPixel() const { return m_pixelData.bitsPerPixel(); }
+ const DIBPixelData& windowsDIB() const { return m_pixelData; }
+
+ private:
+ HDC m_hdc;
+ HBITMAP m_bitmap;
+ DIBPixelData m_pixelData;
+ };
+
+ std::unique_ptr<WindowsBitmap> createWindowsBitmap(const IntSize&);
+ // The bitmap should be non-premultiplied.
+ void drawWindowsBitmap(WindowsBitmap*, const IntPoint&);
+#endif
+#if USE(DIRECT2D)
+ GraphicsContext(HDC, ID2D1DCRenderTarget**, RECT, bool hasAlpha = false); // FIXME: To be removed.
+
+ WEBCORE_EXPORT static ID2D1Factory* systemFactory();
+ WEBCORE_EXPORT static ID2D1RenderTarget* defaultRenderTarget();
+
+ WEBCORE_EXPORT void beginDraw();
+ D2D1_COLOR_F colorWithGlobalAlpha(const Color&) const;
+ WEBCORE_EXPORT void endDraw();
+ void flush();
+
+ ID2D1Brush* solidStrokeBrush() const;
+ ID2D1Brush* solidFillBrush() const;
+ ID2D1Brush* patternStrokeBrush() const;
+ ID2D1Brush* patternFillBrush() const;
+ ID2D1StrokeStyle* platformStrokeStyle() const;
+
+ ID2D1SolidColorBrush* brushWithColor(const Color&);
#endif
#else // PLATFORM(WIN)
- bool shouldIncludeChildWindows() const { return false; }
+ bool shouldIncludeChildWindows() const { return false; }
#endif // PLATFORM(WIN)
#endif // OS(WINDOWS)
#if USE(CAIRO)
- GraphicsContext(cairo_t*);
+ GraphicsContext(cairo_t*);
#endif
-#if PLATFORM(GTK)
- void setGdkExposeEvent(GdkEventExpose*);
- GdkWindow* gdkWindow() const;
- GdkEventExpose* gdkExposeEvent() const;
-#endif
+ static void adjustLineToPixelBoundaries(FloatPoint& p1, FloatPoint& p2, float strokeWidth, StrokeStyle);
- static void adjustLineToPixelBoundaries(FloatPoint& p1, FloatPoint& p2, float strokeWidth, StrokeStyle);
+ bool supportsInternalLinks() const;
- private:
-#if !PLATFORM(IOS)
- void platformInit(PlatformGraphicsContext*);
-#else
- void platformInit(PlatformGraphicsContext*, bool shouldUseContextColors);
-#endif
- void platformDestroy();
+private:
+ void platformInit(PlatformGraphicsContext*);
+ void platformDestroy();
#if PLATFORM(WIN) && !USE(WINGDI)
- void platformInit(HDC, bool hasAlpha = false);
+ void platformInit(HDC, bool hasAlpha = false);
#endif
- void savePlatformState();
- void restorePlatformState();
+#if USE(DIRECT2D)
+ void platformInit(HDC, ID2D1RenderTarget**, RECT, bool hasAlpha = false);
+ void drawWithoutShadow(const FloatRect& boundingRect, const std::function<void(ID2D1RenderTarget*)>&);
+ void drawWithShadow(const FloatRect& boundingRect, const std::function<void(ID2D1RenderTarget*)>&);
+#endif
- void setPlatformTextDrawingMode(TextDrawingModeFlags);
+ void savePlatformState();
+ void restorePlatformState();
- void setPlatformStrokeColor(const Color&, ColorSpace);
- void setPlatformStrokeStyle(StrokeStyle);
- void setPlatformStrokeThickness(float);
+ void setPlatformTextDrawingMode(TextDrawingModeFlags);
- void setPlatformFillColor(const Color&, ColorSpace);
+ void setPlatformStrokeColor(const Color&);
+ void setPlatformStrokeStyle(StrokeStyle);
+ void setPlatformStrokeThickness(float);
- void setPlatformShouldAntialias(bool);
- void setPlatformShouldSmoothFonts(bool);
+ void setPlatformFillColor(const Color&);
- void setPlatformShadow(const FloatSize&, float blur, const Color&, ColorSpace);
- void clearPlatformShadow();
+ void setPlatformShouldAntialias(bool);
+ void setPlatformShouldSmoothFonts(bool);
+ void setPlatformImageInterpolationQuality(InterpolationQuality);
- void setPlatformCompositeOperation(CompositeOperator, BlendMode = BlendModeNormal);
+ void setPlatformShadow(const FloatSize&, float blur, const Color&);
+ void clearPlatformShadow();
- void beginPlatformTransparencyLayer(float opacity);
- void endPlatformTransparencyLayer();
- static bool supportsTransparencyLayers();
+ void setPlatformAlpha(float);
+ void setPlatformCompositeOperation(CompositeOperator, BlendMode = BlendModeNormal);
- void fillEllipseAsPath(const FloatRect&);
- void strokeEllipseAsPath(const FloatRect&);
+ void beginPlatformTransparencyLayer(float opacity);
+ void endPlatformTransparencyLayer();
+ static bool supportsTransparencyLayers();
- void platformFillEllipse(const FloatRect&);
- void platformStrokeEllipse(const FloatRect&);
+ void fillEllipseAsPath(const FloatRect&);
+ void strokeEllipseAsPath(const FloatRect&);
- GraphicsContextPlatformPrivate* m_data;
+ void platformFillEllipse(const FloatRect&);
+ void platformStrokeEllipse(const FloatRect&);
- GraphicsContextState m_state;
- Vector<GraphicsContextState> m_stack;
- bool m_updatingControlTints;
- unsigned m_transparencyCount;
- };
+ void platformFillRoundedRect(const FloatRoundedRect&, const Color&);
- class GraphicsContextStateSaver {
- WTF_MAKE_FAST_ALLOCATED;
- public:
- GraphicsContextStateSaver(GraphicsContext& context, bool saveAndRestore = true)
- : m_context(context)
- , m_saveAndRestore(saveAndRestore)
- {
- if (m_saveAndRestore)
- m_context.save();
- }
-
- ~GraphicsContextStateSaver()
- {
- if (m_saveAndRestore)
- m_context.restore();
- }
-
- void save()
- {
- ASSERT(!m_saveAndRestore);
- m_context.save();
- m_saveAndRestore = true;
- }
+ FloatRect computeLineBoundsAndAntialiasingModeForText(const FloatPoint&, float width, bool printing, Color&);
- void restore()
- {
- ASSERT(m_saveAndRestore);
- m_context.restore();
- m_saveAndRestore = false;
- }
-
- GraphicsContext* context() const { return &m_context; }
+ float dashedLineCornerWidthForStrokeWidth(float) const;
+ float dashedLinePatternWidthForStrokeWidth(float) const;
+ float dashedLinePatternOffsetForPatternAndStrokeWidth(float patternWidth, float strokeWidth) const;
+ Vector<FloatPoint> centerLineAndCutOffCorners(bool isVerticalLine, float cornerWidth, FloatPoint point1, FloatPoint point2) const;
- private:
- GraphicsContext& m_context;
- bool m_saveAndRestore;
- };
+ GraphicsContextPlatformPrivate* m_data { nullptr };
+ DisplayList::Recorder* m_displayListRecorder { nullptr };
-} // namespace WebCore
+ GraphicsContextState m_state;
+ Vector<GraphicsContextState, 1> m_stack;
-#endif // GraphicsContext_h
+ const NonPaintingReasons m_nonPaintingReasons { NonPaintingReasons::NoReasons };
+ unsigned m_transparencyCount { 0 };
+};
+class GraphicsContextStateSaver {
+ WTF_MAKE_FAST_ALLOCATED;
+public:
+ GraphicsContextStateSaver(GraphicsContext& context, bool saveAndRestore = true)
+ : m_context(context)
+ , m_saveAndRestore(saveAndRestore)
+ {
+ if (m_saveAndRestore)
+ m_context.save();
+ }
+
+ ~GraphicsContextStateSaver()
+ {
+ if (m_saveAndRestore)
+ m_context.restore();
+ }
+
+ void save()
+ {
+ ASSERT(!m_saveAndRestore);
+ m_context.save();
+ m_saveAndRestore = true;
+ }
+
+ void restore()
+ {
+ ASSERT(m_saveAndRestore);
+ m_context.restore();
+ m_saveAndRestore = false;
+ }
+
+ GraphicsContext* context() const { return &m_context; }
+
+private:
+ GraphicsContext& m_context;
+ bool m_saveAndRestore;
+};
+
+class InterpolationQualityMaintainer {
+public:
+ explicit InterpolationQualityMaintainer(GraphicsContext& graphicsContext, InterpolationQuality interpolationQualityToUse)
+ : m_graphicsContext(graphicsContext)
+ , m_currentInterpolationQuality(graphicsContext.imageInterpolationQuality())
+ , m_interpolationQualityChanged(interpolationQualityToUse != InterpolationDefault && m_currentInterpolationQuality != interpolationQualityToUse)
+ {
+ if (m_interpolationQualityChanged)
+ m_graphicsContext.setImageInterpolationQuality(interpolationQualityToUse);
+ }
+
+ explicit InterpolationQualityMaintainer(GraphicsContext& graphicsContext, std::optional<InterpolationQuality> interpolationQuality)
+ : InterpolationQualityMaintainer(graphicsContext, interpolationQuality ? interpolationQuality.value() : graphicsContext.imageInterpolationQuality())
+ {
+ }
+
+ ~InterpolationQualityMaintainer()
+ {
+ if (m_interpolationQualityChanged)
+ m_graphicsContext.setImageInterpolationQuality(m_currentInterpolationQuality);
+ }
+
+private:
+ GraphicsContext& m_graphicsContext;
+ InterpolationQuality m_currentInterpolationQuality;
+ bool m_interpolationQualityChanged;
+};
+
+} // namespace WebCore
diff --git a/Source/WebCore/platform/graphics/GraphicsContext3D.cpp b/Source/WebCore/platform/graphics/GraphicsContext3D.cpp
index 47ed32f3c..76bab7c7b 100644
--- a/Source/WebCore/platform/graphics/GraphicsContext3D.cpp
+++ b/Source/WebCore/platform/graphics/GraphicsContext3D.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2010 Apple Inc. All rights reserved.
+ * Copyright (C) 2010, 2016 Apple Inc. All rights reserved.
* Copyright (C) 2010 Google Inc. All rights reserved.
* Copyright (C) 2010 Mozilla Corporation. All rights reserved.
*
@@ -12,10 +12,10 @@
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
- * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
@@ -27,7 +27,7 @@
#include "config.h"
-#if USE(3D_GRAPHICS)
+#if ENABLE(GRAPHICS_CONTEXT_3D)
#include "GraphicsContext3D.h"
@@ -62,6 +62,12 @@ GraphicsContext3D::DataFormat getDataFormat(GC3Denum destinationFormat, GC3Denum
case GraphicsContext3D::LUMINANCE_ALPHA:
dstFormat = GraphicsContext3D::DataFormatRA8;
break;
+ case GraphicsContext3D::SRGB:
+ dstFormat = GraphicsContext3D::DataFormatRGB8;
+ break;
+ case GraphicsContext3D::SRGB_ALPHA:
+ dstFormat = GraphicsContext3D::DataFormatRGBA8;
+ break;
default:
ASSERT_NOT_REACHED();
}
@@ -92,6 +98,12 @@ GraphicsContext3D::DataFormat getDataFormat(GC3Denum destinationFormat, GC3Denum
case GraphicsContext3D::LUMINANCE_ALPHA:
dstFormat = GraphicsContext3D::DataFormatRA16F;
break;
+ case GraphicsContext3D::SRGB:
+ dstFormat = GraphicsContext3D::DataFormatRGB16F;
+ break;
+ case GraphicsContext3D::SRGB_ALPHA:
+ dstFormat = GraphicsContext3D::DataFormatRGBA16F;
+ break;
default:
ASSERT_NOT_REACHED();
}
@@ -113,6 +125,12 @@ GraphicsContext3D::DataFormat getDataFormat(GC3Denum destinationFormat, GC3Denum
case GraphicsContext3D::LUMINANCE_ALPHA:
dstFormat = GraphicsContext3D::DataFormatRA32F;
break;
+ case GraphicsContext3D::SRGB:
+ dstFormat = GraphicsContext3D::DataFormatRGB32F;
+ break;
+ case GraphicsContext3D::SRGB_ALPHA:
+ dstFormat = GraphicsContext3D::DataFormatRGBA32F;
+ break;
default:
ASSERT_NOT_REACHED();
}
@@ -129,9 +147,9 @@ bool GraphicsContext3D::texImage2DResourceSafe(GC3Denum target, GC3Dint level, G
{
ASSERT(unpackAlignment == 1 || unpackAlignment == 2 || unpackAlignment == 4 || unpackAlignment == 8);
std::unique_ptr<unsigned char[]> zero;
- if (!isResourceSafe() && width > 0 && height > 0) {
+ if (width > 0 && height > 0) {
unsigned int size;
- GC3Denum error = computeImageSizeInBytes(format, type, width, height, unpackAlignment, &size, 0);
+ GC3Denum error = computeImageSizeInBytes(format, type, width, height, unpackAlignment, &size, nullptr);
if (error != GraphicsContext3D::NO_ERROR) {
synthesizeGLError(error);
return false;
@@ -149,32 +167,47 @@ bool GraphicsContext3D::texImage2DResourceSafe(GC3Denum target, GC3Dint level, G
bool GraphicsContext3D::computeFormatAndTypeParameters(GC3Denum format, GC3Denum type, unsigned int* componentsPerPixel, unsigned int* bytesPerComponent)
{
switch (format) {
+ case GraphicsContext3D::RED:
+ case GraphicsContext3D::RED_INTEGER:
case GraphicsContext3D::ALPHA:
case GraphicsContext3D::LUMINANCE:
case GraphicsContext3D::DEPTH_COMPONENT:
case GraphicsContext3D::DEPTH_STENCIL:
*componentsPerPixel = 1;
break;
+ case GraphicsContext3D::RG:
+ case GraphicsContext3D::RG_INTEGER:
case GraphicsContext3D::LUMINANCE_ALPHA:
*componentsPerPixel = 2;
break;
case GraphicsContext3D::RGB:
+ case GraphicsContext3D::RGB_INTEGER:
+ case Extensions3D::SRGB_EXT:
*componentsPerPixel = 3;
break;
case GraphicsContext3D::RGBA:
+ case GraphicsContext3D::RGBA_INTEGER:
case Extensions3D::BGRA_EXT: // GL_EXT_texture_format_BGRA8888
+ case Extensions3D::SRGB_ALPHA_EXT:
*componentsPerPixel = 4;
break;
default:
return false;
}
+
switch (type) {
case GraphicsContext3D::UNSIGNED_BYTE:
*bytesPerComponent = sizeof(GC3Dubyte);
break;
+ case GraphicsContext3D::BYTE:
+ *bytesPerComponent = sizeof(GC3Dbyte);
+ break;
case GraphicsContext3D::UNSIGNED_SHORT:
*bytesPerComponent = sizeof(GC3Dushort);
break;
+ case GraphicsContext3D::SHORT:
+ *bytesPerComponent = sizeof(GC3Dshort);
+ break;
case GraphicsContext3D::UNSIGNED_SHORT_5_6_5:
case GraphicsContext3D::UNSIGNED_SHORT_4_4_4_4:
case GraphicsContext3D::UNSIGNED_SHORT_5_5_5_1:
@@ -182,21 +215,113 @@ bool GraphicsContext3D::computeFormatAndTypeParameters(GC3Denum format, GC3Denum
*bytesPerComponent = sizeof(GC3Dushort);
break;
case GraphicsContext3D::UNSIGNED_INT_24_8:
+ case GraphicsContext3D::UNSIGNED_INT_2_10_10_10_REV:
+ case GraphicsContext3D::UNSIGNED_INT_10F_11F_11F_REV:
+ case GraphicsContext3D::UNSIGNED_INT_5_9_9_9_REV:
+ *componentsPerPixel = 1;
+ *bytesPerComponent = sizeof(GC3Duint);
+ break;
case GraphicsContext3D::UNSIGNED_INT:
*bytesPerComponent = sizeof(GC3Duint);
break;
+ case GraphicsContext3D::INT:
+ *bytesPerComponent = sizeof(GC3Dint);
+ break;
case GraphicsContext3D::FLOAT: // OES_texture_float
*bytesPerComponent = sizeof(GC3Dfloat);
break;
+ case GraphicsContext3D::HALF_FLOAT:
case GraphicsContext3D::HALF_FLOAT_OES: // OES_texture_half_float
*bytesPerComponent = sizeof(GC3Dhalffloat);
break;
+ case GraphicsContext3D::FLOAT_32_UNSIGNED_INT_24_8_REV:
+ *bytesPerComponent = sizeof(GC3Dfloat) + sizeof(GC3Duint);
+ break;
default:
return false;
}
return true;
}
+bool GraphicsContext3D::possibleFormatAndTypeForInternalFormat(GC3Denum internalFormat, GC3Denum& format, GC3Denum& type)
+{
+#define POSSIBLE_FORMAT_TYPE_CASE(internalFormatMacro, formatMacro, typeMacro) case internalFormatMacro: \
+ format = formatMacro; \
+ type = GraphicsContext3D::typeMacro; \
+ break;
+
+ switch (internalFormat) {
+ POSSIBLE_FORMAT_TYPE_CASE(GraphicsContext3D::RGB , GraphicsContext3D::RGB , UNSIGNED_BYTE );
+ POSSIBLE_FORMAT_TYPE_CASE(GraphicsContext3D::RGBA , GraphicsContext3D::RGBA , UNSIGNED_BYTE );
+ POSSIBLE_FORMAT_TYPE_CASE(GraphicsContext3D::LUMINANCE_ALPHA , GraphicsContext3D::LUMINANCE_ALPHA, UNSIGNED_BYTE );
+ POSSIBLE_FORMAT_TYPE_CASE(GraphicsContext3D::LUMINANCE , GraphicsContext3D::LUMINANCE , UNSIGNED_BYTE );
+ POSSIBLE_FORMAT_TYPE_CASE(GraphicsContext3D::ALPHA , GraphicsContext3D::ALPHA , UNSIGNED_BYTE );
+ POSSIBLE_FORMAT_TYPE_CASE(GraphicsContext3D::R8 , GraphicsContext3D::RED , UNSIGNED_BYTE );
+ POSSIBLE_FORMAT_TYPE_CASE(GraphicsContext3D::R8_SNORM , GraphicsContext3D::RED , BYTE );
+ POSSIBLE_FORMAT_TYPE_CASE(GraphicsContext3D::R16F , GraphicsContext3D::RED , HALF_FLOAT );
+ POSSIBLE_FORMAT_TYPE_CASE(GraphicsContext3D::R32F , GraphicsContext3D::RED , FLOAT );
+ POSSIBLE_FORMAT_TYPE_CASE(GraphicsContext3D::R8UI , GraphicsContext3D::RED_INTEGER , UNSIGNED_BYTE );
+ POSSIBLE_FORMAT_TYPE_CASE(GraphicsContext3D::R8I , GraphicsContext3D::RED_INTEGER , BYTE );
+ POSSIBLE_FORMAT_TYPE_CASE(GraphicsContext3D::R16UI , GraphicsContext3D::RED_INTEGER , UNSIGNED_SHORT );
+ POSSIBLE_FORMAT_TYPE_CASE(GraphicsContext3D::R16I , GraphicsContext3D::RED_INTEGER , SHORT );
+ POSSIBLE_FORMAT_TYPE_CASE(GraphicsContext3D::R32UI , GraphicsContext3D::RED_INTEGER , UNSIGNED_INT );
+ POSSIBLE_FORMAT_TYPE_CASE(GraphicsContext3D::R32I , GraphicsContext3D::RED_INTEGER , INT );
+ POSSIBLE_FORMAT_TYPE_CASE(GraphicsContext3D::RG8 , GraphicsContext3D::RG , UNSIGNED_BYTE );
+ POSSIBLE_FORMAT_TYPE_CASE(GraphicsContext3D::RG8_SNORM , GraphicsContext3D::RG , BYTE );
+ POSSIBLE_FORMAT_TYPE_CASE(GraphicsContext3D::RG16F , GraphicsContext3D::RG , HALF_FLOAT );
+ POSSIBLE_FORMAT_TYPE_CASE(GraphicsContext3D::RG32F , GraphicsContext3D::RG , FLOAT );
+ POSSIBLE_FORMAT_TYPE_CASE(GraphicsContext3D::RG8UI , GraphicsContext3D::RG_INTEGER , UNSIGNED_BYTE );
+ POSSIBLE_FORMAT_TYPE_CASE(GraphicsContext3D::RG8I , GraphicsContext3D::RG_INTEGER , BYTE );
+ POSSIBLE_FORMAT_TYPE_CASE(GraphicsContext3D::RG16UI , GraphicsContext3D::RG_INTEGER , UNSIGNED_SHORT );
+ POSSIBLE_FORMAT_TYPE_CASE(GraphicsContext3D::RG16I , GraphicsContext3D::RG_INTEGER , SHORT );
+ POSSIBLE_FORMAT_TYPE_CASE(GraphicsContext3D::RG32UI , GraphicsContext3D::RG_INTEGER , UNSIGNED_INT );
+ POSSIBLE_FORMAT_TYPE_CASE(GraphicsContext3D::RG32I , GraphicsContext3D::RG_INTEGER , INT );
+ POSSIBLE_FORMAT_TYPE_CASE(GraphicsContext3D::RGB8 , GraphicsContext3D::RGB , UNSIGNED_BYTE );
+ POSSIBLE_FORMAT_TYPE_CASE(GraphicsContext3D::SRGB8 , GraphicsContext3D::RGB , UNSIGNED_BYTE );
+ POSSIBLE_FORMAT_TYPE_CASE(GraphicsContext3D::RGB565 , GraphicsContext3D::RGB , UNSIGNED_SHORT_5_6_5 );
+ POSSIBLE_FORMAT_TYPE_CASE(GraphicsContext3D::RGB8_SNORM , GraphicsContext3D::RGB , BYTE );
+ POSSIBLE_FORMAT_TYPE_CASE(GraphicsContext3D::R11F_G11F_B10F , GraphicsContext3D::RGB , UNSIGNED_INT_10F_11F_11F_REV );
+ POSSIBLE_FORMAT_TYPE_CASE(GraphicsContext3D::RGB9_E5 , GraphicsContext3D::RGB , UNSIGNED_INT_5_9_9_9_REV );
+ POSSIBLE_FORMAT_TYPE_CASE(GraphicsContext3D::RGB16F , GraphicsContext3D::RGB , HALF_FLOAT );
+ POSSIBLE_FORMAT_TYPE_CASE(GraphicsContext3D::RGB32F , GraphicsContext3D::RGB , FLOAT );
+ POSSIBLE_FORMAT_TYPE_CASE(GraphicsContext3D::RGB8UI , GraphicsContext3D::RGB_INTEGER , UNSIGNED_BYTE );
+ POSSIBLE_FORMAT_TYPE_CASE(GraphicsContext3D::RGB8I , GraphicsContext3D::RGB_INTEGER , BYTE );
+ POSSIBLE_FORMAT_TYPE_CASE(GraphicsContext3D::RGB16UI , GraphicsContext3D::RGB_INTEGER , UNSIGNED_SHORT );
+ POSSIBLE_FORMAT_TYPE_CASE(GraphicsContext3D::RGB16I , GraphicsContext3D::RGB_INTEGER , SHORT );
+ POSSIBLE_FORMAT_TYPE_CASE(GraphicsContext3D::RGB32UI , GraphicsContext3D::RGB_INTEGER , UNSIGNED_INT );
+ POSSIBLE_FORMAT_TYPE_CASE(GraphicsContext3D::RGB32I , GraphicsContext3D::RGB_INTEGER , INT );
+ POSSIBLE_FORMAT_TYPE_CASE(GraphicsContext3D::RGBA8 , GraphicsContext3D::RGBA , UNSIGNED_BYTE );
+ POSSIBLE_FORMAT_TYPE_CASE(GraphicsContext3D::SRGB8_ALPHA8 , GraphicsContext3D::RGBA , UNSIGNED_BYTE );
+ POSSIBLE_FORMAT_TYPE_CASE(GraphicsContext3D::RGBA8_SNORM , GraphicsContext3D::RGBA , BYTE );
+ POSSIBLE_FORMAT_TYPE_CASE(GraphicsContext3D::RGB5_A1 , GraphicsContext3D::RGBA , UNSIGNED_SHORT_5_5_5_1 );
+ POSSIBLE_FORMAT_TYPE_CASE(GraphicsContext3D::RGBA4 , GraphicsContext3D::RGBA , UNSIGNED_SHORT_4_4_4_4 );
+ POSSIBLE_FORMAT_TYPE_CASE(GraphicsContext3D::RGB10_A2 , GraphicsContext3D::RGBA , UNSIGNED_INT_2_10_10_10_REV );
+ POSSIBLE_FORMAT_TYPE_CASE(GraphicsContext3D::RGBA16F , GraphicsContext3D::RGBA , HALF_FLOAT );
+ POSSIBLE_FORMAT_TYPE_CASE(GraphicsContext3D::RGBA32F , GraphicsContext3D::RGBA , FLOAT );
+ POSSIBLE_FORMAT_TYPE_CASE(GraphicsContext3D::RGBA8UI , GraphicsContext3D::RGBA_INTEGER , UNSIGNED_BYTE );
+ POSSIBLE_FORMAT_TYPE_CASE(GraphicsContext3D::RGBA8I , GraphicsContext3D::RGBA_INTEGER , BYTE );
+ POSSIBLE_FORMAT_TYPE_CASE(GraphicsContext3D::RGB10_A2UI , GraphicsContext3D::RGBA_INTEGER , UNSIGNED_INT_2_10_10_10_REV );
+ POSSIBLE_FORMAT_TYPE_CASE(GraphicsContext3D::RGBA16UI , GraphicsContext3D::RGBA_INTEGER , UNSIGNED_SHORT );
+ POSSIBLE_FORMAT_TYPE_CASE(GraphicsContext3D::RGBA16I , GraphicsContext3D::RGBA_INTEGER , SHORT );
+ POSSIBLE_FORMAT_TYPE_CASE(GraphicsContext3D::RGBA32I , GraphicsContext3D::RGBA_INTEGER , INT );
+ POSSIBLE_FORMAT_TYPE_CASE(GraphicsContext3D::RGBA32UI , GraphicsContext3D::RGBA_INTEGER , UNSIGNED_INT );
+ POSSIBLE_FORMAT_TYPE_CASE(GraphicsContext3D::DEPTH_COMPONENT16 , GraphicsContext3D::DEPTH_COMPONENT, UNSIGNED_SHORT );
+ POSSIBLE_FORMAT_TYPE_CASE(GraphicsContext3D::DEPTH_COMPONENT , GraphicsContext3D::DEPTH_COMPONENT, UNSIGNED_SHORT );
+ POSSIBLE_FORMAT_TYPE_CASE(GraphicsContext3D::DEPTH_COMPONENT24 , GraphicsContext3D::DEPTH_COMPONENT, UNSIGNED_INT );
+ POSSIBLE_FORMAT_TYPE_CASE(GraphicsContext3D::DEPTH_COMPONENT32F, GraphicsContext3D::DEPTH_COMPONENT, FLOAT );
+ POSSIBLE_FORMAT_TYPE_CASE(GraphicsContext3D::DEPTH_STENCIL , GraphicsContext3D::DEPTH_STENCIL , UNSIGNED_INT_24_8 );
+ POSSIBLE_FORMAT_TYPE_CASE(GraphicsContext3D::DEPTH24_STENCIL8 , GraphicsContext3D::DEPTH_STENCIL , UNSIGNED_INT_24_8 );
+ POSSIBLE_FORMAT_TYPE_CASE(GraphicsContext3D::DEPTH32F_STENCIL8 , GraphicsContext3D::DEPTH_STENCIL , FLOAT_32_UNSIGNED_INT_24_8_REV);
+ POSSIBLE_FORMAT_TYPE_CASE(Extensions3D::SRGB_EXT , Extensions3D::SRGB_EXT , UNSIGNED_BYTE );
+ POSSIBLE_FORMAT_TYPE_CASE(Extensions3D::SRGB_ALPHA_EXT , Extensions3D::SRGB_ALPHA_EXT , UNSIGNED_BYTE );
+ default:
+ return false;
+ }
+#undef POSSIBLE_FORMAT_TYPE_CASE
+
+ return true;
+}
+
GC3Denum GraphicsContext3D::computeImageSizeInBytes(GC3Denum format, GC3Denum type, GC3Dsizei width, GC3Dsizei height, GC3Dint alignment, unsigned int* imageSizeInBytes, unsigned int* paddingInBytes)
{
ASSERT(imageSizeInBytes);
@@ -204,7 +329,7 @@ GC3Denum GraphicsContext3D::computeImageSizeInBytes(GC3Denum format, GC3Denum ty
if (width < 0 || height < 0)
return GraphicsContext3D::INVALID_VALUE;
unsigned int bytesPerComponent, componentsPerPixel;
- if (!computeFormatAndTypeParameters(format, type, &bytesPerComponent, &componentsPerPixel))
+ if (!computeFormatAndTypeParameters(format, type, &componentsPerPixel, &bytesPerComponent))
return GraphicsContext3D::INVALID_ENUM;
if (!width || !height) {
*imageSizeInBytes = 0;
@@ -241,14 +366,14 @@ GraphicsContext3D::ImageExtractor::ImageExtractor(Image* image, ImageHtmlDomSour
m_extractSucceeded = extractImage(premultiplyAlpha, ignoreGammaAndColorProfile);
}
-bool GraphicsContext3D::packImageData( Image* image, const void* pixels, GC3Denum format, GC3Denum type, bool flipY, AlphaOp alphaOp, DataFormat sourceFormat, unsigned width, unsigned height, unsigned sourceUnpackAlignment, Vector<uint8_t>& data)
+bool GraphicsContext3D::packImageData(Image* image, const void* pixels, GC3Denum format, GC3Denum type, bool flipY, AlphaOp alphaOp, DataFormat sourceFormat, unsigned width, unsigned height, unsigned sourceUnpackAlignment, Vector<uint8_t>& data)
{
if (!pixels)
return false;
unsigned packedSize;
// Output data is tightly packed (alignment == 1).
- if (computeImageSizeInBytes(format, type, width, height, 1, &packedSize, 0) != GraphicsContext3D::NO_ERROR)
+ if (computeImageSizeInBytes(format, type, width, height, 1, &packedSize, nullptr) != GraphicsContext3D::NO_ERROR)
return false;
data.resize(packedSize);
@@ -268,7 +393,7 @@ bool GraphicsContext3D::extractImageData(ImageData* imageData, GC3Denum format,
unsigned int packedSize;
// Output data is tightly packed (alignment == 1).
- if (computeImageSizeInBytes(format, type, width, height, 1, &packedSize, 0) != GraphicsContext3D::NO_ERROR)
+ if (computeImageSizeInBytes(format, type, width, height, 1, &packedSize, nullptr) != GraphicsContext3D::NO_ERROR)
return false;
data.resize(packedSize);
@@ -341,6 +466,29 @@ bool GraphicsContext3D::packPixels(const uint8_t* sourceData, DataFormat sourceD
int remainder = sourceUnpackAlignment ? (validSrc % sourceUnpackAlignment) : 0;
int srcStride = remainder ? (validSrc + sourceUnpackAlignment - remainder) : validSrc;
+ // FIXME: Implement packing pixels to WebGL 2 formats
+ switch (destinationFormat) {
+ case GraphicsContext3D::RED:
+ case GraphicsContext3D::RED_INTEGER:
+ case GraphicsContext3D::RG:
+ case GraphicsContext3D::RG_INTEGER:
+ case GraphicsContext3D::RGB_INTEGER:
+ case GraphicsContext3D::RGBA_INTEGER:
+ case GraphicsContext3D::DEPTH_COMPONENT:
+ case GraphicsContext3D::DEPTH_STENCIL:
+ return false;
+ }
+ switch (destinationType) {
+ case GraphicsContext3D::BYTE:
+ case GraphicsContext3D::SHORT:
+ case GraphicsContext3D::INT:
+ case GraphicsContext3D::UNSIGNED_INT_2_10_10_10_REV:
+ case GraphicsContext3D::UNSIGNED_INT_10F_11F_11F_REV:
+ case GraphicsContext3D::UNSIGNED_INT_5_9_9_9_REV:
+ case GraphicsContext3D::UNSIGNED_INT_24_8:
+ return false;
+ }
+
DataFormat dstDataFormat = getDataFormat(destinationFormat, destinationType);
int dstStride = width * TexelBytesForFormat(dstDataFormat);
if (flipY) {
@@ -404,21 +552,73 @@ unsigned GraphicsContext3D::getClearBitsByAttachmentType(GC3Denum attachment)
unsigned GraphicsContext3D::getClearBitsByFormat(GC3Denum format)
{
switch (format) {
- case GraphicsContext3D::ALPHA:
- case GraphicsContext3D::LUMINANCE:
- case GraphicsContext3D::LUMINANCE_ALPHA:
case GraphicsContext3D::RGB:
- case GraphicsContext3D::RGB565:
case GraphicsContext3D::RGBA:
- case GraphicsContext3D::RGBA4:
+ case GraphicsContext3D::LUMINANCE_ALPHA:
+ case GraphicsContext3D::LUMINANCE:
+ case GraphicsContext3D::ALPHA:
+ case GraphicsContext3D::R8:
+ case GraphicsContext3D::R8_SNORM:
+ case GraphicsContext3D::R16F:
+ case GraphicsContext3D::R32F:
+ case GraphicsContext3D::R8UI:
+ case GraphicsContext3D::R8I:
+ case GraphicsContext3D::R16UI:
+ case GraphicsContext3D::R16I:
+ case GraphicsContext3D::R32UI:
+ case GraphicsContext3D::R32I:
+ case GraphicsContext3D::RG8:
+ case GraphicsContext3D::RG8_SNORM:
+ case GraphicsContext3D::RG16F:
+ case GraphicsContext3D::RG32F:
+ case GraphicsContext3D::RG8UI:
+ case GraphicsContext3D::RG8I:
+ case GraphicsContext3D::RG16UI:
+ case GraphicsContext3D::RG16I:
+ case GraphicsContext3D::RG32UI:
+ case GraphicsContext3D::RG32I:
+ case GraphicsContext3D::RGB8:
+ case GraphicsContext3D::SRGB8:
+ case GraphicsContext3D::RGB565:
+ case GraphicsContext3D::RGB8_SNORM:
+ case GraphicsContext3D::R11F_G11F_B10F:
+ case GraphicsContext3D::RGB9_E5:
+ case GraphicsContext3D::RGB16F:
+ case GraphicsContext3D::RGB32F:
+ case GraphicsContext3D::RGB8UI:
+ case GraphicsContext3D::RGB8I:
+ case GraphicsContext3D::RGB16UI:
+ case GraphicsContext3D::RGB16I:
+ case GraphicsContext3D::RGB32UI:
+ case GraphicsContext3D::RGB32I:
+ case GraphicsContext3D::RGBA8:
+ case GraphicsContext3D::SRGB8_ALPHA8:
+ case GraphicsContext3D::RGBA8_SNORM:
case GraphicsContext3D::RGB5_A1:
+ case GraphicsContext3D::RGBA4:
+ case GraphicsContext3D::RGB10_A2:
+ case GraphicsContext3D::RGBA16F:
+ case GraphicsContext3D::RGBA32F:
+ case GraphicsContext3D::RGBA8UI:
+ case GraphicsContext3D::RGBA8I:
+ case GraphicsContext3D::RGB10_A2UI:
+ case GraphicsContext3D::RGBA16UI:
+ case GraphicsContext3D::RGBA16I:
+ case GraphicsContext3D::RGBA32I:
+ case GraphicsContext3D::RGBA32UI:
+ case Extensions3D::SRGB_EXT:
+ case Extensions3D::SRGB_ALPHA_EXT:
return GraphicsContext3D::COLOR_BUFFER_BIT;
case GraphicsContext3D::DEPTH_COMPONENT16:
+ case GraphicsContext3D::DEPTH_COMPONENT24:
+ case GraphicsContext3D::DEPTH_COMPONENT32F:
case GraphicsContext3D::DEPTH_COMPONENT:
return GraphicsContext3D::DEPTH_BUFFER_BIT;
case GraphicsContext3D::STENCIL_INDEX8:
return GraphicsContext3D::STENCIL_BUFFER_BIT;
case GraphicsContext3D::DEPTH_STENCIL:
+ case GraphicsContext3D::DEPTH24_STENCIL8:
+ case GraphicsContext3D::DEPTH32F_STENCIL8:
return GraphicsContext3D::DEPTH_BUFFER_BIT | GraphicsContext3D::STENCIL_BUFFER_BIT;
default:
return 0;
@@ -436,10 +636,12 @@ unsigned GraphicsContext3D::getChannelBitsByFormat(GC3Denum format)
return ChannelRGBA;
case GraphicsContext3D::RGB:
case GraphicsContext3D::RGB565:
+ case Extensions3D::SRGB_EXT:
return ChannelRGB;
case GraphicsContext3D::RGBA:
case GraphicsContext3D::RGBA4:
case GraphicsContext3D::RGB5_A1:
+ case Extensions3D::SRGB_ALPHA_EXT:
return ChannelRGBA;
case GraphicsContext3D::DEPTH_COMPONENT16:
case GraphicsContext3D::DEPTH_COMPONENT:
@@ -453,6 +655,12 @@ unsigned GraphicsContext3D::getChannelBitsByFormat(GC3Denum format)
}
}
+#if !PLATFORM(MAC)
+void GraphicsContext3D::setContextVisibility(bool)
+{
+}
+#endif
+
} // namespace WebCore
-#endif // USE(3D_GRAPHICS)
+#endif // ENABLE(GRAPHICS_CONTEXT_3D)
diff --git a/Source/WebCore/platform/graphics/GraphicsContext3D.h b/Source/WebCore/platform/graphics/GraphicsContext3D.h
index 132c5e9e2..75b7e1f92 100644
--- a/Source/WebCore/platform/graphics/GraphicsContext3D.h
+++ b/Source/WebCore/platform/graphics/GraphicsContext3D.h
@@ -10,10 +10,10 @@
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
- * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
@@ -23,10 +23,10 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef GraphicsContext3D_h
-#define GraphicsContext3D_h
+#pragma once
#include "ANGLEWebKitBridge.h"
+#include "GraphicsContext3DAttributes.h"
#include "GraphicsTypes3D.h"
#include "Image.h"
#include "IntRect.h"
@@ -43,16 +43,14 @@
#endif
// FIXME: Find a better way to avoid the name confliction for NO_ERROR.
-#if PLATFORM(WIN) || (PLATFORM(GTK) && OS(WINDOWS))
+#if PLATFORM(WIN)
#undef NO_ERROR
-#endif
-
-#if PLATFORM(GTK)
+#elif PLATFORM(GTK)
// This define is from the X11 headers, but it's used below, so we must undefine it.
#undef VERSION
#endif
-#if PLATFORM(MAC)
+#if PLATFORM(COCOA)
#if PLATFORM(IOS)
#include <OpenGLES/ES2/gl.h>
#ifdef __OBJC__
@@ -62,7 +60,7 @@
#include <wtf/RetainPtr.h>
OBJC_CLASS CALayer;
OBJC_CLASS WebGLLayer;
-#elif PLATFORM(GTK) || PLATFORM(EFL)
+#elif PLATFORM(GTK) || PLATFORM(WIN_CAIRO)
typedef unsigned int GLuint;
#endif
@@ -86,7 +84,6 @@ const PlatformGraphicsContext3D NullPlatformGraphicsContext3D = 0;
const Platform3DObject NullPlatform3DObject = 0;
namespace WebCore {
-class DrawingBuffer;
class Extensions3D;
#if USE(OPENGL_ES_2)
class Extensions3DOpenGLES;
@@ -100,9 +97,13 @@ class ImageSource;
class ImageData;
class IntRect;
class IntSize;
+class WebGLRenderingContextBase;
#if USE(CAIRO)
class PlatformContextCairo;
#endif
+#if USE(TEXTURE_MAPPER)
+class TextureMapperGC3DPlatformLayer;
+#endif
typedef WTF::HashMap<CString, uint64_t> ShaderNameHash;
@@ -117,6 +118,7 @@ class GraphicsContext3DPrivate;
class GraphicsContext3D : public RefCounted<GraphicsContext3D> {
public:
enum {
+ // WebGL 1 constants
DEPTH_BUFFER_BIT = 0x00000100,
STENCIL_BUFFER_BIT = 0x00000400,
COLOR_BUFFER_BIT = 0x00004000,
@@ -371,6 +373,8 @@ public:
VERTEX_ATTRIB_ARRAY_NORMALIZED = 0x886A,
VERTEX_ATTRIB_ARRAY_POINTER = 0x8645,
VERTEX_ATTRIB_ARRAY_BUFFER_BINDING = 0x889F,
+ IMPLEMENTATION_COLOR_READ_TYPE = 0x8B9A,
+ IMPLEMENTATION_COLOR_READ_FORMAT = 0x8B9B,
COMPILE_STATUS = 0x8B81,
INFO_LOG_LENGTH = 0x8B84,
SHADER_SOURCE_LENGTH = 0x8B88,
@@ -391,9 +395,6 @@ public:
DEPTH_COMPONENT16 = 0x81A5,
STENCIL_INDEX = 0x1901,
STENCIL_INDEX8 = 0x8D48,
- DEPTH_STENCIL = 0x84F9,
- UNSIGNED_INT_24_8 = 0x84FA,
- DEPTH24_STENCIL8 = 0x88F0,
RENDERBUFFER_WIDTH = 0x8D42,
RENDERBUFFER_HEIGHT = 0x8D43,
RENDERBUFFER_INTERNAL_FORMAT = 0x8D44,
@@ -410,7 +411,6 @@ public:
COLOR_ATTACHMENT0 = 0x8CE0,
DEPTH_ATTACHMENT = 0x8D00,
STENCIL_ATTACHMENT = 0x8D20,
- DEPTH_STENCIL_ATTACHMENT = 0x821A,
NONE = 0,
FRAMEBUFFER_COMPLETE = 0x8CD5,
FRAMEBUFFER_INCOMPLETE_ATTACHMENT = 0x8CD6,
@@ -428,37 +428,297 @@ public:
CONTEXT_LOST_WEBGL = 0x9242,
UNPACK_COLORSPACE_CONVERSION_WEBGL = 0x9243,
BROWSER_DEFAULT_WEBGL = 0x9244,
- VERTEX_ATTRIB_ARRAY_DIVISOR_ANGLE = 0x88FE
- };
-
- // Context creation attributes.
- struct Attributes {
- Attributes()
- : alpha(true)
- , depth(true)
- , stencil(false)
- , antialias(true)
- , premultipliedAlpha(true)
- , preserveDrawingBuffer(false)
- , noExtensions(false)
- , shareResources(true)
- , preferDiscreteGPU(false)
- , multithreaded(false)
- , forceSoftwareRenderer(false)
- {
- }
-
- bool alpha;
- bool depth;
- bool stencil;
- bool antialias;
- bool premultipliedAlpha;
- bool preserveDrawingBuffer;
- bool noExtensions;
- bool shareResources;
- bool preferDiscreteGPU;
- bool multithreaded;
- bool forceSoftwareRenderer;
+ VERTEX_ATTRIB_ARRAY_DIVISOR_ANGLE = 0x88FE,
+
+ // WebGL2 constants
+ READ_BUFFER = 0x0C02,
+ UNPACK_ROW_LENGTH = 0x0CF2,
+ UNPACK_SKIP_ROWS = 0x0CF3,
+ UNPACK_SKIP_PIXELS = 0x0CF4,
+ PACK_ROW_LENGTH = 0x0D02,
+ PACK_SKIP_ROWS = 0x0D03,
+ PACK_SKIP_PIXELS = 0x0D04,
+ COLOR = 0x1800,
+ DEPTH = 0x1801,
+ STENCIL = 0x1802,
+ RED = 0x1903,
+ RGB8 = 0x8051,
+ RGBA8 = 0x8058,
+ RGB10_A2 = 0x8059,
+ TEXTURE_BINDING_3D = 0x806A,
+ UNPACK_SKIP_IMAGES = 0x806D,
+ UNPACK_IMAGE_HEIGHT = 0x806E,
+ TEXTURE_3D = 0x806F,
+ TEXTURE_WRAP_R = 0x8072,
+ MAX_3D_TEXTURE_SIZE = 0x8073,
+ UNSIGNED_INT_2_10_10_10_REV = 0x8368,
+ MAX_ELEMENTS_VERTICES = 0x80E8,
+ MAX_ELEMENTS_INDICES = 0x80E9,
+ TEXTURE_MIN_LOD = 0x813A,
+ TEXTURE_MAX_LOD = 0x813B,
+ TEXTURE_BASE_LEVEL = 0x813C,
+ TEXTURE_MAX_LEVEL = 0x813D,
+ MIN = 0x8007,
+ MAX = 0x8008,
+ DEPTH_COMPONENT24 = 0x81A6,
+ MAX_TEXTURE_LOD_BIAS = 0x84FD,
+ TEXTURE_COMPARE_MODE = 0x884C,
+ TEXTURE_COMPARE_FUNC = 0x884D,
+ CURRENT_QUERY = 0x8865,
+ QUERY_RESULT = 0x8866,
+ QUERY_RESULT_AVAILABLE = 0x8867,
+ STREAM_READ = 0x88E1,
+ STREAM_COPY = 0x88E2,
+ STATIC_READ = 0x88E5,
+ STATIC_COPY = 0x88E6,
+ DYNAMIC_READ = 0x88E9,
+ DYNAMIC_COPY = 0x88EA,
+ MAX_DRAW_BUFFERS = 0x8824,
+ DRAW_BUFFER0 = 0x8825,
+ DRAW_BUFFER1 = 0x8826,
+ DRAW_BUFFER2 = 0x8827,
+ DRAW_BUFFER3 = 0x8828,
+ DRAW_BUFFER4 = 0x8829,
+ DRAW_BUFFER5 = 0x882A,
+ DRAW_BUFFER6 = 0x882B,
+ DRAW_BUFFER7 = 0x882C,
+ DRAW_BUFFER8 = 0x882D,
+ DRAW_BUFFER9 = 0x882E,
+ DRAW_BUFFER10 = 0x882F,
+ DRAW_BUFFER11 = 0x8830,
+ DRAW_BUFFER12 = 0x8831,
+ DRAW_BUFFER13 = 0x8832,
+ DRAW_BUFFER14 = 0x8833,
+ DRAW_BUFFER15 = 0x8834,
+ MAX_FRAGMENT_UNIFORM_COMPONENTS = 0x8B49,
+ MAX_VERTEX_UNIFORM_COMPONENTS = 0x8B4A,
+ SAMPLER_3D = 0x8B5F,
+ SAMPLER_2D_SHADOW = 0x8B62,
+ FRAGMENT_SHADER_DERIVATIVE_HINT = 0x8B8B,
+ PIXEL_PACK_BUFFER = 0x88EB,
+ PIXEL_UNPACK_BUFFER = 0x88EC,
+ PIXEL_PACK_BUFFER_BINDING = 0x88ED,
+ PIXEL_UNPACK_BUFFER_BINDING = 0x88EF,
+ FLOAT_MAT2x3 = 0x8B65,
+ FLOAT_MAT2x4 = 0x8B66,
+ FLOAT_MAT3x2 = 0x8B67,
+ FLOAT_MAT3x4 = 0x8B68,
+ FLOAT_MAT4x2 = 0x8B69,
+ FLOAT_MAT4x3 = 0x8B6A,
+ SRGB = 0x8C40,
+ SRGB8 = 0x8C41,
+ SRGB_ALPHA = 0x8C42,
+ SRGB8_ALPHA8 = 0x8C43,
+ COMPARE_REF_TO_TEXTURE = 0x884E,
+ RGBA32F = 0x8814,
+ RGB32F = 0x8815,
+ RGBA16F = 0x881A,
+ RGB16F = 0x881B,
+ VERTEX_ATTRIB_ARRAY_INTEGER = 0x88FD,
+ MAX_ARRAY_TEXTURE_LAYERS = 0x88FF,
+ MIN_PROGRAM_TEXEL_OFFSET = 0x8904,
+ MAX_PROGRAM_TEXEL_OFFSET = 0x8905,
+ MAX_VARYING_COMPONENTS = 0x8B4B,
+ TEXTURE_2D_ARRAY = 0x8C1A,
+ TEXTURE_BINDING_2D_ARRAY = 0x8C1D,
+ R11F_G11F_B10F = 0x8C3A,
+ UNSIGNED_INT_10F_11F_11F_REV = 0x8C3B,
+ RGB9_E5 = 0x8C3D,
+ UNSIGNED_INT_5_9_9_9_REV = 0x8C3E,
+ TRANSFORM_FEEDBACK_BUFFER_MODE = 0x8C7F,
+ MAX_TRANSFORM_FEEDBACK_SEPARATE_COMPONENTS = 0x8C80,
+ TRANSFORM_FEEDBACK_VARYINGS = 0x8C83,
+ TRANSFORM_FEEDBACK_BUFFER_START = 0x8C84,
+ TRANSFORM_FEEDBACK_BUFFER_SIZE = 0x8C85,
+ TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN = 0x8C88,
+ RASTERIZER_DISCARD = 0x8C89,
+ MAX_TRANSFORM_FEEDBACK_INTERLEAVED_COMPONENTS = 0x8C8A,
+ MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS = 0x8C8B,
+ INTERLEAVED_ATTRIBS = 0x8C8C,
+ SEPARATE_ATTRIBS = 0x8C8D,
+ TRANSFORM_FEEDBACK_BUFFER = 0x8C8E,
+ TRANSFORM_FEEDBACK_BUFFER_BINDING = 0x8C8F,
+ RGBA32UI = 0x8D70,
+ RGB32UI = 0x8D71,
+ RGBA16UI = 0x8D76,
+ RGB16UI = 0x8D77,
+ RGBA8UI = 0x8D7C,
+ RGB8UI = 0x8D7D,
+ RGBA32I = 0x8D82,
+ RGB32I = 0x8D83,
+ RGBA16I = 0x8D88,
+ RGB16I = 0x8D89,
+ RGBA8I = 0x8D8E,
+ RGB8I = 0x8D8F,
+ RED_INTEGER = 0x8D94,
+ RGB_INTEGER = 0x8D98,
+ RGBA_INTEGER = 0x8D99,
+ SAMPLER_2D_ARRAY = 0x8DC1,
+ SAMPLER_2D_ARRAY_SHADOW = 0x8DC4,
+ SAMPLER_CUBE_SHADOW = 0x8DC5,
+ UNSIGNED_INT_VEC2 = 0x8DC6,
+ UNSIGNED_INT_VEC3 = 0x8DC7,
+ UNSIGNED_INT_VEC4 = 0x8DC8,
+ INT_SAMPLER_2D = 0x8DCA,
+ INT_SAMPLER_3D = 0x8DCB,
+ INT_SAMPLER_CUBE = 0x8DCC,
+ INT_SAMPLER_2D_ARRAY = 0x8DCF,
+ UNSIGNED_INT_SAMPLER_2D = 0x8DD2,
+ UNSIGNED_INT_SAMPLER_3D = 0x8DD3,
+ UNSIGNED_INT_SAMPLER_CUBE = 0x8DD4,
+ UNSIGNED_INT_SAMPLER_2D_ARRAY = 0x8DD7,
+ DEPTH_COMPONENT32F = 0x8CAC,
+ DEPTH32F_STENCIL8 = 0x8CAD,
+ FLOAT_32_UNSIGNED_INT_24_8_REV = 0x8DAD,
+ FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING = 0x8210,
+ FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE = 0x8211,
+ FRAMEBUFFER_ATTACHMENT_RED_SIZE = 0x8212,
+ FRAMEBUFFER_ATTACHMENT_GREEN_SIZE = 0x8213,
+ FRAMEBUFFER_ATTACHMENT_BLUE_SIZE = 0x8214,
+ FRAMEBUFFER_ATTACHMENT_ALPHA_SIZE = 0x8215,
+ FRAMEBUFFER_ATTACHMENT_DEPTH_SIZE = 0x8216,
+ FRAMEBUFFER_ATTACHMENT_STENCIL_SIZE = 0x8217,
+ FRAMEBUFFER_DEFAULT = 0x8218,
+ DEPTH_STENCIL_ATTACHMENT = 0x821A,
+ DEPTH_STENCIL = 0x84F9,
+ UNSIGNED_INT_24_8 = 0x84FA,
+ DEPTH24_STENCIL8 = 0x88F0,
+ UNSIGNED_NORMALIZED = 0x8C17,
+ DRAW_FRAMEBUFFER_BINDING = 0x8CA6, /* Same as FRAMEBUFFER_BINDING */
+ READ_FRAMEBUFFER = 0x8CA8,
+ DRAW_FRAMEBUFFER = 0x8CA9,
+ READ_FRAMEBUFFER_BINDING = 0x8CAA,
+ RENDERBUFFER_SAMPLES = 0x8CAB,
+ FRAMEBUFFER_ATTACHMENT_TEXTURE_LAYER = 0x8CD4,
+ MAX_COLOR_ATTACHMENTS = 0x8CDF,
+ COLOR_ATTACHMENT1 = 0x8CE1,
+ COLOR_ATTACHMENT2 = 0x8CE2,
+ COLOR_ATTACHMENT3 = 0x8CE3,
+ COLOR_ATTACHMENT4 = 0x8CE4,
+ COLOR_ATTACHMENT5 = 0x8CE5,
+ COLOR_ATTACHMENT6 = 0x8CE6,
+ COLOR_ATTACHMENT7 = 0x8CE7,
+ COLOR_ATTACHMENT8 = 0x8CE8,
+ COLOR_ATTACHMENT9 = 0x8CE9,
+ COLOR_ATTACHMENT10 = 0x8CEA,
+ COLOR_ATTACHMENT11 = 0x8CEB,
+ COLOR_ATTACHMENT12 = 0x8CEC,
+ COLOR_ATTACHMENT13 = 0x8CED,
+ COLOR_ATTACHMENT14 = 0x8CEE,
+ COLOR_ATTACHMENT15 = 0x8CEF,
+ FRAMEBUFFER_INCOMPLETE_MULTISAMPLE = 0x8D56,
+ MAX_SAMPLES = 0x8D57,
+ HALF_FLOAT = 0x140B,
+ RG = 0x8227,
+ RG_INTEGER = 0x8228,
+ R8 = 0x8229,
+ RG8 = 0x822B,
+ R16F = 0x822D,
+ R32F = 0x822E,
+ RG16F = 0x822F,
+ RG32F = 0x8230,
+ R8I = 0x8231,
+ R8UI = 0x8232,
+ R16I = 0x8233,
+ R16UI = 0x8234,
+ R32I = 0x8235,
+ R32UI = 0x8236,
+ RG8I = 0x8237,
+ RG8UI = 0x8238,
+ RG16I = 0x8239,
+ RG16UI = 0x823A,
+ RG32I = 0x823B,
+ RG32UI = 0x823C,
+ VERTEX_ARRAY_BINDING = 0x85B5,
+ R8_SNORM = 0x8F94,
+ RG8_SNORM = 0x8F95,
+ RGB8_SNORM = 0x8F96,
+ RGBA8_SNORM = 0x8F97,
+ SIGNED_NORMALIZED = 0x8F9C,
+ COPY_READ_BUFFER = 0x8F36,
+ COPY_WRITE_BUFFER = 0x8F37,
+ COPY_READ_BUFFER_BINDING = 0x8F36, /* Same as COPY_READ_BUFFER */
+ COPY_WRITE_BUFFER_BINDING = 0x8F37, /* Same as COPY_WRITE_BUFFER */
+ UNIFORM_BUFFER = 0x8A11,
+ UNIFORM_BUFFER_BINDING = 0x8A28,
+ UNIFORM_BUFFER_START = 0x8A29,
+ UNIFORM_BUFFER_SIZE = 0x8A2A,
+ MAX_VERTEX_UNIFORM_BLOCKS = 0x8A2B,
+ MAX_FRAGMENT_UNIFORM_BLOCKS = 0x8A2D,
+ MAX_COMBINED_UNIFORM_BLOCKS = 0x8A2E,
+ MAX_UNIFORM_BUFFER_BINDINGS = 0x8A2F,
+ MAX_UNIFORM_BLOCK_SIZE = 0x8A30,
+ MAX_COMBINED_VERTEX_UNIFORM_COMPONENTS = 0x8A31,
+ MAX_COMBINED_FRAGMENT_UNIFORM_COMPONENTS = 0x8A33,
+ UNIFORM_BUFFER_OFFSET_ALIGNMENT = 0x8A34,
+ ACTIVE_UNIFORM_BLOCKS = 0x8A36,
+ UNIFORM_TYPE = 0x8A37,
+ UNIFORM_SIZE = 0x8A38,
+ UNIFORM_BLOCK_INDEX = 0x8A3A,
+ UNIFORM_OFFSET = 0x8A3B,
+ UNIFORM_ARRAY_STRIDE = 0x8A3C,
+ UNIFORM_MATRIX_STRIDE = 0x8A3D,
+ UNIFORM_IS_ROW_MAJOR = 0x8A3E,
+ UNIFORM_BLOCK_BINDING = 0x8A3F,
+ UNIFORM_BLOCK_DATA_SIZE = 0x8A40,
+ UNIFORM_BLOCK_ACTIVE_UNIFORMS = 0x8A42,
+ UNIFORM_BLOCK_ACTIVE_UNIFORM_INDICES = 0x8A43,
+ UNIFORM_BLOCK_REFERENCED_BY_VERTEX_SHADER = 0x8A44,
+ UNIFORM_BLOCK_REFERENCED_BY_FRAGMENT_SHADER = 0x8A46,
+ INVALID_INDEX = 0xFFFFFFFF,
+ MAX_VERTEX_OUTPUT_COMPONENTS = 0x9122,
+ MAX_FRAGMENT_INPUT_COMPONENTS = 0x9125,
+ MAX_SERVER_WAIT_TIMEOUT = 0x9111,
+ OBJECT_TYPE = 0x9112,
+ SYNC_CONDITION = 0x9113,
+ SYNC_STATUS = 0x9114,
+ SYNC_FLAGS = 0x9115,
+ SYNC_FENCE = 0x9116,
+ SYNC_GPU_COMMANDS_COMPLETE = 0x9117,
+ UNSIGNALED = 0x9118,
+ SIGNALED = 0x9119,
+ ALREADY_SIGNALED = 0x911A,
+ TIMEOUT_EXPIRED = 0x911B,
+ CONDITION_SATISFIED = 0x911C,
+#if PLATFORM(WIN)
+ WAIT_FAILED_WIN = 0x911D,
+#else
+ WAIT_FAILED = 0x911D,
+#endif
+ SYNC_FLUSH_COMMANDS_BIT = 0x00000001,
+ VERTEX_ATTRIB_ARRAY_DIVISOR = 0x88FE,
+ ANY_SAMPLES_PASSED = 0x8C2F,
+ ANY_SAMPLES_PASSED_CONSERVATIVE = 0x8D6A,
+ SAMPLER_BINDING = 0x8919,
+ RGB10_A2UI = 0x906F,
+ TEXTURE_SWIZZLE_R = 0x8E42,
+ TEXTURE_SWIZZLE_G = 0x8E43,
+ TEXTURE_SWIZZLE_B = 0x8E44,
+ TEXTURE_SWIZZLE_A = 0x8E45,
+ GREEN = 0x1904,
+ BLUE = 0x1905,
+ INT_2_10_10_10_REV = 0x8D9F,
+ TRANSFORM_FEEDBACK = 0x8E22,
+ TRANSFORM_FEEDBACK_PAUSED = 0x8E23,
+ TRANSFORM_FEEDBACK_ACTIVE = 0x8E24,
+ TRANSFORM_FEEDBACK_BINDING = 0x8E25,
+ COMPRESSED_R11_EAC = 0x9270,
+ COMPRESSED_SIGNED_R11_EAC = 0x9271,
+ COMPRESSED_RG11_EAC = 0x9272,
+ COMPRESSED_SIGNED_RG11_EAC = 0x9273,
+ COMPRESSED_RGB8_ETC2 = 0x9274,
+ COMPRESSED_SRGB8_ETC2 = 0x9275,
+ COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2 = 0x9276,
+ COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2 = 0x9277,
+ COMPRESSED_RGBA8_ETC2_EAC = 0x9278,
+ COMPRESSED_SRGB8_ALPHA8_ETC2_EAC = 0x9279,
+ TEXTURE_IMMUTABLE_FORMAT = 0x912F,
+ MAX_ELEMENT_INDEX = 0x8D6B,
+ NUM_SAMPLE_COUNTS = 0x9380,
+ TEXTURE_IMMUTABLE_LEVELS = 0x82DF,
+
+ // OpenGL ES 3 constants
+ MAP_READ_BIT = 0x0001
};
enum RenderStyle {
@@ -479,26 +739,25 @@ public:
virtual ~ErrorMessageCallback() { }
};
- void setContextLostCallback(PassOwnPtr<ContextLostCallback>);
- void setErrorMessageCallback(PassOwnPtr<ErrorMessageCallback>);
+ void setContextLostCallback(std::unique_ptr<ContextLostCallback>);
+ void setErrorMessageCallback(std::unique_ptr<ErrorMessageCallback>);
- static PassRefPtr<GraphicsContext3D> create(Attributes, HostWindow*, RenderStyle = RenderOffscreen);
- static PassRefPtr<GraphicsContext3D> createForCurrentGLContext();
+ static RefPtr<GraphicsContext3D> create(GraphicsContext3DAttributes, HostWindow*, RenderStyle = RenderOffscreen);
+ static RefPtr<GraphicsContext3D> createForCurrentGLContext();
~GraphicsContext3D();
-#if PLATFORM(MAC)
+#if PLATFORM(COCOA)
PlatformGraphicsContext3D platformGraphicsContext3D() const { return m_contextObj; }
Platform3DObject platformTexture() const { return m_compositorTexture; }
CALayer* platformLayer() const { return reinterpret_cast<CALayer*>(m_webGLLayer.get()); }
#else
PlatformGraphicsContext3D platformGraphicsContext3D();
Platform3DObject platformTexture() const;
-#if USE(ACCELERATED_COMPOSITING)
PlatformLayer* platformLayer() const;
#endif
-#endif
bool makeContextCurrent();
+ void setWebGLContext(WebGLRenderingContextBase* base) { m_webglContext = base; }
// With multisampling on, blit from multisampleFBO to regular FBO.
void prepareTexture();
@@ -538,6 +797,8 @@ public:
unsigned int* imageSizeInBytes,
unsigned int* paddingInBytes);
+ static bool possibleFormatAndTypeForInternalFormat(GC3Denum internalFormat, GC3Denum& format, GC3Denum& type);
+
// Extracts the contents of the given ImageData into the passed Vector,
// packing the pixel data according to the given format and type,
// and obeying the flipY and premultiplyAlpha flags. Returns true
@@ -611,45 +872,55 @@ public:
ALWAYS_INLINE static bool hasAlpha(DataFormat format)
{
- return format == GraphicsContext3D::DataFormatA8
- || format == GraphicsContext3D::DataFormatA16F
- || format == GraphicsContext3D::DataFormatA32F
- || format == GraphicsContext3D::DataFormatRA8
- || format == GraphicsContext3D::DataFormatAR8
- || format == GraphicsContext3D::DataFormatRA16F
- || format == GraphicsContext3D::DataFormatRA32F
- || format == GraphicsContext3D::DataFormatRGBA8
- || format == GraphicsContext3D::DataFormatBGRA8
- || format == GraphicsContext3D::DataFormatARGB8
- || format == GraphicsContext3D::DataFormatABGR8
- || format == GraphicsContext3D::DataFormatRGBA16F
- || format == GraphicsContext3D::DataFormatRGBA32F
- || format == GraphicsContext3D::DataFormatRGBA4444
- || format == GraphicsContext3D::DataFormatRGBA5551;
+ switch (format) {
+ case GraphicsContext3D::DataFormatA8:
+ case GraphicsContext3D::DataFormatA16F:
+ case GraphicsContext3D::DataFormatA32F:
+ case GraphicsContext3D::DataFormatRA8:
+ case GraphicsContext3D::DataFormatAR8:
+ case GraphicsContext3D::DataFormatRA16F:
+ case GraphicsContext3D::DataFormatRA32F:
+ case GraphicsContext3D::DataFormatRGBA8:
+ case GraphicsContext3D::DataFormatBGRA8:
+ case GraphicsContext3D::DataFormatARGB8:
+ case GraphicsContext3D::DataFormatABGR8:
+ case GraphicsContext3D::DataFormatRGBA16F:
+ case GraphicsContext3D::DataFormatRGBA32F:
+ case GraphicsContext3D::DataFormatRGBA4444:
+ case GraphicsContext3D::DataFormatRGBA5551:
+ return true;
+ default:
+ return false;
+ }
}
ALWAYS_INLINE static bool hasColor(DataFormat format)
{
- return format == GraphicsContext3D::DataFormatRGBA8
- || format == GraphicsContext3D::DataFormatRGBA16F
- || format == GraphicsContext3D::DataFormatRGBA32F
- || format == GraphicsContext3D::DataFormatRGB8
- || format == GraphicsContext3D::DataFormatRGB16F
- || format == GraphicsContext3D::DataFormatRGB32F
- || format == GraphicsContext3D::DataFormatBGR8
- || format == GraphicsContext3D::DataFormatBGRA8
- || format == GraphicsContext3D::DataFormatARGB8
- || format == GraphicsContext3D::DataFormatABGR8
- || format == GraphicsContext3D::DataFormatRGBA5551
- || format == GraphicsContext3D::DataFormatRGBA4444
- || format == GraphicsContext3D::DataFormatRGB565
- || format == GraphicsContext3D::DataFormatR8
- || format == GraphicsContext3D::DataFormatR16F
- || format == GraphicsContext3D::DataFormatR32F
- || format == GraphicsContext3D::DataFormatRA8
- || format == GraphicsContext3D::DataFormatRA16F
- || format == GraphicsContext3D::DataFormatRA32F
- || format == GraphicsContext3D::DataFormatAR8;
+ switch (format) {
+ case GraphicsContext3D::DataFormatRGBA8:
+ case GraphicsContext3D::DataFormatRGBA16F:
+ case GraphicsContext3D::DataFormatRGBA32F:
+ case GraphicsContext3D::DataFormatRGB8:
+ case GraphicsContext3D::DataFormatRGB16F:
+ case GraphicsContext3D::DataFormatRGB32F:
+ case GraphicsContext3D::DataFormatBGR8:
+ case GraphicsContext3D::DataFormatBGRA8:
+ case GraphicsContext3D::DataFormatARGB8:
+ case GraphicsContext3D::DataFormatABGR8:
+ case GraphicsContext3D::DataFormatRGBA5551:
+ case GraphicsContext3D::DataFormatRGBA4444:
+ case GraphicsContext3D::DataFormatRGB565:
+ case GraphicsContext3D::DataFormatR8:
+ case GraphicsContext3D::DataFormatR16F:
+ case GraphicsContext3D::DataFormatR32F:
+ case GraphicsContext3D::DataFormatRA8:
+ case GraphicsContext3D::DataFormatRA16F:
+ case GraphicsContext3D::DataFormatRA32F:
+ case GraphicsContext3D::DataFormatAR8:
+ return true;
+ default:
+ return false;
+ }
}
// Check if the format is one of the formats from the ImageData or DOM elements.
@@ -695,6 +966,13 @@ public:
void bufferData(GC3Denum target, GC3Dsizeiptr size, const void* data, GC3Denum usage);
void bufferSubData(GC3Denum target, GC3Dintptr offset, GC3Dsizeiptr size, const void* data);
+ void* mapBufferRange(GC3Denum target, GC3Dintptr offset, GC3Dsizeiptr length, GC3Dbitfield access);
+ GC3Dboolean unmapBuffer(GC3Denum target);
+ void copyBufferSubData(GC3Denum readTarget, GC3Denum writeTarget, GC3Dintptr readOffset, GC3Dintptr writeOffset, GC3Dsizeiptr);
+
+ void texStorage2D(GC3Denum target, GC3Dsizei levels, GC3Denum internalformat, GC3Dsizei width, GC3Dsizei height);
+ void texStorage3D(GC3Denum target, GC3Dsizei levels, GC3Denum internalformat, GC3Dsizei width, GC3Dsizei height, GC3Dsizei depth);
+
GC3Denum checkFramebufferStatus(GC3Denum target);
void clear(GC3Dbitfield mask);
void clearColor(GC3Dclampf red, GC3Dclampf green, GC3Dclampf blue, GC3Dclampf alpha);
@@ -734,11 +1012,12 @@ public:
GC3Dint getAttribLocation(Platform3DObject, const String& name);
void getBooleanv(GC3Denum pname, GC3Dboolean* value);
void getBufferParameteriv(GC3Denum target, GC3Denum pname, GC3Dint* value);
- Attributes getContextAttributes();
+ GraphicsContext3DAttributes getContextAttributes();
GC3Denum getError();
void getFloatv(GC3Denum pname, GC3Dfloat* value);
void getFramebufferAttachmentParameteriv(GC3Denum target, GC3Denum attachment, GC3Denum pname, GC3Dint* value);
void getIntegerv(GC3Denum pname, GC3Dint* value);
+ void getInteger64v(GC3Denum pname, GC3Dint64* value);
void getProgramiv(Platform3DObject program, GC3Denum pname, GC3Dint* value);
void getNonBuiltInActiveSymbolCount(Platform3DObject program, GC3Denum pname, GC3Dint* value);
String getProgramInfoLog(Platform3DObject);
@@ -813,6 +1092,7 @@ public:
void useProgram(Platform3DObject);
void validateProgram(Platform3DObject);
+ bool checkVaryingsPacking(Platform3DObject vertexShader, Platform3DObject fragmentShader) const;
bool precisionsMatch(Platform3DObject vertexShader, Platform3DObject fragmentShader) const;
void vertexAttrib1f(GC3Duint index, GC3Dfloat x);
@@ -834,25 +1114,38 @@ public:
void drawElementsInstanced(GC3Denum mode, GC3Dsizei count, GC3Denum type, GC3Dintptr offset, GC3Dsizei primcount);
void vertexAttribDivisor(GC3Duint index, GC3Duint divisor);
-#if PLATFORM(GTK) || PLATFORM(EFL) || USE(CAIRO)
+ // VertexArrayOject calls
+ Platform3DObject createVertexArray();
+ void deleteVertexArray(Platform3DObject);
+ GC3Dboolean isVertexArray(Platform3DObject);
+ void bindVertexArray(Platform3DObject);
+
+#if PLATFORM(GTK) || USE(CAIRO)
void paintToCanvas(const unsigned char* imagePixels, int imageWidth, int imageHeight,
int canvasWidth, int canvasHeight, PlatformContextCairo* context);
#elif USE(CG)
- void paintToCanvas(const unsigned char* imagePixels, int imageWidth, int imageHeight,
- int canvasWidth, int canvasHeight, GraphicsContext*);
+ void paintToCanvas(const unsigned char* imagePixels, int imageWidth, int imageHeight, int canvasWidth, int canvasHeight, GraphicsContext&);
#endif
void markContextChanged();
void markLayerComposited();
bool layerComposited() const;
+ void forceContextLost();
+ void recycleContext();
- void paintRenderingResultsToCanvas(ImageBuffer*, DrawingBuffer*);
- PassRefPtr<ImageData> paintRenderingResultsToImageData(DrawingBuffer*);
+ void paintRenderingResultsToCanvas(ImageBuffer*);
+ RefPtr<ImageData> paintRenderingResultsToImageData();
bool paintCompositedResultsToCanvas(ImageBuffer*);
#if PLATFORM(IOS)
void endPaint();
#endif
+#if PLATFORM(MAC)
+ void updateCGLContext();
+#endif
+ void setContextVisibility(bool);
+
+ GraphicsContext3DPowerPreference powerPreferenceUsedForCreation() const { return m_powerPreferenceUsedForCreation; }
// Support for buffer creation and deletion
Platform3DObject createBuffer();
@@ -879,11 +1172,15 @@ public:
// getError in the order they were added.
void synthesizeGLError(GC3Denum error);
+ // Read real OpenGL errors, and move them to the synthetic
+ // error list. Return true if at least one error is moved.
+ bool moveErrorsToSyntheticErrorList();
+
// Support for extensions. Returns a non-null object, though not
// all methods it contains may necessarily be supported on the
// current hardware. Must call Extensions3D::supports() to
// determine this.
- Extensions3D* getExtensions();
+ Extensions3D& getExtensions();
IntSize getInternalFramebufferSize() const;
@@ -950,7 +1247,7 @@ public:
ImageSource* m_decoder;
RefPtr<cairo_surface_t> m_imageSurface;
#elif USE(CG)
- CGImageRef m_cgImage;
+ RetainPtr<CGImageRef> m_cgImage;
RetainPtr<CGImageRef> m_decodedImage;
RetainPtr<CFDataRef> m_pixelData;
std::unique_ptr<uint8_t[]> m_formalizedRGBA8Data;
@@ -967,7 +1264,8 @@ public:
};
private:
- GraphicsContext3D(Attributes, HostWindow*, RenderStyle = RenderOffscreen);
+ GraphicsContext3D(GraphicsContext3DAttributes, HostWindow*, RenderStyle = RenderOffscreen);
+ static int GPUCheckCounter;
// Helper for packImageData/extractImageData/extractTextureData which implement packing of pixel
// data into the specified OpenGL destination format and type.
@@ -982,6 +1280,9 @@ private:
// implementation.
void validateDepthStencil(const char* packedDepthStencilExtension);
void validateAttributes();
+
+ // Call to make during draw calls to check on the GPU's status.
+ void checkGPUStatusIfNecessary();
// Read rendering results into a pixel array with the same format as the
// backbuffer.
@@ -989,59 +1290,25 @@ private:
void readPixelsAndConvertToBGRAIfNecessary(int x, int y, int width, int height, unsigned char* pixels);
#if PLATFORM(IOS)
- bool setRenderbufferStorageFromDrawable(GC3Dsizei width, GC3Dsizei height);
+ void setRenderbufferStorageFromDrawable(GC3Dsizei width, GC3Dsizei height);
#endif
bool reshapeFBOs(const IntSize&);
void resolveMultisamplingIfNecessary(const IntRect& = IntRect());
-#if PLATFORM(EFL) && USE(GRAPHICS_SURFACE)
- void createGraphicsSurfaces(const IntSize&);
-#endif
+ void attachDepthAndStencilBufferIfNeeded(GLuint internalDepthStencilFormat, int width, int height);
int m_currentWidth, m_currentHeight;
- bool isResourceSafe();
-#if PLATFORM(IOS)
- PlatformGraphicsContext3D m_contextObj;
- RetainPtr<PlatformLayer> m_webGLLayer;
-#elif PLATFORM(MAC)
- CGLContextObj m_contextObj;
+#if PLATFORM(COCOA)
RetainPtr<WebGLLayer> m_webGLLayer;
-#elif PLATFORM(WIN) && USE(CA)
- RefPtr<PlatformCALayer> m_webGLLayer;
+ PlatformGraphicsContext3D m_contextObj;
#endif
- struct SymbolInfo {
- SymbolInfo()
- : type(0)
- , size(0)
- , precision(SH_PRECISION_UNDEFINED)
- , staticUse(0)
- {
- }
-
- SymbolInfo(GC3Denum type, int size, const String& mappedName, ShPrecisionType precision, int staticUse)
- : type(type)
- , size(size)
- , mappedName(mappedName)
- , precision(precision)
- , staticUse(staticUse)
- {
- }
-
- bool operator==(SymbolInfo& other) const
- {
- return type == other.type && size == other.size && mappedName == other.mappedName;
- }
-
- GC3Denum type;
- int size;
- String mappedName;
- ShPrecisionType precision;
- int staticUse;
- };
+#if PLATFORM(WIN) && USE(CA)
+ RefPtr<PlatformCALayer> m_webGLLayer;
+#endif
- typedef HashMap<String, SymbolInfo> ShaderSymbolMap;
+ typedef HashMap<String, sh::ShaderVariable> ShaderSymbolMap;
struct ShaderSourceEntry {
GC3Denum type;
@@ -1089,7 +1356,11 @@ private:
return filteredToActualUniformIndexMap.size();
}
};
- std::unique_ptr<ActiveShaderSymbolCounts> m_shaderSymbolCount;
+ typedef HashMap<Platform3DObject, ActiveShaderSymbolCounts> ShaderProgramSymbolCountMap;
+ ShaderProgramSymbolCountMap m_shaderProgramSymbolCountMap;
+
+ typedef HashMap<String, String> HashedSymbolMap;
+ HashedSymbolMap m_possiblyUnusedAttributeMap;
String mappedSymbolName(Platform3DObject program, ANGLEShaderSymbolType, const String& name);
String mappedSymbolName(Platform3DObject shaders[2], size_t count, const String& name);
@@ -1097,28 +1368,32 @@ private:
ANGLEWebKitBridge m_compiler;
- OwnPtr<ShaderNameHash> nameHashMapForShaders;
+ std::unique_ptr<ShaderNameHash> nameHashMapForShaders;
-#if ((PLATFORM(GTK) || PLATFORM(EFL) || PLATFORM(WIN)) && USE(OPENGL_ES_2))
+#if ((PLATFORM(GTK) || PLATFORM(WIN)) && USE(OPENGL_ES_2))
friend class Extensions3DOpenGLES;
- OwnPtr<Extensions3DOpenGLES> m_extensions;
+ std::unique_ptr<Extensions3DOpenGLES> m_extensions;
#else
friend class Extensions3DOpenGL;
- OwnPtr<Extensions3DOpenGL> m_extensions;
+ std::unique_ptr<Extensions3DOpenGL> m_extensions;
#endif
friend class Extensions3DOpenGLCommon;
- Attributes m_attrs;
+ GraphicsContext3DAttributes m_attrs;
+ GraphicsContext3DPowerPreference m_powerPreferenceUsedForCreation { GraphicsContext3DPowerPreference::Default };
RenderStyle m_renderStyle;
Vector<Vector<float>> m_vertexArray;
GC3Duint m_texture;
GC3Duint m_compositorTexture;
GC3Duint m_fbo;
+#if USE(COORDINATED_GRAPHICS_THREADED)
+ GC3Duint m_intermediateTexture;
+#endif
- GC3Duint m_depthBuffer;
- GC3Duint m_stencilBuffer;
- GC3Duint m_depthStencilBuffer;
+ GC3Duint m_depthBuffer { 0 };
+ GC3Duint m_stencilBuffer { 0 };
+ GC3Duint m_depthStencilBuffer { 0 };
bool m_layerComposited;
GC3Duint m_internalColorFormat;
@@ -1145,10 +1420,23 @@ private:
// Errors raised by synthesizeGLError().
ListHashSet<GC3Denum> m_syntheticErrors;
+#if USE(TEXTURE_MAPPER)
+ friend class TextureMapperGC3DPlatformLayer;
+ std::unique_ptr<TextureMapperGC3DPlatformLayer> m_texmapLayer;
+#else
friend class GraphicsContext3DPrivate;
- OwnPtr<GraphicsContext3DPrivate> m_private;
+ std::unique_ptr<GraphicsContext3DPrivate> m_private;
+#endif
+
+ WebGLRenderingContextBase* m_webglContext;
+
+ bool m_isForWebGL2 { false };
+ bool m_usingCoreProfile { false };
+
+#if USE(CAIRO)
+ Platform3DObject m_vao { 0 };
+#endif
+
};
} // namespace WebCore
-
-#endif // GraphicsContext3D_h
diff --git a/Source/WebCore/platform/graphics/GraphicsContext3DAttributes.h b/Source/WebCore/platform/graphics/GraphicsContext3DAttributes.h
new file mode 100644
index 000000000..0d3da5ed2
--- /dev/null
+++ b/Source/WebCore/platform/graphics/GraphicsContext3DAttributes.h
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2016 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+namespace WebCore {
+
+enum class GraphicsContext3DPowerPreference {
+ Default,
+ LowPower,
+ HighPerformance
+};
+
+struct GraphicsContext3DAttributes {
+ // WebGLContextAttributes
+ bool alpha { true };
+ bool depth { true };
+ bool stencil { false };
+ bool antialias { true };
+ bool premultipliedAlpha { true };
+ bool preserveDrawingBuffer { false };
+ bool failIfMajorPerformanceCaveat { false };
+ using PowerPreference = GraphicsContext3DPowerPreference;
+ PowerPreference powerPreference { PowerPreference::Default };
+
+ // Additional attributes.
+ bool forceSoftwareRenderer { false };
+ bool shareResources { true };
+ bool useGLES3 { false };
+ bool noExtensions { false };
+ float devicePixelRatio { 1 };
+ PowerPreference initialPowerPreference { PowerPreference::Default };
+};
+
+}
diff --git a/Source/WebCore/platform/graphics/GraphicsContext3DPrivate.cpp b/Source/WebCore/platform/graphics/GraphicsContext3DPrivate.cpp
index 049fdd2d1..fc764654f 100644
--- a/Source/WebCore/platform/graphics/GraphicsContext3DPrivate.cpp
+++ b/Source/WebCore/platform/graphics/GraphicsContext3DPrivate.cpp
@@ -18,17 +18,13 @@
*/
#include "config.h"
-#include "GraphicsContext3DPrivate.h"
-#if USE(3D_GRAPHICS)
+#if ENABLE(GRAPHICS_CONTEXT_3D)
+#include "GraphicsContext3DPrivate.h"
#include "HostWindow.h"
-#include "NotImplemented.h"
#include <wtf/StdLibExtras.h>
-#if USE(CAIRO)
-#include "PlatformContextCairo.h"
-#endif
#if USE(OPENGL_ES_2)
#include <GLES2/gl2.h>
@@ -37,26 +33,16 @@
#include "OpenGLShims.h"
#endif
-#if USE(ACCELERATED_COMPOSITING) && USE(TEXTURE_MAPPER) && USE(TEXTURE_MAPPER_GL)
-#include <texmap/TextureMapperGL.h>
-#endif
-
using namespace std;
namespace WebCore {
-PassOwnPtr<GraphicsContext3DPrivate> GraphicsContext3DPrivate::create(GraphicsContext3D* context, GraphicsContext3D::RenderStyle renderStyle)
-{
- return adoptPtr(new GraphicsContext3DPrivate(context, renderStyle));
-}
-
-GraphicsContext3DPrivate::GraphicsContext3DPrivate(GraphicsContext3D* context, GraphicsContext3D::RenderStyle renderStyle)
- : m_context(context)
- , m_renderStyle(renderStyle)
+GraphicsContext3DPrivate::GraphicsContext3DPrivate(GraphicsContext3D*, GraphicsContext3D::RenderStyle renderStyle)
+ : m_renderStyle(renderStyle)
{
switch (renderStyle) {
case GraphicsContext3D::RenderOffscreen:
- m_glContext = GLContext::createOffscreenContext(GLContext::sharingContext());
+ m_glContext = GLContext::createOffscreenContext(&PlatformDisplay::sharedDisplayForCompositing());
break;
case GraphicsContext3D::RenderToCurrentGLContext:
break;
@@ -66,13 +52,7 @@ GraphicsContext3DPrivate::GraphicsContext3DPrivate(GraphicsContext3D* context, G
}
}
-GraphicsContext3DPrivate::~GraphicsContext3DPrivate()
-{
-#if USE(ACCELERATED_COMPOSITING) && USE(TEXTURE_MAPPER)
- if (client())
- client()->platformLayerWillBeDestroyed();
-#endif
-}
+GraphicsContext3DPrivate::~GraphicsContext3DPrivate() = default;
bool GraphicsContext3DPrivate::makeContextCurrent()
{
@@ -81,79 +61,9 @@ bool GraphicsContext3DPrivate::makeContextCurrent()
PlatformGraphicsContext3D GraphicsContext3DPrivate::platformContext()
{
- return m_glContext ? m_glContext->platformContext() : GLContext::getCurrent()->platformContext();
-}
-
-#if USE(ACCELERATED_COMPOSITING) && USE(TEXTURE_MAPPER)
-void GraphicsContext3DPrivate::paintToTextureMapper(TextureMapper* textureMapper, const FloatRect& targetRect, const TransformationMatrix& matrix, float opacity)
-{
- if (!m_glContext)
- return;
-
- ASSERT(m_renderStyle == GraphicsContext3D::RenderOffscreen);
-
- m_context->markLayerComposited();
-
- // FIXME: We do not support mask for the moment with TextureMapperImageBuffer.
- if (textureMapper->accelerationMode() != TextureMapper::OpenGLMode) {
- GraphicsContext* context = textureMapper->graphicsContext();
- context->save();
- context->platformContext()->setGlobalAlpha(opacity);
-
- const int height = m_context->m_currentHeight;
- const int width = m_context->m_currentWidth;
- int totalBytes = width * height * 4;
-
- auto pixels = std::make_unique<unsigned char[]>(totalBytes);
- if (!pixels)
- return;
-
- // OpenGL keeps the pixels stored bottom up, so we need to flip the image here.
- context->translate(0, height);
- context->scale(FloatSize(1, -1));
-
- context->concatCTM(matrix.toAffineTransform());
-
- m_context->readRenderingResults(pixels.get(), totalBytes);
-
- // Premultiply alpha.
- for (int i = 0; i < totalBytes; i += 4)
- if (pixels[i + 3] != 255) {
- pixels[i + 0] = min(255, pixels[i + 0] * pixels[i + 3] / 255);
- pixels[i + 1] = min(255, pixels[i + 1] * pixels[i + 3] / 255);
- pixels[i + 2] = min(255, pixels[i + 2] * pixels[i + 3] / 255);
- }
-
- RefPtr<cairo_surface_t> imageSurface = adoptRef(cairo_image_surface_create_for_data(
- const_cast<unsigned char*>(pixels.get()), CAIRO_FORMAT_ARGB32, width, height, width * 4));
-
- context->platformContext()->drawSurfaceToContext(imageSurface.get(), targetRect, IntRect(0, 0, width, height), context);
-
- context->restore();
- return;
- }
-
-#if USE(TEXTURE_MAPPER_GL)
- if (m_context->m_attrs.antialias && m_context->m_state.boundFBO == m_context->m_multisampleFBO) {
- GLContext* previousActiveContext = GLContext::getCurrent();
- if (previousActiveContext != m_glContext)
- m_context->makeContextCurrent();
-
- m_context->resolveMultisamplingIfNecessary();
- ::glBindFramebuffer(GraphicsContext3D::FRAMEBUFFER, m_context->m_state.boundFBO);
-
- if (previousActiveContext && previousActiveContext != m_glContext)
- previousActiveContext->makeContextCurrent();
- }
-
- TextureMapperGL* texmapGL = static_cast<TextureMapperGL*>(textureMapper);
- TextureMapperGL::Flags flags = TextureMapperGL::ShouldFlipTexture | (m_context->m_attrs.alpha ? TextureMapperGL::ShouldBlend : 0);
- IntSize textureSize(m_context->m_currentWidth, m_context->m_currentHeight);
- texmapGL->drawTexture(m_context->m_texture, flags, textureSize, targetRect, matrix, opacity);
-#endif // USE(ACCELERATED_COMPOSITING_GL)
+ return m_glContext ? m_glContext->platformContext() : GLContext::current()->platformContext();
}
-#endif // USE(ACCELERATED_COMPOSITING)
} // namespace WebCore
-#endif // USE(3D_GRAPHICS)
+#endif // ENABLE(GRAPHICS_CONTEXT_3D)
diff --git a/Source/WebCore/platform/graphics/GraphicsContext3DPrivate.h b/Source/WebCore/platform/graphics/GraphicsContext3DPrivate.h
index 42eb6c2e7..507558297 100644
--- a/Source/WebCore/platform/graphics/GraphicsContext3DPrivate.h
+++ b/Source/WebCore/platform/graphics/GraphicsContext3DPrivate.h
@@ -22,36 +22,23 @@
#include "GLContext.h"
#include "GraphicsContext3D.h"
-#include <wtf/PassOwnPtr.h>
-
-#if USE(ACCELERATED_COMPOSITING) && USE(TEXTURE_MAPPER)
-#include "TextureMapperPlatformLayer.h"
-#endif
+#include "PlatformLayer.h"
namespace WebCore {
-class GraphicsContext3DPrivate
-#if USE(ACCELERATED_COMPOSITING) && USE(TEXTURE_MAPPER)
- : public TextureMapperPlatformLayer
-#endif
-{
+class BitmapTextureGL;
+
+class GraphicsContext3DPrivate {
public:
- static PassOwnPtr<GraphicsContext3DPrivate> create(GraphicsContext3D*, GraphicsContext3D::RenderStyle);
+ GraphicsContext3DPrivate(GraphicsContext3D*, GraphicsContext3D::RenderStyle);
~GraphicsContext3DPrivate();
bool makeContextCurrent();
PlatformGraphicsContext3D platformContext();
GraphicsContext3D::RenderStyle renderStyle() { return m_renderStyle; }
-#if USE(ACCELERATED_COMPOSITING) && USE(TEXTURE_MAPPER)
- virtual void paintToTextureMapper(TextureMapper*, const FloatRect& target, const TransformationMatrix&, float opacity);
-#endif
-
private:
- GraphicsContext3DPrivate(GraphicsContext3D*, GraphicsContext3D::RenderStyle);
-
- GraphicsContext3D* m_context;
- OwnPtr<GLContext> m_glContext;
+ std::unique_ptr<GLContext> m_glContext;
GraphicsContext3D::RenderStyle m_renderStyle;
};
diff --git a/Source/WebCore/platform/graphics/GraphicsLayer.cpp b/Source/WebCore/platform/graphics/GraphicsLayer.cpp
index 3d410b701..2e981c66c 100644
--- a/Source/WebCore/platform/graphics/GraphicsLayer.cpp
+++ b/Source/WebCore/platform/graphics/GraphicsLayer.cpp
@@ -13,7 +13,7 @@
* THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
@@ -25,8 +25,6 @@
#include "config.h"
-#if USE(ACCELERATED_COMPOSITING)
-
#include "GraphicsLayer.h"
#include "FloatPoint.h"
@@ -36,6 +34,7 @@
#include "RotateTransformOperation.h"
#include "TextStream.h"
#include <wtf/HashMap.h>
+#include <wtf/NeverDestroyed.h>
#include <wtf/text/CString.h>
#include <wtf/text/StringBuilder.h>
#include <wtf/text/WTFString.h>
@@ -49,31 +48,64 @@ namespace WebCore {
typedef HashMap<const GraphicsLayer*, Vector<FloatRect>> RepaintMap;
static RepaintMap& repaintRectMap()
{
- DEFINE_STATIC_LOCAL(RepaintMap, map, ());
+ static NeverDestroyed<RepaintMap> map;
return map;
}
-void KeyframeValueList::insert(PassOwnPtr<const AnimationValue> value)
+void KeyframeValueList::insert(std::unique_ptr<const AnimationValue> value)
{
for (size_t i = 0; i < m_values.size(); ++i) {
const AnimationValue* curValue = m_values[i].get();
if (curValue->keyTime() == value->keyTime()) {
ASSERT_NOT_REACHED();
// insert after
- m_values.insert(i + 1, value);
+ m_values.insert(i + 1, WTFMove(value));
return;
}
if (curValue->keyTime() > value->keyTime()) {
// insert before
- m_values.insert(i, value);
+ m_values.insert(i, WTFMove(value));
return;
}
}
- m_values.append(value);
+ m_values.append(WTFMove(value));
+}
+
+#if !USE(CA)
+bool GraphicsLayer::supportsLayerType(Type type)
+{
+ switch (type) {
+ case Type::Normal:
+ case Type::PageTiledBacking:
+ case Type::Scrolling:
+ return true;
+ case Type::Shape:
+ return false;
+ }
+ ASSERT_NOT_REACHED();
+ return false;
+}
+
+bool GraphicsLayer::supportsBackgroundColorContent()
+{
+#if USE(TEXTURE_MAPPER)
+ return true;
+#else
+ return false;
+#endif
}
+#endif
-GraphicsLayer::GraphicsLayer(GraphicsLayerClient* client)
+#if !USE(COORDINATED_GRAPHICS)
+bool GraphicsLayer::supportsContentsTiling()
+{
+ // FIXME: Enable the feature on different ports.
+ return false;
+}
+#endif
+
+GraphicsLayer::GraphicsLayer(Type type, GraphicsLayerClient& client)
: m_client(client)
, m_anchorPoint(0.5f, 0.5f, 0)
, m_opacity(1)
@@ -81,30 +113,32 @@ GraphicsLayer::GraphicsLayer(GraphicsLayerClient* client)
#if ENABLE(CSS_COMPOSITING)
, m_blendMode(BlendModeNormal)
#endif
+ , m_type(type)
, m_contentsOpaque(false)
, m_preserves3D(false)
, m_backfaceVisibility(true)
- , m_usingTiledBacking(false)
, m_masksToBounds(false)
, m_drawsContent(false)
, m_contentsVisible(true)
, m_acceleratesDrawing(false)
- , m_maintainsPixelAlignment(false)
+ , m_usesDisplayListDrawing(false)
, m_appliesPageScale(false)
, m_showDebugBorder(false)
, m_showRepaintCounter(false)
+ , m_isMaskLayer(false)
+ , m_isTrackingDisplayListReplay(false)
+ , m_userInteractionEnabled(true)
, m_paintingPhase(GraphicsLayerPaintAllWithOverflowClip)
, m_contentsOrientation(CompositingCoordinatesTopDown)
- , m_parent(0)
- , m_maskLayer(0)
- , m_replicaLayer(0)
- , m_replicatedLayer(0)
+ , m_parent(nullptr)
+ , m_maskLayer(nullptr)
+ , m_replicaLayer(nullptr)
+ , m_replicatedLayer(nullptr)
, m_repaintCount(0)
, m_customAppearance(NoCustomAppearance)
{
#ifndef NDEBUG
- if (m_client)
- m_client->verifyNotPainting();
+ m_client.verifyNotPainting();
#endif
}
@@ -117,8 +151,7 @@ GraphicsLayer::~GraphicsLayer()
void GraphicsLayer::willBeDestroyed()
{
#ifndef NDEBUG
- if (m_client)
- m_client->verifyNotPainting();
+ m_client.verifyNotPainting();
#endif
if (m_replicaLayer)
m_replicaLayer->setReplicatedLayer(0);
@@ -256,16 +289,62 @@ void GraphicsLayer::removeAllChildren()
void GraphicsLayer::removeFromParent()
{
if (m_parent) {
- unsigned i;
- for (i = 0; i < m_parent->m_children.size(); i++) {
- if (this == m_parent->m_children[i]) {
- m_parent->m_children.remove(i);
- break;
- }
- }
+ m_parent->m_children.removeFirst(this);
+ setParent(nullptr);
+ }
+}
- setParent(0);
+void GraphicsLayer::setMaskLayer(GraphicsLayer* layer)
+{
+ if (layer == m_maskLayer)
+ return;
+
+ if (layer) {
+ layer->removeFromParent();
+ layer->setParent(this);
+ layer->setIsMaskLayer(true);
+ } else if (m_maskLayer) {
+ m_maskLayer->setParent(nullptr);
+ m_maskLayer->setIsMaskLayer(false);
}
+
+ m_maskLayer = layer;
+}
+
+Path GraphicsLayer::shapeLayerPath() const
+{
+#if USE(CA)
+ return m_shapeLayerPath;
+#else
+ return Path();
+#endif
+}
+
+void GraphicsLayer::setShapeLayerPath(const Path& path)
+{
+#if USE(CA)
+ m_shapeLayerPath = path;
+#else
+ UNUSED_PARAM(path);
+#endif
+}
+
+WindRule GraphicsLayer::shapeLayerWindRule() const
+{
+#if USE(CA)
+ return m_shapeLayerWindRule;
+#else
+ return RULE_NONZERO;
+#endif
+}
+
+void GraphicsLayer::setShapeLayerWindRule(WindRule windRule)
+{
+#if USE(CA)
+ m_shapeLayerWindRule = windRule;
+#else
+ UNUSED_PARAM(windRule);
+#endif
}
void GraphicsLayer::noteDeviceOrPageScaleFactorChangedIncludingDescendants()
@@ -284,13 +363,19 @@ void GraphicsLayer::noteDeviceOrPageScaleFactorChangedIncludingDescendants()
childLayers[i]->noteDeviceOrPageScaleFactorChangedIncludingDescendants();
}
+void GraphicsLayer::setIsInWindow(bool inWindow)
+{
+ if (TiledBacking* tiledBacking = this->tiledBacking())
+ tiledBacking->setIsInWindow(inWindow);
+}
+
void GraphicsLayer::setReplicatedByLayer(GraphicsLayer* layer)
{
if (m_replicaLayer == layer)
return;
if (m_replicaLayer)
- m_replicaLayer->setReplicatedLayer(0);
+ m_replicaLayer->setReplicatedLayer(nullptr);
if (layer)
layer->setReplicatedLayer(this);
@@ -298,7 +383,7 @@ void GraphicsLayer::setReplicatedByLayer(GraphicsLayer* layer)
m_replicaLayer = layer;
}
-void GraphicsLayer::setOffsetFromRenderer(const IntSize& offset, ShouldSetNeedsDisplay shouldSetNeedsDisplay)
+void GraphicsLayer::setOffsetFromRenderer(const FloatSize& offset, ShouldSetNeedsDisplay shouldSetNeedsDisplay)
{
if (offset == m_offsetFromRenderer)
return;
@@ -326,17 +411,15 @@ void GraphicsLayer::setBackgroundColor(const Color& color)
m_backgroundColor = color;
}
-void GraphicsLayer::paintGraphicsLayerContents(GraphicsContext& context, const IntRect& clip)
+void GraphicsLayer::paintGraphicsLayerContents(GraphicsContext& context, const FloatRect& clip)
{
- if (m_client) {
- IntSize offset = offsetFromRenderer();
- context.translate(-offset);
+ FloatSize offset = offsetFromRenderer();
+ context.translate(-offset);
- IntRect clipRect(clip);
- clipRect.move(offset);
+ FloatRect clipRect(clip);
+ clipRect.move(offset);
- m_client->paintContents(this, context, m_paintingPhase, clipRect);
- }
+ m_client.paintContents(this, context, m_paintingPhase, clipRect);
}
String GraphicsLayer::animationNameForTransition(AnimatedPropertyID property)
@@ -359,32 +442,37 @@ void GraphicsLayer::resumeAnimations()
void GraphicsLayer::getDebugBorderInfo(Color& color, float& width) const
{
+ width = 2;
+
+ if (needsBackdrop()) {
+ color = Color(255, 0, 255, 128); // has backdrop: magenta
+ width = 12;
+ return;
+ }
+
if (drawsContent()) {
- if (m_usingTiledBacking) {
+ if (tiledBacking()) {
color = Color(255, 128, 0, 128); // tiled layer: orange
- width = 2;
return;
}
color = Color(0, 128, 32, 128); // normal layer: green
- width = 2;
return;
}
- if (hasContentsLayer()) {
- color = Color(255, 150, 255, 200); // non-painting layer with contents: pink
- width = 2;
+ if (usesContentsLayer()) {
+ color = Color(0, 64, 128, 150); // non-painting layer with contents: blue
+ width = 8;
return;
}
if (masksToBounds()) {
color = Color(128, 255, 255, 48); // masking layer: pale blue
- width = 20;
+ width = 16;
return;
}
color = Color(255, 255, 0, 192); // container: yellow
- width = 2;
}
void GraphicsLayer::updateDebugIndicators()
@@ -428,7 +516,6 @@ void GraphicsLayer::distributeOpacity(float accumulatedOpacity)
}
}
-#if ENABLE(CSS_FILTERS)
static inline const FilterOperations& filterOperationsAt(const KeyframeValueList& valueList, size_t index)
{
return static_cast<const FilterAnimationValue&>(valueList.at(index)).value();
@@ -436,7 +523,11 @@ static inline const FilterOperations& filterOperationsAt(const KeyframeValueList
int GraphicsLayer::validateFilterOperations(const KeyframeValueList& valueList)
{
- ASSERT(valueList.property() == AnimatedPropertyWebkitFilter);
+#if ENABLE(FILTERS_LEVEL_2)
+ ASSERT(valueList.property() == AnimatedPropertyFilter || valueList.property() == AnimatedPropertyWebkitBackdropFilter);
+#else
+ ASSERT(valueList.property() == AnimatedPropertyFilter);
+#endif
if (valueList.size() < 2)
return -1;
@@ -466,7 +557,6 @@ int GraphicsLayer::validateFilterOperations(const KeyframeValueList& valueList)
return firstIndex;
}
-#endif
// An "invalid" list is one whose functions don't match, and therefore has to be animated as a Matrix
// The hasBigRotation flag will always return false if isValid is false. Otherwise hasBigRotation is
@@ -479,7 +569,7 @@ static inline const TransformOperations& operationsAt(const KeyframeValueList& v
int GraphicsLayer::validateTransformOperations(const KeyframeValueList& valueList, bool& hasBigRotation)
{
- ASSERT(valueList.property() == AnimatedPropertyWebkitTransform);
+ ASSERT(valueList.property() == AnimatedPropertyTransform);
hasBigRotation = false;
@@ -511,8 +601,8 @@ int GraphicsLayer::validateTransformOperations(const KeyframeValueList& valueLis
}
// Keyframes are valid, check for big rotations.
- double lastRotAngle = 0.0;
- double maxRotAngle = -1.0;
+ double lastRotationAngle = 0.0;
+ double maxRotationAngle = -1.0;
for (size_t j = 0; j < firstVal.operations().size(); ++j) {
TransformOperation::OperationType type = firstVal.operations().at(j)->type();
@@ -522,23 +612,23 @@ int GraphicsLayer::validateTransformOperations(const KeyframeValueList& valueLis
type == TransformOperation::ROTATE_Y ||
type == TransformOperation::ROTATE_Z ||
type == TransformOperation::ROTATE_3D) {
- lastRotAngle = static_cast<RotateTransformOperation*>(firstVal.operations().at(j).get())->angle();
+ lastRotationAngle = downcast<RotateTransformOperation>(*firstVal.operations().at(j)).angle();
- if (maxRotAngle < 0)
- maxRotAngle = fabs(lastRotAngle);
+ if (maxRotationAngle < 0)
+ maxRotationAngle = fabs(lastRotationAngle);
for (size_t i = firstIndex + 1; i < valueList.size(); ++i) {
const TransformOperations& val = operationsAt(valueList, i);
- double rotAngle = val.operations().isEmpty() ? 0 : (static_cast<RotateTransformOperation*>(val.operations().at(j).get())->angle());
- double diffAngle = fabs(rotAngle - lastRotAngle);
- if (diffAngle > maxRotAngle)
- maxRotAngle = diffAngle;
- lastRotAngle = rotAngle;
+ double rotationAngle = val.operations().isEmpty() ? 0 : downcast<RotateTransformOperation>(*val.operations().at(j)).angle();
+ double diffAngle = fabs(rotationAngle - lastRotationAngle);
+ if (diffAngle > maxRotationAngle)
+ maxRotationAngle = diffAngle;
+ lastRotationAngle = rotationAngle;
}
}
}
- hasBigRotation = maxRotAngle >= 180.0;
+ hasBigRotation = maxRotationAngle >= 180.0;
return firstIndex;
}
@@ -559,21 +649,36 @@ void GraphicsLayer::resetTrackedRepaints()
void GraphicsLayer::addRepaintRect(const FloatRect& repaintRect)
{
- if (m_client->isTrackingRepaints()) {
- FloatRect largestRepaintRect(FloatPoint(), m_size);
- largestRepaintRect.intersect(repaintRect);
- RepaintMap::iterator repaintIt = repaintRectMap().find(this);
- if (repaintIt == repaintRectMap().end()) {
- Vector<FloatRect> repaintRects;
- repaintRects.append(largestRepaintRect);
- repaintRectMap().set(this, repaintRects);
- } else {
- Vector<FloatRect>& repaintRects = repaintIt->value;
- repaintRects.append(largestRepaintRect);
- }
+ if (!m_client.isTrackingRepaints())
+ return;
+
+ FloatRect largestRepaintRect(FloatPoint(), m_size);
+ largestRepaintRect.intersect(repaintRect);
+ RepaintMap::iterator repaintIt = repaintRectMap().find(this);
+ if (repaintIt == repaintRectMap().end()) {
+ Vector<FloatRect> repaintRects;
+ repaintRects.append(largestRepaintRect);
+ repaintRectMap().set(this, repaintRects);
+ } else {
+ Vector<FloatRect>& repaintRects = repaintIt->value;
+ repaintRects.append(largestRepaintRect);
}
}
+void GraphicsLayer::traverse(GraphicsLayer& layer, std::function<void (GraphicsLayer&)> traversalFunc)
+{
+ traversalFunc(layer);
+
+ for (auto* childLayer : layer.children())
+ traverse(*childLayer, traversalFunc);
+
+ if (auto* replicaLayer = layer.replicaLayer())
+ traverse(*replicaLayer, traversalFunc);
+
+ if (auto* maskLayer = layer.maskLayer())
+ traverse(*maskLayer, traversalFunc);
+}
+
void GraphicsLayer::dumpLayer(TextStream& ts, int indent, LayerTreeAsTextBehavior behavior) const
{
writeIndent(ts, indent);
@@ -590,6 +695,20 @@ void GraphicsLayer::dumpLayer(TextStream& ts, int indent, LayerTreeAsTextBehavio
ts << ")\n";
}
+static void dumpChildren(TextStream& ts, const Vector<GraphicsLayer*>& children, unsigned& totalChildCount, int indent, LayerTreeAsTextBehavior behavior)
+{
+ totalChildCount += children.size();
+ for (auto* child : children) {
+ if ((behavior & LayerTreeAsTextDebug) || !child->client().shouldSkipLayerInDump(child, behavior)) {
+ child->dumpLayer(ts, indent + 2, behavior);
+ continue;
+ }
+
+ totalChildCount--;
+ dumpChildren(ts, child->children(), totalChildCount, indent, behavior);
+ }
+}
+
void GraphicsLayer::dumpProperties(TextStream& ts, int indent, LayerTreeAsTextBehavior behavior) const
{
if (m_position != FloatPoint()) {
@@ -597,6 +716,11 @@ void GraphicsLayer::dumpProperties(TextStream& ts, int indent, LayerTreeAsTextBe
ts << "(position " << m_position.x() << " " << m_position.y() << ")\n";
}
+ if (m_approximatePosition) {
+ writeIndent(ts, indent + 1);
+ ts << "(approximate position " << m_approximatePosition.value().x() << " " << m_approximatePosition.value().y() << ")\n";
+ }
+
if (m_boundsOrigin != FloatPoint()) {
writeIndent(ts, indent + 1);
ts << "(bounds origin " << m_boundsOrigin.x() << " " << m_boundsOrigin.y() << ")\n";
@@ -604,7 +728,10 @@ void GraphicsLayer::dumpProperties(TextStream& ts, int indent, LayerTreeAsTextBe
if (m_anchorPoint != FloatPoint3D(0.5f, 0.5f, 0)) {
writeIndent(ts, indent + 1);
- ts << "(anchor " << m_anchorPoint.x() << " " << m_anchorPoint.y() << ")\n";
+ ts << "(anchor " << m_anchorPoint.x() << " " << m_anchorPoint.y();
+ if (m_anchorPoint.z())
+ ts << " " << m_anchorPoint.z();
+ ts << ")\n";
}
if (m_size != IntSize()) {
@@ -616,15 +743,23 @@ void GraphicsLayer::dumpProperties(TextStream& ts, int indent, LayerTreeAsTextBe
writeIndent(ts, indent + 1);
ts << "(opacity " << m_opacity << ")\n";
}
-
- if (m_usingTiledBacking) {
+
+#if ENABLE(CSS_COMPOSITING)
+ if (m_blendMode != BlendModeNormal) {
+ writeIndent(ts, indent + 1);
+ ts << "(blendMode " << compositeOperatorName(CompositeSourceOver, m_blendMode) << ")\n";
+ }
+#endif
+
+ if (type() == Type::Normal && tiledBacking()) {
writeIndent(ts, indent + 1);
- ts << "(usingTiledLayer " << m_usingTiledBacking << ")\n";
+ ts << "(usingTiledLayer 1)\n";
}
- if (m_contentsOpaque) {
+ bool needsIOSDumpRenderTreeMainFrameRenderViewLayerIsAlwaysOpaqueHack = m_client.needsIOSDumpRenderTreeMainFrameRenderViewLayerIsAlwaysOpaqueHack(*this);
+ if (m_contentsOpaque || needsIOSDumpRenderTreeMainFrameRenderViewLayerIsAlwaysOpaqueHack) {
writeIndent(ts, indent + 1);
- ts << "(contentsOpaque " << m_contentsOpaque << ")\n";
+ ts << "(contentsOpaque " << (m_contentsOpaque || needsIOSDumpRenderTreeMainFrameRenderViewLayerIsAlwaysOpaqueHack) << ")\n";
}
if (m_preserves3D) {
@@ -632,7 +767,7 @@ void GraphicsLayer::dumpProperties(TextStream& ts, int indent, LayerTreeAsTextBe
ts << "(preserves3D " << m_preserves3D << ")\n";
}
- if (m_drawsContent && m_client->shouldDumpPropertyForLayer(this, "drawsContent")) {
+ if (m_drawsContent && m_client.shouldDumpPropertyForLayer(this, "drawsContent")) {
writeIndent(ts, indent + 1);
ts << "(drawsContent " << m_drawsContent << ")\n";
}
@@ -649,19 +784,21 @@ void GraphicsLayer::dumpProperties(TextStream& ts, int indent, LayerTreeAsTextBe
if (behavior & LayerTreeAsTextDebug) {
writeIndent(ts, indent + 1);
- ts << "(";
- if (m_client)
- ts << "client " << static_cast<void*>(m_client);
- else
- ts << "no client";
- ts << ")\n";
+ ts << "(primary-layer-id " << primaryLayerID() << ")\n";
+ writeIndent(ts, indent + 1);
+ ts << "(client " << static_cast<void*>(&m_client) << ")\n";
}
- if (m_backgroundColor.isValid() && m_client->shouldDumpPropertyForLayer(this, "backgroundColor")) {
+ if (m_backgroundColor.isValid() && m_client.shouldDumpPropertyForLayer(this, "backgroundColor")) {
writeIndent(ts, indent + 1);
ts << "(backgroundColor " << m_backgroundColor.nameForRenderTreeAsText() << ")\n";
}
+ if (behavior & LayerTreeAsTextIncludeAcceleratesDrawing && m_acceleratesDrawing) {
+ writeIndent(ts, indent + 1);
+ ts << "(acceleratesDrawing " << m_acceleratesDrawing << ")\n";
+ }
+
if (!m_transform.isIdentity()) {
writeIndent(ts, indent + 1);
ts << "(transform ";
@@ -682,6 +819,15 @@ void GraphicsLayer::dumpProperties(TextStream& ts, int indent, LayerTreeAsTextBe
ts << "[" << m_childrenTransform.m41() << " " << m_childrenTransform.m42() << " " << m_childrenTransform.m43() << " " << m_childrenTransform.m44() << "])\n";
}
+ if (m_maskLayer) {
+ writeIndent(ts, indent + 1);
+ ts << "(mask layer";
+ if (behavior & LayerTreeAsTextDebug)
+ ts << " " << m_maskLayer;
+ ts << ")\n";
+ m_maskLayer->dumpLayer(ts, indent + 2, behavior);
+ }
+
if (m_replicaLayer) {
writeIndent(ts, indent + 1);
ts << "(replica layer";
@@ -699,7 +845,7 @@ void GraphicsLayer::dumpProperties(TextStream& ts, int indent, LayerTreeAsTextBe
ts << ")\n";
}
- if (behavior & LayerTreeAsTextIncludeRepaintRects && repaintRectMap().contains(this) && !repaintRectMap().get(this).isEmpty() && m_client->shouldDumpPropertyForLayer(this, "repaintRects")) {
+ if (behavior & LayerTreeAsTextIncludeRepaintRects && repaintRectMap().contains(this) && !repaintRectMap().get(this).isEmpty() && m_client.shouldDumpPropertyForLayer(this, "repaintRects")) {
writeIndent(ts, indent + 1);
ts << "(repaint rects\n";
for (size_t i = 0; i < repaintRectMap().get(this).size(); ++i) {
@@ -732,6 +878,10 @@ void GraphicsLayer::dumpProperties(TextStream& ts, int indent, LayerTreeAsTextBe
writeIndent(ts, indent + 2);
ts << "GraphicsLayerPaintMask\n";
}
+ if (paintingPhase() & GraphicsLayerPaintChildClippingMask) {
+ writeIndent(ts, indent + 2);
+ ts << "GraphicsLayerPaintChildClippingMask\n";
+ }
if (paintingPhase() & GraphicsLayerPaintOverflowContents) {
writeIndent(ts, indent + 2);
ts << "GraphicsLayerPaintOverflowContents\n";
@@ -748,19 +898,9 @@ void GraphicsLayer::dumpProperties(TextStream& ts, int indent, LayerTreeAsTextBe
if (m_children.size()) {
TextStream childrenStream;
- unsigned totalChildCount = m_children.size();
- for (size_t childIndex = 0; childIndex < m_children.size(); childIndex++) {
- GraphicsLayer* child = m_children[childIndex];
- if (!m_client->shouldSkipLayerInDump(child)) {
- child->dumpLayer(childrenStream, indent + 2, behavior);
- continue;
- }
-
- const Vector<GraphicsLayer*>& grandChildren = child->children();
- totalChildCount += grandChildren.size() - 1;
- for (size_t grandChildIndex = 0; grandChildIndex < grandChildren.size(); grandChildIndex++)
- grandChildren[grandChildIndex]->dumpLayer(childrenStream, indent + 2, behavior);
- }
+
+ unsigned totalChildCount = 0;
+ dumpChildren(childrenStream, m_children, totalChildCount, indent, behavior);
writeIndent(childrenStream, indent + 1);
childrenStream << ")\n";
@@ -773,9 +913,32 @@ void GraphicsLayer::dumpProperties(TextStream& ts, int indent, LayerTreeAsTextBe
}
}
+TextStream& operator<<(TextStream& ts, const Vector<GraphicsLayer::PlatformLayerID>& layers)
+{
+ for (size_t i = 0; i < layers.size(); ++i) {
+ if (i)
+ ts << " ";
+ ts << layers[i];
+ }
+
+ return ts;
+}
+
+TextStream& operator<<(TextStream& ts, const WebCore::GraphicsLayer::CustomAppearance& customAppearance)
+{
+ switch (customAppearance) {
+ case GraphicsLayer::CustomAppearance::NoCustomAppearance: ts << "none"; break;
+ case GraphicsLayer::CustomAppearance::ScrollingOverhang: ts << "scrolling-overhang"; break;
+ case GraphicsLayer::CustomAppearance::ScrollingShadow: ts << "scrolling-shadow"; break;
+ case GraphicsLayer::CustomAppearance::LightBackdropAppearance: ts << "light-backdrop"; break;
+ case GraphicsLayer::CustomAppearance::DarkBackdropAppearance: ts << "dark-backdrop"; break;
+ }
+ return ts;
+}
+
String GraphicsLayer::layerTreeAsText(LayerTreeAsTextBehavior behavior) const
{
- TextStream ts;
+ TextStream ts(TextStream::LineMode::MultipleLine, TextStream::Formatting::SVGStyleRect);
dumpLayer(ts, 0, behavior);
return ts.release();
@@ -783,15 +946,13 @@ String GraphicsLayer::layerTreeAsText(LayerTreeAsTextBehavior behavior) const
} // namespace WebCore
-#ifndef NDEBUG
+#if ENABLE(TREE_DEBUGGING)
void showGraphicsLayerTree(const WebCore::GraphicsLayer* layer)
{
if (!layer)
return;
- String output = layer->layerTreeAsText(LayerTreeAsTextDebug | LayerTreeAsTextIncludeVisibleRects | LayerTreeAsTextIncludeTileCaches);
+ String output = layer->layerTreeAsText(WebCore::LayerTreeAsTextDebug | WebCore::LayerTreeAsTextIncludeVisibleRects | WebCore::LayerTreeAsTextIncludeTileCaches | WebCore::LayerTreeAsTextIncludeContentLayers);
fprintf(stderr, "%s\n", output.utf8().data());
}
#endif
-
-#endif // USE(ACCELERATED_COMPOSITING)
diff --git a/Source/WebCore/platform/graphics/GraphicsLayer.h b/Source/WebCore/platform/graphics/GraphicsLayer.h
index 3d57e804a..b685335d2 100644
--- a/Source/WebCore/platform/graphics/GraphicsLayer.h
+++ b/Source/WebCore/platform/graphics/GraphicsLayer.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009 Apple Inc. All rights reserved.
+ * Copyright (C) 2009-2017 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -13,7 +13,7 @@
* THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
@@ -23,45 +23,28 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef GraphicsLayer_h
-#define GraphicsLayer_h
-
-#if USE(ACCELERATED_COMPOSITING)
+#pragma once
#include "Animation.h"
#include "Color.h"
+#include "FilterOperations.h"
#include "FloatPoint.h"
#include "FloatPoint3D.h"
+#include "FloatRoundedRect.h"
#include "FloatSize.h"
#include "GraphicsLayerClient.h"
-#include "IntRect.h"
+#include "Path.h"
#include "PlatformLayer.h"
#include "TransformOperations.h"
-#include <wtf/OwnPtr.h>
-#include <wtf/PassOwnPtr.h>
-
-#if ENABLE(CSS_FILTERS)
-#include "FilterOperations.h"
-#endif
+#include "WindRule.h"
+#include <wtf/TypeCasts.h>
#if ENABLE(CSS_COMPOSITING)
#include "GraphicsTypes.h"
#endif
-enum LayerTreeAsTextBehaviorFlags {
- LayerTreeAsTextBehaviorNormal = 0,
- LayerTreeAsTextDebug = 1 << 0, // Dump extra debugging info like layer addresses.
- LayerTreeAsTextIncludeVisibleRects = 1 << 1,
- LayerTreeAsTextIncludeTileCaches = 1 << 2,
- LayerTreeAsTextIncludeRepaintRects = 1 << 3,
- LayerTreeAsTextIncludePaintingPhases = 1 << 4,
- LayerTreeAsTextIncludeContentLayers = 1 << 5
-};
-typedef unsigned LayerTreeAsTextBehavior;
-
namespace WebCore {
-class FloatRect;
class GraphicsContext;
class GraphicsLayerFactory;
class Image;
@@ -70,6 +53,10 @@ class TiledBacking;
class TimingFunction;
class TransformationMatrix;
+namespace DisplayList {
+typedef unsigned AsTextFlags;
+}
+
// Base class for animation values (also used for transitions). Here to
// represent values for properties being animated via the GraphicsLayer,
// without pulling in style-related data from outside of the platform directory.
@@ -81,16 +68,26 @@ public:
double keyTime() const { return m_keyTime; }
const TimingFunction* timingFunction() const { return m_timingFunction.get(); }
- virtual PassOwnPtr<AnimationValue> clone() const = 0;
+ virtual std::unique_ptr<AnimationValue> clone() const = 0;
protected:
- AnimationValue(double keyTime, PassRefPtr<TimingFunction> timingFunction = 0)
+ AnimationValue(double keyTime, TimingFunction* timingFunction = nullptr)
: m_keyTime(keyTime)
, m_timingFunction(timingFunction)
{
}
+ AnimationValue(const AnimationValue& other)
+ : m_keyTime(other.m_keyTime)
+ , m_timingFunction(other.m_timingFunction ? RefPtr<TimingFunction> { other.m_timingFunction->clone() } : nullptr)
+ {
+ }
+
+ AnimationValue(AnimationValue&&) = default;
+
private:
+ void operator=(const AnimationValue&) = delete;
+
double m_keyTime;
RefPtr<TimingFunction> m_timingFunction;
};
@@ -99,25 +96,20 @@ private:
// FIXME: Should be moved to its own header file.
class FloatAnimationValue : public AnimationValue {
public:
- static PassOwnPtr<FloatAnimationValue> create(double keyTime, float value, PassRefPtr<TimingFunction> timingFunction = 0)
+ FloatAnimationValue(double keyTime, float value, TimingFunction* timingFunction = nullptr)
+ : AnimationValue(keyTime, timingFunction)
+ , m_value(value)
{
- return adoptPtr(new FloatAnimationValue(keyTime, value, timingFunction));
}
- virtual PassOwnPtr<AnimationValue> clone() const override
+ std::unique_ptr<AnimationValue> clone() const override
{
- return adoptPtr(new FloatAnimationValue(*this));
+ return std::make_unique<FloatAnimationValue>(*this);
}
float value() const { return m_value; }
private:
- FloatAnimationValue(double keyTime, float value, PassRefPtr<TimingFunction> timingFunction)
- : AnimationValue(keyTime, timingFunction)
- , m_value(value)
- {
- }
-
float m_value;
};
@@ -125,55 +117,63 @@ private:
// FIXME: Should be moved to its own header file.
class TransformAnimationValue : public AnimationValue {
public:
- static PassOwnPtr<TransformAnimationValue> create(double keyTime, const TransformOperations& value, PassRefPtr<TimingFunction> timingFunction = 0)
+ TransformAnimationValue(double keyTime, const TransformOperations& value, TimingFunction* timingFunction = nullptr)
+ : AnimationValue(keyTime, timingFunction)
+ , m_value(value)
{
- return adoptPtr(new TransformAnimationValue(keyTime, value, timingFunction));
}
- virtual PassOwnPtr<AnimationValue> clone() const override
+ std::unique_ptr<AnimationValue> clone() const override
{
- return adoptPtr(new TransformAnimationValue(*this));
+ return std::make_unique<TransformAnimationValue>(*this);
}
- const TransformOperations& value() const { return m_value; }
-
-private:
- TransformAnimationValue(double keyTime, const TransformOperations& value, PassRefPtr<TimingFunction> timingFunction)
- : AnimationValue(keyTime, timingFunction)
- , m_value(value)
+ TransformAnimationValue(const TransformAnimationValue& other)
+ : AnimationValue(other)
{
+ m_value.operations().reserveInitialCapacity(other.m_value.operations().size());
+ for (auto& operation : other.m_value.operations())
+ m_value.operations().uncheckedAppend(operation->clone());
}
+ TransformAnimationValue(TransformAnimationValue&&) = default;
+
+ const TransformOperations& value() const { return m_value; }
+
+private:
TransformOperations m_value;
};
-#if ENABLE(CSS_FILTERS)
// Used to store one filter value in a keyframe list.
// FIXME: Should be moved to its own header file.
class FilterAnimationValue : public AnimationValue {
public:
- static PassOwnPtr<FilterAnimationValue> create(double keyTime, const FilterOperations& value, PassRefPtr<TimingFunction> timingFunction = 0)
+ FilterAnimationValue(double keyTime, const FilterOperations& value, TimingFunction* timingFunction = nullptr)
+ : AnimationValue(keyTime, timingFunction)
+ , m_value(value)
{
- return adoptPtr(new FilterAnimationValue(keyTime, value, timingFunction));
}
- virtual PassOwnPtr<AnimationValue> clone() const override
+ std::unique_ptr<AnimationValue> clone() const override
{
- return adoptPtr(new FilterAnimationValue(*this));
+ return std::make_unique<FilterAnimationValue>(*this);
}
- const FilterOperations& value() const { return m_value; }
-
-private:
- FilterAnimationValue(double keyTime, const FilterOperations& value, PassRefPtr<TimingFunction> timingFunction)
- : AnimationValue(keyTime, timingFunction)
- , m_value(value)
+ FilterAnimationValue(const FilterAnimationValue& other)
+ : AnimationValue(other)
{
+ m_value.operations().reserveInitialCapacity(other.m_value.operations().size());
+ for (auto& operation : other.m_value.operations())
+ m_value.operations().uncheckedAppend(operation->clone());
}
+ FilterAnimationValue(FilterAnimationValue&&) = default;
+
+ const FilterOperations& value() const { return m_value; }
+
+private:
FilterOperations m_value;
};
-#endif
// Used to store a series of values in a keyframe list.
// Values will all be of the same type, which can be inferred from the property.
@@ -188,13 +188,12 @@ public:
KeyframeValueList(const KeyframeValueList& other)
: m_property(other.property())
{
- for (size_t i = 0; i < other.m_values.size(); ++i)
- m_values.append(other.m_values[i]->clone());
+ m_values.reserveInitialCapacity(other.m_values.size());
+ for (auto& value : other.m_values)
+ m_values.uncheckedAppend(value->clone());
}
- ~KeyframeValueList()
- {
- }
+ KeyframeValueList(KeyframeValueList&&) = default;
KeyframeValueList& operator=(const KeyframeValueList& other)
{
@@ -203,6 +202,8 @@ public:
return *this;
}
+ KeyframeValueList& operator=(KeyframeValueList&&) = default;
+
void swap(KeyframeValueList& other)
{
std::swap(m_property, other.m_property);
@@ -213,12 +214,12 @@ public:
size_t size() const { return m_values.size(); }
const AnimationValue& at(size_t i) const { return *m_values.at(i); }
-
+
// Insert, sorted by keyTime.
- void insert(PassOwnPtr<const AnimationValue>);
-
+ WEBCORE_EXPORT void insert(std::unique_ptr<const AnimationValue>);
+
protected:
- Vector<OwnPtr<const AnimationValue>> m_values;
+ Vector<std::unique_ptr<const AnimationValue>> m_values;
AnimatedPropertyID m_property;
};
@@ -228,16 +229,25 @@ protected:
class GraphicsLayer {
WTF_MAKE_NONCOPYABLE(GraphicsLayer); WTF_MAKE_FAST_ALLOCATED;
public:
- static std::unique_ptr<GraphicsLayer> create(GraphicsLayerFactory*, GraphicsLayerClient*);
+ enum class Type {
+ Normal,
+ PageTiledBacking,
+ Scrolling,
+ Shape
+ };
- virtual ~GraphicsLayer();
+ WEBCORE_EXPORT static std::unique_ptr<GraphicsLayer> create(GraphicsLayerFactory*, GraphicsLayerClient&, Type = Type::Normal);
+
+ WEBCORE_EXPORT virtual ~GraphicsLayer();
+
+ Type type() const { return m_type; }
- virtual void initialize() { }
+ virtual void initialize(Type) { }
- typedef uint64_t PlatformLayerID;
+ using PlatformLayerID = uint64_t;
virtual PlatformLayerID primaryLayerID() const { return 0; }
- GraphicsLayerClient* client() const { return m_client; }
+ GraphicsLayerClient& client() const { return m_client; }
// Layer name. Only used to identify layers in debug output
const String& name() const { return m_name; }
@@ -251,23 +261,36 @@ public:
const Vector<GraphicsLayer*>& children() const { return m_children; }
// Returns true if the child list changed.
- virtual bool setChildren(const Vector<GraphicsLayer*>&);
+ WEBCORE_EXPORT virtual bool setChildren(const Vector<GraphicsLayer*>&);
+
+ enum ContentsLayerPurpose {
+ NoContentsLayer = 0,
+ ContentsLayerForImage,
+ ContentsLayerForMedia,
+ ContentsLayerForCanvas,
+ ContentsLayerForBackgroundColor,
+ ContentsLayerForPlugin
+ };
// Add child layers. If the child is already parented, it will be removed from its old parent.
- virtual void addChild(GraphicsLayer*);
- virtual void addChildAtIndex(GraphicsLayer*, int index);
- virtual void addChildAbove(GraphicsLayer* layer, GraphicsLayer* sibling);
- virtual void addChildBelow(GraphicsLayer* layer, GraphicsLayer* sibling);
- virtual bool replaceChild(GraphicsLayer* oldChild, GraphicsLayer* newChild);
+ WEBCORE_EXPORT virtual void addChild(GraphicsLayer*);
+ WEBCORE_EXPORT virtual void addChildAtIndex(GraphicsLayer*, int index);
+ WEBCORE_EXPORT virtual void addChildAbove(GraphicsLayer*, GraphicsLayer* sibling);
+ WEBCORE_EXPORT virtual void addChildBelow(GraphicsLayer*, GraphicsLayer* sibling);
+ WEBCORE_EXPORT virtual bool replaceChild(GraphicsLayer* oldChild, GraphicsLayer* newChild);
- void removeAllChildren();
- virtual void removeFromParent();
+ WEBCORE_EXPORT void removeAllChildren();
+ WEBCORE_EXPORT virtual void removeFromParent();
+ // The parent() of a maskLayer is set to the layer being masked.
GraphicsLayer* maskLayer() const { return m_maskLayer; }
- virtual void setMaskLayer(GraphicsLayer* layer) { m_maskLayer = layer; }
+ virtual void setMaskLayer(GraphicsLayer*);
+
+ void setIsMaskLayer(bool isMask) { m_isMaskLayer = isMask; }
+ bool isMaskLayer() const { return m_isMaskLayer; }
// The given layer will replicate this layer and its children; the replica renders behind this layer.
- virtual void setReplicatedByLayer(GraphicsLayer*);
+ WEBCORE_EXPORT virtual void setReplicatedByLayer(GraphicsLayer*);
// Whether this layer is being replicated by another layer.
bool isReplicated() const { return m_replicaLayer; }
// The layer that replicates this layer (if any).
@@ -281,17 +304,21 @@ public:
SetNeedsDisplay
};
- // Offset is origin of the renderer minus origin of the graphics layer (so either zero or negative).
- IntSize offsetFromRenderer() const { return m_offsetFromRenderer; }
- void setOffsetFromRenderer(const IntSize&, ShouldSetNeedsDisplay = SetNeedsDisplay);
+ // Offset is origin of the renderer minus origin of the graphics layer.
+ FloatSize offsetFromRenderer() const { return m_offsetFromRenderer; }
+ void setOffsetFromRenderer(const FloatSize&, ShouldSetNeedsDisplay = SetNeedsDisplay);
// The position of the layer (the location of its top-left corner in its parent)
const FloatPoint& position() const { return m_position; }
- virtual void setPosition(const FloatPoint& p) { m_position = p; }
+ virtual void setPosition(const FloatPoint& p) { m_approximatePosition = std::nullopt; m_position = p; }
+
+ // approximatePosition, if set, overrides position() and is used during coverage rect computation.
+ FloatPoint approximatePosition() const { return m_approximatePosition ? m_approximatePosition.value() : m_position; }
+ void setApproximatePosition(std::optional<FloatPoint> p) { m_approximatePosition = p; }
// For platforms that move underlying platform layers on a different thread for scrolling; just update the GraphicsLayer state.
virtual void syncPosition(const FloatPoint& p) { m_position = p; }
-
+
// Anchor point: (0, 0) is top left, (1, 1) is bottom right. The anchor point
// affects the origin of the transforms.
const FloatPoint3D& anchorPoint() const { return m_anchorPoint; }
@@ -299,12 +326,15 @@ public:
// The size of the layer.
const FloatSize& size() const { return m_size; }
- virtual void setSize(const FloatSize&);
+ WEBCORE_EXPORT virtual void setSize(const FloatSize&);
// The boundOrigin affects the offset at which content is rendered, and sublayers are positioned.
const FloatPoint& boundsOrigin() const { return m_boundsOrigin; }
virtual void setBoundsOrigin(const FloatPoint& origin) { m_boundsOrigin = origin; }
+ // For platforms that move underlying platform layers on a different thread for scrolling; just update the GraphicsLayer state.
+ virtual void syncBoundsOrigin(const FloatPoint& origin) { m_boundsOrigin = origin; }
+
const TransformationMatrix& transform() const { return m_transform; }
virtual void setTransform(const TransformationMatrix& t) { m_transform = t; }
@@ -323,14 +353,22 @@ public:
bool contentsAreVisible() const { return m_contentsVisible; }
virtual void setContentsVisible(bool b) { m_contentsVisible = b; }
+ bool userInteractionEnabled() const { return m_userInteractionEnabled; }
+ virtual void setUserInteractionEnabled(bool b) { m_userInteractionEnabled = b; }
+
bool acceleratesDrawing() const { return m_acceleratesDrawing; }
virtual void setAcceleratesDrawing(bool b) { m_acceleratesDrawing = b; }
+ bool usesDisplayListDrawing() const { return m_usesDisplayListDrawing; }
+ virtual void setUsesDisplayListDrawing(bool b) { m_usesDisplayListDrawing = b; }
+
+ bool needsBackdrop() const { return !m_backdropFilters.isEmpty(); }
+
// The color used to paint the layer background. Pass an invalid color to remove it.
// Note that this covers the entire layer. Use setContentsToSolidColor() if the color should
// only cover the contentsRect.
const Color& backgroundColor() const { return m_backgroundColor; }
- virtual void setBackgroundColor(const Color&);
+ WEBCORE_EXPORT virtual void setBackgroundColor(const Color&);
// opaque means that we know the layer contents have no alpha
bool contentsOpaque() const { return m_contentsOpaque; }
@@ -342,12 +380,15 @@ public:
float opacity() const { return m_opacity; }
virtual void setOpacity(float opacity) { m_opacity = opacity; }
-#if ENABLE(CSS_FILTERS)
const FilterOperations& filters() const { return m_filters; }
-
- // Returns true if filter can be rendered by the compositor
+ // Returns true if filter can be rendered by the compositor.
virtual bool setFilters(const FilterOperations& filters) { m_filters = filters; return true; }
-#endif
+
+ const FilterOperations& backdropFilters() const { return m_backdropFilters; }
+ virtual bool setBackdropFilters(const FilterOperations& filters) { m_backdropFilters = filters; return true; }
+
+ virtual void setBackdropFiltersRect(const FloatRoundedRect& backdropFiltersRect) { m_backdropFiltersRect = backdropFiltersRect; }
+ const FloatRoundedRect& backdropFiltersRect() const { return m_backdropFiltersRect; }
#if ENABLE(CSS_COMPOSITING)
BlendMode blendMode() const { return m_blendMode; }
@@ -370,52 +411,58 @@ public:
virtual void setContentsNeedsDisplay() { };
// The tile phase is relative to the GraphicsLayer bounds.
- virtual void setContentsTilePhase(const IntPoint& p) { m_contentsTilePhase = p; }
- IntPoint contentsTilePhase() const { return m_contentsTilePhase; }
+ virtual void setContentsTilePhase(const FloatSize& p) { m_contentsTilePhase = p; }
+ FloatSize contentsTilePhase() const { return m_contentsTilePhase; }
- virtual void setContentsTileSize(const IntSize& s) { m_contentsTileSize = s; }
- IntSize contentsTileSize() const { return m_contentsTileSize; }
+ virtual void setContentsTileSize(const FloatSize& s) { m_contentsTileSize = s; }
+ FloatSize contentsTileSize() const { return m_contentsTileSize; }
bool hasContentsTiling() const { return !m_contentsTileSize.isEmpty(); }
// Set that the position/size of the contents (image or video).
- IntRect contentsRect() const { return m_contentsRect; }
- virtual void setContentsRect(const IntRect& r) { m_contentsRect = r; }
+ FloatRect contentsRect() const { return m_contentsRect; }
+ virtual void setContentsRect(const FloatRect& r) { m_contentsRect = r; }
+
+ // Set a rounded rect that will be used to clip the layer contents.
+ FloatRoundedRect contentsClippingRect() const { return m_contentsClippingRect; }
+ virtual void setContentsClippingRect(const FloatRoundedRect& roundedRect) { m_contentsClippingRect = roundedRect; }
+
+ // Set a rounded rect that is used to clip this layer and its descendants (implies setting masksToBounds).
+ // Returns false if the platform can't support this rounded clip, and we should fall back to painting a mask.
+ FloatRoundedRect maskToBoundsRect() const { return m_masksToBoundsRect; };
+ virtual bool setMasksToBoundsRect(const FloatRoundedRect& roundedRect) { m_masksToBoundsRect = roundedRect; return false; }
- IntRect contentsClippingRect() const { return m_contentsClippingRect; }
- virtual void setContentsClippingRect(const IntRect& r) { m_contentsClippingRect = r; }
+ Path shapeLayerPath() const;
+ virtual void setShapeLayerPath(const Path&);
+
+ WindRule shapeLayerWindRule() const;
+ virtual void setShapeLayerWindRule(WindRule);
// Transitions are identified by a special animation name that cannot clash with a keyframe identifier.
static String animationNameForTransition(AnimatedPropertyID);
// Return true if the animation is handled by the compositing system. If this returns
- // false, the animation will be run by AnimationController.
+ // false, the animation will be run by CSSAnimationController.
// These methods handle both transitions and keyframe animations.
- virtual bool addAnimation(const KeyframeValueList&, const IntSize& /*boxSize*/, const Animation*, const String& /*animationName*/, double /*timeOffset*/) { return false; }
+ virtual bool addAnimation(const KeyframeValueList&, const FloatSize& /*boxSize*/, const Animation*, const String& /*animationName*/, double /*timeOffset*/) { return false; }
virtual void pauseAnimation(const String& /*animationName*/, double /*timeOffset*/) { }
virtual void removeAnimation(const String& /*animationName*/) { }
- virtual void suspendAnimations(double time);
- virtual void resumeAnimations();
+ WEBCORE_EXPORT virtual void suspendAnimations(double time);
+ WEBCORE_EXPORT virtual void resumeAnimations();
// Layer contents
virtual void setContentsToImage(Image*) { }
virtual bool shouldDirectlyCompositeImage(Image*) const { return true; }
- virtual void setContentsToMedia(PlatformLayer*) { } // video or plug-in
#if PLATFORM(IOS)
virtual PlatformLayer* contentsLayerForMedia() const { return 0; }
#endif
// Pass an invalid color to remove the contents layer.
virtual void setContentsToSolidColor(const Color&) { }
- virtual void setContentsToCanvas(PlatformLayer*) { }
- // FIXME: webkit.org/b/109658
- // Should unify setContentsToMedia and setContentsToCanvas
- virtual void setContentsToPlatformLayer(PlatformLayer* layer) { setContentsToMedia(layer); }
- virtual bool hasContentsLayer() const { return false; }
+ virtual void setContentsToPlatformLayer(PlatformLayer*, ContentsLayerPurpose) { }
+ virtual bool usesContentsLayer() const { return false; }
// Callback from the underlying graphics system to draw layer contents.
- void paintGraphicsLayerContents(GraphicsContext&, const IntRect& clip);
- // Callback from the underlying graphics system when the layer has been displayed
- virtual void layerDidDisplay(PlatformLayer*) { }
+ void paintGraphicsLayerContents(GraphicsContext&, const FloatRect& clip);
// For hosting this GraphicsLayer in a native layer hierarchy.
virtual PlatformLayer* platformLayer() const { return 0; }
@@ -441,32 +488,36 @@ public:
virtual void setDebugBackgroundColor(const Color&) { }
virtual void setDebugBorder(const Color&, float /*borderWidth*/) { }
- enum CustomAppearance { NoCustomAppearance, ScrollingOverhang, ScrollingShadow };
+ enum CustomAppearance { NoCustomAppearance, ScrollingOverhang, ScrollingShadow, LightBackdropAppearance, DarkBackdropAppearance };
virtual void setCustomAppearance(CustomAppearance customAppearance) { m_customAppearance = customAppearance; }
CustomAppearance customAppearance() const { return m_customAppearance; }
// z-position is the z-equivalent of position(). It's only used for debugging purposes.
virtual float zPosition() const { return m_zPosition; }
- virtual void setZPosition(float);
+ WEBCORE_EXPORT virtual void setZPosition(float);
- virtual void distributeOpacity(float);
- virtual float accumulatedOpacity() const;
+ WEBCORE_EXPORT virtual void distributeOpacity(float);
+ WEBCORE_EXPORT virtual float accumulatedOpacity() const;
- virtual void setMaintainsPixelAlignment(bool maintainsAlignment) { m_maintainsPixelAlignment = maintainsAlignment; }
- virtual bool maintainsPixelAlignment() const { return m_maintainsPixelAlignment; }
#if PLATFORM(IOS)
- virtual FloatSize pixelAlignmentOffset() const { return FloatSize(); }
bool hasFlattenedPerspectiveTransform() const { return !preserves3D() && m_childrenTransform.hasPerspective(); }
#endif
+ virtual FloatSize pixelAlignmentOffset() const { return FloatSize(); }
virtual void setAppliesPageScale(bool appliesScale = true) { m_appliesPageScale = appliesScale; }
virtual bool appliesPageScale() const { return m_appliesPageScale; }
- float pageScaleFactor() const { return m_client ? m_client->pageScaleFactor() : 1; }
- float deviceScaleFactor() const { return m_client ? m_client->deviceScaleFactor() : 1; }
+ float pageScaleFactor() const { return m_client.pageScaleFactor(); }
+ float deviceScaleFactor() const { return m_client.deviceScaleFactor(); }
+
+ // Whether this layer is viewport constrained, implying that it's moved around externally from GraphicsLayer (e.g. by the scrolling tree).
+ virtual void setIsViewportConstrained(bool) { }
+ virtual bool isViewportConstrained() const { return false; }
virtual void deviceOrPageScaleFactorChanged() { }
- void noteDeviceOrPageScaleFactorChangedIncludingDescendants();
+ WEBCORE_EXPORT void noteDeviceOrPageScaleFactorChangedIncludingDescendants();
+
+ void setIsInWindow(bool);
// Some compositing systems may do internal batching to synchronize compositing updates
// with updates drawn into the window. These methods flush internal batched state on this layer
@@ -480,35 +531,26 @@ public:
// Return a string with a human readable form of the layer tree, If debug is true
// pointers for the layers and timing data will be included in the returned string.
- String layerTreeAsText(LayerTreeAsTextBehavior = LayerTreeAsTextBehaviorNormal) const;
+ WEBCORE_EXPORT String layerTreeAsText(LayerTreeAsTextBehavior = LayerTreeAsTextBehaviorNormal) const;
+
+ // For testing.
+ WEBCORE_EXPORT virtual String displayListAsText(DisplayList::AsTextFlags) const { return String(); }
+
+ WEBCORE_EXPORT virtual void setIsTrackingDisplayListReplay(bool isTracking) { m_isTrackingDisplayListReplay = isTracking; }
+ WEBCORE_EXPORT virtual bool isTrackingDisplayListReplay() const { return m_isTrackingDisplayListReplay; }
+ WEBCORE_EXPORT virtual String replayDisplayListAsText(DisplayList::AsTextFlags) const { return String(); }
// Return an estimate of the backing store memory cost (in bytes). May be incorrect for tiled layers.
- virtual double backingStoreMemoryEstimate() const;
+ WEBCORE_EXPORT virtual double backingStoreMemoryEstimate() const;
- bool usingTiledBacking() const { return m_usingTiledBacking; }
virtual TiledBacking* tiledBacking() const { return 0; }
void resetTrackedRepaints();
void addRepaintRect(const FloatRect&);
- static bool supportsBackgroundColorContent()
- {
-#if USE(CA) || USE(TEXTURE_MAPPER)
- return true;
-#else
- return false;
-#endif
- }
-
-#if USE(COORDINATED_GRAPHICS)
+ static bool supportsBackgroundColorContent();
+ static bool supportsLayerType(Type);
static bool supportsContentsTiling();
-#else
- static bool supportsContentsTiling()
- {
- // FIXME: Enable the feature on different ports.
- return false;
- }
-#endif
void updateDebugIndicators();
@@ -516,20 +558,25 @@ public:
virtual bool isGraphicsLayerCA() const { return false; }
virtual bool isGraphicsLayerCARemote() const { return false; }
+ virtual bool isGraphicsLayerTextureMapper() const { return false; }
+ virtual bool isCoordinatedGraphicsLayer() const { return false; }
+
+ static void traverse(GraphicsLayer&, std::function<void (GraphicsLayer&)>);
protected:
+ WEBCORE_EXPORT explicit GraphicsLayer(Type, GraphicsLayerClient&);
+
// Should be called from derived class destructors. Should call willBeDestroyed() on super.
- virtual void willBeDestroyed();
+ WEBCORE_EXPORT virtual void willBeDestroyed();
-#if ENABLE(CSS_FILTERS)
// This method is used by platform GraphicsLayer classes to clear the filters
// when compositing is not done in hardware. It is not virtual, so the caller
// needs to notifiy the change to the platform layer as needed.
void clearFilters() { m_filters.clear(); }
+ void clearBackdropFilters() { m_backdropFilters.clear(); }
// Given a KeyframeValueList containing filterOperations, return true if the operations are valid.
static int validateFilterOperations(const KeyframeValueList&);
-#endif
// Given a list of TransformAnimationValues, see if all the operations for each keyframe match. If so
// return the index of the KeyframeValueList entry that has that list of operations (it may not be
@@ -546,21 +593,23 @@ protected:
GraphicsLayer* replicatedLayer() const { return m_replicatedLayer; }
virtual void setReplicatedLayer(GraphicsLayer* layer) { m_replicatedLayer = layer; }
- GraphicsLayer(GraphicsLayerClient*);
-
void dumpProperties(TextStream&, int indent, LayerTreeAsTextBehavior) const;
virtual void dumpAdditionalProperties(TextStream&, int /*indent*/, LayerTreeAsTextBehavior) const { }
- virtual void getDebugBorderInfo(Color&, float& width) const;
+ WEBCORE_EXPORT virtual void getDebugBorderInfo(Color&, float& width) const;
- GraphicsLayerClient* m_client;
+ GraphicsLayerClient& m_client;
String m_name;
// Offset from the owning renderer
- IntSize m_offsetFromRenderer;
+ FloatSize m_offsetFromRenderer;
// Position is relative to the parent GraphicsLayer
FloatPoint m_position;
+
+ // If set, overrides m_position. Only used for coverage computation.
+ std::optional<FloatPoint> m_approximatePosition;
+
FloatPoint3D m_anchorPoint;
FloatSize m_size;
FloatPoint m_boundsOrigin;
@@ -572,26 +621,29 @@ protected:
float m_opacity;
float m_zPosition;
-#if ENABLE(CSS_FILTERS)
FilterOperations m_filters;
-#endif
+ FilterOperations m_backdropFilters;
#if ENABLE(CSS_COMPOSITING)
BlendMode m_blendMode;
#endif
+ const Type m_type;
+
bool m_contentsOpaque : 1;
bool m_preserves3D: 1;
bool m_backfaceVisibility : 1;
- bool m_usingTiledBacking : 1;
bool m_masksToBounds : 1;
bool m_drawsContent : 1;
bool m_contentsVisible : 1;
bool m_acceleratesDrawing : 1;
- bool m_maintainsPixelAlignment : 1;
+ bool m_usesDisplayListDrawing : 1;
bool m_appliesPageScale : 1; // Set for the layer which has the page scale applied to it.
bool m_showDebugBorder : 1;
bool m_showRepaintCounter : 1;
+ bool m_isMaskLayer : 1;
+ bool m_isTrackingDisplayListReplay : 1;
+ bool m_userInteractionEnabled : 1;
GraphicsLayerPaintingPhase m_paintingPhase;
CompositingCoordinatesOrientation m_contentsOrientation; // affects orientation of layer contents
@@ -606,25 +658,33 @@ protected:
GraphicsLayer* m_replicatedLayer; // For a replica layer, a reference to the original layer.
FloatPoint m_replicatedLayerPosition; // For a replica layer, the position of the replica.
- IntRect m_contentsRect;
- IntRect m_contentsClippingRect;
- IntPoint m_contentsTilePhase;
- IntSize m_contentsTileSize;
+ FloatRect m_contentsRect;
+ FloatRoundedRect m_contentsClippingRect;
+ FloatRoundedRect m_masksToBoundsRect;
+ FloatSize m_contentsTilePhase;
+ FloatSize m_contentsTileSize;
+ FloatRoundedRect m_backdropFiltersRect;
int m_repaintCount;
CustomAppearance m_customAppearance;
+
+#if USE(CA)
+ Path m_shapeLayerPath;
+ WindRule m_shapeLayerWindRule { RULE_NONZERO };
+#endif
};
-#define GRAPHICSLAYER_TYPE_CASTS(ToValueTypeName, predicate) \
- TYPE_CASTS_BASE(ToValueTypeName, WebCore::GraphicsLayer, value, value->predicate, value.predicate)
+WEBCORE_EXPORT TextStream& operator<<(TextStream&, const Vector<GraphicsLayer::PlatformLayerID>&);
+WEBCORE_EXPORT TextStream& operator<<(TextStream&, const GraphicsLayer::CustomAppearance&);
} // namespace WebCore
-#ifndef NDEBUG
-// Outside the WebCore namespace for ease of invocation from gdb.
+#define SPECIALIZE_TYPE_TRAITS_GRAPHICSLAYER(ToValueTypeName, predicate) \
+SPECIALIZE_TYPE_TRAITS_BEGIN(ToValueTypeName) \
+ static bool isType(const WebCore::GraphicsLayer& layer) { return layer.predicate; } \
+SPECIALIZE_TYPE_TRAITS_END()
+
+#if ENABLE(TREE_DEBUGGING)
+// Outside the WebCore namespace for ease of invocation from the debugger.
void showGraphicsLayerTree(const WebCore::GraphicsLayer* layer);
#endif
-
-#endif // USE(ACCELERATED_COMPOSITING)
-
-#endif // GraphicsLayer_h
diff --git a/Source/WebCore/platform/graphics/GraphicsLayerClient.h b/Source/WebCore/platform/graphics/GraphicsLayerClient.h
index 90f9e823b..f6e90b8e3 100644
--- a/Source/WebCore/platform/graphics/GraphicsLayerClient.h
+++ b/Source/WebCore/platform/graphics/GraphicsLayerClient.h
@@ -13,7 +13,7 @@
* THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
@@ -26,11 +26,14 @@
#ifndef GraphicsLayerClient_h
#define GraphicsLayerClient_h
-#if USE(ACCELERATED_COMPOSITING)
+#include "IntSize.h"
+#include "TiledBacking.h"
+#include <wtf/Forward.h>
namespace WebCore {
class FloatPoint;
+class FloatRect;
class GraphicsContext;
class GraphicsLayer;
class IntPoint;
@@ -38,41 +41,59 @@ class IntRect;
class TransformationMatrix;
enum GraphicsLayerPaintingPhaseFlags {
- GraphicsLayerPaintBackground = (1 << 0),
- GraphicsLayerPaintForeground = (1 << 1),
- GraphicsLayerPaintMask = (1 << 2),
- GraphicsLayerPaintOverflowContents = (1 << 3),
- GraphicsLayerPaintCompositedScroll = (1 << 4),
- GraphicsLayerPaintAllWithOverflowClip = (GraphicsLayerPaintBackground | GraphicsLayerPaintForeground | GraphicsLayerPaintMask)
+ GraphicsLayerPaintBackground = 1 << 0,
+ GraphicsLayerPaintForeground = 1 << 1,
+ GraphicsLayerPaintMask = 1 << 2,
+ GraphicsLayerPaintClipPath = 1 << 3,
+ GraphicsLayerPaintOverflowContents = 1 << 4,
+ GraphicsLayerPaintCompositedScroll = 1 << 5,
+ GraphicsLayerPaintChildClippingMask = 1 << 6,
+ GraphicsLayerPaintAllWithOverflowClip = GraphicsLayerPaintBackground | GraphicsLayerPaintForeground
};
typedef unsigned GraphicsLayerPaintingPhase;
enum AnimatedPropertyID {
AnimatedPropertyInvalid,
- AnimatedPropertyWebkitTransform,
+ AnimatedPropertyTransform,
AnimatedPropertyOpacity,
AnimatedPropertyBackgroundColor,
- AnimatedPropertyWebkitFilter
+ AnimatedPropertyFilter
+#if ENABLE(FILTERS_LEVEL_2)
+ , AnimatedPropertyWebkitBackdropFilter
+#endif
+};
+
+enum LayerTreeAsTextBehaviorFlags {
+ LayerTreeAsTextBehaviorNormal = 0,
+ LayerTreeAsTextDebug = 1 << 0, // Dump extra debugging info like layer addresses.
+ LayerTreeAsTextIncludeVisibleRects = 1 << 1,
+ LayerTreeAsTextIncludeTileCaches = 1 << 2,
+ LayerTreeAsTextIncludeRepaintRects = 1 << 3,
+ LayerTreeAsTextIncludePaintingPhases = 1 << 4,
+ LayerTreeAsTextIncludeContentLayers = 1 << 5,
+ LayerTreeAsTextIncludePageOverlayLayers = 1 << 6,
+ LayerTreeAsTextIncludeAcceleratesDrawing = 1 << 7,
};
+typedef unsigned LayerTreeAsTextBehavior;
class GraphicsLayerClient {
public:
virtual ~GraphicsLayerClient() {}
- virtual bool shouldUseTiledBacking(const GraphicsLayer*) const { return false; }
virtual void tiledBackingUsageChanged(const GraphicsLayer*, bool /*usingTiledBacking*/) { }
// Callback for when hardware-accelerated animation started.
- virtual void notifyAnimationStarted(const GraphicsLayer*, double time) = 0;
+ virtual void notifyAnimationStarted(const GraphicsLayer*, const String& /*animationKey*/, double /*time*/) { }
+ virtual void notifyAnimationEnded(const GraphicsLayer*, const String& /*animationKey*/) { }
// Notification that a layer property changed that requires a subsequent call to flushCompositingState()
// to appear on the screen.
- virtual void notifyFlushRequired(const GraphicsLayer*) = 0;
+ virtual void notifyFlushRequired(const GraphicsLayer*) { }
// Notification that this layer requires a flush before the next display refresh.
virtual void notifyFlushBeforeDisplayRefresh(const GraphicsLayer*) { }
- virtual void paintContents(const GraphicsLayer*, GraphicsContext&, GraphicsLayerPaintingPhase, const IntRect& inClip) = 0;
+ virtual void paintContents(const GraphicsLayer*, GraphicsContext&, GraphicsLayerPaintingPhase, const FloatRect& /* inClip */) { }
virtual void didCommitChangesForLayer(const GraphicsLayer*) const { }
// Provides current transform (taking transform-origin and animations into account). Input matrix has been
@@ -87,14 +108,25 @@ public:
virtual float deviceScaleFactor() const { return 1; }
// Page scale factor.
virtual float pageScaleFactor() const { return 1; }
+ virtual float zoomedOutPageScaleFactor() const { return 0; }
virtual float contentsScaleMultiplierForNewTiles(const GraphicsLayer*) const { return 1; }
+ virtual bool paintsOpaquelyAtNonIntegralScales(const GraphicsLayer*) const { return false; }
virtual bool isTrackingRepaints() const { return false; }
- virtual bool shouldSkipLayerInDump(const GraphicsLayer*) const { return false; }
+ virtual bool shouldSkipLayerInDump(const GraphicsLayer*, LayerTreeAsTextBehavior) const { return false; }
virtual bool shouldDumpPropertyForLayer(const GraphicsLayer*, const char*) const { return true; }
+ virtual bool shouldAggressivelyRetainTiles(const GraphicsLayer*) const { return false; }
+ virtual bool shouldTemporarilyRetainTileCohorts(const GraphicsLayer*) const { return true; }
+
+ virtual bool useGiantTiles() const { return false; }
+
+ virtual bool needsPixelAligment() const { return false; }
+
+ virtual bool needsIOSDumpRenderTreeMainFrameRenderViewLayerIsAlwaysOpaqueHack(const GraphicsLayer&) const { return false; }
+
#ifndef NDEBUG
// RenderLayerBacking overrides this to verify that it is not
// currently painting contents. An ASSERT fails, if it is.
@@ -107,6 +139,4 @@ public:
} // namespace WebCore
-#endif // USE(ACCELERATED_COMPOSITING)
-
#endif // GraphicsLayerClient_h
diff --git a/Source/WebCore/platform/graphics/GraphicsLayerFactory.h b/Source/WebCore/platform/graphics/GraphicsLayerFactory.h
index 6413e93cb..bc09caf06 100644
--- a/Source/WebCore/platform/graphics/GraphicsLayerFactory.h
+++ b/Source/WebCore/platform/graphics/GraphicsLayerFactory.h
@@ -26,24 +26,18 @@
#ifndef GraphicsLayerFactory_h
#define GraphicsLayerFactory_h
-#if USE(ACCELERATED_COMPOSITING)
-
+#include "GraphicsLayer.h"
#include <wtf/Forward.h>
namespace WebCore {
-class GraphicsLayer;
-class GraphicsLayerClient;
-
class GraphicsLayerFactory {
public:
virtual ~GraphicsLayerFactory() { }
- virtual std::unique_ptr<GraphicsLayer> createGraphicsLayer(GraphicsLayerClient*) = 0;
+ virtual std::unique_ptr<GraphicsLayer> createGraphicsLayer(GraphicsLayer::Type, GraphicsLayerClient&) = 0;
};
} // namespace WebCore
-#endif // USE(ACCELERATED_COMPOSITING)
-
#endif // GraphicsLayerFactory_h
diff --git a/Source/WebCore/platform/graphics/GraphicsLayerTransform.cpp b/Source/WebCore/platform/graphics/GraphicsLayerTransform.cpp
index 04363ccdc..95dfab485 100644
--- a/Source/WebCore/platform/graphics/GraphicsLayerTransform.cpp
+++ b/Source/WebCore/platform/graphics/GraphicsLayerTransform.cpp
@@ -78,13 +78,13 @@ void GraphicsLayerTransform::setChildrenTransform(const TransformationMatrix& tr
m_dirty = true;
}
-TransformationMatrix GraphicsLayerTransform::combined()
+const TransformationMatrix& GraphicsLayerTransform::combined() const
{
ASSERT(!m_dirty);
return m_combined;
}
-TransformationMatrix GraphicsLayerTransform::combinedForChildren()
+const TransformationMatrix& GraphicsLayerTransform::combinedForChildren() const
{
ASSERT(!m_dirty);
if (m_childrenDirty)
@@ -96,10 +96,10 @@ void GraphicsLayerTransform::combineTransforms(const TransformationMatrix& paren
{
float originX = m_anchorPoint.x() * m_size.width();
float originY = m_anchorPoint.y() * m_size.height();
- m_combined =
- TransformationMatrix(parentTransform)
- .translate3d(originX + m_position.x(), originY + m_position.y(), m_anchorPoint.z() )
- .multiply(m_local);
+ m_combined = parentTransform;
+ m_combined
+ .translate3d(originX + m_position.x(), originY + m_position.y(), m_anchorPoint.z())
+ .multiply(m_local);
// The children transform will take it from here, if it gets used.
m_combinedForChildren = m_combined;
@@ -109,7 +109,7 @@ void GraphicsLayerTransform::combineTransforms(const TransformationMatrix& paren
m_childrenDirty = true;
}
-void GraphicsLayerTransform::combineTransformsForChildren()
+void GraphicsLayerTransform::combineTransformsForChildren() const
{
ASSERT(!m_dirty);
ASSERT(m_childrenDirty);
diff --git a/Source/WebCore/platform/graphics/GraphicsLayerTransform.h b/Source/WebCore/platform/graphics/GraphicsLayerTransform.h
index 5da25b4d8..5a2f66ffb 100644
--- a/Source/WebCore/platform/graphics/GraphicsLayerTransform.h
+++ b/Source/WebCore/platform/graphics/GraphicsLayerTransform.h
@@ -36,25 +36,25 @@ public:
void setFlattening(bool);
void setLocalTransform(const TransformationMatrix&);
void setChildrenTransform(const TransformationMatrix&);
- TransformationMatrix combined();
- TransformationMatrix combinedForChildren();
+ const TransformationMatrix& combined() const;
+ const TransformationMatrix& combinedForChildren() const;
void combineTransforms(const TransformationMatrix& parentTransform);
private:
- void combineTransformsForChildren();
+ void combineTransformsForChildren() const;
FloatPoint3D m_anchorPoint;
FloatPoint m_position;
FloatSize m_size;
bool m_flattening;
bool m_dirty;
- bool m_childrenDirty;
+ mutable bool m_childrenDirty;
TransformationMatrix m_local;
TransformationMatrix m_children;
TransformationMatrix m_combined;
- TransformationMatrix m_combinedForChildren;
+ mutable TransformationMatrix m_combinedForChildren;
};
}
diff --git a/Source/WebCore/platform/graphics/GraphicsLayerUpdater.cpp b/Source/WebCore/platform/graphics/GraphicsLayerUpdater.cpp
index b3c943644..a911bddb4 100644
--- a/Source/WebCore/platform/graphics/GraphicsLayerUpdater.cpp
+++ b/Source/WebCore/platform/graphics/GraphicsLayerUpdater.cpp
@@ -24,27 +24,26 @@
*/
#include "config.h"
-
-#if USE(ACCELERATED_COMPOSITING)
-
#include "GraphicsLayerUpdater.h"
+#include "DisplayRefreshMonitorManager.h"
#include "GraphicsLayer.h"
namespace WebCore {
-GraphicsLayerUpdater::GraphicsLayerUpdater(GraphicsLayerUpdaterClient* client, PlatformDisplayID displayID)
+#if USE(REQUEST_ANIMATION_FRAME_DISPLAY_MONITOR)
+GraphicsLayerUpdater::GraphicsLayerUpdater(GraphicsLayerUpdaterClient& client, PlatformDisplayID displayID)
: m_client(client)
- , m_scheduled(false)
{
-#if USE(REQUEST_ANIMATION_FRAME_DISPLAY_MONITOR)
- DisplayRefreshMonitorManager::sharedManager()->registerClient(this);
- DisplayRefreshMonitorManager::sharedManager()->windowScreenDidChange(displayID, this);
- DisplayRefreshMonitorManager::sharedManager()->scheduleAnimation(this);
+ DisplayRefreshMonitorManager::sharedManager().registerClient(*this);
+ DisplayRefreshMonitorManager::sharedManager().windowScreenDidChange(displayID, *this);
+ DisplayRefreshMonitorManager::sharedManager().scheduleAnimation(*this);
+}
#else
- UNUSED_PARAM(displayID);
-#endif
+GraphicsLayerUpdater::GraphicsLayerUpdater(GraphicsLayerUpdaterClient&, PlatformDisplayID)
+{
}
+#endif
GraphicsLayerUpdater::~GraphicsLayerUpdater()
{
@@ -57,7 +56,7 @@ void GraphicsLayerUpdater::scheduleUpdate()
return;
#if USE(REQUEST_ANIMATION_FRAME_DISPLAY_MONITOR)
- DisplayRefreshMonitorManager::sharedManager()->scheduleAnimation(this);
+ DisplayRefreshMonitorManager::sharedManager().scheduleAnimation(*this);
#endif
m_scheduled = true;
}
@@ -65,21 +64,25 @@ void GraphicsLayerUpdater::scheduleUpdate()
void GraphicsLayerUpdater::screenDidChange(PlatformDisplayID displayID)
{
#if USE(REQUEST_ANIMATION_FRAME_DISPLAY_MONITOR)
- DisplayRefreshMonitorManager::sharedManager()->windowScreenDidChange(displayID, this);
+ DisplayRefreshMonitorManager::sharedManager().windowScreenDidChange(displayID, *this);
#else
UNUSED_PARAM(displayID);
#endif
}
-void GraphicsLayerUpdater::displayRefreshFired(double timestamp)
+#if USE(REQUEST_ANIMATION_FRAME_DISPLAY_MONITOR)
+void GraphicsLayerUpdater::displayRefreshFired()
{
- UNUSED_PARAM(timestamp);
m_scheduled = false;
-
- if (m_client)
- m_client->flushLayers(this);
+ m_client.flushLayersSoon(*this);
}
+#endif
-} // namespace WebCore
+#if USE(REQUEST_ANIMATION_FRAME_DISPLAY_MONITOR)
+RefPtr<DisplayRefreshMonitor> GraphicsLayerUpdater::createDisplayRefreshMonitor(PlatformDisplayID displayID) const
+{
+ return m_client.createDisplayRefreshMonitor(displayID);
+}
+#endif
-#endif // USE(ACCELERATED_COMPOSITING)
+} // namespace WebCore
diff --git a/Source/WebCore/platform/graphics/GraphicsLayerUpdater.h b/Source/WebCore/platform/graphics/GraphicsLayerUpdater.h
index 108099ae6..472f2d3b0 100644
--- a/Source/WebCore/platform/graphics/GraphicsLayerUpdater.h
+++ b/Source/WebCore/platform/graphics/GraphicsLayerUpdater.h
@@ -23,12 +23,9 @@
* THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef GraphicsLayerUpdater_h
-#define GraphicsLayerUpdater_h
+#pragma once
-#if USE(ACCELERATED_COMPOSITING)
-
-#include "DisplayRefreshMonitor.h"
+#include "DisplayRefreshMonitorClient.h"
#include "PlatformScreen.h"
namespace WebCore {
@@ -38,7 +35,10 @@ class GraphicsLayerUpdater;
class GraphicsLayerUpdaterClient {
public:
virtual ~GraphicsLayerUpdaterClient() { }
- virtual void flushLayers(GraphicsLayerUpdater*) = 0;
+ virtual void flushLayersSoon(GraphicsLayerUpdater&) = 0;
+#if USE(REQUEST_ANIMATION_FRAME_DISPLAY_MONITOR)
+ virtual RefPtr<DisplayRefreshMonitor> createDisplayRefreshMonitor(PlatformDisplayID) const = 0;
+#endif
};
class GraphicsLayerUpdater
@@ -46,22 +46,24 @@ class GraphicsLayerUpdater
: public DisplayRefreshMonitorClient
#endif
{
+ WTF_MAKE_FAST_ALLOCATED;
public:
- GraphicsLayerUpdater(GraphicsLayerUpdaterClient*, PlatformDisplayID);
+ GraphicsLayerUpdater(GraphicsLayerUpdaterClient&, PlatformDisplayID);
virtual ~GraphicsLayerUpdater();
void scheduleUpdate();
void screenDidChange(PlatformDisplayID);
-private:
- virtual void displayRefreshFired(double timestamp);
+#if USE(REQUEST_ANIMATION_FRAME_DISPLAY_MONITOR)
+ RefPtr<DisplayRefreshMonitor> createDisplayRefreshMonitor(PlatformDisplayID) const override;
+#endif
- GraphicsLayerUpdaterClient* m_client;
- bool m_scheduled;
+private:
+#if USE(REQUEST_ANIMATION_FRAME_DISPLAY_MONITOR)
+ void displayRefreshFired() override;
+ GraphicsLayerUpdaterClient& m_client;
+#endif
+ bool m_scheduled { false };
};
} // namespace WebCore
-
-#endif // USE(ACCELERATED_COMPOSITING)
-
-#endif // GraphicsLayerUpdater_h
diff --git a/Source/WebCore/platform/graphics/GraphicsTypes.cpp b/Source/WebCore/platform/graphics/GraphicsTypes.cpp
index 8d1150194..28e5cc6bb 100644
--- a/Source/WebCore/platform/graphics/GraphicsTypes.cpp
+++ b/Source/WebCore/platform/graphics/GraphicsTypes.cpp
@@ -1,6 +1,7 @@
/*
- * Copyright (C) 2006 Apple Computer, Inc. All rights reserved.
+ * Copyright (C) 2006 Apple Inc. All rights reserved.
* Copyright (C) 2012 Rik Cabanier (cabanier@adobe.com)
+ * Copyright (C) 2014 Adobe Systems Incorporated. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -11,10 +12,10 @@
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
- * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
@@ -27,6 +28,7 @@
#include "config.h"
#include "GraphicsTypes.h"
+#include "TextStream.h"
#include <wtf/Assertions.h>
#include <wtf/text/WTFString.h>
@@ -45,15 +47,17 @@ static const char* const compositeOperatorNames[] = {
"destination-atop",
"xor",
"darker",
- "lighter"
+ "lighter",
+ "difference"
};
static const char* const blendOperatorNames[] = {
+ "normal",
"multiply",
"screen",
- "overlay",
"darken",
"lighten",
+ "overlay",
"color-dodge",
"color-burn",
"hard-light",
@@ -63,11 +67,25 @@ static const char* const blendOperatorNames[] = {
"hue",
"saturation",
"color",
- "luminosity"
+ "luminosity",
+ "plus-darker",
+ "plus-lighter"
};
const int numCompositeOperatorNames = WTF_ARRAY_LENGTH(compositeOperatorNames);
const int numBlendOperatorNames = WTF_ARRAY_LENGTH(blendOperatorNames);
+bool parseBlendMode(const String& s, BlendMode& blendMode)
+{
+ for (int i = 0; i < numBlendOperatorNames; i++) {
+ if (s == blendOperatorNames[i]) {
+ blendMode = static_cast<BlendMode>(i + BlendModeNormal);
+ return true;
+ }
+ }
+
+ return false;
+}
+
bool parseCompositeAndBlendOperator(const String& s, CompositeOperator& op, BlendMode& blendOp)
{
for (int i = 0; i < numCompositeOperatorNames; i++) {
@@ -78,13 +96,10 @@ bool parseCompositeAndBlendOperator(const String& s, CompositeOperator& op, Blen
}
}
- for (int i = 0; i < numBlendOperatorNames; i++) {
- if (s == blendOperatorNames[i]) {
- blendOp = static_cast<BlendMode>(i+1);
- // For now, blending will always assume source-over. This will be fixed in the future
- op = CompositeSourceOver;
- return true;
- }
+ if (parseBlendMode(s, blendOp)) {
+ // For now, blending will always assume source-over. This will be fixed in the future
+ op = CompositeSourceOver;
+ return true;
}
return false;
@@ -96,13 +111,20 @@ String compositeOperatorName(CompositeOperator op, BlendMode blendOp)
{
ASSERT(op >= 0);
ASSERT(op < numCompositeOperatorNames);
- ASSERT(blendOp >= 0);
+ ASSERT(blendOp >= BlendModeNormal);
ASSERT(blendOp <= numBlendOperatorNames);
- if (blendOp != BlendModeNormal)
- return blendOperatorNames[blendOp-1];
+ if (blendOp > BlendModeNormal)
+ return blendOperatorNames[blendOp - BlendModeNormal];
return compositeOperatorNames[op];
}
+static String blendModeName(BlendMode blendOp)
+{
+ ASSERT(blendOp >= BlendModeNormal);
+ ASSERT(blendOp <= BlendModePlusLighter);
+ return blendOperatorNames[blendOp - BlendModeNormal];
+}
+
bool parseLineCap(const String& s, LineCap& cap)
{
if (s == "butt") {
@@ -223,4 +245,61 @@ bool parseTextBaseline(const String& s, TextBaseline& baseline)
return false;
}
+TextStream& operator<<(TextStream& ts, CompositeOperator op)
+{
+ return ts << compositeOperatorName(op, BlendModeNormal);
+}
+
+TextStream& operator<<(TextStream& ts, BlendMode blendMode)
+{
+ return ts << blendModeName(blendMode);
+}
+
+TextStream& operator<<(TextStream& ts, WindRule rule)
+{
+ switch (rule) {
+ case RULE_NONZERO:
+ ts << "NON-ZERO";
+ break;
+ case RULE_EVENODD:
+ ts << "EVEN-ODD";
+ break;
+ }
+
+ return ts;
+}
+
+TextStream& operator<<(TextStream& ts, LineCap capStyle)
+{
+ switch (capStyle) {
+ case ButtCap:
+ ts << "BUTT";
+ break;
+ case RoundCap:
+ ts << "ROUND";
+ break;
+ case SquareCap:
+ ts << "SQUARE";
+ break;
+ }
+ return ts;
+}
+
+TextStream& operator<<(TextStream& ts, LineJoin joinStyle)
+{
+ switch (joinStyle) {
+ case MiterJoin:
+ ts << "MITER";
+ break;
+ case RoundJoin:
+ ts << "ROUND";
+ break;
+ case BevelJoin:
+ ts << "BEVEL";
+ break;
+ }
+ return ts;
+}
+
+
}
diff --git a/Source/WebCore/platform/graphics/GraphicsTypes.h b/Source/WebCore/platform/graphics/GraphicsTypes.h
index 586f7ec69..ede792db5 100644
--- a/Source/WebCore/platform/graphics/GraphicsTypes.h
+++ b/Source/WebCore/platform/graphics/GraphicsTypes.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2004, 2005, 2006 Apple Computer, Inc. All rights reserved.
+ * Copyright (C) 2004, 2005, 2006 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -10,10 +10,10 @@
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
- * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
@@ -26,76 +26,101 @@
#ifndef GraphicsTypes_h
#define GraphicsTypes_h
+#include "WindRule.h"
#include <wtf/Forward.h>
namespace WebCore {
- enum CompositeOperator {
- CompositeClear,
- CompositeCopy,
- CompositeSourceOver,
- CompositeSourceIn,
- CompositeSourceOut,
- CompositeSourceAtop,
- CompositeDestinationOver,
- CompositeDestinationIn,
- CompositeDestinationOut,
- CompositeDestinationAtop,
- CompositeXOR,
- CompositePlusDarker,
- CompositePlusLighter,
- CompositeDifference
- };
-
- enum BlendMode {
- BlendModeNormal,
- BlendModeMultiply,
- BlendModeScreen,
- BlendModeOverlay,
- BlendModeDarken,
- BlendModeLighten,
- BlendModeColorDodge,
- BlendModeColorBurn,
- BlendModeHardLight,
- BlendModeSoftLight,
- BlendModeDifference,
- BlendModeExclusion,
- BlendModeHue,
- BlendModeSaturation,
- BlendModeColor,
- BlendModeLuminosity
- };
-
- enum GradientSpreadMethod {
- SpreadMethodPad,
- SpreadMethodReflect,
- SpreadMethodRepeat
- };
-
- enum LineCap { ButtCap, RoundCap, SquareCap };
-
- enum LineJoin { MiterJoin, RoundJoin, BevelJoin };
-
- enum HorizontalAlignment { AlignLeft, AlignRight, AlignHCenter };
-
- enum TextBaseline { AlphabeticTextBaseline, TopTextBaseline, MiddleTextBaseline, BottomTextBaseline, IdeographicTextBaseline, HangingTextBaseline };
-
- enum TextAlign { StartTextAlign, EndTextAlign, LeftTextAlign, CenterTextAlign, RightTextAlign };
-
- String compositeOperatorName(CompositeOperator, BlendMode);
- bool parseCompositeAndBlendOperator(const String&, CompositeOperator&, BlendMode&);
-
- String lineCapName(LineCap);
- bool parseLineCap(const String&, LineCap&);
-
- String lineJoinName(LineJoin);
- bool parseLineJoin(const String&, LineJoin&);
-
- String textAlignName(TextAlign);
- bool parseTextAlign(const String&, TextAlign&);
-
- String textBaselineName(TextBaseline);
- bool parseTextBaseline(const String&, TextBaseline&);
+enum CompositeOperator {
+ CompositeClear,
+ CompositeCopy,
+ CompositeSourceOver,
+ CompositeSourceIn,
+ CompositeSourceOut,
+ CompositeSourceAtop,
+ CompositeDestinationOver,
+ CompositeDestinationIn,
+ CompositeDestinationOut,
+ CompositeDestinationAtop,
+ CompositeXOR,
+ CompositePlusDarker,
+ CompositePlusLighter,
+ CompositeDifference
+};
+
+enum BlendMode {
+ BlendModeNormal = 1, // Start with 1 to match SVG's blendmode enumeration.
+ BlendModeMultiply,
+ BlendModeScreen,
+ BlendModeDarken,
+ BlendModeLighten,
+ BlendModeOverlay,
+ BlendModeColorDodge,
+ BlendModeColorBurn,
+ BlendModeHardLight,
+ BlendModeSoftLight,
+ BlendModeDifference,
+ BlendModeExclusion,
+ BlendModeHue,
+ BlendModeSaturation,
+ BlendModeColor,
+ BlendModeLuminosity,
+ BlendModePlusDarker,
+ BlendModePlusLighter
+};
+
+enum GradientSpreadMethod {
+ SpreadMethodPad,
+ SpreadMethodReflect,
+ SpreadMethodRepeat
+};
+
+enum InterpolationQuality {
+ InterpolationDefault,
+ InterpolationNone,
+ InterpolationLow,
+ InterpolationMedium,
+ InterpolationHigh
+};
+
+enum LineCap { ButtCap, RoundCap, SquareCap };
+
+enum LineJoin { MiterJoin, RoundJoin, BevelJoin };
+
+enum HorizontalAlignment { AlignLeft, AlignRight, AlignHCenter };
+
+enum TextBaseline { AlphabeticTextBaseline, TopTextBaseline, MiddleTextBaseline, BottomTextBaseline, IdeographicTextBaseline, HangingTextBaseline };
+
+enum TextAlign { StartTextAlign, EndTextAlign, LeftTextAlign, CenterTextAlign, RightTextAlign };
+
+enum RenderingMode {
+ Unaccelerated,
+ UnacceleratedNonPlatformBuffer, // Use plain memory allocation rather than platform API to allocate backing store.
+ Accelerated
+};
+
+String compositeOperatorName(CompositeOperator, BlendMode);
+bool parseBlendMode(const String&, BlendMode&);
+bool parseCompositeAndBlendOperator(const String&, CompositeOperator&, BlendMode&);
+
+String lineCapName(LineCap);
+bool parseLineCap(const String&, LineCap&);
+
+String lineJoinName(LineJoin);
+bool parseLineJoin(const String&, LineJoin&);
+
+String textAlignName(TextAlign);
+bool parseTextAlign(const String&, TextAlign&);
+
+String textBaselineName(TextBaseline);
+bool parseTextBaseline(const String&, TextBaseline&);
+
+class TextStream;
+WEBCORE_EXPORT TextStream& operator<<(TextStream&, BlendMode);
+WEBCORE_EXPORT TextStream& operator<<(TextStream&, CompositeOperator);
+WEBCORE_EXPORT TextStream& operator<<(TextStream&, WindRule);
+WEBCORE_EXPORT TextStream& operator<<(TextStream&, LineCap);
+WEBCORE_EXPORT TextStream& operator<<(TextStream&, LineJoin);
} // namespace WebCore
diff --git a/Source/WebCore/platform/graphics/GraphicsTypes3D.h b/Source/WebCore/platform/graphics/GraphicsTypes3D.h
index 1fa8eaab1..1ab1a4f64 100644
--- a/Source/WebCore/platform/graphics/GraphicsTypes3D.h
+++ b/Source/WebCore/platform/graphics/GraphicsTypes3D.h
@@ -10,10 +10,10 @@
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
- * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
@@ -46,6 +46,8 @@ typedef float GC3Dclampf;
typedef intptr_t GC3Dintptr;
typedef intptr_t GC3Dsizeiptr;
typedef char GC3Dchar;
+typedef long long GC3Dint64;
+typedef unsigned long long GC3Duint64;
typedef GC3Duint Platform3DObject;
diff --git a/Source/WebCore/platform/graphics/ISOVTTCue.cpp b/Source/WebCore/platform/graphics/ISOVTTCue.cpp
new file mode 100644
index 000000000..f231948cd
--- /dev/null
+++ b/Source/WebCore/platform/graphics/ISOVTTCue.cpp
@@ -0,0 +1,153 @@
+/*
+ * Copyright (C) 2014 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "ISOVTTCue.h"
+
+#include "Logging.h"
+#include <runtime/ArrayBuffer.h>
+#include <runtime/DataView.h>
+#include <runtime/Int8Array.h>
+#include <runtime/JSCInlines.h>
+#include <runtime/TypedArrayInlines.h>
+#include <wtf/NeverDestroyed.h>
+#include <wtf/text/CString.h>
+#include <wtf/text/StringBuilder.h>
+
+namespace JSC {
+class ArrayBuffer;
+}
+
+namespace WebCore {
+
+ISOBox::ISOBox(ArrayBuffer* data)
+{
+ m_type = peekType(data);
+ m_length = peekLength(data);
+ ASSERT(m_length >= 8);
+}
+
+String ISOBox::peekType(ArrayBuffer* data)
+{
+ ASSERT_WITH_SECURITY_IMPLICATION(data->byteLength() >= 2 * sizeof(uint32_t));
+ if (data->byteLength() < 2 * sizeof(uint32_t))
+ return emptyString();
+
+ StringBuilder builder;
+ RefPtr<Int8Array> array = JSC::Int8Array::create(data, 4, sizeof(uint32_t));
+ for (int i = 0; i < 4; ++i)
+ builder.append(array->item(i));
+ return builder.toString();
+}
+
+size_t ISOBox::peekLength(ArrayBuffer* data)
+{
+ ASSERT_WITH_SECURITY_IMPLICATION(data->byteLength() >= sizeof(uint32_t));
+ if (data->byteLength() < sizeof(uint32_t))
+ return 0;
+
+ return JSC::DataView::create(data, 0, sizeof(uint32_t))->get<uint32_t>(0, false);
+}
+
+String ISOBox::peekString(ArrayBuffer* data, unsigned offset, unsigned length)
+{
+ ASSERT_WITH_SECURITY_IMPLICATION(offset + length <= data->byteLength());
+ if (data->byteLength() < offset + length)
+ return emptyString();
+
+ return String::fromUTF8(JSC::Uint8Array::create(data, offset, length)->data(), length);
+}
+
+static const AtomicString& vttCueBoxType()
+{
+ static NeverDestroyed<AtomicString> vttc("vttc", AtomicString::ConstructFromLiteral);
+ return vttc;
+}
+
+static const AtomicString& vttIdBoxType()
+{
+ static NeverDestroyed<AtomicString> iden("iden", AtomicString::ConstructFromLiteral);
+ return iden;
+}
+
+static const AtomicString& vttSettingsBoxType()
+{
+ static NeverDestroyed<AtomicString> sttg("sttg", AtomicString::ConstructFromLiteral);
+ return sttg;
+}
+
+static const AtomicString& vttPayloadBoxType()
+{
+ static NeverDestroyed<AtomicString> payl("payl", AtomicString::ConstructFromLiteral);
+ return payl;
+}
+
+static const AtomicString& vttCurrentTimeBoxType()
+{
+ static NeverDestroyed<AtomicString> ctim("ctim", AtomicString::ConstructFromLiteral);
+ return ctim;
+}
+
+static const AtomicString& vttCueSourceIDBoxType()
+{
+ static NeverDestroyed<AtomicString> vsid("vsid", AtomicString::ConstructFromLiteral);
+ return vsid;
+}
+
+const AtomicString& ISOWebVTTCue::boxType()
+{
+ return vttCueBoxType();
+}
+
+ISOWebVTTCue::ISOWebVTTCue(const MediaTime& presentationTime, const MediaTime& duration, JSC::ArrayBuffer* buffer)
+ : ISOBox(buffer)
+ , m_presentationTime(presentationTime)
+ , m_duration(duration)
+{
+ size_t offset = ISOBox::boxHeaderSize();
+ while (offset < length() && length() - offset > ISOBox::boxHeaderSize()) {
+ RefPtr<ArrayBuffer> subBuffer = buffer->slice(offset);
+ String boxType = ISOBox::peekType(subBuffer.get());
+ size_t boxSize = ISOBox::peekLength(subBuffer.get());
+ size_t boxDataSize = boxSize - ISOBox::boxHeaderSize();
+
+ if (boxType == vttCueSourceIDBoxType())
+ m_sourceID = peekString(subBuffer.get(), ISOBox::boxHeaderSize(), boxDataSize);
+ else if (boxType == vttIdBoxType())
+ m_identifier = peekString(subBuffer.get(), ISOBox::boxHeaderSize(), boxDataSize);
+ else if (boxType == vttCurrentTimeBoxType())
+ m_originalStartTime = peekString(subBuffer.get(), ISOBox::boxHeaderSize(), boxDataSize);
+ else if (boxType == vttSettingsBoxType())
+ m_settings = peekString(subBuffer.get(), ISOBox::boxHeaderSize(), boxDataSize);
+ else if (boxType == vttPayloadBoxType())
+ m_cueText = peekString(subBuffer.get(), ISOBox::boxHeaderSize(), boxDataSize);
+ else
+ LOG(Media, "ISOWebVTTCue::ISOWebVTTCue - skipping box id = \"%s\", size = %zu", boxType.utf8().data(), boxSize);
+
+ offset += boxSize;
+ }
+}
+
+} // namespace WebCore
diff --git a/Source/WebCore/platform/graphics/ISOVTTCue.h b/Source/WebCore/platform/graphics/ISOVTTCue.h
new file mode 100644
index 000000000..edc57da45
--- /dev/null
+++ b/Source/WebCore/platform/graphics/ISOVTTCue.h
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2014 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef ISOVTTCue_h
+#define ISOVTTCue_h
+
+#include <wtf/MediaTime.h>
+#include <wtf/text/WTFString.h>
+
+namespace JSC {
+class ArrayBuffer;
+}
+
+namespace WebCore {
+
+// An ISOBox represents a ISO-BMFF box. Data in the structure is big-endian. The layout of the data structure as follows:
+// 4 bytes : 4CC : identifier
+// 4 bytes : unsigned : length
+class ISOBox {
+public:
+ static String peekType(JSC::ArrayBuffer*);
+ static size_t peekLength(JSC::ArrayBuffer*);
+ static String peekString(JSC::ArrayBuffer*, unsigned offset, unsigned length);
+ static unsigned boxHeaderSize() { return 2 * sizeof(uint32_t); }
+
+ size_t length() const { return m_length; }
+ const AtomicString& type() const { return m_type; }
+
+protected:
+ ISOBox(JSC::ArrayBuffer*);
+
+private:
+ size_t m_length;
+ AtomicString m_type;
+};
+
+// 4 bytes : 4CC : identifier = 'vttc'
+// 4 bytes : unsigned : length
+// N bytes : CueSourceIDBox : box : optional
+// N bytes : CueIDBox : box : optional
+// N bytes : CueTimeBox : box : optional
+// N bytes : CueSettingsBox : box : optional
+// N bytes : CuePayloadBox : box : required
+class ISOWebVTTCue : public ISOBox {
+public:
+ ISOWebVTTCue(const MediaTime& presentationTime, const MediaTime& duration, JSC::ArrayBuffer*);
+
+ static const AtomicString& boxType();
+
+ const MediaTime& presentationTime() const { return m_presentationTime; }
+ const MediaTime& duration() const { return m_duration; }
+
+ const String& sourceID() const { return m_sourceID; }
+ const String& id() const { return m_identifier; }
+ const String& originalStartTime() const { return m_originalStartTime; }
+ const String& settings() const { return m_settings; }
+ const String& cueText() const { return m_cueText; }
+
+private:
+ MediaTime m_presentationTime;
+ MediaTime m_duration;
+
+ String m_sourceID;
+ String m_identifier;
+ String m_originalStartTime;
+ String m_settings;
+ String m_cueText;
+};
+
+}
+
+#endif // ISOVTTCue_h
diff --git a/Source/WebCore/platform/graphics/Icon.h b/Source/WebCore/platform/graphics/Icon.h
index 82798c306..7b6a30470 100644
--- a/Source/WebCore/platform/graphics/Icon.h
+++ b/Source/WebCore/platform/graphics/Icon.h
@@ -21,17 +21,16 @@
#ifndef Icon_h
#define Icon_h
-#include <wtf/PassRefPtr.h>
-#include <wtf/RefCounted.h>
#include <wtf/Forward.h>
+#include <wtf/RefCounted.h>
+#include <wtf/RefPtr.h>
+#include <wtf/RetainPtr.h>
#include <wtf/Vector.h>
#if PLATFORM(IOS)
-#include "NativeImagePtr.h"
+#include "NativeImage.h"
#include <CoreGraphics/CoreGraphics.h>
-#include <wtf/RetainPtr.h>
#elif PLATFORM(MAC)
-#include <wtf/RetainPtr.h>
OBJC_CLASS NSImage;
#elif PLATFORM(WIN)
typedef struct HICON__* HICON;
@@ -42,28 +41,33 @@ typedef struct _GdkPixbuf GdkPixbuf;
namespace WebCore {
class GraphicsContext;
-class IntRect;
+class FloatRect;
class Icon : public RefCounted<Icon> {
public:
- static PassRefPtr<Icon> createIconForFiles(const Vector<String>& filenames);
+ WEBCORE_EXPORT static RefPtr<Icon> createIconForFiles(const Vector<String>& filenames);
- ~Icon();
+ WEBCORE_EXPORT ~Icon();
- void paint(GraphicsContext*, const IntRect&);
+ void paint(GraphicsContext&, const FloatRect&);
#if PLATFORM(WIN)
- static PassRefPtr<Icon> create(HICON hIcon) { return adoptRef(new Icon(hIcon)); }
+ static RefPtr<Icon> create(HICON hIcon) { return adoptRef(new Icon(hIcon)); }
#endif
#if PLATFORM(IOS)
// FIXME: Make this work for non-iOS ports and remove the PLATFORM(IOS)-guard.
- static PassRefPtr<Icon> createIconForImage(NativeImagePtr);
+ WEBCORE_EXPORT static RefPtr<Icon> createIconForImage(const NativeImagePtr&);
+#endif
+
+#if PLATFORM(MAC)
+ static RefPtr<Icon> createIconForUTI(const String&);
+ static RefPtr<Icon> createIconForFileExtension(const String&);
#endif
private:
#if PLATFORM(IOS)
- Icon(CGImageRef);
+ Icon(const RetainPtr<CGImageRef>&);
RetainPtr<CGImageRef> m_cgImage;
#elif PLATFORM(MAC)
Icon(NSImage*);
@@ -74,9 +78,6 @@ private:
#elif PLATFORM(GTK)
Icon();
GdkPixbuf* m_icon;
-#elif PLATFORM(EFL)
- Icon();
- Evas_Object* m_icon;
#endif
};
diff --git a/Source/WebCore/platform/graphics/Image.cpp b/Source/WebCore/platform/graphics/Image.cpp
index 0e3651a3c..bff9854e6 100644
--- a/Source/WebCore/platform/graphics/Image.cpp
+++ b/Source/WebCore/platform/graphics/Image.cpp
@@ -1,6 +1,6 @@
/*
* Copyright (C) 2006 Samuel Weinig (sam.weinig@gmail.com)
- * Copyright (C) 2004, 2005, 2006 Apple Computer, Inc. All rights reserved.
+ * Copyright (C) 2004, 2005, 2006 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -11,10 +11,10 @@
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
- * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
@@ -31,10 +31,10 @@
#include "BitmapImage.h"
#include "GraphicsContext.h"
#include "ImageObserver.h"
-#include "IntRect.h"
#include "Length.h"
#include "MIMETypeRegistry.h"
#include "SharedBuffer.h"
+#include "TextStream.h"
#include <math.h>
#include <wtf/MainThread.h>
#include <wtf/StdLibExtras.h>
@@ -57,8 +57,8 @@ Image::~Image()
Image* Image::nullImage()
{
ASSERT(isMainThread());
- static Image* nullImage = BitmapImage::create().leakRef();
- return nullImage;
+ static Image& nullImage = BitmapImage::create().leakRef();
+ return &nullImage;
}
bool Image::supportsType(const String& type)
@@ -66,9 +66,9 @@ bool Image::supportsType(const String& type)
return MIMETypeRegistry::isSupportedImageResourceMIMEType(type);
}
-bool Image::setData(PassRefPtr<SharedBuffer> data, bool allDataReceived)
+bool Image::setData(RefPtr<SharedBuffer>&& data, bool allDataReceived)
{
- m_encodedImageData = data;
+ m_encodedImageData = WTFMove(data);
if (!m_encodedImageData.get())
return true;
@@ -79,32 +79,32 @@ bool Image::setData(PassRefPtr<SharedBuffer> data, bool allDataReceived)
return dataChanged(allDataReceived);
}
-void Image::fillWithSolidColor(GraphicsContext* ctxt, const FloatRect& dstRect, const Color& color, ColorSpace styleColorSpace, CompositeOperator op)
+void Image::fillWithSolidColor(GraphicsContext& ctxt, const FloatRect& dstRect, const Color& color, CompositeOperator op)
{
- if (!color.alpha())
+ if (!color.isVisible())
return;
- CompositeOperator previousOperator = ctxt->compositeOperation();
- ctxt->setCompositeOperation(!color.hasAlpha() && op == CompositeSourceOver ? CompositeCopy : op);
- ctxt->fillRect(dstRect, color, styleColorSpace);
- ctxt->setCompositeOperation(previousOperator);
+ CompositeOperator previousOperator = ctxt.compositeOperation();
+ ctxt.setCompositeOperation(color.isOpaque() && op == CompositeSourceOver ? CompositeCopy : op);
+ ctxt.fillRect(dstRect, color);
+ ctxt.setCompositeOperation(previousOperator);
}
-void Image::draw(GraphicsContext* ctx, const FloatRect& dstRect, const FloatRect& srcRect, ColorSpace styleColorSpace, CompositeOperator op, BlendMode blendMode, ImageOrientationDescription description)
+void Image::drawTiled(GraphicsContext& ctxt, const FloatRect& destRect, const FloatPoint& srcPoint, const FloatSize& scaledTileSize, const FloatSize& spacing, CompositeOperator op, BlendMode blendMode)
{
- draw(ctx, dstRect, srcRect, styleColorSpace, op, blendMode, description);
-}
-
-void Image::drawTiled(GraphicsContext* ctxt, const FloatRect& destRect, const FloatPoint& srcPoint, const FloatSize& scaledTileSize, ColorSpace styleColorSpace, CompositeOperator op, BlendMode blendMode)
-{
- if (mayFillWithSolidColor()) {
- fillWithSolidColor(ctxt, destRect, solidColor(), styleColorSpace, op);
+ Color color = singlePixelSolidColor();
+ if (color.isValid()) {
+ fillWithSolidColor(ctxt, destRect, color, op);
return;
}
ASSERT(!isBitmapImage() || notSolidColor());
+#if PLATFORM(IOS)
+ FloatSize intrinsicTileSize = originalSize();
+#else
FloatSize intrinsicTileSize = size();
+#endif
if (hasRelativeWidth())
intrinsicTileSize.setWidth(scaledTileSize.width());
if (hasRelativeHeight())
@@ -114,32 +114,32 @@ void Image::drawTiled(GraphicsContext* ctxt, const FloatRect& destRect, const Fl
scaledTileSize.height() / intrinsicTileSize.height());
FloatRect oneTileRect;
- FloatSize actualTileSize(scaledTileSize.width() + spaceSize().width(), scaledTileSize.height() + spaceSize().height());
+ FloatSize actualTileSize(scaledTileSize.width() + spacing.width(), scaledTileSize.height() + spacing.height());
oneTileRect.setX(destRect.x() + fmodf(fmodf(-srcPoint.x(), actualTileSize.width()) - actualTileSize.width(), actualTileSize.width()));
oneTileRect.setY(destRect.y() + fmodf(fmodf(-srcPoint.y(), actualTileSize.height()) - actualTileSize.height(), actualTileSize.height()));
oneTileRect.setSize(scaledTileSize);
- // Check and see if a single draw of the image can cover the entire area we are supposed to tile.
- if (oneTileRect.contains(destRect) && !ctxt->drawLuminanceMask()) {
+ // Check and see if a single draw of the image can cover the entire area we are supposed to tile.
+ if (oneTileRect.contains(destRect) && !ctxt.drawLuminanceMask()) {
FloatRect visibleSrcRect;
visibleSrcRect.setX((destRect.x() - oneTileRect.x()) / scale.width());
visibleSrcRect.setY((destRect.y() - oneTileRect.y()) / scale.height());
visibleSrcRect.setWidth(destRect.width() / scale.width());
visibleSrcRect.setHeight(destRect.height() / scale.height());
- draw(ctxt, destRect, visibleSrcRect, styleColorSpace, op, blendMode, ImageOrientationDescription());
+ draw(ctxt, destRect, visibleSrcRect, op, blendMode, ImageOrientationDescription());
return;
}
#if PLATFORM(IOS)
// When using accelerated drawing on iOS, it's faster to stretch an image than to tile it.
- if (ctxt->isAcceleratedContext()) {
+ if (ctxt.isAcceleratedContext()) {
if (size().width() == 1 && intersection(oneTileRect, destRect).height() == destRect.height()) {
FloatRect visibleSrcRect;
visibleSrcRect.setX(0);
visibleSrcRect.setY((destRect.y() - oneTileRect.y()) / scale.height());
visibleSrcRect.setWidth(1);
visibleSrcRect.setHeight(destRect.height() / scale.height());
- draw(ctxt, destRect, visibleSrcRect, styleColorSpace, op, BlendModeNormal, ImageOrientationDescription());
+ draw(ctxt, destRect, visibleSrcRect, op, BlendModeNormal, ImageOrientationDescription());
return;
}
if (size().height() == 1 && intersection(oneTileRect, destRect).width() == destRect.width()) {
@@ -148,92 +148,138 @@ void Image::drawTiled(GraphicsContext* ctxt, const FloatRect& destRect, const Fl
visibleSrcRect.setY(0);
visibleSrcRect.setWidth(destRect.width() / scale.width());
visibleSrcRect.setHeight(1);
- draw(ctxt, destRect, visibleSrcRect, styleColorSpace, op, BlendModeNormal, ImageOrientationDescription());
+ draw(ctxt, destRect, visibleSrcRect, op, BlendModeNormal, ImageOrientationDescription());
return;
}
}
#endif
-#if PLATFORM(IOS)
- // CGPattern uses lots of memory got caching when the tile size is large (<rdar://problem/4691859>,
- // <rdar://problem/6239505>). Memory consumption depends on the transformed tile size which can get
+ // Patterned images and gradients can use lots of memory for caching when the
+ // tile size is large (<rdar://problem/4691859>, <rdar://problem/6239505>).
+ // Memory consumption depends on the transformed tile size which can get
// larger than the original tile if user zooms in enough.
+#if PLATFORM(IOS)
const float maxPatternTilePixels = 512 * 512;
- FloatRect transformedTileSize = ctxt->getCTM().mapRect(FloatRect(FloatPoint(), scaledTileSize));
+#else
+ const float maxPatternTilePixels = 2048 * 2048;
+#endif
+ FloatRect transformedTileSize = ctxt.getCTM().mapRect(FloatRect(FloatPoint(), scaledTileSize));
float transformedTileSizePixels = transformedTileSize.width() * transformedTileSize.height();
+ FloatRect currentTileRect = oneTileRect;
if (transformedTileSizePixels > maxPatternTilePixels) {
- float fromY = (destRect.y() - oneTileRect.y()) / scale.height();
- float toY = oneTileRect.y();
- while (toY < CGRectGetMaxY(destRect)) {
- float fromX = (destRect.x() - oneTileRect.x()) / scale.width();
- float toX = oneTileRect.x();
- while (toX < CGRectGetMaxX(destRect)) {
- CGRect toRect = CGRectIntersection(destRect, CGRectMake(toX, toY, oneTileRect.width(), oneTileRect.height()));
- CGRect fromRect = CGRectMake(fromX, fromY, toRect.size.width / scale.width(), toRect.size.height / scale.height());
- draw(ctxt, toRect, fromRect, styleColorSpace, op, BlendModeNormal, ImageOrientationDescription());
- toX += oneTileRect.width();
- fromX = 0;
+ GraphicsContextStateSaver stateSaver(ctxt);
+ ctxt.clip(destRect);
+
+ currentTileRect.shiftYEdgeTo(destRect.y());
+ float toY = currentTileRect.y();
+ while (toY < destRect.maxY()) {
+ currentTileRect.shiftXEdgeTo(destRect.x());
+ float toX = currentTileRect.x();
+ while (toX < destRect.maxX()) {
+ FloatRect toRect(toX, toY, currentTileRect.width(), currentTileRect.height());
+ FloatRect fromRect(toFloatPoint(currentTileRect.location() - oneTileRect.location()), currentTileRect.size());
+ fromRect.scale(1 / scale.width(), 1 / scale.height());
+
+ draw(ctxt, toRect, fromRect, op, BlendModeNormal, ImageOrientationDescription());
+ toX += currentTileRect.width();
+ currentTileRect.shiftXEdgeTo(oneTileRect.x());
}
- toY += oneTileRect.height();
- fromY = 0;
+ toY += currentTileRect.height();
+ currentTileRect.shiftYEdgeTo(oneTileRect.y());
}
return;
}
-#endif
AffineTransform patternTransform = AffineTransform().scaleNonUniform(scale.width(), scale.height());
FloatRect tileRect(FloatPoint(), intrinsicTileSize);
- drawPattern(ctxt, tileRect, patternTransform, oneTileRect.location(), styleColorSpace, op, destRect, blendMode);
-
-#if PLATFORM(IOS)
- startAnimation(false);
-#else
+ drawPattern(ctxt, destRect, tileRect, patternTransform, oneTileRect.location(), spacing, op, blendMode);
startAnimation();
-#endif
}
// FIXME: Merge with the other drawTiled eventually, since we need a combination of both for some things.
-void Image::drawTiled(GraphicsContext* ctxt, const FloatRect& dstRect, const FloatRect& srcRect,
- const FloatSize& tileScaleFactor, TileRule hRule, TileRule vRule, ColorSpace styleColorSpace, CompositeOperator op)
+void Image::drawTiled(GraphicsContext& ctxt, const FloatRect& dstRect, const FloatRect& srcRect,
+ const FloatSize& tileScaleFactor, TileRule hRule, TileRule vRule, CompositeOperator op)
{
- if (mayFillWithSolidColor()) {
- fillWithSolidColor(ctxt, dstRect, solidColor(), styleColorSpace, op);
+ Color color = singlePixelSolidColor();
+ if (color.isValid()) {
+ fillWithSolidColor(ctxt, dstRect, color, op);
return;
}
- // FIXME: We do not support 'round' or 'space' yet. For now just map them to 'repeat'.
- if (hRule == RoundTile || hRule == SpaceTile)
- hRule = RepeatTile;
- if (vRule == RoundTile || vRule == SpaceTile)
- vRule = RepeatTile;
+ FloatSize tileScale = tileScaleFactor;
+ FloatSize spacing;
+
+ // FIXME: These rules follow CSS border-image rules, but they should not be down here in Image.
+ bool centerOnGapHorizonally = false;
+ bool centerOnGapVertically = false;
+ switch (hRule) {
+ case RoundTile: {
+ int numItems = std::max<int>(floorf(dstRect.width() / srcRect.width()), 1);
+ tileScale.setWidth(dstRect.width() / (srcRect.width() * numItems));
+ break;
+ }
+ case SpaceTile: {
+ int numItems = floorf(dstRect.width() / srcRect.width());
+ if (!numItems)
+ return;
+ spacing.setWidth((dstRect.width() - srcRect.width() * numItems) / (numItems + 1));
+ tileScale.setWidth(1);
+ centerOnGapHorizonally = !(numItems & 1);
+ break;
+ }
+ case StretchTile:
+ case RepeatTile:
+ break;
+ }
- AffineTransform patternTransform = AffineTransform().scaleNonUniform(tileScaleFactor.width(), tileScaleFactor.height());
+ switch (vRule) {
+ case RoundTile: {
+ int numItems = std::max<int>(floorf(dstRect.height() / srcRect.height()), 1);
+ tileScale.setHeight(dstRect.height() / (srcRect.height() * numItems));
+ break;
+ }
+ case SpaceTile: {
+ int numItems = floorf(dstRect.height() / srcRect.height());
+ if (!numItems)
+ return;
+ spacing.setHeight((dstRect.height() - srcRect.height() * numItems) / (numItems + 1));
+ tileScale.setHeight(1);
+ centerOnGapVertically = !(numItems & 1);
+ break;
+ }
+ case StretchTile:
+ case RepeatTile:
+ break;
+ }
+
+ AffineTransform patternTransform = AffineTransform().scaleNonUniform(tileScale.width(), tileScale.height());
// We want to construct the phase such that the pattern is centered (when stretch is not
// set for a particular rule).
- float hPhase = tileScaleFactor.width() * srcRect.x();
- float vPhase = tileScaleFactor.height() * srcRect.y();
- float scaledTileWidth = tileScaleFactor.width() * srcRect.width();
- float scaledTileHeight = tileScaleFactor.height() * srcRect.height();
- if (hRule == Image::RepeatTile)
+ float hPhase = tileScale.width() * srcRect.x();
+ float vPhase = tileScale.height() * srcRect.y();
+ float scaledTileWidth = tileScale.width() * srcRect.width();
+ float scaledTileHeight = tileScale.height() * srcRect.height();
+
+ if (centerOnGapHorizonally)
+ hPhase -= spacing.width();
+ else if (hRule == Image::RepeatTile || hRule == Image::SpaceTile)
hPhase -= (dstRect.width() - scaledTileWidth) / 2;
- if (vRule == Image::RepeatTile)
- vPhase -= (dstRect.height() - scaledTileHeight) / 2;
- FloatPoint patternPhase(dstRect.x() - hPhase, dstRect.y() - vPhase);
-
- drawPattern(ctxt, srcRect, patternTransform, patternPhase, styleColorSpace, op, dstRect);
-#if PLATFORM(IOS)
- startAnimation(false);
-#else
+ if (centerOnGapVertically)
+ vPhase -= spacing.height();
+ else if (vRule == Image::RepeatTile || vRule == Image::SpaceTile)
+ vPhase -= (dstRect.height() - scaledTileHeight) / 2;
+
+ FloatPoint patternPhase(dstRect.x() - hPhase, dstRect.y() - vPhase);
+ drawPattern(ctxt, dstRect, srcRect, patternTransform, patternPhase, spacing, op);
startAnimation();
-#endif
}
#if ENABLE(IMAGE_DECODER_DOWN_SAMPLING)
FloatRect Image::adjustSourceRectForDownSampling(const FloatRect& srcRect, const IntSize& scaledSize) const
{
- const IntSize unscaledSize = size();
+ const FloatSize unscaledSize = size();
if (unscaledSize == scaledSize)
return srcRect;
@@ -249,9 +295,45 @@ FloatRect Image::adjustSourceRectForDownSampling(const FloatRect& srcRect, const
void Image::computeIntrinsicDimensions(Length& intrinsicWidth, Length& intrinsicHeight, FloatSize& intrinsicRatio)
{
+#if PLATFORM(IOS)
+ intrinsicRatio = originalSize();
+#else
intrinsicRatio = size();
+#endif
intrinsicWidth = Length(intrinsicRatio.width(), Fixed);
intrinsicHeight = Length(intrinsicRatio.height(), Fixed);
}
+void Image::dump(TextStream& ts) const
+{
+ if (isAnimated())
+ ts.dumpProperty("animated", isAnimated());
+
+ if (isNull())
+ ts.dumpProperty("is-null-image", true);
+
+ ts.dumpProperty("size", size());
+}
+
+TextStream& operator<<(TextStream& ts, const Image& image)
+{
+ TextStream::GroupScope scope(ts);
+
+ if (image.isBitmapImage())
+ ts << "bitmap image";
+ else if (image.isCrossfadeGeneratedImage())
+ ts << "crossfade image";
+ else if (image.isNamedImageGeneratedImage())
+ ts << "named image";
+ else if (image.isGradientImage())
+ ts << "gradient image";
+ else if (image.isSVGImage())
+ ts << "svg image";
+ else if (image.isPDFDocumentImage())
+ ts << "pdf image";
+
+ image.dump(ts);
+ return ts;
+}
+
}
diff --git a/Source/WebCore/platform/graphics/Image.h b/Source/WebCore/platform/graphics/Image.h
index 251f5a439..ab414c031 100644
--- a/Source/WebCore/platform/graphics/Image.h
+++ b/Source/WebCore/platform/graphics/Image.h
@@ -1,6 +1,6 @@
/*
* Copyright (C) 2006 Samuel Weinig (sam.weinig@gmail.com)
- * Copyright (C) 2004, 2005, 2006, 2013 Apple Computer, Inc. All rights reserved.
+ * Copyright (C) 2004, 2005, 2006, 2013 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -11,10 +11,10 @@
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
- * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
@@ -28,19 +28,20 @@
#define Image_h
#include "Color.h"
-#include "ColorSpace.h"
+#include "FloatRect.h"
#include "FloatSize.h"
#include "GraphicsTypes.h"
#include "ImageOrientation.h"
-#include "IntRect.h"
-#include "NativeImagePtr.h"
+#include "NativeImage.h"
+#include <wtf/Optional.h>
#include <wtf/PassRefPtr.h>
#include <wtf/RefCounted.h>
#include <wtf/RefPtr.h>
#include <wtf/RetainPtr.h>
+#include <wtf/TypeCasts.h>
#include <wtf/text/WTFString.h>
-#if PLATFORM(MAC)
+#if USE(APPKIT)
OBJC_CLASS NSImage;
#endif
@@ -58,21 +59,10 @@ typedef struct HBITMAP__ *HBITMAP;
typedef struct _GdkPixbuf GdkPixbuf;
#endif
-#if PLATFORM(EFL)
-#if USE(EO)
-typedef struct _Eo_Opaque Evas;
-typedef struct _Eo_Opaque Evas_Object;
-#else
-typedef struct _Evas Evas;
-typedef struct _Evas_Object Evas_Object;
-#endif
-#endif
-
namespace WebCore {
class AffineTransform;
class FloatPoint;
-class FloatRect;
class FloatSize;
class GraphicsContext;
class SharedBuffer;
@@ -82,43 +72,49 @@ struct Length;
class ImageObserver;
class Image : public RefCounted<Image> {
- friend class GeneratedImage;
- friend class CrossfadeGeneratedImage;
- friend class GradientImage;
friend class GraphicsContext;
-
public:
virtual ~Image();
- static PassRefPtr<Image> create(ImageObserver* = 0);
- static PassRefPtr<Image> loadPlatformResource(const char* name);
- static bool supportsType(const String&);
+ static PassRefPtr<Image> create(ImageObserver* = nullptr);
+ WEBCORE_EXPORT static PassRefPtr<Image> loadPlatformResource(const char* name);
+ WEBCORE_EXPORT static bool supportsType(const String&);
- virtual bool isSVGImage() const { return false; }
virtual bool isBitmapImage() const { return false; }
+ virtual bool isGeneratedImage() const { return false; }
+ virtual bool isCrossfadeGeneratedImage() const { return false; }
+ virtual bool isNamedImageGeneratedImage() const { return false; }
+ virtual bool isGradientImage() const { return false; }
+ virtual bool isSVGImage() const { return false; }
virtual bool isPDFDocumentImage() const { return false; }
- virtual bool currentFrameKnownToBeOpaque() = 0;
+
+ virtual bool currentFrameKnownToBeOpaque() const = 0;
+ virtual bool isAnimated() const { return false; }
// Derived classes should override this if they can assure that
// the image contains only resources from its own security origin.
virtual bool hasSingleSecurityOrigin() const { return false; }
- static Image* nullImage();
+ WEBCORE_EXPORT static Image* nullImage();
bool isNull() const { return size().isEmpty(); }
- virtual void setContainerSize(const IntSize&) { }
+ virtual void setContainerSize(const FloatSize&) { }
virtual bool usesContainerSize() const { return false; }
virtual bool hasRelativeWidth() const { return false; }
virtual bool hasRelativeHeight() const { return false; }
virtual void computeIntrinsicDimensions(Length& intrinsicWidth, Length& intrinsicHeight, FloatSize& intrinsicRatio);
- virtual IntSize size() const = 0;
- IntRect rect() const { return IntRect(IntPoint(), size()); }
- int width() const { return size().width(); }
- int height() const { return size().height(); }
- virtual bool getHotSpot(IntPoint&) const { return false; }
+ virtual FloatSize size() const = 0;
+ FloatRect rect() const { return FloatRect(FloatPoint(), size()); }
+ float width() const { return size().width(); }
+ float height() const { return size().height(); }
+ virtual std::optional<IntPoint> hotSpot() const { return std::nullopt; }
+
+#if PLATFORM(IOS)
+ virtual FloatSize originalSize() const { return size(); }
+#endif
- bool setData(PassRefPtr<SharedBuffer> data, bool allDataReceived);
+ WEBCORE_EXPORT bool setData(RefPtr<SharedBuffer>&& data, bool allDataReceived);
virtual bool dataChanged(bool /*allDataReceived*/) { return false; }
virtual String filenameExtension() const { return String(); } // null string if unknown
@@ -126,12 +122,14 @@ public:
virtual void destroyDecodedData(bool destroyAll = true) = 0;
SharedBuffer* data() { return m_encodedImageData.get(); }
+ const SharedBuffer* data() const { return m_encodedImageData.get(); }
// Animation begins whenever someone draws the image, so startAnimation() is not normally called.
// It will automatically pause once all observers no longer want to render the image anywhere.
- virtual void startAnimation(bool /*catchUpIfNecessary*/ = true) { }
+ virtual void startAnimation() { }
virtual void stopAnimation() {}
virtual void resetAnimation() {}
+ virtual void newFrameNativeImageAvailableAtIndex(size_t) { }
// Typically the CachedImage that owns us.
ImageObserver* imageObserver() const { return m_imageObserver; }
@@ -139,24 +137,21 @@ public:
enum TileRule { StretchTile, RoundTile, SpaceTile, RepeatTile };
- virtual PassNativeImagePtr nativeImageForCurrentFrame() { return 0; }
- virtual ImageOrientation orientationForCurrentFrame() { return ImageOrientation(); }
+ virtual NativeImagePtr nativeImage(const GraphicsContext* = nullptr) { return nullptr; }
+ virtual NativeImagePtr nativeImageOfSize(const IntSize&, const GraphicsContext* = nullptr) { return nullptr; }
+ virtual NativeImagePtr nativeImageForCurrentFrame(const GraphicsContext* = nullptr) { return nullptr; }
+ virtual ImageOrientation orientationForCurrentFrame() const { return ImageOrientation(); }
+ virtual Vector<NativeImagePtr> framesNativeImages() { return { }; }
// Accessors for native image formats.
#if USE(APPKIT)
- virtual NSImage* getNSImage() { return 0; }
+ virtual NSImage *nsImage() { return nullptr; }
+ virtual RetainPtr<NSImage> snapshotNSImage() { return nullptr; }
#endif
-#if PLATFORM(MAC)
- virtual CFDataRef getTIFFRepresentation() { return 0; }
-#endif
-
-#if USE(CG)
- virtual CGImageRef getCGImageRef() { return 0; }
- virtual CGImageRef getFirstCGImageRefOfSize(const IntSize&) { return 0; }
- virtual RetainPtr<CFArrayRef> getCGImageArray() { return 0; }
- static RetainPtr<CGImageRef> imageWithColorSpace(CGImageRef originalImage, ColorSpace);
+#if PLATFORM(COCOA)
+ virtual CFDataRef tiffRepresentation() { return nullptr; }
#endif
#if PLATFORM(WIN)
@@ -165,16 +160,11 @@ public:
#endif
#if PLATFORM(GTK)
- virtual GdkPixbuf* getGdkPixbuf() { return 0; }
- static PassRefPtr<Image> loadPlatformThemeIcon(const char* name, int size);
-#endif
-
-#if PLATFORM(EFL)
- virtual Evas_Object* getEvasObject(Evas*) { return 0; }
+ virtual GdkPixbuf* getGdkPixbuf() { return nullptr; }
#endif
- virtual void drawPattern(GraphicsContext*, const FloatRect& srcRect, const AffineTransform& patternTransform,
- const FloatPoint& phase, ColorSpace styleColorSpace, CompositeOperator, const FloatRect& destRect, BlendMode = BlendModeNormal);
+ virtual void drawPattern(GraphicsContext&, const FloatRect& destRect, const FloatRect& srcRect, const AffineTransform& patternTransform,
+ const FloatPoint& phase, const FloatSize& spacing, CompositeOperator, BlendMode = BlendModeNormal);
#if ENABLE(IMAGE_DECODER_DOWN_SAMPLING)
FloatRect adjustSourceRectForDownSampling(const FloatRect& srcRect, const IntSize& scaledSize) const;
@@ -184,38 +174,35 @@ public:
virtual bool notSolidColor() { return true; }
#endif
- FloatSize spaceSize() const { return m_space; }
- void setSpaceSize(const FloatSize& space)
- {
- m_space = space;
- }
+ virtual void dump(TextStream&) const;
+
protected:
- Image(ImageObserver* = 0);
+ Image(ImageObserver* = nullptr);
- static void fillWithSolidColor(GraphicsContext*, const FloatRect& dstRect, const Color&, ColorSpace styleColorSpace, CompositeOperator);
+ static void fillWithSolidColor(GraphicsContext&, const FloatRect& dstRect, const Color&, CompositeOperator);
- // The ColorSpace parameter will only be used for untagged images.
#if PLATFORM(WIN)
- virtual void drawFrameMatchingSourceSize(GraphicsContext*, const FloatRect& dstRect, const IntSize& srcSize, ColorSpace styleColorSpace, CompositeOperator) { }
+ virtual void drawFrameMatchingSourceSize(GraphicsContext&, const FloatRect& dstRect, const IntSize& srcSize, CompositeOperator) { }
#endif
- virtual void draw(GraphicsContext*, const FloatRect& dstRect, const FloatRect& srcRect, ColorSpace styleColorSpace, CompositeOperator, BlendMode, ImageOrientationDescription);
- void drawTiled(GraphicsContext*, const FloatRect& dstRect, const FloatPoint& srcPoint, const FloatSize& tileSize, ColorSpace styleColorSpace,
- CompositeOperator , BlendMode);
- void drawTiled(GraphicsContext*, const FloatRect& dstRect, const FloatRect& srcRect, const FloatSize& tileScaleFactor, TileRule hRule, TileRule vRule, ColorSpace styleColorSpace, CompositeOperator);
+ virtual void draw(GraphicsContext&, const FloatRect& dstRect, const FloatRect& srcRect, CompositeOperator, BlendMode, ImageOrientationDescription) = 0;
+ void drawTiled(GraphicsContext&, const FloatRect& dstRect, const FloatPoint& srcPoint, const FloatSize& tileSize, const FloatSize& spacing, CompositeOperator, BlendMode);
+ void drawTiled(GraphicsContext&, const FloatRect& dstRect, const FloatRect& srcRect, const FloatSize& tileScaleFactor, TileRule hRule, TileRule vRule, CompositeOperator);
// Supporting tiled drawing
- virtual bool mayFillWithSolidColor() { return false; }
- virtual Color solidColor() const { return Color(); }
-
+ virtual Color singlePixelSolidColor() const { return Color(); }
+
private:
RefPtr<SharedBuffer> m_encodedImageData;
ImageObserver* m_imageObserver;
- FloatSize m_space;
};
-#define IMAGE_TYPE_CASTS(ToClassName) \
- TYPE_CASTS_BASE(ToClassName, Image, image, image->is##ToClassName(), image.is##ToClassName())
+TextStream& operator<<(TextStream&, const Image&);
-}
+} // namespace WebCore
-#endif
+#define SPECIALIZE_TYPE_TRAITS_IMAGE(ToClassName) \
+SPECIALIZE_TYPE_TRAITS_BEGIN(WebCore::ToClassName) \
+ static bool isType(const WebCore::Image& image) { return image.is##ToClassName(); } \
+SPECIALIZE_TYPE_TRAITS_END()
+
+#endif // Image_h
diff --git a/Source/WebCore/platform/graphics/ImageBackingStore.h b/Source/WebCore/platform/graphics/ImageBackingStore.h
new file mode 100644
index 000000000..4c59f6842
--- /dev/null
+++ b/Source/WebCore/platform/graphics/ImageBackingStore.h
@@ -0,0 +1,224 @@
+/*
+ * Copyright (C) 2016 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include "Color.h"
+#include "IntRect.h"
+#include "IntSize.h"
+#include "NativeImage.h"
+#include "SharedBuffer.h"
+
+namespace WebCore {
+
+class ImageBackingStore {
+ WTF_MAKE_FAST_ALLOCATED;
+public:
+ static std::unique_ptr<ImageBackingStore> create(const IntSize& size, bool premultiplyAlpha = true)
+ {
+ return std::unique_ptr<ImageBackingStore>(new ImageBackingStore(size, premultiplyAlpha));
+ }
+
+ static std::unique_ptr<ImageBackingStore> create(const ImageBackingStore& other)
+ {
+ return std::unique_ptr<ImageBackingStore>(new ImageBackingStore(other));
+ }
+
+ NativeImagePtr image() const;
+
+ bool setSize(const IntSize& size)
+ {
+ if (size.isEmpty())
+ return false;
+
+ Vector<char> buffer;
+ size_t bufferSize = size.area().unsafeGet() * sizeof(RGBA32);
+
+ if (!buffer.tryReserveCapacity(bufferSize))
+ return false;
+
+ buffer.resize(bufferSize);
+ m_pixels = SharedBuffer::adoptVector(buffer);
+ m_pixelsPtr = reinterpret_cast<RGBA32*>(const_cast<char*>(m_pixels->data()));
+ m_size = size;
+ m_frameRect = IntRect(IntPoint(), m_size);
+ clear();
+ return true;
+ }
+
+ void setFrameRect(const IntRect& frameRect)
+ {
+ ASSERT(!m_size.isEmpty());
+ ASSERT(inBounds(frameRect));
+ m_frameRect = frameRect;
+ }
+
+ const IntSize& size() const { return m_size; }
+ const IntRect& frameRect() const { return m_frameRect; }
+
+ void clear()
+ {
+ memset(m_pixelsPtr, 0, (m_size.area() * sizeof(RGBA32)).unsafeGet());
+ }
+
+ void clearRect(const IntRect& rect)
+ {
+ if (rect.isEmpty() || !inBounds(rect))
+ return;
+
+ size_t rowBytes = rect.width() * sizeof(RGBA32);
+ RGBA32* start = pixelAt(rect.x(), rect.y());
+ for (int i = 0; i < rect.height(); ++i) {
+ memset(start, 0, rowBytes);
+ start += m_size.width();
+ }
+ }
+
+ void fillRect(const IntRect &rect, unsigned r, unsigned g, unsigned b, unsigned a)
+ {
+ if (rect.isEmpty() || !inBounds(rect))
+ return;
+
+ RGBA32* start = pixelAt(rect.x(), rect.y());
+ RGBA32 pixelValue = this->pixelValue(r, g, b, a);
+ for (int i = 0; i < rect.height(); ++i) {
+ for (int j = 0; j < rect.width(); ++j)
+ start[j] = pixelValue;
+ start += m_size.width();
+ }
+ }
+
+ void repeatFirstRow(const IntRect& rect)
+ {
+ if (rect.isEmpty() || !inBounds(rect))
+ return;
+
+ size_t rowBytes = rect.width() * sizeof(RGBA32);
+ RGBA32* src = pixelAt(rect.x(), rect.y());
+ RGBA32* dest = src + m_size.width();
+ for (int i = 1; i < rect.height(); ++i) {
+ memcpy(dest, src, rowBytes);
+ dest += m_size.width();
+ }
+ }
+
+ RGBA32* pixelAt(int x, int y) const
+ {
+ ASSERT(inBounds(IntPoint(x, y)));
+ return m_pixelsPtr + y * m_size.width() + x;
+ }
+
+ void setPixel(RGBA32* dest, unsigned r, unsigned g, unsigned b, unsigned a)
+ {
+ ASSERT(dest);
+ *dest = pixelValue(r, g, b, a);
+ }
+
+ void setPixel(int x, int y, unsigned r, unsigned g, unsigned b, unsigned a)
+ {
+ setPixel(pixelAt(x, y), r, g, b, a);
+ }
+
+#if ENABLE(APNG)
+ void blendPixel(RGBA32* dest, unsigned r, unsigned g, unsigned b, unsigned a)
+ {
+ if (!a)
+ return;
+
+ if (a >= 255 || !alphaChannel(*dest)) {
+ setPixel(dest, r, g, b, a);
+ return;
+ }
+
+ if (!m_premultiplyAlpha)
+ *dest = makePremultipliedRGBA(redChannel(*dest), greenChannel(*dest), blueChannel(*dest), alphaChannel(*dest), false);
+
+ unsigned d = 255 - a;
+
+ r = fastDivideBy255(r * a + redChannel(*dest) * d);
+ g = fastDivideBy255(g * a + greenChannel(*dest) * d);
+ b = fastDivideBy255(b * a + blueChannel(*dest) * d);
+ a += fastDivideBy255(d * alphaChannel(*dest));
+
+ if (m_premultiplyAlpha)
+ *dest = makeRGBA(r, g, b, a);
+ else
+ *dest = makeUnPremultipliedRGBA(r, g, b, a);
+ }
+#endif
+
+ static bool isOverSize(const IntSize& size)
+ {
+ static unsigned long long MaxPixels = ((1 << 29) - 1);
+ unsigned long long pixels = static_cast<unsigned long long>(size.width()) * static_cast<unsigned long long>(size.height());
+ return pixels > MaxPixels;
+ }
+
+private:
+ ImageBackingStore(const IntSize& size, bool premultiplyAlpha = true)
+ : m_premultiplyAlpha(premultiplyAlpha)
+ {
+ ASSERT(!size.isEmpty() && !isOverSize(size));
+ setSize(size);
+ }
+
+ ImageBackingStore(const ImageBackingStore& other)
+ : m_size(other.m_size)
+ , m_premultiplyAlpha(other.m_premultiplyAlpha)
+ {
+ ASSERT(!m_size.isEmpty() && !isOverSize(m_size));
+ m_pixels = other.m_pixels->copy();
+ m_pixelsPtr = reinterpret_cast<RGBA32*>(const_cast<char*>(m_pixels->data()));
+ }
+
+ bool inBounds(const IntPoint& point) const
+ {
+ return IntRect(IntPoint(), m_size).contains(point);
+ }
+
+ bool inBounds(const IntRect& rect) const
+ {
+ return IntRect(IntPoint(), m_size).contains(rect);
+ }
+
+ RGBA32 pixelValue(unsigned r, unsigned g, unsigned b, unsigned a) const
+ {
+ if (m_premultiplyAlpha && !a)
+ return 0;
+
+ if (m_premultiplyAlpha && a < 255)
+ return makePremultipliedRGBA(r, g, b, a, false);
+
+ return makeRGBA(r, g, b, a);
+ }
+
+ RefPtr<SharedBuffer> m_pixels;
+ RGBA32* m_pixelsPtr { nullptr };
+ IntSize m_size;
+ IntRect m_frameRect; // This will always just be the entire buffer except for GIF and PNG frames
+ bool m_premultiplyAlpha { true };
+};
+
+}
diff --git a/Source/WebCore/platform/graphics/ImageBuffer.cpp b/Source/WebCore/platform/graphics/ImageBuffer.cpp
index 833534c6c..eae4c4eda 100644
--- a/Source/WebCore/platform/graphics/ImageBuffer.cpp
+++ b/Source/WebCore/platform/graphics/ImageBuffer.cpp
@@ -1,6 +1,7 @@
/*
* Copyright (C) 2009 Dirk Schulze <krit@webkit.org>
* Copyright (C) Research In Motion Limited 2011. All rights reserved.
+ * Copyright (C) 2016 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -11,10 +12,10 @@
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
- * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
@@ -27,16 +28,90 @@
#include "config.h"
#include "ImageBuffer.h"
+#include "GraphicsContext.h"
#include "IntRect.h"
#include <wtf/MathExtras.h>
+#include <wtf/NeverDestroyed.h>
namespace WebCore {
-#if !USE(CG)
+static const float MaxClampedLength = 4096;
+static const float MaxClampedArea = MaxClampedLength * MaxClampedLength;
+
+std::unique_ptr<ImageBuffer> ImageBuffer::create(const FloatSize& size, RenderingMode renderingMode, float resolutionScale, ColorSpace colorSpace)
+{
+ bool success = false;
+ std::unique_ptr<ImageBuffer> buffer(new ImageBuffer(size, resolutionScale, colorSpace, renderingMode, success));
+ if (!success)
+ return nullptr;
+ return buffer;
+}
+
+#if USE(DIRECT2D)
+std::unique_ptr<ImageBuffer> ImageBuffer::create(const FloatSize& size, RenderingMode renderingMode, const GraphicsContext* targetContext, float resolutionScale, ColorSpace colorSpace)
+{
+ bool success = false;
+ std::unique_ptr<ImageBuffer> buffer(new ImageBuffer(size, resolutionScale, colorSpace, renderingMode, targetContext, success));
+ if (!success)
+ return nullptr;
+ return buffer;
+}
+#endif
+
+bool ImageBuffer::sizeNeedsClamping(const FloatSize& size)
+{
+ if (size.isEmpty())
+ return false;
+
+ return floorf(size.height()) * floorf(size.width()) > MaxClampedArea;
+}
+
+bool ImageBuffer::sizeNeedsClamping(const FloatSize& size, FloatSize& scale)
+{
+ FloatSize scaledSize(size);
+ scaledSize.scale(scale.width(), scale.height());
+
+ if (!sizeNeedsClamping(scaledSize))
+ return false;
+
+ // The area of scaled size is bigger than the upper limit, adjust the scale to fit.
+ scale.scale(sqrtf(MaxClampedArea / (scaledSize.width() * scaledSize.height())));
+ ASSERT(!sizeNeedsClamping(size, scale));
+ return true;
+}
+
+FloatSize ImageBuffer::clampedSize(const FloatSize& size)
+{
+ return size.shrunkTo(FloatSize(MaxClampedLength, MaxClampedLength));
+}
+
+FloatSize ImageBuffer::clampedSize(const FloatSize& size, FloatSize& scale)
+{
+ if (size.isEmpty())
+ return size;
+
+ FloatSize clampedSize = ImageBuffer::clampedSize(size);
+ scale = FloatSize(clampedSize.width() / size.width(), clampedSize.height() / size.height());
+ ASSERT(!sizeNeedsClamping(clampedSize));
+ ASSERT(!sizeNeedsClamping(size, scale));
+ return clampedSize;
+}
+
+FloatRect ImageBuffer::clampedRect(const FloatRect& rect)
+{
+ return FloatRect(rect.location(), clampedSize(rect.size()));
+}
+
+#if !(USE(CG) || USE(DIRECT2D))
+FloatSize ImageBuffer::sizeForDestinationSize(FloatSize size) const
+{
+ return size;
+}
+
void ImageBuffer::transformColorSpace(ColorSpace srcColorSpace, ColorSpace dstColorSpace)
{
- DEFINE_STATIC_LOCAL(Vector<int>, deviceRgbLUT, ());
- DEFINE_STATIC_LOCAL(Vector<int>, linearRgbLUT, ());
+ static NeverDestroyed<Vector<int>> deviceRgbLUT;
+ static NeverDestroyed<Vector<int>> linearRgbLUT;
if (srcColorSpace == dstColorSpace)
return;
@@ -47,27 +122,27 @@ void ImageBuffer::transformColorSpace(ColorSpace srcColorSpace, ColorSpace dstCo
return;
if (dstColorSpace == ColorSpaceLinearRGB) {
- if (linearRgbLUT.isEmpty()) {
+ if (linearRgbLUT.get().isEmpty()) {
for (unsigned i = 0; i < 256; i++) {
float color = i / 255.0f;
color = (color <= 0.04045f ? color / 12.92f : pow((color + 0.055f) / 1.055f, 2.4f));
color = std::max(0.0f, color);
color = std::min(1.0f, color);
- linearRgbLUT.append(static_cast<int>(round(color * 255)));
+ linearRgbLUT.get().append(static_cast<int>(round(color * 255)));
}
}
- platformTransformColorSpace(linearRgbLUT);
+ platformTransformColorSpace(linearRgbLUT.get());
} else if (dstColorSpace == ColorSpaceDeviceRGB) {
- if (deviceRgbLUT.isEmpty()) {
+ if (deviceRgbLUT.get().isEmpty()) {
for (unsigned i = 0; i < 256; i++) {
float color = i / 255.0f;
color = (powf(color, 1.0f / 2.4f) * 1.055f) - 0.055f;
color = std::max(0.0f, color);
color = std::min(1.0f, color);
- deviceRgbLUT.append(static_cast<int>(round(color * 255)));
+ deviceRgbLUT.get().append(static_cast<int>(round(color * 255)));
}
}
- platformTransformColorSpace(deviceRgbLUT);
+ platformTransformColorSpace(deviceRgbLUT.get());
}
}
#endif // USE(CG)
@@ -98,21 +173,61 @@ void ImageBuffer::convertToLuminanceMask()
genericConvertToLuminanceMask();
}
-#if USE(ACCELERATED_COMPOSITING) && !USE(CAIRO)
+#if !USE(CAIRO)
PlatformLayer* ImageBuffer::platformLayer() const
{
return 0;
}
-#endif
-bool ImageBuffer::copyToPlatformTexture(GraphicsContext3D&, Platform3DObject, GC3Denum, bool, bool)
+bool ImageBuffer::copyToPlatformTexture(GraphicsContext3D&, GC3Denum, Platform3DObject, GC3Denum, bool, bool)
{
return false;
}
+#endif
+
+std::unique_ptr<ImageBuffer> ImageBuffer::createCompatibleBuffer(const FloatSize& size, ColorSpace colorSpace, const GraphicsContext& context)
+{
+ if (size.isEmpty())
+ return nullptr;
+
+ IntSize scaledSize = ImageBuffer::compatibleBufferSize(size, context);
+
+ auto buffer = ImageBuffer::createCompatibleBuffer(scaledSize, 1, colorSpace, context);
+ if (!buffer)
+ return nullptr;
+
+ // Set up a corresponding scale factor on the graphics context.
+ buffer->context().scale(FloatSize(scaledSize.width() / size.width(), scaledSize.height() / size.height()));
+ return buffer;
+}
+
+std::unique_ptr<ImageBuffer> ImageBuffer::createCompatibleBuffer(const FloatSize& size, float resolutionScale, ColorSpace colorSpace, const GraphicsContext& context)
+{
+ return create(size, context.renderingMode(), resolutionScale, colorSpace);
+}
-std::unique_ptr<ImageBuffer> ImageBuffer::createCompatibleBuffer(const IntSize& size, float resolutionScale, ColorSpace colorSpace, const GraphicsContext* context, bool)
+IntSize ImageBuffer::compatibleBufferSize(const FloatSize& size, const GraphicsContext& context)
{
- return create(size, resolutionScale, colorSpace, context->isAcceleratedContext() ? Accelerated : Unaccelerated);
+ // Enlarge the buffer size if the context's transform is scaling it so we need a higher
+ // resolution than one pixel per unit.
+ return expandedIntSize(size * context.scaleFactor());
}
+bool ImageBuffer::isCompatibleWithContext(const GraphicsContext& context) const
+{
+ return areEssentiallyEqual(context.scaleFactor(), this->context().scaleFactor());
+}
+
+#if !USE(IOSURFACE_CANVAS_BACKING_STORE)
+size_t ImageBuffer::memoryCost() const
+{
+ return 4 * internalSize().width() * internalSize().height();
+}
+
+size_t ImageBuffer::externalMemoryCost() const
+{
+ return 0;
+}
+#endif
+
}
diff --git a/Source/WebCore/platform/graphics/ImageBuffer.h b/Source/WebCore/platform/graphics/ImageBuffer.h
index b62e5069f..397beee5a 100644
--- a/Source/WebCore/platform/graphics/ImageBuffer.h
+++ b/Source/WebCore/platform/graphics/ImageBuffer.h
@@ -12,10 +12,10 @@
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
- * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
@@ -30,143 +30,157 @@
#include "AffineTransform.h"
#include "ColorSpace.h"
-#include "FloatRect.h"
-#include "GraphicsContext.h"
-#if USE(ACCELERATED_COMPOSITING)
-#include "PlatformLayer.h"
-#endif
#include "GraphicsTypes.h"
#include "GraphicsTypes3D.h"
#include "IntSize.h"
#include "ImageBufferData.h"
+#include "PlatformLayer.h"
+#include <memory>
#include <runtime/Uint8ClampedArray.h>
#include <wtf/Forward.h>
-#include <wtf/OwnPtr.h>
-#include <wtf/PassRefPtr.h>
+#include <wtf/RefPtr.h>
#include <wtf/Vector.h>
namespace WebCore {
- class Image;
- class ImageData;
- class IntPoint;
- class IntRect;
- class GraphicsContext3D;
-
- enum Multiply {
- Premultiplied,
- Unmultiplied
- };
-
- enum RenderingMode {
- Unaccelerated,
- UnacceleratedNonPlatformBuffer, // Use plain memory allocation rather than platform API to allocate backing store.
- Accelerated
- };
+class FloatRect;
+class GraphicsContext;
+class GraphicsContext3D;
+class Image;
+class ImageData;
+class IntPoint;
+class IntRect;
+
+enum Multiply {
+ Premultiplied,
+ Unmultiplied
+};
+
+enum BackingStoreCopy {
+ CopyBackingStore, // Guarantee subsequent draws don't affect the copy.
+ DontCopyBackingStore // Subsequent draws may affect the copy.
+};
+
+enum ScaleBehavior {
+ Scaled,
+ Unscaled
+};
+
+class ImageBuffer {
+ WTF_MAKE_NONCOPYABLE(ImageBuffer); WTF_MAKE_FAST_ALLOCATED;
+ friend class IOSurface;
+public:
+ // Will return a null pointer on allocation failure.
+ WEBCORE_EXPORT static std::unique_ptr<ImageBuffer> create(const FloatSize&, RenderingMode, float resolutionScale = 1, ColorSpace = ColorSpaceSRGB);
+#if USE(DIRECT2D)
+ WEBCORE_EXPORT static std::unique_ptr<ImageBuffer> create(const FloatSize&, RenderingMode, const GraphicsContext*, float resolutionScale = 1, ColorSpace = ColorSpaceSRGB);
+#endif
+
+ // Create an image buffer compatible with the context, with suitable resolution for drawing into the buffer and then into this context.
+ static std::unique_ptr<ImageBuffer> createCompatibleBuffer(const FloatSize&, const GraphicsContext&);
+ static std::unique_ptr<ImageBuffer> createCompatibleBuffer(const FloatSize&, ColorSpace, const GraphicsContext&);
+ static std::unique_ptr<ImageBuffer> createCompatibleBuffer(const FloatSize&, float resolutionScale, ColorSpace, const GraphicsContext&);
+
+ static IntSize compatibleBufferSize(const FloatSize&, const GraphicsContext&);
+ bool isCompatibleWithContext(const GraphicsContext&) const;
+
+ WEBCORE_EXPORT ~ImageBuffer();
+
+ // The actual resolution of the backing store
+ const IntSize& internalSize() const { return m_size; }
+ const IntSize& logicalSize() const { return m_logicalSize; }
+
+ FloatSize sizeForDestinationSize(FloatSize) const;
+
+ float resolutionScale() const { return m_resolutionScale; }
+
+ WEBCORE_EXPORT GraphicsContext& context() const;
+
+ WEBCORE_EXPORT RefPtr<Image> copyImage(BackingStoreCopy = CopyBackingStore, ScaleBehavior = Scaled) const;
+ WEBCORE_EXPORT static RefPtr<Image> sinkIntoImage(std::unique_ptr<ImageBuffer>, ScaleBehavior = Scaled);
+ // Give hints on the faster copyImage Mode, return DontCopyBackingStore if it supports the DontCopyBackingStore behavior
+ // or return CopyBackingStore if it doesn't.
+ static BackingStoreCopy fastCopyImageMode();
+
+ enum CoordinateSystem { LogicalCoordinateSystem, BackingStoreCoordinateSystem };
+
+ RefPtr<Uint8ClampedArray> getUnmultipliedImageData(const IntRect&, CoordinateSystem = LogicalCoordinateSystem) const;
+ RefPtr<Uint8ClampedArray> getPremultipliedImageData(const IntRect&, CoordinateSystem = LogicalCoordinateSystem) const;
+
+ void putByteArray(Multiply multiplied, Uint8ClampedArray*, const IntSize& sourceSize, const IntRect& sourceRect, const IntPoint& destPoint, CoordinateSystem = LogicalCoordinateSystem);
+
+ void convertToLuminanceMask();
- enum BackingStoreCopy {
- CopyBackingStore, // Guarantee subsequent draws don't affect the copy.
- DontCopyBackingStore // Subsequent draws may affect the copy.
- };
-
- enum ScaleBehavior {
- Scaled,
- Unscaled
- };
-
- class ImageBuffer {
- WTF_MAKE_NONCOPYABLE(ImageBuffer); WTF_MAKE_FAST_ALLOCATED;
- public:
- // Will return a null pointer on allocation failure.
- static std::unique_ptr<ImageBuffer> create(const IntSize& size, float resolutionScale = 1, ColorSpace colorSpace = ColorSpaceDeviceRGB, RenderingMode renderingMode = Unaccelerated)
- {
- bool success = false;
- std::unique_ptr<ImageBuffer> buffer(new ImageBuffer(size, resolutionScale, colorSpace, renderingMode, success));
- if (!success)
- return nullptr;
- return buffer;
- }
-
- static std::unique_ptr<ImageBuffer> createCompatibleBuffer(const IntSize&, float resolutionScale, ColorSpace, const GraphicsContext*, bool hasAlpha);
-
- ~ImageBuffer();
-
- // The actual resolution of the backing store
- const IntSize& internalSize() const { return m_size; }
- const IntSize& logicalSize() const { return m_logicalSize; }
-
- GraphicsContext* context() const;
-
- PassRefPtr<Image> copyImage(BackingStoreCopy = CopyBackingStore, ScaleBehavior = Scaled) const;
- // Give hints on the faster copyImage Mode, return DontCopyBackingStore if it supports the DontCopyBackingStore behavior
- // or return CopyBackingStore if it doesn't.
- static BackingStoreCopy fastCopyImageMode();
-
- enum CoordinateSystem { LogicalCoordinateSystem, BackingStoreCoordinateSystem };
-
- PassRefPtr<Uint8ClampedArray> getUnmultipliedImageData(const IntRect&, CoordinateSystem = LogicalCoordinateSystem) const;
- PassRefPtr<Uint8ClampedArray> getPremultipliedImageData(const IntRect&, CoordinateSystem = LogicalCoordinateSystem) const;
-
- void putByteArray(Multiply multiplied, Uint8ClampedArray*, const IntSize& sourceSize, const IntRect& sourceRect, const IntPoint& destPoint, CoordinateSystem = LogicalCoordinateSystem);
-
- void convertToLuminanceMask();
-
- String toDataURL(const String& mimeType, const double* quality = 0, CoordinateSystem = LogicalCoordinateSystem) const;
+ String toDataURL(const String& mimeType, std::optional<double> quality = std::nullopt, CoordinateSystem = LogicalCoordinateSystem) const;
#if !USE(CG)
- AffineTransform baseTransform() const { return AffineTransform(); }
- void transformColorSpace(ColorSpace srcColorSpace, ColorSpace dstColorSpace);
- void platformTransformColorSpace(const Vector<int>&);
+ AffineTransform baseTransform() const { return AffineTransform(); }
+ void transformColorSpace(ColorSpace srcColorSpace, ColorSpace dstColorSpace);
+ void platformTransformColorSpace(const Vector<int>&);
#else
- AffineTransform baseTransform() const { return AffineTransform(1, 0, 0, -1, 0, m_data.m_backingStoreSize.height()); }
+ AffineTransform baseTransform() const { return AffineTransform(1, 0, 0, -1, 0, m_data.backingStoreSize.height()); }
#endif
-#if USE(ACCELERATED_COMPOSITING)
- PlatformLayer* platformLayer() const;
+ PlatformLayer* platformLayer() const;
+
+#if USE(CAIRO)
+ NativeImagePtr nativeImage() const;
#endif
- // FIXME: current implementations of this method have the restriction that they only work
- // with textures that are RGB or RGBA format, and UNSIGNED_BYTE type.
- bool copyToPlatformTexture(GraphicsContext3D&, Platform3DObject, GC3Denum, bool, bool);
+ size_t memoryCost() const;
+ size_t externalMemoryCost() const;
+
+ // FIXME: current implementations of this method have the restriction that they only work
+ // with textures that are RGB or RGBA format, and UNSIGNED_BYTE type.
+ bool copyToPlatformTexture(GraphicsContext3D&, GC3Denum, Platform3DObject, GC3Denum, bool, bool);
- FloatSize spaceSize() const { return m_space; }
- void setSpaceSize(const FloatSize& space)
- {
- m_space = space;
- }
+ // These functions are used when clamping the ImageBuffer which is created for filter, masker or clipper.
+ static bool sizeNeedsClamping(const FloatSize&);
+ static bool sizeNeedsClamping(const FloatSize&, FloatSize& scale);
+ static FloatSize clampedSize(const FloatSize&);
+ static FloatSize clampedSize(const FloatSize&, FloatSize& scale);
+ static FloatRect clampedRect(const FloatRect&);
- private:
+private:
#if USE(CG)
- // The returned image might be larger than the internalSize(). If you want the smaller
- // image, crop the result.
- RetainPtr<CGImageRef> copyNativeImage(BackingStoreCopy = CopyBackingStore) const;
- void flushContext() const;
+ // The returned image might be larger than the internalSize(). If you want the smaller
+ // image, crop the result.
+ RetainPtr<CGImageRef> copyNativeImage(BackingStoreCopy = CopyBackingStore) const;
+ static RetainPtr<CGImageRef> sinkIntoNativeImage(std::unique_ptr<ImageBuffer>);
+ void flushContext() const;
+#elif USE(DIRECT2D)
+ void flushContext() const;
#endif
- void clip(GraphicsContext*, const FloatRect&) const;
+
+ void draw(GraphicsContext&, const FloatRect& destRect, const FloatRect& srcRect = FloatRect(0, 0, -1, -1), CompositeOperator = CompositeSourceOver, BlendMode = BlendModeNormal);
+ void drawPattern(GraphicsContext&, const FloatRect& destRect, const FloatRect& srcRect, const AffineTransform& patternTransform, const FloatPoint& phase, const FloatSize& spacing, CompositeOperator, BlendMode = BlendModeNormal);
- void draw(GraphicsContext*, ColorSpace, const FloatRect& destRect, const FloatRect& srcRect = FloatRect(0, 0, -1, -1), CompositeOperator = CompositeSourceOver, BlendMode = BlendModeNormal, bool useLowQualityScale = false);
- void drawPattern(GraphicsContext*, const FloatRect& srcRect, const AffineTransform& patternTransform, const FloatPoint& phase, ColorSpace styleColorSpace, CompositeOperator, const FloatRect& destRect, BlendMode = BlendModeNormal);
+ static void drawConsuming(std::unique_ptr<ImageBuffer>, GraphicsContext&, const FloatRect& destRect, const FloatRect& srcRect = FloatRect(0, 0, -1, -1), CompositeOperator = CompositeSourceOver, BlendMode = BlendModeNormal);
- inline void genericConvertToLuminanceMask();
+ inline void genericConvertToLuminanceMask();
- friend class GraphicsContext;
- friend class GeneratedImage;
- friend class CrossfadeGeneratedImage;
- friend class GradientImage;
+ friend class GraphicsContext;
+ friend class GeneratedImage;
+ friend class CrossfadeGeneratedImage;
+ friend class NamedImageGeneratedImage;
+ friend class GradientImage;
- private:
- ImageBufferData m_data;
- IntSize m_size;
- IntSize m_logicalSize;
- float m_resolutionScale;
- OwnPtr<GraphicsContext> m_context;
- FloatSize m_space;
+private:
+ ImageBufferData m_data;
+ IntSize m_size;
+ IntSize m_logicalSize;
+ float m_resolutionScale;
- // This constructor will place its success into the given out-variable
- // so that create() knows when it should return failure.
- ImageBuffer(const IntSize&, float resolutionScale, ColorSpace, RenderingMode, bool& success);
- };
+ // This constructor will place its success into the given out-variable
+ // so that create() knows when it should return failure.
+ WEBCORE_EXPORT ImageBuffer(const FloatSize&, float resolutionScale, ColorSpace, RenderingMode, bool& success);
+#if USE(CG)
+ ImageBuffer(const FloatSize&, float resolutionScale, CGColorSpaceRef, RenderingMode, bool& success);
+#elif USE(DIRECT2D)
+ ImageBuffer(const FloatSize&, float resolutionScale, ColorSpace, RenderingMode, const GraphicsContext*, bool& success);
+#endif
+};
#if USE(CG)
- String ImageDataToDataURL(const ImageData&, const String& mimeType, const double* quality);
+String dataURL(const ImageData&, const String& mimeType, std::optional<double> quality);
#endif
} // namespace WebCore
diff --git a/Source/WebCore/platform/graphics/ImageBufferData.h b/Source/WebCore/platform/graphics/ImageBufferData.h
index 97cc6d452..f89ec0d36 100644
--- a/Source/WebCore/platform/graphics/ImageBufferData.h
+++ b/Source/WebCore/platform/graphics/ImageBufferData.h
@@ -10,10 +10,10 @@
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
- * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
@@ -25,8 +25,8 @@
#if USE(CG)
#include "ImageBufferDataCG.h"
+#elif USE(DIRECT2D)
+#include "ImageBufferDataDirect2D.h"
#elif USE(CAIRO)
#include "ImageBufferDataCairo.h"
-#elif USE(WINGDI)
-#include "ImageBufferDataWince.h"
#endif
diff --git a/Source/WebCore/platform/graphics/ImageFrame.cpp b/Source/WebCore/platform/graphics/ImageFrame.cpp
new file mode 100644
index 000000000..ea40ef532
--- /dev/null
+++ b/Source/WebCore/platform/graphics/ImageFrame.cpp
@@ -0,0 +1,175 @@
+/*
+ * Copyright (C) 2016 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "ImageFrame.h"
+
+#include <wtf/NeverDestroyed.h>
+
+namespace WebCore {
+
+ImageFrame::ImageFrame()
+{
+}
+
+ImageFrame::~ImageFrame()
+{
+ clearImage();
+}
+
+const ImageFrame& ImageFrame::defaultFrame()
+{
+ static NeverDestroyed<ImageFrame> sharedInstance;
+ return sharedInstance;
+}
+
+ImageFrame& ImageFrame::operator=(const ImageFrame& other)
+{
+ if (this == &other)
+ return *this;
+
+ m_decoding = other.m_decoding;
+ m_size = other.m_size;
+
+#if !USE(CG)
+ if (other.backingStore())
+ initialize(*other.backingStore());
+ else
+ m_backingStore = nullptr;
+ m_disposalMethod = other.m_disposalMethod;
+#endif
+
+ m_nativeImage = other.m_nativeImage;
+ m_subsamplingLevel = other.m_subsamplingLevel;
+ m_sizeForDrawing = other.m_sizeForDrawing;
+
+ m_orientation = other.m_orientation;
+ m_duration = other.m_duration;
+ m_hasAlpha = other.m_hasAlpha;
+ return *this;
+}
+
+unsigned ImageFrame::clearImage()
+{
+#if !USE(CG)
+ if (hasBackingStore())
+ m_backingStore = nullptr;
+#endif
+
+ if (!hasNativeImage())
+ return 0;
+
+ unsigned frameBytes = this->frameBytes();
+
+ clearNativeImageSubimages(m_nativeImage);
+ m_nativeImage = nullptr;
+
+ return frameBytes;
+}
+
+unsigned ImageFrame::clear()
+{
+ unsigned frameBytes = clearImage();
+ *this = ImageFrame();
+ return frameBytes;
+}
+
+#if !USE(CG)
+bool ImageFrame::initialize(const ImageBackingStore& backingStore)
+{
+ if (&backingStore == this->backingStore())
+ return true;
+
+ m_backingStore = ImageBackingStore::create(backingStore);
+ return m_backingStore != nullptr;
+}
+
+bool ImageFrame::initialize(const IntSize& size, bool premultiplyAlpha)
+{
+ if (size.isEmpty())
+ return false;
+
+ m_backingStore = ImageBackingStore::create(size, premultiplyAlpha);
+ return m_backingStore != nullptr;
+}
+#endif
+
+IntSize ImageFrame::size() const
+{
+#if !USE(CG)
+ if (hasBackingStore())
+ return backingStore()->size();
+#endif
+ return m_size;
+}
+
+static int maxDimension(const IntSize& size)
+{
+ return std::max(size.width(), size.height());
+}
+
+bool ImageFrame::isBeingDecoded(const std::optional<IntSize>& sizeForDrawing) const
+{
+ if (!m_sizeForDecoding.size())
+ return false;
+
+ if (!sizeForDrawing)
+ return true;
+
+ // Return true if the ImageFrame will be decoded eventually with a suitable sizeForDecoding.
+ return maxDimension(m_sizeForDecoding.last()) >= maxDimension(*sizeForDrawing);
+}
+
+bool ImageFrame::hasValidNativeImage(const std::optional<SubsamplingLevel>& subsamplingLevel, const std::optional<IntSize>& sizeForDrawing) const
+{
+ ASSERT_IMPLIES(!subsamplingLevel, !sizeForDrawing);
+
+ if (!hasNativeImage())
+ return false;
+
+ // The caller does not care about subsamplingLevel or sizeForDrawing. The current NativeImage is fine.
+ if (!subsamplingLevel)
+ return true;
+
+ if (*subsamplingLevel < m_subsamplingLevel)
+ return false;
+
+ // The NativeImage was decoded with the native size. So it is valid for any size.
+ if (!m_sizeForDrawing)
+ return true;
+
+ // The NativeImage was decoded for a specific size. The two sizeForDrawings have to match.
+ return sizeForDrawing && maxDimension(*m_sizeForDrawing) >= maxDimension(*sizeForDrawing);
+}
+
+Color ImageFrame::singlePixelSolidColor() const
+{
+ if (!hasNativeImage() || m_size != IntSize(1, 1))
+ return Color();
+
+ return nativeImageSinglePixelSolidColor(m_nativeImage);
+}
+
+}
diff --git a/Source/WebCore/platform/graphics/ImageFrame.h b/Source/WebCore/platform/graphics/ImageFrame.h
new file mode 100644
index 000000000..60593a42b
--- /dev/null
+++ b/Source/WebCore/platform/graphics/ImageFrame.h
@@ -0,0 +1,164 @@
+/*
+ * Copyright (C) 2016 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include "Color.h"
+#include "ImageBackingStore.h"
+#include "ImageOrientation.h"
+#include "IntSize.h"
+#include "NativeImage.h"
+#include <wtf/Deque.h>
+
+namespace WebCore {
+
+class Color;
+
+// There are four subsampling levels: 0 = 1x, 1 = 0.5x, 2 = 0.25x, 3 = 0.125x.
+enum class SubsamplingLevel {
+ First = 0,
+ Default = First,
+ Level0 = First,
+ Level1,
+ Level2,
+ Level3,
+ Last = Level3,
+ Max
+};
+
+inline SubsamplingLevel& operator++(SubsamplingLevel& subsamplingLevel)
+{
+ subsamplingLevel = static_cast<SubsamplingLevel>(static_cast<int>(subsamplingLevel) + 1);
+ ASSERT(subsamplingLevel <= SubsamplingLevel::Max);
+ return subsamplingLevel;
+}
+
+typedef int RepetitionCount;
+
+enum {
+ RepetitionCountNone = 0,
+ RepetitionCountOnce = 1,
+ RepetitionCountInfinite = -1,
+};
+
+enum class AlphaOption {
+ Premultiplied,
+ NotPremultiplied
+};
+
+enum class GammaAndColorProfileOption {
+ Applied,
+ Ignored
+};
+
+class ImageFrame {
+ friend class ImageFrameCache;
+public:
+ enum class Caching { Metadata, MetadataAndImage };
+ enum class Decoding { None, Partial, Complete };
+
+ ImageFrame();
+ ImageFrame(const ImageFrame& other) { operator=(other); }
+
+ ~ImageFrame();
+
+ static const ImageFrame& defaultFrame();
+
+ ImageFrame& operator=(const ImageFrame& other);
+
+ unsigned clearImage();
+ unsigned clear();
+
+#if !USE(CG)
+ bool initialize(const ImageBackingStore&);
+ bool initialize(const IntSize&, bool premultiplyAlpha);
+#endif
+
+ void setDecoding(Decoding decoding) { m_decoding = decoding; }
+ Decoding decoding() const { return m_decoding; }
+ void enqueueSizeForDecoding(const IntSize& sizeForDecoding) { m_sizeForDecoding.append(sizeForDecoding); }
+ void dequeueSizeForDecoding() { m_sizeForDecoding.removeFirst(); }
+ void clearSizeForDecoding() { m_sizeForDecoding.clear(); }
+
+ bool isEmpty() const { return m_decoding == Decoding::None; }
+ bool isBeingDecoded(const std::optional<IntSize>& sizeForDrawing = { }) const;
+ bool isPartial() const { return m_decoding == Decoding::Partial; }
+ bool isComplete() const { return m_decoding == Decoding::Complete; }
+
+ IntSize size() const;
+ IntSize sizeRespectingOrientation() const { return !m_orientation.usesWidthAsHeight() ? size() : size().transposedSize(); }
+ unsigned frameBytes() const { return hasNativeImage() ? (size().area() * sizeof(RGBA32)).unsafeGet() : 0; }
+ SubsamplingLevel subsamplingLevel() const { return m_subsamplingLevel; }
+ std::optional<IntSize> sizeForDrawing() const { return m_sizeForDrawing; }
+
+#if !USE(CG)
+ enum class DisposalMethod { Unspecified, DoNotDispose, RestoreToBackground, RestoreToPrevious };
+ void setDisposalMethod(DisposalMethod method) { m_disposalMethod = method; }
+ DisposalMethod disposalMethod() const { return m_disposalMethod; }
+#endif
+
+ NativeImagePtr nativeImage() const { return m_nativeImage; }
+
+ void setOrientation(ImageOrientation orientation) { m_orientation = orientation; };
+ ImageOrientation orientation() const { return m_orientation; }
+
+ void setDuration(float duration) { m_duration = duration; }
+ float duration() const { return m_duration; }
+
+ void setHasAlpha(bool hasAlpha) { m_hasAlpha = hasAlpha; }
+ bool hasAlpha() const { return !hasMetadata() || m_hasAlpha; }
+
+ bool hasNativeImage() const { return m_nativeImage; }
+ bool hasValidNativeImage(const std::optional<SubsamplingLevel>&, const std::optional<IntSize>& sizeForDrawing) const;
+ bool hasDecodedNativeImage() const { return hasNativeImage() && sizeForDrawing(); }
+ bool hasMetadata() const { return !size().isEmpty(); }
+
+#if !USE(CG)
+ ImageBackingStore* backingStore() const { return m_backingStore ? m_backingStore.get() : nullptr; }
+ bool hasBackingStore() const { return backingStore(); }
+#endif
+
+ Color singlePixelSolidColor() const;
+
+private:
+ Decoding m_decoding { Decoding::None };
+ IntSize m_size;
+
+#if !USE(CG)
+ std::unique_ptr<ImageBackingStore> m_backingStore;
+ DisposalMethod m_disposalMethod { DisposalMethod::Unspecified };
+#endif
+
+ NativeImagePtr m_nativeImage;
+ SubsamplingLevel m_subsamplingLevel { SubsamplingLevel::Default };
+ std::optional<IntSize> m_sizeForDrawing;
+ Deque<IntSize, 4> m_sizeForDecoding;
+
+ ImageOrientation m_orientation { DefaultImageOrientation };
+ float m_duration { 0 };
+ bool m_hasAlpha { true };
+};
+
+}
diff --git a/Source/WebCore/platform/graphics/ImageFrameCache.cpp b/Source/WebCore/platform/graphics/ImageFrameCache.cpp
new file mode 100644
index 000000000..cb1453aa7
--- /dev/null
+++ b/Source/WebCore/platform/graphics/ImageFrameCache.cpp
@@ -0,0 +1,541 @@
+/*
+ * Copyright (C) 2016 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "ImageFrameCache.h"
+
+#include "Image.h"
+#include "ImageObserver.h"
+
+#if USE(CG)
+#include "ImageDecoderCG.h"
+#elif USE(DIRECT2D)
+#include "ImageDecoderDirect2D.h"
+#include <WinCodec.h>
+#else
+#include "ImageDecoder.h"
+#endif
+
+#include <wtf/CheckedArithmetic.h>
+#include <wtf/MainThread.h>
+#include <wtf/RunLoop.h>
+
+namespace WebCore {
+
+ImageFrameCache::ImageFrameCache(Image* image)
+ : m_image(image)
+{
+}
+
+ImageFrameCache::ImageFrameCache(NativeImagePtr&& nativeImage)
+{
+ m_frameCount = 1;
+ m_isSizeAvailable = true;
+ growFrames();
+
+ setNativeImage(WTFMove(nativeImage));
+
+ m_decodedSize = m_frames[0].frameBytes();
+
+ // The assumption is the memory image will be displayed with the default
+ // orientation. So set m_sizeRespectingOrientation to be the same as m_size.
+ m_size = m_frames[0].size();
+ m_sizeRespectingOrientation = m_size;
+}
+
+ImageFrameCache::~ImageFrameCache()
+{
+ ASSERT(!hasDecodingQueue());
+}
+
+void ImageFrameCache::setDecoder(ImageDecoder* decoder)
+{
+ if (m_decoder == decoder)
+ return;
+
+ // Changing the decoder has to stop the decoding thread. The current frame will
+ // continue decoding safely because the decoding thread has its own
+ // reference of the old decoder.
+ stopAsyncDecodingQueue();
+ m_decoder = decoder;
+}
+
+ImageDecoder* ImageFrameCache::decoder() const
+{
+ return m_decoder.get();
+}
+
+void ImageFrameCache::destroyDecodedData(size_t frameCount, size_t excludeFrame)
+{
+ unsigned decodedSize = 0;
+
+ ASSERT(frameCount <= m_frames.size());
+
+ for (size_t index = 0; index < frameCount; ++index) {
+ if (index == excludeFrame)
+ continue;
+ decodedSize += m_frames[index++].clearImage();
+ }
+
+ decodedSizeReset(decodedSize);
+}
+
+void ImageFrameCache::destroyIncompleteDecodedData()
+{
+ unsigned decodedSize = 0;
+
+ for (auto& frame : m_frames) {
+ if (!frame.hasMetadata() || frame.isComplete())
+ continue;
+
+ decodedSize += frame.clear();
+ }
+
+ decodedSizeDecreased(decodedSize);
+}
+
+void ImageFrameCache::decodedSizeChanged(long long decodedSize)
+{
+ if (!decodedSize || !m_image || !m_image->imageObserver())
+ return;
+
+ m_image->imageObserver()->decodedSizeChanged(m_image, decodedSize);
+}
+
+void ImageFrameCache::decodedSizeIncreased(unsigned decodedSize)
+{
+ if (!decodedSize)
+ return;
+
+ m_decodedSize += decodedSize;
+
+ // The fully-decoded frame will subsume the partially decoded data used
+ // to determine image properties.
+ long long changeSize = static_cast<long long>(decodedSize) - m_decodedPropertiesSize;
+ m_decodedPropertiesSize = 0;
+ decodedSizeChanged(changeSize);
+}
+
+void ImageFrameCache::decodedSizeDecreased(unsigned decodedSize)
+{
+ if (!decodedSize)
+ return;
+
+ ASSERT(m_decodedSize >= decodedSize);
+ m_decodedSize -= decodedSize;
+ decodedSizeChanged(-static_cast<long long>(decodedSize));
+}
+
+void ImageFrameCache::decodedSizeReset(unsigned decodedSize)
+{
+ ASSERT(m_decodedSize >= decodedSize);
+ m_decodedSize -= decodedSize;
+
+ // Clearing the ImageSource destroys the extra decoded data used for
+ // determining image properties.
+ decodedSize += m_decodedPropertiesSize;
+ m_decodedPropertiesSize = 0;
+ decodedSizeChanged(-static_cast<long long>(decodedSize));
+}
+
+void ImageFrameCache::didDecodeProperties(unsigned decodedPropertiesSize)
+{
+ if (m_decodedSize)
+ return;
+
+ long long decodedSize = static_cast<long long>(decodedPropertiesSize) - m_decodedPropertiesSize;
+ m_decodedPropertiesSize = decodedPropertiesSize;
+ decodedSizeChanged(decodedSize);
+}
+
+void ImageFrameCache::growFrames()
+{
+ ASSERT(isSizeAvailable());
+ ASSERT(m_frames.size() <= frameCount());
+ m_frames.grow(frameCount());
+}
+
+void ImageFrameCache::setNativeImage(NativeImagePtr&& nativeImage)
+{
+ ASSERT(m_frames.size() == 1);
+ ImageFrame& frame = m_frames[0];
+
+ ASSERT(!isDecoderAvailable());
+
+ frame.m_nativeImage = WTFMove(nativeImage);
+
+ frame.m_decoding = ImageFrame::Decoding::Complete;
+ frame.m_size = nativeImageSize(frame.m_nativeImage);
+ frame.m_hasAlpha = nativeImageHasAlpha(frame.m_nativeImage);
+}
+
+void ImageFrameCache::setFrameNativeImageAtIndex(NativeImagePtr&& nativeImage, size_t index, SubsamplingLevel subsamplingLevel, const std::optional<IntSize>& sizeForDrawing)
+{
+ ASSERT(index < m_frames.size());
+ ImageFrame& frame = m_frames[index];
+
+ ASSERT(isDecoderAvailable());
+
+ frame.m_nativeImage = WTFMove(nativeImage);
+ setFrameMetadataAtIndex(index, subsamplingLevel, sizeForDrawing);
+}
+
+void ImageFrameCache::setFrameMetadataAtIndex(size_t index, SubsamplingLevel subsamplingLevel, const std::optional<IntSize>& sizeForDrawing)
+{
+ ASSERT(index < m_frames.size());
+ ImageFrame& frame = m_frames[index];
+
+ ASSERT(isDecoderAvailable());
+ frame.m_decoding = m_decoder->frameIsCompleteAtIndex(index) ? ImageFrame::Decoding::Complete : ImageFrame::Decoding::Partial;
+ if (frame.hasMetadata())
+ return;
+
+ frame.m_subsamplingLevel = subsamplingLevel;
+
+ if (!sizeForDrawing) {
+ frame.m_size = m_decoder->frameSizeAtIndex(index, frame.m_subsamplingLevel);
+ frame.m_sizeForDrawing = { };
+ } else {
+ ASSERT(frame.nativeImage());
+ frame.m_size = nativeImageSize(frame.nativeImage());
+ frame.m_sizeForDrawing = sizeForDrawing;
+ }
+
+ frame.m_orientation = m_decoder->frameOrientationAtIndex(index);
+ frame.m_hasAlpha = m_decoder->frameHasAlphaAtIndex(index);
+
+ if (repetitionCount())
+ frame.m_duration = m_decoder->frameDurationAtIndex(index);
+}
+
+void ImageFrameCache::replaceFrameNativeImageAtIndex(NativeImagePtr&& nativeImage, size_t index, SubsamplingLevel subsamplingLevel, const std::optional<IntSize>& sizeForDrawing)
+{
+ ASSERT(index < m_frames.size());
+ ImageFrame& frame = m_frames[index];
+
+ if (!frame.hasValidNativeImage(subsamplingLevel, sizeForDrawing)) {
+ // Clear the current image frame and update the observer with this clearance.
+ unsigned decodedSize = frame.clear();
+ decodedSizeDecreased(decodedSize);
+ }
+
+ // Do not cache the NativeImage if adding its frameByes to the MemoryCache will cause numerical overflow.
+ size_t frameBytes = size().unclampedArea() * sizeof(RGBA32);
+ if (!WTF::isInBounds<unsigned>(frameBytes + decodedSize()))
+ return;
+
+ // Copy the new image to the cache.
+ setFrameNativeImageAtIndex(WTFMove(nativeImage), index, subsamplingLevel, sizeForDrawing);
+
+ // Update the observer with the new image frame bytes.
+ decodedSizeIncreased(frame.frameBytes());
+}
+
+void ImageFrameCache::cacheFrameNativeImageAtIndex(NativeImagePtr&& nativeImage, size_t index, SubsamplingLevel subsamplingLevel, const IntSize& sizeForDrawing)
+{
+ if (!isDecoderAvailable())
+ return;
+
+ ASSERT(index < m_frames.size());
+ ASSERT(m_frames[index].isBeingDecoded(sizeForDrawing));
+
+ // Clean the old native image and set a new one
+ replaceFrameNativeImageAtIndex(WTFMove(nativeImage), index, subsamplingLevel, sizeForDrawing);
+ m_frames[index].dequeueSizeForDecoding();
+
+ // Notify the image with the readiness of the new frame NativeImage.
+ if (m_image)
+ m_image->newFrameNativeImageAvailableAtIndex(index);
+}
+
+Ref<WorkQueue> ImageFrameCache::decodingQueue()
+{
+ if (!m_decodingQueue)
+ m_decodingQueue = WorkQueue::create("org.webkit.ImageDecoder", WorkQueue::Type::Serial, WorkQueue::QOS::UserInteractive);
+
+ return *m_decodingQueue;
+}
+
+void ImageFrameCache::startAsyncDecodingQueue()
+{
+ if (hasDecodingQueue() || !isDecoderAvailable())
+ return;
+
+ m_frameRequestQueue.open();
+
+ Ref<ImageFrameCache> protectedThis = Ref<ImageFrameCache>(*this);
+ Ref<WorkQueue> protectedQueue = decodingQueue();
+ Ref<ImageDecoder> protectedDecoder = Ref<ImageDecoder>(*m_decoder);
+
+ // We need to protect this, m_decodingQueue and m_decoder from being deleted while we are in the decoding loop.
+ decodingQueue()->dispatch([this, protectedThis = WTFMove(protectedThis), protectedQueue = WTFMove(protectedQueue), protectedDecoder = WTFMove(protectedDecoder)] {
+ ImageFrameRequest frameRequest;
+
+ while (m_frameRequestQueue.dequeue(frameRequest)) {
+ // Get the frame NativeImage on the decoding thread.
+ NativeImagePtr nativeImage = protectedDecoder->createFrameImageAtIndex(frameRequest.index, frameRequest.subsamplingLevel, frameRequest.sizeForDrawing);
+
+ // Update the cached frames on the main thread to avoid updating the MemoryCache from a different thread.
+ callOnMainThread([this, protectedQueue = protectedQueue.copyRef(), nativeImage, frameRequest] () mutable {
+ // The queue may be closed if after we got the frame NativeImage, stopAsyncDecodingQueue() was called
+ if (protectedQueue.ptr() == m_decodingQueue)
+ cacheFrameNativeImageAtIndex(WTFMove(nativeImage), frameRequest.index, frameRequest.subsamplingLevel, frameRequest.sizeForDrawing);
+ });
+ }
+ });
+}
+
+bool ImageFrameCache::requestFrameAsyncDecodingAtIndex(size_t index, SubsamplingLevel subsamplingLevel, const IntSize& sizeForDrawing)
+{
+ if (!isDecoderAvailable())
+ return false;
+
+ ASSERT(index < m_frames.size());
+ ImageFrame& frame = m_frames[index];
+
+ // We need to coalesce multiple requests for decoding the same ImageFrame while it
+ // is still being decoded. This may happen if the image rectangle is repainted
+ // multiple times while the ImageFrame has not finished decoding.
+ if (frame.isBeingDecoded(sizeForDrawing))
+ return true;
+
+ if (frame.hasValidNativeImage(subsamplingLevel, sizeForDrawing))
+ return false;
+
+ if (!hasDecodingQueue())
+ startAsyncDecodingQueue();
+
+ frame.enqueueSizeForDecoding(sizeForDrawing);
+ m_frameRequestQueue.enqueue({ index, subsamplingLevel, sizeForDrawing });
+ return true;
+}
+
+void ImageFrameCache::stopAsyncDecodingQueue()
+{
+ if (!hasDecodingQueue())
+ return;
+
+ m_frameRequestQueue.close();
+ m_decodingQueue = nullptr;
+
+ for (ImageFrame& frame : m_frames) {
+ if (frame.isBeingDecoded()) {
+ frame.clearSizeForDecoding();
+ frame.clear();
+ }
+ }
+}
+
+const ImageFrame& ImageFrameCache::frameAtIndexCacheIfNeeded(size_t index, ImageFrame::Caching caching, const std::optional<SubsamplingLevel>& subsamplingLevel, const std::optional<IntSize>& sizeForDrawing)
+{
+ ASSERT(index < m_frames.size());
+ ImageFrame& frame = m_frames[index];
+ if (!isDecoderAvailable() || frame.isBeingDecoded(sizeForDrawing))
+ return frame;
+
+ SubsamplingLevel subsamplingLevelValue = subsamplingLevel ? subsamplingLevel.value() : frame.subsamplingLevel();
+
+ switch (caching) {
+ case ImageFrame::Caching::Metadata:
+ // Retrieve the metadata from ImageDecoder if the ImageFrame isn't complete.
+ if (frame.isComplete())
+ break;
+ setFrameMetadataAtIndex(index, subsamplingLevelValue, frame.sizeForDrawing());
+ break;
+
+ case ImageFrame::Caching::MetadataAndImage:
+ // Cache the image and retrieve the metadata from ImageDecoder only if there was not valid image stored.
+ if (frame.hasValidNativeImage(subsamplingLevel, sizeForDrawing))
+ break;
+ // We have to perform synchronous image decoding in this code path regardless of the sizeForDrawing value.
+ // So pass an empty sizeForDrawing to create an ImageFrame with the native size.
+ replaceFrameNativeImageAtIndex(m_decoder->createFrameImageAtIndex(index, subsamplingLevelValue, { }), index, subsamplingLevelValue, { });
+ break;
+ }
+
+ return frame;
+}
+
+void ImageFrameCache::clearMetadata()
+{
+ m_frameCount = std::nullopt;
+ m_singlePixelSolidColor = std::nullopt;
+}
+
+template<typename T, T (ImageDecoder::*functor)() const>
+T ImageFrameCache::metadata(const T& defaultValue, std::optional<T>* cachedValue)
+{
+ if (cachedValue && *cachedValue)
+ return cachedValue->value();
+
+ if (!isDecoderAvailable() || !m_decoder->isSizeAvailable())
+ return defaultValue;
+
+ if (!cachedValue)
+ return (*m_decoder.*functor)();
+
+ *cachedValue = (*m_decoder.*functor)();
+ didDecodeProperties(m_decoder->bytesDecodedToDetermineProperties());
+ return cachedValue->value();
+}
+
+template<typename T, typename... Args>
+T ImageFrameCache::frameMetadataAtIndex(size_t index, T (ImageFrame::*functor)(Args...) const, Args&&... args)
+{
+ const ImageFrame& frame = index < m_frames.size() ? m_frames[index] : ImageFrame::defaultFrame();
+ return (frame.*functor)(std::forward<Args>(args)...);
+}
+
+template<typename T, typename... Args>
+T ImageFrameCache::frameMetadataAtIndexCacheIfNeeded(size_t index, T (ImageFrame::*functor)() const, std::optional<T>* cachedValue, Args&&... args)
+{
+ if (cachedValue && *cachedValue)
+ return cachedValue->value();
+
+ const ImageFrame& frame = index < m_frames.size() ? frameAtIndexCacheIfNeeded(index, std::forward<Args>(args)...) : ImageFrame::defaultFrame();
+
+ // Don't cache any unavailable frame metadata.
+ if (!frame.hasMetadata() || !cachedValue)
+ return (frame.*functor)();
+
+ *cachedValue = (frame.*functor)();
+ return cachedValue->value();
+}
+
+bool ImageFrameCache::isSizeAvailable()
+{
+ if (m_isSizeAvailable)
+ return m_isSizeAvailable.value();
+
+ if (!isDecoderAvailable() || !m_decoder->isSizeAvailable())
+ return false;
+
+ m_isSizeAvailable = true;
+ didDecodeProperties(m_decoder->bytesDecodedToDetermineProperties());
+ return true;
+}
+
+size_t ImageFrameCache::frameCount()
+{
+ return metadata<size_t, (&ImageDecoder::frameCount)>(m_frames.size(), &m_frameCount);
+}
+
+RepetitionCount ImageFrameCache::repetitionCount()
+{
+ return metadata<RepetitionCount, (&ImageDecoder::repetitionCount)>(RepetitionCountNone, &m_repetitionCount);
+}
+
+String ImageFrameCache::filenameExtension()
+{
+ return metadata<String, (&ImageDecoder::filenameExtension)>(String(), &m_filenameExtension);
+}
+
+std::optional<IntPoint> ImageFrameCache::hotSpot()
+{
+ return metadata<std::optional<IntPoint>, (&ImageDecoder::hotSpot)>(std::nullopt, &m_hotSpot);
+}
+
+IntSize ImageFrameCache::size()
+{
+#if !USE(CG)
+ // It's possible that we have decoded the metadata, but not frame contents yet. In that case ImageDecoder claims to
+ // have the size available, but the frame cache is empty. Return the decoder size without caching in such case.
+ if (m_frames.isEmpty() && isDecoderAvailable())
+ return m_decoder->size();
+#endif
+ return frameMetadataAtIndexCacheIfNeeded<IntSize>(0, (&ImageFrame::size), &m_size, ImageFrame::Caching::Metadata, SubsamplingLevel::Default);
+}
+
+IntSize ImageFrameCache::sizeRespectingOrientation()
+{
+ return frameMetadataAtIndexCacheIfNeeded<IntSize>(0, (&ImageFrame::sizeRespectingOrientation), &m_sizeRespectingOrientation, ImageFrame::Caching::Metadata, SubsamplingLevel::Default);
+}
+
+Color ImageFrameCache::singlePixelSolidColor()
+{
+ return frameCount() == 1 ? frameMetadataAtIndexCacheIfNeeded<Color>(0, (&ImageFrame::singlePixelSolidColor), &m_singlePixelSolidColor, ImageFrame::Caching::MetadataAndImage) : Color();
+}
+
+bool ImageFrameCache::frameIsBeingDecodedAtIndex(size_t index, const std::optional<IntSize>& sizeForDrawing)
+{
+ return frameMetadataAtIndex<bool>(index, (&ImageFrame::isBeingDecoded), sizeForDrawing);
+}
+
+bool ImageFrameCache::frameIsCompleteAtIndex(size_t index)
+{
+ return frameMetadataAtIndex<bool>(index, (&ImageFrame::isComplete));
+}
+
+bool ImageFrameCache::frameHasAlphaAtIndex(size_t index)
+{
+ return frameMetadataAtIndex<bool>(index, (&ImageFrame::hasAlpha));
+}
+
+bool ImageFrameCache::frameHasImageAtIndex(size_t index)
+{
+ return frameMetadataAtIndex<bool>(index, (&ImageFrame::hasNativeImage));
+}
+
+bool ImageFrameCache::frameHasValidNativeImageAtIndex(size_t index, const std::optional<SubsamplingLevel>& subsamplingLevel, const std::optional<IntSize>& sizeForDrawing)
+{
+ return frameMetadataAtIndex<bool>(index, (&ImageFrame::hasValidNativeImage), subsamplingLevel, sizeForDrawing);
+}
+
+bool ImageFrameCache::frameHasDecodedNativeImage(size_t index)
+{
+ return frameMetadataAtIndex<bool>(index, (&ImageFrame::hasDecodedNativeImage));
+}
+
+SubsamplingLevel ImageFrameCache::frameSubsamplingLevelAtIndex(size_t index)
+{
+ return frameMetadataAtIndex<SubsamplingLevel>(index, (&ImageFrame::subsamplingLevel));
+}
+
+IntSize ImageFrameCache::frameSizeAtIndex(size_t index, SubsamplingLevel subsamplingLevel)
+{
+ return frameMetadataAtIndexCacheIfNeeded<IntSize>(index, (&ImageFrame::size), nullptr, ImageFrame::Caching::Metadata, subsamplingLevel);
+}
+
+unsigned ImageFrameCache::frameBytesAtIndex(size_t index, SubsamplingLevel subsamplingLevel)
+{
+ return frameMetadataAtIndexCacheIfNeeded<unsigned>(index, (&ImageFrame::frameBytes), nullptr, ImageFrame::Caching::Metadata, subsamplingLevel);
+}
+
+float ImageFrameCache::frameDurationAtIndex(size_t index)
+{
+ return frameMetadataAtIndexCacheIfNeeded<float>(index, (&ImageFrame::duration), nullptr, ImageFrame::Caching::Metadata);
+}
+
+ImageOrientation ImageFrameCache::frameOrientationAtIndex(size_t index)
+{
+ return frameMetadataAtIndexCacheIfNeeded<ImageOrientation>(index, (&ImageFrame::orientation), nullptr, ImageFrame::Caching::Metadata);
+}
+
+NativeImagePtr ImageFrameCache::frameImageAtIndex(size_t index, const std::optional<SubsamplingLevel>& subsamplingLevel, const std::optional<IntSize>& sizeForDrawing)
+{
+ return frameMetadataAtIndexCacheIfNeeded<NativeImagePtr>(index, (&ImageFrame::nativeImage), nullptr, ImageFrame::Caching::MetadataAndImage, subsamplingLevel, sizeForDrawing);
+}
+
+}
diff --git a/Source/WebCore/platform/graphics/ImageFrameCache.h b/Source/WebCore/platform/graphics/ImageFrameCache.h
new file mode 100644
index 000000000..d623573e6
--- /dev/null
+++ b/Source/WebCore/platform/graphics/ImageFrameCache.h
@@ -0,0 +1,168 @@
+/*
+ * Copyright (C) 2016 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include "ImageFrame.h"
+#include "TextStream.h"
+
+#include <wtf/Forward.h>
+#include <wtf/Optional.h>
+#include <wtf/SynchronizedFixedQueue.h>
+#include <wtf/WorkQueue.h>
+#include <wtf/threads/BinarySemaphore.h>
+
+namespace WebCore {
+
+class GraphicsContext;
+class Image;
+class ImageDecoder;
+
+class ImageFrameCache : public RefCounted<ImageFrameCache> {
+ friend class ImageSource;
+public:
+ static Ref<ImageFrameCache> create(Image* image)
+ {
+ return adoptRef(*new ImageFrameCache(image));
+ }
+
+ static Ref<ImageFrameCache> create(NativeImagePtr&& nativeImage)
+ {
+ return adoptRef(*new ImageFrameCache(WTFMove(nativeImage)));
+ }
+
+ ~ImageFrameCache();
+
+ void setDecoder(ImageDecoder*);
+ ImageDecoder* decoder() const;
+
+ unsigned decodedSize() const { return m_decodedSize; }
+ void destroyAllDecodedData() { destroyDecodedData(frameCount(), frameCount()); }
+ void destroyAllDecodedDataExcludeFrame(size_t excludeFrame) { destroyDecodedData(frameCount(), excludeFrame); }
+ void destroyDecodedDataBeforeFrame(size_t beforeFrame) { destroyDecodedData(beforeFrame, beforeFrame); }
+ void destroyIncompleteDecodedData();
+
+ void growFrames();
+ void clearMetadata();
+
+ // Asynchronous image decoding
+ void startAsyncDecodingQueue();
+ bool requestFrameAsyncDecodingAtIndex(size_t, SubsamplingLevel, const IntSize&);
+ void stopAsyncDecodingQueue();
+ bool hasDecodingQueue() { return m_decodingQueue; }
+
+ // Image metadata which is calculated either by the ImageDecoder or directly
+ // from the NativeImage if this class was created for a memory image.
+ bool isSizeAvailable();
+ size_t frameCount();
+ RepetitionCount repetitionCount();
+ String filenameExtension();
+ std::optional<IntPoint> hotSpot();
+
+ // Image metadata which is calculated from the first ImageFrame.
+ IntSize size();
+ IntSize sizeRespectingOrientation();
+
+ Color singlePixelSolidColor();
+
+ // ImageFrame metadata which does not require caching the ImageFrame.
+ bool frameIsBeingDecodedAtIndex(size_t, const std::optional<IntSize>& sizeForDrawing);
+ bool frameIsCompleteAtIndex(size_t);
+ bool frameHasAlphaAtIndex(size_t);
+ bool frameHasImageAtIndex(size_t);
+ bool frameHasValidNativeImageAtIndex(size_t, const std::optional<SubsamplingLevel>&, const std::optional<IntSize>& sizeForDrawing);
+ bool frameHasDecodedNativeImage(size_t);
+ SubsamplingLevel frameSubsamplingLevelAtIndex(size_t);
+
+ // ImageFrame metadata which forces caching or re-caching the ImageFrame.
+ IntSize frameSizeAtIndex(size_t, SubsamplingLevel = SubsamplingLevel::Default);
+ unsigned frameBytesAtIndex(size_t, SubsamplingLevel = SubsamplingLevel::Default);
+ float frameDurationAtIndex(size_t);
+ ImageOrientation frameOrientationAtIndex(size_t);
+ NativeImagePtr frameImageAtIndex(size_t, const std::optional<SubsamplingLevel>&, const std::optional<IntSize>& sizeForDrawing);
+
+private:
+ ImageFrameCache(Image*);
+ ImageFrameCache(NativeImagePtr&&);
+
+ template<typename T, T (ImageDecoder::*functor)() const>
+ T metadata(const T& defaultValue, std::optional<T>* cachedValue = nullptr);
+
+ template<typename T, typename... Args>
+ T frameMetadataAtIndex(size_t, T (ImageFrame::*functor)(Args...) const, Args&&...);
+
+ template<typename T, typename... Args>
+ T frameMetadataAtIndexCacheIfNeeded(size_t, T (ImageFrame::*functor)() const, std::optional<T>* cachedValue, Args&&...);
+
+ bool isDecoderAvailable() const { return m_decoder; }
+ void destroyDecodedData(size_t frameCount, size_t excludeFrame);
+ void decodedSizeChanged(long long decodedSize);
+ void didDecodeProperties(unsigned decodedPropertiesSize);
+ void decodedSizeIncreased(unsigned decodedSize);
+ void decodedSizeDecreased(unsigned decodedSize);
+ void decodedSizeReset(unsigned decodedSize);
+
+ void setNativeImage(NativeImagePtr&&);
+ void setFrameNativeImageAtIndex(NativeImagePtr&&, size_t, SubsamplingLevel, const std::optional<IntSize>& sizeForDrawing);
+ void setFrameMetadataAtIndex(size_t, SubsamplingLevel, const std::optional<IntSize>& sizeForDrawing);
+ void replaceFrameNativeImageAtIndex(NativeImagePtr&&, size_t, SubsamplingLevel, const std::optional<IntSize>& sizeForDrawing);
+ void cacheFrameNativeImageAtIndex(NativeImagePtr&&, size_t, SubsamplingLevel, const IntSize& sizeForDrawing);
+
+ Ref<WorkQueue> decodingQueue();
+
+ const ImageFrame& frameAtIndexCacheIfNeeded(size_t, ImageFrame::Caching, const std::optional<SubsamplingLevel>& = { }, const std::optional<IntSize>& sizeForDrawing = { });
+
+ Image* m_image { nullptr };
+ RefPtr<ImageDecoder> m_decoder;
+ unsigned m_decodedSize { 0 };
+ unsigned m_decodedPropertiesSize { 0 };
+
+ Vector<ImageFrame, 1> m_frames;
+
+ // Asynchronous image decoding.
+ struct ImageFrameRequest {
+ size_t index;
+ SubsamplingLevel subsamplingLevel;
+ IntSize sizeForDrawing;
+ };
+ static const int BufferSize = 8;
+ using FrameRequestQueue = SynchronizedFixedQueue<ImageFrameRequest, BufferSize>;
+ FrameRequestQueue m_frameRequestQueue;
+ RefPtr<WorkQueue> m_decodingQueue;
+
+ // Image metadata.
+ std::optional<bool> m_isSizeAvailable;
+ std::optional<size_t> m_frameCount;
+ std::optional<RepetitionCount> m_repetitionCount;
+ std::optional<String> m_filenameExtension;
+ std::optional<std::optional<IntPoint>> m_hotSpot;
+
+ // Image metadata which is calculated from the first ImageFrame.
+ std::optional<IntSize> m_size;
+ std::optional<IntSize> m_sizeRespectingOrientation;
+ std::optional<Color> m_singlePixelSolidColor;
+};
+
+}
diff --git a/Source/WebCore/platform/graphics/ImageObserver.h b/Source/WebCore/platform/graphics/ImageObserver.h
index 8b693d984..7c86194c6 100644
--- a/Source/WebCore/platform/graphics/ImageObserver.h
+++ b/Source/WebCore/platform/graphics/ImageObserver.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2004, 2005, 2006 Apple Computer, Inc. All rights reserved.
+ * Copyright (C) 2004, 2005, 2006 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -10,10 +10,10 @@
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
- * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
@@ -30,6 +30,7 @@ namespace WebCore {
class Image;
class IntRect;
+class URL;
// Interface for notification about changes to an image, including decoding,
// drawing, and animating.
@@ -37,13 +38,18 @@ class ImageObserver {
protected:
virtual ~ImageObserver() {}
public:
- virtual void decodedSizeChanged(const Image*, int delta) = 0;
+ virtual URL sourceUrl() const = 0;
+ virtual bool allowSubsampling() const = 0;
+ virtual bool allowLargeImageAsyncDecoding() const = 0;
+ virtual bool allowAnimatedImageAsyncDecoding() const = 0;
+ virtual bool showDebugBackground() const = 0;
+ virtual void decodedSizeChanged(const Image*, long long delta) = 0;
+
virtual void didDraw(const Image*) = 0;
- virtual bool shouldPauseAnimation(const Image*) = 0;
virtual void animationAdvanced(const Image*) = 0;
- virtual void changedInRect(const Image*, const IntRect&) = 0;
+ virtual void changedInRect(const Image*, const IntRect* changeRect = nullptr) = 0;
};
}
diff --git a/Source/WebCore/platform/graphics/ImageOrientation.h b/Source/WebCore/platform/graphics/ImageOrientation.h
index 4630dc6c0..2b3910690 100644
--- a/Source/WebCore/platform/graphics/ImageOrientation.h
+++ b/Source/WebCore/platform/graphics/ImageOrientation.h
@@ -84,7 +84,12 @@ struct ImageOrientationDescription {
class ImageOrientation {
public:
- ImageOrientation(ImageOrientationEnum orientation = DefaultImageOrientation)
+ ImageOrientation()
+ : m_orientation(DefaultImageOrientation)
+ {
+ }
+
+ explicit ImageOrientation(ImageOrientationEnum orientation)
: m_orientation(orientation)
{
}
@@ -101,14 +106,16 @@ public:
{
// Values direct from images may be invalid, in which case we use the default.
if (exifValue < OriginTopLeft || exifValue > OriginLeftBottom)
- return DefaultImageOrientation;
- return static_cast<ImageOrientationEnum>(exifValue);
+ return ImageOrientation();
+ return ImageOrientation(static_cast<ImageOrientationEnum>(exifValue));
}
// This transform can be used for drawing an image according to the orientation.
// It should be used in a right-handed coordinate system.
AffineTransform transformFromDefault(const FloatSize& drawnSize) const;
+ inline operator ImageOrientationEnum() const { return m_orientation; }
+
inline bool operator==(const ImageOrientation& other) const { return other.m_orientation == m_orientation; }
inline bool operator!=(const ImageOrientation& other) const { return !(*this == other); }
diff --git a/Source/WebCore/platform/graphics/ImageRenderingMode.h b/Source/WebCore/platform/graphics/ImageRenderingMode.h
new file mode 100644
index 000000000..3e462fba5
--- /dev/null
+++ b/Source/WebCore/platform/graphics/ImageRenderingMode.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2011 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef ImageRenderingMode_h
+#define ImageRenderingMode_h
+
+namespace WebCore {
+
+enum ImageRenderingMode { AutoImageRendering, OptimizeContrast };
+
+} // namespace WebCore
+
+#endif // ImageRenderingMode_h
diff --git a/Source/WebCore/platform/graphics/ImageSource.cpp b/Source/WebCore/platform/graphics/ImageSource.cpp
index c041f5638..688a8c727 100644
--- a/Source/WebCore/platform/graphics/ImageSource.cpp
+++ b/Source/WebCore/platform/graphics/ImageSource.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2006 Apple Computer, Inc. All rights reserved.
+ * Copyright (C) 2006, 2010, 2011, 2012, 2014, 2016 Apple Inc. All rights reserved.
* Copyright (C) 2007 Alp Toker <alp.toker@collabora.co.uk>
* Copyright (C) 2008, Google Inc. All rights reserved.
* Copyright (C) 2007-2009 Torch Mobile, Inc
@@ -13,10 +13,10 @@
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
- * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
@@ -29,19 +29,29 @@
#include "config.h"
#include "ImageSource.h"
+#if USE(CG)
+#include "ImageDecoderCG.h"
+#elif USE(DIRECT2D)
+#include "GraphicsContext.h"
+#include "ImageDecoderDirect2D.h"
+#include <WinCodec.h>
+#else
#include "ImageDecoder.h"
+#endif
#include "ImageOrientation.h"
-#include "NotImplemented.h"
-namespace WebCore {
+#include <wtf/CurrentTime.h>
-#if ENABLE(IMAGE_DECODER_DOWN_SAMPLING)
-unsigned ImageSource::s_maxPixelsPerDecodedImage = 1024 * 1024;
-#endif
+namespace WebCore {
-ImageSource::ImageSource(ImageSource::AlphaOption alphaOption, ImageSource::GammaAndColorProfileOption gammaAndColorProfileOption)
- : m_decoder(0)
+ImageSource::ImageSource(NativeImagePtr&& nativeImage)
+ : m_frameCache(ImageFrameCache::create(WTFMove(nativeImage)))
+{
+}
+
+ImageSource::ImageSource(Image* image, AlphaOption alphaOption, GammaAndColorProfileOption gammaAndColorProfileOption)
+ : m_frameCache(ImageFrameCache::create(image))
, m_alphaOption(alphaOption)
, m_gammaAndColorProfileOption(gammaAndColorProfileOption)
{
@@ -49,159 +59,154 @@ ImageSource::ImageSource(ImageSource::AlphaOption alphaOption, ImageSource::Gamm
ImageSource::~ImageSource()
{
- clear(true);
}
-void ImageSource::clear(bool destroyAll, size_t clearBeforeFrame, SharedBuffer* data, bool allDataReceived)
+void ImageSource::clearFrameBufferCache(size_t clearBeforeFrame)
{
- if (!destroyAll) {
- if (m_decoder)
- m_decoder->clearFrameBufferCache(clearBeforeFrame);
+ if (!isDecoderAvailable())
return;
- }
-
- delete m_decoder;
- m_decoder = 0;
- if (data)
- setData(data, allDataReceived);
+ m_decoder->clearFrameBufferCache(clearBeforeFrame);
}
-bool ImageSource::initialized() const
+void ImageSource::clear(SharedBuffer* data)
{
- return m_decoder;
+ m_decoder = nullptr;
+ m_frameCache->setDecoder(nullptr);
+ setData(data, isAllDataReceived());
}
-void ImageSource::setData(SharedBuffer* data, bool allDataReceived)
+bool ImageSource::ensureDecoderAvailable(SharedBuffer* data)
{
- // Make the decoder by sniffing the bytes.
- // This method will examine the data and instantiate an instance of the appropriate decoder plugin.
- // If insufficient bytes are available to determine the image type, no decoder plugin will be
- // made.
- if (!m_decoder) {
- m_decoder = static_cast<NativeImageDecoderPtr>(NativeImageDecoder::create(*data, m_alphaOption, m_gammaAndColorProfileOption));
-#if ENABLE(IMAGE_DECODER_DOWN_SAMPLING)
- if (m_decoder && s_maxPixelsPerDecodedImage)
- m_decoder->setMaxNumPixels(s_maxPixelsPerDecodedImage);
-#endif
- }
+ if (!data || isDecoderAvailable())
+ return true;
- if (m_decoder)
- m_decoder->setData(data, allDataReceived);
-}
+ m_decoder = ImageDecoder::create(*data, m_alphaOption, m_gammaAndColorProfileOption);
+ if (!isDecoderAvailable())
+ return false;
-String ImageSource::filenameExtension() const
-{
- return m_decoder ? m_decoder->filenameExtension() : String();
+ m_frameCache->setDecoder(m_decoder.get());
+ return true;
}
-bool ImageSource::isSizeAvailable()
+void ImageSource::setDecoderTargetContext(const GraphicsContext* targetContext)
{
- return m_decoder && m_decoder->isSizeAvailable();
+#if USE(DIRECT2D)
+ if (!isDecoderAvailable())
+ return;
+
+ if (targetContext)
+ m_decoder->setTargetContext(targetContext->platformContext());
+#else
+ UNUSED_PARAM(targetContext);
+#endif
}
-IntSize ImageSource::size(ImageOrientationDescription description) const
+void ImageSource::setData(SharedBuffer* data, bool allDataReceived)
{
- return frameSizeAtIndex(0, description);
+ if (!data || !ensureDecoderAvailable(data))
+ return;
+
+ m_decoder->setData(*data, allDataReceived);
}
-IntSize ImageSource::frameSizeAtIndex(size_t index, ImageOrientationDescription description) const
+bool ImageSource::dataChanged(SharedBuffer* data, bool allDataReceived)
{
- if (!m_decoder)
- return IntSize();
+ m_frameCache->destroyIncompleteDecodedData();
- IntSize size = m_decoder->frameSizeAtIndex(index);
- if ((description.respectImageOrientation() == RespectImageOrientation) && m_decoder->orientation().usesWidthAsHeight())
- return IntSize(size.height(), size.width());
+#if PLATFORM(IOS)
+ // FIXME: We should expose a setting to enable/disable progressive loading and make this
+ // code conditional on it. Then we can remove the PLATFORM(IOS)-guard.
+ static const double chunkLoadIntervals[] = {0, 1, 3, 6, 15};
+ double interval = chunkLoadIntervals[std::min(m_progressiveLoadChunkCount, static_cast<uint16_t>(4))];
- return size;
-}
+ bool needsUpdate = false;
-bool ImageSource::getHotSpot(IntPoint& hotSpot) const
-{
- return m_decoder ? m_decoder->hotSpot(hotSpot) : false;
-}
+ // The first time through, the chunk time will be 0 and the image will get an update.
+ if (currentTime() - m_progressiveLoadChunkTime > interval) {
+ needsUpdate = true;
+ m_progressiveLoadChunkTime = currentTime();
+ ASSERT(m_progressiveLoadChunkCount <= std::numeric_limits<uint16_t>::max());
+ ++m_progressiveLoadChunkCount;
+ }
-size_t ImageSource::bytesDecodedToDetermineProperties() const
-{
- return 0;
+ if (needsUpdate || allDataReceived)
+ setData(data, allDataReceived);
+#else
+ setData(data, allDataReceived);
+#endif
+
+ m_frameCache->clearMetadata();
+ if (!isSizeAvailable())
+ return false;
+
+ m_frameCache->growFrames();
+ return true;
}
-int ImageSource::repetitionCount()
+bool ImageSource::isAllDataReceived()
{
- return m_decoder ? m_decoder->repetitionCount() : cAnimationNone;
+ return isDecoderAvailable() ? m_decoder->isAllDataReceived() : m_frameCache->frameCount();
}
-size_t ImageSource::frameCount() const
+bool ImageSource::isAsyncDecodingRequired()
{
- return m_decoder ? m_decoder->frameCount() : 0;
+ // FIXME: figure out the best heuristic for enabling async image decoding.
+ return size().area() * sizeof(RGBA32) >= 100 * KB;
}
-PassNativeImagePtr ImageSource::createFrameAtIndex(size_t index, float* scale)
+SubsamplingLevel ImageSource::maximumSubsamplingLevel()
{
- UNUSED_PARAM(scale);
+ if (m_maximumSubsamplingLevel)
+ return m_maximumSubsamplingLevel.value();
- if (!m_decoder)
- return 0;
+ if (!isDecoderAvailable() || !m_decoder->frameAllowSubsamplingAtIndex(0))
+ return SubsamplingLevel::Default;
- ImageFrame* buffer = m_decoder->frameBufferAtIndex(index);
- if (!buffer || buffer->status() == ImageFrame::FrameEmpty)
- return 0;
+ // FIXME: this value was chosen to be appropriate for iOS since the image
+ // subsampling is only enabled by default on iOS. Choose a different value
+ // if image subsampling is enabled on other platform.
+ const int maximumImageAreaBeforeSubsampling = 5 * 1024 * 1024;
+ SubsamplingLevel level = SubsamplingLevel::First;
- // Zero-height images can cause problems for some ports. If we have an
- // empty image dimension, just bail.
- if (size().isEmpty())
- return 0;
+ for (; level < SubsamplingLevel::Last; ++level) {
+ if (frameSizeAtIndex(0, level).area().unsafeGet() < maximumImageAreaBeforeSubsampling)
+ break;
+ }
- // Return the buffer contents as a native image. For some ports, the data
- // is already in a native container, and this just increments its refcount.
- return buffer->asNewNativeImage();
+ m_maximumSubsamplingLevel = level;
+ return m_maximumSubsamplingLevel.value();
}
-float ImageSource::frameDurationAtIndex(size_t index)
+SubsamplingLevel ImageSource::subsamplingLevelForScale(float scale)
{
- if (!m_decoder)
- return 0;
+ if (!(scale > 0 && scale <= 1))
+ return SubsamplingLevel::Default;
- ImageFrame* buffer = m_decoder->frameBufferAtIndex(index);
- if (!buffer || buffer->status() == ImageFrame::FrameEmpty)
- return 0;
-
- // Many annoying ads specify a 0 duration to make an image flash as quickly as possible.
- // We follow Firefox's behavior and use a duration of 100 ms for any frames that specify
- // a duration of <= 10 ms. See <rdar://problem/7689300> and <http://webkit.org/b/36082>
- // for more information.
- const float duration = buffer->duration() / 1000.0f;
- if (duration < 0.011f)
- return 0.100f;
- return duration;
+ int result = std::ceil(std::log2(1 / scale));
+ return static_cast<SubsamplingLevel>(std::min(result, static_cast<int>(maximumSubsamplingLevel())));
}
-ImageOrientation ImageSource::orientationAtIndex(size_t) const
+NativeImagePtr ImageSource::createFrameImageAtIndex(size_t index, SubsamplingLevel subsamplingLevel)
{
- return m_decoder ? m_decoder->orientation() : DefaultImageOrientation;
+ return isDecoderAvailable() ? m_decoder->createFrameImageAtIndex(index, subsamplingLevel) : nullptr;
}
-bool ImageSource::frameHasAlphaAtIndex(size_t index)
+NativeImagePtr ImageSource::frameImageAtIndex(size_t index, const std::optional<SubsamplingLevel>& subsamplingLevel, const std::optional<IntSize>& sizeForDrawing, const GraphicsContext* targetContext)
{
- if (!m_decoder)
- return true;
- return m_decoder->frameHasAlphaAtIndex(index);
+ setDecoderTargetContext(targetContext);
+ return m_frameCache->frameImageAtIndex(index, subsamplingLevel, sizeForDrawing);
}
-bool ImageSource::frameIsCompleteAtIndex(size_t index)
+void ImageSource::dump(TextStream& ts)
{
- if (!m_decoder)
- return false;
-
- ImageFrame* buffer = m_decoder->frameBufferAtIndex(index);
- return buffer && buffer->status() == ImageFrame::FrameComplete;
-}
+ ts.dumpProperty("type", filenameExtension());
+ ts.dumpProperty("frame-count", frameCount());
+ ts.dumpProperty("repetitions", repetitionCount());
+ ts.dumpProperty("solid-color", singlePixelSolidColor());
-unsigned ImageSource::frameBytesAtIndex(size_t index) const
-{
- if (!m_decoder)
- return 0;
- return m_decoder->frameBytesAtIndex(index);
+ ImageOrientation orientation = frameOrientationAtIndex(0);
+ if (orientation != OriginTopLeft)
+ ts.dumpProperty("orientation", orientation);
}
}
diff --git a/Source/WebCore/platform/graphics/ImageSource.h b/Source/WebCore/platform/graphics/ImageSource.h
index 72e119a07..4ccf07973 100644
--- a/Source/WebCore/platform/graphics/ImageSource.h
+++ b/Source/WebCore/platform/graphics/ImageSource.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2004, 2005, 2006 Apple Computer, Inc. All rights reserved.
+ * Copyright (C) 2004-2008, 2010, 2012, 2014, 2016 Apple Inc. All rights reserved.
* Copyright (C) 2007-2008 Torch Mobile, Inc.
*
* Redistribution and use in source and binary forms, with or without
@@ -11,10 +11,10 @@
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
- * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
@@ -24,171 +24,105 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef ImageSource_h
-#define ImageSource_h
+#pragma once
+#include "ImageFrame.h"
+#include "ImageFrameCache.h"
#include "ImageOrientation.h"
-#include "NativeImagePtr.h"
-
+#include "IntPoint.h"
+#include "NativeImage.h"
+#include "TextStream.h"
#include <wtf/Forward.h>
#include <wtf/Noncopyable.h>
-#include <wtf/Vector.h>
-
-#if USE(CG)
-typedef struct CGImageSource* CGImageSourceRef;
-typedef const struct __CFData* CFDataRef;
-#endif
+#include <wtf/Optional.h>
namespace WebCore {
+class GraphicsContext;
+class ImageDecoder;
class ImageOrientation;
class IntPoint;
class IntSize;
class SharedBuffer;
-#if USE(CG)
-typedef CGImageSourceRef NativeImageDecoderPtr;
-#else
-class ImageDecoder;
-typedef ImageDecoder* NativeImageDecoderPtr;
-#endif
-
-#if USE(CG)
-#define NativeImageDecoder ImageDecoder
-#else
-typedef ImageDecoder NativeImageDecoder;
-#endif
-
-// Right now GIFs are the only recognized image format that supports animation.
-// The animation system and the constants below are designed with this in mind.
-// GIFs have an optional 16-bit unsigned loop count that describes how an
-// animated GIF should be cycled. If the loop count is absent, the animation
-// cycles once; if it is 0, the animation cycles infinitely; otherwise the
-// animation plays n + 1 cycles (where n is the specified loop count). If the
-// GIF decoder defaults to cAnimationLoopOnce in the absence of any loop count
-// and translates an explicit "0" loop count to cAnimationLoopInfinite, then we
-// get a couple of nice side effects:
-// * By making cAnimationLoopOnce be 0, we allow the animation cycling code in
-// BitmapImage.cpp to avoid special-casing it, and simply treat all
-// non-negative loop counts identically.
-// * By making the other two constants negative, we avoid conflicts with any
-// real loop count values.
-const int cAnimationLoopOnce = 0;
-const int cAnimationLoopInfinite = -1;
-const int cAnimationNone = -2;
-
class ImageSource {
WTF_MAKE_NONCOPYABLE(ImageSource);
+ friend class BitmapImage;
public:
- enum AlphaOption {
- AlphaPremultiplied,
- AlphaNotPremultiplied
- };
-
- enum GammaAndColorProfileOption {
- GammaAndColorProfileApplied,
- GammaAndColorProfileIgnored
- };
-
-#if USE(CG)
- enum ShouldSkipMetadata {
- DoNotSkipMetadata,
- SkipMetadata
- };
-#endif
-
- ImageSource(AlphaOption alphaOption = AlphaPremultiplied, GammaAndColorProfileOption gammaAndColorProfileOption = GammaAndColorProfileApplied);
+ ImageSource(NativeImagePtr&&);
+ ImageSource(Image*, AlphaOption = AlphaOption::Premultiplied, GammaAndColorProfileOption = GammaAndColorProfileOption::Applied);
~ImageSource();
- // Tells the ImageSource that the Image no longer cares about decoded frame
- // data -- at all (if |destroyAll| is true), or before frame
- // |clearBeforeFrame| (if |destroyAll| is false). The ImageSource should
- // delete cached decoded data for these frames where possible to keep memory
- // usage low. When |destroyAll| is true, the ImageSource should also reset
- // any local state so that decoding can begin again.
- //
- // Implementations that delete less than what's specified above waste
- // memory. Implementations that delete more may burn CPU re-decoding frames
- // that could otherwise have been cached, or encounter errors if they're
- // asked to decode frames they can't decode due to the loss of previous
- // decoded frames.
- //
- // Callers should not call clear(false, n) and subsequently call
- // createFrameAtIndex(m) with m < n, unless they first call clear(true).
- // This ensures that stateful ImageSources/decoders will work properly.
- //
- // The |data| and |allDataReceived| parameters should be supplied by callers
- // who set |destroyAll| to true if they wish to be able to continue using
- // the ImageSource. This way implementations which choose to destroy their
- // decoders in some cases can reconstruct them correctly.
- void clear(bool destroyAll,
- size_t clearBeforeFrame = 0,
- SharedBuffer* data = NULL,
- bool allDataReceived = false);
-
- bool initialized() const;
+ void destroyAllDecodedData() { m_frameCache->destroyAllDecodedData(); }
+ void destroyAllDecodedDataExcludeFrame(size_t excludeFrame) { m_frameCache->destroyAllDecodedDataExcludeFrame(excludeFrame); }
+ void destroyDecodedDataBeforeFrame(size_t beforeFrame) { m_frameCache->destroyDecodedDataBeforeFrame(beforeFrame); }
+ void clearFrameBufferCache(size_t);
+ void clear(SharedBuffer* data);
- void setData(SharedBuffer* data, bool allDataReceived);
- String filenameExtension() const;
-
- bool isSizeAvailable();
- IntSize size(ImageOrientationDescription = ImageOrientationDescription()) const;
- IntSize frameSizeAtIndex(size_t, ImageOrientationDescription = ImageOrientationDescription()) const;
+ bool ensureDecoderAvailable(SharedBuffer*);
+ bool isDecoderAvailable() const { return m_decoder.get(); }
-#if PLATFORM(IOS)
- IntSize originalSize(RespectImageOrientationEnum = DoNotRespectImageOrientation) const;
- bool isSubsampled() const { return m_baseSubsampling; }
-#endif
-
- bool getHotSpot(IntPoint&) const;
-
- size_t bytesDecodedToDetermineProperties() const;
-
- int repetitionCount();
-
- size_t frameCount() const;
+ void setData(SharedBuffer* data, bool allDataReceived);
+ bool dataChanged(SharedBuffer* data, bool allDataReceived);
+
+ unsigned decodedSize() const { return m_frameCache->decodedSize(); }
+ bool isAllDataReceived();
+
+ bool isAsyncDecodingRequired();
+ bool requestFrameAsyncDecodingAtIndex(size_t index, SubsamplingLevel subsamplingLevel, const IntSize& sizeForDrawing) { return m_frameCache->requestFrameAsyncDecodingAtIndex(index, subsamplingLevel, sizeForDrawing); }
+ bool hasDecodingQueue() const { return m_frameCache->hasDecodingQueue(); }
+ void stopAsyncDecodingQueue() { m_frameCache->stopAsyncDecodingQueue(); }
+
+ // Image metadata which is calculated by the decoder or can deduced by the case of the memory NativeImage.
+ bool isSizeAvailable() { return m_frameCache->isSizeAvailable(); }
+ size_t frameCount() { return m_frameCache->frameCount(); }
+ RepetitionCount repetitionCount() { return m_frameCache->repetitionCount(); }
+ String filenameExtension() { return m_frameCache->filenameExtension(); }
+ std::optional<IntPoint> hotSpot() { return m_frameCache->hotSpot(); }
+
+ // Image metadata which is calculated from the first ImageFrame.
+ IntSize size() { return m_frameCache->size(); }
+ IntSize sizeRespectingOrientation() { return m_frameCache->sizeRespectingOrientation(); }
+ Color singlePixelSolidColor() { return m_frameCache->singlePixelSolidColor(); }
+
+ // ImageFrame metadata which does not require caching the ImageFrame.
+ bool frameIsBeingDecodedAtIndex(size_t index, const std::optional<IntSize>& sizeForDrawing) { return m_frameCache->frameIsBeingDecodedAtIndex(index, sizeForDrawing); }
+ bool frameIsCompleteAtIndex(size_t index) { return m_frameCache->frameIsCompleteAtIndex(index); }
+ bool frameHasAlphaAtIndex(size_t index) { return m_frameCache->frameHasAlphaAtIndex(index); }
+ bool frameHasImageAtIndex(size_t index) { return m_frameCache->frameHasImageAtIndex(index); }
+ bool frameHasValidNativeImageAtIndex(size_t index, const std::optional<SubsamplingLevel>& subsamplingLevel, const std::optional<IntSize>& sizeForDrawing) { return m_frameCache->frameHasValidNativeImageAtIndex(index, subsamplingLevel, sizeForDrawing); }
+ bool frameHasDecodedNativeImage(size_t index) { return m_frameCache->frameHasDecodedNativeImage(index); }
+ SubsamplingLevel frameSubsamplingLevelAtIndex(size_t index) { return m_frameCache->frameSubsamplingLevelAtIndex(index); }
+
+ // ImageFrame metadata which forces caching or re-caching the ImageFrame.
+ IntSize frameSizeAtIndex(size_t index, SubsamplingLevel subsamplingLevel = SubsamplingLevel::Default) { return m_frameCache->frameSizeAtIndex(index, subsamplingLevel); }
+ unsigned frameBytesAtIndex(size_t index, SubsamplingLevel subsamplingLevel = SubsamplingLevel::Default) { return m_frameCache->frameBytesAtIndex(index, subsamplingLevel); }
+ float frameDurationAtIndex(size_t index) { return m_frameCache->frameDurationAtIndex(index); }
+ ImageOrientation frameOrientationAtIndex(size_t index) { return m_frameCache->frameOrientationAtIndex(index); }
+ NativeImagePtr frameImageAtIndex(size_t index, const std::optional<SubsamplingLevel>& = { }, const std::optional<IntSize>& sizeForDrawing = { }, const GraphicsContext* targetContext = nullptr);
+
+ SubsamplingLevel maximumSubsamplingLevel();
+ SubsamplingLevel subsamplingLevelForScale(float);
+ NativeImagePtr createFrameImageAtIndex(size_t, SubsamplingLevel = SubsamplingLevel::Default);
- // Callers should not call this after calling clear() with a higher index;
- // see comments on clear() above.
- PassNativeImagePtr createFrameAtIndex(size_t, float* scale = 0);
+private:
+ void dump(TextStream&);
- float frameDurationAtIndex(size_t);
- bool frameHasAlphaAtIndex(size_t); // Whether or not the frame actually used any alpha.
- bool frameIsCompleteAtIndex(size_t); // Whether or not the frame is completely decoded.
- ImageOrientation orientationAtIndex(size_t) const; // EXIF image orientation
+ void setDecoderTargetContext(const GraphicsContext*);
- // Return the number of bytes in the decoded frame. If the frame is not yet
- // decoded then return 0.
- unsigned frameBytesAtIndex(size_t) const;
+ Ref<ImageFrameCache> m_frameCache;
+ RefPtr<ImageDecoder> m_decoder;
-#if ENABLE(IMAGE_DECODER_DOWN_SAMPLING)
- static unsigned maxPixelsPerDecodedImage() { return s_maxPixelsPerDecodedImage; }
- static void setMaxPixelsPerDecodedImage(unsigned maxPixels) { s_maxPixelsPerDecodedImage = maxPixels; }
-#endif
+ std::optional<SubsamplingLevel> m_maximumSubsamplingLevel;
#if PLATFORM(IOS)
- static bool acceleratedImageDecodingEnabled() { return s_acceleratedImageDecoding; }
- static void setAcceleratedImageDecodingEnabled(bool flag) { s_acceleratedImageDecoding = flag; }
+ // FIXME: We should expose a setting to enable/disable progressive loading so that we can remove the PLATFORM(IOS)-guard.
+ double m_progressiveLoadChunkTime { 0 };
+ uint16_t m_progressiveLoadChunkCount { 0 };
#endif
-private:
- NativeImageDecoderPtr m_decoder;
-
-#if !USE(CG)
- AlphaOption m_alphaOption;
- GammaAndColorProfileOption m_gammaAndColorProfileOption;
-#endif
-#if ENABLE(IMAGE_DECODER_DOWN_SAMPLING)
- static unsigned s_maxPixelsPerDecodedImage;
-#endif
-#if PLATFORM(IOS)
- mutable int m_baseSubsampling;
- mutable bool m_isProgressive;
- CFDictionaryRef imageSourceOptions(ShouldSkipMetadata, int subsampling = 0) const;
- static bool s_acceleratedImageDecoding;
-#endif
+ AlphaOption m_alphaOption { AlphaOption::Premultiplied };
+ GammaAndColorProfileOption m_gammaAndColorProfileOption { GammaAndColorProfileOption::Applied };
};
}
-
-#endif
diff --git a/Source/WebCore/platform/graphics/InbandTextTrackPrivate.h b/Source/WebCore/platform/graphics/InbandTextTrackPrivate.h
index e7e9160a7..363bec272 100644
--- a/Source/WebCore/platform/graphics/InbandTextTrackPrivate.h
+++ b/Source/WebCore/platform/graphics/InbandTextTrackPrivate.h
@@ -10,10 +10,10 @@
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
- * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
@@ -35,6 +35,7 @@ namespace WebCore {
class InbandTextTrackPrivate : public TrackPrivateBase {
public:
enum CueFormat {
+ Data,
Generic,
WebVTT
};
@@ -42,7 +43,7 @@ public:
virtual ~InbandTextTrackPrivate() { }
void setClient(InbandTextTrackPrivateClient* client) { m_client = client; }
- virtual InbandTextTrackPrivateClient* client() const override { return m_client; }
+ InbandTextTrackPrivateClient* client() const override { return m_client; }
enum Mode {
Disabled,
@@ -68,9 +69,10 @@ public:
virtual bool isMainProgramContent() const { return true; }
virtual bool isEasyToRead() const { return false; }
virtual bool isDefault() const { return false; }
- virtual AtomicString label() const { return emptyAtom; }
- virtual AtomicString language() const { return emptyAtom; }
- virtual AtomicString id() const { return emptyAtom; }
+ AtomicString label() const override { return emptyAtom; }
+ AtomicString language() const override { return emptyAtom; }
+ AtomicString id() const override { return emptyAtom; }
+ virtual AtomicString inBandMetadataTrackDispatchType() const { return emptyAtom; }
virtual int textTrackIndex() const { return 0; }
diff --git a/Source/WebCore/platform/graphics/InbandTextTrackPrivateClient.h b/Source/WebCore/platform/graphics/InbandTextTrackPrivateClient.h
index 7999f5d4f..9a11079bb 100644
--- a/Source/WebCore/platform/graphics/InbandTextTrackPrivateClient.h
+++ b/Source/WebCore/platform/graphics/InbandTextTrackPrivateClient.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2012, 2013 Apple Inc. All rights reserved.
+ * Copyright (C) 2012-2017 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -10,10 +10,10 @@
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
- * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
@@ -23,35 +23,38 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef InbandTextTrackPrivateClient_h
-#define InbandTextTrackPrivateClient_h
+#pragma once
+
+#if ENABLE(VIDEO_TRACK)
#include "Color.h"
#include "TrackPrivateBase.h"
+#include <wtf/MediaTime.h>
-#if ENABLE(VIDEO_TRACK)
+#if ENABLE(DATACUE_VALUE)
+#include "SerializedPlatformRepresentation.h"
+#endif
namespace WebCore {
class InbandTextTrackPrivate;
+class ISOWebVTTCue;
class GenericCueData : public RefCounted<GenericCueData> {
public:
+ static Ref<GenericCueData> create() { return adoptRef(*new GenericCueData); }
- static PassRefPtr<GenericCueData> create() { return adoptRef(new GenericCueData()); }
- virtual ~GenericCueData() { }
-
- double startTime() const { return m_startTime; }
- void setStartTime(double startTime) { m_startTime = startTime; }
+ MediaTime startTime() const { return m_startTime; }
+ void setStartTime(const MediaTime& startTime) { m_startTime = startTime; }
- double endTime() const { return m_endTime; }
- void setEndTime(double endTime) { m_endTime = endTime; }
+ MediaTime endTime() const { return m_endTime; }
+ void setEndTime(const MediaTime& endTime) { m_endTime = endTime; }
- String id() const { return m_id; }
- void setId(String id) { m_id = id; }
+ const String& id() const { return m_id; }
+ void setId(const String& id) { m_id = id; }
- String content() const { return m_content; }
- void setContent(String content) { m_content = content; }
+ const String& content() const { return m_content; }
+ void setContent(const String& content) { m_content = content; }
double line() const { return m_line; }
void setLine(double line) { m_line = line; }
@@ -62,17 +65,12 @@ public:
double size() const { return m_size; }
void setSize(double size) { m_size = size; }
- enum Alignment {
- None,
- Start,
- Middle,
- End
- };
+ enum Alignment { None, Start, Middle, End };
Alignment align() const { return m_align; }
void setAlign(Alignment align) { m_align = align; }
- String fontName() const { return m_fontName; }
- void setFontName(String fontName) { m_fontName = fontName; }
+ const String& fontName() const { return m_fontName; }
+ void setFontName(const String& fontName) { m_fontName = fontName; }
double baseFontSize() const { return m_baseFontSize; }
void setBaseFontSize(double baseFontSize) { m_baseFontSize = baseFontSize; }
@@ -80,68 +78,92 @@ public:
double relativeFontSize() const { return m_relativeFontSize; }
void setRelativeFontSize(double relativeFontSize) { m_relativeFontSize = relativeFontSize; }
- Color foregroundColor() const { return m_foregroundColor; }
- void setForegroundColor(RGBA32 color) { m_foregroundColor.setRGB(color); }
+ const Color& foregroundColor() const { return m_foregroundColor; }
+ void setForegroundColor(const Color& color) { m_foregroundColor = color; }
- Color backgroundColor() const { return m_backgroundColor; }
- void setBackgroundColor(RGBA32 color) { m_backgroundColor.setRGB(color); }
-
- Color highlightColor() const { return m_highlightColor; }
- void setHighlightColor(RGBA32 color) { m_highlightColor.setRGB(color); }
-
- enum Status {
- Uninitialized,
- Partial,
- Complete,
- };
+ const Color& backgroundColor() const { return m_backgroundColor; }
+ void setBackgroundColor(const Color& color) { m_backgroundColor = color; }
+
+ const Color& highlightColor() const { return m_highlightColor; }
+ void setHighlightColor(const Color& color) { m_highlightColor = color; }
+
+ enum Status { Uninitialized, Partial, Complete };
Status status() { return m_status; }
void setStatus(Status status) { m_status = status; }
-
+
+ bool doesExtendCueData(const GenericCueData&) const;
+
private:
- GenericCueData()
- : m_startTime(0)
- , m_endTime(0)
- , m_line(-1)
- , m_position(-1)
- , m_size(-1)
- , m_align(None)
- , m_baseFontSize(0)
- , m_relativeFontSize(0)
- , m_status(Uninitialized)
- {
- }
-
- double m_startTime;
- double m_endTime;
+ GenericCueData() = default;
+
+ MediaTime m_startTime;
+ MediaTime m_endTime;
String m_id;
String m_content;
- double m_line;
- double m_position;
- double m_size;
- Alignment m_align;
+ double m_line { -1 };
+ double m_position { -1 };
+ double m_size { -1 };
+ Alignment m_align { None };
String m_fontName;
- double m_baseFontSize;
- double m_relativeFontSize;
+ double m_baseFontSize { 0 };
+ double m_relativeFontSize { 0 };
Color m_foregroundColor;
Color m_backgroundColor;
Color m_highlightColor;
- Status m_status;
+ Status m_status { Uninitialized };
};
-class WebVTTCueData;
-
+inline bool GenericCueData::doesExtendCueData(const GenericCueData& other) const
+{
+ if (m_relativeFontSize != other.m_relativeFontSize)
+ return false;
+ if (m_baseFontSize != other.m_baseFontSize)
+ return false;
+ if (m_position != other.m_position)
+ return false;
+ if (m_line != other.m_line)
+ return false;
+ if (m_size != other.m_size)
+ return false;
+ if (m_align != other.m_align)
+ return false;
+ if (m_foregroundColor != other.m_foregroundColor)
+ return false;
+ if (m_backgroundColor != other.m_backgroundColor)
+ return false;
+ if (m_highlightColor != other.m_highlightColor)
+ return false;
+ if (m_fontName != other.m_fontName)
+ return false;
+ if (m_id != other.m_id)
+ return false;
+ if (m_content != other.m_content)
+ return false;
+
+ return true;
+}
+
class InbandTextTrackPrivateClient : public TrackPrivateBaseClient {
public:
virtual ~InbandTextTrackPrivateClient() { }
- virtual void addGenericCue(InbandTextTrackPrivate*, PassRefPtr<GenericCueData>) = 0;
- virtual void updateGenericCue(InbandTextTrackPrivate*, GenericCueData*) = 0;
- virtual void removeGenericCue(InbandTextTrackPrivate*, GenericCueData*) = 0;
+ virtual void addDataCue(const MediaTime& start, const MediaTime& end, const void*, unsigned) = 0;
+
+#if ENABLE(DATACUE_VALUE)
+ virtual void addDataCue(const MediaTime& start, const MediaTime& end, Ref<SerializedPlatformRepresentation>&&, const String&) = 0;
+ virtual void updateDataCue(const MediaTime& start, const MediaTime& end, SerializedPlatformRepresentation&) = 0;
+ virtual void removeDataCue(const MediaTime& start, const MediaTime& end, SerializedPlatformRepresentation&) = 0;
+#endif
- virtual void parseWebVTTCueData(InbandTextTrackPrivate*, const char* data, unsigned length) = 0;
+ virtual void addGenericCue(GenericCueData&) = 0;
+ virtual void updateGenericCue(GenericCueData&) = 0;
+ virtual void removeGenericCue(GenericCueData&) = 0;
+
+ virtual void parseWebVTTFileHeader(String&&) { ASSERT_NOT_REACHED(); }
+ virtual void parseWebVTTCueData(const char* data, unsigned length) = 0;
+ virtual void parseWebVTTCueData(const ISOWebVTTCue&) = 0;
};
} // namespace WebCore
#endif
-#endif
diff --git a/Source/WebCore/platform/graphics/IntPoint.cpp b/Source/WebCore/platform/graphics/IntPoint.cpp
index b93405195..58df52627 100644
--- a/Source/WebCore/platform/graphics/IntPoint.cpp
+++ b/Source/WebCore/platform/graphics/IntPoint.cpp
@@ -10,10 +10,10 @@
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
- * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
@@ -26,13 +26,28 @@
#include "config.h"
#include "IntPoint.h"
-#include <wtf/PrintStream.h>
+#include "FloatPoint.h"
+#include "TextStream.h"
namespace WebCore {
-void IntPoint::dump(PrintStream& out) const
+IntPoint::IntPoint(const FloatPoint& p)
+ : m_x(clampToInteger(p.x()))
+ , m_y(clampToInteger(p.y()))
{
- out.printf("(%d, %d)", x(), y());
+}
+
+IntPoint IntPoint::constrainedBetween(const IntPoint& min, const IntPoint& max) const
+{
+ return {
+ std::max(min.x(), std::min(max.x(), m_x)),
+ std::max(min.y(), std::min(max.y(), m_y))
+ };
+}
+
+TextStream& operator<<(TextStream& ts, const IntPoint& p)
+{
+ return ts << "(" << p.x() << "," << p.y() << ")";
}
}
diff --git a/Source/WebCore/platform/graphics/IntPoint.h b/Source/WebCore/platform/graphics/IntPoint.h
index 275b73f1b..283f4e1e7 100644
--- a/Source/WebCore/platform/graphics/IntPoint.h
+++ b/Source/WebCore/platform/graphics/IntPoint.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2004, 2005, 2006 Apple Computer, Inc. All rights reserved.
+ * Copyright (C) 2004-2016 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -10,10 +10,10 @@
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
- * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
@@ -27,7 +27,11 @@
#define IntPoint_h
#include "IntSize.h"
-#include <wtf/MathExtras.h>
+#include <cmath>
+
+#if PLATFORM(MAC) && defined __OBJC__
+#import <Foundation/NSGeometry.h>
+#endif
#if USE(CG)
typedef struct CGPoint CGPoint;
@@ -47,21 +51,28 @@ typedef struct _NSPoint NSPoint;
#if PLATFORM(WIN)
typedef struct tagPOINT POINT;
typedef struct tagPOINTS POINTS;
-#elif PLATFORM(GTK)
-typedef struct _GdkPoint GdkPoint;
-#elif PLATFORM(EFL)
-typedef struct _Evas_Point Evas_Point;
+
+struct D2D_POINT_2U;
+typedef D2D_POINT_2U D2D1_POINT_2U;
+
+struct D2D_POINT_2F;
+typedef D2D_POINT_2F D2D1_POINT_2F;
#endif
namespace WebCore {
+class FloatPoint;
+class TextStream;
+
class IntPoint {
public:
IntPoint() : m_x(0), m_y(0) { }
IntPoint(int x, int y) : m_x(x), m_y(y) { }
- explicit IntPoint(const IntSize& size) : m_x(size.width()), m_y(size.height()) { }
+ WEBCORE_EXPORT explicit IntPoint(const IntSize& size) : m_x(size.width()), m_y(size.height()) { }
+ WEBCORE_EXPORT explicit IntPoint(const FloatPoint&); // don't do this implicitly since it's lossy
static IntPoint zero() { return IntPoint(); }
+ bool isZero() const { return !m_x && !m_y; }
int x() const { return m_x; }
int y() const { return m_y; }
@@ -77,19 +88,30 @@ public:
m_x = lroundf(static_cast<float>(m_x * sx));
m_y = lroundf(static_cast<float>(m_y * sy));
}
+
+ void scale(float scale)
+ {
+ this->scale(scale, scale);
+ }
IntPoint expandedTo(const IntPoint& other) const
{
- return IntPoint(m_x > other.m_x ? m_x : other.m_x,
- m_y > other.m_y ? m_y : other.m_y);
+ return {
+ m_x > other.m_x ? m_x : other.m_x,
+ m_y > other.m_y ? m_y : other.m_y
+ };
}
IntPoint shrunkTo(const IntPoint& other) const
{
- return IntPoint(m_x < other.m_x ? m_x : other.m_x,
- m_y < other.m_y ? m_y : other.m_y);
+ return {
+ m_x < other.m_x ? m_x : other.m_x,
+ m_y < other.m_y ? m_y : other.m_y
+ };
}
+ WEBCORE_EXPORT IntPoint constrainedBetween(const IntPoint& min, const IntPoint& max) const;
+
int distanceSquaredToPoint(const IntPoint&) const;
void clampNegativeToZero()
@@ -103,14 +125,14 @@ public:
}
#if USE(CG)
- explicit IntPoint(const CGPoint&); // don't do this implicitly since it's lossy
- operator CGPoint() const;
+ WEBCORE_EXPORT explicit IntPoint(const CGPoint&); // don't do this implicitly since it's lossy
+ WEBCORE_EXPORT operator CGPoint() const;
#endif
#if !PLATFORM(IOS)
#if OS(DARWIN) && !defined(NSGEOMETRY_TYPES_SAME_AS_CGGEOMETRY_TYPES)
- explicit IntPoint(const NSPoint&); // don't do this implicitly since it's lossy
- operator NSPoint() const;
+ WEBCORE_EXPORT explicit IntPoint(const NSPoint&); // don't do this implicitly since it's lossy
+ WEBCORE_EXPORT operator NSPoint() const;
#endif
#endif // !PLATFORM(IOS)
@@ -119,15 +141,12 @@ public:
operator POINT() const;
IntPoint(const POINTS&);
operator POINTS() const;
-#elif PLATFORM(GTK)
- IntPoint(const GdkPoint&);
- operator GdkPoint() const;
-#elif PLATFORM(EFL)
- explicit IntPoint(const Evas_Point&);
- operator Evas_Point() const;
-#endif
- void dump(PrintStream& out) const;
+ IntPoint(const D2D1_POINT_2U&);
+ explicit IntPoint(const D2D1_POINT_2F&); // Don't do this implicitly, since it's lossy.
+ operator D2D1_POINT_2F() const;
+ operator D2D1_POINT_2U() const;
+#endif
private:
int m_x, m_y;
@@ -190,6 +209,8 @@ inline int IntPoint::distanceSquaredToPoint(const IntPoint& point) const
return ((*this) - point).diagonalLengthSquared();
}
+WEBCORE_EXPORT TextStream& operator<<(TextStream&, const IntPoint&);
+
} // namespace WebCore
#endif // IntPoint_h
diff --git a/Source/WebCore/platform/graphics/IntRect.cpp b/Source/WebCore/platform/graphics/IntRect.cpp
index 0e8ff9785..f02181000 100644
--- a/Source/WebCore/platform/graphics/IntRect.cpp
+++ b/Source/WebCore/platform/graphics/IntRect.cpp
@@ -28,8 +28,8 @@
#include "FloatRect.h"
#include "LayoutRect.h"
+#include "TextStream.h"
#include <algorithm>
-#include <wtf/PrintStream.h>
namespace WebCore {
@@ -146,20 +146,12 @@ IntSize IntRect::differenceToPoint(const IntPoint& point) const
return IntSize(xdistance, ydistance);
}
-IntRect unionRect(const Vector<IntRect>& rects)
+TextStream& operator<<(TextStream& ts, const IntRect& r)
{
- IntRect result;
+ if (ts.hasFormattingFlag(TextStream::Formatting::SVGStyleRect))
+ return ts << "at (" << r.x() << "," << r.y() << ") size " << r.width() << "x" << r.height();
- size_t count = rects.size();
- for (size_t i = 0; i < count; ++i)
- result.unite(rects[i]);
-
- return result;
-}
-
-void IntRect::dump(PrintStream& out) const
-{
- out.print(location(), " ", size());
+ return ts << r.location() << " " << r.size();
}
} // namespace WebCore
diff --git a/Source/WebCore/platform/graphics/IntRect.h b/Source/WebCore/platform/graphics/IntRect.h
index d910a15be..b038b2b54 100644
--- a/Source/WebCore/platform/graphics/IntRect.h
+++ b/Source/WebCore/platform/graphics/IntRect.h
@@ -28,13 +28,12 @@
#include "IntPoint.h"
#include "LayoutUnit.h"
-#include <wtf/Vector.h>
#if USE(CG)
typedef struct CGRect CGRect;
#endif
-#if PLATFORM(MAC) && !PLATFORM(IOS)
+#if PLATFORM(MAC)
#ifdef NSGEOMETRY_TYPES_SAME_AS_CGGEOMETRY_TYPES
typedef struct CGRect NSRect;
#else
@@ -50,12 +49,12 @@ typedef struct _NSRect NSRect;
#if PLATFORM(WIN)
typedef struct tagRECT RECT;
-#elif PLATFORM(GTK)
-#ifdef GTK_API_VERSION_2
-typedef struct _GdkRectangle GdkRectangle;
-#endif
-#elif PLATFORM(EFL)
-typedef struct _Eina_Rectangle Eina_Rectangle;
+
+struct D2D_RECT_U;
+typedef D2D_RECT_U D2D1_RECT_U;
+
+struct D2D_RECT_F;
+typedef D2D_RECT_F D2D1_RECT_F;
#endif
#if USE(CAIRO)
@@ -66,6 +65,7 @@ namespace WebCore {
class FloatRect;
class LayoutRect;
+class TextStream;
class IntRect {
WTF_MAKE_FAST_ALLOCATED;
@@ -76,8 +76,8 @@ public:
IntRect(int x, int y, int width, int height)
: m_location(IntPoint(x, y)), m_size(IntSize(width, height)) { }
- explicit IntRect(const FloatRect&); // don't do this implicitly since it's lossy
- explicit IntRect(const LayoutRect&); // don't do this implicitly since it's lossy
+ WEBCORE_EXPORT explicit IntRect(const FloatRect&); // don't do this implicitly since it's lossy
+ WEBCORE_EXPORT explicit IntRect(const LayoutRect&); // don't do this implicitly since it's lossy
IntPoint location() const { return m_location; }
IntSize size() const { return m_size; }
@@ -92,6 +92,9 @@ public:
int width() const { return m_size.width(); }
int height() const { return m_size.height(); }
+ template <typename T = WTF::CrashOnOverflow>
+ Checked<unsigned, T> area() const { return m_size.area<T>(); }
+
void setX(int x) { m_location.setX(x); }
void setY(int y) { m_location.setY(y); }
void setWidth(int width) { m_size.setWidth(width); }
@@ -140,8 +143,8 @@ public:
IntPoint minXMaxYCorner() const { return IntPoint(m_location.x(), m_location.y() + m_size.height()); } // typically bottomLeft
IntPoint maxXMaxYCorner() const { return IntPoint(m_location.x() + m_size.width(), m_location.y() + m_size.height()); } // typically bottomRight
- bool intersects(const IntRect&) const;
- bool contains(const IntRect&) const;
+ WEBCORE_EXPORT bool intersects(const IntRect&) const;
+ WEBCORE_EXPORT bool contains(const IntRect&) const;
// This checks to see if the rect contains x,y in the traditional sense.
// Equivalent to checking if the rect contains a 1x1 rect below and to the right of (px,py).
@@ -149,8 +152,8 @@ public:
{ return px >= x() && px < maxX() && py >= y() && py < maxY(); }
bool contains(const IntPoint& point) const { return contains(point.x(), point.y()); }
- void intersect(const IntRect&);
- void unite(const IntRect&);
+ WEBCORE_EXPORT void intersect(const IntRect&);
+ WEBCORE_EXPORT void unite(const IntRect&);
void uniteIfNonZero(const IntRect&);
void inflateX(int dx)
@@ -164,7 +167,7 @@ public:
m_size.setHeight(m_size.height() + dy + dy);
}
void inflate(int d) { inflateX(d); inflateY(d); }
- void scale(float s);
+ WEBCORE_EXPORT void scale(float s);
IntSize differenceToPoint(const IntPoint&) const;
int distanceSquaredToPoint(const IntPoint& p) const { return differenceToPoint(p).diagonalLengthSquared(); }
@@ -174,14 +177,10 @@ public:
#if PLATFORM(WIN)
IntRect(const RECT&);
operator RECT() const;
-#elif PLATFORM(GTK)
-#ifdef GTK_API_VERSION_2
- IntRect(const GdkRectangle&);
- operator GdkRectangle() const;
-#endif
-#elif PLATFORM(EFL)
- explicit IntRect(const Eina_Rectangle&);
- operator Eina_Rectangle() const;
+ explicit IntRect(const D2D1_RECT_F&);
+ IntRect(const D2D1_RECT_U&);
+ operator D2D1_RECT_F() const;
+ operator D2D1_RECT_U() const;
#endif
#if USE(CAIRO)
@@ -190,19 +189,12 @@ public:
#endif
#if USE(CG)
- operator CGRect() const;
+ WEBCORE_EXPORT operator CGRect() const;
#endif
-#if !PLATFORM(IOS)
-#if (PLATFORM(MAC) && !defined(NSGEOMETRY_TYPES_SAME_AS_CGGEOMETRY_TYPES))
- operator NSRect() const;
+#if PLATFORM(MAC) && !defined(NSGEOMETRY_TYPES_SAME_AS_CGGEOMETRY_TYPES)
+ WEBCORE_EXPORT operator NSRect() const;
#endif
-#endif // !PLATFORM(IOS)
-
- void dump(PrintStream& out) const;
-
- static IntRect infiniteRect();
- bool isInfinite() const;
private:
IntPoint m_location;
@@ -223,8 +215,6 @@ inline IntRect unionRect(const IntRect& a, const IntRect& b)
return c;
}
-IntRect unionRect(const Vector<IntRect>&);
-
inline bool operator==(const IntRect& a, const IntRect& b)
{
return a.location() == b.location() && a.size() == b.size();
@@ -235,17 +225,6 @@ inline bool operator!=(const IntRect& a, const IntRect& b)
return a.location() != b.location() || a.size() != b.size();
}
-inline IntRect IntRect::infiniteRect()
-{
- static IntRect infiniteRect(-LayoutUnit::max() / 2, -LayoutUnit::max() / 2, LayoutUnit::max(), LayoutUnit::max());
- return infiniteRect;
-}
-
-inline bool IntRect::isInfinite() const
-{
- return *this == infiniteRect();
-}
-
inline IntRect& operator-=(IntRect& r, const IntPoint& offset)
{
r.move(-offset.x(), -offset.y());
@@ -259,14 +238,14 @@ inline IntRect operator-(const IntRect& r, const IntPoint& offset)
}
#if USE(CG)
-IntRect enclosingIntRect(const CGRect&);
+WEBCORE_EXPORT IntRect enclosingIntRect(const CGRect&);
#endif
-#if !PLATFORM(IOS)
-#if (PLATFORM(MAC) && !defined(NSGEOMETRY_TYPES_SAME_AS_CGGEOMETRY_TYPES))
-IntRect enclosingIntRect(const NSRect&);
+#if PLATFORM(MAC) && !defined(NSGEOMETRY_TYPES_SAME_AS_CGGEOMETRY_TYPES)
+WEBCORE_EXPORT IntRect enclosingIntRect(const NSRect&);
#endif
-#endif // !PLATFORM(IOS)
+
+WEBCORE_EXPORT TextStream& operator<<(TextStream&, const IntRect&);
} // namespace WebCore
diff --git a/Source/WebCore/platform/graphics/IntRectExtent.h b/Source/WebCore/platform/graphics/IntRectExtent.h
index 6517f8ae0..611739054 100644
--- a/Source/WebCore/platform/graphics/IntRectExtent.h
+++ b/Source/WebCore/platform/graphics/IntRectExtent.h
@@ -30,8 +30,6 @@
#ifndef IntRectExtent_h
#define IntRectExtent_h
-#if ENABLE(CSS_FILTERS)
-
#include "LayoutRect.h"
namespace WebCore {
@@ -107,6 +105,4 @@ inline void operator+=(IntRectExtent& a, const IntRectExtent& b)
} // namespace WebCore
-#endif // ENABLE(CSS_FILTERS)
-
#endif // IntRectExtent_h
diff --git a/Source/WebCore/platform/graphics/IntRectHash.h b/Source/WebCore/platform/graphics/IntRectHash.h
new file mode 100644
index 000000000..8d6520a16
--- /dev/null
+++ b/Source/WebCore/platform/graphics/IntRectHash.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2011 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef IntRectHash_h
+#define IntRectHash_h
+
+#include "IntPointHash.h"
+#include "IntRect.h"
+#include "IntSizeHash.h"
+#include <wtf/HashMap.h>
+#include <wtf/HashSet.h>
+
+namespace WTF {
+
+template<> struct IntHash<WebCore::IntRect> {
+ static unsigned hash(const WebCore::IntRect& key)
+ {
+ return pairIntHash(DefaultHash<WebCore::IntPoint>::Hash::hash(key.location()), DefaultHash<WebCore::IntSize>::Hash::hash(key.size()));
+ }
+ static bool equal(const WebCore::IntRect& a, const WebCore::IntRect& b)
+ {
+ return DefaultHash<WebCore::IntPoint>::Hash::equal(a.location(), b.location()) && DefaultHash<WebCore::IntSize>::Hash::equal(a.size(), b.size());
+ }
+ static const bool safeToCompareToEmptyOrDeleted = true;
+};
+template<> struct DefaultHash<WebCore::IntRect> { typedef IntHash<WebCore::IntRect> Hash; };
+
+template<> struct HashTraits<WebCore::IntRect> : GenericHashTraits<WebCore::IntRect> {
+ static const bool emptyValueIsZero = true;
+ static const bool needsDestruction = false;
+ static void constructDeletedValue(WebCore::IntRect& slot) { new (NotNull, &slot) WebCore::IntRect(-1, -1, -1, -1); }
+ static bool isDeletedValue(const WebCore::IntRect& value) { return value.x() == -1 && value.y() == -1 && value.width() == -1 && value.height() == -1; }
+};
+
+}
+
+#endif
diff --git a/Source/WebCore/platform/graphics/IntSize.cpp b/Source/WebCore/platform/graphics/IntSize.cpp
index 025dca9ca..a3dccb337 100644
--- a/Source/WebCore/platform/graphics/IntSize.cpp
+++ b/Source/WebCore/platform/graphics/IntSize.cpp
@@ -10,10 +10,10 @@
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
- * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
@@ -26,13 +26,28 @@
#include "config.h"
#include "IntSize.h"
-#include <wtf/PrintStream.h>
+#include "FloatSize.h"
+#include "TextStream.h"
namespace WebCore {
-void IntSize::dump(PrintStream& out) const
+IntSize::IntSize(const FloatSize& s)
+ : m_width(clampToInteger(s.width()))
+ , m_height(clampToInteger(s.height()))
{
- out.printf("(%d x %d)", width(), height());
+}
+
+IntSize IntSize::constrainedBetween(const IntSize& min, const IntSize& max) const
+{
+ return {
+ std::max(min.width(), std::min(max.width(), m_width)),
+ std::max(min.height(), std::min(max.height(), m_height))
+ };
+}
+
+TextStream& operator<<(TextStream& ts, const IntSize& size)
+{
+ return ts << "width=" << size.width() << " height=" << size.height();
}
}
diff --git a/Source/WebCore/platform/graphics/IntSize.h b/Source/WebCore/platform/graphics/IntSize.h
index 3f16a862c..d39b65085 100644
--- a/Source/WebCore/platform/graphics/IntSize.h
+++ b/Source/WebCore/platform/graphics/IntSize.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003, 2004, 2005, 2006 Apple Computer, Inc. All rights reserved.
+ * Copyright (C) 2003-2016 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -10,10 +10,10 @@
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
- * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
@@ -26,14 +26,18 @@
#ifndef IntSize_h
#define IntSize_h
+#include "PlatformExportMacros.h"
#include <algorithm>
-#include <wtf/PrintStream.h>
+
+#if PLATFORM(MAC) && defined __OBJC__
+#import <Foundation/NSGeometry.h>
+#endif
#if USE(CG)
typedef struct CGSize CGSize;
#endif
-#if PLATFORM(MAC) && !PLATFORM(IOS)
+#if PLATFORM(MAC)
#ifdef NSGEOMETRY_TYPES_SAME_AS_CGGEOMETRY_TYPES
typedef struct CGSize NSSize;
#else
@@ -49,14 +53,24 @@ typedef struct _NSSize NSSize;
#if PLATFORM(WIN)
typedef struct tagSIZE SIZE;
+
+struct D2D_SIZE_U;
+typedef D2D_SIZE_U D2D1_SIZE_U;
+
+struct D2D_SIZE_F;
+typedef D2D_SIZE_F D2D1_SIZE_F;
#endif
namespace WebCore {
+class FloatSize;
+class TextStream;
+
class IntSize {
public:
IntSize() : m_width(0), m_height(0) { }
IntSize(int width, int height) : m_width(width), m_height(height) { }
+ WEBCORE_EXPORT explicit IntSize(const FloatSize&); // don't do this implicitly since it's lossy
int width() const { return m_width; }
int height() const { return m_height; }
@@ -75,6 +89,12 @@ public:
m_height += height;
}
+ void contract(int width, int height)
+ {
+ m_width -= width;
+ m_height -= height;
+ }
+
void scale(float widthScale, float heightScale)
{
m_width = static_cast<int>(static_cast<float>(m_width) * widthScale);
@@ -109,9 +129,17 @@ public:
m_height = minimumSize.height();
}
- int area() const
+ WEBCORE_EXPORT IntSize constrainedBetween(const IntSize& min, const IntSize& max) const;
+
+ template <typename T = WTF::CrashOnOverflow>
+ Checked<unsigned, T> area() const
{
- return m_width * m_height;
+ return Checked<unsigned, T>(abs(m_width)) * abs(m_height);
+ }
+
+ size_t unclampedArea() const
+ {
+ return static_cast<size_t>(abs(m_width)) * abs(m_height);
}
int diagonalLengthSquared() const
@@ -125,24 +153,24 @@ public:
}
#if USE(CG)
- explicit IntSize(const CGSize&); // don't do this implicitly since it's lossy
- operator CGSize() const;
+ WEBCORE_EXPORT explicit IntSize(const CGSize&); // don't do this implicitly since it's lossy
+ WEBCORE_EXPORT operator CGSize() const;
#endif
-#if !PLATFORM(IOS)
#if PLATFORM(MAC) && !defined(NSGEOMETRY_TYPES_SAME_AS_CGGEOMETRY_TYPES)
- explicit IntSize(const NSSize &); // don't do this implicitly since it's lossy
- operator NSSize() const;
+ WEBCORE_EXPORT explicit IntSize(const NSSize &); // don't do this implicitly since it's lossy
+ WEBCORE_EXPORT operator NSSize() const;
#endif
-#endif // !PLATFORM(IOS)
#if PLATFORM(WIN)
IntSize(const SIZE&);
operator SIZE() const;
+ IntSize(const D2D1_SIZE_U&);
+ explicit IntSize(const D2D1_SIZE_F&); // don't do this implicitly since it's lossy;
+ operator D2D1_SIZE_U() const;
+ operator D2D1_SIZE_F() const;
#endif
- void dump(PrintStream& out) const;
-
private:
int m_width, m_height;
};
@@ -186,6 +214,8 @@ inline bool operator!=(const IntSize& a, const IntSize& b)
return a.width() != b.width() || a.height() != b.height();
}
+WEBCORE_EXPORT TextStream& operator<<(TextStream&, const IntSize&);
+
} // namespace WebCore
#endif // IntSize_h
diff --git a/Source/WebCore/platform/graphics/Latin1TextIterator.h b/Source/WebCore/platform/graphics/Latin1TextIterator.h
index c40189bfe..431d8ff20 100644
--- a/Source/WebCore/platform/graphics/Latin1TextIterator.h
+++ b/Source/WebCore/platform/graphics/Latin1TextIterator.h
@@ -28,18 +28,18 @@ namespace WebCore {
class Latin1TextIterator {
public:
- // The passed in LChar pointer starts at 'currentCharacter'. The iterator operates on the range [currentCharacter, lastCharacter].
- // 'endCharacter' denotes the maximum length of the UChar array, which might exceed 'lastCharacter'.
- Latin1TextIterator(const LChar* characters, int currentCharacter, int lastCharacter, int /*endCharacter*/)
+ // The passed in LChar pointer starts at 'currentIndex'. The iterator operates on the range [currentIndex, lastIndex].
+ // 'endCharacter' denotes the maximum length of the UChar array, which might exceed 'lastIndex'.
+ Latin1TextIterator(const LChar* characters, unsigned currentIndex, unsigned lastIndex, unsigned /*endCharacter*/)
: m_characters(characters)
- , m_currentCharacter(currentCharacter)
- , m_lastCharacter(lastCharacter)
+ , m_currentIndex(currentIndex)
+ , m_lastIndex(lastIndex)
{
}
bool consume(UChar32& character, unsigned& clusterLength)
{
- if (m_currentCharacter >= m_lastCharacter)
+ if (m_currentIndex >= m_lastIndex)
return false;
character = *m_characters;
@@ -50,16 +50,16 @@ public:
void advance(unsigned advanceLength)
{
m_characters += advanceLength;
- m_currentCharacter += advanceLength;
+ m_currentIndex += advanceLength;
}
- int currentCharacter() const { return m_currentCharacter; }
+ unsigned currentIndex() const { return m_currentIndex; }
const LChar* characters() const { return m_characters; }
private:
const LChar* m_characters;
- int m_currentCharacter;
- int m_lastCharacter;
+ unsigned m_currentIndex;
+ unsigned m_lastIndex;
};
}
diff --git a/Source/WebCore/platform/graphics/LayoutBoxExtent.cpp b/Source/WebCore/platform/graphics/LayoutBoxExtent.cpp
deleted file mode 100644
index fc1c86365..000000000
--- a/Source/WebCore/platform/graphics/LayoutBoxExtent.cpp
+++ /dev/null
@@ -1,196 +0,0 @@
-/*
- * Copyright (c) 2012, Google Inc. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are
- * met:
- *
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above
- * copyright notice, this list of conditions and the following disclaimer
- * in the documentation and/or other materials provided with the
- * distribution.
- * * Neither the name of Google Inc. nor the names of its
- * contributors may be used to endorse or promote products derived from
- * this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#include "config.h"
-#include "LayoutBoxExtent.h"
-
-namespace WebCore {
-
-LayoutUnit LayoutBoxExtent::logicalTop(WritingMode writingMode) const
-{
- return isHorizontalWritingMode(writingMode) ? m_top : m_left;
-}
-
-LayoutUnit LayoutBoxExtent::logicalBottom(WritingMode writingMode) const
-{
- return isHorizontalWritingMode(writingMode) ? m_bottom : m_right;
-}
-
-LayoutUnit LayoutBoxExtent::logicalLeft(WritingMode writingMode) const
-{
- return isHorizontalWritingMode(writingMode) ? m_left : m_top;
-}
-
-LayoutUnit LayoutBoxExtent::logicalRight(WritingMode writingMode) const
-{
- return isHorizontalWritingMode(writingMode) ? m_right : m_bottom;
-}
-
-LayoutUnit LayoutBoxExtent::before(WritingMode writingMode) const
-{
- switch (writingMode) {
- case TopToBottomWritingMode:
- return m_top;
- case BottomToTopWritingMode:
- return m_bottom;
- case LeftToRightWritingMode:
- return m_left;
- case RightToLeftWritingMode:
- return m_right;
- }
- ASSERT_NOT_REACHED();
- return m_top;
-}
-
-LayoutUnit LayoutBoxExtent::after(WritingMode writingMode) const
-{
- switch (writingMode) {
- case TopToBottomWritingMode:
- return m_bottom;
- case BottomToTopWritingMode:
- return m_top;
- case LeftToRightWritingMode:
- return m_right;
- case RightToLeftWritingMode:
- return m_left;
- }
- ASSERT_NOT_REACHED();
- return m_bottom;
-}
-
-LayoutUnit LayoutBoxExtent::start(WritingMode writingMode, TextDirection direction) const
-{
- if (isHorizontalWritingMode(writingMode))
- return isLeftToRightDirection(direction) ? m_left : m_right;
- return isLeftToRightDirection(direction) ? m_top : m_bottom;
-}
-
-LayoutUnit LayoutBoxExtent::end(WritingMode writingMode, TextDirection direction) const
-{
- if (isHorizontalWritingMode(writingMode))
- return isLeftToRightDirection(direction) ? m_right : m_left;
- return isLeftToRightDirection(direction) ? m_bottom : m_top;
-}
-
-void LayoutBoxExtent::setBefore(WritingMode writingMode, LayoutUnit value)
-{
- switch (writingMode) {
- case TopToBottomWritingMode:
- m_top = value;
- break;
- case BottomToTopWritingMode:
- m_bottom = value;
- break;
- case LeftToRightWritingMode:
- m_left = value;
- break;
- case RightToLeftWritingMode:
- m_right = value;
- break;
- default:
- ASSERT_NOT_REACHED();
- m_top = value;
- }
-}
-
-void LayoutBoxExtent::setAfter(WritingMode writingMode, LayoutUnit value)
-{
- switch (writingMode) {
- case TopToBottomWritingMode:
- m_bottom = value;
- break;
- case BottomToTopWritingMode:
- m_top = value;
- break;
- case LeftToRightWritingMode:
- m_right = value;
- break;
- case RightToLeftWritingMode:
- m_left = value;
- break;
- default:
- ASSERT_NOT_REACHED();
- m_bottom = value;
- }
-}
-
-void LayoutBoxExtent::setStart(WritingMode writingMode, TextDirection direction, LayoutUnit value)
-{
- if (isHorizontalWritingMode(writingMode)) {
- if (isLeftToRightDirection(direction))
- m_left = value;
- else
- m_right = value;
- } else {
- if (isLeftToRightDirection(direction))
- m_top = value;
- else
- m_bottom = value;
- }
-}
-
-void LayoutBoxExtent::setEnd(WritingMode writingMode, TextDirection direction, LayoutUnit value)
-{
- if (isHorizontalWritingMode(writingMode)) {
- if (isLeftToRightDirection(direction))
- m_right = value;
- else
- m_left = value;
- } else {
- if (isLeftToRightDirection(direction))
- m_bottom = value;
- else
- m_top = value;
- }
-}
-
-LayoutUnit& LayoutBoxExtent::mutableLogicalLeft(WritingMode writingMode)
-{
- return isHorizontalWritingMode(writingMode) ? m_left : m_top;
-}
-
-LayoutUnit& LayoutBoxExtent::mutableLogicalRight(WritingMode writingMode)
-{
- return isHorizontalWritingMode(writingMode) ? m_right : m_bottom;
-}
-
-LayoutUnit& LayoutBoxExtent::mutableBefore(WritingMode writingMode)
-{
- return isHorizontalWritingMode(writingMode) ? (isFlippedBlocksWritingMode(writingMode) ? m_bottom : m_top) :
- (isFlippedBlocksWritingMode(writingMode) ? m_right: m_left);
-}
-
-LayoutUnit& LayoutBoxExtent::mutableAfter(WritingMode writingMode)
-{
- return isHorizontalWritingMode(writingMode) ? (isFlippedBlocksWritingMode(writingMode) ? m_top : m_bottom) :
- (isFlippedBlocksWritingMode(writingMode) ? m_left: m_right);
-}
-
-} // namespace WebCore
diff --git a/Source/WebCore/platform/graphics/LayoutBoxExtent.h b/Source/WebCore/platform/graphics/LayoutBoxExtent.h
deleted file mode 100644
index 1c57275b7..000000000
--- a/Source/WebCore/platform/graphics/LayoutBoxExtent.h
+++ /dev/null
@@ -1,86 +0,0 @@
-/*
- * Copyright (c) 2012, Google Inc. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are
- * met:
- *
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above
- * copyright notice, this list of conditions and the following disclaimer
- * in the documentation and/or other materials provided with the
- * distribution.
- * * Neither the name of Google Inc. nor the names of its
- * contributors may be used to endorse or promote products derived from
- * this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#ifndef LayoutBoxExtent_h
-#define LayoutBoxExtent_h
-
-#include "LayoutUnit.h"
-#include "TextDirection.h"
-#include "WritingMode.h"
-
-namespace WebCore {
-
-class LayoutBoxExtent {
-public:
- LayoutBoxExtent() : m_top(0), m_right(0), m_bottom(0), m_left(0) { }
- LayoutBoxExtent(LayoutUnit top, LayoutUnit right, LayoutUnit bottom, LayoutUnit left)
- : m_top(top), m_right(right), m_bottom(bottom), m_left(left) { }
-
- inline LayoutUnit top() const { return m_top; }
- inline LayoutUnit right() const { return m_right; }
- inline LayoutUnit bottom() const { return m_bottom; }
- inline LayoutUnit left() const { return m_left; }
-
- inline void setTop(LayoutUnit value) { m_top = value; }
- inline void setRight(LayoutUnit value) { m_right = value; }
- inline void setBottom(LayoutUnit value) { m_bottom = value; }
- inline void setLeft(LayoutUnit value) { m_left = value; }
-
- LayoutUnit logicalTop(WritingMode) const;
- LayoutUnit logicalBottom(WritingMode) const;
- LayoutUnit logicalLeft(WritingMode) const;
- LayoutUnit logicalRight(WritingMode) const;
-
- LayoutUnit before(WritingMode) const;
- LayoutUnit after(WritingMode) const;
- LayoutUnit start(WritingMode, TextDirection) const;
- LayoutUnit end(WritingMode, TextDirection) const;
-
- void setBefore(WritingMode, LayoutUnit);
- void setAfter(WritingMode, LayoutUnit);
- void setStart(WritingMode, TextDirection, LayoutUnit);
- void setEnd(WritingMode, TextDirection, LayoutUnit);
-
- LayoutUnit& mutableLogicalLeft(WritingMode);
- LayoutUnit& mutableLogicalRight(WritingMode);
-
- LayoutUnit& mutableBefore(WritingMode);
- LayoutUnit& mutableAfter(WritingMode);
-
-private:
- LayoutUnit m_top;
- LayoutUnit m_right;
- LayoutUnit m_bottom;
- LayoutUnit m_left;
-};
-
-} // namespace WebCore
-
-#endif // LayoutBoxExtent_h
diff --git a/Source/WebCore/platform/graphics/LayoutPoint.cpp b/Source/WebCore/platform/graphics/LayoutPoint.cpp
new file mode 100644
index 000000000..eeb0159ad
--- /dev/null
+++ b/Source/WebCore/platform/graphics/LayoutPoint.cpp
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2015 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "LayoutPoint.h"
+
+#include "TextStream.h"
+
+namespace WebCore {
+
+LayoutPoint LayoutPoint::constrainedBetween(const LayoutPoint& min, const LayoutPoint& max) const
+{
+ return {
+ std::max(min.x(), std::min(max.x(), m_x)),
+ std::max(min.y(), std::min(max.y(), m_y))
+ };
+}
+
+TextStream& operator<<(TextStream& ts, const LayoutPoint& p)
+{
+ if (ts.hasFormattingFlag(TextStream::Formatting::LayoutUnitsAsIntegers))
+ return ts << "(" << p.x().toInt() << "," << p.y().toInt() << ")";
+
+ return ts << "(" << p.x().toFloat() << "," << p.y().toFloat() << ")";
+}
+
+} // namespace WebCore
diff --git a/Source/WebCore/platform/graphics/LayoutPoint.h b/Source/WebCore/platform/graphics/LayoutPoint.h
index 78ecd922e..78ed0c5b4 100644
--- a/Source/WebCore/platform/graphics/LayoutPoint.h
+++ b/Source/WebCore/platform/graphics/LayoutPoint.h
@@ -33,7 +33,6 @@
#include "FloatPoint.h"
#include "LayoutSize.h"
-#include <wtf/MathExtras.h>
namespace WebCore {
@@ -56,20 +55,29 @@ public:
void move(const LayoutSize& s) { move(s.width(), s.height()); }
void moveBy(const LayoutPoint& offset) { move(offset.x(), offset.y()); }
void move(LayoutUnit dx, LayoutUnit dy) { m_x += dx; m_y += dy; }
+
+ void scale(float s)
+ {
+ m_x *= s;
+ m_y *= s;
+ }
+
void scale(float sx, float sy)
{
m_x *= sx;
m_y *= sy;
}
-
+
+ LayoutPoint constrainedBetween(const LayoutPoint& min, const LayoutPoint& max) const;
+
LayoutPoint expandedTo(const LayoutPoint& other) const
{
- return LayoutPoint(std::max(m_x, other.m_x), std::max(m_y, other.m_y));
+ return { std::max(m_x, other.m_x), std::max(m_y, other.m_y) };
}
LayoutPoint shrunkTo(const LayoutPoint& other) const
{
- return LayoutPoint(std::min(m_x, other.m_x), std::min(m_y, other.m_y));
+ return { std::min(m_x, other.m_x), std::min(m_y, other.m_y) };
}
void clampNegativeToZero()
@@ -79,15 +87,15 @@ public:
LayoutPoint transposedPoint() const
{
- return LayoutPoint(m_y, m_x);
+ return { m_y, m_x };
}
LayoutPoint fraction() const
{
- return LayoutPoint(m_x.fraction(), m_y.fraction());
+ return { m_x.fraction(), m_y.fraction() };
}
- operator FloatPoint() const { return FloatPoint(m_x, m_y); }
+ operator FloatPoint() const { return { m_x, m_y }; }
private:
LayoutUnit m_x, m_y;
@@ -140,19 +148,14 @@ inline bool operator!=(const LayoutPoint& a, const LayoutPoint& b)
return a.x() != b.x() || a.y() != b.y();
}
-inline LayoutPoint toPoint(const LayoutSize& size)
+inline LayoutPoint toLayoutPoint(const LayoutSize& size)
{
return LayoutPoint(size.width(), size.height());
}
-inline LayoutPoint toLayoutPoint(const LayoutSize& p)
+inline LayoutSize toLayoutSize(const LayoutPoint& point)
{
- return LayoutPoint(p.width(), p.height());
-}
-
-inline LayoutSize toSize(const LayoutPoint& a)
-{
- return LayoutSize(a.x(), a.y());
+ return LayoutSize(point.x(), point.y());
}
inline IntPoint flooredIntPoint(const LayoutPoint& point)
@@ -167,7 +170,7 @@ inline IntPoint roundedIntPoint(const LayoutPoint& point)
inline IntPoint roundedIntPoint(const LayoutSize& size)
{
- return IntPoint(size.width().round(), size.height().round());
+ return roundedIntPoint(toLayoutPoint(size));
}
inline IntPoint ceiledIntPoint(const LayoutPoint& point)
@@ -185,30 +188,40 @@ inline LayoutPoint ceiledLayoutPoint(const FloatPoint& p)
return LayoutPoint(LayoutUnit::fromFloatCeil(p.x()), LayoutUnit::fromFloatCeil(p.y()));
}
-inline IntSize pixelSnappedIntSize(const LayoutSize& s, const LayoutPoint& p)
+inline IntSize snappedIntSize(const LayoutSize& size, const LayoutPoint& location)
+{
+ auto snap = [] (LayoutUnit a, LayoutUnit b) {
+ LayoutUnit fraction = b.fraction();
+ return roundToInt(fraction + a) - roundToInt(fraction);
+ };
+ return IntSize(snap(size.width(), location.x()), snap(size.height(), location.y()));
+}
+
+inline FloatPoint roundPointToDevicePixels(const LayoutPoint& point, float pixelSnappingFactor, bool directionalRoundingToRight = true, bool directionalRoundingToBottom = true)
{
- return IntSize(snapSizeToPixel(s.width(), p.x()), snapSizeToPixel(s.height(), p.y()));
+ return FloatPoint(roundToDevicePixel(point.x(), pixelSnappingFactor, !directionalRoundingToRight), roundToDevicePixel(point.y(), pixelSnappingFactor, !directionalRoundingToBottom));
}
-inline LayoutPoint roundedLayoutPoint(const FloatPoint& p)
+inline FloatPoint floorPointToDevicePixels(const LayoutPoint& point, float pixelSnappingFactor)
{
-#if ENABLE(SUBPIXEL_LAYOUT)
- return LayoutPoint(p);
-#else
- return roundedIntPoint(p);
-#endif
+ return FloatPoint(floorToDevicePixel(point.x(), pixelSnappingFactor), floorToDevicePixel(point.y(), pixelSnappingFactor));
}
-inline LayoutSize toLayoutSize(const LayoutPoint& p)
+inline FloatPoint ceilPointToDevicePixels(const LayoutPoint& point, float pixelSnappingFactor)
{
- return LayoutSize(p.x(), p.y());
+ return FloatPoint(ceilToDevicePixel(point.x(), pixelSnappingFactor), ceilToDevicePixel(point.y(), pixelSnappingFactor));
}
-inline LayoutPoint flooredLayoutPoint(const FloatSize& s)
+inline FloatSize snapSizeToDevicePixel(const LayoutSize& size, const LayoutPoint& location, float pixelSnappingFactor)
{
- return flooredLayoutPoint(FloatPoint(s));
+ auto snap = [&] (LayoutUnit a, LayoutUnit b) {
+ LayoutUnit fraction = b.fraction();
+ return roundToDevicePixel(fraction + a, pixelSnappingFactor) - roundToDevicePixel(fraction, pixelSnappingFactor);
+ };
+ return FloatSize(snap(size.width(), location.x()), snap(size.height(), location.y()));
}
+TextStream& operator<<(TextStream&, const LayoutPoint&);
} // namespace WebCore
diff --git a/Source/WebCore/platform/graphics/LayoutRect.cpp b/Source/WebCore/platform/graphics/LayoutRect.cpp
index dce0e12db..71b7c62c3 100644
--- a/Source/WebCore/platform/graphics/LayoutRect.cpp
+++ b/Source/WebCore/platform/graphics/LayoutRect.cpp
@@ -31,6 +31,7 @@
#include "config.h"
#include "LayoutRect.h"
+#include "TextStream.h"
#include <algorithm>
namespace WebCore {
@@ -87,6 +88,27 @@ void LayoutRect::unite(const LayoutRect& other)
m_size = newMaxPoint - newLocation;
}
+bool LayoutRect::checkedUnite(const LayoutRect& other)
+{
+ if (other.isEmpty())
+ return true;
+ if (isEmpty()) {
+ *this = other;
+ return true;
+ }
+ if (!isMaxXMaxYRepresentable() || !other.isMaxXMaxYRepresentable())
+ return false;
+ FloatPoint topLeft = FloatPoint(std::min<float>(x(), other.x()), std::min<float>(y(), other.y()));
+ FloatPoint bottomRight = FloatPoint(std::max<float>(maxX(), other.maxX()), std::max<float>(maxY(), other.maxY()));
+ FloatSize size = bottomRight - topLeft;
+
+ if (size.width() >= LayoutUnit::nearlyMax() || size.height() >= LayoutUnit::nearlyMax())
+ return false;
+ m_location = LayoutPoint(topLeft);
+ m_size = LayoutSize(size);
+ return true;
+}
+
void LayoutRect::uniteIfNonZero(const LayoutRect& other)
{
// Handle empty special cases first.
@@ -104,14 +126,15 @@ void LayoutRect::uniteIfNonZero(const LayoutRect& other)
m_size = newMaxPoint - newLocation;
}
-void LayoutRect::scale(float s)
+void LayoutRect::scale(float scaleValue)
{
- m_location.scale(s, s);
- m_size.scale(s);
+ scale(scaleValue, scaleValue);
}
void LayoutRect::scale(float xScale, float yScale)
{
+ if (isInfinite())
+ return;
m_location.scale(xScale, yScale);
m_size.scale(xScale, yScale);
}
@@ -129,22 +152,35 @@ LayoutRect unionRect(const Vector<LayoutRect>& rects)
IntRect enclosingIntRect(const LayoutRect& rect)
{
+ // Empty rects with fractional x, y values turn into non-empty rects when converting to enclosing.
+ // We need to ensure that empty rects stay empty after the conversion, because the selection code expects them to be empty.
IntPoint location = flooredIntPoint(rect.minXMinYCorner());
- IntPoint maxPoint = ceiledIntPoint(rect.maxXMaxYCorner());
-
+ IntPoint maxPoint = IntPoint(rect.width() ? rect.maxX().ceil() : location.x(), rect.height() ? rect.maxY().ceil() : location.y());
return IntRect(location, maxPoint - location);
}
LayoutRect enclosingLayoutRect(const FloatRect& rect)
{
-#if ENABLE(SUBPIXEL_LAYOUT)
LayoutPoint location = flooredLayoutPoint(rect.minXMinYCorner());
LayoutPoint maxPoint = ceiledLayoutPoint(rect.maxXMaxYCorner());
return LayoutRect(location, maxPoint - location);
-#else
- return enclosingIntRect(rect);
-#endif
+}
+
+FloatRect encloseRectToDevicePixels(const LayoutRect& rect, float pixelSnappingFactor)
+{
+ FloatPoint location = floorPointToDevicePixels(rect.minXMinYCorner(), pixelSnappingFactor);
+ FloatPoint maxPoint = ceilPointToDevicePixels(rect.maxXMaxYCorner(), pixelSnappingFactor);
+
+ return FloatRect(location, maxPoint - location);
+}
+
+TextStream& operator<<(TextStream& ts, const LayoutRect& r)
+{
+ if (ts.hasFormattingFlag(TextStream::Formatting::LayoutUnitsAsIntegers))
+ return ts << snappedIntRect(r);
+
+ return ts << FloatRect(r);
}
} // namespace WebCore
diff --git a/Source/WebCore/platform/graphics/LayoutRect.h b/Source/WebCore/platform/graphics/LayoutRect.h
index 66a19bdf1..7442e2fbb 100644
--- a/Source/WebCore/platform/graphics/LayoutRect.h
+++ b/Source/WebCore/platform/graphics/LayoutRect.h
@@ -33,12 +33,14 @@
#include "FloatRect.h"
#include "IntRect.h"
-#include "LayoutBoxExtent.h"
#include "LayoutPoint.h"
+#include "LengthBox.h"
#include <wtf/Vector.h>
namespace WebCore {
+class TextStream;
+
class LayoutRect {
public:
LayoutRect() { }
@@ -46,18 +48,17 @@ public:
: m_location(location), m_size(size) { }
LayoutRect(LayoutUnit x, LayoutUnit y, LayoutUnit width, LayoutUnit height)
: m_location(LayoutPoint(x, y)), m_size(LayoutSize(width, height)) { }
+ LayoutRect(const LayoutPoint& topLeft, const LayoutPoint& bottomRight)
+ : m_location(topLeft), m_size(LayoutSize(bottomRight.x() - topLeft.x(), bottomRight.y() - topLeft.y())) { }
LayoutRect(const FloatPoint& location, const FloatSize& size)
: m_location(location), m_size(size) { }
LayoutRect(const IntRect& rect) : m_location(rect.location()), m_size(rect.size()) { }
-
- explicit LayoutRect(const FloatRect&); // don't do this implicitly since it's lossy
+
+ WEBCORE_EXPORT explicit LayoutRect(const FloatRect&); // don't do this implicitly since it's lossy
LayoutPoint location() const { return m_location; }
LayoutSize size() const { return m_size; }
- IntPoint pixelSnappedLocation() const { return roundedIntPoint(m_location); }
- IntSize pixelSnappedSize() const { return IntSize(snapSizeToPixel(m_size.width(), m_location.x()), snapSizeToPixel(m_size.height(), m_location.y())); }
-
void setLocation(const LayoutPoint& location) { m_location = location; }
void setSize(const LayoutSize& size) { m_size = size; }
@@ -68,13 +69,6 @@ public:
LayoutUnit width() const { return m_size.width(); }
LayoutUnit height() const { return m_size.height(); }
- int pixelSnappedX() const { return x().round(); }
- int pixelSnappedY() const { return y().round(); }
- int pixelSnappedWidth() const { return snapSizeToPixel(width(), x()); }
- int pixelSnappedHeight() const { return snapSizeToPixel(height(), y()); }
- int pixelSnappedMaxX() const { return (m_location.x() + m_size.width()).round(); }
- int pixelSnappedMaxY() const { return (m_location.y() + m_size.height()).round(); }
-
void setX(LayoutUnit x) { m_location.setX(x); }
void setY(LayoutUnit y) { m_location.setY(y); }
void setWidth(LayoutUnit width) { m_size.setWidth(width); }
@@ -132,9 +126,16 @@ public:
LayoutPoint maxXMinYCorner() const { return LayoutPoint(m_location.x() + m_size.width(), m_location.y()); } // typically topRight
LayoutPoint minXMaxYCorner() const { return LayoutPoint(m_location.x(), m_location.y() + m_size.height()); } // typically bottomLeft
LayoutPoint maxXMaxYCorner() const { return LayoutPoint(m_location.x() + m_size.width(), m_location.y() + m_size.height()); } // typically bottomRight
+ bool isMaxXMaxYRepresentable() const
+ {
+ FloatRect rect = *this;
+ float maxX = rect.maxX();
+ float maxY = rect.maxY();
+ return maxX > LayoutUnit::nearlyMin() && maxX < LayoutUnit::nearlyMax() && maxY > LayoutUnit::nearlyMin() && maxY < LayoutUnit::nearlyMax();
+ }
bool intersects(const LayoutRect&) const;
- bool contains(const LayoutRect&) const;
+ WEBCORE_EXPORT bool contains(const LayoutRect&) const;
// This checks to see if the rect contains x,y in the traditional sense.
// Equivalent to checking if the rect contains a 1x1 rect below and to the right of (px,py).
@@ -143,8 +144,9 @@ public:
bool contains(const LayoutPoint& point) const { return contains(point.x(), point.y()); }
void intersect(const LayoutRect&);
- void unite(const LayoutRect&);
+ WEBCORE_EXPORT void unite(const LayoutRect&);
void uniteIfNonZero(const LayoutRect&);
+ bool checkedUnite(const LayoutRect&);
void inflateX(LayoutUnit dx)
{
@@ -157,10 +159,11 @@ public:
m_size.setHeight(m_size.height() + dy + dy);
}
void inflate(LayoutUnit d) { inflateX(d); inflateY(d); }
- void scale(float s);
+ WEBCORE_EXPORT void scale(float);
void scale(float xScale, float yScale);
LayoutRect transposedRect() const { return LayoutRect(m_location.transposedPoint(), m_size.transposedSize()); }
+ bool isInfinite() const { return *this == LayoutRect::infiniteRect(); }
static LayoutRect infiniteRect()
{
@@ -201,36 +204,51 @@ inline bool operator!=(const LayoutRect& a, const LayoutRect& b)
return a.location() != b.location() || a.size() != b.size();
}
-inline IntRect pixelSnappedIntRect(const LayoutRect& rect)
+// Integral snapping functions.
+inline IntRect snappedIntRect(const LayoutRect& rect)
{
-#if ENABLE(SUBPIXEL_LAYOUT)
- return IntRect(roundedIntPoint(rect.location()), IntSize(snapSizeToPixel(rect.width(), rect.x()),
- snapSizeToPixel(rect.height(), rect.y())));
+ return IntRect(roundedIntPoint(rect.location()), snappedIntSize(rect.size(), rect.location()));
+}
-#else
- return IntRect(rect);
-#endif
+inline IntRect snappedIntRect(LayoutUnit left, LayoutUnit top, LayoutUnit width, LayoutUnit height)
+{
+ return IntRect(IntPoint(left.round(), top.round()), snappedIntSize(LayoutSize(width, height), LayoutPoint(left, top)));
}
-IntRect enclosingIntRect(const LayoutRect&);
-LayoutRect enclosingLayoutRect(const FloatRect&);
+inline IntRect snappedIntRect(LayoutPoint location, LayoutSize size)
+{
+ return IntRect(roundedIntPoint(location), snappedIntSize(size, location));
+}
+WEBCORE_EXPORT IntRect enclosingIntRect(const LayoutRect&);
+WEBCORE_EXPORT LayoutRect enclosingLayoutRect(const FloatRect&);
-inline IntRect pixelSnappedIntRect(LayoutUnit left, LayoutUnit top, LayoutUnit width, LayoutUnit height)
+// Device pixel snapping functions.
+inline FloatRect snapRectToDevicePixels(const LayoutRect& rect, float pixelSnappingFactor)
{
- return IntRect(left.round(), top.round(), snapSizeToPixel(width, left), snapSizeToPixel(height, top));
+ return FloatRect(FloatPoint(roundToDevicePixel(rect.x(), pixelSnappingFactor), roundToDevicePixel(rect.y(), pixelSnappingFactor)), snapSizeToDevicePixel(rect.size(), rect.location(), pixelSnappingFactor));
}
-inline IntRect pixelSnappedIntRectFromEdges(LayoutUnit left, LayoutUnit top, LayoutUnit right, LayoutUnit bottom)
+inline FloatRect snapRectToDevicePixels(LayoutUnit x, LayoutUnit y, LayoutUnit width, LayoutUnit height, float pixelSnappingFactor)
{
- return IntRect(left.round(), top.round(), snapSizeToPixel(right - left, left), snapSizeToPixel(bottom - top, top));
+ return snapRectToDevicePixels(LayoutRect(x, y, width, height), pixelSnappingFactor);
}
-inline IntRect pixelSnappedIntRect(LayoutPoint location, LayoutSize size)
+// FIXME: This needs to take vertical centering into account too.
+inline FloatRect snapRectToDevicePixelsWithWritingDirection(const LayoutRect& rect, float deviceScaleFactor, bool ltr)
{
- return IntRect(roundedIntPoint(location), pixelSnappedIntSize(size, location));
+ if (!ltr) {
+ FloatPoint snappedTopRight = roundPointToDevicePixels(rect.maxXMinYCorner(), deviceScaleFactor, ltr);
+ FloatSize snappedSize = snapSizeToDevicePixel(rect.size(), rect.maxXMinYCorner(), deviceScaleFactor);
+ return FloatRect(snappedTopRight.x() - snappedSize.width(), snappedTopRight.y(), snappedSize.width(), snappedSize.height());
+ }
+ return snapRectToDevicePixels(rect, deviceScaleFactor);
}
+FloatRect encloseRectToDevicePixels(const LayoutRect&, float pixelSnappingFactor);
+
+TextStream& operator<<(TextStream&, const LayoutRect&);
+
} // namespace WebCore
#endif // LayoutRect_h
diff --git a/Source/WebCore/platform/graphics/cairo/DrawingBufferCairo.cpp b/Source/WebCore/platform/graphics/LayoutSize.cpp
index 738e49381..1f5e01df2 100644
--- a/Source/WebCore/platform/graphics/cairo/DrawingBufferCairo.cpp
+++ b/Source/WebCore/platform/graphics/LayoutSize.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2010 Apple Inc. All rights reserved.
+ * Copyright (C) 2015 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -13,7 +13,7 @@
* THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
@@ -24,27 +24,23 @@
*/
#include "config.h"
+#include "LayoutSize.h"
-#if ENABLE(ACCELERATED_2D_CANVAS) || USE(3D_GRAPHICS)
-
-#include "DrawingBuffer.h"
-
-#include "Extensions3D.h"
+#include "TextStream.h"
namespace WebCore {
-#if USE(ACCELERATED_COMPOSITING)
-
-unsigned DrawingBuffer::frontColorBuffer() const
+LayoutSize LayoutSize::constrainedBetween(const LayoutSize& min, const LayoutSize& max) const
{
- return colorBuffer();
+ return {
+ std::max(min.width(), std::min(max.width(), m_width)),
+ std::max(min.height(), std::min(max.height(), m_height))
+ };
}
-void DrawingBuffer::paintCompositedResultsToCanvas(ImageBuffer*)
+TextStream& operator<<(TextStream& ts, const LayoutSize& size)
{
-}
-#endif
-
+ return ts << "width=" << size.width().toFloat() << " height=" << size.height().toFloat();
}
-#endif
+} // namespace WebCore
diff --git a/Source/WebCore/platform/graphics/LayoutSize.h b/Source/WebCore/platform/graphics/LayoutSize.h
index f2d09eee7..c11df04e6 100644
--- a/Source/WebCore/platform/graphics/LayoutSize.h
+++ b/Source/WebCore/platform/graphics/LayoutSize.h
@@ -38,6 +38,7 @@
namespace WebCore {
class LayoutPoint;
+class TextStream;
enum AspectRatioFit {
AspectRatioFitShrink,
@@ -86,6 +87,8 @@ public:
m_width *= widthScale;
m_height *= heightScale;
}
+
+ LayoutSize constrainedBetween(const LayoutSize& min, const LayoutSize& max) const;
LayoutSize expandedTo(const LayoutSize& other) const
{
@@ -183,15 +186,13 @@ inline IntSize roundedIntSize(const LayoutSize& s)
return IntSize(s.width().round(), s.height().round());
}
-inline LayoutSize roundedLayoutSize(const FloatSize& s)
+inline FloatSize floorSizeToDevicePixels(const LayoutSize& size, float pixelSnappingFactor)
{
-#if ENABLE(SUBPIXEL_LAYOUT)
- return LayoutSize(s);
-#else
- return roundedIntSize(s);
-#endif
+ return FloatSize(floorToDevicePixel(size.width(), pixelSnappingFactor), floorToDevicePixel(size.height(), pixelSnappingFactor));
}
+TextStream& operator<<(TextStream&, const LayoutSize&);
+
} // namespace WebCore
#endif // LayoutSize_h
diff --git a/Source/WebCore/platform/graphics/LegacyCDMSession.h b/Source/WebCore/platform/graphics/LegacyCDMSession.h
new file mode 100644
index 000000000..8546c712f
--- /dev/null
+++ b/Source/WebCore/platform/graphics/LegacyCDMSession.h
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2014 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#if ENABLE(LEGACY_ENCRYPTED_MEDIA)
+
+#include <runtime/Uint8Array.h>
+#include <wtf/Forward.h>
+
+namespace WebCore {
+
+class CDMSessionClient {
+public:
+ virtual ~CDMSessionClient() { }
+ virtual void sendMessage(Uint8Array*, String destinationURL) = 0;
+
+ enum {
+ MediaKeyErrorUnknown = 1,
+ MediaKeyErrorClient,
+ MediaKeyErrorService,
+ MediaKeyErrorOutput,
+ MediaKeyErrorHardwareChange,
+ MediaKeyErrorDomain,
+ };
+ typedef unsigned short MediaKeyErrorCode;
+ virtual void sendError(MediaKeyErrorCode, uint32_t systemCode) = 0;
+
+ virtual String mediaKeysStorageDirectory() const = 0;
+};
+
+enum CDMSessionType {
+ CDMSessionTypeUnknown,
+ CDMSessionTypeClearKey,
+ CDMSessionTypeAVFoundationObjC,
+ CDMSessionTypeAVStreamSession,
+ CDMSessionTypeAVContentKeySession,
+};
+
+class CDMSession {
+public:
+ virtual ~CDMSession() { }
+
+ virtual CDMSessionType type() { return CDMSessionTypeUnknown; }
+ virtual void setClient(CDMSessionClient*) = 0;
+ virtual const String& sessionId() const = 0;
+ virtual RefPtr<Uint8Array> generateKeyRequest(const String& mimeType, Uint8Array* initData, String& destinationURL, unsigned short& errorCode, uint32_t& systemCode) = 0;
+ virtual void releaseKeys() = 0;
+ virtual bool update(Uint8Array*, RefPtr<Uint8Array>& nextMessage, unsigned short& errorCode, uint32_t& systemCode) = 0;
+ virtual RefPtr<ArrayBuffer> cachedKeyForKeyID(const String&) const { return nullptr; }
+};
+
+}
+
+#endif // ENABLE(LEGACY_ENCRYPTED_MEDIA)
diff --git a/Source/WebCore/platform/graphics/MediaPlaybackTarget.h b/Source/WebCore/platform/graphics/MediaPlaybackTarget.h
new file mode 100644
index 000000000..1619aff37
--- /dev/null
+++ b/Source/WebCore/platform/graphics/MediaPlaybackTarget.h
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2015 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef MediaPlaybackTarget_h
+#define MediaPlaybackTarget_h
+
+#if ENABLE(WIRELESS_PLAYBACK_TARGET)
+
+#include "MediaPlaybackTargetContext.h"
+#include <wtf/NeverDestroyed.h>
+#include <wtf/RefCounted.h>
+
+namespace WebCore {
+
+static const MediaPlaybackTargetContext& noMediaPlaybackTargetContext()
+{
+ static NeverDestroyed<MediaPlaybackTargetContext> context;
+ return context;
+}
+
+class MediaPlaybackTarget : public RefCounted<MediaPlaybackTarget> {
+public:
+ virtual ~MediaPlaybackTarget() { }
+
+ enum TargetType {
+ None,
+ AVFoundation,
+ Mock,
+ };
+ virtual TargetType targetType() const { return None; }
+
+ virtual const MediaPlaybackTargetContext& targetContext() const { return noMediaPlaybackTargetContext(); }
+ virtual bool hasActiveRoute() const { return false; }
+ virtual String deviceName() const { return emptyString(); }
+
+protected:
+ MediaPlaybackTarget() { }
+};
+
+}
+
+#endif // ENABLE(WIRELESS_PLAYBACK_TARGET)
+
+#endif
diff --git a/Source/WebCore/platform/graphics/MediaPlaybackTargetClient.h b/Source/WebCore/platform/graphics/MediaPlaybackTargetClient.h
new file mode 100644
index 000000000..a30421ea6
--- /dev/null
+++ b/Source/WebCore/platform/graphics/MediaPlaybackTargetClient.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2015 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef MediaPlaybackTargetClient_h
+#define MediaPlaybackTargetClient_h
+
+#if ENABLE(WIRELESS_PLAYBACK_TARGET)
+
+#include "MediaPlaybackTarget.h"
+#include "MediaProducer.h"
+
+namespace WebCore {
+
+class MediaPlaybackTarget;
+
+class MediaPlaybackTargetClient {
+public:
+ virtual ~MediaPlaybackTargetClient() { }
+
+ virtual void setPlaybackTarget(Ref<MediaPlaybackTarget>&&) = 0;
+ virtual void externalOutputDeviceAvailableDidChange(bool) = 0;
+ virtual void setShouldPlayToPlaybackTarget(bool) = 0;
+};
+
+} // namespace WebCore
+
+#endif // ENABLE(WIRELESS_PLAYBACK_TARGET)
+
+#endif // MediaPlaybackTargetClient_h
diff --git a/Source/WebCore/platform/graphics/MediaPlaybackTargetContext.h b/Source/WebCore/platform/graphics/MediaPlaybackTargetContext.h
new file mode 100644
index 000000000..1ce4a3ec3
--- /dev/null
+++ b/Source/WebCore/platform/graphics/MediaPlaybackTargetContext.h
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2015 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef MediaPlaybackTargetContext_h
+#define MediaPlaybackTargetContext_h
+
+#if ENABLE(WIRELESS_PLAYBACK_TARGET)
+
+#include <wtf/text/WTFString.h>
+
+OBJC_CLASS AVOutputContext;
+
+#if PLATFORM(COCOA)
+OBJC_CLASS NSKeyedArchiver;
+OBJC_CLASS NSKeyedUnarchiver;
+#endif
+
+namespace WebCore {
+
+class MediaPlaybackTargetContext {
+public:
+ enum Type : int32_t {
+ None,
+ AVOutputContextType,
+ MockType,
+ };
+
+ enum ContextState {
+ Unknown = 0,
+ OutputDeviceUnavailable = 1,
+ OutputDeviceAvailable = 2,
+ };
+ typedef unsigned State;
+
+ MediaPlaybackTargetContext()
+ : m_type(None)
+ {
+ }
+
+ MediaPlaybackTargetContext(AVOutputContext *outputContext)
+ : m_type(AVOutputContextType)
+ , m_outputContext(outputContext)
+ {
+ }
+
+ MediaPlaybackTargetContext(const String& name, State state)
+ : m_type(MockType)
+ , m_name(name)
+ , m_state(state)
+ {
+ }
+
+ Type type() const { return m_type; }
+
+ const String& mockDeviceName() const
+ {
+ ASSERT(m_type == MockType);
+ return m_name;
+ }
+
+ State mockState() const
+ {
+ ASSERT(m_type == MockType);
+ return m_state;
+ }
+
+ AVOutputContext *avOutputContext() const
+ {
+ ASSERT(m_type == AVOutputContextType);
+ return m_outputContext;
+ }
+
+ bool encodingRequiresPlatformData() const { return m_type == AVOutputContextType; }
+
+private:
+ Type m_type { None };
+ AVOutputContext *m_outputContext { nullptr };
+ String m_name;
+ State m_state { Unknown };
+};
+
+}
+
+#endif // ENABLE(WIRELESS_PLAYBACK_TARGET)
+
+#endif // MediaPlaybackTargetContext
diff --git a/Source/WebCore/platform/graphics/MediaPlaybackTargetPicker.cpp b/Source/WebCore/platform/graphics/MediaPlaybackTargetPicker.cpp
new file mode 100644
index 000000000..4931686b3
--- /dev/null
+++ b/Source/WebCore/platform/graphics/MediaPlaybackTargetPicker.cpp
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2015 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "MediaPlaybackTargetPicker.h"
+
+#if ENABLE(WIRELESS_PLAYBACK_TARGET)
+
+#include "Logging.h"
+#include "MediaPlaybackTarget.h"
+
+namespace WebCore {
+
+static const double pendingActionInterval = 1.0 / 10.0;
+
+MediaPlaybackTargetPicker::MediaPlaybackTargetPicker(Client& client)
+ : m_client(&client)
+ , m_pendingActionTimer(RunLoop::main(), this, &MediaPlaybackTargetPicker::pendingActionTimerFired)
+{
+}
+
+MediaPlaybackTargetPicker::~MediaPlaybackTargetPicker()
+{
+ m_pendingActionTimer.stop();
+ m_client = nullptr;
+}
+
+void MediaPlaybackTargetPicker::pendingActionTimerFired()
+{
+ LOG(Media, "MediaPlaybackTargetPicker::pendingActionTimerFired - flags = 0x%x", m_pendingActionFlags);
+
+ PendingActionFlags pendingActions = m_pendingActionFlags;
+ m_pendingActionFlags = 0;
+
+ if (pendingActions & CurrentDeviceDidChange)
+ m_client->setPlaybackTarget(playbackTarget());
+
+ if (pendingActions & OutputDeviceAvailabilityChanged)
+ m_client->externalOutputDeviceAvailableDidChange(externalOutputDeviceAvailable());
+}
+
+void MediaPlaybackTargetPicker::addPendingAction(PendingActionFlags action)
+{
+ if (!m_client)
+ return;
+
+ m_pendingActionFlags |= action;
+ m_pendingActionTimer.startOneShot(pendingActionInterval);
+}
+
+void MediaPlaybackTargetPicker::showPlaybackTargetPicker(const FloatRect&, bool)
+{
+ ASSERT_NOT_REACHED();
+}
+
+void MediaPlaybackTargetPicker::startingMonitoringPlaybackTargets()
+{
+ ASSERT_NOT_REACHED();
+}
+
+void MediaPlaybackTargetPicker::stopMonitoringPlaybackTargets()
+{
+ ASSERT_NOT_REACHED();
+}
+
+void MediaPlaybackTargetPicker::invalidatePlaybackTargets()
+{
+ ASSERT_NOT_REACHED();
+}
+
+} // namespace WebCore
+
+#endif // ENABLE(WIRELESS_PLAYBACK_TARGET)
diff --git a/Source/WebCore/platform/graphics/MediaPlaybackTargetPicker.h b/Source/WebCore/platform/graphics/MediaPlaybackTargetPicker.h
new file mode 100644
index 000000000..afe0bd3ee
--- /dev/null
+++ b/Source/WebCore/platform/graphics/MediaPlaybackTargetPicker.h
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2015 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef MediaPlaybackTargetPicker_h
+#define MediaPlaybackTargetPicker_h
+
+#if ENABLE(WIRELESS_PLAYBACK_TARGET)
+
+#include <wtf/Ref.h>
+#include <wtf/RunLoop.h>
+
+namespace WebCore {
+
+class FloatRect;
+class MediaPlaybackTarget;
+
+class MediaPlaybackTargetPicker {
+public:
+ class Client {
+ protected:
+ virtual ~Client() { }
+
+ public:
+ virtual void setPlaybackTarget(Ref<MediaPlaybackTarget>&&) = 0;
+ virtual void externalOutputDeviceAvailableDidChange(bool) = 0;
+
+ void invalidate();
+ };
+
+ virtual ~MediaPlaybackTargetPicker();
+
+ virtual void showPlaybackTargetPicker(const FloatRect&, bool checkActiveRoute);
+ virtual void startingMonitoringPlaybackTargets();
+ virtual void stopMonitoringPlaybackTargets();
+ virtual void invalidatePlaybackTargets();
+
+ void availableDevicesDidChange() { addPendingAction(OutputDeviceAvailabilityChanged); }
+ void currentDeviceDidChange() { addPendingAction(CurrentDeviceDidChange); }
+
+protected:
+ explicit MediaPlaybackTargetPicker(Client&);
+
+ enum ActionType {
+ OutputDeviceAvailabilityChanged = 1 << 0,
+ CurrentDeviceDidChange = 1 << 1,
+ };
+ typedef unsigned PendingActionFlags;
+
+ void addPendingAction(PendingActionFlags);
+ void pendingActionTimerFired();
+ Client* client() const { return m_client; }
+ void setClient(Client* client) { m_client = client; }
+
+private:
+ virtual bool externalOutputDeviceAvailable() = 0;
+ virtual Ref<MediaPlaybackTarget> playbackTarget() = 0;
+
+ PendingActionFlags m_pendingActionFlags { 0 };
+ Client* m_client;
+ RunLoop::Timer<MediaPlaybackTargetPicker> m_pendingActionTimer;
+};
+
+} // namespace WebCore
+
+#endif // ENABLE(WIRELESS_PLAYBACK_TARGET)
+
+#endif // MediaPlaybackTargetPicker_h
diff --git a/Source/WebCore/platform/graphics/MediaPlayer.cpp b/Source/WebCore/platform/graphics/MediaPlayer.cpp
index eac25540b..a1cc538ce 100644
--- a/Source/WebCore/platform/graphics/MediaPlayer.cpp
+++ b/Source/WebCore/platform/graphics/MediaPlayer.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2007, 2008, 2009, 2010, 2011, 2012, 2013 Apple Inc. All rights reserved.
+ * Copyright (C) 2007-2015 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -10,10 +10,10 @@
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
- * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
@@ -30,14 +30,13 @@
#include "ContentType.h"
#include "Document.h"
-#include "Frame.h"
-#include "FrameView.h"
#include "IntRect.h"
#include "Logging.h"
#include "MIMETypeRegistry.h"
#include "MediaPlayerPrivate.h"
+#include "PlatformTimeRanges.h"
#include "Settings.h"
-#include "TimeRanges.h"
+#include <wtf/NeverDestroyed.h>
#include <wtf/text/CString.h>
#if ENABLE(VIDEO_TRACK)
@@ -45,33 +44,50 @@
#endif
#if ENABLE(MEDIA_SOURCE)
-#include "HTMLMediaSource.h"
+#include "MediaSourcePrivateClient.h"
+#endif
+
+#if ENABLE(MEDIA_STREAM)
+#include "MediaStreamPrivate.h"
#endif
#if USE(GSTREAMER)
#include "MediaPlayerPrivateGStreamer.h"
+#if ENABLE(MEDIA_STREAM) && USE(OPENWEBRTC)
+#include "MediaPlayerPrivateGStreamerOwr.h"
+#endif
#define PlatformMediaEngineClassName MediaPlayerPrivateGStreamer
+#if ENABLE(VIDEO) && ENABLE(MEDIA_SOURCE) && ENABLE(VIDEO_TRACK)
+#include "MediaPlayerPrivateGStreamerMSE.h"
#endif
+#endif // USE(GSTREAMER)
-#if PLATFORM(MAC)
-#if PLATFORM(IOS)
-#include "MediaPlayerPrivateIOS.h"
-#else
+#if USE(MEDIA_FOUNDATION)
+#include "MediaPlayerPrivateMediaFoundation.h"
+#define PlatformMediaEngineClassName MediaPlayerPrivateMediaFoundation
+#endif
+
+#if PLATFORM(COCOA)
+#if USE(QTKIT)
#include "MediaPlayerPrivateQTKit.h"
#endif
+
#if USE(AVFOUNDATION)
#include "MediaPlayerPrivateAVFoundationObjC.h"
-#if ENABLE(MEDIA_SOURCE)
+#endif
+
+#if ENABLE(MEDIA_SOURCE) && USE(AVFOUNDATION)
#include "MediaPlayerPrivateMediaSourceAVFObjC.h"
#endif
+
+#if ENABLE(MEDIA_STREAM) && USE(AVFOUNDATION)
+#include "MediaPlayerPrivateMediaStreamAVFObjC.h"
#endif
-#elif OS(WINCE)
-#include "MediaPlayerPrivateWinCE.h"
-#define PlatformMediaEngineClassName MediaPlayerPrivate
-#elif PLATFORM(WIN) && !USE(GSTREAMER)
-#if USE(AVFOUNDATION)
+
+#endif // PLATFORM(COCOA)
+
+#if PLATFORM(WIN) && USE(AVFOUNDATION) && !USE(GSTREAMER)
#include "MediaPlayerPrivateAVFoundationCF.h"
-#endif // USE(AVFOUNDATION)
#endif
namespace WebCore {
@@ -82,289 +98,295 @@ const PlatformMedia NoPlatformMedia = { PlatformMedia::None, {0} };
class NullMediaPlayerPrivate : public MediaPlayerPrivateInterface {
public:
- NullMediaPlayerPrivate(MediaPlayer*) { }
+ explicit NullMediaPlayerPrivate(MediaPlayer*) { }
- virtual void load(const String&) { }
+ void load(const String&) override { }
#if ENABLE(MEDIA_SOURCE)
- virtual void load(const String&, PassRefPtr<HTMLMediaSource>) { }
+ void load(const String&, MediaSourcePrivateClient*) override { }
#endif
- virtual void cancelLoad() { }
-
- virtual void prepareToPlay() { }
- virtual void play() { }
- virtual void pause() { }
-
- virtual PlatformMedia platformMedia() const { return NoPlatformMedia; }
-#if USE(ACCELERATED_COMPOSITING)
- virtual PlatformLayer* platformLayer() const { return 0; }
+#if ENABLE(MEDIA_STREAM)
+ void load(MediaStreamPrivate&) override { }
#endif
+ void cancelLoad() override { }
- virtual IntSize naturalSize() const { return IntSize(0, 0); }
+ void prepareToPlay() override { }
+ void play() override { }
+ void pause() override { }
- virtual bool hasVideo() const { return false; }
- virtual bool hasAudio() const { return false; }
+ PlatformMedia platformMedia() const override { return NoPlatformMedia; }
+ PlatformLayer* platformLayer() const override { return 0; }
- virtual void setVisible(bool) { }
+ FloatSize naturalSize() const override { return FloatSize(); }
- virtual double durationDouble() const { return 0; }
+ bool hasVideo() const override { return false; }
+ bool hasAudio() const override { return false; }
- virtual double currentTimeDouble() const { return 0; }
- virtual void seekDouble(double) { }
- virtual bool seeking() const { return false; }
+ void setVisible(bool) override { }
- virtual void setRateDouble(double) { }
- virtual void setPreservesPitch(bool) { }
- virtual bool paused() const { return false; }
+ double durationDouble() const override { return 0; }
- virtual void setVolumeDouble(double) { }
+ double currentTimeDouble() const override { return 0; }
+ void seekDouble(double) override { }
+ bool seeking() const override { return false; }
- virtual bool supportsMuting() const { return false; }
- virtual void setMuted(bool) { }
+ void setRateDouble(double) override { }
+ void setPreservesPitch(bool) override { }
+ bool paused() const override { return false; }
- virtual bool hasClosedCaptions() const { return false; }
- virtual void setClosedCaptionsVisible(bool) { };
+ void setVolumeDouble(double) override { }
- virtual MediaPlayer::NetworkState networkState() const { return MediaPlayer::Empty; }
- virtual MediaPlayer::ReadyState readyState() const { return MediaPlayer::HaveNothing; }
+ bool supportsMuting() const override { return false; }
+ void setMuted(bool) override { }
- virtual double maxTimeSeekableDouble() const { return 0; }
- virtual double minTimeSeekable() const { return 0; }
- virtual PassRefPtr<TimeRanges> buffered() const { return TimeRanges::create(); }
+ bool hasClosedCaptions() const override { return false; }
+ void setClosedCaptionsVisible(bool) override { };
- virtual unsigned totalBytes() const { return 0; }
- virtual bool didLoadingProgress() const { return false; }
+ MediaPlayer::NetworkState networkState() const override { return MediaPlayer::Empty; }
+ MediaPlayer::ReadyState readyState() const override { return MediaPlayer::HaveNothing; }
- virtual void setSize(const IntSize&) { }
+ float maxTimeSeekable() const override { return 0; }
+ double minTimeSeekable() const override { return 0; }
+ std::unique_ptr<PlatformTimeRanges> buffered() const override { return std::make_unique<PlatformTimeRanges>(); }
- virtual void paint(GraphicsContext*, const IntRect&) { }
+ unsigned long long totalBytes() const override { return 0; }
+ bool didLoadingProgress() const override { return false; }
- virtual bool canLoadPoster() const { return false; }
- virtual void setPoster(const String&) { }
+ void setSize(const IntSize&) override { }
-#if ENABLE(PLUGIN_PROXY_FOR_VIDEO)
- virtual void deliverNotification(MediaPlayerProxyNotificationType) { }
- virtual void setMediaPlayerProxy(WebMediaPlayerProxy*) { }
- virtual void setControls(bool) { }
-#endif
+ void paint(GraphicsContext&, const FloatRect&) override { }
- virtual bool hasSingleSecurityOrigin() const { return true; }
+ bool canLoadPoster() const override { return false; }
+ void setPoster(const String&) override { }
-#if ENABLE(ENCRYPTED_MEDIA)
- virtual MediaPlayer::MediaKeyException generateKeyRequest(const String&, const unsigned char*, unsigned) override { return MediaPlayer::InvalidPlayerState; }
- virtual MediaPlayer::MediaKeyException addKey(const String&, const unsigned char*, unsigned, const unsigned char*, unsigned, const String&) override { return MediaPlayer::InvalidPlayerState; }
- virtual MediaPlayer::MediaKeyException cancelKeyRequest(const String&, const String&) override { return MediaPlayer::InvalidPlayerState; }
-#endif
+ bool hasSingleSecurityOrigin() const override { return true; }
};
-static PassOwnPtr<MediaPlayerPrivateInterface> createNullMediaPlayer(MediaPlayer* player)
-{
- return adoptPtr(new NullMediaPlayerPrivate(player));
+static MediaPlayerClient& nullMediaPlayerClient()
+{
+ static NeverDestroyed<MediaPlayerClient> client;
+ return client.get();
}
-
// engine support
struct MediaPlayerFactory {
- WTF_MAKE_NONCOPYABLE(MediaPlayerFactory); WTF_MAKE_FAST_ALLOCATED;
-public:
- MediaPlayerFactory(CreateMediaEnginePlayer constructor, MediaEngineSupportedTypes getSupportedTypes, MediaEngineSupportsType supportsTypeAndCodecs,
- MediaEngineGetSitesInMediaCache getSitesInMediaCache, MediaEngineClearMediaCache clearMediaCache, MediaEngineClearMediaCacheForSite clearMediaCacheForSite)
- : constructor(constructor)
- , getSupportedTypes(getSupportedTypes)
- , supportsTypeAndCodecs(supportsTypeAndCodecs)
- , getSitesInMediaCache(getSitesInMediaCache)
- , clearMediaCache(clearMediaCache)
- , clearMediaCacheForSite(clearMediaCacheForSite)
- {
- }
-
CreateMediaEnginePlayer constructor;
MediaEngineSupportedTypes getSupportedTypes;
MediaEngineSupportsType supportsTypeAndCodecs;
- MediaEngineGetSitesInMediaCache getSitesInMediaCache;
+ MediaEngineOriginsInMediaCache originsInMediaCache;
MediaEngineClearMediaCache clearMediaCache;
- MediaEngineClearMediaCacheForSite clearMediaCacheForSite;
+ MediaEngineClearMediaCacheForOrigins clearMediaCacheForOrigins;
+ MediaEngineSupportsKeySystem supportsKeySystem;
};
-static void addMediaEngine(CreateMediaEnginePlayer, MediaEngineSupportedTypes, MediaEngineSupportsType, MediaEngineGetSitesInMediaCache, MediaEngineClearMediaCache, MediaEngineClearMediaCacheForSite);
+static void addMediaEngine(CreateMediaEnginePlayer, MediaEngineSupportedTypes, MediaEngineSupportsType, MediaEngineOriginsInMediaCache, MediaEngineClearMediaCache, MediaEngineClearMediaCacheForOrigins, MediaEngineSupportsKeySystem);
-static MediaPlayerFactory* bestMediaEngineForSupportParameters(const MediaEngineSupportParameters&, MediaPlayerFactory* current = 0);
-static MediaPlayerFactory* nextMediaEngine(MediaPlayerFactory* current);
-
-enum RequeryEngineOptions { DoNotResetEngines, ResetEngines };
-static Vector<MediaPlayerFactory*>& installedMediaEngines(RequeryEngineOptions requeryFlags = DoNotResetEngines )
+static Lock& mediaEngineVectorLock()
{
- DEFINE_STATIC_LOCAL(Vector<MediaPlayerFactory*>, installedEngines, ());
- static bool enginesQueried = false;
-
- if (requeryFlags == ResetEngines) {
- installedEngines.clear();
- enginesQueried = false;
- return installedEngines;
- }
+ static NeverDestroyed<Lock> lock;
+ return lock;
+}
- if (enginesQueried)
- return installedEngines;
+static bool& haveMediaEnginesVector()
+{
+ static bool haveVector;
+ return haveVector;
+}
- enginesQueried = true;
+static Vector<MediaPlayerFactory>& mutableInstalledMediaEnginesVector()
+{
+ static NeverDestroyed<Vector<MediaPlayerFactory>> installedEngines;
+ return installedEngines;
+}
-#if ENABLE(PLUGIN_PROXY_FOR_VIDEO)
- if (Settings::isVideoPluginProxyEnabled())
- MediaPlayerPrivateIOS::registerMediaEngine(addMediaEngine);
-#endif
+static void buildMediaEnginesVector()
+{
+ ASSERT(mediaEngineVectorLock().isLocked());
#if USE(AVFOUNDATION)
if (Settings::isAVFoundationEnabled()) {
-#if PLATFORM(MAC)
+
+#if PLATFORM(COCOA)
MediaPlayerPrivateAVFoundationObjC::registerMediaEngine(addMediaEngine);
+#endif
+
#if ENABLE(MEDIA_SOURCE)
MediaPlayerPrivateMediaSourceAVFObjC::registerMediaEngine(addMediaEngine);
#endif
-#elif PLATFORM(WIN)
+
+#if ENABLE(MEDIA_STREAM)
+ MediaPlayerPrivateMediaStreamAVFObjC::registerMediaEngine(addMediaEngine);
+#endif
+
+#if PLATFORM(WIN)
MediaPlayerPrivateAVFoundationCF::registerMediaEngine(addMediaEngine);
#endif
}
-#endif
+#endif // USE(AVFOUNDATION)
-#if PLATFORM(MAC) && !PLATFORM(IOS)
+#if PLATFORM(MAC) && USE(QTKIT)
if (Settings::isQTKitEnabled())
MediaPlayerPrivateQTKit::registerMediaEngine(addMediaEngine);
#endif
+
+#if ENABLE(MEDIA_STREAM) && USE(GSTREAMER) && USE(OPENWEBRTC)
+ if (Settings::isGStreamerEnabled())
+ MediaPlayerPrivateGStreamerOwr::registerMediaEngine(addMediaEngine);
+#endif
+
#if defined(PlatformMediaEngineClassName)
- PlatformMediaEngineClassName::registerMediaEngine(addMediaEngine);
+#if USE(GSTREAMER)
+ if (Settings::isGStreamerEnabled())
+#endif
+ PlatformMediaEngineClassName::registerMediaEngine(addMediaEngine);
#endif
- return installedEngines;
+#if ENABLE(VIDEO) && USE(GSTREAMER) && ENABLE(MEDIA_SOURCE) && ENABLE(VIDEO_TRACK)
+ if (Settings::isGStreamerEnabled())
+ MediaPlayerPrivateGStreamerMSE::registerMediaEngine(addMediaEngine);
+#endif
+
+ haveMediaEnginesVector() = true;
+}
+
+static const Vector<MediaPlayerFactory>& installedMediaEngines()
+{
+ {
+ LockHolder lock(mediaEngineVectorLock());
+ if (!haveMediaEnginesVector())
+ buildMediaEnginesVector();
+ }
+
+ return mutableInstalledMediaEnginesVector();
}
static void addMediaEngine(CreateMediaEnginePlayer constructor, MediaEngineSupportedTypes getSupportedTypes, MediaEngineSupportsType supportsType,
- MediaEngineGetSitesInMediaCache getSitesInMediaCache, MediaEngineClearMediaCache clearMediaCache, MediaEngineClearMediaCacheForSite clearMediaCacheForSite)
+ MediaEngineOriginsInMediaCache originsInMediaCache, MediaEngineClearMediaCache clearMediaCache, MediaEngineClearMediaCacheForOrigins clearMediaCacheForOrigins, MediaEngineSupportsKeySystem supportsKeySystem)
{
ASSERT(constructor);
ASSERT(getSupportedTypes);
ASSERT(supportsType);
- installedMediaEngines().append(new MediaPlayerFactory(constructor, getSupportedTypes, supportsType, getSitesInMediaCache, clearMediaCache, clearMediaCacheForSite));
+ mutableInstalledMediaEnginesVector().append(MediaPlayerFactory { constructor, getSupportedTypes, supportsType, originsInMediaCache, clearMediaCache, clearMediaCacheForOrigins, supportsKeySystem });
}
static const AtomicString& applicationOctetStream()
{
- DEFINE_STATIC_LOCAL(const AtomicString, applicationOctetStream, ("application/octet-stream", AtomicString::ConstructFromLiteral));
+ static NeverDestroyed<const AtomicString> applicationOctetStream("application/octet-stream", AtomicString::ConstructFromLiteral);
return applicationOctetStream;
}
static const AtomicString& textPlain()
{
- DEFINE_STATIC_LOCAL(const AtomicString, textPlain, ("text/plain", AtomicString::ConstructFromLiteral));
+ static NeverDestroyed<const AtomicString> textPlain("text/plain", AtomicString::ConstructFromLiteral);
return textPlain;
}
static const AtomicString& codecs()
{
- DEFINE_STATIC_LOCAL(const AtomicString, codecs, ("codecs", AtomicString::ConstructFromLiteral));
+ static NeverDestroyed<const AtomicString> codecs("codecs", AtomicString::ConstructFromLiteral);
return codecs;
}
-static MediaPlayerFactory* bestMediaEngineForSupportParameters(const MediaEngineSupportParameters& parameters, MediaPlayerFactory* current)
+static const MediaPlayerFactory* bestMediaEngineForSupportParameters(const MediaEngineSupportParameters& parameters, const MediaPlayerFactory* current = nullptr)
{
- if (parameters.type.isEmpty())
- return 0;
-
- Vector<MediaPlayerFactory*>& engines = installedMediaEngines();
- if (engines.isEmpty())
- return 0;
+ if (parameters.type.isEmpty() && !parameters.isMediaSource && !parameters.isMediaStream)
+ return nullptr;
- // 4.8.10.3 MIME types - In the absence of a specification to the contrary, the MIME type "application/octet-stream"
+ // 4.8.10.3 MIME types - In the absence of a specification to the contrary, the MIME type "application/octet-stream"
// when used with parameters, e.g. "application/octet-stream;codecs=theora", is a type that the user agent knows
// it cannot render.
if (parameters.type == applicationOctetStream()) {
if (!parameters.codecs.isEmpty())
- return 0;
+ return nullptr;
}
- MediaPlayerFactory* engine = 0;
+ const MediaPlayerFactory* foundEngine = nullptr;
MediaPlayer::SupportsType supported = MediaPlayer::IsNotSupported;
- unsigned count = engines.size();
- for (unsigned ndx = 0; ndx < count; ndx++) {
+ for (auto& engine : installedMediaEngines()) {
if (current) {
- if (current == engines[ndx])
- current = 0;
+ if (current == &engine)
+ current = nullptr;
continue;
}
- MediaPlayer::SupportsType engineSupport = engines[ndx]->supportsTypeAndCodecs(parameters);
+ MediaPlayer::SupportsType engineSupport = engine.supportsTypeAndCodecs(parameters);
if (engineSupport > supported) {
supported = engineSupport;
- engine = engines[ndx];
+ foundEngine = &engine;
}
}
- return engine;
+ return foundEngine;
}
-static MediaPlayerFactory* nextMediaEngine(MediaPlayerFactory* current)
+static const MediaPlayerFactory* nextMediaEngine(const MediaPlayerFactory* current)
{
- Vector<MediaPlayerFactory*>& engines = installedMediaEngines();
+ auto& engines = installedMediaEngines();
if (engines.isEmpty())
- return 0;
+ return nullptr;
if (!current)
- return engines.first();
+ return &engines.first();
- size_t currentIndex = engines.find(current);
- if (currentIndex == WTF::notFound || currentIndex + 1 >= engines.size())
- return 0;
+ size_t currentIndex = current - &engines.first();
+ if (currentIndex + 1 >= engines.size())
+ return nullptr;
- return engines[currentIndex + 1];
+ return &engines[currentIndex + 1];
}
// media player
-MediaPlayer::MediaPlayer(MediaPlayerClient* client)
- : m_mediaPlayerClient(client)
- , m_reloadTimer(this, &MediaPlayer::reloadTimerFired)
- , m_private(createNullMediaPlayer(this))
+Ref<MediaPlayer> MediaPlayer::create(MediaPlayerClient& client)
+{
+ return adoptRef(*new MediaPlayer(client));
+}
+
+MediaPlayer::MediaPlayer(MediaPlayerClient& client)
+ : m_client(&client)
+ , m_reloadTimer(*this, &MediaPlayer::reloadTimerFired)
+ , m_private(std::make_unique<NullMediaPlayerPrivate>(this))
, m_currentMediaEngine(0)
- , m_frameView(0)
, m_preload(Auto)
, m_visible(false)
- , m_rate(1.0f)
, m_volume(1.0f)
, m_muted(false)
, m_preservesPitch(true)
, m_privateBrowsing(false)
, m_shouldPrepareToRender(false)
, m_contentMIMETypeWasInferredFromExtension(false)
-#if ENABLE(PLUGIN_PROXY_FOR_VIDEO)
- , m_playerProxy(0)
-#endif
{
-#if ENABLE(PLUGIN_PROXY_FOR_VIDEO)
- Vector<MediaPlayerFactory*>& engines = installedMediaEngines();
- if (Settings::isVideoPluginProxyEnabled() && !engines.isEmpty()) {
- m_currentMediaEngine = engines[0];
- m_private = engines[0]->constructor(this);
- if (m_mediaPlayerClient)
- m_mediaPlayerClient->mediaPlayerEngineUpdated(this);
- }
-#endif
}
MediaPlayer::~MediaPlayer()
{
- m_mediaPlayerClient = 0;
+ ASSERT(!m_initializingMediaEngine);
+}
+
+void MediaPlayer::invalidate()
+{
+ m_client = &nullMediaPlayerClient();
}
bool MediaPlayer::load(const URL& url, const ContentType& contentType, const String& keySystem)
{
- m_contentMIMEType = contentType.type().lower();
+ ASSERT(!m_reloadTimer.isActive());
+
+ // Protect against MediaPlayer being destroyed during a MediaPlayerClient callback.
+ Ref<MediaPlayer> protectedThis(*this);
+
+ m_contentMIMEType = contentType.type().convertToASCIILowercase();
m_contentTypeCodecs = contentType.parameter(codecs());
m_url = url;
- m_keySystem = keySystem.lower();
+ m_keySystem = keySystem.convertToASCIILowercase();
m_contentMIMETypeWasInferredFromExtension = false;
#if ENABLE(MEDIA_SOURCE)
- m_mediaSource = 0;
+ m_mediaSource = nullptr;
+#endif
+#if ENABLE(MEDIA_STREAM)
+ m_mediaStream = nullptr;
#endif
// If the MIME type is missing or is not meaningful, try to figure it out from the URL.
@@ -390,40 +412,73 @@ bool MediaPlayer::load(const URL& url, const ContentType& contentType, const Str
}
#if ENABLE(MEDIA_SOURCE)
-bool MediaPlayer::load(const URL& url, const ContentType& contentType, PassRefPtr<HTMLMediaSource> mediaSource)
+bool MediaPlayer::load(const URL& url, const ContentType& contentType, MediaSourcePrivateClient* mediaSource)
{
+ ASSERT(!m_reloadTimer.isActive());
+ ASSERT(mediaSource);
+
m_mediaSource = mediaSource;
- m_contentMIMEType = contentType.type().lower();
+ m_contentMIMEType = contentType.type().convertToASCIILowercase();
m_contentTypeCodecs = contentType.parameter(codecs());
m_url = url;
- m_keySystem = "";
+ m_keySystem = emptyString();
m_contentMIMETypeWasInferredFromExtension = false;
loadWithNextMediaEngine(0);
return m_currentMediaEngine;
}
#endif
-MediaPlayerFactory* MediaPlayer::nextBestMediaEngine(MediaPlayerFactory* current) const
+#if ENABLE(MEDIA_STREAM)
+bool MediaPlayer::load(MediaStreamPrivate* mediaStream)
+{
+ ASSERT(!m_reloadTimer.isActive());
+ ASSERT(mediaStream);
+
+ m_mediaStream = mediaStream;
+ m_keySystem = emptyString();
+ m_contentMIMEType = emptyString();
+ m_contentMIMETypeWasInferredFromExtension = false;
+ loadWithNextMediaEngine(0);
+ return m_currentMediaEngine;
+}
+#endif
+
+const MediaPlayerFactory* MediaPlayer::nextBestMediaEngine(const MediaPlayerFactory* current) const
{
MediaEngineSupportParameters parameters;
parameters.type = m_contentMIMEType;
parameters.codecs = m_contentTypeCodecs;
parameters.url = m_url;
-#if ENABLE(ENCRYPTED_MEDIA)
- parameters.keySystem = m_keySystem;
-#endif
#if ENABLE(MEDIA_SOURCE)
parameters.isMediaSource = !!m_mediaSource;
#endif
+#if ENABLE(MEDIA_STREAM)
+ parameters.isMediaStream = !!m_mediaStream;
+#endif
return bestMediaEngineForSupportParameters(parameters, current);
}
-void MediaPlayer::loadWithNextMediaEngine(MediaPlayerFactory* current)
+void MediaPlayer::loadWithNextMediaEngine(const MediaPlayerFactory* current)
{
- MediaPlayerFactory* engine = 0;
+#if ENABLE(MEDIA_SOURCE)
+#define MEDIASOURCE m_mediaSource
+#else
+#define MEDIASOURCE 0
+#endif
- if (!m_contentMIMEType.isEmpty())
+#if ENABLE(MEDIA_STREAM)
+#define MEDIASTREAM m_mediaStream
+#else
+#define MEDIASTREAM 0
+#endif
+
+ ASSERT(!m_initializingMediaEngine);
+ m_initializingMediaEngine = true;
+
+ const MediaPlayerFactory* engine = nullptr;
+
+ if (!m_contentMIMEType.isEmpty() || MEDIASTREAM || MEDIASOURCE)
engine = nextBestMediaEngine(current);
// If no MIME type is specified or the type was inferred from the file extension, just use the next engine.
@@ -438,11 +493,7 @@ void MediaPlayer::loadWithNextMediaEngine(MediaPlayerFactory* current)
} else if (m_currentMediaEngine != engine) {
m_currentMediaEngine = engine;
m_private = engine->constructor(this);
- if (m_mediaPlayerClient)
- m_mediaPlayerClient->mediaPlayerEngineUpdated(this);
-#if ENABLE(PLUGIN_PROXY_FOR_VIDEO)
- m_private->setMediaPlayerProxy(m_playerProxy);
-#endif
+ client().mediaPlayerEngineUpdated(this);
m_private->setPrivateBrowsingMode(m_privateBrowsing);
m_private->setPreload(m_preload);
m_private->setPreservesPitch(preservesPitch());
@@ -453,17 +504,22 @@ void MediaPlayer::loadWithNextMediaEngine(MediaPlayerFactory* current)
if (m_private) {
#if ENABLE(MEDIA_SOURCE)
if (m_mediaSource)
- m_private->load(m_url.string(), m_mediaSource);
+ m_private->load(m_url.string(), m_mediaSource.get());
+ else
+#endif
+#if ENABLE(MEDIA_STREAM)
+ if (m_mediaStream)
+ m_private->load(*m_mediaStream);
else
#endif
m_private->load(m_url.string());
} else {
- m_private = createNullMediaPlayer(this);
- if (m_mediaPlayerClient) {
- m_mediaPlayerClient->mediaPlayerEngineUpdated(this);
- m_mediaPlayerClient->mediaPlayerResourceNotSupported(this);
- }
+ m_private = std::make_unique<NullMediaPlayerPrivate>(this);
+ client().mediaPlayerEngineUpdated(this);
+ client().mediaPlayerResourceNotSupported(this);
}
+
+ m_initializingMediaEngine = false;
}
bool MediaPlayer::hasAvailableVideoFrame() const
@@ -507,51 +563,61 @@ void MediaPlayer::pause()
m_private->pause();
}
-#if ENABLE(ENCRYPTED_MEDIA)
-MediaPlayer::MediaKeyException MediaPlayer::generateKeyRequest(const String& keySystem, const unsigned char* initData, unsigned initDataLength)
+void MediaPlayer::setShouldBufferData(bool shouldBuffer)
{
- return m_private->generateKeyRequest(keySystem.lower(), initData, initDataLength);
+ m_private->setShouldBufferData(shouldBuffer);
}
-MediaPlayer::MediaKeyException MediaPlayer::addKey(const String& keySystem, const unsigned char* key, unsigned keyLength, const unsigned char* initData, unsigned initDataLength, const String& sessionId)
+#if ENABLE(LEGACY_ENCRYPTED_MEDIA)
+std::unique_ptr<CDMSession> MediaPlayer::createSession(const String& keySystem, CDMSessionClient* client)
{
- return m_private->addKey(keySystem.lower(), key, keyLength, initData, initDataLength, sessionId);
+ return m_private->createSession(keySystem, client);
}
-MediaPlayer::MediaKeyException MediaPlayer::cancelKeyRequest(const String& keySystem, const String& sessionId)
+void MediaPlayer::setCDMSession(CDMSession* session)
{
- return m_private->cancelKeyRequest(keySystem.lower(), sessionId);
+ m_private->setCDMSession(session);
}
-#endif
-double MediaPlayer::duration() const
+void MediaPlayer::keyAdded()
+{
+ m_private->keyAdded();
+}
+#endif
+
+MediaTime MediaPlayer::duration() const
{
- return m_private->durationDouble();
+ return m_private->durationMediaTime();
}
-double MediaPlayer::startTime() const
+MediaTime MediaPlayer::startTime() const
{
- return m_private->startTimeDouble();
+ return m_private->startTime();
}
-double MediaPlayer::initialTime() const
+MediaTime MediaPlayer::initialTime() const
{
return m_private->initialTime();
}
-double MediaPlayer::currentTime() const
+MediaTime MediaPlayer::currentTime() const
+{
+ return m_private->currentMediaTime();
+}
+
+MediaTime MediaPlayer::getStartDate() const
{
- return m_private->currentTimeDouble();
+ return m_private->getStartDate();
}
-void MediaPlayer::seekWithTolerance(double time, double negativeTolerance, double positiveTolerance)
+void MediaPlayer::seekWithTolerance(const MediaTime& time, const MediaTime& negativeTolerance, const MediaTime& positiveTolerance)
{
m_private->seekWithTolerance(time, negativeTolerance, positiveTolerance);
}
-void MediaPlayer::seek(double time)
+void MediaPlayer::seek(const MediaTime& time)
{
- m_private->seekDouble(time);
+ m_private->seek(time);
}
bool MediaPlayer::paused() const
@@ -569,9 +635,9 @@ bool MediaPlayer::supportsFullscreen() const
return m_private->supportsFullscreen();
}
-bool MediaPlayer::supportsSave() const
+bool MediaPlayer::canSaveMediaData() const
{
- return m_private->supportsSave();
+ return m_private->canSaveMediaData();
}
bool MediaPlayer::supportsScanning() const
@@ -584,7 +650,7 @@ bool MediaPlayer::requiresImmediateCompositing() const
return m_private->requiresImmediateCompositing();
}
-IntSize MediaPlayer::naturalSize()
+FloatSize MediaPlayer::naturalSize()
{
return m_private->naturalSize();
}
@@ -599,12 +665,9 @@ bool MediaPlayer::hasAudio() const
return m_private->hasAudio();
}
-bool MediaPlayer::inMediaDocument()
+bool MediaPlayer::inMediaDocument() const
{
- if (!m_frameView)
- return false;
- Document* document = m_frameView->frame().document();
- return document && document->isMediaDocument();
+ return m_visible && client().mediaPlayerIsInMediaDocument();
}
PlatformMedia MediaPlayer::platformMedia() const
@@ -612,11 +675,53 @@ PlatformMedia MediaPlayer::platformMedia() const
return m_private->platformMedia();
}
-#if USE(ACCELERATED_COMPOSITING)
PlatformLayer* MediaPlayer::platformLayer() const
{
return m_private->platformLayer();
}
+
+#if PLATFORM(IOS) || (PLATFORM(MAC) && ENABLE(VIDEO_PRESENTATION_MODE))
+void MediaPlayer::setVideoFullscreenLayer(PlatformLayer* layer, std::function<void()> completionHandler)
+{
+ m_private->setVideoFullscreenLayer(layer, completionHandler);
+}
+
+void MediaPlayer::setVideoFullscreenFrame(FloatRect frame)
+{
+ m_private->setVideoFullscreenFrame(frame);
+}
+
+void MediaPlayer::setVideoFullscreenGravity(MediaPlayer::VideoGravity gravity)
+{
+ m_private->setVideoFullscreenGravity(gravity);
+}
+
+void MediaPlayer::setVideoFullscreenMode(MediaPlayer::VideoFullscreenMode mode)
+{
+ m_private->setVideoFullscreenMode(mode);
+}
+
+MediaPlayer::VideoFullscreenMode MediaPlayer::fullscreenMode() const
+{
+ return client().mediaPlayerFullscreenMode();
+}
+#endif
+
+#if PLATFORM(IOS)
+NSArray* MediaPlayer::timedMetadata() const
+{
+ return m_private->timedMetadata();
+}
+
+String MediaPlayer::accessLog() const
+{
+ return m_private->accessLog();
+}
+
+String MediaPlayer::errorLog() const
+{
+ return m_private->errorLog();
+}
#endif
MediaPlayer::NetworkState MediaPlayer::networkState()
@@ -669,15 +774,19 @@ void MediaPlayer::setClosedCaptionsVisible(bool closedCaptionsVisible)
double MediaPlayer::rate() const
{
- return m_rate;
+ return m_private->rate();
}
void MediaPlayer::setRate(double rate)
{
- m_rate = rate;
m_private->setRateDouble(rate);
}
+double MediaPlayer::requestedRate() const
+{
+ return client().mediaPlayerRequestedPlaybackRate();
+}
+
bool MediaPlayer::preservesPitch() const
{
return m_preservesPitch;
@@ -689,24 +798,24 @@ void MediaPlayer::setPreservesPitch(bool preservesPitch)
m_private->setPreservesPitch(preservesPitch);
}
-PassRefPtr<TimeRanges> MediaPlayer::buffered()
+std::unique_ptr<PlatformTimeRanges> MediaPlayer::buffered()
{
return m_private->buffered();
}
-PassRefPtr<TimeRanges> MediaPlayer::seekable()
+std::unique_ptr<PlatformTimeRanges> MediaPlayer::seekable()
{
return m_private->seekable();
}
-double MediaPlayer::maxTimeSeekable()
+MediaTime MediaPlayer::maxTimeSeekable()
{
- return m_private->maxTimeSeekableDouble();
+ return m_private->maxMediaTimeSeekable();
}
-double MediaPlayer::minTimeSeekable()
+MediaTime MediaPlayer::minTimeSeekable()
{
- return m_private->minTimeSeekable();
+ return m_private->minMediaTimeSeekable();
}
bool MediaPlayer::didLoadingProgress()
@@ -742,22 +851,22 @@ void MediaPlayer::setPreload(MediaPlayer::Preload preload)
m_private->setPreload(preload);
}
-void MediaPlayer::paint(GraphicsContext* p, const IntRect& r)
+void MediaPlayer::paint(GraphicsContext& p, const FloatRect& r)
{
m_private->paint(p, r);
}
-void MediaPlayer::paintCurrentFrameInContext(GraphicsContext* p, const IntRect& r)
+void MediaPlayer::paintCurrentFrameInContext(GraphicsContext& p, const FloatRect& r)
{
m_private->paintCurrentFrameInContext(p, r);
}
-bool MediaPlayer::copyVideoTextureToPlatformTexture(GraphicsContext3D* context, Platform3DObject texture, GC3Dint level, GC3Denum type, GC3Denum internalFormat, bool premultiplyAlpha, bool flipY)
+bool MediaPlayer::copyVideoTextureToPlatformTexture(GraphicsContext3D* context, Platform3DObject texture, GC3Denum target, GC3Dint level, GC3Denum internalFormat, GC3Denum format, GC3Denum type, bool premultiplyAlpha, bool flipY)
{
- return m_private->copyVideoTextureToPlatformTexture(context, texture, level, type, internalFormat, premultiplyAlpha, flipY);
+ return m_private->copyVideoTextureToPlatformTexture(context, texture, target, level, internalFormat, format, type, premultiplyAlpha, flipY);
}
-PassNativeImagePtr MediaPlayer::nativeImageForCurrentTime()
+NativeImagePtr MediaPlayer::nativeImageForCurrentTime()
{
return m_private->nativeImageForCurrentTime();
}
@@ -769,11 +878,11 @@ MediaPlayer::SupportsType MediaPlayer::supportsType(const MediaEngineSupportPara
if (parameters.type == applicationOctetStream())
return IsNotSupported;
- MediaPlayerFactory* engine = bestMediaEngineForSupportParameters(parameters);
+ const MediaPlayerFactory* engine = bestMediaEngineForSupportParameters(parameters);
if (!engine)
return IsNotSupported;
-#if PLATFORM(MAC)
+#if PLATFORM(COCOA)
// YouTube will ask if the HTMLMediaElement canPlayType video/webm, then
// video/x-flv, then finally video/mp4, and will then load a URL of the first type
// in that list which returns "probably". When Perian is installed,
@@ -782,7 +891,7 @@ MediaPlayer::SupportsType MediaPlayer::supportsType(const MediaEngineSupportPara
// slow connections. <https://bugs.webkit.org/show_bug.cgi?id=86409>
if (client && client->mediaPlayerNeedsSiteSpecificHacks()) {
String host = client->mediaPlayerDocumentHost();
- if ((host.endsWith(".youtube.com", false) || equalIgnoringCase("youtube.com", host))
+ if ((host.endsWith(".youtube.com", false) || equalLettersIgnoringASCIICase(host, "youtube.com"))
&& (parameters.type.startsWith("video/webm", false) || parameters.type.startsWith("video/x-flv", false)))
return IsNotSupported;
}
@@ -793,15 +902,13 @@ MediaPlayer::SupportsType MediaPlayer::supportsType(const MediaEngineSupportPara
return engine->supportsTypeAndCodecs(parameters);
}
-void MediaPlayer::getSupportedTypes(HashSet<String>& types)
+void MediaPlayer::getSupportedTypes(HashSet<String, ASCIICaseInsensitiveHash>& types)
{
- Vector<MediaPlayerFactory*>& engines = installedMediaEngines();
- if (engines.isEmpty())
- return;
-
- unsigned count = engines.size();
- for (unsigned ndx = 0; ndx < count; ndx++)
- engines[ndx]->getSupportedTypes(types);
+ for (auto& engine : installedMediaEngines()) {
+ HashSet<String, ASCIICaseInsensitiveHash> engineTypes;
+ engine.getSupportedTypes(engineTypes);
+ types.add(engineTypes.begin(), engineTypes.end());
+ }
}
bool MediaPlayer::isAvailable()
@@ -809,25 +916,7 @@ bool MediaPlayer::isAvailable()
return !installedMediaEngines().isEmpty();
}
-#if ENABLE(PLUGIN_PROXY_FOR_VIDEO)
-void MediaPlayer::deliverNotification(MediaPlayerProxyNotificationType notification)
-{
- m_private->deliverNotification(notification);
-}
-
-void MediaPlayer::setMediaPlayerProxy(WebMediaPlayerProxy* proxy)
-{
- m_playerProxy = proxy;
- m_private->setMediaPlayerProxy(proxy);
-}
-
-void MediaPlayer::setControls(bool controls)
-{
- m_private->setControls(controls);
-}
-#endif
-
-#if ENABLE(PLUGIN_PROXY_FOR_VIDEO) || USE(NATIVE_FULLSCREEN_VIDEO)
+#if USE(NATIVE_FULLSCREEN_VIDEO)
void MediaPlayer::enterFullscreen()
{
m_private->enterFullscreen();
@@ -839,20 +928,20 @@ void MediaPlayer::exitFullscreen()
}
#endif
-#if ENABLE(IOS_AIRPLAY)
+#if ENABLE(WIRELESS_PLAYBACK_TARGET)
bool MediaPlayer::isCurrentPlaybackTargetWireless() const
{
return m_private->isCurrentPlaybackTargetWireless();
}
-void MediaPlayer::showPlaybackTargetPicker()
+String MediaPlayer::wirelessPlaybackTargetName() const
{
- m_private->showPlaybackTargetPicker();
+ return m_private->wirelessPlaybackTargetName();
}
-bool MediaPlayer::hasWirelessPlaybackTargets() const
+MediaPlayer::WirelessPlaybackTargetType MediaPlayer::wirelessPlaybackTargetType() const
{
- return m_private->hasWirelessPlaybackTargets();
+ return m_private->wirelessPlaybackTargetType();
}
bool MediaPlayer::wirelessVideoPlaybackDisabled() const
@@ -865,24 +954,37 @@ void MediaPlayer::setWirelessVideoPlaybackDisabled(bool disabled)
m_private->setWirelessVideoPlaybackDisabled(disabled);
}
-void MediaPlayer::setHasPlaybackTargetAvailabilityListeners(bool hasListeners)
+void MediaPlayer::currentPlaybackTargetIsWirelessChanged()
{
- m_private->setHasPlaybackTargetAvailabilityListeners(hasListeners);
+ client().mediaPlayerCurrentPlaybackTargetIsWirelessChanged(this);
}
-void MediaPlayer::currentPlaybackTargetIsWirelessChanged()
+bool MediaPlayer::canPlayToWirelessPlaybackTarget() const
+{
+ return m_private->canPlayToWirelessPlaybackTarget();
+}
+
+void MediaPlayer::setWirelessPlaybackTarget(Ref<MediaPlaybackTarget>&& device)
{
- if (m_mediaPlayerClient)
- m_mediaPlayerClient->mediaPlayerCurrentPlaybackTargetIsWirelessChanged(this);
+ m_private->setWirelessPlaybackTarget(WTFMove(device));
}
-void MediaPlayer::playbackTargetAvailabilityChanged()
+void MediaPlayer::setShouldPlayToPlaybackTarget(bool shouldPlay)
{
- if (m_mediaPlayerClient)
- m_mediaPlayerClient->mediaPlayerPlaybackTargetAvailabilityChanged(this);
+ m_private->setShouldPlayToPlaybackTarget(shouldPlay);
}
#endif
+double MediaPlayer::maxFastForwardRate() const
+{
+ return m_private->maxFastForwardRate();
+}
+
+double MediaPlayer::minFastReverseRate() const
+{
+ return m_private->minFastReverseRate();
+}
+
#if USE(NATIVE_FULLSCREEN_VIDEO)
bool MediaPlayer::canEnterFullscreen() const
{
@@ -890,7 +992,6 @@ bool MediaPlayer::canEnterFullscreen() const
}
#endif
-#if USE(ACCELERATED_COMPOSITING)
void MediaPlayer::acceleratedRenderingStateChanged()
{
m_private->acceleratedRenderingStateChanged();
@@ -900,7 +1001,6 @@ bool MediaPlayer::supportsAcceleratedRendering() const
{
return m_private->supportsAcceleratedRendering();
}
-#endif // USE(ACCELERATED_COMPOSITING)
bool MediaPlayer::shouldMaintainAspectRatio() const
{
@@ -927,9 +1027,9 @@ MediaPlayer::MovieLoadType MediaPlayer::movieLoadType() const
return m_private->movieLoadType();
}
-double MediaPlayer::mediaTimeForTimeValue(double timeValue) const
+MediaTime MediaPlayer::mediaTimeForTimeValue(const MediaTime& timeValue) const
{
- return m_private->mediaTimeForTimeValueDouble(timeValue);
+ return m_private->mediaTimeForTimeValue(timeValue);
}
double MediaPlayer::maximumDurationToCacheMediaTime() const
@@ -957,84 +1057,81 @@ unsigned MediaPlayer::videoDecodedByteCount() const
return m_private->videoDecodedByteCount();
}
-void MediaPlayer::reloadTimerFired(Timer<MediaPlayer>&)
+void MediaPlayer::reloadTimerFired()
{
m_private->cancelLoad();
loadWithNextMediaEngine(m_currentMediaEngine);
}
-void MediaPlayer::getSitesInMediaCache(Vector<String>& sites)
+template<typename T>
+static void addToHash(HashSet<T>& toHash, HashSet<T>&& fromHash)
{
- Vector<MediaPlayerFactory*>& engines = installedMediaEngines();
- unsigned size = engines.size();
- for (unsigned i = 0; i < size; i++) {
- if (!engines[i]->getSitesInMediaCache)
- continue;
- Vector<String> engineSites;
- engines[i]->getSitesInMediaCache(engineSites);
- sites.appendVector(engineSites);
- }
+ if (toHash.isEmpty())
+ toHash = WTFMove(fromHash);
+ else
+ toHash.add(fromHash.begin(), fromHash.end());
}
-
-void MediaPlayer::clearMediaCache()
+
+HashSet<RefPtr<SecurityOrigin>> MediaPlayer::originsInMediaCache(const String& path)
{
- Vector<MediaPlayerFactory*>& engines = installedMediaEngines();
- unsigned size = engines.size();
- for (unsigned i = 0; i < size; i++) {
- if (engines[i]->clearMediaCache)
- engines[i]->clearMediaCache();
+ HashSet<RefPtr<SecurityOrigin>> origins;
+ for (auto& engine : installedMediaEngines()) {
+ if (!engine.originsInMediaCache)
+ continue;
+ addToHash(origins, engine.originsInMediaCache(path));
}
+ return origins;
}
-void MediaPlayer::clearMediaCacheForSite(const String& site)
+void MediaPlayer::clearMediaCache(const String& path, std::chrono::system_clock::time_point modifiedSince)
{
- Vector<MediaPlayerFactory*>& engines = installedMediaEngines();
- unsigned size = engines.size();
- for (unsigned i = 0; i < size; i++) {
- if (engines[i]->clearMediaCacheForSite)
- engines[i]->clearMediaCacheForSite(site);
+ for (auto& engine : installedMediaEngines()) {
+ if (engine.clearMediaCache)
+ engine.clearMediaCache(path, modifiedSince);
}
}
-void MediaPlayer::setPrivateBrowsingMode(bool privateBrowsingMode)
+void MediaPlayer::clearMediaCacheForOrigins(const String& path, const HashSet<RefPtr<SecurityOrigin>>& origins)
{
- m_privateBrowsing = privateBrowsingMode;
- m_private->setPrivateBrowsingMode(m_privateBrowsing);
+ for (auto& engine : installedMediaEngines()) {
+ if (engine.clearMediaCacheForOrigins)
+ engine.clearMediaCacheForOrigins(path, origins);
+ }
}
-#if PLATFORM(IOS)
-void MediaPlayer::attributeChanged(const String& name, const String& value)
+bool MediaPlayer::supportsKeySystem(const String& keySystem, const String& mimeType)
{
- m_private->attributeChanged(name, value);
+ for (auto& engine : installedMediaEngines()) {
+ if (engine.supportsKeySystem && engine.supportsKeySystem(keySystem, mimeType))
+ return true;
+ }
+ return false;
}
-bool MediaPlayer::readyForPlayback() const
+void MediaPlayer::setPrivateBrowsingMode(bool privateBrowsingMode)
{
- return m_private->readyForPlayback();
+ m_privateBrowsing = privateBrowsingMode;
+ m_private->setPrivateBrowsingMode(m_privateBrowsing);
}
-#endif
// Client callbacks.
void MediaPlayer::networkStateChanged()
{
// If more than one media engine is installed and this one failed before finding metadata,
// let the next engine try.
- if (m_private->networkState() >= FormatError
- && m_private->readyState() < HaveMetadata
- && installedMediaEngines().size() > 1) {
- if (m_contentMIMEType.isEmpty() || nextBestMediaEngine(m_currentMediaEngine)) {
+ if (m_private->networkState() >= FormatError && m_private->readyState() < HaveMetadata) {
+ client().mediaPlayerEngineFailedToLoad();
+ if (installedMediaEngines().size() > 1 && (m_contentMIMEType.isEmpty() || nextBestMediaEngine(m_currentMediaEngine))) {
m_reloadTimer.startOneShot(0);
return;
}
}
- if (m_mediaPlayerClient)
- m_mediaPlayerClient->mediaPlayerNetworkStateChanged(this);
+ client().mediaPlayerNetworkStateChanged(this);
}
void MediaPlayer::readyStateChanged()
{
- if (m_mediaPlayerClient)
- m_mediaPlayerClient->mediaPlayerReadyStateChanged(this);
+ client().mediaPlayerReadyStateChanged(this);
}
void MediaPlayer::volumeChanged(double newVolume)
@@ -1045,63 +1142,53 @@ void MediaPlayer::volumeChanged(double newVolume)
#else
m_volume = newVolume;
#endif
- if (m_mediaPlayerClient)
- m_mediaPlayerClient->mediaPlayerVolumeChanged(this);
+ client().mediaPlayerVolumeChanged(this);
}
void MediaPlayer::muteChanged(bool newMuted)
{
m_muted = newMuted;
- if (m_mediaPlayerClient)
- m_mediaPlayerClient->mediaPlayerMuteChanged(this);
+ client().mediaPlayerMuteChanged(this);
}
void MediaPlayer::timeChanged()
{
- if (m_mediaPlayerClient)
- m_mediaPlayerClient->mediaPlayerTimeChanged(this);
+ client().mediaPlayerTimeChanged(this);
}
void MediaPlayer::sizeChanged()
{
- if (m_mediaPlayerClient)
- m_mediaPlayerClient->mediaPlayerSizeChanged(this);
+ client().mediaPlayerSizeChanged(this);
}
void MediaPlayer::repaint()
{
- if (m_mediaPlayerClient)
- m_mediaPlayerClient->mediaPlayerRepaint(this);
+ client().mediaPlayerRepaint(this);
}
void MediaPlayer::durationChanged()
{
- if (m_mediaPlayerClient)
- m_mediaPlayerClient->mediaPlayerDurationChanged(this);
+ client().mediaPlayerDurationChanged(this);
}
void MediaPlayer::rateChanged()
{
- if (m_mediaPlayerClient)
- m_mediaPlayerClient->mediaPlayerRateChanged(this);
+ client().mediaPlayerRateChanged(this);
}
void MediaPlayer::playbackStateChanged()
{
- if (m_mediaPlayerClient)
- m_mediaPlayerClient->mediaPlayerPlaybackStateChanged(this);
+ client().mediaPlayerPlaybackStateChanged(this);
}
void MediaPlayer::firstVideoFrameAvailable()
{
- if (m_mediaPlayerClient)
- m_mediaPlayerClient->mediaPlayerFirstVideoFrameAvailable(this);
+ client().mediaPlayerFirstVideoFrameAvailable(this);
}
void MediaPlayer::characteristicChanged()
{
- if (m_mediaPlayerClient)
- m_mediaPlayerClient->mediaPlayerCharacteristicChanged(this);
+ client().mediaPlayerCharacteristicChanged(this);
}
#if ENABLE(WEB_AUDIO)
@@ -1111,56 +1198,31 @@ AudioSourceProvider* MediaPlayer::audioSourceProvider()
}
#endif // WEB_AUDIO
-#if ENABLE(ENCRYPTED_MEDIA)
-void MediaPlayer::keyAdded(const String& keySystem, const String& sessionId)
+#if ENABLE(LEGACY_ENCRYPTED_MEDIA)
+RefPtr<ArrayBuffer> MediaPlayer::cachedKeyForKeyId(const String& keyId) const
{
- if (m_mediaPlayerClient)
- m_mediaPlayerClient->mediaPlayerKeyAdded(this, keySystem, sessionId);
+ return client().mediaPlayerCachedKeyForKeyId(keyId);
}
-void MediaPlayer::keyError(const String& keySystem, const String& sessionId, MediaPlayerClient::MediaKeyErrorCode errorCode, unsigned short systemCode)
-{
- if (m_mediaPlayerClient)
- m_mediaPlayerClient->mediaPlayerKeyError(this, keySystem, sessionId, errorCode, systemCode);
-}
-
-void MediaPlayer::keyMessage(const String& keySystem, const String& sessionId, const unsigned char* message, unsigned messageLength, const URL& defaultURL)
-{
- if (m_mediaPlayerClient)
- m_mediaPlayerClient->mediaPlayerKeyMessage(this, keySystem, sessionId, message, messageLength, defaultURL);
-}
-
-bool MediaPlayer::keyNeeded(const String& keySystem, const String& sessionId, const unsigned char* initData, unsigned initDataLength)
+bool MediaPlayer::keyNeeded(Uint8Array* initData)
{
- if (m_mediaPlayerClient)
- return m_mediaPlayerClient->mediaPlayerKeyNeeded(this, keySystem, sessionId, initData, initDataLength);
- return false;
+ return client().mediaPlayerKeyNeeded(this, initData);
}
-#endif
-#if ENABLE(ENCRYPTED_MEDIA_V2)
-bool MediaPlayer::keyNeeded(Uint8Array* initData)
+String MediaPlayer::mediaKeysStorageDirectory() const
{
- if (m_mediaPlayerClient)
- return m_mediaPlayerClient->mediaPlayerKeyNeeded(this, initData);
- return false;
+ return client().mediaPlayerMediaKeysStorageDirectory();
}
#endif
String MediaPlayer::referrer() const
{
- if (!m_mediaPlayerClient)
- return String();
-
- return m_mediaPlayerClient->mediaPlayerReferrer();
+ return client().mediaPlayerReferrer();
}
String MediaPlayer::userAgent() const
{
- if (!m_mediaPlayerClient)
- return String();
-
- return m_mediaPlayerClient->mediaPlayerUserAgent();
+ return client().mediaPlayerUserAgent();
}
String MediaPlayer::engineDescription() const
@@ -1171,71 +1233,61 @@ String MediaPlayer::engineDescription() const
return m_private->engineDescription();
}
+long MediaPlayer::platformErrorCode() const
+{
+ if (!m_private)
+ return 0;
+
+ return m_private->platformErrorCode();
+}
+
#if PLATFORM(WIN) && USE(AVFOUNDATION)
GraphicsDeviceAdapter* MediaPlayer::graphicsDeviceAdapter() const
{
- if (!m_mediaPlayerClient)
- return 0;
-
- return m_mediaPlayerClient->mediaPlayerGraphicsDeviceAdapter(this);
+ return client().mediaPlayerGraphicsDeviceAdapter(this);
}
#endif
CachedResourceLoader* MediaPlayer::cachedResourceLoader()
{
- if (!m_mediaPlayerClient)
- return 0;
+ return client().mediaPlayerCachedResourceLoader();
+}
- return m_mediaPlayerClient->mediaPlayerCachedResourceLoader();
+PassRefPtr<PlatformMediaResourceLoader> MediaPlayer::createResourceLoader()
+{
+ return client().mediaPlayerCreateResourceLoader();
}
#if ENABLE(VIDEO_TRACK)
-void MediaPlayer::addAudioTrack(PassRefPtr<AudioTrackPrivate> track)
-{
- if (!m_mediaPlayerClient)
- return;
- m_mediaPlayerClient->mediaPlayerDidAddAudioTrack(track);
+void MediaPlayer::addAudioTrack(AudioTrackPrivate& track)
+{
+ client().mediaPlayerDidAddAudioTrack(track);
}
-void MediaPlayer::removeAudioTrack(PassRefPtr<AudioTrackPrivate> track)
+void MediaPlayer::removeAudioTrack(AudioTrackPrivate& track)
{
- if (!m_mediaPlayerClient)
- return;
-
- m_mediaPlayerClient->mediaPlayerDidRemoveAudioTrack(track);
+ client().mediaPlayerDidRemoveAudioTrack(track);
}
-void MediaPlayer::addTextTrack(PassRefPtr<InbandTextTrackPrivate> track)
+void MediaPlayer::addTextTrack(InbandTextTrackPrivate& track)
{
- if (!m_mediaPlayerClient)
- return;
-
- m_mediaPlayerClient->mediaPlayerDidAddTextTrack(track);
+ client().mediaPlayerDidAddTextTrack(track);
}
-void MediaPlayer::removeTextTrack(PassRefPtr<InbandTextTrackPrivate> track)
+void MediaPlayer::removeTextTrack(InbandTextTrackPrivate& track)
{
- if (!m_mediaPlayerClient)
- return;
-
- m_mediaPlayerClient->mediaPlayerDidRemoveTextTrack(track);
+ client().mediaPlayerDidRemoveTextTrack(track);
}
-void MediaPlayer::addVideoTrack(PassRefPtr<VideoTrackPrivate> track)
+void MediaPlayer::addVideoTrack(VideoTrackPrivate& track)
{
- if (!m_mediaPlayerClient)
- return;
-
- m_mediaPlayerClient->mediaPlayerDidAddVideoTrack(track);
+ client().mediaPlayerDidAddVideoTrack(track);
}
-void MediaPlayer::removeVideoTrack(PassRefPtr<VideoTrackPrivate> track)
+void MediaPlayer::removeVideoTrack(VideoTrackPrivate& track)
{
- if (!m_mediaPlayerClient)
- return;
-
- m_mediaPlayerClient->mediaPlayerDidRemoveVideoTrack(track);
+ client().mediaPlayerDidRemoveVideoTrack(track);
}
bool MediaPlayer::requiresTextTrackRepresentation() const
@@ -1247,23 +1299,40 @@ void MediaPlayer::setTextTrackRepresentation(TextTrackRepresentation* representa
{
m_private->setTextTrackRepresentation(representation);
}
-#endif // ENABLE(VIDEO_TRACK)
-#if USE(PLATFORM_TEXT_TRACK_MENU)
-bool MediaPlayer::implementsTextTrackControls() const
+void MediaPlayer::syncTextTrackBounds()
+{
+ m_private->syncTextTrackBounds();
+}
+
+void MediaPlayer::tracksChanged()
+{
+ m_private->tracksChanged();
+}
+
+#if ENABLE(AVF_CAPTIONS)
+
+void MediaPlayer::notifyTrackModeChanged()
{
- return m_private->implementsTextTrackControls();
+ if (m_private)
+ m_private->notifyTrackModeChanged();
}
-PassRefPtr<PlatformTextTrackMenuInterface> MediaPlayer::textTrackMenu()
+Vector<RefPtr<PlatformTextTrack>> MediaPlayer::outOfBandTrackSources()
{
- return m_private->textTrackMenu();
+ return client().outOfBandTrackSources();
}
-#endif // USE(PLATFORM_TEXT_TRACK_MENU)
+
+#endif
+
+#endif // ENABLE(VIDEO_TRACK)
void MediaPlayer::resetMediaEngines()
{
- installedMediaEngines(ResetEngines);
+ LockHolder lock(mediaEngineVectorLock());
+
+ mutableInstalledMediaEnginesVector().clear();
+ haveMediaEnginesVector() = false;
}
#if USE(GSTREAMER)
@@ -1300,6 +1369,11 @@ unsigned long long MediaPlayer::fileSize() const
return m_private->fileSize();
}
+bool MediaPlayer::ended() const
+{
+ return m_private->ended();
+}
+
#if ENABLE(MEDIA_SOURCE)
unsigned long MediaPlayer::totalVideoFrames()
{
@@ -1325,10 +1399,10 @@ unsigned long MediaPlayer::corruptedVideoFrames()
return m_private->corruptedVideoFrames();
}
-double MediaPlayer::totalFrameDelay()
+MediaTime MediaPlayer::totalFrameDelay()
{
if (!m_private)
- return 0;
+ return MediaTime::zeroTime();
return m_private->totalFrameDelay();
}
@@ -1336,10 +1410,22 @@ double MediaPlayer::totalFrameDelay()
bool MediaPlayer::shouldWaitForResponseToAuthenticationChallenge(const AuthenticationChallenge& challenge)
{
- if (!m_mediaPlayerClient)
- return false;
+ return client().mediaPlayerShouldWaitForResponseToAuthenticationChallenge(challenge);
+}
+
+void MediaPlayer::handlePlaybackCommand(PlatformMediaSession::RemoteControlCommandType command)
+{
+ client().mediaPlayerHandlePlaybackCommand(command);
+}
+
+String MediaPlayer::sourceApplicationIdentifier() const
+{
+ return client().mediaPlayerSourceApplicationIdentifier();
+}
- return m_mediaPlayerClient->mediaPlayerShouldWaitForResponseToAuthenticationChallenge(challenge);
+Vector<String> MediaPlayer::preferredAudioCharacteristics() const
+{
+ return client().mediaPlayerPreferredAudioCharacteristics();
}
void MediaPlayerFactorySupport::callRegisterMediaEngine(MediaEngineRegister registerMediaEngine)
@@ -1347,6 +1433,34 @@ void MediaPlayerFactorySupport::callRegisterMediaEngine(MediaEngineRegister regi
registerMediaEngine(addMediaEngine);
}
+bool MediaPlayer::doesHaveAttribute(const AtomicString& attribute, AtomicString* value) const
+{
+ return client().doesHaveAttribute(attribute, value);
+}
+
+#if PLATFORM(IOS)
+String MediaPlayer::mediaPlayerNetworkInterfaceName() const
+{
+ return client().mediaPlayerNetworkInterfaceName();
+}
+
+bool MediaPlayer::getRawCookies(const URL& url, Vector<Cookie>& cookies) const
+{
+ return client().mediaPlayerGetRawCookies(url, cookies);
+}
+#endif
+
+void MediaPlayer::setShouldDisableSleep(bool flag)
+{
+ if (m_private)
+ m_private->setShouldDisableSleep(flag);
+}
+
+bool MediaPlayer::shouldDisableSleep() const
+{
+ return client().mediaPlayerShouldDisableSleep();
+}
+
}
#endif
diff --git a/Source/WebCore/platform/graphics/MediaPlayer.h b/Source/WebCore/platform/graphics/MediaPlayer.h
index c6480e4b0..c9dbfc34a 100644
--- a/Source/WebCore/platform/graphics/MediaPlayer.h
+++ b/Source/WebCore/platform/graphics/MediaPlayer.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2007, 2008, 2009, 2010, 2011, 2012, 2013 Apple Inc. All rights reserved.
+ * Copyright (C) 2007-2014 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -10,10 +10,10 @@
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
- * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
@@ -28,16 +28,19 @@
#if ENABLE(VIDEO)
#include "GraphicsTypes3D.h"
-#if ENABLE(PLUGIN_PROXY_FOR_VIDEO)
-#include "MediaPlayerProxy.h"
-#endif
#include "AudioTrackPrivate.h"
+#include "LegacyCDMSession.h"
#include "InbandTextTrackPrivate.h"
#include "IntRect.h"
#include "URL.h"
#include "LayoutRect.h"
-#include "NativeImagePtr.h"
+#include "MediaPlayerEnums.h"
+#include "NativeImage.h"
+#include "PlatformLayer.h"
+#include "PlatformMediaResourceLoader.h"
+#include "PlatformMediaSession.h"
+#include "SecurityOriginHash.h"
#include "Timer.h"
#include "VideoTrackPrivate.h"
#include <runtime/Uint8Array.h>
@@ -45,20 +48,17 @@
#include <wtf/HashSet.h>
#include <wtf/MediaTime.h>
#include <wtf/Noncopyable.h>
-#include <wtf/OwnPtr.h>
-#include <wtf/PassOwnPtr.h>
+#include <wtf/Ref.h>
+#include <wtf/RefCounted.h>
#include <wtf/text/StringHash.h>
-#if USE(ACCELERATED_COMPOSITING)
-#include "PlatformLayer.h"
-#endif
-
-#if USE(PLATFORM_TEXT_TRACK_MENU)
-#include "PlatformTextTrackMenu.h"
+#if ENABLE(AVF_CAPTIONS)
+#include "PlatformTextTrack.h"
#endif
OBJC_CLASS AVAsset;
OBJC_CLASS AVPlayer;
+OBJC_CLASS NSArray;
OBJC_CLASS QTMovie;
class AVCFPlayer;
@@ -69,12 +69,16 @@ namespace WebCore {
class AudioSourceProvider;
class AuthenticationChallenge;
-class Document;
+class MediaPlaybackTarget;
#if ENABLE(MEDIA_SOURCE)
-class HTMLMediaSource;
+class MediaSourcePrivateClient;
+#endif
+#if ENABLE(MEDIA_STREAM)
+class MediaStreamPrivate;
#endif
class MediaPlayerPrivateInterface;
class TextTrackRepresentation;
+struct Cookie;
// Structure that will hold every native
// types supported by the current media player.
@@ -86,8 +90,6 @@ struct PlatformMedia {
QTMovieType,
QTMovieGWorldType,
QTMovieVisualContextType,
- ChromiumMediaPlayerType,
- QtMediaPlayerType,
AVFoundationMediaPlayerType,
AVFoundationCFMediaPlayerType,
AVFoundationAssetType,
@@ -97,8 +99,6 @@ struct PlatformMedia {
QTMovie* qtMovie;
QTMovieGWorld* qtMovieGWorld;
QTMovieVisualContext* qtMovieVisualContext;
- MediaPlayerPrivateInterface* chromiumMediaPlayer;
- MediaPlayerPrivateInterface* qtMediaPlayer;
AVPlayer* avfMediaPlayer;
AVCFPlayer* avcfMediaPlayer;
AVAsset* avfAsset;
@@ -106,51 +106,42 @@ struct PlatformMedia {
};
struct MediaEngineSupportParameters {
+
+ MediaEngineSupportParameters() { }
+
String type;
String codecs;
URL url;
-#if ENABLE(ENCRYPTED_MEDIA)
- String keySystem;
-#endif
-#if ENABLE(MEDIA_SOURCE)
- bool isMediaSource;
-#endif
-
- MediaEngineSupportParameters()
-#if ENABLE(MEDIA_SOURCE)
- : isMediaSource(false)
-#endif
- {
- }
+ bool isMediaSource { false };
+ bool isMediaStream { false };
};
extern const PlatformMedia NoPlatformMedia;
+class CDMSessionClient;
class CachedResourceLoader;
class ContentType;
-class FrameView;
class GraphicsContext;
class GraphicsContext3D;
class IntRect;
class IntSize;
class MediaPlayer;
+class PlatformTimeRanges;
+
struct MediaPlayerFactory;
-class TimeRanges;
-class HostWindow;
#if PLATFORM(WIN) && USE(AVFOUNDATION)
struct GraphicsDeviceAdapter;
#endif
+#if USE(GSTREAMER)
+class MediaPlayerRequestInstallMissingPluginsCallback;
+#endif
+
class MediaPlayerClient {
public:
- enum CORSMode { Unspecified, Anonymous, UseCredentials };
-
virtual ~MediaPlayerClient() { }
- // Get the document which the media player is owned by
- virtual Document* mediaPlayerOwningDocument() { return 0; }
-
// the network state has changed
virtual void mediaPlayerNetworkStateChanged(MediaPlayer*) { }
@@ -199,67 +190,89 @@ public:
// A characteristic of the media file, eg. video, audio, closed captions, etc, has changed.
virtual void mediaPlayerCharacteristicChanged(MediaPlayer*) { }
-#if USE(ACCELERATED_COMPOSITING)
// whether the rendering system can accelerate the display of this MediaPlayer.
virtual bool mediaPlayerRenderingCanBeAccelerated(MediaPlayer*) { return false; }
// called when the media player's rendering mode changed, which indicates a change in the
// availability of the platformLayer().
virtual void mediaPlayerRenderingModeChanged(MediaPlayer*) { }
-#endif
+
+ // whether accelerated compositing is enabled for video rendering
+ virtual bool mediaPlayerAcceleratedCompositingEnabled() { return false; }
+
+ virtual void mediaPlayerActiveSourceBuffersChanged(const MediaPlayer*) { }
#if PLATFORM(WIN) && USE(AVFOUNDATION)
virtual GraphicsDeviceAdapter* mediaPlayerGraphicsDeviceAdapter(const MediaPlayer*) const { return 0; }
#endif
-#if ENABLE(ENCRYPTED_MEDIA)
- enum MediaKeyErrorCode { UnknownError = 1, ClientError, ServiceError, OutputError, HardwareChangeError, DomainError };
- virtual void mediaPlayerKeyAdded(MediaPlayer*, const String& /* keySystem */, const String& /* sessionId */) { }
- virtual void mediaPlayerKeyError(MediaPlayer*, const String& /* keySystem */, const String& /* sessionId */, MediaKeyErrorCode, unsigned short /* systemCode */) { }
- virtual void mediaPlayerKeyMessage(MediaPlayer*, const String& /* keySystem */, const String& /* sessionId */, const unsigned char* /* message */, unsigned /* messageLength */, const URL& /* defaultURL */) { }
- virtual bool mediaPlayerKeyNeeded(MediaPlayer*, const String& /* keySystem */, const String& /* sessionId */, const unsigned char* /* initData */, unsigned /* initDataLength */) { return false; }
-#endif
-
-#if ENABLE(ENCRYPTED_MEDIA_V2)
+#if ENABLE(LEGACY_ENCRYPTED_MEDIA)
+ virtual RefPtr<ArrayBuffer> mediaPlayerCachedKeyForKeyId(const String&) const { return nullptr; }
virtual bool mediaPlayerKeyNeeded(MediaPlayer*, Uint8Array*) { return false; }
+ virtual String mediaPlayerMediaKeysStorageDirectory() const { return emptyString(); }
#endif
-#if ENABLE(IOS_AIRPLAY)
+#if ENABLE(WIRELESS_PLAYBACK_TARGET)
virtual void mediaPlayerCurrentPlaybackTargetIsWirelessChanged(MediaPlayer*) { };
- virtual void mediaPlayerPlaybackTargetAvailabilityChanged(MediaPlayer*) { };
#endif
virtual String mediaPlayerReferrer() const { return String(); }
virtual String mediaPlayerUserAgent() const { return String(); }
- virtual CORSMode mediaPlayerCORSMode() const { return Unspecified; }
virtual void mediaPlayerEnterFullscreen() { }
virtual void mediaPlayerExitFullscreen() { }
virtual bool mediaPlayerIsFullscreen() const { return false; }
virtual bool mediaPlayerIsFullscreenPermitted() const { return false; }
virtual bool mediaPlayerIsVideo() const { return false; }
virtual LayoutRect mediaPlayerContentBoxRect() const { return LayoutRect(); }
+ virtual float mediaPlayerContentsScale() const { return 1; }
virtual void mediaPlayerSetSize(const IntSize&) { }
virtual void mediaPlayerPause() { }
virtual void mediaPlayerPlay() { }
virtual bool mediaPlayerPlatformVolumeConfigurationRequired() const { return false; }
virtual bool mediaPlayerIsPaused() const { return true; }
virtual bool mediaPlayerIsLooping() const { return false; }
- virtual HostWindow* mediaPlayerHostWindow() { return 0; }
- virtual IntRect mediaPlayerWindowClipRect() { return IntRect(); }
virtual CachedResourceLoader* mediaPlayerCachedResourceLoader() { return 0; }
+ virtual RefPtr<PlatformMediaResourceLoader> mediaPlayerCreateResourceLoader() { return nullptr; }
+ virtual bool doesHaveAttribute(const AtomicString&, AtomicString* = 0) const { return false; }
+ virtual bool mediaPlayerShouldUsePersistentCache() const { return true; }
+ virtual const String& mediaPlayerMediaCacheDirectory() const { return emptyString(); }
#if ENABLE(VIDEO_TRACK)
- virtual void mediaPlayerDidAddAudioTrack(PassRefPtr<AudioTrackPrivate>) { }
- virtual void mediaPlayerDidAddTextTrack(PassRefPtr<InbandTextTrackPrivate>) { }
- virtual void mediaPlayerDidAddVideoTrack(PassRefPtr<VideoTrackPrivate>) { }
- virtual void mediaPlayerDidRemoveAudioTrack(PassRefPtr<AudioTrackPrivate>) { }
- virtual void mediaPlayerDidRemoveTextTrack(PassRefPtr<InbandTextTrackPrivate>) { }
- virtual void mediaPlayerDidRemoveVideoTrack(PassRefPtr<VideoTrackPrivate>) { }
+ virtual void mediaPlayerDidAddAudioTrack(AudioTrackPrivate&) { }
+ virtual void mediaPlayerDidAddTextTrack(InbandTextTrackPrivate&) { }
+ virtual void mediaPlayerDidAddVideoTrack(VideoTrackPrivate&) { }
+ virtual void mediaPlayerDidRemoveAudioTrack(AudioTrackPrivate&) { }
+ virtual void mediaPlayerDidRemoveTextTrack(InbandTextTrackPrivate&) { }
+ virtual void mediaPlayerDidRemoveVideoTrack(VideoTrackPrivate&) { }
virtual void textTrackRepresentationBoundsChanged(const IntRect&) { }
+#if ENABLE(AVF_CAPTIONS)
+ virtual Vector<RefPtr<PlatformTextTrack>> outOfBandTrackSources() { return Vector<RefPtr<PlatformTextTrack>>(); }
+#endif
#endif
+#if PLATFORM(IOS)
+ virtual String mediaPlayerNetworkInterfaceName() const { return String(); }
+ virtual bool mediaPlayerGetRawCookies(const URL&, Vector<Cookie>&) const { return false; }
+#endif
+
virtual bool mediaPlayerShouldWaitForResponseToAuthenticationChallenge(const AuthenticationChallenge&) { return false; }
+ virtual void mediaPlayerHandlePlaybackCommand(PlatformMediaSession::RemoteControlCommandType) { }
+
+ virtual String mediaPlayerSourceApplicationIdentifier() const { return emptyString(); }
+
+ virtual bool mediaPlayerIsInMediaDocument() const { return false; }
+ virtual void mediaPlayerEngineFailedToLoad() const { }
+
+ virtual double mediaPlayerRequestedPlaybackRate() const { return 0; }
+ virtual MediaPlayerEnums::VideoFullscreenMode mediaPlayerFullscreenMode() const { return MediaPlayerEnums::VideoFullscreenModeNone; }
+ virtual Vector<String> mediaPlayerPreferredAudioCharacteristics() const { return Vector<String>(); }
+
+#if USE(GSTREAMER)
+ virtual void requestInstallMissingPlugins(const String&, const String&, MediaPlayerRequestInstallMissingPluginsCallback&) { };
+#endif
+
+ virtual bool mediaPlayerShouldDisableSleep() const { return false; }
};
class MediaPlayerSupportsTypeClient {
@@ -270,48 +283,62 @@ public:
virtual String mediaPlayerDocumentHost() const { return String(); }
};
-class MediaPlayer {
+class MediaPlayer : public MediaPlayerEnums, public RefCounted<MediaPlayer> {
WTF_MAKE_NONCOPYABLE(MediaPlayer); WTF_MAKE_FAST_ALLOCATED;
public:
-
- static PassOwnPtr<MediaPlayer> create(MediaPlayerClient* client)
- {
- return adoptPtr(new MediaPlayer(client));
- }
+ static Ref<MediaPlayer> create(MediaPlayerClient&);
virtual ~MediaPlayer();
+ void invalidate();
+
// Media engine support.
enum SupportsType { IsNotSupported, IsSupported, MayBeSupported };
static MediaPlayer::SupportsType supportsType(const MediaEngineSupportParameters&, const MediaPlayerSupportsTypeClient*);
- static void getSupportedTypes(HashSet<String>&);
+ static void getSupportedTypes(HashSet<String, ASCIICaseInsensitiveHash>&);
static bool isAvailable();
- static void getSitesInMediaCache(Vector<String>&);
- static void clearMediaCache();
- static void clearMediaCacheForSite(const String&);
+ static HashSet<RefPtr<SecurityOrigin>> originsInMediaCache(const String& path);
+ static void clearMediaCache(const String& path, std::chrono::system_clock::time_point modifiedSince);
+ static void clearMediaCacheForOrigins(const String& path, const HashSet<RefPtr<SecurityOrigin>>&);
+ static bool supportsKeySystem(const String& keySystem, const String& mimeType);
bool supportsFullscreen() const;
- bool supportsSave() const;
bool supportsScanning() const;
+ bool canSaveMediaData() const;
bool requiresImmediateCompositing() const;
+ bool doesHaveAttribute(const AtomicString&, AtomicString* value = nullptr) const;
PlatformMedia platformMedia() const;
-#if USE(ACCELERATED_COMPOSITING)
PlatformLayer* platformLayer() const;
+
+#if PLATFORM(IOS) || (PLATFORM(MAC) && ENABLE(VIDEO_PRESENTATION_MODE))
+ void setVideoFullscreenLayer(PlatformLayer*, std::function<void()> completionHandler = [] { });
+ void setVideoFullscreenFrame(FloatRect);
+ using MediaPlayerEnums::VideoGravity;
+ void setVideoFullscreenGravity(VideoGravity);
+ void setVideoFullscreenMode(VideoFullscreenMode);
+ VideoFullscreenMode fullscreenMode() const;
#endif
- IntSize naturalSize();
+#if PLATFORM(IOS)
+ NSArray *timedMetadata() const;
+ String accessLog() const;
+ String errorLog() const;
+#endif
+
+ FloatSize naturalSize();
bool hasVideo() const;
bool hasAudio() const;
- void setFrameView(FrameView* frameView) { m_frameView = frameView; }
- FrameView* frameView() { return m_frameView; }
- bool inMediaDocument();
+ bool inMediaDocument() const;
IntSize size() const { return m_size; }
void setSize(const IntSize& size);
bool load(const URL&, const ContentType&, const String& keySystem);
#if ENABLE(MEDIA_SOURCE)
- bool load(const URL&, const ContentType&, PassRefPtr<HTMLMediaSource>);
+ bool load(const URL&, const ContentType&, MediaSourcePrivateClient*);
+#endif
+#if ENABLE(MEDIA_STREAM)
+ bool load(MediaStreamPrivate*);
#endif
void cancelLoad();
@@ -321,46 +348,49 @@ public:
void prepareToPlay();
void play();
void pause();
+ void setShouldBufferData(bool);
-#if ENABLE(ENCRYPTED_MEDIA)
+#if ENABLE(LEGACY_ENCRYPTED_MEDIA)
// Represents synchronous exceptions that can be thrown from the Encrypted Media methods.
// This is different from the asynchronous MediaKeyError.
enum MediaKeyException { NoError, InvalidPlayerState, KeySystemNotSupported };
- MediaKeyException generateKeyRequest(const String& keySystem, const unsigned char* initData, unsigned initDataLength);
- MediaKeyException addKey(const String& keySystem, const unsigned char* key, unsigned keyLength, const unsigned char* initData, unsigned initDataLength, const String& sessionId);
- MediaKeyException cancelKeyRequest(const String& keySystem, const String& sessionId);
+ std::unique_ptr<CDMSession> createSession(const String& keySystem, CDMSessionClient*);
+ void setCDMSession(CDMSession*);
+ void keyAdded();
#endif
bool paused() const;
bool seeking() const;
static double invalidTime() { return -1.0;}
- double duration() const;
- double currentTime() const;
- void seek(double time);
- void seekWithTolerance(double time, double negativeTolerance, double positiveTolerance);
+ MediaTime duration() const;
+ MediaTime currentTime() const;
+ void seek(const MediaTime&);
+ void seekWithTolerance(const MediaTime&, const MediaTime& negativeTolerance, const MediaTime& positiveTolerance);
- double startTime() const;
+ MediaTime startTime() const;
+ MediaTime initialTime() const;
- double initialTime() const;
+ MediaTime getStartDate() const;
double rate() const;
void setRate(double);
+ double requestedRate() const;
bool preservesPitch() const;
void setPreservesPitch(bool);
- PassRefPtr<TimeRanges> buffered();
- PassRefPtr<TimeRanges> seekable();
- double minTimeSeekable();
- double maxTimeSeekable();
+ std::unique_ptr<PlatformTimeRanges> buffered();
+ std::unique_ptr<PlatformTimeRanges> seekable();
+ MediaTime minTimeSeekable();
+ MediaTime maxTimeSeekable();
bool didLoadingProgress();
double volume() const;
void setVolume(double);
- bool platformVolumeConfigurationRequired() const { return m_mediaPlayerClient->mediaPlayerPlatformVolumeConfigurationRequired(); }
+ bool platformVolumeConfigurationRequired() const { return client().mediaPlayerPlatformVolumeConfigurationRequired(); }
bool muted() const;
void setMuted(bool);
@@ -371,8 +401,8 @@ public:
bool autoplay() const;
void setAutoplay(bool);
- void paint(GraphicsContext*, const IntRect&);
- void paintCurrentFrameInContext(GraphicsContext*, const IntRect&);
+ void paint(GraphicsContext&, const FloatRect&);
+ void paintCurrentFrameInContext(GraphicsContext&, const FloatRect&);
// copyVideoTextureToPlatformTexture() is used to do the GPU-GPU textures copy without a readback to system memory.
// The first five parameters denote the corresponding GraphicsContext, destination texture, requested level, requested type and the required internalFormat for destination texture.
@@ -387,20 +417,20 @@ public:
// In chromium, the implementation is based on GL_CHROMIUM_copy_texture extension which is documented at
// http://src.chromium.org/viewvc/chrome/trunk/src/gpu/GLES2/extensions/CHROMIUM/CHROMIUM_copy_texture.txt and implemented at
// http://src.chromium.org/viewvc/chrome/trunk/src/gpu/command_buffer/service/gles2_cmd_copy_texture_chromium.cc via shaders.
- bool copyVideoTextureToPlatformTexture(GraphicsContext3D*, Platform3DObject texture, GC3Dint level, GC3Denum type, GC3Denum internalFormat, bool premultiplyAlpha, bool flipY);
+ bool copyVideoTextureToPlatformTexture(GraphicsContext3D*, Platform3DObject texture, GC3Denum target, GC3Dint level, GC3Denum internalFormat, GC3Denum format, GC3Denum type, bool premultiplyAlpha, bool flipY);
- PassNativeImagePtr nativeImageForCurrentTime();
+ NativeImagePtr nativeImageForCurrentTime();
- enum NetworkState { Empty, Idle, Loading, Loaded, FormatError, NetworkError, DecodeError };
+ using MediaPlayerEnums::NetworkState;
NetworkState networkState();
- enum ReadyState { HaveNothing, HaveMetadata, HaveCurrentData, HaveFutureData, HaveEnoughData };
+ using MediaPlayerEnums::ReadyState;
ReadyState readyState();
- enum MovieLoadType { Unknown, Download, StoredStream, LiveStream };
+ using MediaPlayerEnums::MovieLoadType;
MovieLoadType movieLoadType() const;
- enum Preload { None, MetaData, Auto };
+ using MediaPlayerEnums::Preload;
Preload preload() const;
void setPreload(Preload);
@@ -418,8 +448,7 @@ public:
void repaint();
- MediaPlayerClient* mediaPlayerClient() const { return m_mediaPlayerClient; }
- void clearMediaPlayerClient() { m_mediaPlayerClient = 0; }
+ MediaPlayerClient& client() const { return *m_client; }
bool hasAvailableVideoFrame() const;
void prepareForRendering();
@@ -427,22 +456,16 @@ public:
bool canLoadPoster() const;
void setPoster(const String&);
-#if ENABLE(PLUGIN_PROXY_FOR_VIDEO)
- void deliverNotification(MediaPlayerProxyNotificationType notification);
- void setMediaPlayerProxy(WebMediaPlayerProxy* proxy);
- void setControls(bool);
-#endif
-
-#if ENABLE(PLUGIN_PROXY_FOR_VIDEO) || USE(NATIVE_FULLSCREEN_VIDEO)
+#if USE(NATIVE_FULLSCREEN_VIDEO)
void enterFullscreen();
void exitFullscreen();
#endif
-#if ENABLE(IOS_AIRPLAY)
- bool isCurrentPlaybackTargetWireless() const;
- void showPlaybackTargetPicker();
+#if ENABLE(WIRELESS_PLAYBACK_TARGET)
+ enum WirelessPlaybackTargetType { TargetTypeNone, TargetTypeAirPlay, TargetTypeTVOut };
+ WirelessPlaybackTargetType wirelessPlaybackTargetType() const;
- bool hasWirelessPlaybackTargets() const;
+ String wirelessPlaybackTargetName() const;
bool wirelessVideoPlaybackDisabled() const;
void setWirelessVideoPlaybackDisabled(bool);
@@ -450,19 +473,24 @@ public:
void currentPlaybackTargetIsWirelessChanged();
void playbackTargetAvailabilityChanged();
- void setHasPlaybackTargetAvailabilityListeners(bool);
+ bool isCurrentPlaybackTargetWireless() const;
+ bool canPlayToWirelessPlaybackTarget() const;
+ void setWirelessPlaybackTarget(Ref<MediaPlaybackTarget>&&);
+
+ void setShouldPlayToPlaybackTarget(bool);
#endif
+ double minFastReverseRate() const;
+ double maxFastForwardRate() const;
+
#if USE(NATIVE_FULLSCREEN_VIDEO)
bool canEnterFullscreen() const;
#endif
-#if USE(ACCELERATED_COMPOSITING)
// whether accelerated rendering is supported by the media engine for the current media.
bool supportsAcceleratedRendering() const;
// called when the rendering system flips the into or out of accelerated rendering mode.
void acceleratedRenderingStateChanged();
-#endif
bool shouldMaintainAspectRatio() const;
void setShouldMaintainAspectRatio(bool);
@@ -475,7 +503,7 @@ public:
bool didPassCORSAccessCheck() const;
- double mediaTimeForTimeValue(double) const;
+ MediaTime mediaTimeForTimeValue(const MediaTime&) const;
double maximumDurationToCacheMediaTime() const;
@@ -490,50 +518,48 @@ public:
AudioSourceProvider* audioSourceProvider();
#endif
-#if ENABLE(ENCRYPTED_MEDIA)
- void keyAdded(const String& keySystem, const String& sessionId);
- void keyError(const String& keySystem, const String& sessionId, MediaPlayerClient::MediaKeyErrorCode, unsigned short systemCode);
- void keyMessage(const String& keySystem, const String& sessionId, const unsigned char* message, unsigned messageLength, const URL& defaultURL);
- bool keyNeeded(const String& keySystem, const String& sessionId, const unsigned char* initData, unsigned initDataLength);
-#endif
-
-#if ENABLE(ENCRYPTED_MEDIA_V2)
+#if ENABLE(LEGACY_ENCRYPTED_MEDIA)
+ RefPtr<ArrayBuffer> cachedKeyForKeyId(const String& keyId) const;
bool keyNeeded(Uint8Array* initData);
+ String mediaKeysStorageDirectory() const;
#endif
String referrer() const;
String userAgent() const;
String engineDescription() const;
-
-#if PLATFORM(IOS)
- void attributeChanged(const String& name, const String& value);
- bool readyForPlayback() const;
-#endif
+ long platformErrorCode() const;
CachedResourceLoader* cachedResourceLoader();
+ PassRefPtr<PlatformMediaResourceLoader> createResourceLoader();
#if ENABLE(VIDEO_TRACK)
- void addAudioTrack(PassRefPtr<AudioTrackPrivate>);
- void addTextTrack(PassRefPtr<InbandTextTrackPrivate>);
- void addVideoTrack(PassRefPtr<VideoTrackPrivate>);
- void removeAudioTrack(PassRefPtr<AudioTrackPrivate>);
- void removeTextTrack(PassRefPtr<InbandTextTrackPrivate>);
- void removeVideoTrack(PassRefPtr<VideoTrackPrivate>);
+ void addAudioTrack(AudioTrackPrivate&);
+ void addTextTrack(InbandTextTrackPrivate&);
+ void addVideoTrack(VideoTrackPrivate&);
+ void removeAudioTrack(AudioTrackPrivate&);
+ void removeTextTrack(InbandTextTrackPrivate&);
+ void removeVideoTrack(VideoTrackPrivate&);
bool requiresTextTrackRepresentation() const;
void setTextTrackRepresentation(TextTrackRepresentation*);
+ void syncTextTrackBounds();
+ void tracksChanged();
+#if ENABLE(AVF_CAPTIONS)
+ void notifyTrackModeChanged();
+ Vector<RefPtr<PlatformTextTrack>> outOfBandTrackSources();
+#endif
#endif
- static void resetMediaEngines();
-
-#if USE(PLATFORM_TEXT_TRACK_MENU)
- bool implementsTextTrackControls() const;
- PassRefPtr<PlatformTextTrackMenuInterface> textTrackMenu();
+#if PLATFORM(IOS)
+ String mediaPlayerNetworkInterfaceName() const;
+ bool getRawCookies(const URL&, Vector<Cookie>&) const;
#endif
+ static void resetMediaEngines();
+
#if USE(GSTREAMER)
- void simulateAudioInterruption();
+ WEBCORE_EXPORT void simulateAudioInterruption();
#endif
String languageOfPrimaryAudioTrack() const;
@@ -546,61 +572,70 @@ public:
unsigned long totalVideoFrames();
unsigned long droppedVideoFrames();
unsigned long corruptedVideoFrames();
- double totalFrameDelay();
+ MediaTime totalFrameDelay();
#endif
bool shouldWaitForResponseToAuthenticationChallenge(const AuthenticationChallenge&);
+ void handlePlaybackCommand(PlatformMediaSession::RemoteControlCommandType);
+ String sourceApplicationIdentifier() const;
+ Vector<String> preferredAudioCharacteristics() const;
+
+ bool ended() const;
+
+ void setShouldDisableSleep(bool);
+ bool shouldDisableSleep() const;
private:
- MediaPlayer(MediaPlayerClient*);
- MediaPlayerFactory* nextBestMediaEngine(MediaPlayerFactory*) const;
- void loadWithNextMediaEngine(MediaPlayerFactory*);
- void reloadTimerFired(Timer<MediaPlayer>&);
+ MediaPlayer(MediaPlayerClient&);
+
+ const MediaPlayerFactory* nextBestMediaEngine(const MediaPlayerFactory*) const;
+ void loadWithNextMediaEngine(const MediaPlayerFactory*);
+ void reloadTimerFired();
static void initializeMediaEngines();
- MediaPlayerClient* m_mediaPlayerClient;
- Timer<MediaPlayer> m_reloadTimer;
- OwnPtr<MediaPlayerPrivateInterface> m_private;
- MediaPlayerFactory* m_currentMediaEngine;
+ MediaPlayerClient* m_client;
+ Timer m_reloadTimer;
+ std::unique_ptr<MediaPlayerPrivateInterface> m_private;
+ const MediaPlayerFactory* m_currentMediaEngine;
URL m_url;
String m_contentMIMEType;
String m_contentTypeCodecs;
String m_keySystem;
- FrameView* m_frameView;
IntSize m_size;
Preload m_preload;
bool m_visible;
- double m_rate;
double m_volume;
bool m_muted;
bool m_preservesPitch;
bool m_privateBrowsing;
bool m_shouldPrepareToRender;
bool m_contentMIMETypeWasInferredFromExtension;
-#if ENABLE(PLUGIN_PROXY_FOR_VIDEO)
- WebMediaPlayerProxy* m_playerProxy; // not owned or used, passed to m_private
-#endif
+ bool m_initializingMediaEngine { false };
#if ENABLE(MEDIA_SOURCE)
- RefPtr<HTMLMediaSource> m_mediaSource;
+ RefPtr<MediaSourcePrivateClient> m_mediaSource;
+#endif
+#if ENABLE(MEDIA_STREAM)
+ RefPtr<MediaStreamPrivate> m_mediaStream;
#endif
};
-typedef PassOwnPtr<MediaPlayerPrivateInterface> (*CreateMediaEnginePlayer)(MediaPlayer*);
-typedef void (*MediaEngineSupportedTypes)(HashSet<String>& types);
+typedef std::function<std::unique_ptr<MediaPlayerPrivateInterface> (MediaPlayer*)> CreateMediaEnginePlayer;
+typedef void (*MediaEngineSupportedTypes)(HashSet<String, ASCIICaseInsensitiveHash>& types);
typedef MediaPlayer::SupportsType (*MediaEngineSupportsType)(const MediaEngineSupportParameters& parameters);
-typedef void (*MediaEngineGetSitesInMediaCache)(Vector<String>&);
-typedef void (*MediaEngineClearMediaCache)();
-typedef void (*MediaEngineClearMediaCacheForSite)(const String&);
+typedef HashSet<RefPtr<SecurityOrigin>> (*MediaEngineOriginsInMediaCache)(const String& path);
+typedef void (*MediaEngineClearMediaCache)(const String& path, std::chrono::system_clock::time_point modifiedSince);
+typedef void (*MediaEngineClearMediaCacheForOrigins)(const String& path, const HashSet<RefPtr<SecurityOrigin>>&);
+typedef bool (*MediaEngineSupportsKeySystem)(const String& keySystem, const String& mimeType);
typedef void (*MediaEngineRegistrar)(CreateMediaEnginePlayer, MediaEngineSupportedTypes, MediaEngineSupportsType,
- MediaEngineGetSitesInMediaCache, MediaEngineClearMediaCache, MediaEngineClearMediaCacheForSite);
+ MediaEngineOriginsInMediaCache, MediaEngineClearMediaCache, MediaEngineClearMediaCacheForOrigins, MediaEngineSupportsKeySystem);
typedef void (*MediaEngineRegister)(MediaEngineRegistrar);
class MediaPlayerFactorySupport {
public:
- static void callRegisterMediaEngine(MediaEngineRegister);
+ WEBCORE_EXPORT static void callRegisterMediaEngine(MediaEngineRegister);
};
}
diff --git a/Source/WebCore/platform/graphics/FontWidthVariant.h b/Source/WebCore/platform/graphics/MediaPlayerEnums.h
index f44329745..3486be3eb 100644
--- a/Source/WebCore/platform/graphics/FontWidthVariant.h
+++ b/Source/WebCore/platform/graphics/MediaPlayerEnums.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2011 Apple Inc. All rights reserved.
+ * Copyright (C) 2015 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -23,23 +23,26 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef FontWidthVariant_h
-#define FontWidthVariant_h
+#ifndef MediaPlayerEnums_h
+#define MediaPlayerEnums_h
namespace WebCore {
-enum FontWidthVariant {
- RegularWidth,
- HalfWidth,
- ThirdWidth,
- QuarterWidth,
- LastFontWidthVariant = QuarterWidth
+class MediaPlayerEnums {
+public:
+ enum NetworkState { Empty, Idle, Loading, Loaded, FormatError, NetworkError, DecodeError };
+ enum ReadyState { HaveNothing, HaveMetadata, HaveCurrentData, HaveFutureData, HaveEnoughData };
+ enum MovieLoadType { Unknown, Download, StoredStream, LiveStream };
+ enum Preload { None, MetaData, Auto };
+ enum VideoGravity { VideoGravityResize, VideoGravityResizeAspect, VideoGravityResizeAspectFill };
+ enum {
+ VideoFullscreenModeNone = 0,
+ VideoFullscreenModeStandard = 1 << 0,
+ VideoFullscreenModePictureInPicture = 1 << 1,
+ };
+ typedef uint32_t VideoFullscreenMode;
};
-const unsigned FontWidthVariantWidth = 2;
+}
-COMPILE_ASSERT(LastFontWidthVariant >> FontWidthVariantWidth == 0, FontWidthVariantWidth_is_correct);
-
-} // namespace WebCore
-
-#endif // FontWidthVariant_h
+#endif
diff --git a/Source/WebCore/platform/graphics/MediaPlayerPrivate.h b/Source/WebCore/platform/graphics/MediaPlayerPrivate.h
index fbafd70cd..43db87b13 100644
--- a/Source/WebCore/platform/graphics/MediaPlayerPrivate.h
+++ b/Source/WebCore/platform/graphics/MediaPlayerPrivate.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009, 2010, 2011, 2012, 2013 Apple Inc. All rights reserved.
+ * Copyright (C) 2009-2014 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -10,10 +10,10 @@
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
- * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
@@ -29,13 +29,14 @@
#if ENABLE(VIDEO)
#include "MediaPlayer.h"
-#include "TimeRanges.h"
+#include "PlatformTimeRanges.h"
#include <wtf/Forward.h>
namespace WebCore {
class IntRect;
class IntSize;
+class MediaPlaybackTarget;
class PlatformTextTrack;
class MediaPlayerPrivateInterface {
@@ -46,25 +47,42 @@ public:
virtual void load(const String& url) = 0;
#if ENABLE(MEDIA_SOURCE)
- virtual void load(const String& url, PassRefPtr<HTMLMediaSource>) = 0;
+ virtual void load(const String& url, MediaSourcePrivateClient*) = 0;
+#endif
+#if ENABLE(MEDIA_STREAM)
+ virtual void load(MediaStreamPrivate&) = 0;
#endif
virtual void cancelLoad() = 0;
virtual void prepareToPlay() { }
virtual PlatformMedia platformMedia() const { return NoPlatformMedia; }
-#if USE(ACCELERATED_COMPOSITING)
virtual PlatformLayer* platformLayer() const { return 0; }
+
+#if PLATFORM(IOS) || (PLATFORM(MAC) && ENABLE(VIDEO_PRESENTATION_MODE))
+ virtual void setVideoFullscreenLayer(PlatformLayer*, std::function<void()> completionHandler) { completionHandler(); }
+ virtual void setVideoFullscreenFrame(FloatRect) { }
+ virtual void setVideoFullscreenGravity(MediaPlayer::VideoGravity) { }
+ virtual void setVideoFullscreenMode(MediaPlayer::VideoFullscreenMode) { }
+#endif
+
+#if PLATFORM(IOS)
+ virtual NSArray *timedMetadata() const { return 0; }
+ virtual String accessLog() const { return emptyString(); }
+ virtual String errorLog() const { return emptyString(); }
#endif
+ virtual long platformErrorCode() const { return 0; }
virtual void play() = 0;
virtual void pause() = 0;
+ virtual void setShouldBufferData(bool) { }
virtual bool supportsFullscreen() const { return false; }
- virtual bool supportsSave() const { return false; }
virtual bool supportsScanning() const { return false; }
virtual bool requiresImmediateCompositing() const { return false; }
- virtual IntSize naturalSize() const = 0;
+ virtual bool canSaveMediaData() const { return false; }
+
+ virtual FloatSize naturalSize() const = 0;
virtual bool hasVideo() const = 0;
virtual bool hasAudio() const = 0;
@@ -73,23 +91,27 @@ public:
virtual float duration() const { return 0; }
virtual double durationDouble() const { return duration(); }
+ virtual MediaTime durationMediaTime() const { return MediaTime::createWithDouble(durationDouble()); }
virtual float currentTime() const { return 0; }
virtual double currentTimeDouble() const { return currentTime(); }
+ virtual MediaTime currentMediaTime() const { return MediaTime::createWithDouble(currentTimeDouble()); }
+
+ virtual MediaTime getStartDate() const { return MediaTime::createWithDouble(std::numeric_limits<double>::quiet_NaN()); }
virtual void seek(float) { }
virtual void seekDouble(double time) { seek(time); }
- virtual void seekWithTolerance(double time, double, double) { seekDouble(time); }
+ virtual void seek(const MediaTime& time) { seekDouble(time.toDouble()); }
+ virtual void seekWithTolerance(const MediaTime& time, const MediaTime&, const MediaTime&) { seek(time); }
virtual bool seeking() const = 0;
- virtual float startTime() const { return 0; }
- virtual double startTimeDouble() const { return startTime(); }
-
- virtual double initialTime() const { return 0; }
+ virtual MediaTime startTime() const { return MediaTime::zeroTime(); }
+ virtual MediaTime initialTime() const { return MediaTime::zeroTime(); }
virtual void setRate(float) { }
virtual void setRateDouble(double rate) { setRate(rate); }
+ virtual double rate() const { return 0; }
virtual void setPreservesPitch(bool) { }
@@ -107,24 +129,29 @@ public:
virtual bool hasClosedCaptions() const { return false; }
virtual void setClosedCaptionsVisible(bool) { }
+ virtual double maxFastForwardRate() const { return std::numeric_limits<double>::infinity(); }
+ virtual double minFastReverseRate() const { return -std::numeric_limits<double>::infinity(); }
+
virtual MediaPlayer::NetworkState networkState() const = 0;
virtual MediaPlayer::ReadyState readyState() const = 0;
- virtual PassRefPtr<TimeRanges> seekable() const { return maxTimeSeekableDouble() ? TimeRanges::create(minTimeSeekable(), maxTimeSeekableDouble()) : TimeRanges::create(); }
+ virtual std::unique_ptr<PlatformTimeRanges> seekable() const { return maxMediaTimeSeekable() == MediaTime::zeroTime() ? std::make_unique<PlatformTimeRanges>() : std::make_unique<PlatformTimeRanges>(minMediaTimeSeekable(), maxMediaTimeSeekable()); }
virtual float maxTimeSeekable() const { return 0; }
- virtual double maxTimeSeekableDouble() const { return maxTimeSeekable(); }
+ virtual MediaTime maxMediaTimeSeekable() const { return MediaTime::createWithDouble(maxTimeSeekable()); }
virtual double minTimeSeekable() const { return 0; }
- virtual PassRefPtr<TimeRanges> buffered() const = 0;
+ virtual MediaTime minMediaTimeSeekable() const { return MediaTime::createWithDouble(minTimeSeekable()); }
+ virtual std::unique_ptr<PlatformTimeRanges> buffered() const = 0;
+ virtual unsigned long long totalBytes() const { return 0; }
virtual bool didLoadingProgress() const = 0;
virtual void setSize(const IntSize&) = 0;
- virtual void paint(GraphicsContext*, const IntRect&) = 0;
+ virtual void paint(GraphicsContext&, const FloatRect&) = 0;
- virtual void paintCurrentFrameInContext(GraphicsContext* c, const IntRect& r) { paint(c, r); }
- virtual bool copyVideoTextureToPlatformTexture(GraphicsContext3D*, Platform3DObject, GC3Dint, GC3Denum, GC3Denum, bool, bool) { return false; }
- virtual PassNativeImagePtr nativeImageForCurrentTime() { return 0; }
+ virtual void paintCurrentFrameInContext(GraphicsContext& c, const FloatRect& r) { paint(c, r); }
+ virtual bool copyVideoTextureToPlatformTexture(GraphicsContext3D*, Platform3DObject, GC3Denum, GC3Dint, GC3Denum, GC3Denum, GC3Denum, bool, bool) { return false; }
+ virtual NativeImagePtr nativeImageForCurrentTime() { return nullptr; }
virtual void setPreload(MediaPlayer::Preload) { }
@@ -133,39 +160,34 @@ public:
virtual bool canLoadPoster() const { return false; }
virtual void setPoster(const String&) { }
-#if ENABLE(PLUGIN_PROXY_FOR_VIDEO)
- virtual void deliverNotification(MediaPlayerProxyNotificationType) { }
- virtual void setMediaPlayerProxy(WebMediaPlayerProxy*) { }
- virtual void setControls(bool) { }
-#endif
-
-#if ENABLE(PLUGIN_PROXY_FOR_VIDEO) || USE(NATIVE_FULLSCREEN_VIDEO)
+#if USE(NATIVE_FULLSCREEN_VIDEO)
virtual void enterFullscreen() { }
virtual void exitFullscreen() { }
#endif
-#if ENABLE(IOS_AIRPLAY)
- virtual bool isCurrentPlaybackTargetWireless() const { return false; }
- virtual void showPlaybackTargetPicker() { }
+#if ENABLE(WIRELESS_PLAYBACK_TARGET)
- virtual bool hasWirelessPlaybackTargets() const { return false; }
+ virtual String wirelessPlaybackTargetName() const { return emptyString(); }
+ virtual MediaPlayer::WirelessPlaybackTargetType wirelessPlaybackTargetType() const { return MediaPlayer::TargetTypeNone; }
- virtual bool wirelessVideoPlaybackDisabled() const { return false; }
+ virtual bool wirelessVideoPlaybackDisabled() const { return true; }
virtual void setWirelessVideoPlaybackDisabled(bool) { }
- virtual void setHasPlaybackTargetAvailabilityListeners(bool) { }
+ virtual bool canPlayToWirelessPlaybackTarget() const { return false; }
+ virtual bool isCurrentPlaybackTargetWireless() const { return false; }
+ virtual void setWirelessPlaybackTarget(Ref<MediaPlaybackTarget>&&) { }
+
+ virtual void setShouldPlayToPlaybackTarget(bool) { }
#endif
#if USE(NATIVE_FULLSCREEN_VIDEO)
virtual bool canEnterFullscreen() const { return false; }
#endif
-#if USE(ACCELERATED_COMPOSITING)
// whether accelerated rendering is supported by the media engine for the current media.
virtual bool supportsAcceleratedRendering() const { return false; }
// called when the rendering system flips the into or out of accelerated rendering mode.
virtual void acceleratedRenderingStateChanged() { }
-#endif
virtual bool shouldMaintainAspectRatio() const { return true; }
virtual void setShouldMaintainAspectRatio(bool) { }
@@ -180,8 +202,7 @@ public:
// Time value in the movie's time scale. It is only necessary to override this if the media
// engine uses rational numbers to represent media time.
- virtual float mediaTimeForTimeValue(float timeValue) const { return timeValue; }
- virtual double mediaTimeForTimeValueDouble(double timeValue) const { return timeValue; }
+ virtual MediaTime mediaTimeForTimeValue(const MediaTime& timeValue) const { return timeValue; }
// Overide this if it is safe for HTMLMediaElement to cache movie time and report
// 'currentTime' as [cached time + elapsed wall time]. Returns the maximum wall time
@@ -193,9 +214,9 @@ public:
virtual unsigned audioDecodedByteCount() const { return 0; }
virtual unsigned videoDecodedByteCount() const { return 0; }
- void getSitesInMediaCache(Vector<String>&) { }
- void clearMediaCache() { }
- void clearMediaCacheForSite(const String&) { }
+ HashSet<RefPtr<SecurityOrigin>> originsInMediaCache(const String&) { return { }; }
+ void clearMediaCache(const String&, std::chrono::system_clock::time_point) { }
+ void clearMediaCacheForOrigins(const String&, const HashSet<RefPtr<SecurityOrigin>>&) { }
virtual void setPrivateBrowsingMode(bool) { }
@@ -205,43 +226,53 @@ public:
virtual AudioSourceProvider* audioSourceProvider() { return 0; }
#endif
-#if ENABLE(ENCRYPTED_MEDIA)
- virtual MediaPlayer::MediaKeyException addKey(const String&, const unsigned char*, unsigned, const unsigned char*, unsigned, const String&) { return MediaPlayer::KeySystemNotSupported; }
- virtual MediaPlayer::MediaKeyException generateKeyRequest(const String&, const unsigned char*, unsigned) { return MediaPlayer::KeySystemNotSupported; }
- virtual MediaPlayer::MediaKeyException cancelKeyRequest(const String&, const String&) { return MediaPlayer::KeySystemNotSupported; }
+#if ENABLE(LEGACY_ENCRYPTED_MEDIA)
+ virtual std::unique_ptr<CDMSession> createSession(const String&, CDMSessionClient*) { return nullptr; }
+ virtual void setCDMSession(CDMSession*) { }
+ virtual void keyAdded() { }
#endif
#if ENABLE(VIDEO_TRACK)
virtual bool requiresTextTrackRepresentation() const { return false; }
virtual void setTextTrackRepresentation(TextTrackRepresentation*) { }
-#endif
-
-#if USE(PLATFORM_TEXT_TRACK_MENU)
- virtual bool implementsTextTrackControls() const { return false; }
- virtual PassRefPtr<PlatformTextTrackMenuInterface> textTrackMenu() { return 0; }
+ virtual void syncTextTrackBounds() { };
+ virtual void tracksChanged() { };
#endif
#if USE(GSTREAMER)
virtual void simulateAudioInterruption() { }
#endif
-#if PLATFORM(IOS)
- virtual void attributeChanged(const String&, const String&) { }
- virtual bool readyForPlayback() const { return true; }
-#endif
-
virtual String languageOfPrimaryAudioTrack() const { return emptyString(); }
- virtual size_t extraMemoryCost() const { return 0; }
-
+ virtual size_t extraMemoryCost() const
+ {
+ MediaTime duration = this->durationMediaTime();
+ if (!duration)
+ return 0;
+
+ unsigned long long extra = totalBytes() * buffered()->totalDuration().toDouble() / duration.toDouble();
+ return static_cast<unsigned>(extra);
+ }
+
virtual unsigned long long fileSize() const { return 0; }
+ virtual bool ended() const { return false; }
+
#if ENABLE(MEDIA_SOURCE)
virtual unsigned long totalVideoFrames() { return 0; }
virtual unsigned long droppedVideoFrames() { return 0; }
virtual unsigned long corruptedVideoFrames() { return 0; }
- virtual double totalFrameDelay() { return 0; }
+ virtual MediaTime totalFrameDelay() { return MediaTime::zeroTime(); }
#endif
+
+#if ENABLE(AVF_CAPTIONS)
+ virtual void notifyTrackModeChanged() { }
+#endif
+
+ virtual void notifyActiveSourceBuffersChanged() { }
+
+ virtual void setShouldDisableSleep(bool) { }
};
}
diff --git a/Source/WebCore/platform/graphics/gstreamer/SourceBufferPrivateGStreamer.h b/Source/WebCore/platform/graphics/MediaSourcePrivate.h
index 4a9eb4abd..718658910 100644
--- a/Source/WebCore/platform/graphics/gstreamer/SourceBufferPrivateGStreamer.h
+++ b/Source/WebCore/platform/graphics/MediaSourcePrivate.h
@@ -1,6 +1,5 @@
/*
* Copyright (C) 2013 Google Inc. All rights reserved.
- * Copyright (C) 2013 Orange
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
@@ -28,35 +27,40 @@
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
+#ifndef MediaSourcePrivate_h
+#define MediaSourcePrivate_h
-#ifndef SourceBufferPrivateGStreamer_h
-#define SourceBufferPrivateGStreamer_h
+#if ENABLE(MEDIA_SOURCE)
-#if ENABLE(MEDIA_SOURCE) && USE(GSTREAMER)
-
-#include "SourceBufferPrivate.h"
-#include "WebKitMediaSourceGStreamer.h"
+#include "MediaPlayer.h"
+#include <wtf/Forward.h>
+#include <wtf/RefCounted.h>
+#include <wtf/Vector.h>
namespace WebCore {
-class SourceBufferPrivateGStreamer final : public SourceBufferPrivate {
+class ContentType;
+class SourceBufferPrivate;
+
+class MediaSourcePrivate : public RefCounted<MediaSourcePrivate> {
public:
- SourceBufferPrivateGStreamer(PassRefPtr<MediaSourceClientGstreamer>, const ContentType&);
- ~SourceBufferPrivateGStreamer() { }
+ typedef Vector<String> CodecsArray;
+
+ MediaSourcePrivate() { }
+ virtual ~MediaSourcePrivate() { }
+
+ enum AddStatus { Ok, NotSupported, ReachedIdLimit };
+ virtual AddStatus addSourceBuffer(const ContentType&, RefPtr<SourceBufferPrivate>&) = 0;
+ virtual void durationChanged() = 0;
+ enum EndOfStreamStatus { EosNoError, EosNetworkError, EosDecodeError };
+ virtual void markEndOfStream(EndOfStreamStatus) = 0;
+ virtual void unmarkEndOfStream() = 0;
- void setClient(SourceBufferPrivateClient*) { }
- AppendResult append(const unsigned char*, unsigned);
- void abort();
- void removedFromMediaSource();
- MediaPlayer::ReadyState readyState() const { return m_readyState; }
- void setReadyState(MediaPlayer::ReadyState readyState) { m_readyState = readyState; }
- void evictCodedFrames() { }
- bool isFull() { return false; }
+ virtual MediaPlayer::ReadyState readyState() const = 0;
+ virtual void setReadyState(MediaPlayer::ReadyState) = 0;
-private:
- String m_type;
- RefPtr<MediaSourceClientGstreamer> m_client;
- MediaPlayer::ReadyState m_readyState;
+ virtual void waitForSeekCompleted() = 0;
+ virtual void seekCompleted() = 0;
};
}
diff --git a/Source/WebCore/platform/graphics/win/SharedGDIObject.h b/Source/WebCore/platform/graphics/MediaSourcePrivateClient.h
index dd2683a77..3f6863593 100644
--- a/Source/WebCore/platform/graphics/win/SharedGDIObject.h
+++ b/Source/WebCore/platform/graphics/MediaSourcePrivateClient.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2013 Apple, Inc. All rights reserved.
+ * Copyright (C) 2013 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -20,44 +20,35 @@
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef SharedGDIObject_h
-#define SharedGDIObject_h
+#ifndef MediaSourcePrivateClient_h
+#define MediaSourcePrivateClient_h
-#include <wtf/PassRefPtr.h>
+#if ENABLE(MEDIA_SOURCE)
+
+#include "PlatformTimeRanges.h"
#include <wtf/RefCounted.h>
-#include <wtf/win/GDIObject.h>
namespace WebCore {
-template <typename T> class SharedGDIObject : public RefCounted<SharedGDIObject<T>> {
+class MediaSourcePrivate;
+
+class MediaSourcePrivateClient : public RefCounted<MediaSourcePrivateClient> {
public:
- static PassRefPtr<SharedGDIObject> create(GDIObject<T> object)
- {
- return adoptRef(new SharedGDIObject<T>(std::move(object)));
- }
-
- T get() const
- {
- return m_object.get();
- }
-
- unsigned hash() const
- {
- return WTF::PtrHash<T>::hash(m_object.get());
- }
-
-private:
- explicit SharedGDIObject(GDIObject<T> object)
- : m_object(std::move(object))
- {
- }
-
- GDIObject<T> m_object;
+ virtual ~MediaSourcePrivateClient() { }
+
+ virtual void setPrivateAndOpen(Ref<MediaSourcePrivate>&&) = 0;
+ virtual MediaTime duration() const = 0;
+ virtual void durationChanged(const MediaTime&) = 0;
+ virtual std::unique_ptr<PlatformTimeRanges> buffered() const = 0;
+ virtual void seekToTime(const MediaTime&) = 0;
+ virtual void monitorSourceBuffers() = 0;
};
-} // namespace WebCore
+}
+
+#endif // ENABLE(MEDIA_SOURCE)
-#endif // SharedGDIObject_h
+#endif
diff --git a/Source/WebCore/platform/graphics/NamedImageGeneratedImage.cpp b/Source/WebCore/platform/graphics/NamedImageGeneratedImage.cpp
new file mode 100644
index 000000000..591b4dde2
--- /dev/null
+++ b/Source/WebCore/platform/graphics/NamedImageGeneratedImage.cpp
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2015 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "NamedImageGeneratedImage.h"
+
+#include "FloatRect.h"
+#include "GraphicsContext.h"
+#include "ImageBuffer.h"
+#include "TextStream.h"
+#include "Theme.h"
+
+namespace WebCore {
+
+NamedImageGeneratedImage::NamedImageGeneratedImage(String name, const FloatSize& size)
+ : m_name(name)
+{
+ setContainerSize(size);
+}
+
+void NamedImageGeneratedImage::draw(GraphicsContext& context, const FloatRect& dstRect, const FloatRect& srcRect, CompositeOperator compositeOp, BlendMode blendMode, ImageOrientationDescription)
+{
+#if USE(NEW_THEME) || PLATFORM(IOS)
+ GraphicsContextStateSaver stateSaver(context);
+ context.setCompositeOperation(compositeOp, blendMode);
+ context.clip(dstRect);
+ context.translate(dstRect.x(), dstRect.y());
+ if (dstRect.size() != srcRect.size())
+ context.scale(FloatSize(dstRect.width() / srcRect.width(), dstRect.height() / srcRect.height()));
+ context.translate(-srcRect.x(), -srcRect.y());
+
+ platformTheme()->drawNamedImage(m_name, context, dstRect);
+#else
+ UNUSED_PARAM(context);
+ UNUSED_PARAM(dstRect);
+ UNUSED_PARAM(srcRect);
+ UNUSED_PARAM(compositeOp);
+ UNUSED_PARAM(blendMode);
+#endif
+}
+
+void NamedImageGeneratedImage::drawPattern(GraphicsContext& context, const FloatRect& dstRect, const FloatRect& srcRect, const AffineTransform& patternTransform, const FloatPoint& phase, const FloatSize& spacing, CompositeOperator compositeOp, BlendMode blendMode)
+{
+#if USE(NEW_THEME)
+ auto imageBuffer = ImageBuffer::createCompatibleBuffer(size(), context);
+ if (!imageBuffer)
+ return;
+
+ GraphicsContext& graphicsContext = imageBuffer->context();
+ platformTheme()->drawNamedImage(m_name, graphicsContext, FloatRect(0, 0, size().width(), size().height()));
+
+ // Tile the image buffer into the context.
+ imageBuffer->drawPattern(context, dstRect, srcRect, patternTransform, phase, spacing, compositeOp, blendMode);
+#else
+ UNUSED_PARAM(context);
+ UNUSED_PARAM(srcRect);
+ UNUSED_PARAM(patternTransform);
+ UNUSED_PARAM(phase);
+ UNUSED_PARAM(spacing);
+ UNUSED_PARAM(dstRect);
+ UNUSED_PARAM(compositeOp);
+ UNUSED_PARAM(blendMode);
+#endif
+}
+
+void NamedImageGeneratedImage::dump(TextStream& ts) const
+{
+ GeneratedImage::dump(ts);
+ ts.dumpProperty("name", m_name);
+}
+
+}
diff --git a/Source/WebCore/platform/graphics/NamedImageGeneratedImage.h b/Source/WebCore/platform/graphics/NamedImageGeneratedImage.h
new file mode 100644
index 000000000..1bac56af9
--- /dev/null
+++ b/Source/WebCore/platform/graphics/NamedImageGeneratedImage.h
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2015 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef NamedImageGeneratedImage_h
+#define NamedImageGeneratedImage_h
+
+#include "FloatSize.h"
+#include "GeneratedImage.h"
+#include "Image.h"
+
+namespace WebCore {
+
+class NamedImageGeneratedImage final : public GeneratedImage {
+public:
+ static Ref<NamedImageGeneratedImage> create(String name, const FloatSize& size)
+ {
+ return adoptRef(*new NamedImageGeneratedImage(name, size));
+ }
+
+protected:
+ void draw(GraphicsContext&, const FloatRect& dstRect, const FloatRect& srcRect, CompositeOperator, BlendMode, ImageOrientationDescription) override;
+ void drawPattern(GraphicsContext&, const FloatRect& dstRect, const FloatRect& srcRect, const AffineTransform& patternTransform, const FloatPoint& phase, const FloatSize& spacing, CompositeOperator, BlendMode) override;
+
+ NamedImageGeneratedImage(String name, const FloatSize&);
+
+private:
+ bool isNamedImageGeneratedImage() const override { return true; }
+ void dump(TextStream&) const override;
+
+ String m_name;
+};
+
+}
+
+#endif
diff --git a/Source/WebCore/platform/graphics/NativeImagePtr.h b/Source/WebCore/platform/graphics/NativeImage.h
index bd2c3267b..4a4f1d0ef 100644
--- a/Source/WebCore/platform/graphics/NativeImagePtr.h
+++ b/Source/WebCore/platform/graphics/NativeImage.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2004, 2005, 2006 Apple Computer, Inc. All rights reserved.
+ * Copyright (C) 2004, 2005, 2006, 2016 Apple Inc. All rights reserved.
* Copyright (C) 2007-2008 Torch Mobile, Inc.
* Copyright (C) 2012 Company 100 Inc.
*
@@ -12,23 +12,26 @@
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
- * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef NativeImagePtr_h
-#define NativeImagePtr_h
+#pragma once
+
+#include "GraphicsTypes.h"
+#include "ImageOrientation.h"
#if USE(CG)
+#include <wtf/RetainPtr.h>
typedef struct CGImage* CGImageRef;
#elif USE(CAIRO)
#include "RefPtrCairo.h"
@@ -36,23 +39,35 @@ typedef struct CGImage* CGImageRef;
#include "SharedBitmap.h"
#endif
+#if USE(DIRECT2D)
+#include "COMPtr.h"
+#include <d2d1.h>
+#endif
+
namespace WebCore {
-// FIXME: NativeImagePtr and PassNativeImagePtr should be smart
-// pointers (see SVGImage::nativeImageForCurrentFrame()).
+class Color;
+class FloatRect;
+class IntSize;
+class GraphicsContext;
+
#if USE(CG)
-typedef CGImageRef NativeImagePtr;
+typedef RetainPtr<CGImageRef> NativeImagePtr;
+#elif USE(DIRECT2D)
+typedef COMPtr<ID2D1Bitmap> NativeImagePtr;
#elif USE(CAIRO)
typedef RefPtr<cairo_surface_t> NativeImagePtr;
-typedef PassRefPtr<cairo_surface_t> PassNativeImagePtr;
#elif USE(WINGDI)
typedef RefPtr<SharedBitmap> NativeImagePtr;
#endif
-#if !USE(CAIRO)
-typedef NativeImagePtr PassNativeImagePtr;
-#endif
+IntSize nativeImageSize(const NativeImagePtr&);
+bool nativeImageHasAlpha(const NativeImagePtr&);
+Color nativeImageSinglePixelSolidColor(const NativeImagePtr&);
-}
+float subsamplingScale(GraphicsContext&, const FloatRect& destRect, const FloatRect& srcRect);
-#endif
+void drawNativeImage(const NativeImagePtr&, GraphicsContext&, const FloatRect&, const FloatRect&, const IntSize&, CompositeOperator, BlendMode, const ImageOrientation&);
+void clearNativeImageSubimages(const NativeImagePtr&);
+
+}
diff --git a/Source/WebCore/platform/graphics/OpenGLESShims.h b/Source/WebCore/platform/graphics/OpenGLESShims.h
index f299156c8..b4e951572 100644
--- a/Source/WebCore/platform/graphics/OpenGLESShims.h
+++ b/Source/WebCore/platform/graphics/OpenGLESShims.h
@@ -10,10 +10,10 @@
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
- * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
@@ -26,7 +26,7 @@
#ifndef OpenGLESShims_h
#define OpenGLESShims_h
-#if PLATFORM(GTK) || PLATFORM(EFL) || PLATFORM(WIN)
+#if PLATFORM(GTK) || PLATFORM(WIN)
#define glBindFramebufferEXT glBindFramebuffer
#define glFramebufferTexture2DEXT glFramebufferTexture2D
#define glBindRenderbufferEXT glBindRenderbuffer
@@ -53,6 +53,12 @@
#define GL_READ_FRAMEBUFFER_EXT 0x8CA8
#define GL_DRAW_FRAMEBUFFER_EXT 0x8CA9
#define FRAMEBUFFER_INCOMPLETE_MULTISAMPLE_EXT 0x9134
+#define GL_VERTEX_SHADER 0x8B31
+#define GL_FRAGMENT_SHADER 0x8B30
+#define GL_SAMPLER_2D 0x8B5E
+#define GL_SAMPLER_CUBE 0x8B60
+#define GL_SAMPLER_2D_RECT_ARB 0x8B63
+#define GL_NONE 0
#endif
#endif // OpenGLESShims_h
diff --git a/Source/WebCore/platform/graphics/OpenGLShims.cpp b/Source/WebCore/platform/graphics/OpenGLShims.cpp
index d77a8b0a6..d3cfd27d5 100644
--- a/Source/WebCore/platform/graphics/OpenGLShims.cpp
+++ b/Source/WebCore/platform/graphics/OpenGLShims.cpp
@@ -17,12 +17,12 @@
*/
#include "config.h"
-#if USE(3D_GRAPHICS) || defined(QT_OPENGL_SHIMS)
+#if ENABLE(GRAPHICS_CONTEXT_3D)
#define DISABLE_SHIMS
#include "OpenGLShims.h"
-#if !OS(WINDOWS)
+#if !PLATFORM(WIN)
#include <dlfcn.h>
#endif
@@ -37,10 +37,10 @@ OpenGLFunctionTable* openGLFunctionTable()
return &table;
}
-#if OS(WINDOWS)
+#if PLATFORM(WIN)
static void* getProcAddress(const char* procName)
{
- return reinterpret_cast<void*>(GetProcAddress(GetModuleHandleA("libGLESv2"), procName));
+ return GetProcAddress(GetModuleHandleA("libGLESv2"), procName);
}
#else
typedef void* (*glGetProcAddressType) (const char* procName);
@@ -70,26 +70,19 @@ static void* lookupOpenGLFunctionAddress(const char* functionName, bool* success
if (target)
return target;
- String fullFunctionName(functionName);
- fullFunctionName.append("ARB");
- target = getProcAddress(fullFunctionName.utf8().data());
+ target = getProcAddress(reinterpret_cast<const char*>(makeString(functionName, "ARB").characters8()));
if (target)
return target;
- fullFunctionName = functionName;
- fullFunctionName.append("EXT");
- target = getProcAddress(fullFunctionName.utf8().data());
+ // FIXME: <https://webkit.org/b/143964> OpenGLShims appears to have a dead store if GLES2
+ target = getProcAddress(reinterpret_cast<const char*>(makeString(functionName, "EXT").characters8()));
#if defined(GL_ES_VERSION_2_0)
- fullFunctionName = functionName;
- fullFunctionName.append("ANGLE");
- target = getProcAddress(fullFunctionName.utf8().data());
+ target = getProcAddress(reinterpret_cast<const char*>(makeString(functionName, "ANGLE").characters8()));
if (target)
return target;
- fullFunctionName = functionName;
- fullFunctionName.append("APPLE");
- target = getProcAddress(fullFunctionName.utf8().data());
+ target = getProcAddress(reinterpret_cast<const char*>(makeString(functionName, "APPLE").characters8()));
#endif
// A null address is still a failure case.
@@ -146,6 +139,9 @@ bool initializeOpenGLShims()
ASSIGN_FUNCTION_TABLE_ENTRY_EXT(glDeleteVertexArrays);
ASSIGN_FUNCTION_TABLE_ENTRY(glDetachShader, success);
ASSIGN_FUNCTION_TABLE_ENTRY(glDisableVertexAttribArray, success);
+ ASSIGN_FUNCTION_TABLE_ENTRY(glDrawArraysInstanced, success);
+ ASSIGN_FUNCTION_TABLE_ENTRY(glDrawBuffers, success);
+ ASSIGN_FUNCTION_TABLE_ENTRY(glDrawElementsInstanced, success);
ASSIGN_FUNCTION_TABLE_ENTRY(glEnableVertexAttribArray, success);
ASSIGN_FUNCTION_TABLE_ENTRY(glFramebufferRenderbuffer, success);
ASSIGN_FUNCTION_TABLE_ENTRY(glFramebufferTexture2D, success);
@@ -166,6 +162,9 @@ bool initializeOpenGLShims()
ASSIGN_FUNCTION_TABLE_ENTRY(glGetShaderInfoLog, success);
ASSIGN_FUNCTION_TABLE_ENTRY(glGetShaderiv, success);
ASSIGN_FUNCTION_TABLE_ENTRY(glGetShaderSource, success);
+ // glGetStringi is only available on OpenGL or GLES versions >= 3.0.
+ // Add it with _EXT so it doesn't cause an initialization failure on lower versions.
+ ASSIGN_FUNCTION_TABLE_ENTRY_EXT(glGetStringi);
ASSIGN_FUNCTION_TABLE_ENTRY(glGetUniformfv, success);
ASSIGN_FUNCTION_TABLE_ENTRY(glGetUniformiv, success);
ASSIGN_FUNCTION_TABLE_ENTRY(glGetUniformLocation, success);
@@ -220,6 +219,7 @@ bool initializeOpenGLShims()
ASSIGN_FUNCTION_TABLE_ENTRY(glVertexAttrib3fv, success);
ASSIGN_FUNCTION_TABLE_ENTRY(glVertexAttrib4f, success);
ASSIGN_FUNCTION_TABLE_ENTRY(glVertexAttrib4fv, success);
+ ASSIGN_FUNCTION_TABLE_ENTRY(glVertexAttribDivisor, success);
ASSIGN_FUNCTION_TABLE_ENTRY(glVertexAttribPointer, success);
if (!success)
@@ -229,4 +229,4 @@ bool initializeOpenGLShims()
} // namespace WebCore
-#endif // USE(3D_GRAPHICS)
+#endif // ENABLE(GRAPHICS_CONTEXT_3D)
diff --git a/Source/WebCore/platform/graphics/OpenGLShims.h b/Source/WebCore/platform/graphics/OpenGLShims.h
index c0e6e6012..a0b8e2ae5 100644
--- a/Source/WebCore/platform/graphics/OpenGLShims.h
+++ b/Source/WebCore/platform/graphics/OpenGLShims.h
@@ -69,6 +69,9 @@ typedef void (GLAPIENTRY *glDeleteShaderType) (GLuint);
typedef void (GLAPIENTRY *glDeleteVertexArraysType) (GLsizei, const GLuint*);
typedef void (GLAPIENTRY *glDetachShaderType) (GLuint, GLuint);
typedef void (GLAPIENTRY *glDisableVertexAttribArrayType) (GLuint);
+typedef void (GLAPIENTRY *glDrawArraysInstancedType) (GLenum, GLint, GLsizei, GLsizei);
+typedef void (GLAPIENTRY *glDrawBuffersType) (GLsizei, const GLenum*);
+typedef void (GLAPIENTRY *glDrawElementsInstancedType) (GLenum, GLsizei, GLenum, const GLvoid*, GLsizei);
typedef void (GLAPIENTRY *glEnableVertexAttribArrayType) (GLuint);
typedef void (GLAPIENTRY *glFramebufferRenderbufferType) (GLenum, GLenum, GLenum, GLuint);
typedef void (GLAPIENTRY *glFramebufferTexture2DType) (GLenum, GLenum, GLenum, GLuint, GLint);
@@ -89,6 +92,7 @@ typedef void (GLAPIENTRY *glGetRenderbufferParameterivType) (GLenum, GLenum, GLi
typedef void (GLAPIENTRY *glGetShaderInfoLogType) (GLuint, GLsizei, GLsizei*, char*);
typedef void (GLAPIENTRY *glGetShaderivType) (GLuint, GLenum, GLint*);
typedef void (GLAPIENTRY *glGetShaderSourceType) (GLuint, GLsizei, GLsizei*, char*);
+typedef const GLubyte* (GLAPIENTRY *glGetStringiType) (GLenum, GLuint);
typedef GLint (GLAPIENTRY *glGetUniformLocationType) (GLuint, const char*);
typedef void (GLAPIENTRY *glGetUniformfvType) (GLuint, GLint, GLfloat*);
typedef void (GLAPIENTRY *glGetUniformivType) (GLuint, GLint, GLint*);
@@ -138,6 +142,7 @@ typedef void (GLAPIENTRY *glVertexAttrib3fType) (GLuint, const GLfloat, const GL
typedef void (GLAPIENTRY *glVertexAttrib3fvType) (GLuint, const GLfloat*);
typedef void (GLAPIENTRY *glVertexAttrib4fType) (GLuint, const GLfloat, const GLfloat, const GLfloat, const GLfloat);
typedef void (GLAPIENTRY *glVertexAttrib4fvType) (GLuint, const GLfloat*);
+typedef void (GLAPIENTRY *glVertexAttribDivisorType) (GLuint, GLuint);
typedef void (GLAPIENTRY *glVertexAttribPointerType) (GLuint, GLint, GLenum, GLboolean, GLsizei, const GLvoid*);
#define FUNCTION_TABLE_ENTRY(FunctionName) FunctionName##Type FunctionName
@@ -171,6 +176,9 @@ typedef struct _OpenGLFunctionTable {
FUNCTION_TABLE_ENTRY(glDeleteVertexArrays);
FUNCTION_TABLE_ENTRY(glDetachShader);
FUNCTION_TABLE_ENTRY(glDisableVertexAttribArray);
+ FUNCTION_TABLE_ENTRY(glDrawArraysInstanced);
+ FUNCTION_TABLE_ENTRY(glDrawBuffers);
+ FUNCTION_TABLE_ENTRY(glDrawElementsInstanced);
FUNCTION_TABLE_ENTRY(glEnableVertexAttribArray);
FUNCTION_TABLE_ENTRY(glFramebufferRenderbuffer);
FUNCTION_TABLE_ENTRY(glFramebufferTexture2D);
@@ -191,6 +199,7 @@ typedef struct _OpenGLFunctionTable {
FUNCTION_TABLE_ENTRY(glGetShaderInfoLog);
FUNCTION_TABLE_ENTRY(glGetShaderiv);
FUNCTION_TABLE_ENTRY(glGetShaderSource);
+ FUNCTION_TABLE_ENTRY(glGetStringi);
FUNCTION_TABLE_ENTRY(glGetUniformfv);
FUNCTION_TABLE_ENTRY(glGetUniformiv);
FUNCTION_TABLE_ENTRY(glGetUniformLocation);
@@ -240,6 +249,7 @@ typedef struct _OpenGLFunctionTable {
FUNCTION_TABLE_ENTRY(glVertexAttrib3fv);
FUNCTION_TABLE_ENTRY(glVertexAttrib4f);
FUNCTION_TABLE_ENTRY(glVertexAttrib4fv);
+ FUNCTION_TABLE_ENTRY(glVertexAttribDivisor);
FUNCTION_TABLE_ENTRY(glVertexAttribPointer);
} OpenGLFunctionTable;
@@ -282,6 +292,12 @@ typedef struct _OpenGLFunctionTable {
#define glDeleteVertexArrays LOOKUP_GL_FUNCTION(glDeleteVertexArrays)
#define glDetachShader LOOKUP_GL_FUNCTION(glDetachShader)
#define glDisableVertexAttribArray LOOKUP_GL_FUNCTION(glDisableVertexAttribArray)
+#define glDrawArraysInstancedEXT glDrawArraysInstanced
+#define glDrawArraysInstanced LOOKUP_GL_FUNCTION(glDrawArraysInstanced)
+#define glDrawBuffersEXT glDrawBuffers
+#define glDrawBuffers LOOKUP_GL_FUNCTION(glDrawBuffers)
+#define glDrawElementsInstancedEXT glDrawElementsInstanced
+#define glDrawElementsInstanced LOOKUP_GL_FUNCTION(glDrawElementsInstanced)
#define glEnableVertexAttribArray LOOKUP_GL_FUNCTION(glEnableVertexAttribArray)
#define glFramebufferRenderbufferEXT glFramebufferRenderbuffer
#define glFramebufferRenderbuffer LOOKUP_GL_FUNCTION(glFramebufferRenderbuffer)
@@ -311,6 +327,7 @@ typedef struct _OpenGLFunctionTable {
#define glGetShaderInfoLog LOOKUP_GL_FUNCTION(glGetShaderInfoLog)
#define glGetShaderiv LOOKUP_GL_FUNCTION(glGetShaderiv)
#define glGetShaderSource LOOKUP_GL_FUNCTION(glGetShaderSource)
+#define glGetStringi LOOKUP_GL_FUNCTION(glGetStringi)
#define glGetUniformfv LOOKUP_GL_FUNCTION(glGetUniformfv)
#define glGetUniformiv LOOKUP_GL_FUNCTION(glGetUniformiv)
#define glGetUniformLocation LOOKUP_GL_FUNCTION(glGetUniformLocation)
@@ -365,6 +382,8 @@ typedef struct _OpenGLFunctionTable {
#define glVertexAttrib3fv LOOKUP_GL_FUNCTION(glVertexAttrib3fv)
#define glVertexAttrib4f LOOKUP_GL_FUNCTION(glVertexAttrib4f)
#define glVertexAttrib4fv LOOKUP_GL_FUNCTION(glVertexAttrib4fv)
+#define glVertexAttribDivisorEXT glVertexAttribDivisor
+#define glVertexAttribDivisor LOOKUP_GL_FUNCTION(glVertexAttribDivisor)
#define glVertexAttribPointer LOOKUP_GL_FUNCTION(glVertexAttribPointer)
#endif
diff --git a/Source/WebCore/platform/graphics/Path.cpp b/Source/WebCore/platform/graphics/Path.cpp
index 88d453c3a..9a3596bf2 100644
--- a/Source/WebCore/platform/graphics/Path.cpp
+++ b/Source/WebCore/platform/graphics/Path.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003, 2006 Apple Computer, Inc. All rights reserved.
+ * Copyright (C) 2003, 2006 Apple Inc. All rights reserved.
* 2006 Rob Buis <buis@kde.org>
* Copyright (C) 2007 Eric Seidel <eric@webkit.org>
*
@@ -12,10 +12,10 @@
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
- * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
@@ -31,69 +31,48 @@
#include "FloatPoint.h"
#include "FloatRect.h"
+#include "FloatRoundedRect.h"
#include "PathTraversalState.h"
#include "RoundedRect.h"
+#include "TextStream.h"
#include <math.h>
#include <wtf/MathExtras.h>
namespace WebCore {
-static void pathLengthApplierFunction(void* info, const PathElement* element)
-{
- PathTraversalState& traversalState = *static_cast<PathTraversalState*>(info);
- if (traversalState.m_success)
- return;
- FloatPoint* points = element->points;
- float segmentLength = 0;
- switch (element->type) {
- case PathElementMoveToPoint:
- segmentLength = traversalState.moveTo(points[0]);
- break;
- case PathElementAddLineToPoint:
- segmentLength = traversalState.lineTo(points[0]);
- break;
- case PathElementAddQuadCurveToPoint:
- segmentLength = traversalState.quadraticBezierTo(points[0], points[1]);
- break;
- case PathElementAddCurveToPoint:
- segmentLength = traversalState.cubicBezierTo(points[0], points[1], points[2]);
- break;
- case PathElementCloseSubpath:
- segmentLength = traversalState.closeSubpath();
- break;
- }
- traversalState.m_totalLength += segmentLength;
- traversalState.processSegment();
-}
-
+#if !USE(DIRECT2D)
float Path::length() const
{
- PathTraversalState traversalState(PathTraversalState::TraversalTotalLength);
- apply(&traversalState, pathLengthApplierFunction);
- return traversalState.m_totalLength;
+ PathTraversalState traversalState(PathTraversalState::Action::TotalLength);
+
+ apply([&traversalState](const PathElement& element) {
+ traversalState.processPathElement(element);
+ });
+
+ return traversalState.totalLength();
}
+#endif
-FloatPoint Path::pointAtLength(float length, bool& ok) const
+PathTraversalState Path::traversalStateAtLength(float length, bool& success) const
{
- PathTraversalState traversalState(PathTraversalState::TraversalPointAtLength);
- traversalState.m_desiredLength = length;
- apply(&traversalState, pathLengthApplierFunction);
- ok = traversalState.m_success;
- return traversalState.m_current;
+ PathTraversalState traversalState(PathTraversalState::Action::VectorAtLength, length);
+
+ apply([&traversalState](const PathElement& element) {
+ traversalState.processPathElement(element);
+ });
+
+ success = traversalState.success();
+ return traversalState;
}
-float Path::normalAngleAtLength(float length, bool& ok) const
+FloatPoint Path::pointAtLength(float length, bool& success) const
{
- PathTraversalState traversalState(PathTraversalState::TraversalNormalAngleAtLength);
- traversalState.m_desiredLength = length ? length : std::numeric_limits<float>::epsilon();
- apply(&traversalState, pathLengthApplierFunction);
- ok = traversalState.m_success;
- return traversalState.m_normalAngle;
+ return traversalStateAtLength(length, success).current();
}
-void Path::addRoundedRect(const RoundedRect& r)
+float Path::normalAngleAtLength(float length, bool& success) const
{
- addRoundedRect(r.rect(), r.radii().topLeft(), r.radii().topRight(), r.radii().bottomLeft(), r.radii().bottomRight());
+ return traversalStateAtLength(length, success).normalAngle();
}
void Path::addRoundedRect(const FloatRect& rect, const FloatSize& roundingRadii, RoundedRectStrategy strategy)
@@ -105,7 +84,7 @@ void Path::addRoundedRect(const FloatRect& rect, const FloatSize& roundingRadii,
FloatSize halfSize(rect.width() / 2, rect.height() / 2);
// Apply the SVG corner radius constraints, per the rect section of the SVG shapes spec: if
- // one of rx,ry is negative, then the other corner radius value is used. If both values are
+ // one of rx,ry is negative, then the other corner radius value is used. If both values are
// negative then rx = ry = 0. If rx is greater than half of the width of the rectangle
// then set rx to half of the width; ry is handled similarly.
@@ -121,41 +100,37 @@ void Path::addRoundedRect(const FloatRect& rect, const FloatSize& roundingRadii,
if (radius.height() > halfSize.height())
radius.setHeight(halfSize.height());
- addPathForRoundedRect(rect, radius, radius, radius, radius, strategy);
+ addRoundedRect(FloatRoundedRect(rect, radius, radius, radius, radius), strategy);
}
-void Path::addRoundedRect(const FloatRect& rect, const FloatSize& topLeftRadius, const FloatSize& topRightRadius, const FloatSize& bottomLeftRadius, const FloatSize& bottomRightRadius, RoundedRectStrategy strategy)
+void Path::addRoundedRect(const FloatRoundedRect& r, RoundedRectStrategy strategy)
{
- if (rect.isEmpty())
+ if (r.isEmpty())
return;
- if (rect.width() < topLeftRadius.width() + topRightRadius.width()
- || rect.width() < bottomLeftRadius.width() + bottomRightRadius.width()
- || rect.height() < topLeftRadius.height() + bottomLeftRadius.height()
- || rect.height() < topRightRadius.height() + bottomRightRadius.height()) {
+ const FloatRoundedRect::Radii& radii = r.radii();
+ const FloatRect& rect = r.rect();
+
+ if (!r.isRenderable()) {
// If all the radii cannot be accommodated, return a rect.
addRect(rect);
return;
}
- addPathForRoundedRect(rect, topLeftRadius, topRightRadius, bottomLeftRadius, bottomRightRadius, strategy);
-}
-
-void Path::addPathForRoundedRect(const FloatRect& rect, const FloatSize& topLeftRadius, const FloatSize& topRightRadius, const FloatSize& bottomLeftRadius, const FloatSize& bottomRightRadius, RoundedRectStrategy strategy)
-{
if (strategy == PreferNativeRoundedRect) {
-#if USE(CG)
- platformAddPathForRoundedRect(rect, topLeftRadius, topRightRadius, bottomLeftRadius, bottomRightRadius);
+#if USE(CG) || USE(DIRECT2D)
+ platformAddPathForRoundedRect(rect, radii.topLeft(), radii.topRight(), radii.bottomLeft(), radii.bottomRight());
return;
#endif
}
- addBeziersForRoundedRect(rect, topLeftRadius, topRightRadius, bottomLeftRadius, bottomRightRadius);
+ addBeziersForRoundedRect(rect, radii.topLeft(), radii.topRight(), radii.bottomLeft(), radii.bottomRight());
}
-// Approximation of control point positions on a bezier to simulate a quarter of a circle.
-// This is 1-kappa, where kappa = 4 * (sqrt(2) - 1) / 3
-static const float gCircleControlPoint = 0.447715f;
+void Path::addRoundedRect(const RoundedRect& r)
+{
+ addRoundedRect(FloatRoundedRect(r));
+}
void Path::addBeziersForRoundedRect(const FloatRect& rect, const FloatSize& topLeftRadius, const FloatSize& topRightRadius, const FloatSize& bottomLeftRadius, const FloatSize& bottomRightRadius)
{
@@ -163,33 +138,85 @@ void Path::addBeziersForRoundedRect(const FloatRect& rect, const FloatSize& topL
addLineTo(FloatPoint(rect.maxX() - topRightRadius.width(), rect.y()));
if (topRightRadius.width() > 0 || topRightRadius.height() > 0)
- addBezierCurveTo(FloatPoint(rect.maxX() - topRightRadius.width() * gCircleControlPoint, rect.y()),
- FloatPoint(rect.maxX(), rect.y() + topRightRadius.height() * gCircleControlPoint),
+ addBezierCurveTo(FloatPoint(rect.maxX() - topRightRadius.width() * circleControlPoint(), rect.y()),
+ FloatPoint(rect.maxX(), rect.y() + topRightRadius.height() * circleControlPoint()),
FloatPoint(rect.maxX(), rect.y() + topRightRadius.height()));
addLineTo(FloatPoint(rect.maxX(), rect.maxY() - bottomRightRadius.height()));
if (bottomRightRadius.width() > 0 || bottomRightRadius.height() > 0)
- addBezierCurveTo(FloatPoint(rect.maxX(), rect.maxY() - bottomRightRadius.height() * gCircleControlPoint),
- FloatPoint(rect.maxX() - bottomRightRadius.width() * gCircleControlPoint, rect.maxY()),
+ addBezierCurveTo(FloatPoint(rect.maxX(), rect.maxY() - bottomRightRadius.height() * circleControlPoint()),
+ FloatPoint(rect.maxX() - bottomRightRadius.width() * circleControlPoint(), rect.maxY()),
FloatPoint(rect.maxX() - bottomRightRadius.width(), rect.maxY()));
addLineTo(FloatPoint(rect.x() + bottomLeftRadius.width(), rect.maxY()));
if (bottomLeftRadius.width() > 0 || bottomLeftRadius.height() > 0)
- addBezierCurveTo(FloatPoint(rect.x() + bottomLeftRadius.width() * gCircleControlPoint, rect.maxY()),
- FloatPoint(rect.x(), rect.maxY() - bottomLeftRadius.height() * gCircleControlPoint),
+ addBezierCurveTo(FloatPoint(rect.x() + bottomLeftRadius.width() * circleControlPoint(), rect.maxY()),
+ FloatPoint(rect.x(), rect.maxY() - bottomLeftRadius.height() * circleControlPoint()),
FloatPoint(rect.x(), rect.maxY() - bottomLeftRadius.height()));
addLineTo(FloatPoint(rect.x(), rect.y() + topLeftRadius.height()));
if (topLeftRadius.width() > 0 || topLeftRadius.height() > 0)
- addBezierCurveTo(FloatPoint(rect.x(), rect.y() + topLeftRadius.height() * gCircleControlPoint),
- FloatPoint(rect.x() + topLeftRadius.width() * gCircleControlPoint, rect.y()),
+ addBezierCurveTo(FloatPoint(rect.x(), rect.y() + topLeftRadius.height() * circleControlPoint()),
+ FloatPoint(rect.x() + topLeftRadius.width() * circleControlPoint(), rect.y()),
FloatPoint(rect.x() + topLeftRadius.width(), rect.y()));
closeSubpath();
}
-#if !USE(CG)
+#if !USE(CG) && !USE(DIRECT2D)
+Path Path::polygonPathFromPoints(const Vector<FloatPoint>& points)
+{
+ Path path;
+ if (points.size() < 2)
+ return path;
+
+ path.moveTo(points[0]);
+ for (size_t i = 1; i < points.size(); ++i)
+ path.addLineTo(points[i]);
+
+ path.closeSubpath();
+ return path;
+}
+
FloatRect Path::fastBoundingRect() const
{
return boundingRect();
}
#endif
+#ifndef NDEBUG
+void Path::dump() const
+{
+ TextStream stream;
+ stream << *this;
+ WTFLogAlways("%s", stream.release().utf8().data());
+}
+#endif
+
+TextStream& operator<<(TextStream& stream, const Path& path)
+{
+ bool isFirst = true;
+ path.apply([&stream, &isFirst](const PathElement& element) {
+ if (!isFirst)
+ stream << ", ";
+ isFirst = false;
+ switch (element.type) {
+ case PathElementMoveToPoint: // The points member will contain 1 value.
+ stream << "move to " << element.points[0];
+ break;
+ case PathElementAddLineToPoint: // The points member will contain 1 value.
+ stream << "add line to " << element.points[0];
+ break;
+ case PathElementAddQuadCurveToPoint: // The points member will contain 2 values.
+ stream << "add quad curve to " << element.points[0] << " " << element.points[1];
+ break;
+ case PathElementAddCurveToPoint: // The points member will contain 3 values.
+ stream << "add curve to " << element.points[0] << " " << element.points[1] << " " << element.points[2];
+ break;
+ case PathElementCloseSubpath: // The points member will contain no values.
+ stream << "close subpath";
+ break;
+ }
+ });
+
+ return stream;
+}
+
}
diff --git a/Source/WebCore/platform/graphics/Path.h b/Source/WebCore/platform/graphics/Path.h
index 161f846ae..2831cfe33 100644
--- a/Source/WebCore/platform/graphics/Path.h
+++ b/Source/WebCore/platform/graphics/Path.h
@@ -1,6 +1,6 @@
/*
- * Copyright (C) 2003, 2006, 2009 Apple Inc. All rights reserved.
- * 2006 Rob Buis <buis@kde.org>
+ * Copyright (C) 2003, 2006, 2009, 2016 Apple Inc. All rights reserved.
+ * Copyright (C) 2006 Rob Buis <buis@kde.org>
* Copyright (C) 2007-2008 Torch Mobile, Inc.
*
* Redistribution and use in source and binary forms, with or without
@@ -12,10 +12,10 @@
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
- * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
@@ -28,24 +28,47 @@
#ifndef Path_h
#define Path_h
+#include "FloatRect.h"
#include "WindRule.h"
+#include <functional>
#include <wtf/FastMalloc.h>
#include <wtf/Forward.h>
+#include <wtf/Vector.h>
#if USE(CG)
+
+#include <wtf/RetainPtr.h>
+#include <CoreGraphics/CGPath.h>
typedef struct CGPath PlatformPath;
+
+#elif USE(DIRECT2D)
+#include "COMPtr.h"
+
+interface ID2D1Geometry;
+interface ID2D1GeometryGroup;
+interface ID2D1PathGeometry;
+interface ID2D1GeometrySink;
+
+typedef ID2D1GeometryGroup PlatformPath;
+
#elif USE(CAIRO)
+
namespace WebCore {
class CairoPath;
}
typedef WebCore::CairoPath PlatformPath;
+
#elif USE(WINGDI)
+
namespace WebCore {
- class PlatformPath;
+class PlatformPath;
}
typedef WebCore::PlatformPath PlatformPath;
+
#else
+
typedef void PlatformPath;
+
#endif
typedef PlatformPath* PlatformPathPtr;
@@ -54,11 +77,13 @@ namespace WebCore {
class AffineTransform;
class FloatPoint;
- class FloatRect;
+ class FloatRoundedRect;
class FloatSize;
class GraphicsContext;
+ class PathTraversalState;
class RoundedRect;
class StrokeStyleApplier;
+ class TextStream;
enum PathElementType {
PathElementMoveToPoint, // The points member will contain 1 value.
@@ -68,7 +93,7 @@ namespace WebCore {
PathElementCloseSubpath // The points member will contain no values.
};
- // The points in the sturcture are the same as those that would be used with the
+ // The points in the structure are the same as those that would be used with the
// add... method. For example, a line returns the endpoint, while a cubic returns
// two tangent points and the endpoint.
struct PathElement {
@@ -76,30 +101,36 @@ namespace WebCore {
FloatPoint* points;
};
- typedef void (*PathApplierFunction)(void* info, const PathElement*);
+ typedef std::function<void (const PathElement&)> PathApplierFunction;
class Path {
WTF_MAKE_FAST_ALLOCATED;
public:
- Path();
- ~Path();
+ WEBCORE_EXPORT Path();
+#if USE(CG)
+ Path(RetainPtr<CGMutablePathRef>);
+#endif
+ WEBCORE_EXPORT ~Path();
- Path(const Path&);
- Path& operator=(const Path&);
+ WEBCORE_EXPORT Path(const Path&);
+ WEBCORE_EXPORT Path& operator=(const Path&);
+
+ static Path polygonPathFromPoints(const Vector<FloatPoint>&);
bool contains(const FloatPoint&, WindRule rule = RULE_NONZERO) const;
bool strokeContains(StrokeStyleApplier*, const FloatPoint&) const;
// fastBoundingRect() should equal or contain boundingRect(); boundingRect()
// should perfectly bound the points within the path.
FloatRect boundingRect() const;
- FloatRect fastBoundingRect() const;
+ WEBCORE_EXPORT FloatRect fastBoundingRect() const;
FloatRect strokeBoundingRect(StrokeStyleApplier* = 0) const;
-
+
float length() const;
- FloatPoint pointAtLength(float length, bool& ok) const;
- float normalAngleAtLength(float length, bool& ok) const;
+ PathTraversalState traversalStateAtLength(float length, bool& success) const;
+ FloatPoint pointAtLength(float length, bool& success) const;
+ float normalAngleAtLength(float length, bool& success) const;
- void clear();
+ WEBCORE_EXPORT void clear();
bool isNull() const { return !m_path; }
bool isEmpty() const;
// Gets the current point of the current path, which is conceptually the final point reached by the path so far.
@@ -107,15 +138,16 @@ namespace WebCore {
bool hasCurrentPoint() const;
FloatPoint currentPoint() const;
- void moveTo(const FloatPoint&);
- void addLineTo(const FloatPoint&);
- void addQuadCurveTo(const FloatPoint& controlPoint, const FloatPoint& endPoint);
- void addBezierCurveTo(const FloatPoint& controlPoint1, const FloatPoint& controlPoint2, const FloatPoint& endPoint);
+ WEBCORE_EXPORT void moveTo(const FloatPoint&);
+ WEBCORE_EXPORT void addLineTo(const FloatPoint&);
+ WEBCORE_EXPORT void addQuadCurveTo(const FloatPoint& controlPoint, const FloatPoint& endPoint);
+ WEBCORE_EXPORT void addBezierCurveTo(const FloatPoint& controlPoint1, const FloatPoint& controlPoint2, const FloatPoint& endPoint);
void addArcTo(const FloatPoint&, const FloatPoint&, float radius);
- void closeSubpath();
+ WEBCORE_EXPORT void closeSubpath();
void addArc(const FloatPoint&, float radius, float startAngle, float endAngle, bool anticlockwise);
void addRect(const FloatRect&);
+ void addEllipse(FloatPoint, float radiusX, float radiusY, float rotation, float startAngle, float endAngle, bool anticlockwise);
void addEllipse(const FloatRect&);
enum RoundedRectStrategy {
@@ -123,32 +155,65 @@ namespace WebCore {
PreferBezierRoundedRect
};
- void addRoundedRect(const FloatRect&, const FloatSize& roundingRadii, RoundedRectStrategy = PreferNativeRoundedRect);
- void addRoundedRect(const FloatRect&, const FloatSize& topLeftRadius, const FloatSize& topRightRadius, const FloatSize& bottomLeftRadius, const FloatSize& bottomRightRadius, RoundedRectStrategy = PreferNativeRoundedRect);
+ WEBCORE_EXPORT void addRoundedRect(const FloatRect&, const FloatSize& roundingRadii, RoundedRectStrategy = PreferNativeRoundedRect);
+ WEBCORE_EXPORT void addRoundedRect(const FloatRoundedRect&, RoundedRectStrategy = PreferNativeRoundedRect);
void addRoundedRect(const RoundedRect&);
+ void addPath(const Path&, const AffineTransform&);
+
void translate(const FloatSize&);
// To keep Path() cheap, it does not allocate a PlatformPath immediately
// meaning Path::platformPath() can return null.
+#if USE(DIRECT2D)
+ PlatformPathPtr platformPath() const { return m_path.get(); }
+#else
PlatformPathPtr platformPath() const { return m_path; }
+#endif
// ensurePlatformPath() will allocate a PlatformPath if it has not yet been and will never return null.
- PlatformPathPtr ensurePlatformPath();
+ WEBCORE_EXPORT PlatformPathPtr ensurePlatformPath();
- void apply(void* info, PathApplierFunction) const;
+ WEBCORE_EXPORT void apply(const PathApplierFunction&) const;
void transform(const AffineTransform&);
- void addPathForRoundedRect(const FloatRect&, const FloatSize& topLeftRadius, const FloatSize& topRightRadius, const FloatSize& bottomLeftRadius, const FloatSize& bottomRightRadius, RoundedRectStrategy = PreferNativeRoundedRect);
+ static float circleControlPoint()
+ {
+ // Approximation of control point positions on a bezier to simulate a quarter of a circle.
+ // This is 1-kappa, where kappa = 4 * (sqrt(2) - 1) / 3
+ return 0.447715;
+ }
+
void addBeziersForRoundedRect(const FloatRect&, const FloatSize& topLeftRadius, const FloatSize& topRightRadius, const FloatSize& bottomLeftRadius, const FloatSize& bottomRightRadius);
-#if USE(CG)
+#if USE(CG) || USE(DIRECT2D)
void platformAddPathForRoundedRect(const FloatRect&, const FloatSize& topLeftRadius, const FloatSize& topRightRadius, const FloatSize& bottomLeftRadius, const FloatSize& bottomRightRadius);
#endif
+#if USE(DIRECT2D)
+ ID2D1GeometrySink* activePath() const { return m_activePath.get(); }
+ void appendGeometry(ID2D1Geometry*);
+ void createGeometryWithFillMode(WindRule, COMPtr<ID2D1GeometryGroup>&) const;
+ void drawDidComplete() const;
+
+ HRESULT initializePathState();
+#endif
+
+#ifndef NDEBUG
+ void dump() const;
+#endif
+
private:
- PlatformPathPtr m_path;
+#if USE(DIRECT2D)
+ COMPtr<ID2D1GeometryGroup> m_path;
+ COMPtr<ID2D1PathGeometry> m_activePathGeometry;
+ COMPtr<ID2D1GeometrySink> m_activePath;
+#else
+ PlatformPathPtr m_path { nullptr };
+#endif
};
+TextStream& operator<<(TextStream&, const Path&);
+
}
#endif
diff --git a/Source/WebCore/platform/graphics/PathTraversalState.cpp b/Source/WebCore/platform/graphics/PathTraversalState.cpp
index cf5371727..c6bf7a6bf 100644
--- a/Source/WebCore/platform/graphics/PathTraversalState.cpp
+++ b/Source/WebCore/platform/graphics/PathTraversalState.cpp
@@ -1,5 +1,6 @@
/*
* Copyright (C) 2006, 2007 Eric Seidel <eric@webkit.org>
+ * Copyright (C) 2015 Apple Inc. All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
@@ -34,7 +35,9 @@ static inline FloatPoint midPoint(const FloatPoint& first, const FloatPoint& sec
static inline float distanceLine(const FloatPoint& start, const FloatPoint& end)
{
- return sqrtf((end.x() - start.x()) * (end.x() - start.x()) + (end.y() - start.y()) * (end.y() - start.y()));
+ float dx = end.x() - start.x();
+ float dy = end.y() - start.y();
+ return sqrtf(dx * dx + dy * dy);
}
struct QuadraticBezier {
@@ -114,110 +117,149 @@ struct CubicBezier {
// Another check which is possible up-front (to send us down the fast path) would be to check if
// approximateDistance() + current total distance > desired distance
template<class CurveType>
-static float curveLength(PathTraversalState& traversalState, CurveType curve)
+static float curveLength(const PathTraversalState& traversalState, const CurveType& originalCurve, FloatPoint& previous, FloatPoint& current)
{
static const unsigned curveStackDepthLimit = 20;
-
- Vector<CurveType> curveStack;
- curveStack.append(curve);
-
+ CurveType curve = originalCurve;
+ Vector<CurveType, curveStackDepthLimit> curveStack;
float totalLength = 0;
- do {
+
+ while (true) {
float length = curve.approximateDistance();
- if ((length - distanceLine(curve.start, curve.end)) > kPathSegmentLengthTolerance && curveStack.size() <= curveStackDepthLimit) {
+
+ if ((length - distanceLine(curve.start, curve.end)) > kPathSegmentLengthTolerance && curveStack.size() < curveStackDepthLimit) {
CurveType leftCurve;
CurveType rightCurve;
curve.split(leftCurve, rightCurve);
curve = leftCurve;
curveStack.append(rightCurve);
- } else {
- totalLength += length;
- if (traversalState.m_action == PathTraversalState::TraversalPointAtLength
- || traversalState.m_action == PathTraversalState::TraversalNormalAngleAtLength) {
- traversalState.m_previous = curve.start;
- traversalState.m_current = curve.end;
- if (traversalState.m_totalLength + totalLength > traversalState.m_desiredLength)
- return totalLength;
- }
- curve = curveStack.last();
- curveStack.removeLast();
+ continue;
}
- } while (!curveStack.isEmpty());
-
+
+ totalLength += length;
+ if (traversalState.action() == PathTraversalState::Action::VectorAtLength) {
+ previous = curve.start;
+ current = curve.end;
+ if (traversalState.totalLength() + totalLength > traversalState.desiredLength())
+ break;
+ }
+
+ if (curveStack.isEmpty())
+ break;
+
+ curve = curveStack.last();
+ curveStack.removeLast();
+ }
+
+ if (traversalState.action() != PathTraversalState::Action::VectorAtLength) {
+ ASSERT(curve.end == originalCurve.end);
+ previous = curve.start;
+ current = curve.end;
+ }
+
return totalLength;
}
-PathTraversalState::PathTraversalState(PathTraversalAction action)
+PathTraversalState::PathTraversalState(Action action, float desiredLength)
: m_action(action)
- , m_success(false)
- , m_totalLength(0)
- , m_segmentIndex(0)
- , m_desiredLength(0)
- , m_normalAngle(0)
+ , m_desiredLength(desiredLength)
{
+ ASSERT(action != Action::TotalLength || !desiredLength);
}
-float PathTraversalState::closeSubpath()
+void PathTraversalState::closeSubpath()
{
- float distance = distanceLine(m_current, m_start);
- m_current = m_control1 = m_control2 = m_start;
- return distance;
+ m_totalLength += distanceLine(m_current, m_start);
+ m_current = m_start;
}
-float PathTraversalState::moveTo(const FloatPoint& point)
+void PathTraversalState::moveTo(const FloatPoint& point)
{
- m_current = m_start = m_control1 = m_control2 = point;
- return 0;
+ m_previous = m_current = m_start = point;
}
-float PathTraversalState::lineTo(const FloatPoint& point)
+void PathTraversalState::lineTo(const FloatPoint& point)
{
- float distance = distanceLine(m_current, point);
- m_current = m_control1 = m_control2 = point;
- return distance;
+ m_totalLength += distanceLine(m_current, point);
+ m_current = point;
}
-float PathTraversalState::quadraticBezierTo(const FloatPoint& newControl, const FloatPoint& newEnd)
+void PathTraversalState::quadraticBezierTo(const FloatPoint& newControl, const FloatPoint& newEnd)
{
- float distance = curveLength<QuadraticBezier>(*this, QuadraticBezier(m_current, newControl, newEnd));
-
- m_control1 = newControl;
- m_control2 = newEnd;
-
- if (m_action != TraversalPointAtLength && m_action != TraversalNormalAngleAtLength)
- m_current = newEnd;
+ m_totalLength += curveLength<QuadraticBezier>(*this, QuadraticBezier(m_current, newControl, newEnd), m_previous, m_current);
+}
- return distance;
+void PathTraversalState::cubicBezierTo(const FloatPoint& newControl1, const FloatPoint& newControl2, const FloatPoint& newEnd)
+{
+ m_totalLength += curveLength<CubicBezier>(*this, CubicBezier(m_current, newControl1, newControl2, newEnd), m_previous, m_current);
}
-float PathTraversalState::cubicBezierTo(const FloatPoint& newControl1, const FloatPoint& newControl2, const FloatPoint& newEnd)
+bool PathTraversalState::finalizeAppendPathElement()
{
- float distance = curveLength<CubicBezier>(*this, CubicBezier(m_current, newControl1, newControl2, newEnd));
+ if (m_action == Action::TotalLength)
+ return false;
- m_control1 = newEnd;
- m_control2 = newControl2;
-
- if (m_action != TraversalPointAtLength && m_action != TraversalNormalAngleAtLength)
- m_current = newEnd;
+ if (m_action == Action::SegmentAtLength) {
+ if (m_totalLength >= m_desiredLength)
+ m_success = true;
+ return m_success;
+ }
- return distance;
-}
+ ASSERT(m_action == Action::VectorAtLength);
-void PathTraversalState::processSegment()
-{
- if (m_action == TraversalSegmentAtLength && m_totalLength >= m_desiredLength)
- m_success = true;
-
- if ((m_action == TraversalPointAtLength || m_action == TraversalNormalAngleAtLength) && m_totalLength >= m_desiredLength) {
+ if (m_totalLength >= m_desiredLength) {
float slope = FloatPoint(m_current - m_previous).slopeAngleRadians();
- if (m_action == TraversalPointAtLength) {
- float offset = m_desiredLength - m_totalLength;
- m_current.move(offset * cosf(slope), offset * sinf(slope));
- } else
+ float offset = m_desiredLength - m_totalLength;
+ m_current.move(offset * cosf(slope), offset * sinf(slope));
+
+ if (!m_isZeroVector && !m_desiredLength)
+ m_isZeroVector = true;
+ else {
+ m_success = true;
m_normalAngle = rad2deg(slope);
- m_success = true;
+ }
}
+
m_previous = m_current;
+ return m_success;
+}
+
+bool PathTraversalState::appendPathElement(PathElementType type, const FloatPoint* points)
+{
+ switch (type) {
+ case PathElementMoveToPoint:
+ moveTo(points[0]);
+ break;
+ case PathElementAddLineToPoint:
+ lineTo(points[0]);
+ break;
+ case PathElementAddQuadCurveToPoint:
+ quadraticBezierTo(points[0], points[1]);
+ break;
+ case PathElementAddCurveToPoint:
+ cubicBezierTo(points[0], points[1], points[2]);
+ break;
+ case PathElementCloseSubpath:
+ closeSubpath();
+ break;
+ }
+
+ return finalizeAppendPathElement();
+}
+
+bool PathTraversalState::processPathElement(PathElementType type, const FloatPoint* points)
+{
+ if (m_success)
+ return true;
+
+ if (m_isZeroVector) {
+ PathTraversalState traversalState(*this);
+ m_success = traversalState.appendPathElement(type, points);
+ m_normalAngle = traversalState.m_normalAngle;
+ return m_success;
+ }
+
+ return appendPathElement(type, points);
}
}
diff --git a/Source/WebCore/platform/graphics/PathTraversalState.h b/Source/WebCore/platform/graphics/PathTraversalState.h
index 6a29dcd5e..bd3c0f1d2 100644
--- a/Source/WebCore/platform/graphics/PathTraversalState.h
+++ b/Source/WebCore/platform/graphics/PathTraversalState.h
@@ -1,5 +1,6 @@
/*
* Copyright (C) 2006, 2007 Eric Seidel <eric@webkit.org>
+ * Copyright (C) 2015 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -10,10 +11,10 @@
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
- * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
@@ -27,45 +28,60 @@
#define PathTraversalState_h
#include "FloatPoint.h"
+#include "Path.h"
namespace WebCore {
class PathTraversalState {
public:
- enum PathTraversalAction {
- TraversalTotalLength,
- TraversalPointAtLength,
- TraversalSegmentAtLength,
- TraversalNormalAngleAtLength
+ enum class Action {
+ TotalLength,
+ VectorAtLength,
+ SegmentAtLength,
};
- PathTraversalState(PathTraversalAction);
-
- float closeSubpath();
- float moveTo(const FloatPoint&);
- float lineTo(const FloatPoint&);
- float quadraticBezierTo(const FloatPoint& newControl, const FloatPoint& newEnd);
- float cubicBezierTo(const FloatPoint& newControl1, const FloatPoint& newControl2, const FloatPoint& newEnd);
-
- void processSegment();
+ PathTraversalState(Action, float desiredLength = 0);
public:
- PathTraversalAction m_action;
- bool m_success;
+ bool processPathElement(PathElementType, const FloatPoint*);
+ bool processPathElement(const PathElement& element) { return processPathElement(element.type, element.points); }
+
+ Action action() const { return m_action; }
+ void setAction(Action action) { m_action = action; }
+ float desiredLength() const { return m_desiredLength; }
+ void setDesiredLength(float desiredLength) { m_desiredLength = desiredLength; }
+
+ // Traversing output -- should be read only
+ bool success() const { return m_success; }
+ float totalLength() const { return m_totalLength; }
+ FloatPoint current() const { return m_current; }
+ float normalAngle() const { return m_normalAngle; }
+
+private:
+ void closeSubpath();
+ void moveTo(const FloatPoint&);
+ void lineTo(const FloatPoint&);
+ void quadraticBezierTo(const FloatPoint&, const FloatPoint&);
+ void cubicBezierTo(const FloatPoint&, const FloatPoint&, const FloatPoint&);
+
+ bool finalizeAppendPathElement();
+ bool appendPathElement(PathElementType, const FloatPoint*);
+
+private:
+ Action m_action;
+ bool m_success { false };
FloatPoint m_current;
FloatPoint m_start;
- FloatPoint m_control1;
- FloatPoint m_control2;
- float m_totalLength;
- unsigned m_segmentIndex;
- float m_desiredLength;
+ float m_totalLength { 0 };
+ float m_desiredLength { 0 };
// For normal calculations
FloatPoint m_previous;
- float m_normalAngle; // degrees
-};
+ float m_normalAngle { 0 }; // degrees
+ bool m_isZeroVector { false };
+};
}
#endif
diff --git a/Source/WebCore/platform/graphics/PathUtilities.cpp b/Source/WebCore/platform/graphics/PathUtilities.cpp
new file mode 100644
index 000000000..914a0a277
--- /dev/null
+++ b/Source/WebCore/platform/graphics/PathUtilities.cpp
@@ -0,0 +1,596 @@
+/*
+ * Copyright (C) 2014-2015 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+#include "config.h"
+#include "PathUtilities.h"
+
+#include "AffineTransform.h"
+#include "BorderData.h"
+#include "FloatPoint.h"
+#include "FloatRect.h"
+#include "FloatRoundedRect.h"
+#include "GeometryUtilities.h"
+#include <math.h>
+#include <wtf/MathExtras.h>
+
+namespace WebCore {
+
+class FloatPointGraph {
+ WTF_MAKE_NONCOPYABLE(FloatPointGraph);
+public:
+ FloatPointGraph() { }
+
+ class Node : public FloatPoint {
+ WTF_MAKE_NONCOPYABLE(Node);
+ public:
+ Node(FloatPoint point)
+ : FloatPoint(point)
+ { }
+
+ const Vector<Node*>& nextPoints() const { return m_nextPoints; }
+ void addNextPoint(Node* node)
+ {
+ if (!m_nextPoints.contains(node))
+ m_nextPoints.append(node);
+ }
+
+ bool isVisited() const { return m_visited; }
+ void visit() { m_visited = true; }
+
+ void reset() { m_visited = false; m_nextPoints.clear(); }
+
+ private:
+ Vector<Node*> m_nextPoints;
+ bool m_visited { false };
+ };
+
+ typedef std::pair<Node*, Node*> Edge;
+ typedef Vector<Edge> Polygon;
+
+ Node* findOrCreateNode(FloatPoint);
+
+ void reset()
+ {
+ for (auto& node : m_allNodes)
+ node->reset();
+ }
+
+private:
+ Vector<std::unique_ptr<Node>> m_allNodes;
+};
+
+FloatPointGraph::Node* FloatPointGraph::findOrCreateNode(FloatPoint point)
+{
+ for (auto& testNode : m_allNodes) {
+ if (areEssentiallyEqual(*testNode, point))
+ return testNode.get();
+ }
+
+ m_allNodes.append(std::make_unique<FloatPointGraph::Node>(point));
+ return m_allNodes.last().get();
+}
+
+static bool findLineSegmentIntersection(const FloatPointGraph::Edge& edgeA, const FloatPointGraph::Edge& edgeB, FloatPoint& intersectionPoint)
+{
+ if (!findIntersection(*edgeA.first, *edgeA.second, *edgeB.first, *edgeB.second, intersectionPoint))
+ return false;
+
+ FloatPoint edgeAVec(*edgeA.second - *edgeA.first);
+ FloatPoint edgeBVec(*edgeB.second - *edgeB.first);
+
+ float dotA = edgeAVec.dot(toFloatPoint(intersectionPoint - *edgeA.first));
+ if (dotA < 0 || dotA > edgeAVec.lengthSquared())
+ return false;
+
+ float dotB = edgeBVec.dot(toFloatPoint(intersectionPoint - *edgeB.first));
+ if (dotB < 0 || dotB > edgeBVec.lengthSquared())
+ return false;
+
+ return true;
+}
+
+static bool addIntersectionPoints(Vector<FloatPointGraph::Polygon>& polys, FloatPointGraph& graph)
+{
+ bool foundAnyIntersections = false;
+
+ Vector<FloatPointGraph::Edge> allEdges;
+ for (auto& poly : polys)
+ allEdges.appendVector(poly);
+
+ for (const FloatPointGraph::Edge& edgeA : allEdges) {
+ Vector<FloatPointGraph::Node*> intersectionPoints({edgeA.first, edgeA.second});
+
+ for (const FloatPointGraph::Edge& edgeB : allEdges) {
+ if (&edgeA == &edgeB)
+ continue;
+
+ FloatPoint intersectionPoint;
+ if (!findLineSegmentIntersection(edgeA, edgeB, intersectionPoint))
+ continue;
+ foundAnyIntersections = true;
+ intersectionPoints.append(graph.findOrCreateNode(intersectionPoint));
+ }
+
+ std::sort(intersectionPoints.begin(), intersectionPoints.end(), [edgeA](auto* a, auto* b) {
+ return FloatPoint(*edgeA.first - *b).lengthSquared() > FloatPoint(*edgeA.first - *a).lengthSquared();
+ });
+
+ for (unsigned pointIndex = 1; pointIndex < intersectionPoints.size(); pointIndex++)
+ intersectionPoints[pointIndex - 1]->addNextPoint(intersectionPoints[pointIndex]);
+ }
+
+ return foundAnyIntersections;
+}
+
+static FloatPointGraph::Polygon walkGraphAndExtractPolygon(FloatPointGraph::Node* startNode)
+{
+ FloatPointGraph::Polygon outPoly;
+
+ FloatPointGraph::Node* currentNode = startNode;
+ FloatPointGraph::Node* previousNode = startNode;
+
+ do {
+ currentNode->visit();
+
+ FloatPoint currentVec(*previousNode - *currentNode);
+ currentVec.normalize();
+
+ // Walk the graph, at each node choosing the next non-visited
+ // point with the greatest internal angle.
+ FloatPointGraph::Node* nextNode = nullptr;
+ float nextNodeAngle = 0;
+ for (auto* potentialNextNode : currentNode->nextPoints()) {
+ if (potentialNextNode == currentNode)
+ continue;
+
+ // If we can get back to the start, we should, ignoring the fact that we already visited it.
+ // Otherwise we'll head inside the shape.
+ if (potentialNextNode == startNode) {
+ nextNode = startNode;
+ break;
+ }
+
+ if (potentialNextNode->isVisited())
+ continue;
+
+ FloatPoint nextVec(*potentialNextNode - *currentNode);
+ nextVec.normalize();
+
+ float angle = acos(nextVec.dot(currentVec));
+ float crossZ = nextVec.x() * currentVec.y() - nextVec.y() * currentVec.x();
+
+ if (crossZ < 0)
+ angle = (2 * piFloat) - angle;
+
+ if (!nextNode || angle > nextNodeAngle) {
+ nextNode = potentialNextNode;
+ nextNodeAngle = angle;
+ }
+ }
+
+ // If we don't end up at a node adjacent to the starting node,
+ // something went wrong (there's probably a hole in the shape),
+ // so bail out. We'll use a bounding box instead.
+ if (!nextNode)
+ return FloatPointGraph::Polygon();
+
+ outPoly.append(std::make_pair(currentNode, nextNode));
+
+ previousNode = currentNode;
+ currentNode = nextNode;
+ } while (currentNode != startNode);
+
+ return outPoly;
+}
+
+static FloatPointGraph::Node* findUnvisitedPolygonStartPoint(Vector<FloatPointGraph::Polygon>& polys)
+{
+ for (auto& poly : polys) {
+ for (auto& edge : poly) {
+ if (edge.first->isVisited() || edge.second->isVisited())
+ goto nextPolygon;
+ }
+
+ // FIXME: We should make sure we find an outside edge to start with.
+ return poly[0].first;
+ nextPolygon:
+ continue;
+ }
+ return nullptr;
+}
+
+static Vector<FloatPointGraph::Polygon> unitePolygons(Vector<FloatPointGraph::Polygon>& polys, FloatPointGraph& graph)
+{
+ graph.reset();
+
+ // There are no intersections, so the polygons are disjoint (we already removed wholly-contained rects in an earlier step).
+ if (!addIntersectionPoints(polys, graph))
+ return polys;
+
+ Vector<FloatPointGraph::Polygon> unitedPolygons;
+
+ while (FloatPointGraph::Node* startNode = findUnvisitedPolygonStartPoint(polys)) {
+ FloatPointGraph::Polygon unitedPolygon = walkGraphAndExtractPolygon(startNode);
+ if (unitedPolygon.isEmpty())
+ return Vector<FloatPointGraph::Polygon>();
+ unitedPolygons.append(unitedPolygon);
+ }
+
+ return unitedPolygons;
+}
+
+static FloatPointGraph::Polygon edgesForRect(FloatRect rect, FloatPointGraph& graph)
+{
+ auto minMin = graph.findOrCreateNode(rect.minXMinYCorner());
+ auto minMax = graph.findOrCreateNode(rect.minXMaxYCorner());
+ auto maxMax = graph.findOrCreateNode(rect.maxXMaxYCorner());
+ auto maxMin = graph.findOrCreateNode(rect.maxXMinYCorner());
+
+ return FloatPointGraph::Polygon({
+ std::make_pair(minMin, maxMin),
+ std::make_pair(maxMin, maxMax),
+ std::make_pair(maxMax, minMax),
+ std::make_pair(minMax, minMin)
+ });
+}
+
+static Vector<FloatPointGraph::Polygon> polygonsForRect(const Vector<FloatRect>& rects, FloatPointGraph& graph)
+{
+ Vector<FloatRect> sortedRects = rects;
+ std::sort(sortedRects.begin(), sortedRects.end(), [](FloatRect a, FloatRect b) { return b.y() > a.y(); });
+
+ Vector<FloatPointGraph::Polygon> rectPolygons;
+ rectPolygons.reserveInitialCapacity(sortedRects.size());
+
+ for (auto& rect : sortedRects) {
+ bool isContained = false;
+ for (auto& otherRect : sortedRects) {
+ if (&rect == &otherRect)
+ continue;
+ if (otherRect.contains(rect)) {
+ isContained = true;
+ break;
+ }
+ }
+
+ if (!isContained)
+ rectPolygons.uncheckedAppend(edgesForRect(rect, graph));
+ }
+ return unitePolygons(rectPolygons, graph);
+}
+
+Vector<Path> PathUtilities::pathsWithShrinkWrappedRects(const Vector<FloatRect>& rects, float radius)
+{
+ Vector<Path> paths;
+
+ if (rects.isEmpty())
+ return paths;
+
+ if (rects.size() > 20) {
+ Path path;
+ path.addRoundedRect(unionRect(rects), FloatSize(radius, radius));
+ paths.append(path);
+ return paths;
+ }
+
+ FloatPointGraph graph;
+ Vector<FloatPointGraph::Polygon> polys = polygonsForRect(rects, graph);
+ if (polys.isEmpty()) {
+ Path path;
+ path.addRoundedRect(unionRect(rects), FloatSize(radius, radius));
+ paths.append(path);
+ return paths;
+ }
+
+ for (auto& poly : polys) {
+ Path path;
+ for (unsigned i = 0; i < poly.size(); ++i) {
+ FloatPointGraph::Edge& toEdge = poly[i];
+ // Connect the first edge to the last.
+ FloatPointGraph::Edge& fromEdge = (i > 0) ? poly[i - 1] : poly[poly.size() - 1];
+
+ FloatPoint fromEdgeVec = toFloatPoint(*fromEdge.second - *fromEdge.first);
+ FloatPoint toEdgeVec = toFloatPoint(*toEdge.second - *toEdge.first);
+
+ // Clamp the radius to no more than half the length of either adjacent edge,
+ // because we want a smooth curve and don't want unequal radii.
+ float clampedRadius = std::min(radius, fabsf(fromEdgeVec.x() ? fromEdgeVec.x() : fromEdgeVec.y()) / 2);
+ clampedRadius = std::min(clampedRadius, fabsf(toEdgeVec.x() ? toEdgeVec.x() : toEdgeVec.y()) / 2);
+
+ FloatPoint fromEdgeNorm = fromEdgeVec;
+ fromEdgeNorm.normalize();
+ FloatPoint toEdgeNorm = toEdgeVec;
+ toEdgeNorm.normalize();
+
+ // Project the radius along the incoming and outgoing edge.
+ FloatSize fromOffset = clampedRadius * toFloatSize(fromEdgeNorm);
+ FloatSize toOffset = clampedRadius * toFloatSize(toEdgeNorm);
+
+ if (!i)
+ path.moveTo(*fromEdge.second - fromOffset);
+ else
+ path.addLineTo(*fromEdge.second - fromOffset);
+ path.addArcTo(*fromEdge.second, *toEdge.first + toOffset, clampedRadius);
+ }
+ path.closeSubpath();
+ paths.append(path);
+ }
+ return paths;
+}
+
+Path PathUtilities::pathWithShrinkWrappedRects(const Vector<FloatRect>& rects, float radius)
+{
+ Vector<Path> paths = pathsWithShrinkWrappedRects(rects, radius);
+
+ Path unionPath;
+ for (const auto& path : paths)
+ unionPath.addPath(path, AffineTransform());
+
+ return unionPath;
+}
+
+static std::pair<FloatPoint, FloatPoint> startAndEndPointsForCorner(const FloatPointGraph::Edge& fromEdge, const FloatPointGraph::Edge& toEdge, const FloatSize& radius)
+{
+ FloatPoint startPoint;
+ FloatPoint endPoint;
+
+ FloatSize fromEdgeVector = *fromEdge.second - *fromEdge.first;
+ FloatSize toEdgeVector = *toEdge.second - *toEdge.first;
+
+ FloatPoint fromEdgeNorm = toFloatPoint(fromEdgeVector);
+ fromEdgeNorm.normalize();
+ FloatSize fromOffset = FloatSize(radius.width() * fromEdgeNorm.x(), radius.height() * fromEdgeNorm.y());
+ startPoint = *fromEdge.second - fromOffset;
+
+ FloatPoint toEdgeNorm = toFloatPoint(toEdgeVector);
+ toEdgeNorm.normalize();
+ FloatSize toOffset = FloatSize(radius.width() * toEdgeNorm.x(), radius.height() * toEdgeNorm.y());
+ endPoint = *toEdge.first + toOffset;
+ return std::make_pair(startPoint, endPoint);
+}
+
+enum class CornerType { TopLeft, TopRight, BottomRight, BottomLeft, Other };
+static CornerType cornerType(const FloatPointGraph::Edge& fromEdge, const FloatPointGraph::Edge& toEdge)
+{
+ auto fromEdgeVector = *fromEdge.second - *fromEdge.first;
+ auto toEdgeVector = *toEdge.second - *toEdge.first;
+
+ if (fromEdgeVector.height() < 0 && toEdgeVector.width() > 0)
+ return CornerType::TopLeft;
+ if (fromEdgeVector.width() > 0 && toEdgeVector.height() > 0)
+ return CornerType::TopRight;
+ if (fromEdgeVector.height() > 0 && toEdgeVector.width() < 0)
+ return CornerType::BottomRight;
+ if (fromEdgeVector.width() < 0 && toEdgeVector.height() < 0)
+ return CornerType::BottomLeft;
+ return CornerType::Other;
+}
+
+static CornerType cornerTypeForMultiline(const FloatPointGraph::Edge& fromEdge, const FloatPointGraph::Edge& toEdge, const Vector<FloatPoint>& corners)
+{
+ auto corner = cornerType(fromEdge, toEdge);
+ if (corner == CornerType::TopLeft && corners.at(0) == *fromEdge.second)
+ return corner;
+ if (corner == CornerType::TopRight && corners.at(1) == *fromEdge.second)
+ return corner;
+ if (corner == CornerType::BottomRight && corners.at(2) == *fromEdge.second)
+ return corner;
+ if (corner == CornerType::BottomLeft && corners.at(3) == *fromEdge.second)
+ return corner;
+ return CornerType::Other;
+}
+
+static std::pair<FloatPoint, FloatPoint> controlPointsForBezierCurve(CornerType cornerType, const FloatPointGraph::Edge& fromEdge,
+ const FloatPointGraph::Edge& toEdge, const FloatSize& radius)
+{
+ FloatPoint cp1;
+ FloatPoint cp2;
+ switch (cornerType) {
+ case CornerType::TopLeft: {
+ cp1 = FloatPoint(fromEdge.second->x(), fromEdge.second->y() + radius.height() * Path::circleControlPoint());
+ cp2 = FloatPoint(toEdge.first->x() + radius.width() * Path::circleControlPoint(), toEdge.first->y());
+ break;
+ }
+ case CornerType::TopRight: {
+ cp1 = FloatPoint(fromEdge.second->x() - radius.width() * Path::circleControlPoint(), fromEdge.second->y());
+ cp2 = FloatPoint(toEdge.first->x(), toEdge.first->y() + radius.height() * Path::circleControlPoint());
+ break;
+ }
+ case CornerType::BottomRight: {
+ cp1 = FloatPoint(fromEdge.second->x(), fromEdge.second->y() - radius.height() * Path::circleControlPoint());
+ cp2 = FloatPoint(toEdge.first->x() - radius.width() * Path::circleControlPoint(), toEdge.first->y());
+ break;
+ }
+ case CornerType::BottomLeft: {
+ cp1 = FloatPoint(fromEdge.second->x() + radius.width() * Path::circleControlPoint(), fromEdge.second->y());
+ cp2 = FloatPoint(toEdge.first->x(), toEdge.first->y() - radius.height() * Path::circleControlPoint());
+ break;
+ }
+ case CornerType::Other: {
+ ASSERT_NOT_REACHED();
+ break;
+ }
+ }
+ return std::make_pair(cp1, cp2);
+}
+
+static FloatRoundedRect::Radii adjustedtRadiiForHuggingCurve(const FloatSize& topLeftRadius, const FloatSize& topRightRadius,
+ const FloatSize& bottomLeftRadius, const FloatSize& bottomRightRadius, float outlineOffset)
+{
+ FloatRoundedRect::Radii radii;
+ // This adjusts the radius so that it follows the border curve even when offset is present.
+ auto adjustedRadius = [outlineOffset](const FloatSize& radius)
+ {
+ FloatSize expandSize;
+ if (radius.width() > outlineOffset)
+ expandSize.setWidth(std::min(outlineOffset, radius.width() - outlineOffset));
+ if (radius.height() > outlineOffset)
+ expandSize.setHeight(std::min(outlineOffset, radius.height() - outlineOffset));
+ FloatSize adjustedRadius = radius;
+ adjustedRadius.expand(expandSize.width(), expandSize.height());
+ // Do not go to negative radius.
+ return adjustedRadius.expandedTo(FloatSize(0, 0));
+ };
+
+ radii.setTopLeft(adjustedRadius(topLeftRadius));
+ radii.setTopRight(adjustedRadius(topRightRadius));
+ radii.setBottomRight(adjustedRadius(bottomRightRadius));
+ radii.setBottomLeft(adjustedRadius(bottomLeftRadius));
+ return radii;
+}
+
+static std::optional<FloatRect> rectFromPolygon(const FloatPointGraph::Polygon& poly)
+{
+ if (poly.size() != 4)
+ return std::optional<FloatRect>();
+
+ std::optional<FloatPoint> topLeft;
+ std::optional<FloatPoint> bottomRight;
+ for (unsigned i = 0; i < poly.size(); ++i) {
+ const auto& toEdge = poly[i];
+ const auto& fromEdge = (i > 0) ? poly[i - 1] : poly[poly.size() - 1];
+ auto corner = cornerType(fromEdge, toEdge);
+ if (corner == CornerType::TopLeft) {
+ ASSERT(!topLeft);
+ topLeft = *fromEdge.second;
+ } else if (corner == CornerType::BottomRight) {
+ ASSERT(!bottomRight);
+ bottomRight = *fromEdge.second;
+ }
+ }
+ if (!topLeft || !bottomRight)
+ return std::optional<FloatRect>();
+ return FloatRect(topLeft.value(), bottomRight.value());
+}
+
+Path PathUtilities::pathWithShrinkWrappedRectsForOutline(const Vector<FloatRect>& rects, const BorderData& borderData,
+ float outlineOffset, TextDirection direction, WritingMode writingMode, float deviceScaleFactor)
+{
+ ASSERT(borderData.hasBorderRadius());
+
+ FloatSize topLeftRadius { borderData.topLeft().width.value(), borderData.topLeft().height.value() };
+ FloatSize topRightRadius { borderData.topRight().width.value(), borderData.topRight().height.value() };
+ FloatSize bottomRightRadius { borderData.bottomRight().width.value(), borderData.bottomRight().height.value() };
+ FloatSize bottomLeftRadius { borderData.bottomLeft().width.value(), borderData.bottomLeft().height.value() };
+
+ auto roundedRect = [topLeftRadius, topRightRadius, bottomRightRadius, bottomLeftRadius, outlineOffset, deviceScaleFactor] (const FloatRect& rect)
+ {
+ auto radii = adjustedtRadiiForHuggingCurve(topLeftRadius, topRightRadius, bottomLeftRadius, bottomRightRadius, outlineOffset);
+ radii.scale(calcBorderRadiiConstraintScaleFor(rect, radii));
+ RoundedRect roundedRect(LayoutRect(rect),
+ RoundedRect::Radii(LayoutSize(radii.topLeft()), LayoutSize(radii.topRight()), LayoutSize(radii.bottomLeft()), LayoutSize(radii.bottomRight())));
+ Path path;
+ path.addRoundedRect(roundedRect.pixelSnappedRoundedRectForPainting(deviceScaleFactor));
+ return path;
+ };
+
+ if (rects.size() == 1)
+ return roundedRect(rects.at(0));
+
+ FloatPointGraph graph;
+ const auto polys = polygonsForRect(rects, graph);
+ // Fall back to corner painting with no radius for empty and disjoint rectangles.
+ if (!polys.size() || polys.size() > 1)
+ return Path();
+ const auto& poly = polys.at(0);
+ // Fast path when poly has one rect only.
+ std::optional<FloatRect> rect = rectFromPolygon(poly);
+ if (rect)
+ return roundedRect(rect.value());
+
+ Path path;
+ // Multiline outline needs to match multiline border painting. Only first and last lines are getting rounded borders.
+ auto isLeftToRight = isLeftToRightDirection(direction);
+ auto firstLineRect = isLeftToRight ? rects.at(0) : rects.at(rects.size() - 1);
+ auto lastLineRect = isLeftToRight ? rects.at(rects.size() - 1) : rects.at(0);
+ // Adjust radius so that it matches the box border.
+ auto firstLineRadii = FloatRoundedRect::Radii(topLeftRadius, topRightRadius, bottomLeftRadius, bottomRightRadius);
+ auto lastLineRadii = FloatRoundedRect::Radii(topLeftRadius, topRightRadius, bottomLeftRadius, bottomRightRadius);
+ firstLineRadii.scale(calcBorderRadiiConstraintScaleFor(firstLineRect, firstLineRadii));
+ lastLineRadii.scale(calcBorderRadiiConstraintScaleFor(lastLineRect, lastLineRadii));
+ topLeftRadius = firstLineRadii.topLeft();
+ bottomLeftRadius = firstLineRadii.bottomLeft();
+ topRightRadius = lastLineRadii.topRight();
+ bottomRightRadius = lastLineRadii.bottomRight();
+ Vector<FloatPoint> corners;
+ // physical topLeft/topRight/bottomRight/bottomLeft
+ auto isHorizontal = isHorizontalWritingMode(writingMode);
+ corners.append(firstLineRect.minXMinYCorner());
+ corners.append(isHorizontal ? lastLineRect.maxXMinYCorner() : firstLineRect.maxXMinYCorner());
+ corners.append(lastLineRect.maxXMaxYCorner());
+ corners.append(isHorizontal ? firstLineRect.minXMaxYCorner() : lastLineRect.minXMaxYCorner());
+
+ for (unsigned i = 0; i < poly.size(); ++i) {
+ auto moveOrAddLineTo = [i, &path] (const FloatPoint& startPoint)
+ {
+ if (!i)
+ path.moveTo(startPoint);
+ else
+ path.addLineTo(startPoint);
+ };
+ const auto& toEdge = poly[i];
+ const auto& fromEdge = (i > 0) ? poly[i - 1] : poly[poly.size() - 1];
+ FloatSize radius;
+ auto corner = cornerTypeForMultiline(fromEdge, toEdge, corners);
+ switch (corner) {
+ case CornerType::TopLeft: {
+ radius = topLeftRadius;
+ break;
+ }
+ case CornerType::TopRight: {
+ radius = topRightRadius;
+ break;
+ }
+ case CornerType::BottomRight: {
+ radius = bottomRightRadius;
+ break;
+ }
+ case CornerType::BottomLeft: {
+ radius = bottomLeftRadius;
+ break;
+ }
+ case CornerType::Other: {
+ // Do not apply border radius on corners that normal border painting skips. (multiline content)
+ moveOrAddLineTo(*fromEdge.second);
+ continue;
+ }
+ }
+ FloatPoint startPoint;
+ FloatPoint endPoint;
+ std::tie(startPoint, endPoint) = startAndEndPointsForCorner(fromEdge, toEdge, radius);
+ moveOrAddLineTo(startPoint);
+
+ FloatPoint cp1;
+ FloatPoint cp2;
+ std::tie(cp1, cp2) = controlPointsForBezierCurve(corner, fromEdge, toEdge, radius);
+ path.addBezierCurveTo(cp1, cp2, endPoint);
+ }
+ path.closeSubpath();
+ return path;
+}
+
+
+}
diff --git a/Source/WebCore/platform/graphics/PathUtilities.h b/Source/WebCore/platform/graphics/PathUtilities.h
new file mode 100644
index 000000000..500041a2d
--- /dev/null
+++ b/Source/WebCore/platform/graphics/PathUtilities.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2014-2015 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef PathUtilities_h
+#define PathUtilities_h
+
+#include "Path.h"
+#include "WritingMode.h"
+#include <wtf/Vector.h>
+
+namespace WebCore {
+class BorderData;
+
+class PathUtilities {
+public:
+ WEBCORE_EXPORT static Path pathWithShrinkWrappedRects(const Vector<FloatRect>& rects, float radius);
+ WEBCORE_EXPORT static Vector<Path> pathsWithShrinkWrappedRects(const Vector<FloatRect>& rects, float radius);
+
+ static Path pathWithShrinkWrappedRectsForOutline(const Vector<FloatRect>&, const BorderData&, float outlineOffset, TextDirection, WritingMode, float deviceScaleFactor);
+};
+
+}
+
+#endif
diff --git a/Source/WebCore/platform/graphics/Pattern.cpp b/Source/WebCore/platform/graphics/Pattern.cpp
index 3b32ebbb8..a9accc284 100644
--- a/Source/WebCore/platform/graphics/Pattern.cpp
+++ b/Source/WebCore/platform/graphics/Pattern.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2006, 2007, 2008 Apple Computer, Inc. All rights reserved.
+ * Copyright (C) 2006, 2007, 2008 Apple Inc. All rights reserved.
* Copyright (C) 2008 Eric Seidel <eric@webkit.org>
*
* Redistribution and use in source and binary forms, with or without
@@ -11,10 +11,10 @@
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
- * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
@@ -31,9 +31,9 @@
namespace WebCore {
-PassRefPtr<Pattern> Pattern::create(PassRefPtr<Image> tileImage, bool repeatX, bool repeatY)
+Ref<Pattern> Pattern::create(PassRefPtr<Image> tileImage, bool repeatX, bool repeatY)
{
- return adoptRef(new Pattern(tileImage, repeatX, repeatY));
+ return adoptRef(*new Pattern(tileImage, repeatX, repeatY));
}
Pattern::Pattern(PassRefPtr<Image> image, bool repeatX, bool repeatY)
diff --git a/Source/WebCore/platform/graphics/Pattern.h b/Source/WebCore/platform/graphics/Pattern.h
index 58508582c..256fa2d5f 100644
--- a/Source/WebCore/platform/graphics/Pattern.h
+++ b/Source/WebCore/platform/graphics/Pattern.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2006, 2007, 2008 Apple Computer, Inc. All rights reserved.
+ * Copyright (C) 2006, 2007, 2008 Apple Inc. All rights reserved.
* Copyright (C) 2008 Eric Seidel <eric@webkit.org>
* Copyright (C) 2007-2008 Torch Mobile, Inc.
*
@@ -12,10 +12,10 @@
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
- * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
@@ -37,6 +37,9 @@
#if USE(CG)
typedef struct CGPattern* CGPatternRef;
typedef CGPatternRef PlatformPatternPtr;
+#elif USE(DIRECT2D)
+interface ID2D1BitmapBrush;
+typedef ID2D1BitmapBrush* PlatformPatternPtr;
#elif USE(CAIRO)
#include <cairo.h>
typedef cairo_pattern_t* PlatformPatternPtr;
@@ -47,19 +50,24 @@ typedef void* PlatformPatternPtr;
namespace WebCore {
class AffineTransform;
+class GraphicsContext;
class Image;
-class Pattern : public RefCounted<Pattern> {
+class Pattern final : public RefCounted<Pattern> {
public:
- static PassRefPtr<Pattern> create(PassRefPtr<Image> tileImage, bool repeatX, bool repeatY);
- virtual ~Pattern();
+ static Ref<Pattern> create(PassRefPtr<Image> tileImage, bool repeatX, bool repeatY);
+ ~Pattern();
Image* tileImage() const { return m_tileImage.get(); }
void platformDestroy();
- // Pattern space is an abstract space that maps to the default user space by the transformation 'userSpaceTransformation'
+ // Pattern space is an abstract space that maps to the default user space by the transformation 'userSpaceTransformation'
+#if !USE(DIRECT2D)
PlatformPatternPtr createPlatformPattern(const AffineTransform& userSpaceTransformation) const;
+#else
+ PlatformPatternPtr createPlatformPattern(const GraphicsContext&, float alpha, const AffineTransform& userSpaceTransformation) const;
+#endif
void setPatternSpaceTransform(const AffineTransform& patternSpaceTransformation);
const AffineTransform& getPatternSpaceTransform() { return m_patternSpaceTransformation; };
void setPlatformPatternSpaceTransform();
diff --git a/Source/WebCore/platform/graphics/PlatformDisplay.cpp b/Source/WebCore/platform/graphics/PlatformDisplay.cpp
new file mode 100644
index 000000000..e387bf9e2
--- /dev/null
+++ b/Source/WebCore/platform/graphics/PlatformDisplay.cpp
@@ -0,0 +1,241 @@
+/*
+ * Copyright (C) 2015 Igalia S.L
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "PlatformDisplay.h"
+
+#include "GLContext.h"
+#include <cstdlib>
+#include <mutex>
+
+#if PLATFORM(X11)
+#include "PlatformDisplayX11.h"
+#endif
+
+#if PLATFORM(WAYLAND)
+#include "PlatformDisplayWayland.h"
+#endif
+
+#if PLATFORM(WIN)
+#include "PlatformDisplayWin.h"
+#endif
+
+#if PLATFORM(GTK)
+#include <gdk/gdk.h>
+#endif
+
+#if PLATFORM(GTK) && PLATFORM(X11)
+#include <gdk/gdkx.h>
+#endif
+
+#if PLATFORM(GTK) && PLATFORM(WAYLAND) && !defined(GTK_API_VERSION_2)
+#include <gdk/gdkwayland.h>
+#endif
+
+#if USE(EGL)
+#include <EGL/egl.h>
+#include <wtf/HashSet.h>
+#include <wtf/NeverDestroyed.h>
+#endif
+
+namespace WebCore {
+
+std::unique_ptr<PlatformDisplay> PlatformDisplay::createPlatformDisplay()
+{
+#if PLATFORM(GTK)
+#if defined(GTK_API_VERSION_2)
+ return std::make_unique<PlatformDisplayX11>(GDK_DISPLAY_XDISPLAY(gdk_display_get_default()));
+#else
+ GdkDisplay* display = gdk_display_manager_get_default_display(gdk_display_manager_get());
+#if PLATFORM(X11)
+ if (GDK_IS_X11_DISPLAY(display))
+ return std::make_unique<PlatformDisplayX11>(GDK_DISPLAY_XDISPLAY(display));
+#endif
+#if PLATFORM(WAYLAND)
+ if (GDK_IS_WAYLAND_DISPLAY(display))
+ return std::make_unique<PlatformDisplayWayland>(gdk_wayland_display_get_wl_display(display));
+#endif
+#endif
+#elif PLATFORM(WIN)
+ return std::make_unique<PlatformDisplayWin>();
+#endif
+
+#if PLATFORM(WAYLAND)
+ if (auto platformDisplay = PlatformDisplayWayland::create())
+ return platformDisplay;
+#endif
+
+#if PLATFORM(X11)
+ if (auto platformDisplay = PlatformDisplayX11::create())
+ return platformDisplay;
+#endif
+
+ // If at this point we still don't have a display, just create a fake display with no native.
+#if PLATFORM(WAYLAND)
+ return std::make_unique<PlatformDisplayWayland>(nullptr);
+#endif
+#if PLATFORM(X11)
+ return std::make_unique<PlatformDisplayX11>(nullptr);
+#endif
+
+ ASSERT_NOT_REACHED();
+ return nullptr;
+}
+
+PlatformDisplay& PlatformDisplay::sharedDisplay()
+{
+ static std::once_flag onceFlag;
+#if COMPILER(CLANG)
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wexit-time-destructors"
+#endif
+ static std::unique_ptr<PlatformDisplay> display;
+#if COMPILER(CLANG)
+#pragma clang diagnostic pop
+#endif
+ std::call_once(onceFlag, []{
+ display = createPlatformDisplay();
+ });
+ return *display;
+}
+
+static PlatformDisplay* s_sharedDisplayForCompositing;
+
+PlatformDisplay& PlatformDisplay::sharedDisplayForCompositing()
+{
+ return s_sharedDisplayForCompositing ? *s_sharedDisplayForCompositing : sharedDisplay();
+}
+
+void PlatformDisplay::setSharedDisplayForCompositing(PlatformDisplay& display)
+{
+ s_sharedDisplayForCompositing = &display;
+}
+
+PlatformDisplay::PlatformDisplay(NativeDisplayOwned displayOwned)
+ : m_nativeDisplayOwned(displayOwned)
+#if USE(EGL)
+ , m_eglDisplay(EGL_NO_DISPLAY)
+#endif
+{
+}
+
+PlatformDisplay::~PlatformDisplay()
+{
+#if USE(EGL)
+ ASSERT(m_eglDisplay == EGL_NO_DISPLAY);
+#endif
+}
+
+#if USE(EGL) || USE(GLX)
+GLContext* PlatformDisplay::sharingGLContext()
+{
+ if (!m_sharingGLContext)
+ m_sharingGLContext = GLContext::createSharingContext(*this);
+ return m_sharingGLContext.get();
+}
+#endif
+
+#if USE(EGL)
+static HashSet<PlatformDisplay*>& eglDisplays()
+{
+ static NeverDestroyed<HashSet<PlatformDisplay*>> displays;
+ return displays;
+}
+
+EGLDisplay PlatformDisplay::eglDisplay() const
+{
+ if (!m_eglDisplayInitialized)
+ const_cast<PlatformDisplay*>(this)->initializeEGLDisplay();
+ return m_eglDisplay;
+}
+
+bool PlatformDisplay::eglCheckVersion(int major, int minor) const
+{
+ if (!m_eglDisplayInitialized)
+ const_cast<PlatformDisplay*>(this)->initializeEGLDisplay();
+
+ return (m_eglMajorVersion > major) || ((m_eglMajorVersion == major) && (m_eglMinorVersion >= minor));
+}
+
+void PlatformDisplay::initializeEGLDisplay()
+{
+ m_eglDisplayInitialized = true;
+
+ if (m_eglDisplay == EGL_NO_DISPLAY) {
+ // EGL is optionally soft linked on Windows.
+#if PLATFORM(WIN)
+ auto eglGetDisplay = eglGetDisplayPtr();
+ if (!eglGetDisplay)
+ return;
+#endif
+ m_eglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
+ if (m_eglDisplay == EGL_NO_DISPLAY)
+ return;
+ }
+
+ EGLint majorVersion, minorVersion;
+ if (eglInitialize(m_eglDisplay, &majorVersion, &minorVersion) == EGL_FALSE) {
+ LOG_ERROR("EGLDisplay Initialization failed.");
+ terminateEGLDisplay();
+ return;
+ }
+
+ m_eglMajorVersion = majorVersion;
+ m_eglMinorVersion = minorVersion;
+
+ eglDisplays().add(this);
+
+ static bool eglAtexitHandlerInitialized = false;
+ if (!eglAtexitHandlerInitialized) {
+ // EGL registers atexit handlers to cleanup its global display list.
+ // Since the global PlatformDisplay instance is created before,
+ // when the PlatformDisplay destructor is called, EGL has already removed the
+ // display from the list, causing eglTerminate() to crash. So, here we register
+ // our own atexit handler, after EGL has been initialized and after the global
+ // instance has been created to ensure we call eglTerminate() before the other
+ // EGL atexit handlers and the PlatformDisplay destructor.
+ // See https://bugs.webkit.org/show_bug.cgi?id=157973.
+ eglAtexitHandlerInitialized = true;
+ std::atexit([] {
+ while (!eglDisplays().isEmpty()) {
+ auto* display = eglDisplays().takeAny();
+ display->terminateEGLDisplay();
+ }
+ });
+ }
+}
+
+void PlatformDisplay::terminateEGLDisplay()
+{
+ m_sharingGLContext = nullptr;
+ ASSERT(m_eglDisplayInitialized);
+ if (m_eglDisplay == EGL_NO_DISPLAY)
+ return;
+ eglTerminate(m_eglDisplay);
+ m_eglDisplay = EGL_NO_DISPLAY;
+}
+#endif // USE(EGL)
+
+} // namespace WebCore
diff --git a/Source/WebCore/platform/graphics/PlatformDisplay.h b/Source/WebCore/platform/graphics/PlatformDisplay.h
new file mode 100644
index 000000000..680b43a69
--- /dev/null
+++ b/Source/WebCore/platform/graphics/PlatformDisplay.h
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2015 Igalia S.L
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef PlatformDisplay_h
+#define PlatformDisplay_h
+
+#include <wtf/Noncopyable.h>
+#include <wtf/TypeCasts.h>
+
+#if USE(EGL)
+typedef void *EGLDisplay;
+#endif
+
+namespace WebCore {
+
+class GLContext;
+
+class PlatformDisplay {
+ WTF_MAKE_NONCOPYABLE(PlatformDisplay); WTF_MAKE_FAST_ALLOCATED;
+public:
+ static PlatformDisplay& sharedDisplay();
+ static PlatformDisplay& sharedDisplayForCompositing();
+ virtual ~PlatformDisplay();
+
+ enum class Type {
+#if PLATFORM(X11)
+ X11,
+#endif
+#if PLATFORM(WAYLAND)
+ Wayland,
+#endif
+#if PLATFORM(WIN)
+ Windows,
+#endif
+ };
+
+ virtual Type type() const = 0;
+
+#if USE(EGL) || USE(GLX)
+ GLContext* sharingGLContext();
+#endif
+
+#if USE(EGL)
+ EGLDisplay eglDisplay() const;
+ bool eglCheckVersion(int major, int minor) const;
+#endif
+
+protected:
+ enum class NativeDisplayOwned { No, Yes };
+ explicit PlatformDisplay(NativeDisplayOwned = NativeDisplayOwned::No);
+
+ static void setSharedDisplayForCompositing(PlatformDisplay&);
+
+ NativeDisplayOwned m_nativeDisplayOwned { NativeDisplayOwned::No };
+
+#if USE(EGL)
+ virtual void initializeEGLDisplay();
+
+ EGLDisplay m_eglDisplay;
+#endif
+
+#if USE(EGL) || USE(GLX)
+ std::unique_ptr<GLContext> m_sharingGLContext;
+#endif
+
+private:
+ static std::unique_ptr<PlatformDisplay> createPlatformDisplay();
+
+#if USE(EGL)
+ void terminateEGLDisplay();
+
+ bool m_eglDisplayInitialized { false };
+ int m_eglMajorVersion { 0 };
+ int m_eglMinorVersion { 0 };
+#endif
+};
+
+} // namespace WebCore
+
+#define SPECIALIZE_TYPE_TRAITS_PLATFORM_DISPLAY(ToClassName, DisplayType) \
+SPECIALIZE_TYPE_TRAITS_BEGIN(WebCore::ToClassName) \
+ static bool isType(const WebCore::PlatformDisplay& display) { return display.type() == WebCore::PlatformDisplay::Type::DisplayType; } \
+SPECIALIZE_TYPE_TRAITS_END()
+
+#endif // PltformDisplay_h
diff --git a/Source/WebCore/platform/graphics/PlatformLayer.h b/Source/WebCore/platform/graphics/PlatformLayer.h
index a18f9b9c3..29f9decb2 100644
--- a/Source/WebCore/platform/graphics/PlatformLayer.h
+++ b/Source/WebCore/platform/graphics/PlatformLayer.h
@@ -13,7 +13,7 @@
* THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
@@ -26,36 +26,23 @@
#ifndef PlatformLayer_h
#define PlatformLayer_h
-#if USE(ACCELERATED_COMPOSITING)
-
-#if PLATFORM(MAC)
+#if PLATFORM(COCOA)
OBJC_CLASS CALayer;
typedef CALayer PlatformLayer;
#elif PLATFORM(WIN) && USE(CA)
typedef struct _CACFLayer PlatformLayer;
-#elif PLATFORM(WIN) && USE(TEXTURE_MAPPER)
+#elif USE(COORDINATED_GRAPHICS_THREADED)
namespace WebCore {
-class TextureMapperPlatformLayer;
-typedef TextureMapperPlatformLayer PlatformLayer;
+class TextureMapperPlatformLayerProxyProvider;
+typedef TextureMapperPlatformLayerProxyProvider PlatformLayer;
};
-#elif PLATFORM(GTK)
-#if USE(TEXTURE_MAPPER_GL)
+#elif USE(TEXTURE_MAPPER)
namespace WebCore {
class TextureMapperPlatformLayer;
typedef TextureMapperPlatformLayer PlatformLayer;
};
-#endif
-#elif PLATFORM(EFL)
-#if USE(TEXTURE_MAPPER)
-namespace WebCore {
-class TextureMapperPlatformLayer;
-typedef TextureMapperPlatformLayer PlatformLayer;
-};
-#endif // USE(TEXTURE_MAPPER)
#else
typedef void* PlatformLayer;
#endif
-#endif // USE(ACCELERATED_COMPOSITING)
-
#endif // PlatformLayer_h
diff --git a/Source/WebCore/platform/graphics/PlatformMediaResourceLoader.h b/Source/WebCore/platform/graphics/PlatformMediaResourceLoader.h
new file mode 100644
index 000000000..bc0631626
--- /dev/null
+++ b/Source/WebCore/platform/graphics/PlatformMediaResourceLoader.h
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2014 Igalia S.L
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#if ENABLE(VIDEO)
+
+#include <wtf/Noncopyable.h>
+#include <wtf/RefCounted.h>
+#include <wtf/ThreadSafeRefCounted.h>
+
+namespace WebCore {
+
+class PlatformMediaResource;
+class ResourceError;
+class ResourceRequest;
+class ResourceResponse;
+
+class PlatformMediaResourceClient {
+public:
+ virtual ~PlatformMediaResourceClient() { }
+
+ virtual void responseReceived(PlatformMediaResource&, const ResourceResponse&) { }
+ virtual void redirectReceived(PlatformMediaResource&, ResourceRequest&, const ResourceResponse&) { }
+ virtual bool shouldCacheResponse(PlatformMediaResource&, const ResourceResponse&) { return true; }
+ virtual void dataSent(PlatformMediaResource&, unsigned long long, unsigned long long) { }
+ virtual void dataReceived(PlatformMediaResource&, const char*, int) { }
+ virtual void accessControlCheckFailed(PlatformMediaResource&, const ResourceError&) { }
+ virtual void loadFailed(PlatformMediaResource&, const ResourceError&) { }
+ virtual void loadFinished(PlatformMediaResource&) { }
+#if USE(SOUP)
+ virtual char* getOrCreateReadBuffer(PlatformMediaResource&, size_t /*requestedSize*/, size_t& /*actualSize*/) { return nullptr; };
+#endif
+};
+
+class PlatformMediaResourceLoader : public ThreadSafeRefCounted<PlatformMediaResourceLoader> {
+ WTF_MAKE_NONCOPYABLE(PlatformMediaResourceLoader); WTF_MAKE_FAST_ALLOCATED;
+public:
+ enum LoadOption {
+ BufferData = 1 << 0,
+ DisallowCaching = 1 << 1,
+ };
+ typedef unsigned LoadOptions;
+
+ virtual ~PlatformMediaResourceLoader() { }
+
+ virtual RefPtr<PlatformMediaResource> requestResource(ResourceRequest&&, LoadOptions) = 0;
+
+protected:
+ PlatformMediaResourceLoader() { }
+};
+
+class PlatformMediaResource : public RefCounted<PlatformMediaResource> {
+ WTF_MAKE_NONCOPYABLE(PlatformMediaResource); WTF_MAKE_FAST_ALLOCATED;
+public:
+ PlatformMediaResource() { }
+ virtual ~PlatformMediaResource() { }
+ virtual void stop() { }
+ virtual void setDefersLoading(bool) { }
+ virtual bool didPassAccessControlCheck() const { return false; }
+
+ void setClient(std::unique_ptr<PlatformMediaResourceClient>&& client) { m_client = WTFMove(client); }
+
+protected:
+ std::unique_ptr<PlatformMediaResourceClient> m_client;
+};
+
+} // namespace WebCore
+
+#endif
diff --git a/Source/WebCore/platform/graphics/PlatformTextTrack.h b/Source/WebCore/platform/graphics/PlatformTextTrack.h
new file mode 100644
index 000000000..20be3d17a
--- /dev/null
+++ b/Source/WebCore/platform/graphics/PlatformTextTrack.h
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 2013, 2014 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef PlatformTextTrack_h
+#define PlatformTextTrack_h
+
+#if ENABLE(AVF_CAPTIONS)
+
+#include <wtf/RefCounted.h>
+#include <wtf/text/WTFString.h>
+
+namespace WebCore {
+
+class TextTrack;
+class InbandTextTrackPrivate;
+
+class PlatformTextTrackClient {
+public:
+ virtual ~PlatformTextTrackClient() { }
+
+ virtual TextTrack* publicTrack() = 0;
+ virtual InbandTextTrackPrivate* privateTrack() { return 0; }
+};
+
+class PlatformTextTrack : public RefCounted<PlatformTextTrack> {
+public:
+ enum TrackKind {
+ Subtitle = 0,
+ Caption = 1,
+ Description = 2,
+ Chapter = 3,
+ MetaData = 4,
+ Forced = 5,
+ };
+ enum TrackType {
+ InBand = 0,
+ OutOfBand = 1,
+ Script = 2
+ };
+ enum TrackMode {
+ Disabled,
+ Hidden,
+ Showing
+ };
+
+ static PassRefPtr<PlatformTextTrack> create(PlatformTextTrackClient* client, const String& label, const String& language, TrackMode mode, TrackKind kind, TrackType type, int uniqueId)
+ {
+ return adoptRef(new PlatformTextTrack(client, label, language, String(), mode, kind, type, uniqueId, false));
+ }
+
+ static PassRefPtr<PlatformTextTrack> createOutOfBand(const String& label, const String& language, const String& url, TrackMode mode, TrackKind kind, int uniqueId, bool isDefault)
+ {
+ return adoptRef(new PlatformTextTrack(nullptr, label, language, url, mode, kind, OutOfBand, uniqueId, isDefault));
+ }
+
+ virtual ~PlatformTextTrack() { }
+
+ TrackType type() const { return m_type; }
+ TrackKind kind() const { return m_kind; }
+ TrackMode mode() const { return m_mode; }
+ const String& label() const { return m_label; }
+ const String& language() const { return m_language; }
+ const String& url() const { return m_url; }
+ PlatformTextTrackClient* client() const { return m_client; }
+ int uniqueId() const { return m_uniqueId; }
+ bool isDefault() const { return m_isDefault; }
+
+ static PlatformTextTrack* captionMenuOffItem()
+ {
+ static PlatformTextTrack* off = PlatformTextTrack::create(nullptr, "off menu item", "", Showing, Subtitle, InBand, 0).leakRef();
+ return off;
+ }
+
+ static PlatformTextTrack* captionMenuAutomaticItem()
+ {
+ static PlatformTextTrack* automatic = PlatformTextTrack::create(nullptr, "automatic menu item", "", Showing, Subtitle, InBand, 0).leakRef();
+ return automatic;
+ }
+
+protected:
+ PlatformTextTrack(PlatformTextTrackClient* client, const String& label, const String& language, const String& url, TrackMode mode, TrackKind kind, TrackType type, int uniqueId, bool isDefault)
+ : m_label(label)
+ , m_language(language)
+ , m_url(url)
+ , m_mode(mode)
+ , m_kind(kind)
+ , m_type(type)
+ , m_client(client)
+ , m_uniqueId(uniqueId)
+ , m_isDefault(isDefault)
+ {
+ }
+
+ String m_label;
+ String m_language;
+ String m_url;
+ TrackMode m_mode;
+ TrackKind m_kind;
+ TrackType m_type;
+ PlatformTextTrackClient* m_client;
+ int m_uniqueId;
+ bool m_isDefault;
+};
+
+}
+
+#endif
+
+#endif // PlatformTextTrack_h
diff --git a/Source/WebCore/platform/graphics/PlatformTimeRanges.cpp b/Source/WebCore/platform/graphics/PlatformTimeRanges.cpp
new file mode 100644
index 000000000..c7b978400
--- /dev/null
+++ b/Source/WebCore/platform/graphics/PlatformTimeRanges.cpp
@@ -0,0 +1,240 @@
+/*
+ * Copyright (C) 2014 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "PlatformTimeRanges.h"
+
+#include <math.h>
+#include <wtf/PrintStream.h>
+
+namespace WebCore {
+
+PlatformTimeRanges::PlatformTimeRanges(const MediaTime& start, const MediaTime& end)
+{
+ add(start, end);
+}
+
+void PlatformTimeRanges::invert()
+{
+ PlatformTimeRanges inverted;
+ MediaTime posInf = MediaTime::positiveInfiniteTime();
+ MediaTime negInf = MediaTime::negativeInfiniteTime();
+
+ if (!m_ranges.size())
+ inverted.add(negInf, posInf);
+ else {
+ MediaTime start = m_ranges.first().m_start;
+ if (start != negInf)
+ inverted.add(negInf, start);
+
+ for (size_t index = 0; index + 1 < m_ranges.size(); ++index)
+ inverted.add(m_ranges[index].m_end, m_ranges[index + 1].m_start);
+
+ MediaTime end = m_ranges.last().m_end;
+ if (end != posInf)
+ inverted.add(end, posInf);
+ }
+
+ m_ranges.swap(inverted.m_ranges);
+}
+
+void PlatformTimeRanges::intersectWith(const PlatformTimeRanges& other)
+{
+ PlatformTimeRanges invertedOther(other);
+
+ invertedOther.invert();
+ invert();
+ unionWith(invertedOther);
+ invert();
+}
+
+void PlatformTimeRanges::unionWith(const PlatformTimeRanges& other)
+{
+ PlatformTimeRanges unioned(*this);
+
+ for (size_t index = 0; index < other.m_ranges.size(); ++index) {
+ const Range& range = other.m_ranges[index];
+ unioned.add(range.m_start, range.m_end);
+ }
+
+ m_ranges.swap(unioned.m_ranges);
+}
+
+MediaTime PlatformTimeRanges::start(unsigned index) const
+{
+ bool ignoredValid;
+ return start(index, ignoredValid);
+}
+
+MediaTime PlatformTimeRanges::start(unsigned index, bool& valid) const
+{
+ if (index >= length()) {
+ valid = false;
+ return MediaTime::zeroTime();
+ }
+
+ valid = true;
+ return m_ranges[index].m_start;
+}
+
+MediaTime PlatformTimeRanges::end(unsigned index) const
+{
+ bool ignoredValid;
+ return end(index, ignoredValid);
+}
+
+MediaTime PlatformTimeRanges::end(unsigned index, bool& valid) const
+{
+ if (index >= length()) {
+ valid = false;
+ return MediaTime::zeroTime();
+ }
+
+ valid = true;
+ return m_ranges[index].m_end;
+}
+
+MediaTime PlatformTimeRanges::duration(unsigned index) const
+{
+ if (index >= length())
+ return MediaTime::invalidTime();
+
+ return m_ranges[index].m_end - m_ranges[index].m_start;
+}
+
+MediaTime PlatformTimeRanges::maximumBufferedTime() const
+{
+ if (!length())
+ return MediaTime::invalidTime();
+
+ return m_ranges[length() - 1].m_end;
+}
+
+void PlatformTimeRanges::add(const MediaTime& start, const MediaTime& end)
+{
+ ASSERT(start <= end);
+ unsigned overlappingArcIndex;
+ Range addedRange(start, end);
+
+ // For each present range check if we need to:
+ // - merge with the added range, in case we are overlapping or contiguous
+ // - Need to insert in place, we we are completely, not overlapping and not contiguous
+ // in between two ranges.
+ //
+ // TODO: Given that we assume that ranges are correctly ordered, this could be optimized.
+
+ for (overlappingArcIndex = 0; overlappingArcIndex < m_ranges.size(); overlappingArcIndex++) {
+ if (addedRange.isOverlappingRange(m_ranges[overlappingArcIndex]) || addedRange.isContiguousWithRange(m_ranges[overlappingArcIndex])) {
+ // We need to merge the addedRange and that range.
+ addedRange = addedRange.unionWithOverlappingOrContiguousRange(m_ranges[overlappingArcIndex]);
+ m_ranges.remove(overlappingArcIndex);
+ overlappingArcIndex--;
+ } else {
+ // Check the case for which there is no more to do
+ if (!overlappingArcIndex) {
+ if (addedRange.isBeforeRange(m_ranges[0])) {
+ // First index, and we are completely before that range (and not contiguous, nor overlapping).
+ // We just need to be inserted here.
+ break;
+ }
+ } else {
+ if (m_ranges[overlappingArcIndex - 1].isBeforeRange(addedRange) && addedRange.isBeforeRange(m_ranges[overlappingArcIndex])) {
+ // We are exactly after the current previous range, and before the current range, while
+ // not overlapping with none of them. Insert here.
+ break;
+ }
+ }
+ }
+ }
+
+ // Now that we are sure we don't overlap with any range, just add it.
+ m_ranges.insert(overlappingArcIndex, addedRange);
+}
+
+bool PlatformTimeRanges::contain(const MediaTime& time) const
+{
+ return find(time) != notFound;
+}
+
+size_t PlatformTimeRanges::find(const MediaTime& time) const
+{
+ bool ignoreInvalid;
+ for (unsigned n = 0; n < length(); n++) {
+ if (time >= start(n, ignoreInvalid) && time <= end(n, ignoreInvalid))
+ return n;
+ }
+ return notFound;
+}
+
+MediaTime PlatformTimeRanges::nearest(const MediaTime& time) const
+{
+ MediaTime closestDelta = MediaTime::positiveInfiniteTime();
+ MediaTime closestTime = MediaTime::zeroTime();
+ unsigned count = length();
+ if (!count)
+ return MediaTime::invalidTime();
+
+ bool ignoreInvalid;
+
+ for (unsigned ndx = 0; ndx < count; ndx++) {
+ MediaTime startTime = start(ndx, ignoreInvalid);
+ MediaTime endTime = end(ndx, ignoreInvalid);
+ if (time >= startTime && time <= endTime)
+ return time;
+
+ MediaTime startTimeDelta = abs(startTime - time);
+ if (startTimeDelta < closestDelta) {
+ closestTime = startTime;
+ closestDelta = startTimeDelta;
+ }
+
+ MediaTime endTimeDelta = abs(endTime - time);
+ if (endTimeDelta < closestDelta) {
+ closestTime = endTime;
+ closestDelta = endTimeDelta;
+ }
+ }
+ return closestTime;
+}
+
+MediaTime PlatformTimeRanges::totalDuration() const
+{
+ MediaTime total = MediaTime::zeroTime();
+
+ for (unsigned n = 0; n < length(); n++)
+ total += abs(end(n) - start(n));
+ return total;
+}
+
+void PlatformTimeRanges::dump(PrintStream& out) const
+{
+ if (!length())
+ return;
+
+ for (size_t i = 0; i < length(); ++i)
+ out.print("[", start(i), "..", end(i), "] ");
+}
+
+}
diff --git a/Source/WebCore/platform/graphics/PlatformTimeRanges.h b/Source/WebCore/platform/graphics/PlatformTimeRanges.h
new file mode 100644
index 000000000..81de8c557
--- /dev/null
+++ b/Source/WebCore/platform/graphics/PlatformTimeRanges.h
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2014 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef PlatformTimeRanges_h
+#define PlatformTimeRanges_h
+
+#include <algorithm>
+#include <wtf/MediaTime.h>
+#include <wtf/RefCounted.h>
+#include <wtf/Vector.h>
+
+namespace WTF {
+class PrintStream;
+}
+
+namespace WebCore {
+
+class PlatformTimeRanges {
+public:
+ explicit PlatformTimeRanges() { }
+ PlatformTimeRanges(const MediaTime& start, const MediaTime& end);
+
+ WEBCORE_EXPORT MediaTime start(unsigned index) const;
+ MediaTime start(unsigned index, bool& valid) const;
+ WEBCORE_EXPORT MediaTime end(unsigned index) const;
+ MediaTime end(unsigned index, bool& valid) const;
+ MediaTime duration(unsigned index) const;
+ MediaTime maximumBufferedTime() const;
+
+ void invert();
+ void intersectWith(const PlatformTimeRanges&);
+ void unionWith(const PlatformTimeRanges&);
+
+ unsigned length() const { return m_ranges.size(); }
+
+ void add(const MediaTime& start, const MediaTime& end);
+
+ bool contain(const MediaTime&) const;
+
+ size_t find(const MediaTime&) const;
+
+ MediaTime nearest(const MediaTime&) const;
+
+ MediaTime totalDuration() const;
+
+ void dump(WTF::PrintStream&) const;
+
+private:
+ // We consider all the Ranges to be semi-bounded as follow: [start, end[
+ struct Range {
+ Range() { }
+ Range(const MediaTime& start, const MediaTime& end)
+ : m_start(start)
+ , m_end(end)
+ {
+ }
+
+ MediaTime m_start;
+ MediaTime m_end;
+
+ inline bool isPointInRange(const MediaTime& point) const
+ {
+ return m_start <= point && point < m_end;
+ }
+
+ inline bool isOverlappingRange(const Range& range) const
+ {
+ return isPointInRange(range.m_start) || isPointInRange(range.m_end) || range.isPointInRange(m_start);
+ }
+
+ inline bool isContiguousWithRange(const Range& range) const
+ {
+ return range.m_start == m_end || range.m_end == m_start;
+ }
+
+ inline Range unionWithOverlappingOrContiguousRange(const Range& range) const
+ {
+ Range ret;
+
+ ret.m_start = std::min(m_start, range.m_start);
+ ret.m_end = std::max(m_end, range.m_end);
+
+ return ret;
+ }
+
+ inline bool isBeforeRange(const Range& range) const
+ {
+ return range.m_start >= m_end;
+ }
+ };
+
+ Vector<Range> m_ranges;
+};
+
+} // namespace WebCore
+
+#endif
diff --git a/Source/WebCore/platform/graphics/Region.cpp b/Source/WebCore/platform/graphics/Region.cpp
index 465a37cde..ce555b721 100644
--- a/Source/WebCore/platform/graphics/Region.cpp
+++ b/Source/WebCore/platform/graphics/Region.cpp
@@ -319,10 +319,10 @@ Region::Shape::SegmentIterator Region::Shape::segments_end(SpanIterator it) cons
#ifndef NDEBUG
void Region::Shape::dump() const
{
- for (Shape::SpanIterator span = spans_begin(), end = spans_end(); span != end; ++span) {
+ for (auto span = spans_begin(), end = spans_end(); span != end; ++span) {
printf("%6d: (", span->y);
- for (Shape::SegmentIterator segment = segments_begin(span), end = segments_end(span); segment != end; ++segment)
+ for (auto segment = segments_begin(span), end = segments_end(span); segment != end; ++segment)
printf("%d ", *segment);
printf(")\n");
}
@@ -331,6 +331,27 @@ void Region::Shape::dump() const
}
#endif
+bool Region::Shape::isValid() const
+{
+ for (auto span = spans_begin(), end = spans_end(); span != end && span + 1 != end; ++span) {
+ int y = span->y;
+ int height = (span + 1)->y - y;
+
+ if (height < 0)
+ return false;
+
+ for (auto segment = segments_begin(span), end = segments_end(span); segment != end && segment + 1 != end; segment += 2) {
+ int x = *segment;
+ int width = *(segment + 1) - x;
+
+ if (width < 0)
+ return false;
+ }
+ }
+
+ return true;
+}
+
IntRect Region::Shape::bounds() const
{
if (isEmpty())
@@ -550,6 +571,11 @@ void Region::dump() const
}
#endif
+void Region::updateBoundsFromShape()
+{
+ m_bounds = m_shape.bounds();
+}
+
void Region::intersect(const Region& region)
{
if (m_bounds.isEmpty())
diff --git a/Source/WebCore/platform/graphics/Region.h b/Source/WebCore/platform/graphics/Region.h
index 565b49b04..d72ec4b26 100644
--- a/Source/WebCore/platform/graphics/Region.h
+++ b/Source/WebCore/platform/graphics/Region.h
@@ -32,31 +32,33 @@
namespace WebCore {
class Region {
+ WTF_MAKE_FAST_ALLOCATED;
+
public:
- Region();
- Region(const IntRect&);
+ WEBCORE_EXPORT Region();
+ WEBCORE_EXPORT Region(const IntRect&);
IntRect bounds() const { return m_bounds; }
bool isEmpty() const { return m_bounds.isEmpty(); }
bool isRect() const { return m_shape.isRect(); }
- Vector<IntRect> rects() const;
+ WEBCORE_EXPORT Vector<IntRect> rects() const;
- void unite(const Region&);
- void intersect(const Region&);
- void subtract(const Region&);
+ WEBCORE_EXPORT void unite(const Region&);
+ WEBCORE_EXPORT void intersect(const Region&);
+ WEBCORE_EXPORT void subtract(const Region&);
- void translate(const IntSize&);
+ WEBCORE_EXPORT void translate(const IntSize&);
// Returns true if the query region is a subset of this region.
- bool contains(const Region&) const;
+ WEBCORE_EXPORT bool contains(const Region&) const;
bool contains(const IntPoint&) const;
// Returns true if the query region intersects any part of this region.
bool intersects(const Region&) const;
- unsigned totalArea() const;
+ WEBCORE_EXPORT unsigned totalArea() const;
unsigned gridSize() const { return m_shape.gridSize(); }
@@ -64,10 +66,20 @@ public:
void dump() const;
#endif
-private:
+ bool isValid() const { return m_shape.isValid(); }
+
+ // This is internal to Region, but exposed just for encoding.
+ // FIXME: figure out a better way to encode WebCore classes.
struct Span {
+ Span()
+ : y(0)
+ , segmentIndex(0)
+ {
+ }
+
Span(int y, size_t segmentIndex)
- : y(y), segmentIndex(segmentIndex)
+ : y(y)
+ , segmentIndex(segmentIndex)
{
}
@@ -75,6 +87,16 @@ private:
size_t segmentIndex;
};
+ // For encoding/decoding only.
+ const Vector<int, 32>& shapeSegments() const { return m_shape.segments(); }
+ const Vector<Span, 16>& shapeSpans() const { return m_shape.spans(); }
+
+ void setShapeSegments(const Vector<int>& segments) { m_shape.setSegments(segments); }
+ void setShapeSpans(const Vector<Span>& spans) { m_shape.setSpans(spans); }
+ WEBCORE_EXPORT void updateBoundsFromShape();
+
+private:
+
class Shape {
public:
Shape();
@@ -97,7 +119,7 @@ private:
static Shape intersectShapes(const Shape& shape1, const Shape& shape2);
static Shape subtractShapes(const Shape& shape1, const Shape& shape2);
- void translate(const IntSize&);
+ WEBCORE_EXPORT void translate(const IntSize&);
void swap(Shape&);
struct CompareContainsOperation;
@@ -105,6 +127,15 @@ private:
template<typename CompareOperation>
static bool compareShapes(const Shape& shape1, const Shape& shape2);
+
+ WEBCORE_EXPORT bool isValid() const;
+
+ // For encoding/decoding only.
+ const Vector<int, 32>& segments() const { return m_segments; }
+ const Vector<Span, 16>& spans() const { return m_spans; }
+
+ void setSegments(const Vector<int>& segments) { m_segments = segments; }
+ void setSpans(const Vector<Span>& spans) { m_spans = spans; }
#ifndef NDEBUG
void dump() const;
@@ -167,6 +198,10 @@ inline bool operator==(const Region& a, const Region& b)
{
return a.m_bounds == b.m_bounds && a.m_shape == b.m_shape;
}
+inline bool operator!=(const Region& a, const Region& b)
+{
+ return !(a == b);
+}
inline bool operator==(const Region::Shape& a, const Region::Shape& b)
{
@@ -177,6 +212,10 @@ inline bool operator==(const Region::Span& a, const Region::Span& b)
{
return a.y == b.y && a.segmentIndex == b.segmentIndex;
}
+inline bool operator!=(const Region::Span& a, const Region::Span& b)
+{
+ return !(a == b);
+}
} // namespace WebCore
diff --git a/Source/WebCore/platform/graphics/RoundedRect.cpp b/Source/WebCore/platform/graphics/RoundedRect.cpp
index 8f2f1cf91..60a451f9e 100644
--- a/Source/WebCore/platform/graphics/RoundedRect.cpp
+++ b/Source/WebCore/platform/graphics/RoundedRect.cpp
@@ -28,6 +28,11 @@
#include "config.h"
#include "RoundedRect.h"
+#include "FloatRoundedRect.h"
+#include "GeometryUtilities.h"
+#include "LayoutRect.h"
+#include "LayoutUnit.h"
+
#include <algorithm>
namespace WebCore {
@@ -45,42 +50,41 @@ void RoundedRect::Radii::scale(float factor)
// If either radius on a corner becomes zero, reset both radii on that corner.
m_topLeft.scale(factor);
if (!m_topLeft.width() || !m_topLeft.height())
- m_topLeft = IntSize();
+ m_topLeft = LayoutSize();
m_topRight.scale(factor);
if (!m_topRight.width() || !m_topRight.height())
- m_topRight = IntSize();
+ m_topRight = LayoutSize();
m_bottomLeft.scale(factor);
if (!m_bottomLeft.width() || !m_bottomLeft.height())
- m_bottomLeft = IntSize();
+ m_bottomLeft = LayoutSize();
m_bottomRight.scale(factor);
if (!m_bottomRight.width() || !m_bottomRight.height())
- m_bottomRight = IntSize();
-
+ m_bottomRight = LayoutSize();
}
-void RoundedRect::Radii::expand(int topWidth, int bottomWidth, int leftWidth, int rightWidth)
+void RoundedRect::Radii::expand(const LayoutUnit& topWidth, const LayoutUnit& bottomWidth, const LayoutUnit& leftWidth, const LayoutUnit& rightWidth)
{
if (m_topLeft.width() > 0 && m_topLeft.height() > 0) {
- m_topLeft.setWidth(std::max<int>(0, m_topLeft.width() + leftWidth));
- m_topLeft.setHeight(std::max<int>(0, m_topLeft.height() + topWidth));
+ m_topLeft.setWidth(std::max<LayoutUnit>(0, m_topLeft.width() + leftWidth));
+ m_topLeft.setHeight(std::max<LayoutUnit>(0, m_topLeft.height() + topWidth));
}
if (m_topRight.width() > 0 && m_topRight.height() > 0) {
- m_topRight.setWidth(std::max<int>(0, m_topRight.width() + rightWidth));
- m_topRight.setHeight(std::max<int>(0, m_topRight.height() + topWidth));
+ m_topRight.setWidth(std::max<LayoutUnit>(0, m_topRight.width() + rightWidth));
+ m_topRight.setHeight(std::max<LayoutUnit>(0, m_topRight.height() + topWidth));
}
if (m_bottomLeft.width() > 0 && m_bottomLeft.height() > 0) {
- m_bottomLeft.setWidth(std::max<int>(0, m_bottomLeft.width() + leftWidth));
- m_bottomLeft.setHeight(std::max<int>(0, m_bottomLeft.height() + bottomWidth));
+ m_bottomLeft.setWidth(std::max<LayoutUnit>(0, m_bottomLeft.width() + leftWidth));
+ m_bottomLeft.setHeight(std::max<LayoutUnit>(0, m_bottomLeft.height() + bottomWidth));
}
if (m_bottomRight.width() > 0 && m_bottomRight.height() > 0) {
- m_bottomRight.setWidth(std::max<int>(0, m_bottomRight.width() + rightWidth));
- m_bottomRight.setHeight(std::max<int>(0, m_bottomRight.height() + bottomWidth));
+ m_bottomRight.setWidth(std::max<LayoutUnit>(0, m_bottomRight.width() + rightWidth));
+ m_bottomRight.setHeight(std::max<LayoutUnit>(0, m_bottomRight.height() + bottomWidth));
}
}
-void RoundedRect::inflateWithRadii(int size)
+void RoundedRect::inflateWithRadii(const LayoutUnit& size)
{
- IntRect old = m_rect;
+ LayoutRect old = m_rect;
m_rect.inflate(size);
// Considering the inflation factor of shorter size to scale the radii seems appropriate here
@@ -131,18 +135,18 @@ void RoundedRect::Radii::excludeLogicalEdges(bool isHorizontal, bool excludeLogi
}
}
-RoundedRect::RoundedRect(int x, int y, int width, int height)
+RoundedRect::RoundedRect(const LayoutUnit& x, const LayoutUnit& y, const LayoutUnit& width, const LayoutUnit& height)
: m_rect(x, y, width, height)
{
}
-RoundedRect::RoundedRect(const IntRect& rect, const Radii& radii)
+RoundedRect::RoundedRect(const LayoutRect& rect, const Radii& radii)
: m_rect(rect)
, m_radii(radii)
{
}
-RoundedRect::RoundedRect(const IntRect& rect, const IntSize& topLeft, const IntSize& topRight, const IntSize& bottomLeft, const IntSize& bottomRight)
+RoundedRect::RoundedRect(const LayoutRect& rect, const LayoutSize& topLeft, const LayoutSize& topRight, const LayoutSize& bottomLeft, const LayoutSize& bottomRight)
: m_rect(rect)
, m_radii(topLeft, topRight, bottomLeft, bottomRight)
{
@@ -186,7 +190,7 @@ bool RoundedRect::intersectsQuad(const FloatQuad& quad) const
if (!quad.intersectsRect(rect))
return false;
- const IntSize& topLeft = m_radii.topLeft();
+ const LayoutSize& topLeft = m_radii.topLeft();
if (!topLeft.isEmpty()) {
FloatRect rect(m_rect.x(), m_rect.y(), topLeft.width(), topLeft.height());
if (quad.intersectsRect(rect)) {
@@ -197,7 +201,7 @@ bool RoundedRect::intersectsQuad(const FloatQuad& quad) const
}
}
- const IntSize& topRight = m_radii.topRight();
+ const LayoutSize& topRight = m_radii.topRight();
if (!topRight.isEmpty()) {
FloatRect rect(m_rect.maxX() - topRight.width(), m_rect.y(), topRight.width(), topRight.height());
if (quad.intersectsRect(rect)) {
@@ -208,7 +212,7 @@ bool RoundedRect::intersectsQuad(const FloatQuad& quad) const
}
}
- const IntSize& bottomLeft = m_radii.bottomLeft();
+ const LayoutSize& bottomLeft = m_radii.bottomLeft();
if (!bottomLeft.isEmpty()) {
FloatRect rect(m_rect.x(), m_rect.maxY() - bottomLeft.height(), bottomLeft.width(), bottomLeft.height());
if (quad.intersectsRect(rect)) {
@@ -219,7 +223,7 @@ bool RoundedRect::intersectsQuad(const FloatQuad& quad) const
}
}
- const IntSize& bottomRight = m_radii.bottomRight();
+ const LayoutSize& bottomRight = m_radii.bottomRight();
if (!bottomRight.isEmpty()) {
FloatRect rect(m_rect.maxX() - bottomRight.width(), m_rect.maxY() - bottomRight.height(), bottomRight.width(), bottomRight.height());
if (quad.intersectsRect(rect)) {
@@ -233,4 +237,72 @@ bool RoundedRect::intersectsQuad(const FloatQuad& quad) const
return true;
}
+bool RoundedRect::contains(const LayoutRect& otherRect) const
+{
+ if (!rect().contains(otherRect))
+ return false;
+
+ const LayoutSize& topLeft = m_radii.topLeft();
+ if (!topLeft.isEmpty()) {
+ FloatPoint center = { m_rect.x() + topLeft.width(), m_rect.y() + topLeft.height() };
+ if (otherRect.x() <= center.x() && otherRect.y() <= center.y()) {
+ if (!ellipseContainsPoint(center, topLeft, otherRect.location()))
+ return false;
+ }
+ }
+
+ const LayoutSize& topRight = m_radii.topRight();
+ if (!topRight.isEmpty()) {
+ FloatPoint center = { m_rect.maxX() - topRight.width(), m_rect.y() + topRight.height() };
+ if (otherRect.maxX() >= center.x() && otherRect.y() <= center.y()) {
+ if (!ellipseContainsPoint(center, topRight, otherRect.location()))
+ return false;
+ }
+ }
+
+ const LayoutSize& bottomLeft = m_radii.bottomLeft();
+ if (!bottomLeft.isEmpty()) {
+ FloatPoint center = { m_rect.x() + bottomLeft.width(), m_rect.maxY() - bottomLeft.height() };
+ if (otherRect.maxX() >= center.x() && otherRect.maxY() >= center.y()) {
+ if (!ellipseContainsPoint(center, bottomLeft, otherRect.location()))
+ return false;
+ }
+ }
+
+ const LayoutSize& bottomRight = m_radii.bottomRight();
+ if (!bottomRight.isEmpty()) {
+ FloatPoint center = { m_rect.maxX() - bottomRight.width(), m_rect.maxY() - bottomRight.height() };
+ if (otherRect.x() <= center.x() && otherRect.maxY() >= center.y()) {
+ if (!ellipseContainsPoint(center, bottomRight, otherRect.location()))
+ return false;
+ }
+ }
+
+ return true;
+}
+
+FloatRoundedRect RoundedRect::pixelSnappedRoundedRectForPainting(float deviceScaleFactor) const
+{
+ LayoutRect originalRect = rect();
+ if (originalRect.isEmpty())
+ return FloatRoundedRect(originalRect, radii());
+
+ FloatRect pixelSnappedRect = snapRectToDevicePixels(originalRect, deviceScaleFactor);
+
+ if (!isRenderable())
+ return FloatRoundedRect(pixelSnappedRect, radii());
+
+ // Snapping usually does not alter size, but when it does, we need to make sure that the final rect is still renderable by distributing the size delta proportionally.
+ FloatRoundedRect::Radii adjustedRadii = radii();
+ adjustedRadii.scale(pixelSnappedRect.width() / originalRect.width().toFloat(), pixelSnappedRect.height() / originalRect.height().toFloat());
+ FloatRoundedRect snappedRoundedRect = FloatRoundedRect(pixelSnappedRect, adjustedRadii);
+ if (!snappedRoundedRect.isRenderable()) {
+ // Floating point mantissa overflow can produce a non-renderable rounded rect.
+ adjustedRadii.shrink(1 / deviceScaleFactor);
+ snappedRoundedRect.setRadii(adjustedRadii);
+ }
+ ASSERT(snappedRoundedRect.isRenderable());
+ return snappedRoundedRect;
+}
+
} // namespace WebCore
diff --git a/Source/WebCore/platform/graphics/RoundedRect.h b/Source/WebCore/platform/graphics/RoundedRect.h
index d923e353f..3f2f7ebc8 100644
--- a/Source/WebCore/platform/graphics/RoundedRect.h
+++ b/Source/WebCore/platform/graphics/RoundedRect.h
@@ -28,17 +28,20 @@
#define RoundedRect_h
#include "FloatQuad.h"
-#include "IntRect.h"
+#include "LayoutRect.h"
+#include "LayoutSize.h"
namespace WebCore {
+class FloatRoundedRect;
+class LayoutUnit;
class RoundedRect {
public:
class Radii {
public:
Radii() {}
- Radii(const IntSize& topLeft, const IntSize& topRight, const IntSize& bottomLeft, const IntSize& bottomRight)
+ Radii(const LayoutSize& topLeft, const LayoutSize& topRight, const LayoutSize& bottomLeft, const LayoutSize& bottomRight)
: m_topLeft(topLeft)
, m_topRight(topRight)
, m_bottomLeft(bottomLeft)
@@ -46,14 +49,14 @@ public:
{
}
- void setTopLeft(const IntSize& size) { m_topLeft = size; }
- void setTopRight(const IntSize& size) { m_topRight = size; }
- void setBottomLeft(const IntSize& size) { m_bottomLeft = size; }
- void setBottomRight(const IntSize& size) { m_bottomRight = size; }
- const IntSize& topLeft() const { return m_topLeft; }
- const IntSize& topRight() const { return m_topRight; }
- const IntSize& bottomLeft() const { return m_bottomLeft; }
- const IntSize& bottomRight() const { return m_bottomRight; }
+ void setTopLeft(const LayoutSize& size) { m_topLeft = size; }
+ void setTopRight(const LayoutSize& size) { m_topRight = size; }
+ void setBottomLeft(const LayoutSize& size) { m_bottomLeft = size; }
+ void setBottomRight(const LayoutSize& size) { m_bottomRight = size; }
+ const LayoutSize& topLeft() const { return m_topLeft; }
+ const LayoutSize& topRight() const { return m_topRight; }
+ const LayoutSize& bottomLeft() const { return m_bottomLeft; }
+ const LayoutSize& bottomRight() const { return m_bottomRight; }
bool isZero() const;
@@ -61,35 +64,38 @@ public:
void excludeLogicalEdges(bool isHorizontal, bool excludeLogicalLeftEdge, bool excludeLogicalRightEdge);
void scale(float factor);
- void expand(int topWidth, int bottomWidth, int leftWidth, int rightWidth);
- void expand(int size) { expand(size, size, size, size); }
- void shrink(int topWidth, int bottomWidth, int leftWidth, int rightWidth) { expand(-topWidth, -bottomWidth, -leftWidth, -rightWidth); }
- void shrink(int size) { shrink(size, size, size, size); }
+ void expand(const LayoutUnit& topWidth, const LayoutUnit& bottomWidth, const LayoutUnit& leftWidth, const LayoutUnit& rightWidth);
+ void expand(const LayoutUnit& size) { expand(size, size, size, size); }
+ void shrink(const LayoutUnit& topWidth, const LayoutUnit& bottomWidth, const LayoutUnit& leftWidth, const LayoutUnit& rightWidth) { expand(-topWidth, -bottomWidth, -leftWidth, -rightWidth); }
+ void shrink(const LayoutUnit& size) { shrink(size, size, size, size); }
+
+ Radii transposedRadii() const { return Radii(m_topLeft.transposedSize(), m_topRight.transposedSize(), m_bottomLeft.transposedSize(), m_bottomRight.transposedSize()); }
private:
- IntSize m_topLeft;
- IntSize m_topRight;
- IntSize m_bottomLeft;
- IntSize m_bottomRight;
+ LayoutSize m_topLeft;
+ LayoutSize m_topRight;
+ LayoutSize m_bottomLeft;
+ LayoutSize m_bottomRight;
};
- explicit RoundedRect(const IntRect&, const Radii& = Radii());
- RoundedRect(int x, int y, int width, int height);
- RoundedRect(const IntRect&, const IntSize& topLeft, const IntSize& topRight, const IntSize& bottomLeft, const IntSize& bottomRight);
+ explicit RoundedRect(const LayoutRect&, const Radii& = Radii());
+ RoundedRect(const LayoutUnit&, const LayoutUnit&, const LayoutUnit& width, const LayoutUnit& height);
+ RoundedRect(const LayoutRect&, const LayoutSize& topLeft, const LayoutSize& topRight, const LayoutSize& bottomLeft, const LayoutSize& bottomRight);
- const IntRect& rect() const { return m_rect; }
+ const LayoutRect& rect() const { return m_rect; }
const Radii& radii() const { return m_radii; }
bool isRounded() const { return !m_radii.isZero(); }
bool isEmpty() const { return m_rect.isEmpty(); }
- void setRect(const IntRect& rect) { m_rect = rect; }
+ void setRect(const LayoutRect& rect) { m_rect = rect; }
void setRadii(const Radii& radii) { m_radii = radii; }
- void move(const IntSize& size) { m_rect.move(size); }
- void inflate(int size) { m_rect.inflate(size); }
- void inflateWithRadii(int size);
- void expandRadii(int size) { m_radii.expand(size); }
- void shrinkRadii(int size) { m_radii.shrink(size); }
+ void move(const LayoutSize& size) { m_rect.move(size); }
+ void moveBy(const LayoutPoint& offset) { m_rect.moveBy(offset); }
+ void inflate(const LayoutUnit& size) { m_rect.inflate(size); }
+ void inflateWithRadii(const LayoutUnit& size);
+ void expandRadii(const LayoutUnit& size) { m_radii.expand(size); }
+ void shrinkRadii(const LayoutUnit& size) { m_radii.shrink(size); }
void includeLogicalEdges(const Radii& edges, bool isHorizontal, bool includeLogicalLeftEdge, bool includeLogicalRightEdge);
void excludeLogicalEdges(bool isHorizontal, bool excludeLogicalLeftEdge, bool excludeLogicalRightEdge);
@@ -100,9 +106,14 @@ public:
// Tests whether the quad intersects any part of this rounded rectangle.
// This only works for convex quads.
bool intersectsQuad(const FloatQuad&) const;
+ bool contains(const LayoutRect&) const;
+
+ FloatRoundedRect pixelSnappedRoundedRectForPainting(float deviceScaleFactor) const;
+
+ RoundedRect transposedRect() const { return RoundedRect(m_rect.transposedRect(), m_radii.transposedRadii()); }
private:
- IntRect m_rect;
+ LayoutRect m_rect;
Radii m_radii;
};
diff --git a/Source/WebCore/platform/graphics/SVGGlyph.cpp b/Source/WebCore/platform/graphics/SVGGlyph.cpp
deleted file mode 100644
index ad3be1efc..000000000
--- a/Source/WebCore/platform/graphics/SVGGlyph.cpp
+++ /dev/null
@@ -1,189 +0,0 @@
-/*
- * Copyright (C) 2007, 2008 Nikolas Zimmermann <zimmermann@kde.org>
- * Copyright (C) Research In Motion Limited 2010-2011. All rights reserved.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public License
- * along with this library; see the file COPYING.LIB. If not, write to
- * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
- * Boston, MA 02110-1301, USA.
- */
-
-#include "config.h"
-
-#if ENABLE(SVG_FONTS)
-#include "SVGGlyph.h"
-
-#include <wtf/unicode/Unicode.h>
-
-namespace WebCore {
-
-// Helper functions to determine the arabic character forms (initial, medial, terminal, isolated)
-enum ArabicCharShapingMode {
- SNone = 0,
- SRight = 1,
- SDual = 2
-};
-
-static const ArabicCharShapingMode s_arabicCharShapingMode[222] = {
- SRight, SRight, SRight, SRight, SDual , SRight, SDual , SRight, SDual , SDual , SDual , SDual , SDual , SRight, /* 0x0622 - 0x062F */
- SRight, SRight, SRight, SDual , SDual , SDual , SDual , SDual , SDual , SDual , SDual , SNone , SNone , SNone , SNone , SNone , /* 0x0630 - 0x063F */
- SNone , SDual , SDual , SDual , SDual , SDual , SDual , SRight, SDual , SDual , SNone , SNone , SNone , SNone , SNone , SNone , /* 0x0640 - 0x064F */
- SNone , SNone , SNone , SNone , SNone , SNone , SNone , SNone , SNone , SNone , SNone , SNone , SNone , SNone , SNone , SNone , /* 0x0650 - 0x065F */
- SNone , SNone , SNone , SNone , SNone , SNone , SNone , SNone , SNone , SNone , SNone , SNone , SNone , SNone , SNone , SNone , /* 0x0660 - 0x066F */
- SNone , SRight, SRight, SRight, SNone , SRight, SRight, SRight, SDual , SDual , SDual , SDual , SDual , SDual , SDual , SDual , /* 0x0670 - 0x067F */
- SDual , SDual , SDual , SDual , SDual , SDual , SDual , SDual , SRight, SRight, SRight, SRight, SRight, SRight, SRight, SRight, /* 0x0680 - 0x068F */
- SRight, SRight, SRight, SRight, SRight, SRight, SRight, SRight, SRight, SRight, SDual , SDual , SDual , SDual , SDual , SDual , /* 0x0690 - 0x069F */
- SDual , SDual , SDual , SDual , SDual , SDual , SDual , SDual , SDual , SDual , SDual , SDual , SDual , SDual , SDual , SDual , /* 0x06A0 - 0x06AF */
- SDual , SDual , SDual , SDual , SDual , SDual , SDual , SDual , SDual , SDual , SDual , SDual , SDual , SDual , SDual , SDual , /* 0x06B0 - 0x06BF */
- SRight, SDual , SRight, SRight, SRight, SRight, SRight, SRight, SRight, SRight, SRight, SRight, SDual , SRight, SDual , SRight, /* 0x06C0 - 0x06CF */
- SDual , SDual , SRight, SRight, SNone , SNone , SNone , SNone , SNone , SNone , SNone , SNone , SNone , SNone , SNone , SNone , /* 0x06D0 - 0x06DF */
- SNone , SNone , SNone , SNone , SNone , SNone , SNone , SNone , SNone , SNone , SNone , SNone , SNone , SNone , SNone , SNone , /* 0x06E0 - 0x06EF */
- SNone , SNone , SNone , SNone , SNone , SNone , SNone , SNone , SNone , SNone , SDual , SDual , SDual , SNone , SNone , SNone /* 0x06F0 - 0x06FF */
-};
-
-static inline SVGGlyph::ArabicForm processArabicFormDetection(const UChar& curChar, bool& lastCharShapesRight, SVGGlyph::ArabicForm* prevForm)
-{
- SVGGlyph::ArabicForm curForm;
-
- ArabicCharShapingMode shapingMode = SNone;
- if (curChar >= 0x0622 && curChar <= 0x06FF)
- shapingMode = s_arabicCharShapingMode[curChar - 0x0622];
-
- // Use a simple state machine to identify the actual arabic form
- // It depends on the order of the arabic form enum:
- // enum ArabicForm { None = 0, Isolated, Terminal, Initial, Medial };
-
- if (lastCharShapesRight && shapingMode == SDual) {
- if (prevForm) {
- int correctedForm = (int) *prevForm + 1;
- ASSERT(correctedForm >= SVGGlyph::None && correctedForm <= SVGGlyph::Medial);
- *prevForm = static_cast<SVGGlyph::ArabicForm>(correctedForm);
- }
-
- curForm = SVGGlyph::Initial;
- } else
- curForm = shapingMode == SNone ? SVGGlyph::None : SVGGlyph::Isolated;
-
- lastCharShapesRight = shapingMode != SNone;
- return curForm;
-}
-
-Vector<SVGGlyph::ArabicForm> charactersWithArabicForm(const String& input, bool rtl)
-{
- Vector<SVGGlyph::ArabicForm> forms;
- unsigned length = input.length();
-
- bool containsArabic = false;
- for (unsigned i = 0; i < length; ++i) {
- if (ublock_getCode(input[i]) == UBLOCK_ARABIC) {
- containsArabic = true;
- break;
- }
- }
-
- if (!containsArabic)
- return forms;
-
- bool lastCharShapesRight = false;
-
- // Start identifying arabic forms
- if (rtl) {
- for (int i = length - 1; i >= 0; --i)
- forms.insert(0, processArabicFormDetection(input[i], lastCharShapesRight, forms.isEmpty() ? 0 : &forms.first()));
- } else {
- for (unsigned i = 0; i < length; ++i)
- forms.append(processArabicFormDetection(input[i], lastCharShapesRight, forms.isEmpty() ? 0 : &forms.last()));
- }
-
- return forms;
-}
-
-static inline bool isCompatibleArabicForm(const SVGGlyph& identifier, const Vector<SVGGlyph::ArabicForm>& chars, unsigned startPosition, unsigned endPosition)
-{
- if (chars.isEmpty())
- return true;
-
- Vector<SVGGlyph::ArabicForm>::const_iterator realEnd = chars.end();
- Vector<SVGGlyph::ArabicForm>::const_iterator it = chars.begin() + startPosition;
- if (it >= realEnd)
- return true;
-
- Vector<SVGGlyph::ArabicForm>::const_iterator end = chars.begin() + endPosition;
- if (end >= realEnd)
- end = realEnd;
-
- for (; it != end; ++it) {
- if (*it != static_cast<SVGGlyph::ArabicForm>(identifier.arabicForm) && *it != SVGGlyph::None)
- return false;
- }
-
- return true;
-}
-
-bool isCompatibleGlyph(const SVGGlyph& identifier, bool isVerticalText, const String& language,
- const Vector<SVGGlyph::ArabicForm>& chars, unsigned startPosition, unsigned endPosition)
-{
- bool valid = true;
-
- // Check wheter orientation if glyph fits within the request
- switch (identifier.orientation) {
- case SVGGlyph::Vertical:
- valid = isVerticalText;
- break;
- case SVGGlyph::Horizontal:
- valid = !isVerticalText;
- break;
- case SVGGlyph::Both:
- break;
- }
-
- if (!valid)
- return false;
-
- // Check wheter languages are compatible
- if (!identifier.languages.isEmpty()) {
- // This glyph exists only in certain languages, if we're not specifying a
- // language on the referencing element we're unable to use this glyph.
- if (language.isEmpty())
- return false;
-
- // Split subcode from language, if existant.
- String languagePrefix;
-
- size_t subCodeSeparator = language.find('-');
- if (subCodeSeparator != notFound)
- languagePrefix = language.left(subCodeSeparator);
-
- Vector<String>::const_iterator it = identifier.languages.begin();
- Vector<String>::const_iterator end = identifier.languages.end();
-
- bool found = false;
- for (; it != end; ++it) {
- const String& cur = *it;
- if (cur == language || cur == languagePrefix) {
- found = true;
- break;
- }
- }
-
- if (!found)
- return false;
- }
-
- // Check wheter arabic form is compatible
- return isCompatibleArabicForm(identifier, chars, startPosition, endPosition);
-}
-
-}
-
-#endif
diff --git a/Source/WebCore/platform/graphics/SVGGlyph.h b/Source/WebCore/platform/graphics/SVGGlyph.h
deleted file mode 100644
index 272811bb6..000000000
--- a/Source/WebCore/platform/graphics/SVGGlyph.h
+++ /dev/null
@@ -1,113 +0,0 @@
-/*
- * Copyright (C) 2007 Eric Seidel <eric@webkit.org>
- * Copyright (C) 2007 Nikolas Zimmermann <zimmermann@kde.org>
- * Copyright (C) 2008 Rob Buis <buis@kde.org>
- * Copyright (C) Research In Motion Limited 2011. All rights reserved.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public License
- * along with this library; see the file COPYING.LIB. If not, write to
- * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
- * Boston, MA 02110-1301, USA.
- */
-
-#ifndef SVGGlyph_h
-#define SVGGlyph_h
-
-#if ENABLE(SVG_FONTS)
-#include "Glyph.h"
-#include "Path.h"
-
-#include <limits>
-#include <wtf/Vector.h>
-#include <wtf/text/WTFString.h>
-
-namespace WebCore {
-
-// Describe a glyph from a SVG Font.
-struct SVGGlyph {
- enum Orientation {
- Vertical,
- Horizontal,
- Both
- };
-
- // SVG Font depends on exactly this order.
- enum ArabicForm {
- None = 0,
- Isolated,
- Terminal,
- Initial,
- Medial
- };
-
- SVGGlyph()
- : isPartOfLigature(false)
- , orientation(Both)
- , arabicForm(None)
- , priority(0)
- , tableEntry(0)
- , unicodeStringLength(0)
- , horizontalAdvanceX(0)
- , verticalOriginX(0)
- , verticalOriginY(0)
- , verticalAdvanceY(0)
- {
- }
-
- // Used to mark our float properties as "to be inherited from SVGFontData"
- static float inheritedValue()
- {
- static float s_inheritedValue = std::numeric_limits<float>::infinity();
- return s_inheritedValue;
- }
-
- bool operator==(const SVGGlyph& other) const
- {
- return isPartOfLigature == other.isPartOfLigature
- && orientation == other.orientation
- && arabicForm == other.arabicForm
- && tableEntry == other.tableEntry
- && unicodeStringLength == other.unicodeStringLength
- && glyphName == other.glyphName
- && horizontalAdvanceX == other.horizontalAdvanceX
- && verticalOriginX == other.verticalOriginX
- && verticalOriginY == other.verticalOriginY
- && verticalAdvanceY == other.verticalAdvanceY
- && languages == other.languages;
- }
-
- bool isPartOfLigature : 1;
-
- unsigned orientation : 2; // Orientation
- unsigned arabicForm : 3; // ArabicForm
- int priority;
- Glyph tableEntry;
- size_t unicodeStringLength;
- String glyphName;
-
- float horizontalAdvanceX;
- float verticalOriginX;
- float verticalOriginY;
- float verticalAdvanceY;
-
- Path pathData;
- Vector<String> languages;
-};
-
-Vector<SVGGlyph::ArabicForm> charactersWithArabicForm(const String& input, bool rtl);
-bool isCompatibleGlyph(const SVGGlyph&, bool isVerticalText, const String& language, const Vector<SVGGlyph::ArabicForm>&, unsigned startPosition, unsigned endPosition);
-
-} // namespace WebCore
-
-#endif // ENABLE(SVG_FONTS)
-#endif
diff --git a/Source/WebCore/platform/graphics/SegmentedFontData.cpp b/Source/WebCore/platform/graphics/SegmentedFontData.cpp
deleted file mode 100644
index efb20a8c1..000000000
--- a/Source/WebCore/platform/graphics/SegmentedFontData.cpp
+++ /dev/null
@@ -1,99 +0,0 @@
-/*
- * Copyright (C) 2008, 2009 Apple Inc. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
- * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
- * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#include "config.h"
-#include "SegmentedFontData.h"
-
-#include "SimpleFontData.h"
-#include <wtf/Assertions.h>
-#include <wtf/text/WTFString.h>
-
-namespace WebCore {
-
-SegmentedFontData::~SegmentedFontData()
-{
- GlyphPageTreeNode::pruneTreeCustomFontData(this);
-}
-
-const SimpleFontData* SegmentedFontData::fontDataForCharacter(UChar32 c) const
-{
- Vector<FontDataRange>::const_iterator end = m_ranges.end();
- for (Vector<FontDataRange>::const_iterator it = m_ranges.begin(); it != end; ++it) {
- if (it->from() <= c && it->to() >= c)
- return it->fontData().get();
- }
- return m_ranges[0].fontData().get();
-}
-
-bool SegmentedFontData::containsCharacter(UChar32 c) const
-{
- Vector<FontDataRange>::const_iterator end = m_ranges.end();
- for (Vector<FontDataRange>::const_iterator it = m_ranges.begin(); it != end; ++it) {
- if (c >= it->from() && c <= it->to())
- return true;
- }
- return false;
-}
-
-bool SegmentedFontData::containsCharacters(const UChar* characters, int length) const
-{
- UChar32 c;
- for (int i = 0; i < length; ) {
- U16_NEXT(characters, i, length, c)
- if (!containsCharacter(c))
- return false;
- }
- return true;
-}
-
-bool SegmentedFontData::isCustomFont() const
-{
- // All segmented fonts are custom fonts.
- return true;
-}
-
-bool SegmentedFontData::isLoading() const
-{
- Vector<FontDataRange>::const_iterator end = m_ranges.end();
- for (Vector<FontDataRange>::const_iterator it = m_ranges.begin(); it != end; ++it) {
- if (it->fontData()->isLoading())
- return true;
- }
- return false;
-}
-
-bool SegmentedFontData::isSegmented() const
-{
- return true;
-}
-
-#ifndef NDEBUG
-String SegmentedFontData::description() const
-{
- return "[segmented font]";
-}
-#endif
-
-} // namespace WebCore
diff --git a/Source/WebCore/platform/graphics/SegmentedFontData.h b/Source/WebCore/platform/graphics/SegmentedFontData.h
deleted file mode 100644
index 45d7d158c..000000000
--- a/Source/WebCore/platform/graphics/SegmentedFontData.h
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
- * Copyright (C) 2008, 2009 Apple Inc. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
- * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
- * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#ifndef SegmentedFontData_h
-#define SegmentedFontData_h
-
-#include "FontData.h"
-#include <wtf/Vector.h>
-
-namespace WebCore {
-
-class SimpleFontData;
-
-struct FontDataRange {
- FontDataRange(UChar32 from, UChar32 to, PassRefPtr<SimpleFontData> fontData)
- : m_from(from)
- , m_to(to)
- , m_fontData(fontData)
- {
- }
-
- UChar32 from() const { return m_from; }
- UChar32 to() const { return m_to; }
- PassRefPtr<SimpleFontData> fontData() const { return m_fontData; }
-
-private:
- UChar32 m_from;
- UChar32 m_to;
- RefPtr<SimpleFontData> m_fontData;
-};
-
-class SegmentedFontData : public FontData {
-public:
- static PassRefPtr<SegmentedFontData> create() { return adoptRef(new SegmentedFontData); }
-
- virtual ~SegmentedFontData();
-
- void appendRange(const FontDataRange& range) { m_ranges.append(range); }
- unsigned numRanges() const { return m_ranges.size(); }
- const FontDataRange& rangeAt(unsigned i) const { return m_ranges[i]; }
-
-#ifndef NDEBUG
- virtual String description() const;
-#endif
-
-private:
- SegmentedFontData() { }
-
- virtual const SimpleFontData* fontDataForCharacter(UChar32) const;
- virtual bool containsCharacters(const UChar*, int length) const;
-
- virtual bool isCustomFont() const;
- virtual bool isLoading() const;
- virtual bool isSegmented() const;
-
- bool containsCharacter(UChar32) const;
-
- Vector<FontDataRange, 1> m_ranges;
-};
-
-} // namespace WebCore
-
-#endif // SegmentedFontData_h
diff --git a/Source/WebCore/platform/graphics/ShadowBlur.cpp b/Source/WebCore/platform/graphics/ShadowBlur.cpp
index 82b180a46..f17b3ffe7 100644
--- a/Source/WebCore/platform/graphics/ShadowBlur.cpp
+++ b/Source/WebCore/platform/graphics/ShadowBlur.cpp
@@ -17,7 +17,7 @@
* THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
@@ -36,6 +36,7 @@
#include "ImageBuffer.h"
#include "Timer.h"
#include <wtf/MathExtras.h>
+#include <wtf/NeverDestroyed.h>
#include <wtf/Noncopyable.h>
namespace WebCore {
@@ -57,7 +58,7 @@ class ScratchBuffer {
WTF_MAKE_FAST_ALLOCATED;
public:
ScratchBuffer()
- : m_purgeTimer(this, &ScratchBuffer::timerFired)
+ : m_purgeTimer(*this, &ScratchBuffer::clearScratchBuffer)
, m_lastWasInset(false)
#if !ASSERT_DISABLED
, m_bufferInUse(false)
@@ -79,19 +80,20 @@ public:
IntSize roundedSize(roundUpToMultipleOf32(size.width()), roundUpToMultipleOf32(size.height()));
clearScratchBuffer();
- m_imageBuffer = ImageBuffer::create(roundedSize, 1);
+
+ // ShadowBlur is not used with accelerated drawing, so it's OK to make an unconditionally unaccelerated buffer.
+ m_imageBuffer = ImageBuffer::create(roundedSize, Unaccelerated, 1);
return m_imageBuffer.get();
}
- bool setCachedShadowValues(const FloatSize& radius, const Color& color, ColorSpace colorSpace, const FloatRect& shadowRect, const RoundedRect::Radii& radii, const FloatSize& layerSize)
+ bool setCachedShadowValues(const FloatSize& radius, const Color& color, const FloatRect& shadowRect, const FloatRoundedRect::Radii& radii, const FloatSize& layerSize)
{
- if (!m_lastWasInset && m_lastRadius == radius && m_lastColor == color && m_lastColorSpace == colorSpace && m_lastShadowRect == shadowRect && m_lastRadii == radii && m_lastLayerSize == layerSize)
+ if (!m_lastWasInset && m_lastRadius == radius && m_lastColor == color && m_lastShadowRect == shadowRect && m_lastRadii == radii && m_lastLayerSize == layerSize)
return false;
m_lastWasInset = false;
m_lastRadius = radius;
m_lastColor = color;
- m_lastColorSpace = colorSpace;
m_lastShadowRect = shadowRect;
m_lastRadii = radii;
m_lastLayerSize = layerSize;
@@ -99,16 +101,15 @@ public:
return true;
}
- bool setCachedInsetShadowValues(const FloatSize& radius, const Color& color, ColorSpace colorSpace, const FloatRect& bounds, const FloatRect& shadowRect, const RoundedRect::Radii& radii)
+ bool setCachedInsetShadowValues(const FloatSize& radius, const Color& color, const FloatRect& bounds, const FloatRect& shadowRect, const FloatRoundedRect::Radii& radii)
{
- if (m_lastWasInset && m_lastRadius == radius && m_lastColor == color && m_lastColorSpace == colorSpace && m_lastInsetBounds == bounds && shadowRect == m_lastShadowRect && radii == m_lastRadii)
+ if (m_lastWasInset && m_lastRadius == radius && m_lastColor == color && m_lastInsetBounds == bounds && shadowRect == m_lastShadowRect && radii == m_lastRadii)
return false;
m_lastWasInset = true;
m_lastInsetBounds = bounds;
m_lastRadius = radius;
m_lastColor = color;
- m_lastColorSpace = colorSpace;
m_lastShadowRect = shadowRect;
m_lastRadii = radii;
@@ -127,14 +128,9 @@ public:
m_purgeTimer.startOneShot(scratchBufferPurgeInterval);
}
- static ScratchBuffer& shared();
+ static ScratchBuffer& singleton();
private:
- void timerFired(Timer<ScratchBuffer>*)
- {
- clearScratchBuffer();
- }
-
void clearScratchBuffer()
{
m_imageBuffer = nullptr;
@@ -143,13 +139,12 @@ private:
}
std::unique_ptr<ImageBuffer> m_imageBuffer;
- Timer<ScratchBuffer> m_purgeTimer;
+ Timer m_purgeTimer;
FloatRect m_lastInsetBounds;
FloatRect m_lastShadowRect;
- RoundedRect::Radii m_lastRadii;
+ FloatRoundedRect::Radii m_lastRadii;
Color m_lastColor;
- ColorSpace m_lastColorSpace;
FloatSize m_lastRadius;
bool m_lastWasInset;
FloatSize m_lastLayerSize;
@@ -159,9 +154,9 @@ private:
#endif
};
-ScratchBuffer& ScratchBuffer::shared()
+ScratchBuffer& ScratchBuffer::singleton()
{
- DEFINE_STATIC_LOCAL(ScratchBuffer, scratchBuffer, ());
+ static NeverDestroyed<ScratchBuffer> scratchBuffer;
return scratchBuffer;
}
@@ -174,9 +169,8 @@ static float radiusToLegacyRadius(float radius)
}
#endif
-ShadowBlur::ShadowBlur(const FloatSize& radius, const FloatSize& offset, const Color& color, ColorSpace colorSpace)
+ShadowBlur::ShadowBlur(const FloatSize& radius, const FloatSize& offset, const Color& color)
: m_color(color)
- , m_colorSpace(colorSpace)
, m_blurRadius(radius)
, m_offset(offset)
, m_layerImage(0)
@@ -187,7 +181,6 @@ ShadowBlur::ShadowBlur(const FloatSize& radius, const FloatSize& offset, const C
ShadowBlur::ShadowBlur(const GraphicsContextState& state)
: m_color(state.shadowColor)
- , m_colorSpace(state.shadowColorSpace)
, m_blurRadius(state.shadowBlur, state.shadowBlur)
, m_offset(state.shadowOffset)
, m_layerImage(0)
@@ -209,12 +202,11 @@ ShadowBlur::ShadowBlur()
{
}
-void ShadowBlur::setShadowValues(const FloatSize& radius, const FloatSize& offset, const Color& color, ColorSpace colorSpace, bool ignoreTransforms)
+void ShadowBlur::setShadowValues(const FloatSize& radius, const FloatSize& offset, const Color& color, bool ignoreTransforms)
{
m_blurRadius = radius;
m_offset = offset;
m_color = color;
- m_colorSpace = colorSpace;
m_shadowsIgnoreTransforms = ignoreTransforms;
updateShadowBlurValues();
@@ -226,7 +218,7 @@ void ShadowBlur::updateShadowBlurValues()
m_blurRadius = m_blurRadius.shrunkTo(FloatSize(128, 128));
// The type of shadow is decided by the blur radius, shadow offset, and shadow color.
- if (!m_color.isValid() || !m_color.alpha()) {
+ if (!m_color.isVisible()) {
// Can't paint the shadow with invalid or invisible color.
m_type = NoShadow;
} else if (m_blurRadius.width() > 0 || m_blurRadius.height() > 0) {
@@ -373,12 +365,12 @@ void ShadowBlur::blurLayerImage(unsigned char* imageData, const IntSize& size, i
}
}
-void ShadowBlur::adjustBlurRadius(GraphicsContext* context)
+void ShadowBlur::adjustBlurRadius(GraphicsContext& context)
{
if (!m_shadowsIgnoreTransforms)
return;
- AffineTransform transform = context->getCTM();
+ AffineTransform transform = context.getCTM();
m_blurRadius.scale(1 / static_cast<float>(transform.xScale()), 1 / static_cast<float>(transform.yScale()));
}
@@ -396,7 +388,7 @@ IntSize ShadowBlur::blurredEdgeSize() const
return edgeSize;
}
-IntRect ShadowBlur::calculateLayerBoundingRect(GraphicsContext* context, const FloatRect& shadowedRect, const IntRect& clipRect)
+IntSize ShadowBlur::calculateLayerBoundingRect(GraphicsContext& context, const FloatRect& shadowedRect, const IntRect& clipRect)
{
IntSize edgeSize = blurredEdgeSize();
@@ -404,11 +396,11 @@ IntRect ShadowBlur::calculateLayerBoundingRect(GraphicsContext* context, const F
FloatRect layerRect;
IntSize inflation;
- const AffineTransform transform = context->getCTM();
+ const AffineTransform transform = context.getCTM();
if (m_shadowsIgnoreTransforms && !transform.isIdentity()) {
FloatQuad transformedPolygon = transform.mapQuad(FloatQuad(shadowedRect));
transformedPolygon.move(m_offset);
- layerRect = transform.inverse().mapQuad(transformedPolygon).boundingBox();
+ layerRect = transform.inverse().value_or(AffineTransform()).mapQuad(transformedPolygon).boundingBox();
} else {
layerRect = shadowedRect;
layerRect.move(m_offset);
@@ -426,7 +418,7 @@ IntRect ShadowBlur::calculateLayerBoundingRect(GraphicsContext* context, const F
if (!clipRect.contains(enclosingIntRect(layerRect))) {
// If we are totally outside the clip region, we aren't painting at all.
if (intersection(layerRect, clipRect).isEmpty())
- return IntRect();
+ return IntSize();
IntRect inflatedClip = clipRect;
// Pixels at the edges can be affected by pixels outside the buffer,
@@ -459,30 +451,30 @@ IntRect ShadowBlur::calculateLayerBoundingRect(GraphicsContext* context, const F
float translationY = -shadowedRect.y() + inflation.height() - fabsf(clippedOut.height());
m_layerContextTranslation = FloatSize(translationX, translationY);
- return enclosingIntRect(layerRect);
+ return expandedIntSize(layerRect.size());
}
-void ShadowBlur::drawShadowBuffer(GraphicsContext* graphicsContext)
+void ShadowBlur::drawShadowBuffer(GraphicsContext& graphicsContext)
{
if (!m_layerImage)
return;
- GraphicsContextStateSaver stateSaver(*graphicsContext);
+ GraphicsContextStateSaver stateSaver(graphicsContext);
IntSize bufferSize = m_layerImage->internalSize();
if (bufferSize != m_layerSize) {
// The rect passed to clipToImageBuffer() has to be the size of the entire buffer,
// but we may not have cleared it all, so clip to the filled part first.
- graphicsContext->clip(FloatRect(m_layerOrigin, m_layerSize));
+ graphicsContext.clip(FloatRect(m_layerOrigin, m_layerSize));
}
- graphicsContext->clipToImageBuffer(m_layerImage, FloatRect(m_layerOrigin, bufferSize));
- graphicsContext->setFillColor(m_color, m_colorSpace);
+ graphicsContext.clipToImageBuffer(*m_layerImage, FloatRect(m_layerOrigin, bufferSize));
+ graphicsContext.setFillColor(m_color);
- graphicsContext->clearShadow();
- graphicsContext->fillRect(FloatRect(m_layerOrigin, m_sourceRect.size()));
+ graphicsContext.clearShadow();
+ graphicsContext.fillRect(FloatRect(m_layerOrigin, m_sourceRect.size()));
}
-static void computeSliceSizesFromRadii(const IntSize& twiceRadius, const RoundedRect::Radii& radii, int& leftSlice, int& rightSlice, int& topSlice, int& bottomSlice)
+static void computeSliceSizesFromRadii(const IntSize& twiceRadius, const FloatRoundedRect::Radii& radii, int& leftSlice, int& rightSlice, int& topSlice, int& bottomSlice)
{
leftSlice = twiceRadius.width() + std::max(radii.topLeft().width(), radii.bottomLeft().width());
rightSlice = twiceRadius.width() + std::max(radii.topRight().width(), radii.bottomRight().width());
@@ -491,7 +483,7 @@ static void computeSliceSizesFromRadii(const IntSize& twiceRadius, const Rounded
bottomSlice = twiceRadius.height() + std::max(radii.bottomLeft().height(), radii.bottomRight().height());
}
-IntSize ShadowBlur::templateSize(const IntSize& radiusPadding, const RoundedRect::Radii& radii) const
+IntSize ShadowBlur::templateSize(const IntSize& radiusPadding, const FloatRoundedRect::Radii& radii) const
{
const int templateSideLength = 1;
@@ -509,134 +501,136 @@ IntSize ShadowBlur::templateSize(const IntSize& radiusPadding, const RoundedRect
templateSideLength + topSlice + bottomSlice);
}
-void ShadowBlur::drawRectShadow(GraphicsContext* graphicsContext, const FloatRect& shadowedRect, const RoundedRect::Radii& radii)
+void ShadowBlur::drawRectShadow(GraphicsContext& graphicsContext, const FloatRoundedRect& shadowedRect)
{
- IntRect layerRect = calculateLayerBoundingRect(graphicsContext, shadowedRect, graphicsContext->clipBounds());
- if (layerRect.isEmpty())
+ IntSize layerSize = calculateLayerBoundingRect(graphicsContext, shadowedRect.rect(), graphicsContext.clipBounds());
+ if (layerSize.isEmpty())
return;
adjustBlurRadius(graphicsContext);
// drawRectShadowWithTiling does not work with rotations.
// https://bugs.webkit.org/show_bug.cgi?id=45042
- if (!graphicsContext->getCTM().preservesAxisAlignment() || m_type != BlurShadow) {
- drawRectShadowWithoutTiling(graphicsContext, shadowedRect, radii, layerRect);
+ if (!graphicsContext.getCTM().preservesAxisAlignment() || m_type != BlurShadow) {
+ drawRectShadowWithoutTiling(graphicsContext, shadowedRect, layerSize);
return;
}
IntSize edgeSize = blurredEdgeSize();
- IntSize templateSize = this->templateSize(edgeSize, radii);
+ IntSize templateSize = this->templateSize(edgeSize, shadowedRect.radii());
+ const FloatRect& rect = shadowedRect.rect();
- if (templateSize.width() > shadowedRect.width() || templateSize.height() > shadowedRect.height()
+ if (templateSize.width() > rect.width() || templateSize.height() > rect.height()
|| (templateSize.width() * templateSize.height() > m_sourceRect.width() * m_sourceRect.height())) {
- drawRectShadowWithoutTiling(graphicsContext, shadowedRect, radii, layerRect);
+ drawRectShadowWithoutTiling(graphicsContext, shadowedRect, layerSize);
return;
}
- drawRectShadowWithTiling(graphicsContext, shadowedRect, radii, templateSize, edgeSize);
+ drawRectShadowWithTiling(graphicsContext, shadowedRect, templateSize, edgeSize);
}
-void ShadowBlur::drawInsetShadow(GraphicsContext* graphicsContext, const FloatRect& rect, const FloatRect& holeRect, const RoundedRect::Radii& holeRadii)
+void ShadowBlur::drawInsetShadow(GraphicsContext& graphicsContext, const FloatRect& rect, const FloatRoundedRect& holeRect)
{
- IntRect layerRect = calculateLayerBoundingRect(graphicsContext, rect, graphicsContext->clipBounds());
- if (layerRect.isEmpty())
+ IntSize layerSize = calculateLayerBoundingRect(graphicsContext, rect, graphicsContext.clipBounds());
+ if (layerSize.isEmpty())
return;
adjustBlurRadius(graphicsContext);
// drawInsetShadowWithTiling does not work with rotations.
// https://bugs.webkit.org/show_bug.cgi?id=45042
- if (!graphicsContext->getCTM().preservesAxisAlignment() || m_type != BlurShadow) {
- drawInsetShadowWithoutTiling(graphicsContext, rect, holeRect, holeRadii, layerRect);
+ if (!graphicsContext.getCTM().preservesAxisAlignment() || m_type != BlurShadow) {
+ drawInsetShadowWithoutTiling(graphicsContext, rect, holeRect, layerSize);
return;
}
IntSize edgeSize = blurredEdgeSize();
- IntSize templateSize = this->templateSize(edgeSize, holeRadii);
+ IntSize templateSize = this->templateSize(edgeSize, holeRect.radii());
+ const FloatRect& hRect = holeRect.rect();
- if (templateSize.width() > holeRect.width() || templateSize.height() > holeRect.height()
- || (templateSize.width() * templateSize.height() > holeRect.width() * holeRect.height())) {
- drawInsetShadowWithoutTiling(graphicsContext, rect, holeRect, holeRadii, layerRect);
+ if (templateSize.width() > hRect.width() || templateSize.height() > hRect.height()
+ || (templateSize.width() * templateSize.height() > hRect.width() * hRect.height())) {
+ drawInsetShadowWithoutTiling(graphicsContext, rect, holeRect, layerSize);
return;
}
- drawInsetShadowWithTiling(graphicsContext, rect, holeRect, holeRadii, templateSize, edgeSize);
+ drawInsetShadowWithTiling(graphicsContext, rect, holeRect, templateSize, edgeSize);
}
-void ShadowBlur::drawRectShadowWithoutTiling(GraphicsContext* graphicsContext, const FloatRect& shadowedRect, const RoundedRect::Radii& radii, const IntRect& layerRect)
+void ShadowBlur::drawRectShadowWithoutTiling(GraphicsContext& graphicsContext, const FloatRoundedRect& shadowedRect, const IntSize& layerSize)
{
- m_layerImage = ScratchBuffer::shared().getScratchBuffer(layerRect.size());
+ m_layerImage = ScratchBuffer::singleton().getScratchBuffer(layerSize);
if (!m_layerImage)
return;
- FloatRect bufferRelativeShadowedRect = shadowedRect;
+ FloatRect bufferRelativeShadowedRect = shadowedRect.rect();
bufferRelativeShadowedRect.move(m_layerContextTranslation);
// Only redraw in the scratch buffer if its cached contents don't match our needs
- bool redrawNeeded = ScratchBuffer::shared().setCachedShadowValues(m_blurRadius, Color::black, ColorSpaceDeviceRGB, bufferRelativeShadowedRect, radii, m_layerSize);
+ bool redrawNeeded = ScratchBuffer::singleton().setCachedShadowValues(m_blurRadius, Color::black, bufferRelativeShadowedRect, shadowedRect.radii(), m_layerSize);
if (redrawNeeded) {
- GraphicsContext* shadowContext = m_layerImage->context();
- GraphicsContextStateSaver stateSaver(*shadowContext);
+ GraphicsContext& shadowContext = m_layerImage->context();
+ GraphicsContextStateSaver stateSaver(shadowContext);
// Add a pixel to avoid later edge aliasing when rotated.
- shadowContext->clearRect(FloatRect(0, 0, m_layerSize.width() + 1, m_layerSize.height() + 1));
- shadowContext->translate(m_layerContextTranslation);
- shadowContext->setFillColor(Color::black, ColorSpaceDeviceRGB);
- if (radii.isZero())
- shadowContext->fillRect(shadowedRect);
+ shadowContext.clearRect(FloatRect(0, 0, m_layerSize.width() + 1, m_layerSize.height() + 1));
+ shadowContext.translate(m_layerContextTranslation);
+ shadowContext.setFillColor(Color::black);
+ if (shadowedRect.radii().isZero())
+ shadowContext.fillRect(shadowedRect.rect());
else {
Path path;
- path.addRoundedRect(shadowedRect, radii.topLeft(), radii.topRight(), radii.bottomLeft(), radii.bottomRight());
- shadowContext->fillPath(path);
+ path.addRoundedRect(shadowedRect);
+ shadowContext.fillPath(path);
}
- blurShadowBuffer(expandedIntSize(m_layerSize));
+ blurShadowBuffer(layerSize);
}
drawShadowBuffer(graphicsContext);
- m_layerImage = 0;
- ScratchBuffer::shared().scheduleScratchBufferPurge();
+ m_layerImage = nullptr;
+ ScratchBuffer::singleton().scheduleScratchBufferPurge();
}
-void ShadowBlur::drawInsetShadowWithoutTiling(GraphicsContext* graphicsContext, const FloatRect& rect, const FloatRect& holeRect, const RoundedRect::Radii& holeRadii, const IntRect& layerRect)
+void ShadowBlur::drawInsetShadowWithoutTiling(GraphicsContext& graphicsContext, const FloatRect& rect, const FloatRoundedRect& holeRect, const IntSize& layerSize)
{
- m_layerImage = ScratchBuffer::shared().getScratchBuffer(layerRect.size());
+ m_layerImage = ScratchBuffer::singleton().getScratchBuffer(layerSize);
if (!m_layerImage)
return;
FloatRect bufferRelativeRect = rect;
bufferRelativeRect.move(m_layerContextTranslation);
- FloatRect bufferRelativeHoleRect = holeRect;
+ FloatRect bufferRelativeHoleRect = holeRect.rect();
bufferRelativeHoleRect.move(m_layerContextTranslation);
// Only redraw in the scratch buffer if its cached contents don't match our needs
- bool redrawNeeded = ScratchBuffer::shared().setCachedInsetShadowValues(m_blurRadius, Color::black, ColorSpaceDeviceRGB, bufferRelativeRect, bufferRelativeHoleRect, holeRadii);
+ bool redrawNeeded = ScratchBuffer::singleton().setCachedInsetShadowValues(m_blurRadius, Color::black, bufferRelativeRect, bufferRelativeHoleRect, holeRect.radii());
if (redrawNeeded) {
- GraphicsContext* shadowContext = m_layerImage->context();
- GraphicsContextStateSaver stateSaver(*shadowContext);
+ GraphicsContext& shadowContext = m_layerImage->context();
+ GraphicsContextStateSaver stateSaver(shadowContext);
// Add a pixel to avoid later edge aliasing when rotated.
- shadowContext->clearRect(FloatRect(0, 0, m_layerSize.width() + 1, m_layerSize.height() + 1));
- shadowContext->translate(m_layerContextTranslation);
+ shadowContext.clearRect(FloatRect(0, 0, m_layerSize.width() + 1, m_layerSize.height() + 1));
+ shadowContext.translate(m_layerContextTranslation);
Path path;
path.addRect(rect);
- if (holeRadii.isZero())
- path.addRect(holeRect);
+ if (holeRect.radii().isZero())
+ path.addRect(holeRect.rect());
else
- path.addRoundedRect(holeRect, holeRadii.topLeft(), holeRadii.topRight(), holeRadii.bottomLeft(), holeRadii.bottomRight());
+ path.addRoundedRect(holeRect);
- shadowContext->setFillRule(RULE_EVENODD);
- shadowContext->setFillColor(Color::black, ColorSpaceDeviceRGB);
- shadowContext->fillPath(path);
+ shadowContext.setFillRule(RULE_EVENODD);
+ shadowContext.setFillColor(Color::black);
+ shadowContext.fillPath(path);
- blurShadowBuffer(expandedIntSize(m_layerSize));
+ blurShadowBuffer(layerSize);
}
drawShadowBuffer(graphicsContext);
- m_layerImage = 0;
- ScratchBuffer::shared().scheduleScratchBufferPurge();
+ m_layerImage = nullptr;
+ ScratchBuffer::singleton().scheduleScratchBufferPurge();
}
/*
@@ -671,9 +665,9 @@ void ShadowBlur::drawInsetShadowWithoutTiling(GraphicsContext* graphicsContext,
the shadow.
*/
-void ShadowBlur::drawInsetShadowWithTiling(GraphicsContext* graphicsContext, const FloatRect& rect, const FloatRect& holeRect, const RoundedRect::Radii& radii, const IntSize& templateSize, const IntSize& edgeSize)
+void ShadowBlur::drawInsetShadowWithTiling(GraphicsContext& graphicsContext, const FloatRect& rect, const FloatRoundedRect& holeRect, const IntSize& templateSize, const IntSize& edgeSize)
{
- m_layerImage = ScratchBuffer::shared().getScratchBuffer(templateSize);
+ m_layerImage = ScratchBuffer::singleton().getScratchBuffer(templateSize);
if (!m_layerImage)
return;
@@ -682,36 +676,36 @@ void ShadowBlur::drawInsetShadowWithTiling(GraphicsContext* graphicsContext, con
FloatRect templateHole = FloatRect(edgeSize.width(), edgeSize.height(), templateSize.width() - 2 * edgeSize.width(), templateSize.height() - 2 * edgeSize.height());
// Only redraw in the scratch buffer if its cached contents don't match our needs
- bool redrawNeeded = ScratchBuffer::shared().setCachedInsetShadowValues(m_blurRadius, m_color, m_colorSpace, templateBounds, templateHole, radii);
+ bool redrawNeeded = ScratchBuffer::singleton().setCachedInsetShadowValues(m_blurRadius, m_color, templateBounds, templateHole, holeRect.radii());
if (redrawNeeded) {
// Draw shadow into a new ImageBuffer.
- GraphicsContext* shadowContext = m_layerImage->context();
- GraphicsContextStateSaver shadowStateSaver(*shadowContext);
- shadowContext->clearRect(templateBounds);
- shadowContext->setFillRule(RULE_EVENODD);
- shadowContext->setFillColor(Color::black, ColorSpaceDeviceRGB);
+ GraphicsContext& shadowContext = m_layerImage->context();
+ GraphicsContextStateSaver shadowStateSaver(shadowContext);
+ shadowContext.clearRect(templateBounds);
+ shadowContext.setFillRule(RULE_EVENODD);
+ shadowContext.setFillColor(Color::black);
Path path;
path.addRect(templateBounds);
- if (radii.isZero())
+ if (holeRect.radii().isZero())
path.addRect(templateHole);
else
- path.addRoundedRect(templateHole, radii.topLeft(), radii.topRight(), radii.bottomLeft(), radii.bottomRight());
+ path.addRoundedRect(FloatRoundedRect(templateHole, holeRect.radii()));
- shadowContext->fillPath(path);
+ shadowContext.fillPath(path);
blurAndColorShadowBuffer(templateSize);
}
FloatSize offset = m_offset;
if (shadowsIgnoreTransforms()) {
- AffineTransform transform = graphicsContext->getCTM();
+ AffineTransform transform = graphicsContext.getCTM();
offset.scale(1 / transform.xScale(), 1 / transform.yScale());
}
FloatRect boundingRect = rect;
boundingRect.move(offset);
- FloatRect destHoleRect = holeRect;
+ FloatRect destHoleRect = holeRect.rect();
destHoleRect.move(offset);
FloatRect destHoleBounds = destHoleRect;
destHoleBounds.inflateX(edgeSize.width());
@@ -723,65 +717,66 @@ void ShadowBlur::drawInsetShadowWithTiling(GraphicsContext* graphicsContext, con
exteriorPath.addRect(destHoleBounds);
{
- GraphicsContextStateSaver fillStateSaver(*graphicsContext);
- graphicsContext->setFillRule(RULE_EVENODD);
- graphicsContext->setFillColor(m_color, m_colorSpace);
- graphicsContext->clearShadow();
- graphicsContext->fillPath(exteriorPath);
+ GraphicsContextStateSaver fillStateSaver(graphicsContext);
+ graphicsContext.setFillRule(RULE_EVENODD);
+ graphicsContext.setFillColor(m_color);
+ graphicsContext.clearShadow();
+ graphicsContext.fillPath(exteriorPath);
}
- drawLayerPieces(graphicsContext, destHoleBounds, radii, edgeSize, templateSize, InnerShadow);
+ drawLayerPieces(graphicsContext, destHoleBounds, holeRect.radii(), edgeSize, templateSize, InnerShadow);
- m_layerImage = 0;
- ScratchBuffer::shared().scheduleScratchBufferPurge();
+ m_layerImage = nullptr;
+ ScratchBuffer::singleton().scheduleScratchBufferPurge();
}
-void ShadowBlur::drawRectShadowWithTiling(GraphicsContext* graphicsContext, const FloatRect& shadowedRect, const RoundedRect::Radii& radii, const IntSize& templateSize, const IntSize& edgeSize)
+void ShadowBlur::drawRectShadowWithTiling(GraphicsContext& graphicsContext, const FloatRoundedRect& shadowedRect, const IntSize& templateSize, const IntSize& edgeSize)
{
- m_layerImage = ScratchBuffer::shared().getScratchBuffer(templateSize);
+ auto& scratchBuffer = ScratchBuffer::singleton();
+ m_layerImage = scratchBuffer.getScratchBuffer(templateSize);
if (!m_layerImage)
return;
FloatRect templateShadow = FloatRect(edgeSize.width(), edgeSize.height(), templateSize.width() - 2 * edgeSize.width(), templateSize.height() - 2 * edgeSize.height());
// Only redraw in the scratch buffer if its cached contents don't match our needs
- bool redrawNeeded = ScratchBuffer::shared().setCachedShadowValues(m_blurRadius, m_color, m_colorSpace, templateShadow, radii, m_layerSize);
+ bool redrawNeeded = scratchBuffer.setCachedShadowValues(m_blurRadius, m_color, templateShadow, shadowedRect.radii(), m_layerSize);
if (redrawNeeded) {
// Draw shadow into the ImageBuffer.
- GraphicsContext* shadowContext = m_layerImage->context();
- GraphicsContextStateSaver shadowStateSaver(*shadowContext);
+ GraphicsContext& shadowContext = m_layerImage->context();
+ GraphicsContextStateSaver shadowStateSaver(shadowContext);
- shadowContext->clearRect(FloatRect(0, 0, templateSize.width(), templateSize.height()));
- shadowContext->setFillColor(Color::black, ColorSpaceDeviceRGB);
+ shadowContext.clearRect(FloatRect(0, 0, templateSize.width(), templateSize.height()));
+ shadowContext.setFillColor(Color::black);
- if (radii.isZero())
- shadowContext->fillRect(templateShadow);
+ if (shadowedRect.radii().isZero())
+ shadowContext.fillRect(templateShadow);
else {
Path path;
- path.addRoundedRect(templateShadow, radii.topLeft(), radii.topRight(), radii.bottomLeft(), radii.bottomRight());
- shadowContext->fillPath(path);
+ path.addRoundedRect(FloatRoundedRect(templateShadow, shadowedRect.radii()));
+ shadowContext.fillPath(path);
}
blurAndColorShadowBuffer(templateSize);
}
FloatSize offset = m_offset;
if (shadowsIgnoreTransforms()) {
- AffineTransform transform = graphicsContext->getCTM();
+ AffineTransform transform = graphicsContext.getCTM();
offset.scale(1 / transform.xScale(), 1 / transform.yScale());
}
- FloatRect shadowBounds = shadowedRect;
+ FloatRect shadowBounds = shadowedRect.rect();
shadowBounds.move(offset);
shadowBounds.inflateX(edgeSize.width());
shadowBounds.inflateY(edgeSize.height());
- drawLayerPieces(graphicsContext, shadowBounds, radii, edgeSize, templateSize, OuterShadow);
+ drawLayerPieces(graphicsContext, shadowBounds, shadowedRect.radii(), edgeSize, templateSize, OuterShadow);
- m_layerImage = 0;
- ScratchBuffer::shared().scheduleScratchBufferPurge();
+ m_layerImage = nullptr;
+ ScratchBuffer::singleton().scheduleScratchBufferPurge();
}
-void ShadowBlur::drawLayerPieces(GraphicsContext* graphicsContext, const FloatRect& shadowBounds, const RoundedRect::Radii& radii, const IntSize& bufferPadding, const IntSize& templateSize, ShadowDirection direction)
+void ShadowBlur::drawLayerPieces(GraphicsContext& graphicsContext, const FloatRect& shadowBounds, const FloatRoundedRect::Radii& radii, const IntSize& bufferPadding, const IntSize& templateSize, ShadowDirection direction)
{
const IntSize twiceRadius = IntSize(bufferPadding.width() * 2, bufferPadding.height() * 2);
@@ -797,65 +792,65 @@ void ShadowBlur::drawLayerPieces(GraphicsContext* graphicsContext, const FloatRe
if (direction == OuterShadow) {
FloatRect shadowInterior(shadowBounds.x() + leftSlice, shadowBounds.y() + topSlice, centerWidth, centerHeight);
if (!shadowInterior.isEmpty()) {
- GraphicsContextStateSaver stateSaver(*graphicsContext);
- graphicsContext->setFillColor(m_color, m_colorSpace);
- graphicsContext->clearShadow();
- graphicsContext->fillRect(shadowInterior);
+ GraphicsContextStateSaver stateSaver(graphicsContext);
+ graphicsContext.setFillColor(m_color);
+ graphicsContext.clearShadow();
+ graphicsContext.fillRect(shadowInterior);
}
}
- GraphicsContextStateSaver stateSaver(*graphicsContext);
- graphicsContext->setFillColor(m_color, m_colorSpace);
- graphicsContext->clearShadow();
+ GraphicsContextStateSaver stateSaver(graphicsContext);
+ graphicsContext.setFillColor(m_color);
+ graphicsContext.clearShadow();
// Note that drawing the ImageBuffer is faster than creating a Image and drawing that,
// because ImageBuffer::draw() knows that it doesn't have to copy the image bits.
FloatRect centerRect(shadowBounds.x() + leftSlice, shadowBounds.y() + topSlice, centerWidth, centerHeight);
- centerRect = graphicsContext->roundToDevicePixels(centerRect);
+ centerRect = graphicsContext.roundToDevicePixels(centerRect);
// Top side.
FloatRect tileRect = FloatRect(leftSlice, 0, templateSideLength, topSlice);
FloatRect destRect = FloatRect(centerRect.x(), centerRect.y() - topSlice, centerRect.width(), topSlice);
- graphicsContext->drawImageBuffer(m_layerImage, ColorSpaceDeviceRGB, destRect, tileRect);
+ graphicsContext.drawImageBuffer(*m_layerImage, destRect, tileRect);
// Draw the bottom side.
tileRect.setY(templateSize.height() - bottomSlice);
tileRect.setHeight(bottomSlice);
destRect.setY(centerRect.maxY());
destRect.setHeight(bottomSlice);
- graphicsContext->drawImageBuffer(m_layerImage, ColorSpaceDeviceRGB, destRect, tileRect);
+ graphicsContext.drawImageBuffer(*m_layerImage, destRect, tileRect);
// Left side.
tileRect = FloatRect(0, topSlice, leftSlice, templateSideLength);
destRect = FloatRect(centerRect.x() - leftSlice, centerRect.y(), leftSlice, centerRect.height());
- graphicsContext->drawImageBuffer(m_layerImage, ColorSpaceDeviceRGB, destRect, tileRect);
+ graphicsContext.drawImageBuffer(*m_layerImage, destRect, tileRect);
// Right side.
tileRect.setX(templateSize.width() - rightSlice);
tileRect.setWidth(rightSlice);
destRect.setX(centerRect.maxX());
destRect.setWidth(rightSlice);
- graphicsContext->drawImageBuffer(m_layerImage, ColorSpaceDeviceRGB, destRect, tileRect);
+ graphicsContext.drawImageBuffer(*m_layerImage, destRect, tileRect);
// Top left corner.
tileRect = FloatRect(0, 0, leftSlice, topSlice);
destRect = FloatRect(centerRect.x() - leftSlice, centerRect.y() - topSlice, leftSlice, topSlice);
- graphicsContext->drawImageBuffer(m_layerImage, ColorSpaceDeviceRGB, destRect, tileRect);
+ graphicsContext.drawImageBuffer(*m_layerImage, destRect, tileRect);
// Top right corner.
tileRect = FloatRect(templateSize.width() - rightSlice, 0, rightSlice, topSlice);
destRect = FloatRect(centerRect.maxX(), centerRect.y() - topSlice, rightSlice, topSlice);
- graphicsContext->drawImageBuffer(m_layerImage, ColorSpaceDeviceRGB, destRect, tileRect);
+ graphicsContext.drawImageBuffer(*m_layerImage, destRect, tileRect);
// Bottom right corner.
tileRect = FloatRect(templateSize.width() - rightSlice, templateSize.height() - bottomSlice, rightSlice, bottomSlice);
destRect = FloatRect(centerRect.maxX(), centerRect.maxY(), rightSlice, bottomSlice);
- graphicsContext->drawImageBuffer(m_layerImage, ColorSpaceDeviceRGB, destRect, tileRect);
+ graphicsContext.drawImageBuffer(*m_layerImage, destRect, tileRect);
// Bottom left corner.
tileRect = FloatRect(0, templateSize.height() - bottomSlice, leftSlice, bottomSlice);
destRect = FloatRect(centerRect.x() - leftSlice, centerRect.maxY(), leftSlice, bottomSlice);
- graphicsContext->drawImageBuffer(m_layerImage, ColorSpaceDeviceRGB, destRect, tileRect);
+ graphicsContext.drawImageBuffer(*m_layerImage, destRect, tileRect);
}
@@ -875,49 +870,50 @@ void ShadowBlur::blurAndColorShadowBuffer(const IntSize& templateSize)
blurShadowBuffer(templateSize);
// Mask the image with the shadow color.
- GraphicsContext* shadowContext = m_layerImage->context();
- GraphicsContextStateSaver stateSaver(*shadowContext);
- shadowContext->setCompositeOperation(CompositeSourceIn);
- shadowContext->setFillColor(m_color, m_colorSpace);
- shadowContext->fillRect(FloatRect(0, 0, templateSize.width(), templateSize.height()));
+ GraphicsContext& shadowContext = m_layerImage->context();
+ GraphicsContextStateSaver stateSaver(shadowContext);
+ shadowContext.setCompositeOperation(CompositeSourceIn);
+ shadowContext.setFillColor(m_color);
+ shadowContext.fillRect(FloatRect(0, 0, templateSize.width(), templateSize.height()));
}
-GraphicsContext* ShadowBlur::beginShadowLayer(GraphicsContext *context, const FloatRect& layerArea)
+GraphicsContext* ShadowBlur::beginShadowLayer(GraphicsContext& context, const FloatRect& layerArea)
{
adjustBlurRadius(context);
- IntRect layerRect = calculateLayerBoundingRect(context, layerArea, context->clipBounds());
+ IntSize layerSize = calculateLayerBoundingRect(context, layerArea, context.clipBounds());
- if (layerRect.isEmpty())
- return 0;
+ if (layerSize.isEmpty())
+ return nullptr;
// We reset the scratch buffer values here, because the buffer will no longer contain
// data from any previous rectangle or inset shadows drawn via the tiling path.
- ScratchBuffer::shared().setCachedShadowValues(FloatSize(), Color::black, ColorSpaceDeviceRGB, IntRect(), RoundedRect::Radii(), m_layerSize);
- m_layerImage = ScratchBuffer::shared().getScratchBuffer(layerRect.size());
+ auto& scratchBuffer = ScratchBuffer::singleton();
+ scratchBuffer.setCachedShadowValues(FloatSize(), Color::black, IntRect(), FloatRoundedRect::Radii(), m_layerSize);
+ m_layerImage = scratchBuffer.getScratchBuffer(layerSize);
- GraphicsContext* shadowContext = m_layerImage->context();
- shadowContext->save();
+ GraphicsContext& shadowContext = m_layerImage->context();
+ shadowContext.save();
// Add a pixel to avoid later edge aliasing when rotated.
- shadowContext->clearRect(FloatRect(0, 0, m_layerSize.width() + 1, m_layerSize.height() + 1));
+ shadowContext.clearRect(FloatRect(0, 0, m_layerSize.width() + 1, m_layerSize.height() + 1));
- shadowContext->translate(m_layerContextTranslation);
- return shadowContext;
+ shadowContext.translate(m_layerContextTranslation);
+ return &shadowContext;
}
-void ShadowBlur::endShadowLayer(GraphicsContext* context)
+void ShadowBlur::endShadowLayer(GraphicsContext& context)
{
- m_layerImage->context()->restore();
+ m_layerImage->context().restore();
blurAndColorShadowBuffer(expandedIntSize(m_layerSize));
- GraphicsContextStateSaver stateSave(*context);
+ GraphicsContextStateSaver stateSave(context);
- context->clearShadow();
- context->drawImageBuffer(m_layerImage, ColorSpaceDeviceRGB, roundedIntPoint(m_layerOrigin), IntRect(0, 0, m_layerSize.width(), m_layerSize.height()), context->compositeOperation());
+ context.clearShadow();
+ context.drawImageBuffer(*m_layerImage, FloatRect(roundedIntPoint(m_layerOrigin), m_layerSize), FloatRect(FloatPoint(), m_layerSize), context.compositeOperation());
- m_layerImage = 0;
- ScratchBuffer::shared().scheduleScratchBufferPurge();
+ m_layerImage = nullptr;
+ ScratchBuffer::singleton().scheduleScratchBufferPurge();
}
} // namespace WebCore
diff --git a/Source/WebCore/platform/graphics/ShadowBlur.h b/Source/WebCore/platform/graphics/ShadowBlur.h
index 32ff60b6c..34869496a 100644
--- a/Source/WebCore/platform/graphics/ShadowBlur.h
+++ b/Source/WebCore/platform/graphics/ShadowBlur.h
@@ -16,7 +16,7 @@
* THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
@@ -30,9 +30,8 @@
#define ShadowBlur_h
#include "Color.h"
-#include "ColorSpace.h"
#include "FloatRect.h"
-#include "RoundedRect.h"
+#include "FloatRoundedRect.h"
#include <wtf/Noncopyable.h>
namespace WebCore {
@@ -51,20 +50,20 @@ public:
BlurShadow
};
- ShadowBlur(const FloatSize& radius, const FloatSize& offset, const Color&, ColorSpace);
+ ShadowBlur(const FloatSize& radius, const FloatSize& offset, const Color&);
ShadowBlur(const GraphicsContextState&);
ShadowBlur();
- void setShadowValues(const FloatSize&, const FloatSize& , const Color&, ColorSpace, bool ignoreTransforms = false);
+ void setShadowValues(const FloatSize&, const FloatSize& , const Color&, bool ignoreTransforms = false);
void setShadowsIgnoreTransforms(bool ignoreTransforms) { m_shadowsIgnoreTransforms = ignoreTransforms; }
bool shadowsIgnoreTransforms() const { return m_shadowsIgnoreTransforms; }
- GraphicsContext* beginShadowLayer(GraphicsContext*, const FloatRect& layerArea);
- void endShadowLayer(GraphicsContext*);
+ GraphicsContext* beginShadowLayer(GraphicsContext&, const FloatRect& layerArea);
+ void endShadowLayer(GraphicsContext&);
- void drawRectShadow(GraphicsContext*, const FloatRect&, const RoundedRect::Radii&);
- void drawInsetShadow(GraphicsContext*, const FloatRect&, const FloatRect& holeRect, const RoundedRect::Radii& holeRadii);
+ void drawRectShadow(GraphicsContext&, const FloatRoundedRect&);
+ void drawInsetShadow(GraphicsContext&, const FloatRect&, const FloatRoundedRect& holeRect);
void blurLayerImage(unsigned char*, const IntSize&, int stride);
@@ -75,25 +74,25 @@ public:
private:
void updateShadowBlurValues();
- void drawShadowBuffer(GraphicsContext*);
+ void drawShadowBuffer(GraphicsContext&);
- void adjustBlurRadius(GraphicsContext*);
+ void adjustBlurRadius(GraphicsContext&);
enum ShadowDirection {
OuterShadow,
InnerShadow
};
- IntRect calculateLayerBoundingRect(GraphicsContext*, const FloatRect& layerArea, const IntRect& clipRect);
- IntSize templateSize(const IntSize& blurredEdgeSize, const RoundedRect::Radii&) const;
+ IntSize calculateLayerBoundingRect(GraphicsContext&, const FloatRect& layerArea, const IntRect& clipRect);
+ IntSize templateSize(const IntSize& blurredEdgeSize, const FloatRoundedRect::Radii&) const;
- void drawRectShadowWithoutTiling(GraphicsContext*, const FloatRect&, const RoundedRect::Radii&, const IntRect& layerRect);
- void drawRectShadowWithTiling(GraphicsContext*, const FloatRect&, const RoundedRect::Radii&, const IntSize& shadowTemplateSize, const IntSize& blurredEdgeSize);
+ void drawRectShadowWithoutTiling(GraphicsContext&, const FloatRoundedRect&, const IntSize& layerSize);
+ void drawRectShadowWithTiling(GraphicsContext&, const FloatRoundedRect&, const IntSize& shadowTemplateSize, const IntSize& blurredEdgeSize);
- void drawInsetShadowWithoutTiling(GraphicsContext*, const FloatRect&, const FloatRect& holeRect, const RoundedRect::Radii&, const IntRect& layerRect);
- void drawInsetShadowWithTiling(GraphicsContext*, const FloatRect&, const FloatRect& holeRect, const RoundedRect::Radii&, const IntSize& shadowTemplateSize, const IntSize& blurredEdgeSize);
+ void drawInsetShadowWithoutTiling(GraphicsContext&, const FloatRect&, const FloatRoundedRect& holeRect, const IntSize& layerSize);
+ void drawInsetShadowWithTiling(GraphicsContext&, const FloatRect&, const FloatRoundedRect& holeRect, const IntSize& shadowTemplateSize, const IntSize& blurredEdgeSize);
- void drawLayerPieces(GraphicsContext*, const FloatRect& shadowBounds, const RoundedRect::Radii&, const IntSize& roundedRadius, const IntSize& templateSize, ShadowDirection);
+ void drawLayerPieces(GraphicsContext&, const FloatRect& shadowBounds, const FloatRoundedRect::Radii&, const IntSize& roundedRadius, const IntSize& templateSize, ShadowDirection);
void blurShadowBuffer(const IntSize& templateSize);
void blurAndColorShadowBuffer(const IntSize& templateSize);
@@ -104,7 +103,6 @@ private:
ShadowType m_type;
Color m_color;
- ColorSpace m_colorSpace;
FloatSize m_blurRadius;
FloatSize m_offset;
diff --git a/Source/WebCore/platform/graphics/SimpleFontData.cpp b/Source/WebCore/platform/graphics/SimpleFontData.cpp
deleted file mode 100644
index b0627ad24..000000000
--- a/Source/WebCore/platform/graphics/SimpleFontData.cpp
+++ /dev/null
@@ -1,305 +0,0 @@
-/*
- * Copyright (C) 2005, 2008, 2010 Apple Inc. All rights reserved.
- * Copyright (C) 2006 Alexey Proskuryakov
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
- * its contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
- * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
- * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
- * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#include "config.h"
-#include "SimpleFontData.h"
-
-#include "Font.h"
-#include "FontCache.h"
-#include <wtf/MathExtras.h>
-
-#if ENABLE(OPENTYPE_VERTICAL)
-#include "OpenTypeVerticalData.h"
-#endif
-
-namespace WebCore {
-
-const float smallCapsFontSizeMultiplier = 0.7f;
-const float emphasisMarkFontSizeMultiplier = 0.5f;
-
-SimpleFontData::SimpleFontData(const FontPlatformData& platformData, bool isCustomFont, bool isLoading, bool isTextOrientationFallback)
- : m_maxCharWidth(-1)
- , m_avgCharWidth(-1)
- , m_platformData(platformData)
- , m_treatAsFixedPitch(false)
- , m_isCustomFont(isCustomFont)
- , m_isLoading(isLoading)
- , m_isTextOrientationFallback(isTextOrientationFallback)
- , m_isBrokenIdeographFallback(false)
-#if ENABLE(OPENTYPE_VERTICAL)
- , m_verticalData(0)
-#endif
- , m_hasVerticalGlyphs(false)
-{
- platformInit();
- platformGlyphInit();
- platformCharWidthInit();
-#if ENABLE(OPENTYPE_VERTICAL)
- if (platformData.orientation() == Vertical && !isTextOrientationFallback) {
- m_verticalData = platformData.verticalData();
- m_hasVerticalGlyphs = m_verticalData.get() && m_verticalData->hasVerticalMetrics();
- }
-#endif
-}
-
-SimpleFontData::SimpleFontData(std::unique_ptr<AdditionalFontData> fontData, float fontSize, bool syntheticBold, bool syntheticItalic)
- : m_platformData(FontPlatformData(fontSize, syntheticBold, syntheticItalic))
- , m_fontData(std::move(fontData))
- , m_treatAsFixedPitch(false)
- , m_isCustomFont(true)
- , m_isLoading(false)
- , m_isTextOrientationFallback(false)
- , m_isBrokenIdeographFallback(false)
-#if ENABLE(OPENTYPE_VERTICAL)
- , m_verticalData(0)
-#endif
- , m_hasVerticalGlyphs(false)
-#if PLATFORM(IOS)
- , m_shouldNotBeUsedForArabic(false)
-#endif
-{
- m_fontData->initializeFontData(this, fontSize);
-}
-
-// Estimates of avgCharWidth and maxCharWidth for platforms that don't support accessing these values from the font.
-void SimpleFontData::initCharWidths()
-{
- GlyphPage* glyphPageZero = GlyphPageTreeNode::getRootChild(this, 0)->page();
-
- // Treat the width of a '0' as the avgCharWidth.
- if (m_avgCharWidth <= 0.f && glyphPageZero) {
- static const UChar32 digitZeroChar = '0';
- Glyph digitZeroGlyph = glyphPageZero->glyphDataForCharacter(digitZeroChar).glyph;
- if (digitZeroGlyph)
- m_avgCharWidth = widthForGlyph(digitZeroGlyph);
- }
-
- // If we can't retrieve the width of a '0', fall back to the x height.
- if (m_avgCharWidth <= 0.f)
- m_avgCharWidth = m_fontMetrics.xHeight();
-
- if (m_maxCharWidth <= 0.f)
- m_maxCharWidth = std::max(m_avgCharWidth, m_fontMetrics.floatAscent());
-}
-
-void SimpleFontData::platformGlyphInit()
-{
- GlyphPage* glyphPageZero = GlyphPageTreeNode::getRootChild(this, 0)->page();
- if (!glyphPageZero) {
- LOG_ERROR("Failed to get glyph page zero.");
- m_spaceGlyph = 0;
- m_spaceWidth = 0;
- m_zeroGlyph = 0;
- m_adjustedSpaceWidth = 0;
- determinePitch();
- m_zeroWidthSpaceGlyph = 0;
- m_missingGlyphData.fontData = this;
- m_missingGlyphData.glyph = 0;
- return;
- }
-
- m_zeroWidthSpaceGlyph = glyphPageZero->glyphDataForCharacter(0).glyph;
-
- // Nasty hack to determine if we should round or ceil space widths.
- // If the font is monospace or fake monospace we ceil to ensure that
- // every character and the space are the same width. Otherwise we round.
- m_spaceGlyph = glyphPageZero->glyphDataForCharacter(' ').glyph;
- float width = widthForGlyph(m_spaceGlyph);
- m_spaceWidth = width;
- m_zeroGlyph = glyphPageZero->glyphDataForCharacter('0').glyph;
- m_fontMetrics.setZeroWidth(widthForGlyph(m_zeroGlyph));
- determinePitch();
- m_adjustedSpaceWidth = m_treatAsFixedPitch ? ceilf(width) : roundf(width);
-
- // Force the glyph for ZERO WIDTH SPACE to have zero width, unless it is shared with SPACE.
- // Helvetica is an example of a non-zero width ZERO WIDTH SPACE glyph.
- // See <http://bugs.webkit.org/show_bug.cgi?id=13178>
- // Ask for the glyph for 0 to avoid paging in ZERO WIDTH SPACE. Control characters, including 0,
- // are mapped to the ZERO WIDTH SPACE glyph.
- if (m_zeroWidthSpaceGlyph == m_spaceGlyph) {
- m_zeroWidthSpaceGlyph = 0;
- LOG_ERROR("Font maps SPACE and ZERO WIDTH SPACE to the same glyph. Glyph width will not be overridden.");
- }
-
- m_missingGlyphData.fontData = this;
- m_missingGlyphData.glyph = 0;
-}
-
-SimpleFontData::~SimpleFontData()
-{
- if (!m_fontData)
- platformDestroy();
-
- if (isCustomFont())
- GlyphPageTreeNode::pruneTreeCustomFontData(this);
- else
- GlyphPageTreeNode::pruneTreeFontData(this);
-}
-
-const SimpleFontData* SimpleFontData::fontDataForCharacter(UChar32) const
-{
- return this;
-}
-
-Glyph SimpleFontData::glyphForCharacter(UChar32 character) const
-{
- GlyphPageTreeNode* node = GlyphPageTreeNode::getRootChild(this, character / GlyphPage::size);
- return node->page() ? node->page()->glyphAt(character % GlyphPage::size) : 0;
-}
-
-bool SimpleFontData::isSegmented() const
-{
- return false;
-}
-
-PassRefPtr<SimpleFontData> SimpleFontData::verticalRightOrientationFontData() const
-{
- if (!m_derivedFontData)
- m_derivedFontData = DerivedFontData::create(isCustomFont());
- if (!m_derivedFontData->verticalRightOrientation) {
- FontPlatformData verticalRightPlatformData(m_platformData);
- verticalRightPlatformData.setOrientation(Horizontal);
- m_derivedFontData->verticalRightOrientation = create(verticalRightPlatformData, isCustomFont(), false, true);
- }
- return m_derivedFontData->verticalRightOrientation;
-}
-
-PassRefPtr<SimpleFontData> SimpleFontData::uprightOrientationFontData() const
-{
- if (!m_derivedFontData)
- m_derivedFontData = DerivedFontData::create(isCustomFont());
- if (!m_derivedFontData->uprightOrientation)
- m_derivedFontData->uprightOrientation = create(m_platformData, isCustomFont(), false, true);
- return m_derivedFontData->uprightOrientation;
-}
-
-PassRefPtr<SimpleFontData> SimpleFontData::smallCapsFontData(const FontDescription& fontDescription) const
-{
- if (!m_derivedFontData)
- m_derivedFontData = DerivedFontData::create(isCustomFont());
- if (!m_derivedFontData->smallCaps)
- m_derivedFontData->smallCaps = createScaledFontData(fontDescription, smallCapsFontSizeMultiplier);
-
- return m_derivedFontData->smallCaps;
-}
-
-PassRefPtr<SimpleFontData> SimpleFontData::emphasisMarkFontData(const FontDescription& fontDescription) const
-{
- if (!m_derivedFontData)
- m_derivedFontData = DerivedFontData::create(isCustomFont());
- if (!m_derivedFontData->emphasisMark)
- m_derivedFontData->emphasisMark = createScaledFontData(fontDescription, emphasisMarkFontSizeMultiplier);
-
- return m_derivedFontData->emphasisMark;
-}
-
-PassRefPtr<SimpleFontData> SimpleFontData::brokenIdeographFontData() const
-{
- if (!m_derivedFontData)
- m_derivedFontData = DerivedFontData::create(isCustomFont());
- if (!m_derivedFontData->brokenIdeograph) {
- m_derivedFontData->brokenIdeograph = create(m_platformData, isCustomFont(), false);
- m_derivedFontData->brokenIdeograph->m_isBrokenIdeographFallback = true;
- }
- return m_derivedFontData->brokenIdeograph;
-}
-
-PassRefPtr<SimpleFontData> SimpleFontData::nonSyntheticItalicFontData() const
-{
- if (!m_derivedFontData)
- m_derivedFontData = DerivedFontData::create(isCustomFont());
- if (!m_derivedFontData->nonSyntheticItalic) {
- FontPlatformData nonSyntheticItalicFontPlatformData(m_platformData);
-#if PLATFORM(MAC)
- nonSyntheticItalicFontPlatformData.m_syntheticOblique = false;
-#endif
- m_derivedFontData->nonSyntheticItalic = create(nonSyntheticItalicFontPlatformData, isCustomFont(), false, true);
- }
- return m_derivedFontData->nonSyntheticItalic;
-}
-
-#ifndef NDEBUG
-String SimpleFontData::description() const
-{
- if (isSVGFont())
- return "[SVG font]";
- if (isCustomFont())
- return "[custom font]";
-
- return platformData().description();
-}
-#endif
-
-PassOwnPtr<SimpleFontData::DerivedFontData> SimpleFontData::DerivedFontData::create(bool forCustomFont)
-{
- return adoptPtr(new DerivedFontData(forCustomFont));
-}
-
-SimpleFontData::DerivedFontData::~DerivedFontData()
-{
- if (!forCustomFont)
- return;
-
- if (smallCaps)
- GlyphPageTreeNode::pruneTreeCustomFontData(smallCaps.get());
- if (emphasisMark)
- GlyphPageTreeNode::pruneTreeCustomFontData(emphasisMark.get());
- if (brokenIdeograph)
- GlyphPageTreeNode::pruneTreeCustomFontData(brokenIdeograph.get());
- if (verticalRightOrientation)
- GlyphPageTreeNode::pruneTreeCustomFontData(verticalRightOrientation.get());
- if (uprightOrientation)
- GlyphPageTreeNode::pruneTreeCustomFontData(uprightOrientation.get());
-#if PLATFORM(MAC)
- if (compositeFontReferences) {
- CFDictionaryRef dictionary = CFDictionaryRef(compositeFontReferences.get());
- CFIndex count = CFDictionaryGetCount(dictionary);
- if (count > 0) {
- Vector<SimpleFontData*, 2> stash(count);
- SimpleFontData** fonts = stash.data();
- CFDictionaryGetKeysAndValues(dictionary, 0, (const void **)fonts);
- while (count-- > 0 && *fonts) {
- RefPtr<SimpleFontData> afont = adoptRef(*fonts++);
- GlyphPageTreeNode::pruneTreeCustomFontData(afont.get());
- }
- }
- }
-#endif
-}
-
-PassRefPtr<SimpleFontData> SimpleFontData::createScaledFontData(const FontDescription& fontDescription, float scaleFactor) const
-{
- // FIXME: Support scaled fonts that used AdditionalFontData.
- if (m_fontData)
- return 0;
-
- return platformCreateScaledFontData(fontDescription, scaleFactor);
-}
-
-} // namespace WebCore
diff --git a/Source/WebCore/platform/graphics/SimpleFontData.h b/Source/WebCore/platform/graphics/SimpleFontData.h
deleted file mode 100644
index ffdde1754..000000000
--- a/Source/WebCore/platform/graphics/SimpleFontData.h
+++ /dev/null
@@ -1,373 +0,0 @@
-/*
- * This file is part of the internal font implementation.
- *
- * Copyright (C) 2006, 2008, 2010 Apple Inc. All rights reserved.
- * Copyright (C) 2007-2008 Torch Mobile, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public License
- * along with this library; see the file COPYING.LIB. If not, write to
- * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
- * Boston, MA 02110-1301, USA.
- *
- */
-
-#ifndef SimpleFontData_h
-#define SimpleFontData_h
-
-#include "FontBaseline.h"
-#include "FontData.h"
-#include "FontMetrics.h"
-#include "FontPlatformData.h"
-#include "FloatRect.h"
-#include "GlyphBuffer.h"
-#include "GlyphMetricsMap.h"
-#include "GlyphPageTreeNode.h"
-#if ENABLE(OPENTYPE_VERTICAL)
-#include "OpenTypeVerticalData.h"
-#endif
-#include "TypesettingFeatures.h"
-#include <wtf/OwnPtr.h>
-#include <wtf/PassOwnPtr.h>
-#include <wtf/text/StringHash.h>
-
-#if PLATFORM(MAC)
-#include "WebCoreSystemInterface.h"
-#endif
-
-#if PLATFORM(MAC)
-#include <wtf/RetainPtr.h>
-#endif
-
-#if (PLATFORM(WIN) && !OS(WINCE))
-#include <usp10.h>
-#endif
-
-#if USE(CAIRO)
-#include <cairo.h>
-#endif
-
-namespace WebCore {
-
-class FontDescription;
-class SharedBuffer;
-struct WidthIterator;
-
-enum FontDataVariant { AutoVariant, NormalVariant, SmallCapsVariant, EmphasisMarkVariant, BrokenIdeographVariant };
-enum Pitch { UnknownPitch, FixedPitch, VariablePitch };
-
-class SimpleFontData final : public FontData {
-public:
- class AdditionalFontData {
- WTF_MAKE_FAST_ALLOCATED;
- public:
- virtual ~AdditionalFontData() { }
-
- virtual void initializeFontData(SimpleFontData*, float fontSize) = 0;
- virtual float widthForSVGGlyph(Glyph, float fontSize) const = 0;
- virtual bool fillSVGGlyphPage(GlyphPage*, unsigned offset, unsigned length, UChar* buffer, unsigned bufferLength, const SimpleFontData*) const = 0;
- virtual bool applySVGGlyphSelection(WidthIterator&, GlyphData&, bool mirror, int currentCharacter, unsigned& advanceLength) const = 0;
- };
-
- // Used to create platform fonts.
- static PassRefPtr<SimpleFontData> create(const FontPlatformData& platformData, bool isCustomFont = false, bool isLoading = false, bool isTextOrientationFallback = false)
- {
- return adoptRef(new SimpleFontData(platformData, isCustomFont, isLoading, isTextOrientationFallback));
- }
-
- // Used to create SVG Fonts.
- static PassRefPtr<SimpleFontData> create(std::unique_ptr<AdditionalFontData> fontData, float fontSize, bool syntheticBold, bool syntheticItalic)
- {
- return adoptRef(new SimpleFontData(std::move(fontData), fontSize, syntheticBold, syntheticItalic));
- }
-
- virtual ~SimpleFontData();
-
- static const SimpleFontData* systemFallback() { return reinterpret_cast<const SimpleFontData*>(-1); }
-
- const FontPlatformData& platformData() const { return m_platformData; }
-#if ENABLE(OPENTYPE_VERTICAL)
- const OpenTypeVerticalData* verticalData() const { return m_verticalData.get(); }
-#endif
-
- PassRefPtr<SimpleFontData> smallCapsFontData(const FontDescription&) const;
- PassRefPtr<SimpleFontData> emphasisMarkFontData(const FontDescription&) const;
- PassRefPtr<SimpleFontData> brokenIdeographFontData() const;
- PassRefPtr<SimpleFontData> nonSyntheticItalicFontData() const;
-
- PassRefPtr<SimpleFontData> variantFontData(const FontDescription& description, FontDataVariant variant) const
- {
- switch (variant) {
- case SmallCapsVariant:
- return smallCapsFontData(description);
- case EmphasisMarkVariant:
- return emphasisMarkFontData(description);
- case BrokenIdeographVariant:
- return brokenIdeographFontData();
- case AutoVariant:
- case NormalVariant:
- break;
- }
- ASSERT_NOT_REACHED();
- return const_cast<SimpleFontData*>(this);
- }
-
- PassRefPtr<SimpleFontData> verticalRightOrientationFontData() const;
- PassRefPtr<SimpleFontData> uprightOrientationFontData() const;
-
- bool hasVerticalGlyphs() const { return m_hasVerticalGlyphs; }
- bool isTextOrientationFallback() const { return m_isTextOrientationFallback; }
-
- FontMetrics& fontMetrics() { return m_fontMetrics; }
- const FontMetrics& fontMetrics() const { return m_fontMetrics; }
- float sizePerUnit() const { return platformData().size() / (fontMetrics().unitsPerEm() ? fontMetrics().unitsPerEm() : 1); }
-
- float maxCharWidth() const { return m_maxCharWidth; }
- void setMaxCharWidth(float maxCharWidth) { m_maxCharWidth = maxCharWidth; }
-
- float avgCharWidth() const { return m_avgCharWidth; }
- void setAvgCharWidth(float avgCharWidth) { m_avgCharWidth = avgCharWidth; }
-
- FloatRect boundsForGlyph(Glyph) const;
- float widthForGlyph(Glyph glyph) const;
- FloatRect platformBoundsForGlyph(Glyph) const;
- float platformWidthForGlyph(Glyph) const;
-
- float spaceWidth() const { return m_spaceWidth; }
- float adjustedSpaceWidth() const { return m_adjustedSpaceWidth; }
- void setSpaceWidth(float spaceWidth) { m_spaceWidth = spaceWidth; }
-
-#if USE(CG) || USE(CAIRO)
- float syntheticBoldOffset() const { return m_syntheticBoldOffset; }
-#endif
-
- Glyph spaceGlyph() const { return m_spaceGlyph; }
- void setSpaceGlyph(Glyph spaceGlyph) { m_spaceGlyph = spaceGlyph; }
- Glyph zeroWidthSpaceGlyph() const { return m_zeroWidthSpaceGlyph; }
- void setZeroWidthSpaceGlyph(Glyph spaceGlyph) { m_zeroWidthSpaceGlyph = spaceGlyph; }
- bool isZeroWidthSpaceGlyph(Glyph glyph) const { return glyph == m_zeroWidthSpaceGlyph && glyph; }
- Glyph zeroGlyph() const { return m_zeroGlyph; }
- void setZeroGlyph(Glyph zeroGlyph) { m_zeroGlyph = zeroGlyph; }
-
- virtual const SimpleFontData* fontDataForCharacter(UChar32) const override;
- virtual bool containsCharacters(const UChar*, int length) const override;
-
- Glyph glyphForCharacter(UChar32) const;
-
- void determinePitch();
- Pitch pitch() const { return m_treatAsFixedPitch ? FixedPitch : VariablePitch; }
-
- AdditionalFontData* fontData() const { return m_fontData.get(); }
- bool isSVGFont() const { return m_fontData != nullptr; }
-
- virtual bool isCustomFont() const override { return m_isCustomFont; }
- virtual bool isLoading() const override { return m_isLoading; }
- virtual bool isSegmented() const override;
-
- const GlyphData& missingGlyphData() const { return m_missingGlyphData; }
- void setMissingGlyphData(const GlyphData& glyphData) { m_missingGlyphData = glyphData; }
-
-#ifndef NDEBUG
- virtual String description() const override;
-#endif
-
-#if USE(APPKIT)
- const SimpleFontData* getCompositeFontReferenceFontData(NSFont *key) const;
- NSFont* getNSFont() const { return m_platformData.font(); }
-#endif
-
-#if PLATFORM(IOS)
- CTFontRef getCTFont() const { return m_platformData.font(); }
- bool shouldNotBeUsedForArabic() const { return m_shouldNotBeUsedForArabic; };
-#endif
-#if PLATFORM(MAC)
- CFDictionaryRef getCFStringAttributes(TypesettingFeatures, FontOrientation) const;
-#endif
-
-#if PLATFORM(MAC) || USE(HARFBUZZ)
- bool canRenderCombiningCharacterSequence(const UChar*, size_t) const;
-#endif
-
- bool applyTransforms(GlyphBufferGlyph* glyphs, GlyphBufferAdvance* advances, size_t glyphCount, TypesettingFeatures typesettingFeatures) const
- {
- if (isSVGFont())
- return false;
-#if PLATFORM(IOS) || (PLATFORM(MAC) && __MAC_OS_X_VERSION_MIN_REQUIRED > 1080)
- wkCTFontTransformOptions options = (typesettingFeatures & Kerning ? wkCTFontTransformApplyPositioning : 0) | (typesettingFeatures & Ligatures ? wkCTFontTransformApplyShaping : 0);
- return wkCTFontTransformGlyphs(m_platformData.ctFont(), glyphs, reinterpret_cast<CGSize*>(advances), glyphCount, options);
-#else
- UNUSED_PARAM(glyphs);
- UNUSED_PARAM(advances);
- UNUSED_PARAM(glyphCount);
- UNUSED_PARAM(typesettingFeatures);
- return false;
-#endif
- }
-
-#if PLATFORM(WIN)
- bool isSystemFont() const { return m_isSystemFont; }
-#if !OS(WINCE) // disable unused members to save space
- SCRIPT_FONTPROPERTIES* scriptFontProperties() const;
- SCRIPT_CACHE* scriptCache() const { return &m_scriptCache; }
-#endif
- static void setShouldApplyMacAscentHack(bool);
- static bool shouldApplyMacAscentHack();
- static float ascentConsideringMacAscentHack(const WCHAR*, float ascent, float descent);
-#endif
-
-private:
- SimpleFontData(const FontPlatformData&, bool isCustomFont = false, bool isLoading = false, bool isTextOrientationFallback = false);
-
- SimpleFontData(std::unique_ptr<AdditionalFontData>, float fontSize, bool syntheticBold, bool syntheticItalic);
-
- void platformInit();
- void platformGlyphInit();
- void platformCharWidthInit();
- void platformDestroy();
-
- void initCharWidths();
-
- PassRefPtr<SimpleFontData> createScaledFontData(const FontDescription&, float scaleFactor) const;
- PassRefPtr<SimpleFontData> platformCreateScaledFontData(const FontDescription&, float scaleFactor) const;
-
-#if (PLATFORM(WIN) && !OS(WINCE))
- void initGDIFont();
- void platformCommonDestroy();
- FloatRect boundsForGDIGlyph(Glyph glyph) const;
- float widthForGDIGlyph(Glyph glyph) const;
-#endif
-
- FontMetrics m_fontMetrics;
- float m_maxCharWidth;
- float m_avgCharWidth;
-
- FontPlatformData m_platformData;
- std::unique_ptr<AdditionalFontData> m_fontData;
-
- mutable OwnPtr<GlyphMetricsMap<FloatRect>> m_glyphToBoundsMap;
- mutable GlyphMetricsMap<float> m_glyphToWidthMap;
-
- bool m_treatAsFixedPitch;
- bool m_isCustomFont; // Whether or not we are custom font loaded via @font-face
- bool m_isLoading; // Whether or not this custom font is still in the act of loading.
-
- bool m_isTextOrientationFallback;
- bool m_isBrokenIdeographFallback;
-#if ENABLE(OPENTYPE_VERTICAL)
- RefPtr<OpenTypeVerticalData> m_verticalData;
-#endif
- bool m_hasVerticalGlyphs;
-
- Glyph m_spaceGlyph;
- float m_spaceWidth;
- Glyph m_zeroGlyph;
- float m_adjustedSpaceWidth;
-
- Glyph m_zeroWidthSpaceGlyph;
-
- GlyphData m_missingGlyphData;
-
- struct DerivedFontData {
- static PassOwnPtr<DerivedFontData> create(bool forCustomFont);
- ~DerivedFontData();
-
- bool forCustomFont;
- RefPtr<SimpleFontData> smallCaps;
- RefPtr<SimpleFontData> emphasisMark;
- RefPtr<SimpleFontData> brokenIdeograph;
- RefPtr<SimpleFontData> verticalRightOrientation;
- RefPtr<SimpleFontData> uprightOrientation;
- RefPtr<SimpleFontData> nonSyntheticItalic;
-#if PLATFORM(MAC)
- mutable RetainPtr<CFMutableDictionaryRef> compositeFontReferences;
-#endif
-
- private:
- DerivedFontData(bool custom)
- : forCustomFont(custom)
- {
- }
- };
-
- mutable OwnPtr<DerivedFontData> m_derivedFontData;
-
-#if USE(CG) || USE(CAIRO)
- float m_syntheticBoldOffset;
-#endif
-
-#if PLATFORM(MAC)
- mutable HashMap<unsigned, RetainPtr<CFDictionaryRef>> m_CFStringAttributes;
-#endif
-
-#if PLATFORM(MAC) || USE(HARFBUZZ)
- mutable OwnPtr<HashMap<String, bool>> m_combiningCharacterSequenceSupport;
-#endif
-
-#if PLATFORM(WIN)
- bool m_isSystemFont;
-#if !OS(WINCE) // disable unused members to save space
- mutable SCRIPT_CACHE m_scriptCache;
- mutable SCRIPT_FONTPROPERTIES* m_scriptFontProperties;
-#endif
-#endif
-#if PLATFORM(IOS)
- bool m_shouldNotBeUsedForArabic;
-#endif
-};
-
-ALWAYS_INLINE FloatRect SimpleFontData::boundsForGlyph(Glyph glyph) const
-{
- if (isZeroWidthSpaceGlyph(glyph))
- return FloatRect();
-
- FloatRect bounds;
- if (m_glyphToBoundsMap) {
- bounds = m_glyphToBoundsMap->metricsForGlyph(glyph);
- if (bounds.width() != cGlyphSizeUnknown)
- return bounds;
- }
-
- bounds = platformBoundsForGlyph(glyph);
- if (!m_glyphToBoundsMap)
- m_glyphToBoundsMap = adoptPtr(new GlyphMetricsMap<FloatRect>);
- m_glyphToBoundsMap->setMetricsForGlyph(glyph, bounds);
- return bounds;
-}
-
-ALWAYS_INLINE float SimpleFontData::widthForGlyph(Glyph glyph) const
-{
- if (isZeroWidthSpaceGlyph(glyph))
- return 0;
-
- float width = m_glyphToWidthMap.metricsForGlyph(glyph);
- if (width != cGlyphSizeUnknown)
- return width;
-
- if (m_fontData)
- width = m_fontData->widthForSVGGlyph(glyph, m_platformData.size());
-#if ENABLE(OPENTYPE_VERTICAL)
- else if (m_verticalData)
-#if USE(CG) || USE(CAIRO)
- width = m_verticalData->advanceHeight(this, glyph) + m_syntheticBoldOffset;
-#else
- width = m_verticalData->advanceHeight(this, glyph);
-#endif
-#endif
- else
- width = platformWidthForGlyph(glyph);
-
- m_glyphToWidthMap.setMetricsForGlyph(glyph, width);
- return width;
-}
-
-} // namespace WebCore
-#endif // SimpleFontData_h
diff --git a/Source/WebCore/platform/graphics/gstreamer/MediaSourceGStreamer.h b/Source/WebCore/platform/graphics/SourceBufferPrivate.h
index ad27d3602..7ac5dd275 100644
--- a/Source/WebCore/platform/graphics/gstreamer/MediaSourceGStreamer.h
+++ b/Source/WebCore/platform/graphics/SourceBufferPrivate.h
@@ -1,6 +1,6 @@
/*
* Copyright (C) 2013 Google Inc. All rights reserved.
- * Copyright (C) 2013 Orange
+ * Copyright (C) 2013-2017 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
@@ -29,35 +29,41 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef MediaSourceGStreamer_h
-#define MediaSourceGStreamer_h
+#pragma once
-#if ENABLE(MEDIA_SOURCE) && USE(GSTREAMER)
-#include "MediaSource.h"
-#include "WebKitMediaSourceGStreamer.h"
+#if ENABLE(MEDIA_SOURCE)
+
+#include "MediaPlayer.h"
namespace WebCore {
-class MediaSourceGStreamer final : public MediaSourcePrivate {
+class MediaSample;
+class SourceBufferPrivateClient;
+
+class SourceBufferPrivate : public RefCounted<SourceBufferPrivate> {
public:
- static void open(PassRefPtr<HTMLMediaSource>, WebKitMediaSrc*);
- ~MediaSourceGStreamer();
- AddStatus addSourceBuffer(const ContentType&, RefPtr<SourceBufferPrivate>&);
- double duration() { return m_duration; }
- void setDuration(double);
- void markEndOfStream(EndOfStreamStatus);
- void unmarkEndOfStream();
- MediaPlayer::ReadyState readyState() const { return m_readyState; }
- void setReadyState(MediaPlayer::ReadyState readyState) { m_readyState = readyState; }
-
-private:
- RefPtr<MediaSourceClientGstreamer> m_client;
- MediaSourceGStreamer(WebKitMediaSrc*);
- double m_duration;
- MediaPlayer::ReadyState m_readyState;
+ virtual ~SourceBufferPrivate() { }
+
+ virtual void setClient(SourceBufferPrivateClient*) = 0;
+
+ virtual void append(const unsigned char* data, unsigned length) = 0;
+ virtual void abort() = 0;
+ virtual void resetParserState() = 0;
+ virtual void removedFromMediaSource() = 0;
+
+ virtual MediaPlayer::ReadyState readyState() const = 0;
+ virtual void setReadyState(MediaPlayer::ReadyState) = 0;
+
+ virtual void flush(const AtomicString&) { }
+ virtual void enqueueSample(Ref<MediaSample>&&, const AtomicString&) { }
+ virtual bool isReadyForMoreSamples(const AtomicString&) { return false; }
+ virtual void setActive(bool) { }
+ virtual void stopAskingForMoreSamples(const AtomicString&) { }
+ virtual void notifyClientWhenReadyForMoreSamples(const AtomicString&) { }
+
+ virtual Vector<String> enqueuedSamplesForTrackID(const AtomicString&) { return { }; }
};
}
#endif
-#endif
diff --git a/Source/WebCore/platform/graphics/SourceBufferPrivateClient.h b/Source/WebCore/platform/graphics/SourceBufferPrivateClient.h
new file mode 100644
index 000000000..18087d5e3
--- /dev/null
+++ b/Source/WebCore/platform/graphics/SourceBufferPrivateClient.h
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2013-2017 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#if ENABLE(MEDIA_SOURCE)
+
+#include <wtf/MediaTime.h>
+#include <wtf/Vector.h>
+
+namespace WebCore {
+
+class AudioTrackPrivate;
+class InbandTextTrackPrivate;
+class MediaSample;
+class MediaDescription;
+class VideoTrackPrivate;
+
+class SourceBufferPrivateClient {
+public:
+ virtual ~SourceBufferPrivateClient() { }
+
+ struct InitializationSegment {
+ MediaTime duration;
+
+ struct AudioTrackInformation {
+ RefPtr<MediaDescription> description;
+ RefPtr<AudioTrackPrivate> track;
+ };
+ Vector<AudioTrackInformation> audioTracks;
+
+ struct VideoTrackInformation {
+ RefPtr<MediaDescription> description;
+ RefPtr<VideoTrackPrivate> track;
+ };
+ Vector<VideoTrackInformation> videoTracks;
+
+ struct TextTrackInformation {
+ RefPtr<MediaDescription> description;
+ RefPtr<InbandTextTrackPrivate> track;
+ };
+ Vector<TextTrackInformation> textTracks;
+ };
+ virtual void sourceBufferPrivateDidReceiveInitializationSegment(const InitializationSegment&) = 0;
+ virtual void sourceBufferPrivateDidReceiveSample(MediaSample&) = 0;
+ virtual bool sourceBufferPrivateHasAudio() const = 0;
+ virtual bool sourceBufferPrivateHasVideo() const = 0;
+
+ virtual void sourceBufferPrivateDidBecomeReadyForMoreSamples(const AtomicString& trackID) = 0;
+
+ virtual MediaTime sourceBufferPrivateFastSeekTimeForMediaTime(const MediaTime& time, const MediaTime&, const MediaTime&) { return time; }
+ virtual void sourceBufferPrivateSeekToTime(const MediaTime&) { };
+
+ enum AppendResult { AppendSucceeded, ReadStreamFailed, ParsingFailed };
+ virtual void sourceBufferPrivateAppendComplete(AppendResult) = 0;
+ virtual void sourceBufferPrivateDidReceiveRenderingError(int errorCode) = 0;
+};
+
+}
+
+#endif // ENABLE(MEDIA_SOURCE)
diff --git a/Source/WebCore/platform/graphics/SpringSolver.h b/Source/WebCore/platform/graphics/SpringSolver.h
new file mode 100644
index 000000000..e7ae9bcfa
--- /dev/null
+++ b/Source/WebCore/platform/graphics/SpringSolver.h
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2016 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+namespace WebCore {
+
+class SpringSolver {
+public:
+ SpringSolver(double mass, double stiffness, double damping, double initialVelocity)
+ {
+ m_w0 = std::sqrt(stiffness / mass);
+ m_zeta = damping / (2 * std::sqrt(stiffness * mass));
+
+ if (m_zeta < 1) {
+ // Under-damped.
+ m_wd = m_w0 * std::sqrt(1 - m_zeta * m_zeta);
+ m_A = 1;
+ m_B = (m_zeta * m_w0 + -initialVelocity) / m_wd;
+ } else {
+ // Critically damped (ignoring over-damped case for now).
+ m_A = 1;
+ m_B = -initialVelocity + m_w0;
+ }
+ }
+
+ double solve(double t)
+ {
+ if (m_zeta < 1) {
+ // Under-damped
+ t = std::exp(-t * m_zeta * m_w0) * (m_A * std::cos(m_wd * t) + m_B * std::sin(m_wd * t));
+ } else {
+ // Critically damped (ignoring over-damped case for now).
+ t = (m_A + m_B * t) * std::exp(-t * m_w0);
+ }
+
+ // Map range from [1..0] to [0..1].
+ return 1 - t;
+ }
+
+private:
+ double m_w0;
+ double m_zeta;
+ double m_wd;
+ double m_A;
+ double m_B;
+};
+
+} // namespace WebCore
diff --git a/Source/WebCore/platform/graphics/StringTruncator.cpp b/Source/WebCore/platform/graphics/StringTruncator.cpp
index 72f80c03d..85a3ad29a 100644
--- a/Source/WebCore/platform/graphics/StringTruncator.cpp
+++ b/Source/WebCore/platform/graphics/StringTruncator.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2005, 2006, 2007 Apple Inc. All rights reserved.
+ * Copyright (C) 2005, 2006, 2007, 2014 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -10,7 +10,7 @@
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
- * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ * 3. Neither the name of Apple Inc. ("Apple") nor the names of
* its contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
@@ -29,11 +29,13 @@
#include "config.h"
#include "StringTruncator.h"
-#include "Font.h"
-#include "TextBreakIterator.h"
+#include "FontCascade.h"
+#include <wtf/text/TextBreakIterator.h>
#include "TextRun.h"
+#include <unicode/ubrk.h>
#include <wtf/Assertions.h>
#include <wtf/Vector.h>
+#include <wtf/text/StringView.h>
#include <wtf/unicode/CharacterNames.h>
namespace WebCore {
@@ -42,19 +44,19 @@ namespace WebCore {
typedef unsigned TruncationFunction(const String&, unsigned length, unsigned keepCount, UChar* buffer, bool shouldInsertEllipsis);
-static inline int textBreakAtOrPreceding(TextBreakIterator* it, int offset)
+static inline int textBreakAtOrPreceding(UBreakIterator* it, int offset)
{
- if (isTextBreak(it, offset))
+ if (ubrk_isBoundary(it, offset))
return offset;
- int result = textBreakPreceding(it, offset);
- return result == TextBreakDone ? 0 : result;
+ int result = ubrk_preceding(it, offset);
+ return result == UBRK_DONE ? 0 : result;
}
-static inline int boundedTextBreakFollowing(TextBreakIterator* it, int offset, int length)
+static inline int boundedTextBreakFollowing(UBreakIterator* it, int offset, int length)
{
- int result = textBreakFollowing(it, offset);
- return result == TextBreakDone ? length : result;
+ int result = ubrk_following(it, offset);
+ return result == UBRK_DONE ? length : result;
}
static unsigned centerTruncateToBuffer(const String& string, unsigned length, unsigned keepCount, UChar* buffer, bool shouldInsertEllipsis)
@@ -69,6 +71,8 @@ static unsigned centerTruncateToBuffer(const String& string, unsigned length, un
#if PLATFORM(IOS)
// FIXME: We should guard this code behind an editing behavior. Then we can remove the PLATFORM(IOS)-guard.
+ // Or just turn it on for all platforms. It seems like good behavior everywhere. Might be better to generalize
+ // it to handle all whitespace, not just "space".
// Strip single character before ellipsis character, when that character is preceded by a space
if (omitStart > 1 && string[omitStart - 1] != space && omitStart > 2 && string[omitStart - 2] == space)
@@ -86,15 +90,13 @@ static unsigned centerTruncateToBuffer(const String& string, unsigned length, un
++omitEnd;
#endif
- unsigned truncatedLength = shouldInsertEllipsis ? omitStart + 1 + (length - omitEnd) : length - (omitEnd - omitStart);
+ unsigned truncatedLength = omitStart + shouldInsertEllipsis + (length - omitEnd);
ASSERT(truncatedLength <= length);
- memcpy(buffer, string.deprecatedCharacters(), sizeof(UChar) * omitStart);
- if (shouldInsertEllipsis) {
- buffer[omitStart] = horizontalEllipsis;
- memcpy(&buffer[omitStart + 1], &string.deprecatedCharacters()[omitEnd], sizeof(UChar) * (length - omitEnd));
- } else
- memcpy(&buffer[omitStart], &string.deprecatedCharacters()[omitEnd], sizeof(UChar) * (length - omitEnd));
+ StringView(string).substring(0, omitStart).getCharactersWithUpconvert(buffer);
+ if (shouldInsertEllipsis)
+ buffer[omitStart++] = horizontalEllipsis;
+ StringView(string).substring(omitEnd, length - omitEnd).getCharactersWithUpconvert(&buffer[omitStart]);
return truncatedLength;
}
@@ -105,6 +107,8 @@ static unsigned rightTruncateToBuffer(const String& string, unsigned length, uns
#if PLATFORM(IOS)
// FIXME: We should guard this code behind an editing behavior. Then we can remove the PLATFORM(IOS)-guard.
+ // Or just turn it on for all platforms. It seems like good behavior everywhere. Might be better to generalize
+ // it to handle all whitespace, not just "space".
// Strip single character before ellipsis character, when that character is preceded by a space
if (keepCount > 1 && string[keepCount - 1] != space && keepCount > 2 && string[keepCount - 2] == space)
@@ -119,7 +123,7 @@ static unsigned rightTruncateToBuffer(const String& string, unsigned length, uns
unsigned keepLength = textBreakAtOrPreceding(it, keepCount);
unsigned truncatedLength = shouldInsertEllipsis ? keepLength + 1 : keepLength;
- memcpy(buffer, string.deprecatedCharacters(), sizeof(UChar) * keepLength);
+ StringView(string).substring(0, keepLength).getCharactersWithUpconvert(buffer);
if (shouldInsertEllipsis)
buffer[keepLength] = horizontalEllipsis;
@@ -133,7 +137,7 @@ static unsigned rightClipToCharacterBuffer(const String& string, unsigned length
NonSharedCharacterBreakIterator it(StringView(string).substring(0, length));
unsigned keepLength = textBreakAtOrPreceding(it, keepCount);
- memcpy(buffer, string.deprecatedCharacters(), sizeof(UChar) * keepLength);
+ StringView(string).substring(0, keepLength).getCharactersWithUpconvert(buffer);
return keepLength;
}
@@ -143,16 +147,20 @@ static unsigned rightClipToWordBuffer(const String& string, unsigned length, uns
ASSERT(keepCount < length);
ASSERT(keepCount < STRING_BUFFER_SIZE);
- TextBreakIterator* it = wordBreakIterator(StringView(string).substring(0, length));
+ UBreakIterator* it = wordBreakIterator(StringView(string).substring(0, length));
unsigned keepLength = textBreakAtOrPreceding(it, keepCount);
- memcpy(buffer, string.deprecatedCharacters(), sizeof(UChar) * keepLength);
+ StringView(string).substring(0, keepLength).getCharactersWithUpconvert(buffer);
#if PLATFORM(IOS)
// FIXME: We should guard this code behind an editing behavior. Then we can remove the PLATFORM(IOS)-guard.
+ // Or just turn it on for all platforms. It seems like good behavior everywhere. Might be better to generalize
+ // it to handle all whitespace, not just "space".
+
// Motivated by <rdar://problem/7439327> truncation should not include a trailing space
- while ((keepLength > 0) && (string[keepLength - 1] == space))
+ while (keepLength && string[keepLength - 1] == space)
--keepLength;
#endif
+
return keepLength;
}
@@ -165,7 +173,7 @@ static unsigned leftTruncateToBuffer(const String& string, unsigned length, unsi
NonSharedCharacterBreakIterator it(string);
unsigned adjustedStartIndex = startIndex;
- startIndex = boundedTextBreakFollowing(it, startIndex, length - startIndex);
+ boundedTextBreakFollowing(it, startIndex, length - startIndex);
// Strip single character after ellipsis character, when that character is preceded by a space
if (adjustedStartIndex < length && string[adjustedStartIndex] != space
@@ -178,22 +186,20 @@ static unsigned leftTruncateToBuffer(const String& string, unsigned length, unsi
if (shouldInsertEllipsis) {
buffer[0] = horizontalEllipsis;
- memcpy(&buffer[1], &string.deprecatedCharacters()[adjustedStartIndex], sizeof(UChar) * (length - adjustedStartIndex + 1));
+ StringView(string).substring(adjustedStartIndex, length - adjustedStartIndex + 1).getCharactersWithUpconvert(&buffer[1]);
return length - adjustedStartIndex + 1;
}
- memcpy(&buffer[0], &string.deprecatedCharacters()[adjustedStartIndex], sizeof(UChar) * (length - adjustedStartIndex + 1));
+ StringView(string).substring(adjustedStartIndex, length - adjustedStartIndex + 1).getCharactersWithUpconvert(&buffer[0]);
return length - adjustedStartIndex;
}
-static float stringWidth(const Font& renderer, const UChar* characters, unsigned length, bool disableRoundingHacks)
+static float stringWidth(const FontCascade& renderer, const UChar* characters, unsigned length)
{
- TextRun run(characters, length);
- if (disableRoundingHacks)
- run.disableRoundingHacks();
+ TextRun run(StringView(characters, length));
return renderer.width(run);
}
-static String truncateString(const String& string, float maxWidth, const Font& font, TruncationFunction truncateToBuffer, bool disableRoundingHacks, float* resultWidth = nullptr, bool shouldInsertEllipsis = true, float customTruncationElementWidth = 0, bool alwaysTruncate = false)
+static String truncateString(const String& string, float maxWidth, const FontCascade& font, TruncationFunction truncateToBuffer, float* resultWidth = nullptr, bool shouldInsertEllipsis = true, float customTruncationElementWidth = 0, bool alwaysTruncate = false)
{
if (string.isEmpty())
return string;
@@ -203,7 +209,7 @@ static String truncateString(const String& string, float maxWidth, const Font& f
ASSERT(maxWidth >= 0);
- float currentEllipsisWidth = shouldInsertEllipsis ? stringWidth(font, &horizontalEllipsis, 1, disableRoundingHacks) : customTruncationElementWidth;
+ float currentEllipsisWidth = shouldInsertEllipsis ? stringWidth(font, &horizontalEllipsis, 1) : customTruncationElementWidth;
UChar stringBuffer[STRING_BUFFER_SIZE];
unsigned truncatedLength;
@@ -218,11 +224,11 @@ static String truncateString(const String& string, float maxWidth, const Font& f
truncatedLength = centerTruncateToBuffer(string, length, keepCount, stringBuffer, shouldInsertEllipsis);
} else {
keepCount = length;
- memcpy(stringBuffer, string.deprecatedCharacters(), sizeof(UChar) * length);
+ StringView(string).getCharactersWithUpconvert(stringBuffer);
truncatedLength = length;
}
- float width = stringWidth(font, stringBuffer, truncatedLength, disableRoundingHacks);
+ float width = stringWidth(font, stringBuffer, truncatedLength);
if (!shouldInsertEllipsis && alwaysTruncate)
width += customTruncationElementWidth;
if ((width - maxWidth) < 0.0001) { // Ignore rounding errors.
@@ -250,11 +256,10 @@ static String truncateString(const String& string, float maxWidth, const Font& f
/ (widthForSmallestKnownToNotFit - widthForLargestKnownToFit);
keepCount = static_cast<unsigned>(maxWidth * ratio);
- if (keepCount <= keepCountForLargestKnownToFit) {
+ if (keepCount <= keepCountForLargestKnownToFit)
keepCount = keepCountForLargestKnownToFit + 1;
- } else if (keepCount >= keepCountForSmallestKnownToNotFit) {
+ else if (keepCount >= keepCountForSmallestKnownToNotFit)
keepCount = keepCountForSmallestKnownToNotFit - 1;
- }
ASSERT_WITH_SECURITY_IMPLICATION(keepCount < length);
ASSERT(keepCount > 0);
@@ -263,7 +268,7 @@ static String truncateString(const String& string, float maxWidth, const Font& f
truncatedLength = truncateToBuffer(string, length, keepCount, stringBuffer, shouldInsertEllipsis);
- width = stringWidth(font, stringBuffer, truncatedLength, disableRoundingHacks);
+ width = stringWidth(font, stringBuffer, truncatedLength);
if (!shouldInsertEllipsis)
width += customTruncationElementWidth;
if (width <= maxWidth) {
@@ -289,44 +294,44 @@ static String truncateString(const String& string, float maxWidth, const Font& f
return String(stringBuffer, truncatedLength);
}
-String StringTruncator::centerTruncate(const String& string, float maxWidth, const Font& font, EnableRoundingHacksOrNot enableRoundingHacks)
+String StringTruncator::centerTruncate(const String& string, float maxWidth, const FontCascade& font)
{
- return truncateString(string, maxWidth, font, centerTruncateToBuffer, !enableRoundingHacks);
+ return truncateString(string, maxWidth, font, centerTruncateToBuffer);
}
-String StringTruncator::rightTruncate(const String& string, float maxWidth, const Font& font, EnableRoundingHacksOrNot enableRoundingHacks)
+String StringTruncator::rightTruncate(const String& string, float maxWidth, const FontCascade& font)
{
- return truncateString(string, maxWidth, font, rightTruncateToBuffer, !enableRoundingHacks);
+ return truncateString(string, maxWidth, font, rightTruncateToBuffer);
}
-float StringTruncator::width(const String& string, const Font& font, EnableRoundingHacksOrNot enableRoundingHacks)
+float StringTruncator::width(const String& string, const FontCascade& font)
{
- return stringWidth(font, string.deprecatedCharacters(), string.length(), !enableRoundingHacks);
+ return stringWidth(font, StringView(string).upconvertedCharacters(), string.length());
}
-String StringTruncator::centerTruncate(const String& string, float maxWidth, const Font& font, EnableRoundingHacksOrNot enableRoundingHacks, float& resultWidth, bool shouldInsertEllipsis, float customTruncationElementWidth)
+String StringTruncator::centerTruncate(const String& string, float maxWidth, const FontCascade& font, float& resultWidth, bool shouldInsertEllipsis, float customTruncationElementWidth)
{
- return truncateString(string, maxWidth, font, centerTruncateToBuffer, !enableRoundingHacks, &resultWidth, shouldInsertEllipsis, customTruncationElementWidth);
+ return truncateString(string, maxWidth, font, centerTruncateToBuffer, &resultWidth, shouldInsertEllipsis, customTruncationElementWidth);
}
-String StringTruncator::rightTruncate(const String& string, float maxWidth, const Font& font, EnableRoundingHacksOrNot enableRoundingHacks, float& resultWidth, bool shouldInsertEllipsis, float customTruncationElementWidth)
+String StringTruncator::rightTruncate(const String& string, float maxWidth, const FontCascade& font, float& resultWidth, bool shouldInsertEllipsis, float customTruncationElementWidth)
{
- return truncateString(string, maxWidth, font, rightTruncateToBuffer, !enableRoundingHacks, &resultWidth, shouldInsertEllipsis, customTruncationElementWidth);
+ return truncateString(string, maxWidth, font, rightTruncateToBuffer, &resultWidth, shouldInsertEllipsis, customTruncationElementWidth);
}
-String StringTruncator::leftTruncate(const String& string, float maxWidth, const Font& font, EnableRoundingHacksOrNot enableRoundingHacks, float& resultWidth, bool shouldInsertEllipsis, float customTruncationElementWidth)
+String StringTruncator::leftTruncate(const String& string, float maxWidth, const FontCascade& font, float& resultWidth, bool shouldInsertEllipsis, float customTruncationElementWidth)
{
- return truncateString(string, maxWidth, font, leftTruncateToBuffer, !enableRoundingHacks, &resultWidth, shouldInsertEllipsis, customTruncationElementWidth);
+ return truncateString(string, maxWidth, font, leftTruncateToBuffer, &resultWidth, shouldInsertEllipsis, customTruncationElementWidth);
}
-String StringTruncator::rightClipToCharacter(const String& string, float maxWidth, const Font& font, EnableRoundingHacksOrNot enableRoundingHacks, float& resultWidth, bool shouldInsertEllipsis, float customTruncationElementWidth)
+String StringTruncator::rightClipToCharacter(const String& string, float maxWidth, const FontCascade& font, float& resultWidth, bool shouldInsertEllipsis, float customTruncationElementWidth)
{
- return truncateString(string, maxWidth, font, rightClipToCharacterBuffer, !enableRoundingHacks, &resultWidth, shouldInsertEllipsis, customTruncationElementWidth);
+ return truncateString(string, maxWidth, font, rightClipToCharacterBuffer, &resultWidth, shouldInsertEllipsis, customTruncationElementWidth);
}
-String StringTruncator::rightClipToWord(const String& string, float maxWidth, const Font& font, EnableRoundingHacksOrNot enableRoundingHacks, float& resultWidth, bool shouldInsertEllipsis, float customTruncationElementWidth, bool alwaysTruncate)
+String StringTruncator::rightClipToWord(const String& string, float maxWidth, const FontCascade& font, float& resultWidth, bool shouldInsertEllipsis, float customTruncationElementWidth, bool alwaysTruncate)
{
- return truncateString(string, maxWidth, font, rightClipToWordBuffer, !enableRoundingHacks, &resultWidth, shouldInsertEllipsis, customTruncationElementWidth, alwaysTruncate);
+ return truncateString(string, maxWidth, font, rightClipToWordBuffer, &resultWidth, shouldInsertEllipsis, customTruncationElementWidth, alwaysTruncate);
}
} // namespace WebCore
diff --git a/Source/WebCore/platform/graphics/StringTruncator.h b/Source/WebCore/platform/graphics/StringTruncator.h
index 13d8e14af..edb22eb05 100644
--- a/Source/WebCore/platform/graphics/StringTruncator.h
+++ b/Source/WebCore/platform/graphics/StringTruncator.h
@@ -10,7 +10,7 @@
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
- * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ * 3. Neither the name of Apple Inc. ("Apple") nor the names of
* its contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
@@ -33,23 +33,21 @@
namespace WebCore {
- class Font;
-
- class StringTruncator {
- public:
- enum EnableRoundingHacksOrNot { DisableRoundingHacks, EnableRoundingHacks };
+class FontCascade;
- static String centerTruncate(const String&, float maxWidth, const Font&, EnableRoundingHacksOrNot = DisableRoundingHacks);
- static String rightTruncate(const String&, float maxWidth, const Font&, EnableRoundingHacksOrNot = DisableRoundingHacks);
+class StringTruncator {
+public:
+ WEBCORE_EXPORT static String centerTruncate(const String&, float maxWidth, const FontCascade&);
+ WEBCORE_EXPORT static String rightTruncate(const String&, float maxWidth, const FontCascade&);
- static String centerTruncate(const String&, float maxWidth, const Font&, EnableRoundingHacksOrNot, float& resultWidth, bool shouldInsertEllipsis = true, float customTruncationElementWidth = 0);
- static String rightTruncate(const String&, float maxWidth, const Font&, EnableRoundingHacksOrNot, float& resultWidth, bool shouldInsertEllipsis = true, float customTruncationElementWidth = 0);
- static String leftTruncate(const String&, float maxWidth, const Font&, EnableRoundingHacksOrNot, float& resultWidth, bool shouldInsertEllipsis = true, float customTruncationElementWidth = 0);
- static String rightClipToCharacter(const String&, float maxWidth, const Font&, EnableRoundingHacksOrNot, float& resultWidth, bool shouldInsertEllipsis = true, float customTruncationElementWidth = 0);
- static String rightClipToWord(const String&, float maxWidth, const Font&, EnableRoundingHacksOrNot, float& resultWidth, bool shouldInsertEllipsis = true, float customTruncationElementWidth = 0, bool alwaysTruncate = false);
+ WEBCORE_EXPORT static String centerTruncate(const String&, float maxWidth, const FontCascade&, float& resultWidth, bool shouldInsertEllipsis = true, float customTruncationElementWidth = 0);
+ WEBCORE_EXPORT static String rightTruncate(const String&, float maxWidth, const FontCascade&, float& resultWidth, bool shouldInsertEllipsis = true, float customTruncationElementWidth = 0);
+ WEBCORE_EXPORT static String leftTruncate(const String&, float maxWidth, const FontCascade&, float& resultWidth, bool shouldInsertEllipsis = true, float customTruncationElementWidth = 0);
+ WEBCORE_EXPORT static String rightClipToCharacter(const String&, float maxWidth, const FontCascade&, float& resultWidth, bool shouldInsertEllipsis = true, float customTruncationElementWidth = 0);
+ WEBCORE_EXPORT static String rightClipToWord(const String&, float maxWidth, const FontCascade&, float& resultWidth, bool shouldInsertEllipsis = true, float customTruncationElementWidth = 0, bool alwaysTruncate = false);
- static float width(const String&, const Font&, EnableRoundingHacksOrNot = DisableRoundingHacks);
- };
+ WEBCORE_EXPORT static float width(const String&, const FontCascade&);
+};
} // namespace WebCore
diff --git a/Source/WebCore/platform/graphics/SurrogatePairAwareTextIterator.cpp b/Source/WebCore/platform/graphics/SurrogatePairAwareTextIterator.cpp
index 9155e711a..682d98e25 100644
--- a/Source/WebCore/platform/graphics/SurrogatePairAwareTextIterator.cpp
+++ b/Source/WebCore/platform/graphics/SurrogatePairAwareTextIterator.cpp
@@ -27,11 +27,11 @@
namespace WebCore {
-SurrogatePairAwareTextIterator::SurrogatePairAwareTextIterator(const UChar* characters, int currentCharacter, int lastCharacter, int endCharacter)
+SurrogatePairAwareTextIterator::SurrogatePairAwareTextIterator(const UChar* characters, unsigned currentIndex, unsigned lastIndex, unsigned endIndex)
: m_characters(characters)
- , m_currentCharacter(currentCharacter)
- , m_lastCharacter(lastCharacter)
- , m_endCharacter(endCharacter)
+ , m_currentIndex(currentIndex)
+ , m_lastIndex(lastIndex)
+ , m_endIndex(endIndex)
{
}
@@ -57,7 +57,7 @@ bool SurrogatePairAwareTextIterator::consumeSlowCase(UChar32& character, unsigne
// Do we have a surrogate pair? If so, determine the full Unicode (32 bit) code point before glyph lookup.
// Make sure we have another character and it's a low surrogate.
- if (m_currentCharacter + 1 >= m_endCharacter)
+ if (m_currentIndex + 1 >= m_endIndex)
return false;
UChar low = m_characters[1];
@@ -74,7 +74,7 @@ UChar32 SurrogatePairAwareTextIterator::normalizeVoicingMarks()
// According to http://www.unicode.org/Public/UNIDATA/UCD.html#Canonical_Combining_Class_Values
static const uint8_t hiraganaKatakanaVoicingMarksCombiningClass = 8;
- if (m_currentCharacter + 1 >= m_endCharacter)
+ if (m_currentIndex + 1 >= m_endIndex)
return 0;
if (u_getCombiningClass(m_characters[1]) == hiraganaKatakanaVoicingMarksCombiningClass) {
diff --git a/Source/WebCore/platform/graphics/SurrogatePairAwareTextIterator.h b/Source/WebCore/platform/graphics/SurrogatePairAwareTextIterator.h
index 85c9694c7..470d14af2 100644
--- a/Source/WebCore/platform/graphics/SurrogatePairAwareTextIterator.h
+++ b/Source/WebCore/platform/graphics/SurrogatePairAwareTextIterator.h
@@ -28,13 +28,13 @@ namespace WebCore {
class SurrogatePairAwareTextIterator {
public:
- // The passed in UChar pointer starts at 'currentCharacter'. The iterator operatoes on the range [currentCharacter, lastCharacter].
- // 'endCharacter' denotes the maximum length of the UChar array, which might exceed 'lastCharacter'.
- SurrogatePairAwareTextIterator(const UChar*, int currentCharacter, int lastCharacter, int endCharacter);
+ // The passed in UChar pointer starts at 'currentIndex'. The iterator operatoes on the range [currentIndex, lastIndex].
+ // 'endIndex' denotes the maximum length of the UChar array, which might exceed 'lastIndex'.
+ SurrogatePairAwareTextIterator(const UChar*, unsigned currentIndex, unsigned lastIndex, unsigned endIndex);
inline bool consume(UChar32& character, unsigned& clusterLength)
{
- if (m_currentCharacter >= m_lastCharacter)
+ if (m_currentIndex >= m_lastIndex)
return false;
character = *m_characters;
@@ -49,10 +49,10 @@ public:
void advance(unsigned advanceLength)
{
m_characters += advanceLength;
- m_currentCharacter += advanceLength;
+ m_currentIndex += advanceLength;
}
- int currentCharacter() const { return m_currentCharacter; }
+ unsigned currentIndex() const { return m_currentIndex; }
const UChar* characters() const { return m_characters; }
private:
@@ -60,9 +60,9 @@ private:
UChar32 normalizeVoicingMarks();
const UChar* m_characters;
- int m_currentCharacter;
- int m_lastCharacter;
- int m_endCharacter;
+ unsigned m_currentIndex;
+ unsigned m_lastIndex;
+ unsigned m_endIndex;
};
}
diff --git a/Source/WebCore/platform/graphics/TextRun.cpp b/Source/WebCore/platform/graphics/TextRun.cpp
index 7d28b1f0f..884cf9b19 100644
--- a/Source/WebCore/platform/graphics/TextRun.cpp
+++ b/Source/WebCore/platform/graphics/TextRun.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2011 Apple Inc. All rights reserved.
+ * Copyright (C) 2011, 2014 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -29,30 +29,15 @@
namespace WebCore {
struct ExpectedTextRunSize {
- const void* pointer;
- int integers[2];
+ StringView text;
+ unsigned integer1;
+ unsigned integer2;
float float1;
-#if ENABLE(SVG)
float float2;
-#endif
float float3;
- uint32_t bitfields : 10;
- unsigned anUnsigned;
- RefPtr<TextRun::RenderingContext> renderingContext;
+ unsigned bitfields : 9;
};
COMPILE_ASSERT(sizeof(TextRun) == sizeof(ExpectedTextRunSize), TextRun_is_not_of_expected_size);
-bool TextRun::s_allowsRoundingHacks = false;
-
-void TextRun::setAllowsRoundingHacks(bool allowsRoundingHacks)
-{
- s_allowsRoundingHacks = allowsRoundingHacks;
-}
-
-bool TextRun::allowsRoundingHacks()
-{
- return s_allowsRoundingHacks;
-}
-
}
diff --git a/Source/WebCore/platform/graphics/TextRun.h b/Source/WebCore/platform/graphics/TextRun.h
index 213b26372..a80fe7e61 100644
--- a/Source/WebCore/platform/graphics/TextRun.h
+++ b/Source/WebCore/platform/graphics/TextRun.h
@@ -24,164 +24,76 @@
#ifndef TextRun_h
#define TextRun_h
-#include "TextDirection.h"
+#include "TextFlags.h"
+#include "WritingMode.h"
#include <wtf/RefCounted.h>
-#include <wtf/text/WTFString.h>
+#include <wtf/text/StringView.h>
namespace WebCore {
class FloatPoint;
class FloatRect;
-class Font;
+class FontCascade;
class GraphicsContext;
class GlyphBuffer;
-class SimpleFontData;
+class GlyphToPathTranslator;
+class Font;
+
struct GlyphData;
struct WidthIterator;
class TextRun {
WTF_MAKE_FAST_ALLOCATED;
public:
- enum ExpansionBehaviorFlags {
- ForbidTrailingExpansion = 0 << 0,
- AllowTrailingExpansion = 1 << 0,
- ForbidLeadingExpansion = 0 << 1,
- AllowLeadingExpansion = 1 << 1,
- };
-
- typedef unsigned ExpansionBehavior;
-
- enum RoundingHackFlags {
- NoRounding = 0,
- RunRounding = 1 << 0,
- WordRounding = 1 << 1,
- };
-
- typedef unsigned RoundingHacks;
-
-#if ENABLE(8BIT_TEXTRUN)
- TextRun(const LChar* c, unsigned len, float xpos = 0, float expansion = 0, ExpansionBehavior expansionBehavior = AllowTrailingExpansion | ForbidLeadingExpansion, TextDirection direction = LTR, bool directionalOverride = false, bool characterScanForCodePath = true, RoundingHacks roundingHacks = RunRounding | WordRounding)
- : m_charactersLength(len)
- , m_len(len)
- , m_xpos(xpos)
-#if ENABLE(SVG)
- , m_horizontalGlyphStretch(1)
-#endif
- , m_expansion(expansion)
- , m_expansionBehavior(expansionBehavior)
- , m_is8Bit(true)
- , m_allowTabs(false)
- , m_direction(direction)
- , m_directionalOverride(directionalOverride)
- , m_characterScanForCodePath(characterScanForCodePath)
- , m_applyRunRounding((roundingHacks & RunRounding) && s_allowsRoundingHacks)
- , m_applyWordRounding((roundingHacks & WordRounding) && s_allowsRoundingHacks)
- , m_disableSpacing(false)
- , m_tabSize(0)
- {
- m_data.characters8 = c;
- }
-#endif
-
- TextRun(const UChar* c, unsigned len, float xpos = 0, float expansion = 0, ExpansionBehavior expansionBehavior = AllowTrailingExpansion | ForbidLeadingExpansion, TextDirection direction = LTR, bool directionalOverride = false, bool characterScanForCodePath = true, RoundingHacks roundingHacks = RunRounding | WordRounding)
- : m_charactersLength(len)
- , m_len(len)
- , m_xpos(xpos)
-#if ENABLE(SVG)
- , m_horizontalGlyphStretch(1)
-#endif
- , m_expansion(expansion)
- , m_expansionBehavior(expansionBehavior)
- , m_is8Bit(false)
- , m_allowTabs(false)
- , m_direction(direction)
- , m_directionalOverride(directionalOverride)
- , m_characterScanForCodePath(characterScanForCodePath)
- , m_applyRunRounding((roundingHacks & RunRounding) && s_allowsRoundingHacks)
- , m_applyWordRounding((roundingHacks & WordRounding) && s_allowsRoundingHacks)
- , m_disableSpacing(false)
+ explicit TextRun(StringView text, float xpos = 0, float expansion = 0, ExpansionBehavior expansionBehavior = DefaultExpansion, TextDirection direction = LTR, bool directionalOverride = false, bool characterScanForCodePath = true)
+ : m_text(text)
+ , m_charactersLength(text.length())
, m_tabSize(0)
- {
- m_data.characters16 = c;
- }
-
- TextRun(const String& s, float xpos = 0, float expansion = 0, ExpansionBehavior expansionBehavior = AllowTrailingExpansion | ForbidLeadingExpansion, TextDirection direction = LTR, bool directionalOverride = false, bool characterScanForCodePath = true, RoundingHacks roundingHacks = RunRounding | WordRounding)
- : m_charactersLength(s.length())
- , m_len(s.length())
, m_xpos(xpos)
-#if ENABLE(SVG)
, m_horizontalGlyphStretch(1)
-#endif
, m_expansion(expansion)
, m_expansionBehavior(expansionBehavior)
, m_allowTabs(false)
, m_direction(direction)
, m_directionalOverride(directionalOverride)
, m_characterScanForCodePath(characterScanForCodePath)
- , m_applyRunRounding((roundingHacks & RunRounding) && s_allowsRoundingHacks)
- , m_applyWordRounding((roundingHacks & WordRounding) && s_allowsRoundingHacks)
, m_disableSpacing(false)
- , m_tabSize(0)
{
-#if ENABLE(8BIT_TEXTRUN)
- if (m_charactersLength && s.is8Bit()) {
- m_data.characters8 = s.characters8();
- m_is8Bit = true;
- } else {
- m_data.characters16 = s.deprecatedCharacters();
- m_is8Bit = false;
- }
-#else
- m_data.characters16 = s.deprecatedCharacters();
- m_is8Bit = false;
-#endif
}
TextRun subRun(unsigned startOffset, unsigned length) const
{
- ASSERT_WITH_SECURITY_IMPLICATION(startOffset < m_len);
+ ASSERT_WITH_SECURITY_IMPLICATION(startOffset < m_text.length());
TextRun result = *this;
-#if ENABLE(8BIT_TEXTRUN)
if (is8Bit()) {
result.setText(data8(startOffset), length);
return result;
}
-#else
- ASSERT(!is8Bit());
-#endif
result.setText(data16(startOffset), length);
return result;
}
- UChar operator[](unsigned i) const { ASSERT_WITH_SECURITY_IMPLICATION(i < m_len); return is8Bit() ? m_data.characters8[i] :m_data.characters16[i]; }
- const LChar* data8(unsigned i) const { ASSERT_WITH_SECURITY_IMPLICATION(i < m_len); ASSERT(is8Bit()); return &m_data.characters8[i]; }
- const UChar* data16(unsigned i) const { ASSERT_WITH_SECURITY_IMPLICATION(i < m_len); ASSERT(!is8Bit()); return &m_data.characters16[i]; }
+ UChar operator[](unsigned i) const { ASSERT_WITH_SECURITY_IMPLICATION(i < m_text.length()); return m_text[i]; }
+ const LChar* data8(unsigned i) const { ASSERT_WITH_SECURITY_IMPLICATION(i < m_text.length()); ASSERT(is8Bit()); return &m_text.characters8()[i]; }
+ const UChar* data16(unsigned i) const { ASSERT_WITH_SECURITY_IMPLICATION(i < m_text.length()); ASSERT(!is8Bit()); return &m_text.characters16()[i]; }
- const LChar* characters8() const { ASSERT(is8Bit()); return m_data.characters8; }
- const UChar* characters16() const { ASSERT(!is8Bit()); return m_data.characters16; }
-
- bool is8Bit() const { return m_is8Bit; }
- int length() const { return m_len; }
- int charactersLength() const { return m_charactersLength; }
- String string() const
- {
- if (is8Bit())
- return String(m_data.characters8, m_len);
- return String(m_data.characters16, m_len);
- }
+ const LChar* characters8() const { ASSERT(is8Bit()); return m_text.characters8(); }
+ const UChar* characters16() const { ASSERT(!is8Bit()); return m_text.characters16(); }
-#if ENABLE(8BIT_TEXTRUN)
- void setText(const LChar* c, unsigned len) { m_data.characters8 = c; m_len = len; m_is8Bit = true;}
-#endif
- void setText(const UChar* c, unsigned len) { m_data.characters16 = c; m_len = len; m_is8Bit = false;}
+ bool is8Bit() const { return m_text.is8Bit(); }
+ unsigned length() const { return m_text.length(); }
+ unsigned charactersLength() const { return m_charactersLength; }
+ String string() const { return m_text.toString(); }
+
+ void setText(const LChar* c, unsigned len) { m_text = StringView(c, len); }
+ void setText(const UChar* c, unsigned len) { m_text = StringView(c, len); }
+ void setText(StringView stringView) { m_text = stringView; }
void setCharactersLength(unsigned charactersLength) { m_charactersLength = charactersLength; }
-#if ENABLE(SVG)
float horizontalGlyphStretch() const { return m_horizontalGlyphStretch; }
void setHorizontalGlyphStretch(float scale) { m_horizontalGlyphStretch = scale; }
-#endif
bool allowTabs() const { return m_allowTabs; }
unsigned tabSize() const { return m_tabSize; }
@@ -190,71 +102,40 @@ public:
float xPos() const { return m_xpos; }
void setXPos(float xPos) { m_xpos = xPos; }
float expansion() const { return m_expansion; }
- bool allowsLeadingExpansion() const { return m_expansionBehavior & AllowLeadingExpansion; }
- bool allowsTrailingExpansion() const { return m_expansionBehavior & AllowTrailingExpansion; }
+ ExpansionBehavior expansionBehavior() const { return m_expansionBehavior; }
TextDirection direction() const { return static_cast<TextDirection>(m_direction); }
bool rtl() const { return m_direction == RTL; }
bool ltr() const { return m_direction == LTR; }
bool directionalOverride() const { return m_directionalOverride; }
bool characterScanForCodePath() const { return m_characterScanForCodePath; }
- bool applyRunRounding() const { return m_applyRunRounding; }
- bool applyWordRounding() const { return m_applyWordRounding; }
bool spacingDisabled() const { return m_disableSpacing; }
void disableSpacing() { m_disableSpacing = true; }
- void disableRoundingHacks() { m_applyRunRounding = m_applyWordRounding = false; }
void setDirection(TextDirection direction) { m_direction = direction; }
void setDirectionalOverride(bool override) { m_directionalOverride = override; }
void setCharacterScanForCodePath(bool scan) { m_characterScanForCodePath = scan; }
-
- class RenderingContext : public RefCounted<RenderingContext> {
- public:
- virtual ~RenderingContext() { }
-
-#if ENABLE(SVG_FONTS)
- virtual GlyphData glyphDataForCharacter(const Font&, WidthIterator&, UChar32 character, bool mirror, int currentCharacter, unsigned& advanceLength) = 0;
- virtual void drawSVGGlyphs(GraphicsContext*, const SimpleFontData*, const GlyphBuffer&, int from, int to, const FloatPoint&) const = 0;
- virtual float floatWidthUsingSVGFont(const Font&, const TextRun&, int& charsConsumed, String& glyphName) const = 0;
- virtual bool applySVGKerning(const SimpleFontData*, WidthIterator&, GlyphBuffer*, int from) const = 0;
-#endif
- };
-
- RenderingContext* renderingContext() const { return m_renderingContext.get(); }
- void setRenderingContext(PassRefPtr<RenderingContext> context) { m_renderingContext = context; }
-
- static void setAllowsRoundingHacks(bool);
- static bool allowsRoundingHacks();
+ StringView text() const { return m_text; }
private:
- static bool s_allowsRoundingHacks;
+ StringView m_text;
+ unsigned m_charactersLength; // Marks the end of the characters buffer. Default equals to length of m_text.
- union {
- const LChar* characters8;
- const UChar* characters16;
- } m_data;
- unsigned m_charactersLength; // Marks the end of the characters buffer. Default equals to m_len.
- unsigned m_len;
+ unsigned m_tabSize;
// m_xpos is the x position relative to the left start of the text line, not relative to the left
// start of the containing block. In the case of right alignment or center alignment, left start of
- // the text line is not the same as left start of the containing block.
+ // the text line is not the same as left start of the containing block. This variable is only used
+ // to calculate the width of \t
float m_xpos;
-#if ENABLE(SVG)
float m_horizontalGlyphStretch;
-#endif
float m_expansion;
- ExpansionBehavior m_expansionBehavior : 2;
- unsigned m_is8Bit : 1;
+ unsigned m_expansionBehavior : 4;
unsigned m_allowTabs : 1;
unsigned m_direction : 1;
unsigned m_directionalOverride : 1; // Was this direction set by an override character.
unsigned m_characterScanForCodePath : 1;
- unsigned m_applyRunRounding : 1;
- unsigned m_applyWordRounding : 1;
unsigned m_disableSpacing : 1;
- unsigned m_tabSize;
- RefPtr<RenderingContext> m_renderingContext;
};
inline void TextRun::setTabSize(bool allow, unsigned size)
diff --git a/Source/WebCore/platform/graphics/TextTrackRepresentation.cpp b/Source/WebCore/platform/graphics/TextTrackRepresentation.cpp
index d178e586f..d06d3a2de 100644
--- a/Source/WebCore/platform/graphics/TextTrackRepresentation.cpp
+++ b/Source/WebCore/platform/graphics/TextTrackRepresentation.cpp
@@ -29,24 +29,26 @@
#include "TextTrackRepresentation.h"
+#include "IntRect.h"
+
namespace WebCore {
class NullTextTrackRepresentation : public TextTrackRepresentation {
public:
virtual ~NullTextTrackRepresentation() { }
- virtual void update() { }
-#if USE(ACCELERATED_COMPOSITING)
- virtual PlatformLayer* platformLayer() { return 0; }
-#endif
- virtual void setContentScale(float) { }
- virtual IntRect bounds() const { return IntRect(); }
+ void update() override { }
+ PlatformLayer* platformLayer() override { return nullptr; }
+ void setContentScale(float) override { }
+ IntRect bounds() const override { return IntRect(); }
};
-#if !PLATFORM(IOS)
-PassOwnPtr<TextTrackRepresentation> TextTrackRepresentation::create(TextTrackRepresentationClient*)
+#if !(PLATFORM(IOS) || (PLATFORM(MAC) && ENABLE(VIDEO_PRESENTATION_MODE)))
+
+std::unique_ptr<TextTrackRepresentation> TextTrackRepresentation::create(TextTrackRepresentationClient&)
{
- return WTF::adoptPtr(new NullTextTrackRepresentation());
+ return std::make_unique<NullTextTrackRepresentation>();
}
+
#endif
}
diff --git a/Source/WebCore/platform/graphics/TextTrackRepresentation.h b/Source/WebCore/platform/graphics/TextTrackRepresentation.h
index 5f844483d..72117048a 100644
--- a/Source/WebCore/platform/graphics/TextTrackRepresentation.h
+++ b/Source/WebCore/platform/graphics/TextTrackRepresentation.h
@@ -28,13 +28,11 @@
#if ENABLE(VIDEO_TRACK)
-#include "IntRect.h"
#include "PlatformLayer.h"
-#include <wtf/PassOwnPtr.h>
+#include <wtf/Forward.h>
namespace WebCore {
-class GraphicsContext;
class Image;
class IntRect;
@@ -42,20 +40,18 @@ class TextTrackRepresentationClient {
public:
virtual ~TextTrackRepresentationClient() { }
- virtual PassRefPtr<Image> createTextTrackRepresentationImage() = 0;
+ virtual RefPtr<Image> createTextTrackRepresentationImage() = 0;
virtual void textTrackRepresentationBoundsChanged(const IntRect&) = 0;
};
class TextTrackRepresentation {
public:
- static PassOwnPtr<TextTrackRepresentation> create(TextTrackRepresentationClient*);
+ static std::unique_ptr<TextTrackRepresentation> create(TextTrackRepresentationClient&);
virtual ~TextTrackRepresentation() { }
virtual void update() = 0;
-#if USE(ACCELERATED_COMPOSITING)
virtual PlatformLayer* platformLayer() = 0;
-#endif
virtual void setContentScale(float) = 0;
virtual IntRect bounds() const = 0;
};
diff --git a/Source/WebCore/platform/graphics/TiledBacking.h b/Source/WebCore/platform/graphics/TiledBacking.h
index c6266a6e5..95f7cb8e7 100644
--- a/Source/WebCore/platform/graphics/TiledBacking.h
+++ b/Source/WebCore/platform/graphics/TiledBacking.h
@@ -23,41 +23,88 @@
* THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef TiledBacking_h
-#define TiledBacking_h
+#pragma once
+
+#include <wtf/MonotonicTime.h>
+#include <wtf/Optional.h>
namespace WebCore {
+enum TileSizeMode {
+ StandardTileSizeMode,
+ GiantTileSizeMode
+};
+
+class FloatPoint;
+class FloatRect;
class IntRect;
-#if PLATFORM(MAC)
class PlatformCALayer;
-#endif
enum ScrollingModeIndication {
+ SynchronousScrollingBecauseOfLackOfScrollingCoordinatorIndication,
SynchronousScrollingBecauseOfStyleIndication,
SynchronousScrollingBecauseOfEventHandlersIndication,
AsyncScrollingIndication
};
+struct VelocityData {
+ double horizontalVelocity;
+ double verticalVelocity;
+ double scaleChangeRate;
+ MonotonicTime lastUpdateTime;
+
+ VelocityData(double horizontal = 0, double vertical = 0, double scaleChange = 0, MonotonicTime updateTime = MonotonicTime())
+ : horizontalVelocity(horizontal)
+ , verticalVelocity(vertical)
+ , scaleChangeRate(scaleChange)
+ , lastUpdateTime(updateTime)
+ {
+ }
+
+ bool velocityOrScaleIsChanging() const
+ {
+ return horizontalVelocity || verticalVelocity || scaleChangeRate;
+ }
+};
+
class TiledBacking {
public:
virtual ~TiledBacking() { }
virtual void setVisibleRect(const FloatRect&) = 0;
virtual FloatRect visibleRect() const = 0;
- virtual bool tilesWouldChangeForVisibleRect(const FloatRect&) const = 0;
- virtual void setExposedRect(const FloatRect&) = 0;
+ // Only used to update the tile coverage map.
+ virtual void setLayoutViewportRect(std::optional<FloatRect>) = 0;
+
+ virtual void setCoverageRect(const FloatRect&) = 0;
+ virtual FloatRect coverageRect() const = 0;
+ virtual bool tilesWouldChangeForCoverageRect(const FloatRect&) const = 0;
+
+ virtual void setTiledScrollingIndicatorPosition(const FloatPoint&) = 0;
+ virtual void setTopContentInset(float) = 0;
+
+ virtual void setVelocity(const VelocityData&) = 0;
+
+ virtual void setTileSizeUpdateDelayDisabledForTesting(bool) = 0;
+
+ enum {
+ NotScrollable = 0,
+ HorizontallyScrollable = 1 << 0,
+ VerticallyScrollable = 1 << 1
+ };
+ typedef unsigned Scrollability;
+ virtual void setScrollability(Scrollability) = 0;
virtual void prepopulateRect(const FloatRect&) = 0;
virtual void setIsInWindow(bool) = 0;
+ virtual bool isInWindow() const = 0;
enum {
CoverageForVisibleArea = 0,
CoverageForVerticalScrolling = 1 << 0,
CoverageForHorizontalScrolling = 1 << 1,
- CoverageForSlowScrolling = 1 << 2, // Indicates that we expect to paint a lot on scrolling.
CoverageForScrolling = CoverageForVerticalScrolling | CoverageForHorizontalScrolling
};
typedef unsigned TileCoverage;
@@ -65,6 +112,11 @@ public:
virtual void setTileCoverage(TileCoverage) = 0;
virtual TileCoverage tileCoverage() const = 0;
+ virtual void adjustTileCoverageRect(FloatRect& coverageRect, const FloatSize& newSize, const FloatRect& previousVisibleRect, const FloatRect& currentVisibleRect, float contentsScale) const = 0;
+
+ virtual void willStartLiveResize() = 0;
+ virtual void didEndLiveResize() = 0;
+
virtual IntSize tileSize() const = 0;
virtual void revalidateTiles() = 0;
@@ -73,35 +125,34 @@ public:
virtual void setScrollingPerformanceLoggingEnabled(bool) = 0;
virtual bool scrollingPerformanceLoggingEnabled() const = 0;
- virtual void setAggressivelyRetainsTiles(bool) = 0;
- virtual bool aggressivelyRetainsTiles() const = 0;
-
- virtual void setUnparentsOffscreenTiles(bool) = 0;
- virtual bool unparentsOffscreenTiles() const = 0;
-
virtual double retainedTileBackingStoreMemory() const = 0;
- virtual void setTileMargins(int marginTop, int marginBottom, int marginLeft, int marginRight) = 0;
+ virtual void setHasMargins(bool marginTop, bool marginBottom, bool marginLeft, bool marginRight) = 0;
+ virtual void setMarginSize(int) = 0;
virtual bool hasMargins() const = 0;
+ virtual bool hasHorizontalMargins() const = 0;
+ virtual bool hasVerticalMargins() const = 0;
virtual int topMarginHeight() const = 0;
virtual int bottomMarginHeight() const = 0;
virtual int leftMarginWidth() const = 0;
virtual int rightMarginWidth() const = 0;
+ virtual void setZoomedOutContentsScale(float) = 0;
+ virtual float zoomedOutContentsScale() const = 0;
+
// Includes margins.
virtual IntRect bounds() const = 0;
+ virtual IntRect boundsWithoutMargin() const = 0;
// Exposed for testing
virtual IntRect tileCoverageRect() const = 0;
virtual IntRect tileGridExtent() const = 0;
virtual void setScrollingModeIndication(ScrollingModeIndication) = 0;
-#if PLATFORM(MAC)
+#if USE(CA)
virtual PlatformCALayer* tiledScrollingIndicatorLayer() = 0;
#endif
};
} // namespace WebCore
-
-#endif // TiledBacking_h
diff --git a/Source/WebCore/platform/graphics/TrackPrivateBase.h b/Source/WebCore/platform/graphics/TrackPrivateBase.h
index 01d23aee9..0b00ca5f6 100644
--- a/Source/WebCore/platform/graphics/TrackPrivateBase.h
+++ b/Source/WebCore/platform/graphics/TrackPrivateBase.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2012 Apple Inc. All rights reserved.
+ * Copyright (C) 2012-2017 Apple Inc. All rights reserved.
* Copyright (C) 2013 Cable Television Labs, Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -12,10 +12,10 @@
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
- * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
@@ -25,33 +25,29 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef TrackPrivateBase_h
-#define TrackPrivateBase_h
-
-#include <wtf/Forward.h>
-#include <wtf/Noncopyable.h>
-#include <wtf/RefCounted.h>
-#include <wtf/text/AtomicString.h>
+#pragma once
#if ENABLE(VIDEO_TRACK)
-namespace WebCore {
+#include <wtf/MediaTime.h>
+#include <wtf/text/AtomicString.h>
-class TrackPrivateBase;
+namespace WebCore {
class TrackPrivateBaseClient {
public:
virtual ~TrackPrivateBaseClient() { }
- virtual void idChanged(TrackPrivateBase*, const String&) = 0;
- virtual void labelChanged(TrackPrivateBase*, const String&) = 0;
- virtual void languageChanged(TrackPrivateBase*, const String&) = 0;
- virtual void willRemove(TrackPrivateBase*) = 0;
+ virtual void idChanged(const AtomicString&) = 0;
+ virtual void labelChanged(const AtomicString&) = 0;
+ virtual void languageChanged(const AtomicString&) = 0;
+ virtual void willRemove() = 0;
};
class TrackPrivateBase : public RefCounted<TrackPrivateBase> {
- WTF_MAKE_NONCOPYABLE(TrackPrivateBase); WTF_MAKE_FAST_ALLOCATED;
+ WTF_MAKE_NONCOPYABLE(TrackPrivateBase);
+ WTF_MAKE_FAST_ALLOCATED;
public:
- virtual ~TrackPrivateBase() { }
+ virtual ~TrackPrivateBase() = default;
virtual TrackPrivateBaseClient* client() const = 0;
@@ -61,17 +57,18 @@ public:
virtual int trackIndex() const { return 0; }
+ virtual MediaTime startTimeVariance() const { return MediaTime::zeroTime(); }
+
void willBeRemoved()
{
- if (TrackPrivateBaseClient* client = this->client())
- client->willRemove(this);
+ if (auto* client = this->client())
+ client->willRemove();
}
protected:
- TrackPrivateBase() { }
+ TrackPrivateBase() = default;
};
} // namespace WebCore
#endif
-#endif
diff --git a/Source/WebCore/platform/graphics/VideoTrackPrivate.h b/Source/WebCore/platform/graphics/VideoTrackPrivate.h
index 917eb9715..f94533567 100644
--- a/Source/WebCore/platform/graphics/VideoTrackPrivate.h
+++ b/Source/WebCore/platform/graphics/VideoTrackPrivate.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2012 Apple Inc. All rights reserved.
+ * Copyright (C) 2012-2017 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -10,10 +10,10 @@
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
- * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
@@ -23,31 +23,23 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef VideoTrackPrivate_h
-#define VideoTrackPrivate_h
-
-#include "TrackPrivateBase.h"
+#pragma once
#if ENABLE(VIDEO_TRACK)
-namespace WebCore {
+#include "TrackPrivateBase.h"
-class VideoTrackPrivate;
+namespace WebCore {
class VideoTrackPrivateClient : public TrackPrivateBaseClient {
public:
- virtual void selectedChanged(VideoTrackPrivate*, bool) = 0;
+ virtual void selectedChanged(bool) = 0;
};
class VideoTrackPrivate : public TrackPrivateBase {
public:
- static PassRefPtr<VideoTrackPrivate> create()
- {
- return adoptRef(new VideoTrackPrivate());
- }
-
void setClient(VideoTrackPrivateClient* client) { m_client = client; }
- virtual VideoTrackPrivateClient* client() const override { return m_client; }
+ VideoTrackPrivateClient* client() const override { return m_client; }
virtual void setSelected(bool selected)
{
@@ -55,26 +47,21 @@ public:
return;
m_selected = selected;
if (m_client)
- m_client->selectedChanged(this, m_selected);
- };
+ m_client->selectedChanged(m_selected);
+ }
virtual bool selected() const { return m_selected; }
enum Kind { Alternative, Captions, Main, Sign, Subtitles, Commentary, None };
virtual Kind kind() const { return None; }
protected:
- VideoTrackPrivate()
- : m_client(0)
- , m_selected(false)
- {
- }
+ VideoTrackPrivate() = default;
private:
- VideoTrackPrivateClient* m_client;
- bool m_selected;
+ VideoTrackPrivateClient* m_client { nullptr };
+ bool m_selected { false };
};
} // namespace WebCore
#endif
-#endif
diff --git a/Source/WebCore/platform/graphics/WOFFFileFormat.cpp b/Source/WebCore/platform/graphics/WOFFFileFormat.cpp
index 051871ac6..c2323cf04 100644
--- a/Source/WebCore/platform/graphics/WOFFFileFormat.cpp
+++ b/Source/WebCore/platform/graphics/WOFFFileFormat.cpp
@@ -30,27 +30,32 @@
#include "SharedBuffer.h"
#include <wtf/ByteOrder.h>
+#if USE(WOFF2)
+#include "woff2_common.h"
+#include "woff2_dec.h"
+#endif
+
namespace WebCore {
-static bool readUInt32(SharedBuffer* buffer, size_t& offset, uint32_t& value)
+static bool readUInt32(SharedBuffer& buffer, size_t& offset, uint32_t& value)
{
- ASSERT_ARG(offset, offset <= buffer->size());
- if (buffer->size() - offset < sizeof(value))
+ ASSERT_ARG(offset, offset <= buffer.size());
+ if (buffer.size() - offset < sizeof(value))
return false;
- value = ntohl(*reinterpret_cast_ptr<const uint32_t*>(buffer->data() + offset));
+ value = ntohl(*reinterpret_cast_ptr<const uint32_t*>(buffer.data() + offset));
offset += sizeof(value);
return true;
}
-static bool readUInt16(SharedBuffer* buffer, size_t& offset, uint16_t& value)
+static bool readUInt16(SharedBuffer& buffer, size_t& offset, uint16_t& value)
{
- ASSERT_ARG(offset, offset <= buffer->size());
- if (buffer->size() - offset < sizeof(value))
+ ASSERT_ARG(offset, offset <= buffer.size());
+ if (buffer.size() - offset < sizeof(value))
return false;
- value = ntohs(*reinterpret_cast_ptr<const uint16_t*>(buffer->data() + offset));
+ value = ntohs(*reinterpret_cast_ptr<const uint16_t*>(buffer.data() + offset));
offset += sizeof(value);
return true;
@@ -70,15 +75,58 @@ static bool writeUInt16(Vector<char>& vector, uint16_t value)
static const uint32_t woffSignature = 0x774f4646; /* 'wOFF' */
-bool isWOFF(SharedBuffer* buffer)
+bool isWOFF(SharedBuffer& buffer)
{
size_t offset = 0;
uint32_t signature;
- return readUInt32(buffer, offset, signature) && signature == woffSignature;
+ if (!readUInt32(buffer, offset, signature))
+ return false;
+
+#if USE(WOFF2)
+ return signature == woffSignature || signature == woff2::kWoff2Signature;
+#else
+ return signature == woffSignature;
+#endif
}
-bool convertWOFFToSfnt(SharedBuffer* woff, Vector<char>& sfnt)
+#if USE(WOFF2)
+class WOFF2VectorOut : public woff2::WOFF2Out {
+public:
+ WOFF2VectorOut(Vector<char>& vector)
+ : m_vector(vector)
+ { }
+
+ bool Write(const void* data, size_t n) override
+ {
+ if (!m_vector.tryReserveCapacity(m_vector.size() + n))
+ return false;
+ m_vector.append(static_cast<const char*>(data), n);
+ return true;
+ }
+
+ bool Write(const void* data, size_t offset, size_t n) override
+ {
+ if (!m_vector.tryReserveCapacity(offset + n))
+ return false;
+ if (offset + n > m_vector.size())
+ m_vector.resize(offset + n);
+ m_vector.remove(offset, n);
+ m_vector.insert(offset, static_cast<const char*>(data), n);
+ return true;
+ }
+
+ size_t Size() override
+ {
+ return m_vector.size();
+ }
+
+private:
+ Vector<char>& m_vector;
+};
+#endif
+
+bool convertWOFFToSfnt(SharedBuffer& woff, Vector<char>& sfnt)
{
ASSERT_ARG(sfnt, sfnt.isEmpty());
@@ -86,7 +134,26 @@ bool convertWOFFToSfnt(SharedBuffer* woff, Vector<char>& sfnt)
// Read the WOFF header.
uint32_t signature;
- if (!readUInt32(woff, offset, signature) || signature != woffSignature) {
+ if (!readUInt32(woff, offset, signature)) {
+ ASSERT_NOT_REACHED();
+ return false;
+ }
+
+#if USE(WOFF2)
+ if (signature == woff2::kWoff2Signature) {
+ const uint8_t* woffData = reinterpret_cast_ptr<const uint8_t*>(woff.data());
+ const size_t woffSize = woff.size();
+ const size_t sfntSize = woff2::ComputeWOFF2FinalSize(woffData, woffSize);
+
+ if (!sfnt.tryReserveCapacity(sfntSize))
+ return false;
+
+ WOFF2VectorOut out(sfnt);
+ return woff2::ConvertWOFF2ToTTF(woffData, woffSize, &out);
+ }
+#endif
+
+ if (signature != woffSignature) {
ASSERT_NOT_REACHED();
return false;
}
@@ -96,7 +163,7 @@ bool convertWOFFToSfnt(SharedBuffer* woff, Vector<char>& sfnt)
return false;
uint32_t length;
- if (!readUInt32(woff, offset, length) || length != woff->size())
+ if (!readUInt32(woff, offset, length) || length != woff.size())
return false;
uint16_t numTables;
@@ -114,7 +181,7 @@ bool convertWOFFToSfnt(SharedBuffer* woff, Vector<char>& sfnt)
if (!readUInt32(woff, offset, totalSfntSize))
return false;
- if (woff->size() - offset < sizeof(uint16_t) + sizeof(uint16_t) + sizeof(uint32_t) + sizeof(uint32_t) + sizeof(uint32_t) + sizeof(uint32_t) + sizeof(uint32_t))
+ if (woff.size() - offset < sizeof(uint16_t) + sizeof(uint16_t) + sizeof(uint32_t) + sizeof(uint32_t) + sizeof(uint32_t) + sizeof(uint32_t) + sizeof(uint32_t))
return false;
offset += sizeof(uint16_t); // majorVersion
@@ -126,7 +193,7 @@ bool convertWOFFToSfnt(SharedBuffer* woff, Vector<char>& sfnt)
offset += sizeof(uint32_t); // privLength
// Check if the WOFF can supply as many tables as it claims it has.
- if (woff->size() - offset < numTables * 5 * sizeof(uint32_t))
+ if (woff.size() - offset < numTables * 5 * sizeof(uint32_t))
return false;
// Write the sfnt offset subtable.
@@ -170,7 +237,7 @@ bool convertWOFFToSfnt(SharedBuffer* woff, Vector<char>& sfnt)
if (!readUInt32(woff, offset, tableCompLength))
return false;
- if (tableOffset > woff->size() || tableCompLength > woff->size() - tableOffset)
+ if (tableOffset > woff.size() || tableCompLength > woff.size() - tableOffset)
return false;
uint32_t tableOrigLength;
@@ -194,7 +261,7 @@ bool convertWOFFToSfnt(SharedBuffer* woff, Vector<char>& sfnt)
if (tableCompLength == tableOrigLength) {
// The table is not compressed.
- if (!sfnt.tryAppend(woff->data() + tableOffset, tableCompLength))
+ if (!sfnt.tryAppend(woff.data() + tableOffset, tableCompLength))
return false;
} else {
uLongf destLen = tableOrigLength;
@@ -202,7 +269,7 @@ bool convertWOFFToSfnt(SharedBuffer* woff, Vector<char>& sfnt)
return false;
Bytef* dest = reinterpret_cast<Bytef*>(sfnt.end());
sfnt.grow(sfnt.size() + tableOrigLength);
- if (uncompress(dest, &destLen, reinterpret_cast<const Bytef*>(woff->data() + tableOffset), tableCompLength) != Z_OK)
+ if (uncompress(dest, &destLen, reinterpret_cast<const Bytef*>(woff.data() + tableOffset), tableCompLength) != Z_OK)
return false;
if (destLen != tableOrigLength)
return false;
diff --git a/Source/WebCore/platform/graphics/WOFFFileFormat.h b/Source/WebCore/platform/graphics/WOFFFileFormat.h
index c6892fde1..44c38245d 100644
--- a/Source/WebCore/platform/graphics/WOFFFileFormat.h
+++ b/Source/WebCore/platform/graphics/WOFFFileFormat.h
@@ -33,11 +33,11 @@ namespace WebCore {
class SharedBuffer;
// Returns whether the buffer is a WOFF file.
-bool isWOFF(SharedBuffer* buffer);
+bool isWOFF(SharedBuffer&);
// Returns false if the WOFF file woff is invalid or could not be converted to sfnt (for example,
// if conversion ran out of memory). Otherwise returns true and writes the sfnt payload into sfnt.
-bool convertWOFFToSfnt(SharedBuffer* woff, Vector<char>& sfnt);
+bool convertWOFFToSfnt(SharedBuffer& woff, Vector<char>& sfnt);
} // namespace WebCore
diff --git a/Source/WebCore/platform/graphics/WidthCache.h b/Source/WebCore/platform/graphics/WidthCache.h
index 884d2a2e0..a6b11eace 100644
--- a/Source/WebCore/platform/graphics/WidthCache.h
+++ b/Source/WebCore/platform/graphics/WidthCache.h
@@ -26,12 +26,12 @@
#ifndef WidthCache_h
#define WidthCache_h
+#include "MemoryPressureHandler.h"
#include "TextRun.h"
#include <wtf/Forward.h>
#include <wtf/HashFunctions.h>
#include <wtf/HashSet.h>
-#include <wtf/RefPtr.h>
-#include <wtf/StringHasher.h>
+#include <wtf/Hasher.h>
namespace WebCore {
@@ -119,8 +119,25 @@ public:
{
}
+ float* add(StringView text, float entry)
+ {
+ if (MemoryPressureHandler::singleton().isUnderMemoryPressure())
+ return nullptr;
+
+ if (static_cast<unsigned>(text.length()) > SmallStringKey::capacity())
+ return nullptr;
+
+ if (m_countdown > 0) {
+ --m_countdown;
+ return nullptr;
+ }
+ return addSlowCase(text, entry);
+ }
+
float* add(const TextRun& run, float entry, bool hasKerningOrLigatures, bool hasWordSpacingOrLetterSpacing, GlyphOverflow* glyphOverflow)
{
+ if (MemoryPressureHandler::singleton().isUnderMemoryPressure())
+ return nullptr;
// The width cache is not really profitable unless we're doing expensive glyph transformations.
if (!hasKerningOrLigatures)
return 0;
@@ -141,7 +158,7 @@ public:
return 0;
}
- return addSlowCase(run, entry);
+ return addSlowCase(run.text(), entry);
}
void clear()
@@ -151,23 +168,24 @@ public:
}
private:
- float* addSlowCase(const TextRun& run, float entry)
+
+ float* addSlowCase(StringView text, float entry)
{
- int length = run.length();
+ int length = text.length();
bool isNewEntry;
- float *value;
+ float* value;
if (length == 1) {
- SingleCharMap::AddResult addResult = m_singleCharMap.add(run[0], entry);
+ SingleCharMap::AddResult addResult = m_singleCharMap.fastAdd(text[0], entry);
isNewEntry = addResult.isNewEntry;
value = &addResult.iterator->value;
} else {
SmallStringKey smallStringKey;
- if (run.is8Bit())
- smallStringKey = SmallStringKey(run.characters8(), length);
+ if (text.is8Bit())
+ smallStringKey = SmallStringKey(text.characters8(), length);
else
- smallStringKey = SmallStringKey(run.characters16(), length);
+ smallStringKey = SmallStringKey(text.characters16(), length);
- Map::AddResult addResult = m_map.add(smallStringKey, entry);
+ Map::AddResult addResult = m_map.fastAdd(smallStringKey, entry);
isNewEntry = addResult.isNewEntry;
value = &addResult.iterator->value;
}
diff --git a/Source/WebCore/platform/graphics/WidthIterator.cpp b/Source/WebCore/platform/graphics/WidthIterator.cpp
index 4c44b6e0f..3785e9567 100644
--- a/Source/WebCore/platform/graphics/WidthIterator.cpp
+++ b/Source/WebCore/platform/graphics/WidthIterator.cpp
@@ -23,9 +23,9 @@
#include "WidthIterator.h"
#include "Font.h"
+#include "FontCascade.h"
#include "GlyphBuffer.h"
#include "Latin1TextIterator.h"
-#include "SimpleFontData.h"
#include "SurrogatePairAwareTextIterator.h"
#include <wtf/MathExtras.h>
@@ -34,20 +34,17 @@ using namespace Unicode;
namespace WebCore {
-WidthIterator::WidthIterator(const Font* font, const TextRun& run, HashSet<const SimpleFontData*>* fallbackFonts, bool accountForGlyphBounds, bool forTextEmphasis)
+WidthIterator::WidthIterator(const FontCascade* font, const TextRun& run, HashSet<const Font*>* fallbackFonts, bool accountForGlyphBounds, bool forTextEmphasis)
: m_font(font)
, m_run(run)
, m_currentCharacter(0)
, m_runWidthSoFar(0)
- , m_isAfterExpansion(!run.allowsLeadingExpansion())
+ , m_isAfterExpansion((run.expansionBehavior() & LeadingExpansionMask) == ForbidLeadingExpansion)
, m_finalRoundingWidth(0)
- , m_typesettingFeatures(font->typesettingFeatures())
, m_fallbackFonts(fallbackFonts)
, m_accountForGlyphBounds(accountForGlyphBounds)
- , m_maxGlyphBoundingBoxY(std::numeric_limits<float>::min())
- , m_minGlyphBoundingBoxY(std::numeric_limits<float>::max())
- , m_firstGlyphOverflow(0)
- , m_lastGlyphOverflow(0)
+ , m_enableKerning(font->enableKerning())
+ , m_requiresShaping(font->requiresShaping())
, m_forTextEmphasis(forTextEmphasis)
{
// If the padding is non-zero, count the number of spaces in the run
@@ -56,33 +53,13 @@ WidthIterator::WidthIterator(const Font* font, const TextRun& run, HashSet<const
if (!m_expansion)
m_expansionPerOpportunity = 0;
else {
- bool isAfterExpansion = m_isAfterExpansion;
- unsigned expansionOpportunityCount = m_run.is8Bit() ? Font::expansionOpportunityCount(m_run.characters8(), m_run.length(), m_run.ltr() ? LTR : RTL, isAfterExpansion) : Font::expansionOpportunityCount(m_run.characters16(), m_run.length(), m_run.ltr() ? LTR : RTL, isAfterExpansion);
- if (isAfterExpansion && !m_run.allowsTrailingExpansion())
- expansionOpportunityCount--;
+ unsigned expansionOpportunityCount = FontCascade::expansionOpportunityCount(m_run.text(), m_run.ltr() ? LTR : RTL, run.expansionBehavior()).first;
if (!expansionOpportunityCount)
m_expansionPerOpportunity = 0;
else
m_expansionPerOpportunity = m_expansion / expansionOpportunityCount;
}
- // Character-index will end up the same or slightly shorter than m_run, so if we reserve that much it will never need to resize.
- m_characterIndexOfGlyph.reserveInitialCapacity(m_run.length());
-}
-
-GlyphData WidthIterator::glyphDataForCharacter(UChar32 character, bool mirror, int currentCharacter, unsigned& advanceLength)
-{
- ASSERT(m_font);
-
-#if ENABLE(SVG_FONTS)
- if (TextRun::RenderingContext* renderingContext = m_run.renderingContext())
- return renderingContext->glyphDataForCharacter(*m_font, *this, character, mirror, currentCharacter, advanceLength);
-#else
- UNUSED_PARAM(currentCharacter);
- UNUSED_PARAM(advanceLength);
-#endif
-
- return m_font->glyphDataForCharacter(character, mirror);
}
struct OriginalAdvancesForCharacterTreatedAsSpace {
@@ -99,39 +76,43 @@ public:
float advanceAtCharacter;
};
-typedef Vector<std::pair<int, OriginalAdvancesForCharacterTreatedAsSpace>, 64> CharactersTreatedAsSpace;
+static inline bool isSoftBankEmoji(UChar32 codepoint)
+{
+ return codepoint >= 0xE001 && codepoint <= 0xE537;
+}
+
+inline auto WidthIterator::shouldApplyFontTransforms(const GlyphBuffer* glyphBuffer, unsigned lastGlyphCount, UChar32 previousCharacter) const -> TransformsType
+{
+ if (glyphBuffer && glyphBuffer->size() == (lastGlyphCount + 1) && isSoftBankEmoji(previousCharacter))
+ return TransformsType::Forced;
+ if (m_run.length() <= 1 || !(m_enableKerning || m_requiresShaping))
+ return TransformsType::None;
+ return TransformsType::NotForced;
+}
-static inline float applyFontTransforms(GlyphBuffer* glyphBuffer, bool ltr, int& lastGlyphCount, const SimpleFontData* fontData, WidthIterator& iterator, TypesettingFeatures typesettingFeatures, CharactersTreatedAsSpace& charactersTreatedAsSpace)
+inline float WidthIterator::applyFontTransforms(GlyphBuffer* glyphBuffer, bool ltr, unsigned& lastGlyphCount, const Font* font, UChar32 previousCharacter, bool force, CharactersTreatedAsSpace& charactersTreatedAsSpace)
{
- ASSERT(typesettingFeatures & (Kerning | Ligatures));
+ ASSERT_UNUSED(previousCharacter, shouldApplyFontTransforms(glyphBuffer, lastGlyphCount, previousCharacter) != WidthIterator::TransformsType::None);
if (!glyphBuffer)
return 0;
- int glyphBufferSize = glyphBuffer->size();
- if (glyphBuffer->size() <= lastGlyphCount + 1)
+ unsigned glyphBufferSize = glyphBuffer->size();
+ if (!force && glyphBufferSize <= lastGlyphCount + 1) {
+ lastGlyphCount = glyphBufferSize;
return 0;
+ }
GlyphBufferAdvance* advances = glyphBuffer->advances(0);
float widthDifference = 0;
- for (int i = lastGlyphCount; i < glyphBufferSize; ++i)
+ for (unsigned i = lastGlyphCount; i < glyphBufferSize; ++i)
widthDifference -= advances[i].width();
+ ASSERT(lastGlyphCount <= glyphBufferSize);
if (!ltr)
glyphBuffer->reverse(lastGlyphCount, glyphBufferSize - lastGlyphCount);
-#if !ENABLE(SVG_FONTS)
- UNUSED_PARAM(iterator);
-#else
- // We need to handle transforms on SVG fonts internally, since they are rendered internally.
- if (fontData->isSVGFont()) {
- ASSERT(iterator.run().renderingContext());
- // SVG font ligatures are handled during glyph selection, only kerning remaining.
- if (typesettingFeatures & Kerning)
- iterator.run().renderingContext()->applySVGKerning(fontData, iterator, glyphBuffer, lastGlyphCount);
- } else
-#endif
- fontData->applyTransforms(glyphBuffer->glyphs(lastGlyphCount), advances + lastGlyphCount, glyphBufferSize - lastGlyphCount, typesettingFeatures);
+ font->applyTransforms(glyphBuffer->glyphs(lastGlyphCount), advances + lastGlyphCount, glyphBufferSize - lastGlyphCount, m_enableKerning, m_requiresShaping);
if (!ltr)
glyphBuffer->reverse(lastGlyphCount, glyphBufferSize - lastGlyphCount);
@@ -145,138 +126,202 @@ static inline float applyFontTransforms(GlyphBuffer* glyphBuffer, bool ltr, int&
}
charactersTreatedAsSpace.clear();
- for (int i = lastGlyphCount; i < glyphBufferSize; ++i)
+ for (unsigned i = lastGlyphCount; i < glyphBufferSize; ++i)
widthDifference += advances[i].width();
lastGlyphCount = glyphBufferSize;
return widthDifference;
}
+static inline std::pair<bool, bool> expansionLocation(bool ideograph, bool treatAsSpace, bool ltr, bool isAfterExpansion, bool forbidLeadingExpansion, bool forbidTrailingExpansion, bool forceLeadingExpansion, bool forceTrailingExpansion)
+{
+ bool expandLeft = ideograph;
+ bool expandRight = ideograph;
+ if (treatAsSpace) {
+ if (ltr)
+ expandRight = true;
+ else
+ expandLeft = true;
+ }
+ if (isAfterExpansion) {
+ if (ltr)
+ expandLeft = false;
+ else
+ expandRight = false;
+ }
+ ASSERT(!forbidLeadingExpansion || !forceLeadingExpansion);
+ ASSERT(!forbidTrailingExpansion || !forceTrailingExpansion);
+ if (forbidLeadingExpansion)
+ expandLeft = false;
+ if (forbidTrailingExpansion)
+ expandRight = false;
+ if (forceLeadingExpansion)
+ expandLeft = true;
+ if (forceTrailingExpansion)
+ expandRight = true;
+ return std::make_pair(expandLeft, expandRight);
+}
+
+static bool characterMustDrawSomething(UChar32 character)
+{
+ // u_hasBinaryProperty(character, UCHAR_EMOJI) would be better to use, but many OSes which
+ // WebKit runs on only have ICU version 55.1 or earlier. UCHAR_EMOJI was added in ICU 57.
+ return character >= 0x1F900 && character <= 0x1F9FF;
+}
+
template <typename TextIterator>
inline unsigned WidthIterator::advanceInternal(TextIterator& textIterator, GlyphBuffer* glyphBuffer)
{
+ // The core logic here needs to match SimpleLineLayout::widthForSimpleText()
bool rtl = m_run.rtl();
bool hasExtraSpacing = (m_font->letterSpacing() || m_font->wordSpacing() || m_expansion) && !m_run.spacingDisabled();
+ bool runForcesLeadingExpansion = (m_run.expansionBehavior() & LeadingExpansionMask) == ForceLeadingExpansion;
+ bool runForcesTrailingExpansion = (m_run.expansionBehavior() & TrailingExpansionMask) == ForceTrailingExpansion;
+ bool runForbidsLeadingExpansion = (m_run.expansionBehavior() & LeadingExpansionMask) == ForbidLeadingExpansion;
+ bool runForbidsTrailingExpansion = (m_run.expansionBehavior() & TrailingExpansionMask) == ForbidTrailingExpansion;
float widthSinceLastRounding = m_runWidthSoFar;
+ float leftoverJustificationWidth = 0;
m_runWidthSoFar = floorf(m_runWidthSoFar);
widthSinceLastRounding -= m_runWidthSoFar;
float lastRoundingWidth = m_finalRoundingWidth;
FloatRect bounds;
- const SimpleFontData* primaryFont = m_font->primaryFont();
- const SimpleFontData* lastFontData = primaryFont;
- int lastGlyphCount = glyphBuffer ? glyphBuffer->size() : 0;
+ const Font& primaryFont = m_font->primaryFont();
+ const Font* lastFontData = &primaryFont;
+ unsigned lastGlyphCount = glyphBuffer ? glyphBuffer->size() : 0;
UChar32 character = 0;
+ UChar32 previousCharacter = 0;
unsigned clusterLength = 0;
CharactersTreatedAsSpace charactersTreatedAsSpace;
+ String normalizedSpacesStringCache;
+ // We are iterating in string order, not glyph order. Compare this to ComplexTextController::adjustGlyphsAndAdvances()
while (textIterator.consume(character, clusterLength)) {
unsigned advanceLength = clusterLength;
- int currentCharacterIndex = textIterator.currentCharacter();
- const GlyphData& glyphData = glyphDataForCharacter(character, rtl, currentCharacterIndex, advanceLength);
+ int currentCharacter = textIterator.currentIndex();
+ const GlyphData& glyphData = m_font->glyphDataForCharacter(character, rtl);
Glyph glyph = glyphData.glyph;
- const SimpleFontData* fontData = glyphData.fontData;
-
- ASSERT(fontData);
+ if (!glyph && !characterMustDrawSomething(character)) {
+ textIterator.advance(advanceLength);
+ continue;
+ }
+ const Font* font = glyphData.font ? glyphData.font : &m_font->primaryFont();
+ ASSERT(font);
// Now that we have a glyph and font data, get its width.
float width;
if (character == '\t' && m_run.allowTabs())
- width = m_font->tabWidth(*fontData, m_run.tabSize(), m_run.xPos() + m_runWidthSoFar + widthSinceLastRounding);
+ width = m_font->tabWidth(*font, m_run.tabSize(), m_run.xPos() + m_runWidthSoFar + widthSinceLastRounding);
else {
- width = fontData->widthForGlyph(glyph);
+ width = font->widthForGlyph(glyph);
-#if ENABLE(SVG)
// SVG uses horizontalGlyphStretch(), when textLength is used to stretch/squeeze text.
width *= m_run.horizontalGlyphStretch();
-#endif
-
- // We special case spaces in two ways when applying word rounding.
- // First, we round spaces to an adjusted width in all fonts.
- // Second, in fixed-pitch fonts we ensure that all characters that
- // match the width of the space character have the same width as the space character.
- if (m_run.applyWordRounding() && width == fontData->spaceWidth() && (fontData->pitch() == FixedPitch || glyph == fontData->spaceGlyph()))
- width = fontData->adjustedSpaceWidth();
}
- if (fontData != lastFontData && width) {
- if (shouldApplyFontTransforms()) {
- m_runWidthSoFar += applyFontTransforms(glyphBuffer, m_run.ltr(), lastGlyphCount, lastFontData, *this, m_typesettingFeatures, charactersTreatedAsSpace);
- lastGlyphCount = glyphBuffer->size(); // applyFontTransforms doesn't update when there had been only one glyph.
+ if (font != lastFontData && width) {
+ auto transformsType = shouldApplyFontTransforms(glyphBuffer, lastGlyphCount, previousCharacter);
+ if (transformsType != TransformsType::None) {
+ m_runWidthSoFar += applyFontTransforms(glyphBuffer, m_run.ltr(), lastGlyphCount, lastFontData, previousCharacter, transformsType == TransformsType::Forced, charactersTreatedAsSpace);
+ if (glyphBuffer)
+ glyphBuffer->shrink(lastGlyphCount);
}
- lastFontData = fontData;
- if (m_fallbackFonts && fontData != primaryFont) {
+ lastFontData = font;
+ if (m_fallbackFonts && font != &primaryFont) {
// FIXME: This does a little extra work that could be avoided if
// glyphDataForCharacter() returned whether it chose to use a small caps font.
if (!m_font->isSmallCaps() || character == u_toupper(character))
- m_fallbackFonts->add(fontData);
+ m_fallbackFonts->add(font);
else {
const GlyphData& uppercaseGlyphData = m_font->glyphDataForCharacter(u_toupper(character), rtl);
- if (uppercaseGlyphData.fontData != primaryFont)
- m_fallbackFonts->add(uppercaseGlyphData.fontData);
+ if (uppercaseGlyphData.font != &primaryFont)
+ m_fallbackFonts->add(uppercaseGlyphData.font);
}
}
}
if (hasExtraSpacing) {
// Account for letter-spacing.
- if (width && m_font->letterSpacing())
+ if (width) {
width += m_font->letterSpacing();
+ width += leftoverJustificationWidth;
+ leftoverJustificationWidth = 0;
+ }
- static bool expandAroundIdeographs = Font::canExpandAroundIdeographsInComplexText();
- bool treatAsSpace = Font::treatAsSpace(character);
- if (treatAsSpace || (expandAroundIdeographs && Font::isCJKIdeographOrSymbol(character))) {
+ static bool expandAroundIdeographs = FontCascade::canExpandAroundIdeographsInComplexText();
+ bool treatAsSpace = FontCascade::treatAsSpace(character);
+ bool currentIsLastCharacter = currentCharacter + advanceLength == static_cast<size_t>(m_run.length());
+ bool forceLeadingExpansion = false; // On the left, regardless of m_run.ltr()
+ bool forceTrailingExpansion = false; // On the right, regardless of m_run.ltr()
+ bool forbidLeadingExpansion = false;
+ bool forbidTrailingExpansion = false;
+ if (runForcesLeadingExpansion)
+ forceLeadingExpansion = m_run.ltr() ? !currentCharacter : currentIsLastCharacter;
+ if (runForcesTrailingExpansion)
+ forceTrailingExpansion = m_run.ltr() ? currentIsLastCharacter : !currentCharacter;
+ if (runForbidsLeadingExpansion)
+ forbidLeadingExpansion = m_run.ltr() ? !currentCharacter : currentIsLastCharacter;
+ if (runForbidsTrailingExpansion)
+ forbidTrailingExpansion = m_run.ltr() ? currentIsLastCharacter : !currentCharacter;
+ bool ideograph = (expandAroundIdeographs && FontCascade::isCJKIdeographOrSymbol(character));
+ if (treatAsSpace || ideograph || forceLeadingExpansion || forceTrailingExpansion) {
// Distribute the run's total expansion evenly over all expansion opportunities in the run.
if (m_expansion) {
- float previousExpansion = m_expansion;
- if (!treatAsSpace && !m_isAfterExpansion) {
- // Take the expansion opportunity before this ideograph.
- m_expansion -= m_expansionPerOpportunity;
- float expansionAtThisOpportunity = !m_run.applyWordRounding() ? m_expansionPerOpportunity : roundf(previousExpansion) - roundf(m_expansion);
- m_runWidthSoFar += expansionAtThisOpportunity;
- if (glyphBuffer) {
- if (glyphBuffer->isEmpty()) {
- if (m_forTextEmphasis)
- glyphBuffer->add(fontData->zeroWidthSpaceGlyph(), fontData, m_expansionPerOpportunity);
- else
- glyphBuffer->add(fontData->spaceGlyph(), fontData, expansionAtThisOpportunity);
- m_characterIndexOfGlyph.append(currentCharacterIndex);
- } else
- glyphBuffer->expandLastAdvance(expansionAtThisOpportunity);
+ bool expandLeft, expandRight;
+ std::tie(expandLeft, expandRight) = expansionLocation(ideograph, treatAsSpace, m_run.ltr(), m_isAfterExpansion, forbidLeadingExpansion, forbidTrailingExpansion, forceLeadingExpansion, forceTrailingExpansion);
+ if (expandLeft) {
+ if (m_run.ltr()) {
+ // Increase previous width
+ m_expansion -= m_expansionPerOpportunity;
+ m_runWidthSoFar += m_expansionPerOpportunity;
+ if (glyphBuffer) {
+ if (glyphBuffer->isEmpty()) {
+ if (m_forTextEmphasis)
+ glyphBuffer->add(font->zeroWidthSpaceGlyph(), font, m_expansionPerOpportunity, currentCharacter);
+ else
+ glyphBuffer->add(font->spaceGlyph(), font, m_expansionPerOpportunity, currentCharacter);
+ } else
+ glyphBuffer->expandLastAdvance(m_expansionPerOpportunity);
+ }
+ } else {
+ // Increase next width
+ leftoverJustificationWidth += m_expansionPerOpportunity;
+ m_isAfterExpansion = true;
}
- previousExpansion = m_expansion;
}
- if (m_run.allowsTrailingExpansion() || (m_run.ltr() && textIterator.currentCharacter() + advanceLength < static_cast<size_t>(m_run.length()))
- || (m_run.rtl() && textIterator.currentCharacter())) {
+ if (expandRight) {
m_expansion -= m_expansionPerOpportunity;
- width += !m_run.applyWordRounding() ? m_expansionPerOpportunity : roundf(previousExpansion) - roundf(m_expansion);
- m_isAfterExpansion = true;
+ width += m_expansionPerOpportunity;
+ if (m_run.ltr())
+ m_isAfterExpansion = true;
}
} else
m_isAfterExpansion = false;
// Account for word spacing.
// We apply additional space between "words" by adding width to the space character.
- if (treatAsSpace && (character != '\t' || !m_run.allowTabs()) && (textIterator.currentCharacter() || character == noBreakSpace) && m_font->wordSpacing())
+ if (treatAsSpace && (character != '\t' || !m_run.allowTabs()) && (currentCharacter || character == noBreakSpace) && m_font->wordSpacing())
width += m_font->wordSpacing();
} else
m_isAfterExpansion = false;
}
- if (shouldApplyFontTransforms() && glyphBuffer && Font::treatAsSpace(character))
+ auto transformsType = shouldApplyFontTransforms(glyphBuffer, lastGlyphCount, previousCharacter);
+ if (transformsType != TransformsType::None && glyphBuffer && FontCascade::treatAsSpace(character)) {
charactersTreatedAsSpace.append(std::make_pair(glyphBuffer->size(),
OriginalAdvancesForCharacterTreatedAsSpace(character == ' ', glyphBuffer->size() ? glyphBuffer->advanceAt(glyphBuffer->size() - 1).width() : 0, width)));
+ }
if (m_accountForGlyphBounds) {
- bounds = fontData->boundsForGlyph(glyph);
- if (!textIterator.currentCharacter())
+ bounds = font->boundsForGlyph(glyph);
+ if (!currentCharacter)
m_firstGlyphOverflow = std::max<float>(0, -bounds.x());
}
- if (m_forTextEmphasis && !Font::canReceiveTextEmphasis(character))
+ if (m_forTextEmphasis && !FontCascade::canReceiveTextEmphasis(character))
glyph = 0;
// Advance past the character we just dealt with.
@@ -284,36 +329,10 @@ inline unsigned WidthIterator::advanceInternal(TextIterator& textIterator, Glyph
float oldWidth = width;
- // Force characters that are used to determine word boundaries for the rounding hack
- // to be integer width, so following words will start on an integer boundary.
- if (m_run.applyWordRounding() && Font::isRoundingHackCharacter(character)) {
- width = ceilf(width);
-
- // Since widthSinceLastRounding can lose precision if we include measurements for
- // preceding whitespace, we bypass it here.
- m_runWidthSoFar += width;
-
- // Since this is a rounding hack character, we should have reset this sum on the previous
- // iteration.
- ASSERT(!widthSinceLastRounding);
- } else {
- // Check to see if the next character is a "rounding hack character", if so, adjust
- // width so that the total run width will be on an integer boundary.
- if ((m_run.applyWordRounding() && textIterator.currentCharacter() < m_run.length() && Font::isRoundingHackCharacter(*(textIterator.characters())))
- || (m_run.applyRunRounding() && textIterator.currentCharacter() >= m_run.length())) {
- float totalWidth = widthSinceLastRounding + width;
- widthSinceLastRounding = ceilf(totalWidth);
- width += widthSinceLastRounding - totalWidth;
- m_runWidthSoFar += widthSinceLastRounding;
- widthSinceLastRounding = 0;
- } else
- widthSinceLastRounding += width;
- }
+ widthSinceLastRounding += width;
- if (glyphBuffer) {
- glyphBuffer->add(glyph, fontData, (rtl ? oldWidth + lastRoundingWidth : width));
- m_characterIndexOfGlyph.append(currentCharacterIndex);
- }
+ if (glyphBuffer)
+ glyphBuffer->add(glyph, font, (rtl ? oldWidth + lastRoundingWidth : width), currentCharacter);
lastRoundingWidth = width - oldWidth;
@@ -322,26 +341,38 @@ inline unsigned WidthIterator::advanceInternal(TextIterator& textIterator, Glyph
m_minGlyphBoundingBoxY = std::min(m_minGlyphBoundingBoxY, bounds.y());
m_lastGlyphOverflow = std::max<float>(0, bounds.maxX() - width);
}
+ previousCharacter = character;
}
- if (shouldApplyFontTransforms())
- m_runWidthSoFar += applyFontTransforms(glyphBuffer, m_run.ltr(), lastGlyphCount, lastFontData, *this, m_typesettingFeatures, charactersTreatedAsSpace);
+ if (leftoverJustificationWidth) {
+ if (m_forTextEmphasis)
+ glyphBuffer->add(lastFontData->zeroWidthSpaceGlyph(), lastFontData, leftoverJustificationWidth, m_run.length() - 1);
+ else
+ glyphBuffer->add(lastFontData->spaceGlyph(), lastFontData, leftoverJustificationWidth, m_run.length() - 1);
+ }
- unsigned consumedCharacters = textIterator.currentCharacter() - m_currentCharacter;
- m_currentCharacter = textIterator.currentCharacter();
+ auto transformsType = shouldApplyFontTransforms(glyphBuffer, lastGlyphCount, previousCharacter);
+ if (transformsType != TransformsType::None) {
+ m_runWidthSoFar += applyFontTransforms(glyphBuffer, m_run.ltr(), lastGlyphCount, lastFontData, previousCharacter, transformsType == TransformsType::Forced, charactersTreatedAsSpace);
+ if (glyphBuffer)
+ glyphBuffer->shrink(lastGlyphCount);
+ }
+
+ unsigned consumedCharacters = textIterator.currentIndex() - m_currentCharacter;
+ m_currentCharacter = textIterator.currentIndex();
m_runWidthSoFar += widthSinceLastRounding;
m_finalRoundingWidth = lastRoundingWidth;
return consumedCharacters;
}
-unsigned WidthIterator::advance(int offset, GlyphBuffer* glyphBuffer)
+unsigned WidthIterator::advance(unsigned offset, GlyphBuffer* glyphBuffer)
{
- int length = m_run.length();
+ unsigned length = m_run.length();
if (offset > length)
offset = length;
- if (m_currentCharacter >= static_cast<unsigned>(offset))
+ if (m_currentCharacter >= offset)
return 0;
if (m_run.is8Bit()) {
@@ -353,4 +384,15 @@ unsigned WidthIterator::advance(int offset, GlyphBuffer* glyphBuffer)
return advanceInternal(textIterator, glyphBuffer);
}
+bool WidthIterator::advanceOneCharacter(float& width, GlyphBuffer& glyphBuffer)
+{
+ unsigned oldSize = glyphBuffer.size();
+ advance(m_currentCharacter + 1, &glyphBuffer);
+ float w = 0;
+ for (unsigned i = oldSize; i < glyphBuffer.size(); ++i)
+ w += glyphBuffer.advanceAt(i).width();
+ width = w;
+ return glyphBuffer.size() > oldSize;
+}
+
}
diff --git a/Source/WebCore/platform/graphics/WidthIterator.h b/Source/WebCore/platform/graphics/WidthIterator.h
index 1344b9ae6..c45fdb8c3 100644
--- a/Source/WebCore/platform/graphics/WidthIterator.h
+++ b/Source/WebCore/platform/graphics/WidthIterator.h
@@ -22,27 +22,28 @@
#ifndef WidthIterator_h
#define WidthIterator_h
-#include "Font.h"
-#include "SVGGlyph.h"
-#include "TextRun.h"
+#include <unicode/umachine.h>
#include <wtf/HashSet.h>
#include <wtf/Vector.h>
-#include <wtf/unicode/Unicode.h>
namespace WebCore {
-class Font;
+class FontCascade;
class GlyphBuffer;
-class SimpleFontData;
+class Font;
class TextRun;
struct GlyphData;
+struct OriginalAdvancesForCharacterTreatedAsSpace;
+
+typedef Vector<std::pair<int, OriginalAdvancesForCharacterTreatedAsSpace>, 64> CharactersTreatedAsSpace;
struct WidthIterator {
WTF_MAKE_FAST_ALLOCATED;
public:
- WidthIterator(const Font*, const TextRun&, HashSet<const SimpleFontData*>* fallbackFonts = 0, bool accountForGlyphBounds = false, bool forTextEmphasis = false);
+ WidthIterator(const FontCascade*, const TextRun&, HashSet<const Font*>* fallbackFonts = 0, bool accountForGlyphBounds = false, bool forTextEmphasis = false);
- unsigned advance(int to, GlyphBuffer*);
+ unsigned advance(unsigned to, GlyphBuffer*);
+ bool advanceOneCharacter(float& width, GlyphBuffer&);
float maxGlyphBoundingBoxY() const { ASSERT(m_accountForGlyphBounds); return m_maxGlyphBoundingBoxY; }
float minGlyphBoundingBoxY() const { ASSERT(m_accountForGlyphBounds); return m_minGlyphBoundingBoxY; }
@@ -52,25 +53,7 @@ public:
const TextRun& run() const { return m_run; }
float runWidthSoFar() const { return m_runWidthSoFar; }
-#if ENABLE(SVG_FONTS)
- String lastGlyphName() const { return m_lastGlyphName; }
- void setLastGlyphName(const String& name) { m_lastGlyphName = name; }
- Vector<SVGGlyph::ArabicForm>& arabicForms() { return m_arabicForms; }
-#endif
-
- static bool supportsTypesettingFeatures(const Font& font)
- {
-#if PLATFORM(IOS) || (PLATFORM(MAC) && __MAC_OS_X_VERSION_MIN_REQUIRED > 1080)
- if (!font.isPrinterFont())
- return !font.typesettingFeatures();
-
- return !(font.typesettingFeatures() & ~(Kerning | Ligatures));
-#else
- return !font.typesettingFeatures();
-#endif
- }
-
- const Font* m_font;
+ const FontCascade* m_font;
const TextRun& m_run;
@@ -80,29 +63,25 @@ public:
float m_expansionPerOpportunity;
bool m_isAfterExpansion;
float m_finalRoundingWidth;
- // An inline capacity of 10 catches around 2/3 of the cases. To catch 90% we would need 32.
- Vector<int, 10> m_characterIndexOfGlyph;
-
-#if ENABLE(SVG_FONTS)
- String m_lastGlyphName;
- Vector<SVGGlyph::ArabicForm> m_arabicForms;
-#endif
private:
- GlyphData glyphDataForCharacter(UChar32, bool mirror, int currentCharacter, unsigned& advanceLength);
+ GlyphData glyphDataForCharacter(UChar32, bool mirror);
template <typename TextIterator>
inline unsigned advanceInternal(TextIterator&, GlyphBuffer*);
- bool shouldApplyFontTransforms() const { return m_run.length() > 1 && (m_typesettingFeatures & (Kerning | Ligatures)); }
-
- TypesettingFeatures m_typesettingFeatures;
- HashSet<const SimpleFontData*>* m_fallbackFonts;
- bool m_accountForGlyphBounds;
- float m_maxGlyphBoundingBoxY;
- float m_minGlyphBoundingBoxY;
- float m_firstGlyphOverflow;
- float m_lastGlyphOverflow;
- bool m_forTextEmphasis;
+ enum class TransformsType { None, Forced, NotForced };
+ TransformsType shouldApplyFontTransforms(const GlyphBuffer*, unsigned lastGlyphCount, UChar32 previousCharacter) const;
+ float applyFontTransforms(GlyphBuffer*, bool ltr, unsigned& lastGlyphCount, const Font*, UChar32 previousCharacter, bool force, CharactersTreatedAsSpace&);
+
+ HashSet<const Font*>* m_fallbackFonts { nullptr };
+ bool m_accountForGlyphBounds { false };
+ bool m_enableKerning { false };
+ bool m_requiresShaping { false };
+ bool m_forTextEmphasis { false };
+ float m_maxGlyphBoundingBoxY { std::numeric_limits<float>::min() };
+ float m_minGlyphBoundingBoxY { std::numeric_limits<float>::max() };
+ float m_firstGlyphOverflow { 0 };
+ float m_lastGlyphOverflow { 0 };
};
}
diff --git a/Source/WebCore/platform/graphics/WindRule.h b/Source/WebCore/platform/graphics/WindRule.h
index 8eb77495a..f50834f5f 100644
--- a/Source/WebCore/platform/graphics/WindRule.h
+++ b/Source/WebCore/platform/graphics/WindRule.h
@@ -12,10 +12,10 @@
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
- * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
diff --git a/Source/WebCore/platform/graphics/cairo/BackingStoreBackendCairo.h b/Source/WebCore/platform/graphics/cairo/BackingStoreBackendCairo.h
new file mode 100644
index 000000000..63828c761
--- /dev/null
+++ b/Source/WebCore/platform/graphics/cairo/BackingStoreBackendCairo.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2011,2014 Igalia S.L.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef BackingStoreBackendCairo_h
+#define BackingStoreBackendCairo_h
+
+#if USE(CAIRO)
+
+#include "IntRect.h"
+#include "RefPtrCairo.h"
+#include <wtf/FastMalloc.h>
+#include <wtf/Noncopyable.h>
+
+typedef struct _cairo_surface cairo_surface_t;
+
+namespace WebCore {
+
+class BackingStoreBackendCairo {
+ WTF_MAKE_NONCOPYABLE(BackingStoreBackendCairo);
+ WTF_MAKE_FAST_ALLOCATED;
+public:
+ virtual ~BackingStoreBackendCairo() { }
+
+ cairo_surface_t* surface() const { return m_surface.get(); }
+ const IntSize& size() const { return m_size; }
+
+ virtual void scroll(const IntRect& scrollRect, const IntSize& scrollOffset) = 0;
+
+protected:
+ BackingStoreBackendCairo(const IntSize& size)
+ : m_size(size)
+ {
+ }
+
+ RefPtr<cairo_surface_t> m_surface;
+ IntSize m_size;
+};
+
+
+} // namespace WebCore
+
+#endif // USE(CAIRO)
+
+#endif // BackingStoreBackendCairo_h
diff --git a/Source/WebCore/platform/graphics/cairo/BackingStoreBackendCairoImpl.cpp b/Source/WebCore/platform/graphics/cairo/BackingStoreBackendCairoImpl.cpp
new file mode 100644
index 000000000..40d06dbf8
--- /dev/null
+++ b/Source/WebCore/platform/graphics/cairo/BackingStoreBackendCairoImpl.cpp
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2011,2014 Igalia S.L.
+ * Copyright (C) 2011 Samsung Electronics
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "config.h"
+#include "BackingStoreBackendCairoImpl.h"
+
+#if USE(CAIRO)
+
+#include "CairoUtilities.h"
+
+namespace WebCore {
+
+BackingStoreBackendCairoImpl::BackingStoreBackendCairoImpl(cairo_surface_t* surface, const IntSize& size)
+ : BackingStoreBackendCairo(size)
+{
+ m_surface = surface;
+
+ // We keep two copies of the surface here, which will double the memory usage, but increase
+ // scrolling performance since we do not have to keep reallocating a memory region during
+ // quick scrolling requests.
+ double xScale, yScale;
+ cairoSurfaceGetDeviceScale(m_surface.get(), xScale, yScale);
+ IntSize scaledSize = size;
+ scaledSize.scale(xScale, yScale);
+ m_scrollSurface = adoptRef(cairo_surface_create_similar(surface, CAIRO_CONTENT_COLOR_ALPHA, scaledSize.width(), scaledSize.height()));
+}
+
+BackingStoreBackendCairoImpl::~BackingStoreBackendCairoImpl()
+{
+}
+
+void BackingStoreBackendCairoImpl::scroll(const IntRect& scrollRect, const IntSize& scrollOffset)
+{
+ IntRect targetRect = scrollRect;
+ targetRect.move(scrollOffset);
+ targetRect.shiftMaxXEdgeTo(targetRect.maxX() - scrollOffset.width());
+ targetRect.shiftMaxYEdgeTo(targetRect.maxY() - scrollOffset.height());
+ if (targetRect.isEmpty())
+ return;
+
+ copyRectFromOneSurfaceToAnother(m_surface.get(), m_scrollSurface.get(), scrollOffset, targetRect);
+ copyRectFromOneSurfaceToAnother(m_scrollSurface.get(), m_surface.get(), IntSize(), targetRect);
+}
+
+} // namespace WebCore
+
+#endif // USE(CAIRO)
diff --git a/Source/WebCore/platform/graphics/cairo/BackingStoreBackendCairoImpl.h b/Source/WebCore/platform/graphics/cairo/BackingStoreBackendCairoImpl.h
new file mode 100644
index 000000000..b1a0149bb
--- /dev/null
+++ b/Source/WebCore/platform/graphics/cairo/BackingStoreBackendCairoImpl.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2013,2014 Igalia S.L.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef BackingStoreBackendCairoImpl_h
+#define BackingStoreBackendCairoImpl_h
+
+#include "BackingStoreBackendCairo.h"
+
+#if USE(CAIRO)
+
+namespace WebCore {
+
+class BackingStoreBackendCairoImpl final : public BackingStoreBackendCairo {
+public:
+ BackingStoreBackendCairoImpl(cairo_surface_t*, const IntSize&);
+ virtual ~BackingStoreBackendCairoImpl();
+
+ void scroll(const IntRect&, const IntSize&) override;
+
+private:
+ RefPtr<cairo_surface_t> m_scrollSurface;
+};
+
+} // namespace WebCore
+
+#endif // USE(CAIRO)
+
+#endif // BackingStoreBackendCairoImpl_h
diff --git a/Source/WebCore/platform/graphics/cairo/BackingStoreBackendCairoX11.cpp b/Source/WebCore/platform/graphics/cairo/BackingStoreBackendCairoX11.cpp
new file mode 100644
index 000000000..437dafc82
--- /dev/null
+++ b/Source/WebCore/platform/graphics/cairo/BackingStoreBackendCairoX11.cpp
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2011,2014 Igalia S.L.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "config.h"
+#include "BackingStoreBackendCairoX11.h"
+
+#if USE(CAIRO) && PLATFORM(X11)
+
+#include "CairoUtilities.h"
+#include <cairo-xlib.h>
+
+namespace WebCore {
+
+BackingStoreBackendCairoX11::BackingStoreBackendCairoX11(unsigned long rootWindowID, Visual* visual, int depth, const IntSize& size, float deviceScaleFactor)
+ : BackingStoreBackendCairo(size)
+{
+ IntSize scaledSize = size;
+ scaledSize.scale(deviceScaleFactor);
+
+ auto* display = downcast<PlatformDisplayX11>(PlatformDisplay::sharedDisplay()).native();
+ m_pixmap = XCreatePixmap(display, rootWindowID, scaledSize.width(), scaledSize.height(), depth);
+ m_gc.reset(XCreateGC(display, m_pixmap.get(), 0, nullptr));
+
+ m_surface = adoptRef(cairo_xlib_surface_create(display, m_pixmap.get(), visual, scaledSize.width(), scaledSize.height()));
+ cairoSurfaceSetDeviceScale(m_surface.get(), deviceScaleFactor, deviceScaleFactor);
+}
+
+BackingStoreBackendCairoX11::~BackingStoreBackendCairoX11()
+{
+ // The pixmap needs to exist when the surface is destroyed, so begin by clearing it.
+ m_surface = nullptr;
+}
+
+void BackingStoreBackendCairoX11::scroll(const IntRect& scrollRect, const IntSize& scrollOffset)
+{
+ IntRect targetRect = scrollRect;
+ targetRect.move(scrollOffset);
+ targetRect.intersect(scrollRect);
+ if (targetRect.isEmpty())
+ return;
+
+ double xScale, yScale;
+ cairoSurfaceGetDeviceScale(m_surface.get(), xScale, yScale);
+ ASSERT(xScale == yScale);
+
+ IntSize scaledScrollOffset = scrollOffset;
+ targetRect.scale(xScale);
+ scaledScrollOffset.scale(xScale, yScale);
+
+ cairo_surface_flush(m_surface.get());
+ XCopyArea(downcast<PlatformDisplayX11>(PlatformDisplay::sharedDisplay()).native(), m_pixmap.get(), m_pixmap.get(), m_gc.get(),
+ targetRect.x() - scaledScrollOffset.width(), targetRect.y() - scaledScrollOffset.height(),
+ targetRect.width(), targetRect.height(), targetRect.x(), targetRect.y());
+ cairo_surface_mark_dirty_rectangle(m_surface.get(), targetRect.x(), targetRect.y(), targetRect.width(), targetRect.height());
+}
+
+} // namespace WebCore
+
+#endif // USE(CAIRO) && PLATFORM(X11)
diff --git a/Source/WebCore/platform/graphics/cairo/BackingStoreBackendCairoX11.h b/Source/WebCore/platform/graphics/cairo/BackingStoreBackendCairoX11.h
new file mode 100644
index 000000000..3441809f5
--- /dev/null
+++ b/Source/WebCore/platform/graphics/cairo/BackingStoreBackendCairoX11.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2013,2014 Igalia S.L.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef BackingStoreBackendCairoX11_h
+#define BackingStoreBackendCairoX11_h
+
+#include "BackingStoreBackendCairo.h"
+
+#if USE(CAIRO) && PLATFORM(X11)
+#include "XUniquePtr.h"
+#include "XUniqueResource.h"
+
+namespace WebCore {
+
+class BackingStoreBackendCairoX11 final : public BackingStoreBackendCairo {
+public:
+ BackingStoreBackendCairoX11(unsigned long rootWindowID, Visual*, int depth, const IntSize&, float deviceScaleFactor);
+ virtual ~BackingStoreBackendCairoX11();
+
+ void scroll(const IntRect& scrollRect, const IntSize& scrollOffset) override;
+
+private:
+ XUniquePixmap m_pixmap;
+ XUniqueGC m_gc;
+};
+
+} // namespace WebCore
+
+#endif // USE(CAIRO) && PLATFORM(X11)
+
+#endif // GtkWidgetBackingStoreX11_h
diff --git a/Source/WebCore/platform/graphics/cairo/BitmapImageCairo.cpp b/Source/WebCore/platform/graphics/cairo/BitmapImageCairo.cpp
deleted file mode 100644
index 4611a306a..000000000
--- a/Source/WebCore/platform/graphics/cairo/BitmapImageCairo.cpp
+++ /dev/null
@@ -1,161 +0,0 @@
-/*
- * Copyright (C) 2004, 2005, 2006 Apple Computer, Inc. All rights reserved.
- * Copyright (C) 2007 Alp Toker <alp@atoker.com>
- * Copyright (C) 2009 Dirk Schulze <krit@webkit.org>
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
- * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
- * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#include "config.h"
-#include "BitmapImage.h"
-
-#include "CairoUtilities.h"
-#include "ImageObserver.h"
-#include "PlatformContextCairo.h"
-#include "Timer.h"
-#include <cairo.h>
-
-namespace WebCore {
-
-BitmapImage::BitmapImage(PassRefPtr<cairo_surface_t> nativeImage, ImageObserver* observer)
- : Image(observer)
- , m_size(cairoSurfaceSize(nativeImage.get()))
- , 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();
-}
-
-void BitmapImage::draw(GraphicsContext* context, const FloatRect& dst, const FloatRect& src, ColorSpace styleColorSpace, CompositeOperator op,
- BlendMode blendMode, ImageOrientationDescription description)
-{
- if (!dst.width() || !dst.height() || !src.width() || !src.height())
- return;
-
- startAnimation();
-
- RefPtr<cairo_surface_t> surface = frameAtIndex(m_currentFrame);
- if (!surface) // If it's too early we won't have an image yet.
- return;
-
- if (mayFillWithSolidColor()) {
- fillWithSolidColor(context, dst, solidColor(), styleColorSpace, 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);
-}
-
-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;
-}
-
-bool FrameData::clear(bool clearMetadata)
-{
- if (clearMetadata)
- m_haveMetadata = false;
-
- if (m_frame) {
- m_frame.clear();
- return true;
- }
- return false;
-}
-
-} // namespace WebCore
-
diff --git a/Source/WebCore/platform/graphics/TextRenderingMode.h b/Source/WebCore/platform/graphics/cairo/CairoUniquePtr.h
index 4f817a4a4..f7a5007d2 100644
--- a/Source/WebCore/platform/graphics/TextRenderingMode.h
+++ b/Source/WebCore/platform/graphics/cairo/CairoUniquePtr.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009 Apple Inc. All rights reserved.
+ * Copyright (C) 2016 Igalia S.L.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -20,16 +20,28 @@
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef TextRenderingMode_h
-#define TextRenderingMode_h
+#pragma once
+
+#include <cairo.h>
+#include <memory>
namespace WebCore {
- enum TextRenderingMode { AutoTextRendering, OptimizeSpeed, OptimizeLegibility, GeometricPrecision };
-
-} // namespace WebCore
+template<typename T> struct CairoPtrDeleter {
+ void operator()(T* ptr) const = delete;
+};
+
+template<typename T>
+using CairoUniquePtr = std::unique_ptr<T, CairoPtrDeleter<T>>;
-#endif // TextRenderingMode_h
+template<> struct CairoPtrDeleter<cairo_font_options_t> {
+ void operator() (cairo_font_options_t* ptr) const
+ {
+ cairo_font_options_destroy(ptr);
+ }
+};
+
+} // namespace WebCore
diff --git a/Source/WebCore/platform/graphics/cairo/CairoUtilities.cpp b/Source/WebCore/platform/graphics/cairo/CairoUtilities.cpp
index 98207e4b2..8a3ff33d0 100644
--- a/Source/WebCore/platform/graphics/cairo/CairoUtilities.cpp
+++ b/Source/WebCore/platform/graphics/cairo/CairoUtilities.cpp
@@ -11,10 +11,10 @@
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
- * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
@@ -27,16 +27,19 @@
#include "config.h"
#include "CairoUtilities.h"
+#if USE(CAIRO)
+
#include "AffineTransform.h"
#include "Color.h"
#include "FloatPoint.h"
#include "FloatRect.h"
#include "IntRect.h"
-#include "OwnPtrCairo.h"
#include "Path.h"
#include "PlatformPathCairo.h"
#include "RefPtrCairo.h"
+#include "Region.h"
#include <wtf/Assertions.h>
+#include <wtf/NeverDestroyed.h>
#include <wtf/Vector.h>
#if ENABLE(ACCELERATED_2D_CANVAS)
@@ -45,6 +48,14 @@
namespace WebCore {
+#if USE(FREETYPE) && !PLATFORM(GTK)
+const cairo_font_options_t* getDefaultCairoFontOptions()
+{
+ static NeverDestroyed<cairo_font_options_t*> options = cairo_font_options_create();
+ return options;
+}
+#endif
+
void copyContextProperties(cairo_t* srcCr, cairo_t* dstCr)
{
cairo_set_antialias(dstCr, cairo_get_antialias(srcCr));
@@ -64,15 +75,20 @@ void copyContextProperties(cairo_t* srcCr, cairo_t* dstCr)
void setSourceRGBAFromColor(cairo_t* context, const Color& color)
{
- float red, green, blue, alpha;
- color.getRGBA(red, green, blue, alpha);
- cairo_set_source_rgba(context, red, green, blue, alpha);
+ if (color.isExtended())
+ cairo_set_source_rgba(context, color.asExtended().red(), color.asExtended().green(), color.asExtended().blue(), color.asExtended().alpha());
+ else {
+ float red, green, blue, alpha;
+ color.getRGBA(red, green, blue, alpha);
+ cairo_set_source_rgba(context, red, green, blue, alpha);
+ }
}
void appendPathToCairoContext(cairo_t* to, cairo_t* from)
{
- OwnPtr<cairo_path_t> cairoPath = adoptPtr(cairo_copy_path(from));
- cairo_append_path(to, cairoPath.get());
+ auto cairoPath = cairo_copy_path(from);
+ cairo_append_path(to, cairoPath);
+ cairo_path_destroy(cairoPath);
}
void setPathOnCairoContext(cairo_t* to, cairo_t* from)
@@ -101,7 +117,7 @@ void appendRegionToCairoContext(cairo_t* to, const cairo_region_t* region)
}
}
-cairo_operator_t toCairoOperator(CompositeOperator op)
+static cairo_operator_t toCairoCompositeOperator(CompositeOperator op)
{
switch (op) {
case CompositeClear:
@@ -136,11 +152,12 @@ cairo_operator_t toCairoOperator(CompositeOperator op)
return CAIRO_OPERATOR_SOURCE;
}
}
-cairo_operator_t toCairoOperator(BlendMode blendOp)
+
+cairo_operator_t toCairoOperator(CompositeOperator op, BlendMode blendOp)
{
switch (blendOp) {
case BlendModeNormal:
- return CAIRO_OPERATOR_OVER;
+ return toCairoCompositeOperator(op);
case BlendModeMultiply:
return CAIRO_OPERATOR_MULTIPLY;
case BlendModeScreen:
@@ -198,8 +215,46 @@ void drawPatternToCairoContext(cairo_t* cr, cairo_surface_t* image, const IntSiz
cairo_pattern_t* pattern = cairo_pattern_create_for_surface(image);
cairo_pattern_set_extend(pattern, CAIRO_EXTEND_REPEAT);
+ // Due to a limitation in pixman, cairo cannot handle transformation matrices with values bigger than 32768. If the value is
+ // bigger, cairo is not able to paint anything, and this is the reason for the missing backgrounds reported in
+ // https://bugs.webkit.org/show_bug.cgi?id=154283.
+
+ // When drawing a pattern there are 2 matrices that can overflow this limitation, and they are the current transformation
+ // matrix (which translates user space coordinates to coordinates of the output device) and the pattern matrix (which translates
+ // user space coordinates to pattern coordinates). The overflow happens only in the translation components of the matrices.
+
+ // To avoid the problem in the transformation matrix what we do is remove the translation components of the transformation matrix
+ // and perform the translation by moving the destination rectangle instead. For this, we get its translation components (which are in
+ // device coordinates) and divide them by the scale factor to take them to user space coordinates. Then we move the transformation
+ // matrix by the opposite of that amount (which will zero the translation components of the transformation matrix), and move
+ // the destination rectangle by the same amount. We also need to apply the same translation to the pattern matrix, so we get the
+ // same pattern coordinates for the new destination rectangle.
+
+ cairo_matrix_t ctm;
+ cairo_get_matrix(cr, &ctm);
+ double dx = 0, dy = 0;
+ cairo_matrix_transform_point(&ctm, &dx, &dy);
+ double xScale = 1, yScale = 1;
+ cairo_matrix_transform_distance(&ctm, &xScale, &yScale);
+
+ dx = dx / xScale;
+ dy = dy / yScale;
+ cairo_translate(cr, -dx, -dy);
+ FloatRect adjustedDestRect(destRect);
+ adjustedDestRect.move(dx, dy);
+
+ // Regarding the pattern matrix, what we do is reduce the translation component of the matrix taking advantage of the fact that we
+ // are drawing a repeated pattern. This means that, assuming that (w, h) is the size of the pattern, samplig it at (x, y) is the same
+ // than sampling it at (x mod w, y mod h), so we transform the translation component of the pattern matrix in that way.
+
cairo_matrix_t patternMatrix = cairo_matrix_t(patternTransform);
- cairo_matrix_t phaseMatrix = {1, 0, 0, 1, phase.x() + tileRect.x() * patternTransform.a(), phase.y() + tileRect.y() * patternTransform.d()};
+ // dx and dy are added here as well to compensate the previous translation of the destination rectangle.
+ double phaseOffsetX = phase.x() + tileRect.x() * patternTransform.a() + dx;
+ double phaseOffsetY = phase.y() + tileRect.y() * patternTransform.d() + dy;
+ // this is where we perform the (x mod w, y mod h) metioned above, but with floats instead of integers.
+ phaseOffsetX -= std::trunc(phaseOffsetX / (tileRect.width() * patternTransform.a())) * tileRect.width() * patternTransform.a();
+ phaseOffsetY -= std::trunc(phaseOffsetY / (tileRect.height() * patternTransform.d())) * tileRect.height() * patternTransform.d();
+ cairo_matrix_t phaseMatrix = {1, 0, 0, 1, phaseOffsetX, phaseOffsetY};
cairo_matrix_t combined;
cairo_matrix_multiply(&combined, &patternMatrix, &phaseMatrix);
cairo_matrix_invert(&combined);
@@ -208,13 +263,13 @@ void drawPatternToCairoContext(cairo_t* cr, cairo_surface_t* image, const IntSiz
cairo_set_operator(cr, op);
cairo_set_source(cr, pattern);
cairo_pattern_destroy(pattern);
- cairo_rectangle(cr, destRect.x(), destRect.y(), destRect.width(), destRect.height());
+ cairo_rectangle(cr, adjustedDestRect.x(), adjustedDestRect.y(), adjustedDestRect.width(), adjustedDestRect.height());
cairo_fill(cr);
cairo_restore(cr);
}
-PassRefPtr<cairo_surface_t> copyCairoImageSurface(cairo_surface_t* originalSurface)
+RefPtr<cairo_surface_t> copyCairoImageSurface(cairo_surface_t* originalSurface)
{
// Cairo doesn't provide a way to copy a cairo_surface_t.
// See http://lists.cairographics.org/archives/cairo/2007-June/010877.html
@@ -260,6 +315,29 @@ IntSize cairoSurfaceSize(cairo_surface_t* surface)
}
}
+void flipImageSurfaceVertically(cairo_surface_t* surface)
+{
+ ASSERT(cairo_surface_get_type(surface) == CAIRO_SURFACE_TYPE_IMAGE);
+
+ IntSize size = cairoSurfaceSize(surface);
+ ASSERT(!size.isEmpty());
+
+ int stride = cairo_image_surface_get_stride(surface);
+ int halfHeight = size.height() / 2;
+
+ uint8_t* source = static_cast<uint8_t*>(cairo_image_surface_get_data(surface));
+ std::unique_ptr<uint8_t[]> tmp = std::make_unique<uint8_t[]>(stride);
+
+ for (int i = 0; i < halfHeight; ++i) {
+ uint8_t* top = source + (i * stride);
+ uint8_t* bottom = source + ((size.height()-i-1) * stride);
+
+ memcpy(tmp.get(), top, stride);
+ memcpy(top, bottom, stride);
+ memcpy(bottom, tmp.get(), stride);
+ }
+}
+
void cairoSurfaceSetDeviceScale(cairo_surface_t* surface, double xScale, double yScale)
{
// This function was added pretty much simultaneous to when 1.13 was branched.
@@ -271,4 +349,28 @@ void cairoSurfaceSetDeviceScale(cairo_surface_t* surface, double xScale, double
ASSERT_UNUSED(yScale, 1 == yScale);
#endif
}
+
+void cairoSurfaceGetDeviceScale(cairo_surface_t* surface, double& xScale, double& yScale)
+{
+#if HAVE(CAIRO_SURFACE_SET_DEVICE_SCALE)
+ cairo_surface_get_device_scale(surface, &xScale, &yScale);
+#else
+ UNUSED_PARAM(surface);
+ xScale = 1;
+ yScale = 1;
+#endif
+}
+
+RefPtr<cairo_region_t> toCairoRegion(const Region& region)
+{
+ RefPtr<cairo_region_t> cairoRegion = adoptRef(cairo_region_create());
+ for (const auto& rect : region.rects()) {
+ cairo_rectangle_int_t cairoRect = rect;
+ cairo_region_union_rectangle(cairoRegion.get(), &cairoRect);
+ }
+ return cairoRegion;
+}
+
} // namespace WebCore
+
+#endif // USE(CAIRO)
diff --git a/Source/WebCore/platform/graphics/cairo/CairoUtilities.h b/Source/WebCore/platform/graphics/cairo/CairoUtilities.h
index df3680c01..9528e0210 100644
--- a/Source/WebCore/platform/graphics/cairo/CairoUtilities.h
+++ b/Source/WebCore/platform/graphics/cairo/CairoUtilities.h
@@ -11,10 +11,10 @@
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
- * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
@@ -24,8 +24,9 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef CairoUtilities_h
-#define CairoUtilities_h
+#pragma once
+
+#if USE(CAIRO)
#include "GraphicsTypes.h"
#include "IntSize.h"
@@ -34,6 +35,10 @@
// This function was added pretty much simultaneous to when 1.13 was branched.
#define HAVE_CAIRO_SURFACE_SET_DEVICE_SCALE CAIRO_VERSION_MAJOR > 1 || (CAIRO_VERSION_MAJOR == 1 && CAIRO_VERSION_MINOR >= 13)
+#if USE(FREETYPE)
+#include <cairo-ft.h>
+#endif
+
namespace WebCore {
class AffineTransform;
class Color;
@@ -42,6 +47,32 @@ class FloatPoint;
class IntSize;
class IntRect;
class Path;
+class Region;
+
+#if USE(FREETYPE)
+class CairoFtFaceLocker {
+public:
+ CairoFtFaceLocker(cairo_scaled_font_t* scaledFont)
+ : m_scaledFont(scaledFont)
+ , m_ftFace(cairo_ft_scaled_font_lock_face(scaledFont))
+ {
+ }
+
+ ~CairoFtFaceLocker()
+ {
+ if (m_ftFace)
+ cairo_ft_scaled_font_unlock_face(m_scaledFont);
+ }
+
+ FT_Face ftFace() const { return m_ftFace; }
+
+private:
+ cairo_scaled_font_t* m_scaledFont { nullptr };
+ FT_Face m_ftFace { nullptr };
+};
+
+const cairo_font_options_t* getDefaultCairoFontOptions();
+#endif
void copyContextProperties(cairo_t* srcCr, cairo_t* dstCr);
void setSourceRGBAFromColor(cairo_t*, const Color&);
@@ -49,18 +80,21 @@ void appendPathToCairoContext(cairo_t* to, cairo_t* from);
void setPathOnCairoContext(cairo_t* to, cairo_t* from);
void appendWebCorePathToCairoContext(cairo_t* context, const Path& path);
void appendRegionToCairoContext(cairo_t*, const cairo_region_t*);
-cairo_operator_t toCairoOperator(CompositeOperator op);
-cairo_operator_t toCairoOperator(BlendMode blendOp);
+cairo_operator_t toCairoOperator(CompositeOperator, BlendMode = BlendModeNormal);
void drawPatternToCairoContext(cairo_t* cr, cairo_surface_t* image, const IntSize& imageSize, const FloatRect& tileRect,
const AffineTransform& patternTransform, const FloatPoint& phase, cairo_operator_t op, const FloatRect& destRect);
-PassRefPtr<cairo_surface_t> copyCairoImageSurface(cairo_surface_t*);
+RefPtr<cairo_surface_t> copyCairoImageSurface(cairo_surface_t*);
void copyRectFromCairoSurfaceToContext(cairo_surface_t* from, cairo_t* to, const IntSize& offset, const IntRect&);
void copyRectFromOneSurfaceToAnother(cairo_surface_t* from, cairo_surface_t* to, const IntSize& offset, const IntRect&, const IntSize& = IntSize(), cairo_operator_t = CAIRO_OPERATOR_OVER);
IntSize cairoSurfaceSize(cairo_surface_t*);
+void flipImageSurfaceVertically(cairo_surface_t*);
void cairoSurfaceSetDeviceScale(cairo_surface_t*, double xScale, double yScale);
+void cairoSurfaceGetDeviceScale(cairo_surface_t*, double& xScale, double& yScale);
+
+RefPtr<cairo_region_t> toCairoRegion(const Region&);
} // namespace WebCore
-#endif // CairoUtilities_h
+#endif // USE(CAIRO)
diff --git a/Source/WebCore/platform/graphics/cairo/DrawErrorUnderline.h b/Source/WebCore/platform/graphics/cairo/DrawErrorUnderline.h
index 4eca4e4c5..ececc562d 100644
--- a/Source/WebCore/platform/graphics/cairo/DrawErrorUnderline.h
+++ b/Source/WebCore/platform/graphics/cairo/DrawErrorUnderline.h
@@ -21,11 +21,11 @@
*
*/
-#if USE(CAIRO)
-
#ifndef DrawErrorUnderline_h
#define DrawErrorUnderline_h
+#if USE(CAIRO)
+
#include <cairo.h>
//
@@ -101,6 +101,6 @@ static inline void drawErrorUnderline(cairo_t* cr, double x, double y, double wi
cairo_fill(cr);
}
-#endif // DrawErrorUnderline_h
+#endif // USE(CAIRO)
-#endif
+#endif // DrawErrorUnderline_h
diff --git a/Source/WebCore/platform/graphics/cairo/FloatRectCairo.cpp b/Source/WebCore/platform/graphics/cairo/FloatRectCairo.cpp
index 9f86f747e..ede90c90f 100644
--- a/Source/WebCore/platform/graphics/cairo/FloatRectCairo.cpp
+++ b/Source/WebCore/platform/graphics/cairo/FloatRectCairo.cpp
@@ -10,10 +10,10 @@
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
- * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
@@ -26,6 +26,8 @@
#include "config.h"
#include "FloatRect.h"
+#if USE(CAIRO)
+
#include <cairo.h>
namespace WebCore {
@@ -43,3 +45,5 @@ FloatRect::operator cairo_rectangle_t() const
}
} // namespace WebCore
+
+#endif // USE(CAIRO)
diff --git a/Source/WebCore/platform/graphics/cairo/FontCairo.cpp b/Source/WebCore/platform/graphics/cairo/FontCairo.cpp
index a09e555d2..a712d0011 100644
--- a/Source/WebCore/platform/graphics/cairo/FontCairo.cpp
+++ b/Source/WebCore/platform/graphics/cairo/FontCairo.cpp
@@ -1,9 +1,10 @@
/*
- * Copyright (C) 2006 Apple Computer, Inc. All rights reserved.
+ * Copyright (C) 2006 Apple Inc. All rights reserved.
* Copyright (C) 2006 Michael Emmel mike.emmel@gmail.com
* Copyright (C) 2007, 2008 Alp Toker <alp@atoker.com>
* Copyright (C) 2009 Dirk Schulze <krit@webkit.org>
* Copyright (C) 2010 Holger Hans Peter Freyther
+ * Copyright (C) 2014 Igalia S.L.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -14,10 +15,10 @@
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
- * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
@@ -28,29 +29,32 @@
*/
#include "config.h"
-#include "Font.h"
+#include "FontCascade.h"
+
+#if USE(CAIRO)
#include "AffineTransform.h"
#include "CairoUtilities.h"
+#include "Font.h"
#include "GlyphBuffer.h"
#include "Gradient.h"
#include "GraphicsContext.h"
#include "ImageBuffer.h"
#include "Pattern.h"
#include "PlatformContextCairo.h"
+#include "PlatformPathCairo.h"
#include "ShadowBlur.h"
-#include "SimpleFontData.h"
namespace WebCore {
-static void drawGlyphsToContext(cairo_t* context, const SimpleFontData* font, GlyphBufferGlyph* glyphs, int numGlyphs)
+static void drawGlyphsToContext(cairo_t* context, const Font& font, GlyphBufferGlyph* glyphs, unsigned numGlyphs)
{
cairo_matrix_t originalTransform;
- float syntheticBoldOffset = font->syntheticBoldOffset();
+ float syntheticBoldOffset = font.syntheticBoldOffset();
if (syntheticBoldOffset)
cairo_get_matrix(context, &originalTransform);
- cairo_set_scaled_font(context, font->platformData().scaledFont());
+ cairo_set_scaled_font(context, font.platformData().scaledFont());
cairo_show_glyphs(context, glyphs, numGlyphs);
if (syntheticBoldOffset) {
@@ -62,21 +66,21 @@ static void drawGlyphsToContext(cairo_t* context, const SimpleFontData* font, Gl
cairo_set_matrix(context, &originalTransform);
}
-static void drawGlyphsShadow(GraphicsContext* graphicsContext, const FloatPoint& point, const SimpleFontData* font, GlyphBufferGlyph* glyphs, int numGlyphs)
+static void drawGlyphsShadow(GraphicsContext& graphicsContext, const FloatPoint& point, const Font& font, GlyphBufferGlyph* glyphs, unsigned numGlyphs)
{
- ShadowBlur& shadow = graphicsContext->platformContext()->shadowBlur();
+ ShadowBlur& shadow = graphicsContext.platformContext()->shadowBlur();
- if (!(graphicsContext->textDrawingMode() & TextModeFill) || shadow.type() == ShadowBlur::NoShadow)
+ if (!(graphicsContext.textDrawingMode() & TextModeFill) || shadow.type() == ShadowBlur::NoShadow)
return;
- if (!graphicsContext->mustUseShadowBlur()) {
+ if (!graphicsContext.mustUseShadowBlur()) {
// Optimize non-blurry shadows, by just drawing text without the ShadowBlur.
- cairo_t* context = graphicsContext->platformContext()->cr();
+ cairo_t* context = graphicsContext.platformContext()->cr();
cairo_save(context);
- FloatSize shadowOffset(graphicsContext->state().shadowOffset);
+ FloatSize shadowOffset(graphicsContext.state().shadowOffset);
cairo_translate(context, shadowOffset.width(), shadowOffset.height());
- setSourceRGBAFromColor(context, graphicsContext->state().shadowColor);
+ setSourceRGBAFromColor(context, graphicsContext.state().shadowColor);
drawGlyphsToContext(context, font, glyphs, numGlyphs);
cairo_restore(context);
@@ -84,7 +88,7 @@ static void drawGlyphsShadow(GraphicsContext* graphicsContext, const FloatPoint&
}
cairo_text_extents_t extents;
- cairo_scaled_font_glyph_extents(font->platformData().scaledFont(), glyphs, numGlyphs, &extents);
+ cairo_scaled_font_glyph_extents(font.platformData().scaledFont(), glyphs, numGlyphs, &extents);
FloatRect fontExtentsRect(point.x() + extents.x_bearing, point.y() + extents.y_bearing, extents.width, extents.height);
if (GraphicsContext* shadowContext = shadow.beginShadowLayer(graphicsContext, fontExtentsRect)) {
@@ -93,29 +97,29 @@ static void drawGlyphsShadow(GraphicsContext* graphicsContext, const FloatPoint&
}
}
-void Font::drawGlyphs(GraphicsContext* context, const SimpleFontData* font, const GlyphBuffer& glyphBuffer,
- int from, int numGlyphs, const FloatPoint& point) const
+void FontCascade::drawGlyphs(GraphicsContext& context, const Font& font, const GlyphBuffer& glyphBuffer,
+ unsigned from, unsigned numGlyphs, const FloatPoint& point, FontSmoothingMode)
{
- if (!font->platformData().size())
+ if (!font.platformData().size())
return;
GlyphBufferGlyph* glyphs = const_cast<GlyphBufferGlyph*>(glyphBuffer.glyphs(from));
float offset = point.x();
- for (int i = 0; i < numGlyphs; i++) {
+ for (unsigned i = 0; i < numGlyphs; i++) {
glyphs[i].x = offset;
glyphs[i].y = point.y();
offset += glyphBuffer.advanceAt(from + i).width();
}
- PlatformContextCairo* platformContext = context->platformContext();
+ PlatformContextCairo* platformContext = context.platformContext();
drawGlyphsShadow(context, point, font, glyphs, numGlyphs);
cairo_t* cr = platformContext->cr();
cairo_save(cr);
- if (context->textDrawingMode() & TextModeFill) {
- platformContext->prepareForFilling(context->state(), PlatformContextCairo::AdjustPatternForGlobalAlpha);
+ if (context.textDrawingMode() & TextModeFill) {
+ platformContext->prepareForFilling(context.state(), PlatformContextCairo::AdjustPatternForGlobalAlpha);
drawGlyphsToContext(cr, font, glyphs, numGlyphs);
}
@@ -123,12 +127,12 @@ void Font::drawGlyphs(GraphicsContext* context, const SimpleFontData* font, cons
// twice the size of the width of the text we will not ask cairo to stroke
// the text as even one single stroke would cover the full wdth of the text.
// See https://bugs.webkit.org/show_bug.cgi?id=33759.
- if (context->textDrawingMode() & TextModeStroke && context->strokeThickness() < 2 * offset) {
- platformContext->prepareForStroking(context->state());
- cairo_set_line_width(cr, context->strokeThickness());
+ if (context.textDrawingMode() & TextModeStroke && context.strokeThickness() < 2 * offset) {
+ platformContext->prepareForStroking(context.state());
+ cairo_set_line_width(cr, context.strokeThickness());
// This may disturb the CTM, but we are going to call cairo_restore soon after.
- cairo_set_scaled_font(cr, font->platformData().scaledFont());
+ cairo_set_scaled_font(cr, font.platformData().scaledFont());
cairo_glyph_path(cr, glyphs, numGlyphs);
cairo_stroke(cr);
}
@@ -136,4 +140,198 @@ void Font::drawGlyphs(GraphicsContext* context, const SimpleFontData* font, cons
cairo_restore(cr);
}
+#if ENABLE(CSS3_TEXT_DECORATION_SKIP_INK)
+struct GlyphIterationState {
+ GlyphIterationState(FloatPoint startingPoint, FloatPoint currentPoint, float centerOfLine, float minX, float maxX)
+ : startingPoint(startingPoint)
+ , currentPoint(currentPoint)
+ , centerOfLine(centerOfLine)
+ , minX(minX)
+ , maxX(maxX)
+ {
+ }
+ FloatPoint startingPoint;
+ FloatPoint currentPoint;
+ float centerOfLine;
+ float minX;
+ float maxX;
+};
+
+static bool findIntersectionPoint(float y, FloatPoint p1, FloatPoint p2, float& x)
+{
+ x = p1.x() + (y - p1.y()) * (p2.x() - p1.x()) / (p2.y() - p1.y());
+ return (p1.y() < y && p2.y() > y) || (p1.y() > y && p2.y() < y);
+}
+
+static void updateX(GlyphIterationState& state, float x)
+{
+ state.minX = std::min(state.minX, x);
+ state.maxX = std::max(state.maxX, x);
+}
+
+// This function is called by Path::apply and is therefore invoked for each contour in a glyph. This
+// function models each contours as a straight line and calculates the intersections between each
+// pseudo-contour and the vertical center of the underline found in GlyphIterationState::centerOfLine.
+// It keeps track of the leftmost and rightmost intersection in GlyphIterationState::minX and
+// GlyphIterationState::maxX.
+static void findPathIntersections(GlyphIterationState& state, const PathElement& element)
+{
+ bool doIntersection = false;
+ FloatPoint point = FloatPoint();
+ switch (element.type) {
+ case PathElementMoveToPoint:
+ state.startingPoint = element.points[0];
+ state.currentPoint = element.points[0];
+ break;
+ case PathElementAddLineToPoint:
+ doIntersection = true;
+ point = element.points[0];
+ break;
+ case PathElementAddQuadCurveToPoint:
+ doIntersection = true;
+ point = element.points[1];
+ break;
+ case PathElementAddCurveToPoint:
+ doIntersection = true;
+ point = element.points[2];
+ break;
+ case PathElementCloseSubpath:
+ doIntersection = true;
+ point = state.startingPoint;
+ break;
+ }
+
+ if (!doIntersection)
+ return;
+
+ float x;
+ if (findIntersectionPoint(state.centerOfLine, state.currentPoint, point, x))
+ updateX(state, x);
+
+ state.currentPoint = point;
}
+
+class CairoGlyphToPathTranslator final : public GlyphToPathTranslator {
+public:
+ CairoGlyphToPathTranslator(const TextRun& textRun, const GlyphBuffer& glyphBuffer, const FloatPoint& textOrigin)
+ : m_index(0)
+ , m_textRun(textRun)
+ , m_glyphBuffer(glyphBuffer)
+ , m_fontData(glyphBuffer.fontAt(m_index))
+ , m_translation(AffineTransform().translate(textOrigin.x(), textOrigin.y()))
+ {
+ }
+
+ bool containsMorePaths() final { return m_index != m_glyphBuffer.size(); }
+ Path path() final;
+ std::pair<float, float> extents() final;
+ GlyphUnderlineType underlineType() final;
+ void advance() final;
+
+private:
+ unsigned m_index;
+ const TextRun& m_textRun;
+ const GlyphBuffer& m_glyphBuffer;
+ const Font* m_fontData;
+ AffineTransform m_translation;
+};
+
+Path CairoGlyphToPathTranslator::path()
+{
+ Path path;
+ path.ensurePlatformPath();
+
+ cairo_glyph_t cairoGlyph = { m_glyphBuffer.glyphAt(m_index), 0, 0 };
+ cairo_set_scaled_font(path.platformPath()->context(), m_fontData->platformData().scaledFont());
+ cairo_glyph_path(path.platformPath()->context(), &cairoGlyph, 1);
+
+ float syntheticBoldOffset = m_fontData->syntheticBoldOffset();
+ if (syntheticBoldOffset) {
+ cairo_translate(path.platformPath()->context(), syntheticBoldOffset, 0);
+ cairo_show_glyphs(path.platformPath()->context(), &cairoGlyph, 1);
+ }
+
+ path.transform(m_translation);
+ return path;
+}
+
+std::pair<float, float> CairoGlyphToPathTranslator::extents()
+{
+ FloatPoint beginning = m_translation.mapPoint(FloatPoint());
+ FloatSize end = m_translation.mapSize(m_glyphBuffer.advanceAt(m_index));
+ return std::make_pair(static_cast<float>(beginning.x()), static_cast<float>(beginning.x() + end.width()));
+}
+
+GlyphToPathTranslator::GlyphUnderlineType CairoGlyphToPathTranslator::underlineType()
+{
+ return computeUnderlineType(m_textRun, m_glyphBuffer, m_index);
+}
+
+void CairoGlyphToPathTranslator::advance()
+{
+ GlyphBufferAdvance advance = m_glyphBuffer.advanceAt(m_index);
+ m_translation = m_translation.translate(advance.width(), advance.height());
+ ++m_index;
+ if (m_index < m_glyphBuffer.size())
+ m_fontData = m_glyphBuffer.fontAt(m_index);
+}
+
+DashArray FontCascade::dashesForIntersectionsWithRect(const TextRun& run, const FloatPoint& textOrigin, const FloatRect& lineExtents) const
+{
+ if (isLoadingCustomFonts())
+ return DashArray();
+
+ GlyphBuffer glyphBuffer;
+ glyphBuffer.saveOffsetsInString();
+ float deltaX;
+ if (codePath(run) != FontCascade::Complex)
+ deltaX = getGlyphsAndAdvancesForSimpleText(run, 0, run.length(), glyphBuffer);
+ else
+ deltaX = getGlyphsAndAdvancesForComplexText(run, 0, run.length(), glyphBuffer);
+
+ if (!glyphBuffer.size())
+ return DashArray();
+
+ // FIXME: Handle SVG + non-SVG interleaved runs. https://bugs.webkit.org/show_bug.cgi?id=133778
+ FloatPoint origin = FloatPoint(textOrigin.x() + deltaX, textOrigin.y());
+ CairoGlyphToPathTranslator translator(run, glyphBuffer, origin);
+ DashArray result;
+ for (int index = 0; translator.containsMorePaths(); ++index, translator.advance()) {
+ float centerOfLine = lineExtents.y() + (lineExtents.height() / 2);
+ GlyphIterationState info = GlyphIterationState(FloatPoint(), FloatPoint(), centerOfLine, lineExtents.x() + lineExtents.width(), lineExtents.x());
+ const Font* localFontData = glyphBuffer.fontAt(index);
+ if (!localFontData) {
+ // The advances will get all messed up if we do anything other than bail here.
+ result.clear();
+ break;
+ }
+ switch (translator.underlineType()) {
+ case GlyphToPathTranslator::GlyphUnderlineType::SkipDescenders: {
+ Path path = translator.path();
+ path.apply([&info](const PathElement& pathElement) {
+ findPathIntersections(info, pathElement);
+ });
+ if (info.minX < info.maxX) {
+ result.append(info.minX - lineExtents.x());
+ result.append(info.maxX - lineExtents.x());
+ }
+ break;
+ }
+ case GlyphToPathTranslator::GlyphUnderlineType::SkipGlyph: {
+ std::pair<float, float> extents = translator.extents();
+ result.append(extents.first - lineExtents.x());
+ result.append(extents.second - lineExtents.x());
+ break;
+ }
+ case GlyphToPathTranslator::GlyphUnderlineType::DrawOverGlyph:
+ // Nothing to do
+ break;
+ }
+ }
+ return result;
+}
+#endif // ENABLE(CSS3_TEXT_DECORATION_SKIP_INK)
+
+} // namespace WebCore
+
+#endif // USE(CAIRO)
diff --git a/Source/WebCore/platform/graphics/cairo/FontCairoHarfbuzzNG.cpp b/Source/WebCore/platform/graphics/cairo/FontCairoHarfbuzzNG.cpp
index 0c940c5d6..cc37d7d58 100644
--- a/Source/WebCore/platform/graphics/cairo/FontCairoHarfbuzzNG.cpp
+++ b/Source/WebCore/platform/graphics/cairo/FontCairoHarfbuzzNG.cpp
@@ -11,10 +11,10 @@
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
- * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
@@ -25,48 +25,44 @@
*/
#include "config.h"
-#include "Font.h"
+#include "FontCascade.h"
+
+#if USE(CAIRO)
+#include "Font.h"
#include "GraphicsContext.h"
#include "HarfBuzzShaper.h"
+#include "LayoutRect.h"
#include "Logging.h"
#include "NotImplemented.h"
#include "PlatformContextCairo.h"
-#include "SimpleFontData.h"
#include <cairo.h>
namespace WebCore {
-float Font::drawComplexText(GraphicsContext* context, const TextRun& run, const FloatPoint& point, int, int) const
+float FontCascade::getGlyphsAndAdvancesForComplexText(const TextRun& run, unsigned, unsigned, GlyphBuffer& glyphBuffer, ForTextEmphasisOrNot /* forTextEmphasis */) const
{
- GlyphBuffer glyphBuffer;
HarfBuzzShaper shaper(this, run);
- if (shaper.shape(&glyphBuffer)) {
- FloatPoint startPoint = point;
- float startX = startPoint.x();
- drawGlyphBuffer(context, run, glyphBuffer, startPoint);
- return startPoint.x() - startX;
+ if (!shaper.shape(&glyphBuffer)) {
+ LOG_ERROR("Shaper couldn't shape glyphBuffer.");
+ return 0;
}
- LOG_ERROR("Shaper couldn't shape glyphBuffer.");
- return 0;
-}
-void Font::drawEmphasisMarksForComplexText(GraphicsContext* /* context */, const TextRun& /* run */, const AtomicString& /* mark */, const FloatPoint& /* point */, int /* from */, int /* to */) const
-{
- notImplemented();
+ // FIXME: Mac returns an initial advance here.
+ return 0;
}
-bool Font::canReturnFallbackFontsForComplexText()
+bool FontCascade::canReturnFallbackFontsForComplexText()
{
return false;
}
-bool Font::canExpandAroundIdeographsInComplexText()
+bool FontCascade::canExpandAroundIdeographsInComplexText()
{
return false;
}
-float Font::floatWidthForComplexText(const TextRun& run, HashSet<const SimpleFontData*>*, GlyphOverflow*) const
+float FontCascade::floatWidthForComplexText(const TextRun& run, HashSet<const Font*>*, GlyphOverflow*) const
{
HarfBuzzShaper shaper(this, run);
if (shaper.shape())
@@ -75,7 +71,7 @@ float Font::floatWidthForComplexText(const TextRun& run, HashSet<const SimpleFon
return 0;
}
-int Font::offsetForPositionForComplexText(const TextRun& run, float x, bool) const
+int FontCascade::offsetForPositionForComplexText(const TextRun& run, float x, bool) const
{
HarfBuzzShaper shaper(this, run);
if (shaper.shape())
@@ -84,13 +80,18 @@ int Font::offsetForPositionForComplexText(const TextRun& run, float x, bool) con
return 0;
}
-FloatRect Font::selectionRectForComplexText(const TextRun& run, const FloatPoint& point, int h, int from, int to) const
+void FontCascade::adjustSelectionRectForComplexText(const TextRun& run, LayoutRect& selectionRect, unsigned from, unsigned to) const
{
HarfBuzzShaper shaper(this, run);
- if (shaper.shape())
- return shaper.selectionRect(point, h, from, to);
+ if (shaper.shape()) {
+ // FIXME: This should mimic Mac port.
+ FloatRect rect = shaper.selectionRect(FloatPoint(selectionRect.location()), selectionRect.height().toInt(), from, to);
+ selectionRect = LayoutRect(rect);
+ return;
+ }
LOG_ERROR("Shaper couldn't shape text run.");
- return FloatRect();
}
-}
+} // namespace WebCore
+
+#endif // USE(CAIRO)
diff --git a/Source/WebCore/platform/graphics/cairo/FontCustomPlatformData.h b/Source/WebCore/platform/graphics/cairo/FontCustomPlatformData.h
index 1514526c2..c95ef9b5b 100644
--- a/Source/WebCore/platform/graphics/cairo/FontCustomPlatformData.h
+++ b/Source/WebCore/platform/graphics/cairo/FontCustomPlatformData.h
@@ -22,9 +22,9 @@
#ifndef FontCustomPlatformData_h
#define FontCustomPlatformData_h
-#include "FontOrientation.h"
-#include "FontRenderingMode.h"
-#include "FontWidthVariant.h"
+#if USE(CAIRO)
+
+#include "TextFlags.h"
#include <wtf/Forward.h>
#include <wtf/Noncopyable.h>
@@ -33,6 +33,7 @@ typedef struct _cairo_font_face cairo_font_face_t;
namespace WebCore {
+class FontDescription;
class FontPlatformData;
class SharedBuffer;
@@ -41,16 +42,17 @@ struct FontCustomPlatformData {
public:
FontCustomPlatformData(FT_Face, SharedBuffer&);
~FontCustomPlatformData();
- FontPlatformData fontPlatformData(int size, bool bold, bool italic, FontOrientation = Horizontal, FontWidthVariant = RegularWidth, FontRenderingMode = NormalRenderingMode);
+ FontPlatformData fontPlatformData(const FontDescription&, bool bold, bool italic);
static bool supportsFormat(const String&);
private:
- FT_Face m_freeTypeFace;
cairo_font_face_t* m_fontFace;
};
std::unique_ptr<FontCustomPlatformData> createFontCustomPlatformData(SharedBuffer&);
-}
+} // namespace WebCore
+
+#endif // USE(CAIRO)
-#endif
+#endif // FontCustomPlatformData_h
diff --git a/Source/WebCore/platform/graphics/cairo/GradientCairo.cpp b/Source/WebCore/platform/graphics/cairo/GradientCairo.cpp
index e0cfe3e65..724d832cc 100644
--- a/Source/WebCore/platform/graphics/cairo/GradientCairo.cpp
+++ b/Source/WebCore/platform/graphics/cairo/GradientCairo.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2006, 2007, 2008 Apple Computer, Inc. All rights reserved.
+ * Copyright (C) 2006, 2007, 2008 Apple Inc. All rights reserved.
* Copyright (C) 2007 Alp Toker <alp@atoker.com>
*
* Redistribution and use in source and binary forms, with or without
@@ -11,10 +11,10 @@
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
- * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
@@ -27,6 +27,8 @@
#include "config.h"
#include "Gradient.h"
+#if USE(CAIRO)
+
#include "GraphicsContext.h"
#include "PlatformContextCairo.h"
#include <cairo.h>
@@ -106,4 +108,6 @@ void Gradient::fill(GraphicsContext* context, const FloatRect& rect)
context->restore();
}
-} //namespace
+} // namespace WebCore
+
+#endif // USE(CAIRO)
diff --git a/Source/WebCore/platform/graphics/cairo/GraphicsContext3DCairo.cpp b/Source/WebCore/platform/graphics/cairo/GraphicsContext3DCairo.cpp
index 940265a9a..b4ae79e4a 100644
--- a/Source/WebCore/platform/graphics/cairo/GraphicsContext3DCairo.cpp
+++ b/Source/WebCore/platform/graphics/cairo/GraphicsContext3DCairo.cpp
@@ -12,10 +12,10 @@
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
- * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
@@ -27,7 +27,9 @@
#include "config.h"
-#if USE(3D_GRAPHICS)
+#if USE(CAIRO)
+
+#if ENABLE(GRAPHICS_CONTEXT_3D)
#include "GraphicsContext3D.h"
#include "CairoUtilities.h"
@@ -38,13 +40,11 @@
#include "PlatformContextCairo.h"
#include "RefPtrCairo.h"
#include <cairo.h>
-#include <wtf/OwnPtr.h>
-#include <wtf/PassOwnPtr.h>
#if PLATFORM(WIN)
-#include "GLSLANG/ShaderLang.h"
+#include <GLSLANG/ShaderLang.h>
#else
-#include "ShaderLang.h"
+#include <ANGLE/ShaderLang.h>
#endif
#if USE(OPENGL_ES_2)
@@ -54,9 +54,13 @@
#include "OpenGLShims.h"
#endif
+#if USE(TEXTURE_MAPPER)
+#include "TextureMapperGC3DPlatformLayer.h"
+#endif
+
namespace WebCore {
-PassRefPtr<GraphicsContext3D> GraphicsContext3D::create(GraphicsContext3D::Attributes attributes, HostWindow* hostWindow, GraphicsContext3D::RenderStyle renderStyle)
+RefPtr<GraphicsContext3D> GraphicsContext3D::create(GraphicsContext3DAttributes attributes, HostWindow* hostWindow, GraphicsContext3D::RenderStyle renderStyle)
{
// This implementation doesn't currently support rendering directly to the HostWindow.
if (renderStyle == RenderDirectlyToHostWindow)
@@ -73,24 +77,30 @@ PassRefPtr<GraphicsContext3D> GraphicsContext3D::create(GraphicsContext3D::Attri
if (!success)
return 0;
- RefPtr<GraphicsContext3D> context = adoptRef(new GraphicsContext3D(attributes, hostWindow, renderStyle));
- return context.release();
+ return adoptRef(new GraphicsContext3D(attributes, hostWindow, renderStyle));
}
-GraphicsContext3D::GraphicsContext3D(GraphicsContext3D::Attributes attributes, HostWindow*, GraphicsContext3D::RenderStyle renderStyle)
+GraphicsContext3D::GraphicsContext3D(GraphicsContext3DAttributes attributes, HostWindow*, GraphicsContext3D::RenderStyle renderStyle)
: m_currentWidth(0)
, m_currentHeight(0)
- , m_compiler(isGLES2Compliant() ? SH_ESSL_OUTPUT : SH_GLSL_OUTPUT)
, m_attrs(attributes)
, m_texture(0)
, m_compositorTexture(0)
, m_fbo(0)
- , m_depthStencilBuffer(0)
+#if USE(COORDINATED_GRAPHICS_THREADED)
+ , m_intermediateTexture(0)
+#endif
+ , m_layerComposited(false)
, m_multisampleFBO(0)
, m_multisampleDepthStencilBuffer(0)
, m_multisampleColorBuffer(0)
- , m_private(GraphicsContext3DPrivate::create(this, renderStyle))
{
+#if USE(TEXTURE_MAPPER)
+ m_texmapLayer = std::make_unique<TextureMapperGC3DPlatformLayer>(*this, renderStyle);
+#else
+ m_private = std::make_unique<GraphicsContext3DPrivate>(this, renderStyle);
+#endif
+
makeContextCurrent();
validateAttributes();
@@ -109,9 +119,24 @@ GraphicsContext3D::GraphicsContext3D(GraphicsContext3D::Attributes attributes, H
::glGenFramebuffers(1, &m_fbo);
::glBindFramebuffer(GL_FRAMEBUFFER, m_fbo);
- m_state.boundFBO = m_fbo;
- if (!m_attrs.antialias && (m_attrs.stencil || m_attrs.depth))
- ::glGenRenderbuffers(1, &m_depthStencilBuffer);
+#if USE(COORDINATED_GRAPHICS_THREADED)
+ ::glGenTextures(1, &m_compositorTexture);
+ ::glBindTexture(GL_TEXTURE_2D, m_compositorTexture);
+ ::glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ ::glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ ::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ ::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+
+ ::glGenTextures(1, &m_intermediateTexture);
+ ::glBindTexture(GL_TEXTURE_2D, m_intermediateTexture);
+ ::glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ ::glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ ::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ ::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+
+ ::glBindTexture(GL_TEXTURE_2D, 0);
+#endif
+
// Create a multisample FBO.
if (m_attrs.antialias) {
@@ -121,9 +146,51 @@ GraphicsContext3D::GraphicsContext3D(GraphicsContext3D::Attributes attributes, H
::glGenRenderbuffers(1, &m_multisampleColorBuffer);
if (m_attrs.stencil || m_attrs.depth)
::glGenRenderbuffers(1, &m_multisampleDepthStencilBuffer);
+ } else {
+ // Bind canvas FBO.
+ glBindFramebuffer(GraphicsContext3D::FRAMEBUFFER, m_fbo);
+ m_state.boundFBO = m_fbo;
+#if USE(OPENGL_ES_2)
+ if (m_attrs.depth)
+ glGenRenderbuffers(1, &m_depthBuffer);
+ if (m_attrs.stencil)
+ glGenRenderbuffers(1, &m_stencilBuffer);
+#endif
+ if (m_attrs.stencil || m_attrs.depth)
+ glGenRenderbuffers(1, &m_depthStencilBuffer);
}
}
+#if !USE(OPENGL_ES_2)
+ ::glEnable(GL_VERTEX_PROGRAM_POINT_SIZE);
+
+ if (GLContext::current()->version() >= 320) {
+ m_usingCoreProfile = true;
+
+ // From version 3.2 on we use the OpenGL Core profile, so request that ouput to the shader compiler.
+ // OpenGL version 3.2 uses GLSL version 1.50.
+ m_compiler = ANGLEWebKitBridge(SH_GLSL_150_CORE_OUTPUT);
+
+ // From version 3.2 on we use the OpenGL Core profile, and we need a VAO for rendering.
+ // A VAO could be created and bound by each component using GL rendering (TextureMapper, WebGL, etc). This is
+ // a simpler solution: the first GraphicsContext3D created on a GLContext will create and bind a VAO for that context.
+ GC3Dint currentVAO = 0;
+ getIntegerv(GraphicsContext3D::VERTEX_ARRAY_BINDING, &currentVAO);
+ if (!currentVAO) {
+ m_vao = createVertexArray();
+ bindVertexArray(m_vao);
+ }
+ } else {
+ // For lower versions request the compatibility output to the shader compiler.
+ m_compiler = ANGLEWebKitBridge(SH_GLSL_COMPATIBILITY_OUTPUT);
+
+ // GL_POINT_SPRITE is needed in lower versions.
+ ::glEnable(GL_POINT_SPRITE);
+ }
+#else
+ m_compiler = ANGLEWebKitBridge(SH_ESSL_OUTPUT);
+#endif
+
// ANGLE initialization.
ShBuiltInResources ANGLEResources;
ShInitBuiltInResources(&ANGLEResources);
@@ -145,18 +212,18 @@ GraphicsContext3D::GraphicsContext3D(GraphicsContext3D::Attributes attributes, H
m_compiler.setResources(ANGLEResources);
-#if !USE(OPENGL_ES_2)
- ::glEnable(GL_VERTEX_PROGRAM_POINT_SIZE);
- ::glEnable(GL_POINT_SPRITE);
-#endif
-
::glClearColor(0, 0, 0, 0);
}
GraphicsContext3D::~GraphicsContext3D()
{
+#if USE(TEXTURE_MAPPER)
+ if (m_texmapLayer->renderStyle() == RenderToCurrentGLContext)
+ return;
+#else
if (m_private->renderStyle() == RenderToCurrentGLContext)
return;
+#endif
makeContextCurrent();
if (m_texture)
@@ -169,11 +236,24 @@ GraphicsContext3D::~GraphicsContext3D()
if (m_attrs.stencil || m_attrs.depth)
::glDeleteRenderbuffers(1, &m_multisampleDepthStencilBuffer);
::glDeleteFramebuffers(1, &m_multisampleFBO);
- } else {
- if (m_attrs.stencil || m_attrs.depth)
+ } else if (m_attrs.stencil || m_attrs.depth) {
+#if USE(OPENGL_ES_2)
+ if (m_depthBuffer)
+ glDeleteRenderbuffers(1, &m_depthBuffer);
+
+ if (m_stencilBuffer)
+ glDeleteRenderbuffers(1, &m_stencilBuffer);
+#endif
+ if (m_depthStencilBuffer)
::glDeleteRenderbuffers(1, &m_depthStencilBuffer);
}
::glDeleteFramebuffers(1, &m_fbo);
+#if USE(COORDINATED_GRAPHICS_THREADED)
+ ::glDeleteTextures(1, &m_intermediateTexture);
+#endif
+
+ if (m_vao)
+ deleteVertexArray(m_vao);
}
GraphicsContext3D::ImageExtractor::~ImageExtractor()
@@ -187,17 +267,21 @@ bool GraphicsContext3D::ImageExtractor::extractImage(bool premultiplyAlpha, bool
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);
+ AlphaOption alphaOption = premultiplyAlpha ? AlphaOption::Premultiplied : AlphaOption::NotPremultiplied;
+ GammaAndColorProfileOption gammaAndColorProfileOption = ignoreGammaAndColorProfile ? GammaAndColorProfileOption::Ignored : GammaAndColorProfileOption::Applied;
+ m_decoder = new ImageSource(nullptr, alphaOption, gammaAndColorProfileOption);
+
if (!m_decoder)
return false;
- ImageSource& decoder = *m_decoder;
+ ImageSource& decoder = *m_decoder;
m_alphaOp = AlphaDoNothing;
+
if (m_image->data()) {
decoder.setData(m_image->data(), true);
- if (!decoder.frameCount() || !decoder.frameIsCompleteAtIndex(0))
+ if (!decoder.frameCount())
return false;
- m_imageSurface = decoder.createFrameAtIndex(0);
+ m_imageSurface = decoder.createFrameImageAtIndex(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,
@@ -209,9 +293,10 @@ bool GraphicsContext3D::ImageExtractor::extractImage(bool premultiplyAlpha, bool
// 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();
+ IntSize surfaceSize = cairoSurfaceSize(m_imageSurface.get());
+ auto tmpSurface = adoptRef(cairo_image_surface_create(CAIRO_FORMAT_ARGB32, surfaceSize.width(), surfaceSize.height()));
+ copyRectFromOneSurfaceToAnother(m_imageSurface.get(), tmpSurface.get(), IntSize(), IntRect(IntPoint(), surfaceSize), IntSize(), CAIRO_OPERATOR_SOURCE);
+ m_imageSurface = WTFMove(tmpSurface);
}
}
@@ -272,24 +357,37 @@ void GraphicsContext3D::paintToCanvas(const unsigned char* imagePixels, int imag
context->restore();
}
-void GraphicsContext3D::setContextLostCallback(PassOwnPtr<ContextLostCallback>)
+void GraphicsContext3D::setContextLostCallback(std::unique_ptr<ContextLostCallback>)
{
}
-void GraphicsContext3D::setErrorMessageCallback(PassOwnPtr<ErrorMessageCallback>)
+void GraphicsContext3D::setErrorMessageCallback(std::unique_ptr<ErrorMessageCallback>)
{
}
bool GraphicsContext3D::makeContextCurrent()
{
- if (!m_private)
- return false;
- return m_private->makeContextCurrent();
+#if USE(TEXTURE_MAPPER)
+ if (m_texmapLayer)
+ return m_texmapLayer->makeContextCurrent();
+#else
+ if (m_private)
+ return m_private->makeContextCurrent();
+#endif
+ return false;
+}
+
+void GraphicsContext3D::checkGPUStatusIfNecessary()
+{
}
PlatformGraphicsContext3D GraphicsContext3D::platformGraphicsContext3D()
{
+#if USE(TEXTURE_MAPPER)
+ return m_texmapLayer->platformContext();
+#else
return m_private->platformContext();
+#endif
}
Platform3DObject GraphicsContext3D::platformTexture() const
@@ -306,13 +404,33 @@ bool GraphicsContext3D::isGLES2Compliant() const
#endif
}
-#if USE(ACCELERATED_COMPOSITING)
PlatformLayer* GraphicsContext3D::platformLayer() const
{
+#if USE(TEXTURE_MAPPER)
+ return m_texmapLayer.get();
+#else
return m_private.get();
+#endif
+}
+
+#if PLATFORM(GTK)
+Extensions3D& GraphicsContext3D::getExtensions()
+{
+ if (!m_extensions) {
+#if USE(OPENGL_ES_2)
+ // glGetStringi is not available on GLES2.
+ m_extensions = std::make_unique<Extensions3DOpenGLES>(this, false);
+#else
+ // From OpenGL 3.2 on we use the Core profile, and there we must use glGetStringi.
+ m_extensions = std::make_unique<Extensions3DOpenGL>(this, GLContext::current()->version() >= 320);
+#endif
+ }
+ return *m_extensions;
}
#endif
} // namespace WebCore
-#endif // USE(3D_GRAPHICS)
+#endif // ENABLE(GRAPHICS_CONTEXT_3D)
+
+#endif // USE(CAIRO)
diff --git a/Source/WebCore/platform/graphics/cairo/GraphicsContextCairo.cpp b/Source/WebCore/platform/graphics/cairo/GraphicsContextCairo.cpp
index 0fa750396..ac4b4a089 100644
--- a/Source/WebCore/platform/graphics/cairo/GraphicsContextCairo.cpp
+++ b/Source/WebCore/platform/graphics/cairo/GraphicsContextCairo.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2006 Apple Computer, Inc. All rights reserved.
+ * Copyright (C) 2006 Apple Inc. All rights reserved.
* Copyright (C) 2007 Alp Toker <alp@atoker.com>
* Copyright (C) 2008, 2009 Dirk Schulze <krit@webkit.org>
* Copyright (C) 2008 Nuanti Ltd.
@@ -17,10 +17,10 @@
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
- * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
@@ -37,30 +37,29 @@
#include "AffineTransform.h"
#include "CairoUtilities.h"
+#include "DisplayListRecorder.h"
#include "DrawErrorUnderline.h"
#include "FloatConversion.h"
#include "FloatRect.h"
+#include "FloatRoundedRect.h"
#include "Font.h"
#include "GraphicsContextPlatformPrivateCairo.h"
+#include "ImageBuffer.h"
#include "IntRect.h"
#include "NotImplemented.h"
-#include "OwnPtrCairo.h"
#include "Path.h"
#include "Pattern.h"
#include "PlatformContextCairo.h"
#include "PlatformPathCairo.h"
#include "RefPtrCairo.h"
#include "ShadowBlur.h"
-#include "SimpleFontData.h"
#include "TransformationMatrix.h"
#include <cairo.h>
#include <math.h>
#include <stdio.h>
#include <wtf/MathExtras.h>
-#if PLATFORM(GTK)
-#include <gdk/gdk.h>
-#elif PLATFORM(WIN)
+#if PLATFORM(WIN)
#include <cairo-win32.h>
#endif
@@ -71,36 +70,31 @@ namespace WebCore {
// A helper which quickly fills a rectangle with a simple color fill.
static inline void fillRectWithColor(cairo_t* cr, const FloatRect& rect, const Color& color)
{
- if (!color.alpha() && cairo_get_operator(cr) == CAIRO_OPERATOR_OVER)
+ if (!color.isVisible() && cairo_get_operator(cr) == CAIRO_OPERATOR_OVER)
return;
+
setSourceRGBAFromColor(cr, color);
cairo_rectangle(cr, rect.x(), rect.y(), rect.width(), rect.height());
cairo_fill(cr);
}
-static void addConvexPolygonToContext(cairo_t* context, size_t numPoints, const FloatPoint* points)
-{
- cairo_move_to(context, points[0].x(), points[0].y());
- for (size_t i = 1; i < numPoints; i++)
- cairo_line_to(context, points[i].x(), points[i].y());
- cairo_close_path(context);
-}
-
enum PathDrawingStyle {
Fill = 1,
Stroke = 2,
FillAndStroke = Fill + Stroke
};
-static inline void drawPathShadow(GraphicsContext* context, PathDrawingStyle drawingStyle)
+static inline void drawPathShadow(GraphicsContext& context, PathDrawingStyle drawingStyle)
{
- ShadowBlur& shadow = context->platformContext()->shadowBlur();
+ ShadowBlur& shadow = context.platformContext()->shadowBlur();
if (shadow.type() == ShadowBlur::NoShadow)
return;
// Calculate the extents of the rendered solid paths.
- cairo_t* cairoContext = context->platformContext()->cr();
- OwnPtr<cairo_path_t> path = adoptPtr(cairo_copy_path(cairoContext));
+ cairo_t* cairoContext = context.platformContext()->cr();
+ std::unique_ptr<cairo_path_t, void(*)(cairo_path_t*)> path(cairo_copy_path(cairoContext), [](cairo_path_t* path) {
+ cairo_path_destroy(path);
+ });
FloatRect solidFigureExtents;
double x0 = 0;
@@ -130,14 +124,14 @@ static inline void drawPathShadow(GraphicsContext* context, PathDrawingStyle dra
if (drawingStyle & Fill) {
cairo_save(cairoShadowContext);
cairo_append_path(cairoShadowContext, path.get());
- shadowContext->platformContext()->prepareForFilling(context->state(), PlatformContextCairo::NoAdjustment);
+ shadowContext->platformContext()->prepareForFilling(context.state(), PlatformContextCairo::NoAdjustment);
cairo_fill(cairoShadowContext);
cairo_restore(cairoShadowContext);
}
if (drawingStyle & Stroke) {
cairo_append_path(cairoShadowContext, path.get());
- shadowContext->platformContext()->prepareForStroking(context->state(), PlatformContextCairo::DoNotPreserveAlpha);
+ shadowContext->platformContext()->prepareForStroking(context.state(), PlatformContextCairo::DoNotPreserveAlpha);
cairo_stroke(cairoShadowContext);
}
@@ -150,44 +144,45 @@ static inline void drawPathShadow(GraphicsContext* context, PathDrawingStyle dra
cairo_append_path(cairoContext, path.get());
}
-static inline void fillCurrentCairoPath(GraphicsContext* context)
+static inline void fillCurrentCairoPath(GraphicsContext& context)
{
- cairo_t* cr = context->platformContext()->cr();
+ cairo_t* cr = context.platformContext()->cr();
cairo_save(cr);
- context->platformContext()->prepareForFilling(context->state(), PlatformContextCairo::AdjustPatternForGlobalAlpha);
+ context.platformContext()->prepareForFilling(context.state(), PlatformContextCairo::AdjustPatternForGlobalAlpha);
cairo_fill(cr);
cairo_restore(cr);
}
-static inline void shadowAndFillCurrentCairoPath(GraphicsContext* context)
+static inline void shadowAndFillCurrentCairoPath(GraphicsContext& context)
{
drawPathShadow(context, Fill);
fillCurrentCairoPath(context);
}
-static inline void shadowAndStrokeCurrentCairoPath(GraphicsContext* context)
+static inline void shadowAndStrokeCurrentCairoPath(GraphicsContext& context)
{
drawPathShadow(context, Stroke);
- context->platformContext()->prepareForStroking(context->state());
- cairo_stroke(context->platformContext()->cr());
+ context.platformContext()->prepareForStroking(context.state());
+ cairo_stroke(context.platformContext()->cr());
}
GraphicsContext::GraphicsContext(cairo_t* cr)
- : m_updatingControlTints(false),
- m_transparencyCount(0)
{
+ if (!cr)
+ return;
+
m_data = new GraphicsContextPlatformPrivateToplevel(new PlatformContextCairo(cr));
}
void GraphicsContext::platformInit(PlatformContextCairo* platformContext)
{
+ if (!platformContext)
+ return;
+
m_data = new GraphicsContextPlatformPrivate(platformContext);
- if (platformContext)
- m_data->syncContext(platformContext->cr());
- else
- setPaintingDisabled(true);
+ m_data->syncContext(platformContext->cr());
}
void GraphicsContext::platformDestroy()
@@ -200,6 +195,11 @@ AffineTransform GraphicsContext::getCTM(IncludeDeviceScale) const
if (paintingDisabled())
return AffineTransform();
+ if (isRecording()) {
+ WTFLogAlways("GraphicsContext::getCTM() is not yet compatible with recording contexts.");
+ return AffineTransform();
+ }
+
cairo_t* cr = platformContext()->cr();
cairo_matrix_t m;
cairo_get_matrix(cr, &m);
@@ -213,28 +213,34 @@ PlatformContextCairo* GraphicsContext::platformContext() const
void GraphicsContext::savePlatformState()
{
+ ASSERT(!isRecording());
platformContext()->save();
m_data->save();
}
void GraphicsContext::restorePlatformState()
{
+ ASSERT(!isRecording());
platformContext()->restore();
m_data->restore();
platformContext()->shadowBlur().setShadowValues(FloatSize(m_state.shadowBlur, m_state.shadowBlur),
m_state.shadowOffset,
m_state.shadowColor,
- m_state.shadowColorSpace,
m_state.shadowsIgnoreTransforms);
}
// Draws a filled rectangle with a stroked border.
-void GraphicsContext::drawRect(const IntRect& rect)
+void GraphicsContext::drawRect(const FloatRect& rect, float borderThickness)
{
if (paintingDisabled())
return;
+ if (isRecording()) {
+ m_displayListRecorder->drawRect(rect, borderThickness);
+ return;
+ }
+
ASSERT(!rect.isEmpty());
cairo_t* cr = platformContext()->cr();
@@ -247,108 +253,132 @@ void GraphicsContext::drawRect(const IntRect& rect)
FloatRect r(rect);
r.inflate(-.5f);
cairo_rectangle(cr, r.x(), r.y(), r.width(), r.height());
- cairo_set_line_width(cr, 1.0);
+ cairo_set_line_width(cr, 1.0); // borderThickness?
cairo_stroke(cr);
}
cairo_restore(cr);
}
-static double calculateStrokePatternOffset(int distance, int patternWidth)
+void GraphicsContext::drawNativeImage(const NativeImagePtr& image, const FloatSize& imageSize, const FloatRect& destRect, const FloatRect& srcRect, CompositeOperator op, BlendMode blendMode, ImageOrientation orientation)
{
- // Example: 80 pixels with a width of 30 pixels. Remainder is 20.
- // The maximum pixels of line we could paint will be 50 pixels.
- int remainder = distance % patternWidth;
- int numSegments = (distance - remainder) / patternWidth;
+ if (paintingDisabled())
+ return;
- // Special case 1px dotted borders for speed.
- if (patternWidth == 1)
- return 1;
+ if (isRecording()) {
+ m_displayListRecorder->drawNativeImage(image, imageSize, destRect, srcRect, op, blendMode, orientation);
+ return;
+ }
- bool evenNumberOfSegments = !(numSegments % 2);
- if (remainder)
- evenNumberOfSegments = !evenNumberOfSegments;
+ platformContext()->save();
- if (evenNumberOfSegments) {
- if (remainder)
- return (patternWidth - remainder) + (remainder / 2);
- return patternWidth / 2;
+ // Set the compositing operation.
+ if (op == CompositeSourceOver && blendMode == BlendModeNormal)
+ setCompositeOperation(CompositeCopy);
+ else
+ setCompositeOperation(op, blendMode);
+
+ FloatRect dst = destRect;
+
+ if (orientation != DefaultImageOrientation) {
+ // ImageOrientation expects the origin to be at (0, 0).
+ translate(dst.x(), dst.y());
+ dst.setLocation(FloatPoint());
+ concatCTM(orientation.transformFromDefault(dst.size()));
+ if (orientation.usesWidthAsHeight()) {
+ // The destination rectangle will have its 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.
+ dst = FloatRect(dst.x(), dst.y(), dst.height(), dst.width());
+ }
}
- // Odd number of segments.
- if (remainder)
- return (patternWidth - remainder) / 2.f;
- return 0;
+ platformContext()->drawSurfaceToContext(image.get(), dst, srcRect, *this);
+ platformContext()->restore();
}
-static void drawLineOnCairoContext(GraphicsContext* graphicsContext, cairo_t* context, const FloatPoint& point1, const FloatPoint& point2)
+// This is only used to draw borders, so we should not draw shadows.
+void GraphicsContext::drawLine(const FloatPoint& point1, const FloatPoint& point2)
{
- StrokeStyle style = graphicsContext->strokeStyle();
- if (style == NoStroke)
+ if (paintingDisabled())
return;
- const Color& strokeColor = graphicsContext->strokeColor();
- int strokeThickness = floorf(graphicsContext->strokeThickness());
- if (graphicsContext->strokeThickness() < 1)
- strokeThickness = 1;
+ if (strokeStyle() == NoStroke)
+ return;
- int patternWidth = 0;
- if (style == DottedStroke)
- patternWidth = strokeThickness;
- else if (style == DashedStroke)
- patternWidth = 3 * strokeThickness;
+ if (isRecording()) {
+ m_displayListRecorder->drawLine(point1, point2);
+ return;
+ }
- bool isVerticalLine = point1.x() == point2.x();
- FloatPoint point1OnPixelBoundaries = point1;
- FloatPoint point2OnPixelBoundaries = point2;
- GraphicsContext::adjustLineToPixelBoundaries(point1OnPixelBoundaries, point2OnPixelBoundaries, strokeThickness, style);
+ const Color& strokeColor = this->strokeColor();
+ float thickness = strokeThickness();
+ bool isVerticalLine = (point1.x() + thickness == point2.x());
+ float strokeWidth = isVerticalLine ? point2.y() - point1.y() : point2.x() - point1.x();
+ if (!thickness || !strokeWidth)
+ return;
- if (patternWidth) {
- // Do a rect fill of our endpoints. This ensures we always have the
- // appearance of being a border. We then draw the actual dotted/dashed line.
- FloatRect firstRect(point1OnPixelBoundaries, FloatSize(strokeThickness, strokeThickness));
- FloatRect secondRect(point2OnPixelBoundaries, FloatSize(strokeThickness, strokeThickness));
+ cairo_t* cairoContext = platformContext()->cr();
+ StrokeStyle strokeStyle = this->strokeStyle();
+ float cornerWidth = 0;
+ bool drawsDashedLine = strokeStyle == DottedStroke || strokeStyle == DashedStroke;
+
+ if (drawsDashedLine) {
+ cairo_save(cairoContext);
+ // Figure out end points to ensure we always paint corners.
+ cornerWidth = dashedLineCornerWidthForStrokeWidth(strokeWidth);
if (isVerticalLine) {
- firstRect.move(-strokeThickness / 2, -strokeThickness);
- secondRect.move(-strokeThickness / 2, 0);
+ fillRectWithColor(cairoContext, FloatRect(point1.x(), point1.y(), thickness, cornerWidth), strokeColor);
+ fillRectWithColor(cairoContext, FloatRect(point1.x(), point2.y() - cornerWidth, thickness, cornerWidth), strokeColor);
} else {
- firstRect.move(-strokeThickness, -strokeThickness / 2);
- secondRect.move(0, -strokeThickness / 2);
+ fillRectWithColor(cairoContext, FloatRect(point1.x(), point1.y(), cornerWidth, thickness), strokeColor);
+ fillRectWithColor(cairoContext, FloatRect(point2.x() - cornerWidth, point1.y(), cornerWidth, thickness), strokeColor);
+ }
+ strokeWidth -= 2 * cornerWidth;
+ float patternWidth = dashedLinePatternWidthForStrokeWidth(strokeWidth);
+ // Check if corner drawing sufficiently covers the line.
+ if (strokeWidth <= patternWidth + 1) {
+ cairo_restore(cairoContext);
+ return;
}
- fillRectWithColor(context, firstRect, strokeColor);
- fillRectWithColor(context, secondRect, strokeColor);
- int distance = (isVerticalLine ? (point2.y() - point1.y()) : (point2.x() - point1.x())) - 2 * strokeThickness;
- double patternOffset = calculateStrokePatternOffset(distance, patternWidth);
- double patternWidthAsDouble = patternWidth;
- cairo_set_dash(context, &patternWidthAsDouble, 1, patternOffset);
+ float patternOffset = dashedLinePatternOffsetForPatternAndStrokeWidth(patternWidth, strokeWidth);
+ const double dashedLine[2] = { static_cast<double>(patternWidth), static_cast<double>(patternWidth) };
+ cairo_set_dash(cairoContext, dashedLine, 2, patternOffset);
+ } else {
+ setSourceRGBAFromColor(cairoContext, strokeColor);
+ if (thickness < 1)
+ cairo_set_line_width(cairoContext, 1);
}
- setSourceRGBAFromColor(context, strokeColor);
- cairo_set_line_width(context, strokeThickness);
- cairo_move_to(context, point1OnPixelBoundaries.x(), point1OnPixelBoundaries.y());
- cairo_line_to(context, point2OnPixelBoundaries.x(), point2OnPixelBoundaries.y());
- cairo_stroke(context);
-}
-// This is only used to draw borders, so we should not draw shadows.
-void GraphicsContext::drawLine(const IntPoint& point1, const IntPoint& point2)
-{
- if (paintingDisabled())
- return;
+ auto centeredPoints = centerLineAndCutOffCorners(isVerticalLine, cornerWidth, point1, point2);
+ auto p1 = centeredPoints[0];
+ auto p2 = centeredPoints[1];
- cairo_t* cairoContext = platformContext()->cr();
- cairo_save(cairoContext);
- drawLineOnCairoContext(this, cairoContext, point1, point2);
- cairo_restore(cairoContext);
+ if (shouldAntialias())
+ cairo_set_antialias(cairoContext, CAIRO_ANTIALIAS_NONE);
+
+ cairo_new_path(cairoContext);
+ cairo_move_to(cairoContext, p1.x(), p1.y());
+ cairo_line_to(cairoContext, p2.x(), p2.y());
+ cairo_stroke(cairoContext);
+ if (drawsDashedLine)
+ cairo_restore(cairoContext);
+ if (shouldAntialias())
+ cairo_set_antialias(cairoContext, CAIRO_ANTIALIAS_DEFAULT);
}
// This method is only used to draw the little circles used in lists.
-void GraphicsContext::drawEllipse(const IntRect& rect)
+void GraphicsContext::drawEllipse(const FloatRect& rect)
{
if (paintingDisabled())
return;
+ if (isRecording()) {
+ m_displayListRecorder->drawEllipse(rect);
+ return;
+ }
+
cairo_t* cr = platformContext()->cr();
cairo_save(cr);
float yRadius = .5 * rect.height();
@@ -358,7 +388,7 @@ void GraphicsContext::drawEllipse(const IntRect& rect)
cairo_arc(cr, 0., 0., 1., 0., 2 * piFloat);
cairo_restore(cr);
- if (fillColor().alpha()) {
+ if (fillColor().isVisible()) {
setSourceRGBAFromColor(cr, fillColor());
cairo_fill_preserve(cr);
}
@@ -371,67 +401,19 @@ void GraphicsContext::drawEllipse(const IntRect& rect)
cairo_new_path(cr);
}
-void GraphicsContext::drawConvexPolygon(size_t npoints, const FloatPoint* points, bool shouldAntialias)
+void GraphicsContext::fillPath(const Path& path)
{
- if (paintingDisabled())
+ if (paintingDisabled() || path.isEmpty())
return;
- if (npoints <= 1)
+ if (isRecording()) {
+ m_displayListRecorder->fillPath(path);
return;
-
- cairo_t* cr = platformContext()->cr();
-
- cairo_save(cr);
- cairo_set_antialias(cr, shouldAntialias ? CAIRO_ANTIALIAS_DEFAULT : CAIRO_ANTIALIAS_NONE);
- addConvexPolygonToContext(cr, npoints, points);
-
- if (fillColor().alpha()) {
- setSourceRGBAFromColor(cr, fillColor());
- cairo_set_fill_rule(cr, CAIRO_FILL_RULE_EVEN_ODD);
- cairo_fill_preserve(cr);
}
- if (strokeStyle() != NoStroke) {
- setSourceRGBAFromColor(cr, strokeColor());
- cairo_set_line_width(cr, strokeThickness());
- cairo_stroke(cr);
- } else
- cairo_new_path(cr);
-
- cairo_restore(cr);
-}
-
-void GraphicsContext::clipConvexPolygon(size_t numPoints, const FloatPoint* points, bool antialiased)
-{
- if (paintingDisabled())
- return;
-
- if (numPoints <= 1)
- return;
-
- cairo_t* cr = platformContext()->cr();
-
- cairo_new_path(cr);
- cairo_fill_rule_t savedFillRule = cairo_get_fill_rule(cr);
- cairo_antialias_t savedAntialiasRule = cairo_get_antialias(cr);
-
- cairo_set_antialias(cr, antialiased ? CAIRO_ANTIALIAS_DEFAULT : CAIRO_ANTIALIAS_NONE);
- cairo_set_fill_rule(cr, CAIRO_FILL_RULE_WINDING);
- addConvexPolygonToContext(cr, numPoints, points);
- cairo_clip(cr);
-
- cairo_set_antialias(cr, savedAntialiasRule);
- cairo_set_fill_rule(cr, savedFillRule);
-}
-
-void GraphicsContext::fillPath(const Path& path)
-{
- if (paintingDisabled() || path.isEmpty())
- return;
-
cairo_t* cr = platformContext()->cr();
setPathOnCairoContext(cr, path.platformPath()->context());
- shadowAndFillCurrentCairoPath(this);
+ shadowAndFillCurrentCairoPath(*this);
}
void GraphicsContext::strokePath(const Path& path)
@@ -439,9 +421,14 @@ void GraphicsContext::strokePath(const Path& path)
if (paintingDisabled() || path.isEmpty())
return;
+ if (isRecording()) {
+ m_displayListRecorder->strokePath(path);
+ return;
+ }
+
cairo_t* cr = platformContext()->cr();
setPathOnCairoContext(cr, path.platformPath()->context());
- shadowAndStrokeCurrentCairoPath(this);
+ shadowAndStrokeCurrentCairoPath(*this);
}
void GraphicsContext::fillRect(const FloatRect& rect)
@@ -449,18 +436,28 @@ void GraphicsContext::fillRect(const FloatRect& rect)
if (paintingDisabled())
return;
+ if (isRecording()) {
+ m_displayListRecorder->fillRect(rect);
+ return;
+ }
+
cairo_t* cr = platformContext()->cr();
cairo_rectangle(cr, rect.x(), rect.y(), rect.width(), rect.height());
- shadowAndFillCurrentCairoPath(this);
+ shadowAndFillCurrentCairoPath(*this);
}
-void GraphicsContext::fillRect(const FloatRect& rect, const Color& color, ColorSpace)
+void GraphicsContext::fillRect(const FloatRect& rect, const Color& color)
{
if (paintingDisabled())
return;
+ if (isRecording()) {
+ m_displayListRecorder->fillRect(rect, color);
+ return;
+ }
+
if (hasShadow())
- platformContext()->shadowBlur().drawRectShadow(this, rect, RoundedRect::Radii());
+ platformContext()->shadowBlur().drawRectShadow(*this, FloatRoundedRect(rect));
fillRectWithColor(platformContext()->cr(), rect, color);
}
@@ -470,6 +467,11 @@ void GraphicsContext::clip(const FloatRect& rect)
if (paintingDisabled())
return;
+ if (isRecording()) {
+ m_displayListRecorder->clip(rect);
+ return;
+ }
+
cairo_t* cr = platformContext()->cr();
cairo_rectangle(cr, rect.x(), rect.y(), rect.width(), rect.height());
cairo_fill_rule_t savedFillRule = cairo_get_fill_rule(cr);
@@ -492,15 +494,44 @@ void GraphicsContext::clipPath(const Path& path, WindRule clipRule)
if (paintingDisabled())
return;
+ if (isRecording()) {
+ m_displayListRecorder->clipPath(path, clipRule);
+ return;
+ }
+
cairo_t* cr = platformContext()->cr();
if (!path.isNull())
setPathOnCairoContext(cr, path.platformPath()->context());
+
+ cairo_fill_rule_t savedFillRule = cairo_get_fill_rule(cr);
cairo_set_fill_rule(cr, clipRule == RULE_EVENODD ? CAIRO_FILL_RULE_EVEN_ODD : CAIRO_FILL_RULE_WINDING);
cairo_clip(cr);
+ cairo_set_fill_rule(cr, savedFillRule);
+
+ m_data->clip(path);
+}
+
+void GraphicsContext::clipToImageBuffer(ImageBuffer& buffer, const FloatRect& destRect)
+{
+ if (paintingDisabled())
+ return;
+
+ RefPtr<Image> image = buffer.copyImage(DontCopyBackingStore);
+ RefPtr<cairo_surface_t> surface = image->nativeImageForCurrentFrame();
+ if (surface)
+ platformContext()->pushImageMask(surface.get(), destRect);
}
IntRect GraphicsContext::clipBounds() const
{
+ if (paintingDisabled())
+ return IntRect();
+
+ if (isRecording()) {
+ WTFLogAlways("Getting the clip bounds not yet supported with display lists");
+ return IntRect(-2048, -2048, 4096, 4096); // FIXME: display lists.
+ }
+
double x1, x2, y1, y2;
cairo_clip_extents(platformContext()->cr(), &x1, &y1, &x2, &y2);
return enclosingIntRect(FloatRect(x1, y1, x2 - x1, y2 - y1));
@@ -510,11 +541,13 @@ static inline void adjustFocusRingColor(Color& color)
{
#if !PLATFORM(GTK)
// Force the alpha to 50%. This matches what the Mac does with outline rings.
- color.setRGB(makeRGBA(color.red(), color.green(), color.blue(), 127));
+ color = Color(makeRGBA(color.red(), color.green(), color.blue(), 127));
+#else
+ UNUSED_PARAM(color);
#endif
}
-static inline void adjustFocusRingLineWidth(int& width)
+static inline void adjustFocusRingLineWidth(float& width)
{
#if PLATFORM(GTK)
width = 2;
@@ -532,8 +565,11 @@ static inline StrokeStyle focusRingStrokeStyle()
#endif
}
-void GraphicsContext::drawFocusRing(const Path& path, int width, int /* offset */, const Color& color)
+void GraphicsContext::drawFocusRing(const Path& path, float width, float /* offset */, const Color& color)
{
+ if (paintingDisabled())
+ return;
+
// FIXME: We should draw paths that describe a rectangle with rounded corners
// so as to be consistent with how we draw rectangular focus rings.
Color ringColor = color;
@@ -542,109 +578,91 @@ void GraphicsContext::drawFocusRing(const Path& path, int width, int /* offset *
cairo_t* cr = platformContext()->cr();
cairo_save(cr);
+ cairo_push_group(cr);
appendWebCorePathToCairoContext(cr, path);
setSourceRGBAFromColor(cr, ringColor);
cairo_set_line_width(cr, width);
setPlatformStrokeStyle(focusRingStrokeStyle());
- cairo_stroke(cr);
+ cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
+ cairo_stroke_preserve(cr);
+
+ cairo_set_operator(cr, CAIRO_OPERATOR_CLEAR);
+ cairo_set_fill_rule(cr, CAIRO_FILL_RULE_WINDING);
+ cairo_fill(cr);
+
+ cairo_pop_group_to_source(cr);
+ cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
+ cairo_paint(cr);
cairo_restore(cr);
}
-void GraphicsContext::drawFocusRing(const Vector<IntRect>& rects, int width, int /* offset */, const Color& color)
+void GraphicsContext::drawFocusRing(const Vector<FloatRect>& rects, float width, float /* offset */, const Color& color)
{
if (paintingDisabled())
return;
- unsigned rectCount = rects.size();
-
- cairo_t* cr = platformContext()->cr();
- cairo_save(cr);
- cairo_push_group(cr);
- cairo_new_path(cr);
-
+ Path path;
#if PLATFORM(GTK)
-#ifdef GTK_API_VERSION_2
- GdkRegion* reg = gdk_region_new();
-#else
- cairo_region_t* reg = cairo_region_create();
-#endif
-
- for (unsigned i = 0; i < rectCount; i++) {
-#ifdef GTK_API_VERSION_2
- GdkRectangle rect = rects[i];
- gdk_region_union_with_rect(reg, &rect);
-#else
- cairo_rectangle_int_t rect = rects[i];
- cairo_region_union_rectangle(reg, &rect);
-#endif
- }
- gdk_cairo_region(cr, reg);
-#ifdef GTK_API_VERSION_2
- gdk_region_destroy(reg);
-#else
- cairo_region_destroy(reg);
-#endif
+ for (const auto& rect : rects)
+ path.addRect(rect);
#else
+ unsigned rectCount = rects.size();
int radius = (width - 1) / 2;
- Path path;
+ Path subPath;
for (unsigned i = 0; i < rectCount; ++i) {
if (i > 0)
- path.clear();
- path.addRoundedRect(rects[i], FloatSize(radius, radius));
- appendWebCorePathToCairoContext(cr, path);
+ subPath.clear();
+ subPath.addRoundedRect(rects[i], FloatSize(radius, radius));
+ path.addPath(subPath, AffineTransform());
}
#endif
- Color ringColor = color;
- adjustFocusRingColor(ringColor);
- adjustFocusRingLineWidth(width);
- setSourceRGBAFromColor(cr, ringColor);
- cairo_set_line_width(cr, width);
- setPlatformStrokeStyle(focusRingStrokeStyle());
-
- cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
- cairo_stroke_preserve(cr);
-
- cairo_set_operator(cr, CAIRO_OPERATOR_CLEAR);
- cairo_set_fill_rule(cr, CAIRO_FILL_RULE_WINDING);
- cairo_fill(cr);
-
- cairo_pop_group_to_source(cr);
- cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
- cairo_paint(cr);
- cairo_restore(cr);
+ drawFocusRing(path, width, 0, color);
}
-FloatRect GraphicsContext::computeLineBoundsForText(const FloatPoint& origin, float width, bool)
+void GraphicsContext::drawLineForText(const FloatPoint& origin, float width, bool printing, bool doubleUnderlines, StrokeStyle)
{
- return FloatRect(origin, FloatSize(width, strokeThickness()));
+ DashArray widths;
+ widths.append(width);
+ widths.append(0);
+ drawLinesForText(origin, widths, printing, doubleUnderlines);
}
-void GraphicsContext::drawLineForText(const FloatPoint& origin, float width, bool printing)
+void GraphicsContext::drawLinesForText(const FloatPoint& point, const DashArray& widths, bool printing, bool doubleUnderlines, StrokeStyle)
{
if (paintingDisabled())
return;
- cairo_t* cairoContext = platformContext()->cr();
- cairo_save(cairoContext);
-
- // This bumping of <1 stroke thicknesses matches the one in drawLineOnCairoContext.
- FloatPoint endPoint(origin + IntSize(width, 0));
- FloatRect lineExtents = computeLineBoundsForText(origin, width, printing);
+ if (widths.size() <= 0)
+ return;
- ShadowBlur& shadow = platformContext()->shadowBlur();
- if (GraphicsContext* shadowContext = shadow.beginShadowLayer(this, lineExtents)) {
- drawLineOnCairoContext(this, shadowContext->platformContext()->cr(), origin, endPoint);
- shadow.endShadowLayer(this);
+ if (isRecording()) {
+ m_displayListRecorder->drawLinesForText(point, widths, printing, doubleUnderlines, strokeThickness());
+ return;
}
- drawLineOnCairoContext(this, cairoContext, origin, endPoint);
- cairo_restore(cairoContext);
-}
+ Color localStrokeColor(strokeColor());
-void GraphicsContext::drawLinesForText(const FloatPoint& point, const DashArray& widths, bool printing)
-{
+ FloatRect bounds = computeLineBoundsAndAntialiasingModeForText(point, widths.last(), printing, localStrokeColor);
+
+ Vector<FloatRect, 4> dashBounds;
+ ASSERT(!(widths.size() % 2));
+ dashBounds.reserveInitialCapacity(dashBounds.size() / 2);
for (size_t i = 0; i < widths.size(); i += 2)
- drawLineForText(FloatPoint(point.x() + widths[i], point.y()), widths[i+1] - widths[i], printing);
+ dashBounds.append(FloatRect(FloatPoint(bounds.x() + widths[i], bounds.y()), FloatSize(widths[i+1] - widths[i], bounds.height())));
+
+ if (doubleUnderlines) {
+ // The space between double underlines is equal to the height of the underline
+ for (size_t i = 0; i < widths.size(); i += 2)
+ dashBounds.append(FloatRect(FloatPoint(bounds.x() + widths[i], bounds.y() + 2 * bounds.height()), FloatSize(widths[i+1] - widths[i], bounds.height())));
+ }
+
+ cairo_t* cr = platformContext()->cr();
+ cairo_save(cr);
+
+ for (auto& dash : dashBounds)
+ fillRectWithColor(cr, dash, localStrokeColor);
+
+ cairo_restore(cr);
}
void GraphicsContext::updateDocumentMarkerResources()
@@ -679,6 +697,14 @@ void GraphicsContext::drawLineForDocumentMarker(const FloatPoint& origin, float
FloatRect GraphicsContext::roundToDevicePixels(const FloatRect& frect, RoundingMode)
{
+ if (paintingDisabled())
+ return frect;
+
+ if (isRecording()) {
+ WTFLogAlways("GraphicsContext::roundToDevicePixels() is not yet compatible with recording contexts.");
+ return frect;
+ }
+
FloatRect result;
double x = frect.x();
double y = frect.y();
@@ -701,7 +727,7 @@ FloatRect GraphicsContext::roundToDevicePixels(const FloatRect& frect, RoundingM
width = 1;
else
width = round(width);
- if (height > -1 && width < 0)
+ if (height > -1 && height < 0)
height = -1;
else if (height > 0 && height < 1)
height = 1;
@@ -719,18 +745,23 @@ void GraphicsContext::translate(float x, float y)
if (paintingDisabled())
return;
+ if (isRecording()) {
+ m_displayListRecorder->translate(x, y);
+ return;
+ }
+
cairo_t* cr = platformContext()->cr();
cairo_translate(cr, x, y);
m_data->translate(x, y);
}
-void GraphicsContext::setPlatformFillColor(const Color&, ColorSpace)
+void GraphicsContext::setPlatformFillColor(const Color&)
{
// Cairo contexts can't hold separate fill and stroke colors
// so we set them just before we actually fill or stroke
}
-void GraphicsContext::setPlatformStrokeColor(const Color&, ColorSpace)
+void GraphicsContext::setPlatformStrokeColor(const Color&)
{
// Cairo contexts can't hold separate fill and stroke colors
// so we set them just before we actually fill or stroke
@@ -741,17 +772,21 @@ void GraphicsContext::setPlatformStrokeThickness(float strokeThickness)
if (paintingDisabled())
return;
+ ASSERT(!isRecording());
+
cairo_set_line_width(platformContext()->cr(), strokeThickness);
}
void GraphicsContext::setPlatformStrokeStyle(StrokeStyle strokeStyle)
{
- static double dashPattern[] = {5.0, 5.0};
- static double dotPattern[] = {1.0, 1.0};
+ static const double dashPattern[] = { 5.0, 5.0 };
+ static const double dotPattern[] = { 1.0, 1.0 };
if (paintingDisabled())
return;
+ ASSERT(!isRecording());
+
switch (strokeStyle) {
case NoStroke:
// FIXME: is it the right way to emulate NoStroke?
@@ -771,7 +806,7 @@ void GraphicsContext::setPlatformStrokeStyle(StrokeStyle strokeStyle)
}
}
-void GraphicsContext::setURLForRect(const URL&, const IntRect&)
+void GraphicsContext::setURLForRect(const URL&, const FloatRect&)
{
notImplemented();
}
@@ -781,6 +816,11 @@ void GraphicsContext::concatCTM(const AffineTransform& transform)
if (paintingDisabled())
return;
+ if (isRecording()) {
+ m_displayListRecorder->concatCTM(transform);
+ return;
+ }
+
cairo_t* cr = platformContext()->cr();
const cairo_matrix_t matrix = cairo_matrix_t(transform);
cairo_transform(cr, &matrix);
@@ -792,13 +832,18 @@ void GraphicsContext::setCTM(const AffineTransform& transform)
if (paintingDisabled())
return;
+ if (isRecording()) {
+ WTFLogAlways("GraphicsContext::setCTM() is not compatible with recording contexts.");
+ return;
+ }
+
cairo_t* cr = platformContext()->cr();
const cairo_matrix_t matrix = cairo_matrix_t(transform);
cairo_set_matrix(cr, &matrix);
m_data->setCTM(transform);
}
-void GraphicsContext::setPlatformShadow(FloatSize const& size, float, Color const&, ColorSpace)
+void GraphicsContext::setPlatformShadow(FloatSize const& size, float, Color const&)
{
if (paintingDisabled())
return;
@@ -813,7 +858,6 @@ void GraphicsContext::setPlatformShadow(FloatSize const& size, float, Color cons
platformContext()->shadowBlur().setShadowValues(FloatSize(m_state.shadowBlur, m_state.shadowBlur),
m_state.shadowOffset,
m_state.shadowColor,
- m_state.shadowColorSpace,
m_state.shadowsIgnoreTransforms);
}
@@ -830,6 +874,8 @@ void GraphicsContext::beginPlatformTransparencyLayer(float opacity)
if (paintingDisabled())
return;
+ ASSERT(!isRecording());
+
cairo_t* cr = platformContext()->cr();
cairo_push_group(cr);
m_data->layers.append(opacity);
@@ -840,6 +886,8 @@ void GraphicsContext::endPlatformTransparencyLayer()
if (paintingDisabled())
return;
+ ASSERT(!isRecording());
+
cairo_t* cr = platformContext()->cr();
cairo_pop_group_to_source(cr);
@@ -857,6 +905,11 @@ void GraphicsContext::clearRect(const FloatRect& rect)
if (paintingDisabled())
return;
+ if (isRecording()) {
+ m_displayListRecorder->clearRect(rect);
+ return;
+ }
+
cairo_t* cr = platformContext()->cr();
cairo_save(cr);
@@ -871,11 +924,16 @@ void GraphicsContext::strokeRect(const FloatRect& rect, float width)
if (paintingDisabled())
return;
+ if (isRecording()) {
+ m_displayListRecorder->strokeRect(rect, width);
+ return;
+ }
+
cairo_t* cr = platformContext()->cr();
cairo_save(cr);
cairo_rectangle(cr, rect.x(), rect.y(), rect.width(), rect.height());
cairo_set_line_width(cr, width);
- shadowAndStrokeCurrentCairoPath(this);
+ shadowAndStrokeCurrentCairoPath(*this);
cairo_restore(cr);
}
@@ -884,6 +942,11 @@ void GraphicsContext::setLineCap(LineCap lineCap)
if (paintingDisabled())
return;
+ if (isRecording()) {
+ m_displayListRecorder->setLineCap(lineCap);
+ return;
+ }
+
cairo_line_cap_t cairoCap = CAIRO_LINE_CAP_BUTT;
switch (lineCap) {
case ButtCap:
@@ -899,9 +962,29 @@ void GraphicsContext::setLineCap(LineCap lineCap)
cairo_set_line_cap(platformContext()->cr(), cairoCap);
}
+static inline bool isDashArrayAllZero(const DashArray& dashes)
+{
+ for (auto& dash : dashes) {
+ if (dash)
+ return false;
+ }
+ return true;
+}
+
void GraphicsContext::setLineDash(const DashArray& dashes, float dashOffset)
{
- cairo_set_dash(platformContext()->cr(), dashes.data(), dashes.size(), dashOffset);
+ if (paintingDisabled())
+ return;
+
+ if (isRecording()) {
+ m_displayListRecorder->setLineDash(dashes, dashOffset);
+ return;
+ }
+
+ if (isDashArrayAllZero(dashes))
+ cairo_set_dash(platformContext()->cr(), 0, 0, 0);
+ else
+ cairo_set_dash(platformContext()->cr(), dashes.data(), dashes.size(), dashOffset);
}
void GraphicsContext::setLineJoin(LineJoin lineJoin)
@@ -909,6 +992,11 @@ void GraphicsContext::setLineJoin(LineJoin lineJoin)
if (paintingDisabled())
return;
+ if (isRecording()) {
+ m_displayListRecorder->setLineJoin(lineJoin);
+ return;
+ }
+
cairo_line_join_t cairoJoin = CAIRO_LINE_JOIN_MITER;
switch (lineJoin) {
case MiterJoin:
@@ -929,10 +1017,16 @@ void GraphicsContext::setMiterLimit(float miter)
if (paintingDisabled())
return;
+ if (isRecording()) {
+ // Maybe this should be part of the state.
+ m_displayListRecorder->setMiterLimit(miter);
+ return;
+ }
+
cairo_set_miter_limit(platformContext()->cr(), miter);
}
-void GraphicsContext::setAlpha(float alpha)
+void GraphicsContext::setPlatformAlpha(float alpha)
{
platformContext()->setGlobalAlpha(alpha);
}
@@ -942,39 +1036,12 @@ void GraphicsContext::setPlatformCompositeOperation(CompositeOperator op, BlendM
if (paintingDisabled())
return;
- cairo_operator_t cairo_op;
- if (blendOp == BlendModeNormal)
- cairo_op = toCairoOperator(op);
- else
- cairo_op = toCairoOperator(blendOp);
-
- cairo_set_operator(platformContext()->cr(), cairo_op);
-}
-
-void GraphicsContext::clip(const Path& path, WindRule windRule)
-{
- if (paintingDisabled())
- return;
-
- cairo_t* cr = platformContext()->cr();
- OwnPtr<cairo_path_t> pathCopy;
- if (!path.isNull()) {
- pathCopy = adoptPtr(cairo_copy_path(path.platformPath()->context()));
- cairo_append_path(cr, pathCopy.get());
- }
- cairo_fill_rule_t savedFillRule = cairo_get_fill_rule(cr);
- if (windRule == RULE_NONZERO)
- cairo_set_fill_rule(cr, CAIRO_FILL_RULE_WINDING);
- else
- cairo_set_fill_rule(cr, CAIRO_FILL_RULE_EVEN_ODD);
- cairo_clip(cr);
- cairo_set_fill_rule(cr, savedFillRule);
- m_data->clip(path);
+ cairo_set_operator(platformContext()->cr(), toCairoOperator(op, blendOp));
}
void GraphicsContext::canvasClip(const Path& path, WindRule windRule)
{
- clip(path, windRule);
+ clipPath(path, windRule);
}
void GraphicsContext::clipOut(const Path& path)
@@ -982,6 +1049,11 @@ void GraphicsContext::clipOut(const Path& path)
if (paintingDisabled())
return;
+ if (isRecording()) {
+ m_displayListRecorder->clipOut(path);
+ return;
+ }
+
cairo_t* cr = platformContext()->cr();
double x1, y1, x2, y2;
cairo_clip_extents(cr, &x1, &y1, &x2, &y2);
@@ -999,6 +1071,11 @@ void GraphicsContext::rotate(float radians)
if (paintingDisabled())
return;
+ if (isRecording()) {
+ m_displayListRecorder->rotate(radians);
+ return;
+ }
+
cairo_rotate(platformContext()->cr(), radians);
m_data->rotate(radians);
}
@@ -1008,15 +1085,25 @@ void GraphicsContext::scale(const FloatSize& size)
if (paintingDisabled())
return;
+ if (isRecording()) {
+ m_displayListRecorder->scale(size);
+ return;
+ }
+
cairo_scale(platformContext()->cr(), size.width(), size.height());
m_data->scale(size);
}
-void GraphicsContext::clipOut(const IntRect& r)
+void GraphicsContext::clipOut(const FloatRect& r)
{
if (paintingDisabled())
return;
+ if (isRecording()) {
+ m_displayListRecorder->clipOut(r);
+ return;
+ }
+
cairo_t* cr = platformContext()->cr();
double x1, y1, x2, y2;
cairo_clip_extents(cr, &x1, &y1, &x2, &y2);
@@ -1028,31 +1115,38 @@ void GraphicsContext::clipOut(const IntRect& r)
cairo_set_fill_rule(cr, savedFillRule);
}
-void GraphicsContext::fillRoundedRect(const IntRect& r, const IntSize& topLeft, const IntSize& topRight, const IntSize& bottomLeft, const IntSize& bottomRight, const Color& color, ColorSpace)
+void GraphicsContext::platformFillRoundedRect(const FloatRoundedRect& rect, const Color& color)
{
if (paintingDisabled())
return;
+ ASSERT(!isRecording());
+
if (hasShadow())
- platformContext()->shadowBlur().drawRectShadow(this, r, RoundedRect::Radii(topLeft, topRight, bottomLeft, bottomRight));
+ platformContext()->shadowBlur().drawRectShadow(*this, rect);
cairo_t* cr = platformContext()->cr();
cairo_save(cr);
Path path;
- path.addRoundedRect(r, topLeft, topRight, bottomLeft, bottomRight);
+ path.addRoundedRect(rect);
appendWebCorePathToCairoContext(cr, path);
setSourceRGBAFromColor(cr, color);
cairo_fill(cr);
cairo_restore(cr);
}
-void GraphicsContext::fillRectWithRoundedHole(const IntRect& rect, const RoundedRect& roundedHoleRect, const Color& color, ColorSpace)
+void GraphicsContext::fillRectWithRoundedHole(const FloatRect& rect, const FloatRoundedRect& roundedHoleRect, const Color& color)
{
if (paintingDisabled() || !color.isValid())
return;
+ if (isRecording()) {
+ m_displayListRecorder->fillRectWithRoundedHole(rect, roundedHoleRect, color);
+ return;
+ }
+
if (this->mustUseShadowBlur())
- platformContext()->shadowBlur().drawInsetShadow(this, rect, roundedHoleRect.rect(), roundedHoleRect.radii());
+ platformContext()->shadowBlur().drawInsetShadow(*this, rect, roundedHoleRect);
Path path;
path.addRect(rect);
@@ -1064,57 +1158,57 @@ void GraphicsContext::fillRectWithRoundedHole(const IntRect& rect, const Rounded
cairo_t* cr = platformContext()->cr();
cairo_save(cr);
setPathOnCairoContext(platformContext()->cr(), path.platformPath()->context());
- fillCurrentCairoPath(this);
+ fillCurrentCairoPath(*this);
cairo_restore(cr);
}
-#if PLATFORM(GTK)
-void GraphicsContext::setGdkExposeEvent(GdkEventExpose* expose)
+void GraphicsContext::drawPattern(Image& image, const FloatRect& destRect, const FloatRect& tileRect, const AffineTransform& patternTransform, const FloatPoint& phase, const FloatSize& spacing, CompositeOperator op, BlendMode blendMode)
{
- m_data->expose = expose;
-}
+ if (paintingDisabled())
+ return;
-GdkEventExpose* GraphicsContext::gdkExposeEvent() const
-{
- return m_data->expose;
-}
+ if (isRecording()) {
+ m_displayListRecorder->drawPattern(image, destRect, tileRect, patternTransform, phase, spacing, op, blendMode);
+ return;
+ }
-GdkWindow* GraphicsContext::gdkWindow() const
-{
- if (!m_data->expose)
- return 0;
+ RefPtr<cairo_surface_t> surface = image.nativeImageForCurrentFrame();
+ if (!surface) // If it's too early we won't have an image yet.
+ return;
- return m_data->expose->window;
+ cairo_t* cr = platformContext()->cr();
+ drawPatternToCairoContext(cr, surface.get(), IntSize(image.size()), tileRect, patternTransform, phase, toCairoOperator(op, blendMode), destRect);
}
-#endif
void GraphicsContext::setPlatformShouldAntialias(bool enable)
{
if (paintingDisabled())
return;
+ ASSERT(!isRecording());
+
// When true, use the default Cairo backend antialias mode (usually this
// enables standard 'grayscale' antialiasing); false to explicitly disable
- // antialiasing. This is the same strategy as used in drawConvexPolygon().
+ // antialiasing.
cairo_set_antialias(platformContext()->cr(), enable ? CAIRO_ANTIALIAS_DEFAULT : CAIRO_ANTIALIAS_NONE);
}
-void GraphicsContext::setImageInterpolationQuality(InterpolationQuality quality)
+void GraphicsContext::setPlatformImageInterpolationQuality(InterpolationQuality quality)
{
- platformContext()->setImageInterpolationQuality(quality);
-}
+ ASSERT(!isRecording());
-InterpolationQuality GraphicsContext::imageInterpolationQuality() const
-{
- return platformContext()->imageInterpolationQuality();
+ platformContext()->setImageInterpolationQuality(quality);
}
bool GraphicsContext::isAcceleratedContext() const
{
+ if (isRecording())
+ return false;
+
return cairo_surface_get_type(cairo_get_target(platformContext()->cr())) == CAIRO_SURFACE_TYPE_GL;
}
-#if ENABLE(3D_RENDERING) && USE(TEXTURE_MAPPER)
+#if ENABLE(3D_TRANSFORMS) && USE(TEXTURE_MAPPER)
TransformationMatrix GraphicsContext::get3DTransform() const
{
// FIXME: Can we approximate the transformation better than this?
@@ -1130,7 +1224,7 @@ void GraphicsContext::set3DTransform(const TransformationMatrix& transform)
{
setCTM(transform.toAffineTransform());
}
-#endif
+#endif // ENABLE(3D_TRANSFORMS) && USE(TEXTURE_MAPPER)
} // namespace WebCore
diff --git a/Source/WebCore/platform/graphics/cairo/GraphicsContextPlatformPrivateCairo.h b/Source/WebCore/platform/graphics/cairo/GraphicsContextPlatformPrivateCairo.h
index fc7c8a5d4..b2d25d6a7 100644
--- a/Source/WebCore/platform/graphics/cairo/GraphicsContextPlatformPrivateCairo.h
+++ b/Source/WebCore/platform/graphics/cairo/GraphicsContextPlatformPrivateCairo.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2006 Apple Computer, Inc. All rights reserved.
+ * Copyright (C) 2006 Apple Inc. All rights reserved.
* Copyright (C) 2007 Alp Toker <alp@atoker.com>
* Copyright (C) 2008 Brent Fulgham <bfulgham@gmail.com>
*
@@ -12,10 +12,10 @@
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
- * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
@@ -30,17 +30,15 @@
#include "GraphicsContext.h"
+#if USE(CAIRO)
+
#include "PlatformContextCairo.h"
#include "RefPtrCairo.h"
#include <cairo.h>
#include <math.h>
#include <stdio.h>
-#include <wtf/MathExtras.h>
-#if PLATFORM(GTK)
-#include <pango/pango.h>
-typedef struct _GdkExposeEvent GdkExposeEvent;
-#elif PLATFORM(WIN)
+#if PLATFORM(WIN)
#include <cairo-win32.h>
#endif
@@ -50,9 +48,6 @@ class GraphicsContextPlatformPrivate {
public:
GraphicsContextPlatformPrivate(PlatformContextCairo* newPlatformContext)
: platformContext(newPlatformContext)
-#if PLATFORM(GTK)
- , expose(0)
-#endif
#if PLATFORM(WIN) || (PLATFORM(GTK) && OS(WINDOWS))
// NOTE: These may note be needed: review and remove once Cairo implementation is complete
, m_hdc(0)
@@ -96,9 +91,6 @@ public:
PlatformContextCairo* platformContext;
Vector<float> layers;
-#if PLATFORM(GTK)
- GdkEventExpose* expose;
-#endif
#if PLATFORM(WIN) || (PLATFORM(GTK) && OS(WINDOWS))
HDC m_hdc;
bool m_shouldIncludeChildWindows;
@@ -124,4 +116,6 @@ public:
} // namespace WebCore
+#endif // USE(CAIRO)
+
#endif // GraphicsContextPlatformPrivateCairo_h
diff --git a/Source/WebCore/platform/graphics/cairo/ImageBufferCairo.cpp b/Source/WebCore/platform/graphics/cairo/ImageBufferCairo.cpp
index 0d97204f4..041373a01 100644
--- a/Source/WebCore/platform/graphics/cairo/ImageBufferCairo.cpp
+++ b/Source/WebCore/platform/graphics/cairo/ImageBufferCairo.cpp
@@ -13,10 +13,10 @@
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
- * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
@@ -29,6 +29,8 @@
#include "config.h"
#include "ImageBuffer.h"
+#if USE(CAIRO)
+
#include "BitmapImage.h"
#include "CairoUtilities.h"
#include "Color.h"
@@ -39,82 +41,201 @@
#include "PlatformContextCairo.h"
#include "RefPtrCairo.h"
#include <cairo.h>
+#include <runtime/JSCInlines.h>
+#include <runtime/TypedArrayInlines.h>
#include <wtf/Vector.h>
#include <wtf/text/Base64.h>
#include <wtf/text/WTFString.h>
#if ENABLE(ACCELERATED_2D_CANVAS)
#include "GLContext.h"
-#include "OpenGLShims.h"
#include "TextureMapperGL.h"
#include <cairo-gl.h>
+
+#if USE(OPENGL_ES_2)
+#include <GLES2/gl2.h>
+#else
+#include "OpenGLShims.h"
+#endif
+
+#if USE(COORDINATED_GRAPHICS_THREADED)
+#include "TextureMapperPlatformLayerBuffer.h"
+#include "TextureMapperPlatformLayerProxy.h"
+#endif
#endif
using namespace std;
namespace WebCore {
-ImageBufferData::ImageBufferData(const IntSize& size)
+ImageBufferData::ImageBufferData(const IntSize& size, RenderingMode renderingMode)
: m_platformContext(0)
, m_size(size)
+ , m_renderingMode(renderingMode)
#if ENABLE(ACCELERATED_2D_CANVAS)
+#if USE(COORDINATED_GRAPHICS_THREADED)
+ , m_compositorTexture(0)
+#endif
, m_texture(0)
#endif
{
+#if ENABLE(ACCELERATED_2D_CANVAS) && USE(COORDINATED_GRAPHICS_THREADED)
+ if (m_renderingMode == RenderingMode::Accelerated)
+ m_platformLayerProxy = adoptRef(new TextureMapperPlatformLayerProxy);
+#endif
+}
+
+ImageBufferData::~ImageBufferData()
+{
+ if (m_renderingMode != Accelerated)
+ return;
+
+#if ENABLE(ACCELERATED_2D_CANVAS)
+ GLContext* previousActiveContext = GLContext::current();
+ PlatformDisplay::sharedDisplayForCompositing().sharingGLContext()->makeContextCurrent();
+
+ if (m_texture)
+ glDeleteTextures(1, &m_texture);
+
+#if USE(COORDINATED_GRAPHICS_THREADED)
+ if (m_compositorTexture)
+ glDeleteTextures(1, &m_compositorTexture);
+#endif
+
+ if (previousActiveContext)
+ previousActiveContext->makeContextCurrent();
+#endif
}
#if ENABLE(ACCELERATED_2D_CANVAS)
-PassRefPtr<cairo_surface_t> createCairoGLSurface(const IntSize& size, uint32_t& texture)
+#if USE(COORDINATED_GRAPHICS_THREADED)
+void ImageBufferData::createCompositorBuffer()
+{
+ auto* context = PlatformDisplay::sharedDisplayForCompositing().sharingGLContext();
+ context->makeContextCurrent();
+
+ glGenTextures(1, &m_compositorTexture);
+ glBindTexture(GL_TEXTURE_2D, m_compositorTexture);
+ glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
+ glTexImage2D(GL_TEXTURE_2D, 0 , GL_RGBA, m_size.width(), m_size.height(), 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
+
+ m_compositorSurface = adoptRef(cairo_gl_surface_create_for_texture(context->cairoDevice(), CAIRO_CONTENT_COLOR_ALPHA, m_compositorTexture, m_size.width(), m_size.height()));
+ m_compositorCr = adoptRef(cairo_create(m_compositorSurface.get()));
+ cairo_set_antialias(m_compositorCr.get(), CAIRO_ANTIALIAS_NONE);
+}
+
+void ImageBufferData::swapBuffersIfNeeded()
+{
+ GLContext* previousActiveContext = GLContext::current();
+
+ if (!m_compositorTexture) {
+ createCompositorBuffer();
+ LockHolder holder(m_platformLayerProxy->lock());
+ m_platformLayerProxy->pushNextBuffer(std::make_unique<TextureMapperPlatformLayerBuffer>(m_compositorTexture, m_size, TextureMapperGL::ShouldBlend));
+ }
+
+ // It would be great if we could just swap the buffers here as we do with webgl, but that breaks the cases
+ // where one frame uses the content already rendered in the previous frame. So we just copy the content
+ // into the compositor buffer.
+ cairo_set_source_surface(m_compositorCr.get(), m_surface.get(), 0, 0);
+ cairo_set_operator(m_compositorCr.get(), CAIRO_OPERATOR_SOURCE);
+ cairo_paint(m_compositorCr.get());
+
+ if (previousActiveContext)
+ previousActiveContext->makeContextCurrent();
+}
+#endif
+
+void clearSurface(cairo_surface_t* surface)
+{
+ if (cairo_surface_status(surface) != CAIRO_STATUS_SUCCESS)
+ return;
+
+ RefPtr<cairo_t> cr = adoptRef(cairo_create(surface));
+ cairo_set_operator(cr.get(), CAIRO_OPERATOR_CLEAR);
+ cairo_paint(cr.get());
+}
+
+void ImageBufferData::createCairoGLSurface()
{
- GLContext::sharingContext()->makeContextCurrent();
+ auto* context = PlatformDisplay::sharedDisplayForCompositing().sharingGLContext();
+ context->makeContextCurrent();
// We must generate the texture ourselves, because there is no Cairo API for extracting it
// from a pre-existing surface.
- glGenTextures(1, &texture);
- glBindTexture(GL_TEXTURE_2D, texture);
- glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
- glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
+ glGenTextures(1, &m_texture);
+ glBindTexture(GL_TEXTURE_2D, m_texture);
+ glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
- glTexImage2D(GL_TEXTURE_2D, 0 /* level */, GL_RGBA8, size.width(), size.height(), 0 /* border */, GL_RGBA, GL_UNSIGNED_BYTE, 0);
+ glTexImage2D(GL_TEXTURE_2D, 0 /* level */, GL_RGBA, m_size.width(), m_size.height(), 0 /* border */, GL_RGBA, GL_UNSIGNED_BYTE, 0);
- GLContext* context = GLContext::sharingContext();
cairo_device_t* device = context->cairoDevice();
// Thread-awareness is a huge performance hit on non-Intel drivers.
cairo_gl_device_set_thread_aware(device, FALSE);
- return adoptRef(cairo_gl_surface_create_for_texture(device, CAIRO_CONTENT_COLOR_ALPHA, texture, size.width(), size.height()));
+ m_surface = adoptRef(cairo_gl_surface_create_for_texture(device, CAIRO_CONTENT_COLOR_ALPHA, m_texture, m_size.width(), m_size.height()));
+ clearSurface(m_surface.get());
}
#endif
-ImageBuffer::ImageBuffer(const IntSize& size, float /* resolutionScale */, ColorSpace, RenderingMode renderingMode, bool& success)
- : m_data(size)
- , m_size(size)
+ImageBuffer::ImageBuffer(const FloatSize& size, float resolutionScale, ColorSpace, RenderingMode renderingMode, bool& success)
+ : m_data(IntSize(size), renderingMode)
, m_logicalSize(size)
+ , m_resolutionScale(resolutionScale)
{
success = false; // Make early return mean error.
+
+ float scaledWidth = ceilf(m_resolutionScale * size.width());
+ float scaledHeight = ceilf(m_resolutionScale * size.height());
+
+ // FIXME: Should we automatically use a lower resolution?
+ if (!FloatSize(scaledWidth, scaledHeight).isExpressibleAsIntSize())
+ return;
+
+ m_size = IntSize(scaledWidth, scaledHeight);
+ m_data.m_size = m_size;
+
if (m_size.isEmpty())
return;
#if ENABLE(ACCELERATED_2D_CANVAS)
- if (renderingMode == Accelerated)
- m_data.m_surface = createCairoGLSurface(size, m_data.m_texture);
- else
+ if (m_data.m_renderingMode == Accelerated) {
+ m_data.createCairoGLSurface();
+ if (!m_data.m_surface || cairo_surface_status(m_data.m_surface.get()) != CAIRO_STATUS_SUCCESS)
+ m_data.m_renderingMode = Unaccelerated; // If allocation fails, fall back to non-accelerated path.
+ }
+ if (m_data.m_renderingMode == Unaccelerated)
#else
- ASSERT_UNUSED(renderingMode, renderingMode != Accelerated);
+ ASSERT(m_data.m_renderingMode != Accelerated);
#endif
- m_data.m_surface = adoptRef(cairo_image_surface_create(CAIRO_FORMAT_ARGB32, size.width(), size.height()));
+ {
+ static cairo_user_data_key_t s_surfaceDataKey;
+
+ int stride = cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, m_size.width());
+ auto* surfaceData = fastZeroedMalloc(m_size.height() * stride);
+
+ m_data.m_surface = adoptRef(cairo_image_surface_create_for_data(static_cast<unsigned char*>(surfaceData), CAIRO_FORMAT_ARGB32, m_size.width(), m_size.height(), stride));
+ cairo_surface_set_user_data(m_data.m_surface.get(), &s_surfaceDataKey, surfaceData, [](void* data) { fastFree(data); });
+ }
if (cairo_surface_status(m_data.m_surface.get()) != CAIRO_STATUS_SUCCESS)
return; // create will notice we didn't set m_initialized and fail.
+ cairoSurfaceSetDeviceScale(m_data.m_surface.get(), m_resolutionScale, m_resolutionScale);
+
RefPtr<cairo_t> cr = adoptRef(cairo_create(m_data.m_surface.get()));
m_data.m_platformContext.setCr(cr.get());
- m_context = adoptPtr(new GraphicsContext(&m_data.m_platformContext));
+ m_data.m_context = std::make_unique<GraphicsContext>(&m_data.m_platformContext);
success = true;
}
@@ -122,18 +243,29 @@ ImageBuffer::~ImageBuffer()
{
}
-GraphicsContext* ImageBuffer::context() const
+std::unique_ptr<ImageBuffer> ImageBuffer::createCompatibleBuffer(const FloatSize& size, const GraphicsContext& context)
+{
+ return createCompatibleBuffer(size, ColorSpaceSRGB, context);
+}
+
+GraphicsContext& ImageBuffer::context() const
+{
+ return *m_data.m_context;
+}
+
+RefPtr<Image> ImageBuffer::sinkIntoImage(std::unique_ptr<ImageBuffer> imageBuffer, ScaleBehavior scaleBehavior)
{
- return m_context.get();
+ return imageBuffer->copyImage(DontCopyBackingStore, scaleBehavior);
}
-PassRefPtr<Image> ImageBuffer::copyImage(BackingStoreCopy copyBehavior, ScaleBehavior) const
+RefPtr<Image> ImageBuffer::copyImage(BackingStoreCopy copyBehavior, ScaleBehavior) const
{
+ // copyCairoImageSurface inherits surface's device scale factor.
if (copyBehavior == CopyBackingStore)
return BitmapImage::create(copyCairoImageSurface(m_data.m_surface.get()));
// BitmapImage will release the passed in surface on destruction
- return BitmapImage::create(m_data.m_surface);
+ return BitmapImage::create(RefPtr<cairo_surface_t>(m_data.m_surface));
}
BackingStoreCopy ImageBuffer::fastCopyImageMode()
@@ -141,24 +273,24 @@ BackingStoreCopy ImageBuffer::fastCopyImageMode()
return DontCopyBackingStore;
}
-void ImageBuffer::clip(GraphicsContext* context, const FloatRect& maskRect) const
+void ImageBuffer::drawConsuming(std::unique_ptr<ImageBuffer> imageBuffer, GraphicsContext& destContext, const FloatRect& destRect, const FloatRect& srcRect, CompositeOperator op, BlendMode blendMode)
{
- context->platformContext()->pushImageMask(m_data.m_surface.get(), maskRect);
+ imageBuffer->draw(destContext, destRect, srcRect, op, blendMode);
}
-void ImageBuffer::draw(GraphicsContext* destinationContext, ColorSpace styleColorSpace, const FloatRect& destRect, const FloatRect& srcRect,
- CompositeOperator op, BlendMode blendMode, bool useLowQualityScale)
+void ImageBuffer::draw(GraphicsContext& destinationContext, const FloatRect& destRect, const FloatRect& srcRect,
+ CompositeOperator op, BlendMode blendMode)
{
- BackingStoreCopy copyMode = destinationContext == context() ? CopyBackingStore : DontCopyBackingStore;
+ BackingStoreCopy copyMode = &destinationContext == &context() ? CopyBackingStore : DontCopyBackingStore;
RefPtr<Image> image = copyImage(copyMode);
- destinationContext->drawImage(image.get(), styleColorSpace, destRect, srcRect, op, blendMode, ImageOrientationDescription(), useLowQualityScale);
+ destinationContext.drawImage(*image, destRect, srcRect, ImagePaintingOptions(op, blendMode, ImageOrientationDescription()));
}
-void ImageBuffer::drawPattern(GraphicsContext* context, const FloatRect& srcRect, const AffineTransform& patternTransform,
- const FloatPoint& phase, ColorSpace styleColorSpace, CompositeOperator op, const FloatRect& destRect, BlendMode)
+void ImageBuffer::drawPattern(GraphicsContext& context, const FloatRect& destRect, const FloatRect& srcRect, const AffineTransform& patternTransform,
+ const FloatPoint& phase, const FloatSize& spacing, CompositeOperator op, BlendMode)
{
- RefPtr<Image> image = copyImage(DontCopyBackingStore);
- image->drawPattern(context, srcRect, patternTransform, phase, styleColorSpace, op, destRect);
+ if (RefPtr<Image> image = copyImage(DontCopyBackingStore))
+ image->drawPattern(context, destRect, srcRect, patternTransform, phase, spacing, op);
}
void ImageBuffer::platformTransformColorSpace(const Vector<int>& lookUpTable)
@@ -181,10 +313,10 @@ void ImageBuffer::platformTransformColorSpace(const Vector<int>& lookUpTable)
*pixel = premultipliedARGBFromColor(pixelColor);
}
}
- cairo_surface_mark_dirty_rectangle(m_data.m_surface.get(), 0, 0, m_size.width(), m_size.height());
+ cairo_surface_mark_dirty_rectangle(m_data.m_surface.get(), 0, 0, m_logicalSize.width(), m_logicalSize.height());
}
-PassRefPtr<cairo_surface_t> copySurfaceToImageAndAdjustRect(cairo_surface_t* surface, IntRect& rect)
+RefPtr<cairo_surface_t> copySurfaceToImageAndAdjustRect(cairo_surface_t* surface, IntRect& rect)
{
cairo_surface_type_t surfaceType = cairo_surface_get_type(surface);
@@ -199,9 +331,11 @@ PassRefPtr<cairo_surface_t> copySurfaceToImageAndAdjustRect(cairo_surface_t* sur
}
template <Multiply multiplied>
-PassRefPtr<Uint8ClampedArray> getImageData(const IntRect& rect, const ImageBufferData& data, const IntSize& size)
+RefPtr<Uint8ClampedArray> getImageData(const IntRect& rect, const IntRect& logicalRect, const ImageBufferData& data, const IntSize& size, const IntSize& logicalSize, float resolutionScale)
{
RefPtr<Uint8ClampedArray> result = Uint8ClampedArray::createUninitialized(rect.width() * rect.height() * 4);
+ if (!result)
+ return nullptr;
if (rect.x() < 0 || rect.y() < 0 || (rect.x() + rect.width()) > size.width() || (rect.y() + rect.height()) > size.height())
result->zeroFill();
@@ -228,13 +362,17 @@ PassRefPtr<Uint8ClampedArray> getImageData(const IntRect& rect, const ImageBuffe
endy = size.height();
int numRows = endy - originy;
+ // The size of the derived surface is in BackingStoreCoordinateSystem.
+ // We need to set the device scale for the derived surface from this ImageBuffer.
IntRect imageRect(originx, originy, numColumns, numRows);
RefPtr<cairo_surface_t> imageSurface = copySurfaceToImageAndAdjustRect(data.m_surface.get(), imageRect);
+ cairoSurfaceSetDeviceScale(imageSurface.get(), resolutionScale, resolutionScale);
originx = imageRect.x();
originy = imageRect.y();
if (imageSurface != data.m_surface.get()) {
- IntRect area = intersection(rect, IntRect(0, 0, size.width(), size.height()));
- copyRectFromOneSurfaceToAnother(data.m_surface.get(), imageSurface.get(), IntSize(-area.x(), -area.y()), IntRect(IntPoint(), area.size()), IntSize(), CAIRO_OPERATOR_SOURCE);
+ // This cairo surface operation is done in LogicalCoordinateSystem.
+ IntRect logicalArea = intersection(logicalRect, IntRect(0, 0, logicalSize.width(), logicalSize.height()));
+ copyRectFromOneSurfaceToAnother(data.m_surface.get(), imageSurface.get(), IntSize(-logicalArea.x(), -logicalArea.y()), IntRect(IntPoint(), logicalArea.size()), IntSize(), CAIRO_OPERATOR_SOURCE);
}
unsigned char* dataSrc = cairo_image_surface_get_data(imageSurface.get());
@@ -275,53 +413,91 @@ PassRefPtr<Uint8ClampedArray> getImageData(const IntRect& rect, const ImageBuffe
return result.release();
}
-PassRefPtr<Uint8ClampedArray> ImageBuffer::getUnmultipliedImageData(const IntRect& rect, CoordinateSystem) const
+template<typename Unit>
+inline Unit logicalUnit(const Unit& value, ImageBuffer::CoordinateSystem coordinateSystemOfValue, float resolutionScale)
{
- return getImageData<Unmultiplied>(rect, m_data, m_size);
+ if (coordinateSystemOfValue == ImageBuffer::LogicalCoordinateSystem || resolutionScale == 1.0)
+ return value;
+ Unit result(value);
+ result.scale(1.0 / resolutionScale);
+ return result;
}
-PassRefPtr<Uint8ClampedArray> ImageBuffer::getPremultipliedImageData(const IntRect& rect, CoordinateSystem) const
+template<typename Unit>
+inline Unit backingStoreUnit(const Unit& value, ImageBuffer::CoordinateSystem coordinateSystemOfValue, float resolutionScale)
{
- return getImageData<Premultiplied>(rect, m_data, m_size);
+ if (coordinateSystemOfValue == ImageBuffer::BackingStoreCoordinateSystem || resolutionScale == 1.0)
+ return value;
+ Unit result(value);
+ result.scale(resolutionScale);
+ return result;
}
-void ImageBuffer::putByteArray(Multiply multiplied, Uint8ClampedArray* source, const IntSize& sourceSize, const IntRect& sourceRect, const IntPoint& destPoint, CoordinateSystem)
+RefPtr<Uint8ClampedArray> ImageBuffer::getUnmultipliedImageData(const IntRect& rect, CoordinateSystem coordinateSystem) const
{
+ IntRect logicalRect = logicalUnit(rect, coordinateSystem, m_resolutionScale);
+ IntRect backingStoreRect = backingStoreUnit(rect, coordinateSystem, m_resolutionScale);
+ return getImageData<Unmultiplied>(backingStoreRect, logicalRect, m_data, m_size, m_logicalSize, m_resolutionScale);
+}
- ASSERT(sourceRect.width() > 0);
- ASSERT(sourceRect.height() > 0);
+RefPtr<Uint8ClampedArray> ImageBuffer::getPremultipliedImageData(const IntRect& rect, CoordinateSystem coordinateSystem) const
+{
+ IntRect logicalRect = logicalUnit(rect, coordinateSystem, m_resolutionScale);
+ IntRect backingStoreRect = backingStoreUnit(rect, coordinateSystem, m_resolutionScale);
+ return getImageData<Premultiplied>(backingStoreRect, logicalRect, m_data, m_size, m_logicalSize, m_resolutionScale);
+}
- int originx = sourceRect.x();
- int destx = destPoint.x() + sourceRect.x();
+void ImageBuffer::putByteArray(Multiply multiplied, Uint8ClampedArray* source, const IntSize& sourceSize, const IntRect& sourceRect, const IntPoint& destPoint, CoordinateSystem coordinateSystem)
+{
+ IntRect scaledSourceRect = backingStoreUnit(sourceRect, coordinateSystem, m_resolutionScale);
+ IntSize scaledSourceSize = backingStoreUnit(sourceSize, coordinateSystem, m_resolutionScale);
+ IntPoint scaledDestPoint = backingStoreUnit(destPoint, coordinateSystem, m_resolutionScale);
+ IntRect logicalSourceRect = logicalUnit(sourceRect, coordinateSystem, m_resolutionScale);
+ IntPoint logicalDestPoint = logicalUnit(destPoint, coordinateSystem, m_resolutionScale);
+
+ ASSERT(scaledSourceRect.width() > 0);
+ ASSERT(scaledSourceRect.height() > 0);
+
+ int originx = scaledSourceRect.x();
+ int destx = scaledDestPoint.x() + scaledSourceRect.x();
+ int logicalDestx = logicalDestPoint.x() + logicalSourceRect.x();
ASSERT(destx >= 0);
ASSERT(destx < m_size.width());
ASSERT(originx >= 0);
- ASSERT(originx <= sourceRect.maxX());
+ ASSERT(originx <= scaledSourceRect.maxX());
- int endx = destPoint.x() + sourceRect.maxX();
+ int endx = scaledDestPoint.x() + scaledSourceRect.maxX();
+ int logicalEndx = logicalDestPoint.x() + logicalSourceRect.maxX();
ASSERT(endx <= m_size.width());
int numColumns = endx - destx;
+ int logicalNumColumns = logicalEndx - logicalDestx;
- int originy = sourceRect.y();
- int desty = destPoint.y() + sourceRect.y();
+ int originy = scaledSourceRect.y();
+ int desty = scaledDestPoint.y() + scaledSourceRect.y();
+ int logicalDesty = logicalDestPoint.y() + logicalSourceRect.y();
ASSERT(desty >= 0);
ASSERT(desty < m_size.height());
ASSERT(originy >= 0);
- ASSERT(originy <= sourceRect.maxY());
+ ASSERT(originy <= scaledSourceRect.maxY());
- int endy = destPoint.y() + sourceRect.maxY();
+ int endy = scaledDestPoint.y() + scaledSourceRect.maxY();
+ int logicalEndy = logicalDestPoint.y() + logicalSourceRect.maxY();
ASSERT(endy <= m_size.height());
int numRows = endy - desty;
+ int logicalNumRows = logicalEndy - logicalDesty;
+ // The size of the derived surface is in BackingStoreCoordinateSystem.
+ // We need to set the device scale for the derived surface from this ImageBuffer.
IntRect imageRect(destx, desty, numColumns, numRows);
RefPtr<cairo_surface_t> imageSurface = copySurfaceToImageAndAdjustRect(m_data.m_surface.get(), imageRect);
+ cairoSurfaceSetDeviceScale(imageSurface.get(), m_resolutionScale, m_resolutionScale);
destx = imageRect.x();
desty = imageRect.y();
unsigned char* pixelData = cairo_image_surface_get_data(imageSurface.get());
- unsigned srcBytesPerRow = 4 * sourceSize.width();
+ unsigned srcBytesPerRow = 4 * scaledSourceSize.width();
int stride = cairo_image_surface_get_stride(imageSurface.get());
unsigned char* srcRows = source->data() + originy * srcBytesPerRow + originx * 4;
@@ -351,10 +527,13 @@ void ImageBuffer::putByteArray(Multiply multiplied, Uint8ClampedArray* source, c
srcRows += srcBytesPerRow;
}
- cairo_surface_mark_dirty_rectangle(imageSurface.get(), destx, desty, numColumns, numRows);
+ // This cairo surface operation is done in LogicalCoordinateSystem.
+ cairo_surface_mark_dirty_rectangle(imageSurface.get(), logicalDestx, logicalDesty, logicalNumColumns, logicalNumRows);
- if (imageSurface != m_data.m_surface.get())
- copyRectFromOneSurfaceToAnother(imageSurface.get(), m_data.m_surface.get(), IntSize(), IntRect(0, 0, numColumns, numRows), IntSize(destPoint.x() + sourceRect.x(), destPoint.y() + sourceRect.y()), CAIRO_OPERATOR_SOURCE);
+ if (imageSurface != m_data.m_surface.get()) {
+ // This cairo surface operation is done in LogicalCoordinateSystem.
+ copyRectFromOneSurfaceToAnother(imageSurface.get(), m_data.m_surface.get(), IntSize(), IntRect(0, 0, logicalNumColumns, logicalNumRows), IntSize(logicalDestPoint.x() + logicalSourceRect.x(), logicalDestPoint.y() + logicalSourceRect.y()), CAIRO_OPERATOR_SOURCE);
+ }
}
#if !PLATFORM(GTK)
@@ -372,11 +551,11 @@ static bool encodeImage(cairo_surface_t* image, const String& mimeType, Vector<c
return cairo_surface_write_to_png_stream(image, writeFunction, output) == CAIRO_STATUS_SUCCESS;
}
-String ImageBuffer::toDataURL(const String& mimeType, const double*, CoordinateSystem) const
+String ImageBuffer::toDataURL(const String& mimeType, std::optional<double> quality, CoordinateSystem) const
{
ASSERT(MIMETypeRegistry::isSupportedImageMIMETypeForEncoding(mimeType));
- cairo_surface_t* image = cairo_get_target(context()->platformContext()->cr());
+ cairo_surface_t* image = cairo_get_target(context().platformContext()->cr());
Vector<char> encodedImage;
if (!image || !encodeImage(image, mimeType, &encodedImage))
@@ -389,26 +568,20 @@ String ImageBuffer::toDataURL(const String& mimeType, const double*, CoordinateS
}
#endif
-#if ENABLE(ACCELERATED_2D_CANVAS)
-void ImageBufferData::paintToTextureMapper(TextureMapper* textureMapper, const FloatRect& targetRect, const TransformationMatrix& matrix, float opacity)
+#if ENABLE(ACCELERATED_2D_CANVAS) && !USE(COORDINATED_GRAPHICS_THREADED)
+void ImageBufferData::paintToTextureMapper(TextureMapper& textureMapper, const FloatRect& targetRect, const TransformationMatrix& matrix, float opacity)
{
- if (textureMapper->accelerationMode() != TextureMapper::OpenGLMode) {
- notImplemented();
- return;
- }
-
ASSERT(m_texture);
// Cairo may change the active context, so we make sure to change it back after flushing.
- GLContext* previousActiveContext = GLContext::getCurrent();
+ GLContext* previousActiveContext = GLContext::current();
cairo_surface_flush(m_surface.get());
previousActiveContext->makeContextCurrent();
- static_cast<TextureMapperGL*>(textureMapper)->drawTexture(m_texture, TextureMapperGL::ShouldBlend, m_size, targetRect, matrix, opacity);
+ static_cast<TextureMapperGL&>(textureMapper).drawTexture(m_texture, TextureMapperGL::ShouldBlend, m_size, targetRect, matrix, opacity);
}
#endif
-#if USE(ACCELERATED_COMPOSITING)
PlatformLayer* ImageBuffer::platformLayer() const
{
#if ENABLE(ACCELERATED_2D_CANVAS)
@@ -417,6 +590,59 @@ PlatformLayer* ImageBuffer::platformLayer() const
#endif
return 0;
}
+
+bool ImageBuffer::copyToPlatformTexture(GraphicsContext3D&, GC3Denum target, Platform3DObject destinationTexture, GC3Denum internalformat, bool premultiplyAlpha, bool flipY)
+{
+#if ENABLE(ACCELERATED_2D_CANVAS)
+ ASSERT_WITH_MESSAGE(m_resolutionScale == 1.0, "Since the HiDPI Canvas feature is removed, the resolution factor here is always 1.");
+ if (premultiplyAlpha || flipY)
+ return false;
+
+ if (!m_data.m_texture)
+ return false;
+
+ GC3Denum bindTextureTarget;
+ switch (target) {
+ case GL_TEXTURE_2D:
+ bindTextureTarget = GL_TEXTURE_2D;
+ break;
+ case GL_TEXTURE_CUBE_MAP_POSITIVE_X:
+ case GL_TEXTURE_CUBE_MAP_NEGATIVE_X:
+ case GL_TEXTURE_CUBE_MAP_POSITIVE_Y:
+ case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y:
+ case GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
+ case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z:
+ bindTextureTarget = GL_TEXTURE_CUBE_MAP;
+ break;
+ default:
+ return false;
+ }
+
+ cairo_surface_flush(m_data.m_surface.get());
+
+ std::unique_ptr<GLContext> context = GLContext::createOffscreenContext(&PlatformDisplay::sharedDisplayForCompositing());
+ context->makeContextCurrent();
+ uint32_t fbo;
+ glGenFramebuffers(1, &fbo);
+ glBindFramebuffer(GL_FRAMEBUFFER, fbo);
+ glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, m_data.m_texture, 0);
+ glBindTexture(bindTextureTarget, destinationTexture);
+ glCopyTexImage2D(target, 0, internalformat, 0, 0, m_size.width(), m_size.height(), 0);
+ glBindTexture(bindTextureTarget, 0);
+ glBindFramebuffer(GL_FRAMEBUFFER, 0);
+ glFlush();
+ glDeleteFramebuffers(1, &fbo);
+ return true;
+#else
+ UNUSED_PARAM(target);
+ UNUSED_PARAM(destinationTexture);
+ UNUSED_PARAM(internalformat);
+ UNUSED_PARAM(premultiplyAlpha);
+ UNUSED_PARAM(flipY);
+ return false;
#endif
+}
} // namespace WebCore
+
+#endif // USE(CAIRO)
diff --git a/Source/WebCore/platform/graphics/cairo/ImageBufferDataCairo.h b/Source/WebCore/platform/graphics/cairo/ImageBufferDataCairo.h
index 72ea4c34f..93d817614 100644
--- a/Source/WebCore/platform/graphics/cairo/ImageBufferDataCairo.h
+++ b/Source/WebCore/platform/graphics/cairo/ImageBufferDataCairo.h
@@ -10,10 +10,10 @@
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
- * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
@@ -23,12 +23,19 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
+#ifndef ImageBufferDataCairo_h
+#define ImageBufferDataCairo_h
+
+#if USE(CAIRO)
+
#include "PlatformContextCairo.h"
#include "RefPtrCairo.h"
#if ENABLE(ACCELERATED_2D_CANVAS)
+#include "PlatformLayer.h"
#include "TextureMapper.h"
#include "TextureMapperPlatformLayer.h"
+#include "TextureMapperPlatformLayerProxy.h"
#endif
namespace WebCore {
@@ -37,20 +44,40 @@ class IntSize;
class ImageBufferData
#if ENABLE(ACCELERATED_2D_CANVAS)
- : public TextureMapperPlatformLayer
+ : public PlatformLayer
#endif
{
public:
- ImageBufferData(const IntSize&);
+ ImageBufferData(const IntSize&, RenderingMode);
+ virtual ~ImageBufferData();
RefPtr<cairo_surface_t> m_surface;
PlatformContextCairo m_platformContext;
+ std::unique_ptr<GraphicsContext> m_context;
IntSize m_size;
+ RenderingMode m_renderingMode;
#if ENABLE(ACCELERATED_2D_CANVAS)
- virtual void paintToTextureMapper(TextureMapper*, const FloatRect& target, const TransformationMatrix&, float opacity);
+ void createCairoGLSurface();
+
+#if USE(COORDINATED_GRAPHICS_THREADED)
+ RefPtr<TextureMapperPlatformLayerProxy> proxy() const override { return m_platformLayerProxy.copyRef(); }
+ void swapBuffersIfNeeded() override;
+ void createCompositorBuffer();
+
+ RefPtr<TextureMapperPlatformLayerProxy> m_platformLayerProxy;
+ RefPtr<cairo_surface_t> m_compositorSurface;
+ uint32_t m_compositorTexture;
+ RefPtr<cairo_t> m_compositorCr;
+#else
+ virtual void paintToTextureMapper(TextureMapper&, const FloatRect& target, const TransformationMatrix&, float opacity);
+#endif
uint32_t m_texture;
#endif
};
} // namespace WebCore
+
+#endif // USE(CAIRO)
+
+#endif // ImageBufferDataCairo_h
diff --git a/Source/WebCore/platform/graphics/cairo/ImageCairo.cpp b/Source/WebCore/platform/graphics/cairo/ImageCairo.cpp
index 46a39b45b..4e5365958 100644
--- a/Source/WebCore/platform/graphics/cairo/ImageCairo.cpp
+++ b/Source/WebCore/platform/graphics/cairo/ImageCairo.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2004, 2005, 2006 Apple Computer, Inc. All rights reserved.
+ * Copyright (C) 2004, 2005, 2006 Apple Inc. All rights reserved.
* Copyright (C) 2007 Alp Toker <alp@atoker.com>
* Copyright (C) 2009 Dirk Schulze <krit@webkit.org>
*
@@ -12,10 +12,10 @@
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
- * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
@@ -31,30 +31,21 @@
#if USE(CAIRO)
#include "AffineTransform.h"
-#include "CairoUtilities.h"
#include "Color.h"
#include "GraphicsContext.h"
#include "ImageObserver.h"
-#include "PlatformContextCairo.h"
-#include <cairo.h>
-#include <math.h>
namespace WebCore {
-void Image::drawPattern(GraphicsContext* context, const FloatRect& tileRect, const AffineTransform& patternTransform,
- const FloatPoint& phase, ColorSpace, CompositeOperator op, const FloatRect& destRect, BlendMode)
+void Image::drawPattern(GraphicsContext& context, const FloatRect& destRect, const FloatRect& tileRect, const AffineTransform& patternTransform,
+ const FloatPoint& phase, const FloatSize& spacing, CompositeOperator op, BlendMode blendMode)
{
- RefPtr<cairo_surface_t> surface = nativeImageForCurrentFrame();
- if (!surface) // If it's too early we won't have an image yet.
- return;
-
- cairo_t* cr = context->platformContext()->cr();
- drawPatternToCairoContext(cr, surface.get(), size(), tileRect, patternTransform, phase, toCairoOperator(op), destRect);
+ context.drawPattern(*this, destRect, tileRect, patternTransform, phase, spacing, op, blendMode);
if (imageObserver())
imageObserver()->didDraw(this);
}
-}
+} // namespace WebCore
#endif // USE(CAIRO)
diff --git a/Source/WebCore/platform/graphics/cairo/IntRectCairo.cpp b/Source/WebCore/platform/graphics/cairo/IntRectCairo.cpp
index cef3af905..054bd2ac3 100644
--- a/Source/WebCore/platform/graphics/cairo/IntRectCairo.cpp
+++ b/Source/WebCore/platform/graphics/cairo/IntRectCairo.cpp
@@ -21,6 +21,8 @@
#include "config.h"
#include "IntRect.h"
+#if USE(CAIRO)
+
#include <cairo.h>
namespace WebCore {
@@ -37,4 +39,6 @@ IntRect::operator cairo_rectangle_int_t() const
return r;
}
-}
+} // namespace WebCore
+
+#endif // USE(CAIRO)
diff --git a/Source/WebCore/platform/graphics/cairo/NativeImageCairo.cpp b/Source/WebCore/platform/graphics/cairo/NativeImageCairo.cpp
new file mode 100644
index 000000000..69b7bb0f8
--- /dev/null
+++ b/Source/WebCore/platform/graphics/cairo/NativeImageCairo.cpp
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2016 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "NativeImage.h"
+
+#if USE(CAIRO)
+
+#include "CairoUtilities.h"
+#include "PlatformContextCairo.h"
+#include <cairo.h>
+
+namespace WebCore {
+
+IntSize nativeImageSize(const NativeImagePtr& image)
+{
+ return image ? cairoSurfaceSize(image.get()) : IntSize();
+}
+
+bool nativeImageHasAlpha(const NativeImagePtr& image)
+{
+ return !image || cairo_surface_get_content(image.get()) != CAIRO_CONTENT_COLOR;
+}
+
+Color nativeImageSinglePixelSolidColor(const NativeImagePtr& image)
+{
+ if (!image || nativeImageSize(image) != IntSize(1, 1))
+ return Color();
+
+ if (cairo_surface_get_type(image.get()) != CAIRO_SURFACE_TYPE_IMAGE)
+ return Color();
+
+ RGBA32* pixel = reinterpret_cast_ptr<RGBA32*>(cairo_image_surface_get_data(image.get()));
+ return colorFromPremultipliedARGB(*pixel);
+}
+
+float subsamplingScale(GraphicsContext&, const FloatRect&, const FloatRect&)
+{
+ return 1;
+}
+
+void drawNativeImage(const NativeImagePtr& image, GraphicsContext& context, const FloatRect& destRect, const FloatRect& srcRect, const IntSize&, CompositeOperator op, BlendMode mode, const ImageOrientation& orientation)
+{
+ context.save();
+
+ // Set the compositing operation.
+ if (op == CompositeSourceOver && mode == BlendModeNormal && !nativeImageHasAlpha(image))
+ context.setCompositeOperation(CompositeCopy);
+ else
+ context.setCompositeOperation(op, mode);
+
+#if ENABLE(IMAGE_DECODER_DOWN_SAMPLING)
+ IntSize scaledSize = nativeImageSize(image);
+ FloatRect adjustedSrcRect = adjustSourceRectForDownSampling(srcRect, scaledSize);
+#else
+ FloatRect adjustedSrcRect(srcRect);
+#endif
+
+ FloatRect adjustedDestRect = destRect;
+
+ if (orientation != DefaultImageOrientation) {
+ // ImageOrientation expects the origin to be at (0, 0).
+ context.translate(destRect.x(), destRect.y());
+ adjustedDestRect.setLocation(FloatPoint());
+ context.concatCTM(orientation.transformFromDefault(adjustedDestRect.size()));
+ if (orientation.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.
+ adjustedDestRect.setSize(adjustedDestRect.size().transposedSize());
+ }
+ }
+
+ context.platformContext()->drawSurfaceToContext(image.get(), adjustedDestRect, adjustedSrcRect, context);
+ context.restore();
+}
+
+void clearNativeImageSubimages(const NativeImagePtr&)
+{
+}
+
+} // namespace WebCore
+
+#endif // USE(CAIRO)
diff --git a/Source/WebCore/platform/graphics/cairo/OwnPtrCairo.cpp b/Source/WebCore/platform/graphics/cairo/OwnPtrCairo.cpp
deleted file mode 100644
index 1594e7b5d..000000000
--- a/Source/WebCore/platform/graphics/cairo/OwnPtrCairo.cpp
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * Copyright (C) 2010 Collabora Ltd.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free
- * Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
- * Boston, MA 02110-1301 USA
- */
-
-#include "config.h"
-#include "OwnPtrCairo.h"
-
-#if USE(FREETYPE)
-#include <cairo-ft.h>
-#include <fontconfig/fcfreetype.h>
-#endif
-
-#include <cairo.h>
-
-namespace WTF {
-
-#if USE(FREETYPE)
-template <> void deleteOwnedPtr<FcObjectSet>(FcObjectSet* ptr)
-{
- if (ptr)
- FcObjectSetDestroy(ptr);
-}
-
-template <> void deleteOwnedPtr<FcFontSet>(FcFontSet* ptr)
-{
- if (ptr)
- FcFontSetDestroy(ptr);
-}
-#endif
-
-template <> void deleteOwnedPtr<cairo_path_t>(cairo_path_t* ptr)
-{
- if (ptr)
- cairo_path_destroy(ptr);
-}
-
-} // namespace WTF
diff --git a/Source/WebCore/platform/graphics/cairo/OwnPtrCairo.h b/Source/WebCore/platform/graphics/cairo/OwnPtrCairo.h
deleted file mode 100644
index 3704b6a17..000000000
--- a/Source/WebCore/platform/graphics/cairo/OwnPtrCairo.h
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * Copyright (C) 2010 Collabora Ltd.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free
- * Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
- * Boston, MA 02110-1301 USA
- */
-
-#ifndef OwnPtrCairo_h
-#define OwnPtrCairo_h
-
-#include <wtf/OwnPtr.h>
-#include <wtf/PassOwnPtr.h>
-
-#if USE(FREETYPE)
-typedef struct _FcObjectSet FcObjectSet;
-typedef struct _FcFontSet FcFontSet;
-#endif
-
-typedef struct cairo_path cairo_path_t;
-
-namespace WTF {
-
-#if USE(FREETYPE)
-template <> void deleteOwnedPtr<FcObjectSet>(FcObjectSet*);
-template <> void deleteOwnedPtr<FcFontSet>(FcFontSet*);
-#endif
-
-template <> void deleteOwnedPtr<cairo_path_t>(cairo_path_t*);
-
-} // namespace WTF
-
-#endif
diff --git a/Source/WebCore/platform/graphics/cairo/PathCairo.cpp b/Source/WebCore/platform/graphics/cairo/PathCairo.cpp
index 5fac5f0b9..6ab79100c 100644
--- a/Source/WebCore/platform/graphics/cairo/PathCairo.cpp
+++ b/Source/WebCore/platform/graphics/cairo/PathCairo.cpp
@@ -26,10 +26,11 @@
#include "config.h"
#include "Path.h"
+#if USE(CAIRO)
+
#include "AffineTransform.h"
#include "FloatRect.h"
#include "GraphicsContext.h"
-#include "OwnPtrCairo.h"
#include "PlatformPathCairo.h"
#include "StrokeStyleApplier.h"
#include <cairo.h>
@@ -57,8 +58,9 @@ Path::Path(const Path& other)
return;
cairo_t* cr = ensurePlatformPath()->context();
- OwnPtr<cairo_path_t> pathCopy = adoptPtr(cairo_copy_path(other.platformPath()->context()));
- cairo_append_path(cr, pathCopy.get());
+ auto pathCopy = cairo_copy_path(other.platformPath()->context());
+ cairo_append_path(cr, pathCopy);
+ cairo_path_destroy(pathCopy);
}
PlatformPathPtr Path::ensurePlatformPath()
@@ -81,8 +83,9 @@ Path& Path::operator=(const Path& other)
} else {
clear();
cairo_t* cr = ensurePlatformPath()->context();
- OwnPtr<cairo_path_t> pathCopy = adoptPtr(cairo_copy_path(other.platformPath()->context()));
- cairo_append_path(cr, pathCopy.get());
+ auto pathCopy = cairo_copy_path(other.platformPath()->context());
+ cairo_append_path(cr, pathCopy);
+ cairo_path_destroy(pathCopy);
}
return *this;
@@ -281,6 +284,22 @@ void Path::addArcTo(const FloatPoint& p1, const FloatPoint& p2, float radius)
addArc(p, radius, sa, ea, anticlockwise);
}
+void Path::addEllipse(FloatPoint point, float radiusX, float radiusY, float rotation, float startAngle, float endAngle, bool anticlockwise)
+{
+ cairo_t* cr = ensurePlatformPath()->context();
+ cairo_save(cr);
+ cairo_translate(cr, point.x(), point.y());
+ cairo_rotate(cr, rotation);
+ cairo_scale(cr, radiusX, radiusY);
+
+ if (anticlockwise)
+ cairo_arc_negative(cr, 0, 0, 1, startAngle, endAngle);
+ else
+ cairo_arc(cr, 0, 0, 1, startAngle, endAngle);
+
+ cairo_restore(cr);
+}
+
void Path::addEllipse(const FloatRect& rect)
{
cairo_t* cr = ensurePlatformPath()->context();
@@ -293,6 +312,24 @@ void Path::addEllipse(const FloatRect& rect)
cairo_restore(cr);
}
+void Path::addPath(const Path& path, const AffineTransform& transform)
+{
+ if (path.isNull())
+ return;
+
+ cairo_matrix_t matrix(transform);
+ if (cairo_matrix_invert(&matrix) != CAIRO_STATUS_SUCCESS)
+ return;
+
+ cairo_t* cr = path.platformPath()->context();
+ cairo_save(cr);
+ cairo_transform(cr, &matrix);
+ auto pathCopy = cairo_copy_path(cr);
+ cairo_restore(cr);
+ cairo_append_path(ensurePlatformPath()->context(), pathCopy);
+ cairo_path_destroy(pathCopy);
+}
+
void Path::closeSubpath()
{
cairo_t* cr = ensurePlatformPath()->context();
@@ -353,13 +390,13 @@ bool Path::strokeContains(StrokeStyleApplier* applier, const FloatPoint& point)
return cairo_in_stroke(cr, point.x(), point.y());
}
-void Path::apply(void* info, PathApplierFunction function) const
+void Path::apply(const PathApplierFunction& function) const
{
if (isNull())
return;
cairo_t* cr = platformPath()->context();
- OwnPtr<cairo_path_t> pathCopy = adoptPtr(cairo_copy_path(cr));
+ auto pathCopy = cairo_copy_path(cr);
cairo_path_data_t* data;
PathElement pelement;
FloatPoint points[3];
@@ -371,26 +408,27 @@ void Path::apply(void* info, PathApplierFunction function) const
case CAIRO_PATH_MOVE_TO:
pelement.type = PathElementMoveToPoint;
pelement.points[0] = FloatPoint(data[1].point.x,data[1].point.y);
- function(info, &pelement);
+ function(pelement);
break;
case CAIRO_PATH_LINE_TO:
pelement.type = PathElementAddLineToPoint;
pelement.points[0] = FloatPoint(data[1].point.x,data[1].point.y);
- function(info, &pelement);
+ function(pelement);
break;
case CAIRO_PATH_CURVE_TO:
pelement.type = PathElementAddCurveToPoint;
pelement.points[0] = FloatPoint(data[1].point.x,data[1].point.y);
pelement.points[1] = FloatPoint(data[2].point.x,data[2].point.y);
pelement.points[2] = FloatPoint(data[3].point.x,data[3].point.y);
- function(info, &pelement);
+ function(pelement);
break;
case CAIRO_PATH_CLOSE_PATH:
pelement.type = PathElementCloseSubpath;
- function(info, &pelement);
+ function(pelement);
break;
}
}
+ cairo_path_destroy(pathCopy);
}
void Path::transform(const AffineTransform& trans)
@@ -402,3 +440,5 @@ void Path::transform(const AffineTransform& trans)
}
} // namespace WebCore
+
+#endif // USE(CAIRO)
diff --git a/Source/WebCore/platform/graphics/cairo/PatternCairo.cpp b/Source/WebCore/platform/graphics/cairo/PatternCairo.cpp
index 8870d3e03..81bc7ce96 100644
--- a/Source/WebCore/platform/graphics/cairo/PatternCairo.cpp
+++ b/Source/WebCore/platform/graphics/cairo/PatternCairo.cpp
@@ -10,10 +10,10 @@
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
- * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
@@ -26,6 +26,8 @@
#include "config.h"
#include "Pattern.h"
+#if USE(CAIRO)
+
#include "AffineTransform.h"
#include "GraphicsContext.h"
#include <cairo.h>
@@ -50,4 +52,6 @@ cairo_pattern_t* Pattern::createPlatformPattern(const AffineTransform&) const
return pattern;
}
-}
+} // namespace WebCore
+
+#endif // USE(CAIRO)
diff --git a/Source/WebCore/platform/graphics/cairo/PlatformContextCairo.cpp b/Source/WebCore/platform/graphics/cairo/PlatformContextCairo.cpp
index fc2b2b0fc..65d7eeee9 100644
--- a/Source/WebCore/platform/graphics/cairo/PlatformContextCairo.cpp
+++ b/Source/WebCore/platform/graphics/cairo/PlatformContextCairo.cpp
@@ -12,10 +12,10 @@
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
- * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
@@ -28,10 +28,11 @@
#include "config.h"
#include "PlatformContextCairo.h"
+#if USE(CAIRO)
+
#include "CairoUtilities.h"
#include "Gradient.h"
#include "GraphicsContext.h"
-#include "OwnPtrCairo.h"
#include "Pattern.h"
#include <cairo.h>
@@ -136,9 +137,13 @@ void PlatformContextCairo::pushImageMask(cairo_surface_t* surface, const FloatRe
cairo_push_group(m_cr.get());
cairo_set_operator(m_cr.get(), CAIRO_OPERATOR_SOURCE);
- cairo_set_source_surface(m_cr.get(), currentTarget, 0, 0);
- cairo_rectangle(m_cr.get(), rect.x(), rect.y(), rect.width(), rect.height());
+ // To avoid the limit of Pixman backend, we need to reduce the size of pattern matrix
+ // See https://bugs.webkit.org/show_bug.cgi?id=154283
+ cairo_set_source_surface(m_cr.get(), currentTarget, rect.x(), rect.y());
+ cairo_translate(m_cr.get(), rect.x(), rect.y());
+ cairo_rectangle(m_cr.get(), 0, 0, rect.width(), rect.height());
cairo_fill(m_cr.get());
+ cairo_translate(m_cr.get(), -rect.x(), -rect.y());
}
static void drawPatternToCairoContext(cairo_t* cr, cairo_pattern_t* pattern, const FloatRect& destRect, float alpha)
@@ -154,7 +159,7 @@ static void drawPatternToCairoContext(cairo_t* cr, cairo_pattern_t* pattern, con
cairo_fill(cr);
}
-void PlatformContextCairo::drawSurfaceToContext(cairo_surface_t* surface, const FloatRect& destRect, const FloatRect& originalSrcRect, GraphicsContext* context)
+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)
@@ -197,11 +202,11 @@ void PlatformContextCairo::drawSurfaceToContext(cairo_surface_t* surface, const
cairo_pattern_set_filter(pattern.get(), CAIRO_FILTER_FAST);
break;
case InterpolationMedium:
- case InterpolationHigh:
- cairo_pattern_set_filter(pattern.get(), CAIRO_FILTER_BILINEAR);
- break;
case InterpolationDefault:
- cairo_pattern_set_filter(pattern.get(), CAIRO_FILTER_BILINEAR);
+ 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);
@@ -215,7 +220,7 @@ void PlatformContextCairo::drawSurfaceToContext(cairo_surface_t* surface, const
cairo_matrix_t matrix = { scaleX, 0, 0, scaleY, leftPadding, topPadding };
cairo_pattern_set_matrix(pattern.get(), &matrix);
- ShadowBlur& shadow = context->platformContext()->shadowBlur();
+ 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);
@@ -304,7 +309,7 @@ void PlatformContextCairo::clipForPatternFilling(const GraphicsContextState& sta
ASSERT(state.fillPattern);
// Hold current cairo path in a variable for restoring it after configuring the pattern clip rectangle.
- OwnPtr<cairo_path_t> currentPath = adoptPtr(cairo_copy_path(m_cr.get()));
+ auto currentPath = cairo_copy_path(m_cr.get());
cairo_new_path(m_cr.get());
// Initialize clipping extent from current cairo clip extents, then shrink if needed according to pattern.
@@ -335,7 +340,10 @@ void PlatformContextCairo::clipForPatternFilling(const GraphicsContextState& sta
}
// Restoring cairo path.
- cairo_append_path(m_cr.get(), currentPath.get());
+ cairo_append_path(m_cr.get(), currentPath);
+ cairo_path_destroy(currentPath);
}
} // namespace WebCore
+
+#endif // USE(CAIRO)
diff --git a/Source/WebCore/platform/graphics/cairo/PlatformContextCairo.h b/Source/WebCore/platform/graphics/cairo/PlatformContextCairo.h
index d7f1205f7..642a3052c 100644
--- a/Source/WebCore/platform/graphics/cairo/PlatformContextCairo.h
+++ b/Source/WebCore/platform/graphics/cairo/PlatformContextCairo.h
@@ -10,10 +10,10 @@
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
- * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
@@ -26,6 +26,8 @@
#ifndef PlatformContextCairo_h
#define PlatformContextCairo_h
+#if USE(CAIRO)
+
#include "GraphicsContext.h"
#include "RefPtrCairo.h"
#include "ShadowBlur.h"
@@ -56,7 +58,7 @@ public:
float globalAlpha() const;
void pushImageMask(cairo_surface_t*, const FloatRect&);
- void drawSurfaceToContext(cairo_surface_t*, const FloatRect& destRect, const FloatRect& srcRect, GraphicsContext*);
+ void drawSurfaceToContext(cairo_surface_t*, const FloatRect& destRect, const FloatRect& srcRect, GraphicsContext&);
void setImageInterpolationQuality(InterpolationQuality);
InterpolationQuality imageInterpolationQuality() const;
@@ -84,4 +86,6 @@ private:
} // namespace WebCore
+#endif // USE(CAIRO)
+
#endif // PlatformContextCairo_h
diff --git a/Source/WebCore/platform/graphics/cairo/PlatformPathCairo.cpp b/Source/WebCore/platform/graphics/cairo/PlatformPathCairo.cpp
index 3a7d5128a..14c8d9bcd 100644
--- a/Source/WebCore/platform/graphics/cairo/PlatformPathCairo.cpp
+++ b/Source/WebCore/platform/graphics/cairo/PlatformPathCairo.cpp
@@ -20,20 +20,23 @@
#include "config.h"
#include "PlatformPathCairo.h"
+#if USE(CAIRO)
+
#include <cairo.h>
namespace WebCore {
-static cairo_surface_t* getPathSurface()
+static cairo_surface_t* pathSurface()
{
- return cairo_image_surface_create(CAIRO_FORMAT_A8, 1, 1);
+ static cairo_surface_t* s_pathSurface = cairo_image_surface_create(CAIRO_FORMAT_A8, 1, 1);
+ return s_pathSurface;
}
-static cairo_surface_t* gPathSurface = getPathSurface();
-
CairoPath::CairoPath()
- : m_cr(adoptRef(cairo_create(gPathSurface)))
+ : m_cr(adoptRef(cairo_create(pathSurface())))
{
}
-}
+} // namespace WebCore
+
+#endif // USE(CAIRO)
diff --git a/Source/WebCore/platform/graphics/cairo/PlatformPathCairo.h b/Source/WebCore/platform/graphics/cairo/PlatformPathCairo.h
index 938b942a4..5f1059446 100644
--- a/Source/WebCore/platform/graphics/cairo/PlatformPathCairo.h
+++ b/Source/WebCore/platform/graphics/cairo/PlatformPathCairo.h
@@ -21,6 +21,8 @@
#ifndef PlatformPathCairo_h
#define PlatformPathCairo_h
+#if USE(CAIRO)
+
#include "RefPtrCairo.h"
namespace WebCore {
@@ -40,4 +42,6 @@ private:
} // namespace WebCore
+#endif // USE(CAIRO)
+
#endif // PlatformPathCairo_h
diff --git a/Source/WebCore/platform/graphics/cairo/RefPtrCairo.cpp b/Source/WebCore/platform/graphics/cairo/RefPtrCairo.cpp
index 0f55f5797..61a0369f5 100644
--- a/Source/WebCore/platform/graphics/cairo/RefPtrCairo.cpp
+++ b/Source/WebCore/platform/graphics/cairo/RefPtrCairo.cpp
@@ -19,6 +19,8 @@
#include "config.h"
#include "RefPtrCairo.h"
+#if USE(CAIRO)
+
#include <cairo.h>
#if USE(FREETYPE)
@@ -30,89 +32,102 @@ namespace WTF {
template<> void refIfNotNull(cairo_t* ptr)
{
- if (LIKELY(ptr != 0))
+ if (LIKELY(ptr))
cairo_reference(ptr);
}
template<> void derefIfNotNull(cairo_t* ptr)
{
- if (LIKELY(ptr != 0))
+ if (LIKELY(ptr))
cairo_destroy(ptr);
}
template<> void refIfNotNull(cairo_surface_t* ptr)
{
- if (LIKELY(ptr != 0))
+ if (LIKELY(ptr))
cairo_surface_reference(ptr);
}
template<> void derefIfNotNull(cairo_surface_t* ptr)
{
- if (LIKELY(ptr != 0))
+ if (LIKELY(ptr))
cairo_surface_destroy(ptr);
}
template<> void refIfNotNull(cairo_font_face_t* ptr)
{
- if (LIKELY(ptr != 0))
+ if (LIKELY(ptr))
cairo_font_face_reference(ptr);
}
template<> void derefIfNotNull(cairo_font_face_t* ptr)
{
- if (LIKELY(ptr != 0))
+ if (LIKELY(ptr))
cairo_font_face_destroy(ptr);
}
template<> void refIfNotNull(cairo_scaled_font_t* ptr)
{
- if (LIKELY(ptr != 0))
+ if (LIKELY(ptr))
cairo_scaled_font_reference(ptr);
}
template<> void derefIfNotNull(cairo_scaled_font_t* ptr)
{
- if (LIKELY(ptr != 0))
+ if (LIKELY(ptr))
cairo_scaled_font_destroy(ptr);
}
template<> void refIfNotNull(cairo_pattern_t* ptr)
{
- if (LIKELY(ptr != 0))
+ if (LIKELY(ptr))
cairo_pattern_reference(ptr);
}
template<> void derefIfNotNull(cairo_pattern_t* ptr)
{
- if (LIKELY(ptr != 0))
+ if (LIKELY(ptr))
cairo_pattern_destroy(ptr);
}
template<> void refIfNotNull(cairo_region_t* ptr)
{
- if (LIKELY(ptr != 0))
+ if (LIKELY(ptr))
cairo_region_reference(ptr);
}
template<> void derefIfNotNull(cairo_region_t* ptr)
{
- if (LIKELY(ptr != 0))
+ if (LIKELY(ptr))
cairo_region_destroy(ptr);
}
#if USE(FREETYPE)
template<> void refIfNotNull(FcPattern* ptr)
{
- if (LIKELY(ptr != 0))
+ if (LIKELY(ptr))
FcPatternReference(ptr);
}
template<> void derefIfNotNull(FcPattern* ptr)
{
- if (LIKELY(ptr != 0))
+ if (LIKELY(ptr))
FcPatternDestroy(ptr);
}
-#endif
+template<> void refIfNotNull(FcConfig* ptr)
+{
+ if (LIKELY(ptr))
+ FcConfigReference(ptr);
+}
+template<> void derefIfNotNull(FcConfig* ptr)
+{
+ if (LIKELY(ptr))
+ FcConfigDestroy(ptr);
}
+#endif
+
+} // namespace WTF
+
+#endif // USE(CAIRO)
diff --git a/Source/WebCore/platform/graphics/cairo/RefPtrCairo.h b/Source/WebCore/platform/graphics/cairo/RefPtrCairo.h
index 0e1baf50a..8e92f4e6d 100644
--- a/Source/WebCore/platform/graphics/cairo/RefPtrCairo.h
+++ b/Source/WebCore/platform/graphics/cairo/RefPtrCairo.h
@@ -20,6 +20,8 @@
#ifndef RefPtrCairo_h
#define RefPtrCairo_h
+#if USE(CAIRO)
+
#include <wtf/RefPtr.h>
typedef struct _cairo cairo_t;
@@ -31,6 +33,7 @@ typedef struct _cairo_region cairo_region_t;
#if USE(FREETYPE)
typedef struct _FcPattern FcPattern;
+typedef struct _FcConfig FcConfig;
#endif
namespace WTF {
@@ -56,8 +59,13 @@ template<> void derefIfNotNull(cairo_region_t*);
#if USE(FREETYPE)
template<> void refIfNotNull(FcPattern* ptr);
template<> void derefIfNotNull(FcPattern* ptr);
+
+template<> void refIfNotNull(FcConfig* ptr);
+template<> void derefIfNotNull(FcConfig* ptr);
#endif
-}
+} // namespace WTF
+
+#endif // USE(CAIRO)
#endif // RefPtrCairo_h
diff --git a/Source/WebCore/platform/graphics/cairo/TransformationMatrixCairo.cpp b/Source/WebCore/platform/graphics/cairo/TransformationMatrixCairo.cpp
index bebc4a321..a5cc372f8 100644
--- a/Source/WebCore/platform/graphics/cairo/TransformationMatrixCairo.cpp
+++ b/Source/WebCore/platform/graphics/cairo/TransformationMatrixCairo.cpp
@@ -9,10 +9,10 @@
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
- * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
@@ -23,12 +23,13 @@
*/
#include "config.h"
-#include "AffineTransform.h"
#include "TransformationMatrix.h"
+#if USE(CAIRO)
+
+#include "AffineTransform.h"
#include "FloatRect.h"
#include "IntRect.h"
-
#include <cairo.h>
namespace WebCore {
@@ -61,6 +62,6 @@ AffineTransform::operator cairo_matrix_t() const
return m;
}
-}
+} // namespace WebCore
-// vim: ts=4 sw=4 et
+#endif // USE(CAIRO)
diff --git a/Source/WebCore/platform/graphics/cpu/arm/filters/FEBlendNEON.h b/Source/WebCore/platform/graphics/cpu/arm/filters/FEBlendNEON.h
index e41ffa216..50ec87961 100644
--- a/Source/WebCore/platform/graphics/cpu/arm/filters/FEBlendNEON.h
+++ b/Source/WebCore/platform/graphics/cpu/arm/filters/FEBlendNEON.h
@@ -1,6 +1,7 @@
/*
* Copyright (C) 2012 University of Szeged
* Copyright (C) 2012 Gabor Rapcsanyi
+ * Copyright (C) 2014 Adobe Systems Incorporated. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -27,7 +28,7 @@
#ifndef FEBlendNEON_h
#define FEBlendNEON_h
-#if ENABLE(FILTERS) && HAVE(ARM_NEON_INTRINSICS)
+#if HAVE(ARM_NEON_INTRINSICS)
#include "FEBlend.h"
#include <arm_neon.h>
@@ -105,6 +106,39 @@ public:
}
};
+void FEBlend::platformApplySoftware()
+{
+ FilterEffect* in = inputEffect(0);
+ FilterEffect* in2 = inputEffect(1);
+
+ Uint8ClampedArray* dstPixelArray = createPremultipliedImageResult();
+ if (!dstPixelArray)
+ return;
+
+ IntRect effectADrawingRect = requestedRegionOfInputImageData(in->absolutePaintRect());
+ RefPtr<Uint8ClampedArray> srcPixelArrayA = in->asPremultipliedImage(effectADrawingRect);
+
+ IntRect effectBDrawingRect = requestedRegionOfInputImageData(in2->absolutePaintRect());
+ RefPtr<Uint8ClampedArray> srcPixelArrayB = in2->asPremultipliedImage(effectBDrawingRect);
+
+ unsigned pixelArrayLength = srcPixelArrayA->length();
+ ASSERT(pixelArrayLength == srcPixelArrayB->length());
+
+ if (pixelArrayLength >= 8) {
+ platformApplyNEON(srcPixelArrayA->data(), srcPixelArrayB->data(), dstPixelArray->data(), pixelArrayLength);
+ return;
+ }
+ // If there is just one pixel we expand it to two.
+ ASSERT(pixelArrayLength > 0);
+ uint32_t sourceA[2] = {0, 0};
+ uint32_t sourceBAndDest[2] = {0, 0};
+
+ sourceA[0] = reinterpret_cast<uint32_t*>(srcPixelArrayA->data())[0];
+ sourceBAndDest[0] = reinterpret_cast<uint32_t*>(srcPixelArrayB->data())[0];
+ platformApplyNEON(reinterpret_cast<uint8_t*>(sourceA), reinterpret_cast<uint8_t*>(sourceBAndDest), reinterpret_cast<uint8_t*>(sourceBAndDest), 8);
+ reinterpret_cast<uint32_t*>(dstPixelArray->data())[0] = sourceBAndDest[0];
+}
+
void FEBlend::platformApplyNEON(unsigned char* srcPixelArrayA, unsigned char* srcPixelArrayB, unsigned char* dstPixelArray,
unsigned colorArrayLength)
{
@@ -129,22 +163,21 @@ void FEBlend::platformApplyNEON(unsigned char* srcPixelArrayA, unsigned char* sr
uint16x8_t result;
switch (m_mode) {
- case FEBLEND_MODE_NORMAL:
+ case BlendModeNormal:
result = FEBlendUtilitiesNEON::normal(doubblePixelA, doubblePixelB, alphaA, alphaB, sixteenConst255, sixteenConstOne);
break;
- case FEBLEND_MODE_MULTIPLY:
+ case BlendModeMultiply:
result = FEBlendUtilitiesNEON::multiply(doubblePixelA, doubblePixelB, alphaA, alphaB, sixteenConst255, sixteenConstOne);
break;
- case FEBLEND_MODE_SCREEN:
+ case BlendModeScreen:
result = FEBlendUtilitiesNEON::screen(doubblePixelA, doubblePixelB, alphaA, alphaB, sixteenConst255, sixteenConstOne);
break;
- case FEBLEND_MODE_DARKEN:
+ case BlendModeDarken:
result = FEBlendUtilitiesNEON::darken(doubblePixelA, doubblePixelB, alphaA, alphaB, sixteenConst255, sixteenConstOne);
break;
- case FEBLEND_MODE_LIGHTEN:
+ case BlendModeLighten:
result = FEBlendUtilitiesNEON::lighten(doubblePixelA, doubblePixelB, alphaA, alphaB, sixteenConst255, sixteenConstOne);
break;
- case FEBLEND_MODE_UNKNOWN:
default:
result = vdupq_n_u16(0);
break;
@@ -168,6 +201,6 @@ void FEBlend::platformApplyNEON(unsigned char* srcPixelArrayA, unsigned char* sr
} // namespace WebCore
-#endif // ENABLE(FILTERS) && HAVE(ARM_NEON_INTRINSICS)
+#endif // HAVE(ARM_NEON_INTRINSICS)
#endif // FEBlendNEON_h
diff --git a/Source/WebCore/platform/graphics/cpu/arm/filters/FECompositeArithmeticNEON.h b/Source/WebCore/platform/graphics/cpu/arm/filters/FECompositeArithmeticNEON.h
index 2354dfc80..298f9afa7 100644
--- a/Source/WebCore/platform/graphics/cpu/arm/filters/FECompositeArithmeticNEON.h
+++ b/Source/WebCore/platform/graphics/cpu/arm/filters/FECompositeArithmeticNEON.h
@@ -27,9 +27,10 @@
#ifndef FECompositeArithmeticNEON_h
#define FECompositeArithmeticNEON_h
-#if ENABLE(FILTERS) && HAVE(ARM_NEON_INTRINSICS)
+#if HAVE(ARM_NEON_INTRINSICS)
#include "FEComposite.h"
+#include "NEONHelpers.h"
#include <arm_neon.h>
namespace WebCore {
@@ -49,13 +50,8 @@ inline void FEComposite::computeArithmeticPixelsNeon(unsigned char* source, unsi
uint32_t* destinationEndPixel = destinationPixel + (pixelArrayLength >> 2);
while (destinationPixel < destinationEndPixel) {
- uint32x2_t temporary1 = vset_lane_u32(*sourcePixel, temporary1, 0);
- uint16x4_t temporary2 = vget_low_u16(vmovl_u8(vreinterpret_u8_u32(temporary1)));
- float32x4_t sourcePixelAsFloat = vcvtq_f32_u32(vmovl_u16(temporary2));
-
- temporary1 = vset_lane_u32(*destinationPixel, temporary1, 0);
- temporary2 = vget_low_u16(vmovl_u8(vreinterpret_u8_u32(temporary1)));
- float32x4_t destinationPixelAsFloat = vcvtq_f32_u32(vmovl_u16(temporary2));
+ float32x4_t sourcePixelAsFloat = loadRGBA8AsFloat(sourcePixel);
+ float32x4_t destinationPixelAsFloat = loadRGBA8AsFloat(destinationPixel);
float32x4_t result = vmulq_f32(sourcePixelAsFloat, k2x4);
result = vmlaq_f32(result, destinationPixelAsFloat, k3x4);
@@ -94,6 +90,6 @@ inline void FEComposite::platformArithmeticNeon(unsigned char* source, unsigned
} // namespace WebCore
-#endif // ENABLE(FILTERS) && HAVE(ARM_NEON_INTRINSICS)
+#endif // HAVE(ARM_NEON_INTRINSICS)
#endif // FECompositeArithmeticNEON_h
diff --git a/Source/WebCore/platform/graphics/cpu/arm/filters/FEGaussianBlurNEON.h b/Source/WebCore/platform/graphics/cpu/arm/filters/FEGaussianBlurNEON.h
index 3779c2ec5..600704ebb 100644
--- a/Source/WebCore/platform/graphics/cpu/arm/filters/FEGaussianBlurNEON.h
+++ b/Source/WebCore/platform/graphics/cpu/arm/filters/FEGaussianBlurNEON.h
@@ -27,7 +27,7 @@
#ifndef FEGaussianBlurNEON_h
#define FEGaussianBlurNEON_h
-#if ENABLE(FILTERS) && HAVE(ARM_NEON_INTRINSICS)
+#if HAVE(ARM_NEON_INTRINSICS)
#include "FEGaussianBlur.h"
#include "NEONHelpers.h"
@@ -73,6 +73,6 @@ inline void boxBlurNEON(Uint8ClampedArray* srcPixelArray, Uint8ClampedArray* dst
} // namespace WebCore
-#endif // ENABLE(FILTERS) && HAVE(ARM_NEON_INTRINSICS)
+#endif // HAVE(ARM_NEON_INTRINSICS)
#endif // FEGaussianBlurNEON_h
diff --git a/Source/WebCore/platform/graphics/cpu/arm/filters/FELightingNEON.cpp b/Source/WebCore/platform/graphics/cpu/arm/filters/FELightingNEON.cpp
index 789b6aa48..fe44c6275 100644
--- a/Source/WebCore/platform/graphics/cpu/arm/filters/FELightingNEON.cpp
+++ b/Source/WebCore/platform/graphics/cpu/arm/filters/FELightingNEON.cpp
@@ -27,7 +27,7 @@
#include "config.h"
#include "FELightingNEON.h"
-#if CPU(ARM_NEON) && CPU(ARM_TRADITIONAL) && COMPILER(GCC)
+#if CPU(ARM_NEON) && CPU(ARM_TRADITIONAL) && COMPILER(GCC_OR_CLANG)
namespace WebCore {
@@ -500,4 +500,4 @@ int FELighting::getPowerCoefficients(float exponent)
} // namespace WebCore
-#endif // CPU(ARM_NEON) && COMPILER(GCC)
+#endif // CPU(ARM_NEON) && COMPILER(GCC_OR_CLANG)
diff --git a/Source/WebCore/platform/graphics/cpu/arm/filters/FELightingNEON.h b/Source/WebCore/platform/graphics/cpu/arm/filters/FELightingNEON.h
index 1e7140164..41b61e14a 100644
--- a/Source/WebCore/platform/graphics/cpu/arm/filters/FELightingNEON.h
+++ b/Source/WebCore/platform/graphics/cpu/arm/filters/FELightingNEON.h
@@ -27,7 +27,7 @@
#ifndef FELightingNEON_h
#define FELightingNEON_h
-#if CPU(ARM_NEON) && CPU(ARM_TRADITIONAL) && COMPILER(GCC)
+#if CPU(ARM_NEON) && CPU(ARM_TRADITIONAL) && COMPILER(GCC_OR_CLANG)
#include "FELighting.h"
#include <wtf/ParallelJobs.h>
@@ -194,6 +194,6 @@ inline void FELighting::platformApplyNeon(LightingData& data, LightSource::Paint
} // namespace WebCore
-#endif // CPU(ARM_NEON) && COMPILER(GCC)
+#endif // CPU(ARM_NEON) && COMPILER(GCC_OR_CLANG)
#endif // FELightingNEON_h
diff --git a/Source/WebCore/platform/graphics/cpu/arm/filters/NEONHelpers.h b/Source/WebCore/platform/graphics/cpu/arm/filters/NEONHelpers.h
index 3708b9d0a..3a13e4a4b 100644
--- a/Source/WebCore/platform/graphics/cpu/arm/filters/NEONHelpers.h
+++ b/Source/WebCore/platform/graphics/cpu/arm/filters/NEONHelpers.h
@@ -27,7 +27,7 @@
#ifndef NEONHelpers_h
#define NEONHelpers_h
-#if ENABLE(FILTERS) && HAVE(ARM_NEON_INTRINSICS)
+#if HAVE(ARM_NEON_INTRINSICS)
#include <arm_neon.h>
@@ -50,6 +50,6 @@ inline void storeFloatAsRGBA8(float32x4_t data, uint32_t* destination)
} // namespace WebCore
-#endif // ENABLE(FILTERS) && HAVE(ARM_NEON_INTRINSICS)
+#endif // HAVE(ARM_NEON_INTRINSICS)
#endif // NEONHelpers_h
diff --git a/Source/WebCore/platform/graphics/cv/PixelBufferConformerCV.cpp b/Source/WebCore/platform/graphics/cv/PixelBufferConformerCV.cpp
new file mode 100644
index 000000000..0b647eff9
--- /dev/null
+++ b/Source/WebCore/platform/graphics/cv/PixelBufferConformerCV.cpp
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2016 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "PixelBufferConformerCV.h"
+
+#include "GraphicsContextCG.h"
+#include "SoftLinking.h"
+
+#include "CoreVideoSoftLink.h"
+
+#if USE(VIDEOTOOLBOX)
+SOFT_LINK_FRAMEWORK_OPTIONAL(VideoToolbox)
+SOFT_LINK(VideoToolbox, VTPixelBufferConformerCreateWithAttributes, OSStatus, (CFAllocatorRef allocator, CFDictionaryRef attributes, VTPixelBufferConformerRef* conformerOut), (allocator, attributes, conformerOut));
+SOFT_LINK(VideoToolbox, VTPixelBufferConformerIsConformantPixelBuffer, Boolean, (VTPixelBufferConformerRef conformer, CVPixelBufferRef pixBuf), (conformer, pixBuf))
+SOFT_LINK(VideoToolbox, VTPixelBufferConformerCopyConformedPixelBuffer, OSStatus, (VTPixelBufferConformerRef conformer, CVPixelBufferRef sourceBuffer, Boolean ensureModifiable, CVPixelBufferRef* conformedBufferOut), (conformer, sourceBuffer, ensureModifiable, conformedBufferOut))
+#endif
+
+namespace WebCore {
+
+PixelBufferConformerCV::PixelBufferConformerCV(CFDictionaryRef attributes)
+{
+#if USE(VIDEOTOOLBOX)
+ VTPixelBufferConformerRef conformer = nullptr;
+ VTPixelBufferConformerCreateWithAttributes(kCFAllocatorDefault, attributes, &conformer);
+ ASSERT(conformer);
+ m_pixelConformer = adoptCF(conformer);
+#else
+ UNUSED_PARAM(attributes);
+ ASSERT(!attributes);
+#endif
+}
+
+static const void* CVPixelBufferGetBytePointerCallback(void* info)
+{
+ CVPixelBufferRef pixelBuffer = static_cast<CVPixelBufferRef>(info);
+ CVPixelBufferLockBaseAddress(pixelBuffer, kCVPixelBufferLock_ReadOnly);
+ return CVPixelBufferGetBaseAddress(pixelBuffer);
+}
+
+static void CVPixelBufferReleaseBytePointerCallback(void* info, const void*)
+{
+ CVPixelBufferRef pixelBuffer = static_cast<CVPixelBufferRef>(info);
+ CVPixelBufferUnlockBaseAddress(pixelBuffer, kCVPixelBufferLock_ReadOnly);
+}
+
+static void CVPixelBufferReleaseInfoCallback(void* info)
+{
+ CVPixelBufferRef pixelBuffer = static_cast<CVPixelBufferRef>(info);
+ CFRelease(pixelBuffer);
+}
+
+RetainPtr<CGImageRef> PixelBufferConformerCV::createImageFromPixelBuffer(CVPixelBufferRef rawBuffer)
+{
+ RetainPtr<CVPixelBufferRef> buffer { rawBuffer };
+ size_t width = CVPixelBufferGetWidth(buffer.get());
+ size_t height = CVPixelBufferGetHeight(buffer.get());
+
+#if USE(VIDEOTOOLBOX)
+ if (!VTPixelBufferConformerIsConformantPixelBuffer(m_pixelConformer.get(), buffer.get())) {
+ CVPixelBufferRef outputBuffer = nullptr;
+ OSStatus status = VTPixelBufferConformerCopyConformedPixelBuffer(m_pixelConformer.get(), buffer.get(), false, &outputBuffer);
+ if (status != noErr || !outputBuffer)
+ return nullptr;
+ buffer = adoptCF(outputBuffer);
+ }
+#endif
+
+ CGBitmapInfo bitmapInfo = kCGBitmapByteOrder32Little | kCGImageAlphaFirst;
+ size_t bytesPerRow = CVPixelBufferGetBytesPerRow(buffer.get());
+ size_t byteLength = CVPixelBufferGetDataSize(buffer.get());
+
+ CFRetain(buffer.get()); // Balanced by CVPixelBufferReleaseInfoCallback in providerCallbacks.
+ CGDataProviderDirectCallbacks providerCallbacks = { 0, CVPixelBufferGetBytePointerCallback, CVPixelBufferReleaseBytePointerCallback, 0, CVPixelBufferReleaseInfoCallback };
+ RetainPtr<CGDataProviderRef> provider = adoptCF(CGDataProviderCreateDirect(buffer.get(), byteLength, &providerCallbacks));
+
+ return adoptCF(CGImageCreate(width, height, 8, 32, bytesPerRow, sRGBColorSpaceRef(), bitmapInfo, provider.get(), nullptr, false, kCGRenderingIntentDefault));
+}
+
+}
diff --git a/Source/WebCore/platform/graphics/cv/PixelBufferConformerCV.h b/Source/WebCore/platform/graphics/cv/PixelBufferConformerCV.h
new file mode 100644
index 000000000..76ea81b20
--- /dev/null
+++ b/Source/WebCore/platform/graphics/cv/PixelBufferConformerCV.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2016 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef PixelBufferConformerCV_h
+#define PixelBufferConformerCV_h
+
+#include <wtf/RetainPtr.h>
+
+typedef struct OpaqueVTPixelBufferConformer* VTPixelBufferConformerRef;
+typedef struct CGImage* CGImageRef;
+typedef struct __CVBuffer *CVPixelBufferRef;
+
+namespace WebCore {
+
+class PixelBufferConformerCV {
+public:
+ PixelBufferConformerCV(CFDictionaryRef attributes);
+ RetainPtr<CGImageRef> createImageFromPixelBuffer(CVPixelBufferRef);
+
+private:
+#if USE(VIDEOTOOLBOX)
+ RetainPtr<VTPixelBufferConformerRef> m_pixelConformer;
+#endif
+};
+
+}
+
+#endif // PixelBufferConformerCV_h
diff --git a/Source/WebCore/platform/graphics/cv/TextureCacheCV.h b/Source/WebCore/platform/graphics/cv/TextureCacheCV.h
new file mode 100644
index 000000000..d796dd76e
--- /dev/null
+++ b/Source/WebCore/platform/graphics/cv/TextureCacheCV.h
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2016 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef TextureCacheCV_h
+#define TextureCacheCV_h
+
+#include "GraphicsTypes3D.h"
+#include <wtf/Ref.h>
+#include <wtf/RetainPtr.h>
+#include <wtf/WeakPtr.h>
+
+typedef struct __CVBuffer* CVImageBufferRef;
+typedef CVImageBufferRef CVOpenGLTextureRef;
+typedef CVImageBufferRef CVOpenGLESTextureRef;
+typedef struct __CVOpenGLTextureCache *CVOpenGLTextureCacheRef;
+typedef struct __CVOpenGLESTextureCache *CVOpenGLESTextureCacheRef;
+
+namespace WebCore {
+
+class GraphicsContext3D;
+
+class TextureCacheCV {
+public:
+ static std::unique_ptr<TextureCacheCV> create(GraphicsContext3D&);
+
+#if PLATFORM(IOS)
+ typedef CVOpenGLESTextureCacheRef TextureCacheType;
+ typedef CVOpenGLESTextureRef TextureType;
+#else
+ typedef CVOpenGLTextureCacheRef TextureCacheType;
+ typedef CVOpenGLTextureRef TextureType;
+#endif
+
+ TextureCacheCV(GraphicsContext3D&, RetainPtr<TextureCacheType>&&);
+
+ RetainPtr<TextureType> textureFromImage(CVImageBufferRef, GC3Denum outputTarget, GC3Dint level, GC3Denum internalFormat, GC3Denum format, GC3Denum type);
+ GraphicsContext3D& context() { return m_context.get(); }
+
+private:
+ Ref<GraphicsContext3D> m_context;
+ RetainPtr<TextureCacheType> m_cache;
+ WeakPtrFactory<TextureCacheCV> m_weakPtrFactory;
+};
+
+}
+
+#endif // TextureCacheCV_h
diff --git a/Source/WebCore/platform/graphics/cv/VideoTextureCopierCV.cpp b/Source/WebCore/platform/graphics/cv/VideoTextureCopierCV.cpp
new file mode 100644
index 000000000..1d1ea39c8
--- /dev/null
+++ b/Source/WebCore/platform/graphics/cv/VideoTextureCopierCV.cpp
@@ -0,0 +1,212 @@
+/*
+ * Copyright (C) 2016 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "VideoTextureCopierCV.h"
+
+#include "Logging.h"
+#include <wtf/NeverDestroyed.h>
+
+#if PLATFORM(IOS)
+#include <OpenGLES/ES3/glext.h>
+#endif
+
+#include "CoreVideoSoftLink.h"
+
+namespace WebCore {
+
+VideoTextureCopierCV::VideoTextureCopierCV(GraphicsContext3D& context)
+ : m_context(context)
+ , m_readFramebuffer(context.createFramebuffer())
+{
+}
+
+VideoTextureCopierCV::~VideoTextureCopierCV()
+{
+ m_context->deleteFramebuffer(m_readFramebuffer);
+}
+
+#if !LOG_DISABLED
+
+#define STRINGIFY_PAIR(e) e, #e
+static std::map<uint32_t, const char*>& enumToStringMap()
+{
+ static NeverDestroyed<std::map<uint32_t, const char*>> map;
+ if (map.get().empty()) {
+ std::map<uint32_t, const char*> stringMap;
+ map.get().emplace(STRINGIFY_PAIR(GL_RGB));
+ map.get().emplace(STRINGIFY_PAIR(GL_RGBA));
+ map.get().emplace(STRINGIFY_PAIR(GL_LUMINANCE_ALPHA));
+ map.get().emplace(STRINGIFY_PAIR(GL_LUMINANCE));
+ map.get().emplace(STRINGIFY_PAIR(GL_ALPHA));
+ map.get().emplace(STRINGIFY_PAIR(GL_R8));
+ map.get().emplace(STRINGIFY_PAIR(GL_R16F));
+ map.get().emplace(STRINGIFY_PAIR(GL_R32F));
+ map.get().emplace(STRINGIFY_PAIR(GL_R8UI));
+ map.get().emplace(STRINGIFY_PAIR(GL_R8I));
+ map.get().emplace(STRINGIFY_PAIR(GL_R16UI));
+ map.get().emplace(STRINGIFY_PAIR(GL_R16I));
+ map.get().emplace(STRINGIFY_PAIR(GL_R32UI));
+ map.get().emplace(STRINGIFY_PAIR(GL_R32I));
+ map.get().emplace(STRINGIFY_PAIR(GL_RG8));
+ map.get().emplace(STRINGIFY_PAIR(GL_RG16F));
+ map.get().emplace(STRINGIFY_PAIR(GL_RG32F));
+ map.get().emplace(STRINGIFY_PAIR(GL_RG8UI));
+ map.get().emplace(STRINGIFY_PAIR(GL_RG8I));
+ map.get().emplace(STRINGIFY_PAIR(GL_RG16UI));
+ map.get().emplace(STRINGIFY_PAIR(GL_RG16I));
+ map.get().emplace(STRINGIFY_PAIR(GL_RG32UI));
+ map.get().emplace(STRINGIFY_PAIR(GL_RG32I));
+ map.get().emplace(STRINGIFY_PAIR(GL_RGB8));
+ map.get().emplace(STRINGIFY_PAIR(GL_SRGB8));
+ map.get().emplace(STRINGIFY_PAIR(GL_RGBA8));
+ map.get().emplace(STRINGIFY_PAIR(GL_SRGB8_ALPHA8));
+ map.get().emplace(STRINGIFY_PAIR(GL_RGBA4));
+ map.get().emplace(STRINGIFY_PAIR(GL_RGB10_A2));
+ map.get().emplace(STRINGIFY_PAIR(GL_DEPTH_COMPONENT16));
+ map.get().emplace(STRINGIFY_PAIR(GL_DEPTH_COMPONENT24));
+ map.get().emplace(STRINGIFY_PAIR(GL_DEPTH_COMPONENT32F));
+ map.get().emplace(STRINGIFY_PAIR(GL_DEPTH24_STENCIL8));
+ map.get().emplace(STRINGIFY_PAIR(GL_DEPTH32F_STENCIL8));
+ map.get().emplace(STRINGIFY_PAIR(GL_RGB));
+ map.get().emplace(STRINGIFY_PAIR(GL_RGBA));
+ map.get().emplace(STRINGIFY_PAIR(GL_LUMINANCE_ALPHA));
+ map.get().emplace(STRINGIFY_PAIR(GL_LUMINANCE));
+ map.get().emplace(STRINGIFY_PAIR(GL_ALPHA));
+ map.get().emplace(STRINGIFY_PAIR(GL_RED));
+ map.get().emplace(STRINGIFY_PAIR(GL_RG_INTEGER));
+ map.get().emplace(STRINGIFY_PAIR(GL_DEPTH_STENCIL));
+ map.get().emplace(STRINGIFY_PAIR(GL_UNSIGNED_BYTE));
+ map.get().emplace(STRINGIFY_PAIR(GL_UNSIGNED_SHORT_5_6_5));
+ map.get().emplace(STRINGIFY_PAIR(GL_UNSIGNED_SHORT_4_4_4_4));
+ map.get().emplace(STRINGIFY_PAIR(GL_UNSIGNED_SHORT_5_5_5_1));
+ map.get().emplace(STRINGIFY_PAIR(GL_BYTE));
+ map.get().emplace(STRINGIFY_PAIR(GL_HALF_FLOAT));
+ map.get().emplace(STRINGIFY_PAIR(GL_FLOAT));
+ map.get().emplace(STRINGIFY_PAIR(GL_UNSIGNED_SHORT));
+ map.get().emplace(STRINGIFY_PAIR(GL_SHORT));
+ map.get().emplace(STRINGIFY_PAIR(GL_UNSIGNED_INT));
+ map.get().emplace(STRINGIFY_PAIR(GL_INT));
+ map.get().emplace(STRINGIFY_PAIR(GL_UNSIGNED_INT_2_10_10_10_REV));
+ map.get().emplace(STRINGIFY_PAIR(GL_UNSIGNED_INT_24_8));
+ map.get().emplace(STRINGIFY_PAIR(GL_FLOAT_32_UNSIGNED_INT_24_8_REV));
+
+#if PLATFORM(IOS)
+ map.get().emplace(STRINGIFY_PAIR(GL_RED_INTEGER));
+ map.get().emplace(STRINGIFY_PAIR(GL_RGB_INTEGER));
+ map.get().emplace(STRINGIFY_PAIR(GL_RG8_SNORM));
+ map.get().emplace(STRINGIFY_PAIR(GL_RGB565));
+ map.get().emplace(STRINGIFY_PAIR(GL_RGB8_SNORM));
+ map.get().emplace(STRINGIFY_PAIR(GL_R11F_G11F_B10F));
+ map.get().emplace(STRINGIFY_PAIR(GL_RGB9_E5));
+ map.get().emplace(STRINGIFY_PAIR(GL_RGB16F));
+ map.get().emplace(STRINGIFY_PAIR(GL_RGB32F));
+ map.get().emplace(STRINGIFY_PAIR(GL_RGB8UI));
+ map.get().emplace(STRINGIFY_PAIR(GL_RGB8I));
+ map.get().emplace(STRINGIFY_PAIR(GL_RGB16UI));
+ map.get().emplace(STRINGIFY_PAIR(GL_RGB16I));
+ map.get().emplace(STRINGIFY_PAIR(GL_RGB32UI));
+ map.get().emplace(STRINGIFY_PAIR(GL_RGB32I));
+ map.get().emplace(STRINGIFY_PAIR(GL_RGBA8_SNORM));
+ map.get().emplace(STRINGIFY_PAIR(GL_RGBA16F));
+ map.get().emplace(STRINGIFY_PAIR(GL_RGBA32F));
+ map.get().emplace(STRINGIFY_PAIR(GL_RGBA8UI));
+ map.get().emplace(STRINGIFY_PAIR(GL_RGBA8I));
+ map.get().emplace(STRINGIFY_PAIR(GL_RGB10_A2UI));
+ map.get().emplace(STRINGIFY_PAIR(GL_RGBA16UI));
+ map.get().emplace(STRINGIFY_PAIR(GL_RGBA16I));
+ map.get().emplace(STRINGIFY_PAIR(GL_RGBA32I));
+ map.get().emplace(STRINGIFY_PAIR(GL_RGBA32UI));
+ map.get().emplace(STRINGIFY_PAIR(GL_RGB5_A1));
+ map.get().emplace(STRINGIFY_PAIR(GL_RG));
+ map.get().emplace(STRINGIFY_PAIR(GL_RGBA_INTEGER));
+ map.get().emplace(STRINGIFY_PAIR(GL_DEPTH_COMPONENT));
+ map.get().emplace(STRINGIFY_PAIR(GL_UNSIGNED_INT_10F_11F_11F_REV));
+ map.get().emplace(STRINGIFY_PAIR(GL_UNSIGNED_INT_5_9_9_9_REV));
+#endif
+ }
+ return map.get();
+}
+
+#endif
+
+bool VideoTextureCopierCV::copyVideoTextureToPlatformTexture(TextureType inputTexture, size_t width, size_t height, Platform3DObject outputTexture, GC3Denum outputTarget, GC3Dint level, GC3Denum internalFormat, GC3Denum format, GC3Denum type, bool premultiplyAlpha, bool flipY)
+{
+ if (flipY || premultiplyAlpha)
+ return false;
+
+ if (!inputTexture)
+ return false;
+
+#if PLATFORM(IOS)
+ Platform3DObject videoTextureName = CVOpenGLESTextureGetName(inputTexture);
+ GC3Denum videoTextureTarget = CVOpenGLESTextureGetTarget(inputTexture);
+#else
+ Platform3DObject videoTextureName = CVOpenGLTextureGetName(inputTexture);
+ GC3Denum videoTextureTarget = CVOpenGLTextureGetTarget(inputTexture);
+#endif
+
+ LOG(Media, "VideoTextureCopierCV::copyVideoTextureToPlatformTexture(%p) - internalFormat: %s, format: %s, type: %s", this, enumToStringMap()[internalFormat], enumToStringMap()[format], enumToStringMap()[type]);
+
+ // Save the origial bound texture & framebuffer names so we can re-bind them after copying the video texture.
+ GC3Dint boundTexture = 0;
+ GC3Dint boundReadFramebuffer = 0;
+ m_context->getIntegerv(GraphicsContext3D::TEXTURE_BINDING_2D, &boundTexture);
+ m_context->getIntegerv(GraphicsContext3D::READ_FRAMEBUFFER_BINDING, &boundReadFramebuffer);
+
+ m_context->bindTexture(videoTextureTarget, videoTextureName);
+ m_context->texParameteri(GraphicsContext3D::TEXTURE_2D, GraphicsContext3D::TEXTURE_MIN_FILTER, GraphicsContext3D::LINEAR);
+ m_context->texParameterf(GraphicsContext3D::TEXTURE_2D, GraphicsContext3D::TEXTURE_WRAP_S, GraphicsContext3D::CLAMP_TO_EDGE);
+ m_context->texParameterf(GraphicsContext3D::TEXTURE_2D, GraphicsContext3D::TEXTURE_WRAP_T, GraphicsContext3D::CLAMP_TO_EDGE);
+
+ // Make that framebuffer the read source from which drawing commands will read voxels.
+ m_context->bindFramebuffer(GraphicsContext3D::READ_FRAMEBUFFER, m_readFramebuffer);
+
+ // Allocate uninitialized memory for the output texture.
+ m_context->bindTexture(outputTarget, outputTexture);
+ m_context->texParameteri(GraphicsContext3D::TEXTURE_2D, GraphicsContext3D::TEXTURE_MIN_FILTER, GraphicsContext3D::LINEAR);
+ m_context->texParameterf(GraphicsContext3D::TEXTURE_2D, GraphicsContext3D::TEXTURE_WRAP_S, GraphicsContext3D::CLAMP_TO_EDGE);
+ m_context->texParameterf(GraphicsContext3D::TEXTURE_2D, GraphicsContext3D::TEXTURE_WRAP_T, GraphicsContext3D::CLAMP_TO_EDGE);
+ m_context->texImage2DDirect(outputTarget, level, internalFormat, width, height, 0, format, type, nullptr);
+
+ // Attach the video texture to the framebuffer.
+ m_context->framebufferTexture2D(GraphicsContext3D::READ_FRAMEBUFFER, GraphicsContext3D::COLOR_ATTACHMENT0, videoTextureTarget, videoTextureName, level);
+
+ GC3Denum status = m_context->checkFramebufferStatus(GraphicsContext3D::READ_FRAMEBUFFER);
+ if (status != GraphicsContext3D::FRAMEBUFFER_COMPLETE)
+ return false;
+
+ // Copy texture from the read framebuffer (and thus the video texture) to the output texture.
+ m_context->copyTexImage2D(outputTarget, level, internalFormat, 0, 0, width, height, 0);
+
+ // Restore the previous texture and framebuffer bindings.
+ m_context->bindTexture(outputTarget, boundTexture);
+ m_context->bindFramebuffer(GraphicsContext3D::READ_FRAMEBUFFER, boundReadFramebuffer);
+
+ return !m_context->getError();
+}
+
+
+}
diff --git a/Source/WebCore/platform/graphics/cv/VideoTextureCopierCV.h b/Source/WebCore/platform/graphics/cv/VideoTextureCopierCV.h
new file mode 100644
index 000000000..b5f512f89
--- /dev/null
+++ b/Source/WebCore/platform/graphics/cv/VideoTextureCopierCV.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2016 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef VideoTextureCopierCV_h
+#define VideoTextureCopierCV_h
+
+#import "GraphicsContext3D.h"
+
+typedef struct __CVBuffer* CVImageBufferRef;
+typedef CVImageBufferRef CVOpenGLTextureRef;
+typedef CVImageBufferRef CVOpenGLESTextureRef;
+
+namespace WebCore {
+
+class VideoTextureCopierCV {
+public:
+ VideoTextureCopierCV(GraphicsContext3D&);
+ ~VideoTextureCopierCV();
+
+#if PLATFORM(IOS)
+ typedef CVOpenGLESTextureRef TextureType;
+#else
+ typedef CVOpenGLTextureRef TextureType;
+#endif
+
+ bool copyVideoTextureToPlatformTexture(TextureType, size_t width, size_t height, Platform3DObject outputTexture, GC3Denum outputTarget, GC3Dint level, GC3Denum internalFormat, GC3Denum format, GC3Denum type, bool premultiplyAlpha, bool flipY);
+
+ GraphicsContext3D& context() { return m_context.get(); }
+
+private:
+ Ref<GraphicsContext3D> m_context;
+ Platform3DObject m_readFramebuffer;
+};
+
+}
+
+#endif // VideoTextureCopierCV_h
diff --git a/Source/WebCore/platform/graphics/displaylists/DisplayList.cpp b/Source/WebCore/platform/graphics/displaylists/DisplayList.cpp
new file mode 100644
index 000000000..cd9c933d6
--- /dev/null
+++ b/Source/WebCore/platform/graphics/displaylists/DisplayList.cpp
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 2016 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "DisplayList.h"
+
+#include "DisplayListItems.h"
+#include "Logging.h"
+#include "TextStream.h"
+#include <wtf/StdLibExtras.h>
+
+namespace WebCore {
+namespace DisplayList {
+
+#if !defined(NDEBUG) || !LOG_DISABLED
+WTF::CString DisplayList::description() const
+{
+ TextStream ts;
+ ts << *this;
+ return ts.release().utf8();
+}
+
+void DisplayList::dump() const
+{
+ fprintf(stderr, "%s", description().data());
+}
+#endif
+
+void DisplayList::clear()
+{
+ m_list.clear();
+}
+
+void DisplayList::removeItemsFromIndex(size_t index)
+{
+ m_list.resize(index);
+}
+
+bool DisplayList::shouldDumpForFlags(AsTextFlags flags, const Item& item)
+{
+ switch (item.type()) {
+ case ItemType::SetState:
+ if (!(flags & AsTextFlag::IncludesPlatformOperations)) {
+ const auto& stateItem = downcast<SetState>(item);
+ // FIXME: for now, only drop the item if the only state-change flags are platform-specific.
+ if (stateItem.state().m_changeFlags == GraphicsContextState::ShouldSubpixelQuantizeFontsChange)
+ return false;
+
+ if (stateItem.state().m_changeFlags == GraphicsContextState::ShouldSubpixelQuantizeFontsChange)
+ return false;
+ }
+ break;
+#if USE(CG)
+ case ItemType::ApplyFillPattern:
+ case ItemType::ApplyStrokePattern:
+ if (!(flags & AsTextFlag::IncludesPlatformOperations))
+ return false;
+ break;
+#endif
+ default:
+ break;
+ }
+ return true;
+}
+
+String DisplayList::asText(AsTextFlags flags) const
+{
+ TextStream stream(TextStream::LineMode::MultipleLine, TextStream::Formatting::SVGStyleRect);
+ for (auto& item : m_list) {
+ if (!shouldDumpForFlags(flags, item))
+ continue;
+ stream << item;
+ }
+ return stream.release();
+}
+
+void DisplayList::dump(TextStream& ts) const
+{
+ TextStream::GroupScope group(ts);
+ ts << "display list";
+
+ size_t numItems = m_list.size();
+ for (size_t i = 0; i < numItems; ++i) {
+ TextStream::GroupScope scope(ts);
+ ts << i << " " << m_list[i].get();
+ }
+ ts.startGroup();
+ ts << "size in bytes: " << sizeInBytes();
+ ts.endGroup();
+}
+
+size_t DisplayList::sizeInBytes() const
+{
+ size_t result = 0;
+ for (auto& ref : m_list)
+ result += Item::sizeInBytes(ref);
+
+ return result;
+}
+
+} // namespace DisplayList
+
+TextStream& operator<<(TextStream& ts, const DisplayList::DisplayList& displayList)
+{
+ displayList.dump(ts);
+ return ts;
+}
+
+} // namespace WebCore
diff --git a/Source/WebCore/platform/graphics/displaylists/DisplayList.h b/Source/WebCore/platform/graphics/displaylists/DisplayList.h
new file mode 100644
index 000000000..da455d25f
--- /dev/null
+++ b/Source/WebCore/platform/graphics/displaylists/DisplayList.h
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2016 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef DisplayList_h
+#define DisplayList_h
+
+#include "DisplayListItems.h"
+#include <wtf/FastMalloc.h>
+#include <wtf/Noncopyable.h>
+#include <wtf/text/WTFString.h>
+
+namespace WebCore {
+
+class FloatRect;
+class GraphicsContext;
+class TextStream;
+
+namespace DisplayList {
+
+class Item;
+
+enum AsTextFlag {
+ None = 0,
+ IncludesPlatformOperations = 1 << 0,
+};
+
+typedef unsigned AsTextFlags;
+
+class DisplayList {
+ WTF_MAKE_NONCOPYABLE(DisplayList); WTF_MAKE_FAST_ALLOCATED;
+ friend class Recorder;
+ friend class Replayer;
+public:
+ DisplayList() = default;
+ DisplayList(DisplayList&&) = default;
+
+ DisplayList& operator=(DisplayList&&) = default;
+
+ void dump(TextStream&) const;
+
+ const Vector<Ref<Item>>& list() const { return m_list; }
+ Item& itemAt(size_t index)
+ {
+ ASSERT(index < m_list.size());
+ return m_list[index].get();
+ }
+
+ void clear();
+ void removeItemsFromIndex(size_t);
+
+ size_t itemCount() const { return m_list.size(); }
+ size_t sizeInBytes() const;
+
+ String asText(AsTextFlags) const;
+
+#if !defined(NDEBUG) || !LOG_DISABLED
+ WTF::CString description() const;
+ void dump() const;
+#endif
+
+private:
+ Item& append(Ref<Item>&& item)
+ {
+ m_list.append(WTFMove(item));
+ return m_list.last().get();
+ }
+
+ // Less efficient append, only used for tracking replay.
+ void appendItem(Item& item)
+ {
+ m_list.append(item);
+ }
+
+ static bool shouldDumpForFlags(AsTextFlags, const Item&);
+
+ Vector<Ref<Item>>& list() { return m_list; }
+
+ Vector<Ref<Item>> m_list;
+};
+
+} // DisplayList
+
+TextStream& operator<<(TextStream&, const DisplayList::DisplayList&);
+
+} // WebCore
+
+using WebCore::DisplayList::DisplayList;
+
+#endif /* DisplayList_h */
diff --git a/Source/WebCore/platform/graphics/displaylists/DisplayListItems.cpp b/Source/WebCore/platform/graphics/displaylists/DisplayListItems.cpp
new file mode 100644
index 000000000..0fca123da
--- /dev/null
+++ b/Source/WebCore/platform/graphics/displaylists/DisplayListItems.cpp
@@ -0,0 +1,1151 @@
+/*
+ * Copyright (C) 2015 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "DisplayListItems.h"
+
+#include "FontCascade.h"
+#include "TextStream.h"
+
+namespace WebCore {
+namespace DisplayList {
+
+// Should match RenderTheme::platformFocusRingWidth()
+static const float platformFocusRingWidth = 3;
+
+#if !defined(NDEBUG) || !LOG_DISABLED
+WTF::CString Item::description() const
+{
+ TextStream ts;
+ ts << *this;
+ return ts.release().utf8();
+}
+#endif
+
+size_t Item::sizeInBytes(const Item& item)
+{
+ switch (item.type()) {
+ case ItemType::Save:
+ return sizeof(downcast<Save>(item));
+ case ItemType::Restore:
+ return sizeof(downcast<Restore>(item));
+ case ItemType::Translate:
+ return sizeof(downcast<Translate>(item));
+ case ItemType::Rotate:
+ return sizeof(downcast<Rotate>(item));
+ case ItemType::Scale:
+ return sizeof(downcast<Scale>(item));
+ case ItemType::ConcatenateCTM:
+ return sizeof(downcast<ConcatenateCTM>(item));
+ case ItemType::SetState:
+ return sizeof(downcast<SetState>(item));
+ case ItemType::SetLineCap:
+ return sizeof(downcast<SetLineCap>(item));
+ case ItemType::SetLineDash:
+ return sizeof(downcast<SetLineDash>(item));
+ case ItemType::SetLineJoin:
+ return sizeof(downcast<SetLineJoin>(item));
+ case ItemType::SetMiterLimit:
+ return sizeof(downcast<SetMiterLimit>(item));
+ case ItemType::ClearShadow:
+ return sizeof(downcast<ClearShadow>(item));
+ case ItemType::Clip:
+ return sizeof(downcast<Clip>(item));
+ case ItemType::ClipOut:
+ return sizeof(downcast<ClipOut>(item));
+ case ItemType::ClipOutToPath:
+ return sizeof(downcast<ClipOutToPath>(item));
+ case ItemType::ClipPath:
+ return sizeof(downcast<ClipPath>(item));
+ case ItemType::DrawGlyphs:
+ return sizeof(downcast<DrawGlyphs>(item));
+ case ItemType::DrawImage:
+ return sizeof(downcast<DrawImage>(item));
+ case ItemType::DrawTiledImage:
+ return sizeof(downcast<DrawTiledImage>(item));
+ case ItemType::DrawTiledScaledImage:
+ return sizeof(downcast<DrawTiledScaledImage>(item));
+#if USE(CG) || USE(CAIRO)
+ case ItemType::DrawNativeImage:
+ return sizeof(downcast<DrawNativeImage>(item));
+#endif
+ case ItemType::DrawPattern:
+ return sizeof(downcast<DrawPattern>(item));
+ case ItemType::DrawRect:
+ return sizeof(downcast<DrawRect>(item));
+ case ItemType::DrawLine:
+ return sizeof(downcast<DrawLine>(item));
+ case ItemType::DrawLinesForText:
+ return sizeof(downcast<DrawLinesForText>(item));
+ case ItemType::DrawLineForDocumentMarker:
+ return sizeof(downcast<DrawLineForDocumentMarker>(item));
+ case ItemType::DrawEllipse:
+ return sizeof(downcast<DrawEllipse>(item));
+ case ItemType::DrawPath:
+ return sizeof(downcast<DrawPath>(item));
+ case ItemType::DrawFocusRingPath:
+ return sizeof(downcast<DrawFocusRingPath>(item));
+ case ItemType::DrawFocusRingRects:
+ return sizeof(downcast<DrawFocusRingRects>(item));
+ case ItemType::FillRect:
+ return sizeof(downcast<FillRect>(item));
+ case ItemType::FillRectWithColor:
+ return sizeof(downcast<FillRectWithColor>(item));
+ case ItemType::FillRectWithGradient:
+ return sizeof(downcast<FillRectWithGradient>(item));
+ case ItemType::FillCompositedRect:
+ return sizeof(downcast<FillCompositedRect>(item));
+ case ItemType::FillRoundedRect:
+ return sizeof(downcast<FillRoundedRect>(item));
+ case ItemType::FillRectWithRoundedHole:
+ return sizeof(downcast<FillRectWithRoundedHole>(item));
+ case ItemType::FillPath:
+ return sizeof(downcast<FillPath>(item));
+ case ItemType::FillEllipse:
+ return sizeof(downcast<FillEllipse>(item));
+ case ItemType::StrokeRect:
+ return sizeof(downcast<StrokeRect>(item));
+ case ItemType::StrokePath:
+ return sizeof(downcast<StrokePath>(item));
+ case ItemType::StrokeEllipse:
+ return sizeof(downcast<StrokeEllipse>(item));
+ case ItemType::ClearRect:
+ return sizeof(downcast<ClearRect>(item));
+ case ItemType::BeginTransparencyLayer:
+ return sizeof(downcast<BeginTransparencyLayer>(item));
+ case ItemType::EndTransparencyLayer:
+ return sizeof(downcast<EndTransparencyLayer>(item));
+#if USE(CG)
+ case ItemType::ApplyStrokePattern:
+ return sizeof(downcast<ApplyStrokePattern>(item));
+ case ItemType::ApplyFillPattern:
+ return sizeof(downcast<ApplyFillPattern>(item));
+#endif
+ case ItemType::ApplyDeviceScaleFactor:
+ return sizeof(downcast<ApplyDeviceScaleFactor>(item));
+ }
+ return 0;
+}
+
+static TextStream& operator<<(TextStream& ts, const DrawingItem& item)
+{
+ ts.startGroup();
+ ts << "extent ";
+ if (item.extentKnown())
+ ts << item.extent();
+ else
+ ts << "unknown";
+
+ ts.endGroup();
+ return ts;
+}
+
+void Save::apply(GraphicsContext& context) const
+{
+ context.save();
+}
+
+static TextStream& operator<<(TextStream& ts, const Save& item)
+{
+ ts.dumpProperty("restore-index", item.restoreIndex());
+ return ts;
+}
+
+void Restore::apply(GraphicsContext& context) const
+{
+ context.restore();
+}
+
+void Translate::apply(GraphicsContext& context) const
+{
+ context.translate(m_x, m_y);
+}
+
+static TextStream& operator<<(TextStream& ts, const Translate& item)
+{
+ ts.dumpProperty("x", item.x());
+ ts.dumpProperty("y", item.y());
+
+ return ts;
+}
+
+void Rotate::apply(GraphicsContext& context) const
+{
+ context.rotate(m_angle);
+}
+
+static TextStream& operator<<(TextStream& ts, const Rotate& item)
+{
+ ts.dumpProperty("angle", item.angle());
+
+ return ts;
+}
+
+void Scale::apply(GraphicsContext& context) const
+{
+ context.scale(m_size);
+}
+
+static TextStream& operator<<(TextStream& ts, const Scale& item)
+{
+ ts.dumpProperty("size", item.amount());
+
+ return ts;
+}
+
+ConcatenateCTM::ConcatenateCTM(const AffineTransform& transform)
+ : Item(ItemType::ConcatenateCTM)
+ , m_transform(transform)
+{
+}
+
+void ConcatenateCTM::apply(GraphicsContext& context) const
+{
+ context.concatCTM(m_transform);
+}
+
+static TextStream& operator<<(TextStream& ts, const ConcatenateCTM& item)
+{
+ ts.dumpProperty("ctm", item.transform());
+
+ return ts;
+}
+
+void SetState::apply(GraphicsContext& context) const
+{
+ m_state.apply(context);
+}
+
+void SetState::accumulate(const GraphicsContextState& state, GraphicsContextState::StateChangeFlags flags)
+{
+ m_state.accumulate(state, flags);
+}
+
+static TextStream& operator<<(TextStream& ts, const SetState& state)
+{
+ ts << state.state();
+ return ts;
+}
+
+void SetLineCap::apply(GraphicsContext& context) const
+{
+ context.setLineCap(m_lineCap);
+}
+
+static TextStream& operator<<(TextStream& ts, const SetLineCap& lineCap)
+{
+ ts.dumpProperty("line-cap", lineCap.lineCap());
+ return ts;
+}
+
+void SetLineDash::apply(GraphicsContext& context) const
+{
+ context.setLineDash(m_dashArray, m_dashOffset);
+}
+
+static TextStream& operator<<(TextStream& ts, const SetLineDash& lineDash)
+{
+ ts.dumpProperty("dash-array", lineDash.dashArray());
+ ts.dumpProperty("dash-offset", lineDash.dashOffset());
+ return ts;
+}
+
+void SetLineJoin::apply(GraphicsContext& context) const
+{
+ context.setLineJoin(m_lineJoin);
+}
+
+static TextStream& operator<<(TextStream& ts, const SetLineJoin& lineJoin)
+{
+ ts.dumpProperty("line-join", lineJoin.lineJoin());
+ return ts;
+}
+
+void SetMiterLimit::apply(GraphicsContext& context) const
+{
+ context.setMiterLimit(m_miterLimit);
+}
+
+static TextStream& operator<<(TextStream& ts, const SetMiterLimit& miterLimit)
+{
+ ts.dumpProperty("mitre-limit", miterLimit.miterLimit());
+ return ts;
+}
+
+void ClearShadow::apply(GraphicsContext& context) const
+{
+ context.clearShadow();
+}
+
+void Clip::apply(GraphicsContext& context) const
+{
+ context.clip(m_rect);
+}
+
+static TextStream& operator<<(TextStream& ts, const Clip& item)
+{
+ ts.dumpProperty("rect", item.rect());
+ return ts;
+}
+
+void ClipOut::apply(GraphicsContext& context) const
+{
+ context.clipOut(m_rect);
+}
+
+static TextStream& operator<<(TextStream& ts, const ClipOut& item)
+{
+ ts.dumpProperty("rect", item.rect());
+ return ts;
+}
+
+void ClipOutToPath::apply(GraphicsContext& context) const
+{
+ context.clipOut(m_path);
+}
+
+static TextStream& operator<<(TextStream& ts, const ClipOutToPath& item)
+{
+ ts.dumpProperty("path", item.path());
+ return ts;
+}
+
+void ClipPath::apply(GraphicsContext& context) const
+{
+ context.clipPath(m_path, m_windRule);
+}
+
+static TextStream& operator<<(TextStream& ts, const ClipPath& item)
+{
+ ts.dumpProperty("path", item.path());
+ ts.dumpProperty("wind-rule", item.windRule());
+ return ts;
+}
+
+DrawGlyphs::DrawGlyphs(const Font& font, const GlyphBufferGlyph* glyphs, const GlyphBufferAdvance* advances, unsigned count, const FloatPoint& blockLocation, const FloatSize& localAnchor, FontSmoothingMode smoothingMode)
+ : DrawingItem(ItemType::DrawGlyphs)
+ , m_font(const_cast<Font&>(font))
+ , m_blockLocation(blockLocation)
+ , m_localAnchor(localAnchor)
+ , m_smoothingMode(smoothingMode)
+{
+ m_glyphs.reserveInitialCapacity(count);
+ m_advances.reserveInitialCapacity(count);
+ for (unsigned i = 0; i < count; ++i) {
+ m_glyphs.uncheckedAppend(glyphs[i]);
+ m_advances.uncheckedAppend(advances[i]);
+ }
+ computeBounds();
+}
+
+inline GlyphBuffer DrawGlyphs::generateGlyphBuffer() const
+{
+ GlyphBuffer result;
+ for (size_t i = 0; i < m_glyphs.size(); ++i) {
+#if USE(CAIRO)
+ result.add(m_glyphs[i].index, &m_font.get(), m_advances[i]);
+#else
+ result.add(m_glyphs[i], &m_font.get(), m_advances[i]);
+#endif
+ }
+ return result;
+}
+
+void DrawGlyphs::apply(GraphicsContext& context) const
+{
+ FontCascade::drawGlyphs(context, m_font, generateGlyphBuffer(), 0, m_glyphs.size(), anchorPoint(), m_smoothingMode);
+}
+
+void DrawGlyphs::computeBounds()
+{
+ // FIXME: This code doesn't actually take the extents of the glyphs into consideration. It assumes that
+ // the glyph lies entirely within its [(ascent + descent), advance] rect.
+ float ascent = m_font->fontMetrics().floatAscent();
+ float descent = m_font->fontMetrics().floatDescent();
+ FloatPoint current = toFloatPoint(localAnchor());
+ size_t numGlyphs = m_glyphs.size();
+ for (size_t i = 0; i < numGlyphs; ++i) {
+ GlyphBufferAdvance advance = m_advances[i];
+ FloatRect glyphRect = FloatRect(current.x(), current.y() - ascent, advance.width(), ascent + descent);
+ m_bounds.unite(glyphRect);
+
+ current.move(advance.width(), advance.height());
+ }
+}
+
+std::optional<FloatRect> DrawGlyphs::localBounds(const GraphicsContext&) const
+{
+ FloatRect localBounds = m_bounds;
+ localBounds.move(m_blockLocation.x(), m_blockLocation.y());
+ return localBounds;
+}
+
+static TextStream& operator<<(TextStream& ts, const DrawGlyphs& item)
+{
+ ts << static_cast<const DrawingItem&>(item);
+ // FIXME: dump more stuff.
+ ts.dumpProperty("block-location", item.blockLocation());
+ ts.dumpProperty("local-anchor", item.localAnchor());
+ ts.dumpProperty("anchor-point", item.anchorPoint());
+ ts.dumpProperty("length", item.glyphs().size());
+
+ return ts;
+}
+
+DrawImage::DrawImage(Image& image, const FloatRect& destination, const FloatRect& source, const ImagePaintingOptions& imagePaintingOptions)
+ : DrawingItem(ItemType::DrawImage)
+ , m_image(image)
+ , m_destination(destination)
+ , m_source(source)
+ , m_imagePaintingOptions(imagePaintingOptions)
+{
+}
+
+void DrawImage::apply(GraphicsContext& context) const
+{
+ context.drawImage(m_image.get(), m_destination, m_source, m_imagePaintingOptions);
+}
+
+static TextStream& operator<<(TextStream& ts, const DrawImage& item)
+{
+ ts << static_cast<const DrawingItem&>(item);
+ ts.dumpProperty("image", item.image());
+ ts.dumpProperty("source-rect", item.source());
+ ts.dumpProperty("dest-rect", item.destination());
+ return ts;
+}
+
+DrawTiledImage::DrawTiledImage(Image& image, const FloatRect& destination, const FloatPoint& source, const FloatSize& tileSize, const FloatSize& spacing, const ImagePaintingOptions& imagePaintingOptions)
+ : DrawingItem(ItemType::DrawTiledImage)
+ , m_image(image)
+ , m_destination(destination)
+ , m_source(source)
+ , m_tileSize(tileSize)
+ , m_spacing(spacing)
+ , m_imagePaintingOptions(imagePaintingOptions)
+{
+}
+
+void DrawTiledImage::apply(GraphicsContext& context) const
+{
+ context.drawTiledImage(m_image.get(), m_destination, m_source, m_tileSize, m_spacing, m_imagePaintingOptions);
+}
+
+static TextStream& operator<<(TextStream& ts, const DrawTiledImage& item)
+{
+ ts << static_cast<const DrawingItem&>(item);
+ ts.dumpProperty("image", item.image());
+ ts.dumpProperty("source-point", item.source());
+ ts.dumpProperty("dest-rect", item.destination());
+ ts.dumpProperty("tile-size", item.tileSize());
+ ts.dumpProperty("spacing", item.spacing());
+ return ts;
+}
+
+DrawTiledScaledImage::DrawTiledScaledImage(Image& image, const FloatRect& destination, const FloatRect& source, const FloatSize& tileScaleFactor, Image::TileRule hRule, Image::TileRule vRule, const ImagePaintingOptions& imagePaintingOptions)
+ : DrawingItem(ItemType::DrawTiledScaledImage)
+ , m_image(image)
+ , m_destination(destination)
+ , m_source(source)
+ , m_tileScaleFactor(tileScaleFactor)
+ , m_hRule(hRule)
+ , m_vRule(vRule)
+ , m_imagePaintingOptions(imagePaintingOptions)
+{
+}
+
+void DrawTiledScaledImage::apply(GraphicsContext& context) const
+{
+ context.drawTiledImage(m_image.get(), m_destination, m_source, m_tileScaleFactor, m_hRule, m_vRule, m_imagePaintingOptions);
+}
+
+static TextStream& operator<<(TextStream& ts, const DrawTiledScaledImage& item)
+{
+ ts << static_cast<const DrawingItem&>(item);
+ ts.dumpProperty("image", item.image());
+ ts.dumpProperty("source-rect", item.source());
+ ts.dumpProperty("dest-rect", item.destination());
+ return ts;
+}
+
+#if USE(CG) || USE(CAIRO)
+DrawNativeImage::DrawNativeImage(const NativeImagePtr& image, const FloatSize& imageSize, const FloatRect& destRect, const FloatRect& srcRect, CompositeOperator op, BlendMode blendMode, ImageOrientation orientation)
+ : DrawingItem(ItemType::DrawNativeImage)
+#if USE(CG)
+ // FIXME: Need to store an image for Cairo.
+ , m_image(image)
+#endif
+ , m_imageSize(imageSize)
+ , m_destination(destRect)
+ , m_srcRect(srcRect)
+#if USE(CG)
+ , m_op(op)
+ , m_blendMode(blendMode)
+#endif
+ , m_orientation(orientation)
+{
+#if !USE(CG)
+ UNUSED_PARAM(image);
+ UNUSED_PARAM(op);
+ UNUSED_PARAM(blendMode);
+#endif
+}
+
+void DrawNativeImage::apply(GraphicsContext& context) const
+{
+#if USE(CG)
+ context.drawNativeImage(m_image, m_imageSize, m_destination, m_srcRect, m_op, m_blendMode, m_orientation);
+#else
+ UNUSED_PARAM(context);
+#endif
+}
+
+static TextStream& operator<<(TextStream& ts, const DrawNativeImage& item)
+{
+ ts << static_cast<const DrawingItem&>(item);
+ // FIXME: dump more stuff.
+ ts.dumpProperty("source-rect", item.source());
+ ts.dumpProperty("dest-rect", item.destination());
+ return ts;
+}
+#endif
+
+DrawPattern::DrawPattern(Image& image, const FloatRect& destRect, const FloatRect& tileRect, const AffineTransform& patternTransform, const FloatPoint& phase, const FloatSize& spacing, CompositeOperator op, BlendMode blendMode)
+ : DrawingItem(ItemType::DrawPattern)
+ , m_image(image)
+ , m_patternTransform(patternTransform)
+ , m_tileRect(tileRect)
+ , m_destination(destRect)
+ , m_phase(phase)
+ , m_spacing(spacing)
+ , m_op(op)
+ , m_blendMode(blendMode)
+{
+}
+
+void DrawPattern::apply(GraphicsContext& context) const
+{
+ context.drawPattern(m_image.get(), m_destination, m_tileRect, m_patternTransform, m_phase, m_spacing, m_op, m_blendMode);
+}
+
+static TextStream& operator<<(TextStream& ts, const DrawPattern& item)
+{
+ ts << static_cast<const DrawingItem&>(item);
+ ts.dumpProperty("image", item.image());
+ ts.dumpProperty("pattern-transform", item.patternTransform());
+ ts.dumpProperty("tile-rect", item.tileRect());
+ ts.dumpProperty("dest-rect", item.destRect());
+ ts.dumpProperty("phase", item.phase());
+ ts.dumpProperty("spacing", item.spacing());
+ return ts;
+}
+
+void DrawRect::apply(GraphicsContext& context) const
+{
+ context.drawRect(m_rect, m_borderThickness);
+}
+
+static TextStream& operator<<(TextStream& ts, const DrawRect& item)
+{
+ ts << static_cast<const DrawingItem&>(item);
+ ts.dumpProperty("rect", item.rect());
+ ts.dumpProperty("border-thickness", item.borderThickness());
+ return ts;
+}
+
+std::optional<FloatRect> DrawLine::localBounds(const GraphicsContext&) const
+{
+ FloatRect bounds;
+ bounds.fitToPoints(m_point1, m_point2);
+ return bounds;
+}
+
+void DrawLine::apply(GraphicsContext& context) const
+{
+ context.drawLine(m_point1, m_point2);
+}
+
+static TextStream& operator<<(TextStream& ts, const DrawLine& item)
+{
+ ts << static_cast<const DrawingItem&>(item);
+ ts.dumpProperty("point-1", item.point1());
+ ts.dumpProperty("point-2", item.point2());
+ return ts;
+}
+
+void DrawLinesForText::apply(GraphicsContext& context) const
+{
+ context.drawLinesForText(point(), m_widths, m_printing, m_doubleLines);
+}
+
+std::optional<FloatRect> DrawLinesForText::localBounds(const GraphicsContext&) const
+{
+ // This function needs to return a value equal to or enclosing what GraphicsContext::computeLineBoundsAndAntialiasingModeForText() returns.
+
+ if (!m_widths.size())
+ return FloatRect();
+
+ FloatRect result(point(), FloatSize(m_widths.last(), m_strokeWidth));
+ result.inflate(1); // Account for pixel snapping. FIXME: This isn't perfect, as it doesn't take the CTM into account.
+ return result;
+}
+
+static TextStream& operator<<(TextStream& ts, const DrawLinesForText& item)
+{
+ ts << static_cast<const DrawingItem&>(item);
+ ts.dumpProperty("block-location", item.blockLocation());
+ ts.dumpProperty("local-anchor", item.localAnchor());
+ ts.dumpProperty("point", item.point());
+ ts.dumpProperty("double", item.doubleLines());
+ ts.dumpProperty("widths", item.widths());
+ ts.dumpProperty("is-printing", item.isPrinting());
+ ts.dumpProperty("double", item.doubleLines());
+ return ts;
+}
+
+void DrawLineForDocumentMarker::apply(GraphicsContext& context) const
+{
+ context.drawLineForDocumentMarker(m_point, m_width, m_style);
+}
+
+std::optional<FloatRect> DrawLineForDocumentMarker::localBounds(const GraphicsContext&) const
+{
+ // This function needs to return a value equal to or enclosing what GraphicsContext::drawLineForDocumentMarker() returns.
+
+ FloatRect result(m_point, FloatSize(m_width, cMisspellingLineThickness));
+ result.inflate(cMisspellingLineThickness); // Account for "misspelling dot" snapping.
+ return result;
+}
+
+static TextStream& operator<<(TextStream& ts, const DrawLineForDocumentMarker& item)
+{
+ ts << static_cast<const DrawingItem&>(item);
+ ts.dumpProperty("point", item.point());
+ ts.dumpProperty("width", item.width());
+ return ts;
+}
+
+void DrawEllipse::apply(GraphicsContext& context) const
+{
+ context.drawEllipse(m_rect);
+}
+
+static TextStream& operator<<(TextStream& ts, const DrawEllipse& item)
+{
+ ts.dumpProperty("rect", item.rect());
+ return ts;
+}
+
+void DrawPath::apply(GraphicsContext& context) const
+{
+#if USE(CG)
+ context.drawPath(m_path);
+#else
+ UNUSED_PARAM(context);
+#endif
+}
+
+static TextStream& operator<<(TextStream& ts, const DrawPath& item)
+{
+ ts << static_cast<const DrawingItem&>(item);
+// ts.dumpProperty("path", item.path()); // FIXME: add logging for paths.
+ return ts;
+}
+
+void DrawFocusRingPath::apply(GraphicsContext& context) const
+{
+ context.drawFocusRing(m_path, m_width, m_offset, m_color);
+}
+
+std::optional<FloatRect> DrawFocusRingPath::localBounds(const GraphicsContext&) const
+{
+ FloatRect result = m_path.fastBoundingRect();
+ result.inflate(platformFocusRingWidth);
+ return result;
+}
+
+static TextStream& operator<<(TextStream& ts, const DrawFocusRingPath& item)
+{
+ ts << static_cast<const DrawingItem&>(item);
+// ts.dumpProperty("path", item.path()); // FIXME: add logging for paths.
+ ts.dumpProperty("width", item.width());
+ ts.dumpProperty("offset", item.offset());
+ ts.dumpProperty("color", item.color());
+ return ts;
+}
+
+void DrawFocusRingRects::apply(GraphicsContext& context) const
+{
+ context.drawFocusRing(m_rects, m_width, m_offset, m_color);
+}
+
+std::optional<FloatRect> DrawFocusRingRects::localBounds(const GraphicsContext&) const
+{
+ FloatRect result;
+ for (auto& rect : m_rects)
+ result.unite(rect);
+ result.inflate(platformFocusRingWidth);
+ return result;
+}
+
+static TextStream& operator<<(TextStream& ts, const DrawFocusRingRects& item)
+{
+ ts << static_cast<const DrawingItem&>(item);
+ ts.dumpProperty("rects", item.rects());
+ ts.dumpProperty("width", item.width());
+ ts.dumpProperty("offset", item.offset());
+ ts.dumpProperty("color", item.color());
+ return ts;
+}
+
+void FillRect::apply(GraphicsContext& context) const
+{
+ context.fillRect(m_rect);
+}
+
+static TextStream& operator<<(TextStream& ts, const FillRect& item)
+{
+ ts << static_cast<const DrawingItem&>(item);
+ ts.dumpProperty("rect", item.rect());
+ return ts;
+}
+
+void FillRectWithColor::apply(GraphicsContext& context) const
+{
+ context.fillRect(m_rect, m_color);
+}
+
+static TextStream& operator<<(TextStream& ts, const FillRectWithColor& item)
+{
+ ts << static_cast<const DrawingItem&>(item);
+ ts.dumpProperty("rect", item.rect());
+ ts.dumpProperty("color", item.color());
+ return ts;
+}
+
+void FillRectWithGradient::apply(GraphicsContext& context) const
+{
+ context.fillRect(m_rect, m_gradient.get());
+}
+
+static TextStream& operator<<(TextStream& ts, const FillRectWithGradient& item)
+{
+ ts << static_cast<const DrawingItem&>(item);
+ // FIXME: log gradient.
+ ts.dumpProperty("rect", item.rect());
+ return ts;
+}
+
+void FillCompositedRect::apply(GraphicsContext& context) const
+{
+ context.fillRect(m_rect, m_color, m_op, m_blendMode);
+}
+
+static TextStream& operator<<(TextStream& ts, const FillCompositedRect& item)
+{
+ ts << static_cast<const DrawingItem&>(item);
+ ts.dumpProperty("rect", item.rect());
+ ts.dumpProperty("color", item.color());
+ ts.dumpProperty("composite-operation", item.compositeOperator());
+ ts.dumpProperty("blend-mode", item.blendMode());
+ return ts;
+}
+
+void FillRoundedRect::apply(GraphicsContext& context) const
+{
+ context.fillRoundedRect(m_rect, m_color, m_blendMode);
+}
+
+static TextStream& operator<<(TextStream& ts, const FillRoundedRect& item)
+{
+ ts << static_cast<const DrawingItem&>(item);
+ ts.dumpProperty("rect", item.roundedRect());
+ ts.dumpProperty("color", item.color());
+ ts.dumpProperty("blend-mode", item.blendMode());
+ return ts;
+}
+
+void FillRectWithRoundedHole::apply(GraphicsContext& context) const
+{
+ context.fillRectWithRoundedHole(m_rect, m_roundedHoleRect, m_color);
+}
+
+static TextStream& operator<<(TextStream& ts, const FillRectWithRoundedHole& item)
+{
+ ts << static_cast<const DrawingItem&>(item);
+ ts.dumpProperty("rect", item.rect());
+ ts.dumpProperty("rounded-hole-rect", item.roundedHoleRect());
+ ts.dumpProperty("color", item.color());
+ return ts;
+}
+
+void FillPath::apply(GraphicsContext& context) const
+{
+ context.fillPath(m_path);
+}
+
+static TextStream& operator<<(TextStream& ts, const FillPath& item)
+{
+ ts << static_cast<const DrawingItem&>(item);
+ ts.dumpProperty("path", item.path());
+ return ts;
+}
+
+void FillEllipse::apply(GraphicsContext& context) const
+{
+ context.fillEllipse(m_rect);
+}
+
+static TextStream& operator<<(TextStream& ts, const FillEllipse& item)
+{
+ ts << static_cast<const DrawingItem&>(item);
+ ts.dumpProperty("rect", item.rect());
+ return ts;
+}
+
+std::optional<FloatRect> StrokeRect::localBounds(const GraphicsContext&) const
+{
+ FloatRect bounds = m_rect;
+ bounds.expand(m_lineWidth, m_lineWidth);
+ return bounds;
+}
+
+void StrokeRect::apply(GraphicsContext& context) const
+{
+ context.strokeRect(m_rect, m_lineWidth);
+}
+
+static TextStream& operator<<(TextStream& ts, const StrokeRect& item)
+{
+ ts << static_cast<const DrawingItem&>(item);
+ ts.dumpProperty("rect", item.rect());
+ ts.dumpProperty("line-width", item.lineWidth());
+ return ts;
+}
+
+std::optional<FloatRect> StrokePath::localBounds(const GraphicsContext& context) const
+{
+ // FIXME: Need to take stroke thickness into account correctly, via CGPathByStrokingPath().
+ float strokeThickness = context.strokeThickness();
+
+ FloatRect bounds = m_path.boundingRect();
+ bounds.expand(strokeThickness, strokeThickness);
+ return bounds;
+}
+
+void StrokePath::apply(GraphicsContext& context) const
+{
+ context.strokePath(m_path);
+}
+
+static TextStream& operator<<(TextStream& ts, const StrokePath& item)
+{
+ ts << static_cast<const DrawingItem&>(item);
+ ts.dumpProperty("path", item.path());
+ return ts;
+}
+
+std::optional<FloatRect> StrokeEllipse::localBounds(const GraphicsContext& context) const
+{
+ float strokeThickness = context.strokeThickness();
+
+ FloatRect bounds = m_rect;
+ bounds.expand(strokeThickness, strokeThickness);
+ return bounds;
+}
+
+void StrokeEllipse::apply(GraphicsContext& context) const
+{
+ context.strokeEllipse(m_rect);
+}
+
+static TextStream& operator<<(TextStream& ts, const StrokeEllipse& item)
+{
+ ts << static_cast<const DrawingItem&>(item);
+ ts.dumpProperty("rect", item.rect());
+ return ts;
+}
+
+void ClearRect::apply(GraphicsContext& context) const
+{
+ context.clearRect(m_rect);
+}
+
+static TextStream& operator<<(TextStream& ts, const ClearRect& item)
+{
+ ts << static_cast<const DrawingItem&>(item);
+ ts.dumpProperty("rect", item.rect());
+ return ts;
+}
+
+void BeginTransparencyLayer::apply(GraphicsContext& context) const
+{
+ context.beginTransparencyLayer(m_opacity);
+}
+
+static TextStream& operator<<(TextStream& ts, const BeginTransparencyLayer& item)
+{
+ ts << static_cast<const DrawingItem&>(item);
+ ts.dumpProperty("opacity", item.opacity());
+ return ts;
+}
+
+void EndTransparencyLayer::apply(GraphicsContext& context) const
+{
+ context.endTransparencyLayer();
+}
+
+#if USE(CG)
+void ApplyStrokePattern::apply(GraphicsContext& context) const
+{
+ context.applyStrokePattern();
+}
+
+void ApplyFillPattern::apply(GraphicsContext& context) const
+{
+ context.applyFillPattern();
+}
+#endif
+
+void ApplyDeviceScaleFactor::apply(GraphicsContext& context) const
+{
+ context.applyDeviceScaleFactor(m_scaleFactor);
+}
+
+static TextStream& operator<<(TextStream& ts, const ApplyDeviceScaleFactor& item)
+{
+ ts.dumpProperty("scale-factor", item.scaleFactor());
+ return ts;
+}
+
+static TextStream& operator<<(TextStream& ts, const ItemType& type)
+{
+ switch (type) {
+ case ItemType::Save: ts << "save"; break;
+ case ItemType::Restore: ts << "restore"; break;
+ case ItemType::Translate: ts << "translate"; break;
+ case ItemType::Rotate: ts << "rotate"; break;
+ case ItemType::Scale: ts << "scale"; break;
+ case ItemType::ConcatenateCTM: ts << "concatentate-ctm"; break;
+ case ItemType::SetState: ts << "set-state"; break;
+ case ItemType::SetLineCap: ts << "set-line-cap"; break;
+ case ItemType::SetLineDash: ts << "set-line-dash"; break;
+ case ItemType::SetLineJoin: ts << "set-line-join"; break;
+ case ItemType::SetMiterLimit: ts << "set-miter-limit"; break;
+ case ItemType::Clip: ts << "clip"; break;
+ case ItemType::ClipOut: ts << "clip-out"; break;
+ case ItemType::ClipOutToPath: ts << "clip-out-to-path"; break;
+ case ItemType::ClipPath: ts << "clip-path"; break;
+ case ItemType::DrawGlyphs: ts << "draw-glyphs"; break;
+ case ItemType::DrawImage: ts << "draw-image"; break;
+ case ItemType::DrawTiledImage: ts << "draw-tiled-image"; break;
+ case ItemType::DrawTiledScaledImage: ts << "draw-tiled-scaled-image"; break;
+#if USE(CG) || USE(CAIRO)
+ case ItemType::DrawNativeImage: ts << "draw-native-image"; break;
+#endif
+ case ItemType::DrawPattern: ts << "draw-pattern"; break;
+ case ItemType::DrawRect: ts << "draw-rect"; break;
+ case ItemType::DrawLine: ts << "draw-line"; break;
+ case ItemType::DrawLinesForText: ts << "draw-lines-for-text"; break;
+ case ItemType::DrawLineForDocumentMarker: ts << "draw-lines-for-document-marker"; break;
+ case ItemType::DrawEllipse: ts << "draw-ellipse"; break;
+ case ItemType::DrawPath: ts << "draw-path"; break;
+ case ItemType::DrawFocusRingPath: ts << "draw-focus-ring-path"; break;
+ case ItemType::DrawFocusRingRects: ts << "draw-focus-ring-rects"; break;
+ case ItemType::FillRect: ts << "fill-rect"; break;
+ case ItemType::FillRectWithColor: ts << "fill-rect-with-color"; break;
+ case ItemType::FillRectWithGradient: ts << "fill-rect-with-gradient"; break;
+ case ItemType::FillCompositedRect: ts << "fill-composited-rect"; break;
+ case ItemType::FillRoundedRect: ts << "fill-rounded-rect"; break;
+ case ItemType::FillRectWithRoundedHole: ts << "fill-rect-with-rounded-hole"; break;
+ case ItemType::FillPath: ts << "fill-path"; break;
+ case ItemType::FillEllipse: ts << "fill-ellipse"; break;
+ case ItemType::StrokeRect: ts << "stroke-rect"; break;
+ case ItemType::StrokePath: ts << "stroke-path"; break;
+ case ItemType::StrokeEllipse: ts << "stroke-ellipse"; break;
+ case ItemType::ClearRect: ts << "clear-rect"; break;
+ case ItemType::BeginTransparencyLayer: ts << "begin-transparency-layer"; break;
+ case ItemType::EndTransparencyLayer: ts << "end-transparency-layer"; break;
+#if USE(CG)
+ case ItemType::ApplyStrokePattern: ts << "apply-stroke-pattern"; break;
+ case ItemType::ApplyFillPattern: ts << "apply-fill-pattern"; break;
+#endif
+ case ItemType::ApplyDeviceScaleFactor: ts << "apply-device-scale-factor"; break;
+ case ItemType::ClearShadow: ts << "clear-shadow"; break;
+ }
+ return ts;
+}
+
+TextStream& operator<<(TextStream& ts, const Item& item)
+{
+ TextStream::GroupScope group(ts);
+ ts << item.type();
+
+ // FIXME: Make a macro which takes a macro for all these enumeration switches
+ switch (item.type()) {
+ case ItemType::Save:
+ ts << downcast<Save>(item);
+ break;
+ case ItemType::Translate:
+ ts << downcast<Translate>(item);
+ break;
+ case ItemType::Rotate:
+ ts << downcast<Rotate>(item);
+ break;
+ case ItemType::Scale:
+ ts << downcast<Scale>(item);
+ break;
+ case ItemType::ConcatenateCTM:
+ ts << downcast<ConcatenateCTM>(item);
+ break;
+ case ItemType::SetState:
+ ts << downcast<SetState>(item);
+ break;
+ case ItemType::SetLineCap:
+ ts << downcast<SetLineCap>(item);
+ break;
+ case ItemType::SetLineDash:
+ ts << downcast<SetLineDash>(item);
+ break;
+ case ItemType::SetLineJoin:
+ ts << downcast<SetLineJoin>(item);
+ break;
+ case ItemType::SetMiterLimit:
+ ts << downcast<SetMiterLimit>(item);
+ break;
+ case ItemType::Clip:
+ ts << downcast<Clip>(item);
+ break;
+ case ItemType::ClipOut:
+ ts << downcast<ClipOut>(item);
+ break;
+ case ItemType::ClipOutToPath:
+ ts << downcast<ClipOutToPath>(item);
+ break;
+ case ItemType::ClipPath:
+ ts << downcast<ClipPath>(item);
+ break;
+ case ItemType::DrawGlyphs:
+ ts << downcast<DrawGlyphs>(item);
+ break;
+ case ItemType::DrawImage:
+ ts << downcast<DrawImage>(item);
+ break;
+ case ItemType::DrawTiledImage:
+ ts << downcast<DrawTiledImage>(item);
+ break;
+ case ItemType::DrawTiledScaledImage:
+ ts << downcast<DrawTiledScaledImage>(item);
+ break;
+#if USE(CG) || USE(CAIRO)
+ case ItemType::DrawNativeImage:
+ ts << downcast<DrawNativeImage>(item);
+ break;
+#endif
+ case ItemType::DrawPattern:
+ ts << downcast<DrawPattern>(item);
+ break;
+ case ItemType::DrawRect:
+ ts << downcast<DrawRect>(item);
+ break;
+ case ItemType::DrawLine:
+ ts << downcast<DrawLine>(item);
+ break;
+ case ItemType::DrawLinesForText:
+ ts << downcast<DrawLinesForText>(item);
+ break;
+ case ItemType::DrawLineForDocumentMarker:
+ ts << downcast<DrawLineForDocumentMarker>(item);
+ break;
+ case ItemType::DrawEllipse:
+ ts << downcast<DrawEllipse>(item);
+ break;
+ case ItemType::DrawPath:
+ ts << downcast<DrawPath>(item);
+ break;
+ case ItemType::DrawFocusRingPath:
+ ts << downcast<DrawFocusRingPath>(item);
+ break;
+ case ItemType::DrawFocusRingRects:
+ ts << downcast<DrawFocusRingRects>(item);
+ break;
+ case ItemType::FillRect:
+ ts << downcast<FillRect>(item);
+ break;
+ case ItemType::FillRectWithColor:
+ ts << downcast<FillRectWithColor>(item);
+ break;
+ case ItemType::FillRectWithGradient:
+ ts << downcast<FillRectWithGradient>(item);
+ break;
+ case ItemType::FillCompositedRect:
+ ts << downcast<FillCompositedRect>(item);
+ break;
+ case ItemType::FillRoundedRect:
+ ts << downcast<FillRoundedRect>(item);
+ break;
+ case ItemType::FillRectWithRoundedHole:
+ ts << downcast<FillRectWithRoundedHole>(item);
+ break;
+ case ItemType::FillPath:
+ ts << downcast<FillPath>(item);
+ break;
+ case ItemType::FillEllipse:
+ ts << downcast<FillEllipse>(item);
+ break;
+ case ItemType::StrokeRect:
+ ts << downcast<StrokeRect>(item);
+ break;
+ case ItemType::StrokePath:
+ ts << downcast<StrokePath>(item);
+ break;
+ case ItemType::StrokeEllipse:
+ ts << downcast<StrokeEllipse>(item);
+ break;
+ case ItemType::ClearRect:
+ ts << downcast<ClearRect>(item);
+ break;
+ case ItemType::BeginTransparencyLayer:
+ ts << downcast<BeginTransparencyLayer>(item);
+ break;
+ case ItemType::ApplyDeviceScaleFactor:
+ ts << downcast<ApplyDeviceScaleFactor>(item);
+ break;
+
+ // Items with no additional data.
+ case ItemType::Restore:
+ case ItemType::EndTransparencyLayer:
+#if USE(CG)
+ case ItemType::ApplyStrokePattern:
+ case ItemType::ApplyFillPattern:
+#endif
+ case ItemType::ClearShadow:
+ break;
+ }
+ return ts;
+}
+
+}
+}
diff --git a/Source/WebCore/platform/graphics/displaylists/DisplayListItems.h b/Source/WebCore/platform/graphics/displaylists/DisplayListItems.h
new file mode 100644
index 000000000..8158d3d28
--- /dev/null
+++ b/Source/WebCore/platform/graphics/displaylists/DisplayListItems.h
@@ -0,0 +1,1387 @@
+/*
+ * Copyright (C) 2015 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef DisplayListItems_h
+#define DisplayListItems_h
+
+#include "FloatPoint.h"
+#include "FloatRect.h"
+#include "FloatRoundedRect.h"
+#include "Font.h"
+#include "GlyphBuffer.h"
+#include "GraphicsContext.h"
+#include "Image.h"
+#include <wtf/RefCounted.h>
+#include <wtf/TypeCasts.h>
+
+#if USE(CG)
+#include "GraphicsContextPlatformPrivateCG.h"
+#endif
+
+namespace WebCore {
+
+class TextStream;
+struct ImagePaintingOptions;
+
+namespace DisplayList {
+
+enum class ItemType {
+ Save,
+ Restore,
+ Translate,
+ Rotate,
+ Scale,
+ ConcatenateCTM,
+ SetState,
+ SetLineCap,
+ SetLineDash,
+ SetLineJoin,
+ SetMiterLimit,
+ ClearShadow,
+ Clip,
+ ClipOut,
+ ClipOutToPath,
+ ClipPath,
+ DrawGlyphs,
+ DrawImage,
+ DrawTiledImage,
+ DrawTiledScaledImage,
+#if USE(CG) || USE(CAIRO)
+ DrawNativeImage,
+#endif
+ DrawPattern,
+ DrawRect,
+ DrawLine,
+ DrawLinesForText,
+ DrawLineForDocumentMarker,
+ DrawEllipse,
+ DrawPath,
+ DrawFocusRingPath,
+ DrawFocusRingRects,
+ FillRect,
+ FillRectWithColor,
+ FillRectWithGradient,
+ FillCompositedRect,
+ FillRoundedRect,
+ FillRectWithRoundedHole,
+ FillPath,
+ FillEllipse,
+ StrokeRect,
+ StrokePath,
+ StrokeEllipse,
+ ClearRect,
+ BeginTransparencyLayer,
+ EndTransparencyLayer,
+#if USE(CG)
+ ApplyStrokePattern, // FIXME: should not be a recorded item.
+ ApplyFillPattern, // FIXME: should not be a recorded item.
+#endif
+ ApplyDeviceScaleFactor,
+};
+
+class Item : public RefCounted<Item> {
+public:
+ Item() = delete;
+
+ Item(ItemType type)
+ : m_type(type)
+ {
+ }
+
+ virtual ~Item() { }
+
+ ItemType type() const
+ {
+ return m_type;
+ }
+
+ virtual void apply(GraphicsContext&) const = 0;
+
+ static constexpr bool isDisplayListItem = true;
+
+ virtual bool isDrawingItem() const { return false; }
+
+ // A state item is one preserved by Save/Restore.
+ bool isStateItem() const
+ {
+ return isStateItemType(m_type);
+ }
+
+ static bool isStateItemType(ItemType itemType)
+ {
+ switch (itemType) {
+ case ItemType:: Translate:
+ case ItemType:: Rotate:
+ case ItemType:: Scale:
+ case ItemType:: ConcatenateCTM:
+ case ItemType:: SetState:
+ case ItemType:: SetLineCap:
+ case ItemType:: SetLineDash:
+ case ItemType:: SetLineJoin:
+ case ItemType:: SetMiterLimit:
+ case ItemType:: ClearShadow:
+ return true;
+ default:
+ return false;
+ }
+ return false;
+ }
+
+#if !defined(NDEBUG) || !LOG_DISABLED
+ WTF::CString description() const;
+#endif
+ static size_t sizeInBytes(const Item&);
+
+private:
+ ItemType m_type;
+};
+
+class DrawingItem : public Item {
+public:
+ DrawingItem(ItemType type)
+ : Item(type)
+ {
+ }
+
+ void setExtent(const FloatRect& r) { m_extent = r; }
+ const FloatRect& extent() const { return m_extent.value(); }
+
+ bool extentKnown() const { return static_cast<bool>(m_extent); }
+
+ // Return bounds of this drawing operation in local coordinates.
+ // Does not include effets of transform, shadow etc in the state.
+ virtual std::optional<FloatRect> localBounds(const GraphicsContext&) const { return std::nullopt; }
+
+private:
+ bool isDrawingItem() const override { return true; }
+
+ std::optional<FloatRect> m_extent; // In base coordinates, taking shadows and transforms into account.
+};
+
+class Save : public Item {
+public:
+ static Ref<Save> create()
+ {
+ return adoptRef(*new Save);
+ }
+
+ // Index in the display list of the corresponding Restore item. 0 if unmatched.
+ size_t restoreIndex() const { return m_restoreIndex; }
+ void setRestoreIndex(size_t index) { m_restoreIndex = index; }
+
+private:
+ Save()
+ : Item(ItemType::Save)
+ {
+ }
+
+ void apply(GraphicsContext&) const override;
+
+ size_t m_restoreIndex { 0 };
+};
+
+class Restore : public Item {
+public:
+ static Ref<Restore> create()
+ {
+ return adoptRef(*new Restore);
+ }
+
+private:
+ Restore()
+ : Item(ItemType::Restore)
+ {
+ }
+
+ void apply(GraphicsContext&) const override;
+};
+
+class Translate : public Item {
+public:
+ static Ref<Translate> create(float x, float y)
+ {
+ return adoptRef(*new Translate(x, y));
+ }
+
+ float x() const { return m_x; }
+ float y() const { return m_y; }
+
+private:
+ Translate(float x, float y)
+ : Item(ItemType::Translate)
+ , m_x(x)
+ , m_y(y)
+ {
+ }
+
+ void apply(GraphicsContext&) const override;
+
+ float m_x;
+ float m_y;
+};
+
+class Rotate : public Item {
+public:
+ static Ref<Rotate> create(float angleInRadians)
+ {
+ return adoptRef(*new Rotate(angleInRadians));
+ }
+
+ float angle() const { return m_angle; }
+
+private:
+ Rotate(float angle)
+ : Item(ItemType::Rotate)
+ , m_angle(angle)
+ {
+ }
+
+ void apply(GraphicsContext&) const override;
+
+ float m_angle; // In radians.
+};
+
+class Scale : public Item {
+public:
+ static Ref<Scale> create(const FloatSize& size)
+ {
+ return adoptRef(*new Scale(size));
+ }
+
+ const FloatSize& amount() const { return m_size; }
+
+private:
+ Scale(const FloatSize& size)
+ : Item(ItemType::Scale)
+ , m_size(size)
+ {
+ }
+
+ void apply(GraphicsContext&) const override;
+
+ FloatSize m_size;
+};
+
+class ConcatenateCTM : public Item {
+public:
+ static Ref<ConcatenateCTM> create(const AffineTransform& matrix)
+ {
+ return adoptRef(*new ConcatenateCTM(matrix));
+ }
+
+ const AffineTransform& transform() const { return m_transform; }
+
+private:
+ ConcatenateCTM(const AffineTransform&);
+
+ void apply(GraphicsContext&) const override;
+
+ AffineTransform m_transform;
+};
+
+class SetState : public Item {
+public:
+ static Ref<SetState> create(const GraphicsContextState& state, GraphicsContextState::StateChangeFlags flags)
+ {
+ return adoptRef(*new SetState(state, flags));
+ }
+
+ const GraphicsContextStateChange& state() const { return m_state; }
+
+ void accumulate(const GraphicsContextState&, GraphicsContextState::StateChangeFlags);
+
+ void accumulate(GraphicsContextState&) const;
+
+ static void applyState(GraphicsContext&, const GraphicsContextState&, GraphicsContextState::StateChangeFlags);
+
+ static void dumpStateChanges(TextStream&, const GraphicsContextState&, GraphicsContextState::StateChangeFlags);
+private:
+ SetState(const GraphicsContextState& state, GraphicsContextState::StateChangeFlags flags)
+ : Item(ItemType::SetState)
+ , m_state(state, flags)
+ {
+ }
+
+ void apply(GraphicsContext&) const override;
+
+ GraphicsContextStateChange m_state;
+};
+
+class SetLineCap : public Item {
+public:
+ static Ref<SetLineCap> create(LineCap lineCap)
+ {
+ return adoptRef(*new SetLineCap(lineCap));
+ }
+
+ LineCap lineCap() const { return m_lineCap; }
+
+private:
+ SetLineCap(LineCap lineCap)
+ : Item(ItemType::SetLineCap)
+ , m_lineCap(lineCap)
+ {
+ }
+
+ void apply(GraphicsContext&) const override;
+
+ LineCap m_lineCap;
+};
+
+class SetLineDash : public Item {
+public:
+ static Ref<SetLineDash> create(const DashArray& dashArray, float dashOffset)
+ {
+ return adoptRef(*new SetLineDash(dashArray, dashOffset));
+ }
+
+ const DashArray& dashArray() const { return m_dashArray; }
+ float dashOffset() const { return m_dashOffset; }
+
+private:
+ SetLineDash(const DashArray& dashArray, float dashOffset)
+ : Item(ItemType::SetLineDash)
+ , m_dashArray(dashArray)
+ , m_dashOffset(dashOffset)
+ {
+ }
+
+ void apply(GraphicsContext&) const override;
+
+ DashArray m_dashArray;
+ float m_dashOffset;
+};
+
+class SetLineJoin : public Item {
+public:
+ static Ref<SetLineJoin> create(LineJoin lineJoin)
+ {
+ return adoptRef(*new SetLineJoin(lineJoin));
+ }
+
+ LineJoin lineJoin() const { return m_lineJoin; }
+
+private:
+ SetLineJoin(LineJoin lineJoin)
+ : Item(ItemType::SetLineJoin)
+ , m_lineJoin(lineJoin)
+ {
+ }
+
+ void apply(GraphicsContext&) const override;
+
+ LineJoin m_lineJoin;
+};
+
+class SetMiterLimit : public Item {
+public:
+ static Ref<SetMiterLimit> create(float limit)
+ {
+ return adoptRef(*new SetMiterLimit(limit));
+ }
+
+ float miterLimit() const { return m_miterLimit; }
+
+private:
+ SetMiterLimit(float miterLimit)
+ : Item(ItemType::SetMiterLimit)
+ , m_miterLimit(miterLimit)
+ {
+ }
+
+ void apply(GraphicsContext&) const override;
+
+ float m_miterLimit;
+};
+
+class ClearShadow : public Item {
+public:
+ static Ref<ClearShadow> create()
+ {
+ return adoptRef(*new ClearShadow);
+ }
+
+private:
+ ClearShadow()
+ : Item(ItemType::ClearShadow)
+ {
+ }
+
+ void apply(GraphicsContext&) const override;
+};
+
+// FIXME: treat as DrawingItem?
+class Clip : public Item {
+public:
+ static Ref<Clip> create(const FloatRect& rect)
+ {
+ return adoptRef(*new Clip(rect));
+ }
+
+ FloatRect rect() const { return m_rect; }
+
+private:
+ Clip(const FloatRect& rect)
+ : Item(ItemType::Clip)
+ , m_rect(rect)
+ {
+ }
+
+ void apply(GraphicsContext&) const override;
+
+ FloatRect m_rect;
+};
+
+class ClipOut : public Item {
+public:
+ static Ref<ClipOut> create(const FloatRect& rect)
+ {
+ return adoptRef(*new ClipOut(rect));
+ }
+
+ FloatRect rect() const { return m_rect; }
+
+private:
+ ClipOut(const FloatRect& rect)
+ : Item(ItemType::ClipOut)
+ , m_rect(rect)
+ {
+ }
+
+ void apply(GraphicsContext&) const override;
+
+ FloatRect m_rect;
+};
+
+class ClipOutToPath : public Item {
+public:
+ static Ref<ClipOutToPath> create(const Path& path)
+ {
+ return adoptRef(*new ClipOutToPath(path));
+ }
+
+ const Path& path() const { return m_path; }
+
+private:
+ ClipOutToPath(const Path& path)
+ : Item(ItemType::ClipOutToPath)
+ , m_path(path)
+ {
+ }
+
+ void apply(GraphicsContext&) const override;
+
+ const Path m_path;
+};
+
+class ClipPath : public Item {
+public:
+ static Ref<ClipPath> create(const Path& path, WindRule windRule)
+ {
+ return adoptRef(*new ClipPath(path, windRule));
+ }
+
+ const Path& path() const { return m_path; }
+ WindRule windRule() const { return m_windRule; }
+
+private:
+ ClipPath(const Path& path, WindRule windRule)
+ : Item(ItemType::ClipPath)
+ , m_path(path)
+ , m_windRule(windRule)
+ {
+ }
+
+ void apply(GraphicsContext&) const override;
+
+ const Path m_path;
+ WindRule m_windRule;
+};
+
+class DrawGlyphs : public DrawingItem {
+public:
+ static Ref<DrawGlyphs> create(const Font& font, const GlyphBufferGlyph* glyphs, const GlyphBufferAdvance* advances, unsigned count, const FloatPoint& blockLocation, const FloatSize& localAnchor, FontSmoothingMode smoothingMode)
+ {
+ return adoptRef(*new DrawGlyphs(font, glyphs, advances, count, blockLocation, localAnchor, smoothingMode));
+ }
+
+ const FloatPoint& blockLocation() const { return m_blockLocation; }
+ void setBlockLocation(const FloatPoint& blockLocation) { m_blockLocation = blockLocation; }
+
+ const FloatSize& localAnchor() const { return m_localAnchor; }
+
+ FloatPoint anchorPoint() const { return m_blockLocation + m_localAnchor; }
+
+ const Vector<GlyphBufferGlyph, 128>& glyphs() const { return m_glyphs; }
+
+private:
+ DrawGlyphs(const Font&, const GlyphBufferGlyph*, const GlyphBufferAdvance*, unsigned count, const FloatPoint& blockLocation, const FloatSize& localAnchor, FontSmoothingMode);
+
+ void computeBounds();
+
+ void apply(GraphicsContext&) const override;
+
+ std::optional<FloatRect> localBounds(const GraphicsContext&) const override;
+
+ GlyphBuffer generateGlyphBuffer() const;
+
+ Ref<Font> m_font;
+ Vector<GlyphBufferGlyph, 128> m_glyphs;
+ Vector<GlyphBufferAdvance, 128> m_advances;
+ FloatRect m_bounds;
+ FloatPoint m_blockLocation;
+ FloatSize m_localAnchor;
+ FontSmoothingMode m_smoothingMode;
+};
+
+class DrawImage : public DrawingItem {
+public:
+ static Ref<DrawImage> create(Image& image, const FloatRect& destination, const FloatRect& source, const ImagePaintingOptions& imagePaintingOptions)
+ {
+ return adoptRef(*new DrawImage(image, destination, source, imagePaintingOptions));
+ }
+
+ const Image& image() const { return m_image.get(); }
+ FloatRect source() const { return m_source; }
+ FloatRect destination() const { return m_destination; }
+
+private:
+ DrawImage(Image&, const FloatRect& destination, const FloatRect& source, const ImagePaintingOptions&);
+
+ void apply(GraphicsContext&) const override;
+
+ std::optional<FloatRect> localBounds(const GraphicsContext&) const override { return m_destination; }
+
+ mutable Ref<Image> m_image; // FIXME: Drawing images can cause their animations to progress. This shouldn't have to be mutable.
+ FloatRect m_destination;
+ FloatRect m_source;
+ ImagePaintingOptions m_imagePaintingOptions;
+};
+
+class DrawTiledImage : public DrawingItem {
+public:
+ static Ref<DrawTiledImage> create(Image& image, const FloatRect& destination, const FloatPoint& source, const FloatSize& tileSize, const FloatSize& spacing, const ImagePaintingOptions& imagePaintingOptions)
+ {
+ return adoptRef(*new DrawTiledImage(image, destination, source, tileSize, spacing, imagePaintingOptions));
+ }
+
+ const Image& image() const { return m_image.get(); }
+ FloatPoint source() const { return m_source; }
+ FloatRect destination() const { return m_destination; }
+
+ FloatSize tileSize() const { return m_tileSize; }
+ FloatSize spacing() const { return m_spacing; }
+
+private:
+ DrawTiledImage(Image&, const FloatRect& destination, const FloatPoint& source, const FloatSize& tileSize, const FloatSize& spacing, const ImagePaintingOptions&);
+
+ void apply(GraphicsContext&) const override;
+
+ std::optional<FloatRect> localBounds(const GraphicsContext&) const override { return m_destination; }
+
+ mutable Ref<Image> m_image; // FIXME: Drawing images can cause their animations to progress. This shouldn't have to be mutable.
+ FloatRect m_destination;
+ FloatPoint m_source;
+ FloatSize m_tileSize;
+ FloatSize m_spacing;
+ ImagePaintingOptions m_imagePaintingOptions;
+};
+
+class DrawTiledScaledImage : public DrawingItem {
+public:
+ static Ref<DrawTiledScaledImage> create(Image& image, const FloatRect& destination, const FloatRect& source, const FloatSize& tileScaleFactor, Image::TileRule hRule, Image::TileRule vRule, const ImagePaintingOptions& imagePaintingOptions)
+ {
+ return adoptRef(*new DrawTiledScaledImage(image, destination, source, tileScaleFactor, hRule, vRule, imagePaintingOptions));
+ }
+
+ const Image& image() const { return m_image.get(); }
+ FloatRect source() const { return m_source; }
+ FloatRect destination() const { return m_destination; }
+
+private:
+ DrawTiledScaledImage(Image&, const FloatRect& destination, const FloatRect& source, const FloatSize& tileScaleFactor, Image::TileRule hRule, Image::TileRule vRule, const ImagePaintingOptions&);
+
+ void apply(GraphicsContext&) const override;
+
+ std::optional<FloatRect> localBounds(const GraphicsContext&) const override { return m_destination; }
+
+ mutable Ref<Image> m_image; // FIXME: Drawing images can cause their animations to progress. This shouldn't have to be mutable.
+ FloatRect m_destination;
+ FloatRect m_source;
+ FloatSize m_tileScaleFactor;
+ Image::TileRule m_hRule;
+ Image::TileRule m_vRule;
+ ImagePaintingOptions m_imagePaintingOptions;
+};
+
+#if USE(CG) || USE(CAIRO)
+class DrawNativeImage : public DrawingItem {
+public:
+ static Ref<DrawNativeImage> create(const NativeImagePtr& image, const FloatSize& imageSize, const FloatRect& destRect, const FloatRect& srcRect, CompositeOperator op, BlendMode blendMode, ImageOrientation orientation)
+ {
+ return adoptRef(*new DrawNativeImage(image, imageSize, destRect, srcRect, op, blendMode, orientation));
+ }
+
+ FloatRect source() const { return m_srcRect; }
+ FloatRect destination() const { return m_destination; }
+
+private:
+ DrawNativeImage(const NativeImagePtr&, const FloatSize& selfSize, const FloatRect& destRect, const FloatRect& srcRect, CompositeOperator, BlendMode, ImageOrientation);
+
+ void apply(GraphicsContext&) const override;
+
+ std::optional<FloatRect> localBounds(const GraphicsContext&) const override { return m_destination; }
+
+#if USE(CG)
+ RetainPtr<CGImageRef> m_image;
+#endif
+ FloatSize m_imageSize;
+ FloatRect m_destination;
+ FloatRect m_srcRect;
+#if USE(CG)
+ CompositeOperator m_op;
+ BlendMode m_blendMode;
+#endif
+ ImageOrientation m_orientation;
+};
+#endif
+
+class DrawPattern : public DrawingItem {
+public:
+ static Ref<DrawPattern> create(Image& image, const FloatRect& destRect, const FloatRect& tileRect, const AffineTransform& patternTransform, const FloatPoint& phase, const FloatSize& spacing, CompositeOperator op, BlendMode blendMode)
+ {
+ return adoptRef(*new DrawPattern(image, destRect, tileRect, patternTransform, phase, spacing, op, blendMode));
+ }
+
+ const Image& image() const { return m_image.get(); }
+ const AffineTransform& patternTransform() const { return m_patternTransform; }
+ FloatRect tileRect() const { return m_tileRect; }
+ FloatRect destRect() const { return m_destination; }
+ FloatPoint phase() const { return m_phase; }
+ FloatSize spacing() const { return m_spacing; }
+
+private:
+ DrawPattern(Image&, const FloatRect& destRect, const FloatRect& srcRect, const AffineTransform&, const FloatPoint& phase, const FloatSize& spacing, CompositeOperator, BlendMode = BlendModeNormal);
+
+ void apply(GraphicsContext&) const override;
+
+ std::optional<FloatRect> localBounds(const GraphicsContext&) const override { return m_destination; }
+
+ mutable Ref<Image> m_image; // FIXME: Drawing images can cause their animations to progress. This shouldn't have to be mutable.
+ AffineTransform m_patternTransform;
+ FloatRect m_tileRect;
+ FloatRect m_destination;
+ FloatPoint m_phase;
+ FloatSize m_spacing;
+ CompositeOperator m_op;
+ BlendMode m_blendMode;
+};
+
+// Is DrawingItem because the size of the transparency layer is implicitly the clip bounds.
+class BeginTransparencyLayer : public DrawingItem {
+public:
+ static Ref<BeginTransparencyLayer> create(float opacity)
+ {
+ return adoptRef(*new BeginTransparencyLayer(opacity));
+ }
+
+ float opacity() const { return m_opacity; }
+
+private:
+ BeginTransparencyLayer(float opacity)
+ : DrawingItem(ItemType::BeginTransparencyLayer)
+ , m_opacity(opacity)
+ {
+ }
+
+ void apply(GraphicsContext&) const override;
+
+ float m_opacity;
+};
+
+class EndTransparencyLayer : public DrawingItem {
+public:
+ static Ref<EndTransparencyLayer> create()
+ {
+ return adoptRef(*new EndTransparencyLayer);
+ }
+
+private:
+ EndTransparencyLayer()
+ : DrawingItem(ItemType::EndTransparencyLayer)
+ {
+ }
+
+ void apply(GraphicsContext&) const override;
+};
+
+class DrawRect : public DrawingItem {
+public:
+ static Ref<DrawRect> create(const FloatRect& rect, float borderThickness)
+ {
+ return adoptRef(*new DrawRect(rect, borderThickness));
+ }
+
+ FloatRect rect() const { return m_rect; }
+ float borderThickness() const { return m_borderThickness; }
+
+private:
+ DrawRect(const FloatRect& rect, float borderThickness)
+ : DrawingItem(ItemType::DrawRect)
+ , m_rect(rect)
+ , m_borderThickness(borderThickness)
+ {
+ }
+
+ void apply(GraphicsContext&) const override;
+ std::optional<FloatRect> localBounds(const GraphicsContext&) const override { return m_rect; }
+
+ FloatRect m_rect;
+ float m_borderThickness;
+};
+
+class DrawLine : public DrawingItem {
+public:
+ static Ref<DrawLine> create(const FloatPoint& point1, const FloatPoint& point2)
+ {
+ return adoptRef(*new DrawLine(point1, point2));
+ }
+
+ FloatPoint point1() const { return m_point1; }
+ FloatPoint point2() const { return m_point2; }
+
+private:
+ DrawLine(const FloatPoint& point1, const FloatPoint& point2)
+ : DrawingItem(ItemType::DrawLine)
+ , m_point1(point1)
+ , m_point2(point2)
+ {
+ }
+
+ void apply(GraphicsContext&) const override;
+ std::optional<FloatRect> localBounds(const GraphicsContext&) const override;
+
+ FloatPoint m_point1;
+ FloatPoint m_point2;
+};
+
+class DrawLinesForText : public DrawingItem {
+public:
+ static Ref<DrawLinesForText> create(const FloatPoint& blockLocation, const FloatSize& localAnchor, const DashArray& widths, bool printing, bool doubleLines, float strokeWidth)
+ {
+ return adoptRef(*new DrawLinesForText(blockLocation, localAnchor, widths, printing, doubleLines, strokeWidth));
+ }
+
+ void setBlockLocation(const FloatPoint& blockLocation) { m_blockLocation = blockLocation; }
+ const FloatPoint& blockLocation() const { return m_blockLocation; }
+ const FloatSize& localAnchor() const { return m_localAnchor; }
+ FloatPoint point() const { return m_blockLocation + m_localAnchor; }
+ const DashArray& widths() const { return m_widths; }
+ bool isPrinting() const { return m_printing; }
+ bool doubleLines() const { return m_doubleLines; }
+
+private:
+ DrawLinesForText(const FloatPoint& blockLocation, const FloatSize& localAnchor, const DashArray& widths, bool printing, bool doubleLines, float strokeWidth)
+ : DrawingItem(ItemType::DrawLinesForText)
+ , m_blockLocation(blockLocation)
+ , m_localAnchor(localAnchor)
+ , m_widths(widths)
+ , m_strokeWidth(strokeWidth)
+ , m_printing(printing)
+ , m_doubleLines(doubleLines)
+ {
+ }
+
+ void apply(GraphicsContext&) const override;
+
+ std::optional<FloatRect> localBounds(const GraphicsContext&) const override;
+
+ FloatPoint m_blockLocation;
+ FloatSize m_localAnchor;
+ DashArray m_widths;
+ float m_strokeWidth;
+ bool m_printing;
+ bool m_doubleLines;
+};
+
+class DrawLineForDocumentMarker : public DrawingItem {
+public:
+ static Ref<DrawLineForDocumentMarker> create(const FloatPoint& point, float width, GraphicsContext::DocumentMarkerLineStyle style)
+ {
+ return adoptRef(*new DrawLineForDocumentMarker(point, width, style));
+ }
+
+ FloatPoint point() const { return m_point; }
+ float width() const { return m_width; }
+
+private:
+ DrawLineForDocumentMarker(const FloatPoint& point, float width, GraphicsContext::DocumentMarkerLineStyle style)
+ : DrawingItem(ItemType::DrawLineForDocumentMarker)
+ , m_point(point)
+ , m_width(width)
+ , m_style(style)
+ {
+ }
+
+ void apply(GraphicsContext&) const override;
+
+ std::optional<FloatRect> localBounds(const GraphicsContext&) const override;
+
+ FloatPoint m_point;
+ float m_width;
+ GraphicsContext::DocumentMarkerLineStyle m_style;
+};
+
+class DrawEllipse : public DrawingItem {
+public:
+ static Ref<DrawEllipse> create(const FloatRect& rect)
+ {
+ return adoptRef(*new DrawEllipse(rect));
+ }
+
+ FloatRect rect() const { return m_rect; }
+
+private:
+ DrawEllipse(const FloatRect& rect)
+ : DrawingItem(ItemType::DrawEllipse)
+ , m_rect(rect)
+ {
+ }
+
+ void apply(GraphicsContext&) const override;
+ std::optional<FloatRect> localBounds(const GraphicsContext&) const override { return m_rect; }
+
+ FloatRect m_rect;
+};
+
+class DrawPath : public DrawingItem {
+public:
+ static Ref<DrawPath> create(const Path& path)
+ {
+ return adoptRef(*new DrawPath(path));
+ }
+
+ const Path& path() const { return m_path; }
+
+private:
+ DrawPath(const Path& path)
+ : DrawingItem(ItemType::DrawPath)
+ , m_path(path)
+ {
+ }
+
+ void apply(GraphicsContext&) const override;
+
+ std::optional<FloatRect> localBounds(const GraphicsContext&) const override { return m_path.fastBoundingRect(); }
+
+ const Path m_path;
+};
+
+class DrawFocusRingPath : public DrawingItem {
+public:
+ static Ref<DrawFocusRingPath> create(const Path& path, int width, int offset, const Color& color)
+ {
+ return adoptRef(*new DrawFocusRingPath(path, width, offset, color));
+ }
+
+ const Path& path() const { return m_path; }
+ int width() const { return m_width; }
+ int offset() const { return m_offset; }
+ const Color& color() const { return m_color; }
+
+private:
+ DrawFocusRingPath(const Path& path, int width, int offset, const Color& color)
+ : DrawingItem(ItemType::DrawFocusRingPath)
+ , m_path(path)
+ , m_width(width)
+ , m_offset(offset)
+ , m_color(color)
+ {
+ }
+
+ void apply(GraphicsContext&) const override;
+
+ std::optional<FloatRect> localBounds(const GraphicsContext&) const override;
+
+ const Path m_path;
+ int m_width;
+ int m_offset;
+ Color m_color;
+};
+
+class DrawFocusRingRects : public DrawingItem {
+public:
+ static Ref<DrawFocusRingRects> create(const Vector<FloatRect>& rects, int width, int offset, const Color& color)
+ {
+ return adoptRef(*new DrawFocusRingRects(rects, width, offset, color));
+ }
+
+ const Vector<FloatRect> rects() const { return m_rects; }
+ int width() const { return m_width; }
+ int offset() const { return m_offset; }
+ const Color& color() const { return m_color; }
+
+private:
+ DrawFocusRingRects(const Vector<FloatRect>& rects, int width, int offset, const Color& color)
+ : DrawingItem(ItemType::DrawFocusRingRects)
+ , m_rects(rects)
+ , m_width(width)
+ , m_offset(offset)
+ , m_color(color)
+ {
+ }
+
+ void apply(GraphicsContext&) const override;
+
+ std::optional<FloatRect> localBounds(const GraphicsContext&) const override;
+
+ Vector<FloatRect> m_rects;
+ int m_width;
+ int m_offset;
+ Color m_color;
+};
+
+class FillRect : public DrawingItem {
+public:
+ static Ref<FillRect> create(const FloatRect& rect)
+ {
+ return adoptRef(*new FillRect(rect));
+ }
+
+ FloatRect rect() const { return m_rect; }
+
+private:
+ FillRect(const FloatRect& rect)
+ : DrawingItem(ItemType::FillRect)
+ , m_rect(rect)
+ {
+ }
+
+ void apply(GraphicsContext&) const override;
+ std::optional<FloatRect> localBounds(const GraphicsContext&) const override { return m_rect; }
+
+ FloatRect m_rect;
+};
+
+// FIXME: Make these inherit from FillRect proper.
+class FillRectWithColor : public DrawingItem {
+public:
+ static Ref<FillRectWithColor> create(const FloatRect& rect, const Color& color)
+ {
+ return adoptRef(*new FillRectWithColor(rect, color));
+ }
+
+ FloatRect rect() const { return m_rect; }
+ const Color& color() const { return m_color; }
+
+private:
+ FillRectWithColor(const FloatRect& rect, const Color& color)
+ : DrawingItem(ItemType::FillRectWithColor)
+ , m_rect(rect)
+ , m_color(color)
+ {
+ }
+
+ void apply(GraphicsContext&) const override;
+ std::optional<FloatRect> localBounds(const GraphicsContext&) const override { return m_rect; }
+
+ FloatRect m_rect;
+ Color m_color;
+};
+
+class FillRectWithGradient : public DrawingItem {
+public:
+ static Ref<FillRectWithGradient> create(const FloatRect& rect, Gradient& gradient)
+ {
+ return adoptRef(*new FillRectWithGradient(rect, gradient));
+ }
+
+ FloatRect rect() const { return m_rect; }
+
+private:
+ FillRectWithGradient(const FloatRect& rect, Gradient& gradient)
+ : DrawingItem(ItemType::FillRectWithGradient)
+ , m_rect(rect)
+ , m_gradient(gradient)
+ {
+ }
+
+ void apply(GraphicsContext&) const override;
+ std::optional<FloatRect> localBounds(const GraphicsContext&) const override { return m_rect; }
+
+ FloatRect m_rect;
+ mutable Ref<Gradient> m_gradient; // FIXME: Make this not mutable
+};
+
+class FillCompositedRect : public DrawingItem {
+public:
+ static Ref<FillCompositedRect> create(const FloatRect& rect, const Color& color, CompositeOperator op, BlendMode blendMode)
+ {
+ return adoptRef(*new FillCompositedRect(rect, color, op, blendMode));
+ }
+
+ FloatRect rect() const { return m_rect; }
+ const Color& color() const { return m_color; }
+ CompositeOperator compositeOperator() const { return m_op; }
+ BlendMode blendMode() const { return m_blendMode; }
+
+private:
+ FillCompositedRect(const FloatRect& rect, const Color& color, CompositeOperator op, BlendMode blendMode)
+ : DrawingItem(ItemType::FillCompositedRect)
+ , m_rect(rect)
+ , m_color(color)
+ , m_op(op)
+ , m_blendMode(blendMode)
+ {
+ }
+
+ void apply(GraphicsContext&) const override;
+ std::optional<FloatRect> localBounds(const GraphicsContext&) const override { return m_rect; }
+
+ FloatRect m_rect;
+ Color m_color;
+ CompositeOperator m_op;
+ BlendMode m_blendMode;
+};
+
+class FillRoundedRect : public DrawingItem {
+public:
+ static Ref<FillRoundedRect> create(const FloatRoundedRect& rect, const Color& color, BlendMode blendMode)
+ {
+ return adoptRef(*new FillRoundedRect(rect, color, blendMode));
+ }
+
+ const FloatRoundedRect& roundedRect() const { return m_rect; }
+ const Color& color() const { return m_color; }
+ BlendMode blendMode() const { return m_blendMode; }
+
+private:
+ FillRoundedRect(const FloatRoundedRect& rect, const Color& color, BlendMode blendMode)
+ : DrawingItem(ItemType::FillRoundedRect)
+ , m_rect(rect)
+ , m_color(color)
+ , m_blendMode(blendMode)
+ {
+ }
+
+ void apply(GraphicsContext&) const override;
+ std::optional<FloatRect> localBounds(const GraphicsContext&) const override { return m_rect.rect(); }
+
+ FloatRoundedRect m_rect;
+ Color m_color;
+ BlendMode m_blendMode;
+};
+
+class FillRectWithRoundedHole : public DrawingItem {
+public:
+ static Ref<FillRectWithRoundedHole> create(const FloatRect& rect, const FloatRoundedRect& roundedHoleRect, const Color& color)
+ {
+ return adoptRef(*new FillRectWithRoundedHole(rect, roundedHoleRect, color));
+ }
+
+ const FloatRect& rect() const { return m_rect; }
+ const FloatRoundedRect& roundedHoleRect() const { return m_roundedHoleRect; }
+ const Color& color() const { return m_color; }
+
+private:
+ FillRectWithRoundedHole(const FloatRect& rect, const FloatRoundedRect& roundedHoleRect, const Color& color)
+ : DrawingItem(ItemType::FillRectWithRoundedHole)
+ , m_rect(rect)
+ , m_roundedHoleRect(roundedHoleRect)
+ , m_color(color)
+ {
+ }
+
+ void apply(GraphicsContext&) const override;
+ std::optional<FloatRect> localBounds(const GraphicsContext&) const override { return m_rect; }
+
+ FloatRect m_rect;
+ FloatRoundedRect m_roundedHoleRect;
+ Color m_color;
+};
+
+class FillPath : public DrawingItem {
+public:
+ static Ref<FillPath> create(const Path& path)
+ {
+ return adoptRef(*new FillPath(path));
+ }
+
+ const Path& path() const { return m_path; }
+
+private:
+ FillPath(const Path& path)
+ : DrawingItem(ItemType::FillPath)
+ , m_path(path)
+ {
+ }
+
+ void apply(GraphicsContext&) const override;
+ std::optional<FloatRect> localBounds(const GraphicsContext&) const override { return m_path.fastBoundingRect(); }
+
+ const Path m_path;
+};
+
+class FillEllipse : public DrawingItem {
+public:
+ static Ref<FillEllipse> create(const FloatRect& rect)
+ {
+ return adoptRef(*new FillEllipse(rect));
+ }
+
+ FloatRect rect() const { return m_rect; }
+
+private:
+ FillEllipse(const FloatRect& rect)
+ : DrawingItem(ItemType::FillEllipse)
+ , m_rect(rect)
+ {
+ }
+
+ void apply(GraphicsContext&) const override;
+
+ std::optional<FloatRect> localBounds(const GraphicsContext&) const override { return m_rect; }
+
+ FloatRect m_rect;
+};
+
+class StrokeRect : public DrawingItem {
+public:
+ static Ref<StrokeRect> create(const FloatRect& rect, float lineWidth)
+ {
+ return adoptRef(*new StrokeRect(rect, lineWidth));
+ }
+
+ FloatRect rect() const { return m_rect; }
+ float lineWidth() const { return m_lineWidth; }
+
+private:
+ StrokeRect(const FloatRect& rect, float lineWidth)
+ : DrawingItem(ItemType::StrokeRect)
+ , m_rect(rect)
+ , m_lineWidth(lineWidth)
+ {
+ }
+
+ void apply(GraphicsContext&) const override;
+ std::optional<FloatRect> localBounds(const GraphicsContext&) const override;
+
+ FloatRect m_rect;
+ float m_lineWidth;
+};
+
+class StrokePath : public DrawingItem {
+public:
+ static Ref<StrokePath> create(const Path& path)
+ {
+ return adoptRef(*new StrokePath(path));
+ }
+
+ const Path& path() const { return m_path; }
+
+private:
+ StrokePath(const Path& path)
+ : DrawingItem(ItemType::StrokePath)
+ , m_path(path)
+ {
+ }
+
+ void apply(GraphicsContext&) const override;
+ std::optional<FloatRect> localBounds(const GraphicsContext&) const override;
+
+ const Path m_path;
+ FloatPoint m_blockLocation;
+};
+
+class StrokeEllipse : public DrawingItem {
+public:
+ static Ref<StrokeEllipse> create(const FloatRect& rect)
+ {
+ return adoptRef(*new StrokeEllipse(rect));
+ }
+
+ FloatRect rect() const { return m_rect; }
+
+private:
+ StrokeEllipse(const FloatRect& rect)
+ : DrawingItem(ItemType::StrokeEllipse)
+ , m_rect(rect)
+ {
+ }
+
+ void apply(GraphicsContext&) const override;
+ std::optional<FloatRect> localBounds(const GraphicsContext&) const override;
+
+ FloatRect m_rect;
+};
+
+class ClearRect : public DrawingItem {
+public:
+ static Ref<ClearRect> create(const FloatRect& rect)
+ {
+ return adoptRef(*new ClearRect(rect));
+ }
+
+ FloatRect rect() const { return m_rect; }
+
+private:
+ ClearRect(const FloatRect& rect)
+ : DrawingItem(ItemType::ClearRect)
+ , m_rect(rect)
+ {
+ }
+
+ void apply(GraphicsContext&) const override;
+ std::optional<FloatRect> localBounds(const GraphicsContext&) const override { return m_rect; }
+
+ FloatRect m_rect;
+};
+
+#if USE(CG)
+class ApplyStrokePattern : public Item {
+public:
+ static Ref<ApplyStrokePattern> create()
+ {
+ return adoptRef(*new ApplyStrokePattern);
+ }
+
+private:
+ ApplyStrokePattern()
+ : Item(ItemType::ApplyStrokePattern)
+ {
+ }
+
+ void apply(GraphicsContext&) const override;
+};
+
+class ApplyFillPattern : public Item {
+public:
+ static Ref<ApplyFillPattern> create()
+ {
+ return adoptRef(*new ApplyFillPattern);
+ }
+
+private:
+ ApplyFillPattern()
+ : Item(ItemType::ApplyFillPattern)
+ {
+ }
+
+ void apply(GraphicsContext&) const override;
+};
+#endif
+
+class ApplyDeviceScaleFactor : public Item {
+public:
+ static Ref<ApplyDeviceScaleFactor> create(float scaleFactor)
+ {
+ return adoptRef(*new ApplyDeviceScaleFactor(scaleFactor));
+ }
+
+ float scaleFactor() const { return m_scaleFactor; }
+
+private:
+ ApplyDeviceScaleFactor(float scaleFactor)
+ : Item(ItemType::ApplyDeviceScaleFactor)
+ , m_scaleFactor(scaleFactor)
+ {
+ }
+
+ void apply(GraphicsContext&) const override;
+
+ float m_scaleFactor;
+};
+
+TextStream& operator<<(TextStream&, const Item&);
+
+} // namespace DisplayList
+} // namespace WebCore
+
+
+#define SPECIALIZE_TYPE_TRAITS_DISPLAYLIST_DRAWINGITEM(ToValueTypeName, predicate) \
+SPECIALIZE_TYPE_TRAITS_BEGIN(WebCore::DisplayList::ToValueTypeName) \
+ static bool isType(const WebCore::DisplayList::Item& object) { return object.predicate; } \
+SPECIALIZE_TYPE_TRAITS_END()
+
+SPECIALIZE_TYPE_TRAITS_DISPLAYLIST_DRAWINGITEM(DrawingItem, isDrawingItem())
+
+#define SPECIALIZE_TYPE_TRAITS_DISPLAYLIST_ITEM(ToValueTypeName) \
+SPECIALIZE_TYPE_TRAITS_BEGIN(WebCore::DisplayList::ToValueTypeName) \
+ static bool isType(const WebCore::DisplayList::Item& item) { return item.type() == WebCore::DisplayList::ItemType::ToValueTypeName; } \
+SPECIALIZE_TYPE_TRAITS_END()
+
+SPECIALIZE_TYPE_TRAITS_DISPLAYLIST_ITEM(Save)
+SPECIALIZE_TYPE_TRAITS_DISPLAYLIST_ITEM(Restore)
+SPECIALIZE_TYPE_TRAITS_DISPLAYLIST_ITEM(Translate)
+SPECIALIZE_TYPE_TRAITS_DISPLAYLIST_ITEM(Rotate)
+SPECIALIZE_TYPE_TRAITS_DISPLAYLIST_ITEM(Scale)
+SPECIALIZE_TYPE_TRAITS_DISPLAYLIST_ITEM(ConcatenateCTM)
+SPECIALIZE_TYPE_TRAITS_DISPLAYLIST_ITEM(SetState)
+SPECIALIZE_TYPE_TRAITS_DISPLAYLIST_ITEM(SetLineCap)
+SPECIALIZE_TYPE_TRAITS_DISPLAYLIST_ITEM(SetLineDash)
+SPECIALIZE_TYPE_TRAITS_DISPLAYLIST_ITEM(SetLineJoin)
+SPECIALIZE_TYPE_TRAITS_DISPLAYLIST_ITEM(SetMiterLimit)
+SPECIALIZE_TYPE_TRAITS_DISPLAYLIST_ITEM(Clip)
+SPECIALIZE_TYPE_TRAITS_DISPLAYLIST_ITEM(ClipOut)
+SPECIALIZE_TYPE_TRAITS_DISPLAYLIST_ITEM(ClipOutToPath)
+SPECIALIZE_TYPE_TRAITS_DISPLAYLIST_ITEM(ClipPath)
+SPECIALIZE_TYPE_TRAITS_DISPLAYLIST_ITEM(DrawGlyphs)
+SPECIALIZE_TYPE_TRAITS_DISPLAYLIST_ITEM(DrawImage)
+SPECIALIZE_TYPE_TRAITS_DISPLAYLIST_ITEM(DrawTiledImage)
+SPECIALIZE_TYPE_TRAITS_DISPLAYLIST_ITEM(DrawTiledScaledImage)
+#if USE(CG) || USE(CAIRO)
+SPECIALIZE_TYPE_TRAITS_DISPLAYLIST_ITEM(DrawNativeImage)
+#endif
+SPECIALIZE_TYPE_TRAITS_DISPLAYLIST_ITEM(DrawPattern)
+SPECIALIZE_TYPE_TRAITS_DISPLAYLIST_ITEM(DrawRect)
+SPECIALIZE_TYPE_TRAITS_DISPLAYLIST_ITEM(DrawLine)
+SPECIALIZE_TYPE_TRAITS_DISPLAYLIST_ITEM(DrawLinesForText)
+SPECIALIZE_TYPE_TRAITS_DISPLAYLIST_ITEM(DrawLineForDocumentMarker)
+SPECIALIZE_TYPE_TRAITS_DISPLAYLIST_ITEM(DrawEllipse)
+SPECIALIZE_TYPE_TRAITS_DISPLAYLIST_ITEM(DrawPath)
+SPECIALIZE_TYPE_TRAITS_DISPLAYLIST_ITEM(DrawFocusRingPath)
+SPECIALIZE_TYPE_TRAITS_DISPLAYLIST_ITEM(DrawFocusRingRects)
+SPECIALIZE_TYPE_TRAITS_DISPLAYLIST_ITEM(FillRect)
+SPECIALIZE_TYPE_TRAITS_DISPLAYLIST_ITEM(FillRectWithColor)
+SPECIALIZE_TYPE_TRAITS_DISPLAYLIST_ITEM(FillRectWithGradient)
+SPECIALIZE_TYPE_TRAITS_DISPLAYLIST_ITEM(FillCompositedRect)
+SPECIALIZE_TYPE_TRAITS_DISPLAYLIST_ITEM(FillRoundedRect)
+SPECIALIZE_TYPE_TRAITS_DISPLAYLIST_ITEM(FillRectWithRoundedHole)
+SPECIALIZE_TYPE_TRAITS_DISPLAYLIST_ITEM(FillPath)
+SPECIALIZE_TYPE_TRAITS_DISPLAYLIST_ITEM(FillEllipse)
+SPECIALIZE_TYPE_TRAITS_DISPLAYLIST_ITEM(StrokeRect)
+SPECIALIZE_TYPE_TRAITS_DISPLAYLIST_ITEM(StrokePath)
+SPECIALIZE_TYPE_TRAITS_DISPLAYLIST_ITEM(StrokeEllipse)
+SPECIALIZE_TYPE_TRAITS_DISPLAYLIST_ITEM(ClearRect)
+SPECIALIZE_TYPE_TRAITS_DISPLAYLIST_ITEM(BeginTransparencyLayer)
+SPECIALIZE_TYPE_TRAITS_DISPLAYLIST_ITEM(EndTransparencyLayer)
+#if USE(CG)
+SPECIALIZE_TYPE_TRAITS_DISPLAYLIST_ITEM(ApplyStrokePattern)
+SPECIALIZE_TYPE_TRAITS_DISPLAYLIST_ITEM(ApplyFillPattern)
+#endif
+SPECIALIZE_TYPE_TRAITS_DISPLAYLIST_ITEM(ApplyDeviceScaleFactor)
+SPECIALIZE_TYPE_TRAITS_DISPLAYLIST_ITEM(ClearShadow)
+
+#endif // DisplayListItems_h
diff --git a/Source/WebCore/platform/graphics/displaylists/DisplayListRecorder.cpp b/Source/WebCore/platform/graphics/displaylists/DisplayListRecorder.cpp
new file mode 100644
index 000000000..6d6853345
--- /dev/null
+++ b/Source/WebCore/platform/graphics/displaylists/DisplayListRecorder.cpp
@@ -0,0 +1,465 @@
+/*
+ * Copyright (C) 2016 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "DisplayListRecorder.h"
+
+#include "DisplayList.h"
+#include "DisplayListItems.h"
+#include "GraphicsContext.h"
+#include "Logging.h"
+#include "TextStream.h"
+#include <wtf/MathExtras.h>
+
+namespace WebCore {
+namespace DisplayList {
+
+Recorder::Recorder(GraphicsContext& context, DisplayList& displayList, const FloatRect& initialClip, const AffineTransform& baseCTM)
+ : m_graphicsContext(context)
+ , m_displayList(displayList)
+{
+ LOG_WITH_STREAM(DisplayLists, stream << "\nRecording with clip " << initialClip);
+ m_graphicsContext.setDisplayListRecorder(this);
+ m_stateStack.append(ContextState(baseCTM, initialClip));
+}
+
+Recorder::~Recorder()
+{
+ ASSERT(m_stateStack.size() == 1); // If this fires, it indicates mismatched save/restore.
+ LOG(DisplayLists, "Recorded display list:\n%s", m_displayList.description().data());
+}
+
+void Recorder::willAppendItem(const Item& item)
+{
+ if (item.isDrawingItem()
+#if USE(CG)
+ || item.type() == ItemType::ApplyStrokePattern || item.type() == ItemType::ApplyStrokePattern
+#endif
+ ) {
+ GraphicsContextStateChange& stateChanges = currentState().stateChange;
+ GraphicsContextState::StateChangeFlags changesFromLastState = stateChanges.changesFromState(currentState().lastDrawingState);
+ if (changesFromLastState) {
+ LOG_WITH_STREAM(DisplayLists, stream << "pre-drawing, saving state " << GraphicsContextStateChange(stateChanges.m_state, changesFromLastState));
+ m_displayList.append(SetState::create(stateChanges.m_state, changesFromLastState));
+ stateChanges.m_changeFlags = 0;
+ currentState().lastDrawingState = stateChanges.m_state;
+ }
+ currentState().wasUsedForDrawing = true;
+ }
+}
+
+void Recorder::updateState(const GraphicsContextState& state, GraphicsContextState::StateChangeFlags flags)
+{
+ currentState().stateChange.accumulate(state, flags);
+}
+
+void Recorder::clearShadow()
+{
+ appendItem(ClearShadow::create());
+}
+
+void Recorder::setLineCap(LineCap lineCap)
+{
+ appendItem(SetLineCap::create(lineCap));
+}
+
+void Recorder::setLineDash(const DashArray& dashArray, float dashOffset)
+{
+ appendItem(SetLineDash::create(dashArray, dashOffset));
+}
+
+void Recorder::setLineJoin(LineJoin lineJoin)
+{
+ appendItem(SetLineJoin::create(lineJoin));
+}
+
+void Recorder::setMiterLimit(float miterLimit)
+{
+ appendItem(SetMiterLimit::create(miterLimit));
+}
+
+void Recorder::drawGlyphs(const Font& font, const GlyphBuffer& glyphBuffer, unsigned from, unsigned numGlyphs, const FloatPoint& startPoint, FontSmoothingMode smoothingMode)
+{
+ DrawingItem& newItem = downcast<DrawingItem>(appendItem(DrawGlyphs::create(font, glyphBuffer.glyphs(from), glyphBuffer.advances(from), numGlyphs, FloatPoint(), toFloatSize(startPoint), smoothingMode)));
+ updateItemExtent(newItem);
+}
+
+void Recorder::drawImage(Image& image, const FloatRect& destination, const FloatRect& source, const ImagePaintingOptions& imagePaintingOptions)
+{
+ DrawingItem& newItem = downcast<DrawingItem>(appendItem(DrawImage::create(image, destination, source, imagePaintingOptions)));
+ updateItemExtent(newItem);
+}
+
+void Recorder::drawTiledImage(Image& image, const FloatRect& destination, const FloatPoint& source, const FloatSize& tileSize, const FloatSize& spacing, const ImagePaintingOptions& imagePaintingOptions)
+{
+ DrawingItem& newItem = downcast<DrawingItem>(appendItem(DrawTiledImage::create(image, destination, source, tileSize, spacing, imagePaintingOptions)));
+ updateItemExtent(newItem);
+}
+
+#if USE(CG) || USE(CAIRO)
+void Recorder::drawNativeImage(const NativeImagePtr& image, const FloatSize& imageSize, const FloatRect& destRect, const FloatRect& srcRect, CompositeOperator op, BlendMode blendMode, ImageOrientation orientation)
+{
+ DrawingItem& newItem = downcast<DrawingItem>(appendItem(DrawNativeImage::create(image, imageSize, destRect, srcRect, op, blendMode, orientation)));
+ updateItemExtent(newItem);
+}
+#endif
+
+void Recorder::drawTiledImage(Image& image, const FloatRect& destination, const FloatRect& source, const FloatSize& tileScaleFactor, Image::TileRule hRule, Image::TileRule vRule, const ImagePaintingOptions& imagePaintingOptions)
+{
+ DrawingItem& newItem = downcast<DrawingItem>(appendItem(DrawTiledScaledImage::create(image, destination, source, tileScaleFactor, hRule, vRule, imagePaintingOptions)));
+ updateItemExtent(newItem);
+}
+
+void Recorder::drawPattern(Image& image, const FloatRect& destRect, const FloatRect& tileRect, const AffineTransform& patternTransform, const FloatPoint& phase, const FloatSize& spacing, CompositeOperator op, BlendMode blendMode)
+{
+ DrawingItem& newItem = downcast<DrawingItem>(appendItem(DrawPattern::create(image, destRect, tileRect, patternTransform, phase, spacing, op, blendMode)));
+ updateItemExtent(newItem);
+}
+
+void Recorder::save()
+{
+ appendItem(Save::create());
+ m_stateStack.append(m_stateStack.last().cloneForSave(m_displayList.itemCount() - 1));
+}
+
+void Recorder::restore()
+{
+ if (!m_stateStack.size())
+ return;
+
+ bool stateUsedForDrawing = currentState().wasUsedForDrawing;
+ size_t saveIndex = currentState().saveItemIndex;
+
+ m_stateStack.removeLast();
+ // Have to avoid eliding nested Save/Restore when a descendant state contains drawing items.
+ currentState().wasUsedForDrawing |= stateUsedForDrawing;
+
+ if (!stateUsedForDrawing && saveIndex) {
+ // This Save/Restore didn't contain any drawing items. Roll back to just before the last save.
+ m_displayList.removeItemsFromIndex(saveIndex);
+ return;
+ }
+
+ appendItem(Restore::create());
+
+ if (saveIndex) {
+ Save& saveItem = downcast<Save>(m_displayList.itemAt(saveIndex));
+ saveItem.setRestoreIndex(m_displayList.itemCount() - 1);
+ }
+}
+
+void Recorder::translate(float x, float y)
+{
+ currentState().translate(x, y);
+ appendItem(Translate::create(x, y));
+}
+
+void Recorder::rotate(float angleInRadians)
+{
+ currentState().rotate(angleInRadians);
+ appendItem(Rotate::create(angleInRadians));
+}
+
+void Recorder::scale(const FloatSize& size)
+{
+ currentState().scale(size);
+ appendItem(Scale::create(size));
+}
+
+void Recorder::concatCTM(const AffineTransform& transform)
+{
+ currentState().concatCTM(transform);
+ appendItem(ConcatenateCTM::create(transform));
+}
+
+void Recorder::beginTransparencyLayer(float opacity)
+{
+ DrawingItem& newItem = downcast<DrawingItem>(appendItem(BeginTransparencyLayer::create(opacity)));
+ updateItemExtent(newItem);
+}
+
+void Recorder::endTransparencyLayer()
+{
+ appendItem(EndTransparencyLayer::create());
+}
+
+void Recorder::drawRect(const FloatRect& rect, float borderThickness)
+{
+ DrawingItem& newItem = downcast<DrawingItem>(appendItem(DrawRect::create(rect, borderThickness)));
+ updateItemExtent(newItem);
+}
+
+void Recorder::drawLine(const FloatPoint& point1, const FloatPoint& point2)
+{
+ DrawingItem& newItem = downcast<DrawingItem>(appendItem(DrawLine::create(point1, point2)));
+ updateItemExtent(newItem);
+}
+
+void Recorder::drawLinesForText(const FloatPoint& point, const DashArray& widths, bool printing, bool doubleLines, float strokeThickness)
+{
+ DrawingItem& newItem = downcast<DrawingItem>(appendItem(DrawLinesForText::create(FloatPoint(), toFloatSize(point), widths, printing, doubleLines, strokeThickness)));
+ updateItemExtent(newItem);
+}
+
+void Recorder::drawLineForDocumentMarker(const FloatPoint& point, float width, GraphicsContext::DocumentMarkerLineStyle style)
+{
+ DrawingItem& newItem = downcast<DrawingItem>(appendItem(DrawLineForDocumentMarker::create(point, width, style)));
+ updateItemExtent(newItem);
+}
+
+void Recorder::drawEllipse(const FloatRect& rect)
+{
+ DrawingItem& newItem = downcast<DrawingItem>(appendItem(DrawEllipse::create(rect)));
+ updateItemExtent(newItem);
+}
+
+void Recorder::drawPath(const Path& path)
+{
+ DrawingItem& newItem = downcast<DrawingItem>(appendItem(DrawPath::create(path)));
+ updateItemExtent(newItem);
+}
+
+void Recorder::drawFocusRing(const Path& path, int width, int offset, const Color& color)
+{
+ DrawingItem& newItem = downcast<DrawingItem>(appendItem(DrawFocusRingPath::create(path, width, offset, color)));
+ updateItemExtent(newItem);
+}
+
+void Recorder::drawFocusRing(const Vector<FloatRect>& rects, int width, int offset, const Color& color)
+{
+ DrawingItem& newItem = downcast<DrawingItem>(appendItem(DrawFocusRingRects::create(rects, width, offset, color)));
+ updateItemExtent(newItem);
+}
+
+void Recorder::fillRect(const FloatRect& rect)
+{
+ DrawingItem& newItem = downcast<DrawingItem>(appendItem(FillRect::create(rect)));
+ updateItemExtent(newItem);
+}
+
+void Recorder::fillRect(const FloatRect& rect, const Color& color)
+{
+ DrawingItem& newItem = downcast<DrawingItem>(appendItem(FillRectWithColor::create(rect, color)));
+ updateItemExtent(newItem);
+}
+
+void Recorder::fillRect(const FloatRect& rect, Gradient& gradient)
+{
+ DrawingItem& newItem = downcast<DrawingItem>(appendItem(FillRectWithGradient::create(rect, gradient)));
+ updateItemExtent(newItem);
+}
+
+void Recorder::fillRect(const FloatRect& rect, const Color& color, CompositeOperator op, BlendMode blendMode)
+{
+ DrawingItem& newItem = downcast<DrawingItem>(appendItem(FillCompositedRect::create(rect, color, op, blendMode)));
+ updateItemExtent(newItem);
+}
+
+void Recorder::fillRoundedRect(const FloatRoundedRect& rect, const Color& color, BlendMode blendMode)
+{
+ DrawingItem& newItem = downcast<DrawingItem>(appendItem(FillRoundedRect::create(rect, color, blendMode)));
+ updateItemExtent(newItem);
+}
+
+void Recorder::fillRectWithRoundedHole(const FloatRect& rect, const FloatRoundedRect& roundedHoleRect, const Color& color)
+{
+ DrawingItem& newItem = downcast<DrawingItem>(appendItem(FillRectWithRoundedHole::create(rect, roundedHoleRect, color)));
+ updateItemExtent(newItem);
+}
+
+void Recorder::fillPath(const Path& path)
+{
+ DrawingItem& newItem = downcast<DrawingItem>(appendItem(FillPath::create(path)));
+ updateItemExtent(newItem);
+}
+
+void Recorder::fillEllipse(const FloatRect& rect)
+{
+ DrawingItem& newItem = downcast<DrawingItem>(appendItem(FillEllipse::create(rect)));
+ updateItemExtent(newItem);
+}
+
+void Recorder::strokeRect(const FloatRect& rect, float lineWidth)
+{
+ DrawingItem& newItem = downcast<DrawingItem>(appendItem(StrokeRect::create(rect, lineWidth)));
+ updateItemExtent(newItem);
+}
+
+void Recorder::strokePath(const Path& path)
+{
+ DrawingItem& newItem = downcast<DrawingItem>(appendItem(StrokePath::create(path)));
+ updateItemExtent(newItem);
+}
+
+void Recorder::strokeEllipse(const FloatRect& rect)
+{
+ DrawingItem& newItem = downcast<DrawingItem>(appendItem(StrokeEllipse::create(rect)));
+ updateItemExtent(newItem);
+}
+
+void Recorder::clearRect(const FloatRect& rect)
+{
+ DrawingItem& newItem = downcast<DrawingItem>(appendItem(ClearRect::create(rect)));
+ updateItemExtent(newItem);
+}
+
+#if USE(CG)
+void Recorder::applyStrokePattern()
+{
+ appendItem(ApplyStrokePattern::create());
+}
+
+void Recorder::applyFillPattern()
+{
+ appendItem(ApplyFillPattern::create());
+}
+#endif
+
+void Recorder::clip(const FloatRect& rect)
+{
+ currentState().clipBounds.intersect(rect);
+ appendItem(Clip::create(rect));
+}
+
+void Recorder::clipOut(const FloatRect& rect)
+{
+ appendItem(ClipOut::create(rect));
+}
+
+void Recorder::clipOut(const Path& path)
+{
+ appendItem(ClipOutToPath::create(path));
+}
+
+void Recorder::clipPath(const Path& path, WindRule windRule)
+{
+ currentState().clipBounds.intersect(path.fastBoundingRect());
+ appendItem(ClipPath::create(path, windRule));
+}
+
+void Recorder::applyDeviceScaleFactor(float deviceScaleFactor)
+{
+ // FIXME: this changes the baseCTM, which will invalidate all of our cached extents.
+ // Assert that it's only called early on?
+ appendItem(ApplyDeviceScaleFactor::create(deviceScaleFactor));
+}
+
+Item& Recorder::appendItem(Ref<Item>&& item)
+{
+ willAppendItem(item.get());
+ return m_displayList.append(WTFMove(item));
+}
+
+void Recorder::updateItemExtent(DrawingItem& item) const
+{
+ if (std::optional<FloatRect> rect = item.localBounds(m_graphicsContext))
+ item.setExtent(extentFromLocalBounds(rect.value()));
+}
+
+// FIXME: share with ShadowData
+static inline float shadowPaintingExtent(float blurRadius)
+{
+ // Blurring uses a Gaussian function whose std. deviation is m_radius/2, and which in theory
+ // extends to infinity. In 8-bit contexts, however, rounding causes the effect to become
+ // undetectable at around 1.4x the radius.
+ const float radiusExtentMultiplier = 1.4;
+ return ceilf(blurRadius * radiusExtentMultiplier);
+}
+
+FloatRect Recorder::extentFromLocalBounds(const FloatRect& rect) const
+{
+ FloatRect bounds = rect;
+ const ContextState& state = currentState();
+
+ FloatSize shadowOffset;
+ float shadowRadius;
+ Color shadowColor;
+ if (m_graphicsContext.getShadow(shadowOffset, shadowRadius, shadowColor)) {
+ FloatRect shadowExtent= bounds;
+ shadowExtent.move(shadowOffset);
+ shadowExtent.inflate(shadowPaintingExtent(shadowRadius));
+ bounds.unite(shadowExtent);
+ }
+
+ FloatRect clippedExtent = intersection(state.clipBounds, bounds);
+ return state.ctm.mapRect(clippedExtent);
+}
+
+const Recorder::ContextState& Recorder::currentState() const
+{
+ ASSERT(m_stateStack.size());
+ return m_stateStack.last();
+}
+
+Recorder::ContextState& Recorder::currentState()
+{
+ ASSERT(m_stateStack.size());
+ return m_stateStack.last();
+}
+
+const AffineTransform& Recorder::ctm() const
+{
+ return currentState().ctm;
+}
+
+const FloatRect& Recorder::clipBounds() const
+{
+ return currentState().clipBounds;
+}
+
+void Recorder::ContextState::translate(float x, float y)
+{
+ ctm.translate(x, y);
+ clipBounds.move(-x, -y);
+}
+
+void Recorder::ContextState::rotate(float angleInRadians)
+{
+ double angleInDegrees = rad2deg(static_cast<double>(angleInRadians));
+ ctm.rotate(angleInDegrees);
+
+ AffineTransform rotation;
+ rotation.rotate(angleInDegrees);
+
+ if (std::optional<AffineTransform> inverse = rotation.inverse())
+ clipBounds = inverse.value().mapRect(clipBounds);
+}
+
+void Recorder::ContextState::scale(const FloatSize& size)
+{
+ ctm.scale(size);
+ clipBounds.scale(1 / size.width(), 1 / size.height());
+}
+
+void Recorder::ContextState::concatCTM(const AffineTransform& matrix)
+{
+ ctm *= matrix;
+
+ if (std::optional<AffineTransform> inverse = matrix.inverse())
+ clipBounds = inverse.value().mapRect(clipBounds);
+}
+
+} // namespace DisplayList
+} // namespace WebCore
diff --git a/Source/WebCore/platform/graphics/displaylists/DisplayListRecorder.h b/Source/WebCore/platform/graphics/displaylists/DisplayListRecorder.h
new file mode 100644
index 000000000..95c626fd6
--- /dev/null
+++ b/Source/WebCore/platform/graphics/displaylists/DisplayListRecorder.h
@@ -0,0 +1,174 @@
+/*
+ * Copyright (C) 2016 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef DisplayListRecorder_h
+#define DisplayListRecorder_h
+
+#include "DisplayList.h"
+#include "GraphicsContext.h" // For InterpolationQuality.
+#include "Image.h" // For Image::TileRule.
+#include "TextFlags.h"
+#include <wtf/Noncopyable.h>
+
+namespace WebCore {
+
+class FloatPoint;
+class FloatRect;
+class GlyphBuffer;
+class FloatPoint;
+class Font;
+class Image;
+
+struct GraphicsContextState;
+struct ImagePaintingOptions;
+
+namespace DisplayList {
+
+class DrawingItem;
+
+class Recorder {
+ WTF_MAKE_NONCOPYABLE(Recorder);
+public:
+ Recorder(GraphicsContext&, DisplayList&, const FloatRect& initialClip, const AffineTransform&);
+ ~Recorder();
+
+ void updateState(const GraphicsContextState&, GraphicsContextState::StateChangeFlags);
+ void clearShadow();
+
+ void setLineCap(LineCap);
+ void setLineDash(const DashArray&, float dashOffset);
+ void setLineJoin(LineJoin);
+ void setMiterLimit(float);
+
+ void fillRect(const FloatRect&);
+ void fillRect(const FloatRect&, const Color&);
+ void fillRect(const FloatRect&, Gradient&);
+ void fillRect(const FloatRect&, const Color&, CompositeOperator, BlendMode);
+ void fillRoundedRect(const FloatRoundedRect&, const Color&, BlendMode);
+ void fillRectWithRoundedHole(const FloatRect&, const FloatRoundedRect& roundedHoleRect, const Color&);
+ void fillPath(const Path&);
+ void fillEllipse(const FloatRect&);
+ void strokeRect(const FloatRect&, float lineWidth);
+ void strokePath(const Path&);
+ void strokeEllipse(const FloatRect&);
+ void clearRect(const FloatRect&);
+
+#if USE(CG)
+ void applyStrokePattern();
+ void applyFillPattern();
+#endif
+
+ void drawGlyphs(const Font&, const GlyphBuffer&, unsigned from, unsigned numGlyphs, const FloatPoint& anchorPoint, FontSmoothingMode);
+
+ void drawImage(Image&, const FloatRect& destination, const FloatRect& source, const ImagePaintingOptions&);
+ void drawTiledImage(Image&, const FloatRect& destination, const FloatPoint& source, const FloatSize& tileSize, const FloatSize& spacing, const ImagePaintingOptions&);
+ void drawTiledImage(Image&, const FloatRect& destination, const FloatRect& source, const FloatSize& tileScaleFactor, Image::TileRule hRule, Image::TileRule vRule, const ImagePaintingOptions&);
+#if USE(CG) || USE(CAIRO)
+ void drawNativeImage(const NativeImagePtr&, const FloatSize& selfSize, const FloatRect& destRect, const FloatRect& srcRect, CompositeOperator, BlendMode, ImageOrientation);
+#endif
+ void drawPattern(Image&, const FloatRect& destRect, const FloatRect& srcRect, const AffineTransform&, const FloatPoint& phase, const FloatSize& spacing, CompositeOperator, BlendMode = BlendModeNormal);
+
+ void drawRect(const FloatRect&, float borderThickness);
+ void drawLine(const FloatPoint&, const FloatPoint&);
+ void drawLinesForText(const FloatPoint&, const DashArray& widths, bool printing, bool doubleLines, float strokeThickness);
+ void drawLineForDocumentMarker(const FloatPoint&, float width, GraphicsContext::DocumentMarkerLineStyle);
+ void drawEllipse(const FloatRect&);
+ void drawPath(const Path&);
+
+ void drawFocusRing(const Path&, int width, int offset, const Color&);
+ void drawFocusRing(const Vector<FloatRect>&, int width, int offset, const Color&);
+
+ void save();
+ void restore();
+
+ void translate(float x, float y);
+ void rotate(float angleInRadians);
+ void scale(const FloatSize&);
+ void concatCTM(const AffineTransform&);
+
+ void beginTransparencyLayer(float opacity);
+ void endTransparencyLayer();
+
+ void clip(const FloatRect&);
+ void clipOut(const FloatRect&);
+ void clipOut(const Path&);
+ void clipPath(const Path&, WindRule);
+
+ void applyDeviceScaleFactor(float);
+
+ size_t itemCount() const { return m_displayList.itemCount(); }
+
+private:
+ Item& appendItem(Ref<Item>&&);
+ void willAppendItem(const Item&);
+
+ FloatRect extentFromLocalBounds(const FloatRect&) const;
+ void updateItemExtent(DrawingItem&) const;
+
+ const AffineTransform& ctm() const;
+ const FloatRect& clipBounds() const;
+
+ struct ContextState {
+ AffineTransform ctm;
+ FloatRect clipBounds;
+ GraphicsContextStateChange stateChange;
+ GraphicsContextState lastDrawingState;
+ bool wasUsedForDrawing { false };
+ size_t saveItemIndex { 0 };
+
+ ContextState(const AffineTransform& transform, const FloatRect& clip)
+ : ctm(transform)
+ , clipBounds(clip)
+ {
+ }
+
+ ContextState cloneForSave(size_t saveIndex) const
+ {
+ ContextState state(ctm, clipBounds);
+ state.stateChange = stateChange;
+ state.lastDrawingState = lastDrawingState;
+ state.saveItemIndex = saveIndex;
+ return state;
+ }
+
+ void translate(float x, float y);
+ void rotate(float angleInRadians);
+ void scale(const FloatSize&);
+ void concatCTM(const AffineTransform&);
+ };
+
+ const ContextState& currentState() const;
+ ContextState& currentState();
+
+ GraphicsContext& m_graphicsContext;
+ DisplayList& m_displayList;
+
+ Vector<ContextState, 32> m_stateStack;
+};
+
+}
+}
+
+#endif // DisplayListRecorder_h
diff --git a/Source/WebCore/platform/graphics/displaylists/DisplayListReplayer.cpp b/Source/WebCore/platform/graphics/displaylists/DisplayListReplayer.cpp
new file mode 100644
index 000000000..8cc915403
--- /dev/null
+++ b/Source/WebCore/platform/graphics/displaylists/DisplayListReplayer.cpp
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2016 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "DisplayListReplayer.h"
+
+#include "DisplayListItems.h"
+#include "GraphicsContext.h"
+#include "Logging.h"
+#include "TextStream.h"
+
+namespace WebCore {
+namespace DisplayList {
+
+Replayer::Replayer(GraphicsContext& context, const DisplayList& displayList)
+ : m_displayList(displayList)
+ , m_context(context)
+{
+}
+
+Replayer::~Replayer()
+{
+}
+
+std::unique_ptr<DisplayList> Replayer::replay(const FloatRect& initialClip, bool trackReplayList)
+{
+ LOG_WITH_STREAM(DisplayLists, stream << "\nReplaying with clip " << initialClip);
+ UNUSED_PARAM(initialClip);
+
+ std::unique_ptr<DisplayList> replayList;
+ if (UNLIKELY(trackReplayList))
+ replayList = std::make_unique<DisplayList>();
+
+ size_t numItems = m_displayList.itemCount();
+ for (size_t i = 0; i < numItems; ++i) {
+ auto& item = m_displayList.list()[i].get();
+
+ if (is<DrawingItem>(item)) {
+ const DrawingItem& drawingItem = downcast<DrawingItem>(item);
+ if (drawingItem.extentKnown() && !drawingItem.extent().intersects(initialClip)) {
+ LOG_WITH_STREAM(DisplayLists, stream << "skipping " << i << " " << item);
+ continue;
+ }
+ }
+
+ LOG_WITH_STREAM(DisplayLists, stream << "applying " << i << " " << item);
+ item.apply(m_context);
+
+ if (UNLIKELY(trackReplayList))
+ replayList->appendItem(const_cast<Item&>(item));
+ }
+
+ return replayList;
+}
+
+} // namespace DisplayList
+} // namespace WebCore
diff --git a/Source/WebCore/platform/graphics/displaylists/DisplayListReplayer.h b/Source/WebCore/platform/graphics/displaylists/DisplayListReplayer.h
new file mode 100644
index 000000000..136c15ba3
--- /dev/null
+++ b/Source/WebCore/platform/graphics/displaylists/DisplayListReplayer.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2016 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef DisplayListReplayer_h
+#define DisplayListReplayer_h
+
+#include "DisplayList.h"
+#include <wtf/Noncopyable.h>
+#include <wtf/text/WTFString.h>
+
+namespace WebCore {
+
+class FloatRect;
+class GraphicsContext;
+
+namespace DisplayList {
+
+class Replayer {
+ WTF_MAKE_NONCOPYABLE(Replayer);
+public:
+ Replayer(GraphicsContext&, const DisplayList&);
+ ~Replayer();
+
+ std::unique_ptr<DisplayList> replay(const FloatRect& initialClip, bool trackReplayList = false);
+
+private:
+ const DisplayList& m_displayList;
+ GraphicsContext& m_context;
+};
+
+}
+}
+
+#endif // DisplayListReplayer_h
diff --git a/Source/WebCore/platform/graphics/egl/GLContextEGL.cpp b/Source/WebCore/platform/graphics/egl/GLContextEGL.cpp
index 329131601..a086efa98 100644
--- a/Source/WebCore/platform/graphics/egl/GLContextEGL.cpp
+++ b/Source/WebCore/platform/graphics/egl/GLContextEGL.cpp
@@ -22,7 +22,8 @@
#if USE(EGL)
#include "GraphicsContext3D.h"
-#include <wtf/OwnPtr.h>
+#include "PlatformDisplay.h"
+#include <EGL/egl.h>
#if USE(CAIRO)
#include <cairo.h>
@@ -45,30 +46,6 @@
namespace WebCore {
-static EGLDisplay gSharedEGLDisplay = EGL_NO_DISPLAY;
-
-#if USE(OPENGL_ES_2)
-static const EGLenum gGLAPI = EGL_OPENGL_ES_API;
-#else
-static const EGLenum gGLAPI = EGL_OPENGL_API;
-#endif
-
-static EGLDisplay sharedEGLDisplay()
-{
- static bool initialized = false;
- if (!initialized) {
- initialized = true;
-#if PLATFORM(X11)
- gSharedEGLDisplay = eglGetDisplay(GLContext::sharedX11Display());
-#else
- gSharedEGLDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
-#endif
- if (gSharedEGLDisplay != EGL_NO_DISPLAY && (!eglInitialize(gSharedEGLDisplay, 0, 0) || !eglBindAPI(gGLAPI)))
- gSharedEGLDisplay = EGL_NO_DISPLAY;
- }
- return gSharedEGLDisplay;
-}
-
static const EGLint gContextAttributes[] = {
#if USE(OPENGL_ES_2)
EGL_CONTEXT_CLIENT_VERSION, 2,
@@ -76,7 +53,13 @@ static const EGLint gContextAttributes[] = {
EGL_NONE
};
-static bool getEGLConfig(EGLConfig* config, GLContextEGL::EGLSurfaceType surfaceType)
+#if USE(OPENGL_ES_2)
+static const EGLenum gEGLAPIVersion = EGL_OPENGL_ES_API;
+#else
+static const EGLenum gEGLAPIVersion = EGL_OPENGL_API;
+#endif
+
+bool GLContextEGL::getEGLConfig(EGLDisplay display, EGLConfig* config, EGLSurfaceType surfaceType)
{
EGLint attributeList[] = {
#if USE(OPENGL_ES_2)
@@ -101,45 +84,52 @@ static bool getEGLConfig(EGLConfig* config, GLContextEGL::EGLSurfaceType surface
attributeList[13] = EGL_PIXMAP_BIT;
break;
case GLContextEGL::WindowSurface:
+ case GLContextEGL::Surfaceless:
attributeList[13] = EGL_WINDOW_BIT;
break;
}
EGLint numberConfigsReturned;
- return eglChooseConfig(sharedEGLDisplay(), attributeList, config, 1, &numberConfigsReturned) && numberConfigsReturned;
+ return eglChooseConfig(display, attributeList, config, 1, &numberConfigsReturned) && numberConfigsReturned;
}
-PassOwnPtr<GLContextEGL> GLContextEGL::createWindowContext(EGLNativeWindowType window, GLContext* sharingContext)
+std::unique_ptr<GLContextEGL> GLContextEGL::createWindowContext(GLNativeWindowType window, PlatformDisplay& platformDisplay, EGLContext sharingContext)
{
- EGLContext eglSharingContext = sharingContext ? static_cast<GLContextEGL*>(sharingContext)->m_context : 0;
-
- EGLDisplay display = sharedEGLDisplay();
- if (display == EGL_NO_DISPLAY)
- return nullptr;
-
+ EGLDisplay display = platformDisplay.eglDisplay();
EGLConfig config;
- if (!getEGLConfig(&config, WindowSurface))
+ if (!getEGLConfig(display, &config, WindowSurface))
return nullptr;
- EGLContext context = eglCreateContext(display, config, eglSharingContext, gContextAttributes);
+ EGLContext context = eglCreateContext(display, config, sharingContext, gContextAttributes);
if (context == EGL_NO_CONTEXT)
return nullptr;
- EGLSurface surface = eglCreateWindowSurface(display, config, window, 0);
- if (surface == EGL_NO_SURFACE)
+ EGLSurface surface = EGL_NO_SURFACE;
+#if PLATFORM(GTK)
+#if PLATFORM(X11)
+ if (platformDisplay.type() == PlatformDisplay::Type::X11)
+ surface = createWindowSurfaceX11(display, config, window);
+#endif
+#if PLATFORM(WAYLAND)
+ if (platformDisplay.type() == PlatformDisplay::Type::Wayland)
+ surface = createWindowSurfaceWayland(display, config, window);
+#endif
+#else
+ surface = eglCreateWindowSurface(display, config, static_cast<EGLNativeWindowType>(window), nullptr);
+#endif
+ if (surface == EGL_NO_SURFACE) {
+ eglDestroyContext(display, context);
return nullptr;
+ }
- return adoptPtr(new GLContextEGL(context, surface, WindowSurface));
+ return std::unique_ptr<GLContextEGL>(new GLContextEGL(platformDisplay, context, surface, WindowSurface));
}
-PassOwnPtr<GLContextEGL> GLContextEGL::createPbufferContext(EGLContext sharingContext)
+std::unique_ptr<GLContextEGL> GLContextEGL::createPbufferContext(PlatformDisplay& platformDisplay, EGLContext sharingContext)
{
- EGLDisplay display = sharedEGLDisplay();
- if (display == EGL_NO_DISPLAY)
- return nullptr;
-
+ EGLDisplay display = platformDisplay.eglDisplay();
EGLConfig config;
- if (!getEGLConfig(&config, PbufferSurface))
+ if (!getEGLConfig(display, &config, PbufferSurface))
return nullptr;
EGLContext context = eglCreateContext(display, config, sharingContext, gContextAttributes);
@@ -153,78 +143,91 @@ PassOwnPtr<GLContextEGL> GLContextEGL::createPbufferContext(EGLContext sharingCo
return nullptr;
}
- return adoptPtr(new GLContextEGL(context, surface, PbufferSurface));
+ return std::unique_ptr<GLContextEGL>(new GLContextEGL(platformDisplay, context, surface, PbufferSurface));
}
-PassOwnPtr<GLContextEGL> GLContextEGL::createPixmapContext(EGLContext sharingContext)
+std::unique_ptr<GLContextEGL> GLContextEGL::createSurfacelessContext(PlatformDisplay& platformDisplay, EGLContext sharingContext)
{
-#if PLATFORM(X11)
- EGLDisplay display = sharedEGLDisplay();
+ EGLDisplay display = platformDisplay.eglDisplay();
if (display == EGL_NO_DISPLAY)
return nullptr;
+ const char* extensions = eglQueryString(display, EGL_EXTENSIONS);
+ if (!GLContext::isExtensionSupported(extensions, "EGL_KHR_surfaceless_context") && !GLContext::isExtensionSupported(extensions, "EGL_KHR_surfaceless_opengl"))
+ return nullptr;
+
EGLConfig config;
- if (!getEGLConfig(&config, PixmapSurface))
+ if (!getEGLConfig(display, &config, Surfaceless))
return nullptr;
EGLContext context = eglCreateContext(display, config, sharingContext, gContextAttributes);
if (context == EGL_NO_CONTEXT)
return nullptr;
- EGLint depth;
- if (!eglGetConfigAttrib(display, config, EGL_DEPTH_SIZE, &depth))
- return nullptr;
+ return std::unique_ptr<GLContextEGL>(new GLContextEGL(platformDisplay, context, EGL_NO_SURFACE, Surfaceless));
+}
- Pixmap pixmap = XCreatePixmap(sharedX11Display(), DefaultRootWindow(sharedX11Display()), 1, 1, depth);
- if (!pixmap)
+std::unique_ptr<GLContextEGL> GLContextEGL::createContext(GLNativeWindowType window, PlatformDisplay& platformDisplay)
+{
+ if (platformDisplay.eglDisplay() == EGL_NO_DISPLAY)
return nullptr;
- EGLSurface surface = eglCreatePixmapSurface(display, config, pixmap, 0);
-
- if (surface == EGL_NO_SURFACE)
+ if (eglBindAPI(gEGLAPIVersion) == EGL_FALSE)
return nullptr;
- return adoptPtr(new GLContextEGL(context, surface, PixmapSurface));
-#else
- return nullptr;
+ EGLContext eglSharingContext = platformDisplay.sharingGLContext() ? static_cast<GLContextEGL*>(platformDisplay.sharingGLContext())->m_context : EGL_NO_CONTEXT;
+ auto context = window ? createWindowContext(window, platformDisplay, eglSharingContext) : nullptr;
+ if (!context)
+ context = createSurfacelessContext(platformDisplay, eglSharingContext);
+ if (!context) {
+#if PLATFORM(X11)
+ if (platformDisplay.type() == PlatformDisplay::Type::X11)
+ context = createPixmapContext(platformDisplay, eglSharingContext);
+#endif
+#if PLATFORM(WAYLAND)
+ if (platformDisplay.type() == PlatformDisplay::Type::Wayland)
+ context = createWaylandContext(platformDisplay, eglSharingContext);
#endif
+ }
+ if (!context)
+ context = createPbufferContext(platformDisplay, eglSharingContext);
+
+ return context;
}
-PassOwnPtr<GLContextEGL> GLContextEGL::createContext(EGLNativeWindowType window, GLContext* sharingContext)
+std::unique_ptr<GLContextEGL> GLContextEGL::createSharingContext(PlatformDisplay& platformDisplay)
{
- if (!sharedEGLDisplay())
+ if (platformDisplay.eglDisplay() == EGL_NO_DISPLAY)
return nullptr;
- static bool initialized = false;
- static bool success = true;
- if (!initialized) {
-#if !USE(OPENGL_ES_2)
- success = initializeOpenGLShims();
-#endif
- initialized = true;
- }
- if (!success)
+ if (eglBindAPI(gEGLAPIVersion) == EGL_FALSE)
return nullptr;
- EGLContext eglSharingContext = sharingContext ? static_cast<GLContextEGL*>(sharingContext)->m_context : 0;
- OwnPtr<GLContextEGL> context = window ? createWindowContext(window, sharingContext) : nullptr;
+ auto context = createSurfacelessContext(platformDisplay);
+ if (!context) {
+#if PLATFORM(X11)
+ if (platformDisplay.type() == PlatformDisplay::Type::X11)
+ context = createPixmapContext(platformDisplay);
+#endif
+#if PLATFORM(WAYLAND)
+ if (platformDisplay.type() == PlatformDisplay::Type::Wayland)
+ context = createWaylandContext(platformDisplay);
+#endif
+ }
if (!context)
- context = createPixmapContext(eglSharingContext);
+ context = createPbufferContext(platformDisplay);
- if (!context)
- context = createPbufferContext(eglSharingContext);
-
- return context.release();
+ return context;
}
-GLContextEGL::GLContextEGL(EGLContext context, EGLSurface surface, EGLSurfaceType type)
- : m_context(context)
+GLContextEGL::GLContextEGL(PlatformDisplay& display, EGLContext context, EGLSurface surface, EGLSurfaceType type)
+ : GLContext(display)
+ , m_context(context)
, m_surface(surface)
, m_type(type)
-#if USE(CAIRO)
- , m_cairoDevice(0)
-#endif
{
+ ASSERT(type != PixmapSurface);
+ ASSERT(type == Surfaceless || surface != EGL_NO_SURFACE);
}
GLContextEGL::~GLContextEGL()
@@ -234,7 +237,7 @@ GLContextEGL::~GLContextEGL()
cairo_device_destroy(m_cairoDevice);
#endif
- EGLDisplay display = sharedEGLDisplay();
+ EGLDisplay display = m_display.eglDisplay();
if (m_context) {
glBindFramebuffer(GL_FRAMEBUFFER, 0);
eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
@@ -243,6 +246,10 @@ GLContextEGL::~GLContextEGL()
if (m_surface)
eglDestroySurface(display, m_surface);
+
+#if PLATFORM(WAYLAND)
+ destroyWaylandWindow();
+#endif
}
bool GLContextEGL::canRenderToDefaultFramebuffer()
@@ -255,9 +262,10 @@ IntSize GLContextEGL::defaultFrameBufferSize()
if (!canRenderToDefaultFramebuffer())
return IntSize();
+ EGLDisplay display = m_display.eglDisplay();
EGLint width, height;
- if (!eglQuerySurface(sharedEGLDisplay(), m_surface, EGL_WIDTH, &width)
- || !eglQuerySurface(sharedEGLDisplay(), m_surface, EGL_HEIGHT, &height))
+ if (!eglQuerySurface(display, m_surface, EGL_WIDTH, &width)
+ || !eglQuerySurface(display, m_surface, EGL_HEIGHT, &height))
return IntSize();
return IntSize(width, height);
@@ -265,19 +273,19 @@ IntSize GLContextEGL::defaultFrameBufferSize()
bool GLContextEGL::makeContextCurrent()
{
- ASSERT(m_context && m_surface);
+ ASSERT(m_context);
GLContext::makeContextCurrent();
if (eglGetCurrentContext() == m_context)
return true;
- return eglMakeCurrent(sharedEGLDisplay(), m_surface, m_surface, m_context);
+ return eglMakeCurrent(m_display.eglDisplay(), m_surface, m_surface, m_context);
}
void GLContextEGL::swapBuffers()
{
ASSERT(m_surface);
- eglSwapBuffers(sharedEGLDisplay(), m_surface);
+ eglSwapBuffers(m_display.eglDisplay(), m_surface);
}
void GLContextEGL::waitNative()
@@ -285,6 +293,12 @@ void GLContextEGL::waitNative()
eglWaitNative(EGL_CORE_NATIVE_ENGINE);
}
+void GLContextEGL::swapInterval(int interval)
+{
+ ASSERT(m_surface);
+ eglSwapInterval(m_display.eglDisplay(), interval);
+}
+
#if USE(CAIRO)
cairo_device_t* GLContextEGL::cairoDevice()
{
@@ -292,14 +306,14 @@ cairo_device_t* GLContextEGL::cairoDevice()
return m_cairoDevice;
#if ENABLE(ACCELERATED_2D_CANVAS)
- m_cairoDevice = cairo_egl_device_create(sharedEGLDisplay(), m_context);
+ m_cairoDevice = cairo_egl_device_create(m_display.eglDisplay(), m_context);
#endif
return m_cairoDevice;
}
#endif
-#if ENABLE(WEBGL)
+#if ENABLE(GRAPHICS_CONTEXT_3D)
PlatformGraphicsContext3D GLContextEGL::platformContext()
{
return m_context;
diff --git a/Source/WebCore/platform/graphics/egl/GLContextEGL.h b/Source/WebCore/platform/graphics/egl/GLContextEGL.h
index c887e0beb..e5a33597d 100644
--- a/Source/WebCore/platform/graphics/egl/GLContextEGL.h
+++ b/Source/WebCore/platform/graphics/egl/GLContextEGL.h
@@ -17,57 +17,92 @@
* Boston, MA 02110-1301 USA
*/
-#ifndef GLContextEGL_h
-#define GLContextEGL_h
+#pragma once
#if USE(EGL)
#include "GLContext.h"
-#include <EGL/egl.h>
+#if PLATFORM(X11)
+#include "XUniqueResource.h"
+#endif
+
+#if PLATFORM(WAYLAND)
+#include "WlUniquePtr.h"
+struct wl_egl_window;
+#endif
+
+typedef void *EGLConfig;
+typedef void *EGLContext;
+typedef void *EGLDisplay;
+typedef void *EGLSurface;
namespace WebCore {
-class GLContextEGL : public GLContext {
+class GLContextEGL final : public GLContext {
WTF_MAKE_NONCOPYABLE(GLContextEGL);
public:
- enum EGLSurfaceType { PbufferSurface, WindowSurface, PixmapSurface };
- static PassOwnPtr<GLContextEGL> createContext(EGLNativeWindowType, GLContext* sharingContext = 0);
- static PassOwnPtr<GLContextEGL> createWindowContext(EGLNativeWindowType, GLContext* sharingContext);
+ static std::unique_ptr<GLContextEGL> createContext(GLNativeWindowType, PlatformDisplay&);
+ static std::unique_ptr<GLContextEGL> createSharingContext(PlatformDisplay&);
virtual ~GLContextEGL();
- virtual bool makeContextCurrent();
- virtual void swapBuffers();
- virtual void waitNative();
- virtual bool canRenderToDefaultFramebuffer();
- virtual IntSize defaultFrameBufferSize();
+
+private:
+ bool makeContextCurrent() override;
+ void swapBuffers() override;
+ void waitNative() override;
+ bool canRenderToDefaultFramebuffer() override;
+ IntSize defaultFrameBufferSize() override;
+ void swapInterval(int) override;
#if USE(CAIRO)
- virtual cairo_device_t* cairoDevice();
+ cairo_device_t* cairoDevice() override;
#endif
+ bool isEGLContext() const override { return true; }
-#if ENABLE(WEBGL)
- virtual PlatformGraphicsContext3D platformContext();
+#if ENABLE(GRAPHICS_CONTEXT_3D)
+ PlatformGraphicsContext3D platformContext() override;
#endif
-private:
- static PassOwnPtr<GLContextEGL> createPbufferContext(EGLContext sharingContext);
- static PassOwnPtr<GLContextEGL> createPixmapContext(EGLContext sharingContext);
+ enum EGLSurfaceType { PbufferSurface, WindowSurface, PixmapSurface, Surfaceless };
- static void addActiveContext(GLContextEGL*);
- static void cleanupSharedEGLDisplay(void);
+ GLContextEGL(PlatformDisplay&, EGLContext, EGLSurface, EGLSurfaceType);
+#if PLATFORM(X11)
+ GLContextEGL(PlatformDisplay&, EGLContext, EGLSurface, XUniquePixmap&&);
+#endif
+#if PLATFORM(WAYLAND)
+ GLContextEGL(PlatformDisplay&, EGLContext, EGLSurface, WlUniquePtr<struct wl_surface>&&, struct wl_egl_window*);
+ void destroyWaylandWindow();
+#endif
+
+ static std::unique_ptr<GLContextEGL> createWindowContext(GLNativeWindowType, PlatformDisplay&, EGLContext sharingContext = nullptr);
+ static std::unique_ptr<GLContextEGL> createPbufferContext(PlatformDisplay&, EGLContext sharingContext = nullptr);
+ static std::unique_ptr<GLContextEGL> createSurfacelessContext(PlatformDisplay&, EGLContext sharingContext = nullptr);
+#if PLATFORM(X11)
+ static std::unique_ptr<GLContextEGL> createPixmapContext(PlatformDisplay&, EGLContext sharingContext = nullptr);
+ static EGLSurface createWindowSurfaceX11(EGLDisplay, EGLConfig, GLNativeWindowType);
+#endif
+#if PLATFORM(WAYLAND)
+ static std::unique_ptr<GLContextEGL> createWaylandContext(PlatformDisplay&, EGLContext sharingContext = nullptr);
+ static EGLSurface createWindowSurfaceWayland(EGLDisplay, EGLConfig, GLNativeWindowType);
+#endif
- GLContextEGL(EGLContext, EGLSurface, EGLSurfaceType);
+ static bool getEGLConfig(EGLDisplay, EGLConfig*, EGLSurfaceType);
- EGLContext m_context;
- EGLSurface m_surface;
+ EGLContext m_context { nullptr };
+ EGLSurface m_surface { nullptr };
EGLSurfaceType m_type;
+#if PLATFORM(X11)
+ XUniquePixmap m_pixmap;
+#endif
+#if PLATFORM(WAYLAND)
+ WlUniquePtr<struct wl_surface> m_wlSurface;
+ struct wl_egl_window* m_wlWindow { nullptr };
+#endif
#if USE(CAIRO)
- cairo_device_t* m_cairoDevice;
+ cairo_device_t* m_cairoDevice { nullptr };
#endif
};
} // namespace WebCore
#endif // USE(EGL)
-
-#endif // GLContextEGL_h
diff --git a/Source/WebCore/platform/graphics/egl/GLContextEGLWayland.cpp b/Source/WebCore/platform/graphics/egl/GLContextEGLWayland.cpp
new file mode 100644
index 000000000..a0ed56fd9
--- /dev/null
+++ b/Source/WebCore/platform/graphics/egl/GLContextEGLWayland.cpp
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2016 Igalia, S.L.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "config.h"
+#include "GLContextEGL.h"
+
+#if USE(EGL) && PLATFORM(WAYLAND)
+
+#include "PlatformDisplayWayland.h"
+// These includes need to be in this order because wayland-egl.h defines WL_EGL_PLATFORM
+// and egl.h checks that to decide whether it's Wayland platform.
+#include <wayland-egl.h>
+#include <EGL/egl.h>
+
+namespace WebCore {
+
+GLContextEGL::GLContextEGL(PlatformDisplay& display, EGLContext context, EGLSurface surface, WlUniquePtr<struct wl_surface>&& wlSurface, struct wl_egl_window* wlWindow)
+ : GLContext(display)
+ , m_context(context)
+ , m_surface(surface)
+ , m_type(WindowSurface)
+ , m_wlSurface(WTFMove(wlSurface))
+ , m_wlWindow(wlWindow)
+{
+}
+
+EGLSurface GLContextEGL::createWindowSurfaceWayland(EGLDisplay display, EGLConfig config, GLNativeWindowType window)
+{
+ return eglCreateWindowSurface(display, config, reinterpret_cast<EGLNativeWindowType>(window), nullptr);
+}
+
+std::unique_ptr<GLContextEGL> GLContextEGL::createWaylandContext(PlatformDisplay& platformDisplay, EGLContext sharingContext)
+{
+ EGLDisplay display = platformDisplay.eglDisplay();
+ EGLConfig config;
+ if (!getEGLConfig(display, &config, WindowSurface))
+ return nullptr;
+
+ static const EGLint contextAttributes[] = {
+#if USE(OPENGL_ES_2)
+ EGL_CONTEXT_CLIENT_VERSION, 2,
+#endif
+ EGL_NONE
+ };
+
+ EGLContext context = eglCreateContext(display, config, sharingContext, contextAttributes);
+ if (context == EGL_NO_CONTEXT)
+ return nullptr;
+
+ WlUniquePtr<struct wl_surface> wlSurface(downcast<PlatformDisplayWayland>(platformDisplay).createSurface());
+ if (!wlSurface) {
+ eglDestroyContext(display, context);
+ return nullptr;
+ }
+
+ EGLNativeWindowType window = wl_egl_window_create(wlSurface.get(), 1, 1);
+ EGLSurface surface = eglCreateWindowSurface(display, config, window, 0);
+ if (surface == EGL_NO_SURFACE) {
+ eglDestroyContext(display, context);
+ wl_egl_window_destroy(window);
+ return nullptr;
+ }
+
+ return std::unique_ptr<GLContextEGL>(new GLContextEGL(platformDisplay, context, surface, WTFMove(wlSurface), window));
+}
+
+void GLContextEGL::destroyWaylandWindow()
+{
+ if (m_wlWindow)
+ wl_egl_window_destroy(m_wlWindow);
+}
+
+} // namespace WebCore
+
+#endif // USE(EGL) && PLATFORM(WAYLAND)
diff --git a/Source/WebCore/platform/graphics/egl/GLContextEGLX11.cpp b/Source/WebCore/platform/graphics/egl/GLContextEGLX11.cpp
new file mode 100644
index 000000000..8fdd4cad3
--- /dev/null
+++ b/Source/WebCore/platform/graphics/egl/GLContextEGLX11.cpp
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2016 Igalia, S.L.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "config.h"
+#include "GLContextEGL.h"
+
+#if USE(EGL) && PLATFORM(X11)
+#include "PlatformDisplayX11.h"
+#include "XErrorTrapper.h"
+#include "XUniquePtr.h"
+#include <EGL/egl.h>
+#include <X11/Xlib.h>
+
+namespace WebCore {
+
+GLContextEGL::GLContextEGL(PlatformDisplay& display, EGLContext context, EGLSurface surface, XUniquePixmap&& pixmap)
+ : GLContext(display)
+ , m_context(context)
+ , m_surface(surface)
+ , m_type(PixmapSurface)
+ , m_pixmap(WTFMove(pixmap))
+{
+}
+
+EGLSurface GLContextEGL::createWindowSurfaceX11(EGLDisplay display, EGLConfig config, GLNativeWindowType window)
+{
+ return eglCreateWindowSurface(display, config, static_cast<EGLNativeWindowType>(window), nullptr);
+}
+
+std::unique_ptr<GLContextEGL> GLContextEGL::createPixmapContext(PlatformDisplay& platformDisplay, EGLContext sharingContext)
+{
+ EGLDisplay display = platformDisplay.eglDisplay();
+ EGLConfig config;
+ if (!getEGLConfig(display, &config, PixmapSurface))
+ return nullptr;
+
+ static const EGLint contextAttributes[] = {
+#if USE(OPENGL_ES_2)
+ EGL_CONTEXT_CLIENT_VERSION, 2,
+#endif
+ EGL_NONE
+ };
+ EGLContext context = eglCreateContext(display, config, sharingContext, contextAttributes);
+ if (context == EGL_NO_CONTEXT)
+ return nullptr;
+
+ EGLint visualId;
+ if (!eglGetConfigAttrib(display, config, EGL_NATIVE_VISUAL_ID, &visualId)) {
+ eglDestroyContext(display, context);
+ return nullptr;
+ }
+
+ Display* x11Display = downcast<PlatformDisplayX11>(platformDisplay).native();
+
+ XVisualInfo visualInfo;
+ visualInfo.visualid = visualId;
+ int numVisuals = 0;
+ XUniquePtr<XVisualInfo> visualInfoList(XGetVisualInfo(x11Display, VisualIDMask, &visualInfo, &numVisuals));
+ if (!visualInfoList || !numVisuals) {
+ eglDestroyContext(display, context);
+ return nullptr;
+ }
+
+ // We are using VisualIDMask so there must be only one.
+ ASSERT(numVisuals == 1);
+ XUniquePixmap pixmap = XCreatePixmap(x11Display, DefaultRootWindow(x11Display), 1, 1, visualInfoList.get()[0].depth);
+ if (!pixmap) {
+ eglDestroyContext(display, context);
+ return nullptr;
+ }
+
+ // Some drivers fail to create the surface producing BadDrawable X error and the default XError handler normally aborts.
+ // However, if the X error is ignored, eglCreatePixmapSurface() ends up returning a surface and we can continue creating
+ // the context. Since this is an offscreen context, it doesn't matter if the pixmap used is not valid because we never do
+ // swap buffers. So, we use a custom XError handler here that ignores BadDrawable errors and only warns about any other
+ // errors without aborting in any case.
+ XErrorTrapper trapper(x11Display, XErrorTrapper::Policy::Warn, { BadDrawable });
+ EGLSurface surface = eglCreatePixmapSurface(display, config, reinterpret_cast<EGLNativePixmapType>(pixmap.get()), 0);
+ if (surface == EGL_NO_SURFACE) {
+ eglDestroyContext(display, context);
+ return nullptr;
+ }
+
+ return std::unique_ptr<GLContextEGL>(new GLContextEGL(platformDisplay, context, surface, WTFMove(pixmap)));
+}
+
+} // namespace WebCore
+
+#endif // USE(EGL) && PLATFORM(X11)
diff --git a/Source/WebCore/platform/graphics/filters/DistantLightSource.cpp b/Source/WebCore/platform/graphics/filters/DistantLightSource.cpp
index 164f36a1b..4bc76704e 100644
--- a/Source/WebCore/platform/graphics/filters/DistantLightSource.cpp
+++ b/Source/WebCore/platform/graphics/filters/DistantLightSource.cpp
@@ -29,8 +29,6 @@
*/
#include "config.h"
-
-#if ENABLE(FILTERS)
#include "DistantLightSource.h"
#include "TextStream.h"
@@ -76,5 +74,3 @@ TextStream& DistantLightSource::externalRepresentation(TextStream& ts) const
}
} // namespace WebCore
-
-#endif // ENABLE(FILTERS)
diff --git a/Source/WebCore/platform/graphics/filters/DistantLightSource.h b/Source/WebCore/platform/graphics/filters/DistantLightSource.h
index bbe224b84..663abaebb 100644
--- a/Source/WebCore/platform/graphics/filters/DistantLightSource.h
+++ b/Source/WebCore/platform/graphics/filters/DistantLightSource.h
@@ -23,28 +23,28 @@
#ifndef DistantLightSource_h
#define DistantLightSource_h
-#if ENABLE(FILTERS)
#include "LightSource.h"
+#include <wtf/Ref.h>
namespace WebCore {
class DistantLightSource : public LightSource {
public:
- static PassRefPtr<DistantLightSource> create(float azimuth, float elevation)
+ static Ref<DistantLightSource> create(float azimuth, float elevation)
{
- return adoptRef(new DistantLightSource(azimuth, elevation));
+ return adoptRef(*new DistantLightSource(azimuth, elevation));
}
float azimuth() const { return m_azimuth; }
float elevation() const { return m_elevation; }
- virtual bool setAzimuth(float) override;
- virtual bool setElevation(float) override;
+ bool setAzimuth(float) override;
+ bool setElevation(float) override;
- virtual void initPaintingData(PaintingData&);
- virtual void updatePaintingData(PaintingData&, int x, int y, float z);
+ void initPaintingData(PaintingData&) override;
+ void updatePaintingData(PaintingData&, int x, int y, float z) override;
- virtual TextStream& externalRepresentation(TextStream&) const;
+ TextStream& externalRepresentation(TextStream&) const override;
private:
DistantLightSource(float azimuth, float elevation)
@@ -60,6 +60,4 @@ private:
} // namespace WebCore
-#endif // ENABLE(FILTERS)
-
#endif // DistantLightSource_h
diff --git a/Source/WebCore/platform/graphics/filters/FEBlend.cpp b/Source/WebCore/platform/graphics/filters/FEBlend.cpp
index 11b707322..ff9111c4b 100644
--- a/Source/WebCore/platform/graphics/filters/FEBlend.cpp
+++ b/Source/WebCore/platform/graphics/filters/FEBlend.cpp
@@ -4,6 +4,7 @@
* Copyright (C) 2005 Eric Seidel <eric@webkit.org>
* Copyright (C) 2009 Dirk Schulze <krit@webkit.org>
* Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies)
+ * Copyright (C) 2014 Adobe Systems Incorporated. All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
@@ -22,11 +23,9 @@
*/
#include "config.h"
-
-#if ENABLE(FILTERS)
#include "FEBlend.h"
-#include "FEBlendNEON.h"
+#include "FEBlendNEON.h"
#include "Filter.h"
#include "FloatPoint.h"
#include "GraphicsContext.h"
@@ -34,27 +33,25 @@
#include <runtime/Uint8ClampedArray.h>
-typedef unsigned char (*BlendType)(unsigned char colorA, unsigned char colorB, unsigned char alphaA, unsigned char alphaB);
-
namespace WebCore {
-FEBlend::FEBlend(Filter* filter, BlendModeType mode)
+FEBlend::FEBlend(Filter& filter, BlendMode mode)
: FilterEffect(filter)
, m_mode(mode)
{
}
-PassRefPtr<FEBlend> FEBlend::create(Filter* filter, BlendModeType mode)
+Ref<FEBlend> FEBlend::create(Filter& filter, BlendMode mode)
{
- return adoptRef(new FEBlend(filter, mode));
+ return adoptRef(*new FEBlend(filter, mode));
}
-BlendModeType FEBlend::blendMode() const
+BlendMode FEBlend::blendMode() const
{
return m_mode;
}
-bool FEBlend::setBlendMode(BlendModeType mode)
+bool FEBlend::setBlendMode(BlendMode mode)
{
if (m_mode == mode)
return false;
@@ -62,158 +59,40 @@ bool FEBlend::setBlendMode(BlendModeType mode)
return true;
}
-inline unsigned char feBlendNormal(unsigned char colorA, unsigned char colorB, unsigned char alphaA, unsigned char)
-{
- return fastDivideBy255((255 - alphaA) * colorB + colorA * 255);
-}
-
-inline unsigned char feBlendMultiply(unsigned char colorA, unsigned char colorB, unsigned char alphaA, unsigned char alphaB)
-{
- return fastDivideBy255((255 - alphaA) * colorB + (255 - alphaB + colorB) * colorA);
-}
-
-inline unsigned char feBlendScreen(unsigned char colorA, unsigned char colorB, unsigned char, unsigned char)
-{
- return fastDivideBy255((colorB + colorA) * 255 - colorA * colorB);
-}
-
-inline unsigned char feBlendDarken(unsigned char colorA, unsigned char colorB, unsigned char alphaA, unsigned char alphaB)
-{
- return fastDivideBy255(std::min((255 - alphaA) * colorB + colorA * 255, (255 - alphaB) * colorA + colorB * 255));
-}
-
-inline unsigned char feBlendLighten(unsigned char colorA, unsigned char colorB, unsigned char alphaA, unsigned char alphaB)
-{
- return fastDivideBy255(std::max((255 - alphaA) * colorB + colorA * 255, (255 - alphaB) * colorA + colorB * 255));
-}
-
-inline unsigned char feBlendUnknown(unsigned char, unsigned char, unsigned char, unsigned char)
-{
- return 0;
-}
-
-template<BlendType BlendFunction>
-static void platformApply(unsigned char* sourcePixelA, unsigned char* sourcePixelB,
- unsigned char* destinationPixel, unsigned pixelArrayLength)
-{
- unsigned len = pixelArrayLength / 4;
- for (unsigned pixelOffset = 0; pixelOffset < len; pixelOffset++) {
- unsigned char alphaA = sourcePixelA[3];
- unsigned char alphaB = sourcePixelB[3];
- destinationPixel[0] = BlendFunction(sourcePixelA[0], sourcePixelB[0], alphaA, alphaB);
- destinationPixel[1] = BlendFunction(sourcePixelA[1], sourcePixelB[1], alphaA, alphaB);
- destinationPixel[2] = BlendFunction(sourcePixelA[2], sourcePixelB[2], alphaA, alphaB);
- destinationPixel[3] = 255 - fastDivideBy255((255 - alphaA) * (255 - alphaB));
- sourcePixelA += 4;
- sourcePixelB += 4;
- destinationPixel += 4;
- }
-}
-
-void FEBlend::platformApplyGeneric(unsigned char* sourcePixelA, unsigned char* sourcePixelB,
- unsigned char* destinationPixel, unsigned pixelArrayLength)
-{
- switch (m_mode) {
- case FEBLEND_MODE_NORMAL:
- platformApply<feBlendNormal>(sourcePixelA, sourcePixelB, destinationPixel, pixelArrayLength);
- break;
- case FEBLEND_MODE_MULTIPLY:
- platformApply<feBlendMultiply>(sourcePixelA, sourcePixelB, destinationPixel, pixelArrayLength);
- break;
- case FEBLEND_MODE_SCREEN:
- platformApply<feBlendScreen>(sourcePixelA, sourcePixelB, destinationPixel, pixelArrayLength);
- break;
- case FEBLEND_MODE_DARKEN:
- platformApply<feBlendDarken>(sourcePixelA, sourcePixelB, destinationPixel, pixelArrayLength);
- break;
- case FEBLEND_MODE_LIGHTEN:
- platformApply<feBlendLighten>(sourcePixelA, sourcePixelB, destinationPixel, pixelArrayLength);
- break;
- case FEBLEND_MODE_UNKNOWN:
- platformApply<feBlendUnknown>(sourcePixelA, sourcePixelB, destinationPixel, pixelArrayLength);
- break;
- }
-}
-
+#if !HAVE(ARM_NEON_INTRINSICS)
void FEBlend::platformApplySoftware()
{
FilterEffect* in = inputEffect(0);
FilterEffect* in2 = inputEffect(1);
- ASSERT(m_mode > FEBLEND_MODE_UNKNOWN);
- ASSERT(m_mode <= FEBLEND_MODE_LIGHTEN);
-
- Uint8ClampedArray* dstPixelArray = createPremultipliedImageResult();
- if (!dstPixelArray)
+ ImageBuffer* resultImage = createImageBufferResult();
+ if (!resultImage)
return;
+ GraphicsContext& filterContext = resultImage->context();
- IntRect effectADrawingRect = requestedRegionOfInputImageData(in->absolutePaintRect());
- RefPtr<Uint8ClampedArray> srcPixelArrayA = in->asPremultipliedImage(effectADrawingRect);
-
- IntRect effectBDrawingRect = requestedRegionOfInputImageData(in2->absolutePaintRect());
- RefPtr<Uint8ClampedArray> srcPixelArrayB = in2->asPremultipliedImage(effectBDrawingRect);
-
- unsigned pixelArrayLength = srcPixelArrayA->length();
- ASSERT(pixelArrayLength == srcPixelArrayB->length());
-
-#if HAVE(ARM_NEON_INTRINSICS)
- if (pixelArrayLength >= 8)
- platformApplyNEON(srcPixelArrayA->data(), srcPixelArrayB->data(), dstPixelArray->data(), pixelArrayLength);
- else { // If there is just one pixel we expand it to two.
- ASSERT(pixelArrayLength > 0);
- uint32_t sourceA[2] = {0, 0};
- uint32_t sourceBAndDest[2] = {0, 0};
+ ImageBuffer* imageBuffer = in->asImageBuffer();
+ ImageBuffer* imageBuffer2 = in2->asImageBuffer();
+ if (!imageBuffer || !imageBuffer2)
+ return;
- sourceA[0] = reinterpret_cast<uint32_t*>(srcPixelArrayA->data())[0];
- sourceBAndDest[0] = reinterpret_cast<uint32_t*>(srcPixelArrayB->data())[0];
- platformApplyNEON(reinterpret_cast<uint8_t*>(sourceA), reinterpret_cast<uint8_t*>(sourceBAndDest), reinterpret_cast<uint8_t*>(sourceBAndDest), 8);
- reinterpret_cast<uint32_t*>(dstPixelArray->data())[0] = sourceBAndDest[0];
- }
-#else
- platformApplyGeneric(srcPixelArrayA->data(), srcPixelArrayB->data(), dstPixelArray->data(), pixelArrayLength);
-#endif
+ filterContext.drawImageBuffer(*imageBuffer2, drawingRegionOfInputImage(in2->absolutePaintRect()));
+ filterContext.drawImageBuffer(*imageBuffer, drawingRegionOfInputImage(in->absolutePaintRect()), IntRect(IntPoint(), imageBuffer->logicalSize()), ImagePaintingOptions(CompositeSourceOver, m_mode));
}
+#endif
void FEBlend::dump()
{
}
-static TextStream& operator<<(TextStream& ts, const BlendModeType& type)
-{
- switch (type) {
- case FEBLEND_MODE_UNKNOWN:
- ts << "UNKNOWN";
- break;
- case FEBLEND_MODE_NORMAL:
- ts << "NORMAL";
- break;
- case FEBLEND_MODE_MULTIPLY:
- ts << "MULTIPLY";
- break;
- case FEBLEND_MODE_SCREEN:
- ts << "SCREEN";
- break;
- case FEBLEND_MODE_DARKEN:
- ts << "DARKEN";
- break;
- case FEBLEND_MODE_LIGHTEN:
- ts << "LIGHTEN";
- break;
- }
- return ts;
-}
-
TextStream& FEBlend::externalRepresentation(TextStream& ts, int indent) const
{
writeIndent(ts, indent);
ts << "[feBlend";
FilterEffect::externalRepresentation(ts);
- ts << " mode=\"" << m_mode << "\"]\n";
+ ts << " mode=\"" << (m_mode == BlendModeNormal ? "normal" : compositeOperatorName(CompositeSourceOver, m_mode)) << "\"]\n";
inputEffect(0)->externalRepresentation(ts, indent + 1);
inputEffect(1)->externalRepresentation(ts, indent + 1);
return ts;
}
} // namespace WebCore
-
-#endif // ENABLE(FILTERS)
diff --git a/Source/WebCore/platform/graphics/filters/FEBlend.h b/Source/WebCore/platform/graphics/filters/FEBlend.h
index 6b00080a8..1c50a77ea 100644
--- a/Source/WebCore/platform/graphics/filters/FEBlend.h
+++ b/Source/WebCore/platform/graphics/filters/FEBlend.h
@@ -2,6 +2,7 @@
* Copyright (C) 2004, 2005, 2006, 2007 Nikolas Zimmermann <zimmermann@kde.org>
* Copyright (C) 2004, 2005 Rob Buis <buis@kde.org>
* Copyright (C) 2005 Eric Seidel <eric@webkit.org>
+ * Copyright (C) 2014 Adobe Systems Incorporated. All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
@@ -22,46 +23,34 @@
#ifndef FEBlend_h
#define FEBlend_h
-#if ENABLE(FILTERS)
#include "FilterEffect.h"
#include "Filter.h"
namespace WebCore {
-enum BlendModeType {
- FEBLEND_MODE_UNKNOWN = 0,
- FEBLEND_MODE_NORMAL = 1,
- FEBLEND_MODE_MULTIPLY = 2,
- FEBLEND_MODE_SCREEN = 3,
- FEBLEND_MODE_DARKEN = 4,
- FEBLEND_MODE_LIGHTEN = 5
-};
-
class FEBlend : public FilterEffect {
public:
- static PassRefPtr<FEBlend> create(Filter*, BlendModeType);
+ static Ref<FEBlend> create(Filter&, BlendMode);
- BlendModeType blendMode() const;
- bool setBlendMode(BlendModeType);
+ BlendMode blendMode() const;
+ bool setBlendMode(BlendMode);
void platformApplyGeneric(unsigned char* srcPixelArrayA, unsigned char* srcPixelArrayB, unsigned char* dstPixelArray,
unsigned colorArrayLength);
void platformApplyNEON(unsigned char* srcPixelArrayA, unsigned char* srcPixelArrayB, unsigned char* dstPixelArray,
unsigned colorArrayLength);
- virtual void platformApplySoftware();
- virtual void dump();
+ void platformApplySoftware() override;
+ void dump() override;
- virtual TextStream& externalRepresentation(TextStream&, int indention) const;
+ TextStream& externalRepresentation(TextStream&, int indention) const override;
private:
- FEBlend(Filter*, BlendModeType);
+ FEBlend(Filter&, BlendMode);
- BlendModeType m_mode;
+ BlendMode m_mode;
};
} // namespace WebCore
-#endif // ENABLE(FILTERS)
-
#endif // FEBlend_h
diff --git a/Source/WebCore/platform/graphics/filters/FEColorMatrix.cpp b/Source/WebCore/platform/graphics/filters/FEColorMatrix.cpp
index 1a5504750..2320d52c2 100644
--- a/Source/WebCore/platform/graphics/filters/FEColorMatrix.cpp
+++ b/Source/WebCore/platform/graphics/filters/FEColorMatrix.cpp
@@ -21,8 +21,6 @@
*/
#include "config.h"
-
-#if ENABLE(FILTERS)
#include "FEColorMatrix.h"
#include "Filter.h"
@@ -34,16 +32,16 @@
namespace WebCore {
-FEColorMatrix::FEColorMatrix(Filter* filter, ColorMatrixType type, const Vector<float>& values)
+FEColorMatrix::FEColorMatrix(Filter& filter, ColorMatrixType type, const Vector<float>& values)
: FilterEffect(filter)
, m_type(type)
, m_values(values)
{
}
-PassRefPtr<FEColorMatrix> FEColorMatrix::create(Filter* filter, ColorMatrixType type, const Vector<float>& values)
+Ref<FEColorMatrix> FEColorMatrix::create(Filter& filter, ColorMatrixType type, const Vector<float>& values)
{
- return adoptRef(new FEColorMatrix(filter, type, values));
+ return adoptRef(*new FEColorMatrix(filter, type, values));
}
ColorMatrixType FEColorMatrix::type() const
@@ -149,25 +147,28 @@ void FEColorMatrix::platformApplySoftware()
if (!resultImage)
return;
- resultImage->context()->drawImageBuffer(in->asImageBuffer(), ColorSpaceDeviceRGB, drawingRegionOfInputImage(in->absolutePaintRect()));
+ ImageBuffer* inBuffer = in->asImageBuffer();
+ if (inBuffer)
+ resultImage->context().drawImageBuffer(*inBuffer, drawingRegionOfInputImage(in->absolutePaintRect()));
- IntRect imageRect(IntPoint(), absolutePaintRect().size());
+ IntRect imageRect(IntPoint(), resultImage->logicalSize());
RefPtr<Uint8ClampedArray> pixelArray = resultImage->getUnmultipliedImageData(imageRect);
+ Vector<float> values = normalizedFloats(m_values);
switch (m_type) {
case FECOLORMATRIX_TYPE_UNKNOWN:
break;
case FECOLORMATRIX_TYPE_MATRIX:
- effectType<FECOLORMATRIX_TYPE_MATRIX>(pixelArray.get(), m_values);
+ effectType<FECOLORMATRIX_TYPE_MATRIX>(pixelArray.get(), values);
break;
case FECOLORMATRIX_TYPE_SATURATE:
- effectType<FECOLORMATRIX_TYPE_SATURATE>(pixelArray.get(), m_values);
+ effectType<FECOLORMATRIX_TYPE_SATURATE>(pixelArray.get(), values);
break;
case FECOLORMATRIX_TYPE_HUEROTATE:
- effectType<FECOLORMATRIX_TYPE_HUEROTATE>(pixelArray.get(), m_values);
+ effectType<FECOLORMATRIX_TYPE_HUEROTATE>(pixelArray.get(), values);
break;
case FECOLORMATRIX_TYPE_LUMINANCETOALPHA:
- effectType<FECOLORMATRIX_TYPE_LUMINANCETOALPHA>(pixelArray.get(), m_values);
+ effectType<FECOLORMATRIX_TYPE_LUMINANCETOALPHA>(pixelArray.get(), values);
setIsAlphaImage(true);
break;
}
@@ -225,5 +226,3 @@ TextStream& FEColorMatrix::externalRepresentation(TextStream& ts, int indent) co
}
} // namespace WebCore
-
-#endif // ENABLE(FILTERS)
diff --git a/Source/WebCore/platform/graphics/filters/FEColorMatrix.h b/Source/WebCore/platform/graphics/filters/FEColorMatrix.h
index 98905c58e..36d3a2437 100644
--- a/Source/WebCore/platform/graphics/filters/FEColorMatrix.h
+++ b/Source/WebCore/platform/graphics/filters/FEColorMatrix.h
@@ -22,7 +22,6 @@
#ifndef FEColorMatrix_h
#define FEColorMatrix_h
-#if ENABLE(FILTERS)
#include "FilterEffect.h"
#include "Filter.h"
@@ -40,7 +39,7 @@ enum ColorMatrixType {
class FEColorMatrix : public FilterEffect {
public:
- static PassRefPtr<FEColorMatrix> create(Filter*, ColorMatrixType, const Vector<float>&);
+ static Ref<FEColorMatrix> create(Filter&, ColorMatrixType, const Vector<float>&);
ColorMatrixType type() const;
bool setType(ColorMatrixType);
@@ -48,19 +47,16 @@ public:
const Vector<float>& values() const;
bool setValues(const Vector<float>&);
- virtual void platformApplySoftware();
-#if ENABLE(OPENCL)
- virtual bool platformApplyOpenCL();
-#endif
- virtual void dump();
+ void platformApplySoftware() override;
+ void dump() override;
- virtual TextStream& externalRepresentation(TextStream&, int indention) const;
+ TextStream& externalRepresentation(TextStream&, int indention) const override;
static inline void calculateSaturateComponents(float* components, float value);
static inline void calculateHueRotateComponents(float* components, float value);
private:
- FEColorMatrix(Filter*, ColorMatrixType, const Vector<float>&);
+ FEColorMatrix(Filter&, ColorMatrixType, const Vector<float>&);
ColorMatrixType m_type;
Vector<float> m_values;
@@ -97,6 +93,4 @@ inline void FEColorMatrix::calculateHueRotateComponents(float* components, float
} // namespace WebCore
-#endif // ENABLE(FILTERS)
-
#endif // FEColorMatrix_h
diff --git a/Source/WebCore/platform/graphics/filters/FEComponentTransfer.cpp b/Source/WebCore/platform/graphics/filters/FEComponentTransfer.cpp
index 1e36e7956..780398d4f 100644
--- a/Source/WebCore/platform/graphics/filters/FEComponentTransfer.cpp
+++ b/Source/WebCore/platform/graphics/filters/FEComponentTransfer.cpp
@@ -22,8 +22,6 @@
*/
#include "config.h"
-
-#if ENABLE(FILTERS)
#include "FEComponentTransfer.h"
#include "Filter.h"
@@ -38,7 +36,7 @@ namespace WebCore {
typedef void (*TransferType)(unsigned char*, const ComponentTransferFunction&);
-FEComponentTransfer::FEComponentTransfer(Filter* filter, const ComponentTransferFunction& redFunc, const ComponentTransferFunction& greenFunc,
+FEComponentTransfer::FEComponentTransfer(Filter& filter, const ComponentTransferFunction& redFunc, const ComponentTransferFunction& greenFunc,
const ComponentTransferFunction& blueFunc, const ComponentTransferFunction& alphaFunc)
: FilterEffect(filter)
, m_redFunc(redFunc)
@@ -48,10 +46,10 @@ FEComponentTransfer::FEComponentTransfer(Filter* filter, const ComponentTransfer
{
}
-PassRefPtr<FEComponentTransfer> FEComponentTransfer::create(Filter* filter, const ComponentTransferFunction& redFunc,
+Ref<FEComponentTransfer> FEComponentTransfer::create(Filter& filter, const ComponentTransferFunction& redFunc,
const ComponentTransferFunction& greenFunc, const ComponentTransferFunction& blueFunc, const ComponentTransferFunction& alphaFunc)
{
- return adoptRef(new FEComponentTransfer(filter, redFunc, greenFunc, blueFunc, alphaFunc));
+ return adoptRef(*new FEComponentTransfer(filter, redFunc, greenFunc, blueFunc, alphaFunc));
}
ComponentTransferFunction FEComponentTransfer::redFunction() const
@@ -247,5 +245,3 @@ TextStream& FEComponentTransfer::externalRepresentation(TextStream& ts, int inde
}
} // namespace WebCore
-
-#endif // ENABLE(FILTERS)
diff --git a/Source/WebCore/platform/graphics/filters/FEComponentTransfer.h b/Source/WebCore/platform/graphics/filters/FEComponentTransfer.h
index cf6b7476d..6619755f0 100644
--- a/Source/WebCore/platform/graphics/filters/FEComponentTransfer.h
+++ b/Source/WebCore/platform/graphics/filters/FEComponentTransfer.h
@@ -22,7 +22,6 @@
#ifndef FEComponentTransfer_h
#define FEComponentTransfer_h
-#if ENABLE(FILTERS)
#include "FilterEffect.h"
#include "Filter.h"
@@ -63,8 +62,8 @@ struct ComponentTransferFunction {
class FEComponentTransfer : public FilterEffect {
public:
- static PassRefPtr<FEComponentTransfer> create(Filter*, const ComponentTransferFunction& redFunc, const ComponentTransferFunction& greenFunc,
- const ComponentTransferFunction& blueFunc, const ComponentTransferFunction& alphaFunc);
+ static Ref<FEComponentTransfer> create(Filter&, const ComponentTransferFunction& redFunc, const ComponentTransferFunction& greenFunc,
+ const ComponentTransferFunction& blueFunc, const ComponentTransferFunction& alphaFunc);
ComponentTransferFunction redFunction() const;
void setRedFunction(const ComponentTransferFunction&);
@@ -78,13 +77,13 @@ public:
ComponentTransferFunction alphaFunction() const;
void setAlphaFunction(const ComponentTransferFunction&);
- virtual void platformApplySoftware();
- virtual void dump();
+ void platformApplySoftware() override;
+ void dump() override;
- virtual TextStream& externalRepresentation(TextStream&, int indention) const;
+ TextStream& externalRepresentation(TextStream&, int indention) const override;
private:
- FEComponentTransfer(Filter*, const ComponentTransferFunction& redFunc, const ComponentTransferFunction& greenFunc,
+ FEComponentTransfer(Filter&, const ComponentTransferFunction& redFunc, const ComponentTransferFunction& greenFunc,
const ComponentTransferFunction& blueFunc, const ComponentTransferFunction& alphaFunc);
void getValues(unsigned char rValues[256], unsigned char gValues[256], unsigned char bValues[256], unsigned char aValues[256]);
@@ -97,6 +96,4 @@ private:
} // namespace WebCore
-#endif // ENABLE(FILTERS)
-
#endif // FEComponentTransfer_h
diff --git a/Source/WebCore/platform/graphics/filters/FEComposite.cpp b/Source/WebCore/platform/graphics/filters/FEComposite.cpp
index 07805de9c..94d16285a 100644
--- a/Source/WebCore/platform/graphics/filters/FEComposite.cpp
+++ b/Source/WebCore/platform/graphics/filters/FEComposite.cpp
@@ -22,8 +22,6 @@
*/
#include "config.h"
-
-#if ENABLE(FILTERS)
#include "FEComposite.h"
#include "FECompositeArithmeticNEON.h"
@@ -35,7 +33,7 @@
namespace WebCore {
-FEComposite::FEComposite(Filter* filter, const CompositeOperationType& type, float k1, float k2, float k3, float k4)
+FEComposite::FEComposite(Filter& filter, const CompositeOperationType& type, float k1, float k2, float k3, float k4)
: FilterEffect(filter)
, m_type(type)
, m_k1(k1)
@@ -45,9 +43,9 @@ FEComposite::FEComposite(Filter* filter, const CompositeOperationType& type, flo
{
}
-PassRefPtr<FEComposite> FEComposite::create(Filter* filter, const CompositeOperationType& type, float k1, float k2, float k3, float k4)
+Ref<FEComposite> FEComposite::create(Filter& filter, const CompositeOperationType& type, float k1, float k2, float k3, float k4)
{
- return adoptRef(new FEComposite(filter, type, k1, k2, k3, k4));
+ return adoptRef(*new FEComposite(filter, type, k1, k2, k3, k4));
}
CompositeOperationType FEComposite::operation() const
@@ -122,6 +120,13 @@ void FEComposite::correctFilterResultIfNeeded()
forceValidPreMultipliedPixels();
}
+
+static unsigned char clampByte(int c)
+{
+ unsigned char buff[] = { static_cast<unsigned char>(c), 255, 0 };
+ unsigned uc = static_cast<unsigned>(c);
+ return buff[!!(uc & ~0xff) + !!(uc & ~(~0u >> 1))];
+}
template <int b1, int b4>
static inline void computeArithmeticPixels(unsigned char* source, unsigned char* destination, int pixelArrayLength,
@@ -143,12 +148,7 @@ static inline void computeArithmeticPixels(unsigned char* source, unsigned char*
if (b4)
result += scaledK4;
- if (result <= 0)
- *destination = 0;
- else if (result >= 255)
- *destination = 255;
- else
- *destination = result;
+ *destination = clampByte(result);
++source;
++destination;
}
@@ -181,6 +181,7 @@ static inline void computeArithmeticPixelsUnclamped(unsigned char* source, unsig
}
}
+#if !HAVE(ARM_NEON_INTRINSICS)
static inline void arithmeticSoftware(unsigned char* source, unsigned char* destination, int pixelArrayLength, float k1, float k2, float k3, float k4)
{
float upperLimit = std::max(0.0f, k1) + std::max(0.0f, k2) + std::max(0.0f, k3) + k4;
@@ -212,6 +213,7 @@ static inline void arithmeticSoftware(unsigned char* source, unsigned char* dest
computeArithmeticPixels<0, 0>(source, destination, pixelArrayLength, k1, k2, k3, k4);
}
}
+#endif
inline void FEComposite::platformArithmeticSoftware(Uint8ClampedArray* source, Uint8ClampedArray* destination,
float k1, float k2, float k3, float k4)
@@ -272,17 +274,17 @@ void FEComposite::platformApplySoftware()
ImageBuffer* resultImage = createImageBufferResult();
if (!resultImage)
return;
- GraphicsContext* filterContext = resultImage->context();
+ GraphicsContext& filterContext = resultImage->context();
ImageBuffer* imageBuffer = in->asImageBuffer();
ImageBuffer* imageBuffer2 = in2->asImageBuffer();
- ASSERT(imageBuffer);
- ASSERT(imageBuffer2);
+ if (!imageBuffer || !imageBuffer2)
+ return;
switch (m_type) {
case FECOMPOSITE_OPERATOR_OVER:
- filterContext->drawImageBuffer(imageBuffer2, ColorSpaceDeviceRGB, drawingRegionOfInputImage(in2->absolutePaintRect()));
- filterContext->drawImageBuffer(imageBuffer, ColorSpaceDeviceRGB, drawingRegionOfInputImage(in->absolutePaintRect()));
+ filterContext.drawImageBuffer(*imageBuffer2, drawingRegionOfInputImage(in2->absolutePaintRect()));
+ filterContext.drawImageBuffer(*imageBuffer, drawingRegionOfInputImage(in->absolutePaintRect()));
break;
case FECOMPOSITE_OPERATOR_IN: {
// Applies only to the intersected region.
@@ -291,25 +293,28 @@ void FEComposite::platformApplySoftware()
destinationRect.intersect(absolutePaintRect());
if (destinationRect.isEmpty())
break;
-
IntRect adjustedDestinationRect = destinationRect - absolutePaintRect().location();
IntRect sourceRect = destinationRect - in->absolutePaintRect().location();
IntRect source2Rect = destinationRect - in2->absolutePaintRect().location();
- filterContext->drawImageBuffer(imageBuffer2, ColorSpaceDeviceRGB, adjustedDestinationRect, source2Rect);
- filterContext->drawImageBuffer(imageBuffer, ColorSpaceDeviceRGB, adjustedDestinationRect, sourceRect, CompositeSourceIn);
+ filterContext.drawImageBuffer(*imageBuffer2, adjustedDestinationRect, source2Rect);
+ filterContext.drawImageBuffer(*imageBuffer, adjustedDestinationRect, sourceRect, CompositeSourceIn);
break;
}
case FECOMPOSITE_OPERATOR_OUT:
- filterContext->drawImageBuffer(imageBuffer, ColorSpaceDeviceRGB, drawingRegionOfInputImage(in->absolutePaintRect()));
- filterContext->drawImageBuffer(imageBuffer2, ColorSpaceDeviceRGB, drawingRegionOfInputImage(in2->absolutePaintRect()), IntRect(IntPoint(), imageBuffer2->logicalSize()), CompositeDestinationOut);
+ filterContext.drawImageBuffer(*imageBuffer, drawingRegionOfInputImage(in->absolutePaintRect()));
+ filterContext.drawImageBuffer(*imageBuffer2, drawingRegionOfInputImage(in2->absolutePaintRect()), IntRect(IntPoint(), imageBuffer2->logicalSize()), CompositeDestinationOut);
break;
case FECOMPOSITE_OPERATOR_ATOP:
- filterContext->drawImageBuffer(imageBuffer2, ColorSpaceDeviceRGB, drawingRegionOfInputImage(in2->absolutePaintRect()));
- filterContext->drawImageBuffer(imageBuffer, ColorSpaceDeviceRGB, drawingRegionOfInputImage(in->absolutePaintRect()), IntRect(IntPoint(), imageBuffer->logicalSize()), CompositeSourceAtop);
+ filterContext.drawImageBuffer(*imageBuffer2, drawingRegionOfInputImage(in2->absolutePaintRect()));
+ filterContext.drawImageBuffer(*imageBuffer, drawingRegionOfInputImage(in->absolutePaintRect()), IntRect(IntPoint(), imageBuffer->logicalSize()), CompositeSourceAtop);
break;
case FECOMPOSITE_OPERATOR_XOR:
- filterContext->drawImageBuffer(imageBuffer2, ColorSpaceDeviceRGB, drawingRegionOfInputImage(in2->absolutePaintRect()));
- filterContext->drawImageBuffer(imageBuffer, ColorSpaceDeviceRGB, drawingRegionOfInputImage(in->absolutePaintRect()), IntRect(IntPoint(), imageBuffer->logicalSize()), CompositeXOR);
+ filterContext.drawImageBuffer(*imageBuffer2, drawingRegionOfInputImage(in2->absolutePaintRect()));
+ filterContext.drawImageBuffer(*imageBuffer, drawingRegionOfInputImage(in->absolutePaintRect()), IntRect(IntPoint(), imageBuffer->logicalSize()), CompositeXOR);
+ break;
+ case FECOMPOSITE_OPERATOR_LIGHTER:
+ filterContext.drawImageBuffer(*imageBuffer2, drawingRegionOfInputImage(in2->absolutePaintRect()));
+ filterContext.drawImageBuffer(*imageBuffer, drawingRegionOfInputImage(in->absolutePaintRect()), IntRect(IntPoint(), imageBuffer->logicalSize()), CompositePlusLighter);
break;
default:
break;
@@ -344,6 +349,9 @@ static TextStream& operator<<(TextStream& ts, const CompositeOperationType& type
case FECOMPOSITE_OPERATOR_ARITHMETIC:
ts << "ARITHMETIC";
break;
+ case FECOMPOSITE_OPERATOR_LIGHTER:
+ ts << "LIGHTER";
+ break;
}
return ts;
}
@@ -363,5 +371,3 @@ TextStream& FEComposite::externalRepresentation(TextStream& ts, int indent) cons
}
} // namespace WebCore
-
-#endif // ENABLE(FILTERS)
diff --git a/Source/WebCore/platform/graphics/filters/FEComposite.h b/Source/WebCore/platform/graphics/filters/FEComposite.h
index 9f78177ea..f515e9500 100644
--- a/Source/WebCore/platform/graphics/filters/FEComposite.h
+++ b/Source/WebCore/platform/graphics/filters/FEComposite.h
@@ -22,7 +22,6 @@
#ifndef FEComposite_h
#define FEComposite_h
-#if ENABLE(FILTERS)
#include "FilterEffect.h"
#include "Filter.h"
@@ -37,12 +36,13 @@ enum CompositeOperationType {
FECOMPOSITE_OPERATOR_OUT = 3,
FECOMPOSITE_OPERATOR_ATOP = 4,
FECOMPOSITE_OPERATOR_XOR = 5,
- FECOMPOSITE_OPERATOR_ARITHMETIC = 6
+ FECOMPOSITE_OPERATOR_ARITHMETIC = 6,
+ FECOMPOSITE_OPERATOR_LIGHTER = 7
};
class FEComposite : public FilterEffect {
public:
- static PassRefPtr<FEComposite> create(Filter*, const CompositeOperationType&, float, float, float, float);
+ static Ref<FEComposite> create(Filter&, const CompositeOperationType&, float, float, float, float);
CompositeOperationType operation() const;
bool setOperation(CompositeOperationType);
@@ -59,20 +59,20 @@ public:
float k4() const;
bool setK4(float);
- virtual void correctFilterResultIfNeeded() override;
+ void correctFilterResultIfNeeded() override;
- virtual void platformApplySoftware();
- virtual void dump();
+ void platformApplySoftware() override;
+ void dump() override;
- virtual void determineAbsolutePaintRect();
+ void determineAbsolutePaintRect() override;
- virtual TextStream& externalRepresentation(TextStream&, int indention) const;
+ TextStream& externalRepresentation(TextStream&, int indention) const override;
protected:
- virtual bool requiresValidPreMultipliedPixels() override { return m_type != FECOMPOSITE_OPERATOR_ARITHMETIC; }
+ bool requiresValidPreMultipliedPixels() override { return m_type != FECOMPOSITE_OPERATOR_ARITHMETIC; }
private:
- FEComposite(Filter*, const CompositeOperationType&, float, float, float, float);
+ FEComposite(Filter&, const CompositeOperationType&, float, float, float, float);
inline void platformArithmeticSoftware(Uint8ClampedArray* source, Uint8ClampedArray* destination,
float k1, float k2, float k3, float k4);
@@ -91,6 +91,4 @@ private:
} // namespace WebCore
-#endif // ENABLE(FILTERS)
-
#endif // FEComposite_h
diff --git a/Source/WebCore/platform/graphics/filters/FEConvolveMatrix.cpp b/Source/WebCore/platform/graphics/filters/FEConvolveMatrix.cpp
index 297ce733c..7d4800d65 100644
--- a/Source/WebCore/platform/graphics/filters/FEConvolveMatrix.cpp
+++ b/Source/WebCore/platform/graphics/filters/FEConvolveMatrix.cpp
@@ -22,8 +22,6 @@
*/
#include "config.h"
-
-#if ENABLE(FILTERS)
#include "FEConvolveMatrix.h"
#include "Filter.h"
@@ -31,10 +29,11 @@
#include <runtime/Uint8ClampedArray.h>
#include <wtf/ParallelJobs.h>
+#include <wtf/WorkQueue.h>
namespace WebCore {
-FEConvolveMatrix::FEConvolveMatrix(Filter* filter, const IntSize& kernelSize,
+FEConvolveMatrix::FEConvolveMatrix(Filter& filter, const IntSize& kernelSize,
float divisor, float bias, const IntPoint& targetOffset, EdgeModeType edgeMode,
const FloatPoint& kernelUnitLength, bool preserveAlpha, const Vector<float>& kernelMatrix)
: FilterEffect(filter)
@@ -51,11 +50,11 @@ FEConvolveMatrix::FEConvolveMatrix(Filter* filter, const IntSize& kernelSize,
ASSERT(m_kernelSize.height() > 0);
}
-PassRefPtr<FEConvolveMatrix> FEConvolveMatrix::create(Filter* filter, const IntSize& kernelSize,
+Ref<FEConvolveMatrix> FEConvolveMatrix::create(Filter& filter, const IntSize& kernelSize,
float divisor, float bias, const IntPoint& targetOffset, EdgeModeType edgeMode,
const FloatPoint& kernelUnitLength, bool preserveAlpha, const Vector<float>& kernelMatrix)
{
- return adoptRef(new FEConvolveMatrix(filter, kernelSize, divisor, bias, targetOffset, edgeMode, kernelUnitLength,
+ return adoptRef(*new FEConvolveMatrix(filter, kernelSize, divisor, bias, targetOffset, edgeMode, kernelUnitLength,
preserveAlpha, kernelMatrix));
}
@@ -242,7 +241,7 @@ ALWAYS_INLINE void setDestinationPixels(Uint8ClampedArray* image, int& pixel, fl
image->set(pixel++, maxAlpha);
}
-#if defined(_MSC_VER) && (_MSC_VER >= 1700)
+#if COMPILER(MSVC)
// Incorrectly diagnosing overwrite of stack in |totals| due to |preserveAlphaValues|.
#pragma warning(push)
#pragma warning(disable: 4789)
@@ -268,7 +267,7 @@ ALWAYS_INLINE void FEConvolveMatrix::fastSetInteriorPixels(PaintingData& paintin
for (int y = yEnd + 1; y > yStart; --y) {
for (int x = clipRight + 1; x > 0; --x) {
- int kernelValue = m_kernelMatrix.size() - 1;
+ int kernelValue = paintingData.kernelMatrix.size() - 1;
int kernelPixel = startKernelPixel;
int width = m_kernelSize.width();
@@ -279,11 +278,11 @@ ALWAYS_INLINE void FEConvolveMatrix::fastSetInteriorPixels(PaintingData& paintin
totals[3] = 0;
while (kernelValue >= 0) {
- totals[0] += m_kernelMatrix[kernelValue] * static_cast<float>(paintingData.srcPixelArray->item(kernelPixel++));
- totals[1] += m_kernelMatrix[kernelValue] * static_cast<float>(paintingData.srcPixelArray->item(kernelPixel++));
- totals[2] += m_kernelMatrix[kernelValue] * static_cast<float>(paintingData.srcPixelArray->item(kernelPixel++));
+ totals[0] += paintingData.kernelMatrix[kernelValue] * static_cast<float>(paintingData.srcPixelArray->item(kernelPixel++));
+ totals[1] += paintingData.kernelMatrix[kernelValue] * static_cast<float>(paintingData.srcPixelArray->item(kernelPixel++));
+ totals[2] += paintingData.kernelMatrix[kernelValue] * static_cast<float>(paintingData.srcPixelArray->item(kernelPixel++));
if (!preserveAlphaValues)
- totals[3] += m_kernelMatrix[kernelValue] * static_cast<float>(paintingData.srcPixelArray->item(kernelPixel));
+ totals[3] += paintingData.kernelMatrix[kernelValue] * static_cast<float>(paintingData.srcPixelArray->item(kernelPixel));
++kernelPixel;
--kernelValue;
if (!--width) {
@@ -348,7 +347,7 @@ void FEConvolveMatrix::fastSetOuterPixels(PaintingData& paintingData, int x1, in
for (int y = height; y > 0; --y) {
for (int x = width; x > 0; --x) {
- int kernelValue = m_kernelMatrix.size() - 1;
+ int kernelValue = paintingData.kernelMatrix.size() - 1;
int kernelPixelX = startKernelPixelX;
int kernelPixelY = startKernelPixelY;
int width = m_kernelSize.width();
@@ -362,12 +361,12 @@ void FEConvolveMatrix::fastSetOuterPixels(PaintingData& paintingData, int x1, in
while (kernelValue >= 0) {
int pixelIndex = getPixelValue(paintingData, kernelPixelX, kernelPixelY);
if (pixelIndex >= 0) {
- totals[0] += m_kernelMatrix[kernelValue] * static_cast<float>(paintingData.srcPixelArray->item(pixelIndex));
- totals[1] += m_kernelMatrix[kernelValue] * static_cast<float>(paintingData.srcPixelArray->item(pixelIndex + 1));
- totals[2] += m_kernelMatrix[kernelValue] * static_cast<float>(paintingData.srcPixelArray->item(pixelIndex + 2));
+ totals[0] += paintingData.kernelMatrix[kernelValue] * static_cast<float>(paintingData.srcPixelArray->item(pixelIndex));
+ totals[1] += paintingData.kernelMatrix[kernelValue] * static_cast<float>(paintingData.srcPixelArray->item(pixelIndex + 1));
+ totals[2] += paintingData.kernelMatrix[kernelValue] * static_cast<float>(paintingData.srcPixelArray->item(pixelIndex + 2));
}
if (!preserveAlphaValues && pixelIndex >= 0)
- totals[3] += m_kernelMatrix[kernelValue] * static_cast<float>(paintingData.srcPixelArray->item(pixelIndex + 3));
+ totals[3] += paintingData.kernelMatrix[kernelValue] * static_cast<float>(paintingData.srcPixelArray->item(pixelIndex + 3));
++kernelPixelX;
--kernelValue;
if (!--width) {
@@ -386,7 +385,7 @@ void FEConvolveMatrix::fastSetOuterPixels(PaintingData& paintingData, int x1, in
}
}
-#if defined(_MSC_VER) && (_MSC_VER >= 1700)
+#if COMPILER(MSVC)
#pragma warning(pop) // Disable of 4789
#endif
@@ -410,11 +409,6 @@ ALWAYS_INLINE void FEConvolveMatrix::setOuterPixels(PaintingData& paintingData,
fastSetOuterPixels<false>(paintingData, x1, y1, x2, y2);
}
-void FEConvolveMatrix::setInteriorPixelsWorker(InteriorPixelParameters* param)
-{
- param->filter->setInteriorPixels(*param->paintingData, param->clipRight, param->clipBottom, param->yStart, param->yEnd);
-}
-
void FEConvolveMatrix::platformApplySoftware()
{
FilterEffect* in = inputEffect(0);
@@ -442,55 +436,41 @@ void FEConvolveMatrix::platformApplySoftware()
paintingData.width = paintSize.width();
paintingData.height = paintSize.height();
paintingData.bias = m_bias * 255;
+ paintingData.kernelMatrix = normalizedFloats(m_kernelMatrix);
// Drawing fully covered pixels
int clipRight = paintSize.width() - m_kernelSize.width();
int clipBottom = paintSize.height() - m_kernelSize.height();
- if (clipRight >= 0 && clipBottom >= 0) {
-
- int optimalThreadNumber = (absolutePaintRect().width() * absolutePaintRect().height()) / s_minimalRectDimension;
- if (optimalThreadNumber > 1) {
- WTF::ParallelJobs<InteriorPixelParameters> parallelJobs(&WebCore::FEConvolveMatrix::setInteriorPixelsWorker, optimalThreadNumber);
- const int numOfThreads = parallelJobs.numberOfJobs();
-
- // Split the job into "heightPerThread" jobs but there a few jobs that need to be slightly larger since
- // heightPerThread * jobs < total size. These extras are handled by the remainder "jobsWithExtra".
- const int heightPerThread = clipBottom / numOfThreads;
- const int jobsWithExtra = clipBottom % numOfThreads;
-
- int startY = 0;
- for (int job = 0; job < numOfThreads; ++job) {
- InteriorPixelParameters& param = parallelJobs.parameter(job);
- param.filter = this;
- param.paintingData = &paintingData;
- param.clipRight = clipRight;
- param.clipBottom = clipBottom;
- param.yStart = startY;
- startY += job < jobsWithExtra ? heightPerThread + 1 : heightPerThread;
- param.yEnd = startY;
- }
-
- parallelJobs.execute();
- } else {
- // Fallback to single threaded mode.
- setInteriorPixels(paintingData, clipRight, clipBottom, 0, clipBottom);
- }
-
- clipRight += m_targetOffset.x() + 1;
- clipBottom += m_targetOffset.y() + 1;
- if (m_targetOffset.y() > 0)
- setOuterPixels(paintingData, 0, 0, paintSize.width(), m_targetOffset.y());
- if (clipBottom < paintSize.height())
- setOuterPixels(paintingData, 0, clipBottom, paintSize.width(), paintSize.height());
- if (m_targetOffset.x() > 0)
- setOuterPixels(paintingData, 0, m_targetOffset.y(), m_targetOffset.x(), clipBottom);
- if (clipRight < paintSize.width())
- setOuterPixels(paintingData, clipRight, m_targetOffset.y(), paintSize.width(), clipBottom);
- } else {
+ if (clipRight < 0 || clipBottom < 0) {
// Rare situation, not optimizied for speed
setOuterPixels(paintingData, 0, 0, paintSize.width(), paintSize.height());
+ return;
}
+
+ if (int iterations = (absolutePaintRect().width() * absolutePaintRect().height()) / s_minimalRectDimension) {
+ int stride = clipBottom / iterations;
+ int chunkCount = (clipBottom + stride - 1) / stride;
+
+ WorkQueue::concurrentApply(chunkCount, [&](size_t index) {
+ int yStart = (stride * index);
+ int yEnd = std::min<int>(stride * (index + 1), clipBottom);
+
+ setInteriorPixels(paintingData, clipRight, clipBottom, yStart, yEnd);
+ });
+ } else
+ setInteriorPixels(paintingData, clipRight, clipBottom, 0, clipBottom);
+
+ clipRight += m_targetOffset.x() + 1;
+ clipBottom += m_targetOffset.y() + 1;
+ if (m_targetOffset.y() > 0)
+ setOuterPixels(paintingData, 0, 0, paintSize.width(), m_targetOffset.y());
+ if (clipBottom < paintSize.height())
+ setOuterPixels(paintingData, 0, clipBottom, paintSize.width(), paintSize.height());
+ if (m_targetOffset.x() > 0)
+ setOuterPixels(paintingData, 0, m_targetOffset.y(), m_targetOffset.x(), clipBottom);
+ if (clipRight < paintSize.width())
+ setOuterPixels(paintingData, clipRight, m_targetOffset.y(), paintSize.width(), clipBottom);
}
void FEConvolveMatrix::dump()
@@ -534,5 +514,3 @@ TextStream& FEConvolveMatrix::externalRepresentation(TextStream& ts, int indent)
}
}; // namespace WebCore
-
-#endif // ENABLE(FILTERS)
diff --git a/Source/WebCore/platform/graphics/filters/FEConvolveMatrix.h b/Source/WebCore/platform/graphics/filters/FEConvolveMatrix.h
index 846e24497..8518c97cc 100644
--- a/Source/WebCore/platform/graphics/filters/FEConvolveMatrix.h
+++ b/Source/WebCore/platform/graphics/filters/FEConvolveMatrix.h
@@ -23,7 +23,6 @@
#ifndef FEConvolveMatrix_h
#define FEConvolveMatrix_h
-#if ENABLE(FILTERS)
#include "FilterEffect.h"
#include "FloatPoint.h"
#include "FloatSize.h"
@@ -41,7 +40,7 @@ enum EdgeModeType {
class FEConvolveMatrix : public FilterEffect {
public:
- static PassRefPtr<FEConvolveMatrix> create(Filter*, const IntSize&,
+ static Ref<FEConvolveMatrix> create(Filter&, const IntSize&,
float, float, const IntPoint&, EdgeModeType, const FloatPoint&,
bool, const Vector<float>&);
@@ -69,12 +68,12 @@ public:
bool preserveAlpha() const;
bool setPreserveAlpha(bool);
- virtual void platformApplySoftware();
- virtual void dump();
+ void platformApplySoftware() override;
+ void dump() override;
- virtual void determineAbsolutePaintRect() { setAbsolutePaintRect(enclosingIntRect(maxEffectRect())); }
+ void determineAbsolutePaintRect() override { setAbsolutePaintRect(enclosingIntRect(maxEffectRect())); }
- virtual TextStream& externalRepresentation(TextStream&, int indention) const;
+ TextStream& externalRepresentation(TextStream&, int indention) const override;
private:
@@ -84,9 +83,10 @@ private:
int width;
int height;
float bias;
+ Vector<float> kernelMatrix;
};
- FEConvolveMatrix(Filter*, const IntSize&, float, float,
+ FEConvolveMatrix(Filter&, const IntSize&, float, float,
const IntPoint&, EdgeModeType, const FloatPoint&, bool, const Vector<float>&);
template<bool preserveAlphaValues>
@@ -104,20 +104,6 @@ private:
// Parallelization parts
static const int s_minimalRectDimension = (100 * 100); // Empirical data limit for parallel jobs
- template<typename Type>
- friend class ParallelJobs;
-
- struct InteriorPixelParameters {
- FEConvolveMatrix* filter;
- PaintingData* paintingData;
- int clipBottom;
- int clipRight;
- int yStart;
- int yEnd;
- };
-
- static void setInteriorPixelsWorker(InteriorPixelParameters*);
-
IntSize m_kernelSize;
float m_divisor;
float m_bias;
@@ -130,6 +116,4 @@ private:
} // namespace WebCore
-#endif // ENABLE(FILTERS)
-
#endif // FEConvolveMatrix_h
diff --git a/Source/WebCore/platform/graphics/filters/FEDiffuseLighting.cpp b/Source/WebCore/platform/graphics/filters/FEDiffuseLighting.cpp
index dadc2962a..1b4303c98 100644
--- a/Source/WebCore/platform/graphics/filters/FEDiffuseLighting.cpp
+++ b/Source/WebCore/platform/graphics/filters/FEDiffuseLighting.cpp
@@ -20,8 +20,6 @@
*/
#include "config.h"
-
-#if ENABLE(FILTERS)
#include "FEDiffuseLighting.h"
#include "LightSource.h"
@@ -29,24 +27,24 @@
namespace WebCore {
-FEDiffuseLighting::FEDiffuseLighting(Filter* filter, const Color& lightingColor, float surfaceScale,
+FEDiffuseLighting::FEDiffuseLighting(Filter& filter, const Color& lightingColor, float surfaceScale,
float diffuseConstant, float kernelUnitLengthX, float kernelUnitLengthY, PassRefPtr<LightSource> lightSource)
: FELighting(filter, DiffuseLighting, lightingColor, surfaceScale, diffuseConstant, 0, 0, kernelUnitLengthX, kernelUnitLengthY, lightSource)
{
}
-PassRefPtr<FEDiffuseLighting> FEDiffuseLighting::create(Filter* filter, const Color& lightingColor,
+Ref<FEDiffuseLighting> FEDiffuseLighting::create(Filter& filter, const Color& lightingColor,
float surfaceScale, float diffuseConstant, float kernelUnitLengthX,
float kernelUnitLengthY, PassRefPtr<LightSource> lightSource)
{
- return adoptRef(new FEDiffuseLighting(filter, lightingColor, surfaceScale, diffuseConstant, kernelUnitLengthX, kernelUnitLengthY, lightSource));
+ return adoptRef(*new FEDiffuseLighting(filter, lightingColor, surfaceScale, diffuseConstant, kernelUnitLengthX, kernelUnitLengthY, lightSource));
}
FEDiffuseLighting::~FEDiffuseLighting()
{
}
-Color FEDiffuseLighting::lightingColor() const
+const Color& FEDiffuseLighting::lightingColor() const
{
return m_lightingColor;
}
@@ -138,5 +136,3 @@ TextStream& FEDiffuseLighting::externalRepresentation(TextStream& ts, int indent
}
} // namespace WebCore
-
-#endif // ENABLE(FILTERS)
diff --git a/Source/WebCore/platform/graphics/filters/FEDiffuseLighting.h b/Source/WebCore/platform/graphics/filters/FEDiffuseLighting.h
index 5f2065163..5fe2be9f4 100644
--- a/Source/WebCore/platform/graphics/filters/FEDiffuseLighting.h
+++ b/Source/WebCore/platform/graphics/filters/FEDiffuseLighting.h
@@ -22,7 +22,6 @@
#ifndef FEDiffuseLighting_h
#define FEDiffuseLighting_h
-#if ENABLE(FILTERS)
#include "FELighting.h"
namespace WebCore {
@@ -31,11 +30,11 @@ class LightSource;
class FEDiffuseLighting : public FELighting {
public:
- static PassRefPtr<FEDiffuseLighting> create(Filter*, const Color&, float, float,
+ static Ref<FEDiffuseLighting> create(Filter&, const Color&, float, float,
float, float, PassRefPtr<LightSource>);
virtual ~FEDiffuseLighting();
- Color lightingColor() const;
+ const Color& lightingColor() const;
bool setLightingColor(const Color&);
float surfaceScale() const;
@@ -53,16 +52,14 @@ public:
const LightSource* lightSource() const;
void setLightSource(PassRefPtr<LightSource>);
- virtual void dump();
+ void dump() override;
- virtual TextStream& externalRepresentation(TextStream&, int indention) const;
+ TextStream& externalRepresentation(TextStream&, int indention) const override;
private:
- FEDiffuseLighting(Filter*, const Color&, float, float, float, float, PassRefPtr<LightSource>);
+ FEDiffuseLighting(Filter&, const Color&, float, float, float, float, PassRefPtr<LightSource>);
};
} // namespace WebCore
-#endif // ENABLE(FILTERS)
-
#endif // FEDiffuseLighting_h
diff --git a/Source/WebCore/platform/graphics/filters/FEDisplacementMap.cpp b/Source/WebCore/platform/graphics/filters/FEDisplacementMap.cpp
index dfb9194da..51eb774d4 100644
--- a/Source/WebCore/platform/graphics/filters/FEDisplacementMap.cpp
+++ b/Source/WebCore/platform/graphics/filters/FEDisplacementMap.cpp
@@ -22,8 +22,6 @@
*/
#include "config.h"
-
-#if ENABLE(FILTERS)
#include "FEDisplacementMap.h"
#include "Filter.h"
@@ -34,7 +32,7 @@
namespace WebCore {
-FEDisplacementMap::FEDisplacementMap(Filter* filter, ChannelSelectorType xChannelSelector, ChannelSelectorType yChannelSelector, float scale)
+FEDisplacementMap::FEDisplacementMap(Filter& filter, ChannelSelectorType xChannelSelector, ChannelSelectorType yChannelSelector, float scale)
: FilterEffect(filter)
, m_xChannelSelector(xChannelSelector)
, m_yChannelSelector(yChannelSelector)
@@ -42,10 +40,10 @@ FEDisplacementMap::FEDisplacementMap(Filter* filter, ChannelSelectorType xChanne
{
}
-PassRefPtr<FEDisplacementMap> FEDisplacementMap::create(Filter* filter, ChannelSelectorType xChannelSelector,
+Ref<FEDisplacementMap> FEDisplacementMap::create(Filter& filter, ChannelSelectorType xChannelSelector,
ChannelSelectorType yChannelSelector, float scale)
{
- return adoptRef(new FEDisplacementMap(filter, xChannelSelector, yChannelSelector, scale));
+ return adoptRef(*new FEDisplacementMap(filter, xChannelSelector, yChannelSelector, scale));
}
ChannelSelectorType FEDisplacementMap::xChannelSelector() const
@@ -123,10 +121,10 @@ void FEDisplacementMap::platformApplySoftware()
ASSERT(srcPixelArrayA->length() == srcPixelArrayB->length());
- Filter* filter = this->filter();
+ Filter& filter = this->filter();
IntSize paintSize = absolutePaintRect().size();
- float scaleX = filter->applyHorizontalScale(m_scale);
- float scaleY = filter->applyVerticalScale(m_scale);
+ float scaleX = filter.applyHorizontalScale(m_scale);
+ float scaleY = filter.applyVerticalScale(m_scale);
float scaleForColorX = scaleX / 255.0;
float scaleForColorY = scaleY / 255.0;
float scaledOffsetX = 0.5 - scaleX * 0.5;
@@ -190,5 +188,3 @@ TextStream& FEDisplacementMap::externalRepresentation(TextStream& ts, int indent
}
} // namespace WebCore
-
-#endif // ENABLE(FILTERS)
diff --git a/Source/WebCore/platform/graphics/filters/FEDisplacementMap.h b/Source/WebCore/platform/graphics/filters/FEDisplacementMap.h
index b0ea3d006..f9afbae1a 100644
--- a/Source/WebCore/platform/graphics/filters/FEDisplacementMap.h
+++ b/Source/WebCore/platform/graphics/filters/FEDisplacementMap.h
@@ -22,7 +22,6 @@
#ifndef FEDisplacementMap_h
#define FEDisplacementMap_h
-#if ENABLE(FILTERS)
#include "FilterEffect.h"
#include "Filter.h"
#include <wtf/text/WTFString.h>
@@ -39,7 +38,7 @@ enum ChannelSelectorType {
class FEDisplacementMap : public FilterEffect {
public:
- static PassRefPtr<FEDisplacementMap> create(Filter*, ChannelSelectorType xChannelSelector, ChannelSelectorType yChannelSelector, float);
+ static Ref<FEDisplacementMap> create(Filter&, ChannelSelectorType xChannelSelector, ChannelSelectorType yChannelSelector, float);
ChannelSelectorType xChannelSelector() const;
bool setXChannelSelector(const ChannelSelectorType);
@@ -51,17 +50,17 @@ public:
bool setScale(float);
void setResultColorSpace(ColorSpace) override;
- virtual void transformResultColorSpace(FilterEffect*, const int) override;
+ void transformResultColorSpace(FilterEffect*, const int) override;
- virtual void platformApplySoftware();
- virtual void dump();
+ void platformApplySoftware() override;
+ void dump() override;
- virtual void determineAbsolutePaintRect() { setAbsolutePaintRect(enclosingIntRect(maxEffectRect())); }
+ void determineAbsolutePaintRect() override { setAbsolutePaintRect(enclosingIntRect(maxEffectRect())); }
- virtual TextStream& externalRepresentation(TextStream&, int indention) const;
+ TextStream& externalRepresentation(TextStream&, int indention) const override;
private:
- FEDisplacementMap(Filter*, ChannelSelectorType xChannelSelector, ChannelSelectorType yChannelSelector, float);
+ FEDisplacementMap(Filter&, ChannelSelectorType xChannelSelector, ChannelSelectorType yChannelSelector, float);
ChannelSelectorType m_xChannelSelector;
ChannelSelectorType m_yChannelSelector;
@@ -70,6 +69,4 @@ private:
} // namespace WebCore
-#endif // ENABLE(FILTERS)
-
#endif // FEDisplacementMap_h
diff --git a/Source/WebCore/platform/graphics/filters/FEDropShadow.cpp b/Source/WebCore/platform/graphics/filters/FEDropShadow.cpp
index bdc94d2d8..fa0be80df 100644
--- a/Source/WebCore/platform/graphics/filters/FEDropShadow.cpp
+++ b/Source/WebCore/platform/graphics/filters/FEDropShadow.cpp
@@ -18,11 +18,8 @@
*/
#include "config.h"
-
-#if ENABLE(FILTERS)
#include "FEDropShadow.h"
-#include "ColorSpace.h"
#include "FEGaussianBlur.h"
#include "Filter.h"
#include "GraphicsContext.h"
@@ -33,7 +30,7 @@
namespace WebCore {
-FEDropShadow::FEDropShadow(Filter* filter, float stdX, float stdY, float dx, float dy, const Color& shadowColor, float shadowOpacity)
+FEDropShadow::FEDropShadow(Filter& filter, float stdX, float stdY, float dx, float dy, const Color& shadowColor, float shadowOpacity)
: FilterEffect(filter)
, m_stdX(stdX)
, m_stdY(stdY)
@@ -44,28 +41,25 @@ FEDropShadow::FEDropShadow(Filter* filter, float stdX, float stdY, float dx, flo
{
}
-PassRefPtr<FEDropShadow> FEDropShadow::create(Filter* filter, float stdX, float stdY, float dx, float dy, const Color& shadowColor, float shadowOpacity)
+Ref<FEDropShadow> FEDropShadow::create(Filter& filter, float stdX, float stdY, float dx, float dy, const Color& shadowColor, float shadowOpacity)
{
- return adoptRef(new FEDropShadow(filter, stdX, stdY, dx, dy, shadowColor, shadowOpacity));
+ return adoptRef(*new FEDropShadow(filter, stdX, stdY, dx, dy, shadowColor, shadowOpacity));
}
void FEDropShadow::determineAbsolutePaintRect()
{
- Filter* filter = this->filter();
- ASSERT(filter);
+ Filter& filter = this->filter();
FloatRect absolutePaintRect = inputEffect(0)->absolutePaintRect();
FloatRect absoluteOffsetPaintRect(absolutePaintRect);
- absoluteOffsetPaintRect.move(filter->applyHorizontalScale(m_dx), filter->applyVerticalScale(m_dy));
+ absoluteOffsetPaintRect.move(filter.applyHorizontalScale(m_dx), filter.applyVerticalScale(m_dy));
absolutePaintRect.unite(absoluteOffsetPaintRect);
-
- unsigned kernelSizeX = 0;
- unsigned kernelSizeY = 0;
- FEGaussianBlur::calculateKernelSize(filter, kernelSizeX, kernelSizeY, m_stdX, m_stdY);
-
+
+ IntSize kernelSize = FEGaussianBlur::calculateKernelSize(filter, FloatPoint(m_stdX, m_stdY));
+
// We take the half kernel size and multiply it with three, because we run box blur three times.
- absolutePaintRect.inflateX(3 * kernelSizeX * 0.5f);
- absolutePaintRect.inflateY(3 * kernelSizeY * 0.5f);
+ absolutePaintRect.inflateX(3 * kernelSize.width() * 0.5f);
+ absolutePaintRect.inflateY(3 * kernelSize.height() * 0.5f);
if (clipsToBounds())
absolutePaintRect.intersect(maxEffectRect());
@@ -83,37 +77,39 @@ void FEDropShadow::platformApplySoftware()
if (!resultImage)
return;
- Filter* filter = this->filter();
- FloatSize blurRadius(filter->applyHorizontalScale(m_stdX), filter->applyVerticalScale(m_stdY));
- FloatSize offset(filter->applyHorizontalScale(m_dx), filter->applyVerticalScale(m_dy));
+ Filter& filter = this->filter();
+ FloatSize blurRadius(2 * filter.applyHorizontalScale(m_stdX), 2 * filter.applyVerticalScale(m_stdY));
+ blurRadius.scale(filter.filterScale());
+ FloatSize offset(filter.applyHorizontalScale(m_dx), filter.applyVerticalScale(m_dy));
FloatRect drawingRegion = drawingRegionOfInputImage(in->absolutePaintRect());
FloatRect drawingRegionWithOffset(drawingRegion);
drawingRegionWithOffset.move(offset);
ImageBuffer* sourceImage = in->asImageBuffer();
- ASSERT(sourceImage);
- GraphicsContext* resultContext = resultImage->context();
- ASSERT(resultContext);
- resultContext->setAlpha(m_shadowOpacity);
- resultContext->drawImageBuffer(sourceImage, ColorSpaceDeviceRGB, drawingRegionWithOffset);
- resultContext->setAlpha(1);
+ if (!sourceImage)
+ return;
- ShadowBlur contextShadow(blurRadius, offset, m_shadowColor, ColorSpaceDeviceRGB);
+ GraphicsContext& resultContext = resultImage->context();
+ resultContext.setAlpha(m_shadowOpacity);
+ resultContext.drawImageBuffer(*sourceImage, drawingRegionWithOffset);
+ resultContext.setAlpha(1);
+
+ ShadowBlur contextShadow(blurRadius, offset, m_shadowColor);
// TODO: Direct pixel access to ImageBuffer would avoid copying the ImageData.
IntRect shadowArea(IntPoint(), resultImage->internalSize());
- RefPtr<Uint8ClampedArray> srcPixelArray = resultImage->getPremultipliedImageData(shadowArea);
+ RefPtr<Uint8ClampedArray> srcPixelArray = resultImage->getPremultipliedImageData(shadowArea, ImageBuffer::BackingStoreCoordinateSystem);
contextShadow.blurLayerImage(srcPixelArray->data(), shadowArea.size(), 4 * shadowArea.size().width());
- resultImage->putByteArray(Premultiplied, srcPixelArray.get(), shadowArea.size(), shadowArea, IntPoint());
+ resultImage->putByteArray(Premultiplied, srcPixelArray.get(), shadowArea.size(), shadowArea, IntPoint(), ImageBuffer::BackingStoreCoordinateSystem);
- resultContext->setCompositeOperation(CompositeSourceIn);
- resultContext->fillRect(FloatRect(FloatPoint(), absolutePaintRect().size()), m_shadowColor, ColorSpaceDeviceRGB);
- resultContext->setCompositeOperation(CompositeDestinationOver);
+ resultContext.setCompositeOperation(CompositeSourceIn);
+ resultContext.fillRect(FloatRect(FloatPoint(), absolutePaintRect().size()), m_shadowColor);
+ resultContext.setCompositeOperation(CompositeDestinationOver);
- resultImage->context()->drawImageBuffer(sourceImage, ColorSpaceDeviceRGB, drawingRegion);
+ resultImage->context().drawImageBuffer(*sourceImage, drawingRegion);
}
void FEDropShadow::dump()
@@ -131,5 +127,3 @@ TextStream& FEDropShadow::externalRepresentation(TextStream& ts, int indent) con
}
} // namespace WebCore
-
-#endif // ENABLE(FILTERS)
diff --git a/Source/WebCore/platform/graphics/filters/FEDropShadow.h b/Source/WebCore/platform/graphics/filters/FEDropShadow.h
index d155b4638..36c9d74b5 100644
--- a/Source/WebCore/platform/graphics/filters/FEDropShadow.h
+++ b/Source/WebCore/platform/graphics/filters/FEDropShadow.h
@@ -20,7 +20,6 @@
#ifndef FEDropShadow_h
#define FEDropShadow_h
-#if ENABLE(FILTERS)
#include "Color.h"
#include "Filter.h"
#include "FilterEffect.h"
@@ -29,7 +28,7 @@ namespace WebCore {
class FEDropShadow : public FilterEffect {
public:
- static PassRefPtr<FEDropShadow> create(Filter*, float, float, float, float, const Color&, float);
+ static Ref<FEDropShadow> create(Filter&, float, float, float, float, const Color&, float);
float stdDeviationX() const { return m_stdX; }
void setStdDeviationX(float stdX) { m_stdX = stdX; }
@@ -43,23 +42,21 @@ public:
float dy() const { return m_dy; }
void setDy(float dy) { m_dy = dy; }
- Color shadowColor() const { return m_shadowColor; }
+ const Color& shadowColor() const { return m_shadowColor; }
void setShadowColor(const Color& shadowColor) { m_shadowColor = shadowColor; }
float shadowOpacity() const { return m_shadowOpacity; }
void setShadowOpacity(float shadowOpacity) { m_shadowOpacity = shadowOpacity; }
- static float calculateStdDeviation(float);
+ void platformApplySoftware() override;
+ void dump() override;
- virtual void platformApplySoftware();
- virtual void dump();
+ void determineAbsolutePaintRect() override;
- virtual void determineAbsolutePaintRect();
-
- virtual TextStream& externalRepresentation(TextStream&, int indention) const;
+ TextStream& externalRepresentation(TextStream&, int indention) const override;
private:
- FEDropShadow(Filter*, float, float, float, float, const Color&, float);
+ FEDropShadow(Filter&, float, float, float, float, const Color&, float);
float m_stdX;
float m_stdY;
@@ -71,5 +68,4 @@ private:
} // namespace WebCore
-#endif // ENABLE(FILTERS)
#endif // FEDropShadow_h
diff --git a/Source/WebCore/platform/graphics/filters/FEFlood.cpp b/Source/WebCore/platform/graphics/filters/FEFlood.cpp
index 35bfb839e..f6c408631 100644
--- a/Source/WebCore/platform/graphics/filters/FEFlood.cpp
+++ b/Source/WebCore/platform/graphics/filters/FEFlood.cpp
@@ -21,8 +21,6 @@
*/
#include "config.h"
-
-#if ENABLE(FILTERS)
#include "FEFlood.h"
#include "Filter.h"
@@ -31,19 +29,19 @@
namespace WebCore {
-FEFlood::FEFlood(Filter* filter, const Color& floodColor, float floodOpacity)
+FEFlood::FEFlood(Filter& filter, const Color& floodColor, float floodOpacity)
: FilterEffect(filter)
, m_floodColor(floodColor)
, m_floodOpacity(floodOpacity)
{
}
-PassRefPtr<FEFlood> FEFlood::create(Filter* filter, const Color& floodColor, float floodOpacity)
+Ref<FEFlood> FEFlood::create(Filter& filter, const Color& floodColor, float floodOpacity)
{
- return adoptRef(new FEFlood(filter, floodColor, floodOpacity));
+ return adoptRef(*new FEFlood(filter, floodColor, floodOpacity));
}
-Color FEFlood::floodColor() const
+const Color& FEFlood::floodColor() const
{
return m_floodColor;
}
@@ -75,8 +73,8 @@ void FEFlood::platformApplySoftware()
if (!resultImage)
return;
- Color color = colorWithOverrideAlpha(floodColor().rgb(), floodOpacity());
- resultImage->context()->fillRect(FloatRect(FloatPoint(), absolutePaintRect().size()), color, ColorSpaceDeviceRGB);
+ const Color& color = colorWithOverrideAlpha(floodColor().rgb(), floodOpacity());
+ resultImage->context().fillRect(FloatRect(FloatPoint(), absolutePaintRect().size()), color);
}
void FEFlood::dump()
@@ -94,5 +92,3 @@ TextStream& FEFlood::externalRepresentation(TextStream& ts, int indent) const
}
} // namespace WebCore
-
-#endif // ENABLE(FILTERS)
diff --git a/Source/WebCore/platform/graphics/filters/FEFlood.h b/Source/WebCore/platform/graphics/filters/FEFlood.h
index ee93fcf55..ae03b1931 100644
--- a/Source/WebCore/platform/graphics/filters/FEFlood.h
+++ b/Source/WebCore/platform/graphics/filters/FEFlood.h
@@ -22,7 +22,6 @@
#ifndef FEFlood_h
#define FEFlood_h
-#if ENABLE(FILTERS)
#include "Color.h"
#include "Filter.h"
#include "FilterEffect.h"
@@ -31,10 +30,10 @@ namespace WebCore {
class FEFlood : public FilterEffect {
public:
- static PassRefPtr<FEFlood> create(Filter* filter, const Color&, float);
+ static Ref<FEFlood> create(Filter&, const Color&, float);
- Color floodColor() const;
- bool setFloodColor(const Color &);
+ const Color& floodColor() const;
+ bool setFloodColor(const Color&);
float floodOpacity() const;
bool setFloodOpacity(float);
@@ -42,22 +41,19 @@ public:
#if !USE(CG)
// feFlood does not perform color interpolation of any kind, so the result is always in the current
// color space regardless of the value of color-interpolation-filters.
- void setOperatingColorSpace(ColorSpace) override { FilterEffect::setResultColorSpace(ColorSpaceDeviceRGB); }
- void setResultColorSpace(ColorSpace) override { FilterEffect::setResultColorSpace(ColorSpaceDeviceRGB); }
+ void setOperatingColorSpace(ColorSpace) override { FilterEffect::setResultColorSpace(ColorSpaceSRGB); }
+ void setResultColorSpace(ColorSpace) override { FilterEffect::setResultColorSpace(ColorSpaceSRGB); }
#endif
- virtual void platformApplySoftware();
-#if ENABLE(OPENCL)
- virtual bool platformApplyOpenCL();
-#endif
- virtual void dump();
+ void platformApplySoftware() override;
+ void dump() override;
- virtual void determineAbsolutePaintRect() { setAbsolutePaintRect(enclosingIntRect(maxEffectRect())); }
+ void determineAbsolutePaintRect() override { setAbsolutePaintRect(enclosingIntRect(maxEffectRect())); }
- virtual TextStream& externalRepresentation(TextStream&, int indention) const;
+ TextStream& externalRepresentation(TextStream&, int indention) const override;
private:
- FEFlood(Filter*, const Color&, float);
+ FEFlood(Filter&, const Color&, float);
Color m_floodColor;
float m_floodOpacity;
@@ -65,6 +61,4 @@ private:
} // namespace WebCore
-#endif // ENABLE(FILTERS)
-
#endif // FEFlood_h
diff --git a/Source/WebCore/platform/graphics/filters/FEGaussianBlur.cpp b/Source/WebCore/platform/graphics/filters/FEGaussianBlur.cpp
index 5669ba12f..0b3b5caf7 100644
--- a/Source/WebCore/platform/graphics/filters/FEGaussianBlur.cpp
+++ b/Source/WebCore/platform/graphics/filters/FEGaussianBlur.cpp
@@ -5,6 +5,7 @@
* Copyright (C) 2009 Dirk Schulze <krit@webkit.org>
* Copyright (C) 2010 Igalia, S.L.
* Copyright (C) Research In Motion Limited 2010. All rights reserved.
+ * Copyright (C) 2015-2016 Apple, Inc. All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
@@ -23,8 +24,6 @@
*/
#include "config.h"
-
-#if ENABLE(FILTERS)
#include "FEGaussianBlur.h"
#include "FEGaussianBlurNEON.h"
@@ -32,7 +31,11 @@
#include "GraphicsContext.h"
#include "TextStream.h"
-#include <runtime/Operations.h>
+#if USE(ACCELERATE)
+#include <Accelerate/Accelerate.h>
+#endif
+
+#include <runtime/JSCInlines.h>
#include <runtime/TypedArrayInlines.h>
#include <runtime/Uint8ClampedArray.h>
#include <wtf/MathExtras.h>
@@ -43,11 +46,39 @@ static inline float gaussianKernelFactor()
return 3 / 4.f * sqrtf(2 * piFloat);
}
-static const unsigned gMaxKernelSize = 1000;
+static const int gMaxKernelSize = 500;
namespace WebCore {
-FEGaussianBlur::FEGaussianBlur(Filter* filter, float x, float y, EdgeModeType edgeMode)
+inline void kernelPosition(int blurIteration, unsigned& radius, int& deltaLeft, int& deltaRight)
+{
+ // Check http://www.w3.org/TR/SVG/filters.html#feGaussianBlurElement for details.
+ switch (blurIteration) {
+ case 0:
+ if (!(radius % 2)) {
+ deltaLeft = radius / 2 - 1;
+ deltaRight = radius - deltaLeft;
+ } else {
+ deltaLeft = radius / 2;
+ deltaRight = radius - deltaLeft;
+ }
+ break;
+ case 1:
+ if (!(radius % 2)) {
+ deltaLeft++;
+ deltaRight--;
+ }
+ break;
+ case 2:
+ if (!(radius % 2)) {
+ deltaRight++;
+ radius++;
+ }
+ break;
+ }
+}
+
+FEGaussianBlur::FEGaussianBlur(Filter& filter, float x, float y, EdgeModeType edgeMode)
: FilterEffect(filter)
, m_stdX(x)
, m_stdY(y)
@@ -55,9 +86,9 @@ FEGaussianBlur::FEGaussianBlur(Filter* filter, float x, float y, EdgeModeType ed
{
}
-PassRefPtr<FEGaussianBlur> FEGaussianBlur::create(Filter* filter, float x, float y, EdgeModeType edgeMode)
+Ref<FEGaussianBlur> FEGaussianBlur::create(Filter& filter, float x, float y, EdgeModeType edgeMode)
{
- return adoptRef(new FEGaussianBlur(filter, x, y, edgeMode));
+ return adoptRef(*new FEGaussianBlur(filter, x, y, edgeMode));
}
float FEGaussianBlur::stdDeviationX() const
@@ -90,86 +121,246 @@ void FEGaussianBlur::setEdgeMode(EdgeModeType edgeMode)
m_edgeMode = edgeMode;
}
-inline void boxBlur(Uint8ClampedArray* srcPixelArray, Uint8ClampedArray* dstPixelArray,
+// This function only operates on Alpha channel.
+inline void boxBlurAlphaOnly(const Uint8ClampedArray* srcPixelArray, Uint8ClampedArray* dstPixelArray,
+ unsigned dx, int& dxLeft, int& dxRight, int& stride, int& strideLine, int& effectWidth, int& effectHeight, const int& maxKernelSize)
+{
+ unsigned char* srcData = srcPixelArray->data();
+ unsigned char* dstData = dstPixelArray->data();
+ // Memory alignment is: RGBA, zero-index based.
+ const int channel = 3;
+
+ for (int y = 0; y < effectHeight; ++y) {
+ int line = y * strideLine;
+ int sum = 0;
+
+ // Fill the kernel.
+ for (int i = 0; i < maxKernelSize; ++i) {
+ unsigned offset = line + i * stride;
+ unsigned char* srcPtr = srcData + offset;
+ sum += srcPtr[channel];
+ }
+
+ // Blurring.
+ for (int x = 0; x < effectWidth; ++x) {
+ unsigned pixelByteOffset = line + x * stride + channel;
+ unsigned char* dstPtr = dstData + pixelByteOffset;
+ *dstPtr = static_cast<unsigned char>(sum / dx);
+
+ // Shift kernel.
+ if (x >= dxLeft) {
+ unsigned leftOffset = pixelByteOffset - dxLeft * stride;
+ unsigned char* srcPtr = srcData + leftOffset;
+ sum -= *srcPtr;
+ }
+
+ if (x + dxRight < effectWidth) {
+ unsigned rightOffset = pixelByteOffset + dxRight * stride;
+ unsigned char* srcPtr = srcData + rightOffset;
+ sum += *srcPtr;
+ }
+ }
+ }
+}
+
+inline void boxBlur(const Uint8ClampedArray* srcPixelArray, Uint8ClampedArray* dstPixelArray,
unsigned dx, int dxLeft, int dxRight, int stride, int strideLine, int effectWidth, int effectHeight, bool alphaImage, EdgeModeType edgeMode)
{
+ const int maxKernelSize = std::min(dxRight, effectWidth);
+ if (alphaImage) {
+ return boxBlurAlphaOnly(srcPixelArray, dstPixelArray, dx, dxLeft, dxRight, stride, strideLine,
+ effectWidth, effectHeight, maxKernelSize);
+ }
+
+ unsigned char* srcData = srcPixelArray->data();
+ unsigned char* dstData = dstPixelArray->data();
+
+ // Concerning the array width/length: it is Element size + Margin + Border. The number of pixels will be
+ // P = width * height * channels.
for (int y = 0; y < effectHeight; ++y) {
int line = y * strideLine;
- for (int channel = 3; channel >= 0; --channel) {
- int sum = 0;
- // The code for edgeMode='none' is the common case and highly optimized.
- // Furthermore, this code path affects more than just the input area.
- if (edgeMode == EDGEMODE_NONE) {
- // Fill the kernel
- int maxKernelSize = std::min(dxRight, effectWidth);
- for (int i = 0; i < maxKernelSize; ++i)
- sum += srcPixelArray->item(line + i * stride + channel);
-
- // Blurring
- for (int x = 0; x < effectWidth; ++x) {
- int pixelByteOffset = line + x * stride + channel;
- dstPixelArray->set(pixelByteOffset, static_cast<unsigned char>(sum / dx));
- // Shift kernel.
- if (x >= dxLeft)
- sum -= srcPixelArray->item(pixelByteOffset - dxLeft * stride);
- if (x + dxRight < effectWidth)
- sum += srcPixelArray->item(pixelByteOffset + dxRight * stride);
+ int sumR = 0, sumG = 0, sumB = 0, sumA = 0;
+
+ if (edgeMode == EDGEMODE_NONE) {
+ // Fill the kernel.
+ for (int i = 0; i < maxKernelSize; ++i) {
+ unsigned offset = line + i * stride;
+ unsigned char* srcPtr = srcData + offset;
+ sumR += *srcPtr++;
+ sumG += *srcPtr++;
+ sumB += *srcPtr++;
+ sumA += *srcPtr;
+ }
+
+ // Blurring.
+ for (int x = 0; x < effectWidth; ++x) {
+ unsigned pixelByteOffset = line + x * stride;
+ unsigned char* dstPtr = dstData + pixelByteOffset;
+
+ *dstPtr++ = static_cast<unsigned char>(sumR / dx);
+ *dstPtr++ = static_cast<unsigned char>(sumG / dx);
+ *dstPtr++ = static_cast<unsigned char>(sumB / dx);
+ *dstPtr = static_cast<unsigned char>(sumA / dx);
+
+ // Shift kernel.
+ if (x >= dxLeft) {
+ unsigned leftOffset = pixelByteOffset - dxLeft * stride;
+ unsigned char* srcPtr = srcData + leftOffset;
+ sumR -= srcPtr[0];
+ sumG -= srcPtr[1];
+ sumB -= srcPtr[2];
+ sumA -= srcPtr[3];
+ }
+
+ if (x + dxRight < effectWidth) {
+ unsigned rightOffset = pixelByteOffset + dxRight * stride;
+ unsigned char* srcPtr = srcData + rightOffset;
+ sumR += srcPtr[0];
+ sumG += srcPtr[1];
+ sumB += srcPtr[2];
+ sumA += srcPtr[3];
+ }
+ }
+
+ } else {
+ // FIXME: Add support for 'wrap' here.
+ // Get edge values for edgeMode 'duplicate'.
+ unsigned char* edgeValueLeft = srcData + line;
+ unsigned char* edgeValueRight = srcData + (line + (effectWidth - 1) * stride);
+
+ // Fill the kernel.
+ for (int i = dxLeft * -1; i < dxRight; ++i) {
+ // Is this right for negative values of 'i'?
+ unsigned offset = line + i * stride;
+ unsigned char* srcPtr = srcData + offset;
+
+ if (i < 0) {
+ sumR += edgeValueLeft[0];
+ sumG += edgeValueLeft[1];
+ sumB += edgeValueLeft[2];
+ sumA += edgeValueLeft[3];
+ } else if (i >= effectWidth) {
+ sumR += edgeValueRight[0];
+ sumG += edgeValueRight[1];
+ sumB += edgeValueRight[2];
+ sumA += edgeValueRight[3];
+ } else {
+ sumR += *srcPtr++;
+ sumG += *srcPtr++;
+ sumB += *srcPtr++;
+ sumA += *srcPtr;
}
- } else {
- // FIXME: Add support for 'wrap' here.
- // Get edge values for edgeMode 'duplicate'.
- int edgeValueLeft = srcPixelArray->item(line + channel);
- int edgeValueRight = srcPixelArray->item(line + (effectWidth - 1) * stride + channel);
- // Fill the kernel
- for (int i = dxLeft * -1; i < dxRight; ++i) {
- if (i < 0)
- sum += edgeValueLeft;
- else if (i >= effectWidth)
- sum += edgeValueRight;
- else
- sum += srcPixelArray->item(line + i * stride + channel);
+ }
+
+ // Blurring.
+ for (int x = 0; x < effectWidth; ++x) {
+ unsigned pixelByteOffset = line + x * stride;
+ unsigned char* dstPtr = dstData + pixelByteOffset;
+
+ *dstPtr++ = static_cast<unsigned char>(sumR / dx);
+ *dstPtr++ = static_cast<unsigned char>(sumG / dx);
+ *dstPtr++ = static_cast<unsigned char>(sumB / dx);
+ *dstPtr = static_cast<unsigned char>(sumA / dx);
+
+ // Shift kernel.
+ if (x < dxLeft) {
+ sumR -= edgeValueLeft[0];
+ sumG -= edgeValueLeft[1];
+ sumB -= edgeValueLeft[2];
+ sumA -= edgeValueLeft[3];
+ } else {
+ unsigned leftOffset = pixelByteOffset - dxLeft * stride;
+ unsigned char* srcPtr = srcData + leftOffset;
+ sumR -= srcPtr[0];
+ sumG -= srcPtr[1];
+ sumB -= srcPtr[2];
+ sumA -= srcPtr[3];
}
- // Blurring
- for (int x = 0; x < effectWidth; ++x) {
- int pixelByteOffset = line + x * stride + channel;
- dstPixelArray->set(pixelByteOffset, static_cast<unsigned char>(sum / dx));
- // Shift kernel.
- if (x < dxLeft)
- sum -= edgeValueLeft;
- else
- sum -= srcPixelArray->item(pixelByteOffset - dxLeft * stride);
- if (x + dxRight >= effectWidth)
- sum += edgeValueRight;
- else
- sum += srcPixelArray->item(pixelByteOffset + dxRight * stride);
+
+ if (x + dxRight >= effectWidth) {
+ sumR += edgeValueRight[0];
+ sumG += edgeValueRight[1];
+ sumB += edgeValueRight[2];
+ sumA += edgeValueRight[3];
+ } else {
+ unsigned rightOffset = pixelByteOffset + dxRight * stride;
+ unsigned char* srcPtr = srcData + rightOffset;
+ sumR += srcPtr[0];
+ sumG += srcPtr[1];
+ sumB += srcPtr[2];
+ sumA += srcPtr[3];
}
}
- if (alphaImage) // Source image is black, it just has different alpha values
- break;
}
}
}
-inline void FEGaussianBlur::platformApplyGeneric(Uint8ClampedArray* srcPixelArray, Uint8ClampedArray* tmpPixelArray, unsigned kernelSizeX, unsigned kernelSizeY, IntSize& paintSize)
+#if USE(ACCELERATE)
+inline void accelerateBoxBlur(const Uint8ClampedArray* src, Uint8ClampedArray* dst, unsigned kernelSize, int stride, int effectWidth, int effectHeight)
+{
+ if (!src || !src->data() || !dst || !dst->data()) {
+ ASSERT_NOT_REACHED();
+ return;
+ }
+
+ if (effectWidth <= 0 || effectHeight <= 0 || stride <= 0) {
+ ASSERT_NOT_REACHED();
+ return;
+ }
+
+ // We must always use an odd radius.
+ if (kernelSize % 2 != 1)
+ kernelSize += 1;
+
+ vImage_Buffer effectInBuffer;
+ effectInBuffer.data = src->data();
+ effectInBuffer.width = effectWidth;
+ effectInBuffer.height = effectHeight;
+ effectInBuffer.rowBytes = stride;
+
+ vImage_Buffer effectOutBuffer;
+ effectOutBuffer.data = dst->data();
+ effectOutBuffer.width = effectWidth;
+ effectOutBuffer.height = effectHeight;
+ effectOutBuffer.rowBytes = stride;
+
+ // Determine the size of a temporary buffer by calling the function first with a special flag. vImage will return
+ // the size needed, or an error (which are all negative).
+ size_t tmpBufferSize = vImageBoxConvolve_ARGB8888(&effectInBuffer, &effectOutBuffer, 0, 0, 0, kernelSize, kernelSize, 0, kvImageEdgeExtend | kvImageGetTempBufferSize);
+ if (tmpBufferSize <= 0)
+ return;
+
+ void* tmpBuffer = fastMalloc(tmpBufferSize);
+ vImageBoxConvolve_ARGB8888(&effectInBuffer, &effectOutBuffer, tmpBuffer, 0, 0, kernelSize, kernelSize, 0, kvImageEdgeExtend);
+ vImageBoxConvolve_ARGB8888(&effectOutBuffer, &effectInBuffer, tmpBuffer, 0, 0, kernelSize, kernelSize, 0, kvImageEdgeExtend);
+ vImageBoxConvolve_ARGB8888(&effectInBuffer, &effectOutBuffer, tmpBuffer, 0, 0, kernelSize, kernelSize, 0, kvImageEdgeExtend);
+ WTF::fastFree(tmpBuffer);
+
+ // The final result should be stored in src.
+ if (dst == src) {
+ ASSERT(src->length() == dst->length());
+ memcpy(dst->data(), src->data(), src->length());
+ }
+}
+#endif
+
+inline void standardBoxBlur(Uint8ClampedArray* src, Uint8ClampedArray* dst, unsigned kernelSizeX, unsigned kernelSizeY, int stride, IntSize& paintSize, bool isAlphaImage, EdgeModeType edgeMode)
{
- int stride = 4 * paintSize.width();
int dxLeft = 0;
int dxRight = 0;
int dyLeft = 0;
int dyRight = 0;
- Uint8ClampedArray* src = srcPixelArray;
- Uint8ClampedArray* dst = tmpPixelArray;
for (int i = 0; i < 3; ++i) {
if (kernelSizeX) {
kernelPosition(i, kernelSizeX, dxLeft, dxRight);
#if HAVE(ARM_NEON_INTRINSICS)
- if (!isAlphaImage())
+ if (!isAlphaImage)
boxBlurNEON(src, dst, kernelSizeX, dxLeft, dxRight, 4, stride, paintSize.width(), paintSize.height());
else
- boxBlur(src, dst, kernelSizeX, dxLeft, dxRight, 4, stride, paintSize.width(), paintSize.height(), true, m_edgeMode);
+ boxBlur(src, dst, kernelSizeX, dxLeft, dxRight, 4, stride, paintSize.width(), paintSize.height(), true, edgeMode);
#else
- boxBlur(src, dst, kernelSizeX, dxLeft, dxRight, 4, stride, paintSize.width(), paintSize.height(), isAlphaImage(), m_edgeMode);
+ boxBlur(src, dst, kernelSizeX, dxLeft, dxRight, 4, stride, paintSize.width(), paintSize.height(), isAlphaImage, edgeMode);
#endif
std::swap(src, dst);
}
@@ -177,23 +368,36 @@ inline void FEGaussianBlur::platformApplyGeneric(Uint8ClampedArray* srcPixelArra
if (kernelSizeY) {
kernelPosition(i, kernelSizeY, dyLeft, dyRight);
#if HAVE(ARM_NEON_INTRINSICS)
- if (!isAlphaImage())
+ if (!isAlphaImage)
boxBlurNEON(src, dst, kernelSizeY, dyLeft, dyRight, stride, 4, paintSize.height(), paintSize.width());
else
- boxBlur(src, dst, kernelSizeY, dyLeft, dyRight, stride, 4, paintSize.height(), paintSize.width(), true, m_edgeMode);
+ boxBlur(src, dst, kernelSizeY, dyLeft, dyRight, stride, 4, paintSize.height(), paintSize.width(), true, edgeMode);
#else
- boxBlur(src, dst, kernelSizeY, dyLeft, dyRight, stride, 4, paintSize.height(), paintSize.width(), isAlphaImage(), m_edgeMode);
+ boxBlur(src, dst, kernelSizeY, dyLeft, dyRight, stride, 4, paintSize.height(), paintSize.width(), isAlphaImage, edgeMode);
#endif
std::swap(src, dst);
}
}
- // The final result should be stored in srcPixelArray.
- if (dst == srcPixelArray) {
+ // The final result should be stored in src.
+ if (dst == src) {
ASSERT(src->length() == dst->length());
memcpy(dst->data(), src->data(), src->length());
}
+}
+
+inline void FEGaussianBlur::platformApplyGeneric(Uint8ClampedArray* srcPixelArray, Uint8ClampedArray* tmpPixelArray, unsigned kernelSizeX, unsigned kernelSizeY, IntSize& paintSize)
+{
+ int stride = 4 * paintSize.width();
+
+#if USE(ACCELERATE)
+ if (kernelSizeX == kernelSizeY && (m_edgeMode == EDGEMODE_NONE || m_edgeMode == EDGEMODE_DUPLICATE)) {
+ accelerateBoxBlur(srcPixelArray, tmpPixelArray, kernelSizeX, stride, paintSize.width(), paintSize.height());
+ return;
+ }
+#endif
+ standardBoxBlur(srcPixelArray, tmpPixelArray, kernelSizeX, kernelSizeY, stride, paintSize, isAlphaImage(), m_edgeMode);
}
void FEGaussianBlur::platformApplyWorker(PlatformApplyParameters* parameters)
@@ -205,6 +409,7 @@ void FEGaussianBlur::platformApplyWorker(PlatformApplyParameters* parameters)
inline void FEGaussianBlur::platformApply(Uint8ClampedArray* srcPixelArray, Uint8ClampedArray* tmpPixelArray, unsigned kernelSizeX, unsigned kernelSizeY, IntSize& paintSize)
{
+#if !USE(ACCELERATE)
int scanline = 4 * paintSize.width();
int extraHeight = 3 * kernelSizeY * 0.5f;
int optimalThreadNumber = (paintSize.width() * paintSize.height()) / (s_minimalRectDimension + extraHeight * paintSize.width());
@@ -266,43 +471,43 @@ inline void FEGaussianBlur::platformApply(Uint8ClampedArray* srcPixelArray, Uint
}
// Fallback to single threaded mode.
}
+#endif
// The selection here eventually should happen dynamically on some platforms.
platformApplyGeneric(srcPixelArray, tmpPixelArray, kernelSizeX, kernelSizeY, paintSize);
}
-void FEGaussianBlur::calculateUnscaledKernelSize(unsigned& kernelSizeX, unsigned& kernelSizeY, float stdX, float stdY)
+static int clampedToKernelSize(float value)
{
- ASSERT(stdX >= 0 && stdY >= 0);
-
- kernelSizeX = 0;
- if (stdX)
- kernelSizeX = std::max<unsigned>(2, static_cast<unsigned>(floorf(stdX * gaussianKernelFactor() + 0.5f)));
- kernelSizeY = 0;
- if (stdY)
- kernelSizeY = std::max<unsigned>(2, static_cast<unsigned>(floorf(stdY * gaussianKernelFactor() + 0.5f)));
+ // Limit the kernel size to 500. A bigger radius won't make a big difference for the result image but
+ // inflates the absolute paint rect too much. This is compatible with Firefox' behavior.
+ unsigned size = std::max<unsigned>(2, static_cast<unsigned>(floorf(value * gaussianKernelFactor() + 0.5f)));
+ return clampTo<int>(std::min(size, static_cast<unsigned>(gMaxKernelSize)));
+}
- // Limit the kernel size to 1000. A bigger radius won't make a big difference for the result image but
- // inflates the absolute paint rect to much. This is compatible with Firefox' behavior.
- if (kernelSizeX > gMaxKernelSize)
- kernelSizeX = gMaxKernelSize;
- if (kernelSizeY > gMaxKernelSize)
- kernelSizeY = gMaxKernelSize;
+IntSize FEGaussianBlur::calculateUnscaledKernelSize(const FloatPoint& stdDeviation)
+{
+ ASSERT(stdDeviation.x() >= 0 && stdDeviation.y() >= 0);
+ IntSize kernelSize;
+
+ if (stdDeviation.x())
+ kernelSize.setWidth(clampedToKernelSize(stdDeviation.x()));
+
+ if (stdDeviation.y())
+ kernelSize.setHeight(clampedToKernelSize(stdDeviation.y()));
+
+ return kernelSize;
}
-void FEGaussianBlur::calculateKernelSize(Filter* filter, unsigned& kernelSizeX, unsigned& kernelSizeY, float stdX, float stdY)
+IntSize FEGaussianBlur::calculateKernelSize(const Filter& filter, const FloatPoint& stdDeviation)
{
- stdX = filter->applyHorizontalScale(stdX);
- stdY = filter->applyVerticalScale(stdY);
-
- calculateUnscaledKernelSize(kernelSizeX, kernelSizeY, stdX, stdY);
+ FloatPoint stdFilterScaled(filter.applyHorizontalScale(stdDeviation.x()), filter.applyVerticalScale(stdDeviation.y()));
+ return calculateUnscaledKernelSize(stdFilterScaled);
}
void FEGaussianBlur::determineAbsolutePaintRect()
{
- unsigned kernelSizeX = 0;
- unsigned kernelSizeY = 0;
- calculateKernelSize(filter(), kernelSizeX, kernelSizeY, m_stdX, m_stdY);
+ IntSize kernelSize = calculateKernelSize(filter(), FloatPoint(m_stdX, m_stdY));
FloatRect absolutePaintRect = inputEffect(0)->absolutePaintRect();
// Edge modes other than 'none' do not inflate the affected paint rect.
@@ -312,8 +517,8 @@ void FEGaussianBlur::determineAbsolutePaintRect()
}
// We take the half kernel size and multiply it with three, because we run box blur three times.
- absolutePaintRect.inflateX(3 * kernelSizeX * 0.5f);
- absolutePaintRect.inflateY(3 * kernelSizeY * 0.5f);
+ absolutePaintRect.inflateX(3 * kernelSize.width() * 0.5f);
+ absolutePaintRect.inflateY(3 * kernelSize.height() * 0.5f);
if (clipsToBounds())
absolutePaintRect.intersect(maxEffectRect());
@@ -339,15 +544,18 @@ void FEGaussianBlur::platformApplySoftware()
if (!m_stdX && !m_stdY)
return;
- unsigned kernelSizeX = 0;
- unsigned kernelSizeY = 0;
- calculateKernelSize(filter(), kernelSizeX, kernelSizeY, m_stdX, m_stdY);
+ IntSize kernelSize = calculateKernelSize(filter(), FloatPoint(m_stdX, m_stdY));
+ kernelSize.scale(filter().filterScale());
IntSize paintSize = absolutePaintRect().size();
- RefPtr<Uint8ClampedArray> tmpImageData = Uint8ClampedArray::createUninitialized(paintSize.width() * paintSize.height() * 4);
- Uint8ClampedArray* tmpPixelArray = tmpImageData.get();
+ paintSize.scale(filter().filterScale());
+ RefPtr<Uint8ClampedArray> tmpImageData = Uint8ClampedArray::createUninitialized((paintSize.area() * 4).unsafeGet());
+ if (!tmpImageData) {
+ WTFLogAlways("FEGaussianBlur::platformApplySoftware Unable to create buffer. Requested size was %d x %d\n", paintSize.width(), paintSize.height());
+ return;
+ }
- platformApply(srcPixelArray, tmpPixelArray, kernelSizeX, kernelSizeY, paintSize);
+ platformApply(srcPixelArray, tmpImageData.get(), kernelSize.width(), kernelSize.height(), paintSize);
}
void FEGaussianBlur::dump()
@@ -364,12 +572,4 @@ TextStream& FEGaussianBlur::externalRepresentation(TextStream& ts, int indent) c
return ts;
}
-float FEGaussianBlur::calculateStdDeviation(float radius)
-{
- // Blur radius represents 2/3 times the kernel size, the dest pixel is half of the radius applied 3 times
- return std::max((radius * 2 / 3.f - 0.5f) / gaussianKernelFactor(), 0.f);
-}
-
} // namespace WebCore
-
-#endif // ENABLE(FILTERS)
diff --git a/Source/WebCore/platform/graphics/filters/FEGaussianBlur.h b/Source/WebCore/platform/graphics/filters/FEGaussianBlur.h
index b666b5955..0748a223d 100644
--- a/Source/WebCore/platform/graphics/filters/FEGaussianBlur.h
+++ b/Source/WebCore/platform/graphics/filters/FEGaussianBlur.h
@@ -22,7 +22,6 @@
#ifndef FEGaussianBlur_h
#define FEGaussianBlur_h
-#if ENABLE(FILTERS)
#include "FEConvolveMatrix.h"
#include "Filter.h"
#include "FilterEffect.h"
@@ -31,7 +30,7 @@ namespace WebCore {
class FEGaussianBlur : public FilterEffect {
public:
- static PassRefPtr<FEGaussianBlur> create(Filter*, float, float, EdgeModeType);
+ static Ref<FEGaussianBlur> create(Filter&, float, float, EdgeModeType);
float stdDeviationX() const;
void setStdDeviationX(float);
@@ -42,16 +41,14 @@ public:
EdgeModeType edgeMode() const;
void setEdgeMode(EdgeModeType);
- static float calculateStdDeviation(float);
+ void platformApplySoftware() override;
+ void dump() override;
- virtual void platformApplySoftware();
- virtual void dump();
-
- virtual void determineAbsolutePaintRect();
- static void calculateKernelSize(Filter*, unsigned& kernelSizeX, unsigned& kernelSizeY, float stdX, float stdY);
- static void calculateUnscaledKernelSize(unsigned& kernelSizeX, unsigned& kernelSizeY, float stdX, float stdY);
+ void determineAbsolutePaintRect() override;
+ static IntSize calculateKernelSize(const Filter&, const FloatPoint& stdDeviation);
+ static IntSize calculateUnscaledKernelSize(const FloatPoint& stdDeviation);
- virtual TextStream& externalRepresentation(TextStream&, int indention) const;
+ TextStream& externalRepresentation(TextStream&, int indention) const override;
private:
static const int s_minimalRectDimension = 100 * 100; // Empirical data limit for parallel jobs
@@ -71,9 +68,8 @@ private:
static void platformApplyWorker(PlatformApplyParameters*);
- FEGaussianBlur(Filter*, float, float, EdgeModeType);
+ FEGaussianBlur(Filter&, float, float, EdgeModeType);
- static inline void kernelPosition(int boxBlur, unsigned& std, int& dLeft, int& dRight);
inline void platformApply(Uint8ClampedArray* srcPixelArray, Uint8ClampedArray* tmpPixelArray, unsigned kernelSizeX, unsigned kernelSizeY, IntSize& paintSize);
inline void platformApplyGeneric(Uint8ClampedArray* srcPixelArray, Uint8ClampedArray* tmpPixelArray, unsigned kernelSizeX, unsigned kernelSizeY, IntSize& paintSize);
@@ -83,36 +79,6 @@ private:
EdgeModeType m_edgeMode;
};
-inline void FEGaussianBlur::kernelPosition(int boxBlur, unsigned& std, int& dLeft, int& dRight)
-{
- // check http://www.w3.org/TR/SVG/filters.html#feGaussianBlurElement for details
- switch (boxBlur) {
- case 0:
- if (!(std % 2)) {
- dLeft = std / 2 - 1;
- dRight = std - dLeft;
- } else {
- dLeft = std / 2;
- dRight = std - dLeft;
- }
- break;
- case 1:
- if (!(std % 2)) {
- dLeft++;
- dRight--;
- }
- break;
- case 2:
- if (!(std % 2)) {
- dRight++;
- std++;
- }
- break;
- }
-}
-
} // namespace WebCore
-#endif // ENABLE(FILTERS)
-
#endif // FEGaussianBlur_h
diff --git a/Source/WebCore/platform/graphics/filters/FELighting.cpp b/Source/WebCore/platform/graphics/filters/FELighting.cpp
index 1a8180334..2925b0eb6 100644
--- a/Source/WebCore/platform/graphics/filters/FELighting.cpp
+++ b/Source/WebCore/platform/graphics/filters/FELighting.cpp
@@ -25,8 +25,6 @@
*/
#include "config.h"
-
-#if ENABLE(FILTERS)
#include "FELighting.h"
#include "FELightingNEON.h"
@@ -34,7 +32,7 @@
namespace WebCore {
-FELighting::FELighting(Filter* filter, LightingType lightingType, const Color& lightingColor, float surfaceScale,
+FELighting::FELighting(Filter& filter, LightingType lightingType, const Color& lightingColor, float surfaceScale,
float diffuseConstant, float specularConstant, float specularExponent,
float kernelUnitLengthX, float kernelUnitLengthY, PassRefPtr<LightSource> lightSource)
: FilterEffect(filter)
@@ -284,7 +282,7 @@ inline void FELighting::platformApplyGeneric(LightingData& data, LightSource::Pa
inline void FELighting::platformApply(LightingData& data, LightSource::PaintingData& paintingData)
{
// The selection here eventually should happen dynamically on some platforms.
-#if CPU(ARM_NEON) && CPU(ARM_TRADITIONAL) && COMPILER(GCC)
+#if CPU(ARM_NEON) && CPU(ARM_TRADITIONAL) && COMPILER(GCC_OR_CLANG)
platformApplyNeon(data, paintingData);
#else
platformApplyGeneric(data, paintingData);
@@ -408,5 +406,3 @@ void FELighting::platformApplySoftware()
}
} // namespace WebCore
-
-#endif // ENABLE(FILTERS)
diff --git a/Source/WebCore/platform/graphics/filters/FELighting.h b/Source/WebCore/platform/graphics/filters/FELighting.h
index eb79bbdbc..bafbf47c4 100644
--- a/Source/WebCore/platform/graphics/filters/FELighting.h
+++ b/Source/WebCore/platform/graphics/filters/FELighting.h
@@ -27,7 +27,6 @@
#ifndef FELighting_h
#define FELighting_h
-#if ENABLE(FILTERS)
#include "Color.h"
#include "Filter.h"
#include "FilterEffect.h"
@@ -44,9 +43,9 @@ struct FELightingPaintingDataForNeon;
class FELighting : public FilterEffect {
public:
- virtual void platformApplySoftware();
+ void platformApplySoftware() override;
- virtual void determineAbsolutePaintRect() { setAbsolutePaintRect(enclosingIntRect(maxEffectRect())); }
+ void determineAbsolutePaintRect() override { setAbsolutePaintRect(enclosingIntRect(maxEffectRect())); }
protected:
static const int s_minimalRectDimension = 100 * 100; // Empirical data limit for parallel jobs
@@ -89,7 +88,7 @@ protected:
static void platformApplyGenericWorker(PlatformApplyGenericParameters*);
static void platformApplyNeonWorker(FELightingPaintingDataForNeon*);
- FELighting(Filter*, LightingType, const Color&, float, float, float, float, float, float, PassRefPtr<LightSource>);
+ FELighting(Filter&, LightingType, const Color&, float, float, float, float, float, float, PassRefPtr<LightSource>);
bool drawLighting(Uint8ClampedArray*, int, int);
inline void inlineSetPixel(int offset, LightingData&, LightSource::PaintingData&,
@@ -121,6 +120,4 @@ protected:
} // namespace WebCore
-#endif // ENABLE(FILTERS)
-
#endif // FELighting_h
diff --git a/Source/WebCore/platform/graphics/filters/FEMerge.cpp b/Source/WebCore/platform/graphics/filters/FEMerge.cpp
index 89fb8cef6..32cf254d5 100644
--- a/Source/WebCore/platform/graphics/filters/FEMerge.cpp
+++ b/Source/WebCore/platform/graphics/filters/FEMerge.cpp
@@ -20,8 +20,6 @@
*/
#include "config.h"
-
-#if ENABLE(FILTERS)
#include "FEMerge.h"
#include "Filter.h"
@@ -30,14 +28,14 @@
namespace WebCore {
-FEMerge::FEMerge(Filter* filter)
+FEMerge::FEMerge(Filter& filter)
: FilterEffect(filter)
{
}
-PassRefPtr<FEMerge> FEMerge::create(Filter* filter)
+Ref<FEMerge> FEMerge::create(Filter& filter)
{
- return adoptRef(new FEMerge(filter));
+ return adoptRef(*new FEMerge(filter));
}
void FEMerge::platformApplySoftware()
@@ -49,10 +47,12 @@ void FEMerge::platformApplySoftware()
if (!resultImage)
return;
- GraphicsContext* filterContext = resultImage->context();
+
+ GraphicsContext& filterContext = resultImage->context();
for (unsigned i = 0; i < size; ++i) {
FilterEffect* in = inputEffect(i);
- filterContext->drawImageBuffer(in->asImageBuffer(), ColorSpaceDeviceRGB, drawingRegionOfInputImage(in->absolutePaintRect()));
+ if (ImageBuffer* inBuffer = in->asImageBuffer())
+ filterContext.drawImageBuffer(*inBuffer, drawingRegionOfInputImage(in->absolutePaintRect()));
}
}
@@ -74,5 +74,3 @@ TextStream& FEMerge::externalRepresentation(TextStream& ts, int indent) const
}
} // namespace WebCore
-
-#endif // ENABLE(FILTERS)
diff --git a/Source/WebCore/platform/graphics/filters/FEMerge.h b/Source/WebCore/platform/graphics/filters/FEMerge.h
index fb4f4f6cf..620f2205e 100644
--- a/Source/WebCore/platform/graphics/filters/FEMerge.h
+++ b/Source/WebCore/platform/graphics/filters/FEMerge.h
@@ -22,31 +22,24 @@
#ifndef FEMerge_h
#define FEMerge_h
-#if ENABLE(FILTERS)
#include "FilterEffect.h"
#include "Filter.h"
-#include <wtf/Vector.h>
namespace WebCore {
class FEMerge : public FilterEffect {
public:
- static PassRefPtr<FEMerge> create(Filter*);
+ static Ref<FEMerge> create(Filter&);
- virtual void platformApplySoftware();
-#if ENABLE(OPENCL)
- virtual bool platformApplyOpenCL();
-#endif
- virtual void dump();
+ void platformApplySoftware() override;
+ void dump() override;
- virtual TextStream& externalRepresentation(TextStream&, int indention) const;
+ TextStream& externalRepresentation(TextStream&, int indention) const override;
private:
- FEMerge(Filter*);
+ FEMerge(Filter&);
};
} // namespace WebCore
-#endif // ENABLE(FILTERS)
-
#endif // FEMerge_h
diff --git a/Source/WebCore/platform/graphics/filters/FEMorphology.cpp b/Source/WebCore/platform/graphics/filters/FEMorphology.cpp
index 6ee8a826d..894fd3d5b 100644
--- a/Source/WebCore/platform/graphics/filters/FEMorphology.cpp
+++ b/Source/WebCore/platform/graphics/filters/FEMorphology.cpp
@@ -22,8 +22,6 @@
*/
#include "config.h"
-
-#if ENABLE(FILTERS)
#include "FEMorphology.h"
#include "Filter.h"
@@ -35,7 +33,7 @@
namespace WebCore {
-FEMorphology::FEMorphology(Filter* filter, MorphologyOperatorType type, float radiusX, float radiusY)
+FEMorphology::FEMorphology(Filter& filter, MorphologyOperatorType type, float radiusX, float radiusY)
: FilterEffect(filter)
, m_type(type)
, m_radiusX(radiusX)
@@ -43,9 +41,9 @@ FEMorphology::FEMorphology(Filter* filter, MorphologyOperatorType type, float ra
{
}
-PassRefPtr<FEMorphology> FEMorphology::create(Filter* filter, MorphologyOperatorType type, float radiusX, float radiusY)
+Ref<FEMorphology> FEMorphology::create(Filter& filter, MorphologyOperatorType type, float radiusX, float radiusY)
{
- return adoptRef(new FEMorphology(filter, type, radiusX, radiusY));
+ return adoptRef(*new FEMorphology(filter, type, radiusX, radiusY));
}
bool FEMorphology::setMorphologyOperator(MorphologyOperatorType type)
@@ -75,9 +73,9 @@ bool FEMorphology::setRadiusY(float radiusY)
void FEMorphology::determineAbsolutePaintRect()
{
FloatRect paintRect = inputEffect(0)->absolutePaintRect();
- Filter* filter = this->filter();
- paintRect.inflateX(filter->applyHorizontalScale(m_radiusX));
- paintRect.inflateY(filter->applyVerticalScale(m_radiusY));
+ Filter& filter = this->filter();
+ paintRect.inflateX(filter.applyHorizontalScale(m_radiusX));
+ paintRect.inflateY(filter.applyVerticalScale(m_radiusY));
if (clipsToBounds())
paintRect.intersect(maxEffectRect());
else
@@ -227,10 +225,10 @@ void FEMorphology::platformApplySoftware()
if (platformApplyDegenerate(dstPixelArray, effectDrawingRect, m_radiusX, m_radiusY))
return;
- Filter* filter = this->filter();
+ Filter& filter = this->filter();
RefPtr<Uint8ClampedArray> srcPixelArray = in->asPremultipliedImage(effectDrawingRect);
- int radiusX = static_cast<int>(floorf(filter->applyHorizontalScale(m_radiusX)));
- int radiusY = static_cast<int>(floorf(filter->applyVerticalScale(m_radiusY)));
+ int radiusX = static_cast<int>(floorf(filter.applyHorizontalScale(m_radiusX)));
+ int radiusY = static_cast<int>(floorf(filter.applyVerticalScale(m_radiusY)));
radiusX = std::min(effectDrawingRect.width() - 1, radiusX);
radiusY = std::min(effectDrawingRect.height() - 1, radiusY);
@@ -240,10 +238,10 @@ void FEMorphology::platformApplySoftware()
PaintingData paintingData;
paintingData.srcPixelArray = srcPixelArray.get();
paintingData.dstPixelArray = dstPixelArray;
- paintingData.width = effectDrawingRect.width();
- paintingData.height = effectDrawingRect.height();
- paintingData.radiusX = radiusX;
- paintingData.radiusY = radiusY;
+ paintingData.width = ceilf(effectDrawingRect.width() * filter.filterScale());
+ paintingData.height = ceilf(effectDrawingRect.height() * filter.filterScale());
+ paintingData.radiusX = ceilf(radiusX * filter.filterScale());
+ paintingData.radiusY = ceilf(radiusY * filter.filterScale());
platformApply(&paintingData);
}
@@ -280,5 +278,3 @@ TextStream& FEMorphology::externalRepresentation(TextStream& ts, int indent) con
}
} // namespace WebCore
-
-#endif // ENABLE(FILTERS)
diff --git a/Source/WebCore/platform/graphics/filters/FEMorphology.h b/Source/WebCore/platform/graphics/filters/FEMorphology.h
index f36c41f99..050fc73d5 100644
--- a/Source/WebCore/platform/graphics/filters/FEMorphology.h
+++ b/Source/WebCore/platform/graphics/filters/FEMorphology.h
@@ -22,7 +22,6 @@
#ifndef FEMorphology_h
#define FEMorphology_h
-#if ENABLE(FILTERS)
#include "FilterEffect.h"
#include "Filter.h"
@@ -36,7 +35,7 @@ enum MorphologyOperatorType {
class FEMorphology : public FilterEffect {
public:
- static PassRefPtr<FEMorphology> create(Filter*, MorphologyOperatorType, float radiusX, float radiusY);
+ static Ref<FEMorphology> create(Filter&, MorphologyOperatorType, float radiusX, float radiusY);
MorphologyOperatorType morphologyOperator() const { return m_type; }
bool setMorphologyOperator(MorphologyOperatorType);
@@ -47,12 +46,12 @@ public:
float radiusY() const { return m_radiusY; }
bool setRadiusY(float);
- virtual void platformApplySoftware();
- virtual void dump();
+ void platformApplySoftware() override;
+ void dump() override;
- virtual void determineAbsolutePaintRect();
+ void determineAbsolutePaintRect() override;
- virtual TextStream& externalRepresentation(TextStream&, int indention) const;
+ TextStream& externalRepresentation(TextStream&, int indention) const override;
struct PaintingData {
Uint8ClampedArray* srcPixelArray;
@@ -77,7 +76,7 @@ public:
inline void platformApply(PaintingData*);
inline void platformApplyGeneric(PaintingData*, const int yStart, const int yEnd);
private:
- FEMorphology(Filter*, MorphologyOperatorType, float radiusX, float radiusY);
+ FEMorphology(Filter&, MorphologyOperatorType, float radiusX, float radiusY);
bool platformApplyDegenerate(Uint8ClampedArray* dstPixelArray, const IntRect& imageRect, int radiusX, int radiusY);
MorphologyOperatorType m_type;
@@ -87,6 +86,4 @@ private:
} // namespace WebCore
-#endif // ENABLE(FILTERS)
-
#endif // FEMorphology_h
diff --git a/Source/WebCore/platform/graphics/filters/FEOffset.cpp b/Source/WebCore/platform/graphics/filters/FEOffset.cpp
index 45c4cc867..ddef58791 100644
--- a/Source/WebCore/platform/graphics/filters/FEOffset.cpp
+++ b/Source/WebCore/platform/graphics/filters/FEOffset.cpp
@@ -22,8 +22,6 @@
*/
#include "config.h"
-
-#if ENABLE(FILTERS)
#include "FEOffset.h"
#include "Filter.h"
@@ -32,16 +30,16 @@
namespace WebCore {
-FEOffset::FEOffset(Filter* filter, float dx, float dy)
+FEOffset::FEOffset(Filter& filter, float dx, float dy)
: FilterEffect(filter)
, m_dx(dx)
, m_dy(dy)
{
}
-PassRefPtr<FEOffset> FEOffset::create(Filter* filter, float dx, float dy)
+Ref<FEOffset> FEOffset::create(Filter& filter, float dx, float dy)
{
- return adoptRef(new FEOffset(filter, dx, dy));
+ return adoptRef(*new FEOffset(filter, dx, dy));
}
float FEOffset::dx() const
@@ -67,8 +65,8 @@ void FEOffset::setDy(float dy)
void FEOffset::determineAbsolutePaintRect()
{
FloatRect paintRect = inputEffect(0)->absolutePaintRect();
- Filter* filter = this->filter();
- paintRect.move(filter->applyHorizontalScale(m_dx), filter->applyVerticalScale(m_dy));
+ Filter& filter = this->filter();
+ paintRect.move(filter.applyHorizontalScale(m_dx), filter.applyVerticalScale(m_dy));
if (clipsToBounds())
paintRect.intersect(maxEffectRect());
else
@@ -81,15 +79,16 @@ void FEOffset::platformApplySoftware()
FilterEffect* in = inputEffect(0);
ImageBuffer* resultImage = createImageBufferResult();
- if (!resultImage)
+ ImageBuffer* inBuffer = in->asImageBuffer();
+ if (!resultImage || !inBuffer)
return;
setIsAlphaImage(in->isAlphaImage());
FloatRect drawingRegion = drawingRegionOfInputImage(in->absolutePaintRect());
- Filter* filter = this->filter();
- drawingRegion.move(filter->applyHorizontalScale(m_dx), filter->applyVerticalScale(m_dy));
- resultImage->context()->drawImageBuffer(in->asImageBuffer(), ColorSpaceDeviceRGB, drawingRegion);
+ Filter& filter = this->filter();
+ drawingRegion.move(filter.applyHorizontalScale(m_dx), filter.applyVerticalScale(m_dy));
+ resultImage->context().drawImageBuffer(*inBuffer, drawingRegion);
}
void FEOffset::dump()
@@ -107,5 +106,3 @@ TextStream& FEOffset::externalRepresentation(TextStream& ts, int indent) const
}
} // namespace WebCore
-
-#endif // ENABLE(FILTERS)
diff --git a/Source/WebCore/platform/graphics/filters/FEOffset.h b/Source/WebCore/platform/graphics/filters/FEOffset.h
index e75febd9c..f196d49e3 100644
--- a/Source/WebCore/platform/graphics/filters/FEOffset.h
+++ b/Source/WebCore/platform/graphics/filters/FEOffset.h
@@ -22,7 +22,6 @@
#ifndef FEOffset_h
#define FEOffset_h
-#if ENABLE(FILTERS)
#include "FilterEffect.h"
#include "Filter.h"
@@ -30,7 +29,7 @@ namespace WebCore {
class FEOffset : public FilterEffect {
public:
- static PassRefPtr<FEOffset> create(Filter*, float dx, float dy);
+ static Ref<FEOffset> create(Filter&, float dx, float dy);
float dx() const;
void setDx(float);
@@ -38,15 +37,15 @@ public:
float dy() const;
void setDy(float);
- virtual void platformApplySoftware();
- virtual void dump();
+ void platformApplySoftware() override;
+ void dump() override;
- virtual void determineAbsolutePaintRect();
+ void determineAbsolutePaintRect() override;
- virtual TextStream& externalRepresentation(TextStream&, int indention) const;
+ TextStream& externalRepresentation(TextStream&, int indention) const override;
private:
- FEOffset(Filter*, float dx, float dy);
+ FEOffset(Filter&, float dx, float dy);
float m_dx;
float m_dy;
@@ -54,6 +53,4 @@ private:
} // namespace WebCore
-#endif // ENABLE(FILTERS)
-
#endif // FEOffset_h
diff --git a/Source/WebCore/platform/graphics/filters/FESpecularLighting.cpp b/Source/WebCore/platform/graphics/filters/FESpecularLighting.cpp
index 249914fe3..e2e834f21 100644
--- a/Source/WebCore/platform/graphics/filters/FESpecularLighting.cpp
+++ b/Source/WebCore/platform/graphics/filters/FESpecularLighting.cpp
@@ -20,8 +20,6 @@
*/
#include "config.h"
-
-#if ENABLE(FILTERS)
#include "FESpecularLighting.h"
#include "LightSource.h"
@@ -29,18 +27,18 @@
namespace WebCore {
-FESpecularLighting::FESpecularLighting(Filter* filter, const Color& lightingColor, float surfaceScale,
+FESpecularLighting::FESpecularLighting(Filter& filter, const Color& lightingColor, float surfaceScale,
float specularConstant, float specularExponent, float kernelUnitLengthX,
float kernelUnitLengthY, PassRefPtr<LightSource> lightSource)
: FELighting(filter, SpecularLighting, lightingColor, surfaceScale, 0, specularConstant, specularExponent, kernelUnitLengthX, kernelUnitLengthY, lightSource)
{
}
-PassRefPtr<FESpecularLighting> FESpecularLighting::create(Filter* filter, const Color& lightingColor,
+Ref<FESpecularLighting> FESpecularLighting::create(Filter& filter, const Color& lightingColor,
float surfaceScale, float specularConstant, float specularExponent,
float kernelUnitLengthX, float kernelUnitLengthY, PassRefPtr<LightSource> lightSource)
{
- return adoptRef(new FESpecularLighting(filter, lightingColor, surfaceScale, specularConstant, specularExponent,
+ return adoptRef(*new FESpecularLighting(filter, lightingColor, surfaceScale, specularConstant, specularExponent,
kernelUnitLengthX, kernelUnitLengthY, lightSource));
}
@@ -48,7 +46,7 @@ FESpecularLighting::~FESpecularLighting()
{
}
-Color FESpecularLighting::lightingColor() const
+const Color& FESpecularLighting::lightingColor() const
{
return m_lightingColor;
}
@@ -153,5 +151,3 @@ TextStream& FESpecularLighting::externalRepresentation(TextStream& ts, int inden
}
} // namespace WebCore
-
-#endif // ENABLE(FILTERS)
diff --git a/Source/WebCore/platform/graphics/filters/FESpecularLighting.h b/Source/WebCore/platform/graphics/filters/FESpecularLighting.h
index 9fa3addac..e04f4436a 100644
--- a/Source/WebCore/platform/graphics/filters/FESpecularLighting.h
+++ b/Source/WebCore/platform/graphics/filters/FESpecularLighting.h
@@ -22,18 +22,17 @@
#ifndef FESpecularLighting_h
#define FESpecularLighting_h
-#if ENABLE(FILTERS)
#include "FELighting.h"
namespace WebCore {
class FESpecularLighting : public FELighting {
public:
- static PassRefPtr<FESpecularLighting> create(Filter*, const Color&, float, float,
+ static Ref<FESpecularLighting> create(Filter&, const Color&, float, float,
float, float, float, PassRefPtr<LightSource>);
virtual ~FESpecularLighting();
- Color lightingColor() const;
+ const Color& lightingColor() const;
bool setLightingColor(const Color&);
float surfaceScale() const;
@@ -54,16 +53,14 @@ public:
const LightSource* lightSource() const;
void setLightSource(PassRefPtr<LightSource>);
- virtual void dump();
+ void dump() override;
- virtual TextStream& externalRepresentation(TextStream&, int indention) const;
+ TextStream& externalRepresentation(TextStream&, int indention) const override;
private:
- FESpecularLighting(Filter*, const Color&, float, float, float, float, float, PassRefPtr<LightSource>);
+ FESpecularLighting(Filter&, const Color&, float, float, float, float, float, PassRefPtr<LightSource>);
};
} // namespace WebCore
-#endif // ENABLE(FILTERS)
-
#endif // FESpecularLighting_h
diff --git a/Source/WebCore/platform/graphics/filters/FETile.cpp b/Source/WebCore/platform/graphics/filters/FETile.cpp
index 4a6c6a4f9..a0e2e1bf6 100644
--- a/Source/WebCore/platform/graphics/filters/FETile.cpp
+++ b/Source/WebCore/platform/graphics/filters/FETile.cpp
@@ -19,8 +19,6 @@
*/
#include "config.h"
-
-#if ENABLE(FILTERS)
#include "FETile.h"
#include "AffineTransform.h"
@@ -33,24 +31,24 @@
namespace WebCore {
-FETile::FETile(Filter* filter)
+FETile::FETile(Filter& filter)
: FilterEffect(filter)
{
}
-PassRefPtr<FETile> FETile::create(Filter* filter)
+Ref<FETile> FETile::create(Filter& filter)
{
- return adoptRef(new FETile(filter));
+ return adoptRef(*new FETile(filter));
}
void FETile::platformApplySoftware()
{
// FIXME: See bug 47315. This is a hack to work around a compile failure, but is incorrect behavior otherwise.
-#if ENABLE(SVG)
FilterEffect* in = inputEffect(0);
ImageBuffer* resultImage = createImageBufferResult();
- if (!resultImage)
+ ImageBuffer* inBuffer = in->asImageBuffer();
+ if (!resultImage || !inBuffer)
return;
setIsAlphaImage(in->isAlphaImage());
@@ -61,28 +59,31 @@ void FETile::platformApplySoftware()
FloatPoint inMaxEffectLocation = tileRect.location();
FloatPoint maxEffectLocation = maxEffectRect().location();
if (in->filterEffectType() == FilterEffectTypeSourceInput) {
- Filter* filter = this->filter();
- tileRect = filter->filterRegion();
- tileRect.scale(filter->filterResolution().width(), filter->filterResolution().height());
+ Filter& filter = this->filter();
+ tileRect = filter.filterRegion();
+ tileRect.scale(filter.filterResolution().width(), filter.filterResolution().height());
}
- std::unique_ptr<ImageBuffer> tileImage;
- if (!SVGRenderingContext::createImageBufferForPattern(tileRect, tileRect, tileImage, ColorSpaceDeviceRGB, filter()->renderingMode()))
+ auto tileImage = SVGRenderingContext::createImageBuffer(tileRect, tileRect, ColorSpaceSRGB, filter().renderingMode());
+ if (!tileImage)
return;
- GraphicsContext* tileImageContext = tileImage->context();
- tileImageContext->translate(-inMaxEffectLocation.x(), -inMaxEffectLocation.y());
- tileImageContext->drawImageBuffer(in->asImageBuffer(), ColorSpaceDeviceRGB, in->absolutePaintRect().location());
+ GraphicsContext& tileImageContext = tileImage->context();
+ tileImageContext.translate(-inMaxEffectLocation.x(), -inMaxEffectLocation.y());
+ tileImageContext.drawImageBuffer(*inBuffer, in->absolutePaintRect().location());
+
+ auto tileImageCopy = ImageBuffer::sinkIntoImage(WTFMove(tileImage));
+ if (!tileImageCopy)
+ return;
- RefPtr<Pattern> pattern = Pattern::create(tileImage->copyImage(CopyBackingStore), true, true);
+ auto pattern = Pattern::create(WTFMove(tileImageCopy), true, true);
AffineTransform patternTransform;
patternTransform.translate(inMaxEffectLocation.x() - maxEffectLocation.x(), inMaxEffectLocation.y() - maxEffectLocation.y());
- pattern->setPatternSpaceTransform(patternTransform);
- GraphicsContext* filterContext = resultImage->context();
- filterContext->setFillPattern(pattern);
- filterContext->fillRect(FloatRect(FloatPoint(), absolutePaintRect().size()));
-#endif
+ pattern.get().setPatternSpaceTransform(patternTransform);
+ GraphicsContext& filterContext = resultImage->context();
+ filterContext.setFillPattern(WTFMove(pattern));
+ filterContext.fillRect(FloatRect(FloatPoint(), absolutePaintRect().size()));
}
void FETile::dump()
@@ -101,5 +102,3 @@ TextStream& FETile::externalRepresentation(TextStream& ts, int indent) const
}
} // namespace WebCore
-
-#endif // ENABLE(FILTERS)
diff --git a/Source/WebCore/platform/graphics/filters/FETile.h b/Source/WebCore/platform/graphics/filters/FETile.h
index 633276a3e..c264cefa4 100644
--- a/Source/WebCore/platform/graphics/filters/FETile.h
+++ b/Source/WebCore/platform/graphics/filters/FETile.h
@@ -22,7 +22,6 @@
#ifndef FETile_h
#define FETile_h
-#if ENABLE(FILTERS)
#include "FilterEffect.h"
#include "Filter.h"
@@ -30,23 +29,21 @@ namespace WebCore {
class FETile : public FilterEffect {
public:
- static PassRefPtr<FETile> create(Filter* filter);
+ static Ref<FETile> create(Filter&);
- virtual void platformApplySoftware();
- virtual void dump();
+ void platformApplySoftware() override;
+ void dump() override;
- virtual void determineAbsolutePaintRect() { setAbsolutePaintRect(enclosingIntRect(maxEffectRect())); }
+ void determineAbsolutePaintRect() override { setAbsolutePaintRect(enclosingIntRect(maxEffectRect())); }
- virtual FilterEffectType filterEffectType() const { return FilterEffectTypeTile; }
+ FilterEffectType filterEffectType() const override { return FilterEffectTypeTile; }
- virtual TextStream& externalRepresentation(TextStream&, int indention) const;
+ TextStream& externalRepresentation(TextStream&, int indention) const override;
private:
- FETile(Filter*);
+ FETile(Filter&);
};
} // namespace WebCore
-#endif // ENABLE(FILTERS)
-
#endif // FETile_h
diff --git a/Source/WebCore/platform/graphics/filters/FETurbulence.cpp b/Source/WebCore/platform/graphics/filters/FETurbulence.cpp
index a5e8b348a..37d9f6f0e 100644
--- a/Source/WebCore/platform/graphics/filters/FETurbulence.cpp
+++ b/Source/WebCore/platform/graphics/filters/FETurbulence.cpp
@@ -23,8 +23,6 @@
*/
#include "config.h"
-
-#if ENABLE(FILTERS)
#include "FETurbulence.h"
#include "Filter.h"
@@ -50,7 +48,7 @@ static const int s_randAmplitude = 16807; // 7**5; primitive root of m
static const int s_randQ = 127773; // m / a
static const int s_randR = 2836; // m % a
-FETurbulence::FETurbulence(Filter* filter, TurbulenceType type, float baseFrequencyX, float baseFrequencyY, int numOctaves, float seed, bool stitchTiles)
+FETurbulence::FETurbulence(Filter& filter, TurbulenceType type, float baseFrequencyX, float baseFrequencyY, int numOctaves, float seed, bool stitchTiles)
: FilterEffect(filter)
, m_type(type)
, m_baseFrequencyX(baseFrequencyX)
@@ -61,9 +59,9 @@ FETurbulence::FETurbulence(Filter* filter, TurbulenceType type, float baseFreque
{
}
-PassRefPtr<FETurbulence> FETurbulence::create(Filter* filter, TurbulenceType type, float baseFrequencyX, float baseFrequencyY, int numOctaves, float seed, bool stitchTiles)
+Ref<FETurbulence> FETurbulence::create(Filter& filter, TurbulenceType type, float baseFrequencyX, float baseFrequencyY, int numOctaves, float seed, bool stitchTiles)
{
- return adoptRef(new FETurbulence(filter, type, baseFrequencyX, baseFrequencyY, numOctaves, seed, stitchTiles));
+ return adoptRef(*new FETurbulence(filter, type, baseFrequencyX, baseFrequencyY, numOctaves, seed, stitchTiles));
}
TurbulenceType FETurbulence::type() const
@@ -182,8 +180,10 @@ inline void FETurbulence::initPaint(PaintingData& paintingData)
for (int i = 0; i < s_blockSize; ++i) {
paintingData.latticeSelector[i] = i;
gradient = paintingData.gradient[channel][i];
- gradient[0] = static_cast<float>((paintingData.random() % (2 * s_blockSize)) - s_blockSize) / s_blockSize;
- gradient[1] = static_cast<float>((paintingData.random() % (2 * s_blockSize)) - s_blockSize) / s_blockSize;
+ do {
+ gradient[0] = static_cast<float>((paintingData.random() % (2 * s_blockSize)) - s_blockSize) / s_blockSize;
+ gradient[1] = static_cast<float>((paintingData.random() % (2 * s_blockSize)) - s_blockSize) / s_blockSize;
+ } while (!gradient[0] && !gradient[1]);
normalizationFactor = sqrtf(gradient[0] * gradient[0] + gradient[1] * gradient[1]);
gradient[0] /= normalizationFactor;
gradient[1] /= normalizationFactor;
@@ -343,7 +343,7 @@ inline void FETurbulence::fillRegion(Uint8ClampedArray* pixelArray, PaintingData
for (int x = 0; x < filterRegion.width(); ++x) {
point.setX(point.x() + 1);
for (channel = 0; channel < 4; ++channel, ++indexOfPixelChannel)
- pixelArray->set(indexOfPixelChannel, calculateTurbulenceValueForPoint(channel, paintingData, stitchData, filter()->mapAbsolutePointToLocalPoint(point)));
+ pixelArray->set(indexOfPixelChannel, calculateTurbulenceValueForPoint(channel, paintingData, stitchData, filter().mapAbsolutePointToLocalPoint(point)));
}
}
}
@@ -435,5 +435,3 @@ TextStream& FETurbulence::externalRepresentation(TextStream& ts, int indent) con
}
} // namespace WebCore
-
-#endif // ENABLE(FILTERS)
diff --git a/Source/WebCore/platform/graphics/filters/FETurbulence.h b/Source/WebCore/platform/graphics/filters/FETurbulence.h
index 84ff11261..4987e3d97 100644
--- a/Source/WebCore/platform/graphics/filters/FETurbulence.h
+++ b/Source/WebCore/platform/graphics/filters/FETurbulence.h
@@ -24,7 +24,6 @@
#ifndef FETurbulence_h
#define FETurbulence_h
-#if ENABLE(FILTERS)
#include "FilterEffect.h"
#include "Filter.h"
@@ -38,7 +37,7 @@ enum TurbulenceType {
class FETurbulence : public FilterEffect {
public:
- static PassRefPtr<FETurbulence> create(Filter*, TurbulenceType, float, float, int, float, bool);
+ static Ref<FETurbulence> create(Filter&, TurbulenceType, float, float, int, float, bool);
TurbulenceType type() const;
bool setType(TurbulenceType);
@@ -60,15 +59,12 @@ public:
static void fillRegionWorker(void*);
- virtual void platformApplySoftware();
-#if ENABLE(OPENCL)
- virtual bool platformApplyOpenCL();
-#endif
- virtual void dump();
+ void platformApplySoftware() override;
+ void dump() override;
- virtual void determineAbsolutePaintRect() { setAbsolutePaintRect(enclosingIntRect(maxEffectRect())); }
+ void determineAbsolutePaintRect() override { setAbsolutePaintRect(enclosingIntRect(maxEffectRect())); }
- virtual TextStream& externalRepresentation(TextStream&, int indention) const;
+ TextStream& externalRepresentation(TextStream&, int indention) const override;
private:
static const int s_blockSize = 256;
@@ -119,7 +115,7 @@ private:
static void fillRegionWorker(FillRegionParameters*);
- FETurbulence(Filter*, TurbulenceType, float, float, int, float, bool);
+ FETurbulence(Filter&, TurbulenceType, float, float, int, float, bool);
inline void initPaint(PaintingData&);
float noise2D(int channel, PaintingData&, StitchData&, const FloatPoint&);
@@ -136,6 +132,4 @@ private:
} // namespace WebCore
-#endif // ENABLE(FILTERS)
-
#endif // FETurbulence_h
diff --git a/Source/WebCore/platform/graphics/filters/Filter.h b/Source/WebCore/platform/graphics/filters/Filter.h
index cea0ff2f4..6d344ed87 100644
--- a/Source/WebCore/platform/graphics/filters/Filter.h
+++ b/Source/WebCore/platform/graphics/filters/Filter.h
@@ -18,35 +18,32 @@
* Boston, MA 02110-1301, USA.
*/
-#ifndef Filter_h
-#define Filter_h
+#pragma once
-#if ENABLE(FILTERS)
-#include "FloatRect.h"
#include "FloatSize.h"
#include "ImageBuffer.h"
-#include <wtf/RefCounted.h>
namespace WebCore {
-class FilterEffect;
-
class Filter : public RefCounted<Filter> {
public:
- Filter(const AffineTransform& absoluteTransform)
+ Filter(const AffineTransform& absoluteTransform, float filterScale = 1)
: m_absoluteTransform(absoluteTransform)
- , m_renderingMode(Unaccelerated)
+ , m_filterScale(filterScale)
{ }
virtual ~Filter() { }
- void setSourceImage(std::unique_ptr<ImageBuffer> sourceImage) { m_sourceImage = std::move(sourceImage); }
+ void setSourceImage(std::unique_ptr<ImageBuffer> sourceImage) { m_sourceImage = WTFMove(sourceImage); }
ImageBuffer* sourceImage() { return m_sourceImage.get(); }
FloatSize filterResolution() const { return m_filterResolution; }
void setFilterResolution(const FloatSize& filterResolution) { m_filterResolution = filterResolution; }
+ float filterScale() const { return m_filterScale; }
+ void setFilterScale(float scale) { m_filterScale = scale; }
+
const AffineTransform& absoluteTransform() const { return m_absoluteTransform; }
- FloatPoint mapAbsolutePointToLocalPoint(const FloatPoint& point) const { return m_absoluteTransform.inverse().mapPoint(point); }
+ FloatPoint mapAbsolutePointToLocalPoint(const FloatPoint& point) const { return m_absoluteTransform.inverse().value_or(AffineTransform()).mapPoint(point); }
RenderingMode renderingMode() const { return m_renderingMode; }
void setRenderingMode(RenderingMode renderingMode) { m_renderingMode = renderingMode; }
@@ -59,18 +56,18 @@ public:
virtual FloatRect sourceImageRect() const = 0;
virtual FloatRect filterRegion() const = 0;
+protected:
+ explicit Filter(const FloatSize& filterResolution)
+ : m_filterResolution(filterResolution)
+ {
+ }
+
private:
std::unique_ptr<ImageBuffer> m_sourceImage;
FloatSize m_filterResolution;
AffineTransform m_absoluteTransform;
- RenderingMode m_renderingMode;
+ RenderingMode m_renderingMode { Unaccelerated };
+ float m_filterScale { 1 };
};
-#define FILTER_TYPE_CASTS(ToValueTypeName, predicate) \
- TYPE_CASTS_BASE(ToValueTypeName, Filter, filter, filter->predicate, filter.predicate)
-
} // namespace WebCore
-
-#endif // ENABLE(FILTERS)
-
-#endif // Filter_h
diff --git a/Source/WebCore/platform/graphics/filters/FilterEffect.cpp b/Source/WebCore/platform/graphics/filters/FilterEffect.cpp
index 62cc4e6ec..2f742ca17 100644
--- a/Source/WebCore/platform/graphics/filters/FilterEffect.cpp
+++ b/Source/WebCore/platform/graphics/filters/FilterEffect.cpp
@@ -3,6 +3,7 @@
* Copyright (C) 2009 Dirk Schulze <krit@webkit.org>
* Copyright (C) Research In Motion Limited 2010. All rights reserved.
* Copyright (C) 2012 University of Szeged
+ * Copyright (C) 2015-2016 Apple Inc. All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
@@ -21,14 +22,12 @@
*/
#include "config.h"
-
-#if ENABLE(FILTERS)
#include "FilterEffect.h"
#include "Filter.h"
#include "ImageBuffer.h"
#include "TextStream.h"
-#include <runtime/Operations.h>
+#include <runtime/JSCInlines.h>
#include <runtime/TypedArrayInlines.h>
#include <runtime/Uint8ClampedArray.h>
@@ -38,7 +37,7 @@
namespace WebCore {
-FilterEffect::FilterEffect(Filter* filter)
+FilterEffect::FilterEffect(Filter& filter)
: m_alphaImage(false)
, m_filter(filter)
, m_hasX(false)
@@ -47,23 +46,14 @@ FilterEffect::FilterEffect(Filter* filter)
, m_hasHeight(false)
, m_clipsToBounds(true)
, m_operatingColorSpace(ColorSpaceLinearRGB)
- , m_resultColorSpace(ColorSpaceDeviceRGB)
+ , m_resultColorSpace(ColorSpaceSRGB)
{
- ASSERT(m_filter);
}
FilterEffect::~FilterEffect()
{
}
-inline bool isFilterSizeValid(IntRect rect)
-{
- if (rect.width() < 0 || rect.width() > kMaxFilterSize
- || rect.height() < 0 || rect.height() > kMaxFilterSize)
- return false;
- return true;
-}
-
void FilterEffect::determineAbsolutePaintRect()
{
m_absolutePaintRect = IntRect();
@@ -89,10 +79,16 @@ IntRect FilterEffect::requestedRegionOfInputImageData(const IntRect& effectRect)
return IntRect(location, m_absolutePaintRect.size());
}
-IntRect FilterEffect::drawingRegionOfInputImage(const IntRect& srcRect) const
+FloatRect FilterEffect::drawingRegionOfInputImage(const IntRect& srcRect) const
{
- return IntRect(IntPoint(srcRect.x() - m_absolutePaintRect.x(),
- srcRect.y() - m_absolutePaintRect.y()), srcRect.size());
+ ASSERT(hasResult());
+
+ FloatSize scale;
+ ImageBuffer::clampedSize(m_absolutePaintRect.size(), scale);
+
+ AffineTransform transform;
+ transform.scale(scale).translate(-m_absolutePaintRect.location());
+ return transform.mapRect(srcRect);
}
FilterEffect* FilterEffect::inputEffect(unsigned number) const
@@ -118,24 +114,6 @@ unsigned FilterEffect::totalNumberOfEffectInputs() const
return collectEffects(this, allEffects);
}
-#if ENABLE(OPENCL)
-void FilterEffect::applyAll()
-{
- if (hasResult())
- return;
- FilterContextOpenCL* context = FilterContextOpenCL::context();
- if (context) {
- apply();
- if (!context->inError())
- return;
- clearResultsRecursive();
- context->destroyContext();
- }
- // Software code path.
- apply();
-}
-#endif
-
void FilterEffect::apply()
{
if (hasResult())
@@ -154,7 +132,7 @@ void FilterEffect::apply()
determineAbsolutePaintRect();
setResultColorSpace(m_operatingColorSpace);
- if (!isFilterSizeValid(m_absolutePaintRect))
+ if (m_absolutePaintRect.isEmpty() || ImageBuffer::sizeNeedsClamping(m_absolutePaintRect.size()))
return;
if (requiresValidPreMultipliedPixels()) {
@@ -163,38 +141,9 @@ void FilterEffect::apply()
}
// Add platform specific apply functions here and return earlier.
-#if ENABLE(OPENCL)
- if (platformApplyOpenCL())
- return;
-#endif
platformApplySoftware();
}
-#if ENABLE(OPENCL)
-// This function will be changed to abstract virtual when all filters are landed.
-bool FilterEffect::platformApplyOpenCL()
-{
- if (!FilterContextOpenCL::context())
- return false;
-
- unsigned size = m_inputEffects.size();
- for (unsigned i = 0; i < size; ++i) {
- FilterEffect* in = m_inputEffects.at(i).get();
- // Software code path expects that at least one of the following fileds is valid.
- if (!in->m_imageBufferResult && !in->m_unmultipliedImageResult && !in->m_premultipliedImageResult)
- in->asImageBuffer();
- }
-
- platformApplySoftware();
- ImageBuffer* sourceImage = asImageBuffer();
- if (sourceImage) {
- RefPtr<Uint8ClampedArray> sourceImageData = sourceImage->getUnmultipliedImageData(IntRect(IntPoint(), sourceImage->internalSize()));
- createOpenCLImageResult(sourceImageData->data());
- }
- return true;
-}
-#endif
-
void FilterEffect::forceValidPreMultipliedPixels()
{
// Must operate on pre-multiplied results; other formats cannot have invalid pixels.
@@ -248,14 +197,9 @@ void FilterEffect::clearResult()
{
if (m_imageBufferResult)
m_imageBufferResult.reset();
- if (m_unmultipliedImageResult)
- m_unmultipliedImageResult.clear();
- if (m_premultipliedImageResult)
- m_premultipliedImageResult.clear();
-#if ENABLE(OPENCL)
- if (m_openCLImageResult)
- m_openCLImageResult.clear();
-#endif
+
+ m_unmultipliedImageResult = nullptr;
+ m_premultipliedImageResult = nullptr;
}
void FilterEffect::clearResultsRecursive()
@@ -273,14 +217,13 @@ void FilterEffect::clearResultsRecursive()
ImageBuffer* FilterEffect::asImageBuffer()
{
if (!hasResult())
- return 0;
+ return nullptr;
if (m_imageBufferResult)
return m_imageBufferResult.get();
-#if ENABLE(OPENCL)
- if (m_openCLImageResult)
- return openCLImageToImageBuffer();
-#endif
- m_imageBufferResult = ImageBuffer::create(m_absolutePaintRect.size(), 1, m_resultColorSpace, m_filter->renderingMode());
+ m_imageBufferResult = ImageBuffer::create(m_absolutePaintRect.size(), m_filter.renderingMode(), m_filter.filterScale(), m_resultColorSpace);
+ if (!m_imageBufferResult)
+ return nullptr;
+
IntRect destinationRect(IntPoint(), m_absolutePaintRect.size());
if (m_premultipliedImageResult)
m_imageBufferResult->putByteArray(Premultiplied, m_premultipliedImageResult.get(), destinationRect.size(), destinationRect, IntPoint());
@@ -289,85 +232,69 @@ ImageBuffer* FilterEffect::asImageBuffer()
return m_imageBufferResult.get();
}
-#if ENABLE(OPENCL)
-ImageBuffer* FilterEffect::openCLImageToImageBuffer()
-{
- FilterContextOpenCL* context = FilterContextOpenCL::context();
- ASSERT(context);
-
- if (context->inError())
- return 0;
-
- size_t origin[3] = { 0, 0, 0 };
- size_t region[3] = { m_absolutePaintRect.width(), m_absolutePaintRect.height(), 1 };
-
- RefPtr<Uint8ClampedArray> destinationPixelArray = Uint8ClampedArray::create(m_absolutePaintRect.width() * m_absolutePaintRect.height() * 4);
-
- if (context->isFailed(clFinish(context->commandQueue())))
- return 0;
-
- if (context->isFailed(clEnqueueReadImage(context->commandQueue(), m_openCLImageResult, CL_TRUE, origin, region, 0, 0, destinationPixelArray->data(), 0, 0, 0)))
- return 0;
-
- m_imageBufferResult = ImageBuffer::create(m_absolutePaintRect.size());
- IntRect destinationRect(IntPoint(), m_absolutePaintRect.size());
- m_imageBufferResult->putByteArray(Unmultiplied, destinationPixelArray.get(), destinationRect.size(), destinationRect, IntPoint());
-
- return m_imageBufferResult.get();
-}
-#endif
-
PassRefPtr<Uint8ClampedArray> FilterEffect::asUnmultipliedImage(const IntRect& rect)
{
- ASSERT(isFilterSizeValid(rect));
- RefPtr<Uint8ClampedArray> imageData = Uint8ClampedArray::createUninitialized(rect.width() * rect.height() * 4);
+ IntSize scaledSize(rect.size());
+ ASSERT(!ImageBuffer::sizeNeedsClamping(scaledSize));
+ scaledSize.scale(m_filter.filterScale());
+ auto imageData = Uint8ClampedArray::createUninitialized((scaledSize.area() * 4).unsafeGet());
copyUnmultipliedImage(imageData.get(), rect);
- return imageData.release();
+ return WTFMove(imageData);
}
PassRefPtr<Uint8ClampedArray> FilterEffect::asPremultipliedImage(const IntRect& rect)
{
- ASSERT(isFilterSizeValid(rect));
- RefPtr<Uint8ClampedArray> imageData = Uint8ClampedArray::createUninitialized(rect.width() * rect.height() * 4);
+ IntSize scaledSize(rect.size());
+ ASSERT(!ImageBuffer::sizeNeedsClamping(scaledSize));
+ scaledSize.scale(m_filter.filterScale());
+ auto imageData = Uint8ClampedArray::createUninitialized((scaledSize.area() * 4).unsafeGet());
copyPremultipliedImage(imageData.get(), rect);
- return imageData.release();
+ return WTFMove(imageData);
}
inline void FilterEffect::copyImageBytes(Uint8ClampedArray* source, Uint8ClampedArray* destination, const IntRect& rect)
{
+ IntRect scaledRect(rect);
+ scaledRect.scale(m_filter.filterScale());
+ IntSize scaledPaintSize(m_absolutePaintRect.size());
+ scaledPaintSize.scale(m_filter.filterScale());
+
+ if (!source || !destination)
+ return;
+
// Initialize the destination to transparent black, if not entirely covered by the source.
- if (rect.x() < 0 || rect.y() < 0 || rect.maxX() > m_absolutePaintRect.width() || rect.maxY() > m_absolutePaintRect.height())
+ if (scaledRect.x() < 0 || scaledRect.y() < 0 || scaledRect.maxX() > scaledPaintSize.width() || scaledRect.maxY() > scaledPaintSize.height())
memset(destination->data(), 0, destination->length());
// Early return if the rect does not intersect with the source.
- if (rect.maxX() <= 0 || rect.maxY() <= 0 || rect.x() >= m_absolutePaintRect.width() || rect.y() >= m_absolutePaintRect.height())
+ if (scaledRect.maxX() <= 0 || scaledRect.maxY() <= 0 || scaledRect.x() >= scaledPaintSize.width() || scaledRect.y() >= scaledPaintSize.height())
return;
- int xOrigin = rect.x();
+ int xOrigin = scaledRect.x();
int xDest = 0;
if (xOrigin < 0) {
xDest = -xOrigin;
xOrigin = 0;
}
- int xEnd = rect.maxX();
- if (xEnd > m_absolutePaintRect.width())
- xEnd = m_absolutePaintRect.width();
+ int xEnd = scaledRect.maxX();
+ if (xEnd > scaledPaintSize.width())
+ xEnd = scaledPaintSize.width();
- int yOrigin = rect.y();
+ int yOrigin = scaledRect.y();
int yDest = 0;
if (yOrigin < 0) {
yDest = -yOrigin;
yOrigin = 0;
}
- int yEnd = rect.maxY();
- if (yEnd > m_absolutePaintRect.height())
- yEnd = m_absolutePaintRect.height();
+ int yEnd = scaledRect.maxY();
+ if (yEnd > scaledPaintSize.height())
+ yEnd = scaledPaintSize.height();
int size = (xEnd - xOrigin) * 4;
- int destinationScanline = rect.width() * 4;
- int sourceScanline = m_absolutePaintRect.width() * 4;
- unsigned char *destinationPixel = destination->data() + ((yDest * rect.width()) + xDest) * 4;
- unsigned char *sourcePixel = source->data() + ((yOrigin * m_absolutePaintRect.width()) + xOrigin) * 4;
+ int destinationScanline = scaledRect.width() * 4;
+ int sourceScanline = scaledPaintSize.width() * 4;
+ unsigned char *destinationPixel = destination->data() + ((yDest * scaledRect.width()) + xDest) * 4;
+ unsigned char *sourcePixel = source->data() + ((yOrigin * scaledPaintSize.width()) + xOrigin) * 4;
while (yOrigin < yEnd) {
memcpy(destinationPixel, sourcePixel, size);
@@ -386,11 +313,17 @@ void FilterEffect::copyUnmultipliedImage(Uint8ClampedArray* destination, const I
if (m_imageBufferResult)
m_unmultipliedImageResult = m_imageBufferResult->getUnmultipliedImageData(IntRect(IntPoint(), m_absolutePaintRect.size()));
else {
- ASSERT(isFilterSizeValid(m_absolutePaintRect));
- m_unmultipliedImageResult = Uint8ClampedArray::createUninitialized(m_absolutePaintRect.width() * m_absolutePaintRect.height() * 4);
+ IntSize inputSize(m_absolutePaintRect.size());
+ ASSERT(!ImageBuffer::sizeNeedsClamping(inputSize));
+ inputSize.scale(m_filter.filterScale());
+ m_unmultipliedImageResult = Uint8ClampedArray::createUninitialized((inputSize.area() * 4).unsafeGet());
+ if (!m_unmultipliedImageResult) {
+ WTFLogAlways("FilterEffect::copyUnmultipliedImage Unable to create buffer. Requested size was %d x %d\n", inputSize.width(), inputSize.height());
+ return;
+ }
unsigned char* sourceComponent = m_premultipliedImageResult->data();
unsigned char* destinationComponent = m_unmultipliedImageResult->data();
- unsigned char* end = sourceComponent + (m_absolutePaintRect.width() * m_absolutePaintRect.height() * 4);
+ unsigned char* end = sourceComponent + (inputSize.area() * 4).unsafeGet();
while (sourceComponent < end) {
int alpha = sourceComponent[3];
if (alpha) {
@@ -420,11 +353,17 @@ void FilterEffect::copyPremultipliedImage(Uint8ClampedArray* destination, const
if (m_imageBufferResult)
m_premultipliedImageResult = m_imageBufferResult->getPremultipliedImageData(IntRect(IntPoint(), m_absolutePaintRect.size()));
else {
- ASSERT(isFilterSizeValid(m_absolutePaintRect));
- m_premultipliedImageResult = Uint8ClampedArray::createUninitialized(m_absolutePaintRect.width() * m_absolutePaintRect.height() * 4);
+ IntSize inputSize(m_absolutePaintRect.size());
+ ASSERT(!ImageBuffer::sizeNeedsClamping(inputSize));
+ inputSize.scale(m_filter.filterScale());
+ m_premultipliedImageResult = Uint8ClampedArray::createUninitialized((inputSize.area() * 4).unsafeGet());
+ if (!m_premultipliedImageResult) {
+ WTFLogAlways("FilterEffect::copyPremultipliedImage Unable to create buffer. Requested size was %d x %d\n", inputSize.width(), inputSize.height());
+ return;
+ }
unsigned char* sourceComponent = m_unmultipliedImageResult->data();
unsigned char* destinationComponent = m_premultipliedImageResult->data();
- unsigned char* end = sourceComponent + (m_absolutePaintRect.width() * m_absolutePaintRect.height() * 4);
+ unsigned char* end = sourceComponent + (inputSize.area() * 4).unsafeGet();
while (sourceComponent < end) {
int alpha = sourceComponent[3];
destinationComponent[0] = static_cast<int>(sourceComponent[0]) * alpha / 255;
@@ -444,11 +383,13 @@ ImageBuffer* FilterEffect::createImageBufferResult()
// Only one result type is allowed.
ASSERT(!hasResult());
if (m_absolutePaintRect.isEmpty())
- return 0;
- m_imageBufferResult = ImageBuffer::create(m_absolutePaintRect.size(), 1, m_resultColorSpace, m_filter->renderingMode());
+ return nullptr;
+
+ FloatSize clampedSize = ImageBuffer::clampedSize(m_absolutePaintRect.size());
+ m_imageBufferResult = ImageBuffer::create(clampedSize, m_filter.renderingMode(), m_filter.filterScale(), m_resultColorSpace);
if (!m_imageBufferResult)
- return 0;
- ASSERT(m_imageBufferResult->context());
+ return nullptr;
+
return m_imageBufferResult.get();
}
@@ -456,11 +397,13 @@ Uint8ClampedArray* FilterEffect::createUnmultipliedImageResult()
{
// Only one result type is allowed.
ASSERT(!hasResult());
- ASSERT(isFilterSizeValid(m_absolutePaintRect));
-
if (m_absolutePaintRect.isEmpty())
- return 0;
- m_unmultipliedImageResult = Uint8ClampedArray::createUninitialized(m_absolutePaintRect.width() * m_absolutePaintRect.height() * 4);
+ return nullptr;
+
+ IntSize resultSize(m_absolutePaintRect.size());
+ ASSERT(!ImageBuffer::sizeNeedsClamping(resultSize));
+ resultSize.scale(m_filter.filterScale());
+ m_unmultipliedImageResult = Uint8ClampedArray::createUninitialized((resultSize.area() * 4).unsafeGet());
return m_unmultipliedImageResult.get();
}
@@ -468,43 +411,15 @@ Uint8ClampedArray* FilterEffect::createPremultipliedImageResult()
{
// Only one result type is allowed.
ASSERT(!hasResult());
- ASSERT(isFilterSizeValid(m_absolutePaintRect));
-
if (m_absolutePaintRect.isEmpty())
- return 0;
- m_premultipliedImageResult = Uint8ClampedArray::createUninitialized(m_absolutePaintRect.width() * m_absolutePaintRect.height() * 4);
- return m_premultipliedImageResult.get();
-}
-
-#if ENABLE(OPENCL)
-OpenCLHandle FilterEffect::createOpenCLImageResult(uint8_t* source)
-{
- FilterContextOpenCL* context = FilterContextOpenCL::context();
- ASSERT(context);
-
- if (context->inError())
- return 0;
-
- ASSERT(!hasResult());
- cl_image_format clImageFormat;
- clImageFormat.image_channel_order = CL_RGBA;
- clImageFormat.image_channel_data_type = CL_UNORM_INT8;
-
- int errorCode = 0;
-#ifdef CL_API_SUFFIX__VERSION_1_2
- cl_image_desc imageDescriptor = { CL_MEM_OBJECT_IMAGE2D, m_absolutePaintRect.width(), m_absolutePaintRect.height(), 0, 0, 0, 0, 0, 0, 0};
- m_openCLImageResult = clCreateImage(context->deviceContext(), CL_MEM_READ_WRITE | (source ? CL_MEM_COPY_HOST_PTR : 0),
- &clImageFormat, &imageDescriptor, source, &errorCode);
-#else
- m_openCLImageResult = clCreateImage2D(context->deviceContext(), CL_MEM_READ_WRITE | (source ? CL_MEM_COPY_HOST_PTR : 0),
- &clImageFormat, m_absolutePaintRect.width(), m_absolutePaintRect.height(), 0, source, &errorCode);
-#endif
- if (context->isFailed(errorCode))
- return 0;
+ return nullptr;
- return m_openCLImageResult;
+ IntSize resultSize(m_absolutePaintRect.size());
+ ASSERT(!ImageBuffer::sizeNeedsClamping(resultSize));
+ resultSize.scale(m_filter.filterScale());
+ m_premultipliedImageResult = Uint8ClampedArray::createUninitialized((resultSize.area() * 4).unsafeGet());
+ return m_premultipliedImageResult.get();
}
-#endif
void FilterEffect::transformResultColorSpace(ColorSpace dstColorSpace)
{
@@ -515,30 +430,16 @@ void FilterEffect::transformResultColorSpace(ColorSpace dstColorSpace)
if (!hasResult() || dstColorSpace == m_resultColorSpace)
return;
-#if ENABLE(OPENCL)
- if (openCLImage()) {
- if (m_imageBufferResult)
- m_imageBufferResult.clear();
- FilterContextOpenCL* context = FilterContextOpenCL::context();
- ASSERT(context);
- context->openCLTransformColorSpace(m_openCLImageResult, absolutePaintRect(), m_resultColorSpace, dstColorSpace);
- } else {
-#endif
-
- // FIXME: We can avoid this potentially unnecessary ImageBuffer conversion by adding
- // color space transform support for the {pre,un}multiplied arrays.
- asImageBuffer()->transformColorSpace(m_resultColorSpace, dstColorSpace);
-
-#if ENABLE(OPENCL)
- }
-#endif
+ // FIXME: We can avoid this potentially unnecessary ImageBuffer conversion by adding
+ // color space transform support for the {pre,un}multiplied arrays.
+ asImageBuffer()->transformColorSpace(m_resultColorSpace, dstColorSpace);
m_resultColorSpace = dstColorSpace;
if (m_unmultipliedImageResult)
- m_unmultipliedImageResult.clear();
+ m_unmultipliedImageResult = nullptr;
if (m_premultipliedImageResult)
- m_premultipliedImageResult.clear();
+ m_premultipliedImageResult = nullptr;
#endif
}
@@ -550,5 +451,3 @@ TextStream& FilterEffect::externalRepresentation(TextStream& ts, int) const
}
} // namespace WebCore
-
-#endif // ENABLE(FILTERS)
diff --git a/Source/WebCore/platform/graphics/filters/FilterEffect.h b/Source/WebCore/platform/graphics/filters/FilterEffect.h
index cb3bdc681..ea4ec776f 100644
--- a/Source/WebCore/platform/graphics/filters/FilterEffect.h
+++ b/Source/WebCore/platform/graphics/filters/FilterEffect.h
@@ -22,24 +22,15 @@
#ifndef FilterEffect_h
#define FilterEffect_h
-#if ENABLE(FILTERS)
#include "ColorSpace.h"
#include "FloatRect.h"
#include "IntRect.h"
-
#include <runtime/Uint8ClampedArray.h>
-
-#include <wtf/HashSet.h>
+#include <wtf/MathExtras.h>
#include <wtf/RefCounted.h>
#include <wtf/RefPtr.h>
#include <wtf/Vector.h>
-#if ENABLE(OPENCL)
-#include "FilterContextOpenCL.h"
-#endif
-
-static const float kMaxFilterSize = 5000.0f;
-
namespace WebCore {
class Filter;
@@ -91,7 +82,7 @@ public:
|| m_premultipliedImageResult;
}
- IntRect drawingRegionOfInputImage(const IntRect&) const;
+ FloatRect drawingRegionOfInputImage(const IntRect&) const;
IntRect requestedRegionOfInputImageData(const IntRect&) const;
// Solid black image with different alpha values.
@@ -149,7 +140,7 @@ public:
FloatRect effectBoundaries() const { return m_effectBoundaries; }
void setEffectBoundaries(const FloatRect& effectBoundaries) { m_effectBoundaries = effectBoundaries; }
- Filter* filter() { return m_filter; }
+ Filter& filter() { return m_filter; }
bool clipsToBounds() const { return m_clipsToBounds; }
void setClipsToBounds(bool value) { m_clipsToBounds = value; }
@@ -163,7 +154,7 @@ public:
void transformResultColorSpace(ColorSpace);
protected:
- FilterEffect(Filter*);
+ FilterEffect(Filter&);
ImageBuffer* createImageBufferResult();
Uint8ClampedArray* createUnmultipliedImageResult();
@@ -180,6 +171,14 @@ protected:
void forceValidPreMultipliedPixels();
void clipAbsolutePaintRect();
+
+ static Vector<float> normalizedFloats(const Vector<float>& values)
+ {
+ Vector<float> normalizedValues(values.size());
+ for (size_t i = 0; i < values.size(); ++i)
+ normalizedValues[i] = normalizedFloat(values[i]);
+ return normalizedValues;
+ }
private:
std::unique_ptr<ImageBuffer> m_imageBufferResult;
@@ -197,7 +196,7 @@ private:
// The maximum size of a filter primitive. In SVG this is the primitive subregion in absolute coordinate space.
// The absolute paint rect should never be bigger than m_maxEffectRect.
FloatRect m_maxEffectRect;
- Filter* m_filter;
+ Filter& m_filter;
private:
inline void copyImageBytes(Uint8ClampedArray* source, Uint8ClampedArray* destination, const IntRect&);
@@ -226,6 +225,4 @@ private:
} // namespace WebCore
-#endif // ENABLE(FILTERS)
-
#endif // FilterEffect_h
diff --git a/Source/WebCore/platform/graphics/filters/FilterOperation.cpp b/Source/WebCore/platform/graphics/filters/FilterOperation.cpp
index 7f517c5e8..035e45bb0 100644
--- a/Source/WebCore/platform/graphics/filters/FilterOperation.cpp
+++ b/Source/WebCore/platform/graphics/filters/FilterOperation.cpp
@@ -10,10 +10,10 @@
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
- * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
@@ -24,20 +24,27 @@
*/
#include "config.h"
-
-#if ENABLE(CSS_FILTERS)
#include "FilterOperation.h"
#include "AnimationUtilities.h"
-
-#if ENABLE(SVG)
+#include "CachedResourceLoader.h"
#include "CachedSVGDocumentReference.h"
-#endif
+#include "FilterEffect.h"
+#include "SVGURIReference.h"
+#include "TextStream.h"
namespace WebCore {
+
+bool DefaultFilterOperation::operator==(const FilterOperation& operation) const
+{
+ if (!isSameType(operation))
+ return false;
+
+ return representedType() == downcast<DefaultFilterOperation>(operation).representedType();
+}
-ReferenceFilterOperation::ReferenceFilterOperation(const String& url, const String& fragment, OperationType type)
- : FilterOperation(type)
+ReferenceFilterOperation::ReferenceFilterOperation(const String& url, const String& fragment)
+ : FilterOperation(REFERENCE)
, m_url(url)
, m_fragment(fragment)
{
@@ -46,15 +53,29 @@ ReferenceFilterOperation::ReferenceFilterOperation(const String& url, const Stri
ReferenceFilterOperation::~ReferenceFilterOperation()
{
}
+
+bool ReferenceFilterOperation::operator==(const FilterOperation& operation) const
+{
+ if (!isSameType(operation))
+ return false;
+
+ return m_url == downcast<ReferenceFilterOperation>(operation).m_url;
+}
-#if ENABLE(SVG)
-CachedSVGDocumentReference* ReferenceFilterOperation::getOrCreateCachedSVGDocumentReference()
+void ReferenceFilterOperation::loadExternalDocumentIfNeeded(CachedResourceLoader& cachedResourceLoader, const ResourceLoaderOptions& options)
{
- if (!m_cachedSVGDocumentReference)
- m_cachedSVGDocumentReference = std::make_unique<CachedSVGDocumentReference>(m_url);
- return m_cachedSVGDocumentReference.get();
+ if (m_cachedSVGDocumentReference)
+ return;
+ if (!SVGURIReference::isExternalURIReference(m_url, *cachedResourceLoader.document()))
+ return;
+ m_cachedSVGDocumentReference = std::make_unique<CachedSVGDocumentReference>(m_url);
+ m_cachedSVGDocumentReference->load(cachedResourceLoader, options);
+}
+
+void ReferenceFilterOperation::setFilterEffect(PassRefPtr<FilterEffect> filterEffect)
+{
+ m_filterEffect = filterEffect;
}
-#endif
PassRefPtr<FilterOperation> BasicColorMatrixFilterOperation::blend(const FilterOperation* from, double progress, bool blendToPassthrough)
{
@@ -64,11 +85,19 @@ PassRefPtr<FilterOperation> BasicColorMatrixFilterOperation::blend(const FilterO
if (blendToPassthrough)
return BasicColorMatrixFilterOperation::create(WebCore::blend(m_amount, passthroughAmount(), progress), m_type);
- const BasicColorMatrixFilterOperation* fromOp = static_cast<const BasicColorMatrixFilterOperation*>(from);
- double fromAmount = fromOp ? fromOp->amount() : passthroughAmount();
+ const BasicColorMatrixFilterOperation* fromOperation = downcast<BasicColorMatrixFilterOperation>(from);
+ double fromAmount = fromOperation ? fromOperation->amount() : passthroughAmount();
return BasicColorMatrixFilterOperation::create(WebCore::blend(fromAmount, m_amount, progress), m_type);
}
+inline bool BasicColorMatrixFilterOperation::operator==(const FilterOperation& operation) const
+{
+ if (!isSameType(operation))
+ return false;
+ const BasicColorMatrixFilterOperation& other = downcast<BasicColorMatrixFilterOperation>(operation);
+ return m_amount == other.m_amount;
+}
+
double BasicColorMatrixFilterOperation::passthroughAmount() const
{
switch (m_type) {
@@ -92,11 +121,19 @@ PassRefPtr<FilterOperation> BasicComponentTransferFilterOperation::blend(const F
if (blendToPassthrough)
return BasicComponentTransferFilterOperation::create(WebCore::blend(m_amount, passthroughAmount(), progress), m_type);
- const BasicComponentTransferFilterOperation* fromOp = static_cast<const BasicComponentTransferFilterOperation*>(from);
- double fromAmount = fromOp ? fromOp->amount() : passthroughAmount();
+ const BasicComponentTransferFilterOperation* fromOperation = downcast<BasicComponentTransferFilterOperation>(from);
+ double fromAmount = fromOperation ? fromOperation->amount() : passthroughAmount();
return BasicComponentTransferFilterOperation::create(WebCore::blend(fromAmount, m_amount, progress), m_type);
}
+inline bool BasicComponentTransferFilterOperation::operator==(const FilterOperation& operation) const
+{
+ if (!isSameType(operation))
+ return false;
+ const BasicComponentTransferFilterOperation& other = downcast<BasicComponentTransferFilterOperation>(operation);
+ return m_amount == other.m_amount;
+}
+
double BasicComponentTransferFilterOperation::passthroughAmount() const
{
switch (m_type) {
@@ -113,7 +150,15 @@ double BasicComponentTransferFilterOperation::passthroughAmount() const
return 0;
}
}
-
+
+bool BlurFilterOperation::operator==(const FilterOperation& operation) const
+{
+ if (!isSameType(operation))
+ return false;
+
+ return m_stdDeviation == downcast<BlurFilterOperation>(operation).stdDeviation();
+}
+
PassRefPtr<FilterOperation> BlurFilterOperation::blend(const FilterOperation* from, double progress, bool blendToPassthrough)
{
if (from && !from->isSameType(*this))
@@ -122,13 +167,21 @@ PassRefPtr<FilterOperation> BlurFilterOperation::blend(const FilterOperation* fr
LengthType lengthType = m_stdDeviation.type();
if (blendToPassthrough)
- return BlurFilterOperation::create(Length(lengthType).blend(m_stdDeviation, progress), m_type);
+ return BlurFilterOperation::create(WebCore::blend(m_stdDeviation, Length(lengthType), progress));
- const BlurFilterOperation* fromOp = static_cast<const BlurFilterOperation*>(from);
- Length fromLength = fromOp ? fromOp->m_stdDeviation : Length(lengthType);
- return BlurFilterOperation::create(m_stdDeviation.blend(fromLength, progress), m_type);
+ const BlurFilterOperation* fromOperation = downcast<BlurFilterOperation>(from);
+ Length fromLength = fromOperation ? fromOperation->m_stdDeviation : Length(lengthType);
+ return BlurFilterOperation::create(WebCore::blend(fromLength, m_stdDeviation, progress));
}
-
+
+bool DropShadowFilterOperation::operator==(const FilterOperation& operation) const
+{
+ if (!isSameType(operation))
+ return false;
+ const DropShadowFilterOperation& other = downcast<DropShadowFilterOperation>(operation);
+ return m_location == other.m_location && m_stdDeviation == other.m_stdDeviation && m_color == other.m_color;
+}
+
PassRefPtr<FilterOperation> DropShadowFilterOperation::blend(const FilterOperation* from, double progress, bool blendToPassthrough)
{
if (from && !from->isSameType(*this))
@@ -138,20 +191,89 @@ PassRefPtr<FilterOperation> DropShadowFilterOperation::blend(const FilterOperati
return DropShadowFilterOperation::create(
WebCore::blend(m_location, IntPoint(), progress),
WebCore::blend(m_stdDeviation, 0, progress),
- WebCore::blend(m_color, Color(Color::transparent), progress),
- m_type);
+ WebCore::blend(m_color, Color(Color::transparent), progress));
- const DropShadowFilterOperation* fromOp = static_cast<const DropShadowFilterOperation*>(from);
- IntPoint fromLocation = fromOp ? fromOp->location() : IntPoint();
- int fromStdDeviation = fromOp ? fromOp->stdDeviation() : 0;
- Color fromColor = fromOp ? fromOp->color() : Color(Color::transparent);
+ const DropShadowFilterOperation* fromOperation = downcast<DropShadowFilterOperation>(from);
+ IntPoint fromLocation = fromOperation ? fromOperation->location() : IntPoint();
+ int fromStdDeviation = fromOperation ? fromOperation->stdDeviation() : 0;
+ Color fromColor = fromOperation ? fromOperation->color() : Color(Color::transparent);
return DropShadowFilterOperation::create(
WebCore::blend(fromLocation, m_location, progress),
WebCore::blend(fromStdDeviation, m_stdDeviation, progress),
- WebCore::blend(fromColor, m_color, progress), m_type);
+ WebCore::blend(fromColor, m_color, progress));
}
-} // namespace WebCore
+TextStream& operator<<(TextStream& ts, const FilterOperation& filter)
+{
+ switch (filter.type()) {
+ case FilterOperation::REFERENCE:
+ ts << "reference";
+ break;
+ case FilterOperation::GRAYSCALE: {
+ const auto& colorMatrixFilter = downcast<BasicColorMatrixFilterOperation>(filter);
+ ts << "grayscale(" << colorMatrixFilter.amount() << ")";
+ break;
+ }
+ case FilterOperation::SEPIA: {
+ const auto& colorMatrixFilter = downcast<BasicColorMatrixFilterOperation>(filter);
+ ts << "sepia(" << colorMatrixFilter.amount() << ")";
+ break;
+ }
+ case FilterOperation::SATURATE: {
+ const auto& colorMatrixFilter = downcast<BasicColorMatrixFilterOperation>(filter);
+ ts << "saturate(" << colorMatrixFilter.amount() << ")";
+ break;
+ }
+ case FilterOperation::HUE_ROTATE: {
+ const auto& colorMatrixFilter = downcast<BasicColorMatrixFilterOperation>(filter);
+ ts << "hue-rotate(" << colorMatrixFilter.amount() << ")";
+ break;
+ }
+ case FilterOperation::INVERT: {
+ const auto& componentTransferFilter = downcast<BasicComponentTransferFilterOperation>(filter);
+ ts << "invert(" << componentTransferFilter.amount() << ")";
+ break;
+ }
+ case FilterOperation::OPACITY: {
+ const auto& componentTransferFilter = downcast<BasicComponentTransferFilterOperation>(filter);
+ ts << "opacity(" << componentTransferFilter.amount() << ")";
+ break;
+ }
+ case FilterOperation::BRIGHTNESS: {
+ const auto& componentTransferFilter = downcast<BasicComponentTransferFilterOperation>(filter);
+ ts << "brightness(" << componentTransferFilter.amount() << ")";
+ break;
+ }
+ case FilterOperation::CONTRAST: {
+ const auto& componentTransferFilter = downcast<BasicComponentTransferFilterOperation>(filter);
+ ts << "contrast(" << componentTransferFilter.amount() << ")";
+ break;
+ }
+ case FilterOperation::BLUR: {
+ const auto& blurFilter = downcast<BlurFilterOperation>(filter);
+ ts << "blur(" << blurFilter.stdDeviation().value() << ")"; // FIXME: should call floatValueForLength() but that's outisde of platform/.
+ break;
+ }
+ case FilterOperation::DROP_SHADOW: {
+ const auto& dropShadowFilter = downcast<DropShadowFilterOperation>(filter);
+ ts << "drop-shadow(" << dropShadowFilter.x() << " " << dropShadowFilter.y() << " " << dropShadowFilter.location() << " ";
+ ts << dropShadowFilter.color() << ")";
+ break;
+ }
+ case FilterOperation::PASSTHROUGH:
+ ts << "passthrough";
+ break;
+ case FilterOperation::DEFAULT: {
+ const auto& defaultFilter = downcast<DefaultFilterOperation>(filter);
+ ts << "default type=" << (int)defaultFilter.representedType();
+ break;
+ }
+ case FilterOperation::NONE:
+ ts << "none";
+ break;
+ }
+ return ts;
+}
-#endif // ENABLE(CSS_FILTERS)
+} // namespace WebCore
diff --git a/Source/WebCore/platform/graphics/filters/FilterOperation.h b/Source/WebCore/platform/graphics/filters/FilterOperation.h
index 442982906..4caeb49b0 100644
--- a/Source/WebCore/platform/graphics/filters/FilterOperation.h
+++ b/Source/WebCore/platform/graphics/filters/FilterOperation.h
@@ -10,31 +10,27 @@
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
- * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef FilterOperation_h
#define FilterOperation_h
-#if ENABLE(CSS_FILTERS)
-
#include "Color.h"
-#include "FilterEffect.h"
#include "LayoutSize.h"
#include "Length.h"
-#include <wtf/OwnPtr.h>
-#include <wtf/PassOwnPtr.h>
#include <wtf/RefCounted.h>
+#include <wtf/TypeCasts.h>
#include <wtf/text/WTFString.h>
// Annoyingly, wingdi.h #defines this.
@@ -46,9 +42,10 @@ namespace WebCore {
// CSS Filters
-#if ENABLE(SVG)
+class CachedResourceLoader;
class CachedSVGDocumentReference;
-#endif
+class FilterEffect;
+struct ResourceLoaderOptions;
class FilterOperation : public RefCounted<FilterOperation> {
public:
@@ -65,35 +62,49 @@ public:
BLUR,
DROP_SHADOW,
PASSTHROUGH,
+ DEFAULT,
NONE
};
virtual ~FilterOperation() { }
+ virtual PassRefPtr<FilterOperation> clone() const = 0;
+
virtual bool operator==(const FilterOperation&) const = 0;
bool operator!=(const FilterOperation& o) const { return !(*this == o); }
virtual PassRefPtr<FilterOperation> blend(const FilterOperation* /*from*/, double /*progress*/, bool /*blendToPassthrough*/ = false)
- {
+ {
ASSERT(!blendingNeedsRendererSize());
- return 0;
+ return 0;
}
virtual PassRefPtr<FilterOperation> blend(const FilterOperation* /*from*/, double /*progress*/, const LayoutSize&, bool /*blendToPassthrough*/ = false)
- {
+ {
ASSERT(blendingNeedsRendererSize());
- return 0;
+ return 0;
+ }
+
+ OperationType type() const { return m_type; }
+
+ bool isBasicColorMatrixFilterOperation() const
+ {
+ return m_type == GRAYSCALE || m_type == SEPIA || m_type == SATURATE || m_type == HUE_ROTATE;
}
- virtual OperationType type() const { return m_type; }
- virtual bool isSameType(const FilterOperation& o) const { return o.type() == m_type; }
-
- virtual bool isDefault() const { return false; }
+ bool isBasicComponentTransferFilterOperation() const
+ {
+ return m_type == INVERT || m_type == BRIGHTNESS || m_type == CONTRAST || m_type == OPACITY;
+ }
+
+ bool isSameType(const FilterOperation& o) const { return o.type() == m_type; }
// True if the alpha channel of any pixel can change under this operation.
virtual bool affectsOpacity() const { return false; }
// True if the the value of one pixel can affect the value of another pixel under this operation, such as blur.
virtual bool movesPixels() const { return false; }
+ // True if the filter should not be allowed to work on content that is not available from this security origin.
+ virtual bool shouldBeRestrictedBySecurityOrigin() const { return false; }
// True if the filter needs the size of the box in order to calculate the animations.
virtual bool blendingNeedsRendererSize() const { return false; }
@@ -106,25 +117,30 @@ protected:
OperationType m_type;
};
-class DefaultFilterOperation : public FilterOperation {
+class WEBCORE_EXPORT DefaultFilterOperation : public FilterOperation {
public:
- static PassRefPtr<DefaultFilterOperation> create(OperationType type)
+ static PassRefPtr<DefaultFilterOperation> create(OperationType representedType)
{
- return adoptRef(new DefaultFilterOperation(type));
+ return adoptRef(new DefaultFilterOperation(representedType));
}
-private:
- virtual bool operator==(const FilterOperation& o) const override
+ PassRefPtr<FilterOperation> clone() const override
{
- return isSameType(o);
+ return adoptRef(new DefaultFilterOperation(representedType()));
}
- virtual bool isDefault() const override { return true; }
+ OperationType representedType() const { return m_representedType; }
- DefaultFilterOperation(OperationType type)
- : FilterOperation(type)
+private:
+ bool operator==(const FilterOperation&) const override;
+
+ DefaultFilterOperation(OperationType representedType)
+ : FilterOperation(DEFAULT)
+ , m_representedType(representedType)
{
}
+
+ OperationType m_representedType;
};
class PassthroughFilterOperation : public FilterOperation {
@@ -134,8 +150,13 @@ public:
return adoptRef(new PassthroughFilterOperation());
}
+ PassRefPtr<FilterOperation> clone() const override
+ {
+ return adoptRef(new PassthroughFilterOperation());
+ }
+
private:
- virtual bool operator==(const FilterOperation& o) const override
+ bool operator==(const FilterOperation& o) const override
{
return isSameType(o);
}
@@ -148,69 +169,69 @@ private:
class ReferenceFilterOperation : public FilterOperation {
public:
- static PassRefPtr<ReferenceFilterOperation> create(const String& url, const String& fragment, OperationType type)
+ static PassRefPtr<ReferenceFilterOperation> create(const String& url, const String& fragment)
{
- return adoptRef(new ReferenceFilterOperation(url, fragment, type));
+ return adoptRef(new ReferenceFilterOperation(url, fragment));
}
virtual ~ReferenceFilterOperation();
- virtual bool affectsOpacity() const override { return true; }
- virtual bool movesPixels() const override { return true; }
+ PassRefPtr<FilterOperation> clone() const override
+ {
+ // Reference filters cannot be cloned.
+ ASSERT_NOT_REACHED();
+ return nullptr;
+ }
+
+ bool affectsOpacity() const override { return true; }
+ bool movesPixels() const override { return true; }
+ // FIXME: This only needs to return true for graphs that include ConvolveMatrix, DisplacementMap, Morphology and possibly Lighting.
+ // https://bugs.webkit.org/show_bug.cgi?id=171753
+ bool shouldBeRestrictedBySecurityOrigin() const override { return true; }
const String& url() const { return m_url; }
const String& fragment() const { return m_fragment; }
-#if ENABLE(SVG)
+ void loadExternalDocumentIfNeeded(CachedResourceLoader&, const ResourceLoaderOptions&);
+
CachedSVGDocumentReference* cachedSVGDocumentReference() const { return m_cachedSVGDocumentReference.get(); }
- CachedSVGDocumentReference* getOrCreateCachedSVGDocumentReference();
-#endif
FilterEffect* filterEffect() const { return m_filterEffect.get(); }
- void setFilterEffect(PassRefPtr<FilterEffect> filterEffect) { m_filterEffect = filterEffect; }
+ void setFilterEffect(PassRefPtr<FilterEffect>);
private:
- ReferenceFilterOperation(const String& url, const String& fragment, OperationType);
+ ReferenceFilterOperation(const String& url, const String& fragment);
- virtual bool operator==(const FilterOperation& o) const override
- {
- if (!isSameType(o))
- return false;
- const ReferenceFilterOperation* other = static_cast<const ReferenceFilterOperation*>(&o);
- return m_url == other->m_url;
- }
+ bool operator==(const FilterOperation&) const override;
String m_url;
String m_fragment;
-#if ENABLE(SVG)
std::unique_ptr<CachedSVGDocumentReference> m_cachedSVGDocumentReference;
-#endif
RefPtr<FilterEffect> m_filterEffect;
};
// GRAYSCALE, SEPIA, SATURATE and HUE_ROTATE are variations on a basic color matrix effect.
// For HUE_ROTATE, the angle of rotation is stored in m_amount.
-class BasicColorMatrixFilterOperation : public FilterOperation {
+class WEBCORE_EXPORT BasicColorMatrixFilterOperation : public FilterOperation {
public:
static PassRefPtr<BasicColorMatrixFilterOperation> create(double amount, OperationType type)
{
return adoptRef(new BasicColorMatrixFilterOperation(amount, type));
}
+ PassRefPtr<FilterOperation> clone() const override
+ {
+ return adoptRef(new BasicColorMatrixFilterOperation(amount(), type()));
+ }
+
double amount() const { return m_amount; }
- virtual PassRefPtr<FilterOperation> blend(const FilterOperation* from, double progress, bool blendToPassthrough = false) override;
+ PassRefPtr<FilterOperation> blend(const FilterOperation* from, double progress, bool blendToPassthrough = false) override;
private:
- virtual bool operator==(const FilterOperation& o) const override
- {
- if (!isSameType(o))
- return false;
- const BasicColorMatrixFilterOperation* other = static_cast<const BasicColorMatrixFilterOperation*>(&o);
- return m_amount == other->m_amount;
- }
-
+ bool operator==(const FilterOperation&) const override;
+
double passthroughAmount() const;
-
+
BasicColorMatrixFilterOperation(double amount, OperationType type)
: FilterOperation(type)
, m_amount(amount)
@@ -221,27 +242,26 @@ private:
};
// INVERT, BRIGHTNESS, CONTRAST and OPACITY are variations on a basic component transfer effect.
-class BasicComponentTransferFilterOperation : public FilterOperation {
+class WEBCORE_EXPORT BasicComponentTransferFilterOperation : public FilterOperation {
public:
static PassRefPtr<BasicComponentTransferFilterOperation> create(double amount, OperationType type)
{
return adoptRef(new BasicComponentTransferFilterOperation(amount, type));
}
+ PassRefPtr<FilterOperation> clone() const override
+ {
+ return adoptRef(new BasicComponentTransferFilterOperation(amount(), type()));
+ }
+
double amount() const { return m_amount; }
- virtual bool affectsOpacity() const override { return m_type == OPACITY; }
+ bool affectsOpacity() const override { return m_type == OPACITY; }
- virtual PassRefPtr<FilterOperation> blend(const FilterOperation* from, double progress, bool blendToPassthrough = false) override;
+ PassRefPtr<FilterOperation> blend(const FilterOperation* from, double progress, bool blendToPassthrough = false) override;
private:
- virtual bool operator==(const FilterOperation& o) const override
- {
- if (!isSameType(o))
- return false;
- const BasicComponentTransferFilterOperation* other = static_cast<const BasicComponentTransferFilterOperation*>(&o);
- return m_amount == other->m_amount;
- }
+ bool operator==(const FilterOperation&) const override;
double passthroughAmount() const;
@@ -254,67 +274,65 @@ private:
double m_amount;
};
-class BlurFilterOperation : public FilterOperation {
+class WEBCORE_EXPORT BlurFilterOperation : public FilterOperation {
public:
- static PassRefPtr<BlurFilterOperation> create(Length stdDeviation, OperationType type)
+ static PassRefPtr<BlurFilterOperation> create(Length stdDeviation)
{
- return adoptRef(new BlurFilterOperation(std::move(stdDeviation), type));
+ return adoptRef(new BlurFilterOperation(WTFMove(stdDeviation)));
+ }
+
+ PassRefPtr<FilterOperation> clone() const override
+ {
+ return adoptRef(new BlurFilterOperation(stdDeviation()));
}
const Length& stdDeviation() const { return m_stdDeviation; }
- virtual bool affectsOpacity() const override { return true; }
- virtual bool movesPixels() const override { return true; }
+ bool affectsOpacity() const override { return true; }
+ bool movesPixels() const override { return true; }
- virtual PassRefPtr<FilterOperation> blend(const FilterOperation* from, double progress, bool blendToPassthrough = false) override;
+ PassRefPtr<FilterOperation> blend(const FilterOperation* from, double progress, bool blendToPassthrough = false) override;
private:
- virtual bool operator==(const FilterOperation& o) const override
- {
- if (!isSameType(o))
- return false;
- const BlurFilterOperation* other = static_cast<const BlurFilterOperation*>(&o);
- return m_stdDeviation == other->m_stdDeviation;
- }
+ bool operator==(const FilterOperation&) const override;
- BlurFilterOperation(Length stdDeviation, OperationType type)
- : FilterOperation(type)
- , m_stdDeviation(std::move(stdDeviation))
+ BlurFilterOperation(Length stdDeviation)
+ : FilterOperation(BLUR)
+ , m_stdDeviation(WTFMove(stdDeviation))
{
}
Length m_stdDeviation;
};
-class DropShadowFilterOperation : public FilterOperation {
+class WEBCORE_EXPORT DropShadowFilterOperation : public FilterOperation {
public:
- static PassRefPtr<DropShadowFilterOperation> create(const IntPoint& location, int stdDeviation, Color color, OperationType type)
+ static PassRefPtr<DropShadowFilterOperation> create(const IntPoint& location, int stdDeviation, const Color& color)
{
- return adoptRef(new DropShadowFilterOperation(location, stdDeviation, color, type));
+ return adoptRef(new DropShadowFilterOperation(location, stdDeviation, color));
+ }
+
+ PassRefPtr<FilterOperation> clone() const override
+ {
+ return adoptRef(new DropShadowFilterOperation(location(), stdDeviation(), color()));
}
int x() const { return m_location.x(); }
int y() const { return m_location.y(); }
IntPoint location() const { return m_location; }
int stdDeviation() const { return m_stdDeviation; }
- Color color() const { return m_color; }
+ const Color& color() const { return m_color; }
- virtual bool affectsOpacity() const override { return true; }
- virtual bool movesPixels() const override { return true; }
+ bool affectsOpacity() const override { return true; }
+ bool movesPixels() const override { return true; }
- virtual PassRefPtr<FilterOperation> blend(const FilterOperation* from, double progress, bool blendToPassthrough = false) override;
+ PassRefPtr<FilterOperation> blend(const FilterOperation* from, double progress, bool blendToPassthrough = false) override;
private:
- virtual bool operator==(const FilterOperation& o) const override
- {
- if (!isSameType(o))
- return false;
- const DropShadowFilterOperation* other = static_cast<const DropShadowFilterOperation*>(&o);
- return m_location == other->m_location && m_stdDeviation == other->m_stdDeviation && m_color == other->m_color;
- }
+ bool operator==(const FilterOperation&) const override;
- DropShadowFilterOperation(const IntPoint& location, int stdDeviation, Color color, OperationType type)
- : FilterOperation(type)
+ DropShadowFilterOperation(const IntPoint& location, int stdDeviation, const Color& color)
+ : FilterOperation(DROP_SHADOW)
, m_location(location)
, m_stdDeviation(stdDeviation)
, m_color(color)
@@ -326,13 +344,21 @@ private:
Color m_color;
};
-#define FILTER_OPERATION_CASTS(ToValueTypeName, predicate) \
- TYPE_CASTS_BASE(ToValueTypeName, FilterOperation, operation, operation->type() == FilterOperation::predicate, operation.type() == FilterOperation::predicate)
-
-FILTER_OPERATION_CASTS(ReferenceFilterOperation, REFERENCE)
+WEBCORE_EXPORT TextStream& operator<<(TextStream&, const FilterOperation&);
} // namespace WebCore
-#endif // ENABLE(CSS_FILTERS)
+#define SPECIALIZE_TYPE_TRAITS_FILTEROPERATION(ToValueTypeName, predicate) \
+SPECIALIZE_TYPE_TRAITS_BEGIN(WebCore::ToValueTypeName) \
+ static bool isType(const WebCore::FilterOperation& operation) { return operation.predicate; } \
+SPECIALIZE_TYPE_TRAITS_END()
+
+SPECIALIZE_TYPE_TRAITS_FILTEROPERATION(DefaultFilterOperation, type() == WebCore::FilterOperation::DEFAULT)
+SPECIALIZE_TYPE_TRAITS_FILTEROPERATION(PassthroughFilterOperation, type() == WebCore::FilterOperation::PASSTHROUGH)
+SPECIALIZE_TYPE_TRAITS_FILTEROPERATION(ReferenceFilterOperation, type() == WebCore::FilterOperation::REFERENCE)
+SPECIALIZE_TYPE_TRAITS_FILTEROPERATION(BasicColorMatrixFilterOperation, isBasicColorMatrixFilterOperation())
+SPECIALIZE_TYPE_TRAITS_FILTEROPERATION(BasicComponentTransferFilterOperation, isBasicComponentTransferFilterOperation())
+SPECIALIZE_TYPE_TRAITS_FILTEROPERATION(BlurFilterOperation, type() == WebCore::FilterOperation::BLUR)
+SPECIALIZE_TYPE_TRAITS_FILTEROPERATION(DropShadowFilterOperation, type() == WebCore::FilterOperation::DROP_SHADOW)
#endif // FilterOperation_h
diff --git a/Source/WebCore/platform/graphics/filters/FilterOperations.cpp b/Source/WebCore/platform/graphics/filters/FilterOperations.cpp
index 4279167cc..a1824f996 100644
--- a/Source/WebCore/platform/graphics/filters/FilterOperations.cpp
+++ b/Source/WebCore/platform/graphics/filters/FilterOperations.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2011 Apple Inc. All rights reserved.
+ * Copyright (C) 2011-2017 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -10,10 +10,10 @@
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
- * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
@@ -29,58 +29,39 @@
#include "FEGaussianBlur.h"
#include "IntSize.h"
#include "LengthFunctions.h"
-
-#if ENABLE(CSS_FILTERS)
+#include "TextStream.h"
namespace WebCore {
static inline IntSize outsetSizeForBlur(float stdDeviation)
{
- unsigned kernelSizeX = 0;
- unsigned kernelSizeY = 0;
- FEGaussianBlur::calculateUnscaledKernelSize(kernelSizeX, kernelSizeY, stdDeviation, stdDeviation);
+ auto kernelSize = FEGaussianBlur::calculateUnscaledKernelSize(FloatPoint(stdDeviation, stdDeviation));
- IntSize outset;
// We take the half kernel size and multiply it with three, because we run box blur three times.
- outset.setWidth(3 * kernelSizeX * 0.5f);
- outset.setHeight(3 * kernelSizeY * 0.5f);
-
- return outset;
-}
-
-FilterOperations::FilterOperations()
-{
+ return {
+ 3 * kernelSize.width() / 2,
+ 3 * kernelSize.height() / 2
+ };
}
-FilterOperations& FilterOperations::operator=(const FilterOperations& other)
+bool FilterOperations::operator==(const FilterOperations& other) const
{
- m_operations = other.m_operations;
- return *this;
-}
-
-bool FilterOperations::operator==(const FilterOperations& o) const
-{
- if (m_operations.size() != o.m_operations.size())
+ size_t size = m_operations.size();
+ if (size != other.m_operations.size())
return false;
-
- unsigned s = m_operations.size();
- for (unsigned i = 0; i < s; i++) {
- if (*m_operations[i] != *o.m_operations[i])
+ for (size_t i = 0; i < size; i++) {
+ if (*m_operations[i] != *other.m_operations[i])
return false;
}
-
return true;
}
bool FilterOperations::operationsMatch(const FilterOperations& other) const
{
- size_t numOperations = operations().size();
- // If the sizes of the function lists don't match, the lists don't match
- if (numOperations != other.operations().size())
+ size_t size = operations().size();
+ if (size != other.operations().size())
return false;
-
- // If the types of each function are not the same, the lists don't match
- for (size_t i = 0; i < numOperations; ++i) {
+ for (size_t i = 0; i < size; ++i) {
if (!operations()[i]->isSameType(*other.operations()[i]))
return false;
}
@@ -89,8 +70,8 @@ bool FilterOperations::operationsMatch(const FilterOperations& other) const
bool FilterOperations::hasReferenceFilter() const
{
- for (size_t i = 0; i < m_operations.size(); ++i) {
- if (m_operations.at(i)->type() == FilterOperation::REFERENCE)
+ for (auto& operation : m_operations) {
+ if (operation->type() == FilterOperation::REFERENCE)
return true;
}
return false;
@@ -98,9 +79,9 @@ bool FilterOperations::hasReferenceFilter() const
bool FilterOperations::hasOutsets() const
{
- for (size_t i = 0; i < m_operations.size(); ++i) {
- FilterOperation::OperationType operationType = m_operations.at(i).get()->type();
- if (operationType == FilterOperation::BLUR || operationType == FilterOperation::DROP_SHADOW)
+ for (auto& operation : m_operations) {
+ auto type = operation->type();
+ if (type == FilterOperation::BLUR || type == FilterOperation::DROP_SHADOW)
return true;
}
return false;
@@ -109,26 +90,25 @@ bool FilterOperations::hasOutsets() const
FilterOutsets FilterOperations::outsets() const
{
FilterOutsets totalOutsets;
- for (size_t i = 0; i < m_operations.size(); ++i) {
- FilterOperation* filterOperation = m_operations.at(i).get();
- switch (filterOperation->type()) {
+ for (auto& operation : m_operations) {
+ switch (operation->type()) {
case FilterOperation::BLUR: {
- BlurFilterOperation* blurOperation = static_cast<BlurFilterOperation*>(filterOperation);
- float stdDeviation = floatValueForLength(blurOperation->stdDeviation(), 0);
+ auto& blurOperation = downcast<BlurFilterOperation>(*operation);
+ float stdDeviation = floatValueForLength(blurOperation.stdDeviation(), 0);
IntSize outsetSize = outsetSizeForBlur(stdDeviation);
FilterOutsets outsets(outsetSize.height(), outsetSize.width(), outsetSize.height(), outsetSize.width());
totalOutsets += outsets;
break;
}
case FilterOperation::DROP_SHADOW: {
- DropShadowFilterOperation* dropShadowOperation = static_cast<DropShadowFilterOperation*>(filterOperation);
- IntSize outsetSize = outsetSizeForBlur(dropShadowOperation->stdDeviation());
- FilterOutsets outsets(
- std::max(0, outsetSize.height() - dropShadowOperation->y()),
- std::max(0, outsetSize.width() + dropShadowOperation->x()),
- std::max(0, outsetSize.height() + dropShadowOperation->y()),
- std::max(0, outsetSize.width() - dropShadowOperation->x())
- );
+ auto& dropShadowOperation = downcast<DropShadowFilterOperation>(*operation);
+ IntSize outsetSize = outsetSizeForBlur(dropShadowOperation.stdDeviation());
+ FilterOutsets outsets {
+ std::max(0, outsetSize.height() - dropShadowOperation.y()),
+ std::max(0, outsetSize.width() + dropShadowOperation.x()),
+ std::max(0, outsetSize.height() + dropShadowOperation.y()),
+ std::max(0, outsetSize.width() - dropShadowOperation.x())
+ };
totalOutsets += outsets;
break;
}
@@ -141,20 +121,43 @@ FilterOutsets FilterOperations::outsets() const
bool FilterOperations::hasFilterThatAffectsOpacity() const
{
- for (size_t i = 0; i < m_operations.size(); ++i)
- if (m_operations[i]->affectsOpacity())
+ for (auto& operation : m_operations) {
+ if (operation->affectsOpacity())
return true;
+ }
return false;
}
bool FilterOperations::hasFilterThatMovesPixels() const
{
- for (size_t i = 0; i < m_operations.size(); ++i)
- if (m_operations[i]->movesPixels())
+ for (auto& operation : m_operations) {
+ if (operation->movesPixels())
return true;
+ }
return false;
}
-} // namespace WebCore
+bool FilterOperations::hasFilterThatShouldBeRestrictedBySecurityOrigin() const
+{
+ for (auto& operation : m_operations) {
+ if (operation->shouldBeRestrictedBySecurityOrigin())
+ return true;
+ }
+ return false;
+}
-#endif // ENABLE(CSS_FILTERS)
+TextStream& operator<<(TextStream& ts, const FilterOperations& filters)
+{
+ for (size_t i = 0; i < filters.size(); ++i) {
+ auto filter = filters.at(i);
+ if (filter)
+ ts << *filter;
+ else
+ ts << "(null)";
+ if (i < filters.size() - 1)
+ ts << " ";
+ }
+ return ts;
+}
+
+} // namespace WebCore
diff --git a/Source/WebCore/platform/graphics/filters/FilterOperations.h b/Source/WebCore/platform/graphics/filters/FilterOperations.h
index c37998e7e..0752e8d44 100644
--- a/Source/WebCore/platform/graphics/filters/FilterOperations.h
+++ b/Source/WebCore/platform/graphics/filters/FilterOperations.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2011 Apple Inc. All rights reserved.
+ * Copyright (C) 2011-2017 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -10,10 +10,10 @@
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
- * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
@@ -23,10 +23,7 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef FilterOperations_h
-#define FilterOperations_h
-
-#if ENABLE(CSS_FILTERS)
+#pragma once
#include "FilterOperation.h"
#include "IntRectExtent.h"
@@ -35,33 +32,22 @@
namespace WebCore {
-typedef IntRectExtent FilterOutsets;
+using FilterOutsets = IntRectExtent;
class FilterOperations {
WTF_MAKE_FAST_ALLOCATED;
public:
- FilterOperations();
- FilterOperations(const FilterOperations& other) { *this = other; }
-
- FilterOperations& operator=(const FilterOperations&);
-
bool operator==(const FilterOperations&) const;
- bool operator!=(const FilterOperations& o) const
- {
- return !(*this == o);
- }
-
- void clear()
- {
- m_operations.clear();
- }
-
+ bool operator!=(const FilterOperations& other) const { return !(*this == other); }
+
+ void clear() { m_operations.clear(); }
+
Vector<RefPtr<FilterOperation>>& operations() { return m_operations; }
const Vector<RefPtr<FilterOperation>>& operations() const { return m_operations; }
- bool isEmpty() const { return !m_operations.size(); }
+ bool isEmpty() const { return m_operations.isEmpty(); }
size_t size() const { return m_operations.size(); }
- const FilterOperation* at(size_t index) const { return index < m_operations.size() ? m_operations.at(index).get() : 0; }
+ const FilterOperation* at(size_t index) const { return index < m_operations.size() ? m_operations[index].get() : nullptr; }
bool operationsMatch(const FilterOperations&) const;
@@ -70,14 +56,14 @@ public:
bool hasFilterThatAffectsOpacity() const;
bool hasFilterThatMovesPixels() const;
+ bool hasFilterThatShouldBeRestrictedBySecurityOrigin() const;
bool hasReferenceFilter() const;
+
private:
Vector<RefPtr<FilterOperation>> m_operations;
};
-} // namespace WebCore
-
-#endif // ENABLE(CSS_FILTERS)
+WEBCORE_EXPORT TextStream& operator<<(TextStream&, const FilterOperations&);
-#endif // FilterOperations_h
+} // namespace WebCore
diff --git a/Source/WebCore/platform/graphics/filters/LightSource.h b/Source/WebCore/platform/graphics/filters/LightSource.h
index 7aa270e5f..7677d583e 100644
--- a/Source/WebCore/platform/graphics/filters/LightSource.h
+++ b/Source/WebCore/platform/graphics/filters/LightSource.h
@@ -24,9 +24,7 @@
#ifndef LightSource_h
#define LightSource_h
-#if ENABLE(FILTERS)
#include "FloatPoint3D.h"
-#include <wtf/PassRefPtr.h>
#include <wtf/RefCounted.h>
namespace WebCore {
@@ -91,6 +89,4 @@ private:
} // namespace WebCore
-#endif // ENABLE(FILTERS)
-
#endif // LightSource_h
diff --git a/Source/WebCore/platform/graphics/filters/PointLightSource.cpp b/Source/WebCore/platform/graphics/filters/PointLightSource.cpp
index 207ed8eae..d50ba04dd 100644
--- a/Source/WebCore/platform/graphics/filters/PointLightSource.cpp
+++ b/Source/WebCore/platform/graphics/filters/PointLightSource.cpp
@@ -29,8 +29,6 @@
*/
#include "config.h"
-
-#if ENABLE(FILTERS)
#include "PointLightSource.h"
#include "TextStream.h"
@@ -73,12 +71,6 @@ bool PointLightSource::setZ(float z)
return true;
}
-static TextStream& operator<<(TextStream& ts, const FloatPoint3D& p)
-{
- ts << "x=" << p.x() << " y=" << p.y() << " z=" << p.z();
- return ts;
-}
-
TextStream& PointLightSource::externalRepresentation(TextStream& ts) const
{
ts << "[type=POINT-LIGHT] ";
@@ -87,5 +79,3 @@ TextStream& PointLightSource::externalRepresentation(TextStream& ts) const
}
}; // namespace WebCore
-
-#endif // ENABLE(FILTERS)
diff --git a/Source/WebCore/platform/graphics/filters/PointLightSource.h b/Source/WebCore/platform/graphics/filters/PointLightSource.h
index abfacc940..75415aa66 100644
--- a/Source/WebCore/platform/graphics/filters/PointLightSource.h
+++ b/Source/WebCore/platform/graphics/filters/PointLightSource.h
@@ -23,27 +23,27 @@
#ifndef PointLightSource_h
#define PointLightSource_h
-#if ENABLE(FILTERS)
#include "LightSource.h"
+#include <wtf/Ref.h>
namespace WebCore {
class PointLightSource : public LightSource {
public:
- static PassRefPtr<PointLightSource> create(const FloatPoint3D& position)
+ static Ref<PointLightSource> create(const FloatPoint3D& position)
{
- return adoptRef(new PointLightSource(position));
+ return adoptRef(*new PointLightSource(position));
}
const FloatPoint3D& position() const { return m_position; }
- virtual bool setX(float) override;
- virtual bool setY(float) override;
- virtual bool setZ(float) override;
+ bool setX(float) override;
+ bool setY(float) override;
+ bool setZ(float) override;
- virtual void initPaintingData(PaintingData&);
- virtual void updatePaintingData(PaintingData&, int x, int y, float z);
+ void initPaintingData(PaintingData&) override;
+ void updatePaintingData(PaintingData&, int x, int y, float z) override;
- virtual TextStream& externalRepresentation(TextStream&) const;
+ TextStream& externalRepresentation(TextStream&) const override;
private:
PointLightSource(const FloatPoint3D& position)
@@ -57,6 +57,4 @@ private:
} // namespace WebCore
-#endif // ENABLE(FILTERS)
-
#endif // PointLightSource_h
diff --git a/Source/WebCore/platform/graphics/filters/SourceAlpha.cpp b/Source/WebCore/platform/graphics/filters/SourceAlpha.cpp
index f286c9562..61402564a 100644
--- a/Source/WebCore/platform/graphics/filters/SourceAlpha.cpp
+++ b/Source/WebCore/platform/graphics/filters/SourceAlpha.cpp
@@ -18,51 +18,49 @@
*/
#include "config.h"
-
-#if ENABLE(FILTERS)
#include "SourceAlpha.h"
#include "Color.h"
#include "Filter.h"
#include "GraphicsContext.h"
#include "TextStream.h"
+#include <wtf/NeverDestroyed.h>
#include <wtf/StdLibExtras.h>
#include <wtf/text/WTFString.h>
namespace WebCore {
-PassRefPtr<SourceAlpha> SourceAlpha::create(Filter* filter)
+Ref<SourceAlpha> SourceAlpha::create(FilterEffect& sourceEffect)
{
- return adoptRef(new SourceAlpha(filter));
+ return adoptRef(*new SourceAlpha(sourceEffect));
}
const AtomicString& SourceAlpha::effectName()
{
- DEFINE_STATIC_LOCAL(const AtomicString, s_effectName, ("SourceAlpha", AtomicString::ConstructFromLiteral));
+ static NeverDestroyed<const AtomicString> s_effectName("SourceAlpha", AtomicString::ConstructFromLiteral);
return s_effectName;
}
void SourceAlpha::determineAbsolutePaintRect()
{
- Filter* filter = this->filter();
- FloatRect paintRect = filter->sourceImageRect();
- paintRect.scale(filter->filterResolution().width(), filter->filterResolution().height());
- setAbsolutePaintRect(enclosingIntRect(paintRect));
+ inputEffect(0)->determineAbsolutePaintRect();
+ setAbsolutePaintRect(inputEffect(0)->absolutePaintRect());
}
void SourceAlpha::platformApplySoftware()
{
ImageBuffer* resultImage = createImageBufferResult();
- Filter* filter = this->filter();
- if (!resultImage || !filter->sourceImage())
+ if (!resultImage)
return;
+ GraphicsContext& filterContext = resultImage->context();
- setIsAlphaImage(true);
+ ImageBuffer* imageBuffer = inputEffect(0)->asImageBuffer();
+ if (!imageBuffer)
+ return;
FloatRect imageRect(FloatPoint(), absolutePaintRect().size());
- GraphicsContext* filterContext = resultImage->context();
- filterContext->fillRect(imageRect, Color::black, ColorSpaceDeviceRGB);
- filterContext->drawImageBuffer(filter->sourceImage(), ColorSpaceDeviceRGB, IntPoint(), CompositeDestinationIn);
+ filterContext.fillRect(imageRect, Color::black);
+ filterContext.drawImageBuffer(*imageBuffer, IntPoint(), CompositeDestinationIn);
}
void SourceAlpha::dump()
@@ -76,6 +74,11 @@ TextStream& SourceAlpha::externalRepresentation(TextStream& ts, int indent) cons
return ts;
}
-} // namespace WebCore
+SourceAlpha::SourceAlpha(FilterEffect& sourceEffect)
+ : FilterEffect(sourceEffect.filter())
+{
+ setOperatingColorSpace(sourceEffect.operatingColorSpace());
+ inputEffects().append(&sourceEffect);
+}
-#endif // ENABLE(FILTERS)
+} // namespace WebCore
diff --git a/Source/WebCore/platform/graphics/filters/SourceAlpha.h b/Source/WebCore/platform/graphics/filters/SourceAlpha.h
index b0220ce9e..deef32427 100644
--- a/Source/WebCore/platform/graphics/filters/SourceAlpha.h
+++ b/Source/WebCore/platform/graphics/filters/SourceAlpha.h
@@ -20,7 +20,6 @@
#ifndef SourceAlpha_h
#define SourceAlpha_h
-#if ENABLE(FILTERS)
#include "FilterEffect.h"
#include "Filter.h"
@@ -28,31 +27,23 @@ namespace WebCore {
class SourceAlpha : public FilterEffect {
public:
- static PassRefPtr<SourceAlpha> create(Filter*);
+ static Ref<SourceAlpha> create(FilterEffect&);
static const AtomicString& effectName();
- virtual void platformApplySoftware();
-#if ENABLE(OPENCL)
- virtual bool platformApplyOpenCL();
-#endif
- virtual void dump();
+ void platformApplySoftware() override;
+ void dump() override;
- virtual void determineAbsolutePaintRect();
+ void determineAbsolutePaintRect() override;
- virtual FilterEffectType filterEffectType() const { return FilterEffectTypeSourceInput; }
+ FilterEffectType filterEffectType() const override { return FilterEffectTypeSourceInput; }
- virtual TextStream& externalRepresentation(TextStream&, int indention) const;
+ TextStream& externalRepresentation(TextStream&, int indention) const override;
private:
- SourceAlpha(Filter* filter)
- : FilterEffect(filter)
- {
- }
+ explicit SourceAlpha(FilterEffect&);
};
} //namespace WebCore
-#endif // ENABLE(FILTERS)
-
#endif // SourceAlpha_h
diff --git a/Source/WebCore/platform/graphics/filters/SourceGraphic.cpp b/Source/WebCore/platform/graphics/filters/SourceGraphic.cpp
index 5eebf2cee..2313b033e 100644
--- a/Source/WebCore/platform/graphics/filters/SourceGraphic.cpp
+++ b/Source/WebCore/platform/graphics/filters/SourceGraphic.cpp
@@ -18,45 +18,46 @@
*/
#include "config.h"
-
-#if ENABLE(FILTERS)
#include "SourceGraphic.h"
#include "Filter.h"
#include "GraphicsContext.h"
#include "TextStream.h"
+#include <wtf/NeverDestroyed.h>
#include <wtf/StdLibExtras.h>
#include <wtf/text/WTFString.h>
namespace WebCore {
-PassRefPtr<SourceGraphic> SourceGraphic::create(Filter* filter)
+Ref<SourceGraphic> SourceGraphic::create(Filter& filter)
{
- return adoptRef(new SourceGraphic(filter));
+ return adoptRef(*new SourceGraphic(filter));
}
const AtomicString& SourceGraphic::effectName()
{
- DEFINE_STATIC_LOCAL(const AtomicString, s_effectName, ("SourceGraphic", AtomicString::ConstructFromLiteral));
+ static NeverDestroyed<const AtomicString> s_effectName("SourceGraphic", AtomicString::ConstructFromLiteral);
return s_effectName;
}
void SourceGraphic::determineAbsolutePaintRect()
{
- Filter* filter = this->filter();
- FloatRect paintRect = filter->sourceImageRect();
- paintRect.scale(filter->filterResolution().width(), filter->filterResolution().height());
+ Filter& filter = this->filter();
+ FloatRect paintRect = filter.sourceImageRect();
+ paintRect.scale(filter.filterResolution().width(), filter.filterResolution().height());
setAbsolutePaintRect(enclosingIntRect(paintRect));
}
void SourceGraphic::platformApplySoftware()
{
+ Filter& filter = this->filter();
+
ImageBuffer* resultImage = createImageBufferResult();
- Filter* filter = this->filter();
- if (!resultImage || !filter->sourceImage())
+ ImageBuffer* sourceImage = filter.sourceImage();
+ if (!resultImage || !sourceImage)
return;
- resultImage->context()->drawImageBuffer(filter->sourceImage(), ColorSpaceDeviceRGB, IntPoint());
+ resultImage->context().drawImageBuffer(*sourceImage, IntPoint());
}
void SourceGraphic::dump()
@@ -71,5 +72,3 @@ TextStream& SourceGraphic::externalRepresentation(TextStream& ts, int indent) co
}
} // namespace WebCore
-
-#endif // ENABLE(FILTERS)
diff --git a/Source/WebCore/platform/graphics/filters/SourceGraphic.h b/Source/WebCore/platform/graphics/filters/SourceGraphic.h
index 4200595fb..3ffd2491b 100644
--- a/Source/WebCore/platform/graphics/filters/SourceGraphic.h
+++ b/Source/WebCore/platform/graphics/filters/SourceGraphic.h
@@ -21,7 +21,6 @@
#ifndef SourceGraphic_h
#define SourceGraphic_h
-#if ENABLE(FILTERS)
#include "FilterEffect.h"
#include "Filter.h"
@@ -29,32 +28,27 @@ namespace WebCore {
class SourceGraphic : public FilterEffect {
public:
- static PassRefPtr<SourceGraphic> create(Filter*);
+ static Ref<SourceGraphic> create(Filter&);
static const AtomicString& effectName();
- virtual void platformApplySoftware();
-#if ENABLE(OPENCL)
- virtual bool platformApplyOpenCL();
-#endif
- virtual void dump();
+ void platformApplySoftware() override;
+ void dump() override;
- virtual void determineAbsolutePaintRect();
+ void determineAbsolutePaintRect() override;
- virtual FilterEffectType filterEffectType() const { return FilterEffectTypeSourceInput; }
+ FilterEffectType filterEffectType() const override { return FilterEffectTypeSourceInput; }
- virtual TextStream& externalRepresentation(TextStream&, int indention) const;
+ TextStream& externalRepresentation(TextStream&, int indention) const override;
private:
- SourceGraphic(Filter* filter)
+ SourceGraphic(Filter& filter)
: FilterEffect(filter)
{
- setOperatingColorSpace(ColorSpaceDeviceRGB);
+ setOperatingColorSpace(ColorSpaceSRGB);
}
};
} //namespace WebCore
-#endif // ENABLE(FILTERS)
-
#endif // SourceGraphic_h
diff --git a/Source/WebCore/platform/graphics/filters/SpotLightSource.cpp b/Source/WebCore/platform/graphics/filters/SpotLightSource.cpp
index 648fcae6d..30c4f61c6 100644
--- a/Source/WebCore/platform/graphics/filters/SpotLightSource.cpp
+++ b/Source/WebCore/platform/graphics/filters/SpotLightSource.cpp
@@ -30,8 +30,6 @@
*/
#include "config.h"
-
-#if ENABLE(FILTERS)
#include "SpotLightSource.h"
#include "TextStream.h"
@@ -177,12 +175,6 @@ bool SpotLightSource::setLimitingConeAngle(float limitingConeAngle)
return true;
}
-static TextStream& operator<<(TextStream& ts, const FloatPoint3D& p)
-{
- ts << "x=" << p.x() << " y=" << p.y() << " z=" << p.z();
- return ts;
-}
-
TextStream& SpotLightSource::externalRepresentation(TextStream& ts) const
{
ts << "[type=SPOT-LIGHT] ";
@@ -194,5 +186,3 @@ TextStream& SpotLightSource::externalRepresentation(TextStream& ts) const
}
}; // namespace WebCore
-
-#endif // ENABLE(FILTERS)
diff --git a/Source/WebCore/platform/graphics/filters/SpotLightSource.h b/Source/WebCore/platform/graphics/filters/SpotLightSource.h
index ec9c5cae8..bd3ee36f0 100644
--- a/Source/WebCore/platform/graphics/filters/SpotLightSource.h
+++ b/Source/WebCore/platform/graphics/filters/SpotLightSource.h
@@ -23,17 +23,17 @@
#ifndef SpotLightSource_h
#define SpotLightSource_h
-#if ENABLE(FILTERS)
#include "LightSource.h"
+#include <wtf/Ref.h>
namespace WebCore {
class SpotLightSource : public LightSource {
public:
- static PassRefPtr<SpotLightSource> create(const FloatPoint3D& position,
+ static Ref<SpotLightSource> create(const FloatPoint3D& position,
const FloatPoint3D& direction, float specularExponent, float limitingConeAngle)
{
- return adoptRef(new SpotLightSource(position, direction, specularExponent, limitingConeAngle));
+ return adoptRef(*new SpotLightSource(position, direction, specularExponent, limitingConeAngle));
}
const FloatPoint3D& position() const { return m_position; }
@@ -41,20 +41,20 @@ public:
float specularExponent() const { return m_specularExponent; }
float limitingConeAngle() const { return m_limitingConeAngle; }
- virtual bool setX(float) override;
- virtual bool setY(float) override;
- virtual bool setZ(float) override;
- virtual bool setPointsAtX(float) override;
- virtual bool setPointsAtY(float) override;
- virtual bool setPointsAtZ(float) override;
+ bool setX(float) override;
+ bool setY(float) override;
+ bool setZ(float) override;
+ bool setPointsAtX(float) override;
+ bool setPointsAtY(float) override;
+ bool setPointsAtZ(float) override;
- virtual bool setSpecularExponent(float) override;
- virtual bool setLimitingConeAngle(float) override;
+ bool setSpecularExponent(float) override;
+ bool setLimitingConeAngle(float) override;
- virtual void initPaintingData(PaintingData&);
- virtual void updatePaintingData(PaintingData&, int x, int y, float z);
+ void initPaintingData(PaintingData&) override;
+ void updatePaintingData(PaintingData&, int x, int y, float z) override;
- virtual TextStream& externalRepresentation(TextStream&) const;
+ TextStream& externalRepresentation(TextStream&) const override;
private:
SpotLightSource(const FloatPoint3D& position, const FloatPoint3D& direction,
@@ -76,6 +76,4 @@ private:
} // namespace WebCore
-#endif // ENABLE(FILTERS)
-
#endif // SpotLightSource_h
diff --git a/Source/WebCore/platform/graphics/freetype/FcUniquePtr.h b/Source/WebCore/platform/graphics/freetype/FcUniquePtr.h
new file mode 100644
index 000000000..1450fbda9
--- /dev/null
+++ b/Source/WebCore/platform/graphics/freetype/FcUniquePtr.h
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2015 Igalia S.L
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef FcUniquePtr_h
+#define FcUniquePtr_h
+
+#if USE(FREETYPE)
+
+#include <fontconfig/fontconfig.h>
+#include <memory>
+
+namespace WebCore {
+
+template<typename T>
+struct FcPtrDeleter {
+ void operator()(T* ptr) const = delete;
+};
+
+template<typename T>
+using FcUniquePtr = std::unique_ptr<T, FcPtrDeleter<T>>;
+
+template<> struct FcPtrDeleter<FcCharSet> {
+ void operator()(FcCharSet* ptr) const
+ {
+ FcCharSetDestroy(ptr);
+ }
+};
+
+template<> struct FcPtrDeleter<FcFontSet> {
+ void operator()(FcFontSet* ptr) const
+ {
+ FcFontSetDestroy(ptr);
+ }
+};
+
+template<> struct FcPtrDeleter<FcLangSet> {
+ void operator()(FcLangSet* ptr) const
+ {
+ FcLangSetDestroy(ptr);
+ }
+};
+
+template<> struct FcPtrDeleter<FcObjectSet> {
+ void operator()(FcObjectSet* ptr) const
+ {
+ FcObjectSetDestroy(ptr);
+ }
+};
+
+} // namespace WebCore
+
+#endif // USE(FREETYPE)
+
+#endif // FcUniquePtr_h
diff --git a/Source/WebCore/platform/graphics/freetype/FontCacheFreeType.cpp b/Source/WebCore/platform/graphics/freetype/FontCacheFreeType.cpp
index cd25f7d73..ff3eacf3d 100644
--- a/Source/WebCore/platform/graphics/freetype/FontCacheFreeType.cpp
+++ b/Source/WebCore/platform/graphics/freetype/FontCacheFreeType.cpp
@@ -22,10 +22,10 @@
#include "config.h"
#include "FontCache.h"
+#include "CairoUtilities.h"
+#include "FcUniquePtr.h"
#include "Font.h"
-#include "OwnPtrCairo.h"
#include "RefPtrCairo.h"
-#include "SimpleFontData.h"
#include "UTF16UChar32Iterator.h"
#include <cairo-ft.h>
#include <cairo.h>
@@ -42,102 +42,130 @@ void FontCache::platformInit()
ASSERT_NOT_REACHED();
}
-FcPattern* createFontConfigPatternForCharacters(const UChar* characters, int bufferLength)
+static RefPtr<FcPattern> createFontConfigPatternForCharacters(const UChar* characters, int bufferLength)
{
- FcPattern* pattern = FcPatternCreate();
- FcCharSet* fontConfigCharSet = FcCharSetCreate();
+ RefPtr<FcPattern> pattern = adoptRef(FcPatternCreate());
+ FcUniquePtr<FcCharSet> fontConfigCharSet(FcCharSetCreate());
UTF16UChar32Iterator iterator(characters, bufferLength);
UChar32 character = iterator.next();
while (character != iterator.end()) {
- FcCharSetAddChar(fontConfigCharSet, character);
+ FcCharSetAddChar(fontConfigCharSet.get(), character);
character = iterator.next();
}
- FcPatternAddCharSet(pattern, FC_CHARSET, fontConfigCharSet);
- FcCharSetDestroy(fontConfigCharSet);
+ FcPatternAddCharSet(pattern.get(), FC_CHARSET, fontConfigCharSet.get());
- FcPatternAddBool(pattern, FC_SCALABLE, FcTrue);
- FcConfigSubstitute(0, pattern, FcMatchPattern);
- FcDefaultSubstitute(pattern);
+ FcPatternAddBool(pattern.get(), FC_SCALABLE, FcTrue);
+ FcConfigSubstitute(nullptr, pattern.get(), FcMatchPattern);
+ cairo_ft_font_options_substitute(getDefaultCairoFontOptions(), pattern.get());
+ FcDefaultSubstitute(pattern.get());
return pattern;
}
-FcPattern* findBestFontGivenFallbacks(const FontPlatformData& fontData, FcPattern* pattern)
+static RefPtr<FcPattern> findBestFontGivenFallbacks(const FontPlatformData& fontData, FcPattern* pattern)
{
- if (!fontData.m_pattern)
- return 0;
-
- if (!fontData.m_fallbacks) {
- FcResult fontConfigResult;
- fontData.m_fallbacks = FcFontSort(0, fontData.m_pattern.get(), FcTrue, 0, &fontConfigResult);
- }
-
- if (!fontData.m_fallbacks)
- return 0;
+ FcFontSet* fallbacks = fontData.fallbacks();
+ if (!fallbacks)
+ return nullptr;
- FcFontSet* sets[] = { fontData.m_fallbacks };
FcResult fontConfigResult;
- return FcFontSetMatch(0, sets, 1, pattern, &fontConfigResult);
+ return FcFontSetMatch(nullptr, &fallbacks, 1, pattern, &fontConfigResult);
}
-PassRefPtr<SimpleFontData> FontCache::systemFallbackForCharacters(const FontDescription& description, const SimpleFontData* originalFontData, bool, const UChar* characters, int length)
+RefPtr<Font> FontCache::systemFallbackForCharacters(const FontDescription& description, const Font* originalFontData, bool, const UChar* characters, unsigned length)
{
- RefPtr<FcPattern> pattern = adoptRef(createFontConfigPatternForCharacters(characters, length));
+ RefPtr<FcPattern> pattern = createFontConfigPatternForCharacters(characters, length);
const FontPlatformData& fontData = originalFontData->platformData();
- RefPtr<FcPattern> fallbackPattern = adoptRef(findBestFontGivenFallbacks(fontData, pattern.get()));
+ RefPtr<FcPattern> fallbackPattern = findBestFontGivenFallbacks(fontData, pattern.get());
if (fallbackPattern) {
FontPlatformData alternateFontData(fallbackPattern.get(), description);
- return getCachedFontData(&alternateFontData, DoNotRetain);
+ return fontForPlatformData(alternateFontData);
}
FcResult fontConfigResult;
- RefPtr<FcPattern> resultPattern = adoptRef(FcFontMatch(0, pattern.get(), &fontConfigResult));
+ RefPtr<FcPattern> resultPattern = adoptRef(FcFontMatch(nullptr, pattern.get(), &fontConfigResult));
if (!resultPattern)
- return 0;
+ return nullptr;
FontPlatformData alternateFontData(resultPattern.get(), description);
- return getCachedFontData(&alternateFontData, DoNotRetain);
+ return fontForPlatformData(alternateFontData);
+}
+
+static Vector<String> patternToFamilies(FcPattern& pattern)
+{
+ char* patternChars = reinterpret_cast<char*>(FcPatternFormat(&pattern, reinterpret_cast<const FcChar8*>("%{family}")));
+ String patternString = String::fromUTF8(patternChars);
+ free(patternChars);
+
+ Vector<String> results;
+ patternString.split(',', results);
+ return results;
}
-PassRefPtr<SimpleFontData> FontCache::getLastResortFallbackFont(const FontDescription& fontDescription, ShouldRetain shouldRetain)
+Vector<String> FontCache::systemFontFamilies()
+{
+ RefPtr<FcPattern> scalablesOnlyPattern = adoptRef(FcPatternCreate());
+ FcPatternAddBool(scalablesOnlyPattern.get(), FC_SCALABLE, FcTrue);
+
+ FcUniquePtr<FcObjectSet> familiesOnly(FcObjectSetBuild(FC_FAMILY, nullptr));
+ FcUniquePtr<FcFontSet> fontSet(FcFontList(nullptr, scalablesOnlyPattern.get(), familiesOnly.get()));
+
+ Vector<String> fontFamilies;
+ for (int i = 0; i < fontSet->nfont; i++) {
+ FcPattern* pattern = fontSet->fonts[i];
+ FcChar8* family = nullptr;
+ FcPatternGetString(pattern, FC_FAMILY, 0, &family);
+ if (family)
+ fontFamilies.appendVector(patternToFamilies(*pattern));
+ }
+
+ return fontFamilies;
+}
+
+Ref<Font> FontCache::lastResortFallbackFontForEveryCharacter(const FontDescription& fontDescription)
+{
+ return lastResortFallbackFont(fontDescription);
+}
+
+Ref<Font> FontCache::lastResortFallbackFont(const FontDescription& fontDescription)
{
// We want to return a fallback font here, otherwise the logic preventing FontConfig
// matches for non-fallback fonts might return 0. See isFallbackFontAllowed.
static AtomicString timesStr("serif");
- return getCachedFontData(fontDescription, timesStr, false, shouldRetain);
+ if (RefPtr<Font> font = fontForFamily(fontDescription, timesStr))
+ return *font;
+
+ // This could be reached due to improperly-installed or misconfigured fontconfig.
+ RELEASE_ASSERT_NOT_REACHED();
}
-void FontCache::getTraitsInFamily(const AtomicString&, Vector<unsigned>&)
+Vector<FontTraitsMask> FontCache::getTraitsInFamily(const AtomicString&)
{
+ return { };
}
-static String getFamilyNameStringFromFontDescriptionAndFamily(const FontDescription& fontDescription, const AtomicString& family)
+static String getFamilyNameStringFromFamily(const AtomicString& family)
{
// If we're creating a fallback font (e.g. "-webkit-monospace"), convert the name into
// the fallback name (like "monospace") that fontconfig understands.
if (family.length() && !family.startsWith("-webkit-"))
return family.string();
- switch (fontDescription.genericFamily()) {
- case FontDescription::StandardFamily:
- case FontDescription::SerifFamily:
+ if (family == standardFamily || family == serifFamily)
return "serif";
- case FontDescription::SansSerifFamily:
+ if (family == sansSerifFamily)
return "sans-serif";
- case FontDescription::MonospaceFamily:
+ if (family == monospaceFamily)
return "monospace";
- case FontDescription::CursiveFamily:
+ if (family == cursiveFamily)
return "cursive";
- case FontDescription::FantasyFamily:
+ if (family == fantasyFamily)
return "fantasy";
- case FontDescription::NoFamily:
- default:
- return "";
- }
+ return "";
}
-int fontWeightToFontconfigWeight(FontWeight weight)
+static int fontWeightToFontconfigWeight(FontWeight weight)
{
switch (weight) {
case FontWeight100:
@@ -164,13 +192,153 @@ int fontWeightToFontconfigWeight(FontWeight weight)
}
}
-PassOwnPtr<FontPlatformData> FontCache::createFontPlatformData(const FontDescription& fontDescription, const AtomicString& family)
+// This is based on Chromium BSD code from Skia (src/ports/SkFontMgr_fontconfig.cpp). It is a
+// hack for lack of API in Fontconfig: https://bugs.freedesktop.org/show_bug.cgi?id=19375
+// FIXME: This is horrible. It should be deleted once Fontconfig can do this itself.
+enum class AliasStrength {
+ Weak,
+ Strong,
+ Done
+};
+
+static AliasStrength strengthOfFirstAlias(const FcPattern& original)
+{
+ // Ideally there would exist a call like
+ // FcResult FcPatternIsWeak(pattern, object, id, FcBool* isWeak);
+ //
+ // However, there is no such call and as of Fc 2.11.0 even FcPatternEquals ignores the weak bit.
+ // Currently, the only reliable way of finding the weak bit is by its effect on matching.
+ // The weak bit only affects the matching of FC_FAMILY and FC_POSTSCRIPT_NAME object values.
+ // A element with the weak bit is scored after FC_LANG, without the weak bit is scored before.
+ // Note that the weak bit is stored on the element, not on the value it holds.
+ FcValue value;
+ FcResult result = FcPatternGet(&original, FC_FAMILY, 0, &value);
+ if (result != FcResultMatch)
+ return AliasStrength::Done;
+
+ RefPtr<FcPattern> pattern = adoptRef(FcPatternDuplicate(&original));
+ FcBool hasMultipleFamilies = true;
+ while (hasMultipleFamilies)
+ hasMultipleFamilies = FcPatternRemove(pattern.get(), FC_FAMILY, 1);
+
+ // Create a font set with two patterns.
+ // 1. the same FC_FAMILY as pattern and a lang object with only 'nomatchlang'.
+ // 2. a different FC_FAMILY from pattern and a lang object with only 'matchlang'.
+ FcUniquePtr<FcFontSet> fontSet(FcFontSetCreate());
+
+ FcUniquePtr<FcLangSet> strongLangSet(FcLangSetCreate());
+ FcLangSetAdd(strongLangSet.get(), reinterpret_cast<const FcChar8*>("nomatchlang"));
+ // Ownership of this FcPattern will be transferred with FcFontSetAdd.
+ FcPattern* strong = FcPatternDuplicate(pattern.get());
+ FcPatternAddLangSet(strong, FC_LANG, strongLangSet.get());
+
+ FcUniquePtr<FcLangSet> weakLangSet(FcLangSetCreate());
+ FcLangSetAdd(weakLangSet.get(), reinterpret_cast<const FcChar8*>("matchlang"));
+ // Ownership of this FcPattern will be transferred via FcFontSetAdd.
+ FcPattern* weak = FcPatternCreate();
+ FcPatternAddString(weak, FC_FAMILY, reinterpret_cast<const FcChar8*>("nomatchstring"));
+ FcPatternAddLangSet(weak, FC_LANG, weakLangSet.get());
+
+ FcFontSetAdd(fontSet.get(), strong);
+ FcFontSetAdd(fontSet.get(), weak);
+
+ // Add 'matchlang' to the copy of the pattern.
+ FcPatternAddLangSet(pattern.get(), FC_LANG, weakLangSet.get());
+
+ // Run a match against the copy of the pattern.
+ // If the first element was weak, then we should match the pattern with 'matchlang'.
+ // If the first element was strong, then we should match the pattern with 'nomatchlang'.
+
+ // Note that this config is only used for FcFontRenderPrepare, which we don't even want.
+ // However, there appears to be no way to match/sort without it.
+ RefPtr<FcConfig> config = adoptRef(FcConfigCreate());
+ FcFontSet* fontSets[1] = { fontSet.get() };
+ RefPtr<FcPattern> match = adoptRef(FcFontSetMatch(config.get(), fontSets, 1, pattern.get(), &result));
+
+ FcLangSet* matchLangSet;
+ FcPatternGetLangSet(match.get(), FC_LANG, 0, &matchLangSet);
+ return FcLangEqual == FcLangSetHasLang(matchLangSet, reinterpret_cast<const FcChar8*>("matchlang"))
+ ? AliasStrength::Weak : AliasStrength::Strong;
+}
+
+static Vector<String> strongAliasesForFamily(const String& family)
+{
+ RefPtr<FcPattern> pattern = adoptRef(FcPatternCreate());
+ if (!FcPatternAddString(pattern.get(), FC_FAMILY, reinterpret_cast<const FcChar8*>(family.utf8().data())))
+ return Vector<String>();
+
+ FcConfigSubstitute(nullptr, pattern.get(), FcMatchPattern);
+ cairo_ft_font_options_substitute(getDefaultCairoFontOptions(), pattern.get());
+ FcDefaultSubstitute(pattern.get());
+
+ FcUniquePtr<FcObjectSet> familiesOnly(FcObjectSetBuild(FC_FAMILY, nullptr));
+ RefPtr<FcPattern> minimal = adoptRef(FcPatternFilter(pattern.get(), familiesOnly.get()));
+
+ // We really want to match strong (preferred) and same (acceptable) only here.
+ // If a family name was specified, assume that any weak matches after the last strong match
+ // are weak (default) and ignore them.
+ // The reason for is that after substitution the pattern for 'sans-serif' looks like
+ // "wwwwwwwwwwwwwwswww" where there are many weak but preferred names, followed by defaults.
+ // So it is possible to have weakly matching but preferred names.
+ // In aliases, bindings are weak by default, so this is easy and common.
+ // If no family name was specified, we'll probably only get weak matches, but that's ok.
+ int lastStrongId = -1;
+ int numIds = 0;
+ for (int id = 0; ; ++id) {
+ AliasStrength result = strengthOfFirstAlias(*minimal);
+ if (result == AliasStrength::Done) {
+ numIds = id;
+ break;
+ }
+ if (result == AliasStrength::Strong)
+ lastStrongId = id;
+ if (!FcPatternRemove(minimal.get(), FC_FAMILY, 0))
+ return Vector<String>();
+ }
+
+ // If they were all weak, then leave the pattern alone.
+ if (lastStrongId < 0)
+ return Vector<String>();
+
+ // Remove everything after the last strong.
+ for (int id = lastStrongId + 1; id < numIds; ++id) {
+ if (!FcPatternRemove(pattern.get(), FC_FAMILY, lastStrongId + 1)) {
+ ASSERT_NOT_REACHED();
+ return Vector<String>();
+ }
+ }
+
+ return patternToFamilies(*pattern);
+}
+
+static bool areStronglyAliased(const String& familyA, const String& familyB)
+{
+ for (auto& family : strongAliasesForFamily(familyA)) {
+ if (family == familyB)
+ return true;
+ }
+ return false;
+}
+
+static inline bool isCommonlyUsedGenericFamily(const String& familyNameString)
+{
+ return equalLettersIgnoringASCIICase(familyNameString, "sans")
+ || equalLettersIgnoringASCIICase(familyNameString, "sans-serif")
+ || equalLettersIgnoringASCIICase(familyNameString, "serif")
+ || equalLettersIgnoringASCIICase(familyNameString, "monospace")
+ || equalLettersIgnoringASCIICase(familyNameString, "fantasy")
+ || equalLettersIgnoringASCIICase(familyNameString, "cursive");
+}
+
+std::unique_ptr<FontPlatformData> FontCache::createFontPlatformData(const FontDescription& fontDescription, const AtomicString& family, const FontFeatureSettings*, const FontVariantSettings*)
{
// The CSS font matching algorithm (http://www.w3.org/TR/css3-fonts/#font-matching-algorithm)
// says that we must find an exact match for font family, slant (italic or oblique can be used)
// and font weight (we only match bold/non-bold here).
RefPtr<FcPattern> pattern = adoptRef(FcPatternCreate());
- String familyNameString(getFamilyNameStringFromFontDescriptionAndFamily(fontDescription, family));
+ // Never choose unscalable fonts, as they pixelate when displayed at different sizes.
+ FcPatternAddBool(pattern.get(), FC_SCALABLE, FcTrue);
+ String familyNameString(getFamilyNameStringFromFamily(family));
if (!FcPatternAddString(pattern.get(), FC_FAMILY, reinterpret_cast<const FcChar8*>(familyNameString.utf8().data())))
return nullptr;
@@ -183,11 +351,19 @@ PassOwnPtr<FontPlatformData> FontCache::createFontPlatformData(const FontDescrip
return nullptr;
// The strategy is originally from Skia (src/ports/SkFontHost_fontconfig.cpp):
-
- // Allow Fontconfig to do pre-match substitution. Unless we are accessing a "fallback"
- // family like "sans," this is the only time we allow Fontconfig to substitute one
- // family name for another (i.e. if the fonts are aliased to each other).
- FcConfigSubstitute(0, pattern.get(), FcMatchPattern);
+ //
+ // We do not normally allow fontconfig to substitute one font family for another, since this
+ // would break CSS font family fallback: the website should be in control of fallback. During
+ // normal font matching, the only font family substitution permitted is for generic families
+ // (sans, serif, monospace) or for strongly-aliased fonts (which are to be treated as
+ // effectively identical). This is because the font matching step is designed to always find a
+ // match for the font, which we don't want.
+ //
+ // Fontconfig is used in two stages: (1) configuration and (2) matching. During the
+ // configuration step, before any matching occurs, we allow arbitrary family substitutions,
+ // since this is an exact matter of respecting the user's font configuration.
+ FcConfigSubstitute(nullptr, pattern.get(), FcMatchPattern);
+ cairo_ft_font_options_substitute(getDefaultCairoFontOptions(), pattern.get());
FcDefaultSubstitute(pattern.get());
FcChar8* fontConfigFamilyNameAfterConfiguration;
@@ -195,7 +371,7 @@ PassOwnPtr<FontPlatformData> FontCache::createFontPlatformData(const FontDescrip
String familyNameAfterConfiguration = String::fromUTF8(reinterpret_cast<char*>(fontConfigFamilyNameAfterConfiguration));
FcResult fontConfigResult;
- RefPtr<FcPattern> resultPattern = adoptRef(FcFontMatch(0, pattern.get(), &fontConfigResult));
+ RefPtr<FcPattern> resultPattern = adoptRef(FcFontMatch(nullptr, pattern.get(), &fontConfigResult));
if (!resultPattern) // No match.
return nullptr;
@@ -203,23 +379,26 @@ PassOwnPtr<FontPlatformData> FontCache::createFontPlatformData(const FontDescrip
FcPatternGetString(resultPattern.get(), FC_FAMILY, 0, &fontConfigFamilyNameAfterMatching);
String familyNameAfterMatching = String::fromUTF8(reinterpret_cast<char*>(fontConfigFamilyNameAfterMatching));
- // If Fontconfig gave use a different font family than the one we requested, we should ignore it
- // and allow WebCore to give us the next font on the CSS fallback list. The only exception is if
- // this family name is a commonly used generic family.
- if (!equalIgnoringCase(familyNameAfterConfiguration, familyNameAfterMatching)
- && !(equalIgnoringCase(familyNameString, "sans") || equalIgnoringCase(familyNameString, "sans-serif")
- || equalIgnoringCase(familyNameString, "serif") || equalIgnoringCase(familyNameString, "monospace")
- || equalIgnoringCase(familyNameString, "fantasy") || equalIgnoringCase(familyNameString, "cursive")))
+ // If Fontconfig gave us a different font family than the one we requested, we should ignore it
+ // and allow WebCore to give us the next font on the CSS fallback list. The exceptions are if
+ // this family name is a commonly-used generic family, or if the families are strongly-aliased.
+ // Checking for a strong alias comes last, since it is slow.
+ if (!equalIgnoringASCIICase(familyNameAfterConfiguration, familyNameAfterMatching) && !isCommonlyUsedGenericFamily(familyNameString) && !areStronglyAliased(familyNameAfterConfiguration, familyNameAfterMatching))
return nullptr;
// Verify that this font has an encoding compatible with Fontconfig. Fontconfig currently
// supports three encodings in FcFreeTypeCharIndex: Unicode, Symbol and AppleRoman.
// If this font doesn't have one of these three encodings, don't select it.
- OwnPtr<FontPlatformData> platformData = adoptPtr(new FontPlatformData(resultPattern.get(), fontDescription));
+ auto platformData = std::make_unique<FontPlatformData>(resultPattern.get(), fontDescription);
if (!platformData->hasCompatibleCharmap())
return nullptr;
- return platformData.release();
+ return platformData;
+}
+
+const AtomicString& FontCache::platformAlternateFamilyName(const AtomicString&)
+{
+ return nullAtom;
}
}
diff --git a/Source/WebCore/platform/graphics/freetype/FontCustomPlatformDataFreeType.cpp b/Source/WebCore/platform/graphics/freetype/FontCustomPlatformDataFreeType.cpp
index fdf7a3633..0075343b7 100644
--- a/Source/WebCore/platform/graphics/freetype/FontCustomPlatformDataFreeType.cpp
+++ b/Source/WebCore/platform/graphics/freetype/FontCustomPlatformDataFreeType.cpp
@@ -35,10 +35,26 @@ static void releaseCustomFontData(void* data)
}
FontCustomPlatformData::FontCustomPlatformData(FT_Face freeTypeFace, SharedBuffer& buffer)
- : m_freeTypeFace(freeTypeFace)
- , m_fontFace(cairo_ft_font_face_create_for_ft_face(freeTypeFace, 0))
+ : m_fontFace(cairo_ft_font_face_create_for_ft_face(freeTypeFace, FT_LOAD_FORCE_AUTOHINT))
{
- // FIXME Should we be setting some hinting options here?
+ // FT_LOAD_FORCE_AUTOHINT prohibits use of the font's native hinting. This
+ // is a safe option for custom fonts because (a) some such fonts may have
+ // broken hinting, which site admins may not notice if other browsers do not
+ // use the native hints, and (b) allowing native hints exposes the FreeType
+ // bytecode interpreter to potentially-malicious input. Treating web fonts
+ // differently than system fonts is non-ideal, but the result of autohinting
+ // is always decent, whereas native hints sometimes look terrible, and
+ // unlike system fonts where Fontconfig may change the hinting settings on a
+ // per-font basis, the same settings are used for all web fonts. Note that
+ // Chrome is considering switching from autohinting to native hinting in
+ // https://code.google.com/p/chromium/issues/detail?id=173207 but this is
+ // more risk than we want to assume for now. See
+ // https://bugs.webkit.org/show_bug.cgi?id=140994 before changing this, and
+ // also consider that (a) the fonts' native hints will all be designed to
+ // work on Windows, and might not look good at all with FreeType, whereas
+ // automatic hints will always look decent, and (b) Fontconfig is not
+ // capable of providing any per-font hinting settings for web fonts, unlike
+ // for system fonts, so it seems acceptable to treat them differently.
buffer.ref(); // This is balanced by the buffer->deref() in releaseCustomFontData.
static cairo_user_data_key_t bufferKey;
@@ -54,13 +70,12 @@ FontCustomPlatformData::FontCustomPlatformData(FT_Face freeTypeFace, SharedBuffe
FontCustomPlatformData::~FontCustomPlatformData()
{
- // m_freeTypeFace will be destroyed along with m_fontFace. See the constructor.
cairo_font_face_destroy(m_fontFace);
}
-FontPlatformData FontCustomPlatformData::fontPlatformData(int size, bool bold, bool italic, FontOrientation orientation, FontWidthVariant, FontRenderingMode)
+FontPlatformData FontCustomPlatformData::fontPlatformData(const FontDescription& description, bool bold, bool italic)
{
- return FontPlatformData(m_fontFace, size, bold, italic, orientation);
+ return FontPlatformData(m_fontFace, description, bold, italic);
}
std::unique_ptr<FontCustomPlatformData> createFontCustomPlatformData(SharedBuffer& buffer)
@@ -79,7 +94,12 @@ std::unique_ptr<FontCustomPlatformData> createFontCustomPlatformData(SharedBuffe
bool FontCustomPlatformData::supportsFormat(const String& format)
{
- return equalIgnoringCase(format, "truetype") || equalIgnoringCase(format, "opentype") || equalIgnoringCase(format, "woff");
+ return equalLettersIgnoringASCIICase(format, "truetype")
+ || equalLettersIgnoringASCIICase(format, "opentype")
+#if USE(WOFF2)
+ || equalLettersIgnoringASCIICase(format, "woff2")
+#endif
+ || equalLettersIgnoringASCIICase(format, "woff");
}
}
diff --git a/Source/WebCore/platform/graphics/freetype/FontPlatformData.h b/Source/WebCore/platform/graphics/freetype/FontPlatformData.h
deleted file mode 100644
index 808ba21ce..000000000
--- a/Source/WebCore/platform/graphics/freetype/FontPlatformData.h
+++ /dev/null
@@ -1,124 +0,0 @@
-/*
- * Copyright (C) 2006, 2007, 2008 Apple Inc.
- * Copyright (C) 2006 Michael Emmel mike.emmel@gmail.com
- * Copyright (C) 2007 Holger Hans Peter Freyther
- * Copyright (C) 2007 Pioneer Research Center USA, Inc.
- * Copyright (C) 2010 Igalia S.L.
- * All rights reserved.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public License
- * along with this library; see the file COPYING.LIB. If not, write to
- * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
- * Boston, MA 02110-1301, USA.
- *
- */
-
-#ifndef FontPlatformDataFreeType_h
-#define FontPlatformDataFreeType_h
-
-#include "FontCache.h"
-#include "FontDescription.h"
-#include "FontOrientation.h"
-#include "GlyphBuffer.h"
-#include "HarfBuzzFace.h"
-#include "OpenTypeVerticalData.h"
-#include "RefPtrCairo.h"
-#include "SharedBuffer.h"
-#include <wtf/Forward.h>
-#include <wtf/HashFunctions.h>
-
-typedef struct _FcFontSet FcFontSet;
-class HarfBuzzFace;
-
-namespace WebCore {
-
-class FontPlatformData {
-public:
- FontPlatformData(WTF::HashTableDeletedValueType)
- : m_fallbacks(0)
- , m_size(0)
- , m_syntheticBold(false)
- , m_syntheticOblique(false)
- , m_scaledFont(hashTableDeletedFontValue())
- , m_orientation(Horizontal)
- { }
-
- FontPlatformData()
- : m_fallbacks(0)
- , m_size(0)
- , m_syntheticBold(false)
- , m_syntheticOblique(false)
- , m_scaledFont(0)
- , m_orientation(Horizontal)
- { }
-
- FontPlatformData(FcPattern*, const FontDescription&);
- FontPlatformData(cairo_font_face_t*, float size, bool bold, bool italic, FontOrientation);
- FontPlatformData(float size, bool bold, bool italic);
- FontPlatformData(const FontPlatformData&);
- FontPlatformData(const FontPlatformData&, float size);
-
- ~FontPlatformData();
-
- HarfBuzzFace* harfBuzzFace() const;
-
- bool isFixedPitch();
- float size() const { return m_size; }
- void setSize(float size) { m_size = size; }
- bool syntheticBold() const { return m_syntheticBold; }
- bool syntheticOblique() const { return m_syntheticOblique; }
- bool hasCompatibleCharmap();
-
- FontOrientation orientation() const { return m_orientation; }
- void setOrientation(FontOrientation);
- PassRefPtr<SharedBuffer> openTypeTable(uint32_t table) const;
- PassRefPtr<OpenTypeVerticalData> verticalData() const;
-
- cairo_scaled_font_t* scaledFont() const { return m_scaledFont; }
-
- unsigned hash() const
- {
- return PtrHash<cairo_scaled_font_t*>::hash(m_scaledFont);
- }
-
- bool operator==(const FontPlatformData&) const;
- FontPlatformData& operator=(const FontPlatformData&);
- bool isHashTableDeletedValue() const
- {
- return m_scaledFont == hashTableDeletedFontValue();
- }
-
-#ifndef NDEBUG
- String description() const;
-#endif
-
- RefPtr<FcPattern> m_pattern;
- mutable FcFontSet* m_fallbacks; // Initialized lazily.
- float m_size;
- bool m_syntheticBold;
- bool m_syntheticOblique;
- bool m_fixedWidth;
- cairo_scaled_font_t* m_scaledFont;
- mutable RefPtr<HarfBuzzFace> m_harfBuzzFace;
-
-private:
- void initializeWithFontFace(cairo_font_face_t*, const FontDescription& = FontDescription());
- static cairo_scaled_font_t* hashTableDeletedFontValue() { return reinterpret_cast<cairo_scaled_font_t*>(-1); }
-
- FontOrientation m_orientation;
- cairo_matrix_t m_horizontalOrientationMatrix;
-};
-
-}
-
-#endif // FontPlatformDataFreeType_h
diff --git a/Source/WebCore/platform/graphics/freetype/FontPlatformDataFreeType.cpp b/Source/WebCore/platform/graphics/freetype/FontPlatformDataFreeType.cpp
index 7c4d853f8..69d640205 100644
--- a/Source/WebCore/platform/graphics/freetype/FontPlatformDataFreeType.cpp
+++ b/Source/WebCore/platform/graphics/freetype/FontPlatformDataFreeType.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2006 Apple Computer, Inc.
+ * Copyright (C) 2006 Apple Inc.
* Copyright (C) 2006 Michael Emmel mike.emmel@gmail.com
* Copyright (C) 2007, 2008 Alp Toker <alp@atoker.com>
* Copyright (C) 2007 Holger Hans Peter Freyther
@@ -25,21 +25,21 @@
#include "config.h"
#include "FontPlatformData.h"
+#include "CairoUniquePtr.h"
+#include "CairoUtilities.h"
+#include "FontCache.h"
#include "FontDescription.h"
+#include "SharedBuffer.h"
#include <cairo-ft.h>
-#include <cairo.h>
#include <fontconfig/fcfreetype.h>
#include <ft2build.h>
#include FT_TRUETYPE_TABLES_H
+#include <wtf/MathExtras.h>
#include <wtf/text/WTFString.h>
-#if !PLATFORM(EFL)
-#include <gdk/gdk.h>
-#endif
-
namespace WebCore {
-cairo_subpixel_order_t convertFontConfigSubpixelOrder(int fontConfigOrder)
+static cairo_subpixel_order_t convertFontConfigSubpixelOrder(int fontConfigOrder)
{
switch (fontConfigOrder) {
case FC_RGBA_RGB:
@@ -57,7 +57,7 @@ cairo_subpixel_order_t convertFontConfigSubpixelOrder(int fontConfigOrder)
return CAIRO_SUBPIXEL_ORDER_DEFAULT;
}
-cairo_hint_style_t convertFontConfigHintStyle(int fontConfigStyle)
+static cairo_hint_style_t convertFontConfigHintStyle(int fontConfigStyle)
{
switch (fontConfigStyle) {
case FC_HINT_NONE:
@@ -72,7 +72,7 @@ cairo_hint_style_t convertFontConfigHintStyle(int fontConfigStyle)
return CAIRO_HINT_STYLE_NONE;
}
-void setCairoFontOptionsFromFontConfigPattern(cairo_font_options_t* options, FcPattern* pattern)
+static void setCairoFontOptionsFromFontConfigPattern(cairo_font_options_t* options, FcPattern* pattern)
{
FcBool booleanResult;
int integerResult;
@@ -102,46 +102,39 @@ void setCairoFontOptionsFromFontConfigPattern(cairo_font_options_t* options, FcP
cairo_font_options_set_hint_style(options, CAIRO_HINT_STYLE_NONE);
}
-static cairo_font_options_t* getDefaultFontOptions()
+static FcPattern* getDefaultFontconfigOptions()
{
-#if PLATFORM(GTK)
- if (GdkScreen* screen = gdk_screen_get_default()) {
- const cairo_font_options_t* screenOptions = gdk_screen_get_font_options(screen);
- if (screenOptions)
- return cairo_font_options_copy(screenOptions);
- }
-#endif
- return cairo_font_options_create();
-}
-
-static void rotateCairoMatrixForVerticalOrientation(cairo_matrix_t* matrix)
-{
- // The resulting transformation matrix for vertical glyphs (V) is a
- // combination of rotation (R) and translation (T) applied on the
- // horizontal matrix (H). V = H . R . T, where R rotates by -90 degrees
- // and T translates by font size towards y axis.
- cairo_matrix_rotate(matrix, -piOverTwoDouble);
- cairo_matrix_translate(matrix, 0.0, 1.0);
+ // Get some generic default settings from fontconfig for web fonts. Strategy
+ // from Behdad Esfahbod in https://code.google.com/p/chromium/issues/detail?id=173207#c35
+ // For web fonts, the hint style is overridden in FontCustomPlatformData::FontCustomPlatformData
+ // so Fontconfig will not affect the hint style, but it may disable hinting completely.
+ static FcPattern* pattern = nullptr;
+ static std::once_flag flag;
+ std::call_once(flag, [](FcPattern*) {
+ pattern = FcPatternCreate();
+ FcConfigSubstitute(nullptr, pattern, FcMatchPattern);
+ cairo_ft_font_options_substitute(getDefaultCairoFontOptions(), pattern);
+ FcDefaultSubstitute(pattern);
+ FcPatternDel(pattern, FC_FAMILY);
+ FcConfigSubstitute(nullptr, pattern, FcMatchFont);
+ }, pattern);
+ return pattern;
}
FontPlatformData::FontPlatformData(FcPattern* pattern, const FontDescription& fontDescription)
: m_pattern(pattern)
- , m_fallbacks(0)
, m_size(fontDescription.computedPixelSize())
- , m_syntheticBold(false)
- , m_syntheticOblique(false)
- , m_fixedWidth(false)
- , m_scaledFont(0)
, m_orientation(fontDescription.orientation())
{
+ ASSERT(m_pattern);
RefPtr<cairo_font_face_t> fontFace = adoptRef(cairo_ft_font_face_create_for_pattern(m_pattern.get()));
- initializeWithFontFace(fontFace.get(), fontDescription);
int spacing;
if (FcPatternGetInteger(pattern, FC_SPACING, 0, &spacing) == FcResultMatch && spacing == FC_MONO)
m_fixedWidth = true;
- if (fontDescription.weight() >= FontWeightBold) {
+ bool descriptionAllowsSyntheticBold = fontDescription.fontSynthesis() & FontSynthesisWeight;
+ if (descriptionAllowsSyntheticBold && fontDescription.weight() >= FontWeightBold) {
// The FC_EMBOLDEN property instructs us to fake the boldness of the font.
FcBool fontConfigEmbolden = FcFalse;
if (FcPatternGetBool(pattern, FC_EMBOLDEN, 0, &fontConfigEmbolden) == FcResultMatch)
@@ -152,36 +145,34 @@ FontPlatformData::FontPlatformData(FcPattern* pattern, const FontDescription& fo
if (!m_syntheticBold && FcPatternGetInteger(pattern, FC_WEIGHT, 0, &weight) == FcResultMatch)
m_syntheticBold = m_syntheticBold || weight < FC_WEIGHT_DEMIBOLD;
}
-}
-FontPlatformData::FontPlatformData(float size, bool bold, bool italic)
- : m_fallbacks(0)
- , m_size(size)
- , m_syntheticBold(bold)
- , m_syntheticOblique(italic)
- , m_fixedWidth(false)
- , m_scaledFont(0)
- , m_orientation(Horizontal)
-{
- // We cannot create a scaled font here.
+ // We requested an italic font, but Fontconfig gave us one that was neither oblique nor italic.
+ int actualFontSlant;
+ bool descriptionAllowsSyntheticOblique = fontDescription.fontSynthesis() & FontSynthesisStyle;
+ if (descriptionAllowsSyntheticOblique && fontDescription.italic()
+ && FcPatternGetInteger(pattern, FC_SLANT, 0, &actualFontSlant) == FcResultMatch) {
+ m_syntheticOblique = actualFontSlant == FC_SLANT_ROMAN;
+ }
+
+ buildScaledFont(fontFace.get());
}
-FontPlatformData::FontPlatformData(cairo_font_face_t* fontFace, float size, bool bold, bool italic, FontOrientation orientation)
- : m_fallbacks(0)
- , m_size(size)
+FontPlatformData::FontPlatformData(cairo_font_face_t* fontFace, const FontDescription& description, bool bold, bool italic)
+ : m_size(description.computedPixelSize())
+ , m_orientation(description.orientation())
, m_syntheticBold(bold)
, m_syntheticOblique(italic)
- , m_fixedWidth(false)
- , m_scaledFont(0)
- , m_orientation(orientation)
{
- initializeWithFontFace(fontFace);
+ buildScaledFont(fontFace);
- FT_Face fontConfigFace = cairo_ft_scaled_font_lock_face(m_scaledFont);
- if (fontConfigFace) {
+ CairoFtFaceLocker cairoFtFaceLocker(m_scaledFont.get());
+ if (FT_Face fontConfigFace = cairoFtFaceLocker.ftFace())
m_fixedWidth = fontConfigFace->face_flags & FT_FACE_FLAG_FIXED_WIDTH;
- cairo_ft_scaled_font_unlock_face(m_scaledFont);
- }
+}
+
+FontPlatformData::FontPlatformData(const FontPlatformData& other)
+{
+ *this = other;
}
FontPlatformData& FontPlatformData::operator=(const FontPlatformData& other)
@@ -191,56 +182,62 @@ FontPlatformData& FontPlatformData::operator=(const FontPlatformData& other)
return *this;
m_size = other.m_size;
+ m_orientation = other.m_orientation;
+ m_widthVariant = other.m_widthVariant;
+ m_textRenderingMode = other.m_textRenderingMode;
+
m_syntheticBold = other.m_syntheticBold;
m_syntheticOblique = other.m_syntheticOblique;
+ m_isColorBitmapFont = other.m_isColorBitmapFont;
+ m_isHashTableDeletedValue = other.m_isHashTableDeletedValue;
+ m_isSystemFont = other.m_isSystemFont;
+
m_fixedWidth = other.m_fixedWidth;
m_pattern = other.m_pattern;
- m_orientation = other.m_orientation;
- m_horizontalOrientationMatrix = other.m_horizontalOrientationMatrix;
-
- if (m_fallbacks) {
- FcFontSetDestroy(m_fallbacks);
- // This will be re-created on demand.
- m_fallbacks = 0;
- }
- if (m_scaledFont && m_scaledFont != hashTableDeletedFontValue())
- cairo_scaled_font_destroy(m_scaledFont);
- m_scaledFont = cairo_scaled_font_reference(other.m_scaledFont);
+ // This will be re-created on demand.
+ m_fallbacks = nullptr;
+ m_scaledFont = other.m_scaledFont;
m_harfBuzzFace = other.m_harfBuzzFace;
return *this;
}
-FontPlatformData::FontPlatformData(const FontPlatformData& other)
- : m_fallbacks(0)
- , m_scaledFont(0)
- , m_harfBuzzFace(other.m_harfBuzzFace)
+FontPlatformData::~FontPlatformData()
{
- *this = other;
}
-FontPlatformData::FontPlatformData(const FontPlatformData& other, float size)
- : m_harfBuzzFace(other.m_harfBuzzFace)
+FontPlatformData FontPlatformData::cloneWithOrientation(const FontPlatformData& source, FontOrientation orientation)
{
- *this = other;
-
- // We need to reinitialize the instance, because the difference in size
- // necessitates a new scaled font instance.
- m_size = size;
- initializeWithFontFace(cairo_scaled_font_get_font_face(m_scaledFont));
+ FontPlatformData copy(source);
+ if (copy.m_scaledFont && copy.m_orientation != orientation) {
+ copy.m_orientation = orientation;
+ copy.buildScaledFont(cairo_scaled_font_get_font_face(copy.m_scaledFont.get()));
+ }
+ return copy;
}
-FontPlatformData::~FontPlatformData()
+FontPlatformData FontPlatformData::cloneWithSyntheticOblique(const FontPlatformData& source, bool syntheticOblique)
{
- if (m_fallbacks) {
- FcFontSetDestroy(m_fallbacks);
- m_fallbacks = 0;
+ FontPlatformData copy(source);
+ if (copy.m_syntheticOblique != syntheticOblique) {
+ copy.m_syntheticOblique = syntheticOblique;
+ ASSERT(copy.m_scaledFont.get());
+ copy.buildScaledFont(cairo_scaled_font_get_font_face(copy.m_scaledFont.get()));
}
+ return copy;
+}
- if (m_scaledFont && m_scaledFont != hashTableDeletedFontValue())
- cairo_scaled_font_destroy(m_scaledFont);
+FontPlatformData FontPlatformData::cloneWithSize(const FontPlatformData& source, float size)
+{
+ FontPlatformData copy(source);
+ copy.m_size = size;
+ // We need to reinitialize the instance, because the difference in size
+ // necessitates a new scaled font instance.
+ ASSERT(copy.m_scaledFont.get());
+ copy.buildScaledFont(cairo_scaled_font_get_font_face(copy.m_scaledFont.get()));
+ return copy;
}
HarfBuzzFace* FontPlatformData::harfBuzzFace() const
@@ -251,12 +248,29 @@ HarfBuzzFace* FontPlatformData::harfBuzzFace() const
return m_harfBuzzFace.get();
}
-bool FontPlatformData::isFixedPitch()
+FcFontSet* FontPlatformData::fallbacks() const
+{
+ if (m_fallbacks)
+ return m_fallbacks.get();
+
+ if (m_pattern) {
+ FcResult fontConfigResult;
+ m_fallbacks.reset(FcFontSort(nullptr, m_pattern.get(), FcTrue, nullptr, &fontConfigResult));
+ }
+ return m_fallbacks.get();
+}
+
+bool FontPlatformData::isFixedPitch() const
{
return m_fixedWidth;
}
-bool FontPlatformData::operator==(const FontPlatformData& other) const
+unsigned FontPlatformData::hash() const
+{
+ return PtrHash<cairo_scaled_font_t*>::hash(m_scaledFont.get());
+}
+
+bool FontPlatformData::platformIsEqual(const FontPlatformData& other) const
{
// FcPatternEqual does not support null pointers as arguments.
if ((m_pattern && !other.m_pattern)
@@ -264,11 +278,7 @@ bool FontPlatformData::operator==(const FontPlatformData& other) const
|| (m_pattern != other.m_pattern && !FcPatternEqual(m_pattern.get(), other.m_pattern.get())))
return false;
- return m_scaledFont == other.m_scaledFont
- && m_size == other.m_size
- && m_syntheticOblique == other.m_syntheticOblique
- && m_orientation == other.m_orientation
- && m_syntheticBold == other.m_syntheticBold;
+ return m_scaledFont == other.m_scaledFont;
}
#ifndef NDEBUG
@@ -278,127 +288,87 @@ String FontPlatformData::description() const
}
#endif
-void FontPlatformData::initializeWithFontFace(cairo_font_face_t* fontFace, const FontDescription& fontDescription)
+void FontPlatformData::buildScaledFont(cairo_font_face_t* fontFace)
{
- cairo_font_options_t* options = getDefaultFontOptions();
+ CairoUniquePtr<cairo_font_options_t> options(cairo_font_options_copy(getDefaultCairoFontOptions()));
+ FcPattern* optionsPattern = m_pattern ? m_pattern.get() : getDefaultFontconfigOptions();
+ setCairoFontOptionsFromFontConfigPattern(options.get(), optionsPattern);
cairo_matrix_t ctm;
cairo_matrix_init_identity(&ctm);
- // Scaling a font with width zero size leads to a failed cairo_scaled_font_t instantiations.
- // Instead we scale we scale the font to a very tiny size and just abort rendering later on.
- float realSize = m_size ? m_size : 1;
-
+ // FontConfig may return a list of transformation matrices with the pattern, for instance,
+ // for fonts that are oblique. We use that to initialize the cairo font matrix.
cairo_matrix_t fontMatrix;
- if (!m_pattern)
- cairo_matrix_init_scale(&fontMatrix, realSize, realSize);
- else {
- setCairoFontOptionsFromFontConfigPattern(options, m_pattern.get());
-
- // FontConfig may return a list of transformation matrices with the pattern, for instance,
- // for fonts that are oblique. We use that to initialize the cairo font matrix.
- FcMatrix fontConfigMatrix, *tempFontConfigMatrix;
- FcMatrixInit(&fontConfigMatrix);
-
- // These matrices may be stacked in the pattern, so it's our job to get them all and multiply them.
- for (int i = 0; FcPatternGetMatrix(m_pattern.get(), FC_MATRIX, i, &tempFontConfigMatrix) == FcResultMatch; i++)
- FcMatrixMultiply(&fontConfigMatrix, &fontConfigMatrix, tempFontConfigMatrix);
- cairo_matrix_init(&fontMatrix, fontConfigMatrix.xx, -fontConfigMatrix.yx,
- -fontConfigMatrix.xy, fontConfigMatrix.yy, 0, 0);
-
- // We requested an italic font, but Fontconfig gave us one that was neither oblique nor italic.
- int actualFontSlant;
- if (fontDescription.italic() && FcPatternGetInteger(m_pattern.get(), FC_SLANT, 0, &actualFontSlant) == FcResultMatch)
- m_syntheticOblique = actualFontSlant == FC_SLANT_ROMAN;
-
- // The matrix from FontConfig does not include the scale.
- cairo_matrix_scale(&fontMatrix, realSize, realSize);
- }
+ FcMatrix fontConfigMatrix, *tempFontConfigMatrix;
+ FcMatrixInit(&fontConfigMatrix);
+
+ // These matrices may be stacked in the pattern, so it's our job to get them all and multiply them.
+ for (int i = 0; FcPatternGetMatrix(optionsPattern, FC_MATRIX, i, &tempFontConfigMatrix) == FcResultMatch; i++)
+ FcMatrixMultiply(&fontConfigMatrix, &fontConfigMatrix, tempFontConfigMatrix);
+ cairo_matrix_init(&fontMatrix, fontConfigMatrix.xx, -fontConfigMatrix.yx,
+ -fontConfigMatrix.xy, fontConfigMatrix.yy, 0, 0);
+
+ // The matrix from FontConfig does not include the scale. Scaling a font with width zero size leads
+ // to a failed cairo_scaled_font_t instantiations. Instead we scale we scale the font to a very tiny
+ // size and just abort rendering later on.
+ float realSize = m_size ? m_size : 1;
+ cairo_matrix_scale(&fontMatrix, realSize, realSize);
if (syntheticOblique()) {
static const float syntheticObliqueSkew = -tanf(14 * acosf(0) / 90);
- cairo_matrix_t skew = {1, 0, syntheticObliqueSkew, 1, 0, 0};
- cairo_matrix_multiply(&fontMatrix, &skew, &fontMatrix);
+ static const cairo_matrix_t skew = {1, 0, syntheticObliqueSkew, 1, 0, 0};
+ static const cairo_matrix_t verticalSkew = {1, -syntheticObliqueSkew, 0, 1, 0, 0};
+ cairo_matrix_multiply(&fontMatrix, m_orientation == Vertical ? &verticalSkew : &skew, &fontMatrix);
}
- m_horizontalOrientationMatrix = fontMatrix;
- if (m_orientation == Vertical)
- rotateCairoMatrixForVerticalOrientation(&fontMatrix);
-
- m_scaledFont = cairo_scaled_font_create(fontFace, &fontMatrix, &ctm, options);
- cairo_font_options_destroy(options);
-}
+ if (m_orientation == Vertical) {
+ // The resulting transformation matrix for vertical glyphs (V) is a
+ // combination of rotation (R) and translation (T) applied on the
+ // horizontal matrix (H). V = H . R . T, where R rotates by -90 degrees
+ // and T translates by font size towards y axis.
+ cairo_matrix_rotate(&fontMatrix, -piOverTwoDouble);
+ cairo_matrix_translate(&fontMatrix, 0.0, 1.0);
+ }
-bool FontPlatformData::hasCompatibleCharmap()
-{
- ASSERT(m_scaledFont);
- FT_Face freeTypeFace = cairo_ft_scaled_font_lock_face(m_scaledFont);
- bool hasCompatibleCharmap = !(FT_Select_Charmap(freeTypeFace, ft_encoding_unicode)
- && FT_Select_Charmap(freeTypeFace, ft_encoding_symbol)
- && FT_Select_Charmap(freeTypeFace, ft_encoding_apple_roman));
- cairo_ft_scaled_font_unlock_face(m_scaledFont);
- return hasCompatibleCharmap;
+ m_scaledFont = adoptRef(cairo_scaled_font_create(fontFace, &fontMatrix, &ctm, options.get()));
}
-PassRefPtr<OpenTypeVerticalData> FontPlatformData::verticalData() const
+bool FontPlatformData::hasCompatibleCharmap() const
{
- ASSERT(hash());
- return fontCache()->getVerticalData(String::number(hash()), *this);
+ ASSERT(m_scaledFont.get());
+ CairoFtFaceLocker cairoFtFaceLocker(m_scaledFont.get());
+ FT_Face freeTypeFace = cairoFtFaceLocker.ftFace();
+ if (!freeTypeFace)
+ return false;
+ return !(FT_Select_Charmap(freeTypeFace, ft_encoding_unicode)
+ && FT_Select_Charmap(freeTypeFace, ft_encoding_symbol)
+ && FT_Select_Charmap(freeTypeFace, ft_encoding_apple_roman));
}
-PassRefPtr<SharedBuffer> FontPlatformData::openTypeTable(uint32_t table) const
+RefPtr<SharedBuffer> FontPlatformData::openTypeTable(uint32_t table) const
{
- FT_Face freeTypeFace = cairo_ft_scaled_font_lock_face(m_scaledFont);
+ CairoFtFaceLocker cairoFtFaceLocker(m_scaledFont.get());
+ FT_Face freeTypeFace = cairoFtFaceLocker.ftFace();
if (!freeTypeFace)
- return 0;
+ return nullptr;
FT_ULong tableSize = 0;
// Tag bytes need to be reversed because OT_MAKE_TAG uses big-endian order.
uint32_t tag = FT_MAKE_TAG((table & 0xff), (table & 0xff00) >> 8, (table & 0xff0000) >> 16, table >> 24);
if (FT_Load_Sfnt_Table(freeTypeFace, tag, 0, 0, &tableSize))
- return 0;
+ return nullptr;
RefPtr<SharedBuffer> buffer = SharedBuffer::create(tableSize);
- FT_ULong expectedTableSize = tableSize;
if (buffer->size() != tableSize)
- return 0;
+ return nullptr;
+ FT_ULong expectedTableSize = tableSize;
FT_Error error = FT_Load_Sfnt_Table(freeTypeFace, tag, 0, reinterpret_cast<FT_Byte*>(const_cast<char*>(buffer->data())), &tableSize);
if (error || tableSize != expectedTableSize)
- return 0;
-
- cairo_ft_scaled_font_unlock_face(m_scaledFont);
+ return nullptr;
- return buffer.release();
+ return buffer;
}
-void FontPlatformData::setOrientation(FontOrientation orientation)
-{
- ASSERT(m_scaledFont);
-
- if (!m_scaledFont || (m_orientation == orientation))
- return;
-
- cairo_matrix_t transformationMatrix;
- cairo_matrix_init_identity(&transformationMatrix);
-
- cairo_matrix_t fontMatrix;
- cairo_scaled_font_get_font_matrix(m_scaledFont, &fontMatrix);
-
- cairo_font_options_t* options = getDefaultFontOptions();
-
- // In case of vertical orientation, rotate the transformation matrix.
- // Otherwise restore the horizontal orientation matrix.
- if (orientation == Vertical)
- rotateCairoMatrixForVerticalOrientation(&fontMatrix);
- else
- fontMatrix = m_horizontalOrientationMatrix;
-
- cairo_font_face_t* fontFace = cairo_scaled_font_get_font_face(m_scaledFont);
- cairo_scaled_font_destroy(m_scaledFont);
- m_scaledFont = cairo_scaled_font_create(fontFace, &fontMatrix, &transformationMatrix, options);
- cairo_font_options_destroy(options);
- m_orientation = orientation;
-}
-
-}
+} // namespace WebCore
diff --git a/Source/WebCore/platform/graphics/freetype/GlyphPageTreeNodeFreeType.cpp b/Source/WebCore/platform/graphics/freetype/GlyphPageTreeNodeFreeType.cpp
index d156d8c83..caa4b8de7 100644
--- a/Source/WebCore/platform/graphics/freetype/GlyphPageTreeNodeFreeType.cpp
+++ b/Source/WebCore/platform/graphics/freetype/GlyphPageTreeNodeFreeType.cpp
@@ -12,7 +12,7 @@
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
- * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ * 3. Neither the name of Apple Inc. ("Apple") nor the names of
* its contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
@@ -29,9 +29,10 @@
*/
#include "config.h"
-#include "GlyphPageTreeNode.h"
+#include "GlyphPage.h"
-#include "SimpleFontData.h"
+#include "CairoUtilities.h"
+#include "Font.h"
#include "UTF16UChar32Iterator.h"
#include <cairo-ft.h>
#include <cairo.h>
@@ -39,32 +40,33 @@
namespace WebCore {
-bool GlyphPage::fill(unsigned offset, unsigned length, UChar* buffer, unsigned bufferLength, const SimpleFontData* fontData)
+bool GlyphPage::fill(UChar* buffer, unsigned bufferLength)
{
- cairo_scaled_font_t* scaledFont = fontData->platformData().scaledFont();
+ const Font& font = this->font();
+ cairo_scaled_font_t* scaledFont = font.platformData().scaledFont();
ASSERT(scaledFont);
- FT_Face face = cairo_ft_scaled_font_lock_face(scaledFont);
+ CairoFtFaceLocker cairoFtFaceLocker(scaledFont);
+ FT_Face face = cairoFtFaceLocker.ftFace();
if (!face)
return false;
bool haveGlyphs = false;
UTF16UChar32Iterator iterator(buffer, bufferLength);
- for (unsigned i = 0; i < length; i++) {
+ for (unsigned i = 0; i < GlyphPage::size; i++) {
UChar32 character = iterator.next();
if (character == iterator.end())
break;
Glyph glyph = FcFreeTypeCharIndex(face, character);
if (!glyph)
- setGlyphDataForIndex(offset + i, 0, 0);
+ setGlyphForIndex(i, 0);
else {
- setGlyphDataForIndex(offset + i, glyph, fontData);
+ setGlyphForIndex(i, glyph);
haveGlyphs = true;
}
}
- cairo_ft_scaled_font_unlock_face(scaledFont);
return haveGlyphs;
}
diff --git a/Source/WebCore/platform/graphics/freetype/SimpleFontDataFreeType.cpp b/Source/WebCore/platform/graphics/freetype/SimpleFontDataFreeType.cpp
index 80c4042fa..d528c7bd6 100644
--- a/Source/WebCore/platform/graphics/freetype/SimpleFontDataFreeType.cpp
+++ b/Source/WebCore/platform/graphics/freetype/SimpleFontDataFreeType.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2006 Apple Computer, Inc. All rights reserved.
+ * Copyright (C) 2006 Apple Inc. All rights reserved.
* Copyright (C) 2006 Michael Emmel mike.emmel@gmail.com
* Copyright (C) 2007, 2008 Alp Toker <alp@atoker.com>
* Copyright (C) 2007 Holger Hans Peter Freyther
@@ -14,7 +14,7 @@
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
- * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ * 3. Neither the name of Apple Inc. ("Apple") nor the names of
* its contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
@@ -31,11 +31,11 @@
*/
#include "config.h"
-#include "SimpleFontData.h"
+#include "Font.h"
+#include "CairoUtilities.h"
#include "FloatConversion.h"
#include "FloatRect.h"
-#include "Font.h"
#include "FontCache.h"
#include "FontDescription.h"
#include "GlyphBuffer.h"
@@ -49,13 +49,12 @@
#include FT_TRUETYPE_TAGS_H
#include <unicode/normlzr.h>
#include <wtf/MathExtras.h>
-#include <wtf/unicode/Unicode.h>
namespace WebCore {
-void SimpleFontData::platformInit()
+void Font::platformInit()
{
- if (!m_platformData.m_size)
+ if (!m_platformData.size())
return;
ASSERT(m_platformData.scaledFont());
@@ -64,17 +63,34 @@ void SimpleFontData::platformInit()
float ascent = narrowPrecisionToFloat(fontExtents.ascent);
float descent = narrowPrecisionToFloat(fontExtents.descent);
+ float capHeight = narrowPrecisionToFloat(fontExtents.height);
float lineGap = narrowPrecisionToFloat(fontExtents.height - fontExtents.ascent - fontExtents.descent);
+ {
+ CairoFtFaceLocker cairoFtFaceLocker(m_platformData.scaledFont());
+
+ // If the USE_TYPO_METRICS flag is set in the OS/2 table then we use typo metrics instead.
+ FT_Face freeTypeFace = cairoFtFaceLocker.ftFace();
+ TT_OS2* OS2Table = freeTypeFace ? static_cast<TT_OS2*>(FT_Get_Sfnt_Table(freeTypeFace, ft_sfnt_os2)) : nullptr;
+ if (OS2Table) {
+ const FT_Short kUseTypoMetricsMask = 1 << 7;
+ if (OS2Table->fsSelection & kUseTypoMetricsMask) {
+ // FT_Size_Metrics::y_scale is in 16.16 fixed point format.
+ // Its (fractional) value is a factor that converts vertical metrics from design units to units of 1/64 pixels.
+ double yscale = (freeTypeFace->size->metrics.y_scale / 65536.0) / 64.0;
+ ascent = narrowPrecisionToFloat(yscale * OS2Table->sTypoAscender);
+ descent = -narrowPrecisionToFloat(yscale * OS2Table->sTypoDescender);
+ lineGap = narrowPrecisionToFloat(yscale * OS2Table->sTypoLineGap);
+ }
+ }
+ }
+
m_fontMetrics.setAscent(ascent);
m_fontMetrics.setDescent(descent);
+ m_fontMetrics.setCapHeight(capHeight);
-#if PLATFORM(EFL)
- m_fontMetrics.setLineSpacing(ascent + descent + lineGap);
-#else
// Match CoreGraphics metrics.
m_fontMetrics.setLineSpacing(lroundf(ascent) + lroundf(descent) + lroundf(lineGap));
-#endif
m_fontMetrics.setLineGap(lineGap);
cairo_text_extents_t textExtents;
@@ -85,63 +101,39 @@ void SimpleFontData::platformInit()
m_spaceWidth = narrowPrecisionToFloat((platformData().orientation() == Horizontal) ? textExtents.x_advance : -textExtents.y_advance);
if ((platformData().orientation() == Vertical) && !isTextOrientationFallback()) {
- FT_Face freeTypeFace = cairo_ft_scaled_font_lock_face(m_platformData.scaledFont());
+ CairoFtFaceLocker cairoFtFaceLocker(m_platformData.scaledFont());
+ FT_Face freeTypeFace = cairoFtFaceLocker.ftFace();
m_fontMetrics.setUnitsPerEm(freeTypeFace->units_per_EM);
- cairo_ft_scaled_font_unlock_face(m_platformData.scaledFont());
}
m_syntheticBoldOffset = m_platformData.syntheticBold() ? 1.0f : 0.f;
}
-void SimpleFontData::platformCharWidthInit()
+void Font::platformCharWidthInit()
{
m_avgCharWidth = 0.f;
m_maxCharWidth = 0.f;
initCharWidths();
}
-void SimpleFontData::platformDestroy()
-{
-}
-
-PassRefPtr<SimpleFontData> SimpleFontData::platformCreateScaledFontData(const FontDescription& fontDescription, float scaleFactor) const
+RefPtr<Font> Font::platformCreateScaledFont(const FontDescription& fontDescription, float scaleFactor) const
{
ASSERT(m_platformData.scaledFont());
- return SimpleFontData::create(FontPlatformData(cairo_scaled_font_get_font_face(m_platformData.scaledFont()),
- scaleFactor * fontDescription.computedSize(),
+ FontDescription scaledFontDescription = fontDescription;
+ scaledFontDescription.setComputedSize(scaleFactor * fontDescription.computedSize());
+ return Font::create(FontPlatformData(cairo_scaled_font_get_font_face(m_platformData.scaledFont()),
+ scaledFontDescription,
m_platformData.syntheticBold(),
- m_platformData.syntheticOblique(),
- fontDescription.orientation()),
+ m_platformData.syntheticOblique()),
isCustomFont(), false);
}
-bool SimpleFontData::containsCharacters(const UChar* characters, int bufferLength) const
-{
- ASSERT(m_platformData.scaledFont());
- FT_Face face = cairo_ft_scaled_font_lock_face(m_platformData.scaledFont());
- if (!face)
- return false;
-
- UTF16UChar32Iterator iterator(characters, bufferLength);
- UChar32 character = iterator.next();
- while (character != iterator.end()) {
- if (!FcFreeTypeCharIndex(face, character)) {
- cairo_ft_scaled_font_unlock_face(m_platformData.scaledFont());
- return false;
- }
- character = iterator.next();
- }
-
- cairo_ft_scaled_font_unlock_face(m_platformData.scaledFont());
- return true;
-}
-
-void SimpleFontData::determinePitch()
+void Font::determinePitch()
{
m_treatAsFixedPitch = m_platformData.isFixedPitch();
}
-FloatRect SimpleFontData::platformBoundsForGlyph(Glyph glyph) const
+FloatRect Font::platformBoundsForGlyph(Glyph glyph) const
{
if (!m_platformData.size())
return FloatRect();
@@ -156,7 +148,7 @@ FloatRect SimpleFontData::platformBoundsForGlyph(Glyph glyph) const
return FloatRect();
}
-float SimpleFontData::platformWidthForGlyph(Glyph glyph) const
+float Font::platformWidthForGlyph(Glyph glyph) const
{
if (!m_platformData.size())
return 0;
@@ -172,10 +164,10 @@ float SimpleFontData::platformWidthForGlyph(Glyph glyph) const
}
#if USE(HARFBUZZ)
-bool SimpleFontData::canRenderCombiningCharacterSequence(const UChar* characters, size_t length) const
+bool Font::canRenderCombiningCharacterSequence(const UChar* characters, size_t length) const
{
if (!m_combiningCharacterSequenceSupport)
- m_combiningCharacterSequenceSupport = adoptPtr(new HashMap<String, bool>);
+ m_combiningCharacterSequenceSupport = std::make_unique<HashMap<String, bool>>();
WTF::HashMap<String, bool>::AddResult addResult = m_combiningCharacterSequenceSupport->add(String(characters, length), false);
if (!addResult.isNewEntry)
@@ -188,14 +180,14 @@ bool SimpleFontData::canRenderCombiningCharacterSequence(const UChar* characters
if (U_FAILURE(error) || (static_cast<size_t>(normalizedLength) == length))
return false;
- FT_Face face = cairo_ft_scaled_font_lock_face(m_platformData.scaledFont());
+ CairoFtFaceLocker cairoFtFaceLocker(m_platformData.scaledFont());
+ FT_Face face = cairoFtFaceLocker.ftFace();
if (!face)
return false;
if (FcFreeTypeCharIndex(face, normalizedCharacters[0]))
addResult.iterator->value = true;
- cairo_ft_scaled_font_unlock_face(m_platformData.scaledFont());
return addResult.iterator->value;
}
#endif
diff --git a/Source/WebCore/platform/graphics/glx/GLContextGLX.cpp b/Source/WebCore/platform/graphics/glx/GLContextGLX.cpp
index 176f7722d..451b914ba 100644
--- a/Source/WebCore/platform/graphics/glx/GLContextGLX.cpp
+++ b/Source/WebCore/platform/graphics/glx/GLContextGLX.cpp
@@ -22,9 +22,10 @@
#if USE(GLX)
#include "GraphicsContext3D.h"
#include "OpenGLShims.h"
+#include "PlatformDisplayX11.h"
+#include "XErrorTrapper.h"
#include <GL/glx.h>
#include <cairo.h>
-#include <wtf/OwnPtr.h>
#if ENABLE(ACCELERATED_2D_CANVAS)
#include <cairo-gl.h>
@@ -32,36 +33,130 @@
namespace WebCore {
-PassOwnPtr<GLContextGLX> GLContextGLX::createWindowContext(XID window, GLContext* sharingContext)
+#if !defined(PFNGLXSWAPINTERVALSGIPROC)
+typedef int (*PFNGLXSWAPINTERVALSGIPROC) (int);
+#endif
+#if !defined(PFNGLXCREATECONTEXTATTRIBSARBPROC)
+typedef GLXContext (*PFNGLXCREATECONTEXTATTRIBSARBPROC) (Display *dpy, GLXFBConfig config, GLXContext share_context, Bool direct, const int *attrib_list);
+#endif
+
+static PFNGLXSWAPINTERVALSGIPROC glXSwapIntervalSGI;
+static PFNGLXCREATECONTEXTATTRIBSARBPROC glXCreateContextAttribsARB;
+
+static bool hasSGISwapControlExtension(Display* display)
{
- Display* display = sharedX11Display();
+ static bool initialized = false;
+ if (initialized)
+ return !!glXSwapIntervalSGI;
+
+ initialized = true;
+ if (!GLContext::isExtensionSupported(glXQueryExtensionsString(display, 0), "GLX_SGI_swap_control"))
+ return false;
+
+ glXSwapIntervalSGI = reinterpret_cast<PFNGLXSWAPINTERVALSGIPROC>(glXGetProcAddress(reinterpret_cast<const unsigned char*>("glXSwapIntervalSGI")));
+ return !!glXSwapIntervalSGI;
+}
+
+static bool hasGLXARBCreateContextExtension(Display* display)
+{
+ static bool initialized = false;
+ if (initialized)
+ return !!glXCreateContextAttribsARB;
+
+ initialized = true;
+ if (!GLContext::isExtensionSupported(glXQueryExtensionsString(display, 0), "GLX_ARB_create_context"))
+ return false;
+
+ glXCreateContextAttribsARB = reinterpret_cast<PFNGLXCREATECONTEXTATTRIBSARBPROC>(glXGetProcAddress(reinterpret_cast<const unsigned char*>("glXCreateContextAttribsARB")));
+ return !!glXCreateContextAttribsARB;
+}
+
+static GLXContext createGLXARBContext(Display* display, GLXFBConfig config, GLXContext sharingContext)
+{
+ // We want to create a context with version >= 3.2 core profile, cause that ensures that the i965 driver won't
+ // use the software renderer. If that doesn't work, we will use whatever version available. Unfortunately,
+ // there's no way to know whether glXCreateContextAttribsARB can provide an OpenGL version >= 3.2 until
+ // we actually call it and check the return value. To make things more fun, if a version >= 3.2 cannot be
+ // provided, glXCreateContextAttribsARB will throw a GLXBadFBConfig X error, causing the app to crash.
+ // So, the first time a context is requested, we set a X error trap to disable crashes with GLXBadFBConfig
+ // and then check whether the return value is a context or not.
+
+ static bool canCreate320Context = false;
+ static bool canCreate320ContextInitialized = false;
+
+ static const int contextAttributes[] = {
+ GLX_CONTEXT_MAJOR_VERSION_ARB, 3,
+ GLX_CONTEXT_MINOR_VERSION_ARB, 2,
+ 0
+ };
+
+ if (!canCreate320ContextInitialized) {
+ canCreate320ContextInitialized = true;
+
+ {
+ // Set an X error trapper that ignores errors to avoid crashing on GLXBadFBConfig. Use a scope
+ // here to limit the error trap to just this context creation call.
+ XErrorTrapper trapper(display, XErrorTrapper::Policy::Ignore);
+ GLXContext context = glXCreateContextAttribsARB(display, config, sharingContext, GL_TRUE, contextAttributes);
+ if (context) {
+ canCreate320Context = true;
+ return context;
+ }
+ }
+
+ // Creating the 3.2 context failed, so use whatever is available.
+ return glXCreateContextAttribsARB(display, config, sharingContext, GL_TRUE, nullptr);
+ }
+
+ if (canCreate320Context)
+ return glXCreateContextAttribsARB(display, config, sharingContext, GL_TRUE, contextAttributes);
+
+ return glXCreateContextAttribsARB(display, config, sharingContext, GL_TRUE, nullptr);
+}
+
+std::unique_ptr<GLContextGLX> GLContextGLX::createWindowContext(GLNativeWindowType window, PlatformDisplay& platformDisplay, GLXContext sharingContext)
+{
+ Display* display = downcast<PlatformDisplayX11>(platformDisplay).native();
XWindowAttributes attributes;
- if (!XGetWindowAttributes(display, window, &attributes))
+ if (!XGetWindowAttributes(display, static_cast<Window>(window), &attributes))
return nullptr;
XVisualInfo visualInfo;
visualInfo.visualid = XVisualIDFromVisual(attributes.visual);
- int numReturned = 0;
- XVisualInfo* visualInfoList = XGetVisualInfo(display, VisualIDMask, &visualInfo, &numReturned);
-
- GLXContext glxSharingContext = sharingContext ? static_cast<GLContextGLX*>(sharingContext)->m_context : 0;
- GLXContext context = glXCreateContext(display, visualInfoList, glxSharingContext, True);
- XFree(visualInfoList);
+ int numConfigs = 0;
+ GLXFBConfig config = nullptr;
+ XUniquePtr<GLXFBConfig> configs(glXGetFBConfigs(display, DefaultScreen(display), &numConfigs));
+ for (int i = 0; i < numConfigs; i++) {
+ XUniquePtr<XVisualInfo> glxVisualInfo(glXGetVisualFromFBConfig(display, configs.get()[i]));
+ if (!glxVisualInfo)
+ continue;
+
+ if (glxVisualInfo.get()->visualid == visualInfo.visualid) {
+ config = configs.get()[i];
+ break;
+ }
+ }
+ ASSERT(config);
+
+ XUniqueGLXContext context;
+ if (hasGLXARBCreateContextExtension(display))
+ context.reset(createGLXARBContext(display, config, sharingContext));
+ else {
+ // Legacy OpenGL version.
+ XUniquePtr<XVisualInfo> visualInfoList(glXGetVisualFromFBConfig(display, config));
+ context.reset(glXCreateContext(display, visualInfoList.get(), sharingContext, True));
+ }
if (!context)
return nullptr;
- // GLXPbuffer and XID are both the same types underneath, so we have to share
- // a constructor here with the window path.
- GLContextGLX* contextWrapper = new GLContextGLX(context);
- contextWrapper->m_window = window;
- return adoptPtr(contextWrapper);
+ return std::unique_ptr<GLContextGLX>(new GLContextGLX(platformDisplay, WTFMove(context), window));
}
-PassOwnPtr<GLContextGLX> GLContextGLX::createPbufferContext(GLXContext sharingContext)
+std::unique_ptr<GLContextGLX> GLContextGLX::createPbufferContext(PlatformDisplay& platformDisplay, GLXContext sharingContext)
{
- int fbConfigAttributes[] = {
+ static const int fbConfigAttributes[] = {
GLX_DRAWABLE_TYPE, GLX_PBUFFER_BIT,
GLX_RENDER_TYPE, GLX_RGBA_BIT,
GLX_RED_SIZE, 1,
@@ -73,36 +168,32 @@ PassOwnPtr<GLContextGLX> GLContextGLX::createPbufferContext(GLXContext sharingCo
};
int returnedElements;
- Display* display = sharedX11Display();
- GLXFBConfig* configs = glXChooseFBConfig(display, 0, fbConfigAttributes, &returnedElements);
- if (!returnedElements) {
- XFree(configs);
+ Display* display = downcast<PlatformDisplayX11>(platformDisplay).native();
+ XUniquePtr<GLXFBConfig> configs(glXChooseFBConfig(display, 0, fbConfigAttributes, &returnedElements));
+ if (!returnedElements)
return nullptr;
- }
// We will be rendering to a texture, so our pbuffer does not need to be large.
static const int pbufferAttributes[] = { GLX_PBUFFER_WIDTH, 1, GLX_PBUFFER_HEIGHT, 1, 0 };
- GLXPbuffer pbuffer = glXCreatePbuffer(display, configs[0], pbufferAttributes);
- if (!pbuffer) {
- XFree(configs);
+ XUniqueGLXPbuffer pbuffer(glXCreatePbuffer(display, configs.get()[0], pbufferAttributes));
+ if (!pbuffer)
return nullptr;
+
+ XUniqueGLXContext context;
+ if (hasGLXARBCreateContextExtension(display))
+ context.reset(createGLXARBContext(display, configs.get()[0], sharingContext));
+ else {
+ // Legacy OpenGL version.
+ context.reset(glXCreateNewContext(display, configs.get()[0], GLX_RGBA_TYPE, sharingContext, GL_TRUE));
}
- GLXContext context = glXCreateNewContext(display, configs[0], GLX_RGBA_TYPE, sharingContext, GL_TRUE);
- XFree(configs);
- if (!context) {
- glXDestroyPbuffer(display, pbuffer);
+ if (!context)
return nullptr;
- }
- // GLXPbuffer and XID are both the same types underneath, so we have to share
- // a constructor here with the window path.
- GLContextGLX* contextWrapper = new GLContextGLX(context);
- contextWrapper->m_pbuffer = pbuffer;
- return adoptPtr(contextWrapper);
+ return std::unique_ptr<GLContextGLX>(new GLContextGLX(platformDisplay, WTFMove(context), WTFMove(pbuffer)));
}
-PassOwnPtr<GLContextGLX> GLContextGLX::createPixmapContext(GLXContext sharingContext)
+std::unique_ptr<GLContextGLX> GLContextGLX::createPixmapContext(PlatformDisplay& platformDisplay, GLXContext sharingContext)
{
static int visualAttributes[] = {
GLX_RGBA,
@@ -113,77 +204,68 @@ PassOwnPtr<GLContextGLX> GLContextGLX::createPixmapContext(GLXContext sharingCon
0
};
- Display* display = sharedX11Display();
- XVisualInfo* visualInfo = glXChooseVisual(display, DefaultScreen(display), visualAttributes);
+ Display* display = downcast<PlatformDisplayX11>(platformDisplay).native();
+ XUniquePtr<XVisualInfo> visualInfo(glXChooseVisual(display, DefaultScreen(display), visualAttributes));
if (!visualInfo)
return nullptr;
- GLXContext context = glXCreateContext(display, visualInfo, sharingContext, GL_TRUE);
- if (!context) {
- XFree(visualInfo);
+ XUniqueGLXContext context(glXCreateContext(display, visualInfo.get(), sharingContext, GL_TRUE));
+ if (!context)
return nullptr;
- }
- Pixmap pixmap = XCreatePixmap(display, DefaultRootWindow(display), 1, 1, visualInfo->depth);
- if (!pixmap) {
- XFree(visualInfo);
+ XUniquePixmap pixmap(XCreatePixmap(display, DefaultRootWindow(display), 1, 1, visualInfo->depth));
+ if (!pixmap)
return nullptr;
- }
- GLXPixmap glxPixmap = glXCreateGLXPixmap(display, visualInfo, pixmap);
- if (!glxPixmap) {
- XFreePixmap(display, pixmap);
- XFree(visualInfo);
+ XUniqueGLXPixmap glxPixmap(glXCreateGLXPixmap(display, visualInfo.get(), pixmap.get()));
+ if (!glxPixmap)
return nullptr;
- }
- XFree(visualInfo);
- return adoptPtr(new GLContextGLX(context, pixmap, glxPixmap));
+ return std::unique_ptr<GLContextGLX>(new GLContextGLX(platformDisplay, WTFMove(context), WTFMove(pixmap), WTFMove(glxPixmap)));
}
-PassOwnPtr<GLContextGLX> GLContextGLX::createContext(XID window, GLContext* sharingContext)
+std::unique_ptr<GLContextGLX> GLContextGLX::createContext(GLNativeWindowType window, PlatformDisplay& platformDisplay)
{
- if (!sharedX11Display())
- return nullptr;
-
- static bool initialized = false;
- static bool success = true;
- if (!initialized) {
- success = initializeOpenGLShims();
- initialized = true;
- }
- if (!success)
- return nullptr;
-
- GLXContext glxSharingContext = sharingContext ? static_cast<GLContextGLX*>(sharingContext)->m_context : 0;
- OwnPtr<GLContextGLX> context = window ? createWindowContext(window, sharingContext) : nullptr;
+ GLXContext glxSharingContext = platformDisplay.sharingGLContext() ? static_cast<GLContextGLX*>(platformDisplay.sharingGLContext())->m_context.get() : nullptr;
+ auto context = window ? createWindowContext(window, platformDisplay, glxSharingContext) : nullptr;
if (!context)
- context = createPbufferContext(glxSharingContext);
+ context = createPbufferContext(platformDisplay, glxSharingContext);
if (!context)
- context = createPixmapContext(glxSharingContext);
+ context = createPixmapContext(platformDisplay, glxSharingContext);
+
+ return context;
+}
+
+std::unique_ptr<GLContextGLX> GLContextGLX::createSharingContext(PlatformDisplay& platformDisplay)
+{
+ auto context = createPbufferContext(platformDisplay);
if (!context)
- return nullptr;
+ context = createPixmapContext(platformDisplay);
+ return context;
+}
- return context.release();
+GLContextGLX::GLContextGLX(PlatformDisplay& display, XUniqueGLXContext&& context, GLNativeWindowType window)
+ : GLContext(display)
+ , m_x11Display(downcast<PlatformDisplayX11>(m_display).native())
+ , m_context(WTFMove(context))
+ , m_window(static_cast<Window>(window))
+{
}
-GLContextGLX::GLContextGLX(GLXContext context)
- : m_context(context)
- , m_window(0)
- , m_pbuffer(0)
- , m_pixmap(0)
- , m_glxPixmap(0)
- , m_cairoDevice(0)
+GLContextGLX::GLContextGLX(PlatformDisplay& display, XUniqueGLXContext&& context, XUniqueGLXPbuffer&& pbuffer)
+ : GLContext(display)
+ , m_x11Display(downcast<PlatformDisplayX11>(m_display).native())
+ , m_context(WTFMove(context))
+ , m_pbuffer(WTFMove(pbuffer))
{
}
-GLContextGLX::GLContextGLX(GLXContext context, Pixmap pixmap, GLXPixmap glxPixmap)
- : m_context(context)
- , m_window(0)
- , m_pbuffer(0)
- , m_pixmap(pixmap)
- , m_glxPixmap(glxPixmap)
- , m_cairoDevice(0)
+GLContextGLX::GLContextGLX(PlatformDisplay& display, XUniqueGLXContext&& context, XUniquePixmap&& pixmap, XUniqueGLXPixmap&& glxPixmap)
+ : GLContext(display)
+ , m_x11Display(downcast<PlatformDisplayX11>(m_display).native())
+ , m_context(WTFMove(context))
+ , m_pixmap(WTFMove(pixmap))
+ , m_glxPixmap(WTFMove(glxPixmap))
{
}
@@ -193,24 +275,21 @@ GLContextGLX::~GLContextGLX()
cairo_device_destroy(m_cairoDevice);
if (m_context) {
- // This may be necessary to prevent crashes with NVidia's closed source drivers. Originally
- // from Mozilla's 3D canvas implementation at: http://bitbucket.org/ilmari/canvas3d/
+ // Due to a bug in some nvidia drivers, we need bind the default framebuffer in a context before
+ // destroying it to avoid a crash. In order to do that, we need to make the context current and,
+ // after the bind change, we need to set the previous context again.
+ GLContext* previousActiveContext = GLContext::current();
+ makeContextCurrent();
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
- glXMakeCurrent(sharedX11Display(), None, None);
- glXDestroyContext(sharedX11Display(), m_context);
- }
-
- if (m_pbuffer) {
- glXDestroyPbuffer(sharedX11Display(), m_pbuffer);
- m_pbuffer = 0;
- }
- if (m_glxPixmap) {
- glXDestroyGLXPixmap(sharedX11Display(), m_glxPixmap);
- m_glxPixmap = 0;
- }
- if (m_pixmap) {
- XFreePixmap(sharedX11Display(), m_pixmap);
- m_pixmap = 0;
+ if (previousActiveContext && previousActiveContext != this) {
+ // If there was a previous context different from this one, just make it current again.
+ previousActiveContext->makeContextCurrent();
+ } else {
+ // If there was no previous context or this was the previous, set a void context as current.
+ // We use the GLX function here, and the destructor of GLContext will clean the pointer
+ // returned by GLContext::current().
+ glXMakeCurrent(m_x11Display, None, None);
+ }
}
}
@@ -227,7 +306,7 @@ IntSize GLContextGLX::defaultFrameBufferSize()
int x, y;
Window rootWindow;
unsigned int width, height, borderWidth, depth;
- if (!XGetGeometry(sharedX11Display(), m_window, &rootWindow, &x, &y, &width, &height, &borderWidth, &depth))
+ if (!XGetGeometry(m_x11Display, m_window, &rootWindow, &x, &y, &width, &height, &borderWidth, &depth))
return IntSize();
return IntSize(width, height);
@@ -238,22 +317,22 @@ bool GLContextGLX::makeContextCurrent()
ASSERT(m_context && (m_window || m_pbuffer || m_glxPixmap));
GLContext::makeContextCurrent();
- if (glXGetCurrentContext() == m_context)
+ if (glXGetCurrentContext() == m_context.get())
return true;
if (m_window)
- return glXMakeCurrent(sharedX11Display(), m_window, m_context);
+ return glXMakeCurrent(m_x11Display, m_window, m_context.get());
if (m_pbuffer)
- return glXMakeCurrent(sharedX11Display(), m_pbuffer, m_context);
+ return glXMakeCurrent(m_x11Display, m_pbuffer.get(), m_context.get());
- return ::glXMakeCurrent(sharedX11Display(), m_glxPixmap, m_context);
+ return ::glXMakeCurrent(m_x11Display, m_glxPixmap.get(), m_context.get());
}
void GLContextGLX::swapBuffers()
{
if (m_window)
- glXSwapBuffers(sharedX11Display(), m_window);
+ glXSwapBuffers(m_x11Display, m_window);
}
void GLContextGLX::waitNative()
@@ -261,22 +340,29 @@ void GLContextGLX::waitNative()
glXWaitX();
}
+void GLContextGLX::swapInterval(int interval)
+{
+ if (!hasSGISwapControlExtension(m_x11Display))
+ return;
+ glXSwapIntervalSGI(interval);
+}
+
cairo_device_t* GLContextGLX::cairoDevice()
{
if (m_cairoDevice)
return m_cairoDevice;
-#if ENABLE(ACCELERATED_2D_CANVAS)
- m_cairoDevice = cairo_glx_device_create(sharedX11Display(), m_context);
+#if ENABLE(ACCELERATED_2D_CANVAS) && CAIRO_HAS_GLX_FUNCTIONS
+ m_cairoDevice = cairo_glx_device_create(m_x11Display, m_context.get());
#endif
return m_cairoDevice;
}
-#if USE(3D_GRAPHICS)
+#if ENABLE(GRAPHICS_CONTEXT_3D)
PlatformGraphicsContext3D GLContextGLX::platformContext()
{
- return m_context;
+ return m_context.get();
}
#endif
diff --git a/Source/WebCore/platform/graphics/glx/GLContextGLX.h b/Source/WebCore/platform/graphics/glx/GLContextGLX.h
index edefc04c2..ca24024f6 100644
--- a/Source/WebCore/platform/graphics/glx/GLContextGLX.h
+++ b/Source/WebCore/platform/graphics/glx/GLContextGLX.h
@@ -17,58 +17,61 @@
* Boston, MA 02110-1301 USA
*/
-#ifndef GLContextGLX_h
-#define GLContextGLX_h
+#pragma once
#if USE(GLX)
#include "GLContext.h"
+#include "XUniquePtr.h"
+#include "XUniqueResource.h"
-typedef struct __GLXcontextRec* GLXContext;
-typedef unsigned long GLXPbuffer;
-typedef unsigned long GLXPixmap;
typedef unsigned char GLubyte;
-typedef unsigned long Pixmap;
-typedef unsigned long XID;
+typedef unsigned long Window;
typedef void* ContextKeyType;
+typedef struct _XDisplay Display;
namespace WebCore {
-class GLContextGLX : public GLContext {
+class GLContextGLX final : public GLContext {
WTF_MAKE_NONCOPYABLE(GLContextGLX);
public:
- static PassOwnPtr<GLContextGLX> createContext(XID window, GLContext* sharingContext);
- static PassOwnPtr<GLContextGLX> createWindowContext(XID window, GLContext* sharingContext);
+ static std::unique_ptr<GLContextGLX> createContext(GLNativeWindowType, PlatformDisplay&);
+ static std::unique_ptr<GLContextGLX> createSharingContext(PlatformDisplay&);
virtual ~GLContextGLX();
- virtual bool makeContextCurrent();
- virtual void swapBuffers();
- virtual void waitNative();
- virtual bool canRenderToDefaultFramebuffer();
- virtual IntSize defaultFrameBufferSize();
- virtual cairo_device_t* cairoDevice();
-#if USE(3D_GRAPHICS)
- virtual PlatformGraphicsContext3D platformContext();
+private:
+ bool makeContextCurrent() override;
+ void swapBuffers() override;
+ void waitNative() override;
+ bool canRenderToDefaultFramebuffer() override;
+ IntSize defaultFrameBufferSize() override;
+ void swapInterval(int) override;
+ cairo_device_t* cairoDevice() override;
+ bool isEGLContext() const override { return false; }
+
+#if ENABLE(GRAPHICS_CONTEXT_3D)
+ PlatformGraphicsContext3D platformContext() override;
#endif
-private:
- static PassOwnPtr<GLContextGLX> createPbufferContext(GLXContext sharingContext);
- static PassOwnPtr<GLContextGLX> createPixmapContext(GLXContext sharingContext);
+ GLContextGLX(PlatformDisplay&, XUniqueGLXContext&&, GLNativeWindowType);
+ GLContextGLX(PlatformDisplay&, XUniqueGLXContext&&, XUniqueGLXPbuffer&&);
+ GLContextGLX(PlatformDisplay&, XUniqueGLXContext&&, XUniquePixmap&&, XUniqueGLXPixmap&&);
- GLContextGLX(GLXContext);
- GLContextGLX(GLXContext, Pixmap, GLXPixmap);
+ static std::unique_ptr<GLContextGLX> createWindowContext(GLNativeWindowType, PlatformDisplay&, GLXContext sharingContext = nullptr);
+ static std::unique_ptr<GLContextGLX> createPbufferContext(PlatformDisplay&, GLXContext sharingContext = nullptr);
+ static std::unique_ptr<GLContextGLX> createPixmapContext(PlatformDisplay&, GLXContext sharingContext = nullptr);
- GLXContext m_context;
- XID m_window;
- GLXPbuffer m_pbuffer;
- Pixmap m_pixmap;
- GLXPixmap m_glxPixmap;
- cairo_device_t* m_cairoDevice;
+ Display* m_x11Display { nullptr };
+ XUniqueGLXContext m_context;
+ Window m_window { 0 };
+ XUniqueGLXPbuffer m_pbuffer;
+ XUniquePixmap m_pixmap;
+ XUniqueGLXPixmap m_glxPixmap;
+ cairo_device_t* m_cairoDevice { nullptr };
};
} // namespace WebCore
#endif // USE(GLX)
-#endif // GLContextGLX_h
diff --git a/Source/WebCore/platform/graphics/gpu/DrawingBuffer.cpp b/Source/WebCore/platform/graphics/gpu/DrawingBuffer.cpp
deleted file mode 100644
index 2b96cb594..000000000
--- a/Source/WebCore/platform/graphics/gpu/DrawingBuffer.cpp
+++ /dev/null
@@ -1,466 +0,0 @@
-/*
- * Copyright (c) 2010, Google Inc. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are
- * met:
- *
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above
- * copyright notice, this list of conditions and the following disclaimer
- * in the documentation and/or other materials provided with the
- * distribution.
- * * Neither the name of Google Inc. nor the names of its
- * contributors may be used to endorse or promote products derived from
- * this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#include "config.h"
-
-#if ENABLE(ACCELERATED_2D_CANVAS) || USE(3D_GRAPHICS)
-
-#include "DrawingBuffer.h"
-
-#include "Extensions3D.h"
-#include "GraphicsContext3D.h"
-
-namespace WebCore {
-
-#if PLATFORM(WIN) || USE(CAIRO)
-DrawingBuffer::DrawingBuffer(GraphicsContext3D* context, const IntSize& size, bool multisampleExtensionSupported, bool packedDepthStencilExtensionSupported, PreserveDrawingBuffer preserveDrawingBuffer, AlphaRequirement alpha)
- : m_preserveDrawingBuffer(preserveDrawingBuffer)
- , m_alpha(alpha)
- , m_scissorEnabled(false)
- , m_texture2DBinding(0)
- , m_framebufferBinding(0)
- , m_activeTextureUnit(GraphicsContext3D::TEXTURE0)
- , m_context(context)
- , m_size(-1, -1)
- , m_multisampleExtensionSupported(multisampleExtensionSupported)
- , m_packedDepthStencilExtensionSupported(packedDepthStencilExtensionSupported)
- , m_fbo(context->createFramebuffer())
- , m_colorBuffer(0)
- , m_frontColorBuffer(0)
- , m_separateFrontTexture(false)
- , m_depthStencilBuffer(0)
- , m_depthBuffer(0)
- , m_stencilBuffer(0)
- , m_multisampleFBO(0)
- , m_multisampleColorBuffer(0)
-{
- ASSERT(m_fbo);
- if (!m_fbo) {
- clear();
- return;
- }
-
- // create a texture to render into
- m_colorBuffer = context->createTexture();
- context->bindTexture(GraphicsContext3D::TEXTURE_2D, m_colorBuffer);
- context->texParameterf(GraphicsContext3D::TEXTURE_2D, GraphicsContext3D::TEXTURE_MAG_FILTER, GraphicsContext3D::LINEAR);
- context->texParameterf(GraphicsContext3D::TEXTURE_2D, GraphicsContext3D::TEXTURE_MIN_FILTER, GraphicsContext3D::LINEAR);
- context->texParameteri(GraphicsContext3D::TEXTURE_2D, GraphicsContext3D::TEXTURE_WRAP_S, GraphicsContext3D::CLAMP_TO_EDGE);
- context->texParameteri(GraphicsContext3D::TEXTURE_2D, GraphicsContext3D::TEXTURE_WRAP_T, GraphicsContext3D::CLAMP_TO_EDGE);
- context->bindTexture(GraphicsContext3D::TEXTURE_2D, 0);
-
- createSecondaryBuffers();
- reset(size);
-}
-
-DrawingBuffer::~DrawingBuffer()
-{
- clear();
-}
-#endif
-
-// Global resource ceiling (expressed in terms of pixels) for DrawingBuffer creation and resize.
-// When this limit is set, DrawingBuffer::create() and DrawingBuffer::reset() calls that would
-// exceed the global cap will instead clear the buffer.
-static int s_maximumResourceUsePixels = 0;
-static int s_currentResourceUsePixels = 0;
-static const float s_resourceAdjustedRatio = 0.5;
-
-PassRefPtr<DrawingBuffer> DrawingBuffer::create(GraphicsContext3D* context, const IntSize& size, PreserveDrawingBuffer preserve, AlphaRequirement alpha)
-{
- Extensions3D* extensions = context->getExtensions();
- bool multisampleSupported = extensions->maySupportMultisampling()
- && extensions->supports("GL_ANGLE_framebuffer_blit")
- && extensions->supports("GL_ANGLE_framebuffer_multisample")
- && extensions->supports("GL_OES_rgb8_rgba8");
- if (multisampleSupported) {
- extensions->ensureEnabled("GL_ANGLE_framebuffer_blit");
- extensions->ensureEnabled("GL_ANGLE_framebuffer_multisample");
- extensions->ensureEnabled("GL_OES_rgb8_rgba8");
- }
- bool packedDepthStencilSupported = extensions->supports("GL_OES_packed_depth_stencil");
- if (packedDepthStencilSupported)
- extensions->ensureEnabled("GL_OES_packed_depth_stencil");
- RefPtr<DrawingBuffer> drawingBuffer = adoptRef(new DrawingBuffer(context, size, multisampleSupported, packedDepthStencilSupported, preserve, alpha));
- return (drawingBuffer->m_context) ? drawingBuffer.release() : 0;
-}
-
-void DrawingBuffer::clear()
-{
- if (!m_context)
- return;
-
- m_context->makeContextCurrent();
-
- if (!m_size.isEmpty()) {
- s_currentResourceUsePixels -= m_size.width() * m_size.height();
- m_size = IntSize();
- }
-
- if (m_colorBuffer) {
- m_context->deleteTexture(m_colorBuffer);
- m_colorBuffer = 0;
- }
-
- if (m_frontColorBuffer) {
- m_context->deleteTexture(m_frontColorBuffer);
- m_frontColorBuffer = 0;
- }
-
- if (m_multisampleColorBuffer) {
- m_context->deleteRenderbuffer(m_multisampleColorBuffer);
- m_multisampleColorBuffer = 0;
- }
-
- if (m_depthStencilBuffer) {
- m_context->deleteRenderbuffer(m_depthStencilBuffer);
- m_depthStencilBuffer = 0;
- }
-
- if (m_depthBuffer) {
- m_context->deleteRenderbuffer(m_depthBuffer);
- m_depthBuffer = 0;
- }
-
- if (m_stencilBuffer) {
- m_context->deleteRenderbuffer(m_stencilBuffer);
- m_stencilBuffer = 0;
- }
-
- if (m_multisampleFBO) {
- m_context->deleteFramebuffer(m_multisampleFBO);
- m_multisampleFBO = 0;
- }
-
- if (m_fbo) {
- m_context->deleteFramebuffer(m_fbo);
- m_fbo = 0;
- }
-}
-
-void DrawingBuffer::createSecondaryBuffers()
-{
- // create a multisample FBO
- if (multisample()) {
- m_multisampleFBO = m_context->createFramebuffer();
- m_context->bindFramebuffer(GraphicsContext3D::FRAMEBUFFER, m_multisampleFBO);
- m_multisampleColorBuffer = m_context->createRenderbuffer();
- }
-}
-
-void DrawingBuffer::resizeDepthStencil(int sampleCount)
-{
- const GraphicsContext3D::Attributes& attributes = m_context->getContextAttributes();
- if (attributes.depth && attributes.stencil && m_packedDepthStencilExtensionSupported) {
- if (!m_depthStencilBuffer)
- m_depthStencilBuffer = m_context->createRenderbuffer();
- m_context->bindRenderbuffer(GraphicsContext3D::RENDERBUFFER, m_depthStencilBuffer);
- if (multisample())
- m_context->getExtensions()->renderbufferStorageMultisample(GraphicsContext3D::RENDERBUFFER, sampleCount, Extensions3D::DEPTH24_STENCIL8, m_size.width(), m_size.height());
- else
- m_context->renderbufferStorage(GraphicsContext3D::RENDERBUFFER, Extensions3D::DEPTH24_STENCIL8, m_size.width(), m_size.height());
- m_context->framebufferRenderbuffer(GraphicsContext3D::FRAMEBUFFER, GraphicsContext3D::STENCIL_ATTACHMENT, GraphicsContext3D::RENDERBUFFER, m_depthStencilBuffer);
- m_context->framebufferRenderbuffer(GraphicsContext3D::FRAMEBUFFER, GraphicsContext3D::DEPTH_ATTACHMENT, GraphicsContext3D::RENDERBUFFER, m_depthStencilBuffer);
- } else {
- if (attributes.depth) {
- if (!m_depthBuffer)
- m_depthBuffer = m_context->createRenderbuffer();
- m_context->bindRenderbuffer(GraphicsContext3D::RENDERBUFFER, m_depthBuffer);
- if (multisample())
- m_context->getExtensions()->renderbufferStorageMultisample(GraphicsContext3D::RENDERBUFFER, sampleCount, GraphicsContext3D::DEPTH_COMPONENT16, m_size.width(), m_size.height());
- else
- m_context->renderbufferStorage(GraphicsContext3D::RENDERBUFFER, GraphicsContext3D::DEPTH_COMPONENT16, m_size.width(), m_size.height());
- m_context->framebufferRenderbuffer(GraphicsContext3D::FRAMEBUFFER, GraphicsContext3D::DEPTH_ATTACHMENT, GraphicsContext3D::RENDERBUFFER, m_depthBuffer);
- }
- if (attributes.stencil) {
- if (!m_stencilBuffer)
- m_stencilBuffer = m_context->createRenderbuffer();
- m_context->bindRenderbuffer(GraphicsContext3D::RENDERBUFFER, m_stencilBuffer);
- if (multisample())
- m_context->getExtensions()->renderbufferStorageMultisample(GraphicsContext3D::RENDERBUFFER, sampleCount, GraphicsContext3D::STENCIL_INDEX8, m_size.width(), m_size.height());
- else
- m_context->renderbufferStorage(GraphicsContext3D::RENDERBUFFER, GraphicsContext3D::STENCIL_INDEX8, m_size.width(), m_size.height());
- m_context->framebufferRenderbuffer(GraphicsContext3D::FRAMEBUFFER, GraphicsContext3D::STENCIL_ATTACHMENT, GraphicsContext3D::RENDERBUFFER, m_stencilBuffer);
- }
- }
- m_context->bindRenderbuffer(GraphicsContext3D::RENDERBUFFER, 0);
-}
-
-void DrawingBuffer::clearFramebuffers(GC3Dbitfield clearMask)
-{
- m_context->bindFramebuffer(GraphicsContext3D::FRAMEBUFFER, m_multisampleFBO ? m_multisampleFBO : m_fbo);
-
- m_context->clear(clearMask);
-
- // The multisample fbo was just cleared, but we also need to clear the non-multisampled buffer too.
- if (m_multisampleFBO) {
- m_context->bindFramebuffer(GraphicsContext3D::FRAMEBUFFER, m_fbo);
- m_context->clear(GraphicsContext3D::COLOR_BUFFER_BIT);
- m_context->bindFramebuffer(GraphicsContext3D::FRAMEBUFFER, m_multisampleFBO);
- }
-}
-
-// Only way to ensure that we're not getting a bad framebuffer on some AMD/OSX devices.
-// FIXME: This can be removed once renderbufferStorageMultisample starts reporting GL_OUT_OF_MEMORY properly.
-bool DrawingBuffer::checkBufferIntegrity()
-{
- if (!m_multisampleFBO)
- return true;
-
- if (m_scissorEnabled)
- m_context->disable(GraphicsContext3D::SCISSOR_TEST);
-
- m_context->colorMask(true, true, true, true);
-
- m_context->bindFramebuffer(GraphicsContext3D::FRAMEBUFFER, m_multisampleFBO);
- m_context->clearColor(1.0f, 0.0f, 1.0f, 1.0f);
- m_context->clear(GraphicsContext3D::COLOR_BUFFER_BIT);
-
- commit(0, 0, 1, 1);
-
- unsigned char pixel[4] = {0, 0, 0, 0};
- m_context->readPixels(0, 0, 1, 1, GraphicsContext3D::RGBA, GraphicsContext3D::UNSIGNED_BYTE, &pixel);
-
- if (m_scissorEnabled)
- m_context->enable(GraphicsContext3D::SCISSOR_TEST);
-
- return (pixel[0] == 0xFF && pixel[1] == 0x00 && pixel[2] == 0xFF && pixel[3] == 0xFF);
-}
-
-bool DrawingBuffer::reset(const IntSize& newSize)
-{
- if (!m_context)
- return false;
-
- m_context->makeContextCurrent();
-
- int maxTextureSize = 0;
- m_context->getIntegerv(GraphicsContext3D::MAX_TEXTURE_SIZE, &maxTextureSize);
- if (newSize.height() > maxTextureSize || newSize.width() > maxTextureSize) {
- clear();
- return false;
- }
-
- int pixelDelta = newSize.width() * newSize.height();
- int oldSize = 0;
- if (!m_size.isEmpty()) {
- oldSize = m_size.width() * m_size.height();
- pixelDelta -= oldSize;
- }
-
- IntSize adjustedSize = newSize;
- if (s_maximumResourceUsePixels) {
- while ((s_currentResourceUsePixels + pixelDelta) > s_maximumResourceUsePixels) {
- adjustedSize.scale(s_resourceAdjustedRatio);
- if (adjustedSize.isEmpty()) {
- clear();
- return false;
- }
- pixelDelta = adjustedSize.width() * adjustedSize.height();
- pixelDelta -= oldSize;
- }
- }
-
- const GraphicsContext3D::Attributes& attributes = m_context->getContextAttributes();
-
- if (adjustedSize != m_size) {
-
- unsigned internalColorFormat, colorFormat, internalRenderbufferFormat;
- if (attributes.alpha) {
- internalColorFormat = GraphicsContext3D::RGBA;
- colorFormat = GraphicsContext3D::RGBA;
- internalRenderbufferFormat = Extensions3D::RGBA8_OES;
- } else {
- internalColorFormat = GraphicsContext3D::RGB;
- colorFormat = GraphicsContext3D::RGB;
- internalRenderbufferFormat = Extensions3D::RGB8_OES;
- }
-
- do {
- m_size = adjustedSize;
- // resize multisample FBO
- if (multisample()) {
- int maxSampleCount = 0;
-
- m_context->getIntegerv(Extensions3D::MAX_SAMPLES, &maxSampleCount);
- int sampleCount = std::min(4, maxSampleCount);
-
- m_context->bindFramebuffer(GraphicsContext3D::FRAMEBUFFER, m_multisampleFBO);
-
- m_context->bindRenderbuffer(GraphicsContext3D::RENDERBUFFER, m_multisampleColorBuffer);
- m_context->getExtensions()->renderbufferStorageMultisample(GraphicsContext3D::RENDERBUFFER, sampleCount, internalRenderbufferFormat, m_size.width(), m_size.height());
-
- if (m_context->getError() == GraphicsContext3D::OUT_OF_MEMORY) {
- adjustedSize.scale(s_resourceAdjustedRatio);
- continue;
- }
-
- m_context->framebufferRenderbuffer(GraphicsContext3D::FRAMEBUFFER, GraphicsContext3D::COLOR_ATTACHMENT0, GraphicsContext3D::RENDERBUFFER, m_multisampleColorBuffer);
- resizeDepthStencil(sampleCount);
- if (m_context->checkFramebufferStatus(GraphicsContext3D::FRAMEBUFFER) != GraphicsContext3D::FRAMEBUFFER_COMPLETE) {
- adjustedSize.scale(s_resourceAdjustedRatio);
- continue;
- }
- }
-
- // resize regular FBO
- m_context->bindFramebuffer(GraphicsContext3D::FRAMEBUFFER, m_fbo);
-
- m_context->bindTexture(GraphicsContext3D::TEXTURE_2D, m_colorBuffer);
- m_context->texImage2D(GraphicsContext3D::TEXTURE_2D, 0, internalColorFormat, m_size.width(), m_size.height(), 0, colorFormat, GraphicsContext3D::UNSIGNED_BYTE, 0);
-
- m_context->framebufferTexture2D(GraphicsContext3D::FRAMEBUFFER, GraphicsContext3D::COLOR_ATTACHMENT0, GraphicsContext3D::TEXTURE_2D, m_colorBuffer, 0);
-
- // resize the front color buffer
- if (m_separateFrontTexture) {
- m_context->bindTexture(GraphicsContext3D::TEXTURE_2D, m_frontColorBuffer);
- m_context->texImage2D(GraphicsContext3D::TEXTURE_2D, 0, internalColorFormat, m_size.width(), m_size.height(), 0, colorFormat, GraphicsContext3D::UNSIGNED_BYTE, 0);
- }
-
- m_context->bindTexture(GraphicsContext3D::TEXTURE_2D, 0);
-
- if (!multisample())
- resizeDepthStencil(0);
- if (m_context->checkFramebufferStatus(GraphicsContext3D::FRAMEBUFFER) != GraphicsContext3D::FRAMEBUFFER_COMPLETE) {
- adjustedSize.scale(s_resourceAdjustedRatio);
- continue;
- }
-
-#if OS(DARWIN)
- // FIXME: This can be removed once renderbufferStorageMultisample starts reporting GL_OUT_OF_MEMORY properly on OSX.
- if (!checkBufferIntegrity()) {
- adjustedSize.scale(s_resourceAdjustedRatio);
- continue;
- }
-#endif
-
- break;
-
- } while (!adjustedSize.isEmpty());
-
- pixelDelta = m_size.width() * m_size.height();
- pixelDelta -= oldSize;
- s_currentResourceUsePixels += pixelDelta;
-
- if (!newSize.isEmpty() && adjustedSize.isEmpty()) {
- clear();
- return false;
- }
- }
-
- m_context->disable(GraphicsContext3D::SCISSOR_TEST);
- m_context->clearColor(0, 0, 0, 0);
- m_context->colorMask(true, true, true, true);
-
- GC3Dbitfield clearMask = GraphicsContext3D::COLOR_BUFFER_BIT;
- if (attributes.depth) {
- m_context->clearDepth(1.0f);
- clearMask |= GraphicsContext3D::DEPTH_BUFFER_BIT;
- m_context->depthMask(true);
- }
- if (attributes.stencil) {
- m_context->clearStencil(0);
- clearMask |= GraphicsContext3D::STENCIL_BUFFER_BIT;
- m_context->stencilMaskSeparate(GraphicsContext3D::FRONT, 0xFFFFFFFF);
- }
-
- clearFramebuffers(clearMask);
-
- return true;
-}
-
-void DrawingBuffer::commit(long x, long y, long width, long height)
-{
- if (!m_context)
- return;
-
- if (width < 0)
- width = m_size.width();
- if (height < 0)
- height = m_size.height();
-
- m_context->makeContextCurrent();
-
- if (m_multisampleFBO) {
- m_context->bindFramebuffer(Extensions3D::READ_FRAMEBUFFER, m_multisampleFBO);
- m_context->bindFramebuffer(Extensions3D::DRAW_FRAMEBUFFER, m_fbo);
-
- if (m_scissorEnabled)
- m_context->disable(GraphicsContext3D::SCISSOR_TEST);
-
- // Use NEAREST, because there is no scale performed during the blit.
- m_context->getExtensions()->blitFramebuffer(x, y, width, height, x, y, width, height, GraphicsContext3D::COLOR_BUFFER_BIT, GraphicsContext3D::NEAREST);
-
- if (m_scissorEnabled)
- m_context->enable(GraphicsContext3D::SCISSOR_TEST);
- }
-
- m_context->bindFramebuffer(GraphicsContext3D::FRAMEBUFFER, m_fbo);
-}
-
-void DrawingBuffer::restoreFramebufferBinding()
-{
- if (!m_context || !m_framebufferBinding)
- return;
-
- m_context->bindFramebuffer(GraphicsContext3D::FRAMEBUFFER, m_framebufferBinding);
-}
-
-bool DrawingBuffer::multisample() const
-{
- return m_context && m_context->getContextAttributes().antialias && m_multisampleExtensionSupported;
-}
-
-void DrawingBuffer::discardResources()
-{
- m_colorBuffer = 0;
- m_frontColorBuffer = 0;
- m_multisampleColorBuffer = 0;
-
- m_depthStencilBuffer = 0;
- m_depthBuffer = 0;
-
- m_stencilBuffer = 0;
-
- m_multisampleFBO = 0;
- m_fbo = 0;
-}
-
-void DrawingBuffer::bind()
-{
- if (!m_context)
- return;
-
- m_context->bindFramebuffer(GraphicsContext3D::FRAMEBUFFER, m_multisampleFBO ? m_multisampleFBO : m_fbo);
-}
-
-} // namespace WebCore
-
-#endif
diff --git a/Source/WebCore/platform/graphics/gpu/DrawingBuffer.h b/Source/WebCore/platform/graphics/gpu/DrawingBuffer.h
deleted file mode 100644
index e234e420c..000000000
--- a/Source/WebCore/platform/graphics/gpu/DrawingBuffer.h
+++ /dev/null
@@ -1,173 +0,0 @@
-/*
- * Copyright (c) 2010, Google Inc. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are
- * met:
- *
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above
- * copyright notice, this list of conditions and the following disclaimer
- * in the documentation and/or other materials provided with the
- * distribution.
- * * Neither the name of Google Inc. nor the names of its
- * contributors may be used to endorse or promote products derived from
- * this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#ifndef DrawingBuffer_h
-#define DrawingBuffer_h
-
-#include "GraphicsContext3D.h"
-#include "GraphicsTypes3D.h"
-#include "IntSize.h"
-#include "PlatformLayer.h"
-
-#include <wtf/Noncopyable.h>
-#include <wtf/OwnPtr.h>
-#include <wtf/PassOwnPtr.h>
-#if PLATFORM(MAC)
-#include <wtf/RetainPtr.h>
-#endif
-
-namespace WebCore {
-class GraphicsContext3D;
-
-// Manages a rendering target (framebuffer + attachment) for a canvas. Can publish its rendering
-// results to a PlatformLayer for compositing.
-class DrawingBuffer : public RefCounted<DrawingBuffer> {
-public:
- enum PreserveDrawingBuffer {
- Preserve,
- Discard
- };
-
- enum AlphaRequirement {
- Alpha,
- Opaque
- };
-
- static PassRefPtr<DrawingBuffer> create(GraphicsContext3D*, const IntSize&, PreserveDrawingBuffer, AlphaRequirement);
- friend class GraphicsContext3D;
-
- ~DrawingBuffer();
-
- // Issues a glClear() on all framebuffers associated with this DrawingBuffer. The caller is responsible for
- // making the context current and setting the clear values and masks. Modifies the framebuffer binding.
- void clearFramebuffers(GC3Dbitfield clearMask);
-
- // Returns true if the buffer was successfully resized.
- bool reset(const IntSize&);
- void bind();
- IntSize size() const { return m_size; }
- Platform3DObject colorBuffer() const { return m_colorBuffer; }
-
- // Clear all resources from this object, as well as context. Called when context is destroyed
- // to prevent invalid accesses to the resources.
- void clear();
-
- // Create the depth/stencil and multisample buffers, if needed.
- void createSecondaryBuffers();
-
- void resizeDepthStencil(int sampleCount);
-
- // Copies the multisample color buffer to the normal color buffer and leaves m_fbo bound
- void commit(long x = 0, long y = 0, long width = -1, long height = -1);
-
- // commit should copy the full multisample buffer, and not respect the
- // current scissor bounds. Track the state of the scissor test so that it
- // can be disabled during calls to commit.
- void setScissorEnabled(bool scissorEnabled) { m_scissorEnabled = scissorEnabled; }
-
- // The DrawingBuffer needs to track the texture bound to texture unit 0.
- // The bound texture is tracked to avoid costly queries during rendering.
- void setTexture2DBinding(Platform3DObject texture) { m_texture2DBinding = texture; }
-
- // The DrawingBuffer needs to track the currently bound framebuffer so it
- // restore the binding when needed.
- void setFramebufferBinding(Platform3DObject fbo) { m_framebufferBinding = fbo; }
-
- // Bind to the m_framebufferBinding if it's not 0.
- void restoreFramebufferBinding();
-
- // Track the currently active texture unit. Texture unit 0 is used as host for a scratch
- // texture.
- void setActiveTextureUnit(GC3Dint textureUnit) { m_activeTextureUnit = textureUnit; }
-
- bool multisample() const;
-
- Platform3DObject framebuffer() const;
-
- // Immediately releases ownership of all resources. Call upon loss of the
- // graphics context to prevent freeing invalid resources.
- void discardResources();
-
- void markContentsChanged() { m_contentsChanged = true; }
-
-#if USE(ACCELERATED_COMPOSITING)
- PlatformLayer* platformLayer();
- unsigned frontColorBuffer() const;
- void paintCompositedResultsToCanvas(ImageBuffer*);
-#endif
-
- GraphicsContext3D* graphicsContext3D() const { return m_context.get(); }
-
-private:
- DrawingBuffer(GraphicsContext3D*, const IntSize&, bool multisampleExtensionSupported,
- bool packedDepthStencilExtensionSupported, PreserveDrawingBuffer, AlphaRequirement);
-
- void initialize(const IntSize&);
-
- bool checkBufferIntegrity();
-
- PreserveDrawingBuffer m_preserveDrawingBuffer;
- AlphaRequirement m_alpha;
- bool m_scissorEnabled;
- Platform3DObject m_texture2DBinding;
- Platform3DObject m_framebufferBinding;
- GC3Denum m_activeTextureUnit;
-
- RefPtr<GraphicsContext3D> m_context;
- IntSize m_size;
- bool m_multisampleExtensionSupported;
- bool m_packedDepthStencilExtensionSupported;
- Platform3DObject m_fbo;
- Platform3DObject m_colorBuffer;
- Platform3DObject m_frontColorBuffer;
- bool m_separateFrontTexture;
-
- // This is used when we have OES_packed_depth_stencil.
- Platform3DObject m_depthStencilBuffer;
-
- // These are used when we don't.
- Platform3DObject m_depthBuffer;
- Platform3DObject m_stencilBuffer;
-
- // For multisampling
- Platform3DObject m_multisampleFBO;
- Platform3DObject m_multisampleColorBuffer;
-
- // True if our contents have been modified since the last presentation of this buffer.
- bool m_contentsChanged;
-
-#if PLATFORM(MAC)
- RetainPtr<WebGLLayer> m_platformLayer;
-#endif
-};
-
-} // namespace WebCore
-
-#endif // DrawingBuffer_h
diff --git a/Source/WebCore/platform/graphics/gpu/Texture.cpp b/Source/WebCore/platform/graphics/gpu/Texture.cpp
index 31aaa63ee..aad13f34d 100644
--- a/Source/WebCore/platform/graphics/gpu/Texture.cpp
+++ b/Source/WebCore/platform/graphics/gpu/Texture.cpp
@@ -43,11 +43,11 @@
namespace WebCore {
-Texture::Texture(GraphicsContext3D* context, PassOwnPtr<Vector<unsigned int>> tileTextureIds, Format format, int width, int height, int maxTextureSize)
+Texture::Texture(GraphicsContext3D* context, std::unique_ptr<Vector<unsigned>> tileTextureIds, Format format, int width, int height, int maxTextureSize)
: m_context(context)
, m_format(format)
, m_tiles(IntSize(maxTextureSize, maxTextureSize), IntSize(width, height), true)
- , m_tileTextureIds(tileTextureIds)
+ , m_tileTextureIds(WTFMove(tileTextureIds))
{
}
@@ -66,7 +66,7 @@ static void convertFormat(GraphicsContext3D* context, Texture::Format format, un
*glType = GraphicsContext3D::UNSIGNED_BYTE;
break;
case Texture::BGRA8:
- if (context->getExtensions()->supports("GL_EXT_texture_format_BGRA8888")) {
+ if (context->getExtensions().supports("GL_EXT_texture_format_BGRA8888")) {
*glFormat = Extensions3D::BGRA_EXT;
*glType = GraphicsContext3D::UNSIGNED_BYTE;
} else {
@@ -94,7 +94,7 @@ PassRefPtr<Texture> Texture::create(GraphicsContext3D* context, Format format, i
numTiles = 0;
}
- OwnPtr<Vector<unsigned int>> textureIds = adoptPtr(new Vector<unsigned int>(numTiles));
+ auto textureIds = std::make_unique<Vector<unsigned>>(numTiles);
textureIds->fill(0, numTiles);
for (int i = 0; i < numTiles; i++) {
@@ -120,7 +120,7 @@ PassRefPtr<Texture> Texture::create(GraphicsContext3D* context, Format format, i
tileBoundsWithBorder.height(),
0, glFormat, glType);
}
- return adoptRef(new Texture(context, textureIds.release(), format, width, height, maxTextureSize));
+ return adoptRef(new Texture(context, WTFMove(textureIds), format, width, height, maxTextureSize));
}
template <bool swizzle>
diff --git a/Source/WebCore/platform/graphics/gpu/Texture.h b/Source/WebCore/platform/graphics/gpu/Texture.h
index 6e93b91c2..3aa55f821 100644
--- a/Source/WebCore/platform/graphics/gpu/Texture.h
+++ b/Source/WebCore/platform/graphics/gpu/Texture.h
@@ -32,8 +32,6 @@
#define Texture_h
#include "TilingData.h"
-#include <wtf/OwnPtr.h>
-#include <wtf/PassOwnPtr.h>
#include <wtf/RefCounted.h>
#include <wtf/RefPtr.h>
#include <wtf/Vector.h>
@@ -54,11 +52,11 @@ public:
Format format() const { return m_format; }
const TilingData& tiles() const { return m_tiles; }
private:
- Texture(GraphicsContext3D*, PassOwnPtr<Vector<unsigned int>> tileTextureIds, Format format, int width, int height, int maxTextureSize);
+ Texture(GraphicsContext3D*, std::unique_ptr<Vector<unsigned>> tileTextureIds, Format, int width, int height, int maxTextureSize);
GraphicsContext3D* m_context;
Format m_format;
TilingData m_tiles;
- OwnPtr<Vector<unsigned int>> m_tileTextureIds;
+ std::unique_ptr<Vector<unsigned>> m_tileTextureIds;
};
}
diff --git a/Source/WebCore/platform/graphics/gpu/TilingData.cpp b/Source/WebCore/platform/graphics/gpu/TilingData.cpp
index 23df37da0..6a3d04c4d 100644
--- a/Source/WebCore/platform/graphics/gpu/TilingData.cpp
+++ b/Source/WebCore/platform/graphics/gpu/TilingData.cpp
@@ -30,8 +30,6 @@
#include "config.h"
-#if USE(ACCELERATED_COMPOSITING) || ENABLE(ACCELERATED_2D_CANVAS)
-
#include "TilingData.h"
#include "FloatRect.h"
@@ -203,5 +201,3 @@ void TilingData::recomputeNumTiles()
}
}
-
-#endif
diff --git a/Source/WebCore/platform/graphics/gstreamer/AudioTrackPrivateGStreamer.cpp b/Source/WebCore/platform/graphics/gstreamer/AudioTrackPrivateGStreamer.cpp
index 2f0debcc0..fc2d67471 100644
--- a/Source/WebCore/platform/graphics/gstreamer/AudioTrackPrivateGStreamer.cpp
+++ b/Source/WebCore/platform/graphics/gstreamer/AudioTrackPrivateGStreamer.cpp
@@ -37,6 +37,8 @@ AudioTrackPrivateGStreamer::AudioTrackPrivateGStreamer(GRefPtr<GstElement> playb
: TrackPrivateBaseGStreamer(this, index, pad)
, m_playbin(playbin)
{
+ // FIXME: Get a real ID from the tkhd atom.
+ m_id = "A" + String::number(index);
notifyTrackOfActiveChanged();
}
@@ -53,7 +55,7 @@ void AudioTrackPrivateGStreamer::setEnabled(bool enabled)
AudioTrackPrivate::setEnabled(enabled);
if (enabled && m_playbin)
- g_object_set(m_playbin.get(), "current-audio", m_index, NULL);
+ g_object_set(m_playbin.get(), "current-audio", m_index, nullptr);
}
} // namespace WebCore
diff --git a/Source/WebCore/platform/graphics/gstreamer/AudioTrackPrivateGStreamer.h b/Source/WebCore/platform/graphics/gstreamer/AudioTrackPrivateGStreamer.h
index 1775b2021..bd18e5c58 100644
--- a/Source/WebCore/platform/graphics/gstreamer/AudioTrackPrivateGStreamer.h
+++ b/Source/WebCore/platform/graphics/gstreamer/AudioTrackPrivateGStreamer.h
@@ -41,19 +41,21 @@ public:
return adoptRef(new AudioTrackPrivateGStreamer(playbin, index, pad));
}
- virtual void disconnect() override;
+ void disconnect() override;
- virtual void setEnabled(bool) override;
- virtual void setActive(bool enabled) override { setEnabled(enabled); }
+ void setEnabled(bool) override;
+ void setActive(bool enabled) override { setEnabled(enabled); }
- virtual int trackIndex() const override { return m_index; }
+ int trackIndex() const override { return m_index; }
- virtual AtomicString label() const override { return m_label; }
- virtual AtomicString language() const override { return m_language; }
+ AtomicString id() const override { return m_id; }
+ AtomicString label() const override { return m_label; }
+ AtomicString language() const override { return m_language; }
private:
AudioTrackPrivateGStreamer(GRefPtr<GstElement> playbin, gint index, GRefPtr<GstPad>);
+ AtomicString m_id;
GRefPtr<GstElement> m_playbin;
};
diff --git a/Source/WebCore/platform/graphics/gstreamer/GRefPtrGStreamer.cpp b/Source/WebCore/platform/graphics/gstreamer/GRefPtrGStreamer.cpp
index f7ef46dd8..0ed9b5633 100644
--- a/Source/WebCore/platform/graphics/gstreamer/GRefPtrGStreamer.cpp
+++ b/Source/WebCore/platform/graphics/gstreamer/GRefPtrGStreamer.cpp
@@ -27,7 +27,7 @@ namespace WTF {
template <> GRefPtr<GstElement> adoptGRef(GstElement* ptr)
{
- ASSERT(!ptr || !g_object_is_floating(G_OBJECT(ptr)));
+ ASSERT(!ptr || !g_object_is_floating(ptr));
return GRefPtr<GstElement>(ptr, GRefPtrAdopt);
}
@@ -47,7 +47,7 @@ template <> void derefGPtr<GstElement>(GstElement* ptr)
template <> GRefPtr<GstPad> adoptGRef(GstPad* ptr)
{
- ASSERT(!ptr || !g_object_is_floating(G_OBJECT(ptr)));
+ ASSERT(!ptr || !g_object_is_floating(ptr));
return GRefPtr<GstPad>(ptr, GRefPtrAdopt);
}
@@ -67,7 +67,7 @@ template <> void derefGPtr<GstPad>(GstPad* ptr)
template <> GRefPtr<GstPadTemplate> adoptGRef(GstPadTemplate* ptr)
{
- ASSERT(!ptr || !g_object_is_floating(G_OBJECT(ptr)));
+ ASSERT(!ptr || !g_object_is_floating(ptr));
return GRefPtr<GstPadTemplate>(ptr, GRefPtrAdopt);
}
@@ -103,10 +103,28 @@ template <> void derefGPtr<GstCaps>(GstCaps* ptr)
gst_caps_unref(ptr);
}
+template <> GRefPtr<GstContext> adoptGRef(GstContext* ptr)
+{
+ return GRefPtr<GstContext>(ptr, GRefPtrAdopt);
+}
+
+template <> GstContext* refGPtr<GstContext>(GstContext* ptr)
+{
+ if (ptr)
+ gst_context_ref(ptr);
+ return ptr;
+}
+
+template <> void derefGPtr<GstContext>(GstContext* ptr)
+{
+ if (ptr)
+ gst_context_unref(ptr);
+}
template <> GRefPtr<GstTask> adoptGRef(GstTask* ptr)
{
- ASSERT(!g_object_is_floating(G_OBJECT(ptr)));
+ // There is no need to check the object reference is floating here because
+ // gst_task_init() always sinks it.
return GRefPtr<GstTask>(ptr, GRefPtrAdopt);
}
@@ -126,7 +144,7 @@ template <> void derefGPtr<GstTask>(GstTask* ptr)
template <> GRefPtr<GstBus> adoptGRef(GstBus* ptr)
{
- ASSERT(!g_object_is_floating(G_OBJECT(ptr)));
+ ASSERT(!ptr || !g_object_is_floating(ptr));
return GRefPtr<GstBus>(ptr, GRefPtrAdopt);
}
@@ -146,7 +164,7 @@ template <> void derefGPtr<GstBus>(GstBus* ptr)
template <> GRefPtr<GstElementFactory> adoptGRef(GstElementFactory* ptr)
{
- ASSERT(!g_object_is_floating(G_OBJECT(ptr)));
+ ASSERT(!ptr || !g_object_is_floating(ptr));
return GRefPtr<GstElementFactory>(ptr, GRefPtrAdopt);
}
@@ -183,6 +201,45 @@ template<> void derefGPtr<GstBuffer>(GstBuffer* ptr)
gst_buffer_unref(ptr);
}
+template<> GRefPtr<GstBufferList> adoptGRef(GstBufferList* ptr)
+{
+ return GRefPtr<GstBufferList>(ptr, GRefPtrAdopt);
+}
+
+template<> GstBufferList* refGPtr<GstBufferList>(GstBufferList* ptr)
+{
+ if (ptr)
+ gst_buffer_list_ref(ptr);
+
+ return ptr;
+}
+
+template<> void derefGPtr<GstBufferList>(GstBufferList* ptr)
+{
+ if (ptr)
+ gst_buffer_list_unref(ptr);
+}
+
+template<> GRefPtr<GstBufferPool> adoptGRef(GstBufferPool* ptr)
+{
+ ASSERT(!ptr || !g_object_is_floating(ptr));
+ return GRefPtr<GstBufferPool>(ptr, GRefPtrAdopt);
+}
+
+template<> GstBufferPool* refGPtr<GstBufferPool>(GstBufferPool* ptr)
+{
+ if (ptr)
+ gst_object_ref_sink(GST_OBJECT(ptr));
+
+ return ptr;
+}
+
+template<> void derefGPtr<GstBufferPool>(GstBufferPool* ptr)
+{
+ if (ptr)
+ gst_object_unref(ptr);
+}
+
template<> GRefPtr<GstSample> adoptGRef(GstSample* ptr)
{
return GRefPtr<GstSample>(ptr, GRefPtrAdopt);
@@ -248,7 +305,7 @@ template<> GRefPtr<GstToc> adoptGRef(GstToc* ptr)
template<> GstToc* refGPtr<GstToc>(GstToc* ptr)
{
if (ptr)
- gst_toc_ref(ptr);
+ return gst_toc_ref(ptr);
return ptr;
}
@@ -258,5 +315,76 @@ template<> void derefGPtr<GstToc>(GstToc* ptr)
if (ptr)
gst_toc_unref(ptr);
}
+
+template<> GRefPtr<GstMessage> adoptGRef(GstMessage* ptr)
+{
+ return GRefPtr<GstMessage>(ptr, GRefPtrAdopt);
}
+
+template<> GstMessage* refGPtr<GstMessage>(GstMessage* ptr)
+{
+ if (ptr)
+ return gst_message_ref(ptr);
+
+ return ptr;
+}
+
+template<> void derefGPtr<GstMessage>(GstMessage* ptr)
+{
+ if (ptr)
+ gst_message_unref(ptr);
+}
+
+template <> GRefPtr<WebKitVideoSink> adoptGRef(WebKitVideoSink* ptr)
+{
+ ASSERT(!ptr || !g_object_is_floating(ptr));
+ return GRefPtr<WebKitVideoSink>(ptr, GRefPtrAdopt);
+}
+
+template <> WebKitVideoSink* refGPtr<WebKitVideoSink>(WebKitVideoSink* ptr)
+{
+ if (ptr)
+ gst_object_ref_sink(GST_OBJECT(ptr));
+
+ return ptr;
+}
+
+template <> void derefGPtr<WebKitVideoSink>(WebKitVideoSink* ptr)
+{
+ if (ptr)
+ gst_object_unref(GST_OBJECT(ptr));
+}
+
+template <> GRefPtr<WebKitWebSrc> adoptGRef(WebKitWebSrc* ptr)
+{
+ ASSERT(!ptr || !g_object_is_floating(ptr));
+ return GRefPtr<WebKitWebSrc>(ptr, GRefPtrAdopt);
+}
+
+// This method is only available for WebKitWebSrc and should not be used for any other type.
+// This is only to work around a bug in GST where the URI downloader is not taking the ownership of WebKitWebSrc.
+// See https://bugs.webkit.org/show_bug.cgi?id=144040.
+GRefPtr<WebKitWebSrc> ensureGRef(WebKitWebSrc* ptr)
+{
+ if (ptr && g_object_is_floating(ptr))
+ gst_object_ref_sink(GST_OBJECT(ptr));
+ return GRefPtr<WebKitWebSrc>(ptr);
+}
+
+template <> WebKitWebSrc* refGPtr<WebKitWebSrc>(WebKitWebSrc* ptr)
+{
+ if (ptr)
+ gst_object_ref_sink(GST_OBJECT(ptr));
+
+ return ptr;
+}
+
+template <> void derefGPtr<WebKitWebSrc>(WebKitWebSrc* ptr)
+{
+ if (ptr)
+ gst_object_unref(GST_OBJECT(ptr));
+}
+
+} // namespace WTF
+
#endif // USE(GSTREAMER)
diff --git a/Source/WebCore/platform/graphics/gstreamer/GRefPtrGStreamer.h b/Source/WebCore/platform/graphics/gstreamer/GRefPtrGStreamer.h
index 2ce0a1d80..9f9bb6d17 100644
--- a/Source/WebCore/platform/graphics/gstreamer/GRefPtrGStreamer.h
+++ b/Source/WebCore/platform/graphics/gstreamer/GRefPtrGStreamer.h
@@ -21,20 +21,26 @@
#define GRefPtrGStreamer_h
#if USE(GSTREAMER)
-#include <wtf/gobject/GRefPtr.h>
+#include <wtf/glib/GRefPtr.h>
typedef struct _GstElement GstElement;
typedef struct _GstPad GstPad;
typedef struct _GstPadTemplate GstPadTemplate;
typedef struct _GstCaps GstCaps;
+typedef struct _GstContext GstContext;
typedef struct _GstTask GstTask;
typedef struct _GstBus GstBus;
typedef struct _GstElementFactory GstElementFactory;
typedef struct _GstBuffer GstBuffer;
+typedef struct _GstBufferList GstBufferList;
+typedef struct _GstBufferPool GstBufferPool;
typedef struct _GstSample GstSample;
typedef struct _GstTagList GstTagList;
typedef struct _GstEvent GstEvent;
typedef struct _GstToc GstToc;
+typedef struct _GstMessage GstMessage;
+typedef struct _WebKitVideoSink WebKitVideoSink;
+typedef struct _WebKitWebSrc WebKitWebSrc;
namespace WTF {
@@ -54,6 +60,10 @@ template<> GRefPtr<GstCaps> adoptGRef(GstCaps* ptr);
template<> GstCaps* refGPtr<GstCaps>(GstCaps* ptr);
template<> void derefGPtr<GstCaps>(GstCaps* ptr);
+template<> GRefPtr<GstContext> adoptGRef(GstContext* ptr);
+template<> GstContext* refGPtr<GstContext>(GstContext* ptr);
+template<> void derefGPtr<GstContext>(GstContext* ptr);
+
template<> GRefPtr<GstTask> adoptGRef(GstTask* ptr);
template<> GstTask* refGPtr<GstTask>(GstTask* ptr);
template<> void derefGPtr<GstTask>(GstTask* ptr);
@@ -70,6 +80,14 @@ template<> GRefPtr<GstBuffer> adoptGRef(GstBuffer* ptr);
template<> GstBuffer* refGPtr<GstBuffer>(GstBuffer* ptr);
template<> void derefGPtr<GstBuffer>(GstBuffer* ptr);
+template<> GRefPtr<GstBufferList> adoptGRef(GstBufferList*);
+template<> GstBufferList* refGPtr<GstBufferList>(GstBufferList*);
+template<> void derefGPtr<GstBufferList>(GstBufferList*);
+
+template<> GRefPtr<GstBufferPool> adoptGRef(GstBufferPool*);
+template<> GstBufferPool* refGPtr<GstBufferPool>(GstBufferPool*);
+template<> void derefGPtr<GstBufferPool>(GstBufferPool*);
+
template<> GRefPtr<GstSample> adoptGRef(GstSample* ptr);
template<> GstSample* refGPtr<GstSample>(GstSample* ptr);
template<> void derefGPtr<GstSample>(GstSample* ptr);
@@ -85,7 +103,22 @@ template<> void derefGPtr<GstEvent>(GstEvent* ptr);
template<> GRefPtr<GstToc> adoptGRef(GstToc* ptr);
template<> GstToc* refGPtr<GstToc>(GstToc* ptr);
template<> void derefGPtr<GstToc>(GstToc* ptr);
-}
+
+template<> GRefPtr<GstMessage> adoptGRef(GstMessage*);
+template<> GstMessage* refGPtr<GstMessage>(GstMessage*);
+template<> void derefGPtr<GstMessage>(GstMessage*);
+
+template<> GRefPtr<WebKitVideoSink> adoptGRef(WebKitVideoSink* ptr);
+template<> WebKitVideoSink* refGPtr<WebKitVideoSink>(WebKitVideoSink* ptr);
+template<> void derefGPtr<WebKitVideoSink>(WebKitVideoSink* ptr);
+
+template<> GRefPtr<WebKitWebSrc> adoptGRef(WebKitWebSrc* ptr);
+GRefPtr<WebKitWebSrc> ensureGRef(WebKitWebSrc* ptr);
+template<> WebKitWebSrc* refGPtr<WebKitWebSrc>(WebKitWebSrc* ptr);
+template<> void derefGPtr<WebKitWebSrc>(WebKitWebSrc* ptr);
+
+} // namespace WTF
#endif // USE(GSTREAMER)
-#endif
+
+#endif // GRefPtrGStreamer_h
diff --git a/Source/WebCore/platform/graphics/gstreamer/GStreamerUtilities.cpp b/Source/WebCore/platform/graphics/gstreamer/GStreamerUtilities.cpp
index 7b666944e..770675266 100644
--- a/Source/WebCore/platform/graphics/gstreamer/GStreamerUtilities.cpp
+++ b/Source/WebCore/platform/graphics/gstreamer/GStreamerUtilities.cpp
@@ -1,5 +1,6 @@
/*
- * Copyright (C) 2012 Igalia S.L
+ * Copyright (C) 2012, 2015, 2016 Igalia S.L
+ * Copyright (C) 2015, 2016 Metrological Group B.V.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@@ -22,11 +23,19 @@
#if USE(GSTREAMER)
#include "GStreamerUtilities.h"
+#include "GRefPtrGStreamer.h"
#include "IntSize.h"
-#include <gst/audio/audio.h>
+#include <gst/audio/audio-info.h>
#include <gst/gst.h>
-#include <wtf/gobject/GUniquePtr.h>
+#include <wtf/MathExtras.h>
+#include <wtf/glib/GUniquePtr.h>
+
+#if ENABLE(VIDEO_TRACK) && USE(GSTREAMER_MPEGTS)
+#define GST_USE_UNSTABLE_API
+#include <gst/mpegts/mpegts.h>
+#undef GST_USE_UNSTABLE_API
+#endif
namespace WebCore {
@@ -65,6 +74,22 @@ bool getVideoSizeAndFormatFromCaps(GstCaps* caps, WebCore::IntSize& size, GstVid
return true;
}
+
+bool getSampleVideoInfo(GstSample* sample, GstVideoInfo& videoInfo)
+{
+ if (!GST_IS_SAMPLE(sample))
+ return false;
+
+ GstCaps* caps = gst_sample_get_caps(sample);
+ if (!caps)
+ return false;
+
+ gst_video_info_init(&videoInfo);
+ if (!gst_video_info_from_caps(&videoInfo, caps))
+ return false;
+
+ return true;
+}
#endif
GstBuffer* createGstBuffer(GstBuffer* buffer)
@@ -95,17 +120,17 @@ char* getGstBufferDataPointer(GstBuffer* buffer)
return reinterpret_cast<char*>(mapInfo->data);
}
-void mapGstBuffer(GstBuffer* buffer)
+void mapGstBuffer(GstBuffer* buffer, uint32_t flags)
{
- GstMapInfo* mapInfo = g_slice_new(GstMapInfo);
- if (!gst_buffer_map(buffer, mapInfo, GST_MAP_WRITE)) {
- g_slice_free(GstMapInfo, mapInfo);
+ GstMapInfo* mapInfo = static_cast<GstMapInfo*>(fastMalloc(sizeof(GstMapInfo)));
+ if (!gst_buffer_map(buffer, mapInfo, static_cast<GstMapFlags>(flags))) {
+ fastFree(mapInfo);
gst_buffer_unref(buffer);
return;
}
GstMiniObject* miniObject = reinterpret_cast<GstMiniObject*>(buffer);
- gst_mini_object_set_qdata(miniObject, g_quark_from_static_string(webkitGstMapInfoQuarkString), mapInfo, 0);
+ gst_mini_object_set_qdata(miniObject, g_quark_from_static_string(webkitGstMapInfoQuarkString), mapInfo, nullptr);
}
void unmapGstBuffer(GstBuffer* buffer)
@@ -117,23 +142,101 @@ void unmapGstBuffer(GstBuffer* buffer)
return;
gst_buffer_unmap(buffer, mapInfo);
- g_slice_free(GstMapInfo, mapInfo);
+ fastFree(mapInfo);
}
bool initializeGStreamer()
{
-#if GST_CHECK_VERSION(0, 10, 31)
if (gst_is_initialized())
return true;
-#endif
GUniqueOutPtr<GError> error;
// FIXME: We should probably pass the arguments from the command line.
- bool gstInitialized = gst_init_check(0, 0, &error.outPtr());
+ bool gstInitialized = gst_init_check(nullptr, nullptr, &error.outPtr());
ASSERT_WITH_MESSAGE(gstInitialized, "GStreamer initialization failed: %s", error ? error->message : "unknown error occurred");
+
+#if ENABLE(VIDEO_TRACK) && USE(GSTREAMER_MPEGTS)
+ if (gstInitialized)
+ gst_mpegts_initialize();
+#endif
+
return gstInitialized;
}
+unsigned getGstPlayFlag(const char* nick)
+{
+ static GFlagsClass* flagsClass = static_cast<GFlagsClass*>(g_type_class_ref(g_type_from_name("GstPlayFlags")));
+ ASSERT(flagsClass);
+
+ GFlagsValue* flag = g_flags_get_value_by_nick(flagsClass, nick);
+ if (!flag)
+ return 0;
+
+ return flag->value;
+}
+
+GstClockTime toGstClockTime(float time)
+{
+ // Extract the integer part of the time (seconds) and the fractional part (microseconds). Attempt to
+ // round the microseconds so no floating point precision is lost and we can perform an accurate seek.
+ float seconds;
+ float microSeconds = modff(time, &seconds) * 1000000;
+ GTimeVal timeValue;
+ timeValue.tv_sec = static_cast<glong>(seconds);
+ timeValue.tv_usec = static_cast<glong>(floor(microSeconds + 0.5));
+ return GST_TIMEVAL_TO_TIME(timeValue);
+}
+
+bool gstRegistryHasElementForMediaType(GList* elementFactories, const char* capsString)
+{
+ GRefPtr<GstCaps> caps = adoptGRef(gst_caps_from_string(capsString));
+ GList* candidates = gst_element_factory_list_filter(elementFactories, caps.get(), GST_PAD_SINK, false);
+ bool result = candidates;
+
+ gst_plugin_feature_list_free(candidates);
+ return result;
+}
+
+#if GST_CHECK_VERSION(1, 5, 3) && (ENABLE(LEGACY_ENCRYPTED_MEDIA) || ENABLE(ENCRYPTED_MEDIA))
+GstElement* createGstDecryptor(const gchar* protectionSystem)
+{
+ GstElement* decryptor = nullptr;
+ GList* decryptors = gst_element_factory_list_get_elements(GST_ELEMENT_FACTORY_TYPE_DECRYPTOR, GST_RANK_MARGINAL);
+
+ GST_TRACE("looking for decryptor for %s", protectionSystem);
+
+ for (GList* walk = decryptors; !decryptor && walk; walk = g_list_next(walk)) {
+ GstElementFactory* factory = reinterpret_cast<GstElementFactory*>(walk->data);
+
+ GST_TRACE("checking factory %s", GST_OBJECT_NAME(factory));
+
+ for (const GList* current = gst_element_factory_get_static_pad_templates(factory); current && !decryptor; current = g_list_next(current)) {
+ GstStaticPadTemplate* staticPadTemplate = static_cast<GstStaticPadTemplate*>(current->data);
+ GRefPtr<GstCaps> caps = adoptGRef(gst_static_pad_template_get_caps(staticPadTemplate));
+ unsigned length = gst_caps_get_size(caps.get());
+
+ GST_TRACE("factory %s caps has size %u", GST_OBJECT_NAME(factory), length);
+ for (unsigned i = 0; !decryptor && i < length; ++i) {
+ GstStructure* structure = gst_caps_get_structure(caps.get(), i);
+ GST_TRACE("checking structure %s", gst_structure_get_name(structure));
+ if (gst_structure_has_field_typed(structure, GST_PROTECTION_SYSTEM_ID_CAPS_FIELD, G_TYPE_STRING)) {
+ const gchar* sysId = gst_structure_get_string(structure, GST_PROTECTION_SYSTEM_ID_CAPS_FIELD);
+ GST_TRACE("structure %s has protection system %s", gst_structure_get_name(structure), sysId);
+ if (!g_ascii_strcasecmp(protectionSystem, sysId)) {
+ GST_DEBUG("found decryptor %s for %s", GST_OBJECT_NAME(factory), protectionSystem);
+ decryptor = gst_element_factory_create(factory, nullptr);
+ break;
+ }
+ }
+ }
+ }
+ }
+ gst_plugin_feature_list_free(decryptors);
+ GST_TRACE("returning decryptor %p", decryptor);
+ return decryptor;
+}
+#endif
+
}
#endif // USE(GSTREAMER)
diff --git a/Source/WebCore/platform/graphics/gstreamer/GStreamerUtilities.h b/Source/WebCore/platform/graphics/gstreamer/GStreamerUtilities.h
index 755dbdb72..f79a8cf6f 100644
--- a/Source/WebCore/platform/graphics/gstreamer/GStreamerUtilities.h
+++ b/Source/WebCore/platform/graphics/gstreamer/GStreamerUtilities.h
@@ -1,5 +1,6 @@
/*
- * Copyright (C) 2012 Igalia S.L
+ * Copyright (C) 2012, 2015, 2016 Igalia S.L
+ * Copyright (C) 2015, 2016 Metrological Group B.V.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@@ -16,26 +17,13 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
+#pragma once
+
#include "Logging.h"
#include <gst/gst.h>
-#include <gst/video/video.h>
-
-#define LOG_MEDIA_MESSAGE(...) do { \
- GST_DEBUG(__VA_ARGS__); \
- LOG_VERBOSE(Media, __VA_ARGS__); } while (0)
-
-#define ERROR_MEDIA_MESSAGE(...) do { \
- GST_ERROR(__VA_ARGS__); \
- LOG_VERBOSE(Media, __VA_ARGS__); } while (0)
-
-#define INFO_MEDIA_MESSAGE(...) do { \
- GST_INFO(__VA_ARGS__); \
- LOG_VERBOSE(Media, __VA_ARGS__); } while (0)
-
-#define WARN_MEDIA_MESSAGE(...) do { \
- GST_WARNING(__VA_ARGS__); \
- LOG_VERBOSE(Media, __VA_ARGS__); } while (0)
+#include <gst/video/video-format.h>
+#include <gst/video/video-info.h>
namespace WebCore {
@@ -65,12 +53,19 @@ inline bool webkitGstCheckVersion(guint major, guint minor, guint micro)
GstPad* webkitGstGhostPadFromStaticTemplate(GstStaticPadTemplate*, const gchar* name, GstPad* target);
#if ENABLE(VIDEO)
bool getVideoSizeAndFormatFromCaps(GstCaps*, WebCore::IntSize&, GstVideoFormat&, int& pixelAspectRatioNumerator, int& pixelAspectRatioDenominator, int& stride);
+bool getSampleVideoInfo(GstSample*, GstVideoInfo&);
#endif
GstBuffer* createGstBuffer(GstBuffer*);
GstBuffer* createGstBufferForData(const char* data, int length);
char* getGstBufferDataPointer(GstBuffer*);
-void mapGstBuffer(GstBuffer*);
+void mapGstBuffer(GstBuffer*, uint32_t);
void unmapGstBuffer(GstBuffer*);
bool initializeGStreamer();
+unsigned getGstPlayFlag(const char* nick);
+GstClockTime toGstClockTime(float time);
+bool gstRegistryHasElementForMediaType(GList* elementFactories, const char* capsString);
+#if GST_CHECK_VERSION(1, 5, 3) && (ENABLE(LEGACY_ENCRYPTED_MEDIA) || ENABLE(ENCRYPTED_MEDIA))
+GstElement* createGstDecryptor(const gchar* protectionSystem);
+#endif
}
diff --git a/Source/WebCore/platform/graphics/gstreamer/GUniquePtrGStreamer.h b/Source/WebCore/platform/graphics/gstreamer/GUniquePtrGStreamer.h
new file mode 100644
index 000000000..693990884
--- /dev/null
+++ b/Source/WebCore/platform/graphics/gstreamer/GUniquePtrGStreamer.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2015 Igalia S.L
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifndef GUniquePtrGStreamer_h
+#define GUniquePtrGStreamer_h
+#if USE(GSTREAMER)
+
+#include <gst/gststructure.h>
+#include <gst/pbutils/install-plugins.h>
+#include <wtf/glib/GUniquePtr.h>
+
+namespace WTF {
+
+WTF_DEFINE_GPTR_DELETER(GstStructure, gst_structure_free)
+WTF_DEFINE_GPTR_DELETER(GstInstallPluginsContext, gst_install_plugins_context_free)
+
+}
+
+#endif // USE(GSTREAMER)
+#endif
diff --git a/Source/WebCore/platform/graphics/gstreamer/ImageGStreamer.h b/Source/WebCore/platform/graphics/gstreamer/ImageGStreamer.h
index c9d2fb4e9..1ecd334ba 100644
--- a/Source/WebCore/platform/graphics/gstreamer/ImageGStreamer.h
+++ b/Source/WebCore/platform/graphics/gstreamer/ImageGStreamer.h
@@ -27,7 +27,7 @@
#include "GRefPtrGStreamer.h"
#include <gst/gst.h>
-#include <gst/video/video.h>
+#include <gst/video/video-frame.h>
#include <wtf/PassRefPtr.h>
#include <wtf/RefCounted.h>
@@ -38,9 +38,9 @@ class IntSize;
class ImageGStreamer : public RefCounted<ImageGStreamer> {
public:
- static PassRefPtr<ImageGStreamer> createImage(GstBuffer* buffer, GstCaps* caps)
+ static PassRefPtr<ImageGStreamer> createImage(GstSample* sample)
{
- return adoptRef(new ImageGStreamer(buffer, caps));
+ return adoptRef(new ImageGStreamer(sample));
}
~ImageGStreamer();
@@ -60,7 +60,7 @@ class ImageGStreamer : public RefCounted<ImageGStreamer> {
}
private:
- ImageGStreamer(GstBuffer*, GstCaps*);
+ ImageGStreamer(GstSample*);
RefPtr<BitmapImage> m_image;
FloatRect m_cropRect;
diff --git a/Source/WebCore/platform/graphics/gstreamer/ImageGStreamerCairo.cpp b/Source/WebCore/platform/graphics/gstreamer/ImageGStreamerCairo.cpp
index b153f09ec..c55cfdf97 100644
--- a/Source/WebCore/platform/graphics/gstreamer/ImageGStreamerCairo.cpp
+++ b/Source/WebCore/platform/graphics/gstreamer/ImageGStreamerCairo.cpp
@@ -32,8 +32,9 @@
using namespace std;
using namespace WebCore;
-ImageGStreamer::ImageGStreamer(GstBuffer* buffer, GstCaps* caps)
+ImageGStreamer::ImageGStreamer(GstSample* sample)
{
+ GstCaps* caps = gst_sample_get_caps(sample);
GstVideoInfo videoInfo;
gst_video_info_init(&videoInfo);
if (!gst_video_info_from_caps(&videoInfo, caps))
@@ -42,6 +43,7 @@ ImageGStreamer::ImageGStreamer(GstBuffer* buffer, GstCaps* caps)
// Right now the TextureMapper only supports chromas with one plane
ASSERT(GST_VIDEO_INFO_N_PLANES(&videoInfo) == 1);
+ GstBuffer* buffer = gst_sample_get_buffer(sample);
if (!gst_video_frame_map(&m_videoFrame, &videoInfo, buffer, GST_MAP_READ))
return;
@@ -60,7 +62,7 @@ ImageGStreamer::ImageGStreamer(GstBuffer* buffer, GstCaps* caps)
RefPtr<cairo_surface_t> surface = adoptRef(cairo_image_surface_create_for_data(bufferData, cairoFormat, width, height, stride));
ASSERT(cairo_surface_status(surface.get()) == CAIRO_STATUS_SUCCESS);
- m_image = BitmapImage::create(surface.release());
+ m_image = BitmapImage::create(WTFMove(surface));
if (GstVideoCropMeta* cropMeta = gst_buffer_get_video_crop_meta(buffer))
setCropRect(FloatRect(cropMeta->x, cropMeta->y, cropMeta->width, cropMeta->height));
@@ -69,9 +71,7 @@ ImageGStreamer::ImageGStreamer(GstBuffer* buffer, GstCaps* caps)
ImageGStreamer::~ImageGStreamer()
{
if (m_image)
- m_image.clear();
-
- m_image = 0;
+ m_image = nullptr;
// We keep the buffer memory mapped until the image is destroyed because the internal
// cairo_surface_t was created using cairo_image_surface_create_for_data().
diff --git a/Source/WebCore/platform/graphics/gstreamer/InbandMetadataTextTrackPrivateGStreamer.h b/Source/WebCore/platform/graphics/gstreamer/InbandMetadataTextTrackPrivateGStreamer.h
index b8adc641f..e942a2c5f 100644
--- a/Source/WebCore/platform/graphics/gstreamer/InbandMetadataTextTrackPrivateGStreamer.h
+++ b/Source/WebCore/platform/graphics/gstreamer/InbandMetadataTextTrackPrivateGStreamer.h
@@ -23,8 +23,7 @@
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef InbandMetadataTextTrackPrivateGStreamer_h
-#define InbandMetadataTextTrackPrivateGStreamer_h
+#pragma once
#if ENABLE(VIDEO) && USE(GSTREAMER) && ENABLE(VIDEO_TRACK)
@@ -35,28 +34,44 @@ namespace WebCore {
class InbandMetadataTextTrackPrivateGStreamer : public InbandTextTrackPrivate {
public:
- static PassRefPtr<InbandMetadataTextTrackPrivateGStreamer> create(Kind kind)
+ static PassRefPtr<InbandMetadataTextTrackPrivateGStreamer> create(Kind kind, CueFormat cueFormat, const AtomicString& id = emptyAtom)
{
- return adoptRef(new InbandMetadataTextTrackPrivateGStreamer(kind));
+ return adoptRef(new InbandMetadataTextTrackPrivateGStreamer(kind, cueFormat, id));
}
~InbandMetadataTextTrackPrivateGStreamer() { }
- virtual Kind kind() const override { return m_kind; }
+ Kind kind() const override { return m_kind; }
+ AtomicString id() const override { return m_id; }
+ AtomicString inBandMetadataTrackDispatchType() const override { return m_inBandMetadataTrackDispatchType; }
+ void setInBandMetadataTrackDispatchType(const AtomicString& value) { m_inBandMetadataTrackDispatchType = value; }
+
+ void addDataCue(const MediaTime& start, const MediaTime& end, const void* data, unsigned length)
+ {
+ ASSERT(cueFormat() == Data);
+ client()->addDataCue(start, end, data, length);
+ }
+
+ void addGenericCue(PassRefPtr<GenericCueData> data)
+ {
+ ASSERT(cueFormat() == Generic);
+ client()->addGenericCue(*data);
+ }
private:
- InbandMetadataTextTrackPrivateGStreamer(Kind kind)
- : InbandTextTrackPrivate(Generic)
+ InbandMetadataTextTrackPrivateGStreamer(Kind kind, CueFormat cueFormat, const AtomicString& id)
+ : InbandTextTrackPrivate(cueFormat)
, m_kind(kind)
+ , m_id(id)
{
}
Kind m_kind;
+ AtomicString m_id;
+ AtomicString m_inBandMetadataTrackDispatchType;
};
} // namespace WebCore
#endif // ENABLE(VIDEO) && USE(GSTREAMER) && ENABLE(VIDEO_TRACK)
-
-#endif // InbandMetadataTextTrackPrivateGStreamer_h
diff --git a/Source/WebCore/platform/graphics/gstreamer/InbandTextTrackPrivateGStreamer.cpp b/Source/WebCore/platform/graphics/gstreamer/InbandTextTrackPrivateGStreamer.cpp
index bed10faa7..24e58f160 100644
--- a/Source/WebCore/platform/graphics/gstreamer/InbandTextTrackPrivateGStreamer.cpp
+++ b/Source/WebCore/platform/graphics/gstreamer/InbandTextTrackPrivateGStreamer.cpp
@@ -39,38 +39,20 @@ GST_DEBUG_CATEGORY_EXTERN(webkit_media_player_debug);
namespace WebCore {
-static GstPadProbeReturn textTrackPrivateEventCallback(GstPad*, GstPadProbeInfo* info, InbandTextTrackPrivateGStreamer* track)
-{
- GstEvent* event = gst_pad_probe_info_get_event(info);
- switch (GST_EVENT_TYPE(event)) {
- case GST_EVENT_STREAM_START:
- track->streamChanged();
- break;
- default:
- break;
- }
- return GST_PAD_PROBE_OK;
-}
-
-static gboolean textTrackPrivateSampleTimeoutCallback(InbandTextTrackPrivateGStreamer* track)
-{
- track->notifyTrackOfSample();
- return FALSE;
-}
-
-static gboolean textTrackPrivateStreamTimeoutCallback(InbandTextTrackPrivateGStreamer* track)
-{
- track->notifyTrackOfStreamChanged();
- return FALSE;
-}
-
InbandTextTrackPrivateGStreamer::InbandTextTrackPrivateGStreamer(gint index, GRefPtr<GstPad> pad)
: InbandTextTrackPrivate(WebVTT), TrackPrivateBaseGStreamer(this, index, pad)
- , m_sampleTimerHandler(0)
- , m_streamTimerHandler(0)
{
- m_eventProbe = gst_pad_add_probe(m_pad.get(), GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM,
- reinterpret_cast<GstPadProbeCallback>(textTrackPrivateEventCallback), this, 0);
+ m_eventProbe = gst_pad_add_probe(m_pad.get(), GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM, [] (GstPad*, GstPadProbeInfo* info, gpointer userData) -> GstPadProbeReturn {
+ auto* track = static_cast<InbandTextTrackPrivateGStreamer*>(userData);
+ switch (GST_EVENT_TYPE(gst_pad_probe_info_get_event(info))) {
+ case GST_EVENT_STREAM_START:
+ track->streamChanged();
+ break;
+ default:
+ break;
+ }
+ return GST_PAD_PROBE_OK;
+ }, this, nullptr);
notifyTrackOfStreamChanged();
}
@@ -82,39 +64,35 @@ void InbandTextTrackPrivateGStreamer::disconnect()
gst_pad_remove_probe(m_pad.get(), m_eventProbe);
- if (m_streamTimerHandler)
- g_source_remove(m_streamTimerHandler);
-
TrackPrivateBaseGStreamer::disconnect();
}
void InbandTextTrackPrivateGStreamer::handleSample(GRefPtr<GstSample> sample)
{
- if (m_sampleTimerHandler)
- g_source_remove(m_sampleTimerHandler);
{
- MutexLocker lock(m_sampleMutex);
+ LockHolder lock(m_sampleMutex);
m_pendingSamples.append(sample);
}
- m_sampleTimerHandler = g_timeout_add(0,
- reinterpret_cast<GSourceFunc>(textTrackPrivateSampleTimeoutCallback), this);
+
+ RefPtr<InbandTextTrackPrivateGStreamer> protectedThis(this);
+ m_notifier->notify(MainThreadNotification::NewSample, [protectedThis] {
+ protectedThis->notifyTrackOfSample();
+ });
}
void InbandTextTrackPrivateGStreamer::streamChanged()
{
- if (m_streamTimerHandler)
- g_source_remove(m_streamTimerHandler);
- m_streamTimerHandler = g_timeout_add(0,
- reinterpret_cast<GSourceFunc>(textTrackPrivateStreamTimeoutCallback), this);
+ RefPtr<InbandTextTrackPrivateGStreamer> protectedThis(this);
+ m_notifier->notify(MainThreadNotification::StreamChanged, [protectedThis] {
+ protectedThis->notifyTrackOfStreamChanged();
+ });
}
void InbandTextTrackPrivateGStreamer::notifyTrackOfSample()
{
- m_sampleTimerHandler = 0;
-
Vector<GRefPtr<GstSample> > samples;
{
- MutexLocker lock(m_sampleMutex);
+ LockHolder lock(m_sampleMutex);
m_pendingSamples.swap(samples);
}
@@ -122,28 +100,26 @@ void InbandTextTrackPrivateGStreamer::notifyTrackOfSample()
GRefPtr<GstSample> sample = samples[i];
GstBuffer* buffer = gst_sample_get_buffer(sample.get());
if (!buffer) {
- WARN_MEDIA_MESSAGE("Track %d got sample with no buffer.", m_index);
+ GST_WARNING("Track %d got sample with no buffer.", m_index);
continue;
}
GstMapInfo info;
gboolean ret = gst_buffer_map(buffer, &info, GST_MAP_READ);
ASSERT(ret);
if (!ret) {
- WARN_MEDIA_MESSAGE("Track %d unable to map buffer.", m_index);
+ GST_WARNING("Track %d unable to map buffer.", m_index);
continue;
}
- INFO_MEDIA_MESSAGE("Track %d parsing sample: %.*s", m_index, static_cast<int>(info.size),
+ GST_INFO("Track %d parsing sample: %.*s", m_index, static_cast<int>(info.size),
reinterpret_cast<char*>(info.data));
- client()->parseWebVTTCueData(this, reinterpret_cast<char*>(info.data), info.size);
+ client()->parseWebVTTCueData(reinterpret_cast<char*>(info.data), info.size);
gst_buffer_unmap(buffer, &info);
}
}
void InbandTextTrackPrivateGStreamer::notifyTrackOfStreamChanged()
{
- m_streamTimerHandler = 0;
-
GRefPtr<GstEvent> event = adoptGRef(gst_pad_get_sticky_event(m_pad.get(),
GST_EVENT_STREAM_START, 0));
if (!event)
@@ -151,7 +127,7 @@ void InbandTextTrackPrivateGStreamer::notifyTrackOfStreamChanged()
const gchar* streamId;
gst_event_parse_stream_start(event.get(), &streamId);
- INFO_MEDIA_MESSAGE("Track %d got stream start for stream %s.", m_index, streamId);
+ GST_INFO("Track %d got stream start for stream %s.", m_index, streamId);
m_streamId = streamId;
}
diff --git a/Source/WebCore/platform/graphics/gstreamer/InbandTextTrackPrivateGStreamer.h b/Source/WebCore/platform/graphics/gstreamer/InbandTextTrackPrivateGStreamer.h
index 285cd86c8..cc3a6f0e9 100644
--- a/Source/WebCore/platform/graphics/gstreamer/InbandTextTrackPrivateGStreamer.h
+++ b/Source/WebCore/platform/graphics/gstreamer/InbandTextTrackPrivateGStreamer.h
@@ -31,6 +31,7 @@
#include "GRefPtrGStreamer.h"
#include "InbandTextTrackPrivate.h"
#include "TrackPrivateBaseGStreamer.h"
+#include <wtf/Lock.h>
namespace WebCore {
@@ -44,29 +45,28 @@ public:
return adoptRef(new InbandTextTrackPrivateGStreamer(index, pad));
}
- virtual void disconnect() override;
+ void disconnect() override;
- virtual AtomicString label() const override { return m_label; }
- virtual AtomicString language() const override { return m_language; }
+ AtomicString label() const override { return m_label; }
+ AtomicString language() const override { return m_language; }
- virtual int trackIndex() const override { return m_index; }
+ int trackIndex() const override { return m_index; }
String streamId() const { return m_streamId; }
void handleSample(GRefPtr<GstSample>);
+
+private:
+ InbandTextTrackPrivateGStreamer(gint index, GRefPtr<GstPad>);
+
void streamChanged();
void notifyTrackOfSample();
void notifyTrackOfStreamChanged();
-private:
- InbandTextTrackPrivateGStreamer(gint index, GRefPtr<GstPad>);
-
- guint m_sampleTimerHandler;
- guint m_streamTimerHandler;
gulong m_eventProbe;
Vector<GRefPtr<GstSample> > m_pendingSamples;
String m_streamId;
- Mutex m_sampleMutex;
+ Lock m_sampleMutex;
};
} // namespace WebCore
diff --git a/Source/WebCore/platform/graphics/gstreamer/MainThreadNotifier.h b/Source/WebCore/platform/graphics/gstreamer/MainThreadNotifier.h
new file mode 100644
index 000000000..96b587ff9
--- /dev/null
+++ b/Source/WebCore/platform/graphics/gstreamer/MainThreadNotifier.h
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2015 Igalia S.L
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#pragma once
+
+#include <wtf/Atomics.h>
+#include <wtf/Lock.h>
+#include <wtf/MainThread.h>
+#include <wtf/RunLoop.h>
+#include <wtf/ThreadSafeRefCounted.h>
+
+namespace WebCore {
+
+template <typename T>
+class MainThreadNotifier final : public ThreadSafeRefCounted<MainThreadNotifier<T>> {
+public:
+ static Ref<MainThreadNotifier> create()
+ {
+ return adoptRef(*new MainThreadNotifier());
+ }
+
+ template<typename F>
+ void notify(T notificationType, const F& callbackFunctor)
+ {
+ ASSERT(m_isValid.load());
+ if (isMainThread()) {
+ removePendingNotification(notificationType);
+ callbackFunctor();
+ return;
+ }
+
+ if (!addPendingNotification(notificationType))
+ return;
+
+ RunLoop::main().dispatch([this, protectedThis = makeRef(*this), notificationType, callback = std::function<void()>(callbackFunctor)] {
+ if (!m_isValid.load())
+ return;
+ if (removePendingNotification(notificationType))
+ callback();
+ });
+ }
+
+ void cancelPendingNotifications(unsigned mask = 0)
+ {
+ ASSERT(m_isValid.load());
+ LockHolder locker(m_pendingNotificationsLock);
+ if (mask)
+ m_pendingNotifications &= ~mask;
+ else
+ m_pendingNotifications = 0;
+ }
+
+ void invalidate()
+ {
+ ASSERT(m_isValid.load());
+ m_isValid.store(false);
+ }
+
+private:
+ MainThreadNotifier()
+ {
+ m_isValid.store(true);
+ }
+
+ bool addPendingNotification(T notificationType)
+ {
+ LockHolder locker(m_pendingNotificationsLock);
+ if (notificationType & m_pendingNotifications)
+ return false;
+ m_pendingNotifications |= notificationType;
+ return true;
+ }
+
+ bool removePendingNotification(T notificationType)
+ {
+ LockHolder locker(m_pendingNotificationsLock);
+ if (notificationType & m_pendingNotifications) {
+ m_pendingNotifications &= ~notificationType;
+ return true;
+ }
+ return false;
+ }
+
+ Lock m_pendingNotificationsLock;
+ unsigned m_pendingNotifications { 0 };
+ Atomic<bool> m_isValid;
+};
+
+} // namespace WebCore
+
diff --git a/Source/WebCore/platform/graphics/gstreamer/MediaPlayerPrivateGStreamer.cpp b/Source/WebCore/platform/graphics/gstreamer/MediaPlayerPrivateGStreamer.cpp
index 165c5a06a..cadf905ed 100644
--- a/Source/WebCore/platform/graphics/gstreamer/MediaPlayerPrivateGStreamer.cpp
+++ b/Source/WebCore/platform/graphics/gstreamer/MediaPlayerPrivateGStreamer.cpp
@@ -3,7 +3,9 @@
* Copyright (C) 2007 Collabora Ltd. All rights reserved.
* Copyright (C) 2007 Alp Toker <alp@atoker.com>
* Copyright (C) 2009 Gustavo Noronha Silva <gns@gnome.org>
- * Copyright (C) 2009, 2010, 2011, 2012, 2013 Igalia S.L
+ * Copyright (C) 2009, 2010, 2011, 2012, 2013, 2015, 2016 Igalia S.L
+ * Copyright (C) 2014 Cable Television Laboratories, Inc.
+ * Copyright (C) 2015, 2016 Metrological Group B.V.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
@@ -26,18 +28,24 @@
#if ENABLE(VIDEO) && USE(GSTREAMER)
+#include "FileSystem.h"
#include "GStreamerUtilities.h"
#include "URL.h"
#include "MIMETypeRegistry.h"
#include "MediaPlayer.h"
+#include "MediaPlayerRequestInstallMissingPluginsCallback.h"
#include "NotImplemented.h"
#include "SecurityOrigin.h"
#include "TimeRanges.h"
#include "WebKitWebSourceGStreamer.h"
+#include <glib.h>
#include <gst/gst.h>
#include <gst/pbutils/missing-plugins.h>
#include <limits>
-#include <wtf/gobject/GUniquePtr.h>
+#include <wtf/HexNumber.h>
+#include <wtf/MediaTime.h>
+#include <wtf/NeverDestroyed.h>
+#include <wtf/glib/GUniquePtr.h>
#include <wtf/text/CString.h>
#if ENABLE(VIDEO_TRACK)
@@ -49,6 +57,11 @@
#include "VideoTrackPrivateGStreamer.h"
#endif
+#if ENABLE(VIDEO_TRACK) && USE(GSTREAMER_MPEGTS)
+#define GST_USE_UNSTABLE_API
+#include <gst/mpegts/mpegts.h>
+#undef GST_USE_UNSTABLE_API
+#endif
#include <gst/audio/streamvolume.h>
#if ENABLE(MEDIA_SOURCE)
@@ -56,24 +69,9 @@
#include "WebKitMediaSourceGStreamer.h"
#endif
-// GstPlayFlags flags from playbin2. It is the policy of GStreamer to
-// not publicly expose element-specific enums. That's why this
-// GstPlayFlags enum has been copied here.
-typedef enum {
- GST_PLAY_FLAG_VIDEO = 0x00000001,
- GST_PLAY_FLAG_AUDIO = 0x00000002,
- GST_PLAY_FLAG_TEXT = 0x00000004,
- GST_PLAY_FLAG_VIS = 0x00000008,
- GST_PLAY_FLAG_SOFT_VOLUME = 0x00000010,
- GST_PLAY_FLAG_NATIVE_AUDIO = 0x00000020,
- GST_PLAY_FLAG_NATIVE_VIDEO = 0x00000040,
- GST_PLAY_FLAG_DOWNLOAD = 0x00000080,
- GST_PLAY_FLAG_BUFFERING = 0x000000100
-} GstPlayFlags;
-
-// Max interval in seconds to stay in the READY state on manual
-// state change requests.
-static const guint gReadyStateTimerInterval = 60;
+#if ENABLE(WEB_AUDIO)
+#include "AudioSourceProviderGStreamer.h"
+#endif
GST_DEBUG_CATEGORY_EXTERN(webkit_media_player_debug);
#define GST_CAT_DEFAULT webkit_media_player_debug
@@ -82,127 +80,34 @@ using namespace std;
namespace WebCore {
-static gboolean mediaPlayerPrivateMessageCallback(GstBus*, GstMessage* message, MediaPlayerPrivateGStreamer* player)
-{
- return player->handleMessage(message);
-}
-
-static void mediaPlayerPrivateSourceChangedCallback(GObject*, GParamSpec*, MediaPlayerPrivateGStreamer* player)
-{
- player->sourceChanged();
-}
-
-static void mediaPlayerPrivateVideoSinkCapsChangedCallback(GObject*, GParamSpec*, MediaPlayerPrivateGStreamer* player)
-{
- player->videoCapsChanged();
-}
-
-static void mediaPlayerPrivateVideoChangedCallback(GObject*, MediaPlayerPrivateGStreamer* player)
-{
- player->videoChanged();
-}
-
-static void mediaPlayerPrivateAudioChangedCallback(GObject*, MediaPlayerPrivateGStreamer* player)
+static void busMessageCallback(GstBus*, GstMessage* message, MediaPlayerPrivateGStreamer* player)
{
- player->audioChanged();
+ player->handleMessage(message);
}
-static gboolean mediaPlayerPrivateAudioChangeTimeoutCallback(MediaPlayerPrivateGStreamer* player)
-{
- // This is the callback of the timeout source created in ::audioChanged.
- player->notifyPlayerOfAudio();
- return FALSE;
-}
-
-static void setAudioStreamPropertiesCallback(GstChildProxy*, GObject* object, gchar*,
- MediaPlayerPrivateGStreamer* player)
+void MediaPlayerPrivateGStreamer::setAudioStreamPropertiesCallback(MediaPlayerPrivateGStreamer* player, GObject* object)
{
player->setAudioStreamProperties(object);
}
-static gboolean mediaPlayerPrivateVideoChangeTimeoutCallback(MediaPlayerPrivateGStreamer* player)
-{
- // This is the callback of the timeout source created in ::videoChanged.
- player->notifyPlayerOfVideo();
- return FALSE;
-}
-
-static gboolean mediaPlayerPrivateVideoCapsChangeTimeoutCallback(MediaPlayerPrivateGStreamer* player)
-{
- // This is the callback of the timeout source created in ::videoCapsChanged.
- player->notifyPlayerOfVideoCaps();
- return FALSE;
-}
-
-#if ENABLE(VIDEO_TRACK)
-static void mediaPlayerPrivateTextChangedCallback(GObject*, MediaPlayerPrivateGStreamer* player)
-{
- player->textChanged();
-}
-
-static gboolean mediaPlayerPrivateTextChangeTimeoutCallback(MediaPlayerPrivateGStreamer* player)
-{
- // This is the callback of the timeout source created in ::textChanged.
- player->notifyPlayerOfText();
- return FALSE;
-}
-
-static GstFlowReturn mediaPlayerPrivateNewTextSampleCallback(GObject*, MediaPlayerPrivateGStreamer* player)
-{
- player->newTextSample();
- return GST_FLOW_OK;
-}
-#endif
-
-static gboolean mediaPlayerPrivateReadyStateTimeoutCallback(MediaPlayerPrivateGStreamer* player)
-{
- // This is the callback of the timeout source created in ::changePipelineState.
- // Reset pipeline if we are sitting on READY state when timeout is reached
- player->changePipelineState(GST_STATE_NULL);
- return FALSE;
-}
-
-static void mediaPlayerPrivatePluginInstallerResultFunction(GstInstallPluginsReturn result, gpointer userData)
-{
- MediaPlayerPrivateGStreamer* player = reinterpret_cast<MediaPlayerPrivateGStreamer*>(userData);
- player->handlePluginInstallerResult(result);
-}
-
-static GstClockTime toGstClockTime(float time)
-{
- // Extract the integer part of the time (seconds) and the fractional part (microseconds). Attempt to
- // round the microseconds so no floating point precision is lost and we can perform an accurate seek.
- float seconds;
- float microSeconds = modf(time, &seconds) * 1000000;
- GTimeVal timeValue;
- timeValue.tv_sec = static_cast<glong>(seconds);
- timeValue.tv_usec = static_cast<glong>(roundf(microSeconds / 10000) * 10000);
- return GST_TIMEVAL_TO_TIME(timeValue);
-}
-
void MediaPlayerPrivateGStreamer::setAudioStreamProperties(GObject* object)
{
if (g_strcmp0(G_OBJECT_TYPE_NAME(object), "GstPulseSink"))
return;
- const char* role = m_player->mediaPlayerClient() && m_player->mediaPlayerClient()->mediaPlayerIsVideo()
- ? "video" : "music";
- GstStructure* structure = gst_structure_new("stream-properties", "media.role", G_TYPE_STRING, role, NULL);
- g_object_set(object, "stream-properties", structure, NULL);
+ const char* role = m_player->client().mediaPlayerIsVideo() ? "video" : "music";
+ GstStructure* structure = gst_structure_new("stream-properties", "media.role", G_TYPE_STRING, role, nullptr);
+ g_object_set(object, "stream-properties", structure, nullptr);
gst_structure_free(structure);
GUniquePtr<gchar> elementName(gst_element_get_name(GST_ELEMENT(object)));
- LOG_MEDIA_MESSAGE("Set media.role as %s at %s", role, elementName.get());
-}
-
-PassOwnPtr<MediaPlayerPrivateInterface> MediaPlayerPrivateGStreamer::create(MediaPlayer* player)
-{
- return adoptPtr(new MediaPlayerPrivateGStreamer(player));
+ GST_DEBUG("Set media.role as %s at %s", role, elementName.get());
}
void MediaPlayerPrivateGStreamer::registerMediaEngine(MediaEngineRegistrar registrar)
{
if (isAvailable())
- registrar(create, getSupportedTypes, supportsType, 0, 0, 0);
+ registrar([](MediaPlayer* player) { return std::make_unique<MediaPlayerPrivateGStreamer>(player); },
+ getSupportedTypes, supportsType, nullptr, nullptr, nullptr, supportsKeySystem);
}
bool initializeGStreamerAndRegisterWebKitElements()
@@ -210,17 +115,14 @@ bool initializeGStreamerAndRegisterWebKitElements()
if (!initializeGStreamer())
return false;
- GRefPtr<GstElementFactory> srcFactory = gst_element_factory_find("webkitwebsrc");
+ registerWebKitGStreamerElements();
+
+ GRefPtr<GstElementFactory> srcFactory = adoptGRef(gst_element_factory_find("webkitwebsrc"));
if (!srcFactory) {
GST_DEBUG_CATEGORY_INIT(webkit_media_player_debug, "webkitmediaplayer", 0, "WebKit media player");
- gst_element_register(0, "webkitwebsrc", GST_RANK_PRIMARY + 100, WEBKIT_TYPE_WEB_SRC);
+ gst_element_register(nullptr, "webkitwebsrc", GST_RANK_PRIMARY + 100, WEBKIT_TYPE_WEB_SRC);
}
-#if ENABLE(MEDIA_SOURCE)
- GRefPtr<GstElementFactory> WebKitMediaSrcFactory = gst_element_factory_find("webkitmediasrc");
- if (!WebKitMediaSrcFactory)
- gst_element_register(0, "webkitmediasrc", GST_RANK_PRIMARY + 100, WEBKIT_TYPE_MEDIA_SRC);
-#endif
return true;
}
@@ -229,52 +131,50 @@ bool MediaPlayerPrivateGStreamer::isAvailable()
if (!initializeGStreamerAndRegisterWebKitElements())
return false;
- GRefPtr<GstElementFactory> factory = gst_element_factory_find("playbin");
+ GRefPtr<GstElementFactory> factory = adoptGRef(gst_element_factory_find("playbin"));
return factory;
}
MediaPlayerPrivateGStreamer::MediaPlayerPrivateGStreamer(MediaPlayer* player)
: MediaPlayerPrivateGStreamerBase(player)
- , m_source(0)
- , m_seekTime(0)
+ , m_buffering(false)
+ , m_bufferingPercentage(0)
+ , m_canFallBackToLastFinishedSeekPosition(false)
, m_changingRate(false)
- , m_endTime(numeric_limits<float>::infinity())
+ , m_downloadFinished(false)
+ , m_errorOccured(false)
, m_isEndReached(false)
, m_isStreaming(false)
- , m_mediaLocations(0)
- , m_mediaLocationCurrentIndex(0)
- , m_resetPipeline(false)
+ , m_durationAtEOS(0)
, m_paused(true)
- , m_playbackRatePause(false)
+ , m_playbackRate(1)
+ , m_requestedState(GST_STATE_VOID_PENDING)
+ , m_resetPipeline(false)
, m_seeking(false)
, m_seekIsPending(false)
+ , m_seekTime(0)
+ , m_source(nullptr)
+ , m_volumeAndMuteInitialized(false)
+ , m_weakPtrFactory(this)
+ , m_mediaLocations(nullptr)
+ , m_mediaLocationCurrentIndex(0)
+ , m_playbackRatePause(false)
, m_timeOfOverlappingSeek(-1)
- , m_buffering(false)
- , m_playbackRate(1)
, m_lastPlaybackRate(1)
- , m_errorOccured(false)
- , m_mediaDuration(0)
- , m_downloadFinished(false)
- , m_fillTimer(this, &MediaPlayerPrivateGStreamer::fillTimerFired)
+ , m_fillTimer(*this, &MediaPlayerPrivateGStreamer::fillTimerFired)
, m_maxTimeLoaded(0)
- , m_bufferingPercentage(0)
, m_preload(player->preload())
, m_delayingLoad(false)
- , m_mediaDurationKnown(true)
, m_maxTimeLoadedAtLastDidLoadingProgress(0)
- , m_volumeAndMuteInitialized(false)
, m_hasVideo(false)
, m_hasAudio(false)
- , m_audioTimerHandler(0)
- , m_textTimerHandler(0)
- , m_videoTimerHandler(0)
- , m_videoCapsTimerHandler(0)
- , m_readyTimerHandler(0)
- , m_totalBytes(-1)
+ , m_readyTimerHandler(RunLoop::main(), this, &MediaPlayerPrivateGStreamer::readyTimerFired)
+ , m_totalBytes(0)
, m_preservesPitch(false)
- , m_requestedState(GST_STATE_VOID_PENDING)
- , m_missingPlugins(false)
{
+#if USE(GLIB)
+ m_readyTimerHandler.setPriority(G_PRIORITY_DEFAULT_IDLE);
+#endif
}
MediaPlayerPrivateGStreamer::~MediaPlayerPrivateGStreamer()
@@ -294,50 +194,35 @@ MediaPlayerPrivateGStreamer::~MediaPlayerPrivateGStreamer()
if (m_mediaLocations) {
gst_structure_free(m_mediaLocations);
- m_mediaLocations = 0;
+ m_mediaLocations = nullptr;
}
+ if (WEBKIT_IS_WEB_SRC(m_source.get()) && GST_OBJECT_PARENT(m_source.get()))
+ g_signal_handlers_disconnect_by_func(GST_ELEMENT_PARENT(m_source.get()), reinterpret_cast<gpointer>(uriDecodeBinElementAddedCallback), this);
+
if (m_autoAudioSink)
g_signal_handlers_disconnect_by_func(G_OBJECT(m_autoAudioSink.get()),
reinterpret_cast<gpointer>(setAudioStreamPropertiesCallback), this);
- if (m_readyTimerHandler)
- g_source_remove(m_readyTimerHandler);
-
- if (m_playBin) {
- GRefPtr<GstBus> bus = adoptGRef(gst_pipeline_get_bus(GST_PIPELINE(m_playBin.get())));
- ASSERT(bus);
- g_signal_handlers_disconnect_by_func(bus.get(), reinterpret_cast<gpointer>(mediaPlayerPrivateMessageCallback), this);
- gst_bus_remove_signal_watch(bus.get());
-
- g_signal_handlers_disconnect_by_func(m_playBin.get(), reinterpret_cast<gpointer>(mediaPlayerPrivateSourceChangedCallback), this);
- g_signal_handlers_disconnect_by_func(m_playBin.get(), reinterpret_cast<gpointer>(mediaPlayerPrivateVideoChangedCallback), this);
- g_signal_handlers_disconnect_by_func(m_playBin.get(), reinterpret_cast<gpointer>(mediaPlayerPrivateAudioChangedCallback), this);
-#if ENABLE(VIDEO_TRACK)
- g_signal_handlers_disconnect_by_func(m_playBin.get(), reinterpret_cast<gpointer>(mediaPlayerPrivateNewTextSampleCallback), this);
- g_signal_handlers_disconnect_by_func(m_playBin.get(), reinterpret_cast<gpointer>(mediaPlayerPrivateTextChangedCallback), this);
-#endif
-
- gst_element_set_state(m_playBin.get(), GST_STATE_NULL);
- m_playBin.clear();
+ m_readyTimerHandler.stop();
+ if (m_missingPluginsCallback) {
+ m_missingPluginsCallback->invalidate();
+ m_missingPluginsCallback = nullptr;
}
- if (m_webkitVideoSink) {
- GRefPtr<GstPad> videoSinkPad = adoptGRef(gst_element_get_static_pad(m_webkitVideoSink.get(), "sink"));
- g_signal_handlers_disconnect_by_func(videoSinkPad.get(), reinterpret_cast<gpointer>(mediaPlayerPrivateVideoSinkCapsChangedCallback), this);
+ if (m_videoSink) {
+ GRefPtr<GstPad> videoSinkPad = adoptGRef(gst_element_get_static_pad(m_videoSink.get(), "sink"));
+ g_signal_handlers_disconnect_matched(videoSinkPad.get(), G_SIGNAL_MATCH_DATA, 0, 0, nullptr, nullptr, this);
}
- if (m_videoTimerHandler)
- g_source_remove(m_videoTimerHandler);
-
- if (m_audioTimerHandler)
- g_source_remove(m_audioTimerHandler);
-
- if (m_textTimerHandler)
- g_source_remove(m_textTimerHandler);
-
- if (m_videoCapsTimerHandler)
- g_source_remove(m_videoCapsTimerHandler);
+ if (m_pipeline) {
+ GRefPtr<GstBus> bus = adoptGRef(gst_pipeline_get_bus(GST_PIPELINE(m_pipeline.get())));
+ ASSERT(bus);
+ g_signal_handlers_disconnect_by_func(bus.get(), gpointer(busMessageCallback), this);
+ gst_bus_remove_signal_watch(bus.get());
+ gst_bus_set_sync_handler(bus.get(), nullptr, nullptr, nullptr);
+ g_signal_handlers_disconnect_matched(m_pipeline.get(), G_SIGNAL_MATCH_DATA, 0, 0, nullptr, nullptr, this);
+ }
}
void MediaPlayerPrivateGStreamer::load(const String& urlString)
@@ -354,18 +239,21 @@ void MediaPlayerPrivateGStreamer::load(const String& urlString)
if (url.isLocalFile())
cleanURL = cleanURL.substring(0, url.pathEnd());
- if (!m_playBin)
+ if (!m_pipeline)
createGSTPlayBin();
- ASSERT(m_playBin);
+ if (m_fillTimer.isActive())
+ m_fillTimer.stop();
+
+ ASSERT(m_pipeline);
m_url = URL(URL(), cleanURL);
- g_object_set(m_playBin.get(), "uri", cleanURL.utf8().data(), NULL);
+ g_object_set(m_pipeline.get(), "uri", cleanURL.utf8().data(), nullptr);
- INFO_MEDIA_MESSAGE("Load %s", cleanURL.utf8().data());
+ GST_INFO("Load %s", cleanURL.utf8().data());
if (m_preload == MediaPlayer::None) {
- LOG_MEDIA_MESSAGE("Delaying load.");
+ GST_DEBUG("Delaying load.");
m_delayingLoad = true;
}
@@ -376,24 +264,32 @@ void MediaPlayerPrivateGStreamer::load(const String& urlString)
m_readyState = MediaPlayer::HaveNothing;
m_player->readyStateChanged();
m_volumeAndMuteInitialized = false;
+ m_durationAtEOS = 0;
if (!m_delayingLoad)
commitLoad();
}
#if ENABLE(MEDIA_SOURCE)
-void MediaPlayerPrivateGStreamer::load(const String& url, PassRefPtr<HTMLMediaSource> mediaSource)
+void MediaPlayerPrivateGStreamer::load(const String&, MediaSourcePrivateClient*)
+{
+ // Properly fail so the global MediaPlayer tries to fallback to the next MediaPlayerPrivate.
+ m_networkState = MediaPlayer::FormatError;
+ m_player->networkStateChanged();
+}
+#endif
+
+#if ENABLE(MEDIA_STREAM)
+void MediaPlayerPrivateGStreamer::load(MediaStreamPrivate&)
{
- String mediasourceUri = String::format("mediasource%s", url.utf8().data());
- m_mediaSource = mediaSource;
- load(mediasourceUri);
+ notImplemented();
}
#endif
void MediaPlayerPrivateGStreamer::commitLoad()
{
ASSERT(!m_delayingLoad);
- LOG_MEDIA_MESSAGE("Committing load.");
+ GST_DEBUG("Committing load.");
// GStreamer needs to have the pipeline set to a paused state to
// start providing anything useful.
@@ -403,7 +299,7 @@ void MediaPlayerPrivateGStreamer::commitLoad()
updateStates();
}
-float MediaPlayerPrivateGStreamer::playbackPosition() const
+double MediaPlayerPrivateGStreamer::playbackPosition() const
{
if (m_isEndReached) {
// Position queries on a null pipeline return 0. If we're at
@@ -412,48 +308,56 @@ float MediaPlayerPrivateGStreamer::playbackPosition() const
// what the Media element spec expects us to do.
if (m_seeking)
return m_seekTime;
- if (m_mediaDuration)
- return m_mediaDuration;
+
+ MediaTime mediaDuration = durationMediaTime();
+ if (mediaDuration)
+ return mediaDuration.toDouble();
return 0;
}
// Position is only available if no async state change is going on and the state is either paused or playing.
gint64 position = GST_CLOCK_TIME_NONE;
GstQuery* query= gst_query_new_position(GST_FORMAT_TIME);
- if (gst_element_query(m_playBin.get(), query))
+ if (gst_element_query(m_pipeline.get(), query))
gst_query_parse_position(query, 0, &position);
+ gst_query_unref(query);
- float result = 0.0f;
- if (static_cast<GstClockTime>(position) != GST_CLOCK_TIME_NONE)
- result = static_cast<double>(position) / GST_SECOND;
- else if (m_canFallBackToLastFinishedSeekPositon)
- result = m_seekTime;
-
- LOG_MEDIA_MESSAGE("Position %" GST_TIME_FORMAT, GST_TIME_ARGS(position));
+ GST_DEBUG("Position %" GST_TIME_FORMAT, GST_TIME_ARGS(position));
- gst_query_unref(query);
+ double result = 0.0f;
+ if (static_cast<GstClockTime>(position) != GST_CLOCK_TIME_NONE) {
+ GTimeVal timeValue;
+ GST_TIME_TO_TIMEVAL(position, timeValue);
+ result = static_cast<double>(timeValue.tv_sec + (timeValue.tv_usec / 1000000.0));
+ } else if (m_canFallBackToLastFinishedSeekPosition)
+ result = m_seekTime;
return result;
}
+void MediaPlayerPrivateGStreamer::readyTimerFired()
+{
+ changePipelineState(GST_STATE_NULL);
+}
+
bool MediaPlayerPrivateGStreamer::changePipelineState(GstState newState)
{
- ASSERT(m_playBin);
+ ASSERT(m_pipeline);
GstState currentState;
GstState pending;
- gst_element_get_state(m_playBin.get(), &currentState, &pending, 0);
+ gst_element_get_state(m_pipeline.get(), &currentState, &pending, 0);
if (currentState == newState || pending == newState) {
- LOG_MEDIA_MESSAGE("Rejected state change to %s from %s with %s pending", gst_element_state_get_name(newState),
+ GST_DEBUG("Rejected state change to %s from %s with %s pending", gst_element_state_get_name(newState),
gst_element_state_get_name(currentState), gst_element_state_get_name(pending));
return true;
}
- LOG_MEDIA_MESSAGE("Changing state change to %s from %s with %s pending", gst_element_state_get_name(newState),
+ GST_DEBUG("Changing state change to %s from %s with %s pending", gst_element_state_get_name(newState),
gst_element_state_get_name(currentState), gst_element_state_get_name(pending));
- GstStateChangeReturn setStateResult = gst_element_set_state(m_playBin.get(), newState);
+ GstStateChangeReturn setStateResult = gst_element_set_state(m_pipeline.get(), newState);
GstState pausedOrPlaying = newState == GST_STATE_PLAYING ? GST_STATE_PAUSED : GST_STATE_PLAYING;
if (currentState != pausedOrPlaying && setStateResult == GST_STATE_CHANGE_FAILURE) {
return false;
@@ -463,13 +367,13 @@ bool MediaPlayerPrivateGStreamer::changePipelineState(GstState newState)
// if we stay for too long on READY.
// Also lets remove the timer if we request a state change for any state other than READY.
// See also https://bugs.webkit.org/show_bug.cgi?id=117354
- if (newState == GST_STATE_READY && !m_readyTimerHandler) {
- m_readyTimerHandler = g_timeout_add_seconds(gReadyStateTimerInterval, reinterpret_cast<GSourceFunc>(mediaPlayerPrivateReadyStateTimeoutCallback), this);
- g_source_set_name_by_id(m_readyTimerHandler, "[WebKit] mediaPlayerPrivateReadyStateTimeoutCallback");
- } else if (newState != GST_STATE_READY && m_readyTimerHandler) {
- g_source_remove(m_readyTimerHandler);
- m_readyTimerHandler = 0;
- }
+ if (newState == GST_STATE_READY && !m_readyTimerHandler.isActive()) {
+ // Max interval in seconds to stay in the READY state on manual
+ // state change requests.
+ static const double readyStateTimerDelay = 60;
+ m_readyTimerHandler.startOneShot(readyStateTimerDelay);
+ } else if (newState != GST_STATE_READY)
+ m_readyTimerHandler.stop();
return true;
}
@@ -495,7 +399,7 @@ void MediaPlayerPrivateGStreamer::play()
m_delayingLoad = false;
m_preload = MediaPlayer::Auto;
setDownloadBuffering();
- LOG_MEDIA_MESSAGE("Play");
+ GST_DEBUG("Play");
} else {
loadingFailed(MediaPlayer::Empty);
}
@@ -505,57 +409,56 @@ void MediaPlayerPrivateGStreamer::pause()
{
m_playbackRatePause = false;
GstState currentState, pendingState;
- gst_element_get_state(m_playBin.get(), &currentState, &pendingState, 0);
+ gst_element_get_state(m_pipeline.get(), &currentState, &pendingState, 0);
if (currentState < GST_STATE_PAUSED && pendingState <= GST_STATE_PAUSED)
return;
if (changePipelineState(GST_STATE_PAUSED))
- INFO_MEDIA_MESSAGE("Pause");
+ GST_INFO("Pause");
else
loadingFailed(MediaPlayer::Empty);
}
-float MediaPlayerPrivateGStreamer::duration() const
+MediaTime MediaPlayerPrivateGStreamer::durationMediaTime() const
{
- if (!m_playBin)
- return 0.0f;
+ if (!m_pipeline)
+ return { };
if (m_errorOccured)
- return 0.0f;
+ return { };
- // Media duration query failed already, don't attempt new useless queries.
- if (!m_mediaDurationKnown)
- return numeric_limits<float>::infinity();
+ if (m_durationAtEOS)
+ return MediaTime::createWithDouble(m_durationAtEOS);
- if (m_mediaDuration)
- return m_mediaDuration;
+ // The duration query would fail on a not-prerolled pipeline.
+ if (GST_STATE(m_pipeline.get()) < GST_STATE_PAUSED)
+ return { };
GstFormat timeFormat = GST_FORMAT_TIME;
gint64 timeLength = 0;
- bool failure = !gst_element_query_duration(m_playBin.get(), timeFormat, &timeLength) || static_cast<guint64>(timeLength) == GST_CLOCK_TIME_NONE;
+ bool failure = !gst_element_query_duration(m_pipeline.get(), timeFormat, &timeLength) || static_cast<guint64>(timeLength) == GST_CLOCK_TIME_NONE;
if (failure) {
- LOG_MEDIA_MESSAGE("Time duration query failed for %s", m_url.string().utf8().data());
- return numeric_limits<float>::infinity();
+ GST_DEBUG("Time duration query failed for %s", m_url.string().utf8().data());
+ return MediaTime::positiveInfiniteTime();
}
- LOG_MEDIA_MESSAGE("Duration: %" GST_TIME_FORMAT, GST_TIME_ARGS(timeLength));
+ GST_DEBUG("Duration: %" GST_TIME_FORMAT, GST_TIME_ARGS(timeLength));
- m_mediaDuration = static_cast<double>(timeLength) / GST_SECOND;
- return m_mediaDuration;
+ return MediaTime::createWithDouble(static_cast<double>(timeLength) / GST_SECOND);
// FIXME: handle 3.14.9.5 properly
}
-float MediaPlayerPrivateGStreamer::currentTime() const
+MediaTime MediaPlayerPrivateGStreamer::currentMediaTime() const
{
- if (!m_playBin)
- return 0.0f;
+ if (!m_pipeline)
+ return { };
if (m_errorOccured)
- return 0.0f;
+ return { };
if (m_seeking)
- return m_seekTime;
+ return MediaTime::createWithFloat(m_seekTime);
// Workaround for
// https://bugzilla.gnome.org/show_bug.cgi?id=639941 In GStreamer
@@ -563,30 +466,30 @@ float MediaPlayerPrivateGStreamer::currentTime() const
// negative playback rate. There's no upstream accepted patch for
// this bug yet, hence this temporary workaround.
if (m_isEndReached && m_playbackRate < 0)
- return 0.0f;
+ return { };
- return playbackPosition();
+ return MediaTime::createWithDouble(playbackPosition());
}
void MediaPlayerPrivateGStreamer::seek(float time)
{
- if (!m_playBin)
+ if (!m_pipeline)
return;
if (m_errorOccured)
return;
- INFO_MEDIA_MESSAGE("[Seek] seek attempt to %f secs", time);
+ GST_INFO("[Seek] seek attempt to %f secs", time);
// Avoid useless seeking.
- if (time == currentTime())
+ if (MediaTime::createWithFloat(time) == currentMediaTime())
return;
if (isLiveStream())
return;
GstClockTime clockTime = toGstClockTime(time);
- INFO_MEDIA_MESSAGE("[Seek] seeking to %" GST_TIME_FORMAT " (%f)", GST_TIME_ARGS(clockTime), time);
+ GST_INFO("[Seek] seeking to %" GST_TIME_FORMAT " (%f)", GST_TIME_ARGS(clockTime), time);
if (m_seeking) {
m_timeOfOverlappingSeek = time;
@@ -597,15 +500,15 @@ void MediaPlayerPrivateGStreamer::seek(float time)
}
GstState state;
- GstStateChangeReturn getStateResult = gst_element_get_state(m_playBin.get(), &state, 0, 0);
+ GstStateChangeReturn getStateResult = gst_element_get_state(m_pipeline.get(), &state, nullptr, 0);
if (getStateResult == GST_STATE_CHANGE_FAILURE || getStateResult == GST_STATE_CHANGE_NO_PREROLL) {
- LOG_MEDIA_MESSAGE("[Seek] cannot seek, current state change is %s", gst_element_state_change_return_get_name(getStateResult));
+ GST_DEBUG("[Seek] cannot seek, current state change is %s", gst_element_state_change_return_get_name(getStateResult));
return;
}
if (getStateResult == GST_STATE_CHANGE_ASYNC || state < GST_STATE_PAUSED || m_isEndReached) {
m_seekIsPending = true;
if (m_isEndReached) {
- LOG_MEDIA_MESSAGE("[Seek] reset pipeline");
+ GST_DEBUG("[Seek] reset pipeline");
m_resetPipeline = true;
if (!changePipelineState(GST_STATE_PAUSED))
loadingFailed(MediaPlayer::Empty);
@@ -613,7 +516,7 @@ void MediaPlayerPrivateGStreamer::seek(float time)
} else {
// We can seek now.
if (!doSeek(clockTime, m_player->rate(), static_cast<GstSeekFlags>(GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_ACCURATE))) {
- LOG_MEDIA_MESSAGE("[Seek] seeking to %f failed", time);
+ GST_DEBUG("[Seek] seeking to %f failed", time);
return;
}
}
@@ -627,6 +530,11 @@ bool MediaPlayerPrivateGStreamer::doSeek(gint64 position, float rate, GstSeekFla
{
gint64 startTime, endTime;
+ // TODO: Should do more than that, need to notify the media source
+ // and probably flush the pipeline at least.
+ if (isMediaSource())
+ return true;
+
if (rate > 0) {
startTime = position;
endTime = GST_CLOCK_TIME_NONE;
@@ -635,7 +543,7 @@ bool MediaPlayerPrivateGStreamer::doSeek(gint64 position, float rate, GstSeekFla
// If we are at beginning of media, start from the end to
// avoid immediate EOS.
if (position < 0)
- endTime = static_cast<gint64>(duration() * GST_SECOND);
+ endTime = static_cast<gint64>(durationMediaTime().toDouble() * GST_SECOND);
else
endTime = position;
}
@@ -643,7 +551,7 @@ bool MediaPlayerPrivateGStreamer::doSeek(gint64 position, float rate, GstSeekFla
if (!rate)
rate = 1.0;
- return gst_element_seek(m_playBin.get(), rate, GST_FORMAT_TIME, seekType,
+ return gst_element_seek(m_pipeline.get(), rate, GST_FORMAT_TIME, seekType,
GST_SEEK_TYPE_SET, startTime, GST_SEEK_TYPE_SET, endTime);
}
@@ -655,7 +563,7 @@ void MediaPlayerPrivateGStreamer::updatePlaybackRate()
float currentPosition = static_cast<float>(playbackPosition() * GST_SECOND);
bool mute = false;
- INFO_MEDIA_MESSAGE("Set Rate to %f", m_playbackRate);
+ GST_INFO("Set Rate to %f", m_playbackRate);
if (m_playbackRate > 0) {
// Mute the sound if the playback rate is too extreme and
@@ -667,20 +575,20 @@ void MediaPlayerPrivateGStreamer::updatePlaybackRate()
mute = true;
}
- INFO_MEDIA_MESSAGE("Need to mute audio?: %d", (int) mute);
+ GST_INFO("Need to mute audio?: %d", (int) mute);
if (doSeek(currentPosition, m_playbackRate, static_cast<GstSeekFlags>(GST_SEEK_FLAG_FLUSH))) {
- g_object_set(m_playBin.get(), "mute", mute, NULL);
+ g_object_set(m_pipeline.get(), "mute", mute, nullptr);
m_lastPlaybackRate = m_playbackRate;
} else {
m_playbackRate = m_lastPlaybackRate;
- ERROR_MEDIA_MESSAGE("Set rate to %f failed", m_playbackRate);
+ GST_ERROR("Set rate to %f failed", m_playbackRate);
}
if (m_playbackRatePause) {
GstState state;
GstState pending;
- gst_element_get_state(m_playBin.get(), &state, &pending, 0);
+ gst_element_get_state(m_pipeline.get(), &state, &pending, 0);
if (state != GST_STATE_PLAYING && pending != GST_STATE_PLAYING)
changePipelineState(GST_STATE_PLAYING);
m_playbackRatePause = false;
@@ -693,7 +601,7 @@ void MediaPlayerPrivateGStreamer::updatePlaybackRate()
bool MediaPlayerPrivateGStreamer::paused() const
{
if (m_isEndReached) {
- LOG_MEDIA_MESSAGE("Ignoring pause at EOS");
+ GST_DEBUG("Ignoring pause at EOS");
return true;
}
@@ -701,8 +609,8 @@ bool MediaPlayerPrivateGStreamer::paused() const
return false;
GstState state;
- gst_element_get_state(m_playBin.get(), &state, 0, 0);
- return state == GST_STATE_PAUSED;
+ gst_element_get_state(m_pipeline.get(), &state, nullptr, 0);
+ return state <= GST_STATE_PAUSED;
}
bool MediaPlayerPrivateGStreamer::seeking() const
@@ -710,34 +618,35 @@ bool MediaPlayerPrivateGStreamer::seeking() const
return m_seeking;
}
-void MediaPlayerPrivateGStreamer::videoChanged()
-{
- if (m_videoTimerHandler)
- g_source_remove(m_videoTimerHandler);
- m_videoTimerHandler = g_idle_add_full(G_PRIORITY_DEFAULT, reinterpret_cast<GSourceFunc>(mediaPlayerPrivateVideoChangeTimeoutCallback), this, 0);
-}
-
-void MediaPlayerPrivateGStreamer::videoCapsChanged()
+void MediaPlayerPrivateGStreamer::videoChangedCallback(MediaPlayerPrivateGStreamer* player)
{
- if (m_videoCapsTimerHandler)
- g_source_remove(m_videoCapsTimerHandler);
- m_videoCapsTimerHandler = g_timeout_add(0, reinterpret_cast<GSourceFunc>(mediaPlayerPrivateVideoCapsChangeTimeoutCallback), this);
+ player->m_notifier->notify(MainThreadNotification::VideoChanged, [player] { player->notifyPlayerOfVideo(); });
}
void MediaPlayerPrivateGStreamer::notifyPlayerOfVideo()
{
- m_videoTimerHandler = 0;
+ if (UNLIKELY(!m_pipeline || !m_source))
+ return;
gint numTracks = 0;
- if (m_playBin)
- g_object_get(m_playBin.get(), "n-video", &numTracks, NULL);
+ bool useMediaSource = isMediaSource();
+ GstElement* element = useMediaSource ? m_source.get() : m_pipeline.get();
+ g_object_get(element, "n-video", &numTracks, nullptr);
m_hasVideo = numTracks > 0;
+ if (m_hasVideo)
+ m_player->sizeChanged();
+
+ if (useMediaSource) {
+ GST_DEBUG("Tracks managed by source element. Bailing out now.");
+ m_player->client().mediaPlayerEngineUpdated(m_player);
+ return;
+ }
#if ENABLE(VIDEO_TRACK)
for (gint i = 0; i < numTracks; ++i) {
GRefPtr<GstPad> pad;
- g_signal_emit_by_name(m_playBin.get(), "get-video-pad", i, &pad.outPtr(), NULL);
+ g_signal_emit_by_name(m_pipeline.get(), "get-video-pad", i, &pad.outPtr(), nullptr);
ASSERT(pad);
if (i < static_cast<gint>(m_videoTracks.size())) {
@@ -747,50 +656,60 @@ void MediaPlayerPrivateGStreamer::notifyPlayerOfVideo()
continue;
}
- RefPtr<VideoTrackPrivateGStreamer> track = VideoTrackPrivateGStreamer::create(m_playBin, i, pad);
+ RefPtr<VideoTrackPrivateGStreamer> track = VideoTrackPrivateGStreamer::create(m_pipeline, i, pad);
m_videoTracks.append(track);
- m_player->addVideoTrack(track.release());
+ m_player->addVideoTrack(*track);
}
while (static_cast<gint>(m_videoTracks.size()) > numTracks) {
RefPtr<VideoTrackPrivateGStreamer> track = m_videoTracks.last();
track->disconnect();
m_videoTracks.removeLast();
- m_player->removeVideoTrack(track.release());
+ m_player->removeVideoTrack(*track);
}
#endif
- m_player->mediaPlayerClient()->mediaPlayerEngineUpdated(m_player);
+ m_player->client().mediaPlayerEngineUpdated(m_player);
+}
+
+void MediaPlayerPrivateGStreamer::videoSinkCapsChangedCallback(MediaPlayerPrivateGStreamer* player)
+{
+ player->m_notifier->notify(MainThreadNotification::VideoCapsChanged, [player] { player->notifyPlayerOfVideoCaps(); });
}
void MediaPlayerPrivateGStreamer::notifyPlayerOfVideoCaps()
{
- m_videoCapsTimerHandler = 0;
m_videoSize = IntSize();
- m_player->mediaPlayerClient()->mediaPlayerEngineUpdated(m_player);
+ m_player->client().mediaPlayerEngineUpdated(m_player);
}
-void MediaPlayerPrivateGStreamer::audioChanged()
+void MediaPlayerPrivateGStreamer::audioChangedCallback(MediaPlayerPrivateGStreamer* player)
{
- if (m_audioTimerHandler)
- g_source_remove(m_audioTimerHandler);
- m_audioTimerHandler = g_idle_add_full(G_PRIORITY_DEFAULT, reinterpret_cast<GSourceFunc>(mediaPlayerPrivateAudioChangeTimeoutCallback), this, 0);
+ player->m_notifier->notify(MainThreadNotification::AudioChanged, [player] { player->notifyPlayerOfAudio(); });
}
void MediaPlayerPrivateGStreamer::notifyPlayerOfAudio()
{
- m_audioTimerHandler = 0;
+ if (UNLIKELY(!m_pipeline || !m_source))
+ return;
gint numTracks = 0;
- if (m_playBin)
- g_object_get(m_playBin.get(), "n-audio", &numTracks, NULL);
+ bool useMediaSource = isMediaSource();
+ GstElement* element = useMediaSource ? m_source.get() : m_pipeline.get();
+ g_object_get(element, "n-audio", &numTracks, nullptr);
m_hasAudio = numTracks > 0;
+ if (useMediaSource) {
+ GST_DEBUG("Tracks managed by source element. Bailing out now.");
+ m_player->client().mediaPlayerEngineUpdated(m_player);
+ return;
+ }
+
#if ENABLE(VIDEO_TRACK)
for (gint i = 0; i < numTracks; ++i) {
GRefPtr<GstPad> pad;
- g_signal_emit_by_name(m_playBin.get(), "get-audio-pad", i, &pad.outPtr(), NULL);
+ g_signal_emit_by_name(m_pipeline.get(), "get-audio-pad", i, &pad.outPtr(), nullptr);
ASSERT(pad);
if (i < static_cast<gint>(m_audioTracks.size())) {
@@ -800,41 +719,46 @@ void MediaPlayerPrivateGStreamer::notifyPlayerOfAudio()
continue;
}
- RefPtr<AudioTrackPrivateGStreamer> track = AudioTrackPrivateGStreamer::create(m_playBin, i, pad);
+ RefPtr<AudioTrackPrivateGStreamer> track = AudioTrackPrivateGStreamer::create(m_pipeline, i, pad);
m_audioTracks.insert(i, track);
- m_player->addAudioTrack(track.release());
+ m_player->addAudioTrack(*track);
}
while (static_cast<gint>(m_audioTracks.size()) > numTracks) {
RefPtr<AudioTrackPrivateGStreamer> track = m_audioTracks.last();
track->disconnect();
m_audioTracks.removeLast();
- m_player->removeAudioTrack(track.release());
+ m_player->removeAudioTrack(*track);
}
#endif
- m_player->mediaPlayerClient()->mediaPlayerEngineUpdated(m_player);
+ m_player->client().mediaPlayerEngineUpdated(m_player);
}
#if ENABLE(VIDEO_TRACK)
-void MediaPlayerPrivateGStreamer::textChanged()
+void MediaPlayerPrivateGStreamer::textChangedCallback(MediaPlayerPrivateGStreamer* player)
{
- if (m_textTimerHandler)
- g_source_remove(m_textTimerHandler);
- m_textTimerHandler = g_timeout_add(0, reinterpret_cast<GSourceFunc>(mediaPlayerPrivateTextChangeTimeoutCallback), this);
+ player->m_notifier->notify(MainThreadNotification::TextChanged, [player] { player->notifyPlayerOfText(); });
}
void MediaPlayerPrivateGStreamer::notifyPlayerOfText()
{
- m_textTimerHandler = 0;
+ if (UNLIKELY(!m_pipeline || !m_source))
+ return;
gint numTracks = 0;
- if (m_playBin)
- g_object_get(m_playBin.get(), "n-text", &numTracks, NULL);
+ bool useMediaSource = isMediaSource();
+ GstElement* element = useMediaSource ? m_source.get() : m_pipeline.get();
+ g_object_get(element, "n-text", &numTracks, nullptr);
+
+ if (useMediaSource) {
+ GST_DEBUG("Tracks managed by source element. Bailing out now.");
+ return;
+ }
for (gint i = 0; i < numTracks; ++i) {
GRefPtr<GstPad> pad;
- g_signal_emit_by_name(m_playBin.get(), "get-text-pad", i, &pad.outPtr(), NULL);
+ g_signal_emit_by_name(m_pipeline.get(), "get-text-pad", i, &pad.outPtr(), nullptr);
ASSERT(pad);
if (i < static_cast<gint>(m_textTracks.size())) {
@@ -846,17 +770,23 @@ void MediaPlayerPrivateGStreamer::notifyPlayerOfText()
RefPtr<InbandTextTrackPrivateGStreamer> track = InbandTextTrackPrivateGStreamer::create(i, pad);
m_textTracks.insert(i, track);
- m_player->addTextTrack(track.release());
+ m_player->addTextTrack(*track);
}
while (static_cast<gint>(m_textTracks.size()) > numTracks) {
RefPtr<InbandTextTrackPrivateGStreamer> track = m_textTracks.last();
track->disconnect();
m_textTracks.removeLast();
- m_player->removeTextTrack(track.release());
+ m_player->removeTextTrack(*track);
}
}
+GstFlowReturn MediaPlayerPrivateGStreamer::newTextSampleCallback(MediaPlayerPrivateGStreamer* player)
+{
+ player->newTextSample();
+ return GST_FLOW_OK;
+}
+
void MediaPlayerPrivateGStreamer::newTextSample()
{
if (!m_textAppSink)
@@ -866,7 +796,7 @@ void MediaPlayerPrivateGStreamer::newTextSample()
gst_pad_get_sticky_event(m_textAppSinkPad.get(), GST_EVENT_STREAM_START, 0));
GRefPtr<GstSample> sample;
- g_signal_emit_by_name(m_textAppSink.get(), "pull-sample", &sample.outPtr(), NULL);
+ g_signal_emit_by_name(m_textAppSink.get(), "pull-sample", &sample.outPtr(), nullptr);
ASSERT(sample);
if (streamStartEvent) {
@@ -882,9 +812,9 @@ void MediaPlayerPrivateGStreamer::newTextSample()
}
}
if (!found)
- WARN_MEDIA_MESSAGE("Got sample with unknown stream ID.");
+ GST_WARNING("Got sample with unknown stream ID.");
} else
- WARN_MEDIA_MESSAGE("Unable to handle sample with no stream start event.");
+ GST_WARNING("Unable to handle sample with no stream start event.");
}
#endif
@@ -915,7 +845,7 @@ void MediaPlayerPrivateGStreamer::setRate(float rate)
m_playbackRate = rate;
m_changingRate = true;
- gst_element_get_state(m_playBin.get(), &state, &pending, 0);
+ gst_element_get_state(m_pipeline.get(), &state, &pending, 0);
if (!rate) {
m_changingRate = false;
@@ -932,52 +862,53 @@ void MediaPlayerPrivateGStreamer::setRate(float rate)
updatePlaybackRate();
}
+double MediaPlayerPrivateGStreamer::rate() const
+{
+ return m_playbackRate;
+}
+
void MediaPlayerPrivateGStreamer::setPreservesPitch(bool preservesPitch)
{
m_preservesPitch = preservesPitch;
}
-PassRefPtr<TimeRanges> MediaPlayerPrivateGStreamer::buffered() const
+std::unique_ptr<PlatformTimeRanges> MediaPlayerPrivateGStreamer::buffered() const
{
- RefPtr<TimeRanges> timeRanges = TimeRanges::create();
+ auto timeRanges = std::make_unique<PlatformTimeRanges>();
if (m_errorOccured || isLiveStream())
- return timeRanges.release();
+ return timeRanges;
-#if GST_CHECK_VERSION(0, 10, 31)
- float mediaDuration(duration());
+ float mediaDuration(durationMediaTime().toDouble());
if (!mediaDuration || std::isinf(mediaDuration))
- return timeRanges.release();
+ return timeRanges;
GstQuery* query = gst_query_new_buffering(GST_FORMAT_PERCENT);
- if (!gst_element_query(m_playBin.get(), query)) {
+ if (!gst_element_query(m_pipeline.get(), query)) {
gst_query_unref(query);
- return timeRanges.release();
+ return timeRanges;
}
- for (guint index = 0; index < gst_query_get_n_buffering_ranges(query); index++) {
+ guint numBufferingRanges = gst_query_get_n_buffering_ranges(query);
+ for (guint index = 0; index < numBufferingRanges; index++) {
gint64 rangeStart = 0, rangeStop = 0;
if (gst_query_parse_nth_buffering_range(query, index, &rangeStart, &rangeStop))
- timeRanges->add(static_cast<float>((rangeStart * mediaDuration) / GST_FORMAT_PERCENT_MAX),
- static_cast<float>((rangeStop * mediaDuration) / GST_FORMAT_PERCENT_MAX));
+ timeRanges->add(MediaTime::createWithDouble((rangeStart * mediaDuration) / GST_FORMAT_PERCENT_MAX),
+ MediaTime::createWithDouble((rangeStop * mediaDuration) / GST_FORMAT_PERCENT_MAX));
}
// Fallback to the more general maxTimeLoaded() if no range has
// been found.
if (!timeRanges->length())
if (float loaded = maxTimeLoaded())
- timeRanges->add(0, loaded);
+ timeRanges->add(MediaTime::zeroTime(), MediaTime::createWithDouble(loaded));
gst_query_unref(query);
-#else
- float loaded = maxTimeLoaded();
- if (!m_errorOccured && !isLiveStream() && loaded > 0)
- timeRanges->add(0, loaded);
-#endif
- return timeRanges.release();
+
+ return timeRanges;
}
-gboolean MediaPlayerPrivateGStreamer::handleMessage(GstMessage* message)
+void MediaPlayerPrivateGStreamer::handleMessage(GstMessage* message)
{
GUniqueOutPtr<GError> err;
GUniqueOutPtr<gchar> debug;
@@ -987,7 +918,7 @@ gboolean MediaPlayerPrivateGStreamer::handleMessage(GstMessage* message)
const GstStructure* structure = gst_message_get_structure(message);
GstState requestedState, currentState;
- m_canFallBackToLastFinishedSeekPositon = false;
+ m_canFallBackToLastFinishedSeekPosition = false;
if (structure) {
const gchar* messageTypeName = gst_structure_get_name(structure);
@@ -996,40 +927,37 @@ gboolean MediaPlayerPrivateGStreamer::handleMessage(GstMessage* message)
// notify of the new location(s) of the media.
if (!g_strcmp0(messageTypeName, "redirect")) {
mediaLocationChanged(message);
- return TRUE;
+ return;
}
}
// We ignore state changes from internal elements. They are forwarded to playbin2 anyway.
- bool messageSourceIsPlaybin = GST_MESSAGE_SRC(message) == reinterpret_cast<GstObject*>(m_playBin.get());
+ bool messageSourceIsPlaybin = GST_MESSAGE_SRC(message) == reinterpret_cast<GstObject*>(m_pipeline.get());
- LOG_MEDIA_MESSAGE("Message %s received from element %s", GST_MESSAGE_TYPE_NAME(message), GST_MESSAGE_SRC_NAME(message));
+ GST_DEBUG("Message %s received from element %s", GST_MESSAGE_TYPE_NAME(message), GST_MESSAGE_SRC_NAME(message));
switch (GST_MESSAGE_TYPE(message)) {
case GST_MESSAGE_ERROR:
- if (m_resetPipeline)
- break;
- if (m_missingPlugins)
+ if (m_resetPipeline || m_missingPluginsCallback || m_errorOccured)
break;
gst_message_parse_error(message, &err.outPtr(), &debug.outPtr());
- ERROR_MEDIA_MESSAGE("Error %d: %s (url=%s)", err->code, err->message, m_url.string().utf8().data());
+ GST_ERROR("Error %d: %s (url=%s)", err->code, err->message, m_url.string().utf8().data());
- GST_DEBUG_BIN_TO_DOT_FILE_WITH_TS(GST_BIN(m_playBin.get()), GST_DEBUG_GRAPH_SHOW_ALL, "webkit-video.error");
+ GST_DEBUG_BIN_TO_DOT_FILE_WITH_TS(GST_BIN(m_pipeline.get()), GST_DEBUG_GRAPH_SHOW_ALL, "webkit-video.error");
error = MediaPlayer::Empty;
- if (err->code == GST_STREAM_ERROR_CODEC_NOT_FOUND
- || err->code == GST_STREAM_ERROR_WRONG_TYPE
- || err->code == GST_STREAM_ERROR_FAILED
- || err->code == GST_CORE_ERROR_MISSING_PLUGIN
- || err->code == GST_RESOURCE_ERROR_NOT_FOUND)
+ if (g_error_matches(err.get(), GST_STREAM_ERROR, GST_STREAM_ERROR_CODEC_NOT_FOUND)
+ || g_error_matches(err.get(), GST_STREAM_ERROR, GST_STREAM_ERROR_WRONG_TYPE)
+ || g_error_matches(err.get(), GST_STREAM_ERROR, GST_STREAM_ERROR_FAILED)
+ || g_error_matches(err.get(), GST_CORE_ERROR, GST_CORE_ERROR_MISSING_PLUGIN)
+ || g_error_matches(err.get(), GST_RESOURCE_ERROR, GST_RESOURCE_ERROR_NOT_FOUND))
error = MediaPlayer::FormatError;
- else if (err->domain == GST_STREAM_ERROR) {
+ else if (g_error_matches(err.get(), GST_STREAM_ERROR, GST_STREAM_ERROR_TYPE_NOT_FOUND)) {
// Let the mediaPlayerClient handle the stream error, in
// this case the HTMLMediaElement will emit a stalled
// event.
- if (err->code == GST_STREAM_ERROR_TYPE_NOT_FOUND) {
- ERROR_MEDIA_MESSAGE("Decode error, let the Media element emit a stalled event.");
- break;
- }
+ GST_ERROR("Decode error, let the Media element emit a stalled event.");
+ break;
+ } else if (err->domain == GST_STREAM_ERROR) {
error = MediaPlayer::DecodeError;
attemptNextLocation = true;
} else if (err->domain == GST_RESOURCE_ERROR)
@@ -1055,9 +983,9 @@ gboolean MediaPlayerPrivateGStreamer::handleMessage(GstMessage* message)
// Construct a filename for the graphviz dot file output.
GstState newState;
- gst_message_parse_state_changed(message, &currentState, &newState, 0);
+ gst_message_parse_state_changed(message, &currentState, &newState, nullptr);
CString dotFileName = String::format("webkit-video.%s_%s", gst_element_state_get_name(currentState), gst_element_state_get_name(newState)).utf8();
- GST_DEBUG_BIN_TO_DOT_FILE_WITH_TS(GST_BIN(m_playBin.get()), GST_DEBUG_GRAPH_SHOW_ALL, dotFileName.data());
+ GST_DEBUG_BIN_TO_DOT_FILE_WITH_TS(GST_BIN(m_pipeline.get()), GST_DEBUG_GRAPH_SHOW_ALL, dotFileName.data());
break;
}
@@ -1065,71 +993,176 @@ gboolean MediaPlayerPrivateGStreamer::handleMessage(GstMessage* message)
processBufferingStats(message);
break;
case GST_MESSAGE_DURATION_CHANGED:
- if (messageSourceIsPlaybin)
+ // Duration in MSE is managed by MediaSource, SourceBuffer and AppendPipeline.
+ if (messageSourceIsPlaybin && !isMediaSource())
durationChanged();
break;
case GST_MESSAGE_REQUEST_STATE:
gst_message_parse_request_state(message, &requestedState);
- gst_element_get_state(m_playBin.get(), &currentState, NULL, 250);
+ gst_element_get_state(m_pipeline.get(), &currentState, nullptr, 250 * GST_NSECOND);
if (requestedState < currentState) {
GUniquePtr<gchar> elementName(gst_element_get_name(GST_ELEMENT(message)));
- INFO_MEDIA_MESSAGE("Element %s requested state change to %s", elementName.get(),
+ GST_INFO("Element %s requested state change to %s", elementName.get(),
gst_element_state_get_name(requestedState));
m_requestedState = requestedState;
if (!changePipelineState(requestedState))
loadingFailed(MediaPlayer::Empty);
}
break;
+ case GST_MESSAGE_CLOCK_LOST:
+ // This can only happen in PLAYING state and we should just
+ // get a new clock by moving back to PAUSED and then to
+ // PLAYING again.
+ // This can happen if the stream that ends in a sink that
+ // provides the current clock disappears, for example if
+ // the audio sink provides the clock and the audio stream
+ // is disabled. It also happens relatively often with
+ // HTTP adaptive streams when switching between different
+ // variants of a stream.
+ gst_element_set_state(m_pipeline.get(), GST_STATE_PAUSED);
+ gst_element_set_state(m_pipeline.get(), GST_STATE_PLAYING);
+ break;
+ case GST_MESSAGE_LATENCY:
+ // Recalculate the latency, we don't need any special handling
+ // here other than the GStreamer default.
+ // This can happen if the latency of live elements changes, or
+ // for one reason or another a new live element is added or
+ // removed from the pipeline.
+ gst_bin_recalculate_latency(GST_BIN(m_pipeline.get()));
+ break;
case GST_MESSAGE_ELEMENT:
if (gst_is_missing_plugin_message(message)) {
- gchar* detail = gst_missing_plugin_message_get_installer_detail(message);
- gchar* detailArray[2] = {detail, 0};
- GstInstallPluginsReturn result = gst_install_plugins_async(detailArray, 0, mediaPlayerPrivatePluginInstallerResultFunction, this);
- m_missingPlugins = result == GST_INSTALL_PLUGINS_STARTED_OK;
- g_free(detail);
+ if (gst_install_plugins_supported()) {
+ m_missingPluginsCallback = MediaPlayerRequestInstallMissingPluginsCallback::create([this](uint32_t result) {
+ m_missingPluginsCallback = nullptr;
+ if (result != GST_INSTALL_PLUGINS_SUCCESS)
+ return;
+
+ changePipelineState(GST_STATE_READY);
+ changePipelineState(GST_STATE_PAUSED);
+ });
+ GUniquePtr<char> detail(gst_missing_plugin_message_get_installer_detail(message));
+ GUniquePtr<char> description(gst_missing_plugin_message_get_description(message));
+ m_player->client().requestInstallMissingPlugins(String::fromUTF8(detail.get()), String::fromUTF8(description.get()), *m_missingPluginsCallback);
+ }
+ }
+#if ENABLE(LEGACY_ENCRYPTED_MEDIA)
+ else if (gst_structure_has_name(structure, "drm-key-needed")) {
+ GST_DEBUG("drm-key-needed message from %s", GST_MESSAGE_SRC_NAME(message));
+ GRefPtr<GstEvent> event;
+ gst_structure_get(structure, "event", GST_TYPE_EVENT, &event.outPtr(), nullptr);
+ handleProtectionEvent(event.get());
+ }
+#endif
+#if ENABLE(VIDEO_TRACK) && USE(GSTREAMER_MPEGTS)
+ else {
+ GstMpegtsSection* section = gst_message_parse_mpegts_section(message);
+ if (section) {
+ processMpegTsSection(section);
+ gst_mpegts_section_unref(section);
+ }
}
+#endif
break;
#if ENABLE(VIDEO_TRACK)
case GST_MESSAGE_TOC:
processTableOfContents(message);
break;
#endif
+ case GST_MESSAGE_TAG: {
+ GstTagList* tags = nullptr;
+ GUniqueOutPtr<gchar> tag;
+ gst_message_parse_tag(message, &tags);
+ if (gst_tag_list_get_string(tags, GST_TAG_IMAGE_ORIENTATION, &tag.outPtr())) {
+ if (!g_strcmp0(tag.get(), "rotate-90"))
+ setVideoSourceOrientation(ImageOrientation(OriginRightTop));
+ else if (!g_strcmp0(tag.get(), "rotate-180"))
+ setVideoSourceOrientation(ImageOrientation(OriginBottomRight));
+ else if (!g_strcmp0(tag.get(), "rotate-270"))
+ setVideoSourceOrientation(ImageOrientation(OriginLeftBottom));
+ }
+ gst_tag_list_unref(tags);
+ break;
+ }
default:
- LOG_MEDIA_MESSAGE("Unhandled GStreamer message type: %s",
+ GST_DEBUG("Unhandled GStreamer message type: %s",
GST_MESSAGE_TYPE_NAME(message));
break;
}
- return TRUE;
-}
-
-void MediaPlayerPrivateGStreamer::handlePluginInstallerResult(GstInstallPluginsReturn result)
-{
- m_missingPlugins = false;
- if (result == GST_INSTALL_PLUGINS_SUCCESS) {
- changePipelineState(GST_STATE_READY);
- changePipelineState(GST_STATE_PAUSED);
- }
+ return;
}
void MediaPlayerPrivateGStreamer::processBufferingStats(GstMessage* message)
{
m_buffering = true;
- const GstStructure *structure = gst_message_get_structure(message);
- gst_structure_get_int(structure, "buffer-percent", &m_bufferingPercentage);
+ gst_message_parse_buffering(message, &m_bufferingPercentage);
- LOG_MEDIA_MESSAGE("[Buffering] Buffering: %d%%.", m_bufferingPercentage);
+ GST_DEBUG("[Buffering] Buffering: %d%%.", m_bufferingPercentage);
updateStates();
}
+#if ENABLE(VIDEO_TRACK) && USE(GSTREAMER_MPEGTS)
+void MediaPlayerPrivateGStreamer::processMpegTsSection(GstMpegtsSection* section)
+{
+ ASSERT(section);
+
+ if (section->section_type == GST_MPEGTS_SECTION_PMT) {
+ const GstMpegtsPMT* pmt = gst_mpegts_section_get_pmt(section);
+ m_metadataTracks.clear();
+ for (guint i = 0; i < pmt->streams->len; ++i) {
+ const GstMpegtsPMTStream* stream = static_cast<const GstMpegtsPMTStream*>(g_ptr_array_index(pmt->streams, i));
+ if (stream->stream_type == 0x05 || stream->stream_type >= 0x80) {
+ AtomicString pid = String::number(stream->pid);
+ RefPtr<InbandMetadataTextTrackPrivateGStreamer> track = InbandMetadataTextTrackPrivateGStreamer::create(
+ InbandTextTrackPrivate::Metadata, InbandTextTrackPrivate::Data, pid);
+
+ // 4.7.10.12.2 Sourcing in-band text tracks
+ // If the new text track's kind is metadata, then set the text track in-band metadata track dispatch
+ // type as follows, based on the type of the media resource:
+ // Let stream type be the value of the "stream_type" field describing the text track's type in the
+ // file's program map section, interpreted as an 8-bit unsigned integer. Let length be the value of
+ // the "ES_info_length" field for the track in the same part of the program map section, interpreted
+ // as an integer as defined by the MPEG-2 specification. Let descriptor bytes be the length bytes
+ // following the "ES_info_length" field. The text track in-band metadata track dispatch type must be
+ // set to the concatenation of the stream type byte and the zero or more descriptor bytes bytes,
+ // expressed in hexadecimal using uppercase ASCII hex digits.
+ String inbandMetadataTrackDispatchType;
+ appendUnsignedAsHexFixedSize(stream->stream_type, inbandMetadataTrackDispatchType, 2);
+ for (guint j = 0; j < stream->descriptors->len; ++j) {
+ const GstMpegtsDescriptor* descriptor = static_cast<const GstMpegtsDescriptor*>(g_ptr_array_index(stream->descriptors, j));
+ for (guint k = 0; k < descriptor->length; ++k)
+ appendByteAsHex(descriptor->data[k], inbandMetadataTrackDispatchType);
+ }
+ track->setInBandMetadataTrackDispatchType(inbandMetadataTrackDispatchType);
+
+ m_metadataTracks.add(pid, track);
+ m_player->addTextTrack(*track);
+ }
+ }
+ } else {
+ AtomicString pid = String::number(section->pid);
+ RefPtr<InbandMetadataTextTrackPrivateGStreamer> track = m_metadataTracks.get(pid);
+ if (!track)
+ return;
+
+ GRefPtr<GBytes> data = gst_mpegts_section_get_data(section);
+ gsize size;
+ const void* bytes = g_bytes_get_data(data.get(), &size);
+
+ track->addDataCue(currentMediaTime(), currentMediaTime(), bytes, size);
+ }
+}
+#endif
+
#if ENABLE(VIDEO_TRACK)
void MediaPlayerPrivateGStreamer::processTableOfContents(GstMessage* message)
{
if (m_chaptersTrack)
- m_player->removeTextTrack(m_chaptersTrack);
+ m_player->removeTextTrack(*m_chaptersTrack);
- m_chaptersTrack = InbandMetadataTextTrackPrivateGStreamer::create(InbandTextTrackPrivate::Chapters);
- m_player->addTextTrack(m_chaptersTrack);
+ m_chaptersTrack = InbandMetadataTextTrackPrivateGStreamer::create(InbandTextTrackPrivate::Chapters, InbandTextTrackPrivate::Generic);
+ m_player->addTextTrack(*m_chaptersTrack);
GRefPtr<GstToc> toc;
gboolean updated;
@@ -1137,12 +1170,11 @@ void MediaPlayerPrivateGStreamer::processTableOfContents(GstMessage* message)
ASSERT(toc);
for (GList* i = gst_toc_get_entries(toc.get()); i; i = i->next)
- processTableOfContentsEntry(static_cast<GstTocEntry*>(i->data), 0);
+ processTableOfContentsEntry(static_cast<GstTocEntry*>(i->data));
}
-void MediaPlayerPrivateGStreamer::processTableOfContentsEntry(GstTocEntry* entry, GstTocEntry* parent)
+void MediaPlayerPrivateGStreamer::processTableOfContentsEntry(GstTocEntry* entry)
{
- UNUSED_PARAM(parent);
ASSERT(entry);
RefPtr<GenericCueData> cue = GenericCueData::create();
@@ -1150,13 +1182,13 @@ void MediaPlayerPrivateGStreamer::processTableOfContentsEntry(GstTocEntry* entry
gint64 start = -1, stop = -1;
gst_toc_entry_get_start_stop_times(entry, &start, &stop);
if (start != -1)
- cue->setStartTime(static_cast<double>(start) / GST_SECOND);
+ cue->setStartTime(MediaTime(start, GST_SECOND));
if (stop != -1)
- cue->setEndTime(static_cast<double>(stop) / GST_SECOND);
+ cue->setEndTime(MediaTime(stop, GST_SECOND));
GstTagList* tags = gst_toc_entry_get_tags(entry);
if (tags) {
- gchar* title = 0;
+ gchar* title = nullptr;
gst_tag_list_get_string(tags, GST_TAG_TITLE, &title);
if (title) {
cue->setContent(title);
@@ -1164,18 +1196,18 @@ void MediaPlayerPrivateGStreamer::processTableOfContentsEntry(GstTocEntry* entry
}
}
- m_chaptersTrack->client()->addGenericCue(m_chaptersTrack.get(), cue.release());
+ m_chaptersTrack->addGenericCue(cue.release());
for (GList* i = gst_toc_entry_get_sub_entries(entry); i; i = i->next)
- processTableOfContentsEntry(static_cast<GstTocEntry*>(i->data), entry);
+ processTableOfContentsEntry(static_cast<GstTocEntry*>(i->data));
}
#endif
-void MediaPlayerPrivateGStreamer::fillTimerFired(Timer<MediaPlayerPrivateGStreamer>*)
+void MediaPlayerPrivateGStreamer::fillTimerFired()
{
GstQuery* query = gst_query_new_buffering(GST_FORMAT_PERCENT);
- if (!gst_element_query(m_playBin.get(), query)) {
+ if (!gst_element_query(m_pipeline.get(), query)) {
gst_query_unref(query);
return;
}
@@ -1183,25 +1215,24 @@ void MediaPlayerPrivateGStreamer::fillTimerFired(Timer<MediaPlayerPrivateGStream
gint64 start, stop;
gdouble fillStatus = 100.0;
- gst_query_parse_buffering_range(query, 0, &start, &stop, 0);
+ gst_query_parse_buffering_range(query, nullptr, &start, &stop, nullptr);
gst_query_unref(query);
if (stop != -1)
fillStatus = 100.0 * stop / GST_FORMAT_PERCENT_MAX;
- LOG_MEDIA_MESSAGE("[Buffering] Download buffer filled up to %f%%", fillStatus);
+ GST_DEBUG("[Buffering] Download buffer filled up to %f%%", fillStatus);
- if (!m_mediaDuration)
- durationChanged();
+ float mediaDuration = durationMediaTime().toDouble();
// Update maxTimeLoaded only if the media duration is
// available. Otherwise we can't compute it.
- if (m_mediaDuration) {
+ if (mediaDuration) {
if (fillStatus == 100.0)
- m_maxTimeLoaded = m_mediaDuration;
+ m_maxTimeLoaded = mediaDuration;
else
- m_maxTimeLoaded = static_cast<float>((fillStatus * m_mediaDuration) / 100.0);
- LOG_MEDIA_MESSAGE("[Buffering] Updated maxTimeLoaded: %f", m_maxTimeLoaded);
+ m_maxTimeLoaded = static_cast<float>((fillStatus * mediaDuration) / 100.0);
+ GST_DEBUG("[Buffering] Updated maxTimeLoaded: %f", m_maxTimeLoaded);
}
m_downloadFinished = fillStatus == 100.0;
@@ -1222,12 +1253,13 @@ float MediaPlayerPrivateGStreamer::maxTimeSeekable() const
if (m_errorOccured)
return 0.0f;
- LOG_MEDIA_MESSAGE("maxTimeSeekable");
+ float mediaDuration = durationMediaTime().toDouble();
+ GST_DEBUG("maxTimeSeekable, duration: %f", mediaDuration);
// infinite duration means live stream
- if (std::isinf(duration()))
+ if (std::isinf(mediaDuration))
return 0.0f;
- return duration();
+ return mediaDuration;
}
float MediaPlayerPrivateGStreamer::maxTimeLoaded() const
@@ -1236,29 +1268,29 @@ float MediaPlayerPrivateGStreamer::maxTimeLoaded() const
return 0.0f;
float loaded = m_maxTimeLoaded;
- if (m_isEndReached && m_mediaDuration)
- loaded = m_mediaDuration;
- LOG_MEDIA_MESSAGE("maxTimeLoaded: %f", loaded);
+ if (m_isEndReached)
+ loaded = durationMediaTime().toDouble();
+ GST_DEBUG("maxTimeLoaded: %f", loaded);
return loaded;
}
bool MediaPlayerPrivateGStreamer::didLoadingProgress() const
{
- if (!m_playBin || !m_mediaDuration || !totalBytes())
+ if (UNLIKELY(!m_pipeline || !durationMediaTime() || (!isMediaSource() && !totalBytes())))
return false;
float currentMaxTimeLoaded = maxTimeLoaded();
bool didLoadingProgress = currentMaxTimeLoaded != m_maxTimeLoadedAtLastDidLoadingProgress;
m_maxTimeLoadedAtLastDidLoadingProgress = currentMaxTimeLoaded;
- LOG_MEDIA_MESSAGE("didLoadingProgress: %d", didLoadingProgress);
+ GST_DEBUG("didLoadingProgress: %d", didLoadingProgress);
return didLoadingProgress;
}
-unsigned MediaPlayerPrivateGStreamer::totalBytes() const
+unsigned long long MediaPlayerPrivateGStreamer::totalBytes() const
{
if (m_errorOccured)
return 0;
- if (m_totalBytes != -1)
+ if (m_totalBytes)
return m_totalBytes;
if (!m_source)
@@ -1267,8 +1299,8 @@ unsigned MediaPlayerPrivateGStreamer::totalBytes() const
GstFormat fmt = GST_FORMAT_BYTES;
gint64 length = 0;
if (gst_element_query_duration(m_source.get(), fmt, &length)) {
- INFO_MEDIA_MESSAGE("totalBytes %" G_GINT64_FORMAT, length);
- m_totalBytes = static_cast<unsigned>(length);
+ GST_INFO("totalBytes %" G_GINT64_FORMAT, length);
+ m_totalBytes = static_cast<unsigned long long>(length);
m_isStreaming = !length;
return m_totalBytes;
}
@@ -1302,25 +1334,105 @@ unsigned MediaPlayerPrivateGStreamer::totalBytes() const
gst_iterator_free(iter);
- INFO_MEDIA_MESSAGE("totalBytes %" G_GINT64_FORMAT, length);
- m_totalBytes = static_cast<unsigned>(length);
+ GST_INFO("totalBytes %" G_GINT64_FORMAT, length);
+ m_totalBytes = static_cast<unsigned long long>(length);
m_isStreaming = !length;
return m_totalBytes;
}
+void MediaPlayerPrivateGStreamer::sourceChangedCallback(MediaPlayerPrivateGStreamer* player)
+{
+ player->sourceChanged();
+}
+
+void MediaPlayerPrivateGStreamer::uriDecodeBinElementAddedCallback(GstBin* bin, GstElement* element, MediaPlayerPrivateGStreamer* player)
+{
+ if (g_strcmp0(G_OBJECT_CLASS_NAME(G_OBJECT_GET_CLASS(G_OBJECT(element))), "GstDownloadBuffer"))
+ return;
+
+ player->m_downloadBuffer = element;
+ g_signal_handlers_disconnect_by_func(bin, reinterpret_cast<gpointer>(uriDecodeBinElementAddedCallback), player);
+ g_signal_connect_swapped(element, "notify::temp-location", G_CALLBACK(downloadBufferFileCreatedCallback), player);
+
+ GUniqueOutPtr<char> oldDownloadTemplate;
+ g_object_get(element, "temp-template", &oldDownloadTemplate.outPtr(), nullptr);
+
+ GUniquePtr<char> newDownloadTemplate(g_build_filename(G_DIR_SEPARATOR_S, "var", "tmp", "WebKit-Media-XXXXXX", nullptr));
+ g_object_set(element, "temp-template", newDownloadTemplate.get(), nullptr);
+ GST_TRACE("Reconfigured file download template from '%s' to '%s'", oldDownloadTemplate.get(), newDownloadTemplate.get());
+
+ player->purgeOldDownloadFiles(oldDownloadTemplate.get());
+}
+
+void MediaPlayerPrivateGStreamer::downloadBufferFileCreatedCallback(MediaPlayerPrivateGStreamer* player)
+{
+ ASSERT(player->m_downloadBuffer);
+
+ g_signal_handlers_disconnect_by_func(player->m_downloadBuffer.get(), reinterpret_cast<gpointer>(downloadBufferFileCreatedCallback), player);
+
+ GUniqueOutPtr<char> downloadFile;
+ g_object_get(player->m_downloadBuffer.get(), "temp-location", &downloadFile.outPtr(), nullptr);
+ player->m_downloadBuffer = nullptr;
+
+ if (UNLIKELY(!deleteFile(downloadFile.get()))) {
+ GST_WARNING("Couldn't unlink media temporary file %s after creation", downloadFile.get());
+ return;
+ }
+
+ GST_TRACE("Unlinked media temporary file %s after creation", downloadFile.get());
+}
+
+void MediaPlayerPrivateGStreamer::purgeOldDownloadFiles(const char* downloadFileTemplate)
+{
+ if (!downloadFileTemplate)
+ return;
+
+ GUniquePtr<char> templatePath(g_path_get_dirname(downloadFileTemplate));
+ GUniquePtr<char> templateFile(g_path_get_basename(downloadFileTemplate));
+ String templatePattern = String(templateFile.get()).replace("X", "?");
+
+ for (auto& filePath : listDirectory(templatePath.get(), templatePattern)) {
+ if (UNLIKELY(!deleteFile(filePath))) {
+ GST_WARNING("Couldn't unlink legacy media temporary file: %s", filePath.utf8().data());
+ continue;
+ }
+
+ GST_TRACE("Unlinked legacy media temporary file: %s", filePath.utf8().data());
+ }
+}
+
void MediaPlayerPrivateGStreamer::sourceChanged()
{
+ if (WEBKIT_IS_WEB_SRC(m_source.get()) && GST_OBJECT_PARENT(m_source.get()))
+ g_signal_handlers_disconnect_by_func(GST_ELEMENT_PARENT(m_source.get()), reinterpret_cast<gpointer>(uriDecodeBinElementAddedCallback), this);
+
m_source.clear();
- g_object_get(m_playBin.get(), "source", &m_source.outPtr(), NULL);
+ g_object_get(m_pipeline.get(), "source", &m_source.outPtr(), nullptr);
- if (WEBKIT_IS_WEB_SRC(m_source.get()))
+ if (WEBKIT_IS_WEB_SRC(m_source.get())) {
webKitWebSrcSetMediaPlayer(WEBKIT_WEB_SRC(m_source.get()), m_player);
-#if ENABLE(MEDIA_SOURCE)
- if (m_mediaSource && WEBKIT_IS_MEDIA_SRC(m_source.get())) {
- MediaSourceGStreamer::open(m_mediaSource.get(), WEBKIT_MEDIA_SRC(m_source.get()));
- webKitMediaSrcSetPlayBin(WEBKIT_MEDIA_SRC(m_source.get()), m_playBin.get());
+ g_signal_connect(GST_ELEMENT_PARENT(m_source.get()), "element-added", G_CALLBACK(uriDecodeBinElementAddedCallback), this);
}
-#endif
+}
+
+bool MediaPlayerPrivateGStreamer::hasSingleSecurityOrigin() const
+{
+ if (!m_source)
+ return false;
+
+ if (!WEBKIT_IS_WEB_SRC(m_source.get()))
+ return true;
+
+ GUniqueOutPtr<char> originalURI, resolvedURI;
+ g_object_get(m_source.get(), "location", &originalURI.outPtr(), "resolved-location", &resolvedURI.outPtr(), nullptr);
+ if (!originalURI || !resolvedURI)
+ return false;
+ if (!g_strcmp0(originalURI.get(), resolvedURI.get()))
+ return true;
+
+ Ref<SecurityOrigin> resolvedOrigin(SecurityOrigin::createFromString(String::fromUTF8(resolvedURI.get())));
+ Ref<SecurityOrigin> requestedOrigin(SecurityOrigin::createFromString(String::fromUTF8(originalURI.get())));
+ return resolvedOrigin->isSameSchemeHostPort(requestedOrigin.get());
}
void MediaPlayerPrivateGStreamer::cancelLoad()
@@ -1328,20 +1440,20 @@ void MediaPlayerPrivateGStreamer::cancelLoad()
if (m_networkState < MediaPlayer::Loading || m_networkState == MediaPlayer::Loaded)
return;
- if (m_playBin)
+ if (m_pipeline)
changePipelineState(GST_STATE_READY);
}
void MediaPlayerPrivateGStreamer::asyncStateChangeDone()
{
- if (!m_playBin || m_errorOccured)
+ if (!m_pipeline || m_errorOccured)
return;
if (m_seeking) {
if (m_seekIsPending)
updateStates();
else {
- LOG_MEDIA_MESSAGE("[Seek] seeked to %f", m_seekTime);
+ GST_DEBUG("[Seek] seeked to %f", m_seekTime);
m_seeking = false;
if (m_timeOfOverlappingSeek != m_seekTime && m_timeOfOverlappingSeek != -1) {
seek(m_timeOfOverlappingSeek);
@@ -1352,7 +1464,7 @@ void MediaPlayerPrivateGStreamer::asyncStateChangeDone()
// The pipeline can still have a pending state. In this case a position query will fail.
// Right now we can use m_seekTime as a fallback.
- m_canFallBackToLastFinishedSeekPositon = true;
+ m_canFallBackToLastFinishedSeekPosition = true;
timeChanged();
}
} else
@@ -1361,7 +1473,7 @@ void MediaPlayerPrivateGStreamer::asyncStateChangeDone()
void MediaPlayerPrivateGStreamer::updateStates()
{
- if (!m_playBin)
+ if (!m_pipeline)
return;
if (m_errorOccured)
@@ -1372,25 +1484,19 @@ void MediaPlayerPrivateGStreamer::updateStates()
GstState state;
GstState pending;
- GstStateChangeReturn getStateResult = gst_element_get_state(m_playBin.get(), &state, &pending, 250 * GST_NSECOND);
+ GstStateChangeReturn getStateResult = gst_element_get_state(m_pipeline.get(), &state, &pending, 250 * GST_NSECOND);
bool shouldUpdatePlaybackState = false;
switch (getStateResult) {
case GST_STATE_CHANGE_SUCCESS: {
- LOG_MEDIA_MESSAGE("State: %s, pending: %s", gst_element_state_get_name(state), gst_element_state_get_name(pending));
+ GST_DEBUG("State: %s, pending: %s", gst_element_state_get_name(state), gst_element_state_get_name(pending));
// Do nothing if on EOS and state changed to READY to avoid recreating the player
// on HTMLMediaElement and properly generate the video 'ended' event.
if (m_isEndReached && state == GST_STATE_READY)
break;
- if (state <= GST_STATE_READY) {
- m_resetPipeline = true;
- m_mediaDuration = 0;
- } else {
- m_resetPipeline = false;
- cacheDuration();
- }
+ m_resetPipeline = state <= GST_STATE_READY;
bool didBuffering = m_buffering;
@@ -1408,7 +1514,7 @@ void MediaPlayerPrivateGStreamer::updateStates()
case GST_STATE_PLAYING:
if (m_buffering) {
if (m_bufferingPercentage == 100) {
- LOG_MEDIA_MESSAGE("[Buffering] Complete.");
+ GST_DEBUG("[Buffering] Complete.");
m_buffering = false;
m_readyState = MediaPlayer::HaveEnoughData;
m_networkState = m_downloadFinished ? MediaPlayer::Idle : MediaPlayer::Loading;
@@ -1439,14 +1545,14 @@ void MediaPlayerPrivateGStreamer::updateStates()
}
if (didBuffering && !m_buffering && !m_paused && m_playbackRate) {
- LOG_MEDIA_MESSAGE("[Buffering] Restarting playback.");
+ GST_DEBUG("[Buffering] Restarting playback.");
changePipelineState(GST_STATE_PLAYING);
}
} else if (state == GST_STATE_PLAYING) {
m_paused = false;
if ((m_buffering && !isLiveStream()) || !m_playbackRate) {
- LOG_MEDIA_MESSAGE("[Buffering] Pausing stream for buffering.");
+ GST_DEBUG("[Buffering] Pausing stream for buffering.");
changePipelineState(GST_STATE_PAUSED);
}
} else
@@ -1454,21 +1560,21 @@ void MediaPlayerPrivateGStreamer::updateStates()
if (m_requestedState == GST_STATE_PAUSED && state == GST_STATE_PAUSED) {
shouldUpdatePlaybackState = true;
- LOG_MEDIA_MESSAGE("Requested state change to %s was completed", gst_element_state_get_name(state));
+ GST_DEBUG("Requested state change to %s was completed", gst_element_state_get_name(state));
}
break;
}
case GST_STATE_CHANGE_ASYNC:
- LOG_MEDIA_MESSAGE("Async: State: %s, pending: %s", gst_element_state_get_name(state), gst_element_state_get_name(pending));
+ GST_DEBUG("Async: State: %s, pending: %s", gst_element_state_get_name(state), gst_element_state_get_name(pending));
// Change in progress.
break;
case GST_STATE_CHANGE_FAILURE:
- LOG_MEDIA_MESSAGE("Failure: State: %s, pending: %s", gst_element_state_get_name(state), gst_element_state_get_name(pending));
+ GST_DEBUG("Failure: State: %s, pending: %s", gst_element_state_get_name(state), gst_element_state_get_name(pending));
// Change failed
return;
case GST_STATE_CHANGE_NO_PREROLL:
- LOG_MEDIA_MESSAGE("No preroll: State: %s, pending: %s", gst_element_state_get_name(state), gst_element_state_get_name(pending));
+ GST_DEBUG("No preroll: State: %s, pending: %s", gst_element_state_get_name(state), gst_element_state_get_name(pending));
// Live pipelines go in PAUSED without prerolling.
m_isStreaming = true;
@@ -1488,7 +1594,7 @@ void MediaPlayerPrivateGStreamer::updateStates()
m_networkState = MediaPlayer::Loading;
break;
default:
- LOG_MEDIA_MESSAGE("Else : %d", getStateResult);
+ GST_DEBUG("Else : %d", getStateResult);
break;
}
@@ -1498,22 +1604,22 @@ void MediaPlayerPrivateGStreamer::updateStates()
m_player->playbackStateChanged();
if (m_networkState != oldNetworkState) {
- LOG_MEDIA_MESSAGE("Network State Changed from %u to %u", oldNetworkState, m_networkState);
+ GST_DEBUG("Network State Changed from %u to %u", oldNetworkState, m_networkState);
m_player->networkStateChanged();
}
if (m_readyState != oldReadyState) {
- LOG_MEDIA_MESSAGE("Ready State Changed from %u to %u", oldReadyState, m_readyState);
+ GST_DEBUG("Ready State Changed from %u to %u", oldReadyState, m_readyState);
m_player->readyStateChanged();
}
if (getStateResult == GST_STATE_CHANGE_SUCCESS && state >= GST_STATE_PAUSED) {
updatePlaybackRate();
if (m_seekIsPending) {
- LOG_MEDIA_MESSAGE("[Seek] committing pending seek to %f", m_seekTime);
+ GST_DEBUG("[Seek] committing pending seek to %f", m_seekTime);
m_seekIsPending = false;
m_seeking = doSeek(toGstClockTime(m_seekTime), m_player->rate(), static_cast<GstSeekFlags>(GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_ACCURATE));
if (!m_seeking)
- LOG_MEDIA_MESSAGE("[Seek] seeking to %f failed", m_seekTime);
+ GST_DEBUG("[Seek] seeking to %f failed", m_seekTime);
}
}
}
@@ -1544,7 +1650,7 @@ bool MediaPlayerPrivateGStreamer::loadNextLocation()
return false;
const GValue* locations = gst_structure_get_value(m_mediaLocations, "locations");
- const gchar* newLocation = 0;
+ const gchar* newLocation = nullptr;
if (!locations) {
// Fallback on new-location string.
@@ -1555,7 +1661,7 @@ bool MediaPlayerPrivateGStreamer::loadNextLocation()
if (!newLocation) {
if (m_mediaLocationCurrentIndex < 0) {
- m_mediaLocations = 0;
+ m_mediaLocations = nullptr;
return false;
}
@@ -1580,7 +1686,7 @@ bool MediaPlayerPrivateGStreamer::loadNextLocation()
RefPtr<SecurityOrigin> securityOrigin = SecurityOrigin::create(m_url);
if (securityOrigin->canRequest(newUrl)) {
- INFO_MEDIA_MESSAGE("New media url: %s", newUrl.string().utf8().data());
+ GST_INFO("New media url: %s", newUrl.string().utf8().data());
// Reset player states.
m_networkState = MediaPlayer::Loading;
@@ -1593,16 +1699,16 @@ bool MediaPlayerPrivateGStreamer::loadNextLocation()
changePipelineState(GST_STATE_READY);
GstState state;
- gst_element_get_state(m_playBin.get(), &state, 0, 0);
+ gst_element_get_state(m_pipeline.get(), &state, nullptr, 0);
if (state <= GST_STATE_READY) {
// Set the new uri and start playing.
- g_object_set(m_playBin.get(), "uri", newUrl.string().utf8().data(), NULL);
+ g_object_set(m_pipeline.get(), "uri", newUrl.string().utf8().data(), nullptr);
m_url = newUrl;
changePipelineState(GST_STATE_PLAYING);
return true;
}
} else
- INFO_MEDIA_MESSAGE("Not allowed to load new media location: %s", newUrl.string().utf8().data());
+ GST_INFO("Not allowed to load new media location: %s", newUrl.string().utf8().data());
}
m_mediaLocationCurrentIndex--;
return false;
@@ -1624,49 +1730,29 @@ void MediaPlayerPrivateGStreamer::didEnd()
// Synchronize position and duration values to not confuse the
// HTMLMediaElement. In some cases like reverse playback the
// position is not always reported as 0 for instance.
- float now = currentTime();
- if (now > 0 && now <= duration() && m_mediaDuration != now) {
- m_mediaDurationKnown = true;
- m_mediaDuration = now;
+ MediaTime now = currentMediaTime();
+ if (now > MediaTime { } && now <= durationMediaTime())
m_player->durationChanged();
- }
m_isEndReached = true;
timeChanged();
- if (!m_player->mediaPlayerClient()->mediaPlayerIsLooping()) {
+ if (!m_player->client().mediaPlayerIsLooping()) {
m_paused = true;
+ m_durationAtEOS = durationMediaTime().toDouble();
changePipelineState(GST_STATE_READY);
m_downloadFinished = false;
}
}
-void MediaPlayerPrivateGStreamer::cacheDuration()
-{
- if (m_mediaDuration || !m_mediaDurationKnown)
- return;
-
- float newDuration = duration();
- if (std::isinf(newDuration)) {
- // Only pretend that duration is not available if the the query failed in a stable pipeline state.
- GstState state;
- if (gst_element_get_state(m_playBin.get(), &state, 0, 0) == GST_STATE_CHANGE_SUCCESS && state > GST_STATE_READY)
- m_mediaDurationKnown = false;
- return;
- }
-
- m_mediaDuration = newDuration;
-}
-
void MediaPlayerPrivateGStreamer::durationChanged()
{
- float previousDuration = m_mediaDuration;
+ float previousDuration = durationMediaTime().toDouble();
- cacheDuration();
// Avoid emiting durationchanged in the case where the previous
// duration was 0 because that case is already handled by the
// HTMLMediaElement.
- if (previousDuration && m_mediaDuration != previousDuration)
+ if (previousDuration && durationMediaTime().toDouble() != previousDuration)
m_player->durationChanged();
}
@@ -1683,154 +1769,187 @@ void MediaPlayerPrivateGStreamer::loadingFailed(MediaPlayer::NetworkState error)
}
// Loading failed, remove ready timer.
- if (m_readyTimerHandler) {
- g_source_remove(m_readyTimerHandler);
- m_readyTimerHandler = 0;
- }
-}
-
-static HashSet<String> mimeTypeCache()
-{
- initializeGStreamerAndRegisterWebKitElements();
-
- DEFINE_STATIC_LOCAL(HashSet<String>, cache, ());
- static bool typeListInitialized = false;
-
- if (typeListInitialized)
- return cache;
-
- const char* mimeTypes[] = {
- "application/ogg",
- "application/vnd.apple.mpegurl",
- "application/vnd.rn-realmedia",
- "application/x-3gp",
- "application/x-pn-realaudio",
- "audio/3gpp",
- "audio/aac",
- "audio/flac",
- "audio/iLBC-sh",
- "audio/midi",
- "audio/mobile-xmf",
- "audio/mp1",
- "audio/mp2",
- "audio/mp3",
- "audio/mp4",
- "audio/mpeg",
- "audio/ogg",
- "audio/opus",
- "audio/qcelp",
- "audio/riff-midi",
- "audio/speex",
- "audio/wav",
- "audio/webm",
- "audio/x-ac3",
- "audio/x-aiff",
- "audio/x-amr-nb-sh",
- "audio/x-amr-wb-sh",
- "audio/x-au",
- "audio/x-ay",
- "audio/x-celt",
- "audio/x-dts",
- "audio/x-flac",
- "audio/x-gbs",
- "audio/x-gsm",
- "audio/x-gym",
- "audio/x-imelody",
- "audio/x-ircam",
- "audio/x-kss",
- "audio/x-m4a",
- "audio/x-mod",
- "audio/x-mp3",
- "audio/x-mpeg",
- "audio/x-musepack",
- "audio/x-nist",
- "audio/x-nsf",
- "audio/x-paris",
- "audio/x-sap",
- "audio/x-sbc",
- "audio/x-sds",
- "audio/x-shorten",
- "audio/x-sid",
- "audio/x-spc",
- "audio/x-speex",
- "audio/x-svx",
- "audio/x-ttafile",
- "audio/x-vgm",
- "audio/x-voc",
- "audio/x-vorbis+ogg",
- "audio/x-w64",
- "audio/x-wav",
- "audio/x-wavpack",
- "audio/x-wavpack-correction",
- "video/3gpp",
- "video/mj2",
- "video/mp4",
- "video/mpeg",
- "video/mpegts",
- "video/ogg",
- "video/quicktime",
- "video/vivo",
- "video/webm",
- "video/x-cdxa",
- "video/x-dirac",
- "video/x-dv",
- "video/x-fli",
- "video/x-flv",
- "video/x-h263",
- "video/x-ivf",
- "video/x-m4v",
- "video/x-matroska",
- "video/x-mng",
- "video/x-ms-asf",
- "video/x-msvideo",
- "video/x-mve",
- "video/x-nuv",
- "video/x-vcd"
- };
-
- for (unsigned i = 0; i < (sizeof(mimeTypes) / sizeof(*mimeTypes)); ++i)
- cache.add(String(mimeTypes[i]));
-
- typeListInitialized = true;
- return cache;
-}
-
-void MediaPlayerPrivateGStreamer::getSupportedTypes(HashSet<String>& types)
-{
- types = mimeTypeCache();
+ m_readyTimerHandler.stop();
+}
+
+static HashSet<String, ASCIICaseInsensitiveHash>& mimeTypeSet()
+{
+ static NeverDestroyed<HashSet<String, ASCIICaseInsensitiveHash>> mimeTypes = []()
+ {
+ initializeGStreamerAndRegisterWebKitElements();
+ HashSet<String, ASCIICaseInsensitiveHash> set;
+
+ GList* audioDecoderFactories = gst_element_factory_list_get_elements(GST_ELEMENT_FACTORY_TYPE_DECODER | GST_ELEMENT_FACTORY_TYPE_MEDIA_AUDIO, GST_RANK_MARGINAL);
+ GList* videoDecoderFactories = gst_element_factory_list_get_elements(GST_ELEMENT_FACTORY_TYPE_DECODER | GST_ELEMENT_FACTORY_TYPE_MEDIA_VIDEO, GST_RANK_MARGINAL);
+ GList* demuxerFactories = gst_element_factory_list_get_elements(GST_ELEMENT_FACTORY_TYPE_DEMUXER, GST_RANK_MARGINAL);
+
+ enum ElementType {
+ AudioDecoder = 0,
+ VideoDecoder,
+ Demuxer
+ };
+ struct GstCapsWebKitMapping {
+ ElementType elementType;
+ const char* capsString;
+ Vector<AtomicString> webkitMimeTypes;
+ };
+
+ Vector<GstCapsWebKitMapping> mapping = {
+ {AudioDecoder, "audio/midi", {"audio/midi", "audio/riff-midi"}},
+ {AudioDecoder, "audio/x-sbc", { }},
+ {AudioDecoder, "audio/x-sid", { }},
+ {AudioDecoder, "audio/x-flac", {"audio/x-flac", "audio/flac"}},
+ {AudioDecoder, "audio/x-wav", {"audio/x-wav", "audio/wav"}},
+ {AudioDecoder, "audio/x-wavpack", {"audio/x-wavpack"}},
+ {AudioDecoder, "audio/x-speex", {"audio/speex", "audio/x-speex"}},
+ {AudioDecoder, "audio/x-ac3", { }},
+ {AudioDecoder, "audio/x-eac3", {"audio/x-ac3"}},
+ {AudioDecoder, "audio/x-dts", { }},
+ {VideoDecoder, "video/x-h264, profile=(string)high", {"video/mp4", "video/x-m4v"}},
+ {VideoDecoder, "video/x-msvideocodec", {"video/x-msvideo"}},
+ {VideoDecoder, "video/x-h263", { }},
+ {VideoDecoder, "video/mpegts", { }},
+ {VideoDecoder, "video/mpeg, mpegversion=(int){1,2}, systemstream=(boolean)false", {"video/mpeg"}},
+ {VideoDecoder, "video/x-dirac", { }},
+ {VideoDecoder, "video/x-flash-video", {"video/flv", "video/x-flv"}},
+ {Demuxer, "video/quicktime", { }},
+ {Demuxer, "video/quicktime, variant=(string)3gpp", {"video/3gpp"}},
+ {Demuxer, "application/x-3gp", { }},
+ {Demuxer, "video/x-ms-asf", { }},
+ {Demuxer, "audio/x-aiff", { }},
+ {Demuxer, "application/x-pn-realaudio", { }},
+ {Demuxer, "application/vnd.rn-realmedia", { }},
+ {Demuxer, "audio/x-wav", {"audio/x-wav", "audio/wav"}},
+ {Demuxer, "application/x-hls", {"application/vnd.apple.mpegurl", "application/x-mpegurl"}}
+ };
+
+ for (auto& current : mapping) {
+ GList* factories = demuxerFactories;
+ if (current.elementType == AudioDecoder)
+ factories = audioDecoderFactories;
+ else if (current.elementType == VideoDecoder)
+ factories = videoDecoderFactories;
+
+ if (gstRegistryHasElementForMediaType(factories, current.capsString)) {
+ if (!current.webkitMimeTypes.isEmpty()) {
+ for (const auto& mimeType : current.webkitMimeTypes)
+ set.add(mimeType);
+ } else
+ set.add(AtomicString(current.capsString));
+ }
+ }
+
+ bool opusSupported = false;
+ if (gstRegistryHasElementForMediaType(audioDecoderFactories, "audio/x-opus")) {
+ opusSupported = true;
+ set.add(AtomicString("audio/opus"));
+ }
+
+ bool vorbisSupported = false;
+ if (gstRegistryHasElementForMediaType(demuxerFactories, "application/ogg")) {
+ set.add(AtomicString("application/ogg"));
+
+ vorbisSupported = gstRegistryHasElementForMediaType(audioDecoderFactories, "audio/x-vorbis");
+ if (vorbisSupported) {
+ set.add(AtomicString("audio/ogg"));
+ set.add(AtomicString("audio/x-vorbis+ogg"));
+ }
+
+ if (gstRegistryHasElementForMediaType(videoDecoderFactories, "video/x-theora"))
+ set.add(AtomicString("video/ogg"));
+ }
+
+ bool audioMpegSupported = false;
+ if (gstRegistryHasElementForMediaType(audioDecoderFactories, "audio/mpeg, mpegversion=(int)1, layer=(int)[1, 3]")) {
+ audioMpegSupported = true;
+ set.add(AtomicString("audio/mp1"));
+ set.add(AtomicString("audio/mp3"));
+ set.add(AtomicString("audio/x-mp3"));
+ }
+
+ if (gstRegistryHasElementForMediaType(audioDecoderFactories, "audio/mpeg, mpegversion=(int){2, 4}")) {
+ audioMpegSupported = true;
+ set.add(AtomicString("audio/aac"));
+ set.add(AtomicString("audio/mp2"));
+ set.add(AtomicString("audio/mp4"));
+ set.add(AtomicString("audio/x-m4a"));
+ }
+
+ if (audioMpegSupported) {
+ set.add(AtomicString("audio/mpeg"));
+ set.add(AtomicString("audio/x-mpeg"));
+ }
+
+ if (gstRegistryHasElementForMediaType(demuxerFactories, "video/x-matroska")) {
+ set.add(AtomicString("video/x-matroska"));
+
+ if (gstRegistryHasElementForMediaType(videoDecoderFactories, "video/x-vp8")
+ || gstRegistryHasElementForMediaType(videoDecoderFactories, "video/x-vp9")
+ || gstRegistryHasElementForMediaType(videoDecoderFactories, "video/x-vp10"))
+ set.add(AtomicString("video/webm"));
+
+ if (vorbisSupported || opusSupported)
+ set.add(AtomicString("audio/webm"));
+ }
+
+ gst_plugin_feature_list_free(audioDecoderFactories);
+ gst_plugin_feature_list_free(videoDecoderFactories);
+ gst_plugin_feature_list_free(demuxerFactories);
+ return set;
+ }();
+ return mimeTypes;
+}
+
+void MediaPlayerPrivateGStreamer::getSupportedTypes(HashSet<String, ASCIICaseInsensitiveHash>& types)
+{
+ types = mimeTypeSet();
}
MediaPlayer::SupportsType MediaPlayerPrivateGStreamer::supportsType(const MediaEngineSupportParameters& parameters)
{
+ MediaPlayer::SupportsType result = MediaPlayer::IsNotSupported;
+#if ENABLE(MEDIA_SOURCE)
+ // MediaPlayerPrivateGStreamerMSE is in charge of mediasource playback, not us.
+ if (parameters.isMediaSource)
+ return result;
+#endif
+
+ // MediaStream playback is handled by the OpenWebRTC player.
+ if (parameters.isMediaStream)
+ return result;
+
if (parameters.type.isNull() || parameters.type.isEmpty())
- return MediaPlayer::IsNotSupported;
+ return result;
// spec says we should not return "probably" if the codecs string is empty
- if (mimeTypeCache().contains(parameters.type))
- return parameters.codecs.isEmpty() ? MediaPlayer::MayBeSupported : MediaPlayer::IsSupported;
- return MediaPlayer::IsNotSupported;
+ if (mimeTypeSet().contains(parameters.type))
+ result = parameters.codecs.isEmpty() ? MediaPlayer::MayBeSupported : MediaPlayer::IsSupported;
+
+ return extendedSupportsType(parameters, result);
}
void MediaPlayerPrivateGStreamer::setDownloadBuffering()
{
- if (!m_playBin)
+ if (!m_pipeline)
return;
- GstPlayFlags flags;
- g_object_get(m_playBin.get(), "flags", &flags, NULL);
+ unsigned flags;
+ g_object_get(m_pipeline.get(), "flags", &flags, nullptr);
+
+ unsigned flagDownload = getGstPlayFlag("download");
// We don't want to stop downloading if we already started it.
- if (flags & GST_PLAY_FLAG_DOWNLOAD && m_readyState > MediaPlayer::HaveNothing && !m_resetPipeline)
+ if (flags & flagDownload && m_readyState > MediaPlayer::HaveNothing && !m_resetPipeline)
return;
bool shouldDownload = !isLiveStream() && m_preload == MediaPlayer::Auto;
if (shouldDownload) {
- LOG_MEDIA_MESSAGE("Enabling on-disk buffering");
- g_object_set(m_playBin.get(), "flags", flags | GST_PLAY_FLAG_DOWNLOAD, NULL);
+ GST_DEBUG("Enabling on-disk buffering");
+ g_object_set(m_pipeline.get(), "flags", flags | flagDownload, nullptr);
m_fillTimer.startRepeating(0.2);
} else {
- LOG_MEDIA_MESSAGE("Disabling on-disk buffering");
- g_object_set(m_playBin.get(), "flags", flags & ~GST_PLAY_FLAG_DOWNLOAD, NULL);
+ GST_DEBUG("Disabling on-disk buffering");
+ g_object_set(m_pipeline.get(), "flags", flags & ~flagDownload, nullptr);
m_fillTimer.stop();
}
}
@@ -1851,93 +1970,172 @@ void MediaPlayerPrivateGStreamer::setPreload(MediaPlayer::Preload preload)
GstElement* MediaPlayerPrivateGStreamer::createAudioSink()
{
- m_autoAudioSink = gst_element_factory_make("autoaudiosink", 0);
- g_signal_connect(m_autoAudioSink.get(), "child-added", G_CALLBACK(setAudioStreamPropertiesCallback), this);
+ m_autoAudioSink = gst_element_factory_make("autoaudiosink", nullptr);
+ if (!m_autoAudioSink) {
+ GST_WARNING("GStreamer's autoaudiosink not found. Please check your gst-plugins-good installation");
+ return nullptr;
+ }
- // Construct audio sink only if pitch preserving is enabled.
- if (!m_preservesPitch)
- return m_autoAudioSink.get();
+ g_signal_connect_swapped(m_autoAudioSink.get(), "child-added", G_CALLBACK(setAudioStreamPropertiesCallback), this);
+
+ GstElement* audioSinkBin;
- GstElement* scale = gst_element_factory_make("scaletempo", 0);
- if (!scale) {
- GST_WARNING("Failed to create scaletempo");
+ if (webkitGstCheckVersion(1, 4, 2)) {
+#if ENABLE(WEB_AUDIO)
+ audioSinkBin = gst_bin_new("audio-sink");
+ ensureAudioSourceProvider();
+ m_audioSourceProvider->configureAudioBin(audioSinkBin, nullptr);
+ return audioSinkBin;
+#else
return m_autoAudioSink.get();
+#endif
}
- GstElement* audioSinkBin = gst_bin_new("audio-sink");
- GstElement* convert = gst_element_factory_make("audioconvert", 0);
- GstElement* resample = gst_element_factory_make("audioresample", 0);
+ // Construct audio sink only if pitch preserving is enabled.
+ // If GStreamer 1.4.2 is used the audio-filter playbin property is used instead.
+ if (m_preservesPitch) {
+ GstElement* scale = gst_element_factory_make("scaletempo", nullptr);
+ if (!scale) {
+ GST_WARNING("Failed to create scaletempo");
+ return m_autoAudioSink.get();
+ }
- gst_bin_add_many(GST_BIN(audioSinkBin), scale, convert, resample, m_autoAudioSink.get(), NULL);
+ audioSinkBin = gst_bin_new("audio-sink");
+ gst_bin_add(GST_BIN(audioSinkBin), scale);
+ GRefPtr<GstPad> pad = adoptGRef(gst_element_get_static_pad(scale, "sink"));
+ gst_element_add_pad(audioSinkBin, gst_ghost_pad_new("sink", pad.get()));
- if (!gst_element_link_many(scale, convert, resample, m_autoAudioSink.get(), NULL)) {
- GST_WARNING("Failed to link audio sink elements");
- gst_object_unref(audioSinkBin);
- return m_autoAudioSink.get();
+#if ENABLE(WEB_AUDIO)
+ ensureAudioSourceProvider();
+ m_audioSourceProvider->configureAudioBin(audioSinkBin, scale);
+#else
+ GstElement* convert = gst_element_factory_make("audioconvert", nullptr);
+ GstElement* resample = gst_element_factory_make("audioresample", nullptr);
+
+ gst_bin_add_many(GST_BIN(audioSinkBin), convert, resample, m_autoAudioSink.get(), nullptr);
+
+ if (!gst_element_link_many(scale, convert, resample, m_autoAudioSink.get(), nullptr)) {
+ GST_WARNING("Failed to link audio sink elements");
+ gst_object_unref(audioSinkBin);
+ return m_autoAudioSink.get();
+ }
+#endif
+ return audioSinkBin;
}
- GRefPtr<GstPad> pad = adoptGRef(gst_element_get_static_pad(scale, "sink"));
- gst_element_add_pad(audioSinkBin, gst_ghost_pad_new("sink", pad.get()));
+#if ENABLE(WEB_AUDIO)
+ audioSinkBin = gst_bin_new("audio-sink");
+ ensureAudioSourceProvider();
+ m_audioSourceProvider->configureAudioBin(audioSinkBin, nullptr);
return audioSinkBin;
+#endif
+ ASSERT_NOT_REACHED();
+ return nullptr;
}
GstElement* MediaPlayerPrivateGStreamer::audioSink() const
{
GstElement* sink;
- g_object_get(m_playBin.get(), "audio-sink", &sink, nullptr);
+ g_object_get(m_pipeline.get(), "audio-sink", &sink, nullptr);
return sink;
}
+#if ENABLE(WEB_AUDIO)
+void MediaPlayerPrivateGStreamer::ensureAudioSourceProvider()
+{
+ if (!m_audioSourceProvider)
+ m_audioSourceProvider = std::make_unique<AudioSourceProviderGStreamer>();
+}
+
+AudioSourceProvider* MediaPlayerPrivateGStreamer::audioSourceProvider()
+{
+ ensureAudioSourceProvider();
+ return m_audioSourceProvider.get();
+}
+#endif
+
void MediaPlayerPrivateGStreamer::createGSTPlayBin()
{
- ASSERT(!m_playBin);
+ ASSERT(!m_pipeline);
// gst_element_factory_make() returns a floating reference so
// we should not adopt.
- m_playBin = gst_element_factory_make("playbin", "play");
- setStreamVolumeElement(GST_STREAM_VOLUME(m_playBin.get()));
+ setPipeline(gst_element_factory_make("playbin", "play"));
+ setStreamVolumeElement(GST_STREAM_VOLUME(m_pipeline.get()));
+
+ GRefPtr<GstBus> bus = adoptGRef(gst_pipeline_get_bus(GST_PIPELINE(m_pipeline.get())));
+ gst_bus_set_sync_handler(bus.get(), [](GstBus*, GstMessage* message, gpointer userData) {
+ auto& player = *static_cast<MediaPlayerPrivateGStreamer*>(userData);
+
+ if (player.handleSyncMessage(message)) {
+ gst_message_unref(message);
+ return GST_BUS_DROP;
+ }
- GRefPtr<GstBus> bus = adoptGRef(gst_pipeline_get_bus(GST_PIPELINE(m_playBin.get())));
+ return GST_BUS_PASS;
+ }, this, nullptr);
+
+ // Let also other listeners subscribe to (application) messages in this bus.
gst_bus_add_signal_watch(bus.get());
- g_signal_connect(bus.get(), "message", G_CALLBACK(mediaPlayerPrivateMessageCallback), this);
+ g_signal_connect(bus.get(), "message", G_CALLBACK(busMessageCallback), this);
- g_object_set(m_playBin.get(), "mute", m_player->muted(), NULL);
+ g_object_set(m_pipeline.get(), "mute", m_player->muted(), nullptr);
- g_signal_connect(m_playBin.get(), "notify::source", G_CALLBACK(mediaPlayerPrivateSourceChangedCallback), this);
- g_signal_connect(m_playBin.get(), "video-changed", G_CALLBACK(mediaPlayerPrivateVideoChangedCallback), this);
- g_signal_connect(m_playBin.get(), "audio-changed", G_CALLBACK(mediaPlayerPrivateAudioChangedCallback), this);
+ g_signal_connect_swapped(m_pipeline.get(), "notify::source", G_CALLBACK(sourceChangedCallback), this);
+ g_signal_connect_swapped(m_pipeline.get(), "video-changed", G_CALLBACK(videoChangedCallback), this);
+ g_signal_connect_swapped(m_pipeline.get(), "audio-changed", G_CALLBACK(audioChangedCallback), this);
#if ENABLE(VIDEO_TRACK)
- if (webkitGstCheckVersion(1, 1, 2)) {
- g_signal_connect(m_playBin.get(), "text-changed", G_CALLBACK(mediaPlayerPrivateTextChangedCallback), this);
+ g_signal_connect_swapped(m_pipeline.get(), "text-changed", G_CALLBACK(textChangedCallback), this);
- GstElement* textCombiner = webkitTextCombinerNew();
- ASSERT(textCombiner);
- g_object_set(m_playBin.get(), "text-stream-combiner", textCombiner, NULL);
+ GstElement* textCombiner = webkitTextCombinerNew();
+ ASSERT(textCombiner);
+ g_object_set(m_pipeline.get(), "text-stream-combiner", textCombiner, nullptr);
- m_textAppSink = webkitTextSinkNew();
- ASSERT(m_textAppSink);
+ m_textAppSink = webkitTextSinkNew();
+ ASSERT(m_textAppSink);
- m_textAppSinkPad = adoptGRef(gst_element_get_static_pad(m_textAppSink.get(), "sink"));
- ASSERT(m_textAppSinkPad);
+ m_textAppSinkPad = adoptGRef(gst_element_get_static_pad(m_textAppSink.get(), "sink"));
+ ASSERT(m_textAppSinkPad);
- g_object_set(m_textAppSink.get(), "emit-signals", true, "enable-last-sample", false, "caps", gst_caps_new_empty_simple("text/vtt"), NULL);
- g_signal_connect(m_textAppSink.get(), "new-sample", G_CALLBACK(mediaPlayerPrivateNewTextSampleCallback), this);
+ g_object_set(m_textAppSink.get(), "emit-signals", true, "enable-last-sample", false, "caps", gst_caps_new_empty_simple("text/vtt"), nullptr);
+ g_signal_connect_swapped(m_textAppSink.get(), "new-sample", G_CALLBACK(newTextSampleCallback), this);
- g_object_set(m_playBin.get(), "text-sink", m_textAppSink.get(), NULL);
- }
+ g_object_set(m_pipeline.get(), "text-sink", m_textAppSink.get(), nullptr);
#endif
- g_object_set(m_playBin.get(), "video-sink", createVideoSink(), "audio-sink", createAudioSink(), nullptr);
+ g_object_set(m_pipeline.get(), "video-sink", createVideoSink(), "audio-sink", createAudioSink(), nullptr);
+
+ configurePlaySink();
+
+ // On 1.4.2 and newer we use the audio-filter property instead.
+ // See https://bugzilla.gnome.org/show_bug.cgi?id=735748 for
+ // the reason for using >= 1.4.2 instead of >= 1.4.0.
+ if (m_preservesPitch && webkitGstCheckVersion(1, 4, 2)) {
+ GstElement* scale = gst_element_factory_make("scaletempo", nullptr);
- GRefPtr<GstPad> videoSinkPad = adoptGRef(gst_element_get_static_pad(m_webkitVideoSink.get(), "sink"));
+ if (!scale)
+ GST_WARNING("Failed to create scaletempo");
+ else
+ g_object_set(m_pipeline.get(), "audio-filter", scale, nullptr);
+ }
+
+ if (!m_renderingCanBeAccelerated) {
+ // If not using accelerated compositing, let GStreamer handle
+ // the image-orientation tag.
+ GstElement* videoFlip = gst_element_factory_make("videoflip", nullptr);
+ g_object_set(videoFlip, "method", 8, nullptr);
+ g_object_set(m_pipeline.get(), "video-filter", videoFlip, nullptr);
+ }
+
+ GRefPtr<GstPad> videoSinkPad = adoptGRef(gst_element_get_static_pad(m_videoSink.get(), "sink"));
if (videoSinkPad)
- g_signal_connect(videoSinkPad.get(), "notify::caps", G_CALLBACK(mediaPlayerPrivateVideoSinkCapsChangedCallback), this);
+ g_signal_connect_swapped(videoSinkPad.get(), "notify::caps", G_CALLBACK(videoSinkCapsChangedCallback), this);
}
void MediaPlayerPrivateGStreamer::simulateAudioInterruption()
{
- GstMessage* message = gst_message_new_request_state(GST_OBJECT(m_playBin.get()), GST_STATE_PAUSED);
- gst_element_post_message(m_playBin.get(), message);
+ GstMessage* message = gst_message_new_request_state(GST_OBJECT(m_pipeline.get()), GST_STATE_PAUSED);
+ gst_element_post_message(m_pipeline.get(), message);
}
bool MediaPlayerPrivateGStreamer::didPassCORSAccessCheck() const
@@ -1947,6 +2145,25 @@ bool MediaPlayerPrivateGStreamer::didPassCORSAccessCheck() const
return false;
}
+bool MediaPlayerPrivateGStreamer::canSaveMediaData() const
+{
+ if (isLiveStream())
+ return false;
+
+ if (m_url.isLocalFile())
+ return true;
+
+ if (m_url.protocolIsInHTTPFamily())
+ return true;
+
+ return false;
+}
+
+bool MediaPlayerPrivateGStreamer::handleSyncMessage(GstMessage* message)
+{
+ return MediaPlayerPrivateGStreamerBase::handleSyncMessage(message);
+}
+
}
#endif // USE(GSTREAMER)
diff --git a/Source/WebCore/platform/graphics/gstreamer/MediaPlayerPrivateGStreamer.h b/Source/WebCore/platform/graphics/gstreamer/MediaPlayerPrivateGStreamer.h
index 1990bb20b..953239b58 100644
--- a/Source/WebCore/platform/graphics/gstreamer/MediaPlayerPrivateGStreamer.h
+++ b/Source/WebCore/platform/graphics/gstreamer/MediaPlayerPrivateGStreamer.h
@@ -2,7 +2,9 @@
* Copyright (C) 2007, 2009 Apple Inc. All rights reserved.
* Copyright (C) 2007 Collabora Ltd. All rights reserved.
* Copyright (C) 2007 Alp Toker <alp@atoker.com>
- * Copyright (C) 2009, 2010 Igalia S.L
+ * Copyright (C) 2009, 2010, 2011, 2012, 2013, 2015, 2016 Igalia S.L
+ * Copyright (C) 2014 Cable Television Laboratories, Inc.
+ * Copyright (C) 2015, 2016 Metrological Group B.V.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
@@ -32,185 +34,233 @@
#include <gst/gst.h>
#include <gst/pbutils/install-plugins.h>
#include <wtf/Forward.h>
+#include <wtf/RunLoop.h>
+#include <wtf/WeakPtr.h>
-#if ENABLE(MEDIA_SOURCE)
-#include "MediaSourceGStreamer.h"
+#if ENABLE(VIDEO_TRACK) && USE(GSTREAMER_MPEGTS)
+#include <wtf/text/AtomicStringHash.h>
#endif
typedef struct _GstBuffer GstBuffer;
typedef struct _GstMessage GstMessage;
typedef struct _GstElement GstElement;
+typedef struct _GstMpegtsSection GstMpegtsSection;
namespace WebCore {
+#if ENABLE(WEB_AUDIO)
+class AudioSourceProvider;
+class AudioSourceProviderGStreamer;
+#endif
+
class AudioTrackPrivateGStreamer;
+class InbandMetadataTextTrackPrivateGStreamer;
class InbandTextTrackPrivateGStreamer;
+class MediaPlayerRequestInstallMissingPluginsCallback;
class VideoTrackPrivateGStreamer;
+#if ENABLE(MEDIA_SOURCE)
+class MediaSourcePrivateClient;
+#endif
+
class MediaPlayerPrivateGStreamer : public MediaPlayerPrivateGStreamerBase {
public:
- ~MediaPlayerPrivateGStreamer();
+ explicit MediaPlayerPrivateGStreamer(MediaPlayer*);
+ virtual ~MediaPlayerPrivateGStreamer();
+
static void registerMediaEngine(MediaEngineRegistrar);
- gboolean handleMessage(GstMessage*);
+ void handleMessage(GstMessage*);
void handlePluginInstallerResult(GstInstallPluginsReturn);
- bool hasVideo() const { return m_hasVideo; }
- bool hasAudio() const { return m_hasAudio; }
+ bool hasVideo() const override { return m_hasVideo; }
+ bool hasAudio() const override { return m_hasAudio; }
- void load(const String &url);
+ void load(const String &url) override;
#if ENABLE(MEDIA_SOURCE)
- void load(const String& url, PassRefPtr<HTMLMediaSource>);
+ void load(const String& url, MediaSourcePrivateClient*) override;
+#endif
+#if ENABLE(MEDIA_STREAM)
+ void load(MediaStreamPrivate&) override;
#endif
void commitLoad();
- void cancelLoad();
+ void cancelLoad() override;
- void prepareToPlay();
- void play();
- void pause();
+ void prepareToPlay() override;
+ void play() override;
+ void pause() override;
- bool paused() const;
- bool seeking() const;
+ bool paused() const override;
+ bool seeking() const override;
- float duration() const;
- float currentTime() const;
- void seek(float);
+ MediaTime durationMediaTime() const override;
+ MediaTime currentMediaTime() const override;
+ void seek(float) override;
- void setRate(float);
- void setPreservesPitch(bool);
+ void setRate(float) override;
+ double rate() const override;
+ void setPreservesPitch(bool) override;
- void setPreload(MediaPlayer::Preload);
- void fillTimerFired(Timer<MediaPlayerPrivateGStreamer>*);
+ void setPreload(MediaPlayer::Preload) override;
+ void fillTimerFired();
- PassRefPtr<TimeRanges> buffered() const;
- float maxTimeSeekable() const;
- bool didLoadingProgress() const;
- unsigned totalBytes() const;
- float maxTimeLoaded() const;
+ std::unique_ptr<PlatformTimeRanges> buffered() const override;
+ float maxTimeSeekable() const override;
+ bool didLoadingProgress() const override;
+ unsigned long long totalBytes() const override;
+ float maxTimeLoaded() const override;
+
+ bool hasSingleSecurityOrigin() const override;
void loadStateChanged();
void timeChanged();
void didEnd();
- void durationChanged();
+ virtual void durationChanged();
void loadingFailed(MediaPlayer::NetworkState);
- void videoChanged();
- void videoCapsChanged();
- void audioChanged();
- void notifyPlayerOfVideo();
- void notifyPlayerOfVideoCaps();
- void notifyPlayerOfAudio();
+ virtual void sourceChanged();
-#if ENABLE(VIDEO_TRACK)
- void textChanged();
- void notifyPlayerOfText();
+ GstElement* audioSink() const override;
+ virtual void configurePlaySink() { }
- void newTextSample();
- void notifyPlayerOfNewTextSample();
-#endif
+ void simulateAudioInterruption() override;
- void sourceChanged();
- GstElement* audioSink() const;
+ virtual bool changePipelineState(GstState);
- void setAudioStreamProperties(GObject*);
+#if ENABLE(WEB_AUDIO)
+ AudioSourceProvider* audioSourceProvider() override;
+#endif
- void simulateAudioInterruption();
+ bool isLiveStream() const override { return m_isStreaming; }
- bool changePipelineState(GstState);
+ bool handleSyncMessage(GstMessage*) override;
private:
- MediaPlayerPrivateGStreamer(MediaPlayer*);
-
- static PassOwnPtr<MediaPlayerPrivateInterface> create(MediaPlayer*);
-
- static void getSupportedTypes(HashSet<String>&);
+ static void getSupportedTypes(HashSet<String, ASCIICaseInsensitiveHash>&);
static MediaPlayer::SupportsType supportsType(const MediaEngineSupportParameters&);
static bool isAvailable();
- GstElement* createAudioSink();
+ WeakPtr<MediaPlayerPrivateGStreamer> createWeakPtr() { return m_weakPtrFactory.createWeakPtr(); }
- float playbackPosition() const;
+ GstElement* createAudioSink() override;
- void cacheDuration();
- void updateStates();
- void asyncStateChangeDone();
+ double playbackPosition() const;
+
+ virtual void updateStates();
+ virtual void asyncStateChangeDone();
void createGSTPlayBin();
bool loadNextLocation();
void mediaLocationChanged(GstMessage*);
- void setDownloadBuffering();
+ virtual void setDownloadBuffering();
void processBufferingStats(GstMessage*);
+#if ENABLE(VIDEO_TRACK) && USE(GSTREAMER_MPEGTS)
+ void processMpegTsSection(GstMpegtsSection*);
+#endif
#if ENABLE(VIDEO_TRACK)
void processTableOfContents(GstMessage*);
- void processTableOfContentsEntry(GstTocEntry*, GstTocEntry* parent);
+ void processTableOfContentsEntry(GstTocEntry*);
#endif
- bool doSeek(gint64 position, float rate, GstSeekFlags seekType);
- void updatePlaybackRate();
+ virtual bool doSeek(gint64 position, float rate, GstSeekFlags seekType);
+ virtual void updatePlaybackRate();
+ String engineDescription() const override { return "GStreamer"; }
+ bool didPassCORSAccessCheck() const override;
+ bool canSaveMediaData() const override;
- virtual String engineDescription() const { return "GStreamer"; }
- virtual bool isLiveStream() const { return m_isStreaming; }
- virtual bool didPassCORSAccessCheck() const;
+ void purgeOldDownloadFiles(const char*);
+ static void uriDecodeBinElementAddedCallback(GstBin*, GstElement*, MediaPlayerPrivateGStreamer*);
+ static void downloadBufferFileCreatedCallback(MediaPlayerPrivateGStreamer*);
-private:
- GRefPtr<GstElement> m_playBin;
+protected:
+ void cacheDuration();
+
+ bool m_buffering;
+ int m_bufferingPercentage;
+ mutable float m_cachedPosition;
+ bool m_canFallBackToLastFinishedSeekPosition;
+ bool m_changingRate;
+ bool m_downloadFinished;
+ bool m_errorOccured;
+ mutable bool m_isEndReached;
+ mutable bool m_isStreaming;
+ mutable gdouble m_durationAtEOS;
+ bool m_paused;
+ float m_playbackRate;
+ GstState m_requestedState;
+ bool m_resetPipeline;
+ bool m_seeking;
+ bool m_seekIsPending;
+ float m_seekTime;
GRefPtr<GstElement> m_source;
+ bool m_volumeAndMuteInitialized;
+
+ void readyTimerFired();
+
+ void notifyPlayerOfVideo();
+ void notifyPlayerOfVideoCaps();
+ void notifyPlayerOfAudio();
+
+#if ENABLE(VIDEO_TRACK)
+ void notifyPlayerOfText();
+ void newTextSample();
+#endif
+
+ void ensureAudioSourceProvider();
+ void setAudioStreamProperties(GObject*);
+
+ static void setAudioStreamPropertiesCallback(MediaPlayerPrivateGStreamer*, GObject*);
+
+ static void sourceChangedCallback(MediaPlayerPrivateGStreamer*);
+ static void videoChangedCallback(MediaPlayerPrivateGStreamer*);
+ static void videoSinkCapsChangedCallback(MediaPlayerPrivateGStreamer*);
+ static void audioChangedCallback(MediaPlayerPrivateGStreamer*);
+#if ENABLE(VIDEO_TRACK)
+ static void textChangedCallback(MediaPlayerPrivateGStreamer*);
+ static GstFlowReturn newTextSampleCallback(MediaPlayerPrivateGStreamer*);
+#endif
+
+private:
+ WeakPtrFactory<MediaPlayerPrivateGStreamer> m_weakPtrFactory;
+
#if ENABLE(VIDEO_TRACK)
GRefPtr<GstElement> m_textAppSink;
GRefPtr<GstPad> m_textAppSinkPad;
#endif
- float m_seekTime;
- bool m_changingRate;
- float m_endTime;
- bool m_isEndReached;
- mutable bool m_isStreaming;
GstStructure* m_mediaLocations;
int m_mediaLocationCurrentIndex;
- bool m_resetPipeline;
- bool m_paused;
bool m_playbackRatePause;
- bool m_seeking;
- bool m_seekIsPending;
float m_timeOfOverlappingSeek;
- bool m_canFallBackToLastFinishedSeekPositon;
- bool m_buffering;
- float m_playbackRate;
float m_lastPlaybackRate;
- bool m_errorOccured;
- mutable gfloat m_mediaDuration;
- bool m_downloadFinished;
- Timer<MediaPlayerPrivateGStreamer> m_fillTimer;
+ Timer m_fillTimer;
float m_maxTimeLoaded;
- int m_bufferingPercentage;
MediaPlayer::Preload m_preload;
bool m_delayingLoad;
- bool m_mediaDurationKnown;
mutable float m_maxTimeLoadedAtLastDidLoadingProgress;
- bool m_volumeAndMuteInitialized;
bool m_hasVideo;
bool m_hasAudio;
- guint m_audioTimerHandler;
- guint m_textTimerHandler;
- guint m_videoTimerHandler;
- guint m_videoCapsTimerHandler;
- guint m_readyTimerHandler;
- mutable long m_totalBytes;
+ RunLoop::Timer<MediaPlayerPrivateGStreamer> m_readyTimerHandler;
+ mutable unsigned long long m_totalBytes;
URL m_url;
bool m_preservesPitch;
- GstState m_requestedState;
+#if ENABLE(WEB_AUDIO)
+ std::unique_ptr<AudioSourceProviderGStreamer> m_audioSourceProvider;
+#endif
GRefPtr<GstElement> m_autoAudioSink;
- bool m_missingPlugins;
+ GRefPtr<GstElement> m_downloadBuffer;
+ RefPtr<MediaPlayerRequestInstallMissingPluginsCallback> m_missingPluginsCallback;
#if ENABLE(VIDEO_TRACK)
Vector<RefPtr<AudioTrackPrivateGStreamer>> m_audioTracks;
Vector<RefPtr<InbandTextTrackPrivateGStreamer>> m_textTracks;
Vector<RefPtr<VideoTrackPrivateGStreamer>> m_videoTracks;
- RefPtr<InbandTextTrackPrivate> m_chaptersTrack;
+ RefPtr<InbandMetadataTextTrackPrivateGStreamer> m_chaptersTrack;
#endif
-#if ENABLE(MEDIA_SOURCE)
- RefPtr<HTMLMediaSource> m_mediaSource;
+#if ENABLE(VIDEO_TRACK) && USE(GSTREAMER_MPEGTS)
+ HashMap<AtomicString, RefPtr<InbandMetadataTextTrackPrivateGStreamer>> m_metadataTracks;
#endif
+ virtual bool isMediaSource() const { return false; }
};
}
diff --git a/Source/WebCore/platform/graphics/gstreamer/MediaPlayerPrivateGStreamerBase.cpp b/Source/WebCore/platform/graphics/gstreamer/MediaPlayerPrivateGStreamerBase.cpp
index c6564730a..9332aab49 100644
--- a/Source/WebCore/platform/graphics/gstreamer/MediaPlayerPrivateGStreamerBase.cpp
+++ b/Source/WebCore/platform/graphics/gstreamer/MediaPlayerPrivateGStreamerBase.cpp
@@ -3,7 +3,8 @@
* Copyright (C) 2007 Collabora Ltd. All rights reserved.
* Copyright (C) 2007 Alp Toker <alp@atoker.com>
* Copyright (C) 2009 Gustavo Noronha Silva <gns@gnome.org>
- * Copyright (C) 2009, 2010 Igalia S.L
+ * Copyright (C) 2009, 2010, 2015, 2016 Igalia S.L
+ * Copyright (C) 2015, 2016 Metrological Group B.V.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
@@ -26,7 +27,6 @@
#if ENABLE(VIDEO) && USE(GSTREAMER)
-#include "ColorSpace.h"
#include "GStreamerUtilities.h"
#include "GraphicsContext.h"
#include "GraphicsTypes.h"
@@ -37,16 +37,71 @@
#include "NotImplemented.h"
#include "VideoSinkGStreamer.h"
#include "WebKitWebSourceGStreamer.h"
-#include <gst/gst.h>
-#include <wtf/gobject/GMutexLocker.h>
+#include <wtf/glib/GMutexLocker.h>
+#include <wtf/glib/GUniquePtr.h>
+#include <wtf/text/AtomicString.h>
#include <wtf/text/CString.h>
+#include <wtf/MathExtras.h>
#include <gst/audio/streamvolume.h>
#include <gst/video/gstvideometa.h>
-#if GST_CHECK_VERSION(1, 1, 0) && USE(ACCELERATED_COMPOSITING) && USE(TEXTURE_MAPPER_GL)
+#if USE(GSTREAMER_GL)
+#include <gst/app/gstappsink.h>
+#define GST_USE_UNSTABLE_API
+#include <gst/gl/gl.h>
+#undef GST_USE_UNSTABLE_API
+
+#include "GLContext.h"
+#if USE(GLX)
+#include "GLContextGLX.h"
+#include <gst/gl/x11/gstgldisplay_x11.h>
+#endif
+
+#if USE(EGL)
+#include "GLContextEGL.h"
+#include <gst/gl/egl/gstgldisplay_egl.h>
+#endif
+
+#if PLATFORM(X11)
+#include "PlatformDisplayX11.h"
+#endif
+
+#if PLATFORM(WAYLAND)
+#include "PlatformDisplayWayland.h"
+#endif
+
+// gstglapi.h may include eglplatform.h and it includes X.h, which
+// defines None, breaking MediaPlayer::None enum
+#if PLATFORM(X11) && GST_GL_HAVE_PLATFORM_EGL
+#undef None
+#endif // PLATFORM(X11) && GST_GL_HAVE_PLATFORM_EGL
+#include "VideoTextureCopierGStreamer.h"
+#endif // USE(GSTREAMER_GL)
+
+#if USE(TEXTURE_MAPPER_GL)
+#include "BitmapTextureGL.h"
+#include "BitmapTexturePool.h"
#include "TextureMapperGL.h"
#endif
+#if USE(COORDINATED_GRAPHICS_THREADED)
+#include "TextureMapperPlatformLayerBuffer.h"
+#endif
+
+#if USE(CAIRO) && ENABLE(ACCELERATED_2D_CANVAS)
+#include <cairo-gl.h>
+#endif
+
+#if ENABLE(LEGACY_ENCRYPTED_MEDIA) || ENABLE(ENCRYPTED_MEDIA)
+#include "SharedBuffer.h"
+#include "WebKitClearKeyDecryptorGStreamer.h"
+#if ENABLE(LEGACY_ENCRYPTED_MEDIA)
+#include "UUID.h"
+#include <runtime/JSCInlines.h>
+#include <runtime/TypedArrayInlines.h>
+#include <runtime/Uint8Array.h>
+#endif
+#endif
GST_DEBUG_CATEGORY(webkit_media_player_debug);
#define GST_CAT_DEFAULT webkit_media_player_debug
@@ -55,6 +110,22 @@ using namespace std;
namespace WebCore {
+#if ENABLE(LEGACY_ENCRYPTED_MEDIA)
+static AtomicString keySystemIdToUuid(const AtomicString&);
+#endif
+
+void registerWebKitGStreamerElements()
+{
+#if ENABLE(LEGACY_ENCRYPTED_MEDIA) || ENABLE(ENCRYPTED_MEDIA)
+ if (!webkitGstCheckVersion(1, 6, 1))
+ return;
+
+ GRefPtr<GstElementFactory> clearKeyDecryptorFactory = gst_element_factory_find("webkitclearkey");
+ if (!clearKeyDecryptorFactory)
+ gst_element_register(nullptr, "webkitclearkey", GST_RANK_PRIMARY + 100, WEBKIT_TYPE_MEDIA_CK_DECRYPT);
+#endif
+}
+
static int greatestCommonDivisor(int a, int b)
{
while (b) {
@@ -66,112 +137,345 @@ static int greatestCommonDivisor(int a, int b)
return ABS(a);
}
-static void mediaPlayerPrivateVolumeChangedCallback(GObject*, GParamSpec*, MediaPlayerPrivateGStreamerBase* player)
+#if USE(TEXTURE_MAPPER_GL)
+static inline TextureMapperGL::Flags texMapFlagFromOrientation(const ImageOrientation& orientation)
{
- // This is called when m_volumeElement receives the notify::volume signal.
- LOG_MEDIA_MESSAGE("Volume changed to: %f", player->volume());
- player->volumeChanged();
+ switch (orientation) {
+ case DefaultImageOrientation:
+ return 0;
+ case OriginRightTop:
+ return TextureMapperGL::ShouldRotateTexture90;
+ case OriginBottomRight:
+ return TextureMapperGL::ShouldRotateTexture180;
+ case OriginLeftBottom:
+ return TextureMapperGL::ShouldRotateTexture270;
+ default:
+ ASSERT_NOT_REACHED();
+ }
+
+ return 0;
}
+#endif
+
+#if USE(COORDINATED_GRAPHICS_THREADED) && USE(GSTREAMER_GL)
+class GstVideoFrameHolder : public TextureMapperPlatformLayerBuffer::UnmanagedBufferDataHolder {
+public:
+ explicit GstVideoFrameHolder(GstSample* sample, TextureMapperGL::Flags flags)
+ {
+ GstVideoInfo videoInfo;
+ if (UNLIKELY(!getSampleVideoInfo(sample, videoInfo)))
+ return;
+
+ m_size = IntSize(GST_VIDEO_INFO_WIDTH(&videoInfo), GST_VIDEO_INFO_HEIGHT(&videoInfo));
+ m_flags = flags | (GST_VIDEO_INFO_HAS_ALPHA(&videoInfo) ? TextureMapperGL::ShouldBlend : 0);
+
+ GstBuffer* buffer = gst_sample_get_buffer(sample);
+ if (UNLIKELY(!gst_video_frame_map(&m_videoFrame, &videoInfo, buffer, static_cast<GstMapFlags>(GST_MAP_READ | GST_MAP_GL))))
+ return;
-static gboolean mediaPlayerPrivateVolumeChangeTimeoutCallback(MediaPlayerPrivateGStreamerBase* player)
+ m_textureID = *reinterpret_cast<GLuint*>(m_videoFrame.data[0]);
+ m_isValid = true;
+ }
+
+ virtual ~GstVideoFrameHolder()
+ {
+ if (UNLIKELY(!m_isValid))
+ return;
+
+ gst_video_frame_unmap(&m_videoFrame);
+ }
+
+ const IntSize& size() const { return m_size; }
+ TextureMapperGL::Flags flags() const { return m_flags; }
+ GLuint textureID() const { return m_textureID; }
+ bool isValid() const { return m_isValid; }
+
+private:
+ GstVideoFrame m_videoFrame;
+ IntSize m_size;
+ TextureMapperGL::Flags m_flags;
+ GLuint m_textureID;
+ bool m_isValid { false };
+};
+#endif // USE(COORDINATED_GRAPHICS_THREADED) && USE(GSTREAMER_GL)
+
+MediaPlayerPrivateGStreamerBase::MediaPlayerPrivateGStreamerBase(MediaPlayer* player)
+ : m_notifier(MainThreadNotifier<MainThreadNotification>::create())
+ , m_player(player)
+ , m_fpsSink(nullptr)
+ , m_readyState(MediaPlayer::HaveNothing)
+ , m_networkState(MediaPlayer::Empty)
+#if USE(GSTREAMER_GL) || USE(COORDINATED_GRAPHICS_THREADED)
+ , m_drawTimer(RunLoop::main(), this, &MediaPlayerPrivateGStreamerBase::repaint)
+#endif
+ , m_usingFallbackVideoSink(false)
+#if ENABLE(LEGACY_ENCRYPTED_MEDIA)
+ , m_cdmSession(nullptr)
+#endif
{
- // This is the callback of the timeout source created in ::volumeChanged.
- player->notifyPlayerOfVolumeChange();
- return FALSE;
+ g_mutex_init(&m_sampleMutex);
+#if USE(COORDINATED_GRAPHICS_THREADED)
+ m_platformLayerProxy = adoptRef(new TextureMapperPlatformLayerProxy());
+#endif
}
-static void mediaPlayerPrivateMuteChangedCallback(GObject*, GParamSpec*, MediaPlayerPrivateGStreamerBase* player)
+MediaPlayerPrivateGStreamerBase::~MediaPlayerPrivateGStreamerBase()
{
- // This is called when m_volumeElement receives the notify::mute signal.
- player->muteChanged();
+#if ENABLE(LEGACY_ENCRYPTED_MEDIA)
+ m_protectionCondition.notifyOne();
+#endif
+
+ m_notifier->invalidate();
+
+ cancelRepaint();
+
+ if (m_videoSink) {
+ g_signal_handlers_disconnect_matched(m_videoSink.get(), G_SIGNAL_MATCH_DATA, 0, 0, nullptr, nullptr, this);
+#if USE(GSTREAMER_GL)
+ if (GST_IS_BIN(m_videoSink.get())) {
+ GRefPtr<GstElement> appsink = adoptGRef(gst_bin_get_by_name(GST_BIN_CAST(m_videoSink.get()), "webkit-gl-video-sink"));
+ g_signal_handlers_disconnect_by_data(appsink.get(), this);
+ }
+#endif
+ }
+
+ g_mutex_clear(&m_sampleMutex);
+
+ m_player = nullptr;
+
+ if (m_volumeElement)
+ g_signal_handlers_disconnect_matched(m_volumeElement.get(), G_SIGNAL_MATCH_DATA, 0, 0, nullptr, nullptr, this);
+
+#if USE(TEXTURE_MAPPER_GL) && !USE(COORDINATED_GRAPHICS)
+ if (client())
+ client()->platformLayerWillBeDestroyed();
+#endif
+
+#if ENABLE(LEGACY_ENCRYPTED_MEDIA)
+ m_cdmSession = nullptr;
+#endif
+
+ if (m_pipeline)
+ gst_element_set_state(m_pipeline.get(), GST_STATE_NULL);
}
-static gboolean mediaPlayerPrivateMuteChangeTimeoutCallback(MediaPlayerPrivateGStreamerBase* player)
+void MediaPlayerPrivateGStreamerBase::setPipeline(GstElement* pipeline)
{
- // This is the callback of the timeout source created in ::muteChanged.
- player->notifyPlayerOfMute();
- return FALSE;
+ m_pipeline = pipeline;
}
-static void mediaPlayerPrivateRepaintCallback(WebKitVideoSink*, GstBuffer *buffer, MediaPlayerPrivateGStreamerBase* playerPrivate)
+#if ENABLE(LEGACY_ENCRYPTED_MEDIA)
+static std::pair<Vector<GRefPtr<GstEvent>>, Vector<String>> extractEventsAndSystemsFromMessage(GstMessage* message)
{
- playerPrivate->triggerRepaint(buffer);
+ const GstStructure* structure = gst_message_get_structure(message);
+
+ const GValue* streamEncryptionAllowedSystemsValue = gst_structure_get_value(structure, "stream-encryption-systems");
+ ASSERT(streamEncryptionAllowedSystemsValue && G_VALUE_HOLDS(streamEncryptionAllowedSystemsValue, G_TYPE_STRV));
+ const char** streamEncryptionAllowedSystems = reinterpret_cast<const char**>(g_value_get_boxed(streamEncryptionAllowedSystemsValue));
+ ASSERT(streamEncryptionAllowedSystems);
+ Vector<String> streamEncryptionAllowedSystemsVector;
+ unsigned i;
+ for (i = 0; streamEncryptionAllowedSystems[i]; ++i)
+ streamEncryptionAllowedSystemsVector.append(streamEncryptionAllowedSystems[i]);
+
+ const GValue* streamEncryptionEventsList = gst_structure_get_value(structure, "stream-encryption-events");
+ ASSERT(streamEncryptionEventsList && GST_VALUE_HOLDS_LIST(streamEncryptionEventsList));
+ unsigned streamEncryptionEventsListSize = gst_value_list_get_size(streamEncryptionEventsList);
+ Vector<GRefPtr<GstEvent>> streamEncryptionEventsVector;
+ for (i = 0; i < streamEncryptionEventsListSize; ++i)
+ streamEncryptionEventsVector.append(GRefPtr<GstEvent>(static_cast<GstEvent*>(g_value_get_boxed(gst_value_list_get_value(streamEncryptionEventsList, i)))));
+
+ return std::make_pair(streamEncryptionEventsVector, streamEncryptionAllowedSystemsVector);
}
-
-MediaPlayerPrivateGStreamerBase::MediaPlayerPrivateGStreamerBase(MediaPlayer* player)
- : m_player(player)
- , m_fpsSink(0)
- , m_readyState(MediaPlayer::HaveNothing)
- , m_networkState(MediaPlayer::Empty)
- , m_buffer(0)
- , m_volumeTimerHandler(0)
- , m_muteTimerHandler(0)
- , m_repaintHandler(0)
- , m_volumeSignalHandler(0)
- , m_muteSignalHandler(0)
-{
-#if GLIB_CHECK_VERSION(2, 31, 0)
- m_bufferMutex = new GMutex;
- g_mutex_init(m_bufferMutex);
-#else
- m_bufferMutex = g_mutex_new();
#endif
+
+bool MediaPlayerPrivateGStreamerBase::handleSyncMessage(GstMessage* message)
+{
+ UNUSED_PARAM(message);
+ if (GST_MESSAGE_TYPE(message) != GST_MESSAGE_NEED_CONTEXT)
+ return false;
+
+ const gchar* contextType;
+ gst_message_parse_context_type(message, &contextType);
+
+#if USE(GSTREAMER_GL)
+ GRefPtr<GstContext> elementContext = adoptGRef(requestGLContext(contextType, this));
+ if (elementContext) {
+ gst_element_set_context(GST_ELEMENT(message->src), elementContext.get());
+ return true;
+ }
+#endif // USE(GSTREAMER_GL)
+
+#if ENABLE(LEGACY_ENCRYPTED_MEDIA)
+ if (!g_strcmp0(contextType, "drm-preferred-decryption-system-id")) {
+ if (isMainThread()) {
+ GST_ERROR("can't handle drm-preferred-decryption-system-id need context message in the main thread");
+ ASSERT_NOT_REACHED();
+ return false;
+ }
+ GST_DEBUG("handling drm-preferred-decryption-system-id need context message");
+ std::pair<Vector<GRefPtr<GstEvent>>, Vector<String>> streamEncryptionInformation = extractEventsAndSystemsFromMessage(message);
+ GST_TRACE("found %" G_GSIZE_FORMAT " protection events", streamEncryptionInformation.first.size());
+ Vector<uint8_t> concatenatedInitDataChunks;
+ unsigned concatenatedInitDataChunksNumber = 0;
+ String eventKeySystemIdString;
+ for (auto& event : streamEncryptionInformation.first) {
+ GST_TRACE("handling protection event %u", GST_EVENT_SEQNUM(event.get()));
+ const char* eventKeySystemId = nullptr;
+ GstBuffer* data = nullptr;
+ gst_event_parse_protection(event.get(), &eventKeySystemId, &data, nullptr);
+
+ // Here we receive the DRM init data from the pipeline: we will emit
+ // the needkey event with that data and the browser might create a
+ // CDMSession from this event handler. If such a session was created
+ // We will emit the message event from the session to provide the
+ // DRM challenge to the browser and wait for an update. If on the
+ // contrary no session was created we won't wait and let the pipeline
+ // error out by itself.
+ GstMapInfo mapInfo;
+ if (!gst_buffer_map(data, &mapInfo, GST_MAP_READ)) {
+ GST_WARNING("cannot map %s protection data", eventKeySystemId);
+ break;
+ }
+
+ GST_TRACE("appending init data for %s of size %" G_GSIZE_FORMAT, eventKeySystemId, mapInfo.size);
+ GST_MEMDUMP("init data", reinterpret_cast<const unsigned char *>(mapInfo.data), mapInfo.size);
+ concatenatedInitDataChunks.append(mapInfo.data, mapInfo.size);
+ ++concatenatedInitDataChunksNumber;
+ eventKeySystemIdString = eventKeySystemId;
+ if (streamEncryptionInformation.second.contains(eventKeySystemId)) {
+ GST_TRACE("considering init data handled for %s", eventKeySystemId);
+ m_handledProtectionEvents.add(GST_EVENT_SEQNUM(event.get()));
+ }
+ gst_buffer_unmap(data, &mapInfo);
+ }
+
+ if (!concatenatedInitDataChunksNumber)
+ return false;
+
+ if (concatenatedInitDataChunksNumber > 1)
+ eventKeySystemIdString = emptyString();
+
+ RunLoop::main().dispatch([this, eventKeySystemIdString, initData = WTFMove(concatenatedInitDataChunks)] {
+ GST_DEBUG("scheduling keyNeeded event for %s with concatenated init datas size of %" G_GSIZE_FORMAT, eventKeySystemIdString.utf8().data(), initData.size());
+ GST_MEMDUMP("init datas", initData.data(), initData.size());
+
+ // FIXME: Provide a somehow valid sessionId.
+ RefPtr<Uint8Array> initDataArray = Uint8Array::create(initData.data(), initData.size());
+ needKey(initDataArray);
+ });
+
+ GST_INFO("waiting for a key request to arrive");
+ LockHolder lock(m_protectionMutex);
+ m_protectionCondition.waitFor(m_protectionMutex, Seconds(4), [this] {
+ return !this->m_lastGenerateKeyRequestKeySystemUuid.isEmpty();
+ });
+ if (!m_lastGenerateKeyRequestKeySystemUuid.isEmpty()) {
+ GST_INFO("got a key request, continuing with %s on %s", m_lastGenerateKeyRequestKeySystemUuid.utf8().data(), GST_MESSAGE_SRC_NAME(message));
+
+ GRefPtr<GstContext> context = adoptGRef(gst_context_new("drm-preferred-decryption-system-id", FALSE));
+ GstStructure* contextStructure = gst_context_writable_structure(context.get());
+ gst_structure_set(contextStructure, "decryption-system-id", G_TYPE_STRING, m_lastGenerateKeyRequestKeySystemUuid.utf8().data(), nullptr);
+ gst_element_set_context(GST_ELEMENT(GST_MESSAGE_SRC(message)), context.get());
+ } else
+ GST_WARNING("did not get a proper key request");
+
+ return true;
+ }
+#endif // ENABLE(LEGACY_ENCRYPTED_MEDIA)
+
+ return false;
}
-MediaPlayerPrivateGStreamerBase::~MediaPlayerPrivateGStreamerBase()
+#if USE(GSTREAMER_GL)
+GstContext* MediaPlayerPrivateGStreamerBase::requestGLContext(const gchar* contextType, MediaPlayerPrivateGStreamerBase* player)
{
- if (m_repaintHandler) {
- g_signal_handler_disconnect(m_webkitVideoSink.get(), m_repaintHandler);
- m_repaintHandler = 0;
+ if (!player->ensureGstGLContext())
+ return nullptr;
+
+ if (!g_strcmp0(contextType, GST_GL_DISPLAY_CONTEXT_TYPE)) {
+ GstContext* displayContext = gst_context_new(GST_GL_DISPLAY_CONTEXT_TYPE, TRUE);
+ gst_context_set_gl_display(displayContext, player->gstGLDisplay());
+ return displayContext;
}
-#if GLIB_CHECK_VERSION(2, 31, 0)
- g_mutex_clear(m_bufferMutex);
- delete m_bufferMutex;
+ if (!g_strcmp0(contextType, "gst.gl.app_context")) {
+ GstContext* appContext = gst_context_new("gst.gl.app_context", TRUE);
+ GstStructure* structure = gst_context_writable_structure(appContext);
+#if GST_CHECK_VERSION(1, 11, 0)
+ gst_structure_set(structure, "context", GST_TYPE_GL_CONTEXT, player->gstGLContext(), nullptr);
#else
- g_mutex_free(m_bufferMutex);
+ gst_structure_set(structure, "context", GST_GL_TYPE_CONTEXT, player->gstGLContext(), nullptr);
#endif
+ return appContext;
+ }
- if (m_buffer)
- gst_buffer_unref(m_buffer);
- m_buffer = 0;
-
- m_player = 0;
+ return nullptr;
+}
- if (m_muteTimerHandler)
- g_source_remove(m_muteTimerHandler);
+bool MediaPlayerPrivateGStreamerBase::ensureGstGLContext()
+{
+ if (m_glContext)
+ return true;
+
+ auto& sharedDisplay = PlatformDisplay::sharedDisplayForCompositing();
+ if (!m_glDisplay) {
+#if PLATFORM(X11)
+#if USE(GLX)
+ if (is<PlatformDisplayX11>(sharedDisplay))
+ m_glDisplay = GST_GL_DISPLAY(gst_gl_display_x11_new_with_display(downcast<PlatformDisplayX11>(sharedDisplay).native()));
+#elif USE(EGL)
+ if (is<PlatformDisplayX11>(sharedDisplay))
+ m_glDisplay = GST_GL_DISPLAY(gst_gl_display_egl_new_with_egl_display(downcast<PlatformDisplayX11>(sharedDisplay).eglDisplay()));
+#endif
+#endif
- if (m_volumeTimerHandler)
- g_source_remove(m_volumeTimerHandler);
+#if PLATFORM(WAYLAND)
+ if (is<PlatformDisplayWayland>(sharedDisplay))
+ m_glDisplay = GST_GL_DISPLAY(gst_gl_display_egl_new_with_egl_display(downcast<PlatformDisplayWayland>(sharedDisplay).eglDisplay()));
+#endif
- if (m_volumeSignalHandler) {
- g_signal_handler_disconnect(m_volumeElement.get(), m_volumeSignalHandler);
- m_volumeSignalHandler = 0;
+ ASSERT(m_glDisplay);
}
- if (m_muteSignalHandler) {
- g_signal_handler_disconnect(m_volumeElement.get(), m_muteSignalHandler);
- m_muteSignalHandler = 0;
- }
+ GLContext* webkitContext = sharedDisplay.sharingGLContext();
+ // EGL and GLX are mutually exclusive, no need for ifdefs here.
+ GstGLPlatform glPlatform = webkitContext->isEGLContext() ? GST_GL_PLATFORM_EGL : GST_GL_PLATFORM_GLX;
-#if USE(ACCELERATED_COMPOSITING) && USE(TEXTURE_MAPPER_GL) && !USE(COORDINATED_GRAPHICS)
- if (client())
- client()->platformLayerWillBeDestroyed();
+#if USE(OPENGL_ES_2)
+ GstGLAPI glAPI = GST_GL_API_GLES2;
+#elif USE(OPENGL)
+ GstGLAPI glAPI = GST_GL_API_OPENGL;
+#else
+ ASSERT_NOT_REACHED();
#endif
+
+ PlatformGraphicsContext3D contextHandle = webkitContext->platformContext();
+ if (!contextHandle)
+ return false;
+
+ m_glContext = gst_gl_context_new_wrapped(m_glDisplay.get(), reinterpret_cast<guintptr>(contextHandle), glPlatform, glAPI);
+
+ return true;
}
+#endif // USE(GSTREAMER_GL)
// Returns the size of the video
-IntSize MediaPlayerPrivateGStreamerBase::naturalSize() const
+FloatSize MediaPlayerPrivateGStreamerBase::naturalSize() const
{
if (!hasVideo())
- return IntSize();
+ return FloatSize();
if (!m_videoSize.isEmpty())
return m_videoSize;
- GRefPtr<GstCaps> caps = currentVideoSinkCaps();
+ WTF::GMutexLocker<GMutex> lock(m_sampleMutex);
+ if (!GST_IS_SAMPLE(m_sample.get()))
+ return FloatSize();
+
+ GstCaps* caps = gst_sample_get_caps(m_sample.get());
if (!caps)
- return IntSize();
+ return FloatSize();
// TODO: handle possible clean aperture data. See
@@ -184,11 +488,19 @@ IntSize MediaPlayerPrivateGStreamerBase::naturalSize() const
int pixelAspectRatioNumerator, pixelAspectRatioDenominator, stride;
IntSize originalSize;
GstVideoFormat format;
- if (!getVideoSizeAndFormatFromCaps(caps.get(), originalSize, format, pixelAspectRatioNumerator, pixelAspectRatioDenominator, stride))
- return IntSize();
+ if (!getVideoSizeAndFormatFromCaps(caps, originalSize, format, pixelAspectRatioNumerator, pixelAspectRatioDenominator, stride))
+ return FloatSize();
+
+#if USE(TEXTURE_MAPPER_GL)
+ // When using accelerated compositing, if the video is tagged as rotated 90 or 270 degrees, swap width and height.
+ if (m_renderingCanBeAccelerated) {
+ if (m_videoSourceOrientation.usesWidthAsHeight())
+ originalSize = originalSize.transposedSize();
+ }
+#endif
- LOG_MEDIA_MESSAGE("Original video size: %dx%d", originalSize.width(), originalSize.height());
- LOG_MEDIA_MESSAGE("Pixel aspect ratio: %d/%d", pixelAspectRatioNumerator, pixelAspectRatioDenominator);
+ GST_DEBUG("Original video size: %dx%d", originalSize.width(), originalSize.height());
+ GST_DEBUG("Pixel aspect ratio: %d/%d", pixelAspectRatioNumerator, pixelAspectRatioDenominator);
// Calculate DAR based on PAR and video size.
int displayWidth = originalSize.width() * pixelAspectRatioNumerator;
@@ -202,21 +514,21 @@ IntSize MediaPlayerPrivateGStreamerBase::naturalSize() const
// Apply DAR to original video size. This is the same behavior as in xvimagesink's setcaps function.
guint64 width = 0, height = 0;
if (!(originalSize.height() % displayHeight)) {
- LOG_MEDIA_MESSAGE("Keeping video original height");
+ GST_DEBUG("Keeping video original height");
width = gst_util_uint64_scale_int(originalSize.height(), displayWidth, displayHeight);
height = static_cast<guint64>(originalSize.height());
} else if (!(originalSize.width() % displayWidth)) {
- LOG_MEDIA_MESSAGE("Keeping video original width");
+ GST_DEBUG("Keeping video original width");
height = gst_util_uint64_scale_int(originalSize.width(), displayHeight, displayWidth);
width = static_cast<guint64>(originalSize.width());
} else {
- LOG_MEDIA_MESSAGE("Approximating while keeping original video height");
+ GST_DEBUG("Approximating while keeping original video height");
width = gst_util_uint64_scale_int(originalSize.height(), displayWidth, displayHeight);
height = static_cast<guint64>(originalSize.height());
}
- LOG_MEDIA_MESSAGE("Natural size: %" G_GUINT64_FORMAT "x%" G_GUINT64_FORMAT, width, height);
- m_videoSize = IntSize(static_cast<int>(width), static_cast<int>(height));
+ GST_DEBUG("Natural size: %" G_GUINT64_FORMAT "x%" G_GUINT64_FORMAT, width, height);
+ m_videoSize = FloatSize(static_cast<int>(width), static_cast<int>(height));
return m_videoSize;
}
@@ -225,7 +537,7 @@ void MediaPlayerPrivateGStreamerBase::setVolume(float volume)
if (!m_volumeElement)
return;
- LOG_MEDIA_MESSAGE("Setting volume: %f", volume);
+ GST_DEBUG("Setting volume: %f", volume);
gst_stream_volume_set_volume(m_volumeElement.get(), GST_STREAM_VOLUME_FORMAT_CUBIC, static_cast<double>(volume));
}
@@ -240,8 +552,6 @@ float MediaPlayerPrivateGStreamerBase::volume() const
void MediaPlayerPrivateGStreamerBase::notifyPlayerOfVolumeChange()
{
- m_volumeTimerHandler = 0;
-
if (!m_player || !m_volumeElement)
return;
double volume;
@@ -253,11 +563,12 @@ void MediaPlayerPrivateGStreamerBase::notifyPlayerOfVolumeChange()
m_player->volumeChanged(static_cast<float>(volume));
}
-void MediaPlayerPrivateGStreamerBase::volumeChanged()
+void MediaPlayerPrivateGStreamerBase::volumeChangedCallback(MediaPlayerPrivateGStreamerBase* player)
{
- if (m_volumeTimerHandler)
- g_source_remove(m_volumeTimerHandler);
- m_volumeTimerHandler = g_idle_add_full(G_PRIORITY_DEFAULT, reinterpret_cast<GSourceFunc>(mediaPlayerPrivateVolumeChangeTimeoutCallback), this, 0);
+ // This is called when m_volumeElement receives the notify::volume signal.
+ GST_DEBUG("Volume changed to: %f", player->volume());
+
+ player->m_notifier->notify(MainThreadNotification::VolumeChanged, [player] { player->notifyPlayerOfVolumeChange(); });
}
MediaPlayer::NetworkState MediaPlayerPrivateGStreamerBase::networkState() const
@@ -280,7 +591,7 @@ void MediaPlayerPrivateGStreamerBase::setMuted(bool muted)
if (!m_volumeElement)
return;
- g_object_set(m_volumeElement.get(), "mute", muted, NULL);
+ g_object_set(m_volumeElement.get(), "mute", muted, nullptr);
}
bool MediaPlayerPrivateGStreamerBase::muted() const
@@ -289,145 +600,456 @@ bool MediaPlayerPrivateGStreamerBase::muted() const
return false;
bool muted;
- g_object_get(m_volumeElement.get(), "mute", &muted, NULL);
+ g_object_get(m_volumeElement.get(), "mute", &muted, nullptr);
return muted;
}
void MediaPlayerPrivateGStreamerBase::notifyPlayerOfMute()
{
- m_muteTimerHandler = 0;
-
if (!m_player || !m_volumeElement)
return;
gboolean muted;
- g_object_get(m_volumeElement.get(), "mute", &muted, NULL);
+ g_object_get(m_volumeElement.get(), "mute", &muted, nullptr);
m_player->muteChanged(static_cast<bool>(muted));
}
-void MediaPlayerPrivateGStreamerBase::muteChanged()
+void MediaPlayerPrivateGStreamerBase::muteChangedCallback(MediaPlayerPrivateGStreamerBase* player)
{
- if (m_muteTimerHandler)
- g_source_remove(m_muteTimerHandler);
- m_muteTimerHandler = g_idle_add_full(G_PRIORITY_DEFAULT, reinterpret_cast<GSourceFunc>(mediaPlayerPrivateMuteChangeTimeoutCallback), this, 0);
+ // This is called when m_volumeElement receives the notify::mute signal.
+ player->m_notifier->notify(MainThreadNotification::MuteChanged, [player] { player->notifyPlayerOfMute(); });
}
-
-#if USE(ACCELERATED_COMPOSITING) && USE(TEXTURE_MAPPER_GL) && !USE(COORDINATED_GRAPHICS)
-PassRefPtr<BitmapTexture> MediaPlayerPrivateGStreamerBase::updateTexture(TextureMapper* textureMapper)
+void MediaPlayerPrivateGStreamerBase::acceleratedRenderingStateChanged()
{
- WTF::GMutexLocker lock(m_bufferMutex);
- if (!m_buffer)
- return nullptr;
-
- GRefPtr<GstCaps> caps = currentVideoSinkCaps();
- if (!caps)
- return nullptr;
-
- GstVideoInfo videoInfo;
- gst_video_info_init(&videoInfo);
- if (!gst_video_info_from_caps(&videoInfo, caps.get()))
- return nullptr;
+ m_renderingCanBeAccelerated = m_player && m_player->client().mediaPlayerAcceleratedCompositingEnabled() && m_player->client().mediaPlayerRenderingCanBeAccelerated(m_player);
+}
- IntSize size = IntSize(GST_VIDEO_INFO_WIDTH(&videoInfo), GST_VIDEO_INFO_HEIGHT(&videoInfo));
- RefPtr<BitmapTexture> texture = textureMapper->acquireTextureFromPool(size, GST_VIDEO_INFO_HAS_ALPHA(&videoInfo) ? BitmapTexture::SupportsAlpha : BitmapTexture::NoFlag);
+#if USE(TEXTURE_MAPPER_GL)
+void MediaPlayerPrivateGStreamerBase::updateTexture(BitmapTextureGL& texture, GstVideoInfo& videoInfo)
+{
+ GstBuffer* buffer = gst_sample_get_buffer(m_sample.get());
-#if GST_CHECK_VERSION(1, 1, 0)
GstVideoGLTextureUploadMeta* meta;
- if ((meta = gst_buffer_get_video_gl_texture_upload_meta(m_buffer))) {
+ if ((meta = gst_buffer_get_video_gl_texture_upload_meta(buffer))) {
if (meta->n_textures == 1) { // BRGx & BGRA formats use only one texture.
- const BitmapTextureGL* textureGL = static_cast<const BitmapTextureGL*>(texture.get());
- guint ids[4] = { textureGL->id(), 0, 0, 0 };
+ guint ids[4] = { texture.id(), 0, 0, 0 };
if (gst_video_gl_texture_upload_meta_upload(meta, ids))
- return texture;
+ return;
}
}
-#endif
// Right now the TextureMapper only supports chromas with one plane
ASSERT(GST_VIDEO_INFO_N_PLANES(&videoInfo) == 1);
GstVideoFrame videoFrame;
- if (!gst_video_frame_map(&videoFrame, &videoInfo, m_buffer, GST_MAP_READ))
- return nullptr;
+ if (!gst_video_frame_map(&videoFrame, &videoInfo, buffer, GST_MAP_READ))
+ return;
int stride = GST_VIDEO_FRAME_PLANE_STRIDE(&videoFrame, 0);
const void* srcData = GST_VIDEO_FRAME_PLANE_DATA(&videoFrame, 0);
- texture->updateContents(srcData, WebCore::IntRect(WebCore::IntPoint(0, 0), size), WebCore::IntPoint(0, 0), stride, BitmapTexture::UpdateCannotModifyOriginalImageData);
+ texture.updateContents(srcData, WebCore::IntRect(0, 0, GST_VIDEO_INFO_WIDTH(&videoInfo), GST_VIDEO_INFO_HEIGHT(&videoInfo)), WebCore::IntPoint(0, 0), stride, BitmapTexture::UpdateCannotModifyOriginalImageData);
gst_video_frame_unmap(&videoFrame);
-
- return texture;
}
#endif
-void MediaPlayerPrivateGStreamerBase::triggerRepaint(GstBuffer* buffer)
+#if USE(COORDINATED_GRAPHICS_THREADED)
+void MediaPlayerPrivateGStreamerBase::pushTextureToCompositor()
{
- g_return_if_fail(GST_IS_BUFFER(buffer));
+#if !USE(GSTREAMER_GL)
+ class ConditionNotifier {
+ public:
+ ConditionNotifier(Lock& lock, Condition& condition)
+ : m_locker(lock), m_condition(condition)
+ {
+ }
+ ~ConditionNotifier()
+ {
+ m_condition.notifyOne();
+ }
+ private:
+ LockHolder m_locker;
+ Condition& m_condition;
+ };
+ ConditionNotifier notifier(m_drawMutex, m_drawCondition);
+#endif
- {
- WTF::GMutexLocker lock(m_bufferMutex);
- gst_buffer_replace(&m_buffer, buffer);
+ WTF::GMutexLocker<GMutex> lock(m_sampleMutex);
+ if (!GST_IS_SAMPLE(m_sample.get()))
+ return;
+
+ LockHolder holder(m_platformLayerProxy->lock());
+
+ if (!m_platformLayerProxy->isActive()) {
+ // Consume the buffer (so it gets eventually unreffed) but keep the rest of the info.
+ const GstStructure* info = gst_sample_get_info(m_sample.get());
+ GstStructure* infoCopy = nullptr;
+ if (info)
+ infoCopy = gst_structure_copy(info);
+ m_sample = adoptGRef(gst_sample_new(nullptr, gst_sample_get_caps(m_sample.get()),
+ gst_sample_get_segment(m_sample.get()), infoCopy));
+ return;
}
-#if USE(ACCELERATED_COMPOSITING) && USE(TEXTURE_MAPPER_GL) && !USE(COORDINATED_GRAPHICS)
- if (supportsAcceleratedRendering() && m_player->mediaPlayerClient()->mediaPlayerRenderingCanBeAccelerated(m_player) && client()) {
+#if USE(GSTREAMER_GL)
+ std::unique_ptr<GstVideoFrameHolder> frameHolder = std::make_unique<GstVideoFrameHolder>(m_sample.get(), texMapFlagFromOrientation(m_videoSourceOrientation));
+ if (UNLIKELY(!frameHolder->isValid()))
+ return;
+
+ std::unique_ptr<TextureMapperPlatformLayerBuffer> layerBuffer = std::make_unique<TextureMapperPlatformLayerBuffer>(frameHolder->textureID(), frameHolder->size(), frameHolder->flags());
+ layerBuffer->setUnmanagedBufferDataHolder(WTFMove(frameHolder));
+ m_platformLayerProxy->pushNextBuffer(WTFMove(layerBuffer));
+#else
+ GstVideoInfo videoInfo;
+ if (UNLIKELY(!getSampleVideoInfo(m_sample.get(), videoInfo)))
+ return;
+
+ IntSize size = IntSize(GST_VIDEO_INFO_WIDTH(&videoInfo), GST_VIDEO_INFO_HEIGHT(&videoInfo));
+ std::unique_ptr<TextureMapperPlatformLayerBuffer> buffer = m_platformLayerProxy->getAvailableBuffer(size, GraphicsContext3D::DONT_CARE);
+ if (UNLIKELY(!buffer)) {
+ if (UNLIKELY(!m_context3D))
+ m_context3D = GraphicsContext3D::create(GraphicsContext3DAttributes(), nullptr, GraphicsContext3D::RenderToCurrentGLContext);
+
+ auto texture = BitmapTextureGL::create(*m_context3D);
+ texture->reset(size, GST_VIDEO_INFO_HAS_ALPHA(&videoInfo) ? BitmapTexture::SupportsAlpha : BitmapTexture::NoFlag);
+ buffer = std::make_unique<TextureMapperPlatformLayerBuffer>(WTFMove(texture));
+ }
+ updateTexture(buffer->textureGL(), videoInfo);
+ buffer->setExtraFlags(texMapFlagFromOrientation(m_videoSourceOrientation) | (GST_VIDEO_INFO_HAS_ALPHA(&videoInfo) ? TextureMapperGL::ShouldBlend : 0));
+ m_platformLayerProxy->pushNextBuffer(WTFMove(buffer));
+#endif
+}
+#endif
+
+void MediaPlayerPrivateGStreamerBase::repaint()
+{
+ ASSERT(m_sample);
+ ASSERT(isMainThread());
+
+#if USE(TEXTURE_MAPPER_GL) && !USE(COORDINATED_GRAPHICS)
+ if (m_renderingCanBeAccelerated && client()) {
client()->setPlatformLayerNeedsDisplay();
+#if USE(GSTREAMER_GL)
+ LockHolder lock(m_drawMutex);
+ m_drawCondition.notifyOne();
+#endif
return;
}
#endif
m_player->repaint();
+
+#if USE(GSTREAMER_GL) || USE(COORDINATED_GRAPHICS_THREADED)
+ LockHolder lock(m_drawMutex);
+ m_drawCondition.notifyOne();
+#endif
}
-void MediaPlayerPrivateGStreamerBase::setSize(const IntSize& size)
+void MediaPlayerPrivateGStreamerBase::triggerRepaint(GstSample* sample)
{
- m_size = size;
+ bool triggerResize;
+ {
+ WTF::GMutexLocker<GMutex> lock(m_sampleMutex);
+ triggerResize = !m_sample;
+ m_sample = sample;
+ }
+
+ if (triggerResize) {
+ GST_DEBUG("First sample reached the sink, triggering video dimensions update");
+ m_notifier->notify(MainThreadNotification::SizeChanged, [this] { m_player->sizeChanged(); });
+ }
+
+#if USE(COORDINATED_GRAPHICS_THREADED)
+ if (!m_renderingCanBeAccelerated) {
+ LockHolder locker(m_drawMutex);
+ m_drawTimer.startOneShot(0);
+ m_drawCondition.wait(m_drawMutex);
+ return;
+ }
+
+#if USE(GSTREAMER_GL)
+ pushTextureToCompositor();
+#else
+ {
+ LockHolder lock(m_drawMutex);
+ if (!m_platformLayerProxy->scheduleUpdateOnCompositorThread([this] { this->pushTextureToCompositor(); }))
+ return;
+ m_drawCondition.wait(m_drawMutex);
+ }
+#endif
+ return;
+#else
+#if USE(GSTREAMER_GL)
+ {
+ ASSERT(!isMainThread());
+
+ LockHolder locker(m_drawMutex);
+ m_drawTimer.startOneShot(0);
+ m_drawCondition.wait(m_drawMutex);
+ }
+#else
+ repaint();
+#endif
+#endif
}
-void MediaPlayerPrivateGStreamerBase::paint(GraphicsContext* context, const IntRect& rect)
+void MediaPlayerPrivateGStreamerBase::repaintCallback(MediaPlayerPrivateGStreamerBase* player, GstSample* sample)
{
-#if USE(ACCELERATED_COMPOSITING) && USE(TEXTURE_MAPPER_GL) && !USE(COORDINATED_GRAPHICS)
- if (client())
- return;
+ player->triggerRepaint(sample);
+}
+
+void MediaPlayerPrivateGStreamerBase::cancelRepaint()
+{
+#if USE(TEXTURE_MAPPER_GL) || USE(COORDINATED_GRAPHICS_THREADED)
+ m_drawTimer.stop();
+ LockHolder locker(m_drawMutex);
+ m_drawCondition.notifyOne();
#endif
+}
+
+void MediaPlayerPrivateGStreamerBase::repaintCancelledCallback(MediaPlayerPrivateGStreamerBase* player)
+{
+ player->cancelRepaint();
+}
+
+#if USE(GSTREAMER_GL)
+GstFlowReturn MediaPlayerPrivateGStreamerBase::newSampleCallback(GstElement* sink, MediaPlayerPrivateGStreamerBase* player)
+{
+ GRefPtr<GstSample> sample = adoptGRef(gst_app_sink_pull_sample(GST_APP_SINK(sink)));
+ player->triggerRepaint(sample.get());
+ return GST_FLOW_OK;
+}
+
+GstFlowReturn MediaPlayerPrivateGStreamerBase::newPrerollCallback(GstElement* sink, MediaPlayerPrivateGStreamerBase* player)
+{
+ GRefPtr<GstSample> sample = adoptGRef(gst_app_sink_pull_preroll(GST_APP_SINK(sink)));
+ player->triggerRepaint(sample.get());
+ return GST_FLOW_OK;
+}
+#endif
+
+void MediaPlayerPrivateGStreamerBase::setSize(const IntSize& size)
+{
+ m_size = size;
+}
- if (context->paintingDisabled())
+void MediaPlayerPrivateGStreamerBase::paint(GraphicsContext& context, const FloatRect& rect)
+{
+ if (context.paintingDisabled())
return;
if (!m_player->visible())
return;
- WTF::GMutexLocker lock(m_bufferMutex);
- if (!m_buffer)
+ WTF::GMutexLocker<GMutex> lock(m_sampleMutex);
+ if (!GST_IS_SAMPLE(m_sample.get()))
return;
- GRefPtr<GstCaps> caps = currentVideoSinkCaps();
- if (!caps)
- return;
+ ImagePaintingOptions paintingOptions(CompositeCopy);
+ if (m_renderingCanBeAccelerated)
+ paintingOptions.m_orientationDescription.setImageOrientationEnum(m_videoSourceOrientation);
- RefPtr<ImageGStreamer> gstImage = ImageGStreamer::createImage(m_buffer, caps.get());
+ RefPtr<ImageGStreamer> gstImage = ImageGStreamer::createImage(m_sample.get());
if (!gstImage)
return;
- context->drawImage(reinterpret_cast<Image*>(gstImage->image().get()), ColorSpaceSRGB,
- rect, gstImage->rect(), CompositeCopy, ImageOrientationDescription(), false);
+ if (Image* image = reinterpret_cast<Image*>(gstImage->image().get()))
+ context.drawImage(*image, rect, gstImage->rect(), paintingOptions);
}
-#if USE(ACCELERATED_COMPOSITING) && USE(TEXTURE_MAPPER_GL) && !USE(COORDINATED_GRAPHICS)
-void MediaPlayerPrivateGStreamerBase::paintToTextureMapper(TextureMapper* textureMapper, const FloatRect& targetRect, const TransformationMatrix& matrix, float opacity)
+#if USE(TEXTURE_MAPPER_GL) && !USE(COORDINATED_GRAPHICS)
+void MediaPlayerPrivateGStreamerBase::paintToTextureMapper(TextureMapper& textureMapper, const FloatRect& targetRect, const TransformationMatrix& matrix, float opacity)
{
- if (textureMapper->accelerationMode() != TextureMapper::OpenGLMode)
+ if (!m_player->visible())
return;
- if (!m_player->visible())
+ if (m_usingFallbackVideoSink) {
+ RefPtr<BitmapTexture> texture;
+ IntSize size;
+ TextureMapperGL::Flags flags;
+ {
+ WTF::GMutexLocker<GMutex> lock(m_sampleMutex);
+
+ GstVideoInfo videoInfo;
+ if (UNLIKELY(!getSampleVideoInfo(m_sample.get(), videoInfo)))
+ return;
+
+ size = IntSize(GST_VIDEO_INFO_WIDTH(&videoInfo), GST_VIDEO_INFO_HEIGHT(&videoInfo));
+ flags = texMapFlagFromOrientation(m_videoSourceOrientation) | (GST_VIDEO_INFO_HAS_ALPHA(&videoInfo) ? TextureMapperGL::ShouldBlend : 0);
+ texture = textureMapper.acquireTextureFromPool(size, GST_VIDEO_INFO_HAS_ALPHA(&videoInfo) ? BitmapTexture::SupportsAlpha : BitmapTexture::NoFlag);
+ updateTexture(static_cast<BitmapTextureGL&>(*texture), videoInfo);
+ }
+ TextureMapperGL& texmapGL = reinterpret_cast<TextureMapperGL&>(textureMapper);
+ BitmapTextureGL* textureGL = static_cast<BitmapTextureGL*>(texture.get());
+ texmapGL.drawTexture(textureGL->id(), flags, textureGL->size(), targetRect, matrix, opacity);
return;
+ }
+
+#if USE(GSTREAMER_GL)
+ WTF::GMutexLocker<GMutex> lock(m_sampleMutex);
+
+ GstVideoInfo videoInfo;
+ if (!getSampleVideoInfo(m_sample.get(), videoInfo))
+ return;
+
+ GstBuffer* buffer = gst_sample_get_buffer(m_sample.get());
+ GstVideoFrame videoFrame;
+ if (!gst_video_frame_map(&videoFrame, &videoInfo, buffer, static_cast<GstMapFlags>(GST_MAP_READ | GST_MAP_GL)))
+ return;
+
+ unsigned textureID = *reinterpret_cast<unsigned*>(videoFrame.data[0]);
+ TextureMapperGL::Flags flags = texMapFlagFromOrientation(m_videoSourceOrientation) | (GST_VIDEO_INFO_HAS_ALPHA(&videoInfo) ? TextureMapperGL::ShouldBlend : 0);
+
+ IntSize size = IntSize(GST_VIDEO_INFO_WIDTH(&videoInfo), GST_VIDEO_INFO_HEIGHT(&videoInfo));
+ TextureMapperGL& textureMapperGL = reinterpret_cast<TextureMapperGL&>(textureMapper);
+ textureMapperGL.drawTexture(textureID, flags, size, targetRect, matrix, opacity);
+ gst_video_frame_unmap(&videoFrame);
+#endif
+}
+#endif
+
+#if USE(GSTREAMER_GL)
+#if USE(CAIRO) && ENABLE(ACCELERATED_2D_CANVAS)
+// This should be called with the sample mutex locked.
+GLContext* MediaPlayerPrivateGStreamerBase::prepareContextForCairoPaint(GstVideoInfo& videoInfo, IntSize& size, IntSize& rotatedSize)
+{
+ if (!getSampleVideoInfo(m_sample.get(), videoInfo))
+ return nullptr;
+
+ GLContext* context = PlatformDisplay::sharedDisplayForCompositing().sharingGLContext();
+ context->makeContextCurrent();
+
+ // Thread-awareness is a huge performance hit on non-Intel drivers.
+ cairo_gl_device_set_thread_aware(context->cairoDevice(), FALSE);
+
+ size = IntSize(GST_VIDEO_INFO_WIDTH(&videoInfo), GST_VIDEO_INFO_HEIGHT(&videoInfo));
+ rotatedSize = m_videoSourceOrientation.usesWidthAsHeight() ? size.transposedSize() : size;
+
+ return context;
+}
+
+// This should be called with the sample mutex locked.
+bool MediaPlayerPrivateGStreamerBase::paintToCairoSurface(cairo_surface_t* outputSurface, cairo_device_t* device, GstVideoInfo& videoInfo, const IntSize& size, const IntSize& rotatedSize, bool flipY)
+{
+ GstBuffer* buffer = gst_sample_get_buffer(m_sample.get());
+ GstVideoFrame videoFrame;
+ if (!gst_video_frame_map(&videoFrame, &videoInfo, buffer, static_cast<GstMapFlags>(GST_MAP_READ | GST_MAP_GL)))
+ return false;
+
+ unsigned textureID = *reinterpret_cast<unsigned*>(videoFrame.data[0]);
+ RefPtr<cairo_surface_t> surface = adoptRef(cairo_gl_surface_create_for_texture(device, CAIRO_CONTENT_COLOR_ALPHA, textureID, size.width(), size.height()));
+ RefPtr<cairo_t> cr = adoptRef(cairo_create(outputSurface));
+
+ switch (m_videoSourceOrientation) {
+ case DefaultImageOrientation:
+ break;
+ case OriginRightTop:
+ cairo_translate(cr.get(), rotatedSize.width() * 0.5, rotatedSize.height() * 0.5);
+ cairo_rotate(cr.get(), piOverTwoDouble);
+ cairo_translate(cr.get(), -rotatedSize.height() * 0.5, -rotatedSize.width() * 0.5);
+ break;
+ case OriginBottomRight:
+ cairo_translate(cr.get(), rotatedSize.width() * 0.5, rotatedSize.height() * 0.5);
+ cairo_rotate(cr.get(), piDouble);
+ cairo_translate(cr.get(), -rotatedSize.width() * 0.5, -rotatedSize.height() * 0.5);
+ break;
+ case OriginLeftBottom:
+ cairo_translate(cr.get(), rotatedSize.width() * 0.5, rotatedSize.height() * 0.5);
+ cairo_rotate(cr.get(), 3 * piOverTwoDouble);
+ cairo_translate(cr.get(), -rotatedSize.height() * 0.5, -rotatedSize.width() * 0.5);
+ break;
+ default:
+ ASSERT_NOT_REACHED();
+ break;
+ }
+
+ if (flipY) {
+ cairo_scale(cr.get(), 1.0f, -1.0f);
+ cairo_translate(cr.get(), 0.0f, -size.height());
+ }
+
+ cairo_set_source_surface(cr.get(), surface.get(), 0, 0);
+ cairo_set_operator(cr.get(), CAIRO_OPERATOR_SOURCE);
+ cairo_paint(cr.get());
+
+ gst_video_frame_unmap(&videoFrame);
+
+ return true;
+}
+#endif // USE(CAIRO) && ENABLE(ACCELERATED_2D_CANVAS)
+
+bool MediaPlayerPrivateGStreamerBase::copyVideoTextureToPlatformTexture(GraphicsContext3D* context, Platform3DObject outputTexture, GC3Denum outputTarget, GC3Dint level, GC3Denum internalFormat, GC3Denum format, GC3Denum type, bool premultiplyAlpha, bool flipY)
+{
+#if USE(GSTREAMER_GL)
+ UNUSED_PARAM(context);
+
+ if (m_usingFallbackVideoSink)
+ return false;
+
+ if (premultiplyAlpha)
+ return false;
+
+ WTF::GMutexLocker<GMutex> lock(m_sampleMutex);
+
+ GstVideoInfo videoInfo;
+ if (!getSampleVideoInfo(m_sample.get(), videoInfo))
+ return false;
+
+ GstBuffer* buffer = gst_sample_get_buffer(m_sample.get());
+ GstVideoFrame videoFrame;
+ if (!gst_video_frame_map(&videoFrame, &videoInfo, buffer, static_cast<GstMapFlags>(GST_MAP_READ | GST_MAP_GL)))
+ return false;
+
+ IntSize size(GST_VIDEO_INFO_WIDTH(&videoInfo), GST_VIDEO_INFO_HEIGHT(&videoInfo));
+ if (m_videoSourceOrientation.usesWidthAsHeight())
+ size = size.transposedSize();
+ unsigned textureID = *reinterpret_cast<unsigned*>(videoFrame.data[0]);
+
+ if (!m_videoTextureCopier)
+ m_videoTextureCopier = std::make_unique<VideoTextureCopierGStreamer>();
+
+ bool copied = m_videoTextureCopier->copyVideoTextureToPlatformTexture(textureID, size, outputTexture, outputTarget, level, internalFormat, format, type, flipY, m_videoSourceOrientation);
+
+ gst_video_frame_unmap(&videoFrame);
- RefPtr<BitmapTexture> texture = updateTexture(textureMapper);
- if (texture)
- textureMapper->drawTexture(*texture.get(), targetRect, matrix, opacity);
+ return copied;
+#else
+ return false;
+#endif
}
+
+NativeImagePtr MediaPlayerPrivateGStreamerBase::nativeImageForCurrentTime()
+{
+#if USE(CAIRO) && ENABLE(ACCELERATED_2D_CANVAS)
+ if (m_usingFallbackVideoSink)
+ return nullptr;
+
+ GstVideoInfo videoInfo;
+ IntSize size, rotatedSize;
+ WTF::GMutexLocker<GMutex> lock(m_sampleMutex);
+ GLContext* context = prepareContextForCairoPaint(videoInfo, size, rotatedSize);
+ if (!context)
+ return nullptr;
+
+ RefPtr<cairo_surface_t> rotatedSurface = adoptRef(cairo_gl_surface_create(context->cairoDevice(), CAIRO_CONTENT_COLOR_ALPHA, rotatedSize.width(), rotatedSize.height()));
+ if (!paintToCairoSurface(rotatedSurface.get(), context->cairoDevice(), videoInfo, size, rotatedSize, false))
+ return nullptr;
+
+ return rotatedSurface;
+#else
+ return nullptr;
#endif
+}
+#endif
+
+void MediaPlayerPrivateGStreamerBase::setVideoSourceOrientation(const ImageOrientation& orientation)
+{
+ if (m_videoSourceOrientation == orientation)
+ return;
+
+ m_videoSourceOrientation = orientation;
+}
bool MediaPlayerPrivateGStreamerBase::supportsFullscreen() const
{
@@ -450,25 +1072,88 @@ MediaPlayer::MovieLoadType MediaPlayerPrivateGStreamerBase::movieLoadType() cons
return MediaPlayer::Download;
}
-GRefPtr<GstCaps> MediaPlayerPrivateGStreamerBase::currentVideoSinkCaps() const
+#if USE(GSTREAMER_GL)
+GstElement* MediaPlayerPrivateGStreamerBase::createGLAppSink()
+{
+ if (!webkitGstCheckVersion(1, 8, 0))
+ return nullptr;
+
+ GstElement* appsink = gst_element_factory_make("appsink", "webkit-gl-video-sink");
+ if (!appsink)
+ return nullptr;
+
+ g_object_set(appsink, "enable-last-sample", FALSE, "emit-signals", TRUE, "max-buffers", 1, nullptr);
+ g_signal_connect(appsink, "new-sample", G_CALLBACK(newSampleCallback), this);
+ g_signal_connect(appsink, "new-preroll", G_CALLBACK(newPrerollCallback), this);
+
+ return appsink;
+}
+
+GstElement* MediaPlayerPrivateGStreamerBase::createVideoSinkGL()
{
- if (!m_webkitVideoSink)
+ // FIXME: Currently it's not possible to get the video frames and caps using this approach until
+ // the pipeline gets into playing state. Due to this, trying to grab a frame and painting it by some
+ // other mean (canvas or webgl) before playing state can result in a crash.
+ // This is being handled in https://bugs.webkit.org/show_bug.cgi?id=159460.
+ if (!webkitGstCheckVersion(1, 8, 0))
return nullptr;
- GRefPtr<GstCaps> currentCaps;
- g_object_get(G_OBJECT(m_webkitVideoSink.get()), "current-caps", &currentCaps.outPtr(), NULL);
- return currentCaps;
+ gboolean result = TRUE;
+ GstElement* videoSink = gst_bin_new(nullptr);
+ GstElement* upload = gst_element_factory_make("glupload", nullptr);
+ GstElement* colorconvert = gst_element_factory_make("glcolorconvert", nullptr);
+ GstElement* appsink = createGLAppSink();
+
+ if (!appsink || !upload || !colorconvert) {
+ GST_WARNING("Failed to create GstGL elements");
+ gst_object_unref(videoSink);
+
+ if (upload)
+ gst_object_unref(upload);
+ if (colorconvert)
+ gst_object_unref(colorconvert);
+ if (appsink)
+ gst_object_unref(appsink);
+
+ return nullptr;
+ }
+
+ gst_bin_add_many(GST_BIN(videoSink), upload, colorconvert, appsink, nullptr);
+
+ GRefPtr<GstCaps> caps = adoptGRef(gst_caps_from_string("video/x-raw(" GST_CAPS_FEATURE_MEMORY_GL_MEMORY "), format = (string) { RGBA }"));
+
+ result &= gst_element_link_pads(upload, "src", colorconvert, "sink");
+ result &= gst_element_link_pads_filtered(colorconvert, "src", appsink, "sink", caps.get());
+
+ GRefPtr<GstPad> pad = adoptGRef(gst_element_get_static_pad(upload, "sink"));
+ gst_element_add_pad(videoSink, gst_ghost_pad_new("sink", pad.get()));
+
+ if (!result) {
+ GST_WARNING("Failed to link GstGL elements");
+ gst_object_unref(videoSink);
+ videoSink = nullptr;
+ }
+ return videoSink;
}
+#endif
GstElement* MediaPlayerPrivateGStreamerBase::createVideoSink()
{
- ASSERT(initializeGStreamer());
+ acceleratedRenderingStateChanged();
- GstElement* videoSink = nullptr;
- m_webkitVideoSink = webkitVideoSinkNew();
+#if USE(GSTREAMER_GL)
+ if (m_renderingCanBeAccelerated)
+ m_videoSink = createVideoSinkGL();
+#endif
- m_repaintHandler = g_signal_connect(m_webkitVideoSink.get(), "repaint-requested", G_CALLBACK(mediaPlayerPrivateRepaintCallback), this);
+ if (!m_videoSink) {
+ m_usingFallbackVideoSink = true;
+ m_videoSink = webkitVideoSinkNew();
+ g_signal_connect_swapped(m_videoSink.get(), "repaint-requested", G_CALLBACK(repaintCallback), this);
+ g_signal_connect_swapped(m_videoSink.get(), "repaint-cancelled", G_CALLBACK(repaintCancelledCallback), this);
+ }
+ GstElement* videoSink = nullptr;
m_fpsSink = gst_element_factory_make("fpsdisplaysink", "sink");
if (m_fpsSink) {
g_object_set(m_fpsSink.get(), "silent", TRUE , nullptr);
@@ -477,20 +1162,19 @@ GstElement* MediaPlayerPrivateGStreamerBase::createVideoSink()
#if LOG_DISABLED
g_object_set(m_fpsSink.get(), "text-overlay", FALSE , nullptr);
#else
- WTFLogChannel* channel = logChannelByName("Media");
- if (channel->state != WTFLogChannelOn)
+ if (!isLogChannelEnabled("Media"))
g_object_set(m_fpsSink.get(), "text-overlay", FALSE , nullptr);
#endif // LOG_DISABLED
if (g_object_class_find_property(G_OBJECT_GET_CLASS(m_fpsSink.get()), "video-sink")) {
- g_object_set(m_fpsSink.get(), "video-sink", m_webkitVideoSink.get(), nullptr);
+ g_object_set(m_fpsSink.get(), "video-sink", m_videoSink.get(), nullptr);
videoSink = m_fpsSink.get();
} else
m_fpsSink = nullptr;
}
if (!m_fpsSink)
- videoSink = m_webkitVideoSink.get();
+ videoSink = m_videoSink.get();
ASSERT(videoSink);
@@ -505,23 +1189,23 @@ void MediaPlayerPrivateGStreamerBase::setStreamVolumeElement(GstStreamVolume* vo
// We don't set the initial volume because we trust the sink to keep it for us. See
// https://bugs.webkit.org/show_bug.cgi?id=118974 for more information.
if (!m_player->platformVolumeConfigurationRequired()) {
- LOG_MEDIA_MESSAGE("Setting stream volume to %f", m_player->volume());
- g_object_set(m_volumeElement.get(), "volume", m_player->volume(), NULL);
+ GST_DEBUG("Setting stream volume to %f", m_player->volume());
+ g_object_set(m_volumeElement.get(), "volume", m_player->volume(), nullptr);
} else
- LOG_MEDIA_MESSAGE("Not setting stream volume, trusting system one");
+ GST_DEBUG("Not setting stream volume, trusting system one");
- LOG_MEDIA_MESSAGE("Setting stream muted %d", m_player->muted());
- g_object_set(m_volumeElement.get(), "mute", m_player->muted(), NULL);
+ GST_DEBUG("Setting stream muted %d", m_player->muted());
+ g_object_set(m_volumeElement.get(), "mute", m_player->muted(), nullptr);
- m_volumeSignalHandler = g_signal_connect(m_volumeElement.get(), "notify::volume", G_CALLBACK(mediaPlayerPrivateVolumeChangedCallback), this);
- m_muteSignalHandler = g_signal_connect(m_volumeElement.get(), "notify::mute", G_CALLBACK(mediaPlayerPrivateMuteChangedCallback), this);
+ g_signal_connect_swapped(m_volumeElement.get(), "notify::volume", G_CALLBACK(volumeChangedCallback), this);
+ g_signal_connect_swapped(m_volumeElement.get(), "notify::mute", G_CALLBACK(muteChangedCallback), this);
}
unsigned MediaPlayerPrivateGStreamerBase::decodedFrameCount() const
{
guint64 decodedFrames = 0;
if (m_fpsSink)
- g_object_get(m_fpsSink.get(), "frames-rendered", &decodedFrames, NULL);
+ g_object_get(m_fpsSink.get(), "frames-rendered", &decodedFrames, nullptr);
return static_cast<unsigned>(decodedFrames);
}
@@ -529,7 +1213,7 @@ unsigned MediaPlayerPrivateGStreamerBase::droppedFrameCount() const
{
guint64 framesDropped = 0;
if (m_fpsSink)
- g_object_get(m_fpsSink.get(), "frames-dropped", &framesDropped, NULL);
+ g_object_get(m_fpsSink.get(), "frames-dropped", &framesDropped, nullptr);
return static_cast<unsigned>(framesDropped);
}
@@ -550,13 +1234,95 @@ unsigned MediaPlayerPrivateGStreamerBase::videoDecodedByteCount() const
GstQuery* query = gst_query_new_position(GST_FORMAT_BYTES);
gint64 position = 0;
- if (gst_element_query(m_webkitVideoSink.get(), query))
+ if (gst_element_query(m_videoSink.get(), query))
gst_query_parse_position(query, 0, &position);
gst_query_unref(query);
return static_cast<unsigned>(position);
}
+#if ENABLE(LEGACY_ENCRYPTED_MEDIA)
+void MediaPlayerPrivateGStreamerBase::needKey(RefPtr<Uint8Array> initData)
+{
+ if (!m_player->keyNeeded(initData.get()))
+ GST_INFO("no event handler for key needed");
+}
+
+void MediaPlayerPrivateGStreamerBase::setCDMSession(CDMSession* session)
+{
+ GST_DEBUG("setting CDM session to %p", session);
+ m_cdmSession = session;
+}
+
+void MediaPlayerPrivateGStreamerBase::keyAdded()
+{
+}
+
+void MediaPlayerPrivateGStreamerBase::handleProtectionEvent(GstEvent* event)
+{
+ if (m_handledProtectionEvents.contains(GST_EVENT_SEQNUM(event))) {
+ GST_DEBUG("event %u already handled", GST_EVENT_SEQNUM(event));
+ m_handledProtectionEvents.remove(GST_EVENT_SEQNUM(event));
+ return;
+ }
+
+ const gchar* eventKeySystemId = nullptr;
+ GstBuffer* data = nullptr;
+ gst_event_parse_protection(event, &eventKeySystemId, &data, nullptr);
+
+ GstMapInfo mapInfo;
+ if (!gst_buffer_map(data, &mapInfo, GST_MAP_READ)) {
+ GST_WARNING("cannot map %s protection data", eventKeySystemId);
+ return;
+ }
+
+ GST_DEBUG("scheduling keyNeeded event for %s with init data size of %" G_GSIZE_FORMAT, eventKeySystemId, mapInfo.size);
+ GST_MEMDUMP("init datas", mapInfo.data, mapInfo.size);
+ RefPtr<Uint8Array> initDataArray = Uint8Array::create(mapInfo.data, mapInfo.size);
+ needKey(initDataArray);
+ gst_buffer_unmap(data, &mapInfo);
+}
+
+void MediaPlayerPrivateGStreamerBase::receivedGenerateKeyRequest(const String& keySystem)
+{
+ GST_DEBUG("received generate key request for %s", keySystem.utf8().data());
+ m_lastGenerateKeyRequestKeySystemUuid = keySystemIdToUuid(keySystem);
+ m_protectionCondition.notifyOne();
+}
+
+static AtomicString keySystemIdToUuid(const AtomicString& id)
+{
+ if (equalIgnoringASCIICase(id, CLEAR_KEY_PROTECTION_SYSTEM_ID))
+ return AtomicString(CLEAR_KEY_PROTECTION_SYSTEM_UUID);
+
+ return { };
+}
+
+std::unique_ptr<CDMSession> MediaPlayerPrivateGStreamerBase::createSession(const String& keySystem, CDMSessionClient*)
+{
+ GST_INFO("Requested CDMSession for KeySystem %s: Returning null.", keySystem.utf8().data());
+ return nullptr;
+}
+
+void MediaPlayerPrivateGStreamerBase::dispatchDecryptionKey(GstBuffer* buffer)
+{
+ gst_element_send_event(m_pipeline.get(), gst_event_new_custom(GST_EVENT_CUSTOM_DOWNSTREAM_OOB,
+ gst_structure_new("drm-cipher", "key", GST_TYPE_BUFFER, buffer, nullptr)));
+}
+#endif
+
+bool MediaPlayerPrivateGStreamerBase::supportsKeySystem(const String& keySystem, const String& mimeType)
+{
+ GST_INFO("Checking for KeySystem support with %s and type %s: false.", keySystem.utf8().data(), mimeType.utf8().data());
+ return false;
+}
+
+MediaPlayer::SupportsType MediaPlayerPrivateGStreamerBase::extendedSupportsType(const MediaEngineSupportParameters& parameters, MediaPlayer::SupportsType result)
+{
+ UNUSED_PARAM(parameters);
+ return result;
+}
+
}
#endif // USE(GSTREAMER)
diff --git a/Source/WebCore/platform/graphics/gstreamer/MediaPlayerPrivateGStreamerBase.h b/Source/WebCore/platform/graphics/gstreamer/MediaPlayerPrivateGStreamerBase.h
index dfcab5994..3092c1eff 100644
--- a/Source/WebCore/platform/graphics/gstreamer/MediaPlayerPrivateGStreamerBase.h
+++ b/Source/WebCore/platform/graphics/gstreamer/MediaPlayerPrivateGStreamerBase.h
@@ -2,7 +2,8 @@
* Copyright (C) 2007, 2009 Apple Inc. All rights reserved.
* Copyright (C) 2007 Collabora Ltd. All rights reserved.
* Copyright (C) 2007 Alp Toker <alp@atoker.com>
- * Copyright (C) 2009, 2010 Igalia S.L
+ * Copyright (C) 2009, 2010, 2015, 2016 Igalia S.L
+ * Copyright (C) 2015, 2016 Metrological Group B.V.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
@@ -25,110 +26,227 @@
#if ENABLE(VIDEO) && USE(GSTREAMER)
#include "GRefPtrGStreamer.h"
+#include "MainThreadNotifier.h"
#include "MediaPlayerPrivate.h"
-
+#include "PlatformLayer.h"
#include <glib.h>
-
+#include <gst/gst.h>
+#include <wtf/Condition.h>
#include <wtf/Forward.h>
+#include <wtf/RunLoop.h>
-#if USE(ACCELERATED_COMPOSITING) && USE(TEXTURE_MAPPER_GL) && !USE(COORDINATED_GRAPHICS)
+#if USE(TEXTURE_MAPPER)
#include "TextureMapperPlatformLayer.h"
+#include "TextureMapperPlatformLayerProxy.h"
#endif
-typedef struct _GstBuffer GstBuffer;
-typedef struct _GstElement GstElement;
-typedef struct _GstMessage GstMessage;
typedef struct _GstStreamVolume GstStreamVolume;
-typedef struct _WebKitVideoSink WebKitVideoSink;
+typedef struct _GstVideoInfo GstVideoInfo;
+typedef struct _GstGLContext GstGLContext;
+typedef struct _GstGLDisplay GstGLDisplay;
namespace WebCore {
+class BitmapTextureGL;
+class GLContext;
class GraphicsContext;
+class GraphicsContext3D;
class IntSize;
class IntRect;
+class VideoTextureCopierGStreamer;
+
+void registerWebKitGStreamerElements();
class MediaPlayerPrivateGStreamerBase : public MediaPlayerPrivateInterface
-#if USE(ACCELERATED_COMPOSITING) && USE(TEXTURE_MAPPER_GL) && !USE(COORDINATED_GRAPHICS)
- , public TextureMapperPlatformLayer
+#if USE(COORDINATED_GRAPHICS_THREADED) || (USE(TEXTURE_MAPPER_GL) && !USE(COORDINATED_GRAPHICS))
+ , public PlatformLayer
#endif
{
public:
virtual ~MediaPlayerPrivateGStreamerBase();
- IntSize naturalSize() const;
+ FloatSize naturalSize() const override;
- void setVolume(float);
- float volume() const;
- void volumeChanged();
- void notifyPlayerOfVolumeChange();
+ void setVolume(float) override;
+ virtual float volume() const;
+
+#if USE(GSTREAMER_GL)
+ bool ensureGstGLContext();
+ static GstContext* requestGLContext(const gchar* contextType, MediaPlayerPrivateGStreamerBase*);
+#endif
- bool supportsMuting() const { return true; }
- void setMuted(bool);
+ bool supportsMuting() const override { return true; }
+ void setMuted(bool) override;
bool muted() const;
- void muteChanged();
- void notifyPlayerOfMute();
- MediaPlayer::NetworkState networkState() const;
- MediaPlayer::ReadyState readyState() const;
+ MediaPlayer::NetworkState networkState() const override;
+ MediaPlayer::ReadyState readyState() const override;
- void setVisible(bool) { }
- void setSize(const IntSize&);
+ void setVisible(bool) override { }
+ void setSize(const IntSize&) override;
void sizeChanged();
- void triggerRepaint(GstBuffer*);
- void paint(GraphicsContext*, const IntRect&);
+ void paint(GraphicsContext&, const FloatRect&) override;
- virtual bool hasSingleSecurityOrigin() const { return true; }
+ bool hasSingleSecurityOrigin() const override { return true; }
virtual float maxTimeLoaded() const { return 0.0; }
- bool supportsFullscreen() const;
- PlatformMedia platformMedia() const;
+ bool supportsFullscreen() const override;
+ PlatformMedia platformMedia() const override;
- MediaPlayer::MovieLoadType movieLoadType() const;
+ MediaPlayer::MovieLoadType movieLoadType() const override;
virtual bool isLiveStream() const = 0;
MediaPlayer* mediaPlayer() const { return m_player; }
- unsigned decodedFrameCount() const;
- unsigned droppedFrameCount() const;
- unsigned audioDecodedByteCount() const;
- unsigned videoDecodedByteCount() const;
+ unsigned decodedFrameCount() const override;
+ unsigned droppedFrameCount() const override;
+ unsigned audioDecodedByteCount() const override;
+ unsigned videoDecodedByteCount() const override;
+
+ void acceleratedRenderingStateChanged() override;
+
+#if USE(TEXTURE_MAPPER_GL) && !USE(COORDINATED_GRAPHICS)
+ PlatformLayer* platformLayer() const override { return const_cast<MediaPlayerPrivateGStreamerBase*>(this); }
+#if PLATFORM(WIN_CAIRO)
+ // FIXME: Accelerated rendering has not been implemented for WinCairo yet.
+ bool supportsAcceleratedRendering() const override { return false; }
+#else
+ bool supportsAcceleratedRendering() const override { return true; }
+#endif
+ void paintToTextureMapper(TextureMapper&, const FloatRect&, const TransformationMatrix&, float) override;
+#endif
+
+#if USE(COORDINATED_GRAPHICS_THREADED)
+ PlatformLayer* platformLayer() const override { return const_cast<MediaPlayerPrivateGStreamerBase*>(this); }
+ bool supportsAcceleratedRendering() const override { return true; }
+#endif
+
+#if ENABLE(LEGACY_ENCRYPTED_MEDIA)
+ void needKey(RefPtr<Uint8Array>);
+ void setCDMSession(CDMSession*) override;
+ void keyAdded() override;
+ virtual void dispatchDecryptionKey(GstBuffer*);
+ void handleProtectionEvent(GstEvent*);
+ void receivedGenerateKeyRequest(const String&);
+#endif
+
+ static bool supportsKeySystem(const String& keySystem, const String& mimeType);
+ static MediaPlayer::SupportsType extendedSupportsType(const MediaEngineSupportParameters&, MediaPlayer::SupportsType);
-#if USE(ACCELERATED_COMPOSITING) && USE(TEXTURE_MAPPER_GL) && !USE(COORDINATED_GRAPHICS)
- virtual PlatformLayer* platformLayer() const { return const_cast<MediaPlayerPrivateGStreamerBase*>(this); }
- virtual bool supportsAcceleratedRendering() const { return true; }
- virtual void paintToTextureMapper(TextureMapper*, const FloatRect&, const TransformationMatrix&, float);
+#if USE(GSTREAMER_GL)
+ bool copyVideoTextureToPlatformTexture(GraphicsContext3D*, Platform3DObject, GC3Denum, GC3Dint, GC3Denum, GC3Denum, GC3Denum, bool, bool) override;
+ NativeImagePtr nativeImageForCurrentTime() override;
#endif
+ void setVideoSourceOrientation(const ImageOrientation&);
+ GstElement* pipeline() const { return m_pipeline.get(); }
+
+ virtual bool handleSyncMessage(GstMessage*);
+
protected:
MediaPlayerPrivateGStreamerBase(MediaPlayer*);
virtual GstElement* createVideoSink();
- GRefPtr<GstCaps> currentVideoSinkCaps() const;
+
+#if USE(GSTREAMER_GL)
+ static GstFlowReturn newSampleCallback(GstElement*, MediaPlayerPrivateGStreamerBase*);
+ static GstFlowReturn newPrerollCallback(GstElement*, MediaPlayerPrivateGStreamerBase*);
+ GstElement* createGLAppSink();
+ GstElement* createVideoSinkGL();
+ GstGLContext* gstGLContext() const { return m_glContext.get(); }
+ GstGLDisplay* gstGLDisplay() const { return m_glDisplay.get(); }
+#if USE(CAIRO) && ENABLE(ACCELERATED_2D_CANVAS)
+ GLContext* prepareContextForCairoPaint(GstVideoInfo&, IntSize&, IntSize&);
+ bool paintToCairoSurface(cairo_surface_t*, cairo_device_t*, GstVideoInfo&, const IntSize&, const IntSize&, bool);
+#endif
+#endif
+
+ GstElement* videoSink() const { return m_videoSink.get(); }
void setStreamVolumeElement(GstStreamVolume*);
virtual GstElement* createAudioSink() { return 0; }
virtual GstElement* audioSink() const { return 0; }
+ void setPipeline(GstElement*);
+
+ void triggerRepaint(GstSample*);
+ void repaint();
+ void cancelRepaint();
+
+ static void repaintCallback(MediaPlayerPrivateGStreamerBase*, GstSample*);
+ static void repaintCancelledCallback(MediaPlayerPrivateGStreamerBase*);
+
+ void notifyPlayerOfVolumeChange();
+ void notifyPlayerOfMute();
+
+ static void volumeChangedCallback(MediaPlayerPrivateGStreamerBase*);
+ static void muteChangedCallback(MediaPlayerPrivateGStreamerBase*);
+
+ enum MainThreadNotification {
+ VideoChanged = 1 << 0,
+ VideoCapsChanged = 1 << 1,
+ AudioChanged = 1 << 2,
+ VolumeChanged = 1 << 3,
+ MuteChanged = 1 << 4,
+#if ENABLE(VIDEO_TRACK)
+ TextChanged = 1 << 5,
+#endif
+ SizeChanged = 1 << 6
+ };
+
+ Ref<MainThreadNotifier<MainThreadNotification>> m_notifier;
MediaPlayer* m_player;
+ GRefPtr<GstElement> m_pipeline;
GRefPtr<GstStreamVolume> m_volumeElement;
- GRefPtr<GstElement> m_webkitVideoSink;
+ GRefPtr<GstElement> m_videoSink;
GRefPtr<GstElement> m_fpsSink;
MediaPlayer::ReadyState m_readyState;
- MediaPlayer::NetworkState m_networkState;
+ mutable MediaPlayer::NetworkState m_networkState;
IntSize m_size;
- GMutex* m_bufferMutex;
- GstBuffer* m_buffer;
- unsigned long m_volumeTimerHandler;
- unsigned long m_muteTimerHandler;
- unsigned long m_repaintHandler;
- unsigned long m_volumeSignalHandler;
- unsigned long m_muteSignalHandler;
- mutable IntSize m_videoSize;
-#if USE(ACCELERATED_COMPOSITING) && USE(TEXTURE_MAPPER_GL) && !USE(COORDINATED_GRAPHICS)
- PassRefPtr<BitmapTexture> updateTexture(TextureMapper*);
+ mutable GMutex m_sampleMutex;
+ GRefPtr<GstSample> m_sample;
+#if USE(GSTREAMER_GL) || USE(COORDINATED_GRAPHICS_THREADED)
+ RunLoop::Timer<MediaPlayerPrivateGStreamerBase> m_drawTimer;
+#endif
+ mutable FloatSize m_videoSize;
+ bool m_usingFallbackVideoSink;
+ bool m_renderingCanBeAccelerated { false };
+#if USE(TEXTURE_MAPPER_GL)
+ void updateTexture(BitmapTextureGL&, GstVideoInfo&);
+#endif
+#if USE(GSTREAMER_GL)
+ GRefPtr<GstGLContext> m_glContext;
+ GRefPtr<GstGLDisplay> m_glDisplay;
+#endif
+
+#if USE(COORDINATED_GRAPHICS_THREADED)
+ RefPtr<TextureMapperPlatformLayerProxy> proxy() const override { return m_platformLayerProxy.copyRef(); }
+ void swapBuffersIfNeeded() override { };
+ void pushTextureToCompositor();
+ RefPtr<TextureMapperPlatformLayerProxy> m_platformLayerProxy;
+#endif
+
+#if USE(GSTREAMER_GL) || USE(COORDINATED_GRAPHICS_THREADED)
+ RefPtr<GraphicsContext3D> m_context3D;
+ Condition m_drawCondition;
+ Lock m_drawMutex;
+#endif
+
+ ImageOrientation m_videoSourceOrientation;
+#if ENABLE(LEGACY_ENCRYPTED_MEDIA)
+ std::unique_ptr<CDMSession> createSession(const String&, CDMSessionClient*) override;
+ CDMSession* m_cdmSession;
+ Lock m_protectionMutex;
+ Condition m_protectionCondition;
+ String m_lastGenerateKeyRequestKeySystemUuid;
+ HashSet<uint32_t> m_handledProtectionEvents;
+#endif
+#if USE(GSTREAMER_GL)
+ std::unique_ptr<VideoTextureCopierGStreamer> m_videoTextureCopier;
#endif
};
+
}
#endif // USE(GSTREAMER)
diff --git a/Source/WebCore/platform/graphics/gstreamer/MediaPlayerPrivateGStreamerOwr.cpp b/Source/WebCore/platform/graphics/gstreamer/MediaPlayerPrivateGStreamerOwr.cpp
new file mode 100644
index 000000000..8e3736c8b
--- /dev/null
+++ b/Source/WebCore/platform/graphics/gstreamer/MediaPlayerPrivateGStreamerOwr.cpp
@@ -0,0 +1,501 @@
+/*
+ * Copyright (C) 2012 Collabora Ltd. All rights reserved.
+ * Copyright (C) 2014, 2015 Igalia S.L. All rights reserved.
+ * Copyright (C) 2015 Metrological All rights reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "config.h"
+
+#include "MediaPlayerPrivateGStreamerOwr.h"
+
+#if ENABLE(VIDEO) && ENABLE(MEDIA_STREAM) && USE(GSTREAMER) && USE(OPENWEBRTC)
+
+#include "GStreamerUtilities.h"
+#include "MediaPlayer.h"
+#include "MediaStreamPrivate.h"
+#include "NotImplemented.h"
+#include "RealtimeMediaSourceOwr.h"
+#include "URL.h"
+#include <owr/owr.h>
+#include <owr/owr_gst_audio_renderer.h>
+#include <owr/owr_gst_video_renderer.h>
+#include <wtf/NeverDestroyed.h>
+#include <wtf/text/CString.h>
+
+GST_DEBUG_CATEGORY(webkit_openwebrtc_debug);
+#define GST_CAT_DEFAULT webkit_openwebrtc_debug
+
+namespace WebCore {
+
+MediaPlayerPrivateGStreamerOwr::MediaPlayerPrivateGStreamerOwr(MediaPlayer* player)
+ : MediaPlayerPrivateGStreamerBase(player)
+{
+ initializeGStreamerAndGStreamerDebugging();
+}
+
+MediaPlayerPrivateGStreamerOwr::~MediaPlayerPrivateGStreamerOwr()
+{
+ GST_TRACE("Destroying");
+
+ if (hasAudio())
+ m_audioTrack->removeObserver(*this);
+ if (hasVideo())
+ m_videoTrack->removeObserver(*this);
+
+ m_audioTrackMap.clear();
+ m_videoTrackMap.clear();
+
+ stop();
+}
+
+void MediaPlayerPrivateGStreamerOwr::play()
+{
+ GST_DEBUG("Play");
+
+ if (!m_streamPrivate || !m_streamPrivate->active()) {
+ m_readyState = MediaPlayer::HaveNothing;
+ loadingFailed(MediaPlayer::Empty);
+ return;
+ }
+
+ m_ended = false;
+ m_paused = false;
+
+ GST_DEBUG("Connecting to live stream, descriptor: %p", m_streamPrivate.get());
+
+ if (m_videoTrack)
+ maybeHandleChangeMutedState(*m_videoTrack.get());
+
+ if (m_audioTrack)
+ maybeHandleChangeMutedState(*m_audioTrack.get());
+}
+
+void MediaPlayerPrivateGStreamerOwr::pause()
+{
+ GST_DEBUG("Pause");
+ m_paused = true;
+ disableMediaTracks();
+}
+
+bool MediaPlayerPrivateGStreamerOwr::hasVideo() const
+{
+ return m_videoTrack;
+}
+
+bool MediaPlayerPrivateGStreamerOwr::hasAudio() const
+{
+ return m_audioTrack;
+}
+
+void MediaPlayerPrivateGStreamerOwr::setVolume(float volume)
+{
+ if (!m_audioTrack)
+ return;
+
+ auto& realTimeMediaSource = static_cast<RealtimeMediaSourceOwr&>(m_audioTrack->source());
+ auto mediaSource = OWR_MEDIA_SOURCE(realTimeMediaSource.mediaSource());
+
+ GST_DEBUG("Setting volume: %f", volume);
+ g_object_set(mediaSource, "volume", static_cast<gdouble>(volume), nullptr);
+}
+
+void MediaPlayerPrivateGStreamerOwr::setMuted(bool muted)
+{
+ if (!m_audioTrack)
+ return;
+
+ auto& realTimeMediaSource = static_cast<RealtimeMediaSourceOwr&>(m_audioTrack->source());
+ auto mediaSource = OWR_MEDIA_SOURCE(realTimeMediaSource.mediaSource());
+ if (!mediaSource)
+ return;
+
+ GST_DEBUG("Setting mute: %s", muted ? "on":"off");
+ g_object_set(mediaSource, "mute", muted, nullptr);
+}
+
+float MediaPlayerPrivateGStreamerOwr::currentTime() const
+{
+ gint64 position = GST_CLOCK_TIME_NONE;
+ GstQuery* query = gst_query_new_position(GST_FORMAT_TIME);
+
+ if (m_videoTrack && gst_element_query(m_videoSink.get(), query))
+ gst_query_parse_position(query, 0, &position);
+ else if (m_audioTrack && gst_element_query(m_audioSink.get(), query))
+ gst_query_parse_position(query, 0, &position);
+
+ float result = 0;
+ if (static_cast<GstClockTime>(position) != GST_CLOCK_TIME_NONE)
+ result = static_cast<double>(position) / GST_SECOND;
+
+ GST_LOG("Position %" GST_TIME_FORMAT, GST_TIME_ARGS(position));
+ gst_query_unref(query);
+
+ return result;
+}
+
+void MediaPlayerPrivateGStreamerOwr::load(const String &)
+{
+ // Properly fail so the global MediaPlayer tries to fallback to the next MediaPlayerPrivate.
+ m_networkState = MediaPlayer::FormatError;
+ m_player->networkStateChanged();
+}
+
+#if ENABLE(MEDIA_SOURCE)
+void MediaPlayerPrivateGStreamerOwr::load(const String&, MediaSourcePrivateClient*)
+{
+ // Properly fail so the global MediaPlayer tries to fallback to the next MediaPlayerPrivate.
+ m_networkState = MediaPlayer::FormatError;
+ m_player->networkStateChanged();
+}
+#endif
+
+void MediaPlayerPrivateGStreamerOwr::load(MediaStreamPrivate& streamPrivate)
+{
+ if (!initializeGStreamer())
+ return;
+
+ m_streamPrivate = &streamPrivate;
+ if (!m_streamPrivate->active()) {
+ loadingFailed(MediaPlayer::NetworkError);
+ return;
+ }
+
+ if (streamPrivate.hasVideo() && !m_videoSink)
+ createVideoSink();
+
+ if (streamPrivate.hasAudio() && !m_audioSink)
+ createGSTAudioSinkBin();
+
+ GST_DEBUG("Loading MediaStreamPrivate %p video: %s, audio: %s", &streamPrivate, streamPrivate.hasVideo() ? "yes":"no", streamPrivate.hasAudio() ? "yes":"no");
+
+ m_readyState = MediaPlayer::HaveNothing;
+ m_networkState = MediaPlayer::Loading;
+ m_player->networkStateChanged();
+ m_player->readyStateChanged();
+
+ for (auto track : m_streamPrivate->tracks()) {
+ if (!track->enabled()) {
+ GST_DEBUG("Track %s disabled", track->label().ascii().data());
+ continue;
+ }
+
+ GST_DEBUG("Processing track %s", track->label().ascii().data());
+
+ bool observeTrack = false;
+
+ // TODO: Support for multiple tracks of the same type.
+
+ switch (track->type()) {
+ case RealtimeMediaSource::Audio:
+ if (!m_audioTrack) {
+ String preSelectedDevice = getenv("WEBKIT_AUDIO_DEVICE");
+ if (!preSelectedDevice || (preSelectedDevice == track->label())) {
+ m_audioTrack = track;
+ auto audioTrack = AudioTrackPrivateMediaStream::create(*m_audioTrack.get());
+ m_player->addAudioTrack(*audioTrack);
+ m_audioTrackMap.add(track->id(), audioTrack);
+ observeTrack = true;
+ }
+ }
+ break;
+ case RealtimeMediaSource::Video:
+ if (!m_videoTrack) {
+ String preSelectedDevice = getenv("WEBKIT_VIDEO_DEVICE");
+ if (!preSelectedDevice || (preSelectedDevice == track->label())) {
+ m_videoTrack = track;
+ auto videoTrack = VideoTrackPrivateMediaStream::create(*m_videoTrack.get());
+ m_player->addVideoTrack(*videoTrack);
+ videoTrack->setSelected(true);
+ m_videoTrackMap.add(track->id(), videoTrack);
+ observeTrack = true;
+ }
+ }
+ break;
+ case RealtimeMediaSource::None:
+ GST_WARNING("Loading a track with None type");
+ }
+
+ if (observeTrack)
+ track->addObserver(*this);
+ }
+
+ m_readyState = MediaPlayer::HaveEnoughData;
+ m_player->readyStateChanged();
+}
+
+void MediaPlayerPrivateGStreamerOwr::loadingFailed(MediaPlayer::NetworkState error)
+{
+ if (m_networkState != error) {
+ GST_WARNING("Loading failed, error: %d", error);
+ m_networkState = error;
+ m_player->networkStateChanged();
+ }
+ if (m_readyState != MediaPlayer::HaveNothing) {
+ m_readyState = MediaPlayer::HaveNothing;
+ m_player->readyStateChanged();
+ }
+}
+
+bool MediaPlayerPrivateGStreamerOwr::didLoadingProgress() const
+{
+ // FIXME: Implement loading progress support.
+ return true;
+}
+
+void MediaPlayerPrivateGStreamerOwr::disableMediaTracks()
+{
+ if (m_audioTrack) {
+ GST_DEBUG("Stop: disconnecting audio");
+ g_object_set(m_audioRenderer.get(), "disabled", true, nullptr);
+ owr_media_renderer_set_source(OWR_MEDIA_RENDERER(m_audioRenderer.get()), nullptr);
+ }
+
+ if (m_videoTrack) {
+ GST_DEBUG("Stop: disconnecting video");
+ g_object_set(m_videoRenderer.get(), "disabled", true, nullptr);
+ owr_media_renderer_set_source(OWR_MEDIA_RENDERER(m_videoRenderer.get()), nullptr);
+ }
+}
+
+void MediaPlayerPrivateGStreamerOwr::stop()
+{
+ disableMediaTracks();
+ if (m_videoTrack) {
+ auto videoTrack = m_videoTrackMap.get(m_videoTrack->id());
+ if (videoTrack)
+ videoTrack->setSelected(false);
+ }
+}
+
+void MediaPlayerPrivateGStreamerOwr::registerMediaEngine(MediaEngineRegistrar registrar)
+{
+ if (initializeGStreamerAndGStreamerDebugging()) {
+ registrar([](MediaPlayer* player) {
+ return std::make_unique<MediaPlayerPrivateGStreamerOwr>(player);
+ }, getSupportedTypes, supportsType, nullptr, nullptr, nullptr, nullptr);
+ }
+}
+
+void MediaPlayerPrivateGStreamerOwr::getSupportedTypes(HashSet<String, ASCIICaseInsensitiveHash>& types)
+{
+ // Not supported in this media player.
+ static NeverDestroyed<HashSet<String, ASCIICaseInsensitiveHash>> cache;
+ types = cache;
+}
+
+MediaPlayer::SupportsType MediaPlayerPrivateGStreamerOwr::supportsType(const MediaEngineSupportParameters& parameters)
+{
+ if (parameters.isMediaStream)
+ return MediaPlayer::IsSupported;
+ return MediaPlayer::IsNotSupported;
+}
+
+bool MediaPlayerPrivateGStreamerOwr::initializeGStreamerAndGStreamerDebugging()
+{
+ if (!initializeGStreamer())
+ return false;
+
+ static std::once_flag debugRegisteredFlag;
+ std::call_once(debugRegisteredFlag, [] {
+ GST_DEBUG_CATEGORY_INIT(webkit_openwebrtc_debug, "webkitowrplayer", 0, "WebKit OpenWebRTC player");
+ });
+
+ return true;
+}
+
+void MediaPlayerPrivateGStreamerOwr::createGSTAudioSinkBin()
+{
+ ASSERT(!m_audioSink);
+ GST_DEBUG("Creating audio sink");
+ // FIXME: volume/mute support: https://webkit.org/b/153828.
+
+ // Pre-roll an autoaudiosink so that the platform audio sink is created and
+ // can be retrieved from the autoaudiosink bin.
+ GRefPtr<GstElement> sink = gst_element_factory_make("autoaudiosink", nullptr);
+ GstChildProxy* childProxy = GST_CHILD_PROXY(sink.get());
+ gst_element_set_state(sink.get(), GST_STATE_READY);
+ GRefPtr<GstElement> platformSink = adoptGRef(GST_ELEMENT(gst_child_proxy_get_child_by_index(childProxy, 0)));
+ GstElementFactory* factory = gst_element_get_factory(platformSink.get());
+
+ // Dispose now un-needed autoaudiosink.
+ gst_element_set_state(sink.get(), GST_STATE_NULL);
+
+ // Create a fresh new audio sink compatible with the platform.
+ m_audioSink = gst_element_factory_create(factory, nullptr);
+ m_audioRenderer = adoptGRef(owr_gst_audio_renderer_new(m_audioSink.get()));
+}
+
+void MediaPlayerPrivateGStreamerOwr::trackEnded(MediaStreamTrackPrivate& track)
+{
+ GST_DEBUG("Track ended");
+
+ if (!m_streamPrivate || !m_streamPrivate->active()) {
+ stop();
+ return;
+ }
+
+ if (&track == m_audioTrack)
+ g_object_set(m_audioRenderer.get(), "disabled", true, nullptr);
+ else if (&track == m_videoTrack) {
+ g_object_set(m_videoRenderer.get(), "disabled", true, nullptr);
+ auto& realTimeMediaSource = static_cast<RealtimeMediaSourceOwr&>(m_videoTrack->source());
+ realTimeMediaSource.setWidth(0);
+ realTimeMediaSource.setHeight(0);
+ auto videoTrack = m_videoTrackMap.get(m_videoTrack->id());
+ if (videoTrack)
+ videoTrack->setSelected(false);
+ }
+
+ bool audioDisabled;
+ bool videoDisabled;
+ g_object_get(m_audioRenderer.get(), "disabled", &audioDisabled, nullptr);
+ g_object_get(m_videoRenderer.get(), "disabled", &videoDisabled, nullptr);
+ if (audioDisabled && videoDisabled) {
+ m_ended = true;
+ m_player->timeChanged();
+ }
+}
+
+void MediaPlayerPrivateGStreamerOwr::trackMutedChanged(MediaStreamTrackPrivate& track)
+{
+ GST_DEBUG("Track muted state changed");
+
+ maybeHandleChangeMutedState(track);
+}
+
+void MediaPlayerPrivateGStreamerOwr::maybeHandleChangeMutedState(MediaStreamTrackPrivate& track)
+{
+ auto& realTimeMediaSource = static_cast<RealtimeMediaSourceOwr&>(track.source());
+ auto mediaSource = OWR_MEDIA_SOURCE(realTimeMediaSource.mediaSource());
+
+ GST_DEBUG("%s track now %s", track.type() == RealtimeMediaSource::Audio ? "audio":"video", realTimeMediaSource.muted() ? "muted":"un-muted");
+ switch (track.type()) {
+ case RealtimeMediaSource::Audio:
+ if (!realTimeMediaSource.muted()) {
+ g_object_set(m_audioRenderer.get(), "disabled", false, nullptr);
+ owr_media_renderer_set_source(OWR_MEDIA_RENDERER(m_audioRenderer.get()), mediaSource);
+ } else {
+ g_object_set(m_audioRenderer.get(), "disabled", true, nullptr);
+ owr_media_renderer_set_source(OWR_MEDIA_RENDERER(m_audioRenderer.get()), nullptr);
+ }
+ if (mediaSource)
+ g_object_set(mediaSource, "mute", !track.enabled(), nullptr);
+ break;
+ case RealtimeMediaSource::Video:
+ if (!realTimeMediaSource.muted()) {
+ g_object_set(m_videoRenderer.get(), "disabled", false, nullptr);
+ owr_media_renderer_set_source(OWR_MEDIA_RENDERER(m_videoRenderer.get()), mediaSource);
+ } else {
+ g_object_set(m_videoRenderer.get(), "disabled", true, nullptr);
+ owr_media_renderer_set_source(OWR_MEDIA_RENDERER(m_videoRenderer.get()), nullptr);
+ }
+ break;
+ case RealtimeMediaSource::None:
+ GST_WARNING("Trying to change mute state of a track with None type");
+ }
+}
+
+void MediaPlayerPrivateGStreamerOwr::trackSettingsChanged(MediaStreamTrackPrivate&)
+{
+ GST_DEBUG("Track settings changed");
+}
+
+void MediaPlayerPrivateGStreamerOwr::trackEnabledChanged(MediaStreamTrackPrivate& track)
+{
+ GST_DEBUG("%s track now %s", track.type() == RealtimeMediaSource::Audio ? "audio":"video", track.enabled() ? "enabled":"disabled");
+
+ switch (track.type()) {
+ case RealtimeMediaSource::Audio:
+ g_object_set(m_audioRenderer.get(), "disabled", !track.enabled(), nullptr);
+ break;
+ case RealtimeMediaSource::Video:
+ g_object_set(m_videoRenderer.get(), "disabled", !track.enabled(), nullptr);
+ break;
+ case RealtimeMediaSource::None:
+ GST_WARNING("Trying to change enabled state of a track with None type");
+ }
+}
+
+GstElement* MediaPlayerPrivateGStreamerOwr::createVideoSink()
+{
+ GstElement* sink;
+#if USE(GSTREAMER_GL)
+ // No need to create glupload and glcolorconvert here because they are
+ // already created by the video renderer.
+ // FIXME: This should probably return a RefPtr. See https://bugs.webkit.org/show_bug.cgi?id=164709.
+ sink = MediaPlayerPrivateGStreamerBase::createGLAppSink();
+ m_videoSink = sink;
+#else
+ if (m_streamPrivate->getVideoRenderer()) {
+ m_videoRenderer = m_streamPrivate->getVideoRenderer();
+ m_videoSink = m_streamPrivate->getVideoSinkElement();
+ g_signal_connect_swapped(m_videoSink.get(), "repaint-requested", G_CALLBACK(MediaPlayerPrivateGStreamerBase::repaintCallback), this);
+ g_object_get(m_videoRenderer.get(), "sink", &sink, nullptr);
+ } else {
+ GstElement* gldownload = gst_element_factory_make("gldownload", nullptr);
+ GstElement* videoconvert = gst_element_factory_make("videoconvert", nullptr);
+ GstElement* webkitSink = MediaPlayerPrivateGStreamerBase::createVideoSink();
+ sink = gst_bin_new(nullptr);
+ gst_bin_add_many(GST_BIN(sink), gldownload, videoconvert, webkitSink, nullptr);
+ gst_element_link_many(gldownload, videoconvert, webkitSink, nullptr);
+ GRefPtr<GstPad> pad = adoptGRef(gst_element_get_static_pad(gldownload, "sink"));
+ gst_element_add_pad(sink, gst_ghost_pad_new("sink", pad.get()));
+ }
+#endif
+ if (!m_videoRenderer) {
+ m_videoRenderer = adoptGRef(owr_gst_video_renderer_new(sink));
+#if USE(GSTREAMER_GL)
+ owr_video_renderer_set_request_context_callback(OWR_VIDEO_RENDERER(m_videoRenderer.get()), (OwrVideoRendererRequestContextCallback) MediaPlayerPrivateGStreamerBase::requestGLContext, this, nullptr);
+#endif
+ m_streamPrivate->setVideoRenderer(m_videoRenderer.get(), videoSink());
+ }
+ return sink;
+}
+
+void MediaPlayerPrivateGStreamerOwr::setSize(const IntSize& size)
+{
+ if (size == m_size)
+ return;
+
+ MediaPlayerPrivateGStreamerBase::setSize(size);
+ if (m_videoRenderer)
+ g_object_set(m_videoRenderer.get(), "width", size.width(), "height", size.height(), nullptr);
+
+ if (!m_videoTrack)
+ return;
+
+ auto& realTimeMediaSource = static_cast<RealtimeMediaSourceOwr&>(m_videoTrack->source());
+ realTimeMediaSource.setWidth(size.width());
+ realTimeMediaSource.setHeight(size.height());
+}
+
+FloatSize MediaPlayerPrivateGStreamerOwr::naturalSize() const
+{
+ auto size = MediaPlayerPrivateGStreamerBase::naturalSize();
+
+ // In case we are not playing the video we return the size we set to the media source.
+ if (m_videoTrack && size.isZero()) {
+ auto& realTimeMediaSource = static_cast<RealtimeMediaSourceOwr&>(m_videoTrack->source());
+ return realTimeMediaSource.size();
+ }
+
+ return size;
+}
+
+} // namespace WebCore
+
+#endif // ENABLE(VIDEO) && ENABLE(MEDIA_STREAM) && USE(GSTREAMER) && USE(OPENWEBRTC)
diff --git a/Source/WebCore/platform/graphics/gstreamer/MediaPlayerPrivateGStreamerOwr.h b/Source/WebCore/platform/graphics/gstreamer/MediaPlayerPrivateGStreamerOwr.h
new file mode 100644
index 000000000..334630e45
--- /dev/null
+++ b/Source/WebCore/platform/graphics/gstreamer/MediaPlayerPrivateGStreamerOwr.h
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2015 Igalia S.L. All rights reserved.
+ * Copyright (C) 2015 Metrological. All rights reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef MediaPlayerPrivateGStreamerOwr_h
+#define MediaPlayerPrivateGStreamerOwr_h
+
+#if ENABLE(VIDEO) && ENABLE(MEDIA_STREAM) && USE(GSTREAMER) && USE(OPENWEBRTC)
+
+#include "AudioTrackPrivateMediaStream.h"
+#include "MediaPlayerPrivateGStreamerBase.h"
+#include "MediaStreamTrackPrivate.h"
+#include "VideoTrackPrivateMediaStream.h"
+
+typedef struct _OwrGstVideoRenderer OwrGstVideoRenderer;
+typedef struct _OwrGstAudioRenderer OwrGstAudioRenderer;
+
+namespace WebCore {
+
+class MediaStreamPrivate;
+class RealtimeMediaSourceOwr;
+
+class MediaPlayerPrivateGStreamerOwr : public MediaPlayerPrivateGStreamerBase, private MediaStreamTrackPrivate::Observer {
+public:
+ explicit MediaPlayerPrivateGStreamerOwr(MediaPlayer*);
+ ~MediaPlayerPrivateGStreamerOwr();
+
+ static void registerMediaEngine(MediaEngineRegistrar);
+
+ void setSize(const IntSize&) final;
+
+ FloatSize naturalSize() const final;
+
+private:
+ GstElement* createVideoSink() final;
+ GstElement* audioSink() const final { return m_audioSink.get(); }
+ bool isLiveStream() const final { return true; }
+
+ String engineDescription() const final { return "OpenWebRTC"; }
+
+ void load(const String&) final;
+#if ENABLE(MEDIA_SOURCE)
+ void load(const String&, MediaSourcePrivateClient*) final;
+#endif
+ void load(MediaStreamPrivate&) final;
+ void cancelLoad() final { }
+
+ void prepareToPlay() final { }
+ void play() final;
+ void pause() final;
+
+ bool hasVideo() const final;
+ bool hasAudio() const final;
+
+ float duration() const final { return 0; }
+
+ float currentTime() const final;
+ void seek(float) final { }
+ bool seeking() const final { return false; }
+
+ void setRate(float) final { }
+ void setPreservesPitch(bool) final { }
+ bool paused() const final { return m_paused; }
+
+ void setVolume(float) final;
+ void setMuted(bool) final;
+
+ bool hasClosedCaptions() const final { return false; }
+ void setClosedCaptionsVisible(bool) final { };
+
+ float maxTimeSeekable() const final { return 0; }
+ std::unique_ptr<PlatformTimeRanges> buffered() const final { return std::make_unique<PlatformTimeRanges>(); }
+ bool didLoadingProgress() const final;
+
+ unsigned long long totalBytes() const final { return 0; }
+
+ bool canLoadPoster() const final { return false; }
+ void setPoster(const String&) final { }
+ bool ended() const final { return m_ended; }
+
+ // MediaStreamTrackPrivate::Observer implementation.
+ void trackEnded(MediaStreamTrackPrivate&) final;
+ void trackMutedChanged(MediaStreamTrackPrivate&) final;
+ void trackSettingsChanged(MediaStreamTrackPrivate&) final;
+ void trackEnabledChanged(MediaStreamTrackPrivate&) final;
+
+ static void getSupportedTypes(HashSet<String, ASCIICaseInsensitiveHash>&);
+ static MediaPlayer::SupportsType supportsType(const MediaEngineSupportParameters&);
+ static bool initializeGStreamerAndGStreamerDebugging();
+ void createGSTAudioSinkBin();
+ void loadingFailed(MediaPlayer::NetworkState error);
+ void stop();
+ void maybeHandleChangeMutedState(MediaStreamTrackPrivate&);
+ void disableMediaTracks();
+
+ bool m_paused { true };
+ bool m_ended { false };
+ RefPtr<MediaStreamTrackPrivate> m_videoTrack;
+ RefPtr<MediaStreamTrackPrivate> m_audioTrack;
+ GRefPtr<GstElement> m_audioSink;
+ RefPtr<MediaStreamPrivate> m_streamPrivate;
+ GRefPtr<OwrGstVideoRenderer> m_videoRenderer;
+ GRefPtr<OwrGstAudioRenderer> m_audioRenderer;
+
+ HashMap<String, RefPtr<AudioTrackPrivateMediaStream>> m_audioTrackMap;
+ HashMap<String, RefPtr<VideoTrackPrivateMediaStream>> m_videoTrackMap;
+};
+
+} // namespace WebCore
+
+#endif // ENABLE(VIDEO) && ENABLE(MEDIA_STREAM) && USE(GSTREAMER) && USE(OPENWEBRTC)
+
+#endif // MediaPlayerPrivateGStreamerOwr_h
diff --git a/Source/WebCore/platform/graphics/gstreamer/MediaPlayerRequestInstallMissingPluginsCallback.h b/Source/WebCore/platform/graphics/gstreamer/MediaPlayerRequestInstallMissingPluginsCallback.h
new file mode 100644
index 000000000..95ed2da63
--- /dev/null
+++ b/Source/WebCore/platform/graphics/gstreamer/MediaPlayerRequestInstallMissingPluginsCallback.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2015 Igalia S.L.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * aint with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifndef MediaPlayerRequestInstallMissingPluginsCallback_h
+#define MediaPlayerRequestInstallMissingPluginsCallback_h
+
+#if ENABLE(VIDEO) && USE(GSTREAMER)
+#include <wtf/RefCounted.h>
+
+namespace WebCore {
+
+class MediaPlayerRequestInstallMissingPluginsCallback : public RefCounted<MediaPlayerRequestInstallMissingPluginsCallback> {
+ WTF_MAKE_FAST_ALLOCATED();
+public:
+ static Ref<MediaPlayerRequestInstallMissingPluginsCallback> create(std::function<void (uint32_t)>&& function)
+ {
+ return adoptRef(*new MediaPlayerRequestInstallMissingPluginsCallback(WTFMove(function)));
+ }
+
+ void invalidate()
+ {
+ m_function = nullptr;
+ }
+
+ void complete(uint32_t result)
+ {
+ if (!m_function)
+ return;
+ m_function(result);
+ m_function = nullptr;
+ }
+
+private:
+ MediaPlayerRequestInstallMissingPluginsCallback(std::function<void (uint32_t)>&& function)
+ : m_function(WTFMove(function))
+ {
+ }
+
+ std::function<void (uint32_t)> m_function;
+};
+
+} // namespace WebCore
+
+#endif // ENABLE(VIDEO) && USE(GSTREAMER)
+#endif // MediaPlayerRequestInstallMissingPluginsCallback_h
diff --git a/Source/WebCore/platform/graphics/gstreamer/MediaSourceGStreamer.cpp b/Source/WebCore/platform/graphics/gstreamer/MediaSourceGStreamer.cpp
deleted file mode 100644
index 5982f80b9..000000000
--- a/Source/WebCore/platform/graphics/gstreamer/MediaSourceGStreamer.cpp
+++ /dev/null
@@ -1,84 +0,0 @@
-/*
- * Copyright (C) 2013 Google Inc. All rights reserved.
- * Copyright (C) 2013 Orange
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are
- * met:
- *
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above
- * copyright notice, this list of conditions and the following disclaimer
- * in the documentation and/or other materials provided with the
- * distribution.
- * * Neither the name of Google Inc. nor the names of its
- * contributors may be used to endorse or promote products derived from
- * this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#include "config.h"
-#include "MediaSourceGStreamer.h"
-
-#if ENABLE(MEDIA_SOURCE) && USE(GSTREAMER)
-
-#include "SourceBufferPrivateGStreamer.h"
-#include "WebKitMediaSourceGStreamer.h"
-#include <wtf/gobject/GRefPtr.h>
-
-namespace WebCore {
-
-void MediaSourceGStreamer::open(PassRefPtr<HTMLMediaSource> mediaSource, WebKitMediaSrc* src)
-{
- mediaSource->setPrivateAndOpen(adoptRef(*new MediaSourceGStreamer(src)));
-}
-
-MediaSourceGStreamer::MediaSourceGStreamer(WebKitMediaSrc* src)
- : m_client(adoptRef(new MediaSourceClientGstreamer(src)))
- , m_duration(0.0)
- , m_readyState(MediaPlayer::HaveNothing)
-{
-}
-
-MediaSourceGStreamer::~MediaSourceGStreamer()
-{
-}
-
-MediaSourceGStreamer::AddStatus MediaSourceGStreamer::addSourceBuffer(const ContentType& contentType, RefPtr<SourceBufferPrivate>& sourceBufferPrivate)
-{
- sourceBufferPrivate = adoptRef(new SourceBufferPrivateGStreamer(m_client.get(), contentType));
- return MediaSourceGStreamer::Ok;
-}
-
-void MediaSourceGStreamer::setDuration(double duration)
-{
- ASSERT(m_client);
- m_duration = duration;
- m_client->didReceiveDuration(duration);
-}
-
-void MediaSourceGStreamer::markEndOfStream(EndOfStreamStatus)
-{
- ASSERT(m_client);
- m_client->didFinishLoading(0);
-}
-
-void MediaSourceGStreamer::unmarkEndOfStream()
-{
- ASSERT(m_client);
-}
-
-}
-#endif
diff --git a/Source/WebCore/platform/graphics/gstreamer/SourceBufferPrivateGStreamer.cpp b/Source/WebCore/platform/graphics/gstreamer/SourceBufferPrivateGStreamer.cpp
deleted file mode 100644
index 7068a558c..000000000
--- a/Source/WebCore/platform/graphics/gstreamer/SourceBufferPrivateGStreamer.cpp
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * Copyright (C) 2013 Google Inc. All rights reserved.
- * Copyright (C) 2013 Orange
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are
- * met:
- *
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above
- * copyright notice, this list of conditions and the following disclaimer
- * in the documentation and/or other materials provided with the
- * distribution.
- * * Neither the name of Google Inc. nor the names of its
- * contributors may be used to endorse or promote products derived from
- * this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#include "config.h"
-#include "SourceBufferPrivateGStreamer.h"
-
-#if ENABLE(MEDIA_SOURCE) && USE(GSTREAMER)
-
-#include "ContentType.h"
-#include "NotImplemented.h"
-
-namespace WebCore {
-
-SourceBufferPrivateGStreamer::SourceBufferPrivateGStreamer(PassRefPtr<MediaSourceClientGstreamer> client, const ContentType& contentType)
- : m_readyState(MediaPlayer::HaveNothing)
-{
- m_client = client;
- m_type = contentType.type();
-}
-
-SourceBufferPrivate::AppendResult SourceBufferPrivateGStreamer::append(const unsigned char* data, unsigned length)
-{
- AppendResult result = AppendSucceeded;
- ASSERT(m_client);
- m_client->didReceiveData(reinterpret_cast_ptr<const char*>(data), length, m_type);
- return result;
-}
-
-void SourceBufferPrivateGStreamer::abort()
-{
- notImplemented();
-}
-
-void SourceBufferPrivateGStreamer::removedFromMediaSource()
-{
- notImplemented();
-}
-
-}
-#endif
diff --git a/Source/WebCore/platform/graphics/gstreamer/TextCombinerGStreamer.cpp b/Source/WebCore/platform/graphics/gstreamer/TextCombinerGStreamer.cpp
index 339ca37eb..02de1e8bd 100644
--- a/Source/WebCore/platform/graphics/gstreamer/TextCombinerGStreamer.cpp
+++ b/Source/WebCore/platform/graphics/gstreamer/TextCombinerGStreamer.cpp
@@ -76,7 +76,7 @@ static gboolean webkitTextCombinerPadEvent(GstPad*, GstObject* parent, GstEvent*
static void webkit_text_combiner_init(WebKitTextCombiner* combiner)
{
- combiner->funnel = gst_element_factory_make("funnel", NULL);
+ combiner->funnel = gst_element_factory_make("funnel", nullptr);
ASSERT(combiner->funnel);
gboolean ret = gst_bin_add(GST_BIN(combiner), combiner->funnel);
@@ -147,7 +147,7 @@ static gboolean webkitTextCombinerPadEvent(GstPad* pad, GstObject* parent, GstEv
* the funnel */
if (targetParent == combiner->funnel) {
/* Setup a WebVTT encoder */
- GstElement* encoder = gst_element_factory_make("webvttenc", NULL);
+ GstElement* encoder = gst_element_factory_make("webvttenc", nullptr);
ASSERT(encoder);
ret = gst_bin_add(GST_BIN(combiner), encoder);
@@ -232,7 +232,7 @@ static GstPad* webkitTextCombinerRequestNewPad(GstElement * element,
GstPad* pad = gst_element_request_pad(combiner->funnel, templ, name, caps);
ASSERT(pad);
- GstPad* ghostPad = GST_PAD(g_object_new(WEBKIT_TYPE_TEXT_COMBINER_PAD, "direction", gst_pad_get_direction(pad), NULL));
+ GstPad* ghostPad = GST_PAD(g_object_new(WEBKIT_TYPE_TEXT_COMBINER_PAD, "direction", gst_pad_get_direction(pad), nullptr));
ASSERT(ghostPad);
ret = gst_ghost_pad_construct(GST_GHOST_PAD(ghostPad));
@@ -295,7 +295,7 @@ static void webkit_text_combiner_pad_class_init(WebKitTextCombinerPadClass* klas
GstElement* webkitTextCombinerNew()
{
- return GST_ELEMENT(g_object_new(WEBKIT_TYPE_TEXT_COMBINER, 0));
+ return GST_ELEMENT(g_object_new(WEBKIT_TYPE_TEXT_COMBINER, nullptr));
}
#endif // ENABLE(VIDEO) && USE(GSTREAMER) && ENABLE(VIDEO_TRACK)
diff --git a/Source/WebCore/platform/graphics/gstreamer/TextSinkGStreamer.cpp b/Source/WebCore/platform/graphics/gstreamer/TextSinkGStreamer.cpp
index 678e7ac35..e651debfe 100644
--- a/Source/WebCore/platform/graphics/gstreamer/TextSinkGStreamer.cpp
+++ b/Source/WebCore/platform/graphics/gstreamer/TextSinkGStreamer.cpp
@@ -95,7 +95,7 @@ static void webkit_text_sink_class_init(WebKitTextSinkClass* klass)
GstElement* webkitTextSinkNew()
{
- return GST_ELEMENT(g_object_new(WEBKIT_TYPE_TEXT_SINK, 0));
+ return GST_ELEMENT(g_object_new(WEBKIT_TYPE_TEXT_SINK, nullptr));
}
#endif // ENABLE(VIDEO) && USE(GSTREAMER) && ENABLE(VIDEO_TRACK)
diff --git a/Source/WebCore/platform/graphics/gstreamer/TrackPrivateBaseGStreamer.cpp b/Source/WebCore/platform/graphics/gstreamer/TrackPrivateBaseGStreamer.cpp
index 16bd494a9..af068cb08 100644
--- a/Source/WebCore/platform/graphics/gstreamer/TrackPrivateBaseGStreamer.cpp
+++ b/Source/WebCore/platform/graphics/gstreamer/TrackPrivateBaseGStreamer.cpp
@@ -34,46 +34,25 @@
#include "TrackPrivateBase.h"
#include <glib-object.h>
#include <gst/gst.h>
-#include <wtf/gobject/GUniquePtr.h>
+#include <gst/tag/tag.h>
+#include <wtf/glib/GUniquePtr.h>
+#include <wtf/text/CString.h>
GST_DEBUG_CATEGORY_EXTERN(webkit_media_player_debug);
#define GST_CAT_DEFAULT webkit_media_player_debug
namespace WebCore {
-static void trackPrivateActiveChangedCallback(GObject*, GParamSpec*, TrackPrivateBaseGStreamer* track)
-{
- track->activeChanged();
-}
-
-static void trackPrivateTagsChangedCallback(GObject*, GParamSpec*, TrackPrivateBaseGStreamer* track)
-{
- track->tagsChanged();
-}
-
-static gboolean trackPrivateActiveChangeTimeoutCallback(TrackPrivateBaseGStreamer* track)
-{
- track->notifyTrackOfActiveChanged();
- return FALSE;
-}
-
-static gboolean trackPrivateTagsChangeTimeoutCallback(TrackPrivateBaseGStreamer* track)
-{
- track->notifyTrackOfTagsChanged();
- return FALSE;
-}
-
TrackPrivateBaseGStreamer::TrackPrivateBaseGStreamer(TrackPrivateBase* owner, gint index, GRefPtr<GstPad> pad)
- : m_index(index)
+ : m_notifier(MainThreadNotifier<MainThreadNotification>::create())
+ , m_index(index)
, m_pad(pad)
, m_owner(owner)
- , m_activeTimerHandler(0)
- , m_tagTimerHandler(0)
{
ASSERT(m_pad);
- g_signal_connect(m_pad.get(), "notify::active", G_CALLBACK(trackPrivateActiveChangedCallback), this);
- g_signal_connect(m_pad.get(), "notify::tags", G_CALLBACK(trackPrivateTagsChangedCallback), this);
+ g_signal_connect_swapped(m_pad.get(), "notify::active", G_CALLBACK(activeChangedCallback), this);
+ g_signal_connect_swapped(m_pad.get(), "notify::tags", G_CALLBACK(tagsChangedCallback), this);
// We can't call notifyTrackOfTagsChanged() directly, because we need tagsChanged()
// to setup m_tags.
@@ -83,6 +62,7 @@ TrackPrivateBaseGStreamer::TrackPrivateBaseGStreamer(TrackPrivateBase* owner, gi
TrackPrivateBaseGStreamer::~TrackPrivateBaseGStreamer()
{
disconnect();
+ m_notifier->invalidate();
}
void TrackPrivateBaseGStreamer::disconnect()
@@ -90,67 +70,71 @@ void TrackPrivateBaseGStreamer::disconnect()
if (!m_pad)
return;
- g_signal_handlers_disconnect_by_func(m_pad.get(),
- reinterpret_cast<gpointer>(trackPrivateActiveChangedCallback), this);
- g_signal_handlers_disconnect_by_func(m_pad.get(),
- reinterpret_cast<gpointer>(trackPrivateTagsChangedCallback), this);
-
- if (m_activeTimerHandler)
- g_source_remove(m_activeTimerHandler);
- m_activeTimerHandler = 0;
-
- if (m_tagTimerHandler)
- g_source_remove(m_tagTimerHandler);
- m_tagTimerHandler = 0;
+ m_notifier->cancelPendingNotifications();
+ g_signal_handlers_disconnect_matched(m_pad.get(), G_SIGNAL_MATCH_DATA, 0, 0, nullptr, nullptr, this);
m_pad.clear();
m_tags.clear();
}
-void TrackPrivateBaseGStreamer::activeChanged()
+void TrackPrivateBaseGStreamer::activeChangedCallback(TrackPrivateBaseGStreamer* track)
{
- if (m_activeTimerHandler)
- g_source_remove(m_activeTimerHandler);
- m_activeTimerHandler = g_timeout_add(0,
- reinterpret_cast<GSourceFunc>(trackPrivateActiveChangeTimeoutCallback), this);
- g_source_set_name_by_id(m_activeTimerHandler, "[WebKit] trackPrivateActiveChangeTimeoutCallback");
+ track->m_notifier->notify(MainThreadNotification::ActiveChanged, [track] { track->notifyTrackOfActiveChanged(); });
}
-void TrackPrivateBaseGStreamer::tagsChanged()
+void TrackPrivateBaseGStreamer::tagsChangedCallback(TrackPrivateBaseGStreamer* track)
{
- if (m_tagTimerHandler)
- g_source_remove(m_tagTimerHandler);
+ track->tagsChanged();
+}
+void TrackPrivateBaseGStreamer::tagsChanged()
+{
GRefPtr<GstTagList> tags;
- g_object_get(m_pad.get(), "tags", &tags.outPtr(), NULL);
+ if (g_object_class_find_property(G_OBJECT_GET_CLASS(m_pad.get()), "tags"))
+ g_object_get(m_pad.get(), "tags", &tags.outPtr(), nullptr);
+ else
+ tags = adoptGRef(gst_tag_list_new_empty());
+
{
- MutexLocker lock(m_tagMutex);
+ LockHolder lock(m_tagMutex);
m_tags.swap(tags);
}
- m_tagTimerHandler = g_timeout_add(0,
- reinterpret_cast<GSourceFunc>(trackPrivateTagsChangeTimeoutCallback), this);
- g_source_set_name_by_id(m_tagTimerHandler, "[WebKit] trackPrivateTagsChangeTimeoutCallback");
+ m_notifier->notify(MainThreadNotification::TagsChanged, [this] { notifyTrackOfTagsChanged(); });
}
void TrackPrivateBaseGStreamer::notifyTrackOfActiveChanged()
{
- m_activeTimerHandler = 0;
if (!m_pad)
return;
gboolean active = false;
- if (m_pad)
- g_object_get(m_pad.get(), "active", &active, NULL);
+ if (m_pad && g_object_class_find_property(G_OBJECT_GET_CLASS(m_pad.get()), "active"))
+ g_object_get(m_pad.get(), "active", &active, nullptr);
setActive(active);
}
-bool TrackPrivateBaseGStreamer::getTag(GstTagList* tags, const gchar* tagName, String& value)
+bool TrackPrivateBaseGStreamer::getLanguageCode(GstTagList* tags, AtomicString& value)
+{
+ String language;
+ if (getTag(tags, GST_TAG_LANGUAGE_CODE, language)) {
+ language = gst_tag_get_language_code_iso_639_1(language.utf8().data());
+ GST_INFO("Converted track %d's language code to %s.", m_index, language.utf8().data());
+ if (language != value) {
+ value = language;
+ return true;
+ }
+ }
+ return false;
+}
+
+template<class StringType>
+bool TrackPrivateBaseGStreamer::getTag(GstTagList* tags, const gchar* tagName, StringType& value)
{
GUniqueOutPtr<gchar> tagValue;
if (gst_tag_list_get_string(tags, tagName, &tagValue.outPtr())) {
- INFO_MEDIA_MESSAGE("Track %d got %s %s.", m_index, tagName, tagValue.get());
+ GST_INFO("Track %d got %s %s.", m_index, tagName, tagValue.get());
value = tagValue.get();
return true;
}
@@ -159,24 +143,33 @@ bool TrackPrivateBaseGStreamer::getTag(GstTagList* tags, const gchar* tagName, S
void TrackPrivateBaseGStreamer::notifyTrackOfTagsChanged()
{
- m_tagTimerHandler = 0;
if (!m_pad)
return;
TrackPrivateBaseClient* client = m_owner->client();
+ if (!client)
+ return;
+
GRefPtr<GstTagList> tags;
{
- MutexLocker lock(m_tagMutex);
+ LockHolder lock(m_tagMutex);
tags.swap(m_tags);
}
if (!tags)
return;
- if (getTag(tags.get(), GST_TAG_TITLE, m_label) && client)
- client->labelChanged(m_owner, m_label);
+ if (getTag(tags.get(), GST_TAG_TITLE, m_label))
+ client->labelChanged(m_label);
+
+ AtomicString language;
+ if (!getLanguageCode(tags.get(), language))
+ return;
+
+ if (language == m_language)
+ return;
- if (getTag(tags.get(), GST_TAG_LANGUAGE_CODE, m_language) && client)
- client->languageChanged(m_owner, m_language);
+ m_language = language;
+ client->languageChanged(m_language);
}
} // namespace WebCore
diff --git a/Source/WebCore/platform/graphics/gstreamer/TrackPrivateBaseGStreamer.h b/Source/WebCore/platform/graphics/gstreamer/TrackPrivateBaseGStreamer.h
index 1e3b8c898..8e3488497 100644
--- a/Source/WebCore/platform/graphics/gstreamer/TrackPrivateBaseGStreamer.h
+++ b/Source/WebCore/platform/graphics/gstreamer/TrackPrivateBaseGStreamer.h
@@ -29,6 +29,8 @@
#if ENABLE(VIDEO) && USE(GSTREAMER) && ENABLE(VIDEO_TRACK)
#include "GRefPtrGStreamer.h"
+#include "MainThreadNotifier.h"
+#include <wtf/Lock.h>
#include <wtf/ThreadingPrimitives.h>
#include <wtf/text/WTFString.h>
@@ -48,28 +50,38 @@ public:
void setIndex(int index) { m_index = index; }
- void activeChanged();
- void tagsChanged();
+protected:
+ TrackPrivateBaseGStreamer(TrackPrivateBase* owner, gint index, GRefPtr<GstPad>);
void notifyTrackOfActiveChanged();
void notifyTrackOfTagsChanged();
-protected:
- TrackPrivateBaseGStreamer(TrackPrivateBase* owner, gint index, GRefPtr<GstPad>);
+ enum MainThreadNotification {
+ ActiveChanged = 1 << 0,
+ TagsChanged = 1 << 1,
+ NewSample = 1 << 2,
+ StreamChanged = 1 << 3
+ };
+ Ref<MainThreadNotifier<MainThreadNotification>> m_notifier;
gint m_index;
- String m_label;
- String m_language;
+ AtomicString m_label;
+ AtomicString m_language;
GRefPtr<GstPad> m_pad;
private:
- bool getTag(GstTagList* tags, const gchar* tagName, String& value);
+ bool getLanguageCode(GstTagList* tags, AtomicString& value);
- TrackPrivateBase* m_owner;
- guint m_activeTimerHandler;
- guint m_tagTimerHandler;
+ template<class StringType>
+ bool getTag(GstTagList* tags, const gchar* tagName, StringType& value);
+
+ static void activeChangedCallback(TrackPrivateBaseGStreamer*);
+ static void tagsChangedCallback(TrackPrivateBaseGStreamer*);
- Mutex m_tagMutex;
+ void tagsChanged();
+
+ TrackPrivateBase* m_owner;
+ Lock m_tagMutex;
GRefPtr<GstTagList> m_tags;
};
diff --git a/Source/WebCore/platform/graphics/gstreamer/VideoSinkGStreamer.cpp b/Source/WebCore/platform/graphics/gstreamer/VideoSinkGStreamer.cpp
index 7dd894e28..9adaaa0bb 100644
--- a/Source/WebCore/platform/graphics/gstreamer/VideoSinkGStreamer.cpp
+++ b/Source/WebCore/platform/graphics/gstreamer/VideoSinkGStreamer.cpp
@@ -1,7 +1,8 @@
/*
* Copyright (C) 2007 OpenedHand
* Copyright (C) 2007 Alp Toker <alp@atoker.com>
- * Copyright (C) 2009, 2010, 2011, 2012 Igalia S.L
+ * Copyright (C) 2009, 2010, 2011, 2012, 2015, 2016 Igalia S.L
+ * Copyright (C) 2015, 2016 Metrological Group B.V.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@@ -35,8 +36,8 @@
#include <glib.h>
#include <gst/gst.h>
#include <gst/video/gstvideometa.h>
-#include <wtf/OwnPtr.h>
-#include <wtf/gobject/GMutexLocker.h>
+#include <wtf/Condition.h>
+#include <wtf/RunLoop.h>
using namespace WebCore;
@@ -46,13 +47,8 @@ using namespace WebCore;
#else
#define GST_CAPS_FORMAT "{ xRGB, ARGB }"
#endif
-#if GST_CHECK_VERSION(1, 1, 0)
-#define GST_FEATURED_CAPS GST_VIDEO_CAPS_MAKE_WITH_FEATURES(GST_CAPS_FEATURE_META_GST_VIDEO_GL_TEXTURE_UPLOAD_META, GST_CAPS_FORMAT) ";"
-#else
-#define GST_FEATURED_CAPS
-#endif
-#define WEBKIT_VIDEO_SINK_PAD_CAPS GST_FEATURED_CAPS GST_VIDEO_CAPS_MAKE(GST_CAPS_FORMAT)
+#define WEBKIT_VIDEO_SINK_PAD_CAPS GST_VIDEO_CAPS_MAKE_WITH_FEATURES(GST_CAPS_FEATURE_META_GST_VIDEO_GL_TEXTURE_UPLOAD_META, GST_CAPS_FORMAT) ";" GST_VIDEO_CAPS_MAKE(GST_CAPS_FORMAT)
static GstStaticPadTemplate s_sinkTemplate = GST_STATIC_PAD_TEMPLATE("sink", GST_PAD_SINK, GST_PAD_ALWAYS, GST_STATIC_CAPS(WEBKIT_VIDEO_SINK_PAD_CAPS));
@@ -62,35 +58,123 @@ GST_DEBUG_CATEGORY_STATIC(webkitVideoSinkDebug);
enum {
REPAINT_REQUESTED,
+ REPAINT_CANCELLED,
LAST_SIGNAL
};
-enum {
- PROP_0,
- PROP_CAPS
-};
-
static guint webkitVideoSinkSignals[LAST_SIGNAL] = { 0, };
-struct _WebKitVideoSinkPrivate {
- GstBuffer* buffer;
- guint timeoutId;
- GMutex* bufferMutex;
- GCond* dataCondition;
+static void webkitVideoSinkRepaintRequested(WebKitVideoSink*, GstSample*);
+static GRefPtr<GstSample> webkitVideoSinkRequestRender(WebKitVideoSink*, GstBuffer*);
- GstVideoInfo info;
+class VideoRenderRequestScheduler {
+public:
+ VideoRenderRequestScheduler()
+#if !USE(COORDINATED_GRAPHICS_THREADED)
+ : m_timer(RunLoop::main(), this, &VideoRenderRequestScheduler::render)
+#endif
+ {
+#if PLATFORM(GTK) && !USE(COORDINATED_GRAPHICS_THREADED)
+ // Use a higher priority than WebCore timers (G_PRIORITY_HIGH_IDLE + 20).
+ m_timer.setPriority(G_PRIORITY_HIGH_IDLE + 19);
+#endif
+ }
- GstCaps* currentCaps;
+ void start()
+ {
+ LockHolder locker(m_sampleMutex);
+ m_unlocked = false;
+ }
+
+ void stop()
+ {
+ LockHolder locker(m_sampleMutex);
+ m_sample = nullptr;
+ m_unlocked = true;
+#if !USE(COORDINATED_GRAPHICS_THREADED)
+ m_timer.stop();
+ m_dataCondition.notifyOne();
+#endif
+ }
+
+ void drain()
+ {
+ LockHolder locker(m_sampleMutex);
+ m_sample = nullptr;
+ }
+
+ bool requestRender(WebKitVideoSink* sink, GstBuffer* buffer)
+ {
+ LockHolder locker(m_sampleMutex);
+ if (m_unlocked)
+ return true;
+
+ m_sample = webkitVideoSinkRequestRender(sink, buffer);
+ if (!m_sample)
+ return false;
+
+#if USE(COORDINATED_GRAPHICS_THREADED)
+ auto sample = WTFMove(m_sample);
+ locker.unlockEarly();
+ if (LIKELY(GST_IS_SAMPLE(sample.get())))
+ webkitVideoSinkRepaintRequested(sink, sample.get());
+#else
+ m_sink = sink;
+ m_timer.startOneShot(0);
+ m_dataCondition.wait(m_sampleMutex);
+#endif
+ return true;
+ }
- // If this is TRUE all processing should finish ASAP
+private:
+
+#if !USE(COORDINATED_GRAPHICS_THREADED)
+ void render()
+ {
+ LockHolder locker(m_sampleMutex);
+ GRefPtr<GstSample> sample = WTFMove(m_sample);
+ GRefPtr<WebKitVideoSink> sink = WTFMove(m_sink);
+ if (sample && !m_unlocked && LIKELY(GST_IS_SAMPLE(sample.get())))
+ webkitVideoSinkRepaintRequested(sink.get(), sample.get());
+ m_dataCondition.notifyOne();
+ }
+#endif
+
+ Lock m_sampleMutex;
+ GRefPtr<GstSample> m_sample;
+
+#if !USE(COORDINATED_GRAPHICS_THREADED)
+ RunLoop::Timer<VideoRenderRequestScheduler> m_timer;
+ Condition m_dataCondition;
+ GRefPtr<WebKitVideoSink> m_sink;
+#endif
+
+ // If this is true all processing should finish ASAP
// This is necessary because there could be a race between
// unlock() and render(), where unlock() wins, signals the
- // GCond, then render() tries to render a frame although
+ // Condition, then render() tries to render a frame although
// everything else isn't running anymore. This will lead
// to deadlocks because render() holds the stream lock.
//
- // Protected by the buffer mutex
- bool unlocked;
+ // Protected by the sample mutex
+ bool m_unlocked { false };
+};
+
+struct _WebKitVideoSinkPrivate {
+ _WebKitVideoSinkPrivate()
+ {
+ gst_video_info_init(&info);
+ }
+
+ ~_WebKitVideoSinkPrivate()
+ {
+ if (currentCaps)
+ gst_caps_unref(currentCaps);
+ }
+
+ VideoRenderRequestScheduler scheduler;
+ GstVideoInfo info;
+ GstCaps* currentCaps;
};
#define webkit_video_sink_parent_class parent_class
@@ -100,59 +184,29 @@ G_DEFINE_TYPE_WITH_CODE(WebKitVideoSink, webkit_video_sink, GST_TYPE_VIDEO_SINK,
static void webkit_video_sink_init(WebKitVideoSink* sink)
{
sink->priv = G_TYPE_INSTANCE_GET_PRIVATE(sink, WEBKIT_TYPE_VIDEO_SINK, WebKitVideoSinkPrivate);
-#if GLIB_CHECK_VERSION(2, 31, 0)
- sink->priv->dataCondition = new GCond;
- g_cond_init(sink->priv->dataCondition);
- sink->priv->bufferMutex = new GMutex;
- g_mutex_init(sink->priv->bufferMutex);
-#else
- sink->priv->dataCondition = g_cond_new();
- sink->priv->bufferMutex = g_mutex_new();
-#endif
-
- gst_video_info_init(&sink->priv->info);
+ g_object_set(GST_BASE_SINK(sink), "enable-last-sample", FALSE, nullptr);
+ new (sink->priv) WebKitVideoSinkPrivate();
}
-static gboolean webkitVideoSinkTimeoutCallback(gpointer data)
+static void webkitVideoSinkRepaintRequested(WebKitVideoSink* sink, GstSample* sample)
{
- WebKitVideoSink* sink = reinterpret_cast<WebKitVideoSink*>(data);
- WebKitVideoSinkPrivate* priv = sink->priv;
-
- WTF::GMutexLocker lock(priv->bufferMutex);
- GstBuffer* buffer = priv->buffer;
- priv->buffer = 0;
- priv->timeoutId = 0;
-
- if (!buffer || priv->unlocked || UNLIKELY(!GST_IS_BUFFER(buffer))) {
- g_cond_signal(priv->dataCondition);
- return FALSE;
- }
-
- g_signal_emit(sink, webkitVideoSinkSignals[REPAINT_REQUESTED], 0, buffer);
- gst_buffer_unref(buffer);
- g_cond_signal(priv->dataCondition);
+ g_signal_emit(sink, webkitVideoSinkSignals[REPAINT_REQUESTED], 0, sample);
+}
- return FALSE;
+static void webkitVideoSinkRepaintCancelled(WebKitVideoSink* sink)
+{
+ g_signal_emit(sink, webkitVideoSinkSignals[REPAINT_CANCELLED], 0);
}
-static GstFlowReturn webkitVideoSinkRender(GstBaseSink* baseSink, GstBuffer* buffer)
+static GRefPtr<GstSample> webkitVideoSinkRequestRender(WebKitVideoSink* sink, GstBuffer* buffer)
{
- WebKitVideoSink* sink = WEBKIT_VIDEO_SINK(baseSink);
WebKitVideoSinkPrivate* priv = sink->priv;
-
- WTF::GMutexLocker lock(priv->bufferMutex);
-
- if (priv->unlocked)
- return GST_FLOW_OK;
-
- priv->buffer = gst_buffer_ref(buffer);
+ GRefPtr<GstSample> sample = adoptGRef(gst_sample_new(buffer, priv->currentCaps, nullptr, nullptr));
// The video info structure is valid only if the sink handled an allocation query.
GstVideoFormat format = GST_VIDEO_INFO_FORMAT(&priv->info);
- if (format == GST_VIDEO_FORMAT_UNKNOWN) {
- gst_buffer_unref(buffer);
- return GST_FLOW_ERROR;
- }
+ if (format == GST_VIDEO_FORMAT_UNKNOWN)
+ return nullptr;
#if !(USE(TEXTURE_MAPPER_GL) && !USE(COORDINATED_GRAPHICS))
// Cairo's ARGB has pre-multiplied alpha while GStreamer's doesn't.
@@ -166,10 +220,8 @@ static GstFlowReturn webkitVideoSinkRender(GstBaseSink* baseSink, GstBuffer* buf
GstBuffer* newBuffer = WebCore::createGstBuffer(buffer);
// Check if allocation failed.
- if (UNLIKELY(!newBuffer)) {
- gst_buffer_unref(buffer);
- return GST_FLOW_ERROR;
- }
+ if (UNLIKELY(!newBuffer))
+ return nullptr;
// We don't use Color::premultipliedARGBFromColor() here because
// one function call per video pixel is just too expensive:
@@ -179,15 +231,13 @@ static GstFlowReturn webkitVideoSinkRender(GstBaseSink* baseSink, GstBuffer* buf
GstVideoFrame destinationFrame;
if (!gst_video_frame_map(&sourceFrame, &priv->info, buffer, GST_MAP_READ)) {
- gst_buffer_unref(buffer);
gst_buffer_unref(newBuffer);
- return GST_FLOW_ERROR;
+ return nullptr;
}
if (!gst_video_frame_map(&destinationFrame, &priv->info, newBuffer, GST_MAP_WRITE)) {
gst_video_frame_unmap(&sourceFrame);
- gst_buffer_unref(buffer);
gst_buffer_unref(newBuffer);
- return GST_FLOW_ERROR;
+ return nullptr;
}
const guint8* source = static_cast<guint8*>(GST_VIDEO_FRAME_PLANE_DATA(&sourceFrame, 0));
@@ -215,87 +265,32 @@ static GstFlowReturn webkitVideoSinkRender(GstBaseSink* baseSink, GstBuffer* buf
gst_video_frame_unmap(&sourceFrame);
gst_video_frame_unmap(&destinationFrame);
- gst_buffer_unref(buffer);
- buffer = priv->buffer = newBuffer;
- }
-#endif
-
- // This should likely use a lower priority, but glib currently starves
- // lower priority sources.
- // See: https://bugzilla.gnome.org/show_bug.cgi?id=610830.
- priv->timeoutId = g_timeout_add_full(G_PRIORITY_DEFAULT, 0, webkitVideoSinkTimeoutCallback,
- gst_object_ref(sink), reinterpret_cast<GDestroyNotify>(gst_object_unref));
- g_source_set_name_by_id(priv->timeoutId, "[WebKit] webkitVideoSinkTimeoutCallback");
-
- g_cond_wait(priv->dataCondition, priv->bufferMutex);
- return GST_FLOW_OK;
-}
-
-static void webkitVideoSinkDispose(GObject* object)
-{
- WebKitVideoSink* sink = WEBKIT_VIDEO_SINK(object);
- WebKitVideoSinkPrivate* priv = sink->priv;
-
- if (priv->dataCondition) {
-#if GLIB_CHECK_VERSION(2, 31, 0)
- g_cond_clear(priv->dataCondition);
- delete priv->dataCondition;
-#else
- g_cond_free(priv->dataCondition);
-#endif
- priv->dataCondition = 0;
+ sample = adoptGRef(gst_sample_new(newBuffer, priv->currentCaps, nullptr, nullptr));
+ gst_buffer_unref(newBuffer);
}
-
- if (priv->bufferMutex) {
-#if GLIB_CHECK_VERSION(2, 31, 0)
- g_mutex_clear(priv->bufferMutex);
- delete priv->bufferMutex;
-#else
- g_mutex_free(priv->bufferMutex);
#endif
- priv->bufferMutex = 0;
- }
- G_OBJECT_CLASS(parent_class)->dispose(object);
+ return sample;
}
-static void webkitVideoSinkGetProperty(GObject* object, guint propertyId, GValue* value, GParamSpec* parameterSpec)
+static GstFlowReturn webkitVideoSinkRender(GstBaseSink* baseSink, GstBuffer* buffer)
{
- WebKitVideoSink* sink = WEBKIT_VIDEO_SINK(object);
- WebKitVideoSinkPrivate* priv = sink->priv;
-
- switch (propertyId) {
- case PROP_CAPS: {
- GstCaps* caps = priv->currentCaps;
- if (caps)
- gst_caps_ref(caps);
- g_value_take_boxed(value, caps);
- break;
- }
- default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID(object, propertyId, parameterSpec);
- }
+ WebKitVideoSink* sink = WEBKIT_VIDEO_SINK(baseSink);
+ return sink->priv->scheduler.requestRender(sink, buffer) ? GST_FLOW_OK : GST_FLOW_ERROR;
}
-static void unlockBufferMutex(WebKitVideoSinkPrivate* priv)
+static void webkitVideoSinkFinalize(GObject* object)
{
- WTF::GMutexLocker lock(priv->bufferMutex);
-
- if (priv->buffer) {
- gst_buffer_unref(priv->buffer);
- priv->buffer = 0;
- }
-
- priv->unlocked = true;
-
- g_cond_signal(priv->dataCondition);
+ WEBKIT_VIDEO_SINK(object)->priv->~WebKitVideoSinkPrivate();
+ G_OBJECT_CLASS(parent_class)->finalize(object);
}
static gboolean webkitVideoSinkUnlock(GstBaseSink* baseSink)
{
- WebKitVideoSink* sink = WEBKIT_VIDEO_SINK(baseSink);
+ WebKitVideoSinkPrivate* priv = WEBKIT_VIDEO_SINK(baseSink)->priv;
- unlockBufferMutex(sink->priv);
+ priv->scheduler.stop();
+ webkitVideoSinkRepaintCancelled(WEBKIT_VIDEO_SINK(baseSink));
return GST_CALL_PARENT_WITH_DEFAULT(GST_BASE_SINK_CLASS, unlock, (baseSink), TRUE);
}
@@ -304,10 +299,7 @@ static gboolean webkitVideoSinkUnlockStop(GstBaseSink* baseSink)
{
WebKitVideoSinkPrivate* priv = WEBKIT_VIDEO_SINK(baseSink)->priv;
- {
- WTF::GMutexLocker lock(priv->bufferMutex);
- priv->unlocked = false;
- }
+ priv->scheduler.start();
return GST_CALL_PARENT_WITH_DEFAULT(GST_BASE_SINK_CLASS, unlock_stop, (baseSink), TRUE);
}
@@ -316,11 +308,11 @@ static gboolean webkitVideoSinkStop(GstBaseSink* baseSink)
{
WebKitVideoSinkPrivate* priv = WEBKIT_VIDEO_SINK(baseSink)->priv;
- unlockBufferMutex(priv);
-
+ priv->scheduler.stop();
+ webkitVideoSinkRepaintCancelled(WEBKIT_VIDEO_SINK(baseSink));
if (priv->currentCaps) {
gst_caps_unref(priv->currentCaps);
- priv->currentCaps = 0;
+ priv->currentCaps = nullptr;
}
return TRUE;
@@ -330,8 +322,8 @@ static gboolean webkitVideoSinkStart(GstBaseSink* baseSink)
{
WebKitVideoSinkPrivate* priv = WEBKIT_VIDEO_SINK(baseSink)->priv;
- WTF::GMutexLocker lock(priv->bufferMutex);
- priv->unlocked = false;
+ priv->scheduler.start();
+
return TRUE;
}
@@ -357,7 +349,7 @@ static gboolean webkitVideoSinkSetCaps(GstBaseSink* baseSink, GstCaps* caps)
static gboolean webkitVideoSinkProposeAllocation(GstBaseSink* baseSink, GstQuery* query)
{
GstCaps* caps;
- gst_query_parse_allocation(query, &caps, 0);
+ gst_query_parse_allocation(query, &caps, nullptr);
if (!caps)
return FALSE;
@@ -365,14 +357,27 @@ static gboolean webkitVideoSinkProposeAllocation(GstBaseSink* baseSink, GstQuery
if (!gst_video_info_from_caps(&sink->priv->info, caps))
return FALSE;
- gst_query_add_allocation_meta(query, GST_VIDEO_META_API_TYPE, 0);
- gst_query_add_allocation_meta(query, GST_VIDEO_CROP_META_API_TYPE, 0);
-#if GST_CHECK_VERSION(1, 1, 0)
- gst_query_add_allocation_meta(query, GST_VIDEO_GL_TEXTURE_UPLOAD_META_API_TYPE, 0);
-#endif
+ gst_query_add_allocation_meta(query, GST_VIDEO_META_API_TYPE, nullptr);
+ gst_query_add_allocation_meta(query, GST_VIDEO_CROP_META_API_TYPE, nullptr);
+ gst_query_add_allocation_meta(query, GST_VIDEO_GL_TEXTURE_UPLOAD_META_API_TYPE, nullptr);
return TRUE;
}
+static gboolean webkitVideoSinkEvent(GstBaseSink* baseSink, GstEvent* event)
+{
+ switch (GST_EVENT_TYPE(event)) {
+ case GST_EVENT_FLUSH_START: {
+ WebKitVideoSink* sink = WEBKIT_VIDEO_SINK(baseSink);
+ sink->priv->scheduler.drain();
+
+ GST_DEBUG_OBJECT(sink, "Flush-start, releasing m_sample");
+ }
+ FALLTHROUGH;
+ default:
+ return GST_CALL_PARENT_WITH_DEFAULT(GST_BASE_SINK_CLASS, event, (baseSink, event), TRUE);
+ }
+}
+
static void webkit_video_sink_class_init(WebKitVideoSinkClass* klass)
{
GObjectClass* gobjectClass = G_OBJECT_CLASS(klass);
@@ -384,8 +389,7 @@ static void webkit_video_sink_class_init(WebKitVideoSinkClass* klass)
g_type_class_add_private(klass, sizeof(WebKitVideoSinkPrivate));
- gobjectClass->dispose = webkitVideoSinkDispose;
- gobjectClass->get_property = webkitVideoSinkGetProperty;
+ gobjectClass->finalize = webkitVideoSinkFinalize;
baseSinkClass->unlock = webkitVideoSinkUnlock;
baseSinkClass->unlock_stop = webkitVideoSinkUnlockStop;
@@ -395,9 +399,7 @@ static void webkit_video_sink_class_init(WebKitVideoSinkClass* klass)
baseSinkClass->start = webkitVideoSinkStart;
baseSinkClass->set_caps = webkitVideoSinkSetCaps;
baseSinkClass->propose_allocation = webkitVideoSinkProposeAllocation;
-
- g_object_class_install_property(gobjectClass, PROP_CAPS,
- g_param_spec_boxed("current-caps", "Current-Caps", "Current caps", GST_TYPE_CAPS, G_PARAM_READABLE));
+ baseSinkClass->event = webkitVideoSinkEvent;
webkitVideoSinkSignals[REPAINT_REQUESTED] = g_signal_new("repaint-requested",
G_TYPE_FROM_CLASS(klass),
@@ -408,13 +410,23 @@ static void webkit_video_sink_class_init(WebKitVideoSinkClass* klass)
g_cclosure_marshal_generic,
G_TYPE_NONE, // Return type
1, // Only one parameter
- GST_TYPE_BUFFER);
+ GST_TYPE_SAMPLE);
+ webkitVideoSinkSignals[REPAINT_CANCELLED] = g_signal_new("repaint-cancelled",
+ G_TYPE_FROM_CLASS(klass),
+ G_SIGNAL_RUN_LAST,
+ 0, // Class offset
+ nullptr, // Accumulator
+ nullptr, // Accumulator data
+ g_cclosure_marshal_generic,
+ G_TYPE_NONE, // Return type
+ 0, // No parameters
+ G_TYPE_NONE);
}
GstElement* webkitVideoSinkNew()
{
- return GST_ELEMENT(g_object_new(WEBKIT_TYPE_VIDEO_SINK, 0));
+ return GST_ELEMENT(g_object_new(WEBKIT_TYPE_VIDEO_SINK, nullptr));
}
#endif // ENABLE(VIDEO) && USE(GSTREAMER)
diff --git a/Source/WebCore/platform/graphics/gstreamer/VideoTextureCopierGStreamer.cpp b/Source/WebCore/platform/graphics/gstreamer/VideoTextureCopierGStreamer.cpp
new file mode 100644
index 000000000..abb8b9bcd
--- /dev/null
+++ b/Source/WebCore/platform/graphics/gstreamer/VideoTextureCopierGStreamer.cpp
@@ -0,0 +1,190 @@
+/*
+ Copyright (C) 2016 Igalia S.L.
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+ */
+
+
+#include "config.h"
+#include "VideoTextureCopierGStreamer.h"
+
+#if USE(GSTREAMER_GL)
+
+#include "GLContext.h"
+#include "ImageOrientation.h"
+#include "TextureMapperShaderProgram.h"
+
+namespace WebCore {
+
+VideoTextureCopierGStreamer::VideoTextureCopierGStreamer()
+{
+ GLContext* previousContext = GLContext::current();
+ ASSERT(previousContext);
+ PlatformDisplay::sharedDisplayForCompositing().sharingGLContext()->makeContextCurrent();
+
+ m_context3D = GraphicsContext3D::createForCurrentGLContext();
+
+ m_shaderProgram = TextureMapperShaderProgram::create(*m_context3D, TextureMapperShaderProgram::Texture);
+
+ m_framebuffer = m_context3D->createFramebuffer();
+
+ static const GLfloat vertices[] = { 0, 0, 1, 0, 1, 1, 0, 1 };
+ m_vbo = m_context3D->createBuffer();
+ m_context3D->bindBuffer(GraphicsContext3D::ARRAY_BUFFER, m_vbo);
+ m_context3D->bufferData(GraphicsContext3D::ARRAY_BUFFER, sizeof(GC3Dfloat) * 8, vertices, GraphicsContext3D::STATIC_DRAW);
+
+ updateTextureSpaceMatrix();
+
+ previousContext->makeContextCurrent();
+}
+
+VideoTextureCopierGStreamer::~VideoTextureCopierGStreamer()
+{
+ GLContext* previousContext = GLContext::current();
+ ASSERT(previousContext);
+ PlatformDisplay::sharedDisplayForCompositing().sharingGLContext()->makeContextCurrent();
+
+ m_context3D->deleteFramebuffer(m_framebuffer);
+ m_context3D->deleteBuffer(m_vbo);
+ m_shaderProgram = nullptr;
+ m_context3D = nullptr;
+
+ previousContext->makeContextCurrent();
+}
+
+void VideoTextureCopierGStreamer::updateTextureSpaceMatrix()
+{
+ m_textureSpaceMatrix.makeIdentity();
+
+ switch (m_orientation) {
+ case OriginRightTop:
+ m_textureSpaceMatrix.rotate(-90);
+ m_textureSpaceMatrix.translate(-1, 0);
+ break;
+ case OriginBottomRight:
+ m_textureSpaceMatrix.rotate(180);
+ m_textureSpaceMatrix.translate(-1, -1);
+ break;
+ case OriginLeftBottom:
+ m_textureSpaceMatrix.rotate(-270);
+ m_textureSpaceMatrix.translate(0, -1);
+ break;
+ default:
+ ASSERT_NOT_REACHED();
+ }
+
+ if (!m_flipY) {
+ m_textureSpaceMatrix.flipY();
+ m_textureSpaceMatrix.translate(0, -1);
+ }
+}
+
+void VideoTextureCopierGStreamer::updateTransformationMatrix()
+{
+ FloatRect targetRect = FloatRect(FloatPoint(), m_size);
+ TransformationMatrix identityMatrix;
+ m_modelViewMatrix = TransformationMatrix(identityMatrix).multiply(TransformationMatrix::rectToRect(FloatRect(0, 0, 1, 1), targetRect));
+
+ // Taken from TextureMapperGL.
+ const float nearValue = 9999999;
+ const float farValue = -99999;
+
+ m_projectionMatrix = TransformationMatrix(2.0 / float(m_size.width()), 0, 0, 0,
+ 0, (-2.0) / float(m_size.height()), 0, 0,
+ 0, 0, -2.f / (farValue - nearValue), 0,
+ -1, 1, -(farValue + nearValue) / (farValue - nearValue), 1);
+}
+
+bool VideoTextureCopierGStreamer::copyVideoTextureToPlatformTexture(Platform3DObject inputTexture, IntSize& frameSize, Platform3DObject outputTexture, GC3Denum outputTarget, GC3Dint level, GC3Denum internalFormat, GC3Denum format, GC3Denum type, bool flipY, ImageOrientation& sourceOrientation)
+{
+ if (!m_shaderProgram || !m_framebuffer || !m_vbo || frameSize.isEmpty())
+ return false;
+
+ if (m_size != frameSize) {
+ m_size = frameSize;
+ updateTransformationMatrix();
+ }
+
+ if (m_flipY != flipY || m_orientation != sourceOrientation) {
+ m_flipY = flipY;
+ m_orientation = sourceOrientation;
+ updateTextureSpaceMatrix();
+ }
+
+ // Save previous context and activate the sharing one.
+ GLContext* previousContext = GLContext::current();
+ ASSERT(previousContext);
+ PlatformDisplay::sharedDisplayForCompositing().sharingGLContext()->makeContextCurrent();
+
+ // Save previous bound framebuffer, texture and viewport.
+ GC3Dint boundFramebuffer = 0;
+ GC3Dint boundTexture = 0;
+ GC3Dint previousViewport[4] = { 0, 0, 0, 0};
+ m_context3D->getIntegerv(GraphicsContext3D::FRAMEBUFFER_BINDING, &boundFramebuffer);
+ m_context3D->getIntegerv(GraphicsContext3D::TEXTURE_BINDING_2D, &boundTexture);
+ m_context3D->getIntegerv(GraphicsContext3D::VIEWPORT, previousViewport);
+
+ // Set proper parameters to the output texture and allocate uninitialized memory for it.
+ m_context3D->bindTexture(outputTarget, outputTexture);
+ m_context3D->texParameteri(GraphicsContext3D::TEXTURE_2D, GraphicsContext3D::TEXTURE_MIN_FILTER, GraphicsContext3D::LINEAR);
+ m_context3D->texParameterf(GraphicsContext3D::TEXTURE_2D, GraphicsContext3D::TEXTURE_WRAP_S, GraphicsContext3D::CLAMP_TO_EDGE);
+ m_context3D->texParameterf(GraphicsContext3D::TEXTURE_2D, GraphicsContext3D::TEXTURE_WRAP_T, GraphicsContext3D::CLAMP_TO_EDGE);
+ m_context3D->texImage2DDirect(outputTarget, level, internalFormat, m_size.width(), m_size.height(), 0, format, type, nullptr);
+
+ // Bind framebuffer to paint and attach the destination texture to it.
+ m_context3D->bindFramebuffer(GraphicsContext3D::FRAMEBUFFER, m_framebuffer);
+ m_context3D->framebufferTexture2D(GraphicsContext3D::FRAMEBUFFER, GraphicsContext3D::COLOR_ATTACHMENT0, GL_TEXTURE_2D, outputTexture, 0);
+
+ // Set proper wrap parameter to the source texture.
+ m_context3D->bindTexture(GL_TEXTURE_2D, inputTexture);
+ m_context3D->texParameteri(GraphicsContext3D::TEXTURE_2D, GraphicsContext3D::TEXTURE_MIN_FILTER, GraphicsContext3D::LINEAR);
+ m_context3D->texParameterf(GraphicsContext3D::TEXTURE_2D, GraphicsContext3D::TEXTURE_WRAP_S, GraphicsContext3D::CLAMP_TO_EDGE);
+ m_context3D->texParameterf(GraphicsContext3D::TEXTURE_2D, GraphicsContext3D::TEXTURE_WRAP_T, GraphicsContext3D::CLAMP_TO_EDGE);
+
+ // Set the viewport.
+ m_context3D->viewport(0, 0, m_size.width(), m_size.height());
+
+ // Set program parameters.
+ m_context3D->useProgram(m_shaderProgram->programID());
+ m_context3D->uniform1i(m_shaderProgram->samplerLocation(), 0);
+ m_shaderProgram->setMatrix(m_shaderProgram->modelViewMatrixLocation(), m_modelViewMatrix);
+ m_shaderProgram->setMatrix(m_shaderProgram->projectionMatrixLocation(), m_projectionMatrix);
+ m_shaderProgram->setMatrix(m_shaderProgram->textureSpaceMatrixLocation(), m_textureSpaceMatrix);
+
+ // Perform the copy.
+ m_context3D->enableVertexAttribArray(m_shaderProgram->vertexLocation());
+ m_context3D->bindBuffer(GraphicsContext3D::ARRAY_BUFFER, m_vbo);
+ m_context3D->vertexAttribPointer(m_shaderProgram->vertexLocation(), 2, GraphicsContext3D::FLOAT, false, 0, 0);
+ m_context3D->drawArrays(GraphicsContext3D::TRIANGLE_FAN, 0, 4);
+ m_context3D->bindBuffer(GraphicsContext3D::ARRAY_BUFFER, 0);
+ m_context3D->disableVertexAttribArray(m_shaderProgram->vertexLocation());
+ m_context3D->useProgram(0);
+
+ // Restore previous bindings and viewport.
+ m_context3D->bindFramebuffer(GraphicsContext3D::FRAMEBUFFER, boundFramebuffer);
+ m_context3D->bindTexture(outputTarget, boundTexture);
+ m_context3D->viewport(previousViewport[0], previousViewport[1], previousViewport[2], previousViewport[3]);
+
+ bool ok = (m_context3D->getError() == GraphicsContext3D::NO_ERROR);
+
+ // Restore previous context.
+ previousContext->makeContextCurrent();
+ return ok;
+}
+
+} // namespace WebCore
+
+#endif // USE(GSTREAMER_GL)
diff --git a/Source/WebCore/platform/graphics/gstreamer/VideoTextureCopierGStreamer.h b/Source/WebCore/platform/graphics/gstreamer/VideoTextureCopierGStreamer.h
new file mode 100644
index 000000000..945a7b3b4
--- /dev/null
+++ b/Source/WebCore/platform/graphics/gstreamer/VideoTextureCopierGStreamer.h
@@ -0,0 +1,59 @@
+/*
+ Copyright (C) 2016 Igalia S.L.
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+ */
+
+#ifndef VideoTextureCopierGStreamer_h
+#define VideoTextureCopierGStreamer_h
+
+#if USE(GSTREAMER_GL)
+
+#include "GraphicsContext3D.h"
+#include "TransformationMatrix.h"
+
+namespace WebCore {
+
+class TextureMapperShaderProgram;
+class ImageOrientation;
+
+class VideoTextureCopierGStreamer {
+public:
+ VideoTextureCopierGStreamer();
+ ~VideoTextureCopierGStreamer();
+
+ bool copyVideoTextureToPlatformTexture(Platform3DObject inputTexture, IntSize& frameSize, Platform3DObject outputTexture, GC3Denum outputTarget, GC3Dint level, GC3Denum internalFormat, GC3Denum format, GC3Denum type, bool flipY, ImageOrientation& sourceOrientation);
+ void updateTextureSpaceMatrix();
+ void updateTransformationMatrix();
+
+private:
+ RefPtr<GraphicsContext3D> m_context3D;
+ RefPtr<TextureMapperShaderProgram> m_shaderProgram;
+ Platform3DObject m_framebuffer { 0 };
+ Platform3DObject m_vbo { 0 };
+ bool m_flipY { false };
+ ImageOrientation m_orientation;
+ IntSize m_size;
+ TransformationMatrix m_modelViewMatrix;
+ TransformationMatrix m_projectionMatrix;
+ TransformationMatrix m_textureSpaceMatrix;
+};
+
+} // namespace WebCore
+
+#endif // USE(GSTREAMER_GL)
+
+#endif // VideoTextureCopierGStreamer_h
diff --git a/Source/WebCore/platform/graphics/gstreamer/VideoTrackPrivateGStreamer.cpp b/Source/WebCore/platform/graphics/gstreamer/VideoTrackPrivateGStreamer.cpp
index e3652c350..a6f94b82c 100644
--- a/Source/WebCore/platform/graphics/gstreamer/VideoTrackPrivateGStreamer.cpp
+++ b/Source/WebCore/platform/graphics/gstreamer/VideoTrackPrivateGStreamer.cpp
@@ -37,6 +37,8 @@ VideoTrackPrivateGStreamer::VideoTrackPrivateGStreamer(GRefPtr<GstElement> playb
: TrackPrivateBaseGStreamer(this, index, pad)
, m_playbin(playbin)
{
+ // FIXME: Get a real ID from the tkhd atom.
+ m_id = "V" + String::number(index);
notifyTrackOfActiveChanged();
}
@@ -53,7 +55,7 @@ void VideoTrackPrivateGStreamer::setSelected(bool selected)
VideoTrackPrivate::setSelected(selected);
if (selected && m_playbin)
- g_object_set(m_playbin.get(), "current-video", m_index, NULL);
+ g_object_set(m_playbin.get(), "current-video", m_index, nullptr);
}
} // namespace WebCore
diff --git a/Source/WebCore/platform/graphics/gstreamer/VideoTrackPrivateGStreamer.h b/Source/WebCore/platform/graphics/gstreamer/VideoTrackPrivateGStreamer.h
index b216221e0..ba46a69b2 100644
--- a/Source/WebCore/platform/graphics/gstreamer/VideoTrackPrivateGStreamer.h
+++ b/Source/WebCore/platform/graphics/gstreamer/VideoTrackPrivateGStreamer.h
@@ -41,19 +41,21 @@ public:
return adoptRef(new VideoTrackPrivateGStreamer(playbin, index, pad));
}
- virtual void disconnect() override;
+ void disconnect() override;
- virtual void setSelected(bool) override;
- virtual void setActive(bool enabled) override { setSelected(enabled); }
+ void setSelected(bool) override;
+ void setActive(bool enabled) override { setSelected(enabled); }
- virtual int trackIndex() const override { return m_index; }
+ int trackIndex() const override { return m_index; }
- virtual AtomicString label() const override { return m_label; }
- virtual AtomicString language() const override { return m_language; }
+ AtomicString id() const override { return m_id; }
+ AtomicString label() const override { return m_label; }
+ AtomicString language() const override { return m_language; }
private:
VideoTrackPrivateGStreamer(GRefPtr<GstElement> playbin, gint index, GRefPtr<GstPad>);
+ AtomicString m_id;
GRefPtr<GstElement> m_playbin;
};
diff --git a/Source/WebCore/platform/graphics/gstreamer/WebKitMediaSourceGStreamer.cpp b/Source/WebCore/platform/graphics/gstreamer/WebKitMediaSourceGStreamer.cpp
deleted file mode 100644
index bade219f8..000000000
--- a/Source/WebCore/platform/graphics/gstreamer/WebKitMediaSourceGStreamer.cpp
+++ /dev/null
@@ -1,846 +0,0 @@
-/*
- * Copyright (C) 2009, 2010 Sebastian Dröge <sebastian.droege@collabora.co.uk>
- * Copyright (C) 2013 Collabora Ltd.
- * Copyright (C) 2013 Orange
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- */
-
-#include "config.h"
-#include "WebKitMediaSourceGStreamer.h"
-
-#if ENABLE(VIDEO) && ENABLE(MEDIA_SOURCE) && USE(GSTREAMER)
-
-#include "GRefPtrGStreamer.h"
-#include "GStreamerUtilities.h"
-#include "NotImplemented.h"
-#include "TimeRanges.h"
-#include <gst/app/gstappsrc.h>
-#include <gst/gst.h>
-#include <gst/pbutils/missing-plugins.h>
-#include <wtf/gobject/GUniquePtr.h>
-#include <wtf/text/CString.h>
-
-typedef struct _Source {
- GstElement* appsrc;
- guint sourceid; /* To control the GSource */
- GstPad* srcpad;
- gboolean padAdded;
-
- guint64 offset;
- guint64 size;
- gboolean paused;
-
- guint startId;
- guint stopId;
- guint needDataId;
- guint enoughDataId;
- guint seekId;
-
- guint64 requestedOffset;
-} Source;
-
-
-#define WEBKIT_MEDIA_SRC_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE((obj), WEBKIT_TYPE_MEDIA_SRC, WebKitMediaSrcPrivate))
-
-struct _WebKitMediaSrcPrivate {
- gchar* uri;
- Source sourceVideo;
- Source sourceAudio;
- WebCore::MediaPlayer* player;
- GstElement* playbin;
- gint64 duration;
- gboolean seekable;
- gboolean noMorePad;
- // TRUE if appsrc's version is >= 0.10.27, see
- // https://bugzilla.gnome.org/show_bug.cgi?id=609423
- gboolean haveAppSrc27;
- guint nbSource;
-};
-
-enum {
- PropLocation = 1,
- ProLast
-};
-
-static GstStaticPadTemplate srcTemplate = GST_STATIC_PAD_TEMPLATE("src_%u", GST_PAD_SRC, GST_PAD_SOMETIMES, GST_STATIC_CAPS_ANY);
-
-GST_DEBUG_CATEGORY_STATIC(webkit_media_src_debug);
-#define GST_CAT_DEFAULT webkit_media_src_debug
-
-static void webKitMediaSrcUriHandlerInit(gpointer gIface, gpointer ifaceData);
-static void webKitMediaSrcFinalize(GObject*);
-static void webKitMediaSrcSetProperty(GObject*, guint propertyId, const GValue*, GParamSpec*);
-static void webKitMediaSrcGetProperty(GObject*, guint propertyId, GValue*, GParamSpec*);
-static GstStateChangeReturn webKitMediaSrcChangeState(GstElement*, GstStateChange);
-static gboolean webKitMediaSrcQueryWithParent(GstPad*, GstObject*, GstQuery*);
-
-static void webKitMediaVideoSrcNeedDataCb(GstAppSrc*, guint, gpointer);
-static void webKitMediaVideoSrcEnoughDataCb(GstAppSrc*, gpointer);
-static gboolean webKitMediaVideoSrcSeekDataCb(GstAppSrc*, guint64, gpointer);
-static void webKitMediaAudioSrcNeedDataCb(GstAppSrc*, guint, gpointer);
-static void webKitMediaAudioSrcEnoughDataCb(GstAppSrc*, gpointer);
-static gboolean webKitMediaAudioSrcSeekDataCb(GstAppSrc*, guint64, gpointer);
-static GstAppSrcCallbacks appsrcCallbacksVideo = {
- webKitMediaVideoSrcNeedDataCb,
- webKitMediaVideoSrcEnoughDataCb,
- webKitMediaVideoSrcSeekDataCb,
- { 0 }
-};
-static GstAppSrcCallbacks appsrcCallbacksAudio = {
- webKitMediaAudioSrcNeedDataCb,
- webKitMediaAudioSrcEnoughDataCb,
- webKitMediaAudioSrcSeekDataCb,
- { 0 }
-};
-#define webkit_media_src_parent_class parent_class
-// We split this out into another macro to avoid a check-webkit-style error.
-#define WEBKIT_MEDIA_SRC_CATEGORY_INIT GST_DEBUG_CATEGORY_INIT(webkit_media_src_debug, "webkitmediasrc", 0, "websrc element");
-G_DEFINE_TYPE_WITH_CODE(WebKitMediaSrc, webkit_media_src, GST_TYPE_BIN,
- G_IMPLEMENT_INTERFACE(GST_TYPE_URI_HANDLER, webKitMediaSrcUriHandlerInit);
- WEBKIT_MEDIA_SRC_CATEGORY_INIT);
-
-static void webkit_media_src_class_init(WebKitMediaSrcClass* klass)
-{
- GObjectClass* oklass = G_OBJECT_CLASS(klass);
- GstElementClass* eklass = GST_ELEMENT_CLASS(klass);
-
- oklass->finalize = webKitMediaSrcFinalize;
- oklass->set_property = webKitMediaSrcSetProperty;
- oklass->get_property = webKitMediaSrcGetProperty;
-
- gst_element_class_add_pad_template(eklass, gst_static_pad_template_get(&srcTemplate));
-
- gst_element_class_set_metadata(eklass, "WebKit Media source element", "Source", "Handles Blob uris", "Stephane Jadaud <sjadaud@sii.fr>");
-
- /* Allows setting the uri using the 'location' property, which is used
- * for example by gst_element_make_from_uri() */
- g_object_class_install_property(oklass,
- PropLocation,
- g_param_spec_string("location", "location", "Location to read from", 0,
- (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
-
- eklass->change_state = webKitMediaSrcChangeState;
-
- g_type_class_add_private(klass, sizeof(WebKitMediaSrcPrivate));
-}
-
-static void webKitMediaSrcAddSrc(WebKitMediaSrc* src, GstElement* element)
-{
- GstPad* ghostPad;
- WebKitMediaSrcPrivate* priv = src->priv;
-
- if (!gst_bin_add(GST_BIN(src), element)) {
- GST_DEBUG_OBJECT(src, "Src element not added");
- return;
- }
- GRefPtr<GstPad> targetsrc = adoptGRef(gst_element_get_static_pad(element, "src"));
- if (!targetsrc) {
- GST_DEBUG_OBJECT(src, "Pad not found");
- return;
- }
-
- gst_element_sync_state_with_parent(element);
- GUniquePtr<gchar> name(g_strdup_printf("src_%u", priv->nbSource));
- ghostPad = WebCore::webkitGstGhostPadFromStaticTemplate(&srcTemplate, name.get(), targetsrc.get());
- gst_pad_set_active(ghostPad, TRUE);
-
- priv->nbSource++;
-
- if (priv->sourceVideo.appsrc == element)
- priv->sourceVideo.srcpad = ghostPad;
- else if (priv->sourceAudio.appsrc == element)
- priv->sourceAudio.srcpad = ghostPad;
-
- GST_OBJECT_FLAG_SET(ghostPad, GST_PAD_FLAG_NEED_PARENT);
- gst_pad_set_query_function(ghostPad, webKitMediaSrcQueryWithParent);
-}
-
-static void webkit_media_src_init(WebKitMediaSrc* src)
-{
- WebKitMediaSrcPrivate* priv = WEBKIT_MEDIA_SRC_GET_PRIVATE(src);
- src->priv = priv;
-
- priv->sourceVideo.appsrc = gst_element_factory_make("appsrc", "videoappsrc");
- gst_app_src_set_callbacks(GST_APP_SRC(priv->sourceVideo.appsrc), &appsrcCallbacksVideo, src, 0);
- webKitMediaSrcAddSrc(src, priv->sourceVideo.appsrc);
-
- priv->sourceAudio.appsrc = gst_element_factory_make("appsrc", "audioappsrc");
- gst_app_src_set_callbacks(GST_APP_SRC(priv->sourceAudio.appsrc), &appsrcCallbacksAudio, src, 0);
- webKitMediaSrcAddSrc(src, priv->sourceAudio.appsrc);
-}
-
-static void webKitMediaSrcFinalize(GObject* object)
-{
- WebKitMediaSrc* src = WEBKIT_MEDIA_SRC(object);
- WebKitMediaSrcPrivate* priv = src->priv;
-
- g_free(priv->uri);
-
- GST_CALL_PARENT(G_OBJECT_CLASS, finalize, (object));
-}
-
-static void webKitMediaSrcSetProperty(GObject* object, guint propId, const GValue* value, GParamSpec* pspec)
-{
- WebKitMediaSrc* src = WEBKIT_MEDIA_SRC(object);
- switch (propId) {
- case PropLocation:
- gst_uri_handler_set_uri(reinterpret_cast<GstURIHandler*>(src), g_value_get_string(value), 0);
- break;
- default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID(object, propId, pspec);
- break;
- }
-}
-
-static void webKitMediaSrcGetProperty(GObject* object, guint propId, GValue* value, GParamSpec* pspec)
-{
- WebKitMediaSrc* src = WEBKIT_MEDIA_SRC(object);
- WebKitMediaSrcPrivate* priv = src->priv;
-
- GST_OBJECT_LOCK(src);
- switch (propId) {
- case PropLocation:
- g_value_set_string(value, priv->uri);
- break;
- default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID(object, propId, pspec);
- break;
- }
- GST_OBJECT_UNLOCK(src);
-}
-
-// must be called on main thread and with object unlocked
-static gboolean webKitMediaVideoSrcStop(WebKitMediaSrc* src)
-{
- WebKitMediaSrcPrivate* priv = src->priv;
- gboolean seeking;
-
- GST_OBJECT_LOCK(src);
-
- seeking = priv->sourceVideo.seekId;
-
- if (priv->sourceVideo.startId) {
- g_source_remove(priv->sourceVideo.startId);
- priv->sourceVideo.startId = 0;
- }
-
- priv->player = 0;
- priv->playbin = 0;
-
- if (priv->sourceVideo.needDataId)
- g_source_remove(priv->sourceVideo.needDataId);
- priv->sourceVideo.needDataId = 0;
-
- if (priv->sourceVideo.enoughDataId)
- g_source_remove(priv->sourceVideo.enoughDataId);
- priv->sourceVideo.enoughDataId = 0;
-
- if (priv->sourceVideo.seekId)
- g_source_remove(priv->sourceVideo.seekId);
-
- priv->sourceVideo.seekId = 0;
-
- priv->sourceVideo.paused = FALSE;
- priv->sourceVideo.offset = 0;
- priv->seekable = FALSE;
-
- priv->duration = 0;
- priv->nbSource = 0;
-
- priv->sourceVideo.stopId = 0;
-
- GST_OBJECT_UNLOCK(src);
-
- if (priv->sourceVideo.appsrc) {
- gst_app_src_set_caps(GST_APP_SRC(priv->sourceVideo.appsrc), 0);
- if (!seeking)
- gst_app_src_set_size(GST_APP_SRC(priv->sourceVideo.appsrc), -1);
- }
-
- GST_DEBUG_OBJECT(src, "Stopped request");
-
- return FALSE;
-}
-
-static gboolean webKitMediaAudioSrcStop(WebKitMediaSrc* src)
-{
- WebKitMediaSrcPrivate* priv = src->priv;
- gboolean seeking;
-
- GST_OBJECT_LOCK(src);
-
- seeking = priv->sourceAudio.seekId;
-
- if (priv->sourceAudio.startId) {
- g_source_remove(priv->sourceAudio.startId);
- priv->sourceAudio.startId = 0;
- }
-
- priv->player = 0;
-
- priv->playbin = 0;
-
- if (priv->sourceAudio.needDataId)
- g_source_remove(priv->sourceAudio.needDataId);
- priv->sourceAudio.needDataId = 0;
-
- if (priv->sourceAudio.enoughDataId)
- g_source_remove(priv->sourceAudio.enoughDataId);
- priv->sourceAudio.enoughDataId = 0;
-
- if (priv->sourceAudio.seekId)
- g_source_remove(priv->sourceAudio.seekId);
-
- priv->sourceAudio.seekId = 0;
-
- priv->sourceAudio.paused = FALSE;
-
- priv->sourceAudio.offset = 0;
-
- priv->seekable = FALSE;
-
- priv->duration = 0;
- priv->nbSource = 0;
-
- priv->sourceAudio.stopId = 0;
-
- GST_OBJECT_UNLOCK(src);
-
- if (priv->sourceAudio.appsrc) {
- gst_app_src_set_caps(GST_APP_SRC(priv->sourceAudio.appsrc), 0);
- if (!seeking)
- gst_app_src_set_size(GST_APP_SRC(priv->sourceAudio.appsrc), -1);
- }
-
- GST_DEBUG_OBJECT(src, "Stopped request");
-
- return FALSE;
-}
-
-// must be called on main thread and with object unlocked
-static gboolean webKitMediaVideoSrcStart(WebKitMediaSrc* src)
-{
- WebKitMediaSrcPrivate* priv = src->priv;
-
- GST_OBJECT_LOCK(src);
- if (!priv->uri) {
- GST_ERROR_OBJECT(src, "No URI provided");
- GST_OBJECT_UNLOCK(src);
- webKitMediaVideoSrcStop(src);
- return FALSE;
- }
-
- priv->sourceVideo.startId = 0;
-
- GST_OBJECT_UNLOCK(src);
- GST_DEBUG_OBJECT(src, "Started request");
-
- return FALSE;
-}
-
-// must be called on main thread and with object unlocked
-static gboolean webKitMediaAudioSrcStart(WebKitMediaSrc* src)
-{
- WebKitMediaSrcPrivate* priv = src->priv;
-
- GST_OBJECT_LOCK(src);
- if (!priv->uri) {
- GST_ERROR_OBJECT(src, "No URI provided");
- GST_OBJECT_UNLOCK(src);
- webKitMediaAudioSrcStop(src);
- return FALSE;
- }
-
- priv->sourceAudio.startId = 0;
-
- GST_OBJECT_UNLOCK(src);
- GST_DEBUG_OBJECT(src, "Started request");
-
- return FALSE;
-}
-
-static GstStateChangeReturn webKitMediaSrcChangeState(GstElement* element, GstStateChange transition)
-{
- GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
- WebKitMediaSrc* src = WEBKIT_MEDIA_SRC(element);
- WebKitMediaSrcPrivate* priv = src->priv;
-
- switch (transition) {
- case GST_STATE_CHANGE_NULL_TO_READY:
- if (!priv->sourceVideo.appsrc && !priv->sourceAudio.appsrc) {
- gst_element_post_message(element,
- gst_missing_element_message_new(element, "appsrc"));
- GST_ELEMENT_ERROR(src, CORE, MISSING_PLUGIN, (0), ("no appsrc"));
- return GST_STATE_CHANGE_FAILURE;
- }
- break;
- default:
- break;
- }
-
- ret = GST_ELEMENT_CLASS(parent_class)->change_state(element, transition);
- if (G_UNLIKELY(ret == GST_STATE_CHANGE_FAILURE)) {
- GST_DEBUG_OBJECT(src, "State change failed");
- return ret;
- }
-
- switch (transition) {
- case GST_STATE_CHANGE_READY_TO_PAUSED:
- GST_DEBUG_OBJECT(src, "READY->PAUSED");
- GST_OBJECT_LOCK(src);
- priv->sourceVideo.startId = g_timeout_add_full(G_PRIORITY_DEFAULT, 0, (GSourceFunc) webKitMediaVideoSrcStart, gst_object_ref(src), (GDestroyNotify) gst_object_unref);
- g_source_set_name_by_id(priv->sourceVideo.startId, "[WebKit] webKitMediaVideoSrcStart");
- priv->sourceAudio.startId = g_timeout_add_full(G_PRIORITY_DEFAULT, 0, (GSourceFunc) webKitMediaAudioSrcStart, gst_object_ref(src), (GDestroyNotify) gst_object_unref);
- g_source_set_name_by_id(priv->sourceAudio.startId, "[WebKit] webKitMediaAudioSrcStart");
- GST_OBJECT_UNLOCK(src);
- break;
- case GST_STATE_CHANGE_PAUSED_TO_READY:
- GST_DEBUG_OBJECT(src, "PAUSED->READY");
- GST_OBJECT_LOCK(src);
- priv->sourceVideo.stopId = g_timeout_add_full(G_PRIORITY_DEFAULT, 0, (GSourceFunc) webKitMediaVideoSrcStop, gst_object_ref(src), (GDestroyNotify) gst_object_unref);
- g_source_set_name_by_id(priv->sourceVideo.stopId, "[WebKit] webKitMediaVideoSrcStop");
- priv->sourceAudio.stopId = g_timeout_add_full(G_PRIORITY_DEFAULT, 0, (GSourceFunc) webKitMediaAudioSrcStop, gst_object_ref(src), (GDestroyNotify) gst_object_unref);
- g_source_set_name_by_id(priv->sourceAudio.stopId, "[WebKit] webKitMediaAudioSrcStop");
- GST_OBJECT_UNLOCK(src);
- break;
- default:
- break;
- }
-
- return ret;
-}
-
-static gboolean webKitMediaSrcQueryWithParent(GstPad* pad, GstObject* parent, GstQuery* query)
-{
- WebKitMediaSrc* src = WEBKIT_MEDIA_SRC(GST_ELEMENT(parent));
- gboolean result = FALSE;
-
- switch (GST_QUERY_TYPE(query)) {
- case GST_QUERY_DURATION: {
- GstFormat format;
- gst_query_parse_duration(query, &format, NULL);
-
- GST_DEBUG_OBJECT(src, "duration query in format %s", gst_format_get_name(format));
- GST_OBJECT_LOCK(src);
- if ((format == GST_FORMAT_TIME) && (src->priv->duration > 0)) {
- gst_query_set_duration(query, format, src->priv->duration);
- result = TRUE;
- }
- GST_OBJECT_UNLOCK(src);
- break;
- }
- case GST_QUERY_URI: {
- GST_OBJECT_LOCK(src);
- gst_query_set_uri(query, src->priv->uri);
- GST_OBJECT_UNLOCK(src);
- result = TRUE;
- break;
- }
- default: {
- GRefPtr<GstPad> target = adoptGRef(gst_ghost_pad_get_target(GST_GHOST_PAD_CAST(pad)));
- // Forward the query to the proxy target pad.
- if (target)
- result = gst_pad_query(target.get(), query);
- break;
- }
- }
-
- return result;
-}
-
-// uri handler interface
-static GstURIType webKitMediaSrcUriGetType(GType)
-{
- return GST_URI_SRC;
-}
-
-const gchar* const* webKitMediaSrcGetProtocols(GType)
-{
- static const char* protocols[] = {"mediasourceblob", 0 };
- return protocols;
-}
-
-static gchar* webKitMediaSrcGetUri(GstURIHandler* handler)
-{
- WebKitMediaSrc* src = WEBKIT_MEDIA_SRC(handler);
- gchar* ret;
-
- GST_OBJECT_LOCK(src);
- ret = g_strdup(src->priv->uri);
- GST_OBJECT_UNLOCK(src);
- return ret;
-}
-
-static gboolean webKitMediaSrcSetUri(GstURIHandler* handler, const gchar* uri, GError** error)
-{
- WebKitMediaSrc* src = WEBKIT_MEDIA_SRC(handler);
- WebKitMediaSrcPrivate* priv = src->priv;
- if (GST_STATE(src) >= GST_STATE_PAUSED) {
- GST_ERROR_OBJECT(src, "URI can only be set in states < PAUSED");
- return FALSE;
- }
-
- GST_OBJECT_LOCK(src);
- g_free(priv->uri);
- priv->uri = 0;
- if (!uri) {
- GST_OBJECT_UNLOCK(src);
- return TRUE;
- }
-
- WebCore::URL url(WebCore::URL(), uri);
-
- priv->uri = g_strdup(url.string().utf8().data());
- GST_OBJECT_UNLOCK(src);
- return TRUE;
-}
-
-static void webKitMediaSrcUriHandlerInit(gpointer gIface, gpointer)
-{
- GstURIHandlerInterface* iface = (GstURIHandlerInterface *) gIface;
-
- iface->get_type = webKitMediaSrcUriGetType;
- iface->get_protocols = webKitMediaSrcGetProtocols;
- iface->get_uri = webKitMediaSrcGetUri;
- iface->set_uri = webKitMediaSrcSetUri;
-}
-
-// appsrc callbacks
-static gboolean webKitMediaVideoSrcNeedDataMainCb(WebKitMediaSrc* src)
-{
- WebKitMediaSrcPrivate* priv = src->priv;
-
- GST_OBJECT_LOCK(src);
- // already stopped
- if (!priv->sourceVideo.needDataId) {
- GST_OBJECT_UNLOCK(src);
- return FALSE;
- }
-
- priv->sourceVideo.paused = FALSE;
- priv->sourceVideo.needDataId = 0;
- GST_OBJECT_UNLOCK(src);
-
- return FALSE;
-}
-
-static gboolean webKitMediaAudioSrcNeedDataMainCb(WebKitMediaSrc* src)
-{
- WebKitMediaSrcPrivate* priv = src->priv;
-
- GST_OBJECT_LOCK(src);
- // already stopped
- if (!priv->sourceAudio.needDataId) {
- GST_OBJECT_UNLOCK(src);
- return FALSE;
- }
-
- priv->sourceAudio.paused = FALSE;
- priv->sourceAudio.needDataId = 0;
- GST_OBJECT_UNLOCK(src);
-
- return FALSE;
-}
-
-static void webKitMediaVideoSrcNeedDataCb(GstAppSrc*, guint length, gpointer userData)
-{
- WebKitMediaSrc* src = WEBKIT_MEDIA_SRC(userData);
- WebKitMediaSrcPrivate* priv = src->priv;
-
- GST_DEBUG_OBJECT(src, "Need more data: %u", length);
-
- GST_OBJECT_LOCK(src);
- if (priv->sourceVideo.needDataId || !priv->sourceVideo.paused) {
- GST_OBJECT_UNLOCK(src);
- return;
- }
-
- priv->sourceVideo.needDataId = g_timeout_add_full(G_PRIORITY_DEFAULT, 0, (GSourceFunc) webKitMediaVideoSrcNeedDataMainCb, gst_object_ref(src), (GDestroyNotify) gst_object_unref);
- g_source_set_name_by_id(priv->sourceVideo.needDataId, "[WebKit] webKitMediaVideoSrcNeedDataMainCb");
- GST_OBJECT_UNLOCK(src);
-}
-
-static void webKitMediaAudioSrcNeedDataCb(GstAppSrc*, guint length, gpointer userData)
-{
- WebKitMediaSrc* src = WEBKIT_MEDIA_SRC(userData);
- WebKitMediaSrcPrivate* priv = src->priv;
-
- GST_DEBUG_OBJECT(src, "Need more data: %u", length);
-
- GST_OBJECT_LOCK(src);
- if (priv->sourceAudio.needDataId || !priv->sourceAudio.paused) {
- GST_OBJECT_UNLOCK(src);
- return;
- }
-
- priv->sourceAudio.needDataId = g_timeout_add_full(G_PRIORITY_DEFAULT, 0, (GSourceFunc) webKitMediaAudioSrcNeedDataMainCb, gst_object_ref(src), (GDestroyNotify) gst_object_unref);
- g_source_set_name_by_id(priv->sourceAudio.needDataId, "[WebKit] webKitMediaAudioSrcNeedDataMainCb");
- GST_OBJECT_UNLOCK(src);
-}
-
-static gboolean webKitMediaVideoSrcEnoughDataMainCb(WebKitMediaSrc* src)
-{
- WebKitMediaSrcPrivate* priv = src->priv;
-
- GST_OBJECT_LOCK(src);
- // already stopped
- if (!priv->sourceVideo.enoughDataId) {
- GST_OBJECT_UNLOCK(src);
- return FALSE;
- }
-
- priv->sourceVideo.paused = TRUE;
- priv->sourceVideo.enoughDataId = 0;
- GST_OBJECT_UNLOCK(src);
-
- return FALSE;
-}
-
-static gboolean webKitMediaAudioSrcEnoughDataMainCb(WebKitMediaSrc* src)
-{
- WebKitMediaSrcPrivate* priv = src->priv;
-
- GST_OBJECT_LOCK(src);
- // already stopped
- if (!priv->sourceAudio.enoughDataId) {
- GST_OBJECT_UNLOCK(src);
- return FALSE;
- }
-
- priv->sourceAudio.paused = TRUE;
- priv->sourceAudio.enoughDataId = 0;
- GST_OBJECT_UNLOCK(src);
-
- return FALSE;
-}
-
-static void webKitMediaVideoSrcEnoughDataCb(GstAppSrc*, gpointer userData)
-{
- WebKitMediaSrc* src = WEBKIT_MEDIA_SRC(userData);
- WebKitMediaSrcPrivate* priv = src->priv;
-
- GST_DEBUG_OBJECT(src, "Have enough data");
-
- GST_OBJECT_LOCK(src);
- if (priv->sourceVideo.enoughDataId || priv->sourceVideo.paused) {
- GST_OBJECT_UNLOCK(src);
- return;
- }
-
- priv->sourceVideo.enoughDataId = g_timeout_add_full(G_PRIORITY_DEFAULT, 0, (GSourceFunc) webKitMediaVideoSrcEnoughDataMainCb, gst_object_ref(src), (GDestroyNotify) gst_object_unref);
- g_source_set_name_by_id(priv->sourceVideo.enoughDataId, "[WebKit] webKitMediaVideoSrcEnoughDataMainCb");
- GST_OBJECT_UNLOCK(src);
-}
-
-static void webKitMediaAudioSrcEnoughDataCb(GstAppSrc*, gpointer userData)
-{
- WebKitMediaSrc* src = WEBKIT_MEDIA_SRC(userData);
- WebKitMediaSrcPrivate* priv = src->priv;
-
- GST_DEBUG_OBJECT(src, "Have enough data");
-
- GST_OBJECT_LOCK(src);
- if (priv->sourceAudio.enoughDataId || priv->sourceAudio.paused) {
- GST_OBJECT_UNLOCK(src);
- return;
- }
-
- priv->sourceAudio.enoughDataId = g_timeout_add_full(G_PRIORITY_DEFAULT, 0, (GSourceFunc) webKitMediaAudioSrcEnoughDataMainCb, gst_object_ref(src), (GDestroyNotify) gst_object_unref);
- g_source_set_name_by_id(priv->sourceAudio.enoughDataId, "[WebKit] webKitMediaAudioSrcEnoughDataMainCb");
- GST_OBJECT_UNLOCK(src);
-}
-
-static gboolean webKitMediaVideoSrcSeekMainCb(WebKitMediaSrc* src)
-{
- notImplemented();
- src->priv->sourceVideo.seekId = 0;
- return FALSE;
-}
-
-static gboolean webKitMediaAudioSrcSeekMainCb(WebKitMediaSrc* src)
-{
- notImplemented();
- src->priv->sourceAudio.seekId = 0;
- return FALSE;
-}
-
-static gboolean webKitMediaVideoSrcSeekDataCb(GstAppSrc*, guint64 offset, gpointer userData)
-{
- WebKitMediaSrc* src = WEBKIT_MEDIA_SRC(userData);
- WebKitMediaSrcPrivate* priv = src->priv;
-
- GST_DEBUG_OBJECT(src, "Seeking to offset: %" G_GUINT64_FORMAT, offset);
- GST_OBJECT_LOCK(src);
- if (offset == priv->sourceVideo.offset && priv->sourceVideo.requestedOffset == priv->sourceVideo.offset) {
- GST_OBJECT_UNLOCK(src);
- return TRUE;
- }
-
- if (!priv->seekable) {
- GST_OBJECT_UNLOCK(src);
- return FALSE;
- }
- if (offset > priv->sourceVideo.size) {
- GST_OBJECT_UNLOCK(src);
- return FALSE;
- }
-
- GST_DEBUG_OBJECT(src, "Doing range-request seek");
- priv->sourceVideo.requestedOffset = offset;
-
- if (priv->sourceVideo.seekId)
- g_source_remove(priv->sourceVideo.seekId);
- priv->sourceVideo.seekId = g_timeout_add_full(G_PRIORITY_DEFAULT, 0, (GSourceFunc) webKitMediaVideoSrcSeekMainCb, gst_object_ref(src), (GDestroyNotify) gst_object_unref);
- g_source_set_name_by_id(priv->sourceVideo.seekId, "[WebKit] webKitMediaVideoSrcSeekMainCb");
- GST_OBJECT_UNLOCK(src);
-
- return TRUE;
-}
-
-static gboolean webKitMediaAudioSrcSeekDataCb(GstAppSrc*, guint64 offset, gpointer userData)
-{
- WebKitMediaSrc* src = WEBKIT_MEDIA_SRC(userData);
- WebKitMediaSrcPrivate* priv = src->priv;
-
- GST_DEBUG_OBJECT(src, "Seeking to offset: %" G_GUINT64_FORMAT, offset);
- GST_OBJECT_LOCK(src);
- if (offset == priv->sourceAudio.offset && priv->sourceAudio.requestedOffset == priv->sourceAudio.offset) {
- GST_OBJECT_UNLOCK(src);
- return TRUE;
- }
-
- if (!priv->seekable) {
- GST_OBJECT_UNLOCK(src);
- return FALSE;
- }
- if (offset > priv->sourceAudio.size) {
- GST_OBJECT_UNLOCK(src);
- return FALSE;
- }
-
- GST_DEBUG_OBJECT(src, "Doing range-request seek");
- priv->sourceAudio.requestedOffset = offset;
-
- if (priv->sourceAudio.seekId)
- g_source_remove(priv->sourceAudio.seekId);
- priv->sourceAudio.seekId = g_timeout_add_full(G_PRIORITY_DEFAULT, 0, (GSourceFunc) webKitMediaAudioSrcSeekMainCb, gst_object_ref(src), (GDestroyNotify) gst_object_unref);
- g_source_set_name_by_id(priv->sourceAudio.seekId, "[WebKit] webKitMediaAudioSrcSeekMainCb");
- GST_OBJECT_UNLOCK(src);
-
- return TRUE;
-}
-
-void webKitMediaSrcSetMediaPlayer(WebKitMediaSrc* src, WebCore::MediaPlayer* player)
-{
- WebKitMediaSrcPrivate* priv = src->priv;
- priv->player = player;
-}
-
-void webKitMediaSrcSetPlayBin(WebKitMediaSrc* src, GstElement* playBin)
-{
- WebKitMediaSrcPrivate* priv = src->priv;
- priv->playbin = playBin;
-}
-
-MediaSourceClientGstreamer::MediaSourceClientGstreamer(WebKitMediaSrc* src)
- : m_src(static_cast<WebKitMediaSrc*>(gst_object_ref(src)))
-{
-}
-
-MediaSourceClientGstreamer::~MediaSourceClientGstreamer()
-{
- gst_object_unref(m_src);
-}
-
-void MediaSourceClientGstreamer::didReceiveDuration(double duration)
-{
- WebKitMediaSrcPrivate* priv = m_src->priv;
- GST_DEBUG_OBJECT(m_src, "Received duration: %lf", duration);
-
- GST_OBJECT_LOCK(m_src);
- priv->duration = duration >= 0.0 ? static_cast<gint64>(duration*GST_SECOND) : 0;
- GST_OBJECT_UNLOCK(m_src);
-}
-
-void MediaSourceClientGstreamer::didReceiveData(const char* data, int length, String type)
-{
- WebKitMediaSrcPrivate* priv = m_src->priv;
- GstFlowReturn ret = GST_FLOW_OK;
- GstBuffer * buffer;
-
- if (type.startsWith("video")) {
- if (priv->noMorePad == FALSE && priv->sourceVideo.padAdded == TRUE) {
- gst_element_no_more_pads(GST_ELEMENT(m_src));
- priv->noMorePad = TRUE;
- }
- if (priv->noMorePad == FALSE && priv->sourceVideo.padAdded == FALSE) {
- gst_element_add_pad(GST_ELEMENT(m_src), priv->sourceVideo.srcpad);
- priv->sourceVideo.padAdded = TRUE;
- }
- GST_OBJECT_LOCK(m_src);
- buffer = WebCore::createGstBufferForData(data, length);
- GST_OBJECT_UNLOCK(m_src);
-
- ret = gst_app_src_push_buffer(GST_APP_SRC(priv->sourceVideo.appsrc), buffer);
- } else if (type.startsWith("audio")) {
- if (priv->noMorePad == FALSE && priv->sourceAudio.padAdded == TRUE) {
- gst_element_no_more_pads(GST_ELEMENT(m_src));
- priv->noMorePad = TRUE;
- }
- if (priv->noMorePad == FALSE && priv->sourceAudio.padAdded == FALSE) {
- gst_element_add_pad(GST_ELEMENT(m_src), priv->sourceAudio.srcpad);
- priv->sourceAudio.padAdded = TRUE;
- }
- GST_OBJECT_LOCK(m_src);
- buffer = WebCore::createGstBufferForData(data, length);
- GST_OBJECT_UNLOCK(m_src);
-
- ret = gst_app_src_push_buffer(GST_APP_SRC(priv->sourceAudio.appsrc), buffer);
- }
-
- if (ret != GST_FLOW_OK && ret != GST_FLOW_EOS)
- GST_ELEMENT_ERROR(m_src, CORE, FAILED, (0), (0));
-}
-
-void MediaSourceClientGstreamer::didFinishLoading(double)
-{
- WebKitMediaSrcPrivate* priv = m_src->priv;
-
- GST_DEBUG_OBJECT(m_src, "Have EOS");
-
- GST_OBJECT_LOCK(m_src);
- if (!priv->sourceVideo.seekId) {
- GST_OBJECT_UNLOCK(m_src);
- gst_app_src_end_of_stream(GST_APP_SRC(priv->sourceVideo.appsrc));
- } else
- GST_OBJECT_UNLOCK(m_src);
-
- GST_OBJECT_LOCK(m_src);
- if (!priv->sourceAudio.seekId) {
- GST_OBJECT_UNLOCK(m_src);
- gst_app_src_end_of_stream(GST_APP_SRC(priv->sourceAudio.appsrc));
- } else
- GST_OBJECT_UNLOCK(m_src);
-}
-
-void MediaSourceClientGstreamer::didFail()
-{
- gst_app_src_end_of_stream(GST_APP_SRC(m_src->priv->sourceVideo.appsrc));
- gst_app_src_end_of_stream(GST_APP_SRC(m_src->priv->sourceAudio.appsrc));
-}
-
-#endif // USE(GSTREAMER)
-
diff --git a/Source/WebCore/platform/graphics/gstreamer/WebKitWebSourceGStreamer.cpp b/Source/WebCore/platform/graphics/gstreamer/WebKitWebSourceGStreamer.cpp
index c7d8eca76..1b31b380b 100644
--- a/Source/WebCore/platform/graphics/gstreamer/WebKitWebSourceGStreamer.cpp
+++ b/Source/WebCore/platform/graphics/gstreamer/WebKitWebSourceGStreamer.cpp
@@ -22,144 +22,160 @@
#if ENABLE(VIDEO) && USE(GSTREAMER)
-#include "CachedRawResource.h"
-#include "CachedRawResourceClient.h"
-#include "CachedResourceHandle.h"
-#include "CachedResourceLoader.h"
-#include "CachedResourceRequest.h"
-#include "CrossOriginAccessControl.h"
#include "GRefPtrGStreamer.h"
#include "GStreamerUtilities.h"
+#include "GUniquePtrGStreamer.h"
+#include "HTTPHeaderNames.h"
+#include "MainThreadNotifier.h"
#include "MediaPlayer.h"
#include "NotImplemented.h"
+#include "PlatformMediaResourceLoader.h"
+#include "ResourceError.h"
#include "ResourceHandle.h"
#include "ResourceHandleClient.h"
#include "ResourceRequest.h"
#include "ResourceResponse.h"
-#include "SecurityOrigin.h"
#include "SharedBuffer.h"
#include <gst/app/gstappsrc.h>
#include <gst/gst.h>
#include <gst/pbutils/missing-plugins.h>
+#include <wtf/MainThread.h>
#include <wtf/Noncopyable.h>
-#include <wtf/gobject/GMutexLocker.h>
-#include <wtf/gobject/GRefPtr.h>
-#include <wtf/gobject/GUniquePtr.h>
+#include <wtf/glib/GMutexLocker.h>
+#include <wtf/glib/GRefPtr.h>
+#include <wtf/glib/GUniquePtr.h>
#include <wtf/text/CString.h>
+#if USE(SOUP)
+#include "SoupNetworkSession.h"
+#endif
+
using namespace WebCore;
-enum CORSAccessCheckResult {
- CORSNoCheck,
- CORSSuccess,
- CORSFailure
+class StreamingClient {
+public:
+ StreamingClient(WebKitWebSrc*, ResourceRequest&&);
+ virtual ~StreamingClient();
+
+protected:
+ char* createReadBuffer(size_t requestedSize, size_t& actualSize);
+ void handleResponseReceived(const ResourceResponse&);
+ void handleDataReceived(const char*, int);
+ void handleNotifyFinished();
+
+ GRefPtr<GstElement> m_src;
+ ResourceRequest m_request;
};
-class StreamingClient {
- public:
- StreamingClient(WebKitWebSrc*);
- virtual ~StreamingClient();
+class CachedResourceStreamingClient final : public PlatformMediaResourceClient, public StreamingClient {
+ WTF_MAKE_NONCOPYABLE(CachedResourceStreamingClient);
+public:
+ CachedResourceStreamingClient(WebKitWebSrc*, ResourceRequest&&);
+ virtual ~CachedResourceStreamingClient();
+
+private:
+ // PlatformMediaResourceClient virtual methods.
+#if USE(SOUP)
+ char* getOrCreateReadBuffer(PlatformMediaResource&, size_t requestedSize, size_t& actualSize) override;
+#endif
+ void responseReceived(PlatformMediaResource&, const ResourceResponse&) override;
+ void dataReceived(PlatformMediaResource&, const char*, int) override;
+ void accessControlCheckFailed(PlatformMediaResource&, const ResourceError&) override;
+ void loadFailed(PlatformMediaResource&, const ResourceError&) override;
+ void loadFinished(PlatformMediaResource&) override;
+};
- virtual bool loadFailed() const = 0;
- virtual void setDefersLoading(bool) = 0;
+class ResourceHandleStreamingClient : public ThreadSafeRefCounted<ResourceHandleStreamingClient>, public ResourceHandleClient, public StreamingClient {
+public:
+ static Ref<ResourceHandleStreamingClient> create(WebKitWebSrc* src, ResourceRequest&& request)
+ {
+ return adoptRef(*new ResourceHandleStreamingClient(src, WTFMove(request)));
+ }
+ virtual ~ResourceHandleStreamingClient();
- protected:
- char* createReadBuffer(size_t requestedSize, size_t& actualSize);
- void handleResponseReceived(const ResourceResponse&, CORSAccessCheckResult);
- void handleDataReceived(const char*, int);
- void handleNotifyFinished();
+ void invalidate();
- GstElement* m_src;
-};
+ // StreamingClient virtual methods.
+ bool loadFailed() const;
+ void setDefersLoading(bool);
-class CachedResourceStreamingClient : public CachedRawResourceClient, public StreamingClient {
- WTF_MAKE_NONCOPYABLE(CachedResourceStreamingClient); WTF_MAKE_FAST_ALLOCATED;
- public:
- CachedResourceStreamingClient(WebKitWebSrc*, CachedResourceLoader*, const ResourceRequest&, MediaPlayerClient::CORSMode);
- virtual ~CachedResourceStreamingClient();
-
- // StreamingClient virtual methods.
- virtual bool loadFailed() const;
- virtual void setDefersLoading(bool);
-
- private:
- // CachedResourceClient virtual methods.
- virtual char* getOrCreateReadBuffer(CachedResource*, size_t requestedSize, size_t& actualSize);
- virtual void responseReceived(CachedResource*, const ResourceResponse&);
- virtual void dataReceived(CachedResource*, const char*, int);
- virtual void notifyFinished(CachedResource*);
-
- CachedResourceHandle<CachedRawResource> m_resource;
- RefPtr<SecurityOrigin> m_origin;
+private:
+ ResourceHandleStreamingClient(WebKitWebSrc*, ResourceRequest&&);
+ void cleanupAndStopRunLoop();
+
+ // ResourceHandleClient virtual methods.
+#if USE(SOUP)
+ char* getOrCreateReadBuffer(size_t requestedSize, size_t& actualSize) override;
+#endif
+ ResourceRequest willSendRequest(ResourceHandle*, ResourceRequest&&, ResourceResponse&&) override;
+ void didReceiveResponse(ResourceHandle*, ResourceResponse&&) override;
+ void didReceiveData(ResourceHandle*, const char*, unsigned, int) override;
+ void didReceiveBuffer(ResourceHandle*, Ref<SharedBuffer>&&, int encodedLength) override;
+ void didFinishLoading(ResourceHandle*, double) override;
+ void didFail(ResourceHandle*, const ResourceError&) override;
+ void wasBlocked(ResourceHandle*) override;
+ void cannotShowURL(ResourceHandle*) override;
+
+ ThreadIdentifier m_thread { 0 };
+ Lock m_initializeRunLoopConditionMutex;
+ Condition m_initializeRunLoopCondition;
+ RunLoop* m_runLoop { nullptr };
+ Lock m_terminateRunLoopConditionMutex;
+ Condition m_terminateRunLoopCondition;
+ RefPtr<ResourceHandle> m_resource;
+#if USE(SOUP)
+ std::unique_ptr<SoupNetworkSession> m_session;
+#endif
};
-class ResourceHandleStreamingClient : public ResourceHandleClient, public StreamingClient {
- WTF_MAKE_NONCOPYABLE(ResourceHandleStreamingClient); WTF_MAKE_FAST_ALLOCATED;
- public:
- ResourceHandleStreamingClient(WebKitWebSrc*, const ResourceRequest&);
- virtual ~ResourceHandleStreamingClient();
-
- // StreamingClient virtual methods.
- virtual bool loadFailed() const;
- virtual void setDefersLoading(bool);
-
- private:
- // ResourceHandleClient virtual methods.
- virtual char* getOrCreateReadBuffer(size_t requestedSize, size_t& actualSize);
- virtual void willSendRequest(ResourceHandle*, ResourceRequest&, const ResourceResponse&);
- virtual void didReceiveResponse(ResourceHandle*, const ResourceResponse&);
- virtual void didReceiveData(ResourceHandle*, const char*, unsigned, int);
- virtual void didReceiveBuffer(ResourceHandle*, PassRefPtr<SharedBuffer>, int encodedLength);
- virtual void didFinishLoading(ResourceHandle*, double /*finishTime*/);
- virtual void didFail(ResourceHandle*, const ResourceError&);
- virtual void wasBlocked(ResourceHandle*);
- virtual void cannotShowURL(ResourceHandle*);
-
- RefPtr<ResourceHandle> m_resource;
+enum MainThreadSourceNotification {
+ Start = 1 << 0,
+ Stop = 1 << 1,
+ NeedData = 1 << 2,
+ EnoughData = 1 << 3,
+ Seek = 1 << 4
};
#define WEBKIT_WEB_SRC_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE((obj), WEBKIT_TYPE_WEB_SRC, WebKitWebSrcPrivate))
struct _WebKitWebSrcPrivate {
GstAppSrc* appsrc;
GstPad* srcpad;
- gchar* uri;
+ CString originalURI;
+ CString redirectedURI;
+ bool keepAlive;
+ GUniquePtr<GstStructure> extraHeaders;
+ bool compress;
+ GUniquePtr<gchar> httpMethod;
WebCore::MediaPlayer* player;
- StreamingClient* client;
+ RefPtr<PlatformMediaResourceLoader> loader;
+ RefPtr<PlatformMediaResource> resource;
+ RefPtr<ResourceHandleStreamingClient> client;
- CORSAccessCheckResult corsAccessCheck;
+ bool didPassAccessControlCheck;
guint64 offset;
guint64 size;
gboolean seekable;
- gboolean paused;
+ bool paused;
+ bool isSeeking;
guint64 requestedOffset;
- guint startID;
- guint stopID;
- guint needDataID;
- guint enoughDataID;
- guint seekID;
-
+ bool createdInMainThread;
+ RefPtr<MainThreadNotifier<MainThreadSourceNotification>> notifier;
GRefPtr<GstBuffer> buffer;
-
- // icecast stuff
- gboolean iradioMode;
- gchar* iradioName;
- gchar* iradioGenre;
- gchar* iradioUrl;
- gchar* iradioTitle;
};
enum {
- PROP_IRADIO_MODE = 1,
- PROP_IRADIO_NAME,
- PROP_IRADIO_GENRE,
- PROP_IRADIO_URL,
- PROP_IRADIO_TITLE,
- PROP_LOCATION
+ PROP_0,
+ PROP_LOCATION,
+ PROP_RESOLVED_LOCATION,
+ PROP_KEEP_ALIVE,
+ PROP_EXTRA_HEADERS,
+ PROP_COMPRESS,
+ PROP_METHOD
};
static GstStaticPadTemplate srcTemplate = GST_STATIC_PAD_TEMPLATE("src",
@@ -180,15 +196,24 @@ static GstStateChangeReturn webKitWebSrcChangeState(GstElement*, GstStateChange)
static gboolean webKitWebSrcQueryWithParent(GstPad*, GstObject*, GstQuery*);
-static void webKitWebSrcNeedDataCb(GstAppSrc*, guint length, gpointer userData);
-static void webKitWebSrcEnoughDataCb(GstAppSrc*, gpointer userData);
-static gboolean webKitWebSrcSeekDataCb(GstAppSrc*, guint64 offset, gpointer userData);
+static void webKitWebSrcNeedData(WebKitWebSrc*);
+static void webKitWebSrcEnoughData(WebKitWebSrc*);
+static gboolean webKitWebSrcSeek(WebKitWebSrc*, guint64);
static GstAppSrcCallbacks appsrcCallbacks = {
- webKitWebSrcNeedDataCb,
- webKitWebSrcEnoughDataCb,
- webKitWebSrcSeekDataCb,
- { 0 }
+ // need_data
+ [](GstAppSrc*, guint, gpointer userData) {
+ webKitWebSrcNeedData(WEBKIT_WEB_SRC(userData));
+ },
+ // enough_data
+ [](GstAppSrc*, gpointer userData) {
+ webKitWebSrcEnoughData(WEBKIT_WEB_SRC(userData));
+ },
+ // seek_data
+ [](GstAppSrc*, guint64 offset, gpointer userData) -> gboolean {
+ return webKitWebSrcSeek(WEBKIT_WEB_SRC(userData), offset);
+ },
+ { nullptr }
};
#define webkit_web_src_parent_class parent_class
@@ -213,57 +238,32 @@ static void webkit_web_src_class_init(WebKitWebSrcClass* klass)
gst_element_class_set_metadata(eklass, "WebKit Web source element", "Source", "Handles HTTP/HTTPS uris",
"Sebastian Dröge <sebastian.droege@collabora.co.uk>");
- // icecast stuff
- g_object_class_install_property(oklass,
- PROP_IRADIO_MODE,
- g_param_spec_boolean("iradio-mode",
- "iradio-mode",
- "Enable internet radio mode (extraction of shoutcast/icecast metadata)",
- FALSE,
- (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
-
- g_object_class_install_property(oklass,
- PROP_IRADIO_NAME,
- g_param_spec_string("iradio-name",
- "iradio-name",
- "Name of the stream",
- 0,
- (GParamFlags) (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)));
-
- g_object_class_install_property(oklass,
- PROP_IRADIO_GENRE,
- g_param_spec_string("iradio-genre",
- "iradio-genre",
- "Genre of the stream",
- 0,
- (GParamFlags) (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)));
-
- g_object_class_install_property(oklass,
- PROP_IRADIO_URL,
- g_param_spec_string("iradio-url",
- "iradio-url",
- "Homepage URL for radio stream",
- 0,
- (GParamFlags) (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)));
-
- g_object_class_install_property(oklass,
- PROP_IRADIO_TITLE,
- g_param_spec_string("iradio-title",
- "iradio-title",
- "Name of currently playing song",
- 0,
- (GParamFlags) (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)));
-
-
/* Allows setting the uri using the 'location' property, which is used
* for example by gst_element_make_from_uri() */
- g_object_class_install_property(oklass,
- PROP_LOCATION,
- g_param_spec_string("location",
- "location",
- "Location to read from",
- 0,
- (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
+ g_object_class_install_property(oklass, PROP_LOCATION,
+ g_param_spec_string("location", "location", "Location to read from",
+ nullptr, static_cast<GParamFlags>(G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
+
+ g_object_class_install_property(oklass, PROP_RESOLVED_LOCATION,
+ g_param_spec_string("resolved-location", "Resolved location", "The location resolved by the server",
+ nullptr, static_cast<GParamFlags>(G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)));
+
+ g_object_class_install_property(oklass, PROP_KEEP_ALIVE,
+ g_param_spec_boolean("keep-alive", "keep-alive", "Use HTTP persistent connections",
+ FALSE, static_cast<GParamFlags>(G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
+
+ g_object_class_install_property(oklass, PROP_EXTRA_HEADERS,
+ g_param_spec_boxed("extra-headers", "Extra Headers", "Extra headers to append to the HTTP request",
+ GST_TYPE_STRUCTURE, static_cast<GParamFlags>(G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
+
+ g_object_class_install_property(oklass, PROP_COMPRESS,
+ g_param_spec_boolean("compress", "Compress", "Allow compressed content encodings",
+ FALSE, static_cast<GParamFlags>(G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
+
+ g_object_class_install_property(oklass, PROP_METHOD,
+ g_param_spec_string("method", "method", "The HTTP method to use (default: GET)",
+ nullptr, static_cast<GParamFlags>(G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
+
eklass->change_state = webKitWebSrcChangeState;
g_type_class_add_private(klass, sizeof(WebKitWebSrcPrivate));
@@ -274,8 +274,12 @@ static void webkit_web_src_init(WebKitWebSrc* src)
WebKitWebSrcPrivate* priv = WEBKIT_WEB_SRC_GET_PRIVATE(src);
src->priv = priv;
+ new (priv) WebKitWebSrcPrivate();
+
+ priv->createdInMainThread = isMainThread();
+ priv->notifier = MainThreadNotifier<MainThreadSourceNotification>::create();
- priv->appsrc = GST_APP_SRC(gst_element_factory_make("appsrc", 0));
+ priv->appsrc = GST_APP_SRC(gst_element_factory_make("appsrc", nullptr));
if (!priv->appsrc) {
GST_ERROR_OBJECT(src, "Failed to create appsrc");
return;
@@ -292,7 +296,7 @@ static void webkit_web_src_init(WebKitWebSrc* src)
GST_OBJECT_FLAG_SET(priv->srcpad, GST_PAD_FLAG_NEED_PARENT);
gst_pad_set_query_function(priv->srcpad, webKitWebSrcQueryWithParent);
- gst_app_src_set_callbacks(priv->appsrc, &appsrcCallbacks, src, 0);
+ gst_app_src_set_callbacks(priv->appsrc, &appsrcCallbacks, src, nullptr);
gst_app_src_set_emit_signals(priv->appsrc, FALSE);
gst_app_src_set_stream_type(priv->appsrc, GST_APP_STREAM_TYPE_SEEKABLE);
@@ -313,28 +317,32 @@ static void webkit_web_src_init(WebKitWebSrc* src)
// likely that libsoup already provides new data before
// the queue is really empty.
// This might need tweaking for ports not using libsoup.
- g_object_set(priv->appsrc, "min-percent", 20, NULL);
+ g_object_set(priv->appsrc, "min-percent", 20, nullptr);
- gst_app_src_set_caps(priv->appsrc, 0);
+ gst_base_src_set_automatic_eos(GST_BASE_SRC(priv->appsrc), FALSE);
+
+ gst_app_src_set_caps(priv->appsrc, nullptr);
gst_app_src_set_size(priv->appsrc, -1);
}
static void webKitWebSrcDispose(GObject* object)
{
- WebKitWebSrc* src = WEBKIT_WEB_SRC(object);
- WebKitWebSrcPrivate* priv = src->priv;
+ WebKitWebSrcPrivate* priv = WEBKIT_WEB_SRC(object)->priv;
+ if (priv->notifier) {
+ priv->notifier->invalidate();
+ priv->notifier = nullptr;
+ }
- priv->player = 0;
+ priv->player = nullptr;
GST_CALL_PARENT(G_OBJECT_CLASS, dispose, (object));
}
static void webKitWebSrcFinalize(GObject* object)
{
- WebKitWebSrc* src = WEBKIT_WEB_SRC(object);
- WebKitWebSrcPrivate* priv = src->priv;
+ WebKitWebSrcPrivate* priv = WEBKIT_WEB_SRC(object)->priv;
- g_free(priv->uri);
+ priv->~WebKitWebSrcPrivate();
GST_CALL_PARENT(G_OBJECT_CLASS, finalize, (object));
}
@@ -342,16 +350,24 @@ static void webKitWebSrcFinalize(GObject* object)
static void webKitWebSrcSetProperty(GObject* object, guint propID, const GValue* value, GParamSpec* pspec)
{
WebKitWebSrc* src = WEBKIT_WEB_SRC(object);
- WebKitWebSrcPrivate* priv = src->priv;
switch (propID) {
- case PROP_IRADIO_MODE: {
- WTF::GMutexLocker locker(GST_OBJECT_GET_LOCK(src));
- priv->iradioMode = g_value_get_boolean(value);
+ case PROP_LOCATION:
+ gst_uri_handler_set_uri(reinterpret_cast<GstURIHandler*>(src), g_value_get_string(value), nullptr);
+ break;
+ case PROP_KEEP_ALIVE:
+ src->priv->keepAlive = g_value_get_boolean(value);
+ break;
+ case PROP_EXTRA_HEADERS: {
+ const GstStructure* s = gst_value_get_structure(value);
+ src->priv->extraHeaders.reset(s ? gst_structure_copy(s) : nullptr);
break;
}
- case PROP_LOCATION:
- gst_uri_handler_set_uri(reinterpret_cast<GstURIHandler*>(src), g_value_get_string(value), 0);
+ case PROP_COMPRESS:
+ src->priv->compress = g_value_get_boolean(value);
+ break;
+ case PROP_METHOD:
+ src->priv->httpMethod.reset(g_value_dup_string(value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID(object, propID, pspec);
@@ -364,25 +380,25 @@ static void webKitWebSrcGetProperty(GObject* object, guint propID, GValue* value
WebKitWebSrc* src = WEBKIT_WEB_SRC(object);
WebKitWebSrcPrivate* priv = src->priv;
- WTF::GMutexLocker locker(GST_OBJECT_GET_LOCK(src));
+ WTF::GMutexLocker<GMutex> locker(*GST_OBJECT_GET_LOCK(src));
switch (propID) {
- case PROP_IRADIO_MODE:
- g_value_set_boolean(value, priv->iradioMode);
+ case PROP_LOCATION:
+ g_value_set_string(value, priv->originalURI.data());
break;
- case PROP_IRADIO_NAME:
- g_value_set_string(value, priv->iradioName);
+ case PROP_RESOLVED_LOCATION:
+ g_value_set_string(value, priv->redirectedURI.isNull() ? priv->originalURI.data() : priv->redirectedURI.data());
break;
- case PROP_IRADIO_GENRE:
- g_value_set_string(value, priv->iradioGenre);
+ case PROP_KEEP_ALIVE:
+ g_value_set_boolean(value, priv->keepAlive);
break;
- case PROP_IRADIO_URL:
- g_value_set_string(value, priv->iradioUrl);
+ case PROP_EXTRA_HEADERS:
+ gst_value_set_structure(value, priv->extraHeaders.get());
break;
- case PROP_IRADIO_TITLE:
- g_value_set_string(value, priv->iradioTitle);
+ case PROP_COMPRESS:
+ g_value_set_boolean(value, priv->compress);
break;
- case PROP_LOCATION:
- g_value_set_string(value, priv->uri);
+ case PROP_METHOD:
+ g_value_set_string(value, priv->httpMethod.get());
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID(object, propID, pspec);
@@ -390,123 +406,159 @@ static void webKitWebSrcGetProperty(GObject* object, guint propID, GValue* value
}
}
-static void removeTimeoutSources(WebKitWebSrc* src)
+static void webKitWebSrcStop(WebKitWebSrc* src)
{
WebKitWebSrcPrivate* priv = src->priv;
- if (priv->startID)
- g_source_remove(priv->startID);
- priv->startID = 0;
-
- if (priv->needDataID)
- g_source_remove(priv->needDataID);
- priv->needDataID = 0;
-
- if (priv->enoughDataID)
- g_source_remove(priv->enoughDataID);
- priv->enoughDataID = 0;
-
- if (priv->seekID)
- g_source_remove(priv->seekID);
- priv->seekID = 0;
-}
-
-static gboolean webKitWebSrcStop(WebKitWebSrc* src)
-{
- WebKitWebSrcPrivate* priv = src->priv;
-
- ASSERT(isMainThread());
-
- WTF::GMutexLocker locker(GST_OBJECT_GET_LOCK(src));
-
- bool seeking = priv->seekID;
-
- removeTimeoutSources(src);
- priv->stopID = 0;
+ if (priv->resource || (priv->loader && !priv->keepAlive)) {
+ GRefPtr<WebKitWebSrc> protector = WTF::ensureGRef(src);
+ priv->notifier->cancelPendingNotifications(MainThreadSourceNotification::NeedData | MainThreadSourceNotification::EnoughData | MainThreadSourceNotification::Seek);
+ priv->notifier->notify(MainThreadSourceNotification::Stop, [protector, keepAlive = priv->keepAlive] {
+ WebKitWebSrcPrivate* priv = protector->priv;
+
+ WTF::GMutexLocker<GMutex> locker(*GST_OBJECT_GET_LOCK(protector.get()));
+ if (priv->resource) {
+ priv->resource->stop();
+ priv->resource->setClient(nullptr);
+ priv->resource = nullptr;
+ }
+
+ if (!keepAlive)
+ priv->loader = nullptr;
+ });
+ }
if (priv->client) {
- delete priv->client;
- priv->client = 0;
+ priv->client->invalidate();
+ priv->client = nullptr;
}
+ WTF::GMutexLocker<GMutex> locker(*GST_OBJECT_GET_LOCK(src));
+
+ bool wasSeeking = std::exchange(priv->isSeeking, false);
+
if (priv->buffer) {
unmapGstBuffer(priv->buffer.get());
priv->buffer.clear();
}
- priv->paused = FALSE;
-
- g_free(priv->iradioName);
- priv->iradioName = 0;
-
- g_free(priv->iradioGenre);
- priv->iradioGenre = 0;
-
- g_free(priv->iradioUrl);
- priv->iradioUrl = 0;
-
- g_free(priv->iradioTitle);
- priv->iradioTitle = 0;
+ priv->paused = false;
priv->offset = 0;
priv->seekable = FALSE;
- if (!seeking) {
+ if (!wasSeeking) {
priv->size = 0;
priv->requestedOffset = 0;
- priv->player = 0;
+ priv->player = nullptr;
}
locker.unlock();
if (priv->appsrc) {
- gst_app_src_set_caps(priv->appsrc, 0);
- if (!seeking)
+ gst_app_src_set_caps(priv->appsrc, nullptr);
+ if (!wasSeeking)
gst_app_src_set_size(priv->appsrc, -1);
}
GST_DEBUG_OBJECT(src, "Stopped request");
+}
+
+static bool webKitWebSrcSetExtraHeader(GQuark fieldId, const GValue* value, gpointer userData)
+{
+ GUniquePtr<gchar> fieldContent;
+
+ if (G_VALUE_HOLDS_STRING(value))
+ fieldContent.reset(g_value_dup_string(value));
+ else {
+ GValue dest = G_VALUE_INIT;
+
+ g_value_init(&dest, G_TYPE_STRING);
+ if (g_value_transform(value, &dest))
+ fieldContent.reset(g_value_dup_string(&dest));
+ }
+
+ const gchar* fieldName = g_quark_to_string(fieldId);
+ if (!fieldContent.get()) {
+ GST_ERROR("extra-headers field '%s' contains no value or can't be converted to a string", fieldName);
+ return false;
+ }
- return FALSE;
+ GST_DEBUG("Appending extra header: \"%s: %s\"", fieldName, fieldContent.get());
+ ResourceRequest* request = static_cast<ResourceRequest*>(userData);
+ request->setHTTPHeaderField(fieldName, fieldContent.get());
+ return true;
}
-static gboolean webKitWebSrcStart(WebKitWebSrc* src)
+static gboolean webKitWebSrcProcessExtraHeaders(GQuark fieldId, const GValue* value, gpointer userData)
{
- WebKitWebSrcPrivate* priv = src->priv;
+ if (G_VALUE_TYPE(value) == GST_TYPE_ARRAY) {
+ unsigned size = gst_value_array_get_size(value);
- ASSERT(isMainThread());
+ for (unsigned i = 0; i < size; i++) {
+ if (!webKitWebSrcSetExtraHeader(fieldId, gst_value_array_get_value(value, i), userData))
+ return FALSE;
+ }
+ return TRUE;
+ }
- WTF::GMutexLocker locker(GST_OBJECT_GET_LOCK(src));
+ if (G_VALUE_TYPE(value) == GST_TYPE_LIST) {
+ unsigned size = gst_value_list_get_size(value);
+
+ for (unsigned i = 0; i < size; i++) {
+ if (!webKitWebSrcSetExtraHeader(fieldId, gst_value_list_get_value(value, i), userData))
+ return FALSE;
+ }
+ return TRUE;
+ }
+
+ return webKitWebSrcSetExtraHeader(fieldId, value, userData);
+}
+
+static void webKitWebSrcStart(WebKitWebSrc* src)
+{
+ WebKitWebSrcPrivate* priv = src->priv;
- priv->startID = 0;
- priv->corsAccessCheck = CORSNoCheck;
+ WTF::GMutexLocker<GMutex> locker(*GST_OBJECT_GET_LOCK(src));
- if (!priv->uri) {
+ priv->didPassAccessControlCheck = false;
+
+ if (priv->originalURI.isNull()) {
GST_ERROR_OBJECT(src, "No URI provided");
locker.unlock();
webKitWebSrcStop(src);
- return FALSE;
+ return;
}
ASSERT(!priv->client);
- URL url = URL(URL(), priv->uri);
+ GST_DEBUG_OBJECT(src, "Fetching %s", priv->originalURI.data());
+ URL url = URL(URL(), priv->originalURI.data());
ResourceRequest request(url);
request.setAllowCookies(true);
request.setFirstPartyForCookies(url);
+ priv->size = 0;
+
if (priv->player)
request.setHTTPReferrer(priv->player->referrer());
+ if (priv->httpMethod.get())
+ request.setHTTPMethod(priv->httpMethod.get());
+
#if USE(SOUP)
- // Let's disable HTTP Accept-Encoding here as we don't want the received response to be
- // encoded in any way as we need to rely on the proper size of the returned data on
+ // By default, HTTP Accept-Encoding is disabled here as we don't
+ // want the received response to be encoded in any way as we need
+ // to rely on the proper size of the returned data on
// didReceiveResponse.
// If Accept-Encoding is used, the server may send the data in encoded format and
// request.expectedContentLength() will have the "wrong" size (the size of the
// compressed data), even though the data received in didReceiveData is uncompressed.
- request.setAcceptEncoding(false);
+ // This is however useful to enable for adaptive streaming
+ // scenarios, when the demuxer needs to download playlists.
+ if (!priv->compress)
+ request.setAcceptEncoding(false);
#endif
// Let Apple web servers know we want to access their nice movie trailers.
@@ -516,36 +568,55 @@ static gboolean webKitWebSrcStart(WebKitWebSrc* src)
if (priv->requestedOffset) {
GUniquePtr<gchar> val(g_strdup_printf("bytes=%" G_GUINT64_FORMAT "-", priv->requestedOffset));
- request.setHTTPHeaderField("Range", val.get());
+ request.setHTTPHeaderField(HTTPHeaderName::Range, val.get());
}
priv->offset = priv->requestedOffset;
- if (priv->iradioMode)
- request.setHTTPHeaderField("icy-metadata", "1");
+ if (!priv->keepAlive) {
+ GST_DEBUG_OBJECT(src, "Persistent connection support disabled");
+ request.setHTTPHeaderField(HTTPHeaderName::Connection, "close");
+ }
- // Needed to use DLNA streaming servers
- request.setHTTPHeaderField("transferMode.dlna", "Streaming");
+ if (priv->extraHeaders)
+ gst_structure_foreach(priv->extraHeaders.get(), webKitWebSrcProcessExtraHeaders, &request);
- if (priv->player) {
- if (CachedResourceLoader* loader = priv->player->cachedResourceLoader())
- priv->client = new CachedResourceStreamingClient(src, loader, request, priv->player->mediaPlayerClient()->mediaPlayerCORSMode());
- }
+ // We always request Icecast/Shoutcast metadata, just in case ...
+ request.setHTTPHeaderField(HTTPHeaderName::IcyMetadata, "1");
- if (!priv->client)
- priv->client = new ResourceHandleStreamingClient(src, request);
+ if (!priv->player || !priv->createdInMainThread) {
+ priv->client = ResourceHandleStreamingClient::create(src, WTFMove(request));
+ if (priv->client->loadFailed()) {
+ GST_ERROR_OBJECT(src, "Failed to setup streaming client");
+ locker.unlock();
+ webKitWebSrcStop(src);
+ } else
+ GST_DEBUG_OBJECT(src, "Started request");
+ return;
+ }
- if (!priv->client || priv->client->loadFailed()) {
- GST_ERROR_OBJECT(src, "Failed to setup streaming client");
- if (priv->client) {
- delete priv->client;
- priv->client = 0;
+ locker.unlock();
+ GRefPtr<WebKitWebSrc> protector = WTF::ensureGRef(src);
+ priv->notifier->notify(MainThreadSourceNotification::Start, [protector, request = WTFMove(request)] {
+ WebKitWebSrcPrivate* priv = protector->priv;
+
+ WTF::GMutexLocker<GMutex> locker(*GST_OBJECT_GET_LOCK(protector.get()));
+ if (!priv->loader)
+ priv->loader = priv->player->createResourceLoader();
+
+ PlatformMediaResourceLoader::LoadOptions loadOptions = 0;
+ if (request.url().protocolIsBlob())
+ loadOptions |= PlatformMediaResourceLoader::LoadOption::BufferData;
+ priv->resource = priv->loader->requestResource(ResourceRequest(request), loadOptions);
+ if (priv->resource) {
+ priv->resource->setClient(std::make_unique<CachedResourceStreamingClient>(protector.get(), ResourceRequest(request)));
+ GST_DEBUG_OBJECT(protector.get(), "Started request");
+ } else {
+ GST_ERROR_OBJECT(protector.get(), "Failed to setup streaming client");
+ priv->loader = nullptr;
+ locker.unlock();
+ webKitWebSrcStop(protector.get());
}
- locker.unlock();
- webKitWebSrcStop(src);
- return FALSE;
- }
- GST_DEBUG_OBJECT(src, "Started request");
- return FALSE;
+ });
}
static GstStateChangeReturn webKitWebSrcChangeState(GstElement* element, GstStateChange transition)
@@ -559,7 +630,7 @@ static GstStateChangeReturn webKitWebSrcChangeState(GstElement* element, GstStat
if (!priv->appsrc) {
gst_element_post_message(element,
gst_missing_element_message_new(element, "appsrc"));
- GST_ELEMENT_ERROR(src, CORE, MISSING_PLUGIN, (0), ("no appsrc"));
+ GST_ELEMENT_ERROR(src, CORE, MISSING_PLUGIN, (nullptr), ("no appsrc"));
return GST_STATE_CHANGE_FAILURE;
}
break;
@@ -573,18 +644,19 @@ static GstStateChangeReturn webKitWebSrcChangeState(GstElement* element, GstStat
return ret;
}
- WTF::GMutexLocker locker(GST_OBJECT_GET_LOCK(src));
switch (transition) {
case GST_STATE_CHANGE_READY_TO_PAUSED:
+ {
GST_DEBUG_OBJECT(src, "READY->PAUSED");
- priv->startID = g_idle_add_full(G_PRIORITY_DEFAULT, (GSourceFunc) webKitWebSrcStart, gst_object_ref(src), (GDestroyNotify) gst_object_unref);
+ webKitWebSrcStart(src);
break;
+ }
case GST_STATE_CHANGE_PAUSED_TO_READY:
+ {
GST_DEBUG_OBJECT(src, "PAUSED->READY");
- // cancel pending sources
- removeTimeoutSources(src);
- priv->stopID = g_idle_add_full(G_PRIORITY_DEFAULT, (GSourceFunc) webKitWebSrcStop, gst_object_ref(src), (GDestroyNotify) gst_object_unref);
+ webKitWebSrcStop(src);
break;
+ }
default:
break;
}
@@ -601,10 +673,10 @@ static gboolean webKitWebSrcQueryWithParent(GstPad* pad, GstObject* parent, GstQ
case GST_QUERY_DURATION: {
GstFormat format;
- gst_query_parse_duration(query, &format, NULL);
+ gst_query_parse_duration(query, &format, nullptr);
GST_DEBUG_OBJECT(src, "duration query in format %s", gst_format_get_name(format));
- WTF::GMutexLocker locker(GST_OBJECT_GET_LOCK(src));
+ WTF::GMutexLocker<GMutex> locker(*GST_OBJECT_GET_LOCK(src));
if (format == GST_FORMAT_BYTES && src->priv->size > 0) {
gst_query_set_duration(query, format, src->priv->size);
result = TRUE;
@@ -612,8 +684,19 @@ static gboolean webKitWebSrcQueryWithParent(GstPad* pad, GstObject* parent, GstQ
break;
}
case GST_QUERY_URI: {
- WTF::GMutexLocker locker(GST_OBJECT_GET_LOCK(src));
- gst_query_set_uri(query, src->priv->uri);
+ WTF::GMutexLocker<GMutex> locker(*GST_OBJECT_GET_LOCK(src));
+ gst_query_set_uri(query, src->priv->originalURI.data());
+ if (!src->priv->redirectedURI.isNull())
+ gst_query_set_uri_redirection(query, src->priv->redirectedURI.data());
+ result = TRUE;
+ break;
+ }
+ case GST_QUERY_SCHEDULING: {
+ GstSchedulingFlags flags;
+ int minSize, maxSize, align;
+
+ gst_query_parse_scheduling(query, &flags, &minSize, &maxSize, &align);
+ gst_query_set_scheduling(query, static_cast<GstSchedulingFlags>(flags | GST_SCHEDULING_FLAG_BANDWIDTH_LIMITED), minSize, maxSize, align);
result = TRUE;
break;
}
@@ -632,7 +715,7 @@ static gboolean webKitWebSrcQueryWithParent(GstPad* pad, GstObject* parent, GstQ
static bool urlHasSupportedProtocol(const URL& url)
{
- return url.isValid() && (url.protocolIsInHTTPFamily() || url.protocolIs("blob"));
+ return url.isValid() && (url.protocolIsInHTTPFamily() || url.protocolIsBlob());
}
// uri handler interface
@@ -644,7 +727,7 @@ static GstURIType webKitWebSrcUriGetType(GType)
const gchar* const* webKitWebSrcGetProtocols(GType)
{
- static const char* protocols[] = {"http", "https", "blob", 0 };
+ static const char* protocols[] = {"http", "https", "blob", nullptr };
return protocols;
}
@@ -653,8 +736,8 @@ static gchar* webKitWebSrcGetUri(GstURIHandler* handler)
WebKitWebSrc* src = WEBKIT_WEB_SRC(handler);
gchar* ret;
- WTF::GMutexLocker locker(GST_OBJECT_GET_LOCK(src));
- ret = g_strdup(src->priv->uri);
+ WTF::GMutexLocker<GMutex> locker(*GST_OBJECT_GET_LOCK(src));
+ ret = g_strdup(src->priv->originalURI.data());
return ret;
}
@@ -668,11 +751,10 @@ static gboolean webKitWebSrcSetUri(GstURIHandler* handler, const gchar* uri, GEr
return FALSE;
}
- WTF::GMutexLocker locker(GST_OBJECT_GET_LOCK(src));
-
- g_free(priv->uri);
- priv->uri = 0;
+ WTF::GMutexLocker<GMutex> locker(*GST_OBJECT_GET_LOCK(src));
+ priv->redirectedURI = CString();
+ priv->originalURI = CString();
if (!uri)
return TRUE;
@@ -682,7 +764,7 @@ static gboolean webKitWebSrcSetUri(GstURIHandler* handler, const gchar* uri, GEr
return FALSE;
}
- priv->uri = g_strdup(url.string().utf8().data());
+ priv->originalURI = url.string().utf8();
return TRUE;
}
@@ -696,152 +778,122 @@ static void webKitWebSrcUriHandlerInit(gpointer gIface, gpointer)
iface->set_uri = webKitWebSrcSetUri;
}
-// appsrc callbacks
-
-static gboolean webKitWebSrcNeedDataMainCb(WebKitWebSrc* src)
-{
- WebKitWebSrcPrivate* priv = src->priv;
-
- ASSERT(isMainThread());
-
- WTF::GMutexLocker locker(GST_OBJECT_GET_LOCK(src));
- // already stopped
- if (!priv->needDataID)
- return FALSE;
-
- priv->paused = FALSE;
- priv->needDataID = 0;
- locker.unlock();
-
- if (priv->client)
- priv->client->setDefersLoading(false);
- return FALSE;
-}
-
-static void webKitWebSrcNeedDataCb(GstAppSrc*, guint length, gpointer userData)
+static void webKitWebSrcNeedData(WebKitWebSrc* src)
{
- WebKitWebSrc* src = WEBKIT_WEB_SRC(userData);
WebKitWebSrcPrivate* priv = src->priv;
- GST_DEBUG_OBJECT(src, "Need more data: %u", length);
+ GST_DEBUG_OBJECT(src, "Need more data");
- WTF::GMutexLocker locker(GST_OBJECT_GET_LOCK(src));
- if (priv->needDataID || !priv->paused) {
- return;
+ {
+ WTF::GMutexLocker<GMutex> locker(*GST_OBJECT_GET_LOCK(src));
+ if (!priv->paused)
+ return;
+ priv->paused = false;
+ if (priv->client) {
+ priv->client->setDefersLoading(false);
+ return;
+ }
}
- priv->needDataID = g_idle_add_full(G_PRIORITY_DEFAULT, (GSourceFunc) webKitWebSrcNeedDataMainCb, gst_object_ref(src), (GDestroyNotify) gst_object_unref);
+ GRefPtr<WebKitWebSrc> protector = WTF::ensureGRef(src);
+ priv->notifier->notify(MainThreadSourceNotification::NeedData, [protector] {
+ WebKitWebSrcPrivate* priv = protector->priv;
+ if (priv->resource)
+ priv->resource->setDefersLoading(false);
+ });
}
-static gboolean webKitWebSrcEnoughDataMainCb(WebKitWebSrc* src)
+static void webKitWebSrcEnoughData(WebKitWebSrc* src)
{
WebKitWebSrcPrivate* priv = src->priv;
- ASSERT(isMainThread());
-
- WTF::GMutexLocker locker(GST_OBJECT_GET_LOCK(src));
- // already stopped
- if (!priv->enoughDataID)
- return FALSE;
-
- priv->paused = TRUE;
- priv->enoughDataID = 0;
- locker.unlock();
-
- if (priv->client)
- priv->client->setDefersLoading(true);
- return FALSE;
-}
-
-static void webKitWebSrcEnoughDataCb(GstAppSrc*, gpointer userData)
-{
- WebKitWebSrc* src = WEBKIT_WEB_SRC(userData);
- WebKitWebSrcPrivate* priv = src->priv;
-
GST_DEBUG_OBJECT(src, "Have enough data");
- WTF::GMutexLocker locker(GST_OBJECT_GET_LOCK(src));
- if (priv->enoughDataID || priv->paused) {
- return;
+ {
+ WTF::GMutexLocker<GMutex> locker(*GST_OBJECT_GET_LOCK(src));
+ if (priv->paused)
+ return;
+ priv->paused = true;
+ if (priv->client) {
+ priv->client->setDefersLoading(true);
+ return;
+ }
}
- priv->enoughDataID = g_idle_add_full(G_PRIORITY_DEFAULT, (GSourceFunc) webKitWebSrcEnoughDataMainCb, gst_object_ref(src), (GDestroyNotify) gst_object_unref);
+ GRefPtr<WebKitWebSrc> protector = WTF::ensureGRef(src);
+ priv->notifier->notify(MainThreadSourceNotification::EnoughData, [protector] {
+ WebKitWebSrcPrivate* priv = protector->priv;
+ if (priv->resource)
+ priv->resource->setDefersLoading(true);
+ });
}
-static gboolean webKitWebSrcSeekMainCb(WebKitWebSrc* src)
+static gboolean webKitWebSrcSeek(WebKitWebSrc* src, guint64 offset)
{
WebKitWebSrcPrivate* priv = src->priv;
- ASSERT(isMainThread());
+ {
+ WTF::GMutexLocker<GMutex> locker(*GST_OBJECT_GET_LOCK(src));
+ if (offset == priv->offset && priv->requestedOffset == priv->offset)
+ return TRUE;
- WTF::GMutexLocker locker(GST_OBJECT_GET_LOCK(src));
- // already stopped
- if (!priv->seekID)
- return FALSE;
- locker.unlock();
+ if (!priv->seekable)
+ return FALSE;
- webKitWebSrcStop(src);
- webKitWebSrcStart(src);
-
- return FALSE;
-}
-
-static gboolean webKitWebSrcSeekDataCb(GstAppSrc*, guint64 offset, gpointer userData)
-{
- WebKitWebSrc* src = WEBKIT_WEB_SRC(userData);
- WebKitWebSrcPrivate* priv = src->priv;
+ priv->isSeeking = true;
+ priv->requestedOffset = offset;
+ }
- GST_DEBUG_OBJECT(src, "Seeking to offset: %" G_GUINT64_FORMAT, offset);
- WTF::GMutexLocker locker(GST_OBJECT_GET_LOCK(src));
- if (offset == priv->offset && priv->requestedOffset == priv->offset)
+ GST_DEBUG_OBJECT(src, "Seeking to offset: %" G_GUINT64_FORMAT, src->priv->requestedOffset);
+ if (priv->client) {
+ webKitWebSrcStop(src);
+ webKitWebSrcStart(src);
return TRUE;
+ }
- if (!priv->seekable)
- return FALSE;
-
- GST_DEBUG_OBJECT(src, "Doing range-request seek");
- priv->requestedOffset = offset;
-
- if (priv->seekID)
- g_source_remove(priv->seekID);
- priv->seekID = g_idle_add_full(G_PRIORITY_DEFAULT, (GSourceFunc) webKitWebSrcSeekMainCb, gst_object_ref(src), (GDestroyNotify) gst_object_unref);
+ GRefPtr<WebKitWebSrc> protector = WTF::ensureGRef(src);
+ priv->notifier->notify(MainThreadSourceNotification::Seek, [protector] {
+ webKitWebSrcStop(protector.get());
+ webKitWebSrcStart(protector.get());
+ });
return TRUE;
}
void webKitWebSrcSetMediaPlayer(WebKitWebSrc* src, WebCore::MediaPlayer* player)
{
ASSERT(player);
- WTF::GMutexLocker locker(GST_OBJECT_GET_LOCK(src));
+ ASSERT(src->priv->createdInMainThread);
+ WTF::GMutexLocker<GMutex> locker(*GST_OBJECT_GET_LOCK(src));
src->priv->player = player;
}
bool webKitSrcPassedCORSAccessCheck(WebKitWebSrc* src)
{
- return src->priv->corsAccessCheck == CORSSuccess;
+ return src->priv->didPassAccessControlCheck;
}
-StreamingClient::StreamingClient(WebKitWebSrc* src)
- : m_src(static_cast<GstElement*>(gst_object_ref(src)))
+StreamingClient::StreamingClient(WebKitWebSrc* src, ResourceRequest&& request)
+ : m_src(GST_ELEMENT(src))
+ , m_request(WTFMove(request))
{
}
StreamingClient::~StreamingClient()
{
- gst_object_unref(m_src);
}
char* StreamingClient::createReadBuffer(size_t requestedSize, size_t& actualSize)
{
- WebKitWebSrc* src = WEBKIT_WEB_SRC(m_src);
+ WebKitWebSrc* src = WEBKIT_WEB_SRC(m_src.get());
WebKitWebSrcPrivate* priv = src->priv;
ASSERT(!priv->buffer);
GstBuffer* buffer = gst_buffer_new_and_alloc(requestedSize);
- mapGstBuffer(buffer);
+ mapGstBuffer(buffer, GST_MAP_WRITE);
- WTF::GMutexLocker locker(GST_OBJECT_GET_LOCK(src));
+ WTF::GMutexLocker<GMutex> locker(*GST_OBJECT_GET_LOCK(src));
priv->buffer = adoptGRef(buffer);
locker.unlock();
@@ -849,29 +901,27 @@ char* StreamingClient::createReadBuffer(size_t requestedSize, size_t& actualSize
return getGstBufferDataPointer(buffer);
}
-void StreamingClient::handleResponseReceived(const ResourceResponse& response, CORSAccessCheckResult corsAccessCheck)
+void StreamingClient::handleResponseReceived(const ResourceResponse& response)
{
- WebKitWebSrc* src = WEBKIT_WEB_SRC(m_src);
+ WebKitWebSrc* src = WEBKIT_WEB_SRC(m_src.get());
WebKitWebSrcPrivate* priv = src->priv;
GST_DEBUG_OBJECT(src, "Received response: %d", response.httpStatusCode());
- if (response.httpStatusCode() >= 400 || corsAccessCheck == CORSFailure) {
- // Received error code or CORS check failed
- if (corsAccessCheck == CORSFailure)
- GST_ELEMENT_ERROR(src, RESOURCE, READ, ("Cross-origin stream load denied by Cross-Origin Resource Sharing policy."), (nullptr));
- else
- GST_ELEMENT_ERROR(src, RESOURCE, READ, ("Received %d HTTP error code", response.httpStatusCode()), (nullptr));
+ auto responseURI = response.url().string().utf8();
+ if (priv->originalURI != responseURI)
+ priv->redirectedURI = WTFMove(responseURI);
+
+ if (response.httpStatusCode() >= 400) {
+ GST_ELEMENT_ERROR(src, RESOURCE, READ, ("Received %d HTTP error code", response.httpStatusCode()), (nullptr));
gst_app_src_end_of_stream(priv->appsrc);
webKitWebSrcStop(src);
return;
}
- WTF::GMutexLocker locker(GST_OBJECT_GET_LOCK(src));
+ WTF::GMutexLocker<GMutex> locker(*GST_OBJECT_GET_LOCK(src));
- priv->corsAccessCheck = corsAccessCheck;
-
- if (priv->seekID) {
+ if (priv->isSeeking) {
GST_DEBUG_OBJECT(src, "Seek in progress, ignoring response");
return;
}
@@ -896,43 +946,9 @@ void StreamingClient::handleResponseReceived(const ResourceResponse& response, C
length += priv->requestedOffset;
priv->size = length >= 0 ? length : 0;
- priv->seekable = length > 0 && g_ascii_strcasecmp("none", response.httpHeaderField("Accept-Ranges").utf8().data());
-
- // Wait until we unlock to send notifications
- g_object_freeze_notify(G_OBJECT(src));
-
- GstTagList* tags = gst_tag_list_new_empty();
- String value = response.httpHeaderField("icy-name");
- if (!value.isEmpty()) {
- g_free(priv->iradioName);
- priv->iradioName = g_strdup(value.utf8().data());
- g_object_notify(G_OBJECT(src), "iradio-name");
- gst_tag_list_add(tags, GST_TAG_MERGE_REPLACE, GST_TAG_ORGANIZATION, priv->iradioName, NULL);
- }
- value = response.httpHeaderField("icy-genre");
- if (!value.isEmpty()) {
- g_free(priv->iradioGenre);
- priv->iradioGenre = g_strdup(value.utf8().data());
- g_object_notify(G_OBJECT(src), "iradio-genre");
- gst_tag_list_add(tags, GST_TAG_MERGE_REPLACE, GST_TAG_GENRE, priv->iradioGenre, NULL);
- }
- value = response.httpHeaderField("icy-url");
- if (!value.isEmpty()) {
- g_free(priv->iradioUrl);
- priv->iradioUrl = g_strdup(value.utf8().data());
- g_object_notify(G_OBJECT(src), "iradio-url");
- gst_tag_list_add(tags, GST_TAG_MERGE_REPLACE, GST_TAG_LOCATION, priv->iradioUrl, NULL);
- }
- value = response.httpHeaderField("icy-title");
- if (!value.isEmpty()) {
- g_free(priv->iradioTitle);
- priv->iradioTitle = g_strdup(value.utf8().data());
- g_object_notify(G_OBJECT(src), "iradio-title");
- gst_tag_list_add(tags, GST_TAG_MERGE_REPLACE, GST_TAG_TITLE, priv->iradioTitle, NULL);
- }
+ priv->seekable = length > 0 && g_ascii_strcasecmp("none", response.httpHeaderField(HTTPHeaderName::AcceptRanges).utf8().data());
locker.unlock();
- g_object_thaw_notify(G_OBJECT(src));
// notify size/duration
if (length > 0) {
@@ -940,33 +956,30 @@ void StreamingClient::handleResponseReceived(const ResourceResponse& response, C
} else
gst_app_src_set_size(priv->appsrc, -1);
- // icecast stuff
- value = response.httpHeaderField("icy-metaint");
- if (!value.isEmpty()) {
- gchar* endptr = 0;
- gint64 icyMetaInt = g_ascii_strtoll(value.utf8().data(), &endptr, 10);
-
- if (endptr && *endptr == '\0' && icyMetaInt > 0) {
- GRefPtr<GstCaps> caps = adoptGRef(gst_caps_new_simple("application/x-icy", "metadata-interval", G_TYPE_INT, (gint) icyMetaInt, NULL));
-
- gst_app_src_set_caps(priv->appsrc, caps.get());
- }
- } else
- gst_app_src_set_caps(priv->appsrc, 0);
-
- // notify tags
- if (gst_tag_list_is_empty(tags))
- gst_tag_list_unref(tags);
- else
- gst_pad_push_event(priv->srcpad, gst_event_new_tag(tags));
+ gst_app_src_set_caps(priv->appsrc, nullptr);
+
+ // Emit a GST_EVENT_CUSTOM_DOWNSTREAM_STICKY event to let GStreamer know about the HTTP headers sent and received.
+ GstStructure* httpHeaders = gst_structure_new_empty("http-headers");
+ gst_structure_set(httpHeaders, "uri", G_TYPE_STRING, priv->originalURI.data(), nullptr);
+ if (!priv->redirectedURI.isNull())
+ gst_structure_set(httpHeaders, "redirection-uri", G_TYPE_STRING, priv->redirectedURI.data(), nullptr);
+ GUniquePtr<GstStructure> headers(gst_structure_new_empty("request-headers"));
+ for (const auto& header : m_request.httpHeaderFields())
+ gst_structure_set(headers.get(), header.key.utf8().data(), G_TYPE_STRING, header.value.utf8().data(), nullptr);
+ gst_structure_set(httpHeaders, "request-headers", GST_TYPE_STRUCTURE, headers.get(), nullptr);
+ headers.reset(gst_structure_new_empty("response-headers"));
+ for (const auto& header : response.httpHeaderFields())
+ gst_structure_set(headers.get(), header.key.utf8().data(), G_TYPE_STRING, header.value.utf8().data(), nullptr);
+ gst_structure_set(httpHeaders, "response-headers", GST_TYPE_STRUCTURE, headers.get(), nullptr);
+ gst_pad_push_event(GST_BASE_SRC_PAD(priv->appsrc), gst_event_new_custom(GST_EVENT_CUSTOM_DOWNSTREAM_STICKY, httpHeaders));
}
void StreamingClient::handleDataReceived(const char* data, int length)
{
- WebKitWebSrc* src = WEBKIT_WEB_SRC(m_src);
+ WebKitWebSrc* src = WEBKIT_WEB_SRC(m_src.get());
WebKitWebSrcPrivate* priv = src->priv;
- WTF::GMutexLocker locker(GST_OBJECT_GET_LOCK(src));
+ WTF::GMutexLocker<GMutex> locker(*GST_OBJECT_GET_LOCK(src));
GST_LOG_OBJECT(src, "Have %lld bytes of data", priv->buffer ? static_cast<long long>(gst_buffer_get_size(priv->buffer.get())) : length);
@@ -975,7 +988,7 @@ void StreamingClient::handleDataReceived(const char* data, int length)
if (priv->buffer)
unmapGstBuffer(priv->buffer.get());
- if (priv->seekID) {
+ if (priv->isSeeking) {
GST_DEBUG_OBJECT(src, "Seek in progress, ignoring data");
priv->buffer.clear();
return;
@@ -1025,111 +1038,135 @@ void StreamingClient::handleDataReceived(const char* data, int length)
GstFlowReturn ret = gst_app_src_push_buffer(priv->appsrc, priv->buffer.leakRef());
if (ret != GST_FLOW_OK && ret != GST_FLOW_EOS)
- GST_ELEMENT_ERROR(src, CORE, FAILED, (0), (0));
+ GST_ELEMENT_ERROR(src, CORE, FAILED, (nullptr), (nullptr));
}
void StreamingClient::handleNotifyFinished()
{
- WebKitWebSrc* src = WEBKIT_WEB_SRC(m_src);
+ WebKitWebSrc* src = WEBKIT_WEB_SRC(m_src.get());
WebKitWebSrcPrivate* priv = src->priv;
GST_DEBUG_OBJECT(src, "Have EOS");
- WTF::GMutexLocker locker(GST_OBJECT_GET_LOCK(src));
- if (!priv->seekID) {
+ WTF::GMutexLocker<GMutex> locker(*GST_OBJECT_GET_LOCK(src));
+ if (!priv->isSeeking) {
locker.unlock();
gst_app_src_end_of_stream(priv->appsrc);
}
}
-CachedResourceStreamingClient::CachedResourceStreamingClient(WebKitWebSrc* src, CachedResourceLoader* resourceLoader, const ResourceRequest& request, MediaPlayerClient::CORSMode corsMode)
- : StreamingClient(src)
+CachedResourceStreamingClient::CachedResourceStreamingClient(WebKitWebSrc* src, ResourceRequest&& request)
+ : StreamingClient(src, WTFMove(request))
{
- DataBufferingPolicy bufferingPolicy = request.url().protocolIs("blob") ? BufferData : DoNotBufferData;
- RequestOriginPolicy corsPolicy = corsMode != MediaPlayerClient::Unspecified ? PotentiallyCrossOriginEnabled : UseDefaultOriginRestrictionsForType;
- StoredCredentials allowCredentials = corsMode == MediaPlayerClient::UseCredentials ? AllowStoredCredentials : DoNotAllowStoredCredentials;
- ResourceLoaderOptions options(SendCallbacks, DoNotSniffContent, bufferingPolicy, allowCredentials, DoNotAskClientForCrossOriginCredentials, DoSecurityCheck, corsPolicy);
-
- CachedResourceRequest cacheRequest(request, options);
-
- if (corsMode != MediaPlayerClient::Unspecified) {
- m_origin = resourceLoader->document() ? resourceLoader->document()->securityOrigin() : nullptr;
- updateRequestForAccessControl(cacheRequest.mutableResourceRequest(), m_origin.get(), allowCredentials);
- }
-
- // TODO: Decide whether to use preflight mode for cross-origin requests (see http://wkbug.com/131484).
- m_resource = resourceLoader->requestRawResource(cacheRequest);
- if (m_resource)
- m_resource->addClient(this);
}
CachedResourceStreamingClient::~CachedResourceStreamingClient()
{
- if (m_resource) {
- m_resource->removeClient(this);
- m_resource = 0;
- }
}
-bool CachedResourceStreamingClient::loadFailed() const
+#if USE(SOUP)
+char* CachedResourceStreamingClient::getOrCreateReadBuffer(PlatformMediaResource&, size_t requestedSize, size_t& actualSize)
{
- return !m_resource;
+ return createReadBuffer(requestedSize, actualSize);
}
+#endif
-void CachedResourceStreamingClient::setDefersLoading(bool defers)
+void CachedResourceStreamingClient::responseReceived(PlatformMediaResource&, const ResourceResponse& response)
{
- if (m_resource)
- m_resource->setDefersLoading(defers);
+ WebKitWebSrcPrivate* priv = WEBKIT_WEB_SRC(m_src.get())->priv;
+ priv->didPassAccessControlCheck = priv->resource->didPassAccessControlCheck();
+ handleResponseReceived(response);
}
-char* CachedResourceStreamingClient::getOrCreateReadBuffer(CachedResource*, size_t requestedSize, size_t& actualSize)
+void CachedResourceStreamingClient::dataReceived(PlatformMediaResource&, const char* data, int length)
{
- return createReadBuffer(requestedSize, actualSize);
+ handleDataReceived(data, length);
}
-void CachedResourceStreamingClient::responseReceived(CachedResource* resource, const ResourceResponse& response)
+void CachedResourceStreamingClient::accessControlCheckFailed(PlatformMediaResource&, const ResourceError& error)
{
- CORSAccessCheckResult corsAccessCheck = CORSNoCheck;
- if (m_origin)
- corsAccessCheck = (m_origin->canRequest(response.url()) || resource->passesAccessControlCheck(m_origin.get())) ? CORSSuccess : CORSFailure;
- handleResponseReceived(response, corsAccessCheck);
+ WebKitWebSrc* src = WEBKIT_WEB_SRC(m_src.get());
+ GST_ELEMENT_ERROR(src, RESOURCE, READ, ("%s", error.localizedDescription().utf8().data()), (nullptr));
+ gst_app_src_end_of_stream(src->priv->appsrc);
+ webKitWebSrcStop(src);
}
-void CachedResourceStreamingClient::dataReceived(CachedResource*, const char* data, int length)
+void CachedResourceStreamingClient::loadFailed(PlatformMediaResource&, const ResourceError& error)
{
- handleDataReceived(data, length);
+ WebKitWebSrc* src = WEBKIT_WEB_SRC(m_src.get());
+
+ if (!error.isCancellation()) {
+ GST_ERROR_OBJECT(src, "Have failure: %s", error.localizedDescription().utf8().data());
+ GST_ELEMENT_ERROR(src, RESOURCE, FAILED, ("%s", error.localizedDescription().utf8().data()), (nullptr));
+ }
+
+ gst_app_src_end_of_stream(src->priv->appsrc);
}
-void CachedResourceStreamingClient::notifyFinished(CachedResource* resource)
+void CachedResourceStreamingClient::loadFinished(PlatformMediaResource&)
{
- if (resource->loadFailedOrCanceled()) {
- WebKitWebSrc* src = WEBKIT_WEB_SRC(m_src);
+ handleNotifyFinished();
+}
- if (!resource->wasCanceled()) {
- const ResourceError& error = resource->resourceError();
- GST_ERROR_OBJECT(src, "Have failure: %s", error.localizedDescription().utf8().data());
- GST_ELEMENT_ERROR(src, RESOURCE, FAILED, ("%s", error.localizedDescription().utf8().data()), (0));
+ResourceHandleStreamingClient::ResourceHandleStreamingClient(WebKitWebSrc* src, ResourceRequest&& request)
+ : StreamingClient(src, WTFMove(request))
+{
+ LockHolder locker(m_initializeRunLoopConditionMutex);
+ m_thread = createThread("ResourceHandleStreamingClient", [this] {
+ {
+ LockHolder locker(m_initializeRunLoopConditionMutex);
+ m_runLoop = &RunLoop::current();
+#if USE(SOUP)
+ m_session = std::make_unique<SoupNetworkSession>();
+ m_resource = ResourceHandle::create(*m_session, m_request, this, true, false);
+#else
+ // FIXME: This create will hit an assert in debug builds. See https://bugs.webkit.org/show_bug.cgi?id=167003.
+ m_resource = ResourceHandle::create(nullptr, m_request, this, true, false);
+#endif
+ m_initializeRunLoopCondition.notifyOne();
}
- gst_app_src_end_of_stream(src->priv->appsrc);
- return;
- }
+ if (!m_resource)
+ return;
- handleNotifyFinished();
+ m_runLoop->dispatch([this] { m_resource->setDefersLoading(false); });
+ m_runLoop->run();
+ });
+ m_initializeRunLoopCondition.wait(m_initializeRunLoopConditionMutex);
}
-ResourceHandleStreamingClient::ResourceHandleStreamingClient(WebKitWebSrc* src, const ResourceRequest& request)
- : StreamingClient(src)
+ResourceHandleStreamingClient::~ResourceHandleStreamingClient()
{
- m_resource = ResourceHandle::create(0 /*context*/, request, this, false, false);
+ if (m_thread) {
+ detachThread(m_thread);
+ m_thread = 0;
+ }
}
-ResourceHandleStreamingClient::~ResourceHandleStreamingClient()
+void ResourceHandleStreamingClient::cleanupAndStopRunLoop()
+{
+ m_resource->clearClient();
+ m_resource->cancel();
+ m_resource = nullptr;
+#if USE(SOUP)
+ m_session = nullptr;
+#endif
+ m_runLoop->stop();
+}
+
+void ResourceHandleStreamingClient::invalidate()
{
- if (m_resource) {
- m_resource->cancel();
- m_resource.release();
- m_resource = 0;
+ if (m_runLoop == &RunLoop::current()) {
+ cleanupAndStopRunLoop();
+ return;
}
+
+ LockHolder locker(m_terminateRunLoopConditionMutex);
+ m_runLoop->dispatch([this, protectedThis = makeRef(*this)] {
+ cleanupAndStopRunLoop();
+ LockHolder locker(m_terminateRunLoopConditionMutex);
+ m_terminateRunLoopCondition.notifyOne();
+ });
+ m_terminateRunLoopCondition.wait(m_terminateRunLoopConditionMutex);
}
bool ResourceHandleStreamingClient::loadFailed() const
@@ -1139,31 +1176,40 @@ bool ResourceHandleStreamingClient::loadFailed() const
void ResourceHandleStreamingClient::setDefersLoading(bool defers)
{
- if (m_resource)
- m_resource->setDefersLoading(defers);
+ m_runLoop->dispatch([this, protectedThis = makeRef(*this), defers] {
+ if (m_resource)
+ m_resource->setDefersLoading(defers);
+ });
}
+#if USE(SOUP)
char* ResourceHandleStreamingClient::getOrCreateReadBuffer(size_t requestedSize, size_t& actualSize)
{
return createReadBuffer(requestedSize, actualSize);
}
+#endif
-void ResourceHandleStreamingClient::willSendRequest(ResourceHandle*, ResourceRequest&, const ResourceResponse&)
+ResourceRequest ResourceHandleStreamingClient::willSendRequest(ResourceHandle*, ResourceRequest&& request, ResourceResponse&&)
{
+ return WTFMove(request);
}
-void ResourceHandleStreamingClient::didReceiveResponse(ResourceHandle*, const ResourceResponse& response)
+void ResourceHandleStreamingClient::didReceiveResponse(ResourceHandle*, ResourceResponse&& response)
{
- handleResponseReceived(response, CORSNoCheck);
+ if (m_resource)
+ handleResponseReceived(response);
}
-void ResourceHandleStreamingClient::didReceiveData(ResourceHandle*, const char* data, unsigned length, int)
+void ResourceHandleStreamingClient::didReceiveData(ResourceHandle*, const char* /* data */, unsigned /* length */, int)
{
ASSERT_NOT_REACHED();
}
-void ResourceHandleStreamingClient::didReceiveBuffer(ResourceHandle*, PassRefPtr<SharedBuffer> buffer, int /* encodedLength */)
+void ResourceHandleStreamingClient::didReceiveBuffer(ResourceHandle*, Ref<SharedBuffer>&& buffer, int /* encodedLength */)
{
+ if (!m_resource)
+ return;
+
// This pattern is suggested by SharedBuffer.h.
const char* segment;
unsigned position = 0;
@@ -1175,44 +1221,45 @@ void ResourceHandleStreamingClient::didReceiveBuffer(ResourceHandle*, PassRefPtr
void ResourceHandleStreamingClient::didFinishLoading(ResourceHandle*, double)
{
- handleNotifyFinished();
+ if (m_resource)
+ handleNotifyFinished();
}
void ResourceHandleStreamingClient::didFail(ResourceHandle*, const ResourceError& error)
{
- WebKitWebSrc* src = WEBKIT_WEB_SRC(m_src);
+ WebKitWebSrc* src = WEBKIT_WEB_SRC(m_src.get());
GST_ERROR_OBJECT(src, "Have failure: %s", error.localizedDescription().utf8().data());
- GST_ELEMENT_ERROR(src, RESOURCE, FAILED, ("%s", error.localizedDescription().utf8().data()), (0));
+ GST_ELEMENT_ERROR(src, RESOURCE, FAILED, ("%s", error.localizedDescription().utf8().data()), (nullptr));
gst_app_src_end_of_stream(src->priv->appsrc);
}
void ResourceHandleStreamingClient::wasBlocked(ResourceHandle*)
{
- WebKitWebSrc* src = WEBKIT_WEB_SRC(m_src);
+ WebKitWebSrc* src = WEBKIT_WEB_SRC(m_src.get());
GUniquePtr<gchar> uri;
GST_ERROR_OBJECT(src, "Request was blocked");
- WTF::GMutexLocker locker(GST_OBJECT_GET_LOCK(src));
- uri.reset(g_strdup(src->priv->uri));
+ WTF::GMutexLocker<GMutex> locker(*GST_OBJECT_GET_LOCK(src));
+ uri.reset(g_strdup(src->priv->originalURI.data()));
locker.unlock();
- GST_ELEMENT_ERROR(src, RESOURCE, OPEN_READ, ("Access to \"%s\" was blocked", uri.get()), (0));
+ GST_ELEMENT_ERROR(src, RESOURCE, OPEN_READ, ("Access to \"%s\" was blocked", uri.get()), (nullptr));
}
void ResourceHandleStreamingClient::cannotShowURL(ResourceHandle*)
{
- WebKitWebSrc* src = WEBKIT_WEB_SRC(m_src);
+ WebKitWebSrc* src = WEBKIT_WEB_SRC(m_src.get());
GUniquePtr<gchar> uri;
GST_ERROR_OBJECT(src, "Cannot show URL");
- WTF::GMutexLocker locker(GST_OBJECT_GET_LOCK(src));
- uri.reset(g_strdup(src->priv->uri));
+ WTF::GMutexLocker<GMutex> locker(*GST_OBJECT_GET_LOCK(src));
+ uri.reset(g_strdup(src->priv->originalURI.data()));
locker.unlock();
- GST_ELEMENT_ERROR(src, RESOURCE, OPEN_READ, ("Can't show \"%s\"", uri.get()), (0));
+ GST_ELEMENT_ERROR(src, RESOURCE, OPEN_READ, ("Can't show \"%s\"", uri.get()), (nullptr));
}
#endif // USE(GSTREAMER)
diff --git a/Source/WebCore/platform/graphics/gstreamer/eme/WebKitClearKeyDecryptorGStreamer.cpp b/Source/WebCore/platform/graphics/gstreamer/eme/WebKitClearKeyDecryptorGStreamer.cpp
new file mode 100644
index 000000000..dc697089f
--- /dev/null
+++ b/Source/WebCore/platform/graphics/gstreamer/eme/WebKitClearKeyDecryptorGStreamer.cpp
@@ -0,0 +1,260 @@
+/* GStreamer ClearKey common encryption decryptor
+ *
+ * Copyright (C) 2016 Metrological
+ * Copyright (C) 2016 Igalia S.L
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Suite 500,
+ * Boston, MA 02110-1335, USA.
+ */
+
+#include "config.h"
+#include "WebKitClearKeyDecryptorGStreamer.h"
+
+#if (ENABLE(LEGACY_ENCRYPTED_MEDIA) || ENABLE(ENCRYPTED_MEDIA)) && USE(GSTREAMER)
+
+#include "GRefPtrGStreamer.h"
+#include <gcrypt.h>
+#include <gst/base/gstbytereader.h>
+#include <wtf/RunLoop.h>
+
+#define CLEARKEY_SIZE 16
+
+#define WEBKIT_MEDIA_CK_DECRYPT_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE((obj), WEBKIT_TYPE_MEDIA_CK_DECRYPT, WebKitMediaClearKeyDecryptPrivate))
+struct _WebKitMediaClearKeyDecryptPrivate {
+ GRefPtr<GstBuffer> key;
+ gcry_cipher_hd_t handle;
+};
+
+static void webKitMediaClearKeyDecryptorFinalize(GObject*);
+static gboolean webKitMediaClearKeyDecryptorHandleKeyResponse(WebKitMediaCommonEncryptionDecrypt* self, GstEvent*);
+static gboolean webKitMediaClearKeyDecryptorSetupCipher(WebKitMediaCommonEncryptionDecrypt*);
+static gboolean webKitMediaClearKeyDecryptorDecrypt(WebKitMediaCommonEncryptionDecrypt*, GstBuffer* iv, GstBuffer* sample, unsigned subSamplesCount, GstBuffer* subSamples);
+static void webKitMediaClearKeyDecryptorReleaseCipher(WebKitMediaCommonEncryptionDecrypt*);
+
+GST_DEBUG_CATEGORY_STATIC(webkit_media_clear_key_decrypt_debug_category);
+#define GST_CAT_DEFAULT webkit_media_clear_key_decrypt_debug_category
+
+static GstStaticPadTemplate sinkTemplate = GST_STATIC_PAD_TEMPLATE("sink",
+ GST_PAD_SINK,
+ GST_PAD_ALWAYS,
+ GST_STATIC_CAPS("application/x-cenc, original-media-type=(string)video/x-h264, protection-system=(string)" CLEAR_KEY_PROTECTION_SYSTEM_UUID "; "
+ "application/x-cenc, original-media-type=(string)audio/mpeg, protection-system=(string)" CLEAR_KEY_PROTECTION_SYSTEM_UUID));
+
+static GstStaticPadTemplate srcTemplate = GST_STATIC_PAD_TEMPLATE("src",
+ GST_PAD_SRC,
+ GST_PAD_ALWAYS,
+ GST_STATIC_CAPS("video/x-h264; audio/mpeg"));
+
+#define webkit_media_clear_key_decrypt_parent_class parent_class
+G_DEFINE_TYPE(WebKitMediaClearKeyDecrypt, webkit_media_clear_key_decrypt, WEBKIT_TYPE_MEDIA_CENC_DECRYPT);
+
+static void webkit_media_clear_key_decrypt_class_init(WebKitMediaClearKeyDecryptClass* klass)
+{
+ GObjectClass* gobjectClass = G_OBJECT_CLASS(klass);
+ gobjectClass->finalize = webKitMediaClearKeyDecryptorFinalize;
+
+ GstElementClass* elementClass = GST_ELEMENT_CLASS(klass);
+ gst_element_class_add_pad_template(elementClass, gst_static_pad_template_get(&sinkTemplate));
+ gst_element_class_add_pad_template(elementClass, gst_static_pad_template_get(&srcTemplate));
+
+ gst_element_class_set_static_metadata(elementClass,
+ "Decrypt content encrypted using ISOBMFF ClearKey Common Encryption",
+ GST_ELEMENT_FACTORY_KLASS_DECRYPTOR,
+ "Decrypts media that has been encrypted using ISOBMFF ClearKey Common Encryption.",
+ "Philippe Normand <philn@igalia.com>");
+
+ GST_DEBUG_CATEGORY_INIT(webkit_media_clear_key_decrypt_debug_category,
+ "webkitclearkey", 0, "ClearKey decryptor");
+
+ WebKitMediaCommonEncryptionDecryptClass* cencClass = WEBKIT_MEDIA_CENC_DECRYPT_CLASS(klass);
+ cencClass->protectionSystemId = CLEAR_KEY_PROTECTION_SYSTEM_UUID;
+ cencClass->handleKeyResponse = GST_DEBUG_FUNCPTR(webKitMediaClearKeyDecryptorHandleKeyResponse);
+ cencClass->setupCipher = GST_DEBUG_FUNCPTR(webKitMediaClearKeyDecryptorSetupCipher);
+ cencClass->decrypt = GST_DEBUG_FUNCPTR(webKitMediaClearKeyDecryptorDecrypt);
+ cencClass->releaseCipher = GST_DEBUG_FUNCPTR(webKitMediaClearKeyDecryptorReleaseCipher);
+
+ g_type_class_add_private(klass, sizeof(WebKitMediaClearKeyDecryptPrivate));
+}
+
+static void webkit_media_clear_key_decrypt_init(WebKitMediaClearKeyDecrypt* self)
+{
+ WebKitMediaClearKeyDecryptPrivate* priv = WEBKIT_MEDIA_CK_DECRYPT_GET_PRIVATE(self);
+
+ if (!gcry_check_version(GCRYPT_VERSION))
+ GST_ERROR_OBJECT(self, "Libgcrypt failed to initialize");
+
+ // Allocate a pool of 16k secure memory. This make the secure memory
+ // available and also drops privileges where needed.
+ gcry_control(GCRYCTL_INIT_SECMEM, 16384, 0);
+
+ gcry_control(GCRYCTL_INITIALIZATION_FINISHED, 0);
+
+ self->priv = priv;
+ new (priv) WebKitMediaClearKeyDecryptPrivate();
+}
+
+static void webKitMediaClearKeyDecryptorFinalize(GObject* object)
+{
+ WebKitMediaClearKeyDecrypt* self = WEBKIT_MEDIA_CK_DECRYPT(object);
+ WebKitMediaClearKeyDecryptPrivate* priv = self->priv;
+
+ priv->~WebKitMediaClearKeyDecryptPrivate();
+
+ GST_CALL_PARENT(G_OBJECT_CLASS, finalize, (object));
+}
+
+static gboolean webKitMediaClearKeyDecryptorHandleKeyResponse(WebKitMediaCommonEncryptionDecrypt* self, GstEvent* event)
+{
+ WebKitMediaClearKeyDecryptPrivate* priv = WEBKIT_MEDIA_CK_DECRYPT_GET_PRIVATE(WEBKIT_MEDIA_CK_DECRYPT(self));
+ const GstStructure* structure = gst_event_get_structure(event);
+
+ if (!gst_structure_has_name(structure, "drm-cipher"))
+ return FALSE;
+
+ const GValue* value = gst_structure_get_value(structure, "key");
+ priv->key.clear();
+ priv->key = adoptGRef(gst_buffer_copy(gst_value_get_buffer(value)));
+ return TRUE;
+}
+
+static gboolean webKitMediaClearKeyDecryptorSetupCipher(WebKitMediaCommonEncryptionDecrypt* self)
+{
+ WebKitMediaClearKeyDecryptPrivate* priv = WEBKIT_MEDIA_CK_DECRYPT_GET_PRIVATE(WEBKIT_MEDIA_CK_DECRYPT(self));
+ gcry_error_t error;
+
+ ASSERT(priv->key);
+ if (!priv->key) {
+ GST_ERROR_OBJECT(self, "Decryption key not provided");
+ return false;
+ }
+
+ error = gcry_cipher_open(&(priv->handle), GCRY_CIPHER_AES128, GCRY_CIPHER_MODE_CTR, GCRY_CIPHER_SECURE);
+ if (error) {
+ GST_ERROR_OBJECT(self, "Failed to create AES 128 CTR cipher handle: %s", gpg_strerror(error));
+ return false;
+ }
+
+ GstMapInfo keyMap;
+ if (!gst_buffer_map(priv->key.get(), &keyMap, GST_MAP_READ)) {
+ GST_ERROR_OBJECT(self, "Failed to map decryption key");
+ return false;
+ }
+
+ ASSERT(keyMap.size == CLEARKEY_SIZE);
+ error = gcry_cipher_setkey(priv->handle, keyMap.data, keyMap.size);
+ gst_buffer_unmap(priv->key.get(), &keyMap);
+ if (error) {
+ GST_ERROR_OBJECT(self, "gcry_cipher_setkey failed: %s", gpg_strerror(error));
+ return false;
+ }
+
+ return true;
+}
+
+static gboolean webKitMediaClearKeyDecryptorDecrypt(WebKitMediaCommonEncryptionDecrypt* self, GstBuffer* ivBuffer, GstBuffer* buffer, unsigned subSampleCount, GstBuffer* subSamplesBuffer)
+{
+ GstMapInfo ivMap;
+ if (!gst_buffer_map(ivBuffer, &ivMap, GST_MAP_READ)) {
+ GST_ERROR_OBJECT(self, "Failed to map IV");
+ return false;
+ }
+
+ uint8_t ctr[CLEARKEY_SIZE];
+ if (ivMap.size == 8) {
+ memset(ctr + 8, 0, 8);
+ memcpy(ctr, ivMap.data, 8);
+ } else {
+ ASSERT(ivMap.size == CLEARKEY_SIZE);
+ memcpy(ctr, ivMap.data, CLEARKEY_SIZE);
+ }
+ gst_buffer_unmap(ivBuffer, &ivMap);
+
+ WebKitMediaClearKeyDecryptPrivate* priv = WEBKIT_MEDIA_CK_DECRYPT_GET_PRIVATE(WEBKIT_MEDIA_CK_DECRYPT(self));
+ gcry_error_t error = gcry_cipher_setctr(priv->handle, ctr, CLEARKEY_SIZE);
+ if (error) {
+ GST_ERROR_OBJECT(self, "gcry_cipher_setctr failed: %s", gpg_strerror(error));
+ return false;
+ }
+
+ GstMapInfo map;
+ gboolean bufferMapped = gst_buffer_map(buffer, &map, static_cast<GstMapFlags>(GST_MAP_READWRITE));
+ if (!bufferMapped) {
+ GST_ERROR_OBJECT(self, "Failed to map buffer");
+ return false;
+ }
+
+ GstMapInfo subSamplesMap;
+ gboolean subsamplesBufferMapped = gst_buffer_map(subSamplesBuffer, &subSamplesMap, GST_MAP_READ);
+ if (!subsamplesBufferMapped) {
+ GST_ERROR_OBJECT(self, "Failed to map subsample buffer");
+ gst_buffer_unmap(buffer, &map);
+ return false;
+ }
+
+ GstByteReader* reader = gst_byte_reader_new(subSamplesMap.data, subSamplesMap.size);
+ unsigned position = 0;
+ unsigned sampleIndex = 0;
+
+ GST_DEBUG_OBJECT(self, "position: %d, size: %zu", position, map.size);
+
+ while (position < map.size) {
+ guint16 nBytesClear = 0;
+ guint32 nBytesEncrypted = 0;
+
+ if (sampleIndex < subSampleCount) {
+ if (!gst_byte_reader_get_uint16_be(reader, &nBytesClear)
+ || !gst_byte_reader_get_uint32_be(reader, &nBytesEncrypted)) {
+ GST_DEBUG_OBJECT(self, "unsupported");
+ gst_byte_reader_free(reader);
+ gst_buffer_unmap(buffer, &map);
+ gst_buffer_unmap(subSamplesBuffer, &subSamplesMap);
+ return false;
+ }
+
+ sampleIndex++;
+ } else {
+ nBytesClear = 0;
+ nBytesEncrypted = map.size - position;
+ }
+
+ GST_TRACE_OBJECT(self, "%d bytes clear (todo=%zu)", nBytesClear, map.size - position);
+ position += nBytesClear;
+ if (nBytesEncrypted) {
+ GST_TRACE_OBJECT(self, "%d bytes encrypted (todo=%zu)", nBytesEncrypted, map.size - position);
+ error = gcry_cipher_decrypt(priv->handle, map.data + position, nBytesEncrypted, 0, 0);
+ if (error) {
+ GST_ERROR_OBJECT(self, "decryption failed: %s", gpg_strerror(error));
+ gst_byte_reader_free(reader);
+ gst_buffer_unmap(buffer, &map);
+ gst_buffer_unmap(subSamplesBuffer, &subSamplesMap);
+ return false;
+ }
+ position += nBytesEncrypted;
+ }
+ }
+
+ gst_byte_reader_free(reader);
+ gst_buffer_unmap(buffer, &map);
+ gst_buffer_unmap(subSamplesBuffer, &subSamplesMap);
+ return true;
+}
+
+static void webKitMediaClearKeyDecryptorReleaseCipher(WebKitMediaCommonEncryptionDecrypt* self)
+{
+ WebKitMediaClearKeyDecryptPrivate* priv = WEBKIT_MEDIA_CK_DECRYPT_GET_PRIVATE(WEBKIT_MEDIA_CK_DECRYPT(self));
+ gcry_cipher_close(priv->handle);
+}
+
+#endif // (ENABLE(LEGACY_ENCRYPTED_MEDIA) || ENABLE(ENCRYPTED_MEDIA)) && USE(GSTREAMER)
diff --git a/Source/WebCore/platform/graphics/gstreamer/eme/WebKitClearKeyDecryptorGStreamer.h b/Source/WebCore/platform/graphics/gstreamer/eme/WebKitClearKeyDecryptorGStreamer.h
new file mode 100644
index 000000000..30cfa299b
--- /dev/null
+++ b/Source/WebCore/platform/graphics/gstreamer/eme/WebKitClearKeyDecryptorGStreamer.h
@@ -0,0 +1,57 @@
+/* GStreamer ClearKey common encryption decryptor
+ *
+ * Copyright (C) 2016 Metrological
+ * Copyright (C) 2016 Igalia S.L
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#pragma once
+
+#if (ENABLE(LEGACY_ENCRYPTED_MEDIA) || ENABLE(ENCRYPTED_MEDIA)) && USE(GSTREAMER)
+
+#include "WebKitCommonEncryptionDecryptorGStreamer.h"
+
+#define CLEAR_KEY_PROTECTION_SYSTEM_UUID "58147ec8-0423-4659-92e6-f52c5ce8c3cc"
+#define CLEAR_KEY_PROTECTION_SYSTEM_ID "org.w3.clearkey"
+
+G_BEGIN_DECLS
+
+#define WEBKIT_TYPE_MEDIA_CK_DECRYPT (webkit_media_clear_key_decrypt_get_type())
+#define WEBKIT_MEDIA_CK_DECRYPT(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), WEBKIT_TYPE_MEDIA_CK_DECRYPT, WebKitMediaClearKeyDecrypt))
+#define WEBKIT_MEDIA_CK_DECRYPT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), WEBKIT_TYPE_MEDIA_CK_DECRYPT, WebKitMediaClearKeyDecryptClass))
+#define WEBKIT_IS_MEDIA_CK_DECRYPT(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), WEBKIT_TYPE_MEDIA_CK_DECRYPT))
+#define WEBKIT_IS_MEDIA_CK_DECRYPT_CLASS(obj) (G_TYPE_CHECK_CLASS_TYPE((klass), WEBKIT_TYPE_MEDIA_CK_DECRYPT))
+
+typedef struct _WebKitMediaClearKeyDecrypt WebKitMediaClearKeyDecrypt;
+typedef struct _WebKitMediaClearKeyDecryptClass WebKitMediaClearKeyDecryptClass;
+typedef struct _WebKitMediaClearKeyDecryptPrivate WebKitMediaClearKeyDecryptPrivate;
+
+GType webkit_media_clear_key_decrypt_get_type(void);
+
+struct _WebKitMediaClearKeyDecrypt {
+ WebKitMediaCommonEncryptionDecrypt parent;
+
+ WebKitMediaClearKeyDecryptPrivate* priv;
+};
+
+struct _WebKitMediaClearKeyDecryptClass {
+ WebKitMediaCommonEncryptionDecryptClass parentClass;
+};
+
+G_END_DECLS
+
+#endif // (ENABLE(LEGACY_ENCRYPTED_MEDIA) || ENABLE(ENCRYPTED_MEDIA)) && USE(GSTREAMER)
diff --git a/Source/WebCore/platform/graphics/gstreamer/eme/WebKitCommonEncryptionDecryptorGStreamer.cpp b/Source/WebCore/platform/graphics/gstreamer/eme/WebKitCommonEncryptionDecryptorGStreamer.cpp
new file mode 100644
index 000000000..389808050
--- /dev/null
+++ b/Source/WebCore/platform/graphics/gstreamer/eme/WebKitCommonEncryptionDecryptorGStreamer.cpp
@@ -0,0 +1,362 @@
+/* GStreamer ClearKey common encryption decryptor
+ *
+ * Copyright (C) 2013 YouView TV Ltd. <alex.ashley@youview.com>
+ * Copyright (C) 2016 Metrological
+ * Copyright (C) 2016 Igalia S.L
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Suite 500,
+ * Boston, MA 02110-1335, USA.
+ */
+
+#include "config.h"
+#include "WebKitCommonEncryptionDecryptorGStreamer.h"
+
+#if (ENABLE(LEGACY_ENCRYPTED_MEDIA) || ENABLE(ENCRYPTED_MEDIA)) && USE(GSTREAMER)
+
+#include "GRefPtrGStreamer.h"
+#include <wtf/Condition.h>
+#include <wtf/RunLoop.h>
+
+#define WEBKIT_MEDIA_CENC_DECRYPT_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE((obj), WEBKIT_TYPE_MEDIA_CENC_DECRYPT, WebKitMediaCommonEncryptionDecryptPrivate))
+struct _WebKitMediaCommonEncryptionDecryptPrivate {
+ GRefPtr<GstEvent> protectionEvent;
+
+ bool keyReceived;
+ Lock mutex;
+ Condition condition;
+};
+
+static GstStateChangeReturn webKitMediaCommonEncryptionDecryptorChangeState(GstElement*, GstStateChange transition);
+static void webKitMediaCommonEncryptionDecryptorFinalize(GObject*);
+static GstCaps* webkitMediaCommonEncryptionDecryptTransformCaps(GstBaseTransform*, GstPadDirection, GstCaps*, GstCaps*);
+static GstFlowReturn webkitMediaCommonEncryptionDecryptTransformInPlace(GstBaseTransform*, GstBuffer*);
+static gboolean webkitMediaCommonEncryptionDecryptSinkEventHandler(GstBaseTransform*, GstEvent*);
+
+static gboolean webKitMediaCommonEncryptionDecryptDefaultSetupCipher(WebKitMediaCommonEncryptionDecrypt*);
+static void webKitMediaCommonEncryptionDecryptDefaultReleaseCipher(WebKitMediaCommonEncryptionDecrypt*);
+
+GST_DEBUG_CATEGORY_STATIC(webkit_media_common_encryption_decrypt_debug_category);
+#define GST_CAT_DEFAULT webkit_media_common_encryption_decrypt_debug_category
+
+#define webkit_media_common_encryption_decrypt_parent_class parent_class
+G_DEFINE_TYPE(WebKitMediaCommonEncryptionDecrypt, webkit_media_common_encryption_decrypt, GST_TYPE_BASE_TRANSFORM);
+
+static void webkit_media_common_encryption_decrypt_class_init(WebKitMediaCommonEncryptionDecryptClass* klass)
+{
+ GObjectClass* gobjectClass = G_OBJECT_CLASS(klass);
+ gobjectClass->finalize = webKitMediaCommonEncryptionDecryptorFinalize;
+
+ GST_DEBUG_CATEGORY_INIT(webkit_media_common_encryption_decrypt_debug_category,
+ "webkitcenc", 0, "Common Encryption base class");
+
+ GstElementClass* elementClass = GST_ELEMENT_CLASS(klass);
+ elementClass->change_state = GST_DEBUG_FUNCPTR(webKitMediaCommonEncryptionDecryptorChangeState);
+
+ GstBaseTransformClass* baseTransformClass = GST_BASE_TRANSFORM_CLASS(klass);
+ baseTransformClass->transform_ip = GST_DEBUG_FUNCPTR(webkitMediaCommonEncryptionDecryptTransformInPlace);
+ baseTransformClass->transform_caps = GST_DEBUG_FUNCPTR(webkitMediaCommonEncryptionDecryptTransformCaps);
+ baseTransformClass->transform_ip_on_passthrough = FALSE;
+ baseTransformClass->sink_event = GST_DEBUG_FUNCPTR(webkitMediaCommonEncryptionDecryptSinkEventHandler);
+
+ klass->setupCipher = GST_DEBUG_FUNCPTR(webKitMediaCommonEncryptionDecryptDefaultSetupCipher);
+ klass->releaseCipher = GST_DEBUG_FUNCPTR(webKitMediaCommonEncryptionDecryptDefaultReleaseCipher);
+
+ g_type_class_add_private(klass, sizeof(WebKitMediaCommonEncryptionDecryptPrivate));
+}
+
+static void webkit_media_common_encryption_decrypt_init(WebKitMediaCommonEncryptionDecrypt* self)
+{
+ WebKitMediaCommonEncryptionDecryptPrivate* priv = WEBKIT_MEDIA_CENC_DECRYPT_GET_PRIVATE(self);
+
+ self->priv = priv;
+ new (priv) WebKitMediaCommonEncryptionDecryptPrivate();
+
+ GstBaseTransform* base = GST_BASE_TRANSFORM(self);
+ gst_base_transform_set_in_place(base, TRUE);
+ gst_base_transform_set_passthrough(base, FALSE);
+ gst_base_transform_set_gap_aware(base, FALSE);
+}
+
+static void webKitMediaCommonEncryptionDecryptorFinalize(GObject* object)
+{
+ WebKitMediaCommonEncryptionDecrypt* self = WEBKIT_MEDIA_CENC_DECRYPT(object);
+ WebKitMediaCommonEncryptionDecryptPrivate* priv = self->priv;
+
+ priv->~WebKitMediaCommonEncryptionDecryptPrivate();
+ GST_CALL_PARENT(G_OBJECT_CLASS, finalize, (object));
+}
+
+static GstCaps* webkitMediaCommonEncryptionDecryptTransformCaps(GstBaseTransform* base, GstPadDirection direction, GstCaps* caps, GstCaps* filter)
+{
+ if (direction == GST_PAD_UNKNOWN)
+ return nullptr;
+
+ GST_DEBUG_OBJECT(base, "direction: %s, caps: %" GST_PTR_FORMAT " filter: %" GST_PTR_FORMAT, (direction == GST_PAD_SRC) ? "src" : "sink", caps, filter);
+
+ GstCaps* transformedCaps = gst_caps_new_empty();
+ WebKitMediaCommonEncryptionDecrypt* self = WEBKIT_MEDIA_CENC_DECRYPT(base);
+ WebKitMediaCommonEncryptionDecryptClass* klass = WEBKIT_MEDIA_CENC_DECRYPT_GET_CLASS(self);
+
+ unsigned size = gst_caps_get_size(caps);
+ for (unsigned i = 0; i < size; ++i) {
+ GstStructure* incomingStructure = gst_caps_get_structure(caps, i);
+ GRefPtr<GstStructure> outgoingStructure = nullptr;
+
+ if (direction == GST_PAD_SINK) {
+ if (!gst_structure_has_field(incomingStructure, "original-media-type"))
+ continue;
+
+ outgoingStructure = adoptGRef(gst_structure_copy(incomingStructure));
+ gst_structure_set_name(outgoingStructure.get(), gst_structure_get_string(outgoingStructure.get(), "original-media-type"));
+
+ // Filter out the DRM related fields from the down-stream caps.
+ for (int j = 0; j < gst_structure_n_fields(incomingStructure); ++j) {
+ const gchar* fieldName = gst_structure_nth_field_name(incomingStructure, j);
+
+ if (g_str_has_prefix(fieldName, "protection-system")
+ || g_str_has_prefix(fieldName, "original-media-type"))
+ gst_structure_remove_field(outgoingStructure.get(), fieldName);
+ }
+ } else {
+ outgoingStructure = adoptGRef(gst_structure_copy(incomingStructure));
+ // Filter out the video related fields from the up-stream caps,
+ // because they are not relevant to the input caps of this element and
+ // can cause caps negotiation failures with adaptive bitrate streams.
+ for (int index = gst_structure_n_fields(outgoingStructure.get()) - 1; index >= 0; --index) {
+ const gchar* fieldName = gst_structure_nth_field_name(outgoingStructure.get(), index);
+ GST_TRACE("Check field \"%s\" for removal", fieldName);
+
+ if (!g_strcmp0(fieldName, "base-profile")
+ || !g_strcmp0(fieldName, "codec_data")
+ || !g_strcmp0(fieldName, "height")
+ || !g_strcmp0(fieldName, "framerate")
+ || !g_strcmp0(fieldName, "level")
+ || !g_strcmp0(fieldName, "pixel-aspect-ratio")
+ || !g_strcmp0(fieldName, "profile")
+ || !g_strcmp0(fieldName, "rate")
+ || !g_strcmp0(fieldName, "width")) {
+ gst_structure_remove_field(outgoingStructure.get(), fieldName);
+ GST_TRACE("Removing field %s", fieldName);
+ }
+ }
+
+ gst_structure_set(outgoingStructure.get(), "protection-system", G_TYPE_STRING, klass->protectionSystemId,
+ "original-media-type", G_TYPE_STRING, gst_structure_get_name(incomingStructure), nullptr);
+
+ gst_structure_set_name(outgoingStructure.get(), "application/x-cenc");
+ }
+
+ bool duplicate = false;
+ unsigned size = gst_caps_get_size(transformedCaps);
+
+ for (unsigned index = 0; !duplicate && index < size; ++index) {
+ GstStructure* structure = gst_caps_get_structure(transformedCaps, index);
+ if (gst_structure_is_equal(structure, outgoingStructure.get()))
+ duplicate = true;
+ }
+
+ if (!duplicate)
+ gst_caps_append_structure(transformedCaps, outgoingStructure.leakRef());
+ }
+
+ if (filter) {
+ GstCaps* intersection;
+
+ GST_DEBUG_OBJECT(base, "Using filter caps %" GST_PTR_FORMAT, filter);
+ intersection = gst_caps_intersect_full(transformedCaps, filter, GST_CAPS_INTERSECT_FIRST);
+ gst_caps_unref(transformedCaps);
+ transformedCaps = intersection;
+ }
+
+ GST_DEBUG_OBJECT(base, "returning %" GST_PTR_FORMAT, transformedCaps);
+ return transformedCaps;
+}
+
+static GstFlowReturn webkitMediaCommonEncryptionDecryptTransformInPlace(GstBaseTransform* base, GstBuffer* buffer)
+{
+ WebKitMediaCommonEncryptionDecrypt* self = WEBKIT_MEDIA_CENC_DECRYPT(base);
+ WebKitMediaCommonEncryptionDecryptPrivate* priv = WEBKIT_MEDIA_CENC_DECRYPT_GET_PRIVATE(self);
+ LockHolder locker(priv->mutex);
+
+ // The key might not have been received yet. Wait for it.
+ if (!priv->keyReceived) {
+ GST_DEBUG_OBJECT(self, "key not available yet, waiting for it");
+ if (GST_STATE(GST_ELEMENT(self)) < GST_STATE_PAUSED || (GST_STATE_TARGET(GST_ELEMENT(self)) != GST_STATE_VOID_PENDING && GST_STATE_TARGET(GST_ELEMENT(self)) < GST_STATE_PAUSED)) {
+ GST_ERROR_OBJECT(self, "can't process key requests in less than PAUSED state");
+ return GST_FLOW_NOT_SUPPORTED;
+ }
+ priv->condition.waitFor(priv->mutex, Seconds(5), [priv] {
+ return priv->keyReceived;
+ });
+ if (!priv->keyReceived) {
+ GST_ERROR_OBJECT(self, "key not available");
+ return GST_FLOW_NOT_SUPPORTED;
+ }
+ GST_DEBUG_OBJECT(self, "key received, continuing");
+ }
+
+ GstProtectionMeta* protectionMeta = reinterpret_cast<GstProtectionMeta*>(gst_buffer_get_protection_meta(buffer));
+ if (!protectionMeta) {
+ GST_ERROR_OBJECT(self, "Failed to get GstProtection metadata from buffer %p", buffer);
+ return GST_FLOW_NOT_SUPPORTED;
+ }
+
+ unsigned ivSize;
+ if (!gst_structure_get_uint(protectionMeta->info, "iv_size", &ivSize)) {
+ GST_ERROR_OBJECT(self, "Failed to get iv_size");
+ gst_buffer_remove_meta(buffer, reinterpret_cast<GstMeta*>(protectionMeta));
+ return GST_FLOW_NOT_SUPPORTED;
+ }
+
+ gboolean encrypted;
+ if (!gst_structure_get_boolean(protectionMeta->info, "encrypted", &encrypted)) {
+ GST_ERROR_OBJECT(self, "Failed to get encrypted flag");
+ gst_buffer_remove_meta(buffer, reinterpret_cast<GstMeta*>(protectionMeta));
+ return GST_FLOW_NOT_SUPPORTED;
+ }
+
+ if (!ivSize || !encrypted) {
+ gst_buffer_remove_meta(buffer, reinterpret_cast<GstMeta*>(protectionMeta));
+ return GST_FLOW_OK;
+ }
+
+ GST_DEBUG_OBJECT(base, "protection meta: %" GST_PTR_FORMAT, protectionMeta->info);
+
+ unsigned subSampleCount;
+ if (!gst_structure_get_uint(protectionMeta->info, "subsample_count", &subSampleCount)) {
+ GST_ERROR_OBJECT(self, "Failed to get subsample_count");
+ gst_buffer_remove_meta(buffer, reinterpret_cast<GstMeta*>(protectionMeta));
+ return GST_FLOW_NOT_SUPPORTED;
+ }
+
+ const GValue* value;
+ GstBuffer* subSamplesBuffer = nullptr;
+ if (subSampleCount) {
+ value = gst_structure_get_value(protectionMeta->info, "subsamples");
+ if (!value) {
+ GST_ERROR_OBJECT(self, "Failed to get subsamples");
+ gst_buffer_remove_meta(buffer, reinterpret_cast<GstMeta*>(protectionMeta));
+ return GST_FLOW_NOT_SUPPORTED;
+ }
+ subSamplesBuffer = gst_value_get_buffer(value);
+ }
+
+ WebKitMediaCommonEncryptionDecryptClass* klass = WEBKIT_MEDIA_CENC_DECRYPT_GET_CLASS(self);
+ if (!klass->setupCipher(self)) {
+ GST_ERROR_OBJECT(self, "Failed to configure cipher");
+ gst_buffer_remove_meta(buffer, reinterpret_cast<GstMeta*>(protectionMeta));
+ return GST_FLOW_NOT_SUPPORTED;
+ }
+
+ value = gst_structure_get_value(protectionMeta->info, "iv");
+ if (!value) {
+ GST_ERROR_OBJECT(self, "Failed to get IV for sample");
+ klass->releaseCipher(self);
+ gst_buffer_remove_meta(buffer, reinterpret_cast<GstMeta*>(protectionMeta));
+ return GST_FLOW_NOT_SUPPORTED;
+ }
+
+ GstBuffer* ivBuffer = gst_value_get_buffer(value);
+ GST_TRACE_OBJECT(self, "decrypting");
+ if (!klass->decrypt(self, ivBuffer, buffer, subSampleCount, subSamplesBuffer)) {
+ GST_ERROR_OBJECT(self, "Decryption failed");
+ klass->releaseCipher(self);
+ gst_buffer_remove_meta(buffer, reinterpret_cast<GstMeta*>(protectionMeta));
+ return GST_FLOW_NOT_SUPPORTED;
+ }
+
+ klass->releaseCipher(self);
+ gst_buffer_remove_meta(buffer, reinterpret_cast<GstMeta*>(protectionMeta));
+ return GST_FLOW_OK;
+}
+
+
+static gboolean webkitMediaCommonEncryptionDecryptSinkEventHandler(GstBaseTransform* trans, GstEvent* event)
+{
+ WebKitMediaCommonEncryptionDecrypt* self = WEBKIT_MEDIA_CENC_DECRYPT(trans);
+ WebKitMediaCommonEncryptionDecryptPrivate* priv = WEBKIT_MEDIA_CENC_DECRYPT_GET_PRIVATE(self);
+ WebKitMediaCommonEncryptionDecryptClass* klass = WEBKIT_MEDIA_CENC_DECRYPT_GET_CLASS(self);
+ gboolean result = FALSE;
+
+ switch (GST_EVENT_TYPE(event)) {
+ case GST_EVENT_PROTECTION: {
+ const char* systemId = nullptr;
+
+ gst_event_parse_protection(event, &systemId, nullptr, nullptr);
+ GST_TRACE_OBJECT(self, "received protection event for %s", systemId);
+
+ if (!g_strcmp0(systemId, klass->protectionSystemId)) {
+ GST_DEBUG_OBJECT(self, "sending protection event to the pipeline");
+ gst_element_post_message(GST_ELEMENT(self),
+ gst_message_new_element(GST_OBJECT(self),
+ gst_structure_new("drm-key-needed", "event", GST_TYPE_EVENT, event, nullptr)));
+ }
+
+ gst_event_unref(event);
+ result = TRUE;
+ break;
+ }
+ case GST_EVENT_CUSTOM_DOWNSTREAM_OOB: {
+ if (klass->handleKeyResponse(self, event)) {
+ GST_DEBUG_OBJECT(self, "key received");
+ priv->keyReceived = true;
+ priv->condition.notifyOne();
+ }
+
+ gst_event_unref(event);
+ result = TRUE;
+ break;
+ }
+ default:
+ result = GST_BASE_TRANSFORM_CLASS(parent_class)->sink_event(trans, event);
+ break;
+ }
+
+ return result;
+}
+
+static GstStateChangeReturn webKitMediaCommonEncryptionDecryptorChangeState(GstElement* element, GstStateChange transition)
+{
+ WebKitMediaCommonEncryptionDecrypt* self = WEBKIT_MEDIA_CENC_DECRYPT(element);
+ WebKitMediaCommonEncryptionDecryptPrivate* priv = WEBKIT_MEDIA_CENC_DECRYPT_GET_PRIVATE(self);
+
+ switch (transition) {
+ case GST_STATE_CHANGE_PAUSED_TO_READY:
+ GST_DEBUG_OBJECT(self, "PAUSED->READY");
+ priv->condition.notifyOne();
+ break;
+ default:
+ break;
+ }
+
+ GstStateChangeReturn result = GST_ELEMENT_CLASS(parent_class)->change_state(element, transition);
+
+ // Add post-transition code here.
+
+ return result;
+}
+
+
+static gboolean webKitMediaCommonEncryptionDecryptDefaultSetupCipher(WebKitMediaCommonEncryptionDecrypt*)
+{
+ return true;
+}
+
+
+static void webKitMediaCommonEncryptionDecryptDefaultReleaseCipher(WebKitMediaCommonEncryptionDecrypt*)
+{
+}
+
+#endif // (ENABLE(LEGACY_ENCRYPTED_MEDIA) || ENABLE(ENCRYPTED_MEDIA)) && USE(GSTREAMER)
diff --git a/Source/WebCore/platform/graphics/gstreamer/eme/WebKitCommonEncryptionDecryptorGStreamer.h b/Source/WebCore/platform/graphics/gstreamer/eme/WebKitCommonEncryptionDecryptorGStreamer.h
new file mode 100644
index 000000000..dcae82790
--- /dev/null
+++ b/Source/WebCore/platform/graphics/gstreamer/eme/WebKitCommonEncryptionDecryptorGStreamer.h
@@ -0,0 +1,64 @@
+/* GStreamer ClearKey common encryption decryptor
+ *
+ * Copyright (C) 2013 YouView TV Ltd. <alex.ashley@youview.com>
+ * Copyright (C) 2016 Metrological
+ * Copyright (C) 2016 Igalia S.L
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#pragma once
+
+#if (ENABLE(LEGACY_ENCRYPTED_MEDIA) || ENABLE(ENCRYPTED_MEDIA)) && USE(GSTREAMER)
+
+#include <gst/base/gstbasetransform.h>
+#include <gst/gst.h>
+
+G_BEGIN_DECLS
+
+#define WEBKIT_TYPE_MEDIA_CENC_DECRYPT (webkit_media_common_encryption_decrypt_get_type())
+#define WEBKIT_MEDIA_CENC_DECRYPT(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), WEBKIT_TYPE_MEDIA_CENC_DECRYPT, WebKitMediaCommonEncryptionDecrypt))
+#define WEBKIT_MEDIA_CENC_DECRYPT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), WEBKIT_TYPE_MEDIA_CENC_DECRYPT, WebKitMediaCommonEncryptionDecryptClass))
+#define WEBKIT_MEDIA_CENC_DECRYPT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), WEBKIT_TYPE_MEDIA_CENC_DECRYPT, WebKitMediaCommonEncryptionDecryptClass))
+
+#define WEBKIT_IS_MEDIA_CENC_DECRYPT(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), WEBKIT_TYPE_MEDIA_CENC_DECRYPT))
+#define WEBKIT_IS_MEDIA_CENC_DECRYPT_CLASS(obj) (G_TYPE_CHECK_CLASS_TYPE((klass), WEBKIT_TYPE_MEDIA_CENC_DECRYPT))
+
+typedef struct _WebKitMediaCommonEncryptionDecrypt WebKitMediaCommonEncryptionDecrypt;
+typedef struct _WebKitMediaCommonEncryptionDecryptClass WebKitMediaCommonEncryptionDecryptClass;
+typedef struct _WebKitMediaCommonEncryptionDecryptPrivate WebKitMediaCommonEncryptionDecryptPrivate;
+
+GType webkit_media_common_encryption_decrypt_get_type(void);
+
+struct _WebKitMediaCommonEncryptionDecrypt {
+ GstBaseTransform parent;
+
+ WebKitMediaCommonEncryptionDecryptPrivate* priv;
+};
+
+struct _WebKitMediaCommonEncryptionDecryptClass {
+ GstBaseTransformClass parentClass;
+
+ const char* protectionSystemId;
+ gboolean (*handleKeyResponse)(WebKitMediaCommonEncryptionDecrypt*, GstEvent* event);
+ gboolean (*setupCipher)(WebKitMediaCommonEncryptionDecrypt*);
+ gboolean (*decrypt)(WebKitMediaCommonEncryptionDecrypt*, GstBuffer* ivBuffer, GstBuffer* buffer, unsigned subSamplesCount, GstBuffer* subSamplesBuffer);
+ void (*releaseCipher)(WebKitMediaCommonEncryptionDecrypt*);
+};
+
+G_END_DECLS
+
+#endif // (ENABLE(LEGACY_ENCRYPTED_MEDIA) || ENABLE(ENCRYPTED_MEDIA)) && USE(GSTREAMER)
diff --git a/Source/WebCore/platform/graphics/gstreamer/mse/AppendPipeline.cpp b/Source/WebCore/platform/graphics/gstreamer/mse/AppendPipeline.cpp
new file mode 100644
index 000000000..c4f2b06bc
--- /dev/null
+++ b/Source/WebCore/platform/graphics/gstreamer/mse/AppendPipeline.cpp
@@ -0,0 +1,1188 @@
+/*
+ * Copyright (C) 2016 Metrological Group B.V.
+ * Copyright (C) 2016 Igalia S.L
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * aint with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include "config.h"
+#include "AppendPipeline.h"
+
+#if ENABLE(VIDEO) && USE(GSTREAMER) && ENABLE(MEDIA_SOURCE)
+
+#include "AudioTrackPrivateGStreamer.h"
+#include "GRefPtrGStreamer.h"
+#include "GStreamerMediaDescription.h"
+#include "GStreamerMediaSample.h"
+#include "GStreamerUtilities.h"
+#include "InbandTextTrackPrivateGStreamer.h"
+#include "MediaDescription.h"
+#include "SourceBufferPrivateGStreamer.h"
+#include "VideoTrackPrivateGStreamer.h"
+
+#include <gst/app/gstappsink.h>
+#include <gst/app/gstappsrc.h>
+#include <gst/gst.h>
+#include <gst/pbutils/pbutils.h>
+#include <gst/video/video.h>
+#include <wtf/Condition.h>
+#include <wtf/glib/GLibUtilities.h>
+
+GST_DEBUG_CATEGORY_EXTERN(webkit_mse_debug);
+#define GST_CAT_DEFAULT webkit_mse_debug
+
+namespace WebCore {
+
+static const char* dumpAppendState(AppendPipeline::AppendState appendState)
+{
+ switch (appendState) {
+ case AppendPipeline::AppendState::Invalid:
+ return "Invalid";
+ case AppendPipeline::AppendState::NotStarted:
+ return "NotStarted";
+ case AppendPipeline::AppendState::Ongoing:
+ return "Ongoing";
+ case AppendPipeline::AppendState::KeyNegotiation:
+ return "KeyNegotiation";
+ case AppendPipeline::AppendState::DataStarve:
+ return "DataStarve";
+ case AppendPipeline::AppendState::Sampling:
+ return "Sampling";
+ case AppendPipeline::AppendState::LastSample:
+ return "LastSample";
+ case AppendPipeline::AppendState::Aborting:
+ return "Aborting";
+ default:
+ return "(unknown)";
+ }
+}
+
+static void appendPipelineAppsrcNeedData(GstAppSrc*, guint, AppendPipeline*);
+static void appendPipelineDemuxerPadAdded(GstElement*, GstPad*, AppendPipeline*);
+static void appendPipelineDemuxerPadRemoved(GstElement*, GstPad*, AppendPipeline*);
+static void appendPipelineAppsinkCapsChanged(GObject*, GParamSpec*, AppendPipeline*);
+static GstPadProbeReturn appendPipelineAppsrcDataLeaving(GstPad*, GstPadProbeInfo*, AppendPipeline*);
+#if !LOG_DISABLED
+static GstPadProbeReturn appendPipelinePadProbeDebugInformation(GstPad*, GstPadProbeInfo*, struct PadProbeInformation*);
+#endif
+static GstPadProbeReturn appendPipelineDemuxerBlackHolePadProbe(GstPad*, GstPadProbeInfo*, gpointer);
+static GstFlowReturn appendPipelineAppsinkNewSample(GstElement*, AppendPipeline*);
+static void appendPipelineAppsinkEOS(GstElement*, AppendPipeline*);
+
+static void appendPipelineNeedContextMessageCallback(GstBus*, GstMessage* message, AppendPipeline* appendPipeline)
+{
+ GST_TRACE("received callback");
+ appendPipeline->handleNeedContextSyncMessage(message);
+}
+
+static void appendPipelineApplicationMessageCallback(GstBus*, GstMessage* message, AppendPipeline* appendPipeline)
+{
+ appendPipeline->handleApplicationMessage(message);
+}
+
+#if ENABLE(LEGACY_ENCRYPTED_MEDIA)
+static void appendPipelineElementMessageCallback(GstBus*, GstMessage* message, AppendPipeline* appendPipeline)
+{
+ appendPipeline->handleElementMessage(message);
+}
+#endif
+
+AppendPipeline::AppendPipeline(Ref<MediaSourceClientGStreamerMSE> mediaSourceClient, Ref<SourceBufferPrivateGStreamer> sourceBufferPrivate, MediaPlayerPrivateGStreamerMSE& playerPrivate)
+ : m_mediaSourceClient(mediaSourceClient.get())
+ , m_sourceBufferPrivate(sourceBufferPrivate.get())
+ , m_playerPrivate(&playerPrivate)
+ , m_id(0)
+ , m_appsrcAtLeastABufferLeft(false)
+ , m_appsrcNeedDataReceived(false)
+ , m_appsrcDataLeavingProbeId(0)
+ , m_appendState(AppendState::NotStarted)
+ , m_abortPending(false)
+ , m_streamType(Unknown)
+{
+ ASSERT(WTF::isMainThread());
+
+ GST_TRACE("Creating AppendPipeline (%p)", this);
+
+ // FIXME: give a name to the pipeline, maybe related with the track it's managing.
+ // The track name is still unknown at this time, though.
+ m_pipeline = gst_pipeline_new(nullptr);
+
+ m_bus = adoptGRef(gst_pipeline_get_bus(GST_PIPELINE(m_pipeline.get())));
+ gst_bus_add_signal_watch(m_bus.get());
+ gst_bus_enable_sync_message_emission(m_bus.get());
+
+ g_signal_connect(m_bus.get(), "sync-message::need-context", G_CALLBACK(appendPipelineNeedContextMessageCallback), this);
+ g_signal_connect(m_bus.get(), "message::application", G_CALLBACK(appendPipelineApplicationMessageCallback), this);
+#if ENABLE(LEGACY_ENCRYPTED_MEDIA)
+ g_signal_connect(m_bus.get(), "message::element", G_CALLBACK(appendPipelineElementMessageCallback), this);
+#endif
+
+ // We assign the created instances here instead of adoptRef() because gst_bin_add_many()
+ // below will already take the initial reference and we need an additional one for us.
+ m_appsrc = gst_element_factory_make("appsrc", nullptr);
+ m_demux = gst_element_factory_make("qtdemux", nullptr);
+ m_appsink = gst_element_factory_make("appsink", nullptr);
+
+ gst_app_sink_set_emit_signals(GST_APP_SINK(m_appsink.get()), TRUE);
+ gst_base_sink_set_sync(GST_BASE_SINK(m_appsink.get()), FALSE);
+
+ GRefPtr<GstPad> appsinkPad = adoptGRef(gst_element_get_static_pad(m_appsink.get(), "sink"));
+ g_signal_connect(appsinkPad.get(), "notify::caps", G_CALLBACK(appendPipelineAppsinkCapsChanged), this);
+
+ setAppsrcDataLeavingProbe();
+
+#if !LOG_DISABLED
+ GRefPtr<GstPad> demuxerPad = adoptGRef(gst_element_get_static_pad(m_demux.get(), "sink"));
+ m_demuxerDataEnteringPadProbeInformation.appendPipeline = this;
+ m_demuxerDataEnteringPadProbeInformation.description = "demuxer data entering";
+ m_demuxerDataEnteringPadProbeInformation.probeId = gst_pad_add_probe(demuxerPad.get(), GST_PAD_PROBE_TYPE_BUFFER, reinterpret_cast<GstPadProbeCallback>(appendPipelinePadProbeDebugInformation), &m_demuxerDataEnteringPadProbeInformation, nullptr);
+ m_appsinkDataEnteringPadProbeInformation.appendPipeline = this;
+ m_appsinkDataEnteringPadProbeInformation.description = "appsink data entering";
+ m_appsinkDataEnteringPadProbeInformation.probeId = gst_pad_add_probe(appsinkPad.get(), GST_PAD_PROBE_TYPE_BUFFER, reinterpret_cast<GstPadProbeCallback>(appendPipelinePadProbeDebugInformation), &m_appsinkDataEnteringPadProbeInformation, nullptr);
+#endif
+
+ // These signals won't be connected outside of the lifetime of "this".
+ g_signal_connect(m_appsrc.get(), "need-data", G_CALLBACK(appendPipelineAppsrcNeedData), this);
+ g_signal_connect(m_demux.get(), "pad-added", G_CALLBACK(appendPipelineDemuxerPadAdded), this);
+ g_signal_connect(m_demux.get(), "pad-removed", G_CALLBACK(appendPipelineDemuxerPadRemoved), this);
+ g_signal_connect(m_appsink.get(), "new-sample", G_CALLBACK(appendPipelineAppsinkNewSample), this);
+ g_signal_connect(m_appsink.get(), "eos", G_CALLBACK(appendPipelineAppsinkEOS), this);
+
+ // Add_many will take ownership of a reference. That's why we used an assignment before.
+ gst_bin_add_many(GST_BIN(m_pipeline.get()), m_appsrc.get(), m_demux.get(), nullptr);
+ gst_element_link(m_appsrc.get(), m_demux.get());
+
+ gst_element_set_state(m_pipeline.get(), GST_STATE_READY);
+};
+
+AppendPipeline::~AppendPipeline()
+{
+ ASSERT(WTF::isMainThread());
+
+ {
+ LockHolder locker(m_newSampleLock);
+ setAppendState(AppendState::Invalid);
+ m_newSampleCondition.notifyOne();
+ }
+
+ {
+ LockHolder locker(m_padAddRemoveLock);
+ m_playerPrivate = nullptr;
+ m_padAddRemoveCondition.notifyOne();
+ }
+
+ GST_TRACE("Destroying AppendPipeline (%p)", this);
+
+ // FIXME: Maybe notify appendComplete here?
+
+ if (m_pipeline) {
+ ASSERT(m_bus);
+ gst_bus_remove_signal_watch(m_bus.get());
+ gst_element_set_state(m_pipeline.get(), GST_STATE_NULL);
+ m_pipeline = nullptr;
+ }
+
+ if (m_appsrc) {
+ removeAppsrcDataLeavingProbe();
+ g_signal_handlers_disconnect_by_data(m_appsrc.get(), this);
+ m_appsrc = nullptr;
+ }
+
+ if (m_demux) {
+#if !LOG_DISABLED
+ GRefPtr<GstPad> demuxerPad = adoptGRef(gst_element_get_static_pad(m_demux.get(), "sink"));
+ gst_pad_remove_probe(demuxerPad.get(), m_demuxerDataEnteringPadProbeInformation.probeId);
+#endif
+
+ g_signal_handlers_disconnect_by_data(m_demux.get(), this);
+ m_demux = nullptr;
+ }
+
+ if (m_appsink) {
+ GRefPtr<GstPad> appsinkPad = adoptGRef(gst_element_get_static_pad(m_appsink.get(), "sink"));
+ g_signal_handlers_disconnect_by_data(appsinkPad.get(), this);
+ g_signal_handlers_disconnect_by_data(m_appsink.get(), this);
+
+#if !LOG_DISABLED
+ gst_pad_remove_probe(appsinkPad.get(), m_appsinkDataEnteringPadProbeInformation.probeId);
+#endif
+
+ m_appsink = nullptr;
+ }
+
+ m_appsinkCaps = nullptr;
+ m_demuxerSrcPadCaps = nullptr;
+};
+
+#if ENABLE(LEGACY_ENCRYPTED_MEDIA)
+void AppendPipeline::dispatchPendingDecryptionKey()
+{
+ ASSERT(m_decryptor);
+ ASSERT(m_pendingKey);
+ ASSERT(m_appendState == KeyNegotiation);
+ GST_TRACE("dispatching key to append pipeline %p", this);
+ gst_element_send_event(m_pipeline.get(), gst_event_new_custom(GST_EVENT_CUSTOM_DOWNSTREAM_OOB,
+ gst_structure_new("drm-cipher", "key", GST_TYPE_BUFFER, m_pendingKey.get(), nullptr)));
+ m_pendingKey.clear();
+ setAppendState(AppendState::Ongoing);
+}
+
+void AppendPipeline::dispatchDecryptionKey(GstBuffer* buffer)
+{
+ if (m_appendState == AppendState::KeyNegotiation) {
+ GST_TRACE("append pipeline %p in key negotiation", this);
+ m_pendingKey = buffer;
+ if (m_decryptor)
+ dispatchPendingDecryptionKey();
+ else
+ GST_TRACE("no decryptor yet, waiting for it");
+ } else
+ GST_TRACE("append pipeline %p not in key negotiation", this);
+}
+#endif
+
+void AppendPipeline::clearPlayerPrivate()
+{
+ ASSERT(WTF::isMainThread());
+ GST_DEBUG("cleaning private player");
+
+ {
+ LockHolder locker(m_newSampleLock);
+ // Make sure that AppendPipeline won't process more data from now on and
+ // instruct handleNewSample to abort itself from now on as well.
+ setAppendState(AppendState::Invalid);
+
+ // Awake any pending handleNewSample operation in the streaming thread.
+ m_newSampleCondition.notifyOne();
+ }
+
+ {
+ LockHolder locker(m_padAddRemoveLock);
+ m_playerPrivate = nullptr;
+ m_padAddRemoveCondition.notifyOne();
+ }
+
+ // And now that no handleNewSample operations will remain stalled waiting
+ // for the main thread, stop the pipeline.
+ if (m_pipeline)
+ gst_element_set_state(m_pipeline.get(), GST_STATE_NULL);
+}
+
+void AppendPipeline::handleNeedContextSyncMessage(GstMessage* message)
+{
+ const gchar* contextType = nullptr;
+ gst_message_parse_context_type(message, &contextType);
+ GST_TRACE("context type: %s", contextType);
+ if (!g_strcmp0(contextType, "drm-preferred-decryption-system-id"))
+ setAppendState(AppendPipeline::AppendState::KeyNegotiation);
+
+ // MediaPlayerPrivateGStreamerBase will take care of setting up encryption.
+ if (m_playerPrivate)
+ m_playerPrivate->handleSyncMessage(message);
+}
+
+#if ENABLE(LEGACY_ENCRYPTED_MEDIA)
+void AppendPipeline::handleElementMessage(GstMessage* message)
+{
+ ASSERT(WTF::isMainThread());
+
+ const GstStructure* structure = gst_message_get_structure(message);
+ GST_TRACE("%s message from %s", gst_structure_get_name(structure), GST_MESSAGE_SRC_NAME(message));
+ if (m_playerPrivate && gst_structure_has_name(structure, "drm-key-needed")) {
+ setAppendState(AppendPipeline::AppendState::KeyNegotiation);
+
+ GST_DEBUG("sending drm-key-needed message from %s to the player", GST_MESSAGE_SRC_NAME(message));
+ GRefPtr<GstEvent> event;
+ gst_structure_get(structure, "event", GST_TYPE_EVENT, &event.outPtr(), nullptr);
+ m_playerPrivate->handleProtectionEvent(event.get());
+ }
+}
+#endif
+
+void AppendPipeline::handleApplicationMessage(GstMessage* message)
+{
+ ASSERT(WTF::isMainThread());
+
+ const GstStructure* structure = gst_message_get_structure(message);
+
+ if (gst_structure_has_name(structure, "appsrc-need-data")) {
+ handleAppsrcNeedDataReceived();
+ return;
+ }
+
+ if (gst_structure_has_name(structure, "appsrc-buffer-left")) {
+ handleAppsrcAtLeastABufferLeft();
+ return;
+ }
+
+ if (gst_structure_has_name(structure, "demuxer-connect-to-appsink")) {
+ GRefPtr<GstPad> demuxerSrcPad;
+ gst_structure_get(structure, "demuxer-src-pad", G_TYPE_OBJECT, &demuxerSrcPad.outPtr(), nullptr);
+ ASSERT(demuxerSrcPad);
+ connectDemuxerSrcPadToAppsink(demuxerSrcPad.get());
+ return;
+ }
+
+ if (gst_structure_has_name(structure, "appsink-caps-changed")) {
+ appsinkCapsChanged();
+ return;
+ }
+
+ if (gst_structure_has_name(structure, "appsink-new-sample")) {
+ GRefPtr<GstSample> newSample;
+ gst_structure_get(structure, "new-sample", GST_TYPE_SAMPLE, &newSample.outPtr(), nullptr);
+
+ appsinkNewSample(newSample.get());
+ return;
+ }
+
+ if (gst_structure_has_name(structure, "appsink-eos")) {
+ appsinkEOS();
+ return;
+ }
+
+ ASSERT_NOT_REACHED();
+}
+
+void AppendPipeline::handleAppsrcNeedDataReceived()
+{
+ if (!m_appsrcAtLeastABufferLeft) {
+ GST_TRACE("discarding until at least a buffer leaves appsrc");
+ return;
+ }
+
+ ASSERT(m_appendState == AppendState::Ongoing || m_appendState == AppendState::Sampling);
+ ASSERT(!m_appsrcNeedDataReceived);
+
+ GST_TRACE("received need-data from appsrc");
+
+ m_appsrcNeedDataReceived = true;
+ checkEndOfAppend();
+}
+
+void AppendPipeline::handleAppsrcAtLeastABufferLeft()
+{
+ m_appsrcAtLeastABufferLeft = true;
+ GST_TRACE("received buffer-left from appsrc");
+#if LOG_DISABLED
+ removeAppsrcDataLeavingProbe();
+#endif
+}
+
+gint AppendPipeline::id()
+{
+ ASSERT(WTF::isMainThread());
+
+ if (m_id)
+ return m_id;
+
+ static gint s_totalAudio = 0;
+ static gint s_totalVideo = 0;
+ static gint s_totalText = 0;
+
+ switch (m_streamType) {
+ case Audio:
+ m_id = ++s_totalAudio;
+ break;
+ case Video:
+ m_id = ++s_totalVideo;
+ break;
+ case Text:
+ m_id = ++s_totalText;
+ break;
+ case Unknown:
+ case Invalid:
+ GST_ERROR("Trying to get id for a pipeline of Unknown/Invalid type");
+ ASSERT_NOT_REACHED();
+ break;
+ }
+
+ GST_DEBUG("streamType=%d, id=%d", static_cast<int>(m_streamType), m_id);
+
+ return m_id;
+}
+
+void AppendPipeline::setAppendState(AppendState newAppendState)
+{
+ ASSERT(WTF::isMainThread());
+ // Valid transitions:
+ // NotStarted-->Ongoing-->DataStarve-->NotStarted
+ // | | `->Aborting-->NotStarted
+ // | `->Sampling-···->Sampling-->LastSample-->NotStarted
+ // | | `->Aborting-->NotStarted
+ // | `->KeyNegotiation-->Ongoing-->[...]
+ // `->Aborting-->NotStarted
+ AppendState oldAppendState = m_appendState;
+ AppendState nextAppendState = AppendState::Invalid;
+
+ if (oldAppendState != newAppendState)
+ GST_TRACE("%s --> %s", dumpAppendState(oldAppendState), dumpAppendState(newAppendState));
+
+ bool ok = false;
+
+ switch (oldAppendState) {
+ case AppendState::NotStarted:
+ switch (newAppendState) {
+ case AppendState::Ongoing:
+ ok = true;
+ gst_element_set_state(m_pipeline.get(), GST_STATE_PLAYING);
+ break;
+ case AppendState::NotStarted:
+ ok = true;
+ if (m_pendingBuffer) {
+ GST_TRACE("pushing pending buffer %p", m_pendingBuffer.get());
+ gst_app_src_push_buffer(GST_APP_SRC(appsrc()), m_pendingBuffer.leakRef());
+ nextAppendState = AppendState::Ongoing;
+ }
+ break;
+ case AppendState::Aborting:
+ ok = true;
+ nextAppendState = AppendState::NotStarted;
+ break;
+ case AppendState::Invalid:
+ ok = true;
+ break;
+ default:
+ break;
+ }
+ break;
+ case AppendState::KeyNegotiation:
+ switch (newAppendState) {
+ case AppendState::Ongoing:
+ case AppendState::Invalid:
+ ok = true;
+ break;
+ default:
+ break;
+ }
+ break;
+ case AppendState::Ongoing:
+ switch (newAppendState) {
+ case AppendState::KeyNegotiation:
+ case AppendState::Sampling:
+ case AppendState::Invalid:
+ ok = true;
+ break;
+ case AppendState::DataStarve:
+ ok = true;
+ GST_DEBUG("received all pending samples");
+ m_sourceBufferPrivate->didReceiveAllPendingSamples();
+ if (m_abortPending)
+ nextAppendState = AppendState::Aborting;
+ else
+ nextAppendState = AppendState::NotStarted;
+ break;
+ default:
+ break;
+ }
+ break;
+ case AppendState::DataStarve:
+ switch (newAppendState) {
+ case AppendState::NotStarted:
+ case AppendState::Invalid:
+ ok = true;
+ break;
+ case AppendState::Aborting:
+ ok = true;
+ nextAppendState = AppendState::NotStarted;
+ break;
+ default:
+ break;
+ }
+ break;
+ case AppendState::Sampling:
+ switch (newAppendState) {
+ case AppendState::Sampling:
+ case AppendState::Invalid:
+ ok = true;
+ break;
+ case AppendState::LastSample:
+ ok = true;
+ GST_DEBUG("received all pending samples");
+ m_sourceBufferPrivate->didReceiveAllPendingSamples();
+ if (m_abortPending)
+ nextAppendState = AppendState::Aborting;
+ else
+ nextAppendState = AppendState::NotStarted;
+ break;
+ default:
+ break;
+ }
+ break;
+ case AppendState::LastSample:
+ switch (newAppendState) {
+ case AppendState::NotStarted:
+ case AppendState::Invalid:
+ ok = true;
+ break;
+ case AppendState::Aborting:
+ ok = true;
+ nextAppendState = AppendState::NotStarted;
+ break;
+ default:
+ break;
+ }
+ break;
+ case AppendState::Aborting:
+ switch (newAppendState) {
+ case AppendState::NotStarted:
+ ok = true;
+ resetPipeline();
+ m_abortPending = false;
+ nextAppendState = AppendState::NotStarted;
+ break;
+ case AppendState::Invalid:
+ ok = true;
+ break;
+ default:
+ break;
+ }
+ break;
+ case AppendState::Invalid:
+ ok = true;
+ break;
+ }
+
+ if (ok)
+ m_appendState = newAppendState;
+ else
+ GST_ERROR("Invalid append state transition %s --> %s", dumpAppendState(oldAppendState), dumpAppendState(newAppendState));
+
+ ASSERT(ok);
+
+ if (nextAppendState != AppendState::Invalid)
+ setAppendState(nextAppendState);
+}
+
+void AppendPipeline::parseDemuxerSrcPadCaps(GstCaps* demuxerSrcPadCaps)
+{
+ ASSERT(WTF::isMainThread());
+
+ m_demuxerSrcPadCaps = adoptGRef(demuxerSrcPadCaps);
+ m_streamType = WebCore::MediaSourceStreamTypeGStreamer::Unknown;
+
+ GstStructure* structure = gst_caps_get_structure(m_demuxerSrcPadCaps.get(), 0);
+ bool sizeConfigured = false;
+
+#if GST_CHECK_VERSION(1, 5, 3) && (ENABLE(LEGACY_ENCRYPTED_MEDIA) || ENABLE(ENCRYPTED_MEDIA))
+ if (gst_structure_has_name(structure, "application/x-cenc")) {
+ // Any previous decryptor should have been removed from the pipeline by disconnectFromAppSinkFromStreamingThread()
+ ASSERT(!m_decryptor);
+
+ m_decryptor = WebCore::createGstDecryptor(gst_structure_get_string(structure, "protection-system"));
+ if (!m_decryptor) {
+ GST_ERROR("decryptor not found for caps: %" GST_PTR_FORMAT, m_demuxerSrcPadCaps.get());
+ return;
+ }
+
+ const gchar* originalMediaType = gst_structure_get_string(structure, "original-media-type");
+
+ if (!MediaPlayerPrivateGStreamerMSE::supportsCodecs(originalMediaType)) {
+ m_presentationSize = WebCore::FloatSize();
+ m_streamType = WebCore::MediaSourceStreamTypeGStreamer::Invalid;
+ } else if (g_str_has_prefix(originalMediaType, "video/")) {
+ int width = 0;
+ int height = 0;
+ float finalHeight = 0;
+
+ if (gst_structure_get_int(structure, "width", &width) && gst_structure_get_int(structure, "height", &height)) {
+ int ratioNumerator = 1;
+ int ratioDenominator = 1;
+
+ gst_structure_get_fraction(structure, "pixel-aspect-ratio", &ratioNumerator, &ratioDenominator);
+ finalHeight = height * ((float) ratioDenominator / (float) ratioNumerator);
+ }
+
+ m_presentationSize = WebCore::FloatSize(width, finalHeight);
+ m_streamType = WebCore::MediaSourceStreamTypeGStreamer::Video;
+ } else {
+ m_presentationSize = WebCore::FloatSize();
+ if (g_str_has_prefix(originalMediaType, "audio/"))
+ m_streamType = WebCore::MediaSourceStreamTypeGStreamer::Audio;
+ else if (g_str_has_prefix(originalMediaType, "text/"))
+ m_streamType = WebCore::MediaSourceStreamTypeGStreamer::Text;
+ }
+ sizeConfigured = true;
+ }
+#endif
+
+ if (!sizeConfigured) {
+ const char* structureName = gst_structure_get_name(structure);
+ GstVideoInfo info;
+
+ if (!MediaPlayerPrivateGStreamerMSE::supportsCodecs(structureName)) {
+ m_presentationSize = WebCore::FloatSize();
+ m_streamType = WebCore::MediaSourceStreamTypeGStreamer::Invalid;
+ } else if (g_str_has_prefix(structureName, "video/") && gst_video_info_from_caps(&info, demuxerSrcPadCaps)) {
+ float width, height;
+
+ width = info.width;
+ height = info.height * ((float) info.par_d / (float) info.par_n);
+
+ m_presentationSize = WebCore::FloatSize(width, height);
+ m_streamType = WebCore::MediaSourceStreamTypeGStreamer::Video;
+ } else {
+ m_presentationSize = WebCore::FloatSize();
+ if (g_str_has_prefix(structureName, "audio/"))
+ m_streamType = WebCore::MediaSourceStreamTypeGStreamer::Audio;
+ else if (g_str_has_prefix(structureName, "text/"))
+ m_streamType = WebCore::MediaSourceStreamTypeGStreamer::Text;
+ }
+ }
+}
+
+void AppendPipeline::appsinkCapsChanged()
+{
+ ASSERT(WTF::isMainThread());
+
+ if (!m_appsink)
+ return;
+
+ GRefPtr<GstPad> pad = adoptGRef(gst_element_get_static_pad(m_appsink.get(), "sink"));
+ GRefPtr<GstCaps> caps = adoptGRef(gst_pad_get_current_caps(pad.get()));
+
+ if (!caps)
+ return;
+
+ // This means that we're right after a new track has appeared. Otherwise, it's a caps change inside the same track.
+ bool previousCapsWereNull = !m_appsinkCaps;
+
+ if (m_appsinkCaps != caps) {
+ m_appsinkCaps = WTFMove(caps);
+ if (m_playerPrivate && previousCapsWereNull)
+ m_playerPrivate->trackDetected(this, m_oldTrack, m_track);
+ didReceiveInitializationSegment();
+ gst_element_set_state(m_pipeline.get(), GST_STATE_PLAYING);
+ }
+}
+
+void AppendPipeline::checkEndOfAppend()
+{
+ ASSERT(WTF::isMainThread());
+
+ if (!m_appsrcNeedDataReceived || (m_appendState != AppendState::Ongoing && m_appendState != AppendState::Sampling))
+ return;
+
+ GST_TRACE("end of append data mark was received");
+
+ switch (m_appendState) {
+ case AppendState::Ongoing:
+ GST_TRACE("DataStarve");
+ m_appsrcNeedDataReceived = false;
+ setAppendState(AppendState::DataStarve);
+ break;
+ case AppendState::Sampling:
+ GST_TRACE("LastSample");
+ m_appsrcNeedDataReceived = false;
+ setAppendState(AppendState::LastSample);
+ break;
+ default:
+ ASSERT_NOT_REACHED();
+ break;
+ }
+}
+
+void AppendPipeline::appsinkNewSample(GstSample* sample)
+{
+ ASSERT(WTF::isMainThread());
+
+ {
+ LockHolder locker(m_newSampleLock);
+
+ // Ignore samples if we're not expecting them. Refuse processing if we're in Invalid state.
+ if (m_appendState != AppendState::Ongoing && m_appendState != AppendState::Sampling) {
+ GST_WARNING("Unexpected sample, appendState=%s", dumpAppendState(m_appendState));
+ // FIXME: Return ERROR and find a more robust way to detect that all the
+ // data has been processed, so we don't need to resort to these hacks.
+ // All in all, return OK, even if it's not the proper thing to do. We don't want to break the demuxer.
+ m_flowReturn = GST_FLOW_OK;
+ m_newSampleCondition.notifyOne();
+ return;
+ }
+
+ RefPtr<GStreamerMediaSample> mediaSample = WebCore::GStreamerMediaSample::create(sample, m_presentationSize, trackId());
+
+ GST_TRACE("append: trackId=%s PTS=%f presentationSize=%.0fx%.0f", mediaSample->trackID().string().utf8().data(), mediaSample->presentationTime().toFloat(), mediaSample->presentationSize().width(), mediaSample->presentationSize().height());
+
+ // If we're beyond the duration, ignore this sample and the remaining ones.
+ MediaTime duration = m_mediaSourceClient->duration();
+ if (duration.isValid() && !duration.indefiniteTime() && mediaSample->presentationTime() > duration) {
+ GST_DEBUG("Detected sample (%f) beyond the duration (%f), declaring LastSample", mediaSample->presentationTime().toFloat(), duration.toFloat());
+ setAppendState(AppendState::LastSample);
+ m_flowReturn = GST_FLOW_OK;
+ m_newSampleCondition.notifyOne();
+ return;
+ }
+
+ // Add a gap sample if a gap is detected before the first sample.
+ if (mediaSample->decodeTime() == MediaTime::zeroTime()
+ && mediaSample->presentationTime() > MediaTime::zeroTime()
+ && mediaSample->presentationTime() <= MediaTime::createWithDouble(0.1)) {
+ GST_DEBUG("Adding gap offset");
+ mediaSample->applyPtsOffset(MediaTime::zeroTime());
+ }
+
+ m_sourceBufferPrivate->didReceiveSample(*mediaSample);
+ setAppendState(AppendState::Sampling);
+ m_flowReturn = GST_FLOW_OK;
+ m_newSampleCondition.notifyOne();
+ }
+
+ checkEndOfAppend();
+}
+
+void AppendPipeline::appsinkEOS()
+{
+ ASSERT(WTF::isMainThread());
+
+ switch (m_appendState) {
+ case AppendState::Aborting:
+ // Ignored. Operation completion will be managed by the Aborting->NotStarted transition.
+ return;
+ case AppendState::Ongoing:
+ // Finish Ongoing and Sampling states.
+ setAppendState(AppendState::DataStarve);
+ break;
+ case AppendState::Sampling:
+ setAppendState(AppendState::LastSample);
+ break;
+ default:
+ GST_DEBUG("Unexpected EOS");
+ break;
+ }
+}
+
+void AppendPipeline::didReceiveInitializationSegment()
+{
+ ASSERT(WTF::isMainThread());
+
+ WebCore::SourceBufferPrivateClient::InitializationSegment initializationSegment;
+
+ GST_DEBUG("Notifying SourceBuffer for track %s", (m_track) ? m_track->id().string().utf8().data() : nullptr);
+ initializationSegment.duration = m_mediaSourceClient->duration();
+
+ switch (m_streamType) {
+ case Audio: {
+ WebCore::SourceBufferPrivateClient::InitializationSegment::AudioTrackInformation info;
+ info.track = static_cast<AudioTrackPrivateGStreamer*>(m_track.get());
+ info.description = WebCore::GStreamerMediaDescription::create(m_demuxerSrcPadCaps.get());
+ initializationSegment.audioTracks.append(info);
+ break;
+ }
+ case Video: {
+ WebCore::SourceBufferPrivateClient::InitializationSegment::VideoTrackInformation info;
+ info.track = static_cast<VideoTrackPrivateGStreamer*>(m_track.get());
+ info.description = WebCore::GStreamerMediaDescription::create(m_demuxerSrcPadCaps.get());
+ initializationSegment.videoTracks.append(info);
+ break;
+ }
+ default:
+ GST_ERROR("Unsupported stream type or codec");
+ break;
+ }
+
+ m_sourceBufferPrivate->didReceiveInitializationSegment(initializationSegment);
+}
+
+AtomicString AppendPipeline::trackId()
+{
+ ASSERT(WTF::isMainThread());
+
+ if (!m_track)
+ return AtomicString();
+
+ return m_track->id();
+}
+
+void AppendPipeline::resetPipeline()
+{
+ ASSERT(WTF::isMainThread());
+ GST_DEBUG("resetting pipeline");
+ m_appsrcAtLeastABufferLeft = false;
+ setAppsrcDataLeavingProbe();
+
+ {
+ LockHolder locker(m_newSampleLock);
+ m_newSampleCondition.notifyOne();
+ gst_element_set_state(m_pipeline.get(), GST_STATE_READY);
+ gst_element_get_state(m_pipeline.get(), nullptr, nullptr, 0);
+ }
+
+#if (!(LOG_DISABLED || defined(GST_DISABLE_GST_DEBUG)))
+ {
+ static unsigned i = 0;
+ // This is here for debugging purposes. It does not make sense to have it as class member.
+ WTF::String dotFileName = String::format("reset-pipeline-%d", ++i);
+ gst_debug_bin_to_dot_file(GST_BIN(m_pipeline.get()), GST_DEBUG_GRAPH_SHOW_ALL, dotFileName.utf8().data());
+ }
+#endif
+
+}
+
+void AppendPipeline::setAppsrcDataLeavingProbe()
+{
+ if (m_appsrcDataLeavingProbeId)
+ return;
+
+ GST_TRACE("setting appsrc data leaving probe");
+
+ GRefPtr<GstPad> appsrcPad = adoptGRef(gst_element_get_static_pad(m_appsrc.get(), "src"));
+ m_appsrcDataLeavingProbeId = gst_pad_add_probe(appsrcPad.get(), GST_PAD_PROBE_TYPE_BUFFER, reinterpret_cast<GstPadProbeCallback>(appendPipelineAppsrcDataLeaving), this, nullptr);
+}
+
+void AppendPipeline::removeAppsrcDataLeavingProbe()
+{
+ if (!m_appsrcDataLeavingProbeId)
+ return;
+
+ GST_TRACE("removing appsrc data leaving probe");
+
+ GRefPtr<GstPad> appsrcPad = adoptGRef(gst_element_get_static_pad(m_appsrc.get(), "src"));
+ gst_pad_remove_probe(appsrcPad.get(), m_appsrcDataLeavingProbeId);
+ m_appsrcDataLeavingProbeId = 0;
+}
+
+void AppendPipeline::abort()
+{
+ ASSERT(WTF::isMainThread());
+ GST_DEBUG("aborting");
+
+ m_pendingBuffer = nullptr;
+
+ // Abort already ongoing.
+ if (m_abortPending)
+ return;
+
+ m_abortPending = true;
+ if (m_appendState == AppendState::NotStarted)
+ setAppendState(AppendState::Aborting);
+ // Else, the automatic state transitions will take care when the ongoing append finishes.
+}
+
+GstFlowReturn AppendPipeline::pushNewBuffer(GstBuffer* buffer)
+{
+ GstFlowReturn result;
+
+ if (m_abortPending) {
+ m_pendingBuffer = adoptGRef(buffer);
+ result = GST_FLOW_OK;
+ } else {
+ setAppendState(AppendPipeline::AppendState::Ongoing);
+ GST_TRACE("pushing new buffer %p", buffer);
+ result = gst_app_src_push_buffer(GST_APP_SRC(appsrc()), buffer);
+ }
+
+ return result;
+}
+
+void AppendPipeline::reportAppsrcAtLeastABufferLeft()
+{
+ GST_TRACE("buffer left appsrc, reposting to bus");
+ GstStructure* structure = gst_structure_new_empty("appsrc-buffer-left");
+ GstMessage* message = gst_message_new_application(GST_OBJECT(m_appsrc.get()), structure);
+ gst_bus_post(m_bus.get(), message);
+}
+
+void AppendPipeline::reportAppsrcNeedDataReceived()
+{
+ GST_TRACE("received need-data signal at appsrc, reposting to bus");
+ GstStructure* structure = gst_structure_new_empty("appsrc-need-data");
+ GstMessage* message = gst_message_new_application(GST_OBJECT(m_appsrc.get()), structure);
+ gst_bus_post(m_bus.get(), message);
+}
+
+GstFlowReturn AppendPipeline::handleNewAppsinkSample(GstElement* appsink)
+{
+ ASSERT(!WTF::isMainThread());
+
+ // Even if we're disabled, it's important to pull the sample out anyway to
+ // avoid deadlocks when changing to GST_STATE_NULL having a non empty appsink.
+ GRefPtr<GstSample> sample = adoptGRef(gst_app_sink_pull_sample(GST_APP_SINK(appsink)));
+ LockHolder locker(m_newSampleLock);
+
+ if (!m_playerPrivate || m_appendState == AppendState::Invalid) {
+ GST_WARNING("AppendPipeline has been disabled, ignoring this sample");
+ return GST_FLOW_ERROR;
+ }
+
+ GstStructure* structure = gst_structure_new("appsink-new-sample", "new-sample", GST_TYPE_SAMPLE, sample.get(), nullptr);
+ GstMessage* message = gst_message_new_application(GST_OBJECT(appsink), structure);
+ gst_bus_post(m_bus.get(), message);
+ GST_TRACE("appsink-new-sample message posted to bus");
+
+ m_newSampleCondition.wait(m_newSampleLock);
+ // We've been awaken because the sample was processed or because of
+ // an exceptional condition (entered in Invalid state, destructor, etc.).
+ // We can't reliably delete info here, appendPipelineAppsinkNewSampleMainThread will do it.
+
+ return m_flowReturn;
+}
+
+void AppendPipeline::connectDemuxerSrcPadToAppsinkFromAnyThread(GstPad* demuxerSrcPad)
+{
+ if (!m_appsink)
+ return;
+
+ GST_DEBUG("connecting to appsink");
+
+ if (m_demux->numsrcpads > 1) {
+ GST_WARNING("Only one stream per SourceBuffer is allowed! Ignoring stream %d by adding a black hole probe.", m_demux->numsrcpads);
+ gulong probeId = gst_pad_add_probe(demuxerSrcPad, GST_PAD_PROBE_TYPE_BUFFER, reinterpret_cast<GstPadProbeCallback>(appendPipelineDemuxerBlackHolePadProbe), nullptr, nullptr);
+ g_object_set_data(G_OBJECT(demuxerSrcPad), "blackHoleProbeId", GULONG_TO_POINTER(probeId));
+ return;
+ }
+
+ GRefPtr<GstPad> appsinkSinkPad = adoptGRef(gst_element_get_static_pad(m_appsink.get(), "sink"));
+
+ // Only one stream per demuxer is supported.
+ ASSERT(!gst_pad_is_linked(appsinkSinkPad.get()));
+
+ gint64 timeLength = 0;
+ if (gst_element_query_duration(m_demux.get(), GST_FORMAT_TIME, &timeLength)
+ && static_cast<guint64>(timeLength) != GST_CLOCK_TIME_NONE)
+ m_initialDuration = MediaTime(GST_TIME_AS_USECONDS(timeLength), G_USEC_PER_SEC);
+ else
+ m_initialDuration = MediaTime::positiveInfiniteTime();
+
+ if (WTF::isMainThread())
+ connectDemuxerSrcPadToAppsink(demuxerSrcPad);
+ else {
+ // Call connectDemuxerSrcPadToAppsink() in the main thread and wait.
+ LockHolder locker(m_padAddRemoveLock);
+ if (!m_playerPrivate)
+ return;
+
+ GstStructure* structure = gst_structure_new("demuxer-connect-to-appsink", "demuxer-src-pad", G_TYPE_OBJECT, demuxerSrcPad, nullptr);
+ GstMessage* message = gst_message_new_application(GST_OBJECT(m_demux.get()), structure);
+ gst_bus_post(m_bus.get(), message);
+ GST_TRACE("demuxer-connect-to-appsink message posted to bus");
+
+ m_padAddRemoveCondition.wait(m_padAddRemoveLock);
+ }
+
+ // Must be done in the thread we were called from (usually streaming thread).
+ bool isData = (m_streamType == WebCore::MediaSourceStreamTypeGStreamer::Audio)
+ || (m_streamType == WebCore::MediaSourceStreamTypeGStreamer::Video)
+ || (m_streamType == WebCore::MediaSourceStreamTypeGStreamer::Text);
+
+ if (isData) {
+ // FIXME: Only add appsink one time. This method can be called several times.
+ GRefPtr<GstObject> parent = adoptGRef(gst_element_get_parent(m_appsink.get()));
+ if (!parent)
+ gst_bin_add(GST_BIN(m_pipeline.get()), m_appsink.get());
+
+#if ENABLE(LEGACY_ENCRYPTED_MEDIA) || ENABLE(ENCRYPTED_MEDIA)
+ if (m_decryptor) {
+ gst_object_ref(m_decryptor.get());
+ gst_bin_add(GST_BIN(m_pipeline.get()), m_decryptor.get());
+
+ GRefPtr<GstPad> decryptorSinkPad = adoptGRef(gst_element_get_static_pad(m_decryptor.get(), "sink"));
+ gst_pad_link(demuxerSrcPad, decryptorSinkPad.get());
+
+ GRefPtr<GstPad> decryptorSrcPad = adoptGRef(gst_element_get_static_pad(m_decryptor.get(), "src"));
+ gst_pad_link(decryptorSrcPad.get(), appsinkSinkPad.get());
+
+ gst_element_sync_state_with_parent(m_appsink.get());
+ gst_element_sync_state_with_parent(m_decryptor.get());
+
+#if ENABLE(LEGACY_ENCRYPTED_MEDIA)
+ if (m_pendingKey)
+ dispatchPendingDecryptionKey();
+#endif
+ } else {
+#endif
+ gst_pad_link(demuxerSrcPad, appsinkSinkPad.get());
+ gst_element_sync_state_with_parent(m_appsink.get());
+#if ENABLE(LEGACY_ENCRYPTED_MEDIA) || ENABLE(ENCRYPTED_MEDIA)
+ }
+#endif
+ gst_element_set_state(m_pipeline.get(), GST_STATE_PAUSED);
+ }
+}
+
+void AppendPipeline::connectDemuxerSrcPadToAppsink(GstPad* demuxerSrcPad)
+{
+ ASSERT(WTF::isMainThread());
+ GST_DEBUG("Connecting to appsink");
+
+ LockHolder locker(m_padAddRemoveLock);
+ GRefPtr<GstPad> sinkSinkPad = adoptGRef(gst_element_get_static_pad(m_appsink.get(), "sink"));
+
+ // Only one stream per demuxer is supported.
+ ASSERT(!gst_pad_is_linked(sinkSinkPad.get()));
+
+ GRefPtr<GstCaps> caps = adoptGRef(gst_pad_get_current_caps(GST_PAD(demuxerSrcPad)));
+
+ if (!caps || m_appendState == AppendState::Invalid || !m_playerPrivate) {
+ m_padAddRemoveCondition.notifyOne();
+ return;
+ }
+
+#ifndef GST_DISABLE_GST_DEBUG
+ {
+ GUniquePtr<gchar> strcaps(gst_caps_to_string(caps.get()));
+ GST_DEBUG("%s", strcaps.get());
+ }
+#endif
+
+ if (m_initialDuration > m_mediaSourceClient->duration()
+ || (m_mediaSourceClient->duration().isInvalid() && m_initialDuration > MediaTime::zeroTime()))
+ m_mediaSourceClient->durationChanged(m_initialDuration);
+
+ m_oldTrack = m_track;
+
+ parseDemuxerSrcPadCaps(gst_caps_ref(caps.get()));
+
+ switch (m_streamType) {
+ case WebCore::MediaSourceStreamTypeGStreamer::Audio:
+ if (m_playerPrivate)
+ m_track = WebCore::AudioTrackPrivateGStreamer::create(m_playerPrivate->pipeline(), id(), sinkSinkPad.get());
+ break;
+ case WebCore::MediaSourceStreamTypeGStreamer::Video:
+ if (m_playerPrivate)
+ m_track = WebCore::VideoTrackPrivateGStreamer::create(m_playerPrivate->pipeline(), id(), sinkSinkPad.get());
+ break;
+ case WebCore::MediaSourceStreamTypeGStreamer::Text:
+ m_track = WebCore::InbandTextTrackPrivateGStreamer::create(id(), sinkSinkPad.get());
+ break;
+ case WebCore::MediaSourceStreamTypeGStreamer::Invalid:
+ {
+ GUniquePtr<gchar> strcaps(gst_caps_to_string(caps.get()));
+ GST_DEBUG("Unsupported track codec: %s", strcaps.get());
+ }
+ // This is going to cause an error which will detach the SourceBuffer and tear down this
+ // AppendPipeline, so we need the padAddRemove lock released before continuing.
+ m_track = nullptr;
+ m_padAddRemoveCondition.notifyOne();
+ locker.unlockEarly();
+ didReceiveInitializationSegment();
+ return;
+ default:
+ // No useful data, but notify anyway to complete the append operation.
+ GST_DEBUG("Received all pending samples (no data)");
+ m_sourceBufferPrivate->didReceiveAllPendingSamples();
+ break;
+ }
+
+ m_padAddRemoveCondition.notifyOne();
+}
+
+void AppendPipeline::disconnectDemuxerSrcPadFromAppsinkFromAnyThread(GstPad* demuxerSrcPad)
+{
+ // Must be done in the thread we were called from (usually streaming thread).
+ if (!gst_pad_is_linked(demuxerSrcPad)) {
+ gulong probeId = GPOINTER_TO_ULONG(g_object_get_data(G_OBJECT(demuxerSrcPad), "blackHoleProbeId"));
+ if (probeId) {
+ GST_DEBUG("Disconnecting black hole probe.");
+ g_object_set_data(G_OBJECT(demuxerSrcPad), "blackHoleProbeId", nullptr);
+ gst_pad_remove_probe(demuxerSrcPad, probeId);
+ } else
+ GST_WARNING("Not disconnecting demuxer src pad because it wasn't linked");
+ return;
+ }
+
+ GST_DEBUG("Disconnecting appsink");
+
+#if ENABLE(LEGACY_ENCRYPTED_MEDIA) || ENABLE(ENCRYPTED_MEDIA)
+ if (m_decryptor) {
+ gst_element_unlink(m_decryptor.get(), m_appsink.get());
+ gst_element_unlink(m_demux.get(), m_decryptor.get());
+ gst_element_set_state(m_decryptor.get(), GST_STATE_NULL);
+ gst_bin_remove(GST_BIN(m_pipeline.get()), m_decryptor.get());
+ } else
+#endif
+ gst_element_unlink(m_demux.get(), m_appsink.get());
+}
+
+static void appendPipelineAppsinkCapsChanged(GObject* appsinkPad, GParamSpec*, AppendPipeline* appendPipeline)
+{
+ GstStructure* structure = gst_structure_new_empty("appsink-caps-changed");
+ GstMessage* message = gst_message_new_application(GST_OBJECT(appsinkPad), structure);
+ gst_bus_post(appendPipeline->bus(), message);
+ GST_TRACE("appsink-caps-changed message posted to bus");
+}
+
+static GstPadProbeReturn appendPipelineAppsrcDataLeaving(GstPad*, GstPadProbeInfo* info, AppendPipeline* appendPipeline)
+{
+ ASSERT(GST_PAD_PROBE_INFO_TYPE(info) & GST_PAD_PROBE_TYPE_BUFFER);
+
+ GstBuffer* buffer = GST_PAD_PROBE_INFO_BUFFER(info);
+ gsize bufferSize = gst_buffer_get_size(buffer);
+
+ GST_TRACE("buffer of size %" G_GSIZE_FORMAT " going thru", bufferSize);
+
+ appendPipeline->reportAppsrcAtLeastABufferLeft();
+
+ return GST_PAD_PROBE_OK;
+}
+
+#if !LOG_DISABLED
+static GstPadProbeReturn appendPipelinePadProbeDebugInformation(GstPad*, GstPadProbeInfo* info, struct PadProbeInformation* padProbeInformation)
+{
+ ASSERT(GST_PAD_PROBE_INFO_TYPE(info) & GST_PAD_PROBE_TYPE_BUFFER);
+ GstBuffer* buffer = GST_PAD_PROBE_INFO_BUFFER(info);
+ GST_TRACE("%s: buffer of size %" G_GSIZE_FORMAT " going thru", padProbeInformation->description, gst_buffer_get_size(buffer));
+ return GST_PAD_PROBE_OK;
+}
+#endif
+
+static GstPadProbeReturn appendPipelineDemuxerBlackHolePadProbe(GstPad*, GstPadProbeInfo* info, gpointer)
+{
+ ASSERT(GST_PAD_PROBE_INFO_TYPE(info) & GST_PAD_PROBE_TYPE_BUFFER);
+ GstBuffer* buffer = GST_PAD_PROBE_INFO_BUFFER(info);
+ GST_TRACE("buffer of size %" G_GSIZE_FORMAT " ignored", gst_buffer_get_size(buffer));
+ return GST_PAD_PROBE_DROP;
+}
+
+static void appendPipelineAppsrcNeedData(GstAppSrc*, guint, AppendPipeline* appendPipeline)
+{
+ appendPipeline->reportAppsrcNeedDataReceived();
+}
+
+static void appendPipelineDemuxerPadAdded(GstElement*, GstPad* demuxerSrcPad, AppendPipeline* appendPipeline)
+{
+ appendPipeline->connectDemuxerSrcPadToAppsinkFromAnyThread(demuxerSrcPad);
+}
+
+static void appendPipelineDemuxerPadRemoved(GstElement*, GstPad* demuxerSrcPad, AppendPipeline* appendPipeline)
+{
+ appendPipeline->disconnectDemuxerSrcPadFromAppsinkFromAnyThread(demuxerSrcPad);
+}
+
+static GstFlowReturn appendPipelineAppsinkNewSample(GstElement* appsink, AppendPipeline* appendPipeline)
+{
+ return appendPipeline->handleNewAppsinkSample(appsink);
+}
+
+static void appendPipelineAppsinkEOS(GstElement*, AppendPipeline* appendPipeline)
+{
+ if (WTF::isMainThread())
+ appendPipeline->appsinkEOS();
+ else {
+ GstStructure* structure = gst_structure_new_empty("appsink-eos");
+ GstMessage* message = gst_message_new_application(GST_OBJECT(appendPipeline->appsink()), structure);
+ gst_bus_post(appendPipeline->bus(), message);
+ GST_TRACE("appsink-eos message posted to bus");
+ }
+
+ GST_DEBUG("%s main thread", (WTF::isMainThread()) ? "Is" : "Not");
+}
+
+
+
+} // namespace WebCore.
+
+#endif // USE(GSTREAMER)
diff --git a/Source/WebCore/platform/graphics/gstreamer/mse/AppendPipeline.h b/Source/WebCore/platform/graphics/gstreamer/mse/AppendPipeline.h
new file mode 100644
index 000000000..301265eb9
--- /dev/null
+++ b/Source/WebCore/platform/graphics/gstreamer/mse/AppendPipeline.h
@@ -0,0 +1,165 @@
+/*
+ * Copyright (C) 2016 Metrological Group B.V.
+ * Copyright (C) 2016 Igalia S.L
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * aint with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#pragma once
+
+#if ENABLE(VIDEO) && USE(GSTREAMER) && ENABLE(MEDIA_SOURCE)
+
+#include "GRefPtrGStreamer.h"
+#include "MediaPlayerPrivateGStreamerMSE.h"
+#include "MediaSourceClientGStreamerMSE.h"
+#include "SourceBufferPrivateGStreamer.h"
+
+#include <gst/gst.h>
+#include <wtf/Condition.h>
+
+namespace WebCore {
+
+#if !LOG_DISABLED
+struct PadProbeInformation {
+ AppendPipeline* appendPipeline;
+ const char* description;
+ gulong probeId;
+};
+#endif
+
+class AppendPipeline : public ThreadSafeRefCounted<AppendPipeline> {
+public:
+ enum class AppendState { Invalid, NotStarted, Ongoing, KeyNegotiation, DataStarve, Sampling, LastSample, Aborting };
+
+ AppendPipeline(Ref<MediaSourceClientGStreamerMSE>, Ref<SourceBufferPrivateGStreamer>, MediaPlayerPrivateGStreamerMSE&);
+ virtual ~AppendPipeline();
+
+ void handleNeedContextSyncMessage(GstMessage*);
+ void handleApplicationMessage(GstMessage*);
+#if ENABLE(LEGACY_ENCRYPTED_MEDIA)
+ void handleElementMessage(GstMessage*);
+#endif
+
+ gint id();
+ AppendState appendState() { return m_appendState; }
+ void setAppendState(AppendState);
+
+ GstFlowReturn handleNewAppsinkSample(GstElement*);
+ GstFlowReturn pushNewBuffer(GstBuffer*);
+#if ENABLE(LEGACY_ENCRYPTED_MEDIA)
+ void dispatchDecryptionKey(GstBuffer*);
+#endif
+
+ // Takes ownership of caps.
+ void parseDemuxerSrcPadCaps(GstCaps*);
+ void appsinkCapsChanged();
+ void appsinkNewSample(GstSample*);
+ void appsinkEOS();
+ void didReceiveInitializationSegment();
+ AtomicString trackId();
+ void abort();
+
+ void clearPlayerPrivate();
+ Ref<SourceBufferPrivateGStreamer> sourceBufferPrivate() { return m_sourceBufferPrivate.get(); }
+ GstBus* bus() { return m_bus.get(); }
+ GstElement* pipeline() { return m_pipeline.get(); }
+ GstElement* appsrc() { return m_appsrc.get(); }
+ GstElement* appsink() { return m_appsink.get(); }
+ GstCaps* demuxerSrcPadCaps() { return m_demuxerSrcPadCaps.get(); }
+ GstCaps* appsinkCaps() { return m_appsinkCaps.get(); }
+ RefPtr<WebCore::TrackPrivateBase> track() { return m_track; }
+ WebCore::MediaSourceStreamTypeGStreamer streamType() { return m_streamType; }
+
+ void disconnectDemuxerSrcPadFromAppsinkFromAnyThread(GstPad*);
+ void connectDemuxerSrcPadToAppsinkFromAnyThread(GstPad*);
+ void connectDemuxerSrcPadToAppsink(GstPad*);
+
+ void reportAppsrcAtLeastABufferLeft();
+ void reportAppsrcNeedDataReceived();
+
+private:
+ void resetPipeline();
+ void checkEndOfAppend();
+ void handleAppsrcAtLeastABufferLeft();
+ void handleAppsrcNeedDataReceived();
+ void removeAppsrcDataLeavingProbe();
+ void setAppsrcDataLeavingProbe();
+#if ENABLE(LEGACY_ENCRYPTED_MEDIA)
+ void dispatchPendingDecryptionKey();
+#endif
+
+private:
+ Ref<MediaSourceClientGStreamerMSE> m_mediaSourceClient;
+ Ref<SourceBufferPrivateGStreamer> m_sourceBufferPrivate;
+ MediaPlayerPrivateGStreamerMSE* m_playerPrivate;
+
+ // (m_mediaType, m_id) is unique.
+ gint m_id;
+
+ MediaTime m_initialDuration;
+
+ GstFlowReturn m_flowReturn;
+
+ GRefPtr<GstElement> m_pipeline;
+ GRefPtr<GstBus> m_bus;
+ GRefPtr<GstElement> m_appsrc;
+ GRefPtr<GstElement> m_demux;
+#if ENABLE(LEGACY_ENCRYPTED_MEDIA) || ENABLE(ENCRYPTED_MEDIA)
+ GRefPtr<GstElement> m_decryptor;
+#endif
+ // The demuxer has one src stream only, so only one appsink is needed and linked to it.
+ GRefPtr<GstElement> m_appsink;
+
+ Lock m_newSampleLock;
+ Condition m_newSampleCondition;
+ Lock m_padAddRemoveLock;
+ Condition m_padAddRemoveCondition;
+
+ GRefPtr<GstCaps> m_appsinkCaps;
+ GRefPtr<GstCaps> m_demuxerSrcPadCaps;
+ FloatSize m_presentationSize;
+
+ bool m_appsrcAtLeastABufferLeft;
+ bool m_appsrcNeedDataReceived;
+
+ gulong m_appsrcDataLeavingProbeId;
+#if !LOG_DISABLED
+ struct PadProbeInformation m_demuxerDataEnteringPadProbeInformation;
+ struct PadProbeInformation m_appsinkDataEnteringPadProbeInformation;
+#endif
+
+ // Keeps track of the states of append processing, to avoid performing actions inappropriate for the current state
+ // (eg: processing more samples when the last one has been detected, etc.). See setAppendState() for valid
+ // transitions.
+ AppendState m_appendState;
+
+ // Aborts can only be completed when the normal sample detection has finished. Meanwhile, the willing to abort is
+ // expressed in this field.
+ bool m_abortPending;
+
+ WebCore::MediaSourceStreamTypeGStreamer m_streamType;
+ RefPtr<WebCore::TrackPrivateBase> m_oldTrack;
+ RefPtr<WebCore::TrackPrivateBase> m_track;
+
+ GRefPtr<GstBuffer> m_pendingBuffer;
+#if ENABLE(LEGACY_ENCRYPTED_MEDIA)
+ GRefPtr<GstBuffer> m_pendingKey;
+#endif
+};
+
+} // namespace WebCore.
+
+#endif // USE(GSTREAMER)
diff --git a/Source/WebCore/platform/graphics/gstreamer/mse/GStreamerMediaDescription.cpp b/Source/WebCore/platform/graphics/gstreamer/mse/GStreamerMediaDescription.cpp
new file mode 100644
index 000000000..776a0be9b
--- /dev/null
+++ b/Source/WebCore/platform/graphics/gstreamer/mse/GStreamerMediaDescription.cpp
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2016 Metrological Group B.V.
+ * Copyright (C) 2016 Igalia S.L
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * aint with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include "config.h"
+#include "GStreamerMediaDescription.h"
+
+#include "GUniquePtrGStreamer.h"
+
+#include <gst/pbutils/pbutils.h>
+#include <wtf/text/AtomicString.h>
+#include <wtf/text/WTFString.h>
+
+#if ENABLE(VIDEO) && USE(GSTREAMER) && ENABLE(MEDIA_SOURCE)
+
+namespace WebCore {
+
+AtomicString GStreamerMediaDescription::codec() const
+{
+ GUniquePtr<gchar> description(gst_pb_utils_get_codec_description(m_caps.get()));
+ String codecName(description.get());
+
+ // Report "H.264 (Main Profile)" and "H.264 (High Profile)" just as "H.264" to allow changes between both variants
+ // go unnoticed to the SourceBuffer layer.
+ if (codecName.startsWith("H.264")) {
+ size_t braceStart = codecName.find(" (");
+ size_t braceEnd = codecName.find(")");
+ if (braceStart != notFound && braceEnd != notFound)
+ codecName.remove(braceStart, braceEnd-braceStart);
+ }
+
+ return codecName;
+}
+
+bool GStreamerMediaDescription::isVideo() const
+{
+ GstStructure* structure = gst_caps_get_structure(m_caps.get(), 0);
+ const gchar* name = gst_structure_get_name(structure);
+
+ return g_str_has_prefix(name, "video/");
+}
+
+bool GStreamerMediaDescription::isAudio() const
+{
+ GstStructure* structure = gst_caps_get_structure(m_caps.get(), 0);
+ const gchar* name = gst_structure_get_name(structure);
+
+ return g_str_has_prefix(name, "audio/");
+}
+
+bool GStreamerMediaDescription::isText() const
+{
+ // FIXME: Implement proper text track support.
+ return false;
+}
+
+} // namespace WebCore.
+
+#endif // USE(GSTREAMER)
diff --git a/Source/WebCore/platform/graphics/gstreamer/mse/GStreamerMediaDescription.h b/Source/WebCore/platform/graphics/gstreamer/mse/GStreamerMediaDescription.h
new file mode 100644
index 000000000..84e263caa
--- /dev/null
+++ b/Source/WebCore/platform/graphics/gstreamer/mse/GStreamerMediaDescription.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2016 Metrological Group B.V.
+ * Copyright (C) 2016 Igalia S.L
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * aint with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#pragma once
+
+#if ENABLE(VIDEO) && USE(GSTREAMER) && ENABLE(MEDIA_SOURCE)
+
+#include "GRefPtrGStreamer.h"
+#include "MediaDescription.h"
+
+#include <gst/gst.h>
+
+namespace WebCore {
+
+class GStreamerMediaDescription : public MediaDescription {
+public:
+ static Ref<GStreamerMediaDescription> create(GstCaps* caps)
+ {
+ return adoptRef(*new GStreamerMediaDescription(caps));
+ }
+
+ virtual ~GStreamerMediaDescription() = default;
+
+ AtomicString codec() const override;
+ bool isVideo() const override;
+ bool isAudio() const override;
+ bool isText() const override;
+
+private:
+ GStreamerMediaDescription(GstCaps* caps)
+ : MediaDescription()
+ , m_caps(caps)
+ {
+ }
+
+ GRefPtr<GstCaps> m_caps;
+};
+
+} // namespace WebCore.
+
+#endif // USE(GSTREAMER)
diff --git a/Source/WebCore/platform/graphics/gstreamer/mse/GStreamerMediaSample.cpp b/Source/WebCore/platform/graphics/gstreamer/mse/GStreamerMediaSample.cpp
new file mode 100644
index 000000000..86d4329df
--- /dev/null
+++ b/Source/WebCore/platform/graphics/gstreamer/mse/GStreamerMediaSample.cpp
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2016 Metrological Group B.V.
+ * Copyright (C) 2016 Igalia S.L
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * aint with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include "config.h"
+#include "GStreamerMediaSample.h"
+
+#include "GStreamerUtilities.h"
+
+#if ENABLE(VIDEO) && USE(GSTREAMER) && ENABLE(MEDIA_SOURCE)
+
+namespace WebCore {
+
+GStreamerMediaSample::GStreamerMediaSample(GstSample* sample, const FloatSize& presentationSize, const AtomicString& trackId)
+ : MediaSample()
+ , m_pts(MediaTime::zeroTime())
+ , m_dts(MediaTime::zeroTime())
+ , m_duration(MediaTime::zeroTime())
+ , m_trackId(trackId)
+ , m_size(0)
+ , m_presentationSize(presentationSize)
+ , m_flags(MediaSample::IsSync)
+{
+
+ if (!sample)
+ return;
+
+ GstBuffer* buffer = gst_sample_get_buffer(sample);
+ if (!buffer)
+ return;
+
+ auto createMediaTime =
+ [](GstClockTime time) -> MediaTime {
+ return MediaTime(GST_TIME_AS_USECONDS(time), G_USEC_PER_SEC);
+ };
+
+ if (GST_BUFFER_PTS_IS_VALID(buffer))
+ m_pts = createMediaTime(GST_BUFFER_PTS(buffer));
+ if (GST_BUFFER_DTS_IS_VALID(buffer))
+ m_dts = createMediaTime(GST_BUFFER_DTS(buffer));
+ if (GST_BUFFER_DURATION_IS_VALID(buffer))
+ m_duration = createMediaTime(GST_BUFFER_DURATION(buffer));
+
+ m_size = gst_buffer_get_size(buffer);
+ m_sample = sample;
+
+ if (GST_BUFFER_FLAG_IS_SET(buffer, GST_BUFFER_FLAG_DELTA_UNIT))
+ m_flags = MediaSample::None;
+
+ if (GST_BUFFER_FLAG_IS_SET(buffer, GST_BUFFER_FLAG_DECODE_ONLY))
+ m_flags = static_cast<MediaSample::SampleFlags>(m_flags | MediaSample::IsNonDisplaying);
+}
+
+Ref<GStreamerMediaSample> GStreamerMediaSample::createFakeSample(GstCaps*, MediaTime pts, MediaTime dts, MediaTime duration, const FloatSize& presentationSize, const AtomicString& trackId)
+{
+ GStreamerMediaSample* gstreamerMediaSample = new GStreamerMediaSample(nullptr, presentationSize, trackId);
+ gstreamerMediaSample->m_pts = pts;
+ gstreamerMediaSample->m_dts = dts;
+ gstreamerMediaSample->m_duration = duration;
+ gstreamerMediaSample->m_flags = MediaSample::IsNonDisplaying;
+ return adoptRef(*gstreamerMediaSample);
+}
+
+void GStreamerMediaSample::applyPtsOffset(MediaTime timestampOffset)
+{
+ if (m_pts > timestampOffset) {
+ m_duration = m_duration + (m_pts - timestampOffset);
+ m_pts = timestampOffset;
+ }
+}
+
+void GStreamerMediaSample::offsetTimestampsBy(const MediaTime& timestampOffset)
+{
+ if (!timestampOffset)
+ return;
+ m_pts += timestampOffset;
+ m_dts += timestampOffset;
+ GstBuffer* buffer = gst_sample_get_buffer(m_sample.get());
+ if (buffer) {
+ GST_BUFFER_PTS(buffer) = toGstClockTime(m_pts.toFloat());
+ GST_BUFFER_DTS(buffer) = toGstClockTime(m_dts.toFloat());
+ }
+}
+
+Ref<MediaSample> GStreamerMediaSample::createNonDisplayingCopy() const
+{
+ if (!m_sample)
+ return createFakeSample(nullptr, m_pts, m_dts, m_duration, m_presentationSize, m_trackId);
+
+ GstBuffer* buffer = gst_sample_get_buffer(m_sample.get());
+ GST_BUFFER_FLAG_SET(buffer, GST_BUFFER_FLAG_DECODE_ONLY);
+
+ GstCaps* caps = gst_sample_get_caps(m_sample.get());
+ GstSegment* segment = gst_sample_get_segment(m_sample.get());
+ const GstStructure* originalInfo = gst_sample_get_info(m_sample.get());
+ GstStructure* info = originalInfo ? gst_structure_copy(originalInfo) : nullptr;
+ GRefPtr<GstSample> sample = adoptGRef(gst_sample_new(buffer, caps, segment, info));
+
+ return adoptRef(*new GStreamerMediaSample(sample.get(), m_presentationSize, m_trackId));
+}
+
+} // namespace WebCore.
+
+#endif // USE(GSTREAMER)
diff --git a/Source/WebCore/platform/graphics/gstreamer/mse/GStreamerMediaSample.h b/Source/WebCore/platform/graphics/gstreamer/mse/GStreamerMediaSample.h
new file mode 100644
index 000000000..49e12b5c3
--- /dev/null
+++ b/Source/WebCore/platform/graphics/gstreamer/mse/GStreamerMediaSample.h
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2016 Metrological Group B.V.
+ * Copyright (C) 2016 Igalia S.L
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * aint with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#pragma once
+
+#if ENABLE(VIDEO) && USE(GSTREAMER) && ENABLE(MEDIA_SOURCE)
+
+#include "FloatSize.h"
+#include "GRefPtrGStreamer.h"
+#include "MediaSample.h"
+#include <gst/gst.h>
+#include <wtf/text/AtomicString.h>
+
+namespace WebCore {
+
+class GStreamerMediaSample : public MediaSample {
+public:
+ static Ref<GStreamerMediaSample> create(GstSample* sample, const FloatSize& presentationSize, const AtomicString& trackId)
+ {
+ return adoptRef(*new GStreamerMediaSample(sample, presentationSize, trackId));
+ }
+
+ static Ref<GStreamerMediaSample> createFakeSample(GstCaps*, MediaTime pts, MediaTime dts, MediaTime duration, const FloatSize& presentationSize, const AtomicString& trackId);
+
+ void applyPtsOffset(MediaTime);
+ MediaTime presentationTime() const override { return m_pts; }
+ MediaTime decodeTime() const override { return m_dts; }
+ MediaTime duration() const override { return m_duration; }
+ AtomicString trackID() const override { return m_trackId; }
+ void setTrackID(const String& trackId) override { m_trackId = trackId; }
+ size_t sizeInBytes() const override { return m_size; }
+ GstSample* sample() const { return m_sample.get(); }
+ FloatSize presentationSize() const override { return m_presentationSize; }
+ void offsetTimestampsBy(const MediaTime&) override;
+ void setTimestamps(const MediaTime&, const MediaTime&) override { }
+ bool isDivisable() const override { return false; }
+ std::pair<RefPtr<MediaSample>, RefPtr<MediaSample>> divide(const MediaTime&) override { return { nullptr, nullptr }; }
+ Ref<MediaSample> createNonDisplayingCopy() const override;
+ SampleFlags flags() const override { return m_flags; }
+ PlatformSample platformSample() override { return PlatformSample(); }
+ void dump(PrintStream&) const override { }
+
+private:
+ GStreamerMediaSample(GstSample*, const FloatSize& presentationSize, const AtomicString& trackId);
+ virtual ~GStreamerMediaSample() = default;
+
+ MediaTime m_pts;
+ MediaTime m_dts;
+ MediaTime m_duration;
+ AtomicString m_trackId;
+ size_t m_size;
+ GRefPtr<GstSample> m_sample;
+ FloatSize m_presentationSize;
+ MediaSample::SampleFlags m_flags;
+};
+
+} // namespace WebCore.
+
+#endif // USE(GSTREAMER)
diff --git a/Source/WebCore/platform/graphics/gstreamer/mse/MediaPlayerPrivateGStreamerMSE.cpp b/Source/WebCore/platform/graphics/gstreamer/mse/MediaPlayerPrivateGStreamerMSE.cpp
new file mode 100644
index 000000000..4614eb9b9
--- /dev/null
+++ b/Source/WebCore/platform/graphics/gstreamer/mse/MediaPlayerPrivateGStreamerMSE.cpp
@@ -0,0 +1,860 @@
+/*
+ * Copyright (C) 2007, 2009 Apple Inc. All rights reserved.
+ * Copyright (C) 2007 Collabora Ltd. All rights reserved.
+ * Copyright (C) 2007 Alp Toker <alp@atoker.com>
+ * Copyright (C) 2009 Gustavo Noronha Silva <gns@gnome.org>
+ * Copyright (C) 2009, 2010, 2011, 2012, 2013, 2016 Igalia S.L
+ * Copyright (C) 2015 Sebastian Dröge <sebastian@centricular.com>
+ * Copyright (C) 2015, 2016 Metrological Group B.V.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * aint with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include "config.h"
+#include "MediaPlayerPrivateGStreamerMSE.h"
+
+#if ENABLE(VIDEO) && USE(GSTREAMER) && ENABLE(MEDIA_SOURCE)
+
+#include "AppendPipeline.h"
+#include "AudioTrackPrivateGStreamer.h"
+#include "GStreamerUtilities.h"
+#include "InbandTextTrackPrivateGStreamer.h"
+#include "MIMETypeRegistry.h"
+#include "MediaDescription.h"
+#include "MediaPlayer.h"
+#include "NotImplemented.h"
+#include "SourceBufferPrivateGStreamer.h"
+#include "TimeRanges.h"
+#include "URL.h"
+#include "VideoTrackPrivateGStreamer.h"
+
+#include <fnmatch.h>
+#include <gst/app/gstappsink.h>
+#include <gst/app/gstappsrc.h>
+#include <gst/gst.h>
+#include <gst/pbutils/pbutils.h>
+#include <gst/video/video.h>
+#include <wtf/Condition.h>
+#include <wtf/NeverDestroyed.h>
+
+static const char* dumpReadyState(WebCore::MediaPlayer::ReadyState readyState)
+{
+ switch (readyState) {
+ case WebCore::MediaPlayer::HaveNothing: return "HaveNothing";
+ case WebCore::MediaPlayer::HaveMetadata: return "HaveMetadata";
+ case WebCore::MediaPlayer::HaveCurrentData: return "HaveCurrentData";
+ case WebCore::MediaPlayer::HaveFutureData: return "HaveFutureData";
+ case WebCore::MediaPlayer::HaveEnoughData: return "HaveEnoughData";
+ default: return "(unknown)";
+ }
+}
+
+GST_DEBUG_CATEGORY(webkit_mse_debug);
+#define GST_CAT_DEFAULT webkit_mse_debug
+
+namespace WebCore {
+
+void MediaPlayerPrivateGStreamerMSE::registerMediaEngine(MediaEngineRegistrar registrar)
+{
+ if (isAvailable()) {
+ registrar([](MediaPlayer* player) { return std::make_unique<MediaPlayerPrivateGStreamerMSE>(player); },
+ getSupportedTypes, supportsType, nullptr, nullptr, nullptr, supportsKeySystem);
+ }
+}
+
+bool initializeGStreamerAndRegisterWebKitMSEElement()
+{
+ if (UNLIKELY(!initializeGStreamer()))
+ return false;
+
+ registerWebKitGStreamerElements();
+
+ GST_DEBUG_CATEGORY_INIT(webkit_mse_debug, "webkitmse", 0, "WebKit MSE media player");
+
+ GRefPtr<GstElementFactory> WebKitMediaSrcFactory = adoptGRef(gst_element_factory_find("webkitmediasrc"));
+ if (UNLIKELY(!WebKitMediaSrcFactory))
+ gst_element_register(nullptr, "webkitmediasrc", GST_RANK_PRIMARY + 100, WEBKIT_TYPE_MEDIA_SRC);
+ return true;
+}
+
+bool MediaPlayerPrivateGStreamerMSE::isAvailable()
+{
+ if (UNLIKELY(!initializeGStreamerAndRegisterWebKitMSEElement()))
+ return false;
+
+ GRefPtr<GstElementFactory> factory = adoptGRef(gst_element_factory_find("playbin"));
+ return factory;
+}
+
+MediaPlayerPrivateGStreamerMSE::MediaPlayerPrivateGStreamerMSE(MediaPlayer* player)
+ : MediaPlayerPrivateGStreamer(player)
+{
+ GST_TRACE("creating the player (%p)", this);
+}
+
+MediaPlayerPrivateGStreamerMSE::~MediaPlayerPrivateGStreamerMSE()
+{
+ GST_TRACE("destroying the player (%p)", this);
+
+ for (auto iterator : m_appendPipelinesMap)
+ iterator.value->clearPlayerPrivate();
+
+ if (m_source) {
+ webKitMediaSrcSetMediaPlayerPrivate(WEBKIT_MEDIA_SRC(m_source.get()), nullptr);
+ g_signal_handlers_disconnect_by_data(m_source.get(), this);
+ }
+
+ if (m_playbackPipeline)
+ m_playbackPipeline->setWebKitMediaSrc(nullptr);
+}
+
+void MediaPlayerPrivateGStreamerMSE::load(const String& urlString)
+{
+ if (!urlString.startsWith("mediasource")) {
+ // Properly fail so the global MediaPlayer tries to fallback to the next MediaPlayerPrivate.
+ m_networkState = MediaPlayer::FormatError;
+ m_player->networkStateChanged();
+ return;
+ }
+
+ if (UNLIKELY(!initializeGStreamerAndRegisterWebKitMSEElement()))
+ return;
+
+ if (!m_playbackPipeline)
+ m_playbackPipeline = PlaybackPipeline::create();
+
+ MediaPlayerPrivateGStreamer::load(urlString);
+}
+
+void MediaPlayerPrivateGStreamerMSE::load(const String& url, MediaSourcePrivateClient* mediaSource)
+{
+ m_mediaSource = mediaSource;
+ load(String::format("mediasource%s", url.utf8().data()));
+}
+
+void MediaPlayerPrivateGStreamerMSE::pause()
+{
+ m_paused = true;
+ MediaPlayerPrivateGStreamer::pause();
+}
+
+MediaTime MediaPlayerPrivateGStreamerMSE::durationMediaTime() const
+{
+ if (UNLIKELY(!m_pipeline || m_errorOccured))
+ return MediaTime();
+
+ return m_mediaTimeDuration;
+}
+
+void MediaPlayerPrivateGStreamerMSE::seek(float time)
+{
+ if (UNLIKELY(!m_pipeline || m_errorOccured))
+ return;
+
+ GST_INFO("[Seek] seek attempt to %f secs", time);
+
+ // Avoid useless seeking.
+ float current = currentMediaTime().toFloat();
+ if (time == current) {
+ if (!m_seeking)
+ timeChanged();
+ return;
+ }
+
+ if (isLiveStream())
+ return;
+
+ if (m_seeking && m_seekIsPending) {
+ m_seekTime = time;
+ return;
+ }
+
+ GST_DEBUG("Seeking from %f to %f seconds", current, time);
+
+ float prevSeekTime = m_seekTime;
+ m_seekTime = time;
+
+ if (!doSeek()) {
+ m_seekTime = prevSeekTime;
+ GST_WARNING("Seeking to %f failed", time);
+ return;
+ }
+
+ m_isEndReached = false;
+ GST_DEBUG("m_seeking=%s, m_seekTime=%f", m_seeking ? "true" : "false", m_seekTime);
+}
+
+void MediaPlayerPrivateGStreamerMSE::configurePlaySink()
+{
+ MediaPlayerPrivateGStreamer::configurePlaySink();
+
+ GRefPtr<GstElement> playsink = adoptGRef(gst_bin_get_by_name(GST_BIN(m_pipeline.get()), "playsink"));
+ if (playsink) {
+ // The default value (0) means "send events to all the sinks", instead
+ // of "only to the first that returns true". This is needed for MSE seek.
+ g_object_set(G_OBJECT(playsink.get()), "send-event-mode", 0, nullptr);
+ }
+}
+
+bool MediaPlayerPrivateGStreamerMSE::changePipelineState(GstState newState)
+{
+ if (seeking()) {
+ GST_DEBUG("Rejected state change to %s while seeking",
+ gst_element_state_get_name(newState));
+ return true;
+ }
+
+ return MediaPlayerPrivateGStreamer::changePipelineState(newState);
+}
+
+void MediaPlayerPrivateGStreamerMSE::notifySeekNeedsDataForTime(const MediaTime& seekTime)
+{
+ // Reenqueue samples needed to resume playback in the new position.
+ m_mediaSource->seekToTime(seekTime);
+
+ GST_DEBUG("MSE seek to %f finished", seekTime.toDouble());
+
+ if (!m_gstSeekCompleted) {
+ m_gstSeekCompleted = true;
+ maybeFinishSeek();
+ }
+}
+
+bool MediaPlayerPrivateGStreamerMSE::doSeek(gint64, float, GstSeekFlags)
+{
+ // Use doSeek() instead. If anybody is calling this version of doSeek(), something is wrong.
+ ASSERT_NOT_REACHED();
+ return false;
+}
+
+bool MediaPlayerPrivateGStreamerMSE::doSeek()
+{
+ GstClockTime position = toGstClockTime(m_seekTime);
+ MediaTime seekTime = MediaTime::createWithDouble(m_seekTime);
+ double rate = m_player->rate();
+ GstSeekFlags seekType = static_cast<GstSeekFlags>(GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_ACCURATE);
+
+ // Always move to seeking state to report correct 'currentTime' while pending for actual seek to complete.
+ m_seeking = true;
+
+ // Check if playback pipeline is ready for seek.
+ GstState state, newState;
+ GstStateChangeReturn getStateResult = gst_element_get_state(m_pipeline.get(), &state, &newState, 0);
+ if (getStateResult == GST_STATE_CHANGE_FAILURE || getStateResult == GST_STATE_CHANGE_NO_PREROLL) {
+ GST_DEBUG("[Seek] cannot seek, current state change is %s", gst_element_state_change_return_get_name(getStateResult));
+ webKitMediaSrcSetReadyForSamples(WEBKIT_MEDIA_SRC(m_source.get()), true);
+ m_seeking = false;
+ return false;
+ }
+ if ((getStateResult == GST_STATE_CHANGE_ASYNC
+ && !(state == GST_STATE_PLAYING && newState == GST_STATE_PAUSED))
+ || state < GST_STATE_PAUSED
+ || m_isEndReached
+ || !m_gstSeekCompleted) {
+ CString reason = "Unknown reason";
+ if (getStateResult == GST_STATE_CHANGE_ASYNC) {
+ reason = String::format("In async change %s --> %s",
+ gst_element_state_get_name(state),
+ gst_element_state_get_name(newState)).utf8();
+ } else if (state < GST_STATE_PAUSED)
+ reason = "State less than PAUSED";
+ else if (m_isEndReached)
+ reason = "End reached";
+ else if (!m_gstSeekCompleted)
+ reason = "Previous seek is not finished yet";
+
+ GST_DEBUG("[Seek] Delaying the seek: %s", reason.data());
+
+ m_seekIsPending = true;
+
+ if (m_isEndReached) {
+ GST_DEBUG("[Seek] reset pipeline");
+ m_resetPipeline = true;
+ m_seeking = false;
+ if (!changePipelineState(GST_STATE_PAUSED))
+ loadingFailed(MediaPlayer::Empty);
+ else
+ m_seeking = true;
+ }
+
+ return m_seeking;
+ }
+
+ // Stop accepting new samples until actual seek is finished.
+ webKitMediaSrcSetReadyForSamples(WEBKIT_MEDIA_SRC(m_source.get()), false);
+
+ // Correct seek time if it helps to fix a small gap.
+ if (!isTimeBuffered(seekTime)) {
+ // Look if a near future time (<0.1 sec.) is buffered and change the seek target time.
+ if (m_mediaSource) {
+ const MediaTime miniGap = MediaTime::createWithDouble(0.1);
+ MediaTime nearest = m_mediaSource->buffered()->nearest(seekTime);
+ if (nearest.isValid() && nearest > seekTime && (nearest - seekTime) <= miniGap && isTimeBuffered(nearest + miniGap)) {
+ GST_DEBUG("[Seek] Changed the seek target time from %f to %f, a near point in the future", seekTime.toFloat(), nearest.toFloat());
+ seekTime = nearest;
+ }
+ }
+ }
+
+ // Check if MSE has samples for requested time and defer actual seek if needed.
+ if (!isTimeBuffered(seekTime)) {
+ GST_DEBUG("[Seek] Delaying the seek: MSE is not ready");
+ GstStateChangeReturn setStateResult = gst_element_set_state(m_pipeline.get(), GST_STATE_PAUSED);
+ if (setStateResult == GST_STATE_CHANGE_FAILURE) {
+ GST_DEBUG("[Seek] Cannot seek, failed to pause playback pipeline.");
+ webKitMediaSrcSetReadyForSamples(WEBKIT_MEDIA_SRC(m_source.get()), true);
+ m_seeking = false;
+ return false;
+ }
+ m_readyState = MediaPlayer::HaveMetadata;
+ notifySeekNeedsDataForTime(seekTime);
+ ASSERT(!m_mseSeekCompleted);
+ return true;
+ }
+
+ // Complete previous MSE seek if needed.
+ if (!m_mseSeekCompleted) {
+ m_mediaSource->monitorSourceBuffers();
+ ASSERT(m_mseSeekCompleted);
+ // Note: seekCompleted will recursively call us.
+ return m_seeking;
+ }
+
+ GST_DEBUG("We can seek now");
+
+ gint64 startTime = position, endTime = GST_CLOCK_TIME_NONE;
+ if (rate < 0) {
+ startTime = 0;
+ endTime = position;
+ }
+
+ if (!rate)
+ rate = 1;
+
+ GST_DEBUG("Actual seek to %" GST_TIME_FORMAT ", end time: %" GST_TIME_FORMAT ", rate: %f", GST_TIME_ARGS(startTime), GST_TIME_ARGS(endTime), rate);
+
+ // This will call notifySeekNeedsData() after some time to tell that the pipeline is ready for sample enqueuing.
+ webKitMediaSrcPrepareSeek(WEBKIT_MEDIA_SRC(m_source.get()), seekTime);
+
+ m_gstSeekCompleted = false;
+ if (!gst_element_seek(m_pipeline.get(), rate, GST_FORMAT_TIME, seekType, GST_SEEK_TYPE_SET, startTime, GST_SEEK_TYPE_SET, endTime)) {
+ webKitMediaSrcSetReadyForSamples(WEBKIT_MEDIA_SRC(m_source.get()), true);
+ m_seeking = false;
+ m_gstSeekCompleted = true;
+ GST_DEBUG("doSeek(): gst_element_seek() failed, returning false");
+ return false;
+ }
+
+ // The samples will be enqueued in notifySeekNeedsData().
+ GST_DEBUG("doSeek(): gst_element_seek() succeeded, returning true");
+ return true;
+}
+
+void MediaPlayerPrivateGStreamerMSE::maybeFinishSeek()
+{
+ if (!m_seeking || !m_mseSeekCompleted || !m_gstSeekCompleted)
+ return;
+
+ GstState state, newState;
+ GstStateChangeReturn getStateResult = gst_element_get_state(m_pipeline.get(), &state, &newState, 0);
+
+ if (getStateResult == GST_STATE_CHANGE_ASYNC
+ && !(state == GST_STATE_PLAYING && newState == GST_STATE_PAUSED)) {
+ GST_DEBUG("[Seek] Delaying seek finish");
+ return;
+ }
+
+ if (m_seekIsPending) {
+ GST_DEBUG("[Seek] Committing pending seek to %f", m_seekTime);
+ m_seekIsPending = false;
+ if (!doSeek()) {
+ GST_WARNING("[Seek] Seeking to %f failed", m_seekTime);
+ m_cachedPosition = -1;
+ }
+ return;
+ }
+
+ GST_DEBUG("[Seek] Seeked to %f", m_seekTime);
+
+ webKitMediaSrcSetReadyForSamples(WEBKIT_MEDIA_SRC(m_source.get()), true);
+ m_seeking = false;
+ m_cachedPosition = -1;
+ // The pipeline can still have a pending state. In this case a position query will fail.
+ // Right now we can use m_seekTime as a fallback.
+ m_canFallBackToLastFinishedSeekPosition = true;
+ timeChanged();
+}
+
+void MediaPlayerPrivateGStreamerMSE::updatePlaybackRate()
+{
+ notImplemented();
+}
+
+bool MediaPlayerPrivateGStreamerMSE::seeking() const
+{
+ return m_seeking;
+}
+
+// FIXME: MediaPlayerPrivateGStreamer manages the ReadyState on its own. We shouldn't change it manually.
+void MediaPlayerPrivateGStreamerMSE::setReadyState(MediaPlayer::ReadyState readyState)
+{
+ if (readyState == m_readyState)
+ return;
+
+ if (seeking()) {
+ GST_DEBUG("Skip ready state change(%s -> %s) due to seek\n", dumpReadyState(m_readyState), dumpReadyState(readyState));
+ return;
+ }
+
+ GST_DEBUG("Ready State Changed manually from %u to %u", m_readyState, readyState);
+ MediaPlayer::ReadyState oldReadyState = m_readyState;
+ m_readyState = readyState;
+ GST_DEBUG("m_readyState: %s -> %s", dumpReadyState(oldReadyState), dumpReadyState(m_readyState));
+
+ if (oldReadyState < MediaPlayer::HaveCurrentData && m_readyState >= MediaPlayer::HaveCurrentData) {
+ GST_DEBUG("[Seek] Reporting load state changed to trigger seek continuation");
+ loadStateChanged();
+ }
+ m_player->readyStateChanged();
+
+ GstState pipelineState;
+ GstStateChangeReturn getStateResult = gst_element_get_state(m_pipeline.get(), &pipelineState, nullptr, 250 * GST_NSECOND);
+ bool isPlaying = (getStateResult == GST_STATE_CHANGE_SUCCESS && pipelineState == GST_STATE_PLAYING);
+
+ if (m_readyState == MediaPlayer::HaveMetadata && oldReadyState > MediaPlayer::HaveMetadata && isPlaying) {
+ GST_TRACE("Changing pipeline to PAUSED...");
+ bool ok = changePipelineState(GST_STATE_PAUSED);
+ GST_TRACE("Changed pipeline to PAUSED: %s", ok ? "Success" : "Error");
+ }
+}
+
+void MediaPlayerPrivateGStreamerMSE::waitForSeekCompleted()
+{
+ if (!m_seeking)
+ return;
+
+ GST_DEBUG("Waiting for MSE seek completed");
+ m_mseSeekCompleted = false;
+}
+
+void MediaPlayerPrivateGStreamerMSE::seekCompleted()
+{
+ if (m_mseSeekCompleted)
+ return;
+
+ GST_DEBUG("MSE seek completed");
+ m_mseSeekCompleted = true;
+
+ doSeek();
+
+ if (!seeking() && m_readyState >= MediaPlayer::HaveFutureData)
+ changePipelineState(GST_STATE_PLAYING);
+
+ if (!seeking())
+ m_player->timeChanged();
+}
+
+void MediaPlayerPrivateGStreamerMSE::setRate(float)
+{
+ notImplemented();
+}
+
+std::unique_ptr<PlatformTimeRanges> MediaPlayerPrivateGStreamerMSE::buffered() const
+{
+ return m_mediaSource ? m_mediaSource->buffered() : std::make_unique<PlatformTimeRanges>();
+}
+
+void MediaPlayerPrivateGStreamerMSE::sourceChanged()
+{
+ m_source = nullptr;
+ g_object_get(m_pipeline.get(), "source", &m_source.outPtr(), nullptr);
+
+ ASSERT(WEBKIT_IS_MEDIA_SRC(m_source.get()));
+
+ m_playbackPipeline->setWebKitMediaSrc(WEBKIT_MEDIA_SRC(m_source.get()));
+
+ MediaSourceGStreamer::open(*m_mediaSource.get(), *this);
+ g_signal_connect_swapped(m_source.get(), "video-changed", G_CALLBACK(videoChangedCallback), this);
+ g_signal_connect_swapped(m_source.get(), "audio-changed", G_CALLBACK(audioChangedCallback), this);
+ g_signal_connect_swapped(m_source.get(), "text-changed", G_CALLBACK(textChangedCallback), this);
+ webKitMediaSrcSetMediaPlayerPrivate(WEBKIT_MEDIA_SRC(m_source.get()), this);
+}
+
+void MediaPlayerPrivateGStreamerMSE::updateStates()
+{
+ if (UNLIKELY(!m_pipeline || m_errorOccured))
+ return;
+
+ MediaPlayer::NetworkState oldNetworkState = m_networkState;
+ MediaPlayer::ReadyState oldReadyState = m_readyState;
+ GstState state, pending;
+
+ GstStateChangeReturn getStateResult = gst_element_get_state(m_pipeline.get(), &state, &pending, 250 * GST_NSECOND);
+
+ bool shouldUpdatePlaybackState = false;
+ switch (getStateResult) {
+ case GST_STATE_CHANGE_SUCCESS: {
+ GST_DEBUG("State: %s, pending: %s", gst_element_state_get_name(state), gst_element_state_get_name(pending));
+
+ // Do nothing if on EOS and state changed to READY to avoid recreating the player
+ // on HTMLMediaElement and properly generate the video 'ended' event.
+ if (m_isEndReached && state == GST_STATE_READY)
+ break;
+
+ m_resetPipeline = (state <= GST_STATE_READY);
+ if (m_resetPipeline)
+ m_mediaTimeDuration = MediaTime::zeroTime();
+
+ // Update ready and network states.
+ switch (state) {
+ case GST_STATE_NULL:
+ m_readyState = MediaPlayer::HaveNothing;
+ GST_DEBUG("m_readyState=%s", dumpReadyState(m_readyState));
+ m_networkState = MediaPlayer::Empty;
+ break;
+ case GST_STATE_READY:
+ m_readyState = MediaPlayer::HaveMetadata;
+ GST_DEBUG("m_readyState=%s", dumpReadyState(m_readyState));
+ m_networkState = MediaPlayer::Empty;
+ break;
+ case GST_STATE_PAUSED:
+ case GST_STATE_PLAYING:
+ if (seeking()) {
+ m_readyState = MediaPlayer::HaveMetadata;
+ // FIXME: Should we manage NetworkState too?
+ GST_DEBUG("m_readyState=%s", dumpReadyState(m_readyState));
+ } else if (m_buffering) {
+ if (m_bufferingPercentage == 100) {
+ GST_DEBUG("[Buffering] Complete.");
+ m_buffering = false;
+ m_readyState = MediaPlayer::HaveEnoughData;
+ GST_DEBUG("m_readyState=%s", dumpReadyState(m_readyState));
+ m_networkState = m_downloadFinished ? MediaPlayer::Idle : MediaPlayer::Loading;
+ } else {
+ m_readyState = MediaPlayer::HaveCurrentData;
+ GST_DEBUG("m_readyState=%s", dumpReadyState(m_readyState));
+ m_networkState = MediaPlayer::Loading;
+ }
+ } else if (m_downloadFinished) {
+ m_readyState = MediaPlayer::HaveEnoughData;
+ GST_DEBUG("m_readyState=%s", dumpReadyState(m_readyState));
+ m_networkState = MediaPlayer::Loaded;
+ } else {
+ m_readyState = MediaPlayer::HaveFutureData;
+ GST_DEBUG("m_readyState=%s", dumpReadyState(m_readyState));
+ m_networkState = MediaPlayer::Loading;
+ }
+
+ if (m_eosMarked && state == GST_STATE_PLAYING)
+ m_eosPending = true;
+
+ break;
+ default:
+ ASSERT_NOT_REACHED();
+ break;
+ }
+
+ // Sync states where needed.
+ if (state == GST_STATE_PAUSED) {
+ if (!m_volumeAndMuteInitialized) {
+ notifyPlayerOfVolumeChange();
+ notifyPlayerOfMute();
+ m_volumeAndMuteInitialized = true;
+ }
+
+ if (!seeking() && !m_buffering && !m_paused && m_playbackRate) {
+ GST_DEBUG("[Buffering] Restarting playback.");
+ changePipelineState(GST_STATE_PLAYING);
+ }
+ } else if (state == GST_STATE_PLAYING) {
+ m_paused = false;
+
+ if ((m_buffering && !isLiveStream()) || !m_playbackRate) {
+ GST_DEBUG("[Buffering] Pausing stream for buffering.");
+ changePipelineState(GST_STATE_PAUSED);
+ }
+ } else
+ m_paused = true;
+
+ if (m_requestedState == GST_STATE_PAUSED && state == GST_STATE_PAUSED) {
+ shouldUpdatePlaybackState = true;
+ GST_DEBUG("Requested state change to %s was completed", gst_element_state_get_name(state));
+ }
+
+ break;
+ }
+ case GST_STATE_CHANGE_ASYNC:
+ GST_DEBUG("Async: State: %s, pending: %s", gst_element_state_get_name(state), gst_element_state_get_name(pending));
+ // Change in progress.
+ break;
+ case GST_STATE_CHANGE_FAILURE:
+ GST_WARNING("Failure: State: %s, pending: %s", gst_element_state_get_name(state), gst_element_state_get_name(pending));
+ // Change failed.
+ return;
+ case GST_STATE_CHANGE_NO_PREROLL:
+ GST_DEBUG("No preroll: State: %s, pending: %s", gst_element_state_get_name(state), gst_element_state_get_name(pending));
+
+ // Live pipelines go in PAUSED without prerolling.
+ m_isStreaming = true;
+
+ if (state == GST_STATE_READY) {
+ m_readyState = MediaPlayer::HaveNothing;
+ GST_DEBUG("m_readyState=%s", dumpReadyState(m_readyState));
+ } else if (state == GST_STATE_PAUSED) {
+ m_readyState = MediaPlayer::HaveEnoughData;
+ GST_DEBUG("m_readyState=%s", dumpReadyState(m_readyState));
+ m_paused = true;
+ } else if (state == GST_STATE_PLAYING)
+ m_paused = false;
+
+ if (!m_paused && m_playbackRate)
+ changePipelineState(GST_STATE_PLAYING);
+
+ m_networkState = MediaPlayer::Loading;
+ break;
+ default:
+ GST_DEBUG("Else : %d", getStateResult);
+ break;
+ }
+
+ m_requestedState = GST_STATE_VOID_PENDING;
+
+ if (shouldUpdatePlaybackState)
+ m_player->playbackStateChanged();
+
+ if (m_networkState != oldNetworkState) {
+ GST_DEBUG("Network State Changed from %u to %u", oldNetworkState, m_networkState);
+ m_player->networkStateChanged();
+ }
+ if (m_readyState != oldReadyState) {
+ GST_DEBUG("Ready State Changed from %u to %u", oldReadyState, m_readyState);
+ m_player->readyStateChanged();
+ }
+
+ if (getStateResult == GST_STATE_CHANGE_SUCCESS && state >= GST_STATE_PAUSED) {
+ updatePlaybackRate();
+ maybeFinishSeek();
+ }
+}
+void MediaPlayerPrivateGStreamerMSE::asyncStateChangeDone()
+{
+ if (UNLIKELY(!m_pipeline || m_errorOccured))
+ return;
+
+ if (m_seeking)
+ maybeFinishSeek();
+ else
+ updateStates();
+}
+
+bool MediaPlayerPrivateGStreamerMSE::isTimeBuffered(const MediaTime &time) const
+{
+ bool result = m_mediaSource && m_mediaSource->buffered()->contain(time);
+ GST_DEBUG("Time %f buffered? %s", time.toDouble(), result ? "Yes" : "No");
+ return result;
+}
+
+void MediaPlayerPrivateGStreamerMSE::setMediaSourceClient(Ref<MediaSourceClientGStreamerMSE> client)
+{
+ m_mediaSourceClient = client.ptr();
+}
+
+RefPtr<MediaSourceClientGStreamerMSE> MediaPlayerPrivateGStreamerMSE::mediaSourceClient()
+{
+ return m_mediaSourceClient;
+}
+
+void MediaPlayerPrivateGStreamerMSE::durationChanged()
+{
+ if (!m_mediaSourceClient) {
+ GST_DEBUG("m_mediaSourceClient is null, doing nothing");
+ return;
+ }
+
+ MediaTime previousDuration = m_mediaTimeDuration;
+ m_mediaTimeDuration = m_mediaSourceClient->duration();
+
+ GST_TRACE("previous=%f, new=%f", previousDuration.toFloat(), m_mediaTimeDuration.toFloat());
+
+ // Avoid emiting durationchanged in the case where the previous duration was 0 because that case is already handled
+ // by the HTMLMediaElement.
+ if (m_mediaTimeDuration != previousDuration && m_mediaTimeDuration.isValid() && previousDuration.isValid()) {
+ m_player->durationChanged();
+ m_playbackPipeline->notifyDurationChanged();
+ m_mediaSource->durationChanged(m_mediaTimeDuration);
+ }
+}
+
+static HashSet<String, ASCIICaseInsensitiveHash>& mimeTypeCache()
+{
+ static NeverDestroyed<HashSet<String, ASCIICaseInsensitiveHash>> cache = []()
+ {
+ initializeGStreamerAndRegisterWebKitMSEElement();
+ HashSet<String, ASCIICaseInsensitiveHash> set;
+ const char* mimeTypes[] = {
+ "video/mp4",
+ "audio/mp4"
+ };
+ for (auto& type : mimeTypes)
+ set.add(type);
+ return set;
+ }();
+ return cache;
+}
+
+void MediaPlayerPrivateGStreamerMSE::getSupportedTypes(HashSet<String, ASCIICaseInsensitiveHash>& types)
+{
+ types = mimeTypeCache();
+}
+
+void MediaPlayerPrivateGStreamerMSE::trackDetected(RefPtr<AppendPipeline> appendPipeline, RefPtr<WebCore::TrackPrivateBase> oldTrack, RefPtr<WebCore::TrackPrivateBase> newTrack)
+{
+ ASSERT(appendPipeline->track() == newTrack);
+
+ GstCaps* caps = appendPipeline->appsinkCaps();
+ ASSERT(caps);
+ GST_DEBUG("track ID: %s, caps: %" GST_PTR_FORMAT, newTrack->id().string().latin1().data(), caps);
+
+ GstStructure* structure = gst_caps_get_structure(caps, 0);
+ const gchar* mediaType = gst_structure_get_name(structure);
+ GstVideoInfo info;
+
+ if (g_str_has_prefix(mediaType, "video/") && gst_video_info_from_caps(&info, caps)) {
+ float width, height;
+
+ width = info.width;
+ height = info.height * ((float) info.par_d / (float) info.par_n);
+ m_videoSize.setWidth(width);
+ m_videoSize.setHeight(height);
+ }
+
+ if (!oldTrack)
+ m_playbackPipeline->attachTrack(appendPipeline->sourceBufferPrivate(), newTrack, structure, caps);
+ else
+ m_playbackPipeline->reattachTrack(appendPipeline->sourceBufferPrivate(), newTrack);
+}
+
+bool MediaPlayerPrivateGStreamerMSE::supportsCodecs(const String& codecs)
+{
+ static Vector<const char*> supportedCodecs = { "avc*", "mp4a*", "mpeg", "x-h264" };
+ Vector<String> codecEntries;
+ codecs.split(',', false, codecEntries);
+
+ for (String codec : codecEntries) {
+ bool isCodecSupported = false;
+
+ // If the codec is named like a mimetype (eg: video/avc) remove the "video/" part.
+ size_t slashIndex = codec.find('/');
+ if (slashIndex != WTF::notFound)
+ codec = codec.substring(slashIndex+1);
+
+ const char* codecData = codec.utf8().data();
+ for (const auto& pattern : supportedCodecs) {
+ isCodecSupported = !fnmatch(pattern, codecData, 0);
+ if (isCodecSupported)
+ break;
+ }
+ if (!isCodecSupported)
+ return false;
+ }
+
+ return true;
+}
+
+MediaPlayer::SupportsType MediaPlayerPrivateGStreamerMSE::supportsType(const MediaEngineSupportParameters& parameters)
+{
+ MediaPlayer::SupportsType result = MediaPlayer::IsNotSupported;
+ if (!parameters.isMediaSource)
+ return result;
+
+ // Disable VPX/Opus on MSE for now, mp4/avc1 seems way more reliable currently.
+ if (parameters.type.endsWith("webm"))
+ return result;
+
+ // YouTube TV provides empty types for some videos and we want to be selected as best media engine for them.
+ if (parameters.type.isEmpty()) {
+ result = MediaPlayer::MayBeSupported;
+ return result;
+ }
+
+ // Spec says we should not return "probably" if the codecs string is empty.
+ if (mimeTypeCache().contains(parameters.type)) {
+ if (parameters.codecs.isEmpty())
+ result = MediaPlayer::MayBeSupported;
+ else
+ result = supportsCodecs(parameters.codecs) ? MediaPlayer::IsSupported : MediaPlayer::IsNotSupported;
+ }
+
+ return extendedSupportsType(parameters, result);
+}
+
+#if ENABLE(LEGACY_ENCRYPTED_MEDIA)
+void MediaPlayerPrivateGStreamerMSE::dispatchDecryptionKey(GstBuffer* buffer)
+{
+ for (auto it : m_appendPipelinesMap)
+ it.value->dispatchDecryptionKey(buffer);
+}
+#endif
+
+void MediaPlayerPrivateGStreamerMSE::markEndOfStream(MediaSourcePrivate::EndOfStreamStatus status)
+{
+ if (status != MediaSourcePrivate::EosNoError)
+ return;
+
+ GST_DEBUG("Marking end of stream");
+ m_eosMarked = true;
+ updateStates();
+}
+
+MediaTime MediaPlayerPrivateGStreamerMSE::currentMediaTime() const
+{
+ MediaTime position = MediaPlayerPrivateGStreamer::currentMediaTime();
+
+ if (m_eosPending && (paused() || (position >= durationMediaTime()))) {
+ if (m_networkState != MediaPlayer::Loaded) {
+ m_networkState = MediaPlayer::Loaded;
+ m_player->networkStateChanged();
+ }
+
+ m_eosPending = false;
+ m_isEndReached = true;
+ m_cachedPosition = m_mediaTimeDuration.toFloat();
+ m_durationAtEOS = m_mediaTimeDuration.toFloat();
+ m_player->timeChanged();
+ }
+ return position;
+}
+
+float MediaPlayerPrivateGStreamerMSE::maxTimeSeekable() const
+{
+ if (UNLIKELY(m_errorOccured))
+ return 0;
+
+ GST_DEBUG("maxTimeSeekable");
+ float result = durationMediaTime().toFloat();
+ // Infinite duration means live stream.
+ if (std::isinf(result)) {
+ MediaTime maxBufferedTime = buffered()->maximumBufferedTime();
+ // Return the highest end time reported by the buffered attribute.
+ result = maxBufferedTime.isValid() ? maxBufferedTime.toFloat() : 0;
+ }
+
+ return result;
+}
+
+} // namespace WebCore.
+
+#endif // USE(GSTREAMER)
diff --git a/Source/WebCore/platform/graphics/gstreamer/mse/MediaPlayerPrivateGStreamerMSE.h b/Source/WebCore/platform/graphics/gstreamer/mse/MediaPlayerPrivateGStreamerMSE.h
new file mode 100644
index 000000000..0d3ebb902
--- /dev/null
+++ b/Source/WebCore/platform/graphics/gstreamer/mse/MediaPlayerPrivateGStreamerMSE.h
@@ -0,0 +1,132 @@
+/*
+ * Copyright (C) 2007, 2009 Apple Inc. All rights reserved.
+ * Copyright (C) 2007 Collabora Ltd. All rights reserved.
+ * Copyright (C) 2007 Alp Toker <alp@atoker.com>
+ * Copyright (C) 2009, 2010, 2016 Igalia S.L
+ * Copyright (C) 2015 Sebastian Dröge <sebastian@centricular.com>
+ * Copyright (C) 2015, 2016 Metrological Group B.V.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * aint with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#pragma once
+
+#if ENABLE(VIDEO) && USE(GSTREAMER) && ENABLE(MEDIA_SOURCE)
+
+#include "GRefPtrGStreamer.h"
+#include "MediaPlayerPrivateGStreamer.h"
+#include "MediaSample.h"
+#include "MediaSourceGStreamer.h"
+#include "PlaybackPipeline.h"
+#include "WebKitMediaSourceGStreamer.h"
+
+namespace WebCore {
+
+class MediaSourceClientGStreamerMSE;
+class AppendPipeline;
+class PlaybackPipeline;
+
+class MediaPlayerPrivateGStreamerMSE : public MediaPlayerPrivateGStreamer {
+ WTF_MAKE_NONCOPYABLE(MediaPlayerPrivateGStreamerMSE); WTF_MAKE_FAST_ALLOCATED;
+
+ friend class MediaSourceClientGStreamerMSE;
+
+public:
+ explicit MediaPlayerPrivateGStreamerMSE(MediaPlayer*);
+ virtual ~MediaPlayerPrivateGStreamerMSE();
+
+ static void registerMediaEngine(MediaEngineRegistrar);
+
+ void load(const String&) override;
+ void load(const String&, MediaSourcePrivateClient*) override;
+
+ void setDownloadBuffering() override { };
+
+ bool isLiveStream() const override { return false; }
+ MediaTime currentMediaTime() const override;
+
+ void pause() override;
+ bool seeking() const override;
+ void seek(float) override;
+ void configurePlaySink() override;
+ bool changePipelineState(GstState) override;
+
+ void durationChanged() override;
+ MediaTime durationMediaTime() const override;
+
+ void setRate(float) override;
+ std::unique_ptr<PlatformTimeRanges> buffered() const override;
+ float maxTimeSeekable() const override;
+
+ void sourceChanged() override;
+
+ void setReadyState(MediaPlayer::ReadyState);
+ void waitForSeekCompleted();
+ void seekCompleted();
+ MediaSourcePrivateClient* mediaSourcePrivateClient() { return m_mediaSource.get(); }
+
+ void markEndOfStream(MediaSourcePrivate::EndOfStreamStatus);
+
+#if ENABLE(LEGACY_ENCRYPTED_MEDIA)
+ void dispatchDecryptionKey(GstBuffer*) override;
+#endif
+
+ void trackDetected(RefPtr<AppendPipeline>, RefPtr<WebCore::TrackPrivateBase> oldTrack, RefPtr<WebCore::TrackPrivateBase> newTrack);
+ void notifySeekNeedsDataForTime(const MediaTime&);
+
+ static bool supportsCodecs(const String& codecs);
+
+private:
+ static void getSupportedTypes(HashSet<String, ASCIICaseInsensitiveHash>&);
+ static MediaPlayer::SupportsType supportsType(const MediaEngineSupportParameters&);
+
+ static bool isAvailable();
+
+ // FIXME: Reduce code duplication.
+ void updateStates() override;
+
+ bool doSeek(gint64, float, GstSeekFlags) override;
+ bool doSeek();
+ void maybeFinishSeek();
+ void updatePlaybackRate() override;
+ void asyncStateChangeDone() override;
+
+ // FIXME: Implement.
+ unsigned long totalVideoFrames() override { return 0; }
+ unsigned long droppedVideoFrames() override { return 0; }
+ unsigned long corruptedVideoFrames() override { return 0; }
+ MediaTime totalFrameDelay() override { return MediaTime::zeroTime(); }
+ bool isTimeBuffered(const MediaTime&) const;
+
+ bool isMediaSource() const override { return true; }
+
+ void setMediaSourceClient(Ref<MediaSourceClientGStreamerMSE>);
+ RefPtr<MediaSourceClientGStreamerMSE> mediaSourceClient();
+
+ HashMap<RefPtr<SourceBufferPrivateGStreamer>, RefPtr<AppendPipeline>> m_appendPipelinesMap;
+ bool m_eosMarked = false;
+ mutable bool m_eosPending = false;
+ bool m_gstSeekCompleted = true;
+ RefPtr<MediaSourcePrivateClient> m_mediaSource;
+ RefPtr<MediaSourceClientGStreamerMSE> m_mediaSourceClient;
+ MediaTime m_mediaTimeDuration;
+ bool m_mseSeekCompleted = true;
+ RefPtr<PlaybackPipeline> m_playbackPipeline;
+};
+
+} // namespace WebCore
+
+#endif // USE(GSTREAMER)
diff --git a/Source/WebCore/platform/graphics/gstreamer/mse/MediaSourceClientGStreamerMSE.cpp b/Source/WebCore/platform/graphics/gstreamer/mse/MediaSourceClientGStreamerMSE.cpp
new file mode 100644
index 000000000..441401e6a
--- /dev/null
+++ b/Source/WebCore/platform/graphics/gstreamer/mse/MediaSourceClientGStreamerMSE.cpp
@@ -0,0 +1,216 @@
+/*
+ * Copyright (C) 2016 Metrological Group B.V.
+ * Copyright (C) 2016 Igalia S.L
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * aint with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include "config.h"
+#include "MediaSourceClientGStreamerMSE.h"
+
+#include "AppendPipeline.h"
+#include "MediaPlayerPrivateGStreamerMSE.h"
+#include "WebKitMediaSourceGStreamer.h"
+#include <gst/gst.h>
+
+GST_DEBUG_CATEGORY_EXTERN(webkit_mse_debug);
+#define GST_CAT_DEFAULT webkit_mse_debug
+
+#if ENABLE(VIDEO) && USE(GSTREAMER) && ENABLE(MEDIA_SOURCE)
+
+namespace WebCore {
+
+Ref<MediaSourceClientGStreamerMSE> MediaSourceClientGStreamerMSE::create(MediaPlayerPrivateGStreamerMSE& playerPrivate)
+{
+ ASSERT(WTF::isMainThread());
+
+ // No return adoptRef(new MediaSourceClientGStreamerMSE(playerPrivate)) because the ownership has already been transferred to MediaPlayerPrivateGStreamerMSE.
+ Ref<MediaSourceClientGStreamerMSE> client(adoptRef(*new MediaSourceClientGStreamerMSE(playerPrivate)));
+ playerPrivate.setMediaSourceClient(client.get());
+ return client;
+}
+
+MediaSourceClientGStreamerMSE::MediaSourceClientGStreamerMSE(MediaPlayerPrivateGStreamerMSE& playerPrivate)
+ : m_playerPrivate(&playerPrivate)
+ , m_duration(MediaTime::invalidTime())
+{
+ ASSERT(WTF::isMainThread());
+}
+
+MediaSourceClientGStreamerMSE::~MediaSourceClientGStreamerMSE()
+{
+ ASSERT(WTF::isMainThread());
+}
+
+MediaSourcePrivate::AddStatus MediaSourceClientGStreamerMSE::addSourceBuffer(RefPtr<SourceBufferPrivateGStreamer> sourceBufferPrivate, const ContentType&)
+{
+ ASSERT(WTF::isMainThread());
+
+ if (!m_playerPrivate)
+ return MediaSourcePrivate::AddStatus::NotSupported;
+
+ ASSERT(m_playerPrivate->m_playbackPipeline);
+ ASSERT(sourceBufferPrivate);
+
+ RefPtr<AppendPipeline> appendPipeline = adoptRef(new AppendPipeline(*this, *sourceBufferPrivate, *m_playerPrivate));
+ GST_TRACE("Adding SourceBuffer to AppendPipeline: this=%p sourceBuffer=%p appendPipeline=%p", this, sourceBufferPrivate.get(), appendPipeline.get());
+ m_playerPrivate->m_appendPipelinesMap.add(sourceBufferPrivate, appendPipeline);
+
+ return m_playerPrivate->m_playbackPipeline->addSourceBuffer(sourceBufferPrivate);
+}
+
+const MediaTime& MediaSourceClientGStreamerMSE::duration()
+{
+ ASSERT(WTF::isMainThread());
+
+ return m_duration;
+}
+
+void MediaSourceClientGStreamerMSE::durationChanged(const MediaTime& duration)
+{
+ ASSERT(WTF::isMainThread());
+
+ GST_TRACE("duration: %f", duration.toFloat());
+ if (!duration.isValid() || duration.isPositiveInfinite() || duration.isNegativeInfinite())
+ return;
+
+ m_duration = duration;
+ if (m_playerPrivate)
+ m_playerPrivate->durationChanged();
+}
+
+void MediaSourceClientGStreamerMSE::abort(RefPtr<SourceBufferPrivateGStreamer> sourceBufferPrivate)
+{
+ ASSERT(WTF::isMainThread());
+
+ GST_DEBUG("aborting");
+
+ if (!m_playerPrivate)
+ return;
+
+ RefPtr<AppendPipeline> appendPipeline = m_playerPrivate->m_appendPipelinesMap.get(sourceBufferPrivate);
+
+ ASSERT(appendPipeline);
+
+ appendPipeline->abort();
+}
+
+void MediaSourceClientGStreamerMSE::resetParserState(RefPtr<SourceBufferPrivateGStreamer> sourceBufferPrivate)
+{
+ ASSERT(WTF::isMainThread());
+
+ GST_DEBUG("resetting parser state");
+
+ if (!m_playerPrivate)
+ return;
+
+ RefPtr<AppendPipeline> appendPipeline = m_playerPrivate->m_appendPipelinesMap.get(sourceBufferPrivate);
+
+ ASSERT(appendPipeline);
+
+ appendPipeline->abort();
+}
+
+bool MediaSourceClientGStreamerMSE::append(RefPtr<SourceBufferPrivateGStreamer> sourceBufferPrivate, const unsigned char* data, unsigned length)
+{
+ ASSERT(WTF::isMainThread());
+
+ GST_DEBUG("Appending %u bytes", length);
+
+ if (!m_playerPrivate)
+ return false;
+
+ RefPtr<AppendPipeline> appendPipeline = m_playerPrivate->m_appendPipelinesMap.get(sourceBufferPrivate);
+
+ ASSERT(appendPipeline);
+
+ void* bufferData = fastMalloc(length);
+ GstBuffer* buffer = gst_buffer_new_wrapped_full(static_cast<GstMemoryFlags>(0), bufferData, length, 0, length, bufferData, fastFree);
+ gst_buffer_fill(buffer, 0, data, length);
+
+ return appendPipeline->pushNewBuffer(buffer) == GST_FLOW_OK;
+}
+
+void MediaSourceClientGStreamerMSE::markEndOfStream(MediaSourcePrivate::EndOfStreamStatus status)
+{
+ ASSERT(WTF::isMainThread());
+
+ if (!m_playerPrivate)
+ return;
+
+ m_playerPrivate->markEndOfStream(status);
+}
+
+void MediaSourceClientGStreamerMSE::removedFromMediaSource(RefPtr<SourceBufferPrivateGStreamer> sourceBufferPrivate)
+{
+ ASSERT(WTF::isMainThread());
+
+ if (!m_playerPrivate)
+ return;
+
+ ASSERT(m_playerPrivate->m_playbackPipeline);
+
+ RefPtr<AppendPipeline> appendPipeline = m_playerPrivate->m_appendPipelinesMap.get(sourceBufferPrivate);
+
+ ASSERT(appendPipeline);
+
+ appendPipeline->clearPlayerPrivate();
+ m_playerPrivate->m_appendPipelinesMap.remove(sourceBufferPrivate);
+ // AppendPipeline destructor will take care of cleaning up when appropriate.
+
+ m_playerPrivate->m_playbackPipeline->removeSourceBuffer(sourceBufferPrivate);
+}
+
+void MediaSourceClientGStreamerMSE::flush(AtomicString trackId)
+{
+ ASSERT(WTF::isMainThread());
+
+ if (m_playerPrivate)
+ m_playerPrivate->m_playbackPipeline->flush(trackId);
+}
+
+void MediaSourceClientGStreamerMSE::enqueueSample(PassRefPtr<MediaSample> prpSample)
+{
+ ASSERT(WTF::isMainThread());
+
+ if (m_playerPrivate)
+ m_playerPrivate->m_playbackPipeline->enqueueSample(prpSample);
+}
+
+GRefPtr<WebKitMediaSrc> MediaSourceClientGStreamerMSE::webKitMediaSrc()
+{
+ ASSERT(WTF::isMainThread());
+
+ if (!m_playerPrivate)
+ return nullptr;
+
+ WebKitMediaSrc* source = WEBKIT_MEDIA_SRC(m_playerPrivate->m_source.get());
+
+ ASSERT(WEBKIT_IS_MEDIA_SRC(source));
+
+ return source;
+}
+
+void MediaSourceClientGStreamerMSE::clearPlayerPrivate()
+{
+ ASSERT(WTF::isMainThread());
+
+ m_playerPrivate = nullptr;
+}
+
+} // namespace WebCore.
+
+#endif // USE(GSTREAMER)
diff --git a/Source/WebCore/platform/graphics/gstreamer/mse/MediaSourceClientGStreamerMSE.h b/Source/WebCore/platform/graphics/gstreamer/mse/MediaSourceClientGStreamerMSE.h
new file mode 100644
index 000000000..c3d4ac7bc
--- /dev/null
+++ b/Source/WebCore/platform/graphics/gstreamer/mse/MediaSourceClientGStreamerMSE.h
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2016 Metrological Group B.V.
+ * Copyright (C) 2016 Igalia S.L
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * aint with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#pragma once
+
+#if ENABLE(VIDEO) && USE(GSTREAMER) && ENABLE(MEDIA_SOURCE)
+
+#include "GRefPtrGStreamer.h"
+#include "MediaSourcePrivate.h"
+#include "MediaSourcePrivateClient.h"
+#include "WebKitMediaSourceGStreamer.h"
+#include <wtf/MediaTime.h>
+
+namespace WebCore {
+
+class ContentType;
+class MediaPlayerPrivateGStreamerMSE;
+class MediaSample;
+class SourceBufferPrivateGStreamer;
+
+class MediaSourceClientGStreamerMSE : public RefCounted<MediaSourceClientGStreamerMSE> {
+public:
+ static Ref<MediaSourceClientGStreamerMSE> create(MediaPlayerPrivateGStreamerMSE&);
+ virtual ~MediaSourceClientGStreamerMSE();
+
+ // From MediaSourceGStreamer.
+ MediaSourcePrivate::AddStatus addSourceBuffer(RefPtr<SourceBufferPrivateGStreamer>, const ContentType&);
+ void durationChanged(const MediaTime&);
+ void markEndOfStream(MediaSourcePrivate::EndOfStreamStatus);
+
+ // From SourceBufferPrivateGStreamer.
+ void abort(RefPtr<SourceBufferPrivateGStreamer>);
+ void resetParserState(RefPtr<SourceBufferPrivateGStreamer>);
+ bool append(RefPtr<SourceBufferPrivateGStreamer>, const unsigned char*, unsigned);
+ void removedFromMediaSource(RefPtr<SourceBufferPrivateGStreamer>);
+ void flush(AtomicString);
+ void enqueueSample(PassRefPtr<MediaSample>);
+
+ void clearPlayerPrivate();
+
+ const MediaTime& duration();
+ GRefPtr<WebKitMediaSrc> webKitMediaSrc();
+
+private:
+ MediaSourceClientGStreamerMSE(MediaPlayerPrivateGStreamerMSE&);
+
+ MediaPlayerPrivateGStreamerMSE* m_playerPrivate;
+ MediaTime m_duration;
+};
+
+} // namespace WebCore.
+
+#endif // USE(GSTREAMER)
diff --git a/Source/WebCore/platform/graphics/gstreamer/mse/MediaSourceGStreamer.cpp b/Source/WebCore/platform/graphics/gstreamer/mse/MediaSourceGStreamer.cpp
new file mode 100644
index 000000000..92095b610
--- /dev/null
+++ b/Source/WebCore/platform/graphics/gstreamer/mse/MediaSourceGStreamer.cpp
@@ -0,0 +1,139 @@
+/*
+ * Copyright (C) 2013 Google Inc. All rights reserved.
+ * Copyright (C) 2013 Orange
+ * Copyright (C) 2014 Sebastian Dröge <sebastian@centricular.com>
+ * Copyright (C) 2015, 2016 Metrological Group B.V.
+ * Copyright (C) 2015, 2016 Igalia, S.L
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "MediaSourceGStreamer.h"
+
+#if ENABLE(MEDIA_SOURCE) && USE(GSTREAMER)
+
+#include "ContentType.h"
+#include "MediaPlayerPrivateGStreamer.h"
+#include "MediaPlayerPrivateGStreamerMSE.h"
+#include "MediaSourceClientGStreamerMSE.h"
+#include "NotImplemented.h"
+#include "SourceBufferPrivateGStreamer.h"
+#include "TimeRanges.h"
+#include "WebKitMediaSourceGStreamer.h"
+#include <wtf/PassRefPtr.h>
+#include <wtf/glib/GRefPtr.h>
+
+namespace WebCore {
+
+void MediaSourceGStreamer::open(MediaSourcePrivateClient& mediaSource, MediaPlayerPrivateGStreamerMSE& playerPrivate)
+{
+ mediaSource.setPrivateAndOpen(adoptRef(*new MediaSourceGStreamer(mediaSource, playerPrivate)));
+}
+
+MediaSourceGStreamer::MediaSourceGStreamer(MediaSourcePrivateClient& mediaSource, MediaPlayerPrivateGStreamerMSE& playerPrivate)
+ : MediaSourcePrivate()
+ , m_client(MediaSourceClientGStreamerMSE::create(playerPrivate))
+ , m_mediaSource(mediaSource)
+ , m_playerPrivate(playerPrivate)
+{
+}
+
+MediaSourceGStreamer::~MediaSourceGStreamer()
+{
+ for (auto& sourceBufferPrivate : m_sourceBuffers)
+ sourceBufferPrivate->clearMediaSource();
+}
+
+MediaSourceGStreamer::AddStatus MediaSourceGStreamer::addSourceBuffer(const ContentType& contentType, RefPtr<SourceBufferPrivate>& sourceBufferPrivate)
+{
+ sourceBufferPrivate = SourceBufferPrivateGStreamer::create(this, m_client.get(), contentType);
+ RefPtr<SourceBufferPrivateGStreamer> sourceBufferPrivateGStreamer = static_cast<SourceBufferPrivateGStreamer*>(sourceBufferPrivate.get());
+ m_sourceBuffers.add(sourceBufferPrivateGStreamer);
+ return m_client->addSourceBuffer(sourceBufferPrivateGStreamer, contentType);
+}
+
+void MediaSourceGStreamer::removeSourceBuffer(SourceBufferPrivate* sourceBufferPrivate)
+{
+ RefPtr<SourceBufferPrivateGStreamer> sourceBufferPrivateGStreamer = static_cast<SourceBufferPrivateGStreamer*>(sourceBufferPrivate);
+ ASSERT(m_sourceBuffers.contains(sourceBufferPrivateGStreamer));
+
+ sourceBufferPrivateGStreamer->clearMediaSource();
+ m_sourceBuffers.remove(sourceBufferPrivateGStreamer);
+ m_activeSourceBuffers.remove(sourceBufferPrivateGStreamer.get());
+}
+
+void MediaSourceGStreamer::durationChanged()
+{
+ m_client->durationChanged(m_mediaSource->duration());
+}
+
+void MediaSourceGStreamer::markEndOfStream(EndOfStreamStatus status)
+{
+ m_client->markEndOfStream(status);
+}
+
+void MediaSourceGStreamer::unmarkEndOfStream()
+{
+ notImplemented();
+}
+
+MediaPlayer::ReadyState MediaSourceGStreamer::readyState() const
+{
+ return m_playerPrivate.readyState();
+}
+
+void MediaSourceGStreamer::setReadyState(MediaPlayer::ReadyState state)
+{
+ m_playerPrivate.setReadyState(state);
+}
+
+void MediaSourceGStreamer::waitForSeekCompleted()
+{
+ m_playerPrivate.waitForSeekCompleted();
+}
+
+void MediaSourceGStreamer::seekCompleted()
+{
+ m_playerPrivate.seekCompleted();
+}
+
+void MediaSourceGStreamer::sourceBufferPrivateDidChangeActiveState(SourceBufferPrivateGStreamer* sourceBufferPrivate, bool isActive)
+{
+ if (!isActive)
+ m_activeSourceBuffers.remove(sourceBufferPrivate);
+ else if (!m_activeSourceBuffers.contains(sourceBufferPrivate))
+ m_activeSourceBuffers.add(sourceBufferPrivate);
+}
+
+std::unique_ptr<PlatformTimeRanges> MediaSourceGStreamer::buffered()
+{
+ return m_mediaSource->buffered();
+}
+
+}
+#endif
diff --git a/Source/WebCore/platform/graphics/gstreamer/mse/MediaSourceGStreamer.h b/Source/WebCore/platform/graphics/gstreamer/mse/MediaSourceGStreamer.h
new file mode 100644
index 000000000..c9a09fa04
--- /dev/null
+++ b/Source/WebCore/platform/graphics/gstreamer/mse/MediaSourceGStreamer.h
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2013 Google Inc. All rights reserved.
+ * Copyright (C) 2013 Orange
+ * Copyright (C) 2014 Sebastian Dröge <sebastian@centricular.com>
+ * Copyright (C) 2015, 2016 Metrological Group B.V.
+ * Copyright (C) 2015, 2016 Igalia, S.L
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#if ENABLE(MEDIA_SOURCE) && USE(GSTREAMER)
+#include "MediaSourcePrivate.h"
+
+#include <wtf/Forward.h>
+#include <wtf/HashSet.h>
+
+typedef struct _WebKitMediaSrc WebKitMediaSrc;
+
+namespace WebCore {
+
+class SourceBufferPrivateGStreamer;
+class MediaSourceClientGStreamerMSE;
+class MediaPlayerPrivateGStreamerMSE;
+class PlatformTimeRanges;
+
+// FIXME: Should this be called MediaSourcePrivateGStreamer?
+class MediaSourceGStreamer final : public MediaSourcePrivate {
+public:
+ static void open(MediaSourcePrivateClient&, MediaPlayerPrivateGStreamerMSE&);
+ virtual ~MediaSourceGStreamer();
+
+ MediaSourceClientGStreamerMSE& client() { return m_client.get(); }
+ AddStatus addSourceBuffer(const ContentType&, RefPtr<SourceBufferPrivate>&) override;
+ void removeSourceBuffer(SourceBufferPrivate*);
+
+ void durationChanged() override;
+ void markEndOfStream(EndOfStreamStatus) override;
+ void unmarkEndOfStream() override;
+
+ MediaPlayer::ReadyState readyState() const override;
+ void setReadyState(MediaPlayer::ReadyState) override;
+
+ void waitForSeekCompleted() override;
+ void seekCompleted() override;
+
+ void sourceBufferPrivateDidChangeActiveState(SourceBufferPrivateGStreamer*, bool);
+
+ std::unique_ptr<PlatformTimeRanges> buffered();
+
+private:
+ MediaSourceGStreamer(MediaSourcePrivateClient&, MediaPlayerPrivateGStreamerMSE&);
+
+ HashSet<RefPtr<SourceBufferPrivateGStreamer>> m_sourceBuffers;
+ HashSet<SourceBufferPrivateGStreamer*> m_activeSourceBuffers;
+ Ref<MediaSourceClientGStreamerMSE> m_client;
+ Ref<MediaSourcePrivateClient> m_mediaSource;
+ MediaPlayerPrivateGStreamerMSE& m_playerPrivate;
+};
+
+}
+
+#endif
diff --git a/Source/WebCore/platform/graphics/gstreamer/mse/PlaybackPipeline.cpp b/Source/WebCore/platform/graphics/gstreamer/mse/PlaybackPipeline.cpp
new file mode 100644
index 000000000..95df6d947
--- /dev/null
+++ b/Source/WebCore/platform/graphics/gstreamer/mse/PlaybackPipeline.cpp
@@ -0,0 +1,449 @@
+/*
+ * Copyright (C) 2014, 2015 Sebastian Dröge <sebastian@centricular.com>
+ * Copyright (C) 2016 Metrological Group B.V.
+ * Copyright (C) 2016 Igalia S.L
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * aint with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include "config.h"
+#include "PlaybackPipeline.h"
+
+#if ENABLE(VIDEO) && USE(GSTREAMER) && ENABLE(MEDIA_SOURCE)
+
+#include "AudioTrackPrivateGStreamer.h"
+#include "GStreamerMediaSample.h"
+#include "GStreamerUtilities.h"
+#include "MediaSample.h"
+#include "SourceBufferPrivateGStreamer.h"
+#include "VideoTrackPrivateGStreamer.h"
+
+#include <gst/app/gstappsrc.h>
+#include <gst/gst.h>
+#include <wtf/MainThread.h>
+#include <wtf/RefCounted.h>
+#include <wtf/glib/GMutexLocker.h>
+#include <wtf/glib/GRefPtr.h>
+#include <wtf/glib/GUniquePtr.h>
+#include <wtf/text/AtomicString.h>
+
+GST_DEBUG_CATEGORY_EXTERN(webkit_mse_debug);
+#define GST_CAT_DEFAULT webkit_mse_debug
+
+static Stream* getStreamByTrackId(WebKitMediaSrc*, AtomicString);
+static Stream* getStreamBySourceBufferPrivate(WebKitMediaSrc*, WebCore::SourceBufferPrivateGStreamer*);
+
+static Stream* getStreamByTrackId(WebKitMediaSrc* source, AtomicString trackIdString)
+{
+ // WebKitMediaSrc should be locked at this point.
+ for (Stream* stream : source->priv->streams) {
+ if (stream->type != WebCore::Invalid
+ && ((stream->audioTrack && stream->audioTrack->id() == trackIdString)
+ || (stream->videoTrack && stream->videoTrack->id() == trackIdString) ) ) {
+ return stream;
+ }
+ }
+ return nullptr;
+}
+
+static Stream* getStreamBySourceBufferPrivate(WebKitMediaSrc* source, WebCore::SourceBufferPrivateGStreamer* sourceBufferPrivate)
+{
+ for (Stream* stream : source->priv->streams) {
+ if (stream->sourceBuffer == sourceBufferPrivate)
+ return stream;
+ }
+ return nullptr;
+}
+
+// FIXME: Use gst_app_src_push_sample() instead when we switch to the appropriate GStreamer version.
+static GstFlowReturn pushSample(GstAppSrc* appsrc, GstSample* sample)
+{
+ g_return_val_if_fail(GST_IS_SAMPLE(sample), GST_FLOW_ERROR);
+
+ GstCaps* caps = gst_sample_get_caps(sample);
+ if (caps)
+ gst_app_src_set_caps(appsrc, caps);
+ else
+ GST_WARNING_OBJECT(appsrc, "received sample without caps");
+
+ GstBuffer* buffer = gst_sample_get_buffer(sample);
+ if (UNLIKELY(!buffer)) {
+ GST_WARNING_OBJECT(appsrc, "received sample without buffer");
+ return GST_FLOW_OK;
+ }
+
+ // gst_app_src_push_buffer() steals the reference, we need an additional one.
+ return gst_app_src_push_buffer(appsrc, gst_buffer_ref(buffer));
+}
+
+namespace WebCore {
+
+void PlaybackPipeline::setWebKitMediaSrc(WebKitMediaSrc* webKitMediaSrc)
+{
+ GST_DEBUG("webKitMediaSrc=%p", webKitMediaSrc);
+ m_webKitMediaSrc = webKitMediaSrc;
+}
+
+WebKitMediaSrc* PlaybackPipeline::webKitMediaSrc()
+{
+ return m_webKitMediaSrc.get();
+}
+
+MediaSourcePrivate::AddStatus PlaybackPipeline::addSourceBuffer(RefPtr<SourceBufferPrivateGStreamer> sourceBufferPrivate)
+{
+ WebKitMediaSrcPrivate* priv = m_webKitMediaSrc->priv;
+
+ if (priv->allTracksConfigured) {
+ GST_ERROR_OBJECT(m_webKitMediaSrc.get(), "Adding new source buffers after first data not supported yet");
+ return MediaSourcePrivate::NotSupported;
+ }
+
+ GST_DEBUG_OBJECT(m_webKitMediaSrc.get(), "State %d", int(GST_STATE(m_webKitMediaSrc.get())));
+
+ Stream* stream = new Stream{ };
+ stream->parent = m_webKitMediaSrc.get();
+ stream->appsrc = gst_element_factory_make("appsrc", nullptr);
+ stream->appsrcNeedDataFlag = false;
+ stream->sourceBuffer = sourceBufferPrivate.get();
+
+ // No track has been attached yet.
+ stream->type = Invalid;
+ stream->parser = nullptr;
+ stream->caps = nullptr;
+ stream->audioTrack = nullptr;
+ stream->videoTrack = nullptr;
+ stream->presentationSize = WebCore::FloatSize();
+ stream->lastEnqueuedTime = MediaTime::invalidTime();
+
+ gst_app_src_set_callbacks(GST_APP_SRC(stream->appsrc), &enabledAppsrcCallbacks, stream->parent, nullptr);
+ gst_app_src_set_emit_signals(GST_APP_SRC(stream->appsrc), FALSE);
+ gst_app_src_set_stream_type(GST_APP_SRC(stream->appsrc), GST_APP_STREAM_TYPE_SEEKABLE);
+
+ gst_app_src_set_max_bytes(GST_APP_SRC(stream->appsrc), 2 * WTF::MB);
+ g_object_set(G_OBJECT(stream->appsrc), "block", FALSE, "min-percent", 20, nullptr);
+
+ GST_OBJECT_LOCK(m_webKitMediaSrc.get());
+ priv->streams.prepend(stream);
+ GST_OBJECT_UNLOCK(m_webKitMediaSrc.get());
+
+ gst_bin_add(GST_BIN(m_webKitMediaSrc.get()), stream->appsrc);
+ gst_element_sync_state_with_parent(stream->appsrc);
+
+ return MediaSourcePrivate::Ok;
+}
+
+void PlaybackPipeline::removeSourceBuffer(RefPtr<SourceBufferPrivateGStreamer> sourceBufferPrivate)
+{
+ ASSERT(WTF::isMainThread());
+
+ GST_DEBUG_OBJECT(m_webKitMediaSrc.get(), "Element removed from MediaSource");
+ GST_OBJECT_LOCK(m_webKitMediaSrc.get());
+ WebKitMediaSrcPrivate* priv = m_webKitMediaSrc->priv;
+ Stream* stream = nullptr;
+ Deque<Stream*>::iterator streamPosition = priv->streams.begin();
+
+ for (; streamPosition != priv->streams.end(); ++streamPosition) {
+ if ((*streamPosition)->sourceBuffer == sourceBufferPrivate.get()) {
+ stream = *streamPosition;
+ break;
+ }
+ }
+ if (stream)
+ priv->streams.remove(streamPosition);
+ GST_OBJECT_UNLOCK(m_webKitMediaSrc.get());
+
+ if (stream)
+ webKitMediaSrcFreeStream(m_webKitMediaSrc.get(), stream);
+}
+
+void PlaybackPipeline::attachTrack(RefPtr<SourceBufferPrivateGStreamer> sourceBufferPrivate, RefPtr<TrackPrivateBase> trackPrivate, GstStructure* structure, GstCaps* caps)
+{
+ WebKitMediaSrc* webKitMediaSrc = m_webKitMediaSrc.get();
+
+ GST_OBJECT_LOCK(webKitMediaSrc);
+ Stream* stream = getStreamBySourceBufferPrivate(webKitMediaSrc, sourceBufferPrivate.get());
+ GST_OBJECT_UNLOCK(webKitMediaSrc);
+
+ ASSERT(stream);
+
+ GST_OBJECT_LOCK(webKitMediaSrc);
+ unsigned padId = stream->parent->priv->numberOfPads;
+ stream->parent->priv->numberOfPads++;
+ GST_OBJECT_UNLOCK(webKitMediaSrc);
+
+ const gchar* mediaType = gst_structure_get_name(structure);
+
+ GST_DEBUG_OBJECT(webKitMediaSrc, "Configured track %s: appsrc=%s, padId=%u, mediaType=%s", trackPrivate->id().string().utf8().data(), GST_ELEMENT_NAME(stream->appsrc), padId, mediaType);
+
+ GUniquePtr<gchar> parserBinName(g_strdup_printf("streamparser%u", padId));
+
+ if (!g_strcmp0(mediaType, "video/x-h264")) {
+ GRefPtr<GstCaps> filterCaps = adoptGRef(gst_caps_new_simple("video/x-h264", "alignment", G_TYPE_STRING, "au", nullptr));
+ GstElement* capsfilter = gst_element_factory_make("capsfilter", nullptr);
+ g_object_set(capsfilter, "caps", filterCaps.get(), nullptr);
+
+ stream->parser = gst_bin_new(parserBinName.get());
+
+ GstElement* parser = gst_element_factory_make("h264parse", nullptr);
+ gst_bin_add_many(GST_BIN(stream->parser), parser, capsfilter, nullptr);
+ gst_element_link_pads(parser, "src", capsfilter, "sink");
+
+ GRefPtr<GstPad> pad = adoptGRef(gst_element_get_static_pad(parser, "sink"));
+ gst_element_add_pad(stream->parser, gst_ghost_pad_new("sink", pad.get()));
+
+ pad = adoptGRef(gst_element_get_static_pad(capsfilter, "src"));
+ gst_element_add_pad(stream->parser, gst_ghost_pad_new("src", pad.get()));
+ } else if (!g_strcmp0(mediaType, "video/x-h265")) {
+ GRefPtr<GstCaps> filterCaps = adoptGRef(gst_caps_new_simple("video/x-h265", "alignment", G_TYPE_STRING, "au", nullptr));
+ GstElement* capsfilter = gst_element_factory_make("capsfilter", nullptr);
+ g_object_set(capsfilter, "caps", filterCaps.get(), nullptr);
+
+ stream->parser = gst_bin_new(parserBinName.get());
+
+ GstElement* parser = gst_element_factory_make("h265parse", nullptr);
+ gst_bin_add_many(GST_BIN(stream->parser), parser, capsfilter, nullptr);
+ gst_element_link_pads(parser, "src", capsfilter, "sink");
+
+ GRefPtr<GstPad> pad = adoptGRef(gst_element_get_static_pad(parser, "sink"));
+ gst_element_add_pad(stream->parser, gst_ghost_pad_new("sink", pad.get()));
+
+ pad = adoptGRef(gst_element_get_static_pad(capsfilter, "src"));
+ gst_element_add_pad(stream->parser, gst_ghost_pad_new("src", pad.get()));
+ } else if (!g_strcmp0(mediaType, "audio/mpeg")) {
+ gint mpegversion = -1;
+ gst_structure_get_int(structure, "mpegversion", &mpegversion);
+
+ GstElement* parser = nullptr;
+ if (mpegversion == 1)
+ parser = gst_element_factory_make("mpegaudioparse", nullptr);
+ else if (mpegversion == 2 || mpegversion == 4)
+ parser = gst_element_factory_make("aacparse", nullptr);
+ else
+ ASSERT_NOT_REACHED();
+
+ stream->parser = gst_bin_new(parserBinName.get());
+ gst_bin_add(GST_BIN(stream->parser), parser);
+
+ GRefPtr<GstPad> pad = adoptGRef(gst_element_get_static_pad(parser, "sink"));
+ gst_element_add_pad(stream->parser, gst_ghost_pad_new("sink", pad.get()));
+
+ pad = adoptGRef(gst_element_get_static_pad(parser, "src"));
+ gst_element_add_pad(stream->parser, gst_ghost_pad_new("src", pad.get()));
+ } else if (!g_strcmp0(mediaType, "video/x-vp9"))
+ stream->parser = nullptr;
+ else {
+ GST_ERROR_OBJECT(stream->parent, "Unsupported media format: %s", mediaType);
+ return;
+ }
+
+ GST_OBJECT_LOCK(webKitMediaSrc);
+ stream->type = Unknown;
+ GST_OBJECT_UNLOCK(webKitMediaSrc);
+
+ GRefPtr<GstPad> sourcePad;
+ if (stream->parser) {
+ gst_bin_add(GST_BIN(stream->parent), stream->parser);
+ gst_element_sync_state_with_parent(stream->parser);
+
+ GRefPtr<GstPad> sinkPad = adoptGRef(gst_element_get_static_pad(stream->parser, "sink"));
+ sourcePad = adoptGRef(gst_element_get_static_pad(stream->appsrc, "src"));
+ gst_pad_link(sourcePad.get(), sinkPad.get());
+ sourcePad = adoptGRef(gst_element_get_static_pad(stream->parser, "src"));
+ } else {
+ GST_DEBUG_OBJECT(m_webKitMediaSrc.get(), "Stream of type %s doesn't require a parser bin", mediaType);
+ sourcePad = adoptGRef(gst_element_get_static_pad(stream->appsrc, "src"));
+ }
+ ASSERT(sourcePad);
+
+ // FIXME: Is padId the best way to identify the Stream? What about trackId?
+ g_object_set_data(G_OBJECT(sourcePad.get()), "padId", GINT_TO_POINTER(padId));
+ webKitMediaSrcLinkParser(sourcePad.get(), caps, stream);
+
+ ASSERT(stream->parent->priv->mediaPlayerPrivate);
+ int signal = -1;
+
+ GST_OBJECT_LOCK(webKitMediaSrc);
+ if (g_str_has_prefix(mediaType, "audio")) {
+ stream->type = Audio;
+ stream->parent->priv->numberOfAudioStreams++;
+ signal = SIGNAL_AUDIO_CHANGED;
+ stream->audioTrack = RefPtr<WebCore::AudioTrackPrivateGStreamer>(static_cast<WebCore::AudioTrackPrivateGStreamer*>(trackPrivate.get()));
+ } else if (g_str_has_prefix(mediaType, "video")) {
+ stream->type = Video;
+ stream->parent->priv->numberOfVideoStreams++;
+ signal = SIGNAL_VIDEO_CHANGED;
+ stream->videoTrack = RefPtr<WebCore::VideoTrackPrivateGStreamer>(static_cast<WebCore::VideoTrackPrivateGStreamer*>(trackPrivate.get()));
+ } else if (g_str_has_prefix(mediaType, "text")) {
+ stream->type = Text;
+ stream->parent->priv->numberOfTextStreams++;
+ signal = SIGNAL_TEXT_CHANGED;
+
+ // FIXME: Support text tracks.
+ }
+ GST_OBJECT_UNLOCK(webKitMediaSrc);
+
+ if (signal != -1)
+ g_signal_emit(G_OBJECT(stream->parent), webKitMediaSrcSignals[signal], 0, nullptr);
+}
+
+void PlaybackPipeline::reattachTrack(RefPtr<SourceBufferPrivateGStreamer> sourceBufferPrivate, RefPtr<TrackPrivateBase> trackPrivate)
+{
+ GST_DEBUG("Re-attaching track");
+
+ // FIXME: Maybe remove this method. Now the caps change is managed by gst_appsrc_push_sample() in enqueueSample()
+ // and flushAndEnqueueNonDisplayingSamples().
+
+ WebKitMediaSrc* webKitMediaSrc = m_webKitMediaSrc.get();
+
+ GST_OBJECT_LOCK(webKitMediaSrc);
+ Stream* stream = getStreamBySourceBufferPrivate(webKitMediaSrc, sourceBufferPrivate.get());
+ GST_OBJECT_UNLOCK(webKitMediaSrc);
+
+ ASSERT(stream && stream->type != Invalid);
+
+ // The caps change is managed by gst_appsrc_push_sample() in enqueueSample() and
+ // flushAndEnqueueNonDisplayingSamples(), so the caps aren't set from here.
+ GRefPtr<GstCaps> appsrcCaps = adoptGRef(gst_app_src_get_caps(GST_APP_SRC(stream->appsrc)));
+ const gchar* mediaType = gst_structure_get_name(gst_caps_get_structure(appsrcCaps.get(), 0));
+ int signal = -1;
+
+ GST_OBJECT_LOCK(webKitMediaSrc);
+ if (g_str_has_prefix(mediaType, "audio")) {
+ ASSERT(stream->type == Audio);
+ signal = SIGNAL_AUDIO_CHANGED;
+ stream->audioTrack = RefPtr<WebCore::AudioTrackPrivateGStreamer>(static_cast<WebCore::AudioTrackPrivateGStreamer*>(trackPrivate.get()));
+ } else if (g_str_has_prefix(mediaType, "video")) {
+ ASSERT(stream->type == Video);
+ signal = SIGNAL_VIDEO_CHANGED;
+ stream->videoTrack = RefPtr<WebCore::VideoTrackPrivateGStreamer>(static_cast<WebCore::VideoTrackPrivateGStreamer*>(trackPrivate.get()));
+ } else if (g_str_has_prefix(mediaType, "text")) {
+ ASSERT(stream->type == Text);
+ signal = SIGNAL_TEXT_CHANGED;
+
+ // FIXME: Support text tracks.
+ }
+ GST_OBJECT_UNLOCK(webKitMediaSrc);
+
+ if (signal != -1)
+ g_signal_emit(G_OBJECT(stream->parent), webKitMediaSrcSignals[signal], 0, nullptr);
+}
+
+void PlaybackPipeline::notifyDurationChanged()
+{
+ gst_element_post_message(GST_ELEMENT(m_webKitMediaSrc.get()), gst_message_new_duration_changed(GST_OBJECT(m_webKitMediaSrc.get())));
+ // WebKitMediaSrc will ask MediaPlayerPrivateGStreamerMSE for the new duration later, when somebody asks for it.
+}
+
+void PlaybackPipeline::markEndOfStream(MediaSourcePrivate::EndOfStreamStatus)
+{
+ WebKitMediaSrcPrivate* priv = m_webKitMediaSrc->priv;
+
+ GST_DEBUG_OBJECT(m_webKitMediaSrc.get(), "Have EOS");
+
+ GST_OBJECT_LOCK(m_webKitMediaSrc.get());
+ bool allTracksConfigured = priv->allTracksConfigured;
+ if (!allTracksConfigured)
+ priv->allTracksConfigured = true;
+ GST_OBJECT_UNLOCK(m_webKitMediaSrc.get());
+
+ if (!allTracksConfigured) {
+ gst_element_no_more_pads(GST_ELEMENT(m_webKitMediaSrc.get()));
+ webKitMediaSrcDoAsyncDone(m_webKitMediaSrc.get());
+ }
+
+ Vector<GstAppSrc*> appsrcs;
+
+ GST_OBJECT_LOCK(m_webKitMediaSrc.get());
+ for (Stream* stream : priv->streams) {
+ if (stream->appsrc)
+ appsrcs.append(GST_APP_SRC(stream->appsrc));
+ }
+ GST_OBJECT_UNLOCK(m_webKitMediaSrc.get());
+
+ for (GstAppSrc* appsrc : appsrcs)
+ gst_app_src_end_of_stream(appsrc);
+}
+
+void PlaybackPipeline::flush(AtomicString trackId)
+{
+ ASSERT(WTF::isMainThread());
+
+ GST_DEBUG("flush: trackId=%s", trackId.string().utf8().data());
+
+ GST_OBJECT_LOCK(m_webKitMediaSrc.get());
+ Stream* stream = getStreamByTrackId(m_webKitMediaSrc.get(), trackId);
+
+ if (!stream) {
+ GST_OBJECT_UNLOCK(m_webKitMediaSrc.get());
+ return;
+ }
+
+ stream->lastEnqueuedTime = MediaTime::invalidTime();
+ GST_OBJECT_UNLOCK(m_webKitMediaSrc.get());
+}
+
+void PlaybackPipeline::enqueueSample(RefPtr<MediaSample> mediaSample)
+{
+ ASSERT(WTF::isMainThread());
+
+ AtomicString trackId = mediaSample->trackID();
+
+ GST_TRACE("enqueing sample trackId=%s PTS=%f presentationSize=%.0fx%.0f at %" GST_TIME_FORMAT " duration: %" GST_TIME_FORMAT,
+ trackId.string().utf8().data(), mediaSample->presentationTime().toFloat(),
+ mediaSample->presentationSize().width(), mediaSample->presentationSize().height(),
+ GST_TIME_ARGS(WebCore::toGstClockTime(mediaSample->presentationTime().toDouble())),
+ GST_TIME_ARGS(WebCore::toGstClockTime(mediaSample->duration().toDouble())));
+
+ Stream* stream = getStreamByTrackId(m_webKitMediaSrc.get(), trackId);
+
+ if (!stream) {
+ GST_WARNING("No stream!");
+ return;
+ }
+
+ if (!stream->sourceBuffer->isReadyForMoreSamples(trackId)) {
+ GST_DEBUG("enqueueSample: skip adding new sample for trackId=%s, SB is not ready yet", trackId.string().utf8().data());
+ return;
+ }
+
+ GstElement* appsrc = stream->appsrc;
+ MediaTime lastEnqueuedTime = stream->lastEnqueuedTime;
+
+ GStreamerMediaSample* sample = static_cast<GStreamerMediaSample*>(mediaSample.get());
+ if (sample->sample() && gst_sample_get_buffer(sample->sample())) {
+ GRefPtr<GstSample> gstSample = sample->sample();
+ GstBuffer* buffer = gst_sample_get_buffer(gstSample.get());
+ lastEnqueuedTime = sample->presentationTime();
+
+ GST_BUFFER_FLAG_UNSET(buffer, GST_BUFFER_FLAG_DECODE_ONLY);
+ pushSample(GST_APP_SRC(appsrc), gstSample.get());
+ // gst_app_src_push_sample() uses transfer-none for gstSample.
+
+ stream->lastEnqueuedTime = lastEnqueuedTime;
+ }
+}
+
+GstElement* PlaybackPipeline::pipeline()
+{
+ if (!m_webKitMediaSrc || !GST_ELEMENT_PARENT(GST_ELEMENT(m_webKitMediaSrc.get())))
+ return nullptr;
+
+ return GST_ELEMENT_PARENT(GST_ELEMENT_PARENT(GST_ELEMENT(m_webKitMediaSrc.get())));
+}
+
+} // namespace WebCore.
+
+#endif // USE(GSTREAMER)
diff --git a/Source/WebCore/platform/graphics/gstreamer/mse/PlaybackPipeline.h b/Source/WebCore/platform/graphics/gstreamer/mse/PlaybackPipeline.h
new file mode 100644
index 000000000..08f0e60d3
--- /dev/null
+++ b/Source/WebCore/platform/graphics/gstreamer/mse/PlaybackPipeline.h
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2016 Metrological Group B.V.
+ * Copyright (C) 2016 Igalia S.L
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * aint with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#pragma once
+
+#if ENABLE(VIDEO) && USE(GSTREAMER) && ENABLE(MEDIA_SOURCE)
+
+// PlaybackPipeline is (sort of) a friend class of WebKitMediaSourceGStreamer.
+
+#include "WebKitMediaSourceGStreamer.h"
+#include "WebKitMediaSourceGStreamerPrivate.h"
+
+#include <gst/gst.h>
+#include <wtf/Condition.h>
+#include <wtf/glib/GRefPtr.h>
+
+namespace WTF {
+template<> GRefPtr<WebKitMediaSrc> adoptGRef(WebKitMediaSrc*);
+template<> WebKitMediaSrc* refGPtr<WebKitMediaSrc>(WebKitMediaSrc*);
+template<> void derefGPtr<WebKitMediaSrc>(WebKitMediaSrc*);
+};
+
+namespace WebCore {
+
+class ContentType;
+class SourceBufferPrivateGStreamer;
+class MediaSourceGStreamer;
+
+class PlaybackPipeline: public RefCounted<PlaybackPipeline> {
+public:
+ static Ref<PlaybackPipeline> create()
+ {
+ return adoptRef(*new PlaybackPipeline());
+ }
+
+ virtual ~PlaybackPipeline() = default;
+
+ void setWebKitMediaSrc(WebKitMediaSrc*);
+ WebKitMediaSrc* webKitMediaSrc();
+
+ MediaSourcePrivate::AddStatus addSourceBuffer(RefPtr<SourceBufferPrivateGStreamer>);
+ void removeSourceBuffer(RefPtr<SourceBufferPrivateGStreamer>);
+ void attachTrack(RefPtr<SourceBufferPrivateGStreamer>, RefPtr<TrackPrivateBase>, GstStructure*, GstCaps*);
+ void reattachTrack(RefPtr<SourceBufferPrivateGStreamer>, RefPtr<TrackPrivateBase>);
+ void notifyDurationChanged();
+
+ // From MediaSourceGStreamer.
+ void markEndOfStream(MediaSourcePrivate::EndOfStreamStatus);
+
+ // From SourceBufferPrivateGStreamer.
+ void flush(AtomicString);
+ void enqueueSample(RefPtr<MediaSample>);
+
+ GstElement* pipeline();
+private:
+ PlaybackPipeline() = default;
+ GRefPtr<WebKitMediaSrc> m_webKitMediaSrc;
+};
+
+} // namespace WebCore.
+
+#endif // USE(GSTREAMER)
diff --git a/Source/WebCore/platform/graphics/gstreamer/mse/SourceBufferPrivateGStreamer.cpp b/Source/WebCore/platform/graphics/gstreamer/mse/SourceBufferPrivateGStreamer.cpp
new file mode 100644
index 000000000..e4b107f70
--- /dev/null
+++ b/Source/WebCore/platform/graphics/gstreamer/mse/SourceBufferPrivateGStreamer.cpp
@@ -0,0 +1,177 @@
+/*
+ * Copyright (C) 2013 Google Inc. All rights reserved.
+ * Copyright (C) 2013 Orange
+ * Copyright (C) 2014 Sebastian Dröge <sebastian@centricular.com>
+ * Copyright (C) 2015, 2016 Metrological Group B.V.
+ * Copyright (C) 2015, 2016 Igalia, S.L
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "SourceBufferPrivateGStreamer.h"
+
+#if ENABLE(MEDIA_SOURCE) && USE(GSTREAMER)
+
+#include "ContentType.h"
+#include "GStreamerUtilities.h"
+#include "MediaPlayerPrivateGStreamerMSE.h"
+#include "MediaSample.h"
+#include "MediaSourceClientGStreamerMSE.h"
+#include "MediaSourceGStreamer.h"
+#include "NotImplemented.h"
+#include "WebKitMediaSourceGStreamer.h"
+
+namespace WebCore {
+
+Ref<SourceBufferPrivateGStreamer> SourceBufferPrivateGStreamer::create(MediaSourceGStreamer* mediaSource, Ref<MediaSourceClientGStreamerMSE> client, const ContentType& contentType)
+{
+ return adoptRef(*new SourceBufferPrivateGStreamer(mediaSource, client.get(), contentType));
+}
+
+SourceBufferPrivateGStreamer::SourceBufferPrivateGStreamer(MediaSourceGStreamer* mediaSource, Ref<MediaSourceClientGStreamerMSE> client, const ContentType& contentType)
+ : SourceBufferPrivate()
+ , m_mediaSource(mediaSource)
+ , m_type(contentType)
+ , m_client(client.get())
+{
+}
+
+void SourceBufferPrivateGStreamer::setClient(SourceBufferPrivateClient* client)
+{
+ m_sourceBufferPrivateClient = client;
+}
+
+void SourceBufferPrivateGStreamer::append(const unsigned char* data, unsigned length)
+{
+ ASSERT(m_mediaSource);
+
+ if (!m_sourceBufferPrivateClient)
+ return;
+
+ if (m_client->append(this, data, length))
+ return;
+
+ m_sourceBufferPrivateClient->sourceBufferPrivateAppendComplete(SourceBufferPrivateClient::ReadStreamFailed);
+}
+
+void SourceBufferPrivateGStreamer::abort()
+{
+ m_client->abort(this);
+}
+
+void SourceBufferPrivateGStreamer::resetParserState()
+{
+ m_client->resetParserState(this);
+}
+
+void SourceBufferPrivateGStreamer::removedFromMediaSource()
+{
+ if (m_mediaSource)
+ m_mediaSource->removeSourceBuffer(this);
+ m_client->removedFromMediaSource(this);
+}
+
+MediaPlayer::ReadyState SourceBufferPrivateGStreamer::readyState() const
+{
+ return m_mediaSource->readyState();
+}
+
+void SourceBufferPrivateGStreamer::setReadyState(MediaPlayer::ReadyState state)
+{
+ m_mediaSource->setReadyState(state);
+}
+
+void SourceBufferPrivateGStreamer::flush(const AtomicString& trackId)
+{
+ m_client->flush(trackId);
+}
+
+void SourceBufferPrivateGStreamer::enqueueSample(Ref<MediaSample>&& sample, const AtomicString&)
+{
+ m_notifyWhenReadyForMoreSamples = false;
+
+ m_client->enqueueSample(WTFMove(sample));
+}
+
+bool SourceBufferPrivateGStreamer::isReadyForMoreSamples(const AtomicString&)
+{
+ return m_isReadyForMoreSamples;
+}
+
+void SourceBufferPrivateGStreamer::setReadyForMoreSamples(bool isReady)
+{
+ ASSERT(WTF::isMainThread());
+ m_isReadyForMoreSamples = isReady;
+}
+
+void SourceBufferPrivateGStreamer::notifyReadyForMoreSamples()
+{
+ ASSERT(WTF::isMainThread());
+ setReadyForMoreSamples(true);
+ if (m_notifyWhenReadyForMoreSamples)
+ m_sourceBufferPrivateClient->sourceBufferPrivateDidBecomeReadyForMoreSamples(m_trackId);
+}
+
+void SourceBufferPrivateGStreamer::setActive(bool isActive)
+{
+ if (m_mediaSource)
+ m_mediaSource->sourceBufferPrivateDidChangeActiveState(this, isActive);
+}
+
+void SourceBufferPrivateGStreamer::stopAskingForMoreSamples(const AtomicString&)
+{
+ notImplemented();
+}
+
+void SourceBufferPrivateGStreamer::notifyClientWhenReadyForMoreSamples(const AtomicString& trackId)
+{
+ ASSERT(WTF::isMainThread());
+ m_notifyWhenReadyForMoreSamples = true;
+ m_trackId = trackId;
+}
+
+void SourceBufferPrivateGStreamer::didReceiveInitializationSegment(const SourceBufferPrivateClient::InitializationSegment& initializationSegment)
+{
+ if (m_sourceBufferPrivateClient)
+ m_sourceBufferPrivateClient->sourceBufferPrivateDidReceiveInitializationSegment(initializationSegment);
+}
+
+void SourceBufferPrivateGStreamer::didReceiveSample(MediaSample& sample)
+{
+ if (m_sourceBufferPrivateClient)
+ m_sourceBufferPrivateClient->sourceBufferPrivateDidReceiveSample(sample);
+}
+
+void SourceBufferPrivateGStreamer::didReceiveAllPendingSamples()
+{
+ if (m_sourceBufferPrivateClient)
+ m_sourceBufferPrivateClient->sourceBufferPrivateAppendComplete(SourceBufferPrivateClient::AppendSucceeded);
+}
+
+}
+#endif
diff --git a/Source/WebCore/platform/graphics/gstreamer/mse/SourceBufferPrivateGStreamer.h b/Source/WebCore/platform/graphics/gstreamer/mse/SourceBufferPrivateGStreamer.h
new file mode 100644
index 000000000..5671310ff
--- /dev/null
+++ b/Source/WebCore/platform/graphics/gstreamer/mse/SourceBufferPrivateGStreamer.h
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2013 Google Inc. All rights reserved.
+ * Copyright (C) 2013 Orange
+ * Copyright (C) 2014 Sebastian Dröge <sebastian@centricular.com>
+ * Copyright (C) 2015, 2016 Metrological Group B.V.
+ * Copyright (C) 2015, 2016 Igalia, S.L
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#if ENABLE(MEDIA_SOURCE) && USE(GSTREAMER)
+
+#include "ContentType.h"
+#include "MediaPlayerPrivateGStreamerMSE.h"
+#include "SourceBufferPrivate.h"
+#include "SourceBufferPrivateClient.h"
+#include "WebKitMediaSourceGStreamer.h"
+
+namespace WebCore {
+
+class MediaSourceGStreamer;
+
+class SourceBufferPrivateGStreamer final : public SourceBufferPrivate {
+
+public:
+ static Ref<SourceBufferPrivateGStreamer> create(MediaSourceGStreamer*, Ref<MediaSourceClientGStreamerMSE>, const ContentType&);
+ virtual ~SourceBufferPrivateGStreamer() = default;
+
+ void clearMediaSource() { m_mediaSource = nullptr; }
+
+ void setClient(SourceBufferPrivateClient*) final;
+ void append(const unsigned char*, unsigned) final;
+ void abort() final;
+ void resetParserState() final;
+ void removedFromMediaSource() final;
+ MediaPlayer::ReadyState readyState() const final;
+ void setReadyState(MediaPlayer::ReadyState) final;
+
+ void flush(const AtomicString&) final;
+ void enqueueSample(Ref<MediaSample>&&, const AtomicString&) final;
+ bool isReadyForMoreSamples(const AtomicString&) final;
+ void setActive(bool) final;
+ void stopAskingForMoreSamples(const AtomicString&) final;
+ void notifyClientWhenReadyForMoreSamples(const AtomicString&) final;
+
+ void setReadyForMoreSamples(bool);
+ void notifyReadyForMoreSamples();
+
+ void didReceiveInitializationSegment(const SourceBufferPrivateClient::InitializationSegment&);
+ void didReceiveSample(MediaSample&);
+ void didReceiveAllPendingSamples();
+
+private:
+ SourceBufferPrivateGStreamer(MediaSourceGStreamer*, Ref<MediaSourceClientGStreamerMSE>, const ContentType&);
+ friend class MediaSourceClientGStreamerMSE;
+
+ MediaSourceGStreamer* m_mediaSource;
+ ContentType m_type;
+ Ref<MediaSourceClientGStreamerMSE> m_client;
+ SourceBufferPrivateClient* m_sourceBufferPrivateClient;
+ bool m_isReadyForMoreSamples = true;
+ bool m_notifyWhenReadyForMoreSamples = false;
+ AtomicString m_trackId;
+};
+
+}
+
+#endif
diff --git a/Source/WebCore/platform/graphics/gstreamer/mse/WebKitMediaSourceGStreamer.cpp b/Source/WebCore/platform/graphics/gstreamer/mse/WebKitMediaSourceGStreamer.cpp
new file mode 100644
index 000000000..52ca66867
--- /dev/null
+++ b/Source/WebCore/platform/graphics/gstreamer/mse/WebKitMediaSourceGStreamer.cpp
@@ -0,0 +1,776 @@
+/*
+ * Copyright (C) 2009, 2010 Sebastian Dröge <sebastian.droege@collabora.co.uk>
+ * Copyright (C) 2013 Collabora Ltd.
+ * Copyright (C) 2013 Orange
+ * Copyright (C) 2014, 2015 Sebastian Dröge <sebastian@centricular.com>
+ * Copyright (C) 2015, 2016 Metrological Group B.V.
+ * Copyright (C) 2015, 2016 Igalia, S.L
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "config.h"
+#include "WebKitMediaSourceGStreamer.h"
+
+#include "PlaybackPipeline.h"
+
+#if ENABLE(VIDEO) && ENABLE(MEDIA_SOURCE) && USE(GSTREAMER)
+
+#include "AudioTrackPrivateGStreamer.h"
+#include "GStreamerUtilities.h"
+#include "MediaDescription.h"
+#include "MediaPlayerPrivateGStreamerMSE.h"
+#include "MediaSample.h"
+#include "MediaSourceGStreamer.h"
+#include "NotImplemented.h"
+#include "SourceBufferPrivateGStreamer.h"
+#include "TimeRanges.h"
+#include "VideoTrackPrivateGStreamer.h"
+#include "WebKitMediaSourceGStreamerPrivate.h"
+
+#include <gst/app/app.h>
+#include <gst/app/gstappsrc.h>
+#include <gst/gst.h>
+#include <gst/pbutils/missing-plugins.h>
+#include <gst/pbutils/pbutils.h>
+#include <gst/video/video.h>
+#include <wtf/Condition.h>
+#include <wtf/MainThread.h>
+#include <wtf/glib/GMutexLocker.h>
+#include <wtf/glib/GUniquePtr.h>
+#include <wtf/text/CString.h>
+
+GST_DEBUG_CATEGORY_STATIC(webkit_media_src_debug);
+#define GST_CAT_DEFAULT webkit_media_src_debug
+
+#define webkit_media_src_parent_class parent_class
+#define WEBKIT_MEDIA_SRC_CATEGORY_INIT GST_DEBUG_CATEGORY_INIT(webkit_media_src_debug, "webkitmediasrc", 0, "websrc element");
+
+static GstStaticPadTemplate srcTemplate = GST_STATIC_PAD_TEMPLATE("src_%u", GST_PAD_SRC,
+ GST_PAD_SOMETIMES, GST_STATIC_CAPS_ANY);
+
+static void enabledAppsrcNeedData(GstAppSrc*, guint, gpointer);
+static void enabledAppsrcEnoughData(GstAppSrc*, gpointer);
+static gboolean enabledAppsrcSeekData(GstAppSrc*, guint64, gpointer);
+
+static void disabledAppsrcNeedData(GstAppSrc*, guint, gpointer) { };
+static void disabledAppsrcEnoughData(GstAppSrc*, gpointer) { };
+static gboolean disabledAppsrcSeekData(GstAppSrc*, guint64, gpointer)
+{
+ return FALSE;
+};
+
+GstAppSrcCallbacks enabledAppsrcCallbacks = {
+ enabledAppsrcNeedData,
+ enabledAppsrcEnoughData,
+ enabledAppsrcSeekData,
+ { 0 }
+};
+
+GstAppSrcCallbacks disabledAppsrcCallbacks = {
+ disabledAppsrcNeedData,
+ disabledAppsrcEnoughData,
+ disabledAppsrcSeekData,
+ { 0 }
+};
+
+static Stream* getStreamByAppsrc(WebKitMediaSrc*, GstElement*);
+
+static void enabledAppsrcNeedData(GstAppSrc* appsrc, guint, gpointer userData)
+{
+ WebKitMediaSrc* webKitMediaSrc = static_cast<WebKitMediaSrc*>(userData);
+ ASSERT(WEBKIT_IS_MEDIA_SRC(webKitMediaSrc));
+
+ GST_OBJECT_LOCK(webKitMediaSrc);
+ OnSeekDataAction appsrcSeekDataNextAction = webKitMediaSrc->priv->appsrcSeekDataNextAction;
+ Stream* appsrcStream = getStreamByAppsrc(webKitMediaSrc, GST_ELEMENT(appsrc));
+ bool allAppsrcNeedDataAfterSeek = false;
+
+ if (webKitMediaSrc->priv->appsrcSeekDataCount > 0) {
+ if (appsrcStream && !appsrcStream->appsrcNeedDataFlag) {
+ ++webKitMediaSrc->priv->appsrcNeedDataCount;
+ appsrcStream->appsrcNeedDataFlag = true;
+ }
+ int numAppsrcs = webKitMediaSrc->priv->streams.size();
+ if (webKitMediaSrc->priv->appsrcSeekDataCount == numAppsrcs && webKitMediaSrc->priv->appsrcNeedDataCount == numAppsrcs) {
+ GST_DEBUG("All needDatas completed");
+ allAppsrcNeedDataAfterSeek = true;
+ webKitMediaSrc->priv->appsrcSeekDataCount = 0;
+ webKitMediaSrc->priv->appsrcNeedDataCount = 0;
+ webKitMediaSrc->priv->appsrcSeekDataNextAction = Nothing;
+
+ for (Stream* stream : webKitMediaSrc->priv->streams)
+ stream->appsrcNeedDataFlag = false;
+ }
+ }
+ GST_OBJECT_UNLOCK(webKitMediaSrc);
+
+ if (allAppsrcNeedDataAfterSeek) {
+ GST_DEBUG("All expected appsrcSeekData() and appsrcNeedData() calls performed. Running next action (%d)", static_cast<int>(appsrcSeekDataNextAction));
+
+ switch (appsrcSeekDataNextAction) {
+ case MediaSourceSeekToTime: {
+ GstStructure* structure = gst_structure_new_empty("seek-needs-data");
+ GstMessage* message = gst_message_new_application(GST_OBJECT(appsrc), structure);
+ gst_bus_post(webKitMediaSrc->priv->bus.get(), message);
+ GST_TRACE("seek-needs-data message posted to the bus");
+ break;
+ }
+ case Nothing:
+ break;
+ }
+ } else if (appsrcSeekDataNextAction == Nothing) {
+ LockHolder locker(webKitMediaSrc->priv->streamLock);
+
+ GST_OBJECT_LOCK(webKitMediaSrc);
+
+ // Search again for the Stream, just in case it was removed between the previous lock and this one.
+ appsrcStream = getStreamByAppsrc(webKitMediaSrc, GST_ELEMENT(appsrc));
+
+ if (appsrcStream && appsrcStream->type != WebCore::Invalid) {
+ GstStructure* structure = gst_structure_new("ready-for-more-samples", "appsrc-stream", G_TYPE_POINTER, appsrcStream, nullptr);
+ GstMessage* message = gst_message_new_application(GST_OBJECT(appsrc), structure);
+ gst_bus_post(webKitMediaSrc->priv->bus.get(), message);
+ GST_TRACE("ready-for-more-samples message posted to the bus");
+ }
+
+ GST_OBJECT_UNLOCK(webKitMediaSrc);
+ }
+}
+
+static void enabledAppsrcEnoughData(GstAppSrc *appsrc, gpointer userData)
+{
+ // No need to lock on webKitMediaSrc, we're on the main thread and nobody is going to remove the stream in the meantime.
+ ASSERT(WTF::isMainThread());
+
+ WebKitMediaSrc* webKitMediaSrc = static_cast<WebKitMediaSrc*>(userData);
+ ASSERT(WEBKIT_IS_MEDIA_SRC(webKitMediaSrc));
+ Stream* stream = getStreamByAppsrc(webKitMediaSrc, GST_ELEMENT(appsrc));
+
+ // This callback might have been scheduled from a child thread before the stream was removed.
+ // Then, the removal code might have run, and later this callback.
+ // This check solves the race condition.
+ if (!stream || stream->type == WebCore::Invalid)
+ return;
+
+ stream->sourceBuffer->setReadyForMoreSamples(false);
+}
+
+static gboolean enabledAppsrcSeekData(GstAppSrc*, guint64, gpointer userData)
+{
+ ASSERT(WTF::isMainThread());
+
+ WebKitMediaSrc* webKitMediaSrc = static_cast<WebKitMediaSrc*>(userData);
+
+ ASSERT(WEBKIT_IS_MEDIA_SRC(webKitMediaSrc));
+
+ GST_OBJECT_LOCK(webKitMediaSrc);
+ webKitMediaSrc->priv->appsrcSeekDataCount++;
+ GST_OBJECT_UNLOCK(webKitMediaSrc);
+
+ return TRUE;
+}
+
+static Stream* getStreamByAppsrc(WebKitMediaSrc* source, GstElement* appsrc)
+{
+ for (Stream* stream : source->priv->streams) {
+ if (stream->appsrc == appsrc)
+ return stream;
+ }
+ return nullptr;
+}
+
+G_DEFINE_TYPE_WITH_CODE(WebKitMediaSrc, webkit_media_src, GST_TYPE_BIN,
+ G_IMPLEMENT_INTERFACE(GST_TYPE_URI_HANDLER, webKitMediaSrcUriHandlerInit);
+ WEBKIT_MEDIA_SRC_CATEGORY_INIT);
+
+guint webKitMediaSrcSignals[LAST_SIGNAL] = { 0 };
+
+static void webkit_media_src_class_init(WebKitMediaSrcClass* klass)
+{
+ GObjectClass* oklass = G_OBJECT_CLASS(klass);
+ GstElementClass* eklass = GST_ELEMENT_CLASS(klass);
+
+ oklass->finalize = webKitMediaSrcFinalize;
+ oklass->set_property = webKitMediaSrcSetProperty;
+ oklass->get_property = webKitMediaSrcGetProperty;
+
+ gst_element_class_add_pad_template(eklass, gst_static_pad_template_get(&srcTemplate));
+
+ gst_element_class_set_static_metadata(eklass, "WebKit Media source element", "Source", "Handles Blob uris", "Stephane Jadaud <sjadaud@sii.fr>, Sebastian Dröge <sebastian@centricular.com>, Enrique Ocaña González <eocanha@igalia.com>");
+
+ // Allows setting the uri using the 'location' property, which is used for example by gst_element_make_from_uri().
+ g_object_class_install_property(oklass,
+ PROP_LOCATION,
+ g_param_spec_string("location", "location", "Location to read from", nullptr,
+ GParamFlags(G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
+ g_object_class_install_property(oklass,
+ PROP_N_AUDIO,
+ g_param_spec_int("n-audio", "Number Audio", "Total number of audio streams",
+ 0, G_MAXINT, 0, GParamFlags(G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)));
+ g_object_class_install_property(oklass,
+ PROP_N_VIDEO,
+ g_param_spec_int("n-video", "Number Video", "Total number of video streams",
+ 0, G_MAXINT, 0, GParamFlags(G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)));
+ g_object_class_install_property(oklass,
+ PROP_N_TEXT,
+ g_param_spec_int("n-text", "Number Text", "Total number of text streams",
+ 0, G_MAXINT, 0, GParamFlags(G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)));
+
+ webKitMediaSrcSignals[SIGNAL_VIDEO_CHANGED] =
+ g_signal_new("video-changed", G_TYPE_FROM_CLASS(oklass),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET(WebKitMediaSrcClass, videoChanged), nullptr, nullptr,
+ g_cclosure_marshal_generic, G_TYPE_NONE, 0, G_TYPE_NONE);
+ webKitMediaSrcSignals[SIGNAL_AUDIO_CHANGED] =
+ g_signal_new("audio-changed", G_TYPE_FROM_CLASS(oklass),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET(WebKitMediaSrcClass, audioChanged), nullptr, nullptr,
+ g_cclosure_marshal_generic, G_TYPE_NONE, 0, G_TYPE_NONE);
+ webKitMediaSrcSignals[SIGNAL_TEXT_CHANGED] =
+ g_signal_new("text-changed", G_TYPE_FROM_CLASS(oklass),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET(WebKitMediaSrcClass, textChanged), nullptr, nullptr,
+ g_cclosure_marshal_generic, G_TYPE_NONE, 0, G_TYPE_NONE);
+
+ eklass->change_state = webKitMediaSrcChangeState;
+
+ g_type_class_add_private(klass, sizeof(WebKitMediaSrcPrivate));
+}
+
+static void webkit_media_src_init(WebKitMediaSrc* source)
+{
+ source->priv = WEBKIT_MEDIA_SRC_GET_PRIVATE(source);
+ new (source->priv) WebKitMediaSrcPrivate();
+ source->priv->seekTime = MediaTime::invalidTime();
+ source->priv->appsrcSeekDataCount = 0;
+ source->priv->appsrcNeedDataCount = 0;
+ source->priv->appsrcSeekDataNextAction = Nothing;
+
+ // No need to reset Stream.appsrcNeedDataFlag because there are no Streams at this point yet.
+}
+
+void webKitMediaSrcFinalize(GObject* object)
+{
+ ASSERT(WTF::isMainThread());
+
+ WebKitMediaSrc* source = WEBKIT_MEDIA_SRC(object);
+ WebKitMediaSrcPrivate* priv = source->priv;
+
+ Deque<Stream*> oldStreams;
+ source->priv->streams.swap(oldStreams);
+
+ for (Stream* stream : oldStreams)
+ webKitMediaSrcFreeStream(source, stream);
+
+ priv->seekTime = MediaTime::invalidTime();
+
+ if (priv->mediaPlayerPrivate)
+ webKitMediaSrcSetMediaPlayerPrivate(source, nullptr);
+
+ // We used a placement new for construction, the destructor won't be called automatically.
+ priv->~_WebKitMediaSrcPrivate();
+
+ GST_CALL_PARENT(G_OBJECT_CLASS, finalize, (object));
+}
+
+void webKitMediaSrcSetProperty(GObject* object, guint propId, const GValue* value, GParamSpec* pspec)
+{
+ WebKitMediaSrc* source = WEBKIT_MEDIA_SRC(object);
+
+ switch (propId) {
+ case PROP_LOCATION:
+ gst_uri_handler_set_uri(reinterpret_cast<GstURIHandler*>(source), g_value_get_string(value), nullptr);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(object, propId, pspec);
+ break;
+ }
+}
+
+void webKitMediaSrcGetProperty(GObject* object, guint propId, GValue* value, GParamSpec* pspec)
+{
+ WebKitMediaSrc* source = WEBKIT_MEDIA_SRC(object);
+ WebKitMediaSrcPrivate* priv = source->priv;
+
+ GST_OBJECT_LOCK(source);
+ switch (propId) {
+ case PROP_LOCATION:
+ g_value_set_string(value, priv->location.get());
+ break;
+ case PROP_N_AUDIO:
+ g_value_set_int(value, priv->numberOfAudioStreams);
+ break;
+ case PROP_N_VIDEO:
+ g_value_set_int(value, priv->numberOfVideoStreams);
+ break;
+ case PROP_N_TEXT:
+ g_value_set_int(value, priv->numberOfTextStreams);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(object, propId, pspec);
+ break;
+ }
+ GST_OBJECT_UNLOCK(source);
+}
+
+void webKitMediaSrcDoAsyncStart(WebKitMediaSrc* source)
+{
+ source->priv->asyncStart = true;
+ GST_BIN_CLASS(parent_class)->handle_message(GST_BIN(source),
+ gst_message_new_async_start(GST_OBJECT(source)));
+}
+
+void webKitMediaSrcDoAsyncDone(WebKitMediaSrc* source)
+{
+ WebKitMediaSrcPrivate* priv = source->priv;
+ if (priv->asyncStart) {
+ GST_BIN_CLASS(parent_class)->handle_message(GST_BIN(source),
+ gst_message_new_async_done(GST_OBJECT(source), GST_CLOCK_TIME_NONE));
+ priv->asyncStart = false;
+ }
+}
+
+GstStateChangeReturn webKitMediaSrcChangeState(GstElement* element, GstStateChange transition)
+{
+ WebKitMediaSrc* source = WEBKIT_MEDIA_SRC(element);
+ WebKitMediaSrcPrivate* priv = source->priv;
+
+ switch (transition) {
+ case GST_STATE_CHANGE_READY_TO_PAUSED:
+ priv->allTracksConfigured = false;
+ webKitMediaSrcDoAsyncStart(source);
+ break;
+ default:
+ break;
+ }
+
+ GstStateChangeReturn result = GST_ELEMENT_CLASS(parent_class)->change_state(element, transition);
+ if (G_UNLIKELY(result == GST_STATE_CHANGE_FAILURE)) {
+ GST_WARNING_OBJECT(source, "State change failed");
+ webKitMediaSrcDoAsyncDone(source);
+ return result;
+ }
+
+ switch (transition) {
+ case GST_STATE_CHANGE_READY_TO_PAUSED:
+ result = GST_STATE_CHANGE_ASYNC;
+ break;
+ case GST_STATE_CHANGE_PAUSED_TO_READY:
+ webKitMediaSrcDoAsyncDone(source);
+ priv->allTracksConfigured = false;
+ break;
+ default:
+ break;
+ }
+
+ return result;
+}
+
+gint64 webKitMediaSrcGetSize(WebKitMediaSrc* webKitMediaSrc)
+{
+ gint64 duration = 0;
+ for (Stream* stream : webKitMediaSrc->priv->streams)
+ duration = std::max<gint64>(duration, gst_app_src_get_size(GST_APP_SRC(stream->appsrc)));
+ return duration;
+}
+
+gboolean webKitMediaSrcQueryWithParent(GstPad* pad, GstObject* parent, GstQuery* query)
+{
+ WebKitMediaSrc* source = WEBKIT_MEDIA_SRC(GST_ELEMENT(parent));
+ gboolean result = FALSE;
+
+ switch (GST_QUERY_TYPE(query)) {
+ case GST_QUERY_DURATION: {
+ GstFormat format;
+ gst_query_parse_duration(query, &format, nullptr);
+
+ GST_DEBUG_OBJECT(source, "duration query in format %s", gst_format_get_name(format));
+ GST_OBJECT_LOCK(source);
+ switch (format) {
+ case GST_FORMAT_TIME: {
+ if (source->priv && source->priv->mediaPlayerPrivate) {
+ float duration = source->priv->mediaPlayerPrivate->durationMediaTime().toFloat();
+ if (duration > 0) {
+ gst_query_set_duration(query, format, WebCore::toGstClockTime(duration));
+ GST_DEBUG_OBJECT(source, "Answering: duration=%" GST_TIME_FORMAT, GST_TIME_ARGS(WebCore::toGstClockTime(duration)));
+ result = TRUE;
+ }
+ }
+ break;
+ }
+ case GST_FORMAT_BYTES: {
+ if (source->priv) {
+ gint64 duration = webKitMediaSrcGetSize(source);
+ if (duration) {
+ gst_query_set_duration(query, format, duration);
+ GST_DEBUG_OBJECT(source, "size: %" G_GINT64_FORMAT, duration);
+ result = TRUE;
+ }
+ }
+ break;
+ }
+ default:
+ break;
+ }
+
+ GST_OBJECT_UNLOCK(source);
+ break;
+ }
+ case GST_QUERY_URI:
+ if (source) {
+ GST_OBJECT_LOCK(source);
+ if (source->priv)
+ gst_query_set_uri(query, source->priv->location.get());
+ GST_OBJECT_UNLOCK(source);
+ }
+ result = TRUE;
+ break;
+ default: {
+ GRefPtr<GstPad> target = adoptGRef(gst_ghost_pad_get_target(GST_GHOST_PAD_CAST(pad)));
+ // Forward the query to the proxy target pad.
+ if (target)
+ result = gst_pad_query(target.get(), query);
+ break;
+ }
+ }
+
+ return result;
+}
+
+void webKitMediaSrcUpdatePresentationSize(GstCaps* caps, Stream* stream)
+{
+ GstStructure* structure = gst_caps_get_structure(caps, 0);
+ const gchar* structureName = gst_structure_get_name(structure);
+ GstVideoInfo info;
+
+ GST_OBJECT_LOCK(stream->parent);
+ if (g_str_has_prefix(structureName, "video/") && gst_video_info_from_caps(&info, caps)) {
+ float width, height;
+
+ // FIXME: Correct?.
+ width = info.width;
+ height = info.height * ((float) info.par_d / (float) info.par_n);
+ stream->presentationSize = WebCore::FloatSize(width, height);
+ } else
+ stream->presentationSize = WebCore::FloatSize();
+
+ gst_caps_ref(caps);
+ stream->caps = adoptGRef(caps);
+ GST_OBJECT_UNLOCK(stream->parent);
+}
+
+void webKitMediaSrcLinkStreamToSrcPad(GstPad* sourcePad, Stream* stream)
+{
+ unsigned padId = static_cast<unsigned>(GPOINTER_TO_INT(g_object_get_data(G_OBJECT(sourcePad), "padId")));
+ GST_DEBUG_OBJECT(stream->parent, "linking stream to src pad (id: %u)", padId);
+
+ GUniquePtr<gchar> padName(g_strdup_printf("src_%u", padId));
+ GstPad* ghostpad = WebCore::webkitGstGhostPadFromStaticTemplate(&srcTemplate, padName.get(), sourcePad);
+
+ gst_pad_set_query_function(ghostpad, webKitMediaSrcQueryWithParent);
+
+ gst_pad_set_active(ghostpad, TRUE);
+ gst_element_add_pad(GST_ELEMENT(stream->parent), ghostpad);
+
+ if (stream->decodebinSinkPad) {
+ GST_DEBUG_OBJECT(stream->parent, "A decodebin was previously used for this source, trying to reuse it.");
+ // FIXME: error checking here. Not sure what to do if linking
+ // fails though, because decodebin is out of this source
+ // element's scope, in theory.
+ gst_pad_link(ghostpad, stream->decodebinSinkPad);
+ }
+}
+
+void webKitMediaSrcLinkParser(GstPad* sourcePad, GstCaps* caps, Stream* stream)
+{
+ ASSERT(caps && stream->parent);
+ if (!caps || !stream->parent) {
+ GST_ERROR("Unable to link parser");
+ return;
+ }
+
+ webKitMediaSrcUpdatePresentationSize(caps, stream);
+
+ // FIXME: drop webKitMediaSrcLinkStreamToSrcPad() and move its code here.
+ if (!gst_pad_is_linked(sourcePad)) {
+ GST_DEBUG_OBJECT(stream->parent, "pad not linked yet");
+ webKitMediaSrcLinkStreamToSrcPad(sourcePad, stream);
+ }
+
+ webKitMediaSrcCheckAllTracksConfigured(stream->parent);
+}
+
+void webKitMediaSrcFreeStream(WebKitMediaSrc* source, Stream* stream)
+{
+ if (stream->appsrc) {
+ // Don't trigger callbacks from this appsrc to avoid using the stream anymore.
+ gst_app_src_set_callbacks(GST_APP_SRC(stream->appsrc), &disabledAppsrcCallbacks, nullptr, nullptr);
+ gst_app_src_end_of_stream(GST_APP_SRC(stream->appsrc));
+ }
+
+ if (stream->type != WebCore::Invalid) {
+ GST_DEBUG("Freeing track-related info on stream %p", stream);
+
+ LockHolder locker(source->priv->streamLock);
+
+ if (stream->caps)
+ stream->caps = nullptr;
+
+ if (stream->audioTrack)
+ stream->audioTrack = nullptr;
+ if (stream->videoTrack)
+ stream->videoTrack = nullptr;
+
+ int signal = -1;
+ switch (stream->type) {
+ case WebCore::Audio:
+ signal = SIGNAL_AUDIO_CHANGED;
+ break;
+ case WebCore::Video:
+ signal = SIGNAL_VIDEO_CHANGED;
+ break;
+ case WebCore::Text:
+ signal = SIGNAL_TEXT_CHANGED;
+ break;
+ default:
+ break;
+ }
+ stream->type = WebCore::Invalid;
+
+ if (signal != -1)
+ g_signal_emit(G_OBJECT(source), webKitMediaSrcSignals[signal], 0, nullptr);
+
+ source->priv->streamCondition.notifyOne();
+ }
+
+ GST_DEBUG("Releasing stream: %p", stream);
+ delete stream;
+}
+
+void webKitMediaSrcCheckAllTracksConfigured(WebKitMediaSrc* webKitMediaSrc)
+{
+ bool allTracksConfigured = false;
+
+ GST_OBJECT_LOCK(webKitMediaSrc);
+ if (!webKitMediaSrc->priv->allTracksConfigured) {
+ allTracksConfigured = true;
+ for (Stream* stream : webKitMediaSrc->priv->streams) {
+ if (stream->type == WebCore::Invalid) {
+ allTracksConfigured = false;
+ break;
+ }
+ }
+ if (allTracksConfigured)
+ webKitMediaSrc->priv->allTracksConfigured = true;
+ }
+ GST_OBJECT_UNLOCK(webKitMediaSrc);
+
+ if (allTracksConfigured) {
+ GST_DEBUG("All tracks attached. Completing async state change operation.");
+ gst_element_no_more_pads(GST_ELEMENT(webKitMediaSrc));
+ webKitMediaSrcDoAsyncDone(webKitMediaSrc);
+ }
+}
+
+// Uri handler interface.
+GstURIType webKitMediaSrcUriGetType(GType)
+{
+ return GST_URI_SRC;
+}
+
+const gchar* const* webKitMediaSrcGetProtocols(GType)
+{
+ static const char* protocols[] = {"mediasourceblob", nullptr };
+ return protocols;
+}
+
+gchar* webKitMediaSrcGetUri(GstURIHandler* handler)
+{
+ WebKitMediaSrc* source = WEBKIT_MEDIA_SRC(handler);
+ gchar* result;
+
+ GST_OBJECT_LOCK(source);
+ result = g_strdup(source->priv->location.get());
+ GST_OBJECT_UNLOCK(source);
+ return result;
+}
+
+gboolean webKitMediaSrcSetUri(GstURIHandler* handler, const gchar* uri, GError**)
+{
+ WebKitMediaSrc* source = WEBKIT_MEDIA_SRC(handler);
+
+ if (GST_STATE(source) >= GST_STATE_PAUSED) {
+ GST_ERROR_OBJECT(source, "URI can only be set in states < PAUSED");
+ return FALSE;
+ }
+
+ GST_OBJECT_LOCK(source);
+ WebKitMediaSrcPrivate* priv = source->priv;
+ priv->location = nullptr;
+ if (!uri) {
+ GST_OBJECT_UNLOCK(source);
+ return TRUE;
+ }
+
+ WebCore::URL url(WebCore::URL(), uri);
+
+ priv->location = GUniquePtr<gchar>(g_strdup(url.string().utf8().data()));
+ GST_OBJECT_UNLOCK(source);
+ return TRUE;
+}
+
+void webKitMediaSrcUriHandlerInit(gpointer gIface, gpointer)
+{
+ GstURIHandlerInterface* iface = (GstURIHandlerInterface *) gIface;
+
+ iface->get_type = webKitMediaSrcUriGetType;
+ iface->get_protocols = webKitMediaSrcGetProtocols;
+ iface->get_uri = webKitMediaSrcGetUri;
+ iface->set_uri = webKitMediaSrcSetUri;
+}
+
+static void seekNeedsDataMainThread(WebKitMediaSrc* source)
+{
+ GST_DEBUG("Buffering needed before seek");
+
+ ASSERT(WTF::isMainThread());
+
+ GST_OBJECT_LOCK(source);
+ MediaTime seekTime = source->priv->seekTime;
+ WebCore::MediaPlayerPrivateGStreamerMSE* mediaPlayerPrivate = source->priv->mediaPlayerPrivate;
+
+ if (!mediaPlayerPrivate) {
+ GST_OBJECT_UNLOCK(source);
+ return;
+ }
+
+ for (Stream* stream : source->priv->streams) {
+ if (stream->type != WebCore::Invalid)
+ stream->sourceBuffer->setReadyForMoreSamples(true);
+ }
+ GST_OBJECT_UNLOCK(source);
+ mediaPlayerPrivate->notifySeekNeedsDataForTime(seekTime);
+}
+
+static void notifyReadyForMoreSamplesMainThread(WebKitMediaSrc* source, Stream* appsrcStream)
+{
+ GST_OBJECT_LOCK(source);
+
+ auto it = std::find(source->priv->streams.begin(), source->priv->streams.end(), appsrcStream);
+ if (it == source->priv->streams.end()) {
+ GST_OBJECT_UNLOCK(source);
+ return;
+ }
+
+ WebCore::MediaPlayerPrivateGStreamerMSE* mediaPlayerPrivate = source->priv->mediaPlayerPrivate;
+ if (mediaPlayerPrivate && !mediaPlayerPrivate->seeking())
+ appsrcStream->sourceBuffer->notifyReadyForMoreSamples();
+
+ GST_OBJECT_UNLOCK(source);
+}
+
+static void applicationMessageCallback(GstBus*, GstMessage* message, WebKitMediaSrc* source)
+{
+ ASSERT(WTF::isMainThread());
+ ASSERT(GST_MESSAGE_TYPE(message) == GST_MESSAGE_APPLICATION);
+
+ const GstStructure* structure = gst_message_get_structure(message);
+
+ if (gst_structure_has_name(structure, "seek-needs-data")) {
+ seekNeedsDataMainThread(source);
+ return;
+ }
+
+ if (gst_structure_has_name(structure, "ready-for-more-samples")) {
+ Stream* appsrcStream = nullptr;
+ gst_structure_get(structure, "appsrc-stream", G_TYPE_POINTER, &appsrcStream, nullptr);
+ ASSERT(appsrcStream);
+
+ notifyReadyForMoreSamplesMainThread(source, appsrcStream);
+ return;
+ }
+
+ ASSERT_NOT_REACHED();
+}
+
+void webKitMediaSrcSetMediaPlayerPrivate(WebKitMediaSrc* source, WebCore::MediaPlayerPrivateGStreamerMSE* mediaPlayerPrivate)
+{
+ GST_OBJECT_LOCK(source);
+ if (source->priv->mediaPlayerPrivate && source->priv->mediaPlayerPrivate != mediaPlayerPrivate && source->priv->bus)
+ g_signal_handlers_disconnect_by_func(source->priv->bus.get(), gpointer(applicationMessageCallback), source);
+
+ // Set to nullptr on MediaPlayerPrivateGStreamer destruction, never a dangling pointer.
+ source->priv->mediaPlayerPrivate = mediaPlayerPrivate;
+ source->priv->bus = mediaPlayerPrivate ? adoptGRef(gst_pipeline_get_bus(GST_PIPELINE(mediaPlayerPrivate->pipeline()))) : nullptr;
+ if (source->priv->bus) {
+ // MediaPlayerPrivateGStreamer has called gst_bus_add_signal_watch() at this point, so we can subscribe.
+ g_signal_connect(source->priv->bus.get(), "message::application", G_CALLBACK(applicationMessageCallback), source);
+ }
+ GST_OBJECT_UNLOCK(source);
+}
+
+void webKitMediaSrcSetReadyForSamples(WebKitMediaSrc* source, bool isReady)
+{
+ if (source) {
+ GST_OBJECT_LOCK(source);
+ for (Stream* stream : source->priv->streams)
+ stream->sourceBuffer->setReadyForMoreSamples(isReady);
+ GST_OBJECT_UNLOCK(source);
+ }
+}
+
+void webKitMediaSrcPrepareSeek(WebKitMediaSrc* source, const MediaTime& time)
+{
+ GST_OBJECT_LOCK(source);
+ source->priv->seekTime = time;
+ source->priv->appsrcSeekDataCount = 0;
+ source->priv->appsrcNeedDataCount = 0;
+
+ for (Stream* stream : source->priv->streams) {
+ stream->appsrcNeedDataFlag = false;
+ // Don't allow samples away from the seekTime to be enqueued.
+ stream->lastEnqueuedTime = time;
+ }
+
+ // The pending action will be performed in enabledAppsrcSeekData().
+ source->priv->appsrcSeekDataNextAction = MediaSourceSeekToTime;
+ GST_OBJECT_UNLOCK(source);
+}
+
+namespace WTF {
+template <> GRefPtr<WebKitMediaSrc> adoptGRef(WebKitMediaSrc* ptr)
+{
+ ASSERT(!ptr || !g_object_is_floating(G_OBJECT(ptr)));
+ return GRefPtr<WebKitMediaSrc>(ptr, GRefPtrAdopt);
+}
+
+template <> WebKitMediaSrc* refGPtr<WebKitMediaSrc>(WebKitMediaSrc* ptr)
+{
+ if (ptr)
+ gst_object_ref_sink(GST_OBJECT(ptr));
+
+ return ptr;
+}
+
+template <> void derefGPtr<WebKitMediaSrc>(WebKitMediaSrc* ptr)
+{
+ if (ptr)
+ gst_object_unref(ptr);
+}
+};
+
+#endif // USE(GSTREAMER)
+
diff --git a/Source/WebCore/platform/graphics/gstreamer/WebKitMediaSourceGStreamer.h b/Source/WebCore/platform/graphics/gstreamer/mse/WebKitMediaSourceGStreamer.h
index 78892c3e3..79086054c 100644
--- a/Source/WebCore/platform/graphics/gstreamer/WebKitMediaSourceGStreamer.h
+++ b/Source/WebCore/platform/graphics/gstreamer/mse/WebKitMediaSourceGStreamer.h
@@ -2,6 +2,9 @@
* Copyright (C) 2009, 2010 Sebastian Dröge <sebastian.droege@collabora.co.uk>
* Copyright (C) 2013 Collabora Ltd.
* Copyright (C) 2013 Orange
+ * Copyright (C) 2014, 2015 Sebastian Dröge <sebastian@centricular.com>
+ * Copyright (C) 2015, 2016 Metrological Group B.V.
+ * Copyright (C) 2015, 2016 Igalia, S.L
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@@ -18,13 +21,26 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
-#ifndef WebKitMediaSourceGStreamer_h
-#define WebKitMediaSourceGStreamer_h
+#pragma once
+
#if ENABLE(VIDEO) && ENABLE(MEDIA_SOURCE) && USE(GSTREAMER)
+#include "GRefPtrGStreamer.h"
#include "MediaPlayer.h"
+#include "MediaSource.h"
+#include "MediaSourcePrivate.h"
+#include "SourceBufferPrivate.h"
+#include "SourceBufferPrivateClient.h"
#include <gst/gst.h>
+namespace WebCore {
+
+class MediaPlayerPrivateGStreamerMSE;
+
+enum MediaSourceStreamTypeGStreamer { Invalid, Unknown, Audio, Video, Text };
+
+}
+
G_BEGIN_DECLS
#define WEBKIT_TYPE_MEDIA_SRC (webkit_media_src_get_type ())
@@ -45,28 +61,20 @@ struct _WebKitMediaSrc {
struct _WebKitMediaSrcClass {
GstBinClass parentClass;
+
+ // Notify app that number of audio/video/text streams changed.
+ void (*videoChanged)(WebKitMediaSrc*);
+ void (*audioChanged)(WebKitMediaSrc*);
+ void (*textChanged)(WebKitMediaSrc*);
};
GType webkit_media_src_get_type(void);
-void webKitMediaSrcSetMediaPlayer(WebKitMediaSrc*, WebCore::MediaPlayer*);
-void webKitMediaSrcSetPlayBin(WebKitMediaSrc*, GstElement*);
-G_END_DECLS
-
-class MediaSourceClientGstreamer: public RefCounted<MediaSourceClientGstreamer> {
- public:
- MediaSourceClientGstreamer(WebKitMediaSrc*);
- ~MediaSourceClientGstreamer();
+void webKitMediaSrcSetMediaPlayerPrivate(WebKitMediaSrc*, WebCore::MediaPlayerPrivateGStreamerMSE*);
- void didReceiveDuration(double);
- void didReceiveData(const char*, int, String);
- void didFinishLoading(double);
- void didFail();
-
- private:
- WebKitMediaSrc* m_src;
-};
+void webKitMediaSrcPrepareSeek(WebKitMediaSrc*, const MediaTime&);
+void webKitMediaSrcSetReadyForSamples(WebKitMediaSrc*, bool);
+G_END_DECLS
#endif // USE(GSTREAMER)
-#endif
diff --git a/Source/WebCore/platform/graphics/gstreamer/mse/WebKitMediaSourceGStreamerPrivate.h b/Source/WebCore/platform/graphics/gstreamer/mse/WebKitMediaSourceGStreamerPrivate.h
new file mode 100644
index 000000000..83b523f7d
--- /dev/null
+++ b/Source/WebCore/platform/graphics/gstreamer/mse/WebKitMediaSourceGStreamerPrivate.h
@@ -0,0 +1,143 @@
+/*
+ * Copyright (C) 2016 Metrological Group B.V.
+ * Copyright (C) 2016 Igalia S.L
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * aint with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#pragma once
+
+#if ENABLE(VIDEO) && USE(GSTREAMER) && ENABLE(MEDIA_SOURCE)
+
+#include "AudioTrackPrivateGStreamer.h"
+#include "SourceBufferPrivateGStreamer.h"
+#include "VideoTrackPrivateGStreamer.h"
+#include "WebKitMediaSourceGStreamer.h"
+
+#include <gst/app/gstappsrc.h>
+#include <gst/gst.h>
+#include <wtf/Condition.h>
+#include <wtf/RefPtr.h>
+#include <wtf/glib/GRefPtr.h>
+
+namespace WebCore {
+
+class MediaPlayerPrivateGStreamerMSE;
+
+};
+
+void webKitMediaSrcUriHandlerInit(gpointer, gpointer);
+
+#define WEBKIT_MEDIA_SRC_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE((obj), WEBKIT_TYPE_MEDIA_SRC, WebKitMediaSrcPrivate))
+
+typedef struct _Stream Stream;
+
+struct _Stream {
+ // Fields filled when the Stream is created.
+ WebKitMediaSrc* parent;
+
+ // AppSrc.
+ GstElement* appsrc;
+ GstPad* decodebinSinkPad;
+ WebCore::SourceBufferPrivateGStreamer* sourceBuffer;
+
+ // Fields filled when the track is attached.
+ WebCore::MediaSourceStreamTypeGStreamer type;
+ // Might be 0, e.g. for VP8/VP9.
+ GstElement* parser;
+ GRefPtr<GstCaps> caps;
+ RefPtr<WebCore::AudioTrackPrivateGStreamer> audioTrack;
+ RefPtr<WebCore::VideoTrackPrivateGStreamer> videoTrack;
+ WebCore::FloatSize presentationSize;
+
+ // This helps WebKitMediaSrcPrivate.appsrcNeedDataCount, ensuring that needDatas are
+ // counted only once per each appsrc.
+ bool appsrcNeedDataFlag;
+
+ // Used to enforce continuity in the appended data and avoid breaking the decoder.
+ MediaTime lastEnqueuedTime;
+};
+
+enum {
+ PROP_0,
+ PROP_LOCATION,
+ PROP_N_AUDIO,
+ PROP_N_VIDEO,
+ PROP_N_TEXT,
+ PROP_LAST
+};
+
+enum {
+ SIGNAL_VIDEO_CHANGED,
+ SIGNAL_AUDIO_CHANGED,
+ SIGNAL_TEXT_CHANGED,
+ LAST_SIGNAL
+};
+
+enum OnSeekDataAction {
+ Nothing,
+ MediaSourceSeekToTime
+};
+
+struct _WebKitMediaSrcPrivate {
+ // Used to coordinate the release of Stream track info.
+ Lock streamLock;
+ Condition streamCondition;
+
+ Deque<Stream*> streams;
+ GUniquePtr<gchar> location;
+ int numberOfAudioStreams;
+ int numberOfVideoStreams;
+ int numberOfTextStreams;
+ bool asyncStart;
+ bool allTracksConfigured;
+ unsigned numberOfPads;
+
+ MediaTime seekTime;
+
+ // On seek, we wait for all the seekDatas, then for all the needDatas, and then run the nextAction.
+ OnSeekDataAction appsrcSeekDataNextAction;
+ int appsrcSeekDataCount;
+ int appsrcNeedDataCount;
+
+ GRefPtr<GstBus> bus;
+ WebCore::MediaPlayerPrivateGStreamerMSE* mediaPlayerPrivate;
+};
+
+extern guint webKitMediaSrcSignals[LAST_SIGNAL];
+extern GstAppSrcCallbacks enabledAppsrcCallbacks;
+extern GstAppSrcCallbacks disabledAppsrcCallbacks;
+
+void webKitMediaSrcUriHandlerInit(gpointer gIface, gpointer ifaceData);
+void webKitMediaSrcFinalize(GObject*);
+void webKitMediaSrcSetProperty(GObject*, guint propertyId, const GValue*, GParamSpec*);
+void webKitMediaSrcGetProperty(GObject*, guint propertyId, GValue*, GParamSpec*);
+void webKitMediaSrcDoAsyncStart(WebKitMediaSrc*);
+void webKitMediaSrcDoAsyncDone(WebKitMediaSrc*);
+GstStateChangeReturn webKitMediaSrcChangeState(GstElement*, GstStateChange);
+gint64 webKitMediaSrcGetSize(WebKitMediaSrc*);
+gboolean webKitMediaSrcQueryWithParent(GstPad*, GstObject*, GstQuery*);
+void webKitMediaSrcUpdatePresentationSize(GstCaps*, Stream*);
+void webKitMediaSrcLinkStreamToSrcPad(GstPad*, Stream*);
+void webKitMediaSrcLinkParser(GstPad*, GstCaps*, Stream*);
+void webKitMediaSrcFreeStream(WebKitMediaSrc*, Stream*);
+void webKitMediaSrcCheckAllTracksConfigured(WebKitMediaSrc*);
+GstURIType webKitMediaSrcUriGetType(GType);
+const gchar* const* webKitMediaSrcGetProtocols(GType);
+gchar* webKitMediaSrcGetUri(GstURIHandler*);
+gboolean webKitMediaSrcSetUri(GstURIHandler*, const gchar*, GError**);
+
+#endif // USE(GSTREAMER)
diff --git a/Source/WebCore/platform/graphics/gtk/ColorGtk.cpp b/Source/WebCore/platform/graphics/gtk/ColorGtk.cpp
index 8f56ba6fb..4d94a6947 100644
--- a/Source/WebCore/platform/graphics/gtk/ColorGtk.cpp
+++ b/Source/WebCore/platform/graphics/gtk/ColorGtk.cpp
@@ -26,27 +26,27 @@
namespace WebCore {
Color::Color(const GdkColor& c)
- : m_color(makeRGB(c.red >> 8, c.green >> 8, c.blue >> 8))
- , m_valid(true)
{
+ setRGB(makeRGB(c.red >> 8, c.green >> 8, c.blue >> 8));
}
#ifndef GTK_API_VERSION_2
Color::Color(const GdkRGBA& c)
- : m_color(makeRGBA(static_cast<int>(c.red * 255),
- static_cast<int>(c.green * 255),
- static_cast<int>(c.blue * 255),
- static_cast<int>(c.alpha * 255)))
- , m_valid(true)
{
+ setRGB(makeRGBA(static_cast<int>(c.red * 255),
+ static_cast<int>(c.green * 255),
+ static_cast<int>(c.blue * 255),
+ static_cast<int>(c.alpha * 255)));
}
Color::operator GdkRGBA() const
{
+ if (isExtended())
+ return { asExtended().red(), asExtended().green(), asExtended().blue(), asExtended().alpha() };
+
double red, green, blue, alpha;
getRGBA(red, green, blue, alpha);
- GdkRGBA rgba = { red, green, blue, alpha };
- return rgba;
+ return { red, green, blue, alpha };
}
#endif
diff --git a/Source/WebCore/platform/graphics/gtk/GdkCairoUtilities.cpp b/Source/WebCore/platform/graphics/gtk/GdkCairoUtilities.cpp
index 4b28f0e0e..819441efa 100644
--- a/Source/WebCore/platform/graphics/gtk/GdkCairoUtilities.cpp
+++ b/Source/WebCore/platform/graphics/gtk/GdkCairoUtilities.cpp
@@ -10,10 +10,10 @@
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
- * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
@@ -31,8 +31,25 @@
#include "IntSize.h"
#include <cairo.h>
#include <gtk/gtk.h>
+#include <mutex>
+#include <wtf/NeverDestroyed.h>
-using namespace WebCore;
+namespace WebCore {
+
+const cairo_font_options_t* getDefaultCairoFontOptions()
+{
+ if (auto* screen = gdk_screen_get_default()) {
+ if (auto* options = gdk_screen_get_font_options(screen))
+ return options;
+ }
+
+ static LazyNeverDestroyed<cairo_font_options_t*> options;
+ static std::once_flag flag;
+ std::call_once(flag, [] {
+ options.construct(cairo_font_options_create());
+ });
+ return options;
+}
GdkPixbuf* cairoSurfaceToGdkPixbuf(cairo_surface_t* surface)
{
@@ -40,3 +57,4 @@ GdkPixbuf* cairoSurfaceToGdkPixbuf(cairo_surface_t* surface)
return gdk_pixbuf_get_from_surface(surface, 0, 0, size.width(), size.height());
}
+}
diff --git a/Source/WebCore/platform/graphics/gtk/GdkCairoUtilities.h b/Source/WebCore/platform/graphics/gtk/GdkCairoUtilities.h
index 43a9a0900..e2aeaae22 100644
--- a/Source/WebCore/platform/graphics/gtk/GdkCairoUtilities.h
+++ b/Source/WebCore/platform/graphics/gtk/GdkCairoUtilities.h
@@ -10,10 +10,10 @@
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
- * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
@@ -23,9 +23,10 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef GdkCairoUtilities_h
-#define GdkCairoUtilities_h
+#pragma once
+
+namespace WebCore {
GdkPixbuf* cairoSurfaceToGdkPixbuf(cairo_surface_t*);
-#endif // GdkCairoUtilities_h
+}
diff --git a/Source/WebCore/platform/graphics/gtk/IconGtk.cpp b/Source/WebCore/platform/graphics/gtk/IconGtk.cpp
index 3e957b345..880c4c979 100644
--- a/Source/WebCore/platform/graphics/gtk/IconGtk.cpp
+++ b/Source/WebCore/platform/graphics/gtk/IconGtk.cpp
@@ -11,7 +11,7 @@
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
- * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ * 3. Neither the name of Apple Inc. ("Apple") nor the names of
* its contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
@@ -36,7 +36,7 @@
#include <gtk/gtk.h>
-#include <wtf/PassRefPtr.h>
+#include <wtf/RefPtr.h>
#include <wtf/text/CString.h>
namespace WebCore {
@@ -90,7 +90,7 @@ static String lookupIconName(String MIMEType)
}
// FIXME: Move the code to ChromeClient::iconForFiles().
-PassRefPtr<Icon> Icon::createIconForFiles(const Vector<String>& filenames)
+RefPtr<Icon> Icon::createIconForFiles(const Vector<String>& filenames)
{
if (filenames.isEmpty())
return 0;
@@ -113,13 +113,13 @@ PassRefPtr<Icon> Icon::createIconForFiles(const Vector<String>& filenames)
return 0;
}
-void Icon::paint(GraphicsContext* context, const IntRect& rect)
+void Icon::paint(GraphicsContext& context, const FloatRect& rect)
{
- if (context->paintingDisabled())
+ if (context.paintingDisabled())
return;
// TODO: Scale/clip the image if necessary.
- cairo_t* cr = context->platformContext()->cr();
+ cairo_t* cr = context.platformContext()->cr();
cairo_save(cr);
gdk_cairo_set_source_pixbuf(cr, m_icon, rect.x(), rect.y());
cairo_paint(cr);
diff --git a/Source/WebCore/platform/graphics/gtk/ImageBufferGtk.cpp b/Source/WebCore/platform/graphics/gtk/ImageBufferGtk.cpp
index 78a60c1ef..b2fe2a909 100644
--- a/Source/WebCore/platform/graphics/gtk/ImageBufferGtk.cpp
+++ b/Source/WebCore/platform/graphics/gtk/ImageBufferGtk.cpp
@@ -25,14 +25,14 @@
#include "MIMETypeRegistry.h"
#include <cairo.h>
#include <gtk/gtk.h>
-#include <wtf/gobject/GUniquePtr.h>
+#include <wtf/glib/GUniquePtr.h>
#include <wtf/text/Base64.h>
#include <wtf/text/CString.h>
#include <wtf/text/WTFString.h>
namespace WebCore {
-static bool encodeImage(cairo_surface_t* surface, const String& mimeType, const double* quality, GUniqueOutPtr<gchar>& buffer, gsize& bufferSize)
+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
@@ -75,7 +75,7 @@ static bool encodeImage(cairo_surface_t* surface, const String& mimeType, const
return !error;
}
-String ImageBuffer::toDataURL(const String& mimeType, const double* quality, CoordinateSystem) const
+String ImageBuffer::toDataURL(const String& mimeType, std::optional<double> quality, CoordinateSystem) const
{
ASSERT(MIMETypeRegistry::isSupportedImageMIMETypeForEncoding(mimeType));
diff --git a/Source/WebCore/platform/graphics/gtk/ImageGtk.cpp b/Source/WebCore/platform/graphics/gtk/ImageGtk.cpp
index 6208f7dc1..49ccc9118 100644
--- a/Source/WebCore/platform/graphics/gtk/ImageGtk.cpp
+++ b/Source/WebCore/platform/graphics/gtk/ImageGtk.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2006 Apple Computer, Inc. All rights reserved.
+ * Copyright (C) 2006 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -10,10 +10,10 @@
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
- * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
@@ -26,49 +26,31 @@
#include "config.h"
#include "BitmapImage.h"
-#include "FileSystem.h"
#include "GUniquePtrGtk.h"
#include "GdkCairoUtilities.h"
#include "SharedBuffer.h"
-#include <wtf/gobject/GUniquePtr.h>
-#include <wtf/text/CString.h>
#include <cairo.h>
#include <gtk/gtk.h>
+#include <wtf/glib/GRefPtr.h>
+#include <wtf/glib/GUniquePtr.h>
namespace WebCore {
-static char* getPathToImageResource(char* resource)
+static PassRefPtr<Image> loadImageFromGResource(const char* iconName)
{
- if (g_getenv("WEBKIT_TOP_LEVEL"))
- return g_build_filename(g_getenv("WEBKIT_TOP_LEVEL"), "Source", "WebCore", "Resources", resource, NULL);
-
- return g_build_filename(sharedResourcesPath().data(), "images", resource, NULL);
-}
-
-static CString getThemeIconFileName(const char* name, int size)
-{
- GtkIconInfo* iconInfo = gtk_icon_theme_lookup_icon(gtk_icon_theme_get_default(),
- name, size, GTK_ICON_LOOKUP_NO_SVG);
- // Try to fallback on MISSING_IMAGE.
- if (!iconInfo)
- iconInfo = gtk_icon_theme_lookup_icon(gtk_icon_theme_get_default(),
- GTK_STOCK_MISSING_IMAGE, size,
- GTK_ICON_LOOKUP_NO_SVG);
- if (iconInfo) {
- GUniquePtr<GtkIconInfo> info(iconInfo);
- return CString(gtk_icon_info_get_filename(info.get()));
- }
-
- // No icon was found, this can happen if not GTK theme is set. In
- // that case an empty Image will be created.
- return CString();
+ RefPtr<BitmapImage> icon = BitmapImage::create();
+ GUniquePtr<char> path(g_strdup_printf("/org/webkitgtk/resources/images/%s", iconName));
+ GRefPtr<GBytes> data = adoptGRef(g_resources_lookup_data(path.get(), G_RESOURCE_LOOKUP_FLAGS_NONE, nullptr));
+ ASSERT(data);
+ icon->setData(SharedBuffer::create(static_cast<const unsigned char*>(g_bytes_get_data(data.get(), nullptr)), g_bytes_get_size(data.get())), true);
+ return icon.release();
}
-static PassRefPtr<SharedBuffer> loadResourceSharedBuffer(CString name)
+static PassRefPtr<SharedBuffer> loadResourceSharedBuffer(const char* filename)
{
GUniqueOutPtr<gchar> content;
gsize length;
- if (!g_file_get_contents(name.data(), &content.outPtr(), &length, 0))
+ if (!g_file_get_contents(filename, &content.outPtr(), &length, nullptr))
return SharedBuffer::create();
return SharedBuffer::create(content.get(), length);
@@ -78,33 +60,23 @@ void BitmapImage::invalidatePlatformData()
{
}
-PassRefPtr<Image> loadImageFromFile(CString fileName)
+static PassRefPtr<Image> loadMissingImageIconFromTheme(const char* name)
{
- RefPtr<BitmapImage> img = BitmapImage::create();
- if (!fileName.isNull()) {
- RefPtr<SharedBuffer> buffer = loadResourceSharedBuffer(fileName);
- img->setData(buffer.release(), true);
- }
- return img.release();
-}
-
-PassRefPtr<Image> Image::loadPlatformResource(const char* name)
-{
- CString fileName;
- if (!strcmp("missingImage", name))
- fileName = getThemeIconFileName(GTK_STOCK_MISSING_IMAGE, 16);
- if (fileName.isNull()) {
- GUniquePtr<gchar> imageName(g_strdup_printf("%s.png", name));
- GUniquePtr<gchar> glibFileName(getPathToImageResource(imageName.get()));
- fileName = glibFileName.get();
+ int iconSize = g_str_has_suffix(name, "@2x") ? 32 : 16;
+ RefPtr<BitmapImage> icon = BitmapImage::create();
+ GUniquePtr<GtkIconInfo> iconInfo(gtk_icon_theme_lookup_icon(gtk_icon_theme_get_default(), GTK_STOCK_MISSING_IMAGE, iconSize, GTK_ICON_LOOKUP_NO_SVG));
+ if (iconInfo) {
+ RefPtr<SharedBuffer> buffer = loadResourceSharedBuffer(gtk_icon_info_get_filename(iconInfo.get()));
+ icon->setData(buffer.release(), true);
+ return icon.release();
}
- return loadImageFromFile(fileName);
+ return loadImageFromGResource(name);
}
-PassRefPtr<Image> Image::loadPlatformThemeIcon(const char* name, int size)
+PassRefPtr<Image> Image::loadPlatformResource(const char* name)
{
- return loadImageFromFile(getThemeIconFileName(name, size));
+ return g_str_has_prefix(name, "missingImage") ? loadMissingImageIconFromTheme(name) : loadImageFromGResource(name);
}
GdkPixbuf* BitmapImage::getGdkPixbuf()
diff --git a/Source/WebCore/platform/graphics/gtk/IntPointGtk.cpp b/Source/WebCore/platform/graphics/gtk/IntPointGtk.cpp
deleted file mode 100644
index c4021581e..000000000
--- a/Source/WebCore/platform/graphics/gtk/IntPointGtk.cpp
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * Copyright (C) 2007 Alp Toker <alp@atoker.com>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public License
- * along with this library; see the file COPYING.LIB. If not, write to
- * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
- * Boston, MA 02110-1301, USA.
- */
-
-#include "config.h"
-#include "IntPoint.h"
-
-#include <gdk/gdk.h>
-
-namespace WebCore {
-
-IntPoint::IntPoint(const GdkPoint& p)
- : m_x(p.x)
- , m_y(p.y)
-{
-}
-
-IntPoint::operator GdkPoint() const
-{
- GdkPoint p = { x(), y() };
- return p;
-}
-
-}
-
-// vim: ts=4 sw=4 et
diff --git a/Source/WebCore/platform/graphics/gtk/IntRectGtk.cpp b/Source/WebCore/platform/graphics/gtk/IntRectGtk.cpp
deleted file mode 100644
index 1f312819a..000000000
--- a/Source/WebCore/platform/graphics/gtk/IntRectGtk.cpp
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * Copyright (C) 2007 Alp Toker <alp@atoker.com>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public License
- * along with this library; see the file COPYING.LIB. If not, write to
- * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
- * Boston, MA 02110-1301, USA.
- */
-
-#include "config.h"
-#include "IntRect.h"
-
-#include <gdk/gdk.h>
-
-#ifdef GTK_API_VERSION_2
-namespace WebCore {
-
-IntRect::IntRect(const GdkRectangle& r)
- : m_location(IntPoint(r.x, r.y))
- , m_size(r.width, r.height)
-{
-}
-
-IntRect::operator GdkRectangle() const
-{
- GdkRectangle r = { x(), y(), width(), height() };
- return r;
-}
-
-}
-#endif
diff --git a/Source/WebCore/platform/graphics/harfbuzz/ComplexTextControllerHarfBuzz.cpp b/Source/WebCore/platform/graphics/harfbuzz/ComplexTextControllerHarfBuzz.cpp
new file mode 100644
index 000000000..e97f9c204
--- /dev/null
+++ b/Source/WebCore/platform/graphics/harfbuzz/ComplexTextControllerHarfBuzz.cpp
@@ -0,0 +1,36 @@
+/*
+* Copyright (C) 2017 Apple Inc. All rights reserved.
+*
+* Redistribution and use in source and binary forms, with or without
+* modification, are permitted provided that the following conditions
+* are met:
+* 1. Redistributions of source code must retain the above copyright
+* notice, this list of conditions and the following disclaimer.
+* 2. Redistributions in binary form must reproduce the above copyright
+* notice, this list of conditions and the following disclaimer in the
+* documentation and/or other materials provided with the distribution.
+*
+* THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY
+* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+* DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#include "config.h"
+#include "ComplexTextController.h"
+
+namespace WebCore {
+
+void ComplexTextController::collectComplexTextRunsForCharacters(const UChar*, unsigned, unsigned, const Font*)
+{
+ // FIXME: Implement this.
+ ASSERT_NOT_REACHED();
+}
+
+}
diff --git a/Source/WebCore/platform/graphics/harfbuzz/HarfBuzzFace.cpp b/Source/WebCore/platform/graphics/harfbuzz/HarfBuzzFace.cpp
index efc94f3f9..f03cace8e 100644
--- a/Source/WebCore/platform/graphics/harfbuzz/HarfBuzzFace.cpp
+++ b/Source/WebCore/platform/graphics/harfbuzz/HarfBuzzFace.cpp
@@ -32,8 +32,8 @@
#include "HarfBuzzFace.h"
#include "FontPlatformData.h"
-#include "hb-ot.h"
-#include "hb.h"
+#include <hb-ot.h>
+#include <hb.h>
namespace WebCore {
@@ -74,7 +74,7 @@ typedef HashMap<uint64_t, RefPtr<FaceCacheEntry>, WTF::IntHash<uint64_t>, WTF::U
static HarfBuzzFaceCache* harfBuzzFaceCache()
{
- DEFINE_STATIC_LOCAL(HarfBuzzFaceCache, s_harfBuzzFaceCache, ());
+ DEPRECATED_DEFINE_STATIC_LOCAL(HarfBuzzFaceCache, s_harfBuzzFaceCache, ());
return &s_harfBuzzFaceCache;
}
diff --git a/Source/WebCore/platform/graphics/harfbuzz/HarfBuzzFace.h b/Source/WebCore/platform/graphics/harfbuzz/HarfBuzzFace.h
index 8066528b5..63179fe81 100644
--- a/Source/WebCore/platform/graphics/harfbuzz/HarfBuzzFace.h
+++ b/Source/WebCore/platform/graphics/harfbuzz/HarfBuzzFace.h
@@ -36,7 +36,6 @@
#include <wtf/HashMap.h>
#include <wtf/PassRefPtr.h>
#include <wtf/RefCounted.h>
-#include <wtf/RefPtr.h>
namespace WebCore {
diff --git a/Source/WebCore/platform/graphics/harfbuzz/HarfBuzzFaceCairo.cpp b/Source/WebCore/platform/graphics/harfbuzz/HarfBuzzFaceCairo.cpp
index 9ddf36a7e..61486ca0e 100644
--- a/Source/WebCore/platform/graphics/harfbuzz/HarfBuzzFaceCairo.cpp
+++ b/Source/WebCore/platform/graphics/harfbuzz/HarfBuzzFaceCairo.cpp
@@ -32,10 +32,11 @@
#include "config.h"
#include "HarfBuzzFace.h"
+#include "CairoUtilities.h"
+#include "Font.h"
#include "FontPlatformData.h"
#include "GlyphBuffer.h"
#include "HarfBuzzShaper.h"
-#include "SimpleFontData.h"
#include "TextEncoding.h"
#include <cairo-ft.h>
#include <cairo.h>
@@ -44,6 +45,7 @@
#include FT_TRUETYPE_TABLES_H
#include <hb.h>
#include <wtf/text/CString.h>
+#include <wtf/text/StringView.h>
namespace WebCore {
@@ -56,21 +58,6 @@ struct HarfBuzzFontData {
cairo_scaled_font_t* m_cairoScaledFont;
};
-class CairoFtFaceLocker {
-public:
- CairoFtFaceLocker(cairo_scaled_font_t* cairoScaledFont) : m_scaledFont(cairoScaledFont) { };
- FT_Face lock()
- {
- return cairo_ft_scaled_font_lock_face(m_scaledFont);
- };
- ~CairoFtFaceLocker()
- {
- cairo_ft_scaled_font_unlock_face(m_scaledFont);
- }
-private:
- cairo_scaled_font_t* m_scaledFont;
-};
-
static hb_position_t floatToHarfBuzzPosition(float value)
{
return static_cast<hb_position_t>(value * (1 << 16));
@@ -113,7 +100,7 @@ static hb_bool_t harfBuzzGetGlyph(hb_font_t*, void* fontData, hb_codepoint_t uni
cairo_glyph_t* glyphs = 0;
int numGlyphs = 0;
UChar ch = unicode;
- CString utf8Codepoint = UTF8Encoding().encode(&ch, 1, QuestionMarksForUnencodables);
+ CString utf8Codepoint = UTF8Encoding().encode(StringView(&ch, 1), QuestionMarksForUnencodables);
if (cairo_scaled_font_text_to_glyphs(scaledFont, 0, 0, utf8Codepoint.data(), utf8Codepoint.length(), &glyphs, &numGlyphs, 0, 0, 0) != CAIRO_STATUS_SUCCESS)
return false;
if (!numGlyphs)
@@ -177,9 +164,9 @@ static hb_blob_t* harfBuzzCairoGetTable(hb_face_t*, hb_tag_t tag, void* userData
return 0;
CairoFtFaceLocker cairoFtFaceLocker(scaledFont);
- FT_Face ftFont = cairoFtFaceLocker.lock();
+ FT_Face ftFont = cairoFtFaceLocker.ftFace();
if (!ftFont)
- return 0;
+ return nullptr;
FT_ULong tableSize = 0;
FT_Error error = FT_Load_Sfnt_Table(ftFont, tag, 0, 0, &tableSize);
diff --git a/Source/WebCore/platform/graphics/harfbuzz/HarfBuzzShaper.cpp b/Source/WebCore/platform/graphics/harfbuzz/HarfBuzzShaper.cpp
index d4985c7a2..9e6f44867 100644
--- a/Source/WebCore/platform/graphics/harfbuzz/HarfBuzzShaper.cpp
+++ b/Source/WebCore/platform/graphics/harfbuzz/HarfBuzzShaper.cpp
@@ -31,17 +31,15 @@
#include "config.h"
#include "HarfBuzzShaper.h"
-#include "Font.h"
+#include "FontCascade.h"
#include "HarfBuzzFace.h"
#include "SurrogatePairAwareTextIterator.h"
-#include "TextRun.h"
-#include "hb-icu.h"
+#include <hb-icu.h>
#include <unicode/normlzr.h>
#include <unicode/uchar.h>
#include <wtf/MathExtras.h>
#include <wtf/StdLibExtras.h>
-#include <wtf/Vector.h>
-#include <wtf/unicode/Unicode.h>
+#include <wtf/text/StringView.h>
namespace WebCore {
@@ -73,7 +71,7 @@ static inline float harfBuzzPositionToFloat(hb_position_t value)
return static_cast<float>(value) / (1 << 16);
}
-HarfBuzzShaper::HarfBuzzRun::HarfBuzzRun(const SimpleFontData* fontData, unsigned startIndex, unsigned numCharacters, TextDirection direction, hb_script_t script)
+HarfBuzzShaper::HarfBuzzRun::HarfBuzzRun(const Font* fontData, unsigned startIndex, unsigned numCharacters, TextDirection direction, hb_script_t script)
: m_fontData(fontData)
, m_startIndex(startIndex)
, m_numCharacters(numCharacters)
@@ -85,6 +83,11 @@ HarfBuzzShaper::HarfBuzzRun::HarfBuzzRun(const SimpleFontData* fontData, unsigne
void HarfBuzzShaper::HarfBuzzRun::applyShapeResult(hb_buffer_t* harfBuzzBuffer)
{
m_numGlyphs = hb_buffer_get_length(harfBuzzBuffer);
+ if (!m_numGlyphs) {
+ // HarfBuzzShaper::fillGlyphBuffer gets offsets()[0]
+ m_offsets.resize(1);
+ return;
+ }
m_glyphs.resize(m_numGlyphs);
m_advances.resize(m_numGlyphs);
m_glyphToCharacterIndexes.resize(m_numGlyphs);
@@ -98,7 +101,7 @@ void HarfBuzzShaper::HarfBuzzRun::setGlyphAndPositions(unsigned index, uint16_t
m_offsets[index] = FloatPoint(offsetX, offsetY);
}
-int HarfBuzzShaper::HarfBuzzRun::characterIndexForXPosition(float targetX)
+unsigned HarfBuzzShaper::HarfBuzzRun::characterIndexForXPosition(float targetX)
{
ASSERT(targetX <= m_width);
float currentX = 0;
@@ -157,9 +160,9 @@ float HarfBuzzShaper::HarfBuzzRun::xPositionForOffset(unsigned offset)
return position;
}
-static void normalizeCharacters(const TextRun& run, UChar* destination, int length)
+static void normalizeCharacters(const TextRun& run, UChar* destination, unsigned length)
{
- int position = 0;
+ unsigned position = 0;
bool error = false;
const UChar* source;
String stringFor8BitRun;
@@ -171,12 +174,12 @@ static void normalizeCharacters(const TextRun& run, UChar* destination, int leng
while (position < length) {
UChar32 character;
- int nextPosition = position;
+ unsigned nextPosition = position;
U16_NEXT(source, nextPosition, length, character);
// Don't normalize tabs as they are not treated as spaces for word-end.
- if (Font::treatAsSpace(character) && character != '\t')
+ if (FontCascade::treatAsSpace(character) && character != '\t')
character = ' ';
- else if (Font::treatAsZeroWidthSpaceInComplexScript(character))
+ else if (FontCascade::treatAsZeroWidthSpaceInComplexScript(character))
character = zeroWidthSpace;
U16_APPEND(destination, position, length, character, error);
ASSERT_UNUSED(error, !error);
@@ -184,7 +187,7 @@ static void normalizeCharacters(const TextRun& run, UChar* destination, int leng
}
}
-HarfBuzzShaper::HarfBuzzShaper(const Font* font, const TextRun& run)
+HarfBuzzShaper::HarfBuzzShaper(const FontCascade* font, const TextRun& run)
: m_font(font)
, m_normalizedBufferLength(0)
, m_run(run)
@@ -193,8 +196,6 @@ HarfBuzzShaper::HarfBuzzShaper(const Font* font, const TextRun& run)
, m_padPerWordBreak(0)
, m_padError(0)
, m_letterSpacing(font->letterSpacing())
- , m_fromIndex(0)
- , m_toIndex(m_run.length())
{
m_normalizedBuffer = std::make_unique<UChar[]>(m_run.length() + 1);
m_normalizedBufferLength = m_run.length();
@@ -207,19 +208,19 @@ HarfBuzzShaper::~HarfBuzzShaper()
{
}
-static void normalizeSpacesAndMirrorChars(const UChar* source, UChar* destination, int length, HarfBuzzShaper::NormalizeMode normalizeMode)
+static void normalizeSpacesAndMirrorChars(const UChar* source, UChar* destination, unsigned length, HarfBuzzShaper::NormalizeMode normalizeMode)
{
- int position = 0;
+ unsigned position = 0;
bool error = false;
// Iterate characters in source and mirror character if needed.
while (position < length) {
UChar32 character;
- int nextPosition = position;
+ unsigned nextPosition = position;
U16_NEXT(source, nextPosition, length, character);
// Don't normalize tabs as they are not treated as spaces for word-end
- if (Font::treatAsSpace(character) && character != '\t')
+ if (FontCascade::treatAsSpace(character) && character != '\t')
character = ' ';
- else if (Font::treatAsZeroWidthSpace(character))
+ else if (FontCascade::treatAsZeroWidthSpace(character))
character = zeroWidthSpace;
else if (normalizeMode == HarfBuzzShaper::NormalizeMirrorChars)
character = u_charMirror(character);
@@ -257,7 +258,7 @@ void HarfBuzzShaper::setNormalizedBuffer(NormalizeMode normalizeMode)
} else
runCharacters = m_run.characters16();
- for (int i = 0; i < m_run.length(); ++i) {
+ for (unsigned i = 0; i < m_run.length(); ++i) {
UChar ch = runCharacters[i];
if (::ublock_getCode(ch) == UBLOCK_COMBINING_DIACRITICAL_MARKS) {
icu::Normalizer::normalize(icu::UnicodeString(runCharacters,
@@ -329,18 +330,9 @@ void HarfBuzzShaper::setPadding(int padding)
m_padPerWordBreak = 0;
}
-
-void HarfBuzzShaper::setDrawRange(int from, int to)
-{
- ASSERT_WITH_SECURITY_IMPLICATION(from >= 0);
- ASSERT_WITH_SECURITY_IMPLICATION(to <= m_run.length());
- m_fromIndex = from;
- m_toIndex = to;
-}
-
void HarfBuzzShaper::setFontFeatures()
{
- const FontDescription& description = m_font->fontDescription();
+ const auto& description = m_font->fontDescription();
if (description.orientation() == Vertical) {
static hb_feature_t vert = { HarfBuzzFace::vertTag, 1, 0, static_cast<unsigned>(-1) };
static hb_feature_t vrt2 = { HarfBuzzFace::vrt2Tag, 1, 0, static_cast<unsigned>(-1) };
@@ -350,30 +342,28 @@ void HarfBuzzShaper::setFontFeatures()
hb_feature_t kerning = { HarfBuzzFace::kernTag, 0, 0, static_cast<unsigned>(-1) };
switch (description.kerning()) {
- case FontDescription::NormalKerning:
+ case Kerning::Normal:
kerning.value = 1;
m_features.append(kerning);
break;
- case FontDescription::NoneKerning:
+ case Kerning::NoShift:
kerning.value = 0;
m_features.append(kerning);
break;
- case FontDescription::AutoKerning:
+ case Kerning::Auto:
break;
default:
ASSERT_NOT_REACHED();
}
- FontFeatureSettings* settings = description.featureSettings();
- if (!settings)
- return;
+ const FontFeatureSettings& settings = description.featureSettings();
- unsigned numFeatures = settings->size();
+ unsigned numFeatures = settings.size();
for (unsigned i = 0; i < numFeatures; ++i) {
hb_feature_t feature;
- const UChar* tag = settings->at(i).tag().characters();
+ auto& tag = settings[i].tag();
feature.tag = HB_TAG(tag[0], tag[1], tag[2], tag[3]);
- feature.value = settings->at(i).value();
+ feature.value = settings[i].value();
feature.start = 0;
feature.end = static_cast<unsigned>(-1);
m_features.append(feature);
@@ -413,7 +403,7 @@ bool HarfBuzzShaper::collectHarfBuzzRuns()
if (!iterator.consume(character, clusterLength))
return false;
- const SimpleFontData* nextFontData = m_font->glyphDataForCharacter(character, false).fontData;
+ const Font* nextFontData = m_font->glyphDataForCharacter(character, false).font;
UErrorCode errorCode = U_ZERO_ERROR;
UScriptCode nextScript = uscript_getScript(character, &errorCode);
if (U_FAILURE(errorCode))
@@ -421,19 +411,21 @@ bool HarfBuzzShaper::collectHarfBuzzRuns()
do {
const UChar* currentCharacterPosition = iterator.characters();
- const SimpleFontData* currentFontData = nextFontData;
+ const Font* currentFontData = nextFontData;
+ if (!currentFontData)
+ currentFontData = &m_font->primaryFont();
UScriptCode currentScript = nextScript;
for (iterator.advance(clusterLength); iterator.consume(character, clusterLength); iterator.advance(clusterLength)) {
- if (Font::treatAsZeroWidthSpace(character))
+ if (FontCascade::treatAsZeroWidthSpace(character))
continue;
if (U_GET_GC_MASK(character) & U_GC_M_MASK) {
- int markLength = clusterLength;
+ unsigned markLength = clusterLength;
const UChar* markCharactersEnd = iterator.characters() + clusterLength;
while (markCharactersEnd < normalizedBufferEnd) {
UChar32 nextCharacter;
- int nextCharacterLength = 0;
+ unsigned nextCharacterLength = 0;
U16_NEXT(markCharactersEnd, nextCharacterLength, normalizedBufferEnd - markCharactersEnd, nextCharacter);
if (!(U_GET_GC_MASK(nextCharacter) & U_GC_M_MASK))
break;
@@ -445,9 +437,9 @@ bool HarfBuzzShaper::collectHarfBuzzRuns()
clusterLength = markLength;
continue;
}
- nextFontData = m_font->glyphDataForCharacter(character, false).fontData;
+ nextFontData = m_font->glyphDataForCharacter(character, false).font;
} else
- nextFontData = m_font->glyphDataForCharacter(character, false).fontData;
+ nextFontData = m_font->glyphDataForCharacter(character, false).font;
nextScript = uscript_getScript(character, &errorCode);
if (U_FAILURE(errorCode))
@@ -458,11 +450,11 @@ bool HarfBuzzShaper::collectHarfBuzzRuns()
nextScript = currentScript;
currentCharacterPosition = iterator.characters();
}
- unsigned numCharactersOfCurrentRun = iterator.currentCharacter() - startIndexOfCurrentRun;
+ unsigned numCharactersOfCurrentRun = iterator.currentIndex() - startIndexOfCurrentRun;
hb_script_t script = hb_icu_script_to_script(currentScript);
- m_harfBuzzRuns.append(HarfBuzzRun::create(currentFontData, startIndexOfCurrentRun, numCharactersOfCurrentRun, m_run.direction(), script));
+ m_harfBuzzRuns.append(std::make_unique<HarfBuzzRun>(currentFontData, startIndexOfCurrentRun, numCharactersOfCurrentRun, m_run.direction(), script));
currentFontData = nextFontData;
- startIndexOfCurrentRun = iterator.currentCharacter();
+ startIndexOfCurrentRun = iterator.currentIndex();
} while (iterator.consume(character, clusterLength));
return !m_harfBuzzRuns.isEmpty();
@@ -477,9 +469,7 @@ bool HarfBuzzShaper::shapeHarfBuzzRuns(bool shouldSetDirection)
for (unsigned i = 0; i < m_harfBuzzRuns.size(); ++i) {
unsigned runIndex = m_run.rtl() ? m_harfBuzzRuns.size() - i - 1 : i;
HarfBuzzRun* currentRun = m_harfBuzzRuns[runIndex].get();
- const SimpleFontData* currentFontData = currentRun->fontData();
- if (currentFontData->isSVGFont())
- return false;
+ const Font* currentFontData = currentRun->fontData();
hb_buffer_set_script(harfBuzzBuffer.get(), currentRun->script());
if (shouldSetDirection)
@@ -494,9 +484,10 @@ bool HarfBuzzShaper::shapeHarfBuzzRuns(bool shouldSetDirection)
hb_buffer_add_utf16(harfBuzzBuffer.get(), &preContext, 1, 1, 0);
if (m_font->isSmallCaps() && u_islower(m_normalizedBuffer[currentRun->startIndex()])) {
- String upperText = String(m_normalizedBuffer.get() + currentRun->startIndex(), currentRun->numCharacters()).upper();
- currentFontData = m_font->glyphDataForCharacter(upperText[0], false, SmallCapsVariant).fontData;
- hb_buffer_add_utf16(harfBuzzBuffer.get(), reinterpret_cast<const uint16_t*>(upperText.characters()), currentRun->numCharacters(), 0, currentRun->numCharacters());
+ String upperText = String(m_normalizedBuffer.get() + currentRun->startIndex(), currentRun->numCharacters()).convertToUppercaseWithoutLocale();
+ currentFontData = m_font->glyphDataForCharacter(upperText[0], false, SmallCapsVariant).font;
+ const UChar* characters = StringView(upperText).upconvertedCharacters();
+ hb_buffer_add_utf16(harfBuzzBuffer.get(), reinterpret_cast<const uint16_t*>(characters), currentRun->numCharacters(), 0, currentRun->numCharacters());
} else
hb_buffer_add_utf16(harfBuzzBuffer.get(), reinterpret_cast<const uint16_t*>(m_normalizedBuffer.get() + currentRun->startIndex()), currentRun->numCharacters(), 0, currentRun->numCharacters());
@@ -523,7 +514,7 @@ bool HarfBuzzShaper::shapeHarfBuzzRuns(bool shouldSetDirection)
void HarfBuzzShaper::setGlyphPositionsForHarfBuzzRun(HarfBuzzRun* currentRun, hb_buffer_t* harfBuzzBuffer)
{
- const SimpleFontData* currentFontData = currentRun->fontData();
+ const Font* currentFontData = currentRun->fontData();
hb_glyph_info_t* glyphInfos = hb_buffer_get_glyph_infos(harfBuzzBuffer, 0);
hb_glyph_position_t* glyphPositions = hb_buffer_get_glyph_positions(harfBuzzBuffer, 0);
@@ -545,7 +536,7 @@ void HarfBuzzShaper::setGlyphPositionsForHarfBuzzRun(HarfBuzzRun* currentRun, hb
glyphToCharacterIndexes[i] = glyphInfos[i].cluster;
- if (isClusterEnd && !Font::treatAsZeroWidthSpace(m_normalizedBuffer[currentCharacterIndex]))
+ if (isClusterEnd && !FontCascade::treatAsZeroWidthSpace(m_normalizedBuffer[currentCharacterIndex]))
spacing += m_letterSpacing;
if (isClusterEnd && isWordEnd(currentCharacterIndex))
@@ -587,15 +578,13 @@ void HarfBuzzShaper::fillGlyphBufferFromHarfBuzzRun(GlyphBuffer* glyphBuffer, Ha
float glyphAdvanceX = advances[i] + nextOffset.x() - currentOffset.x();
float glyphAdvanceY = nextOffset.y() - currentOffset.y();
if (m_run.rtl()) {
- if (currentCharacterIndex > m_toIndex)
+ if (currentCharacterIndex > m_run.length())
m_startOffset.move(glyphAdvanceX, glyphAdvanceY);
- else if (currentCharacterIndex >= m_fromIndex)
- glyphBuffer->add(glyphs[i], currentRun->fontData(), createGlyphBufferAdvance(glyphAdvanceX, glyphAdvanceY));
+ else
+ glyphBuffer->add(glyphs[i], currentRun->fontData(), createGlyphBufferAdvance(glyphAdvanceX, glyphAdvanceY), currentCharacterIndex);
} else {
- if (currentCharacterIndex < m_fromIndex)
- m_startOffset.move(glyphAdvanceX, glyphAdvanceY);
- else if (currentCharacterIndex < m_toIndex)
- glyphBuffer->add(glyphs[i], currentRun->fontData(), createGlyphBufferAdvance(glyphAdvanceX, glyphAdvanceY));
+ if (currentCharacterIndex < m_run.length())
+ glyphBuffer->add(glyphs[i], currentRun->fontData(), createGlyphBufferAdvance(glyphAdvanceX, glyphAdvanceY), currentCharacterIndex);
}
}
}
@@ -655,7 +644,7 @@ int HarfBuzzShaper::offsetForPosition(float targetX)
return charactersSoFar;
}
-FloatRect HarfBuzzShaper::selectionRect(const FloatPoint& point, int height, int from, int to)
+FloatRect HarfBuzzShaper::selectionRect(const FloatPoint& point, int height, unsigned from, unsigned to)
{
float currentX = 0;
float fromX = 0;
@@ -663,23 +652,34 @@ FloatRect HarfBuzzShaper::selectionRect(const FloatPoint& point, int height, int
bool foundFromX = false;
bool foundToX = false;
+ std::optional<unsigned> fromIndex = from;
+ std::optional<unsigned> toIndex = to;
+
if (m_run.rtl())
currentX = m_totalWidth;
for (unsigned i = 0; i < m_harfBuzzRuns.size(); ++i) {
if (m_run.rtl())
currentX -= m_harfBuzzRuns[i]->width();
- int numCharacters = m_harfBuzzRuns[i]->numCharacters();
- if (!foundFromX && from >= 0 && from < numCharacters) {
- fromX = m_harfBuzzRuns[i]->xPositionForOffset(from) + currentX;
+ unsigned numCharacters = m_harfBuzzRuns[i]->numCharacters();
+ if (!foundFromX && fromIndex.value() < numCharacters) {
+ fromX = m_harfBuzzRuns[i]->xPositionForOffset(fromIndex.value()) + currentX;
foundFromX = true;
- } else
- from -= numCharacters;
+ } else {
+ if (fromIndex && fromIndex.value() >= numCharacters)
+ fromIndex.value() -= numCharacters;
+ else
+ fromIndex = std::nullopt;
+ }
- if (!foundToX && to >= 0 && to < numCharacters) {
- toX = m_harfBuzzRuns[i]->xPositionForOffset(to) + currentX;
+ if (!foundToX && toIndex.value() < numCharacters) {
+ toX = m_harfBuzzRuns[i]->xPositionForOffset(toIndex.value()) + currentX;
foundToX = true;
- } else
- to -= numCharacters;
+ } else {
+ if (toIndex && toIndex.value() >= numCharacters)
+ toIndex.value() -= numCharacters;
+ else
+ toIndex = std::nullopt;
+ }
if (foundFromX && foundToX)
break;
diff --git a/Source/WebCore/platform/graphics/harfbuzz/HarfBuzzShaper.h b/Source/WebCore/platform/graphics/harfbuzz/HarfBuzzShaper.h
index 5747e4304..593c87cd2 100644
--- a/Source/WebCore/platform/graphics/harfbuzz/HarfBuzzShaper.h
+++ b/Source/WebCore/platform/graphics/harfbuzz/HarfBuzzShaper.h
@@ -37,15 +37,13 @@
#include "hb.h"
#include <memory>
#include <wtf/HashSet.h>
-#include <wtf/OwnPtr.h>
-#include <wtf/PassOwnPtr.h>
#include <wtf/Vector.h>
#include <wtf/unicode/CharacterNames.h>
namespace WebCore {
class Font;
-class SimpleFontData;
+class FontCascade;
class HarfBuzzShaper {
public:
@@ -54,49 +52,43 @@ public:
NormalizeMirrorChars
};
- HarfBuzzShaper(const Font*, const TextRun&);
+ HarfBuzzShaper(const FontCascade*, const TextRun&);
virtual ~HarfBuzzShaper();
- void setDrawRange(int from, int to);
bool shape(GlyphBuffer* = 0);
FloatPoint adjustStartPoint(const FloatPoint&);
float totalWidth() { return m_totalWidth; }
int offsetForPosition(float targetX);
- FloatRect selectionRect(const FloatPoint&, int height, int from, int to);
+ FloatRect selectionRect(const FloatPoint&, int height, unsigned from, unsigned to);
private:
class HarfBuzzRun {
public:
- static PassOwnPtr<HarfBuzzRun> create(const SimpleFontData* fontData, unsigned startIndex, unsigned numCharacters, TextDirection direction, hb_script_t script)
- {
- return adoptPtr(new HarfBuzzRun(fontData, startIndex, numCharacters, direction, script));
- }
+ HarfBuzzRun(const Font*, unsigned startIndex, unsigned numCharacters, TextDirection, hb_script_t);
void applyShapeResult(hb_buffer_t*);
void setGlyphAndPositions(unsigned index, uint16_t glyphId, float advance, float offsetX, float offsetY);
void setWidth(float width) { m_width = width; }
- int characterIndexForXPosition(float targetX);
+ unsigned characterIndexForXPosition(float targetX);
float xPositionForOffset(unsigned offset);
- const SimpleFontData* fontData() { return m_fontData; }
+ const Font* fontData() { return m_fontData; }
unsigned startIndex() const { return m_startIndex; }
unsigned numCharacters() const { return m_numCharacters; }
unsigned numGlyphs() const { return m_numGlyphs; }
- uint16_t* glyphs() { return &m_glyphs[0]; }
- float* advances() { return &m_advances[0]; }
- FloatPoint* offsets() { return &m_offsets[0]; }
- uint16_t* glyphToCharacterIndexes() { return &m_glyphToCharacterIndexes[0]; }
+ uint16_t* glyphs() { return m_glyphs.data(); }
+ float* advances() { return m_advances.data(); }
+ FloatPoint* offsets() { return m_offsets.data(); }
+ uint16_t* glyphToCharacterIndexes() { return m_glyphToCharacterIndexes.data(); }
float width() { return m_width; }
bool rtl() { return m_direction == RTL; }
hb_script_t script() { return m_script; }
private:
- HarfBuzzRun(const SimpleFontData*, unsigned startIndex, unsigned numCharacters, TextDirection, hb_script_t);
-
- const SimpleFontData* m_fontData;
+ const Font* m_fontData;
unsigned m_startIndex;
- size_t m_numCharacters;
+ unsigned m_numCharacters;
unsigned m_numGlyphs;
TextDirection m_direction;
hb_script_t m_script;
@@ -128,7 +120,7 @@ private:
GlyphBufferAdvance createGlyphBufferAdvance(float, float);
- const Font* m_font;
+ const FontCascade* m_font;
std::unique_ptr<UChar[]> m_normalizedBuffer;
unsigned m_normalizedBufferLength;
const TextRun& m_run;
@@ -140,13 +132,10 @@ private:
int m_letterSpacing; // Pixels to be added after each glyph.
Vector<hb_feature_t, 4> m_features;
- Vector<OwnPtr<HarfBuzzRun>, 16> m_harfBuzzRuns;
+ Vector<std::unique_ptr<HarfBuzzRun>, 16> m_harfBuzzRuns;
FloatPoint m_startOffset;
- int m_fromIndex;
- int m_toIndex;
-
float m_totalWidth;
};
diff --git a/Source/WebCore/platform/graphics/opengl/Extensions3DOpenGL.cpp b/Source/WebCore/platform/graphics/opengl/Extensions3DOpenGL.cpp
index f8321cac0..5da632035 100644
--- a/Source/WebCore/platform/graphics/opengl/Extensions3DOpenGL.cpp
+++ b/Source/WebCore/platform/graphics/opengl/Extensions3DOpenGL.cpp
@@ -25,20 +25,17 @@
#include "config.h"
-#if USE(3D_GRAPHICS)
+#if ENABLE(GRAPHICS_CONTEXT_3D)
#include "Extensions3DOpenGL.h"
#include "GraphicsContext3D.h"
-#include <wtf/Vector.h>
#if PLATFORM(IOS)
-#include "ANGLE/ShaderLang.h"
#include <OpenGLES/ES2/glext.h>
#elif PLATFORM(MAC)
-#include "ANGLE/ShaderLang.h"
#include <OpenGL/gl.h>
-#elif PLATFORM(GTK) || PLATFORM(EFL) || PLATFORM(WIN)
+#elif PLATFORM(GTK) || PLATFORM(WIN)
#include "OpenGLShims.h"
#endif
@@ -48,8 +45,8 @@
namespace WebCore {
-Extensions3DOpenGL::Extensions3DOpenGL(GraphicsContext3D* context)
- : Extensions3DOpenGLCommon(context)
+Extensions3DOpenGL::Extensions3DOpenGL(GraphicsContext3D* context, bool useIndexedGetString)
+ : Extensions3DOpenGLCommon(context, useIndexedGetString)
{
}
@@ -86,7 +83,7 @@ Platform3DObject Extensions3DOpenGL::createVertexArrayOES()
{
m_context->makeContextCurrent();
GLuint array = 0;
-#if (PLATFORM(GTK) || PLATFORM(EFL) || PLATFORM(WIN))
+#if (PLATFORM(GTK) || PLATFORM(WIN) || PLATFORM(IOS))
if (isVertexArrayObjectSupported())
glGenVertexArrays(1, &array);
#elif defined(GL_APPLE_vertex_array_object) && GL_APPLE_vertex_array_object
@@ -101,7 +98,7 @@ void Extensions3DOpenGL::deleteVertexArrayOES(Platform3DObject array)
return;
m_context->makeContextCurrent();
-#if (PLATFORM(GTK) || PLATFORM(EFL) || PLATFORM(WIN))
+#if (PLATFORM(GTK) || PLATFORM(WIN) || PLATFORM(IOS))
if (isVertexArrayObjectSupported())
glDeleteVertexArrays(1, &array);
#elif defined(GL_APPLE_vertex_array_object) && GL_APPLE_vertex_array_object
@@ -115,7 +112,7 @@ GC3Dboolean Extensions3DOpenGL::isVertexArrayOES(Platform3DObject array)
return GL_FALSE;
m_context->makeContextCurrent();
-#if (PLATFORM(GTK) || PLATFORM(EFL) || PLATFORM(WIN))
+#if (PLATFORM(GTK) || PLATFORM(WIN) || PLATFORM(IOS))
if (isVertexArrayObjectSupported())
return glIsVertexArray(array);
#elif defined(GL_APPLE_vertex_array_object) && GL_APPLE_vertex_array_object
@@ -126,12 +123,8 @@ GC3Dboolean Extensions3DOpenGL::isVertexArrayOES(Platform3DObject array)
void Extensions3DOpenGL::bindVertexArrayOES(Platform3DObject array)
{
-#if PLATFORM(IOS)
- UNUSED_PARAM(array);
-#endif
-
m_context->makeContextCurrent();
-#if (PLATFORM(GTK) || PLATFORM(EFL) || PLATFORM(WIN))
+#if (PLATFORM(GTK) || PLATFORM(WIN) || PLATFORM(IOS))
if (isVertexArrayObjectSupported())
glBindVertexArray(array);
#elif defined(GL_APPLE_vertex_array_object) && GL_APPLE_vertex_array_object
@@ -141,12 +134,6 @@ void Extensions3DOpenGL::bindVertexArrayOES(Platform3DObject array)
#endif
}
-void Extensions3DOpenGL::copyTextureCHROMIUM(GC3Denum, Platform3DObject, Platform3DObject, GC3Dint, GC3Denum)
-{
- // FIXME: implement this function and add GL_CHROMIUM_copy_texture in supports().
- return;
-}
-
void Extensions3DOpenGL::insertEventMarkerEXT(const String&)
{
// FIXME: implement this function and add GL_EXT_debug_marker in supports().
@@ -168,25 +155,50 @@ void Extensions3DOpenGL::popGroupMarkerEXT(void)
bool Extensions3DOpenGL::supportsExtension(const String& name)
{
// GL_ANGLE_framebuffer_blit and GL_ANGLE_framebuffer_multisample are "fake". They are implemented using other
- // extensions. In particular GL_EXT_framebuffer_blit and GL_EXT_framebuffer_multisample
+ // extensions. In particular GL_EXT_framebuffer_blit and GL_EXT_framebuffer_multisample/GL_APPLE_framebuffer_multisample.
if (name == "GL_ANGLE_framebuffer_blit")
return m_availableExtensions.contains("GL_EXT_framebuffer_blit");
if (name == "GL_ANGLE_framebuffer_multisample")
+#if PLATFORM(IOS)
+ return m_availableExtensions.contains("GL_APPLE_framebuffer_multisample");
+#else
return m_availableExtensions.contains("GL_EXT_framebuffer_multisample");
+#endif
+
+ if (name == "GL_ANGLE_instanced_arrays") {
+ return (m_availableExtensions.contains("GL_ARB_instanced_arrays") || m_availableExtensions.contains("GL_EXT_instanced_arrays"))
+ && (m_availableExtensions.contains("GL_ARB_draw_instanced") || m_availableExtensions.contains("GL_EXT_draw_instanced"));
+ }
+
+ if (name == "GL_EXT_sRGB")
+#if PLATFORM(IOS)
+ return m_availableExtensions.contains("GL_EXT_sRGB");
+#else
+ return m_availableExtensions.contains("GL_EXT_texture_sRGB") && (m_availableExtensions.contains("GL_EXT_framebuffer_sRGB") || m_availableExtensions.contains("GL_ARB_framebuffer_sRGB"));
+#endif
+
+ if (name == "GL_EXT_frag_depth")
+#if PLATFORM(MAC)
+ return true;
+#else
+ return m_availableExtensions.contains("GL_EXT_frag_depth");
+#endif
// Desktop GL always supports GL_OES_rgb8_rgba8.
if (name == "GL_OES_rgb8_rgba8")
return true;
- // If GL_ARB_texture_float is available then we report GL_OES_texture_float,
+ // If GL_ARB_texture_float or GL_OES_texture_float is available then we report
// GL_OES_texture_half_float, GL_OES_texture_float_linear and GL_OES_texture_half_float_linear as available.
if (name == "GL_OES_texture_float" || name == "GL_OES_texture_half_float" || name == "GL_OES_texture_float_linear" || name == "GL_OES_texture_half_float_linear")
- return m_availableExtensions.contains("GL_ARB_texture_float");
+ return m_availableExtensions.contains("GL_ARB_texture_float") || m_availableExtensions.contains("GL_OES_texture_float");
// GL_OES_vertex_array_object
if (name == "GL_OES_vertex_array_object") {
-#if (PLATFORM(GTK) || PLATFORM(EFL))
+#if (PLATFORM(GTK))
return m_availableExtensions.contains("GL_ARB_vertex_array_object");
+#elif PLATFORM(IOS)
+ return m_availableExtensions.contains("GL_OES_vertex_array_object");
#else
return m_availableExtensions.contains("GL_APPLE_vertex_array_object");
#endif
@@ -199,12 +211,17 @@ bool Extensions3DOpenGL::supportsExtension(const String& name)
// Desktop GL always supports UNSIGNED_INT indices
if (name == "GL_OES_element_index_uint")
return true;
-
+
+ if (name == "GL_EXT_shader_texture_lod")
+ return m_availableExtensions.contains("GL_EXT_shader_texture_lod");
+
if (name == "GL_EXT_texture_filter_anisotropic")
return m_availableExtensions.contains("GL_EXT_texture_filter_anisotropic");
if (name == "GL_EXT_draw_buffers") {
-#if PLATFORM(MAC)
+#if PLATFORM(IOS)
+ return m_availableExtensions.contains(name);
+#elif PLATFORM(MAC) || PLATFORM(GTK)
return m_availableExtensions.contains("GL_ARB_draw_buffers");
#else
// FIXME: implement support for other platforms.
@@ -223,20 +240,67 @@ bool Extensions3DOpenGL::supportsExtension(const String& name)
void Extensions3DOpenGL::drawBuffersEXT(GC3Dsizei n, const GC3Denum* bufs)
{
// FIXME: implement support for other platforms.
-#if PLATFORM(MAC) && !PLATFORM(IOS)
+#if PLATFORM(MAC)
::glDrawBuffersARB(n, bufs);
+#elif PLATFORM(GTK)
+ ::glDrawBuffers(n, bufs);
#else
UNUSED_PARAM(n);
UNUSED_PARAM(bufs);
#endif
}
+void Extensions3DOpenGL::drawArraysInstanced(GC3Denum mode, GC3Dint first, GC3Dsizei count, GC3Dsizei primcount)
+{
+ m_context->makeContextCurrent();
+#if PLATFORM(GTK)
+ ::glDrawArraysInstanced(mode, first, count, primcount);
+#elif PLATFORM(COCOA)
+ ::glDrawArraysInstancedARB(mode, first, count, primcount);
+#else
+ UNUSED_PARAM(mode);
+ UNUSED_PARAM(first);
+ UNUSED_PARAM(count);
+ UNUSED_PARAM(primcount);
+#endif
+}
+
+void Extensions3DOpenGL::drawElementsInstanced(GC3Denum mode, GC3Dsizei count, GC3Denum type, long long offset, GC3Dsizei primcount)
+{
+ m_context->makeContextCurrent();
+#if PLATFORM(GTK)
+ ::glDrawElementsInstanced(mode, count, type, reinterpret_cast<GLvoid*>(static_cast<intptr_t>(offset)), primcount);
+#elif PLATFORM(COCOA)
+ ::glDrawElementsInstancedARB(mode, count, type, reinterpret_cast<GLvoid*>(static_cast<intptr_t>(offset)), primcount);
+#else
+ UNUSED_PARAM(mode);
+ UNUSED_PARAM(count);
+ UNUSED_PARAM(type);
+ UNUSED_PARAM(offset);
+ UNUSED_PARAM(primcount);
+#endif
+}
+
+void Extensions3DOpenGL::vertexAttribDivisor(GC3Duint index, GC3Duint divisor)
+{
+ m_context->makeContextCurrent();
+#if PLATFORM(GTK)
+ ::glVertexAttribDivisor(index, divisor);
+#elif PLATFORM(COCOA)
+ ::glVertexAttribDivisorARB(index, divisor);
+#else
+ UNUSED_PARAM(index);
+ UNUSED_PARAM(divisor);
+#endif
+}
+
String Extensions3DOpenGL::getExtensions()
{
+ ASSERT(!m_useIndexedGetString);
return String(reinterpret_cast<const char*>(::glGetString(GL_EXTENSIONS)));
}
-#if (PLATFORM(GTK) || PLATFORM(EFL) || PLATFORM(WIN))
+#if (PLATFORM(GTK) || PLATFORM(WIN) || PLATFORM(IOS))
bool Extensions3DOpenGL::isVertexArrayObjectSupported()
{
static const bool supportsVertexArrayObject = supports("GL_OES_vertex_array_object");
@@ -246,4 +310,4 @@ bool Extensions3DOpenGL::isVertexArrayObjectSupported()
} // namespace WebCore
-#endif // USE(3D_GRAPHICS)
+#endif // ENABLE(GRAPHICS_CONTEXT_3D)
diff --git a/Source/WebCore/platform/graphics/opengl/Extensions3DOpenGL.h b/Source/WebCore/platform/graphics/opengl/Extensions3DOpenGL.h
index 81c5e49d5..c3fe83eb5 100644
--- a/Source/WebCore/platform/graphics/opengl/Extensions3DOpenGL.h
+++ b/Source/WebCore/platform/graphics/opengl/Extensions3DOpenGL.h
@@ -23,8 +23,7 @@
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef Extensions3DOpenGL_h
-#define Extensions3DOpenGL_h
+#pragma once
#include "Extensions3DOpenGLCommon.h"
@@ -37,35 +36,35 @@ namespace WebCore {
class Extensions3DOpenGL : public Extensions3DOpenGLCommon {
WTF_MAKE_FAST_ALLOCATED;
public:
+ // This class only needs to be instantiated by GraphicsContext3D implementations.
+ Extensions3DOpenGL(GraphicsContext3D*, bool useIndexedGetString);
virtual ~Extensions3DOpenGL();
// Extensions3D methods.
- virtual void blitFramebuffer(long srcX0, long srcY0, long srcX1, long srcY1, long dstX0, long dstY0, long dstX1, long dstY1, unsigned long mask, unsigned long filter);
- virtual void renderbufferStorageMultisample(unsigned long target, unsigned long samples, unsigned long internalformat, unsigned long width, unsigned long height);
+ void blitFramebuffer(long srcX0, long srcY0, long srcX1, long srcY1, long dstX0, long dstY0, long dstX1, long dstY1, unsigned long mask, unsigned long filter) override;
+ void renderbufferStorageMultisample(unsigned long target, unsigned long samples, unsigned long internalformat, unsigned long width, unsigned long height) override;
+
+ Platform3DObject createVertexArrayOES() override;
+ void deleteVertexArrayOES(Platform3DObject) override;
+ GC3Dboolean isVertexArrayOES(Platform3DObject) override;
+ void bindVertexArrayOES(Platform3DObject) override;
+ void insertEventMarkerEXT(const String&) override;
+ void pushGroupMarkerEXT(const String&) override;
+ void popGroupMarkerEXT(void) override;
+ void drawBuffersEXT(GC3Dsizei, const GC3Denum*) override;
- virtual Platform3DObject createVertexArrayOES();
- virtual void deleteVertexArrayOES(Platform3DObject);
- virtual GC3Dboolean isVertexArrayOES(Platform3DObject);
- virtual void bindVertexArrayOES(Platform3DObject);
- virtual void copyTextureCHROMIUM(GC3Denum, Platform3DObject, Platform3DObject, GC3Dint, GC3Denum);
- virtual void insertEventMarkerEXT(const String&);
- virtual void pushGroupMarkerEXT(const String&);
- virtual void popGroupMarkerEXT(void);
- virtual void drawBuffersEXT(GC3Dsizei, const GC3Denum*);
+ void drawArraysInstanced(GC3Denum mode, GC3Dint first, GC3Dsizei count, GC3Dsizei primcount) override;
+ void drawElementsInstanced(GC3Denum mode, GC3Dsizei count, GC3Denum type, long long offset, GC3Dsizei primcount) override;
+ void vertexAttribDivisor(GC3Duint index, GC3Duint divisor) override;
protected:
- // This class only needs to be instantiated by GraphicsContext3D implementations.
- friend class GraphicsContext3D;
- Extensions3DOpenGL(GraphicsContext3D*);
+ bool supportsExtension(const WTF::String&) override;
+ String getExtensions() override;
- virtual bool supportsExtension(const WTF::String&);
- virtual String getExtensions();
-#if (PLATFORM(GTK) || PLATFORM(EFL) || PLATFORM(WIN))
private:
+#if (PLATFORM(GTK) || PLATFORM(WIN) || PLATFORM(IOS))
bool isVertexArrayObjectSupported();
#endif
};
} // namespace WebCore
-
-#endif // Extensions3DOpenGL_h
diff --git a/Source/WebCore/platform/graphics/opengl/Extensions3DOpenGLCommon.cpp b/Source/WebCore/platform/graphics/opengl/Extensions3DOpenGLCommon.cpp
index 9b7c5ab65..d28d765df 100644
--- a/Source/WebCore/platform/graphics/opengl/Extensions3DOpenGLCommon.cpp
+++ b/Source/WebCore/platform/graphics/opengl/Extensions3DOpenGLCommon.cpp
@@ -26,7 +26,7 @@
#include "config.h"
-#if USE(3D_GRAPHICS)
+#if ENABLE(GRAPHICS_CONTEXT_3D)
#include "Extensions3DOpenGLCommon.h"
#include "ANGLEWebKitBridge.h"
@@ -34,14 +34,18 @@
#if PLATFORM(IOS)
#include <OpenGLES/ES2/glext.h>
+#include <OpenGLES/ES3/gl.h>
#else
#if USE(OPENGL_ES_2)
#include "OpenGLESShims.h"
#include <GLES2/gl2.h>
#include <GLES2/gl2ext.h>
#elif PLATFORM(MAC)
+#define GL_DO_NOT_WARN_IF_MULTI_GL_VERSION_HEADERS_INCLUDED
#include <OpenGL/gl.h>
-#elif PLATFORM(GTK) || PLATFORM(EFL) || PLATFORM(WIN)
+#include <OpenGL/gl3.h>
+#undef GL_DO_NOT_WARN_IF_MULTI_GL_VERSION_HEADERS_INCLUDED
+#elif PLATFORM(GTK) || PLATFORM(WIN)
#include "OpenGLShims.h"
#endif
#endif
@@ -49,54 +53,39 @@
#include <wtf/MainThread.h>
#include <wtf/Vector.h>
-#if PLATFORM(WIN) || (PLATFORM(GTK) && OS(WINDOWS))
-#undef NO_ERROR
-#endif
-
namespace WebCore {
-Extensions3DOpenGLCommon::Extensions3DOpenGLCommon(GraphicsContext3D* context)
+Extensions3DOpenGLCommon::Extensions3DOpenGLCommon(GraphicsContext3D* context, bool useIndexedGetString)
: m_initializedAvailableExtensions(false)
, m_context(context)
, m_isNVIDIA(false)
, m_isAMD(false)
, m_isIntel(false)
- , m_maySupportMultisampling(true)
+ , m_isImagination(false)
, m_requiresBuiltInFunctionEmulation(false)
+ , m_requiresRestrictedMaximumTextureSize(false)
+ , m_useIndexedGetString(useIndexedGetString)
{
m_vendor = String(reinterpret_cast<const char*>(::glGetString(GL_VENDOR)));
+ m_renderer = String(reinterpret_cast<const char*>(::glGetString(GL_RENDERER)));
Vector<String> vendorComponents;
- m_vendor.lower().split(' ', vendorComponents);
+ m_vendor.convertToASCIILowercase().split(' ', vendorComponents);
if (vendorComponents.contains("nvidia"))
m_isNVIDIA = true;
if (vendorComponents.contains("ati") || vendorComponents.contains("amd"))
m_isAMD = true;
if (vendorComponents.contains("intel"))
m_isIntel = true;
+ if (vendorComponents.contains("imagination"))
+ m_isImagination = true;
-#if PLATFORM(MAC) && !PLATFORM(IOS)
+#if PLATFORM(MAC)
if (m_isAMD || m_isIntel)
m_requiresBuiltInFunctionEmulation = true;
- // Currently in Mac we only allow multisampling if the vendor is NVIDIA,
- // or if the vendor is AMD/ATI and the system is 10.7.2 and above.
-
- bool systemSupportsMultisampling = true;
-#if !PLATFORM(IOS) && __MAC_OS_X_VERSION_MIN_REQUIRED < 1080
- ASSERT(isMainThread());
- static SInt32 version;
- if (!version) {
- if (Gestalt(gestaltSystemVersion, &version) != noErr)
- systemSupportsMultisampling = false;
- }
- // See https://bugs.webkit.org/show_bug.cgi?id=77922 for more details
- if (systemSupportsMultisampling)
- systemSupportsMultisampling = version >= 0x1072;
-#endif // SNOW_LEOPARD and LION
-
- if (m_isAMD && !systemSupportsMultisampling)
- m_maySupportMultisampling = false;
+ // Intel HD 3000 devices have problems with large textures. <rdar://problem/16649140>
+ m_requiresRestrictedMaximumTextureSize = m_renderer.startsWith("Intel HD Graphics 3000");
#endif
}
@@ -109,6 +98,12 @@ bool Extensions3DOpenGLCommon::supports(const String& name)
if (!m_initializedAvailableExtensions)
initializeAvailableExtensions();
+ // We explicitly do not support this extension until
+ // we fix the following bug:
+ // https://bugs.webkit.org/show_bug.cgi?id=149734
+ if (name == "GL_ANGLE_translated_shader_source")
+ return false;
+
return supportsExtension(name);
}
@@ -131,6 +126,22 @@ void Extensions3DOpenGLCommon::ensureEnabled(const String& name)
m_context->getIntegerv(Extensions3D::MAX_DRAW_BUFFERS_EXT, &ANGLEResources.MaxDrawBuffers);
compiler.setResources(ANGLEResources);
}
+ } else if (name == "GL_EXT_shader_texture_lod") {
+ // Enable support in ANGLE (if not enabled already)
+ ANGLEWebKitBridge& compiler = m_context->m_compiler;
+ ShBuiltInResources ANGLEResources = compiler.getResources();
+ if (!ANGLEResources.EXT_shader_texture_lod) {
+ ANGLEResources.EXT_shader_texture_lod = 1;
+ compiler.setResources(ANGLEResources);
+ }
+ } else if (name == "GL_EXT_frag_depth") {
+ // Enable support in ANGLE (if not enabled already)
+ ANGLEWebKitBridge& compiler = m_context->m_compiler;
+ ShBuiltInResources ANGLEResources = compiler.getResources();
+ if (!ANGLEResources.EXT_frag_depth) {
+ ANGLEResources.EXT_frag_depth = 1;
+ compiler.setResources(ANGLEResources);
+ }
}
}
@@ -174,22 +185,20 @@ String Extensions3DOpenGLCommon::getTranslatedShaderSourceANGLE(Platform3DObject
String translatedShaderSource;
String shaderInfoLog;
- int extraCompileOptions = SH_MAP_LONG_VARIABLE_NAMES | SH_CLAMP_INDIRECT_ARRAY_BOUNDS | SH_UNFOLD_SHORT_CIRCUIT | SH_ENFORCE_PACKING_RESTRICTIONS;
+ int extraCompileOptions = SH_CLAMP_INDIRECT_ARRAY_BOUNDS | SH_UNFOLD_SHORT_CIRCUIT | SH_INIT_OUTPUT_VARIABLES | SH_ENFORCE_PACKING_RESTRICTIONS | SH_LIMIT_EXPRESSION_COMPLEXITY | SH_LIMIT_CALL_STACK_DEPTH;
if (m_requiresBuiltInFunctionEmulation)
- extraCompileOptions |= SH_EMULATE_BUILT_IN_FUNCTIONS;
+ extraCompileOptions |= SH_EMULATE_ABS_INT_FUNCTION;
- Vector<ANGLEShaderSymbol> symbols;
+ Vector<std::pair<ANGLEShaderSymbolType, sh::ShaderVariable>> symbols;
bool isValid = compiler.compileShaderSource(entry.source.utf8().data(), shaderType, translatedShaderSource, shaderInfoLog, symbols, extraCompileOptions);
entry.log = shaderInfoLog;
entry.isValid = isValid;
- size_t numSymbols = symbols.size();
- for (size_t i = 0; i < numSymbols; ++i) {
- ANGLEShaderSymbol shaderSymbol = symbols[i];
- GraphicsContext3D::SymbolInfo symbolInfo(shaderSymbol.dataType, shaderSymbol.size, shaderSymbol.mappedName, shaderSymbol.precision, shaderSymbol.staticUse);
- entry.symbolMap(shaderSymbol.symbolType).set(shaderSymbol.name, symbolInfo);
+ for (const std::pair<ANGLEShaderSymbolType, sh::ShaderVariable>& pair : symbols) {
+ const std::string& name = pair.second.name;
+ entry.symbolMap(pair.first).set(String(name.c_str(), name.length()), pair.second);
}
if (!isValid)
@@ -200,11 +209,30 @@ String Extensions3DOpenGLCommon::getTranslatedShaderSourceANGLE(Platform3DObject
void Extensions3DOpenGLCommon::initializeAvailableExtensions()
{
- String extensionsString = getExtensions();
- Vector<String> availableExtensions;
- extensionsString.split(" ", availableExtensions);
- for (size_t i = 0; i < availableExtensions.size(); ++i)
- m_availableExtensions.add(availableExtensions[i]);
+#if PLATFORM(MAC) || (PLATFORM(GTK) && !USE(OPENGL_ES_2))
+ if (m_useIndexedGetString) {
+ GLint numExtensions = 0;
+ ::glGetIntegerv(GL_NUM_EXTENSIONS, &numExtensions);
+ for (GLint i = 0; i < numExtensions; ++i)
+ m_availableExtensions.add(glGetStringi(GL_EXTENSIONS, i));
+
+ if (!m_availableExtensions.contains(ASCIILiteral("GL_ARB_texture_storage"))) {
+ GLint majorVersion;
+ glGetIntegerv(GL_MAJOR_VERSION, &majorVersion);
+ GLint minorVersion;
+ glGetIntegerv(GL_MINOR_VERSION, &minorVersion);
+ if (majorVersion > 4 || (majorVersion == 4 && minorVersion >= 2))
+ m_availableExtensions.add(ASCIILiteral("GL_ARB_texture_storage"));
+ }
+ } else
+#endif
+ {
+ String extensionsString = getExtensions();
+ Vector<String> availableExtensions;
+ extensionsString.split(' ', availableExtensions);
+ for (size_t i = 0; i < availableExtensions.size(); ++i)
+ m_availableExtensions.add(availableExtensions[i]);
+ }
m_initializedAvailableExtensions = true;
}
@@ -225,4 +253,4 @@ void Extensions3DOpenGLCommon::getnUniformivEXT(GC3Duint, int, GC3Dsizei, int *)
} // namespace WebCore
-#endif // USE(3D_GRAPHICS)
+#endif // ENABLE(GRAPHICS_CONTEXT_3D)
diff --git a/Source/WebCore/platform/graphics/opengl/Extensions3DOpenGLCommon.h b/Source/WebCore/platform/graphics/opengl/Extensions3DOpenGLCommon.h
index 4cc8dbac1..7c614fb40 100644
--- a/Source/WebCore/platform/graphics/opengl/Extensions3DOpenGLCommon.h
+++ b/Source/WebCore/platform/graphics/opengl/Extensions3DOpenGLCommon.h
@@ -24,8 +24,7 @@
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef Extensions3DOpenGLCommon_h
-#define Extensions3DOpenGLCommon_h
+#pragma once
#include "Extensions3D.h"
@@ -40,36 +39,37 @@ public:
virtual ~Extensions3DOpenGLCommon();
// Extensions3D methods.
- virtual bool supports(const String&);
- virtual void ensureEnabled(const String&);
- virtual bool isEnabled(const String&);
- virtual int getGraphicsResetStatusARB();
+ bool supports(const String&) override;
+ void ensureEnabled(const String&) override;
+ bool isEnabled(const String&) override;
+ int getGraphicsResetStatusARB() override;
- virtual Platform3DObject createVertexArrayOES() = 0;
- virtual void deleteVertexArrayOES(Platform3DObject) = 0;
- virtual GC3Dboolean isVertexArrayOES(Platform3DObject) = 0;
- virtual void bindVertexArrayOES(Platform3DObject) = 0;
+ Platform3DObject createVertexArrayOES() override = 0;
+ void deleteVertexArrayOES(Platform3DObject) override = 0;
+ GC3Dboolean isVertexArrayOES(Platform3DObject) override = 0;
+ void bindVertexArrayOES(Platform3DObject) override = 0;
- virtual void drawBuffersEXT(GC3Dsizei, const GC3Denum*) = 0;
+ void drawBuffersEXT(GC3Dsizei, const GC3Denum*) override = 0;
- virtual String getTranslatedShaderSourceANGLE(Platform3DObject);
+ String getTranslatedShaderSourceANGLE(Platform3DObject) override;
// EXT Robustness - uses getGraphicsResetStatusARB()
- virtual void readnPixelsEXT(int x, int y, GC3Dsizei width, GC3Dsizei height, GC3Denum format, GC3Denum type, GC3Dsizei bufSize, void *data);
- virtual void getnUniformfvEXT(GC3Duint program, int location, GC3Dsizei bufSize, float *params);
- virtual void getnUniformivEXT(GC3Duint program, int location, GC3Dsizei bufSize, int *params);
+ void readnPixelsEXT(int x, int y, GC3Dsizei width, GC3Dsizei height, GC3Denum format, GC3Denum type, GC3Dsizei bufSize, void *data) override;
+ void getnUniformfvEXT(GC3Duint program, int location, GC3Dsizei bufSize, float *params) override;
+ void getnUniformivEXT(GC3Duint program, int location, GC3Dsizei bufSize, int *params) override;
- virtual bool isNVIDIA() { return m_isNVIDIA; }
- virtual bool isAMD() { return m_isAMD; }
- virtual bool isIntel() { return m_isIntel; }
- virtual String vendor() { return m_vendor; }
+ bool isNVIDIA() override { return m_isNVIDIA; }
+ bool isAMD() override { return m_isAMD; }
+ bool isIntel() override { return m_isIntel; }
+ bool isImagination() override { return m_isImagination; }
+ String vendor() override { return m_vendor; }
- virtual bool maySupportMultisampling() { return m_maySupportMultisampling; }
- virtual bool requiresBuiltInFunctionEmulation() { return m_requiresBuiltInFunctionEmulation; }
+ bool requiresBuiltInFunctionEmulation() override { return m_requiresBuiltInFunctionEmulation; }
+ bool requiresRestrictedMaximumTextureSize() override { return m_requiresRestrictedMaximumTextureSize; }
protected:
friend class Extensions3DOpenGLES;
- Extensions3DOpenGLCommon(GraphicsContext3D*);
+ Extensions3DOpenGLCommon(GraphicsContext3D*, bool useIndexedGetString);
virtual bool supportsExtension(const String&) = 0;
virtual String getExtensions() = 0;
@@ -84,12 +84,14 @@ protected:
bool m_isNVIDIA;
bool m_isAMD;
bool m_isIntel;
- bool m_maySupportMultisampling;
+ bool m_isImagination;
bool m_requiresBuiltInFunctionEmulation;
+ bool m_requiresRestrictedMaximumTextureSize;
+
+ bool m_useIndexedGetString { false };
String m_vendor;
+ String m_renderer;
};
} // namespace WebCore
-
-#endif // Extensions3DOpenGLCommon_h
diff --git a/Source/WebCore/platform/graphics/opengl/Extensions3DOpenGLES.cpp b/Source/WebCore/platform/graphics/opengl/Extensions3DOpenGLES.cpp
index dc9e59252..958e09383 100644
--- a/Source/WebCore/platform/graphics/opengl/Extensions3DOpenGLES.cpp
+++ b/Source/WebCore/platform/graphics/opengl/Extensions3DOpenGLES.cpp
@@ -1,6 +1,7 @@
/*
* Copyright (C) 2011 Google Inc. All rights reserved.
* Copyright (C) 2012 Research In Motion Limited. All rights reserved.
+ * Copyright (C) 2014 Collabora Ltd. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -25,21 +26,23 @@
*/
#include "config.h"
-#if USE(3D_GRAPHICS)
+
+#if USE(OPENGL_ES_2)
#include "Extensions3DOpenGLES.h"
+#if ENABLE(GRAPHICS_CONTEXT_3D)
#include "GraphicsContext3D.h"
#include "NotImplemented.h"
#include <EGL/egl.h>
-#include <wtf/Vector.h>
namespace WebCore {
-Extensions3DOpenGLES::Extensions3DOpenGLES(GraphicsContext3D* context)
- : Extensions3DOpenGLCommon(context)
+Extensions3DOpenGLES::Extensions3DOpenGLES(GraphicsContext3D* context, bool useIndexedGetString)
+ : Extensions3DOpenGLCommon(context, useIndexedGetString)
, m_contextResetStatus(GL_NO_ERROR)
, m_supportsOESvertexArrayObject(false)
, m_supportsIMGMultisampledRenderToTexture(false)
+ , m_supportsANGLEinstancedArrays(false)
, m_glFramebufferTexture2DMultisampleIMG(0)
, m_glRenderbufferStorageMultisampleIMG(0)
, m_glBindVertexArrayOES(0)
@@ -50,6 +53,9 @@ Extensions3DOpenGLES::Extensions3DOpenGLES(GraphicsContext3D* context)
, m_glReadnPixelsEXT(0)
, m_glGetnUniformfvEXT(0)
, m_glGetnUniformivEXT(0)
+ , m_glVertexAttribDivisorANGLE(nullptr)
+ , m_glDrawArraysInstancedANGLE(nullptr)
+ , m_glDrawElementsInstancedANGLE(nullptr)
{
}
@@ -73,7 +79,7 @@ void Extensions3DOpenGLES::renderbufferStorageMultisampleIMG(unsigned long targe
m_context->synthesizeGLError(GL_INVALID_OPERATION);
}
-void Extensions3DOpenGLES::blitFramebuffer(long srcX0, long srcY0, long srcX1, long srcY1, long dstX0, long dstY0, long dstX1, long dstY1, unsigned long mask, unsigned long filter)
+void Extensions3DOpenGLES::blitFramebuffer(long /* srcX0 */, long /* srcY0 */, long /* srcX1 */, long /* srcY1 */, long /* dstX0 */, long /* dstY0 */, long /* dstX1 */, long /* dstY1 */, unsigned long /* mask */, unsigned long /* filter */)
{
notImplemented();
}
@@ -86,11 +92,6 @@ void Extensions3DOpenGLES::renderbufferStorageMultisample(unsigned long target,
notImplemented();
}
-void Extensions3DOpenGLES::copyTextureCHROMIUM(GC3Denum, Platform3DObject, Platform3DObject, GC3Dint, GC3Denum)
-{
- notImplemented();
-}
-
void Extensions3DOpenGLES::insertEventMarkerEXT(const String&)
{
notImplemented();
@@ -156,9 +157,10 @@ void Extensions3DOpenGLES::bindVertexArrayOES(Platform3DObject array)
m_context->synthesizeGLError(GL_INVALID_OPERATION);
}
-void Extensions3DOpenGLES::drawBuffersEXT(GC3Dsizei n, const GC3Denum* bufs)
+void Extensions3DOpenGLES::drawBuffersEXT(GC3Dsizei /* n */, const GC3Denum* /* bufs */)
{
// FIXME: implement the support.
+ notImplemented();
}
int Extensions3DOpenGLES::getGraphicsResetStatusARB()
@@ -184,9 +186,9 @@ int Extensions3DOpenGLES::getGraphicsResetStatusARB()
return false;
}
-void Extensions3DOpenGLES::setEXTContextLostCallback(PassOwnPtr<GraphicsContext3D::ContextLostCallback> callback)
+void Extensions3DOpenGLES::setEXTContextLostCallback(std::unique_ptr<GraphicsContext3D::ContextLostCallback> callback)
{
- m_contextLostCallback = callback;
+ m_contextLostCallback = WTFMove(callback);
}
void Extensions3DOpenGLES::readnPixelsEXT(int x, int y, GC3Dsizei width, GC3Dsizei height, GC3Denum format, GC3Denum type, GC3Dsizei bufSize, void *data)
@@ -228,6 +230,39 @@ void Extensions3DOpenGLES::getnUniformivEXT(GC3Duint program, int location, GC3D
m_context->synthesizeGLError(GL_INVALID_OPERATION);
}
+void Extensions3DOpenGLES::drawArraysInstanced(GC3Denum mode, GC3Dint first, GC3Dsizei count, GC3Dsizei primcount)
+{
+ if (!m_glDrawArraysInstancedANGLE) {
+ m_context->synthesizeGLError(GL_INVALID_OPERATION);
+ return;
+ }
+
+ m_context->makeContextCurrent();
+ m_glDrawArraysInstancedANGLE(mode, first, count, primcount);
+}
+
+void Extensions3DOpenGLES::drawElementsInstanced(GC3Denum mode, GC3Dsizei count, GC3Denum type, long long offset, GC3Dsizei primcount)
+{
+ if (!m_glDrawElementsInstancedANGLE) {
+ m_context->synthesizeGLError(GL_INVALID_OPERATION);
+ return;
+ }
+
+ m_context->makeContextCurrent();
+ m_glDrawElementsInstancedANGLE(mode, count, type, reinterpret_cast<GLvoid*>(static_cast<intptr_t>(offset)), primcount);
+}
+
+void Extensions3DOpenGLES::vertexAttribDivisor(GC3Duint index, GC3Duint divisor)
+{
+ if (!m_glVertexAttribDivisorANGLE) {
+ m_context->synthesizeGLError(GL_INVALID_OPERATION);
+ return;
+ }
+
+ m_context->makeContextCurrent();
+ m_glVertexAttribDivisorANGLE(index, divisor);
+}
+
bool Extensions3DOpenGLES::supportsExtension(const String& name)
{
if (m_availableExtensions.contains(name)) {
@@ -238,14 +273,19 @@ bool Extensions3DOpenGLES::supportsExtension(const String& name)
m_glIsVertexArrayOES = reinterpret_cast<PFNGLISVERTEXARRAYOESPROC>(eglGetProcAddress("glIsVertexArrayOES"));
m_supportsOESvertexArrayObject = true;
} else if (!m_supportsIMGMultisampledRenderToTexture && name == "GL_IMG_multisampled_render_to_texture") {
- m_glFramebufferTexture2DMultisampleIMG = reinterpret_cast<PFNGLFRAMEBUFFERTEXTURE2DMULTISAMPLEIMG>(eglGetProcAddress("glFramebufferTexture2DMultisampleIMG"));
- m_glRenderbufferStorageMultisampleIMG = reinterpret_cast<PFNGLRENDERBUFFERSTORAGEMULTISAMPLEIMG>(eglGetProcAddress("glRenderbufferStorageMultisampleIMG"));
+ m_glFramebufferTexture2DMultisampleIMG = reinterpret_cast<PFNGLFRAMEBUFFERTEXTURE2DMULTISAMPLEIMGPROC>(eglGetProcAddress("glFramebufferTexture2DMultisampleIMG"));
+ m_glRenderbufferStorageMultisampleIMG = reinterpret_cast<PFNGLRENDERBUFFERSTORAGEMULTISAMPLEIMGPROC>(eglGetProcAddress("glRenderbufferStorageMultisampleIMG"));
m_supportsIMGMultisampledRenderToTexture = true;
} else if (!m_glGetGraphicsResetStatusEXT && name == "GL_EXT_robustness") {
m_glGetGraphicsResetStatusEXT = reinterpret_cast<PFNGLGETGRAPHICSRESETSTATUSEXTPROC>(eglGetProcAddress("glGetGraphicsResetStatusEXT"));
m_glReadnPixelsEXT = reinterpret_cast<PFNGLREADNPIXELSEXTPROC>(eglGetProcAddress("glReadnPixelsEXT"));
m_glGetnUniformfvEXT = reinterpret_cast<PFNGLGETNUNIFORMFVEXTPROC>(eglGetProcAddress("glGetnUniformfvEXT"));
m_glGetnUniformivEXT = reinterpret_cast<PFNGLGETNUNIFORMIVEXTPROC>(eglGetProcAddress("glGetnUniformivEXT"));
+ } else if (!m_supportsANGLEinstancedArrays && name == "GL_ANGLE_instanced_arrays") {
+ m_glVertexAttribDivisorANGLE = reinterpret_cast<PFNGLVERTEXATTRIBDIVISORANGLEPROC>(eglGetProcAddress("glVertexAttribDivisorANGLE"));
+ m_glDrawArraysInstancedANGLE = reinterpret_cast<PFNGLDRAWARRAYSINSTANCEDANGLEPROC >(eglGetProcAddress("glDrawArraysInstancedANGLE"));
+ m_glDrawElementsInstancedANGLE = reinterpret_cast<PFNGLDRAWELEMENTSINSTANCEDANGLEPROC >(eglGetProcAddress("glDrawElementsInstancedANGLE"));
+ m_supportsANGLEinstancedArrays = true;
} else if (name == "GL_EXT_draw_buffers") {
// FIXME: implement the support.
return false;
@@ -263,4 +303,6 @@ String Extensions3DOpenGLES::getExtensions()
} // namespace WebCore
-#endif // USE(3D_GRAPHICS)
+#endif // ENABLE(GRAPHICS_CONTEXT_3D)
+
+#endif // USE(OPENGL_ES_2)
diff --git a/Source/WebCore/platform/graphics/opengl/Extensions3DOpenGLES.h b/Source/WebCore/platform/graphics/opengl/Extensions3DOpenGLES.h
index 7c40566e9..316e5e9b5 100644
--- a/Source/WebCore/platform/graphics/opengl/Extensions3DOpenGLES.h
+++ b/Source/WebCore/platform/graphics/opengl/Extensions3DOpenGLES.h
@@ -24,11 +24,12 @@
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef Extensions3DOpenGLES_h
-#define Extensions3DOpenGLES_h
+#pragma once
#include "Extensions3DOpenGLCommon.h"
+#if USE(OPENGL_ES_2)
+
#include <GLES2/gl2.h>
#include <GLES2/gl2ext.h>
@@ -61,6 +62,8 @@ namespace WebCore {
class Extensions3DOpenGLES : public Extensions3DOpenGLCommon {
public:
+ // This class only needs to be instantiated by GraphicsContext3D implementations.
+ Extensions3DOpenGLES(GraphicsContext3D*, bool useIndexedGetString);
virtual ~Extensions3DOpenGLES();
virtual void framebufferTexture2DMultisampleIMG(unsigned long target, unsigned long attachment, unsigned long textarget, unsigned int texture, int level, unsigned long samples);
@@ -69,7 +72,6 @@ public:
// Extension3D methods
virtual void blitFramebuffer(long srcX0, long srcY0, long srcX1, long srcY1, long dstX0, long dstY0, long dstX1, long dstY1, unsigned long mask, unsigned long filter);
virtual void renderbufferStorageMultisample(unsigned long target, unsigned long samples, unsigned long internalformat, unsigned long width, unsigned long height);
- virtual void copyTextureCHROMIUM(GC3Denum, Platform3DObject, Platform3DObject, GC3Dint, GC3Denum);
virtual void insertEventMarkerEXT(const String&);
virtual void pushGroupMarkerEXT(const String&);
virtual void popGroupMarkerEXT(void);
@@ -80,9 +82,13 @@ public:
virtual void bindVertexArrayOES(Platform3DObject);
virtual void drawBuffersEXT(GC3Dsizei, const GC3Denum*);
+ virtual void drawArraysInstanced(GC3Denum mode, GC3Dint first, GC3Dsizei count, GC3Dsizei primcount);
+ virtual void drawElementsInstanced(GC3Denum mode, GC3Dsizei count, GC3Denum type, long long offset, GC3Dsizei primcount);
+ virtual void vertexAttribDivisor(GC3Duint index, GC3Duint divisor);
+
// EXT Robustness - reset
virtual int getGraphicsResetStatusARB();
- void setEXTContextLostCallback(PassOwnPtr<GraphicsContext3D::ContextLostCallback>);
+ void setEXTContextLostCallback(std::unique_ptr<GraphicsContext3D::ContextLostCallback>);
// EXT Robustness - etc
virtual void readnPixelsEXT(int x, int y, GC3Dsizei width, GC3Dsizei height, GC3Denum format, GC3Denum type, GC3Dsizei bufSize, void *data);
@@ -90,10 +96,6 @@ public:
virtual void getnUniformivEXT(GC3Duint program, int location, GC3Dsizei bufSize, int *params);
protected:
- // This class only needs to be instantiated by GraphicsContext3D implementations.
- friend class GraphicsContext3D;
- Extensions3DOpenGLES(GraphicsContext3D*);
-
virtual bool supportsExtension(const String&);
virtual String getExtensions();
@@ -101,9 +103,10 @@ protected:
bool m_supportsOESvertexArrayObject;
bool m_supportsIMGMultisampledRenderToTexture;
+ bool m_supportsANGLEinstancedArrays;
- PFNGLFRAMEBUFFERTEXTURE2DMULTISAMPLEIMG m_glFramebufferTexture2DMultisampleIMG;
- PFNGLRENDERBUFFERSTORAGEMULTISAMPLEIMG m_glRenderbufferStorageMultisampleIMG;
+ PFNGLFRAMEBUFFERTEXTURE2DMULTISAMPLEIMGPROC m_glFramebufferTexture2DMultisampleIMG;
+ PFNGLRENDERBUFFERSTORAGEMULTISAMPLEIMGPROC m_glRenderbufferStorageMultisampleIMG;
PFNGLBINDVERTEXARRAYOESPROC m_glBindVertexArrayOES;
PFNGLDELETEVERTEXARRAYSOESPROC m_glDeleteVertexArraysOES;
PFNGLGENVERTEXARRAYSOESPROC m_glGenVertexArraysOES;
@@ -112,10 +115,13 @@ protected:
PFNGLREADNPIXELSEXTPROC m_glReadnPixelsEXT;
PFNGLGETNUNIFORMFVEXTPROC m_glGetnUniformfvEXT;
PFNGLGETNUNIFORMIVEXTPROC m_glGetnUniformivEXT;
+ PFNGLVERTEXATTRIBDIVISORANGLEPROC m_glVertexAttribDivisorANGLE;
+ PFNGLDRAWARRAYSINSTANCEDANGLEPROC m_glDrawArraysInstancedANGLE;
+ PFNGLDRAWELEMENTSINSTANCEDANGLEPROC m_glDrawElementsInstancedANGLE;
- OwnPtr<GraphicsContext3D::ContextLostCallback> m_contextLostCallback;
+ std::unique_ptr<GraphicsContext3D::ContextLostCallback> m_contextLostCallback;
};
} // namespace WebCore
-#endif // Extensions3DOpenGLES_h
+#endif // USE(OPENGL_ES_2)
diff --git a/Source/WebCore/platform/graphics/opengl/GLDefs.h b/Source/WebCore/platform/graphics/opengl/GLDefs.h
new file mode 100644
index 000000000..9a2c2fffd
--- /dev/null
+++ b/Source/WebCore/platform/graphics/opengl/GLDefs.h
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2012 Intel Corporation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef GLDefs_h
+#define GLDefs_h
+
+#define GL_GLEXT_PROTOTYPES 1
+
+#if USE(OPENGL_ES_2)
+#include "Extensions3DOpenGLES.h"
+#include "OpenGLESShims.h"
+#include <GLES2/gl2.h>
+#include <GLES2/gl2ext.h>
+#else
+#include "Extensions3DOpenGL.h"
+#include "OpenGLShims.h"
+#include <GL/gl.h>
+#include <GL/glext.h>
+#if USE(GLX)
+#define GLX_GLXEXT_PROTOTYPES 1
+#include <GL/glx.h>
+#include <GL/glxext.h>
+#endif
+#endif
+
+#if USE(EGL)
+#define EGL_EGLEXT_PROTOTYPES 1
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
+#endif
+
+namespace WebCore {
+
+typedef uint32_t PlatformBufferHandle;
+
+#if USE(GLX)
+typedef GLXContext PlatformContext;
+typedef GLXFBConfig PlatformSurfaceConfig;
+typedef GLXDrawable PlatformDrawable;
+#elif USE(EGL)
+typedef EGLContext PlatformContext;
+typedef EGLConfig PlatformSurfaceConfig;
+typedef EGLSurface PlatformDrawable;
+#else
+typedef void* PlatformContext;
+typedef void* PlatformSurfaceConfig;
+typedef void* PlatformDrawable;
+#endif
+
+}
+
+#endif
diff --git a/Source/WebCore/platform/graphics/opengl/GLPlatformContext.cpp b/Source/WebCore/platform/graphics/opengl/GLPlatformContext.cpp
new file mode 100644
index 000000000..5219a2391
--- /dev/null
+++ b/Source/WebCore/platform/graphics/opengl/GLPlatformContext.cpp
@@ -0,0 +1,273 @@
+/*
+ * Copyright (C) 2012 Intel Corporation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#if ENABLE(GRAPHICS_CONTEXT_3D)
+#include "GLPlatformContext.h"
+
+#if USE(GLX)
+#include "GLXContext.h"
+#elif USE(EGL)
+#include "EGLContext.h"
+#endif
+
+#include "NotImplemented.h"
+
+namespace WebCore {
+
+#if USE(OPENGL_ES_2)
+static PFNGLGETGRAPHICSRESETSTATUSEXTPROC glGetGraphicsResetStatus = 0;
+#else
+static PFNGLGETGRAPHICSRESETSTATUSARBPROC glGetGraphicsResetStatus = 0;
+#endif
+
+class GLCurrentContextWrapper : public GLPlatformContext {
+
+public:
+ GLCurrentContextWrapper()
+ : GLPlatformContext()
+ {
+#if USE(GLX)
+ m_contextHandle = glXGetCurrentContext();
+#elif USE(EGL)
+ m_contextHandle = eglGetCurrentContext();
+#endif
+ }
+
+ virtual ~GLCurrentContextWrapper() { }
+
+ bool isCurrentContext() const override
+ {
+ return true;
+ }
+};
+
+static std::unique_ptr<GLPlatformContext> createOffScreenContext()
+{
+#if USE(GLX)
+ return std::make_unique<GLXOffScreenContext>();
+#elif USE(EGL)
+ return std::make_unique<EGLOffScreenContext>();
+#else
+ return nullptr;
+#endif
+}
+
+static HashSet<String> parseExtensions(const String& extensionsString)
+{
+ Vector<String> extNames;
+ extensionsString.split(' ', extNames);
+ HashSet<String> splitExtNames;
+ unsigned size = extNames.size();
+ for (unsigned i = 0; i < size; ++i)
+ splitExtNames.add(extNames[i]);
+ extNames.clear();
+
+ return splitExtNames;
+}
+
+static void resolveResetStatusExtension()
+{
+ static bool resolvedRobustnessExtension = false;
+ if (!resolvedRobustnessExtension) {
+ resolvedRobustnessExtension = true;
+#if USE(OPENGL_ES_2)
+ glGetGraphicsResetStatus = reinterpret_cast<PFNGLGETGRAPHICSRESETSTATUSEXTPROC>(eglGetProcAddress("glGetGraphicsResetStatusEXT"));
+#elif USE(EGL)
+ glGetGraphicsResetStatus = reinterpret_cast<PFNGLGETGRAPHICSRESETSTATUSARBPROC>(eglGetProcAddress("glGetGraphicsResetStatusARB"));
+#elif USE(GLX)
+ glGetGraphicsResetStatus = reinterpret_cast<PFNGLGETGRAPHICSRESETSTATUSARBPROC>(glXGetProcAddressARB(reinterpret_cast<const GLubyte*>("glGetGraphicsResetStatusARB")));
+#endif
+ }
+}
+
+std::unique_ptr<GLPlatformContext> GLPlatformContext::createContext(GraphicsContext3D::RenderStyle renderStyle)
+{
+#if !USE(OPENGL_ES_2)
+ if (!initializeOpenGLShims())
+ return nullptr;
+#endif
+
+ switch (renderStyle) {
+ case GraphicsContext3D::RenderOffscreen:
+ return createOffScreenContext();
+ case GraphicsContext3D::RenderToCurrentGLContext:
+ return std::make_unique<GLCurrentContextWrapper>();
+ case GraphicsContext3D::RenderDirectlyToHostWindow:
+ ASSERT_NOT_REACHED();
+ break;
+ }
+
+ return nullptr;
+}
+
+bool GLPlatformContext::supportsGLExtension(const String& name)
+{
+ static HashSet<String> supportedExtensions;
+
+ if (!supportedExtensions.size()) {
+ String rawExtensions = reinterpret_cast<const char*>(::glGetString(GL_EXTENSIONS));
+ supportedExtensions = parseExtensions(rawExtensions);
+ }
+
+ if (supportedExtensions.contains(name))
+ return true;
+
+ return false;
+}
+
+#if USE(EGL)
+bool GLPlatformContext::supportsEGLExtension(EGLDisplay display, const String& name)
+{
+ static HashSet<String> supportedExtensions;
+
+ if (!supportedExtensions.size()) {
+ if (display == EGL_NO_DISPLAY)
+ return false;
+
+ String rawExtensions = reinterpret_cast<const char*>(eglQueryString(display, EGL_EXTENSIONS));
+ supportedExtensions = parseExtensions(rawExtensions);
+ }
+
+ if (supportedExtensions.contains(name))
+ return true;
+
+ return false;
+}
+#endif
+
+#if USE(GLX)
+bool GLPlatformContext::supportsGLXExtension(Display* display, const String& name)
+{
+ static HashSet<String> supportedExtensions;
+
+ if (!supportedExtensions.size()) {
+ if (!display)
+ return false;
+
+ String rawExtensions = glXQueryExtensionsString(display, DefaultScreen(display));
+ supportedExtensions = parseExtensions(rawExtensions);
+ }
+
+ if (supportedExtensions.contains(name))
+ return true;
+
+ return false;
+}
+#endif
+
+GLPlatformContext::GLPlatformContext()
+ : m_contextHandle(0)
+ , m_resetLostContext(false)
+{
+}
+
+GLPlatformContext::~GLPlatformContext()
+{
+}
+
+bool GLPlatformContext::makeCurrent(GLPlatformSurface* surface)
+{
+ m_contextLost = false;
+
+ if (isCurrentContext() && (!surface || surface->isCurrentDrawable()))
+ return true;
+
+ GLPlatformContext* currentContext = 0;
+
+ if (!surface || (surface && !surface->drawable()))
+ platformReleaseCurrent();
+ else if (platformMakeCurrent(surface)) {
+ currentContext = this;
+ surface->onMakeCurrent();
+ }
+
+ if (m_resetLostContext) {
+ resolveResetStatusExtension();
+
+ if (glGetGraphicsResetStatus) {
+ GLenum status = glGetGraphicsResetStatus();
+
+ switch (status) {
+ case PLATFORMCONTEXT_NO_ERROR:
+ break;
+ case PLATFORMCONTEXT_GUILTY_CONTEXT_RESET:
+ m_contextLost = true;
+ break;
+ case PLATFORMCONTEXT_INNOCENT_CONTEXT_RESET:
+ break;
+ case PLATFORMCONTEXT_UNKNOWN_CONTEXT_RESET:
+ m_contextLost = true;
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+ return currentContext;
+}
+
+bool GLPlatformContext::isValid() const
+{
+ return !m_contextLost;
+}
+
+void GLPlatformContext::releaseCurrent()
+{
+ if (isCurrentContext())
+ platformReleaseCurrent();
+}
+
+PlatformContext GLPlatformContext::handle() const
+{
+ return m_contextHandle;
+}
+
+bool GLPlatformContext::initialize(GLPlatformSurface*, PlatformContext)
+{
+ return true;
+}
+
+bool GLPlatformContext::platformMakeCurrent(GLPlatformSurface*)
+{
+ return true;
+}
+
+void GLPlatformContext::platformReleaseCurrent()
+{
+ notImplemented();
+}
+
+void GLPlatformContext::destroy()
+{
+ m_contextHandle = 0;
+ m_resetLostContext = false;
+}
+
+} // namespace WebCore
+
+#endif
diff --git a/Source/WebCore/platform/graphics/opengl/GLPlatformContext.h b/Source/WebCore/platform/graphics/opengl/GLPlatformContext.h
new file mode 100644
index 000000000..a999cc0cf
--- /dev/null
+++ b/Source/WebCore/platform/graphics/opengl/GLPlatformContext.h
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2012 Intel Corporation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef GLPlatformContext_h
+#define GLPlatformContext_h
+
+#include "GLDefs.h"
+#include "GLPlatformSurface.h"
+#include "GraphicsContext3D.h"
+#include <wtf/Noncopyable.h>
+
+// Encapsulates an OpenGL context, hiding platform specific management.
+namespace WebCore {
+
+class GLPlatformContext {
+ WTF_MAKE_NONCOPYABLE(GLPlatformContext);
+
+public:
+ // From http://www.khronos.org/registry/gles/extensions/EXT/EXT_robustness.txt
+ enum PlatformContextReset {
+ PLATFORMCONTEXT_NO_ERROR = 0x0000,
+ PLATFORMCONTEXT_GUILTY_CONTEXT_RESET = 0x8253,
+ PLATFORMCONTEXT_INNOCENT_CONTEXT_RESET = 0x8254,
+ PLATFORMCONTEXT_UNKNOWN_CONTEXT_RESET = 0x8255,
+ };
+
+ static std::unique_ptr<GLPlatformContext> createContext(GraphicsContext3D::RenderStyle);
+
+ static bool supportsGLExtension(const String&);
+
+#if USE(EGL)
+ static bool supportsEGLExtension(EGLDisplay, const String&);
+#endif
+
+#if USE(GLX)
+ static bool supportsGLXExtension(Display*, const String&);
+#endif
+
+ virtual ~GLPlatformContext();
+
+ virtual bool initialize(GLPlatformSurface*, PlatformContext = 0);
+
+ // Makes this and surface as current context and drawable.
+ // Calling this function with no surface is same as calling releaseCurrent.
+ // Does nothing if this is already current Context.
+ bool makeCurrent(GLPlatformSurface* = 0);
+
+ // Sets Current Context and Drawable as Null.
+ // Doesn't have any effect if this is not the current Context.
+ void releaseCurrent();
+
+ virtual PlatformContext handle() const;
+
+ virtual bool isCurrentContext() const = 0;
+
+ bool isValid() const;
+
+ // Destroys any GL resources associated with this context.
+ virtual void destroy();
+
+protected:
+ GLPlatformContext();
+ virtual bool platformMakeCurrent(GLPlatformSurface*);
+ virtual void platformReleaseCurrent();
+ PlatformContext m_contextHandle;
+ bool m_resetLostContext;
+ bool m_contextLost;
+};
+
+} // namespace WebCore
+
+#endif // GLNativeContext_H
diff --git a/Source/WebCore/platform/graphics/opengl/GLPlatformSurface.h b/Source/WebCore/platform/graphics/opengl/GLPlatformSurface.h
new file mode 100644
index 000000000..2d99d6f2d
--- /dev/null
+++ b/Source/WebCore/platform/graphics/opengl/GLPlatformSurface.h
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2012 Intel Corporation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef GLPlatformSurface_h
+#define GLPlatformSurface_h
+
+#include "GLDefs.h"
+#include "IntRect.h"
+#include <wtf/Noncopyable.h>
+
+// Encapsulates a surface that can be rendered to with GL, hiding platform
+// specific management.
+namespace WebCore {
+
+class GLPlatformSurface {
+ WTF_MAKE_NONCOPYABLE(GLPlatformSurface);
+
+public:
+ enum Attributes {
+ Default = 0x00, // No Alpha channel. Only R,G,B values set.
+ SupportAlpha = 0x01,
+ DoubleBuffered = 0x02
+ };
+
+ typedef unsigned SurfaceAttributes;
+ // Creates a GL surface used for offscreen rendering.
+ static std::unique_ptr<GLPlatformSurface> createOffScreenSurface(SurfaceAttributes = GLPlatformSurface::Default);
+
+ virtual ~GLPlatformSurface();
+
+ const IntRect& geometry() const;
+
+ // Get the underlying platform specific buffer handle.
+ // The handle will be null if surface doesn't support
+ // buffer sharing.
+ PlatformBufferHandle handle() const;
+
+ PlatformDrawable drawable() const;
+
+ virtual SurfaceAttributes attributes() const;
+
+ virtual void swapBuffers();
+
+ virtual bool isCurrentDrawable() const = 0;
+
+ virtual void onMakeCurrent();
+
+ // Convenience Function to update surface backbuffer with texture contents.
+ // Note that the function doesn't track or restore any GL states.
+ // Function does the following(in order):
+ // a) Blits texture contents to back buffer.
+ // b) Calls Swap Buffers.
+ virtual void updateContents(const uint32_t);
+
+ virtual void setGeometry(const IntRect&);
+
+ virtual PlatformSurfaceConfig configuration();
+
+ virtual void destroy();
+
+protected:
+ GLPlatformSurface(SurfaceAttributes);
+
+ PlatformDrawable m_drawable;
+ PlatformBufferHandle m_bufferHandle;
+ IntRect m_rect;
+};
+
+}
+
+#endif
diff --git a/Source/WebCore/platform/graphics/opengl/GraphicsContext3DOpenGL.cpp b/Source/WebCore/platform/graphics/opengl/GraphicsContext3DOpenGL.cpp
index 02e1b9ec8..25c9a221c 100644
--- a/Source/WebCore/platform/graphics/opengl/GraphicsContext3DOpenGL.cpp
+++ b/Source/WebCore/platform/graphics/opengl/GraphicsContext3DOpenGL.cpp
@@ -11,10 +11,10 @@
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
- * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
@@ -26,7 +26,7 @@
#include "config.h"
-#if USE(3D_GRAPHICS)
+#if ENABLE(GRAPHICS_CONTEXT_3D)
#include "GraphicsContext3D.h"
#if PLATFORM(IOS)
@@ -44,14 +44,21 @@
#include <wtf/MainThread.h>
#include <wtf/text/CString.h>
+#if USE(ACCELERATE)
+#include <Accelerate/Accelerate.h>
+#endif
+
#if PLATFORM(IOS)
#import <OpenGLES/ES2/glext.h>
// From <OpenGLES/glext.h>
#define GL_RGBA32F_ARB 0x8814
#define GL_RGB32F_ARB 0x8815
#elif PLATFORM(MAC)
+#define GL_DO_NOT_WARN_IF_MULTI_GL_VERSION_HEADERS_INCLUDED
#include <OpenGL/gl.h>
-#elif PLATFORM(GTK) || PLATFORM(EFL) || PLATFORM(WIN)
+#include <OpenGL/gl3.h>
+#undef GL_DO_NOT_WARN_IF_MULTI_GL_VERSION_HEADERS_INCLUDED
+#elif PLATFORM(GTK) || PLATFORM(WIN)
#include "OpenGLShims.h"
#endif
@@ -65,7 +72,32 @@ void GraphicsContext3D::releaseShaderCompiler()
void GraphicsContext3D::readPixelsAndConvertToBGRAIfNecessary(int x, int y, int width, int height, unsigned char* pixels)
{
- ::glReadPixels(x, y, width, height, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, pixels);
+ // NVIDIA drivers have a bug where calling readPixels in BGRA can return the wrong values for the alpha channel when the alpha is off for the context.
+ if (!m_attrs.alpha && getExtensions().isNVIDIA()) {
+ ::glReadPixels(x, y, width, height, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
+#if USE(ACCELERATE)
+ vImage_Buffer src;
+ src.height = height;
+ src.width = width;
+ src.rowBytes = width * 4;
+ src.data = pixels;
+
+ vImage_Buffer dest;
+ dest.height = height;
+ dest.width = width;
+ dest.rowBytes = width * 4;
+ dest.data = pixels;
+
+ // Swap pixel channels from RGBA to BGRA.
+ const uint8_t map[4] = { 2, 1, 0, 3 };
+ vImagePermuteChannels_ARGB8888(&src, &dest, map, kvImageNoFlags);
+#else
+ int totalBytes = width * height * 4;
+ for (int i = 0; i < totalBytes; i += 4)
+ std::swap(pixels[i], pixels[i + 2]);
+#endif
+ } else
+ ::glReadPixels(x, y, width, height, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, pixels);
}
void GraphicsContext3D::validateAttributes()
@@ -89,9 +121,9 @@ bool GraphicsContext3D::reshapeFBOs(const IntSize& size)
// We don't allow the logic where stencil is required and depth is not.
// See GraphicsContext3D::validateAttributes.
- Extensions3D* extensions = getExtensions();
+ Extensions3D& extensions = getExtensions();
// Use a 24 bit depth buffer where we know we have it.
- if (extensions->supports("GL_EXT_packed_depth_stencil"))
+ if (extensions.supports("GL_EXT_packed_depth_stencil"))
internalDepthStencilFormat = GL_DEPTH24_STENCIL8_EXT;
else
#if PLATFORM(IOS)
@@ -110,7 +142,11 @@ bool GraphicsContext3D::reshapeFBOs(const IntSize& size)
sampleCount = maxSampleCount;
::glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, m_multisampleFBO);
::glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, m_multisampleColorBuffer);
+#if PLATFORM(IOS)
+ ::glRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER_EXT, sampleCount, GL_RGBA8_OES, width, height);
+#else
::glRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER_EXT, sampleCount, m_internalColorFormat, width, height);
+#endif
::glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_RENDERBUFFER_EXT, m_multisampleColorBuffer);
if (m_attrs.stencil || m_attrs.depth) {
::glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, m_multisampleDepthStencilBuffer);
@@ -143,9 +179,31 @@ bool GraphicsContext3D::reshapeFBOs(const IntSize& size)
::glBindTexture(GL_TEXTURE_2D, m_compositorTexture);
::glTexImage2D(GL_TEXTURE_2D, 0, m_internalColorFormat, width, height, 0, colorFormat, GL_UNSIGNED_BYTE, 0);
::glBindTexture(GL_TEXTURE_2D, 0);
+#if USE(COORDINATED_GRAPHICS_THREADED)
+ ::glBindTexture(GL_TEXTURE_2D, m_intermediateTexture);
+ ::glTexImage2D(GL_TEXTURE_2D, 0, m_internalColorFormat, width, height, 0, colorFormat, GL_UNSIGNED_BYTE, 0);
+ ::glBindTexture(GL_TEXTURE_2D, 0);
+#endif
}
#endif
+ attachDepthAndStencilBufferIfNeeded(internalDepthStencilFormat, width, height);
+
+ bool mustRestoreFBO = true;
+ if (m_attrs.antialias) {
+ ::glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, m_multisampleFBO);
+ if (m_state.boundFBO == m_multisampleFBO)
+ mustRestoreFBO = false;
+ } else {
+ if (m_state.boundFBO == m_fbo)
+ mustRestoreFBO = false;
+ }
+
+ return mustRestoreFBO;
+}
+
+void GraphicsContext3D::attachDepthAndStencilBufferIfNeeded(GLuint internalDepthStencilFormat, int width, int height)
+{
if (!m_attrs.antialias && (m_attrs.stencil || m_attrs.depth)) {
::glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, m_depthStencilBuffer);
::glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, internalDepthStencilFormat, width, height);
@@ -160,18 +218,6 @@ bool GraphicsContext3D::reshapeFBOs(const IntSize& size)
// FIXME: cleanup
notImplemented();
}
-
- bool mustRestoreFBO = true;
- if (m_attrs.antialias) {
- ::glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, m_multisampleFBO);
- if (m_state.boundFBO == m_multisampleFBO)
- mustRestoreFBO = false;
- } else {
- if (m_state.boundFBO == m_fbo)
- mustRestoreFBO = false;
- }
-
- return mustRestoreFBO;
}
void GraphicsContext3D::resolveMultisamplingIfNecessary(const IntRect& rect)
@@ -181,11 +227,20 @@ void GraphicsContext3D::resolveMultisamplingIfNecessary(const IntRect& rect)
TemporaryOpenGLSetting scopedDepth(GL_DEPTH_TEST, GL_FALSE);
TemporaryOpenGLSetting scopedStencil(GL_STENCIL_TEST, GL_FALSE);
+#if PLATFORM(IOS)
+ GLint boundFrameBuffer;
+ ::glGetIntegerv(GL_FRAMEBUFFER_BINDING, &boundFrameBuffer);
+#endif
+
::glBindFramebufferEXT(GL_READ_FRAMEBUFFER_EXT, m_multisampleFBO);
::glBindFramebufferEXT(GL_DRAW_FRAMEBUFFER_EXT, m_fbo);
#if PLATFORM(IOS)
UNUSED_PARAM(rect);
+ ::glFlush();
::glResolveMultisampleFramebufferAPPLE();
+ const GLenum discards[] = { GL_COLOR_ATTACHMENT0, GL_DEPTH_ATTACHMENT };
+ ::glDiscardFramebufferEXT(GL_READ_FRAMEBUFFER_APPLE, 2, discards);
+ ::glBindFramebuffer(GL_FRAMEBUFFER, boundFrameBuffer);
#else
IntRect resolveRect = rect;
if (rect.isEmpty())
@@ -236,10 +291,29 @@ void GraphicsContext3D::getIntegerv(GC3Denum pname, GC3Dint* value)
*value /= 4;
break;
case MAX_VARYING_VECTORS:
- ::glGetIntegerv(GL_MAX_VARYING_FLOATS, value);
- *value /= 4;
+ if (isGLES2Compliant()) {
+ ASSERT(::glGetError() == GL_NO_ERROR);
+ ::glGetIntegerv(GL_MAX_VARYING_VECTORS, value);
+ if (::glGetError() == GL_INVALID_ENUM) {
+ ::glGetIntegerv(GL_MAX_VARYING_COMPONENTS, value);
+ *value /= 4;
+ }
+ } else {
+ ::glGetIntegerv(GL_MAX_VARYING_FLOATS, value);
+ *value /= 4;
+ }
break;
#endif
+ case MAX_TEXTURE_SIZE:
+ ::glGetIntegerv(MAX_TEXTURE_SIZE, value);
+ if (getExtensions().requiresRestrictedMaximumTextureSize())
+ *value = std::min(4096, *value);
+ break;
+ case MAX_CUBE_MAP_TEXTURE_SIZE:
+ ::glGetIntegerv(MAX_CUBE_MAP_TEXTURE_SIZE, value);
+ if (getExtensions().requiresRestrictedMaximumTextureSize())
+ *value = std::min(1024, *value);
+ break;
default:
::glGetIntegerv(pname, value);
}
@@ -283,14 +357,15 @@ bool GraphicsContext3D::texImage2D(GC3Denum target, GC3Dint level, GC3Denum inte
return false;
}
+ GC3Denum openGLFormat = format;
GC3Denum openGLInternalFormat = internalformat;
+#if !PLATFORM(IOS)
if (type == GL_FLOAT) {
if (format == GL_RGBA)
openGLInternalFormat = GL_RGBA32F_ARB;
else if (format == GL_RGB)
openGLInternalFormat = GL_RGB32F_ARB;
} else if (type == HALF_FLOAT_OES) {
-#if !PLATFORM(IOS)
if (format == GL_RGBA)
openGLInternalFormat = GL_RGBA16F_ARB;
else if (format == GL_RGB)
@@ -302,9 +377,25 @@ bool GraphicsContext3D::texImage2D(GC3Denum target, GC3Dint level, GC3Denum inte
else if (format == GL_LUMINANCE_ALPHA)
openGLInternalFormat = GL_LUMINANCE_ALPHA16F_ARB;
type = GL_HALF_FLOAT_ARB;
+ }
+
+ ASSERT(format != Extensions3D::SRGB8_ALPHA8_EXT);
+ if (format == Extensions3D::SRGB_ALPHA_EXT)
+ openGLFormat = GL_RGBA;
+ else if (format == Extensions3D::SRGB_EXT)
+ openGLFormat = GL_RGB;
#endif
+
+ if (m_usingCoreProfile && openGLInternalFormat == ALPHA) {
+ // We are using a core profile. This means that GL_ALPHA, which is a valid format in WebGL for texImage2D
+ // is not supported in OpenGL. It needs to be backed with a GL_RED plane. We change the formats to GL_RED
+ // (both need to be GL_ALPHA in WebGL) and instruct the texture to swizzle the red component values with
+ // the the alpha component values.
+ openGLInternalFormat = openGLFormat = RED;
+ texParameteri(target, TEXTURE_SWIZZLE_A, RED);
}
- texImage2DDirect(target, level, openGLInternalFormat, width, height, border, format, type, pixels);
+
+ texImage2DDirect(target, level, openGLInternalFormat, width, height, border, openGLFormat, type, pixels);
return true;
}
@@ -328,12 +419,14 @@ void GraphicsContext3D::clearDepth(GC3Dclampf depth)
#endif
}
-Extensions3D* GraphicsContext3D::getExtensions()
+#if !PLATFORM(GTK)
+Extensions3D& GraphicsContext3D::getExtensions()
{
if (!m_extensions)
- m_extensions = adoptPtr(new Extensions3DOpenGL(this));
- return m_extensions.get();
+ m_extensions = std::make_unique<Extensions3DOpenGL>(this, isGLES2Compliant());
+ return *m_extensions;
}
+#endif
void GraphicsContext3D::readPixels(GC3Dint x, GC3Dint y, GC3Dsizei width, GC3Dsizei height, GC3Denum format, GC3Denum type, void* data)
{
@@ -351,31 +444,6 @@ void GraphicsContext3D::readPixels(GC3Dint x, GC3Dint y, GC3Dsizei width, GC3Dsi
::glBindFramebufferEXT(GraphicsContext3D::FRAMEBUFFER, m_multisampleFBO);
}
-#if !PLATFORM(MAC)
-void GraphicsContext3D::drawArraysInstanced(GC3Denum mode, GC3Dint first, GC3Dsizei count, GC3Dsizei primcount)
-{
- UNUSED_PARAM(mode);
- UNUSED_PARAM(first);
- UNUSED_PARAM(count);
- UNUSED_PARAM(primcount);
-}
-
-void GraphicsContext3D::drawElementsInstanced(GC3Denum mode, GC3Dsizei count, GC3Denum type, GC3Dintptr offset, GC3Dsizei primcount)
-{
- UNUSED_PARAM(mode);
- UNUSED_PARAM(count);
- UNUSED_PARAM(type);
- UNUSED_PARAM(offset);
- UNUSED_PARAM(primcount);
-}
-
-void GraphicsContext3D::vertexAttribDivisor(GC3Duint index, GC3Duint divisor)
-{
- UNUSED_PARAM(index);
- UNUSED_PARAM(divisor);
-}
-#endif
-
}
-#endif // USE(3D_GRAPHICS)
+#endif // ENABLE(GRAPHICS_CONTEXT_3D)
diff --git a/Source/WebCore/platform/graphics/opengl/GraphicsContext3DOpenGLCommon.cpp b/Source/WebCore/platform/graphics/opengl/GraphicsContext3DOpenGLCommon.cpp
index c1356a0e9..1536faf42 100644
--- a/Source/WebCore/platform/graphics/opengl/GraphicsContext3DOpenGLCommon.cpp
+++ b/Source/WebCore/platform/graphics/opengl/GraphicsContext3DOpenGLCommon.cpp
@@ -13,10 +13,10 @@
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
- * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
@@ -28,7 +28,7 @@
#include "config.h"
-#if USE(3D_GRAPHICS)
+#if ENABLE(GRAPHICS_CONTEXT_3D)
#include "GraphicsContext3D.h"
#if PLATFORM(IOS)
@@ -40,26 +40,26 @@
#else
#include "Extensions3DOpenGL.h"
#endif
+#include "ANGLEWebKitBridge.h"
#include "GraphicsContext.h"
#include "ImageBuffer.h"
#include "ImageData.h"
#include "IntRect.h"
#include "IntSize.h"
#include "Logging.h"
-#include "NotImplemented.h"
#include "TemporaryOpenGLSetting.h"
+#include "WebGLRenderingContextBase.h"
#include <cstring>
-#include <runtime/ArrayBuffer.h>
-#include <runtime/ArrayBufferView.h>
-#include <runtime/Float32Array.h>
-#include <runtime/Int32Array.h>
-#include <runtime/Uint8Array.h>
+#include <wtf/HexNumber.h>
#include <wtf/MainThread.h>
+#include <wtf/ThreadSpecific.h>
#include <wtf/text/CString.h>
+#include <wtf/text/StringBuilder.h>
#include <yarr/RegularExpression.h>
#if PLATFORM(IOS)
#import <OpenGLES/ES2/glext.h>
+#import <OpenGLES/ES3/gl.h>
// From <OpenGLES/glext.h>
#define GL_RGBA32F_ARB 0x8814
#define GL_RGB32F_ARB 0x8815
@@ -67,15 +67,35 @@
#if USE(OPENGL_ES_2)
#include "OpenGLESShims.h"
#elif PLATFORM(MAC)
+#define GL_DO_NOT_WARN_IF_MULTI_GL_VERSION_HEADERS_INCLUDED
#include <OpenGL/gl.h>
-#elif PLATFORM(GTK) || PLATFORM(EFL) || PLATFORM(WIN)
+#include <OpenGL/gl3.h>
+#include <OpenGL/gl3ext.h>
+#undef GL_DO_NOT_WARN_IF_MULTI_GL_VERSION_HEADERS_INCLUDED
+#elif PLATFORM(GTK) || PLATFORM(WIN)
#include "OpenGLShims.h"
#endif
#endif
+using namespace WTF;
+
namespace WebCore {
-static ShaderNameHash* currentNameHashMapForShader;
+static ThreadSpecific<ShaderNameHash*>& getCurrentNameHashMapForShader()
+{
+ static std::once_flag onceFlag;
+ static ThreadSpecific<ShaderNameHash*>* sharedNameHash;
+ std::call_once(onceFlag, [] {
+ sharedNameHash = new ThreadSpecific<ShaderNameHash*>;
+ });
+
+ return *sharedNameHash;
+}
+
+static void setCurrentNameHashMapForShader(ShaderNameHash* shaderNameHash)
+{
+ *getCurrentNameHashMapForShader() = shaderNameHash;
+}
// Hash function used by the ANGLE translator/compiler to do
// symbol name mangling. Since this is a static method, before
@@ -90,11 +110,10 @@ static uint64_t nameHashForShader(const char* name, size_t length)
CString nameAsCString = CString(name);
// Look up name in our local map.
- if (currentNameHashMapForShader) {
- ShaderNameHash::iterator result = currentNameHashMapForShader->find(nameAsCString);
- if (result != currentNameHashMapForShader->end())
- return result->value;
- }
+ ShaderNameHash*& currentNameHashMapForShader = *getCurrentNameHashMapForShader();
+ ShaderNameHash::iterator findResult = currentNameHashMapForShader->find(nameAsCString);
+ if (findResult != currentNameHashMapForShader->end())
+ return findResult->value;
unsigned hashValue = nameAsCString.hash();
@@ -111,37 +130,39 @@ static uint64_t nameHashForShader(const char* name, size_t length)
return result;
}
-PassRefPtr<GraphicsContext3D> GraphicsContext3D::createForCurrentGLContext()
+RefPtr<GraphicsContext3D> GraphicsContext3D::createForCurrentGLContext()
{
- RefPtr<GraphicsContext3D> context = adoptRef(new GraphicsContext3D(Attributes(), 0, GraphicsContext3D::RenderToCurrentGLContext));
- return context->m_private ? context.release() : 0;
+ auto context = adoptRef(*new GraphicsContext3D({ }, 0, GraphicsContext3D::RenderToCurrentGLContext));
+#if USE(TEXTURE_MAPPER)
+ if (!context->m_texmapLayer)
+ return nullptr;
+#else
+ if (!context->m_private)
+ return nullptr;
+#endif
+ return WTFMove(context);
}
void GraphicsContext3D::validateDepthStencil(const char* packedDepthStencilExtension)
{
- Extensions3D* extensions = getExtensions();
+ Extensions3D& extensions = getExtensions();
if (m_attrs.stencil) {
- if (extensions->supports(packedDepthStencilExtension)) {
- extensions->ensureEnabled(packedDepthStencilExtension);
+ if (extensions.supports(packedDepthStencilExtension)) {
+ extensions.ensureEnabled(packedDepthStencilExtension);
// Force depth if stencil is true.
m_attrs.depth = true;
} else
m_attrs.stencil = false;
}
if (m_attrs.antialias) {
- if (!extensions->maySupportMultisampling() || !extensions->supports("GL_ANGLE_framebuffer_multisample") || isGLES2Compliant())
+ if (!extensions.supports("GL_ANGLE_framebuffer_multisample") || isGLES2Compliant())
m_attrs.antialias = false;
else
- extensions->ensureEnabled("GL_ANGLE_framebuffer_multisample");
+ extensions.ensureEnabled("GL_ANGLE_framebuffer_multisample");
}
}
-bool GraphicsContext3D::isResourceSafe()
-{
- return false;
-}
-
-void GraphicsContext3D::paintRenderingResultsToCanvas(ImageBuffer* imageBuffer, DrawingBuffer*)
+void GraphicsContext3D::paintRenderingResultsToCanvas(ImageBuffer* imageBuffer)
{
int rowBytes = m_currentWidth * 4;
int totalBytes = rowBytes * m_currentHeight;
@@ -165,8 +186,7 @@ void GraphicsContext3D::paintRenderingResultsToCanvas(ImageBuffer* imageBuffer,
paintToCanvas(pixels.get(), m_currentWidth, m_currentHeight,
imageBuffer->internalSize().width(), imageBuffer->internalSize().height(), imageBuffer->context());
#else
- paintToCanvas(pixels.get(), m_currentWidth, m_currentHeight,
- imageBuffer->internalSize().width(), imageBuffer->internalSize().height(), imageBuffer->context()->platformContext());
+ paintToCanvas(pixels.get(), m_currentWidth, m_currentHeight, imageBuffer->internalSize().width(), imageBuffer->internalSize().height(), imageBuffer->context().platformContext());
#endif
#if PLATFORM(IOS)
@@ -180,14 +200,14 @@ bool GraphicsContext3D::paintCompositedResultsToCanvas(ImageBuffer*)
return false;
}
-PassRefPtr<ImageData> GraphicsContext3D::paintRenderingResultsToImageData(DrawingBuffer*)
+RefPtr<ImageData> GraphicsContext3D::paintRenderingResultsToImageData()
{
// Reading premultiplied alpha would involve unpremultiplying, which is
// lossy.
if (m_attrs.premultipliedAlpha)
- return 0;
+ return nullptr;
- RefPtr<ImageData> imageData = ImageData::create(IntSize(m_currentWidth, m_currentHeight));
+ auto imageData = ImageData::create(IntSize(m_currentWidth, m_currentHeight));
unsigned char* pixels = imageData->data()->data();
int totalBytes = 4 * m_currentWidth * m_currentHeight;
@@ -197,7 +217,7 @@ PassRefPtr<ImageData> GraphicsContext3D::paintRenderingResultsToImageData(Drawin
for (int i = 0; i < totalBytes; i += 4)
std::swap(pixels[i], pixels[i + 2]);
- return imageData.release();
+ return imageData;
}
void GraphicsContext3D::prepareTexture()
@@ -207,12 +227,28 @@ void GraphicsContext3D::prepareTexture()
makeContextCurrent();
+#if !USE(COORDINATED_GRAPHICS_THREADED)
TemporaryOpenGLSetting scopedScissor(GL_SCISSOR_TEST, GL_FALSE);
TemporaryOpenGLSetting scopedDither(GL_DITHER, GL_FALSE);
-
+#endif
+
if (m_attrs.antialias)
resolveMultisamplingIfNecessary();
+#if USE(COORDINATED_GRAPHICS_THREADED)
+ std::swap(m_texture, m_compositorTexture);
+ std::swap(m_texture, m_intermediateTexture);
+ ::glBindFramebuffer(GL_FRAMEBUFFER, m_fbo);
+ ::glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, m_texture, 0);
+ glFlush();
+
+ if (m_state.boundFBO != m_fbo)
+ ::glBindFramebufferEXT(GraphicsContext3D::FRAMEBUFFER, m_state.boundFBO);
+ else
+ ::glBindFramebufferEXT(GraphicsContext3D::FRAMEBUFFER, m_fbo);
+ return;
+#endif
+
::glBindFramebufferEXT(GraphicsContext3D::FRAMEBUFFER, m_fbo);
::glActiveTexture(GL_TEXTURE0);
::glBindTexture(GL_TEXTURE_2D, m_compositorTexture);
@@ -221,8 +257,7 @@ void GraphicsContext3D::prepareTexture()
::glActiveTexture(m_state.activeTexture);
if (m_state.boundFBO != m_fbo)
::glBindFramebufferEXT(GraphicsContext3D::FRAMEBUFFER, m_state.boundFBO);
- ::glFinish();
- m_layerComposited = true;
+ ::glFlush();
}
void GraphicsContext3D::readRenderingResults(unsigned char *pixels, int pixelsSize)
@@ -271,11 +306,6 @@ void GraphicsContext3D::reshape(int width, int height)
markContextChanged();
-#if PLATFORM(EFL) && USE(GRAPHICS_SURFACE)
- ::glFlush(); // Make sure all GL calls have been committed before resizing.
- createGraphicsSurfaces(IntSize(width, height));
-#endif
-
m_currentWidth = width;
m_currentHeight = height;
@@ -334,6 +364,49 @@ void GraphicsContext3D::reshape(int width, int height)
::glFlush();
}
+bool GraphicsContext3D::checkVaryingsPacking(Platform3DObject vertexShader, Platform3DObject fragmentShader) const
+{
+ ASSERT(m_shaderSourceMap.contains(vertexShader));
+ ASSERT(m_shaderSourceMap.contains(fragmentShader));
+ const auto& vertexEntry = m_shaderSourceMap.find(vertexShader)->value;
+ const auto& fragmentEntry = m_shaderSourceMap.find(fragmentShader)->value;
+
+ HashMap<String, sh::ShaderVariable> combinedVaryings;
+ for (const auto& vertexSymbol : vertexEntry.varyingMap) {
+ const String& symbolName = vertexSymbol.key;
+ // The varying map includes variables for each index of an array variable.
+ // We only want a single variable to represent the array.
+ if (symbolName.endsWith("]"))
+ continue;
+
+ // Don't count built in varyings.
+ if (symbolName == "gl_FragCoord" || symbolName == "gl_FrontFacing" || symbolName == "gl_PointCoord")
+ continue;
+
+ const auto& fragmentSymbol = fragmentEntry.varyingMap.find(symbolName);
+ if (fragmentSymbol != fragmentEntry.varyingMap.end())
+ combinedVaryings.add(symbolName, fragmentSymbol->value);
+ }
+
+ size_t numVaryings = combinedVaryings.size();
+ if (!numVaryings)
+ return true;
+
+ std::vector<sh::ShaderVariable> variables;
+ variables.reserve(combinedVaryings.size());
+ for (const auto& varyingSymbol : combinedVaryings.values())
+ variables.push_back(varyingSymbol);
+
+ GC3Dint maxVaryingVectors = 0;
+#if !PLATFORM(IOS) && !((PLATFORM(WIN) || PLATFORM(GTK)) && USE(OPENGL_ES_2))
+ GC3Dint maxVaryingFloats = 0;
+ ::glGetIntegerv(GL_MAX_VARYING_FLOATS, &maxVaryingFloats);
+ maxVaryingVectors = maxVaryingFloats / 4;
+#else
+ ::glGetIntegerv(MAX_VARYING_VECTORS, &maxVaryingVectors);
+#endif
+ return ShCheckVariablesWithinPackingLimits(maxVaryingVectors, variables);
+}
bool GraphicsContext3D::precisionsMatch(Platform3DObject vertexShader, Platform3DObject fragmentShader) const
{
@@ -342,13 +415,16 @@ bool GraphicsContext3D::precisionsMatch(Platform3DObject vertexShader, Platform3
const auto& vertexEntry = m_shaderSourceMap.find(vertexShader)->value;
const auto& fragmentEntry = m_shaderSourceMap.find(fragmentShader)->value;
- HashMap<String, ShPrecisionType> vertexSymbolPrecisionMap;
+ HashMap<String, sh::GLenum> vertexSymbolPrecisionMap;
- for (const auto& entry : vertexEntry.uniformMap)
- vertexSymbolPrecisionMap.add(entry.value.mappedName, entry.value.precision);
+ for (const auto& entry : vertexEntry.uniformMap) {
+ const std::string& mappedName = entry.value.mappedName;
+ vertexSymbolPrecisionMap.add(String(mappedName.c_str(), mappedName.length()), entry.value.precision);
+ }
for (const auto& entry : fragmentEntry.uniformMap) {
- const auto& vertexSymbol = vertexSymbolPrecisionMap.find(entry.value.mappedName);
+ const std::string& mappedName = entry.value.mappedName;
+ const auto& vertexSymbol = vertexSymbolPrecisionMap.find(String(mappedName.c_str(), mappedName.length()));
if (vertexSymbol != vertexSymbolPrecisionMap.end() && vertexSymbol->value != entry.value.precision)
return false;
}
@@ -373,6 +449,7 @@ void GraphicsContext3D::attachShader(Platform3DObject program, Platform3DObject
ASSERT(program);
ASSERT(shader);
makeContextCurrent();
+ m_shaderProgramSymbolCountMap.remove(program);
::glAttachShader(program, shader);
}
@@ -470,6 +547,38 @@ void GraphicsContext3D::bufferSubData(GC3Denum target, GC3Dintptr offset, GC3Dsi
::glBufferSubData(target, offset, size, data);
}
+#if PLATFORM(MAC) || PLATFORM(IOS)
+void* GraphicsContext3D::mapBufferRange(GC3Denum target, GC3Dintptr offset, GC3Dsizeiptr length, GC3Dbitfield access)
+{
+ makeContextCurrent();
+ return ::glMapBufferRange(target, offset, length, access);
+}
+
+GC3Dboolean GraphicsContext3D::unmapBuffer(GC3Denum target)
+{
+ makeContextCurrent();
+ return ::glUnmapBuffer(target);
+}
+
+void GraphicsContext3D::copyBufferSubData(GC3Denum readTarget, GC3Denum writeTarget, GC3Dintptr readOffset, GC3Dintptr writeOffset, GC3Dsizeiptr size)
+{
+ makeContextCurrent();
+ ::glCopyBufferSubData(readTarget, writeTarget, readOffset, writeOffset, size);
+}
+
+void GraphicsContext3D::texStorage2D(GC3Denum target, GC3Dsizei levels, GC3Denum internalformat, GC3Dsizei width, GC3Dsizei height)
+{
+ makeContextCurrent();
+ ::glTexStorage2D(target, levels, internalformat, width, height);
+}
+
+void GraphicsContext3D::texStorage3D(GC3Denum target, GC3Dsizei levels, GC3Denum internalformat, GC3Dsizei width, GC3Dsizei height, GC3Dsizei depth)
+{
+ makeContextCurrent();
+ ::glTexStorage3D(target, levels, internalformat, width, height, depth);
+}
+#endif
+
GC3Denum GraphicsContext3D::checkFramebufferStatus(GC3Denum target)
{
makeContextCurrent();
@@ -512,15 +621,15 @@ void GraphicsContext3D::compileShader(Platform3DObject shader)
ANGLEResources.HashFunction = nameHashForShader;
if (!nameHashMapForShaders)
- nameHashMapForShaders = adoptPtr(new ShaderNameHash);
- currentNameHashMapForShader = nameHashMapForShaders.get();
+ nameHashMapForShaders = std::make_unique<ShaderNameHash>();
+ setCurrentNameHashMapForShader(nameHashMapForShaders.get());
m_compiler.setResources(ANGLEResources);
String translatedShaderSource = m_extensions->getTranslatedShaderSourceANGLE(shader);
ANGLEResources.HashFunction = previousHashFunction;
m_compiler.setResources(ANGLEResources);
- currentNameHashMapForShader = nullptr;
+ setCurrentNameHashMapForShader(nullptr);
if (!translatedShaderSource.length())
return;
@@ -560,8 +669,6 @@ void GraphicsContext3D::compileShader(Platform3DObject shader)
entry.isValid = false;
LOG(WebGL, "Error: shader translator produced a shader that OpenGL would not compile.");
}
-
- m_shaderSymbolCount = nullptr;
}
void GraphicsContext3D::copyTexImage2D(GC3Denum target, GC3Dint level, GC3Denum internalformat, GC3Dint x, GC3Dint y, GC3Dsizei width, GC3Dsizei height, GC3Dint border)
@@ -611,6 +718,7 @@ void GraphicsContext3D::detachShader(Platform3DObject program, Platform3DObject
ASSERT(program);
ASSERT(shader);
makeContextCurrent();
+ m_shaderProgramSymbolCountMap.remove(program);
::glDetachShader(program, shader);
}
@@ -630,12 +738,14 @@ void GraphicsContext3D::drawArrays(GC3Denum mode, GC3Dint first, GC3Dsizei count
{
makeContextCurrent();
::glDrawArrays(mode, first, count);
+ checkGPUStatusIfNecessary();
}
void GraphicsContext3D::drawElements(GC3Denum mode, GC3Dsizei count, GC3Denum type, GC3Dintptr offset)
{
makeContextCurrent();
::glDrawElements(mode, count, type, reinterpret_cast<GLvoid*>(static_cast<intptr_t>(offset)));
+ checkGPUStatusIfNecessary();
}
void GraphicsContext3D::enable(GC3Denum cap)
@@ -718,8 +828,15 @@ bool GraphicsContext3D::getActiveAttribImpl(Platform3DObject program, GC3Duint i
bool GraphicsContext3D::getActiveAttrib(Platform3DObject program, GC3Duint index, ActiveInfo& info)
{
- ASSERT(!m_shaderSymbolCount || index < m_shaderSymbolCount->filteredToActualAttributeIndexMap.size());
- GC3Duint rawIndex = (m_shaderSymbolCount) ? m_shaderSymbolCount->filteredToActualAttributeIndexMap[index] : index;
+ GC3Dint symbolCount;
+ auto result = m_shaderProgramSymbolCountMap.find(program);
+ if (result == m_shaderProgramSymbolCountMap.end()) {
+ getNonBuiltInActiveSymbolCount(program, GraphicsContext3D::ACTIVE_ATTRIBUTES, &symbolCount);
+ result = m_shaderProgramSymbolCountMap.find(program);
+ }
+
+ ActiveShaderSymbolCounts& symbolCounts = result->value;
+ GC3Duint rawIndex = (index < symbolCounts.filteredToActualAttributeIndexMap.size()) ? symbolCounts.filteredToActualAttributeIndexMap[index] : -1;
return getActiveAttribImpl(program, rawIndex, info);
}
@@ -758,8 +875,15 @@ bool GraphicsContext3D::getActiveUniformImpl(Platform3DObject program, GC3Duint
bool GraphicsContext3D::getActiveUniform(Platform3DObject program, GC3Duint index, ActiveInfo& info)
{
- ASSERT(!m_shaderSymbolCount || index < m_shaderSymbolCount->filteredToActualUniformIndexMap.size());
- GC3Duint rawIndex = (m_shaderSymbolCount) ? m_shaderSymbolCount->filteredToActualUniformIndexMap[index] : index;
+ GC3Dint symbolCount;
+ auto result = m_shaderProgramSymbolCountMap.find(program);
+ if (result == m_shaderProgramSymbolCountMap.end()) {
+ getNonBuiltInActiveSymbolCount(program, GraphicsContext3D::ACTIVE_UNIFORMS, &symbolCount);
+ result = m_shaderProgramSymbolCountMap.find(program);
+ }
+
+ ActiveShaderSymbolCounts& symbolCounts = result->value;
+ GC3Duint rawIndex = (index < symbolCounts.filteredToActualUniformIndexMap.size()) ? symbolCounts.filteredToActualUniformIndexMap[index] : -1;
return getActiveUniformImpl(program, rawIndex, info);
}
@@ -774,10 +898,21 @@ void GraphicsContext3D::getAttachedShaders(Platform3DObject program, GC3Dsizei m
::glGetAttachedShaders(program, maxCount, count, shaders);
}
+static String generateHashedName(const String& name)
+{
+ if (name.isEmpty())
+ return name;
+ uint64_t number = nameHashForShader(name.utf8().data(), name.length());
+ StringBuilder builder;
+ builder.appendLiteral("webgl_");
+ appendUnsigned64AsHex(number, builder, Lowercase);
+ return builder.toString();
+}
+
String GraphicsContext3D::mappedSymbolName(Platform3DObject program, ANGLEShaderSymbolType symbolType, const String& name)
{
- GC3Dsizei count;
- Platform3DObject shaders[2];
+ GC3Dsizei count = 0;
+ Platform3DObject shaders[2] = { };
getAttachedShaders(program, 2, &count, shaders);
for (GC3Dsizei i = 0; i < count; ++i) {
@@ -787,9 +922,28 @@ String GraphicsContext3D::mappedSymbolName(Platform3DObject program, ANGLEShader
const ShaderSymbolMap& symbolMap = result->value.symbolMap(symbolType);
ShaderSymbolMap::const_iterator symbolEntry = symbolMap.find(name);
- if (symbolEntry != symbolMap.end())
- return symbolEntry->value.mappedName;
+ if (symbolEntry != symbolMap.end()) {
+ const std::string& mappedName = symbolEntry->value.mappedName;
+ return String(mappedName.c_str(), mappedName.length());
+ }
}
+
+ if (symbolType == SHADER_SYMBOL_TYPE_ATTRIBUTE && !name.isEmpty()) {
+ // Attributes are a special case: they may be requested before any shaders have been compiled,
+ // and aren't even required to be used in any shader program.
+ if (!nameHashMapForShaders)
+ nameHashMapForShaders = std::make_unique<ShaderNameHash>();
+ setCurrentNameHashMapForShader(nameHashMapForShaders.get());
+
+ String generatedName = generateHashedName(name);
+
+ setCurrentNameHashMapForShader(nullptr);
+
+ m_possiblyUnusedAttributeMap.set(generatedName, name);
+
+ return generatedName;
+ }
+
return name;
}
@@ -806,10 +960,20 @@ String GraphicsContext3D::originalSymbolName(Platform3DObject program, ANGLEShad
const ShaderSymbolMap& symbolMap = result->value.symbolMap(symbolType);
for (const auto& symbolEntry : symbolMap) {
- if (symbolEntry.value.mappedName == name)
+ if (name == symbolEntry.value.mappedName.c_str())
return symbolEntry.key;
}
}
+
+ if (symbolType == SHADER_SYMBOL_TYPE_ATTRIBUTE && !name.isEmpty()) {
+ // Attributes are a special case: they may be requested before any shaders have been compiled,
+ // and aren't even required to be used in any shader program.
+
+ const auto& cached = m_possiblyUnusedAttributeMap.find(name);
+ if (cached != m_possiblyUnusedAttributeMap.end())
+ return cached->value;
+ }
+
return name;
}
@@ -823,7 +987,7 @@ String GraphicsContext3D::mappedSymbolName(Platform3DObject shaders[2], size_t c
const ShaderSymbolMap& symbolMap = result->value.symbolMap(static_cast<enum ANGLEShaderSymbolType>(symbolType));
for (const auto& symbolEntry : symbolMap) {
- if (symbolEntry.value.mappedName == name)
+ if (name == symbolEntry.value.mappedName.c_str())
return symbolEntry.key;
}
}
@@ -843,18 +1007,38 @@ int GraphicsContext3D::getAttribLocation(Platform3DObject program, const String&
return ::glGetAttribLocation(program, mappedName.utf8().data());
}
-GraphicsContext3D::Attributes GraphicsContext3D::getContextAttributes()
+GraphicsContext3DAttributes GraphicsContext3D::getContextAttributes()
{
return m_attrs;
}
+bool GraphicsContext3D::moveErrorsToSyntheticErrorList()
+{
+ makeContextCurrent();
+ bool movedAnError = false;
+
+ // Set an arbitrary limit of 100 here to avoid creating a hang if
+ // a problem driver has a bug that causes it to never clear the error.
+ // Otherwise, we would just loop until we got NO_ERROR.
+ for (unsigned i = 0; i < 100; ++i) {
+ GC3Denum error = glGetError();
+ if (error == NO_ERROR)
+ break;
+ m_syntheticErrors.add(error);
+ movedAnError = true;
+ }
+
+ return movedAnError;
+}
+
GC3Denum GraphicsContext3D::getError()
{
- if (m_syntheticErrors.size() > 0) {
- ListHashSet<GC3Denum>::iterator iter = m_syntheticErrors.begin();
- GC3Denum err = *iter;
- m_syntheticErrors.remove(iter);
- return err;
+ if (!m_syntheticErrors.isEmpty()) {
+ // Need to move the current errors to the synthetic error list in case
+ // that error is already there, since the expected behavior of both
+ // glGetError and getError is to only report each error code once.
+ moveErrorsToSyntheticErrorList();
+ return m_syntheticErrors.takeFirst();
}
makeContextCurrent();
@@ -1228,6 +1412,57 @@ void GraphicsContext3D::viewport(GC3Dint x, GC3Dint y, GC3Dsizei width, GC3Dsize
::glViewport(x, y, width, height);
}
+Platform3DObject GraphicsContext3D::createVertexArray()
+{
+ makeContextCurrent();
+ GLuint array = 0;
+#if !USE(OPENGL_ES_2) && (PLATFORM(GTK) || PLATFORM(WIN) || PLATFORM(IOS))
+ glGenVertexArrays(1, &array);
+#elif defined(GL_APPLE_vertex_array_object) && GL_APPLE_vertex_array_object
+ glGenVertexArraysAPPLE(1, &array);
+#endif
+ return array;
+}
+
+void GraphicsContext3D::deleteVertexArray(Platform3DObject array)
+{
+ if (!array)
+ return;
+
+ makeContextCurrent();
+#if !USE(OPENGL_ES_2) && (PLATFORM(GTK) || PLATFORM(WIN) || PLATFORM(IOS))
+ glDeleteVertexArrays(1, &array);
+#elif defined(GL_APPLE_vertex_array_object) && GL_APPLE_vertex_array_object
+ glDeleteVertexArraysAPPLE(1, &array);
+#endif
+}
+
+GC3Dboolean GraphicsContext3D::isVertexArray(Platform3DObject array)
+{
+ if (!array)
+ return GL_FALSE;
+
+ makeContextCurrent();
+#if !USE(OPENGL_ES_2) && (PLATFORM(GTK) || PLATFORM(WIN) || PLATFORM(IOS))
+ return glIsVertexArray(array);
+#elif defined(GL_APPLE_vertex_array_object) && GL_APPLE_vertex_array_object
+ return glIsVertexArrayAPPLE(array);
+#endif
+ return GL_FALSE;
+}
+
+void GraphicsContext3D::bindVertexArray(Platform3DObject array)
+{
+ makeContextCurrent();
+#if !USE(OPENGL_ES_2) && (PLATFORM(GTK) || PLATFORM(WIN) || PLATFORM(IOS))
+ glBindVertexArray(array);
+#elif defined(GL_APPLE_vertex_array_object) && GL_APPLE_vertex_array_object
+ glBindVertexArrayAPPLE(array);
+#else
+ UNUSED_PARAM(array);
+#endif
+}
+
void GraphicsContext3D::getBooleanv(GC3Denum pname, GC3Dboolean* value)
{
makeContextCurrent();
@@ -1245,6 +1480,15 @@ void GraphicsContext3D::getFloatv(GC3Denum pname, GC3Dfloat* value)
makeContextCurrent();
::glGetFloatv(pname, value);
}
+
+void GraphicsContext3D::getInteger64v(GC3Denum pname, GC3Dint64* value)
+{
+ UNUSED_PARAM(pname);
+ makeContextCurrent();
+ *value = 0;
+ // FIXME 141178: Before enabling this we must first switch over to using gl3.h and creating and initialing the WebGL2 context using OpenGL ES 3.0.
+ // ::glGetInteger64v(pname, value);
+}
void GraphicsContext3D::getFramebufferAttachmentParameteriv(GC3Denum target, GC3Denum attachment, GC3Denum pname, GC3Dint* value)
{
@@ -1267,13 +1511,14 @@ void GraphicsContext3D::getNonBuiltInActiveSymbolCount(Platform3DObject program,
return;
makeContextCurrent();
-
- if (m_shaderSymbolCount) {
- *value = m_shaderSymbolCount->countForType(pname);
+ const auto& result = m_shaderProgramSymbolCountMap.find(program);
+ if (result != m_shaderProgramSymbolCountMap.end()) {
+ *value = result->value.countForType(pname);
return;
}
- m_shaderSymbolCount = std::make_unique<ActiveShaderSymbolCounts>();
+ m_shaderProgramSymbolCountMap.set(program, ActiveShaderSymbolCounts());
+ ActiveShaderSymbolCounts& symbolCounts = m_shaderProgramSymbolCountMap.find(program)->value;
// Retrieve the active attributes, build a filtered count, and a mapping of
// our internal attributes indexes to the real unfiltered indexes inside OpenGL.
@@ -1285,7 +1530,7 @@ void GraphicsContext3D::getNonBuiltInActiveSymbolCount(Platform3DObject program,
if (info.name.startsWith("gl_"))
continue;
- m_shaderSymbolCount->filteredToActualAttributeIndexMap.append(i);
+ symbolCounts.filteredToActualAttributeIndexMap.append(i);
}
// Do the same for uniforms.
@@ -1297,10 +1542,10 @@ void GraphicsContext3D::getNonBuiltInActiveSymbolCount(Platform3DObject program,
if (info.name.startsWith("gl_"))
continue;
- m_shaderSymbolCount->filteredToActualUniformIndexMap.append(i);
+ symbolCounts.filteredToActualUniformIndexMap.append(i);
}
- *value = m_shaderSymbolCount->countForType(pname);
+ *value = symbolCounts.countForType(pname);
}
String GraphicsContext3D::getUnmangledInfoLog(Platform3DObject shaders[2], GC3Dsizei count, const String& log)
@@ -1309,7 +1554,7 @@ String GraphicsContext3D::getUnmangledInfoLog(Platform3DObject shaders[2], GC3Ds
JSC::Yarr::RegularExpression regExp("webgl_[0123456789abcdefABCDEF]+", TextCaseSensitive);
- String processedLog;
+ StringBuilder processedLog;
int startFrom = 0;
int matchedLength = 0;
@@ -1329,8 +1574,8 @@ String GraphicsContext3D::getUnmangledInfoLog(Platform3DObject shaders[2], GC3Ds
processedLog.append(log.substring(startFrom, log.length() - startFrom));
- LOG(WebGL, "-->: %s", processedLog.utf8().data());
- return processedLog;
+ LOG(WebGL, "-->: %s", processedLog.toString().utf8().data());
+ return processedLog.toString();
}
String GraphicsContext3D::getProgramInfoLog(Platform3DObject program)
@@ -1501,6 +1746,12 @@ void GraphicsContext3D::texSubImage2D(GC3Denum target, GC3Dint level, GC3Dint xo
type = GL_HALF_FLOAT_ARB;
#endif
+ if (m_usingCoreProfile && format == ALPHA) {
+ // We are using a core profile. This means that GL_ALPHA, which is a valid format in WebGL for texSubImage2D
+ // is not supported in OpenGL. We are using GL_RED to back GL_ALPHA, so do it here as well.
+ format = RED;
+ }
+
// FIXME: we will need to deal with PixelStore params when dealing with image buffers that differ from the subimage size.
::glTexSubImage2D(target, level, xoff, yoff, width, height, format, type, pixels);
}
@@ -1606,6 +1857,10 @@ void GraphicsContext3D::deleteTexture(Platform3DObject texture)
void GraphicsContext3D::synthesizeGLError(GC3Denum error)
{
+ // Need to move the current errors to the synthetic error list to
+ // preserve the order of errors, so a caller to getError will get
+ // any errors from glError before the error we are synthesizing.
+ moveErrorsToSyntheticErrorList();
m_syntheticErrors.add(error);
}
@@ -1624,12 +1879,43 @@ bool GraphicsContext3D::layerComposited() const
return m_layerComposited;
}
+void GraphicsContext3D::forceContextLost()
+{
+#if ENABLE(WEBGL)
+ if (m_webglContext)
+ m_webglContext->forceLostContext(WebGLRenderingContextBase::RealLostContext);
+#endif
+}
+
+void GraphicsContext3D::recycleContext()
+{
+#if ENABLE(WEBGL)
+ if (m_webglContext)
+ m_webglContext->recycleContext();
+#endif
+}
+
void GraphicsContext3D::texImage2DDirect(GC3Denum target, GC3Dint level, GC3Denum internalformat, GC3Dsizei width, GC3Dsizei height, GC3Dint border, GC3Denum format, GC3Denum type, const void* pixels)
{
makeContextCurrent();
::glTexImage2D(target, level, internalformat, width, height, border, format, type, pixels);
}
+void GraphicsContext3D::drawArraysInstanced(GC3Denum mode, GC3Dint first, GC3Dsizei count, GC3Dsizei primcount)
+{
+ getExtensions().drawArraysInstanced(mode, first, count, primcount);
+}
+
+void GraphicsContext3D::drawElementsInstanced(GC3Denum mode, GC3Dsizei count, GC3Denum type, GC3Dintptr offset, GC3Dsizei primcount)
+{
+ getExtensions().drawElementsInstanced(mode, count, type, offset, primcount);
+}
+
+void GraphicsContext3D::vertexAttribDivisor(GC3Duint index, GC3Duint divisor)
+{
+ getExtensions().vertexAttribDivisor(index, divisor);
+}
+
}
-#endif // USE(3D_GRAPHICS)
+#endif // ENABLE(GRAPHICS_CONTEXT_3D)
diff --git a/Source/WebCore/platform/graphics/opengl/GraphicsContext3DOpenGLES.cpp b/Source/WebCore/platform/graphics/opengl/GraphicsContext3DOpenGLES.cpp
index 546b2d433..6b608c66d 100644
--- a/Source/WebCore/platform/graphics/opengl/GraphicsContext3DOpenGLES.cpp
+++ b/Source/WebCore/platform/graphics/opengl/GraphicsContext3DOpenGLES.cpp
@@ -13,10 +13,10 @@
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
- * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
@@ -28,7 +28,7 @@
#include "config.h"
-#if USE(3D_GRAPHICS)
+#if ENABLE(GRAPHICS_CONTEXT_3D)
#include "GraphicsContext3D.h"
@@ -37,6 +37,12 @@
#include "IntSize.h"
#include "NotImplemented.h"
+#if PLATFORM(WIN)
+#include <GLSLANG/ShaderLang.h>
+#else
+#include <ANGLE/ShaderLang.h>
+#endif
+
namespace WebCore {
void GraphicsContext3D::releaseShaderCompiler()
@@ -90,7 +96,7 @@ bool GraphicsContext3D::reshapeFBOs(const IntSize& size)
// We don't allow the logic where stencil is required and depth is not.
// See GraphicsContext3D::validateAttributes.
- bool supportPackedDepthStencilBuffer = (m_attrs.stencil || m_attrs.depth) && getExtensions()->supports("GL_OES_packed_depth_stencil");
+ bool supportPackedDepthStencilBuffer = (m_attrs.stencil || m_attrs.depth) && getExtensions().supports("GL_OES_packed_depth_stencil");
// Resize regular FBO.
bool mustRestoreFBO = false;
@@ -110,29 +116,64 @@ bool GraphicsContext3D::reshapeFBOs(const IntSize& size)
::glBindTexture(GL_TEXTURE_2D, 0);
}
- // We don't support antialiasing yet. See GraphicsContext3D::validateAttributes.
- ASSERT(!m_attrs.antialias);
-
- if (m_attrs.stencil || m_attrs.depth) {
- // Use a 24 bit depth buffer where we know we have it.
- if (supportPackedDepthStencilBuffer) {
- ::glBindRenderbuffer(GL_RENDERBUFFER, m_depthStencilBuffer);
- ::glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8_OES, width, height);
- if (m_attrs.stencil)
- ::glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, m_depthStencilBuffer);
- if (m_attrs.depth)
- ::glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, m_depthStencilBuffer);
- ::glBindRenderbuffer(GL_RENDERBUFFER, 0);
- } else {
- if (m_attrs.stencil) {
- ::glBindRenderbuffer(GL_RENDERBUFFER, m_stencilBuffer);
- ::glRenderbufferStorage(GL_RENDERBUFFER, GL_STENCIL_INDEX8, width, height);
- ::glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, m_stencilBuffer);
+#if USE(COORDINATED_GRAPHICS_THREADED)
+ ::glBindTexture(GL_TEXTURE_2D, m_intermediateTexture);
+ ::glTexImage2D(GL_TEXTURE_2D, 0, m_internalColorFormat, width, height, 0, colorFormat, GL_UNSIGNED_BYTE, 0);
+ ::glBindTexture(GL_TEXTURE_2D, 0);
+#endif
+
+ Extensions3DOpenGLES& extensions = static_cast<Extensions3DOpenGLES&>(getExtensions());
+ if (extensions.isImagination() && m_attrs.antialias) {
+ GLint maxSampleCount;
+ ::glGetIntegerv(Extensions3D::MAX_SAMPLES_IMG, &maxSampleCount);
+ GLint sampleCount = std::min(8, maxSampleCount);
+
+ extensions.framebufferTexture2DMultisampleIMG(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, m_texture, 0, sampleCount);
+
+ if (m_attrs.stencil || m_attrs.depth) {
+ // Use a 24 bit depth buffer where we know we have it.
+ if (supportPackedDepthStencilBuffer) {
+ ::glBindRenderbuffer(GL_RENDERBUFFER, m_depthStencilBuffer);
+ extensions.renderbufferStorageMultisample(GL_RENDERBUFFER, sampleCount, GL_DEPTH24_STENCIL8_OES, width, height);
+ if (m_attrs.stencil)
+ ::glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, m_depthStencilBuffer);
+ if (m_attrs.depth)
+ ::glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, m_depthStencilBuffer);
+ } else {
+ if (m_attrs.stencil) {
+ ::glBindRenderbuffer(GL_RENDERBUFFER, m_stencilBuffer);
+ extensions.renderbufferStorageMultisample(GL_RENDERBUFFER, sampleCount, GL_STENCIL_INDEX8, width, height);
+ ::glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, m_stencilBuffer);
+ }
+ if (m_attrs.depth) {
+ ::glBindRenderbuffer(GL_RENDERBUFFER, m_depthBuffer);
+ extensions.renderbufferStorageMultisample(GL_RENDERBUFFER, sampleCount, GL_DEPTH_COMPONENT16, width, height);
+ ::glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, m_depthBuffer);
+ }
}
- if (m_attrs.depth) {
- ::glBindRenderbuffer(GL_RENDERBUFFER, m_depthBuffer);
- ::glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, width, height);
- ::glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, m_depthBuffer);
+ ::glBindRenderbuffer(GL_RENDERBUFFER, 0);
+ }
+ } else {
+ if (m_attrs.stencil || m_attrs.depth) {
+ // Use a 24 bit depth buffer where we know we have it.
+ if (supportPackedDepthStencilBuffer) {
+ ::glBindRenderbuffer(GL_RENDERBUFFER, m_depthStencilBuffer);
+ ::glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8_OES, width, height);
+ if (m_attrs.stencil)
+ ::glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, m_depthStencilBuffer);
+ if (m_attrs.depth)
+ ::glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, m_depthStencilBuffer);
+ } else {
+ if (m_attrs.stencil) {
+ ::glBindRenderbuffer(GL_RENDERBUFFER, m_stencilBuffer);
+ ::glRenderbufferStorage(GL_RENDERBUFFER, GL_STENCIL_INDEX8, width, height);
+ ::glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, m_stencilBuffer);
+ }
+ if (m_attrs.depth) {
+ ::glBindRenderbuffer(GL_RENDERBUFFER, m_depthBuffer);
+ ::glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, width, height);
+ ::glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, m_depthBuffer);
+ }
}
::glBindRenderbuffer(GL_RENDERBUFFER, 0);
}
@@ -145,7 +186,7 @@ bool GraphicsContext3D::reshapeFBOs(const IntSize& size)
return mustRestoreFBO;
}
-void GraphicsContext3D::resolveMultisamplingIfNecessary(const IntRect& rect)
+void GraphicsContext3D::resolveMultisamplingIfNecessary(const IntRect&)
{
// FIXME: We don't support antialiasing yet.
notImplemented();
@@ -187,11 +228,8 @@ void GraphicsContext3D::validateAttributes()
{
validateDepthStencil("GL_OES_packed_depth_stencil");
- if (m_attrs.antialias) {
- Extensions3D* extensions = getExtensions();
- if (!extensions->supports("GL_IMG_multisampled_render_to_texture"))
- m_attrs.antialias = false;
- }
+ if (m_attrs.antialias && !getExtensions().supports("GL_IMG_multisampled_render_to_texture"))
+ m_attrs.antialias = false;
}
void GraphicsContext3D::depthRange(GC3Dclampf zNear, GC3Dclampf zFar)
@@ -206,36 +244,171 @@ void GraphicsContext3D::clearDepth(GC3Dclampf depth)
::glClearDepthf(depth);
}
-void GraphicsContext3D::drawArraysInstanced(GC3Denum mode, GC3Dint first, GC3Dsizei count, GC3Dsizei primcount)
+#if !PLATFORM(GTK)
+Extensions3D& GraphicsContext3D::getExtensions()
+{
+ if (!m_extensions)
+ m_extensions = std::make_unique<Extensions3DOpenGLES>(this, isGLES2Compliant());
+ return *m_extensions;
+}
+#endif
+
+#if PLATFORM(WIN) && !USE(CAIRO)
+RefPtr<GraphicsContext3D> GraphicsContext3D::create(GraphicsContext3DAttributes attributes, HostWindow* hostWindow, GraphicsContext3D::RenderStyle renderStyle)
{
- UNUSED_PARAM(mode);
- UNUSED_PARAM(first);
- UNUSED_PARAM(count);
- UNUSED_PARAM(primcount);
+ // This implementation doesn't currently support rendering directly to the HostWindow.
+ if (renderStyle == RenderDirectlyToHostWindow)
+ return nullptr;
+
+ static bool initialized = false;
+ static bool success = true;
+ if (!initialized) {
+#if !USE(OPENGL_ES_2)
+ success = initializeOpenGLShims();
+#endif
+ initialized = true;
+ }
+ if (!success)
+ return nullptr;
+
+ return adoptRef(new GraphicsContext3D(attributes, hostWindow, renderStyle));
}
-void GraphicsContext3D::drawElementsInstanced(GC3Denum mode, GC3Dsizei count, GC3Denum type, GC3Dintptr offset, GC3Dsizei primcount)
+GraphicsContext3D::GraphicsContext3D(GraphicsContext3DAttributes attributes, HostWindow*, GraphicsContext3D::RenderStyle renderStyle)
+ : m_currentWidth(0)
+ , m_currentHeight(0)
+ , m_compiler(isGLES2Compliant() ? SH_ESSL_OUTPUT : SH_GLSL_COMPATIBILITY_OUTPUT)
+ , m_attrs(attributes)
+ , m_texture(0)
+ , m_fbo(0)
+ , m_depthStencilBuffer(0)
+ , m_multisampleFBO(0)
+ , m_multisampleDepthStencilBuffer(0)
+ , m_multisampleColorBuffer(0)
+ , m_private(std::make_unique<GraphicsContext3DPrivate>(this, renderStyle))
{
- UNUSED_PARAM(mode);
- UNUSED_PARAM(count);
- UNUSED_PARAM(type);
- UNUSED_PARAM(offset);
- UNUSED_PARAM(primcount);
+ makeContextCurrent();
+
+ validateAttributes();
+
+ if (renderStyle == RenderOffscreen) {
+ // Create a texture to render into.
+ ::glGenTextures(1, &m_texture);
+ ::glBindTexture(GL_TEXTURE_2D, m_texture);
+ ::glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ ::glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ ::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ ::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+ ::glBindTexture(GL_TEXTURE_2D, 0);
+
+ // Create an FBO.
+ ::glGenFramebuffers(1, &m_fbo);
+ ::glBindFramebuffer(GL_FRAMEBUFFER, m_fbo);
+
+ m_state.boundFBO = m_fbo;
+ if (!m_attrs.antialias && (m_attrs.stencil || m_attrs.depth))
+ ::glGenRenderbuffers(1, &m_depthStencilBuffer);
+
+ // Create a multisample FBO.
+ if (m_attrs.antialias) {
+ ::glGenFramebuffers(1, &m_multisampleFBO);
+ ::glBindFramebuffer(GL_FRAMEBUFFER, m_multisampleFBO);
+ m_state.boundFBO = m_multisampleFBO;
+ ::glGenRenderbuffers(1, &m_multisampleColorBuffer);
+ if (m_attrs.stencil || m_attrs.depth)
+ ::glGenRenderbuffers(1, &m_multisampleDepthStencilBuffer);
+ }
+ }
+
+ // ANGLE initialization.
+ ShBuiltInResources ANGLEResources;
+ ShInitBuiltInResources(&ANGLEResources);
+
+ getIntegerv(GraphicsContext3D::MAX_VERTEX_ATTRIBS, &ANGLEResources.MaxVertexAttribs);
+ getIntegerv(GraphicsContext3D::MAX_VERTEX_UNIFORM_VECTORS, &ANGLEResources.MaxVertexUniformVectors);
+ getIntegerv(GraphicsContext3D::MAX_VARYING_VECTORS, &ANGLEResources.MaxVaryingVectors);
+ getIntegerv(GraphicsContext3D::MAX_VERTEX_TEXTURE_IMAGE_UNITS, &ANGLEResources.MaxVertexTextureImageUnits);
+ getIntegerv(GraphicsContext3D::MAX_COMBINED_TEXTURE_IMAGE_UNITS, &ANGLEResources.MaxCombinedTextureImageUnits);
+ getIntegerv(GraphicsContext3D::MAX_TEXTURE_IMAGE_UNITS, &ANGLEResources.MaxTextureImageUnits);
+ getIntegerv(GraphicsContext3D::MAX_FRAGMENT_UNIFORM_VECTORS, &ANGLEResources.MaxFragmentUniformVectors);
+
+ // Always set to 1 for OpenGL ES.
+ ANGLEResources.MaxDrawBuffers = 1;
+
+ GC3Dint range[2], precision;
+ getShaderPrecisionFormat(GraphicsContext3D::FRAGMENT_SHADER, GraphicsContext3D::HIGH_FLOAT, range, &precision);
+ ANGLEResources.FragmentPrecisionHigh = (range[0] || range[1] || precision);
+
+ m_compiler.setResources(ANGLEResources);
+
+#if !USE(OPENGL_ES_2)
+ ::glEnable(GL_VERTEX_PROGRAM_POINT_SIZE);
+ ::glEnable(GL_POINT_SPRITE);
+#endif
+
+ ::glClearColor(0, 0, 0, 0);
}
-void GraphicsContext3D::vertexAttribDivisor(GC3Duint index, GC3Duint divisor)
+GraphicsContext3D::~GraphicsContext3D()
{
- UNUSED_PARAM(index);
- UNUSED_PARAM(divisor);
+ makeContextCurrent();
+ ::glDeleteTextures(1, &m_texture);
+ if (m_attrs.antialias) {
+ ::glDeleteRenderbuffers(1, &m_multisampleColorBuffer);
+ if (m_attrs.stencil || m_attrs.depth)
+ ::glDeleteRenderbuffers(1, &m_multisampleDepthStencilBuffer);
+ ::glDeleteFramebuffers(1, &m_multisampleFBO);
+ } else {
+ if (m_attrs.stencil || m_attrs.depth)
+ ::glDeleteRenderbuffers(1, &m_depthStencilBuffer);
+ }
+ ::glDeleteFramebuffers(1, &m_fbo);
}
-Extensions3D* GraphicsContext3D::getExtensions()
+void GraphicsContext3D::setContextLostCallback(std::unique_ptr<ContextLostCallback>)
{
- if (!m_extensions)
- m_extensions = adoptPtr(new Extensions3DOpenGLES(this));
- return m_extensions.get();
}
+void GraphicsContext3D::setErrorMessageCallback(std::unique_ptr<ErrorMessageCallback>)
+{
+}
+
+bool GraphicsContext3D::makeContextCurrent()
+{
+ if (!m_private)
+ return false;
+ return m_private->makeContextCurrent();
+}
+
+void GraphicsContext3D::checkGPUStatusIfNecessary()
+{
+}
+
+PlatformGraphicsContext3D GraphicsContext3D::platformGraphicsContext3D()
+{
+ return m_private->platformContext();
+}
+
+Platform3DObject GraphicsContext3D::platformTexture() const
+{
+ return m_texture;
+}
+
+bool GraphicsContext3D::isGLES2Compliant() const
+{
+#if USE(OPENGL_ES_2)
+ return true;
+#else
+ return false;
+#endif
+}
+
+PlatformLayer* GraphicsContext3D::platformLayer() const
+{
+ return m_webGLLayer->platformLayer();
+}
+#endif
+
}
-#endif // USE(3D_GRAPHICS)
+#endif // ENABLE(GRAPHICS_CONTEXT_3D)
diff --git a/Source/WebCore/platform/graphics/opengl/TemporaryOpenGLSetting.cpp b/Source/WebCore/platform/graphics/opengl/TemporaryOpenGLSetting.cpp
index a0163770c..7438d9e14 100644
--- a/Source/WebCore/platform/graphics/opengl/TemporaryOpenGLSetting.cpp
+++ b/Source/WebCore/platform/graphics/opengl/TemporaryOpenGLSetting.cpp
@@ -26,7 +26,7 @@
#include "config.h"
-#if USE(3D_GRAPHICS)
+#if ENABLE(GRAPHICS_CONTEXT_3D)
#include "TemporaryOpenGLSetting.h"
#if USE(OPENGL_ES_2)
@@ -36,7 +36,7 @@
#include <OpenGLES/ES2/gl.h>
#elif PLATFORM(MAC)
#include <OpenGL/gl.h>
-#elif PLATFORM(GTK) || PLATFORM(EFL) || PLATFORM(WIN)
+#elif PLATFORM(GTK) || PLATFORM(WIN)
#include "OpenGLShims.h"
#endif
diff --git a/Source/WebCore/platform/graphics/opengl/TemporaryOpenGLSetting.h b/Source/WebCore/platform/graphics/opengl/TemporaryOpenGLSetting.h
index 833f8b6be..cb51695b7 100644
--- a/Source/WebCore/platform/graphics/opengl/TemporaryOpenGLSetting.h
+++ b/Source/WebCore/platform/graphics/opengl/TemporaryOpenGLSetting.h
@@ -38,7 +38,7 @@ namespace WebCore {
// value upon destruction, making it an alternative to checking, clearing, and resetting each flag
// at all of a block's exit points.
//
-// Based on WTF::TemporaryChange<>
+// Based on WTF::SetForScope<>
class TemporaryOpenGLSetting {
WTF_MAKE_NONCOPYABLE(TemporaryOpenGLSetting);
diff --git a/Source/WebCore/platform/graphics/opentype/OpenTypeCG.cpp b/Source/WebCore/platform/graphics/opentype/OpenTypeCG.cpp
new file mode 100644
index 000000000..e1b56fab4
--- /dev/null
+++ b/Source/WebCore/platform/graphics/opentype/OpenTypeCG.cpp
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2015 Frederic Wang (fred.wang@free.fr). All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "OpenTypeCG.h"
+
+#include "OpenTypeTypes.h"
+
+namespace WebCore {
+namespace OpenType {
+
+#if PLATFORM(WIN)
+static const unsigned long kCTFontTableOS2 = 'OS/2';
+#endif
+
+bool fontHasMathTable(CTFontRef ctFont)
+{
+ RetainPtr<CFArrayRef> tableTags = adoptCF(CTFontCopyAvailableTables(ctFont, kCTFontTableOptionNoOptions));
+ if (!tableTags)
+ return false;
+ CFIndex numTables = CFArrayGetCount(tableTags.get());
+ for (CFIndex index = 0; index < numTables; ++index) {
+ CTFontTableTag tag = (CTFontTableTag)(uintptr_t)CFArrayGetValueAtIndex(tableTags.get(), index);
+ if (tag == 'MATH')
+ return true;
+ }
+ return false;
+}
+
+static inline short readShortFromTable(const UInt8* os2Data, CFIndex offset)
+{
+ return *(reinterpret_cast<const OpenType::Int16*>(os2Data + offset));
+}
+
+bool tryGetTypoMetrics(CTFontRef font, short& ascent, short& descent, short& lineGap)
+{
+ bool result = false;
+ if (auto os2Table = adoptCF(CTFontCopyTable(font, kCTFontTableOS2, kCTFontTableOptionNoOptions))) {
+ // For the structure of the OS/2 table, see
+ // https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6OS2.html
+ const CFIndex fsSelectionOffset = 16 * 2 + 10 + 4 * 4 + 4 * 1;
+ const CFIndex sTypoAscenderOffset = fsSelectionOffset + 3 * 2;
+ const CFIndex sTypoDescenderOffset = sTypoAscenderOffset + 2;
+ const CFIndex sTypoLineGapOffset = sTypoDescenderOffset + 2;
+ if (CFDataGetLength(os2Table.get()) >= sTypoLineGapOffset + 2) {
+ const UInt8* os2Data = CFDataGetBytePtr(os2Table.get());
+ // We test the use typo bit on the least significant byte of fsSelection.
+ const UInt8 useTypoMetricsMask = 1 << 7;
+ if (*(os2Data + fsSelectionOffset + 1) & useTypoMetricsMask) {
+ ascent = readShortFromTable(os2Data, sTypoAscenderOffset);
+ descent = readShortFromTable(os2Data, sTypoDescenderOffset);
+ lineGap = readShortFromTable(os2Data, sTypoLineGapOffset);
+ result = true;
+ }
+ }
+ }
+ return result;
+}
+
+} // namespace OpenType
+} // namespace WebCore
diff --git a/Source/WebCore/platform/graphics/opentype/OpenTypeCG.h b/Source/WebCore/platform/graphics/opentype/OpenTypeCG.h
new file mode 100644
index 000000000..975cdaf05
--- /dev/null
+++ b/Source/WebCore/platform/graphics/opentype/OpenTypeCG.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2015 Frederic Wang (fred.wang@free.fr). All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef OpenTypeCG_h
+#define OpenTypeCG_h
+
+#include <CoreGraphics/CoreGraphics.h>
+#include <CoreText/CoreText.h>
+
+#if PLATFORM(WIN)
+#include "CoreTextSPIWin.h"
+#endif
+
+namespace WebCore {
+namespace OpenType {
+
+bool fontHasMathTable(CTFontRef);
+bool tryGetTypoMetrics(CTFontRef, short& ascent, short& descent, short& lineGap);
+
+} // namespace OpenType
+} // namespace WebCore
+#endif // OpenTypeCG_h
diff --git a/Source/WebCore/platform/graphics/opentype/OpenTypeMathData.cpp b/Source/WebCore/platform/graphics/opentype/OpenTypeMathData.cpp
new file mode 100644
index 000000000..c2d50a404
--- /dev/null
+++ b/Source/WebCore/platform/graphics/opentype/OpenTypeMathData.cpp
@@ -0,0 +1,397 @@
+/*
+ * Copyright (C) 2014 Frederic Wang (fred.wang@free.fr). All rights reserved.
+ * Copyright (C) 2016 Igalia S.L. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "OpenTypeMathData.h"
+
+#include "Font.h"
+#include "FontPlatformData.h"
+#if ENABLE(OPENTYPE_MATH)
+#include "OpenTypeTypes.h"
+#endif
+#include "SharedBuffer.h"
+
+using namespace std;
+
+namespace WebCore {
+
+#if ENABLE(OPENTYPE_MATH)
+namespace OpenType {
+
+#if PLATFORM(COCOA)
+const FourCharCode MATHTag = 'MATH';
+#else
+const uint32_t MATHTag = OT_MAKE_TAG('M', 'A', 'T', 'H');
+#endif
+
+#pragma pack(1)
+
+struct MathValueRecord {
+ OpenType::Int16 value;
+ OpenType::Offset deviceTableOffset;
+};
+
+struct MathConstants {
+ OpenType::Int16 intConstants[OpenTypeMathData::ScriptScriptPercentScaleDown - OpenTypeMathData::ScriptPercentScaleDown + 1];
+ OpenType::UInt16 uIntConstants[OpenTypeMathData::DisplayOperatorMinHeight - OpenTypeMathData::DelimitedSubFormulaMinHeight + 1];
+ OpenType::MathValueRecord mathValuesConstants[OpenTypeMathData::RadicalKernAfterDegree - OpenTypeMathData::MathLeading + 1];
+ OpenType::UInt16 radicalDegreeBottomRaisePercent;
+};
+
+struct MathItalicsCorrectionInfo : TableWithCoverage {
+ OpenType::Offset coverageOffset;
+ OpenType::UInt16 italicsCorrectionCount;
+ OpenType::MathValueRecord italicsCorrection[1]; // There are italicsCorrectionCount italic correction values.
+
+ int16_t getItalicCorrection(const SharedBuffer& buffer, Glyph glyph) const
+ {
+ uint16_t count = uint16_t(italicsCorrectionCount);
+ if (!isValidEnd(buffer, &italicsCorrection[count]))
+ return 0;
+
+ uint16_t offset = coverageOffset;
+ if (!offset)
+ return 0;
+ const CoverageTable* coverage = validateOffset<CoverageTable>(buffer, offset);
+ if (!coverage)
+ return 0;
+
+ // We determine the index in the italicsCorrection table.
+ uint32_t i;
+ if (!getCoverageIndex(buffer, coverage, glyph, i) || i >= count)
+ return 0;
+
+ return int16_t(italicsCorrection[i].value);
+ }
+};
+
+struct MathGlyphInfo : TableWithCoverage {
+ OpenType::Offset mathItalicsCorrectionInfoOffset;
+ OpenType::Offset mathTopAccentAttachmentOffset;
+ OpenType::Offset extendedShapeCoverageOffset;
+ OpenType::Offset mathKernInfoOffset;
+
+ const MathItalicsCorrectionInfo* mathItalicsCorrectionInfo(const SharedBuffer& buffer) const
+ {
+ uint16_t offset = mathItalicsCorrectionInfoOffset;
+ if (offset)
+ return validateOffset<MathItalicsCorrectionInfo>(buffer, offset);
+ return nullptr;
+ }
+};
+
+struct GlyphAssembly : TableBase {
+ OpenType::MathValueRecord italicsCorrection;
+ OpenType::UInt16 partCount;
+ struct GlyphPartRecord {
+ OpenType::GlyphID glyph;
+ OpenType::UInt16 startConnectorLength;
+ OpenType::UInt16 endConnectorLength;
+ OpenType::UInt16 fullAdvance;
+ OpenType::UInt16 partFlags;
+ } partRecords[1]; // There are partCount GlyphPartRecord's.
+
+ // PartFlags enumeration currently uses only one bit:
+ // 0x0001 If set, the part can be skipped or repeated.
+ // 0xFFFE Reserved.
+ enum {
+ PartFlagsExtender = 0x01
+ };
+
+ void getAssemblyParts(const SharedBuffer& buffer, Vector<OpenTypeMathData::AssemblyPart>& assemblyParts) const
+ {
+ uint16_t count = partCount;
+ if (!isValidEnd(buffer, &partRecords[count]))
+ return;
+ assemblyParts.resize(count);
+ for (uint16_t i = 0; i < count; i++) {
+ assemblyParts[i].glyph = partRecords[i].glyph;
+ uint16_t flag = partRecords[i].partFlags;
+ assemblyParts[i].isExtender = flag & PartFlagsExtender;
+ }
+ }
+
+};
+
+struct MathGlyphConstruction : TableBase {
+ OpenType::Offset glyphAssemblyOffset;
+ OpenType::UInt16 variantCount;
+ struct MathGlyphVariantRecord {
+ OpenType::GlyphID variantGlyph;
+ OpenType::UInt16 advanceMeasurement;
+ } mathGlyphVariantRecords[1]; // There are variantCount MathGlyphVariantRecord's.
+
+ void getSizeVariants(const SharedBuffer& buffer, Vector<Glyph>& variants) const
+ {
+ uint16_t count = variantCount;
+ if (!isValidEnd(buffer, &mathGlyphVariantRecords[count]))
+ return;
+ variants.resize(count);
+ for (uint16_t i = 0; i < count; i++)
+ variants[i] = mathGlyphVariantRecords[i].variantGlyph;
+ }
+
+ void getAssemblyParts(const SharedBuffer& buffer, Vector<OpenTypeMathData::AssemblyPart>& assemblyParts) const
+ {
+ uint16_t offset = glyphAssemblyOffset;
+ const GlyphAssembly* glyphAssembly = validateOffset<GlyphAssembly>(buffer, offset);
+ if (glyphAssembly)
+ glyphAssembly->getAssemblyParts(buffer, assemblyParts);
+ }
+};
+
+struct MathVariants : TableWithCoverage {
+ OpenType::UInt16 minConnectorOverlap;
+ OpenType::Offset verticalGlyphCoverageOffset;
+ OpenType::Offset horizontalGlyphCoverageOffset;
+ OpenType::UInt16 verticalGlyphCount;
+ OpenType::UInt16 horizontalGlyphCount;
+ OpenType::Offset mathGlyphConstructionsOffset[1]; // There are verticalGlyphCount vertical glyph contructions and horizontalGlyphCount vertical glyph contructions.
+
+ const MathGlyphConstruction* mathGlyphConstruction(const SharedBuffer& buffer, Glyph glyph, bool isVertical) const
+ {
+ uint32_t count = uint16_t(verticalGlyphCount) + uint16_t(horizontalGlyphCount);
+ if (!isValidEnd(buffer, &mathGlyphConstructionsOffset[count]))
+ return nullptr;
+
+ // We determine the coverage table for the specified glyph.
+ uint16_t coverageOffset = isVertical ? verticalGlyphCoverageOffset : horizontalGlyphCoverageOffset;
+ if (!coverageOffset)
+ return nullptr;
+ const CoverageTable* coverage = validateOffset<CoverageTable>(buffer, coverageOffset);
+ if (!coverage)
+ return nullptr;
+
+ // We determine the index in the mathGlyphConstructionsOffset table.
+ uint32_t i;
+ if (!getCoverageIndex(buffer, coverage, glyph, i))
+ return nullptr;
+ count = isVertical ? verticalGlyphCount : horizontalGlyphCount;
+ if (i >= count)
+ return nullptr;
+ if (!isVertical)
+ i += uint16_t(verticalGlyphCount);
+
+ return validateOffset<MathGlyphConstruction>(buffer, mathGlyphConstructionsOffset[i]);
+ }
+};
+
+struct MATHTable : TableBase {
+ OpenType::Fixed version;
+ OpenType::Offset mathConstantsOffset;
+ OpenType::Offset mathGlyphInfoOffset;
+ OpenType::Offset mathVariantsOffset;
+
+ const MathConstants* mathConstants(const SharedBuffer& buffer) const
+ {
+ uint16_t offset = mathConstantsOffset;
+ if (offset)
+ return validateOffset<MathConstants>(buffer, offset);
+ return nullptr;
+ }
+
+ const MathGlyphInfo* mathGlyphInfo(const SharedBuffer& buffer) const
+ {
+ uint16_t offset = mathGlyphInfoOffset;
+ if (offset)
+ return validateOffset<MathGlyphInfo>(buffer, offset);
+ return nullptr;
+ }
+
+ const MathVariants* mathVariants(const SharedBuffer& buffer) const
+ {
+ uint16_t offset = mathVariantsOffset;
+ if (offset)
+ return validateOffset<MathVariants>(buffer, offset);
+ return nullptr;
+ }
+};
+
+#pragma pack()
+
+} // namespace OpenType
+#endif // ENABLE(OPENTYPE_MATH)
+
+#if ENABLE(OPENTYPE_MATH)
+OpenTypeMathData::OpenTypeMathData(const FontPlatformData& font)
+{
+ m_mathBuffer = font.openTypeTable(OpenType::MATHTag);
+ const OpenType::MATHTable* math = OpenType::validateTable<OpenType::MATHTable>(m_mathBuffer);
+ if (!math) {
+ m_mathBuffer = nullptr;
+ return;
+ }
+
+ const OpenType::MathConstants* mathConstants = math->mathConstants(*m_mathBuffer);
+ if (!mathConstants) {
+ m_mathBuffer = nullptr;
+ return;
+ }
+
+ const OpenType::MathVariants* mathVariants = math->mathVariants(*m_mathBuffer);
+ if (!mathVariants)
+ m_mathBuffer = nullptr;
+#elif USE(HARFBUZZ)
+OpenTypeMathData::OpenTypeMathData(const FontPlatformData& font)
+{
+ HarfBuzzFace* face = font.harfBuzzFace();
+ if (face) {
+ m_mathFont.reset(face->createFont());
+ if (!hb_ot_math_has_data(hb_font_get_face(m_mathFont.get())))
+ m_mathFont.release();
+ }
+#else
+OpenTypeMathData::OpenTypeMathData(const FontPlatformData&)
+{
+#endif
+}
+
+OpenTypeMathData::~OpenTypeMathData()
+{
+}
+
+#if ENABLE(OPENTYPE_MATH)
+float OpenTypeMathData::getMathConstant(const Font& font, MathConstant constant) const
+{
+ int32_t value = 0;
+
+ const OpenType::MATHTable* math = OpenType::validateTable<OpenType::MATHTable>(m_mathBuffer);
+ ASSERT(math);
+ const OpenType::MathConstants* mathConstants = math->mathConstants(*m_mathBuffer);
+ ASSERT(mathConstants);
+
+ if (constant >= 0 && constant <= ScriptScriptPercentScaleDown)
+ value = int16_t(mathConstants->intConstants[constant]);
+ else if (constant >= DelimitedSubFormulaMinHeight && constant <= DisplayOperatorMinHeight)
+ value = uint16_t(mathConstants->uIntConstants[constant - DelimitedSubFormulaMinHeight]);
+ else if (constant >= MathLeading && constant <= RadicalKernAfterDegree)
+ value = int16_t(mathConstants->mathValuesConstants[constant - MathLeading].value);
+ else if (constant == RadicalDegreeBottomRaisePercent)
+ value = uint16_t(mathConstants->radicalDegreeBottomRaisePercent);
+
+ if (constant == ScriptPercentScaleDown || constant == ScriptScriptPercentScaleDown || constant == RadicalDegreeBottomRaisePercent)
+ return value / 100.0;
+
+ return value * font.sizePerUnit();
+#elif USE(HARFBUZZ)
+float OpenTypeMathData::getMathConstant(const Font&, MathConstant constant) const
+{
+ hb_position_t value = hb_ot_math_get_constant(m_mathFont.get(), static_cast<hb_ot_math_constant_t>(constant));
+ if (constant == ScriptPercentScaleDown || constant == ScriptScriptPercentScaleDown || constant == RadicalDegreeBottomRaisePercent)
+ return value / 100.0;
+
+ return value / 65536.0;
+#else
+float OpenTypeMathData::getMathConstant(const Font&, MathConstant) const
+{
+ ASSERT_NOT_REACHED();
+ return 0;
+#endif
+}
+
+#if ENABLE(OPENTYPE_MATH)
+float OpenTypeMathData::getItalicCorrection(const Font& font, Glyph glyph) const
+{
+ const OpenType::MATHTable* math = OpenType::validateTable<OpenType::MATHTable>(m_mathBuffer);
+ ASSERT(math);
+ const OpenType::MathGlyphInfo* mathGlyphInfo = math->mathGlyphInfo(*m_mathBuffer);
+ if (!mathGlyphInfo)
+ return 0;
+
+ const OpenType::MathItalicsCorrectionInfo* mathItalicsCorrectionInfo = mathGlyphInfo->mathItalicsCorrectionInfo(*m_mathBuffer);
+ if (!mathItalicsCorrectionInfo)
+ return 0;
+
+ return mathItalicsCorrectionInfo->getItalicCorrection(*m_mathBuffer, glyph) * font.sizePerUnit();
+#elif USE(HARFBUZZ)
+float OpenTypeMathData::getItalicCorrection(const Font&, Glyph glyph) const
+{
+ return hb_ot_math_get_glyph_italics_correction(m_mathFont.get(), glyph) / 65536.0;
+#else
+float OpenTypeMathData::getItalicCorrection(const Font&, Glyph) const
+{
+ ASSERT_NOT_REACHED();
+ return 0;
+#endif
+}
+
+#if ENABLE(OPENTYPE_MATH)
+void OpenTypeMathData::getMathVariants(Glyph glyph, bool isVertical, Vector<Glyph>& sizeVariants, Vector<AssemblyPart>& assemblyParts) const
+{
+ sizeVariants.clear();
+ assemblyParts.clear();
+ const OpenType::MATHTable* math = OpenType::validateTable<OpenType::MATHTable>(m_mathBuffer);
+ ASSERT(math);
+ const OpenType::MathVariants* mathVariants = math->mathVariants(*m_mathBuffer);
+ ASSERT(mathVariants);
+
+ const OpenType::MathGlyphConstruction* mathGlyphConstruction = mathVariants->mathGlyphConstruction(*m_mathBuffer, glyph, isVertical);
+ if (!mathGlyphConstruction)
+ return;
+
+ mathGlyphConstruction->getSizeVariants(*m_mathBuffer, sizeVariants);
+ mathGlyphConstruction->getAssemblyParts(*m_mathBuffer, assemblyParts);
+#elif USE(HARFBUZZ)
+void OpenTypeMathData::getMathVariants(Glyph glyph, bool isVertical, Vector<Glyph>& sizeVariants, Vector<AssemblyPart>& assemblyParts) const
+{
+ hb_direction_t direction = isVertical ? HB_DIRECTION_BTT : HB_DIRECTION_LTR;
+
+ sizeVariants.clear();
+ hb_ot_math_glyph_variant_t variants[10];
+ unsigned variantsSize = WTF_ARRAY_LENGTH(variants);
+ unsigned count;
+ unsigned offset = 0;
+ do {
+ count = variantsSize;
+ hb_ot_math_get_glyph_variants(m_mathFont.get(), glyph, direction, offset, &count, variants);
+ offset += count;
+ for (unsigned i = 0; i < count; i++)
+ sizeVariants.append(variants[i].glyph);
+ } while (count == variantsSize);
+
+ assemblyParts.clear();
+ hb_ot_math_glyph_part_t parts[10];
+ unsigned partsSize = WTF_ARRAY_LENGTH(parts);
+ offset = 0;
+ do {
+ count = partsSize;
+ hb_ot_math_get_glyph_assembly(m_mathFont.get(), glyph, direction, offset, &count, parts, nullptr);
+ offset += count;
+ for (unsigned i = 0; i < count; i++) {
+ AssemblyPart assemblyPart;
+ assemblyPart.glyph = parts[i].glyph;
+ assemblyPart.isExtender = parts[i].flags & HB_MATH_GLYPH_PART_FLAG_EXTENDER;
+ assemblyParts.append(assemblyPart);
+ }
+ } while (count == partsSize);
+#else
+void OpenTypeMathData::getMathVariants(Glyph, bool, Vector<Glyph>&, Vector<AssemblyPart>&) const
+{
+ ASSERT_NOT_REACHED();
+#endif
+}
+
+} // namespace WebCore
diff --git a/Source/WebCore/platform/graphics/opentype/OpenTypeMathData.h b/Source/WebCore/platform/graphics/opentype/OpenTypeMathData.h
new file mode 100644
index 000000000..97d754d02
--- /dev/null
+++ b/Source/WebCore/platform/graphics/opentype/OpenTypeMathData.h
@@ -0,0 +1,146 @@
+/*
+ * Copyright (C) 2014 Frederic Wang (fred.wang@free.fr). All rights reserved.
+ * Copyright (C) 2016 Igalia S.L. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include "Glyph.h"
+#include <wtf/PassRefPtr.h>
+#include <wtf/RefCounted.h>
+#include <wtf/Vector.h>
+
+#if !ENABLE(OPENTYPE_MATH) && USE(HARFBUZZ)
+#include <hb-ot.h>
+#endif
+
+namespace WebCore {
+
+class FontPlatformData;
+class SharedBuffer;
+class Font;
+
+class OpenTypeMathData : public RefCounted<OpenTypeMathData> {
+public:
+ static PassRefPtr<OpenTypeMathData> create(const FontPlatformData& font)
+ {
+ return adoptRef(new OpenTypeMathData(font));
+ }
+ ~OpenTypeMathData();
+
+#if ENABLE(OPENTYPE_MATH)
+ bool hasMathData() const { return m_mathBuffer; }
+#elif USE(HARFBUZZ)
+ bool hasMathData() const { return m_mathFont.get(); }
+#else
+ bool hasMathData() const { return false; }
+#endif
+
+ // These constants are defined in the MATH table.
+ // The implementation of OpenTypeMathData::getMathConstant assumes that they correspond to the indices of the MathContant table.
+ enum MathConstant {
+ ScriptPercentScaleDown,
+ ScriptScriptPercentScaleDown,
+ DelimitedSubFormulaMinHeight,
+ DisplayOperatorMinHeight,
+ MathLeading,
+ AxisHeight,
+ AccentBaseHeight,
+ FlattenedAccentBaseHeight,
+ SubscriptShiftDown,
+ SubscriptTopMax,
+ SubscriptBaselineDropMin,
+ SuperscriptShiftUp,
+ SuperscriptShiftUpCramped,
+ SuperscriptBottomMin,
+ SuperscriptBaselineDropMax,
+ SubSuperscriptGapMin,
+ SuperscriptBottomMaxWithSubscript,
+ SpaceAfterScript,
+ UpperLimitGapMin,
+ UpperLimitBaselineRiseMin,
+ LowerLimitGapMin,
+ LowerLimitBaselineDropMin,
+ StackTopShiftUp,
+ StackTopDisplayStyleShiftUp,
+ StackBottomShiftDown,
+ StackBottomDisplayStyleShiftDown,
+ StackGapMin,
+ StackDisplayStyleGapMin,
+ StretchStackTopShiftUp,
+ StretchStackBottomShiftDown,
+ StretchStackGapAboveMin,
+ StretchStackGapBelowMin,
+ FractionNumeratorShiftUp,
+ FractionNumeratorDisplayStyleShiftUp,
+ FractionDenominatorShiftDown,
+ FractionDenominatorDisplayStyleShiftDown,
+ FractionNumeratorGapMin,
+ FractionNumDisplayStyleGapMin,
+ FractionRuleThickness,
+ FractionDenominatorGapMin,
+ FractionDenomDisplayStyleGapMin,
+ SkewedFractionHorizontalGap,
+ SkewedFractionVerticalGap,
+ OverbarVerticalGap,
+ OverbarRuleThickness,
+ OverbarExtraAscender,
+ UnderbarVerticalGap,
+ UnderbarRuleThickness,
+ UnderbarExtraDescender,
+ RadicalVerticalGap,
+ RadicalDisplayStyleVerticalGap,
+ RadicalRuleThickness,
+ RadicalExtraAscender,
+ RadicalKernBeforeDegree,
+ RadicalKernAfterDegree,
+ RadicalDegreeBottomRaisePercent
+ };
+
+ struct AssemblyPart {
+ Glyph glyph;
+ bool isExtender;
+ };
+
+ float getMathConstant(const Font&, MathConstant) const;
+ float getItalicCorrection(const Font&, Glyph) const;
+ void getMathVariants(Glyph, bool isVertical, Vector<Glyph>& sizeVariants, Vector<AssemblyPart>& assemblyParts) const;
+
+private:
+ explicit OpenTypeMathData(const FontPlatformData&);
+
+#if ENABLE(OPENTYPE_MATH)
+ RefPtr<SharedBuffer> m_mathBuffer;
+#elif USE(HARFBUZZ)
+ struct HbFontDeleter {
+ void operator()(hb_font_t* font)
+ {
+ hb_font_destroy(font);
+ }
+ };
+ std::unique_ptr<hb_font_t, HbFontDeleter> m_mathFont;
+#endif
+};
+
+} // namespace WebCore
diff --git a/Source/WebCore/platform/graphics/opentype/OpenTypeTypes.h b/Source/WebCore/platform/graphics/opentype/OpenTypeTypes.h
index da9cea96c..e89fb5998 100644
--- a/Source/WebCore/platform/graphics/opentype/OpenTypeTypes.h
+++ b/Source/WebCore/platform/graphics/opentype/OpenTypeTypes.h
@@ -25,6 +25,10 @@
#ifndef OpenTypeTypes_h
#define OpenTypeTypes_h
+#if ENABLE(OPENTYPE_MATH)
+#include "Glyph.h"
+#endif
+
#include "SharedBuffer.h"
namespace WebCore {
@@ -100,6 +104,87 @@ protected:
}
};
+#if ENABLE(OPENTYPE_VERTICAL) || ENABLE(OPENTYPE_MATH)
+struct CoverageTable : TableBase {
+ OpenType::UInt16 coverageFormat;
+};
+
+struct Coverage1Table : CoverageTable {
+ OpenType::UInt16 glyphCount;
+ OpenType::GlyphID glyphArray[1];
+};
+
+struct Coverage2Table : CoverageTable {
+ OpenType::UInt16 rangeCount;
+ struct RangeRecord {
+ OpenType::GlyphID start;
+ OpenType::GlyphID end;
+ OpenType::UInt16 startCoverageIndex;
+ } ranges[1];
+};
+#endif // ENABLE(OPENTYPE_VERTICAL) || ENABLE(OPENTYPE_MATH)
+
+#if ENABLE(OPENTYPE_MATH)
+struct TableWithCoverage : TableBase {
+protected:
+ bool getCoverageIndex(const SharedBuffer& buffer, const CoverageTable* coverage, Glyph glyph, uint32_t& coverageIndex) const
+ {
+ switch (coverage->coverageFormat) {
+ case 1: { // Coverage Format 1
+ const Coverage1Table* coverage1 = validatePtr<Coverage1Table>(buffer, coverage);
+ if (!coverage1)
+ return false;
+ uint16_t glyphCount = coverage1->glyphCount;
+ if (!isValidEnd(buffer, &coverage1->glyphArray[glyphCount]))
+ return false;
+
+ // We do a binary search on the glyph indexes.
+ uint32_t imin = 0, imax = glyphCount;
+ while (imin < imax) {
+ uint32_t imid = (imin + imax) >> 1;
+ uint16_t glyphMid = coverage1->glyphArray[imid];
+ if (glyphMid == glyph) {
+ coverageIndex = imid;
+ return true;
+ }
+ if (glyphMid < glyph)
+ imin = imid + 1;
+ else
+ imax = imid;
+ }
+ break;
+ }
+ case 2: { // Coverage Format 2
+ const Coverage2Table* coverage2 = validatePtr<Coverage2Table>(buffer, coverage);
+ if (!coverage2)
+ return false;
+ uint16_t rangeCount = coverage2->rangeCount;
+ if (!isValidEnd(buffer, &coverage2->ranges[rangeCount]))
+ return false;
+
+ // We do a binary search on the ranges.
+ uint32_t imin = 0, imax = rangeCount;
+ while (imin < imax) {
+ uint32_t imid = (imin + imax) >> 1;
+ uint16_t rStart = coverage2->ranges[imid].start;
+ uint16_t rEnd = coverage2->ranges[imid].end;
+ if (rEnd < glyph)
+ imin = imid + 1;
+ else if (glyph < rStart)
+ imax = imid;
+ else {
+ coverageIndex = coverage2->ranges[imid].startCoverageIndex + glyph - rStart;
+ return true;
+ }
+ }
+ break;
+ }
+ }
+ return false;
+ }
+};
+#endif
+
} // namespace OpenType
} // namespace WebCore
#endif // OpenTypeTypes_h
diff --git a/Source/WebCore/platform/graphics/opentype/OpenTypeUtilities.cpp b/Source/WebCore/platform/graphics/opentype/OpenTypeUtilities.cpp
new file mode 100644
index 000000000..f7d0ac8c3
--- /dev/null
+++ b/Source/WebCore/platform/graphics/opentype/OpenTypeUtilities.cpp
@@ -0,0 +1,429 @@
+/*
+ * Copyright (C) 2008, 2009 Apple Inc. All rights reserved.
+ * Copyright (C) 2009 Torch Mobile, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "OpenTypeUtilities.h"
+
+#include "SharedBuffer.h"
+
+namespace WebCore {
+
+struct BigEndianUShort {
+ operator unsigned short() const { return (v & 0x00ff) << 8 | v >> 8; }
+ BigEndianUShort(unsigned short u) : v((u & 0x00ff) << 8 | u >> 8) { }
+ unsigned short v;
+};
+
+struct BigEndianULong {
+ operator unsigned() const { return (v & 0xff) << 24 | (v & 0xff00) << 8 | (v & 0xff0000) >> 8 | v >> 24; }
+ BigEndianULong(unsigned u) : v((u & 0xff) << 24 | (u & 0xff00) << 8 | (u & 0xff0000) >> 8 | u >> 24) { }
+ unsigned v;
+};
+
+#pragma pack(1)
+
+struct EOTPrefix {
+ unsigned eotSize;
+ unsigned fontDataSize;
+ unsigned version;
+ unsigned flags;
+ uint8_t fontPANOSE[10];
+ uint8_t charset;
+ uint8_t italic;
+ unsigned weight;
+ unsigned short fsType;
+ unsigned short magicNumber;
+ unsigned unicodeRange[4];
+ unsigned codePageRange[2];
+ unsigned checkSumAdjustment;
+ unsigned reserved[4];
+ unsigned short padding1;
+};
+
+struct TableDirectoryEntry {
+ BigEndianULong tag;
+ BigEndianULong checkSum;
+ BigEndianULong offset;
+ BigEndianULong length;
+};
+
+#if !USE(CG) || !defined(COREGRAPHICS_INCLUDES_CORESERVICES_HEADER)
+// Fixed type is not defined on non-CG and Windows platforms. |version| in sfntHeader
+// and headTable and |fontRevision| in headTable are of Fixed, but they're
+// not actually refered to anywhere. Therefore, we just have to match
+// the size (4 bytes). For the definition of Fixed type, see
+// http://developer.apple.com/documentation/mac/Legacy/GXEnvironment/GXEnvironment-356.html#HEADING356-6.
+typedef int32_t Fixed;
+#endif
+
+struct sfntHeader {
+ Fixed version;
+ BigEndianUShort numTables;
+ BigEndianUShort searchRange;
+ BigEndianUShort entrySelector;
+ BigEndianUShort rangeShift;
+ TableDirectoryEntry tables[1];
+};
+
+struct OS2Table {
+ BigEndianUShort version;
+ BigEndianUShort avgCharWidth;
+ BigEndianUShort weightClass;
+ BigEndianUShort widthClass;
+ BigEndianUShort fsType;
+ BigEndianUShort subscriptXSize;
+ BigEndianUShort subscriptYSize;
+ BigEndianUShort subscriptXOffset;
+ BigEndianUShort subscriptYOffset;
+ BigEndianUShort superscriptXSize;
+ BigEndianUShort superscriptYSize;
+ BigEndianUShort superscriptXOffset;
+ BigEndianUShort superscriptYOffset;
+ BigEndianUShort strikeoutSize;
+ BigEndianUShort strikeoutPosition;
+ BigEndianUShort familyClass;
+ uint8_t panose[10];
+ BigEndianULong unicodeRange[4];
+ uint8_t vendID[4];
+ BigEndianUShort fsSelection;
+ BigEndianUShort firstCharIndex;
+ BigEndianUShort lastCharIndex;
+ BigEndianUShort typoAscender;
+ BigEndianUShort typoDescender;
+ BigEndianUShort typoLineGap;
+ BigEndianUShort winAscent;
+ BigEndianUShort winDescent;
+ BigEndianULong codePageRange[2];
+ BigEndianUShort xHeight;
+ BigEndianUShort capHeight;
+ BigEndianUShort defaultChar;
+ BigEndianUShort breakChar;
+ BigEndianUShort maxContext;
+};
+
+struct headTable {
+ Fixed version;
+ Fixed fontRevision;
+ BigEndianULong checkSumAdjustment;
+ BigEndianULong magicNumber;
+ BigEndianUShort flags;
+ BigEndianUShort unitsPerEm;
+ long long created;
+ long long modified;
+ BigEndianUShort xMin;
+ BigEndianUShort xMax;
+ BigEndianUShort yMin;
+ BigEndianUShort yMax;
+ BigEndianUShort macStyle;
+ BigEndianUShort lowestRectPPEM;
+ BigEndianUShort fontDirectionHint;
+ BigEndianUShort indexToLocFormat;
+ BigEndianUShort glyphDataFormat;
+};
+
+struct nameRecord {
+ BigEndianUShort platformID;
+ BigEndianUShort encodingID;
+ BigEndianUShort languageID;
+ BigEndianUShort nameID;
+ BigEndianUShort length;
+ BigEndianUShort offset;
+};
+
+struct nameTable {
+ BigEndianUShort format;
+ BigEndianUShort count;
+ BigEndianUShort stringOffset;
+ nameRecord nameRecords[1];
+};
+
+#pragma pack()
+
+EOTHeader::EOTHeader()
+{
+ m_buffer.resize(sizeof(EOTPrefix));
+}
+
+void EOTHeader::updateEOTSize(size_t fontDataSize)
+{
+ prefix()->eotSize = m_buffer.size() + fontDataSize;
+}
+
+void EOTHeader::appendBigEndianString(const BigEndianUShort* string, unsigned short length)
+{
+ size_t oldSize = m_buffer.size();
+ m_buffer.resize(oldSize + length + 2 * sizeof(unsigned short));
+ UChar* dst = reinterpret_cast<UChar*>(m_buffer.data() + oldSize);
+ unsigned i = 0;
+ dst[i++] = length;
+ unsigned numCharacters = length / 2;
+ for (unsigned j = 0; j < numCharacters; j++)
+ dst[i++] = string[j];
+ dst[i] = 0;
+}
+
+void EOTHeader::appendPaddingShort()
+{
+ unsigned short padding = 0;
+ m_buffer.append(reinterpret_cast<uint8_t*>(&padding), sizeof(padding));
+}
+
+bool getEOTHeader(SharedBuffer* fontData, EOTHeader& eotHeader, size_t& overlayDst, size_t& overlaySrc, size_t& overlayLength)
+{
+ overlayDst = 0;
+ overlaySrc = 0;
+ overlayLength = 0;
+
+ size_t dataLength = fontData->size();
+ const char* data = fontData->data();
+
+ EOTPrefix* prefix = eotHeader.prefix();
+
+ prefix->fontDataSize = dataLength;
+ prefix->version = 0x00020001;
+ prefix->flags = 0;
+
+ if (dataLength < offsetof(sfntHeader, tables))
+ return false;
+
+ const sfntHeader* sfnt = reinterpret_cast<const sfntHeader*>(data);
+
+ if (dataLength < offsetof(sfntHeader, tables) + sfnt->numTables * sizeof(TableDirectoryEntry))
+ return false;
+
+ bool haveOS2 = false;
+ bool haveHead = false;
+ bool haveName = false;
+
+ const BigEndianUShort* familyName = 0;
+ unsigned short familyNameLength = 0;
+ const BigEndianUShort* subfamilyName = 0;
+ unsigned short subfamilyNameLength = 0;
+ const BigEndianUShort* fullName = 0;
+ unsigned short fullNameLength = 0;
+ const BigEndianUShort* versionString = 0;
+ unsigned short versionStringLength = 0;
+
+ for (unsigned i = 0; i < sfnt->numTables; i++) {
+ unsigned tableOffset = sfnt->tables[i].offset;
+ unsigned tableLength = sfnt->tables[i].length;
+
+ if (dataLength < tableOffset || dataLength < tableLength || dataLength < tableOffset + tableLength)
+ return false;
+
+ unsigned tableTag = sfnt->tables[i].tag;
+ switch (tableTag) {
+ case 'OS/2':
+ {
+ if (dataLength < tableOffset + sizeof(OS2Table))
+ return false;
+
+ haveOS2 = true;
+ const OS2Table* OS2 = reinterpret_cast<const OS2Table*>(data + tableOffset);
+ for (unsigned j = 0; j < 10; j++)
+ prefix->fontPANOSE[j] = OS2->panose[j];
+ prefix->italic = OS2->fsSelection & 0x01;
+ prefix->weight = OS2->weightClass;
+ // FIXME: Should use OS2->fsType, but some TrueType fonts set it to an over-restrictive value.
+ // Since ATS does not enforce this on Mac OS X, we do not enforce it either.
+ prefix->fsType = 0;
+ for (unsigned j = 0; j < 4; j++)
+ prefix->unicodeRange[j] = OS2->unicodeRange[j];
+ for (unsigned j = 0; j < 2; j++)
+ prefix->codePageRange[j] = OS2->codePageRange[j];
+ break;
+ }
+ case 'head':
+ {
+ if (dataLength < tableOffset + sizeof(headTable))
+ return false;
+
+ haveHead = true;
+ const headTable* head = reinterpret_cast<const headTable*>(data + tableOffset);
+ prefix->checkSumAdjustment = head->checkSumAdjustment;
+ break;
+ }
+ case 'name':
+ {
+ if (dataLength < tableOffset + offsetof(nameTable, nameRecords))
+ return false;
+
+ haveName = true;
+ const nameTable* name = reinterpret_cast<const nameTable*>(data + tableOffset);
+ for (int j = 0; j < name->count; j++) {
+ if (dataLength < tableOffset + offsetof(nameTable, nameRecords) + (j + 1) * sizeof(nameRecord))
+ return false;
+ if (name->nameRecords[j].platformID == 3 && name->nameRecords[j].encodingID == 1 && name->nameRecords[j].languageID == 0x0409) {
+ if (dataLength < tableOffset + name->stringOffset + name->nameRecords[j].offset + name->nameRecords[j].length)
+ return false;
+
+ unsigned short nameLength = name->nameRecords[j].length;
+ const BigEndianUShort* nameString = reinterpret_cast<const BigEndianUShort*>(data + tableOffset + name->stringOffset + name->nameRecords[j].offset);
+
+ switch (name->nameRecords[j].nameID) {
+ case 1:
+ familyNameLength = nameLength;
+ familyName = nameString;
+ break;
+ case 2:
+ subfamilyNameLength = nameLength;
+ subfamilyName = nameString;
+ break;
+ case 4:
+ fullNameLength = nameLength;
+ fullName = nameString;
+ break;
+ case 5:
+ versionStringLength = nameLength;
+ versionString = nameString;
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ if (haveOS2 && haveHead && haveName)
+ break;
+ }
+
+ prefix->charset = DEFAULT_CHARSET;
+ prefix->magicNumber = 0x504c;
+ prefix->reserved[0] = 0;
+ prefix->reserved[1] = 0;
+ prefix->reserved[2] = 0;
+ prefix->reserved[3] = 0;
+ prefix->padding1 = 0;
+
+ eotHeader.appendBigEndianString(familyName, familyNameLength);
+ eotHeader.appendBigEndianString(subfamilyName, subfamilyNameLength);
+ eotHeader.appendBigEndianString(versionString, versionStringLength);
+
+ // If possible, ensure that the family name is a prefix of the full name.
+ if (fullNameLength >= familyNameLength && memcmp(familyName, fullName, familyNameLength)) {
+ overlaySrc = reinterpret_cast<const char*>(fullName) - data;
+ overlayDst = reinterpret_cast<const char*>(familyName) - data;
+ overlayLength = familyNameLength;
+ }
+ eotHeader.appendBigEndianString(fullName, fullNameLength);
+
+ eotHeader.appendPaddingShort();
+ eotHeader.updateEOTSize(fontData->size());
+
+ return true;
+}
+
+// adds fontName to the font table in fontData, and writes the new font table to rewrittenFontTable
+// returns the size of the name table (which is used by renameAndActivateFont), or 0 on early abort
+bool renameFont(const SharedBuffer& fontData, const String& fontName, Vector<char>& rewrittenFontData)
+{
+ size_t originalDataSize = fontData.size();
+ const sfntHeader* sfnt = reinterpret_cast<const sfntHeader*>(fontData.data());
+
+ // Abort if the data is too small to be a font header with a "tables" entry.
+ if (originalDataSize < offsetof(sfntHeader, tables))
+ return false;
+
+ // Abort if the data is too small to hold all the tables specified in the header.
+ if (originalDataSize < offsetof(sfntHeader, tables) + sfnt->numTables * sizeof(TableDirectoryEntry))
+ return false;
+
+ unsigned t;
+ for (t = 0; t < sfnt->numTables; ++t) {
+ if (sfnt->tables[t].tag == 'name')
+ break;
+ }
+ if (t == sfnt->numTables)
+ return false;
+
+ const int nameRecordCount = 5;
+
+ // Rounded up to a multiple of 4 to simplify the checksum calculation.
+ size_t nameTableSize = ((offsetof(nameTable, nameRecords) + nameRecordCount * sizeof(nameRecord) + fontName.length() * sizeof(UChar)) & ~3) + 4;
+
+ rewrittenFontData.resize(fontData.size() + nameTableSize);
+ char* data = rewrittenFontData.data();
+ memcpy(data, fontData.data(), originalDataSize);
+
+ // Make the table directory entry point to the new 'name' table.
+ sfntHeader* rewrittenSfnt = reinterpret_cast<sfntHeader*>(data);
+ rewrittenSfnt->tables[t].length = nameTableSize;
+ rewrittenSfnt->tables[t].offset = originalDataSize;
+
+ // Write the new 'name' table after the original font data.
+ nameTable* name = reinterpret_cast<nameTable*>(data + originalDataSize);
+ name->format = 0;
+ name->count = nameRecordCount;
+ name->stringOffset = offsetof(nameTable, nameRecords) + nameRecordCount * sizeof(nameRecord);
+ for (unsigned i = 0; i < nameRecordCount; ++i) {
+ name->nameRecords[i].platformID = 3;
+ name->nameRecords[i].encodingID = 1;
+ name->nameRecords[i].languageID = 0x0409;
+ name->nameRecords[i].offset = 0;
+ name->nameRecords[i].length = fontName.length() * sizeof(UChar);
+ }
+
+ // The required 'name' record types: Family, Style, Unique, Full and PostScript.
+ name->nameRecords[0].nameID = 1;
+ name->nameRecords[1].nameID = 2;
+ name->nameRecords[2].nameID = 3;
+ name->nameRecords[3].nameID = 4;
+ name->nameRecords[4].nameID = 6;
+
+ for (unsigned i = 0; i < fontName.length(); ++i)
+ reinterpret_cast<BigEndianUShort*>(data + originalDataSize + name->stringOffset)[i] = fontName[i];
+
+ // Update the table checksum in the directory entry.
+ rewrittenSfnt->tables[t].checkSum = 0;
+ for (unsigned i = 0; i * sizeof(BigEndianULong) < nameTableSize; ++i)
+ rewrittenSfnt->tables[t].checkSum = rewrittenSfnt->tables[t].checkSum + reinterpret_cast<BigEndianULong*>(name)[i];
+
+ return true;
+}
+
+// Rename the font and install the new font data into the system
+HANDLE renameAndActivateFont(const SharedBuffer& fontData, const String& fontName)
+{
+ Vector<char> rewrittenFontData;
+ if (!renameFont(fontData, fontName, rewrittenFontData))
+ return 0;
+
+ DWORD numFonts = 0;
+ HANDLE fontHandle = AddFontMemResourceEx(rewrittenFontData.data(), rewrittenFontData.size(), 0, &numFonts);
+
+ if (fontHandle && numFonts < 1) {
+ RemoveFontMemResourceEx(fontHandle);
+ return 0;
+ }
+
+ return fontHandle;
+}
+
+}
diff --git a/Source/WebCore/platform/graphics/win/DIBPixelData.h b/Source/WebCore/platform/graphics/opentype/OpenTypeUtilities.h
index fa2a6c0ba..832a4a85d 100644
--- a/Source/WebCore/platform/graphics/win/DIBPixelData.h
+++ b/Source/WebCore/platform/graphics/opentype/OpenTypeUtilities.h
@@ -1,5 +1,6 @@
/*
- * Copyright (C) 2011 Brent Fulgham <bfulgham@webkit.org>
+ * Copyright (C) 2008, 2009 Apple Inc. All rights reserved.
+ * Copyright (C) 2009 Torch Mobile, Inc.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -20,56 +21,41 @@
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef DIBPixelData_h
-#define DIBPixelData_h
+#ifndef OpenTypeUtilities_h
+#define OpenTypeUtilities_h
-#include "IntRect.h"
-#include "IntSize.h"
#include <windows.h>
-
-#if USE(CG)
-#include <CoreFoundation/CFBase.h>
-#else
-typedef unsigned char UInt8;
-#endif
+#include <wtf/Forward.h>
+#include <wtf/text/WTFString.h>
namespace WebCore {
-class DIBPixelData {
- public:
- DIBPixelData()
- : m_bitmapBuffer(0)
- , m_bitmapBufferLength(0)
- , m_bytesPerRow(0)
- , m_bitsPerPixel(0)
- {
- }
- DIBPixelData(HBITMAP);
+struct BigEndianUShort;
+struct EOTPrefix;
+class SharedBuffer;
- void initialize(HBITMAP);
+struct EOTHeader {
+ EOTHeader();
-#ifndef NDEBUG
- void writeToFile(LPCWSTR);
-#endif
+ size_t size() const { return m_buffer.size(); }
+ const uint8_t* data() const { return m_buffer.data(); }
- UInt8* buffer() const { return m_bitmapBuffer; }
- unsigned bufferLength() const { return m_bitmapBufferLength; }
- const IntSize& size() const { return m_size; }
- unsigned bytesPerRow() const { return m_bytesPerRow; }
- unsigned short bitsPerPixel() const { return m_bitsPerPixel; }
- static void setRGBABitmapAlpha(HDC, const IntRect&, unsigned char);
+ EOTPrefix* prefix() { return reinterpret_cast<EOTPrefix*>(m_buffer.data()); }
+ void updateEOTSize(size_t);
+ void appendBigEndianString(const BigEndianUShort*, unsigned short length);
+ void appendPaddingShort();
- private:
- UInt8* m_bitmapBuffer;
- unsigned m_bitmapBufferLength;
- IntSize m_size;
- unsigned m_bytesPerRow;
- unsigned short m_bitsPerPixel;
+private:
+ Vector<uint8_t, 512> m_buffer;
};
+bool getEOTHeader(SharedBuffer* fontData, EOTHeader& eotHeader, size_t& overlayDst, size_t& overlaySrc, size_t& overlayLength);
+bool renameFont(const SharedBuffer&, const String&, Vector<char>&);
+HANDLE renameAndActivateFont(const SharedBuffer&, const String&);
+
} // namespace WebCore
-#endif // DIBPixelData_h
+#endif // OpenTypeUtilities_h
diff --git a/Source/WebCore/platform/graphics/opentype/OpenTypeVerticalData.cpp b/Source/WebCore/platform/graphics/opentype/OpenTypeVerticalData.cpp
index 35ae71dd7..f2e6812a2 100644
--- a/Source/WebCore/platform/graphics/opentype/OpenTypeVerticalData.cpp
+++ b/Source/WebCore/platform/graphics/opentype/OpenTypeVerticalData.cpp
@@ -27,10 +27,10 @@
#include "OpenTypeVerticalData.h"
#include "FloatRect.h"
+#include "Font.h"
#include "GlyphPage.h"
#include "OpenTypeTypes.h"
#include "SharedBuffer.h"
-#include "SimpleFontData.h"
#include <wtf/RefPtr.h>
using namespace std;
@@ -112,24 +112,6 @@ struct VORGTable {
size_t requiredSize() const { return sizeof(*this) + sizeof(VertOriginYMetrics) * (numVertOriginYMetrics - 1); }
};
-struct CoverageTable : TableBase {
- OpenType::UInt16 coverageFormat;
-};
-
-struct Coverage1Table : CoverageTable {
- OpenType::UInt16 glyphCount;
- OpenType::GlyphID glyphArray[1];
-};
-
-struct Coverage2Table : CoverageTable {
- OpenType::UInt16 rangeCount;
- struct RangeRecord {
- OpenType::GlyphID start;
- OpenType::GlyphID end;
- OpenType::UInt16 startCoverageIndex;
- } ranges[1];
-};
-
struct SubstitutionSubTable : TableBase {
OpenType::UInt16 substFormat;
OpenType::Offset coverageOffset;
@@ -397,39 +379,52 @@ struct GSUBTable : TableBase {
} // namespace OpenType
-OpenTypeVerticalData::OpenTypeVerticalData(const FontPlatformData& platformData)
- : m_defaultVertOriginY(0)
+static bool loadHmtxTable(const FontPlatformData& platformData, Vector<uint16_t>& advanceWidths)
{
- loadMetrics(platformData);
- loadVerticalGlyphSubstitutions(platformData);
-}
-
-void OpenTypeVerticalData::loadMetrics(const FontPlatformData& platformData)
-{
- // Load hhea and hmtx to get x-component of vertical origins.
- // If these tables are missing, it's not an OpenType font.
RefPtr<SharedBuffer> buffer = platformData.openTypeTable(OpenType::HheaTag);
const OpenType::HheaTable* hhea = OpenType::validateTable<OpenType::HheaTable>(buffer);
if (!hhea)
- return;
+ return false;
uint16_t countHmtxEntries = hhea->numberOfHMetrics;
if (!countHmtxEntries) {
LOG_ERROR("Invalid numberOfHMetrics");
- return;
+ return false;
}
buffer = platformData.openTypeTable(OpenType::HmtxTag);
const OpenType::HmtxTable* hmtx = OpenType::validateTable<OpenType::HmtxTable>(buffer, countHmtxEntries);
if (!hmtx) {
LOG_ERROR("hhea exists but hmtx does not (or broken)");
- return;
+ return false;
}
- m_advanceWidths.resize(countHmtxEntries);
+
+ advanceWidths.resize(countHmtxEntries);
for (uint16_t i = 0; i < countHmtxEntries; ++i)
- m_advanceWidths[i] = hmtx->entries[i].advanceWidth;
+ advanceWidths[i] = hmtx->entries[i].advanceWidth;
+ return true;
+}
+
+RefPtr<OpenTypeVerticalData> OpenTypeVerticalData::create(const FontPlatformData& platformData)
+{
+ // Load hhea and hmtx to get x-component of vertical origins. If these tables are missing, it's not an OpenType font.
+ Vector<uint16_t> advanceWidths;
+ if (!loadHmtxTable(platformData, advanceWidths))
+ return nullptr;
+
+ return adoptRef(new OpenTypeVerticalData(platformData, WTFMove(advanceWidths)));
+}
+OpenTypeVerticalData::OpenTypeVerticalData(const FontPlatformData& platformData, Vector<uint16_t>&& advanceWidths)
+ : m_advanceWidths(WTFMove(advanceWidths))
+{
+ loadMetrics(platformData);
+ loadVerticalGlyphSubstitutions(platformData);
+}
+
+void OpenTypeVerticalData::loadMetrics(const FontPlatformData& platformData)
+{
// Load vhea first. This table is required for fonts that support vertical flow.
- buffer = platformData.openTypeTable(OpenType::VheaTag);
+ RefPtr<SharedBuffer> buffer = platformData.openTypeTable(OpenType::VheaTag);
const OpenType::VheaTable* vhea = OpenType::validateTable<OpenType::VheaTable>(buffer);
if (!vhea)
return;
@@ -497,7 +492,7 @@ void OpenTypeVerticalData::loadVerticalGlyphSubstitutions(const FontPlatformData
gsub->getVerticalGlyphSubstitutions(&m_verticalGlyphMap, *buffer.get());
}
-float OpenTypeVerticalData::advanceHeight(const SimpleFontData* font, Glyph glyph) const
+float OpenTypeVerticalData::advanceHeight(const Font* font, Glyph glyph) const
{
size_t countHeights = m_advanceHeights.size();
if (countHeights) {
@@ -510,7 +505,7 @@ float OpenTypeVerticalData::advanceHeight(const SimpleFontData* font, Glyph glyp
return font->fontMetrics().height();
}
-void OpenTypeVerticalData::getVerticalTranslationsForGlyphs(const SimpleFontData* font, const Glyph* glyphs, size_t count, float* outXYArray) const
+void OpenTypeVerticalData::getVerticalTranslationsForGlyphs(const Font* font, const Glyph* glyphs, size_t count, float* outXYArray) const
{
size_t countWidths = m_advanceWidths.size();
ASSERT(countWidths > 0);
@@ -553,19 +548,19 @@ void OpenTypeVerticalData::getVerticalTranslationsForGlyphs(const SimpleFontData
}
}
-void OpenTypeVerticalData::substituteWithVerticalGlyphs(const SimpleFontData* font, GlyphPage* glyphPage, unsigned offset, unsigned length) const
+void OpenTypeVerticalData::substituteWithVerticalGlyphs(const Font* font, GlyphPage* glyphPage) const
{
const HashMap<Glyph, Glyph>& map = m_verticalGlyphMap;
if (map.isEmpty())
return;
- for (unsigned index = offset, end = offset + length; index < end; ++index) {
- Glyph glyph = glyphPage->glyphAt(index);
+ for (unsigned index = 0; index < GlyphPage::size; ++index) {
+ Glyph glyph = glyphPage->glyphForIndex(index);
if (glyph) {
- ASSERT(glyphPage->glyphDataForIndex(index).fontData == font);
+ ASSERT_UNUSED(font, &glyphPage->font() == font);
Glyph to = map.get(glyph);
if (to)
- glyphPage->setGlyphDataForIndex(index, to, font);
+ glyphPage->setGlyphForIndex(index, to);
}
}
}
diff --git a/Source/WebCore/platform/graphics/opentype/OpenTypeVerticalData.h b/Source/WebCore/platform/graphics/opentype/OpenTypeVerticalData.h
index 26bc04d5c..e98aebb50 100644
--- a/Source/WebCore/platform/graphics/opentype/OpenTypeVerticalData.h
+++ b/Source/WebCore/platform/graphics/opentype/OpenTypeVerticalData.h
@@ -29,31 +29,26 @@
#include "Glyph.h"
#include <wtf/HashMap.h>
-#include <wtf/PassRefPtr.h>
#include <wtf/RefCounted.h>
#include <wtf/Vector.h>
namespace WebCore {
+class Font;
class FontPlatformData;
class GlyphPage;
-class SimpleFontData;
class OpenTypeVerticalData : public RefCounted<OpenTypeVerticalData> {
public:
- static PassRefPtr<OpenTypeVerticalData> create(const FontPlatformData& platformData)
- {
- return adoptRef(new OpenTypeVerticalData(platformData));
- }
+ static RefPtr<OpenTypeVerticalData> create(const FontPlatformData&);
- bool isOpenType() const { return !m_advanceWidths.isEmpty(); }
bool hasVerticalMetrics() const { return !m_advanceHeights.isEmpty(); }
- float advanceHeight(const SimpleFontData*, Glyph) const;
- void getVerticalTranslationsForGlyphs(const SimpleFontData*, const Glyph*, size_t, float* outXYArray) const;
- void substituteWithVerticalGlyphs(const SimpleFontData*, GlyphPage*, unsigned offset, unsigned length) const;
+ float advanceHeight(const Font*, Glyph) const;
+ void getVerticalTranslationsForGlyphs(const Font*, const Glyph*, size_t, float* outXYArray) const;
+ void substituteWithVerticalGlyphs(const Font*, GlyphPage*) const;
private:
- explicit OpenTypeVerticalData(const FontPlatformData&);
+ explicit OpenTypeVerticalData(const FontPlatformData&, Vector<uint16_t>&& advanceWidths);
void loadMetrics(const FontPlatformData&);
void loadVerticalGlyphSubstitutions(const FontPlatformData&);
@@ -63,11 +58,8 @@ private:
Vector<uint16_t> m_advanceWidths;
Vector<uint16_t> m_advanceHeights;
Vector<int16_t> m_topSideBearings;
- int16_t m_defaultVertOriginY;
+ int16_t m_defaultVertOriginY { 0 };
HashMap<Glyph, int16_t> m_vertOriginY;
-
- friend class FontCache;
- bool m_inFontCache; // for mark & sweep in FontCache::purgeInactiveFontData()
};
} // namespace WebCore
diff --git a/Source/WebCore/platform/graphics/texmap/BitmapTexture.cpp b/Source/WebCore/platform/graphics/texmap/BitmapTexture.cpp
new file mode 100644
index 000000000..487077be9
--- /dev/null
+++ b/Source/WebCore/platform/graphics/texmap/BitmapTexture.cpp
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies)
+ * Copyright (C) 2014 Igalia S.L.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "BitmapTexture.h"
+
+#include "GraphicsLayer.h"
+#include "ImageBuffer.h"
+#include "TextureMapper.h"
+
+namespace WebCore {
+
+void BitmapTexture::updateContents(TextureMapper&, GraphicsLayer* sourceLayer, const IntRect& targetRect, const IntPoint& offset, UpdateContentsFlag updateContentsFlag, float scale)
+{
+ // Making an unconditionally unaccelerated buffer here is OK because this code
+ // isn't used by any platforms that respect the accelerated bit.
+ std::unique_ptr<ImageBuffer> imageBuffer = ImageBuffer::create(targetRect.size(), Unaccelerated);
+
+ if (!imageBuffer)
+ return;
+
+ GraphicsContext& context = imageBuffer->context();
+ context.setImageInterpolationQuality(InterpolationDefault);
+ context.setTextDrawingMode(TextModeFill);
+
+ IntRect sourceRect(targetRect);
+ sourceRect.setLocation(offset);
+ sourceRect.scale(1 / scale);
+ context.applyDeviceScaleFactor(scale);
+ context.translate(-sourceRect.x(), -sourceRect.y());
+
+ sourceLayer->paintGraphicsLayerContents(context, sourceRect);
+
+ RefPtr<Image> image = imageBuffer->copyImage(DontCopyBackingStore);
+ if (!image)
+ return;
+
+ updateContents(image.get(), targetRect, IntPoint(), updateContentsFlag);
+}
+
+} // namespace
diff --git a/Source/WebCore/platform/graphics/texmap/BitmapTexture.h b/Source/WebCore/platform/graphics/texmap/BitmapTexture.h
new file mode 100644
index 000000000..3245638a8
--- /dev/null
+++ b/Source/WebCore/platform/graphics/texmap/BitmapTexture.h
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies)
+ * Copyright (C) 2014 Igalia S.L.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef BitmapTexture_h
+#define BitmapTexture_h
+
+#include "IntPoint.h"
+#include "IntRect.h"
+#include "IntSize.h"
+#include <wtf/PassRefPtr.h>
+#include <wtf/RefCounted.h>
+
+namespace WebCore {
+
+class FilterOperations;
+class GraphicsLayer;
+class Image;
+class TextureMapper;
+
+// A 2D texture that can be the target of software or GL rendering.
+class BitmapTexture : public RefCounted<BitmapTexture> {
+public:
+ enum Flag {
+ NoFlag = 0,
+ SupportsAlpha = 0x01,
+ FBOAttachment = 0x02
+ };
+
+ enum UpdateContentsFlag {
+ UpdateCanModifyOriginalImageData,
+ UpdateCannotModifyOriginalImageData
+ };
+
+ typedef unsigned Flags;
+
+ BitmapTexture()
+ : m_flags(0)
+ {
+ }
+
+ virtual ~BitmapTexture() { }
+ virtual bool isBackedByOpenGL() const { return false; }
+
+ virtual IntSize size() const = 0;
+ virtual void updateContents(Image*, const IntRect&, const IntPoint& offset, UpdateContentsFlag) = 0;
+ virtual void updateContents(TextureMapper&, GraphicsLayer*, const IntRect& target, const IntPoint& offset, UpdateContentsFlag, float scale = 1);
+ virtual void updateContents(const void*, const IntRect& target, const IntPoint& offset, int bytesPerLine, UpdateContentsFlag) = 0;
+ virtual bool isValid() const = 0;
+ inline Flags flags() const { return m_flags; }
+
+ virtual int bpp() const { return 32; }
+ void reset(const IntSize& size, Flags flags = 0)
+ {
+ m_flags = flags;
+ m_contentSize = size;
+ didReset();
+ }
+ virtual void didReset() { }
+
+ inline IntSize contentSize() const { return m_contentSize; }
+ inline int numberOfBytes() const { return size().width() * size().height() * bpp() >> 3; }
+ inline bool isOpaque() const { return !(m_flags & SupportsAlpha); }
+
+ virtual PassRefPtr<BitmapTexture> applyFilters(TextureMapper&, const FilterOperations&) { return this; }
+
+protected:
+ IntSize m_contentSize;
+
+private:
+ Flags m_flags;
+};
+
+}
+
+#endif // BitmapTexture_h
diff --git a/Source/WebCore/platform/graphics/texmap/BitmapTextureGL.cpp b/Source/WebCore/platform/graphics/texmap/BitmapTextureGL.cpp
new file mode 100644
index 000000000..589060224
--- /dev/null
+++ b/Source/WebCore/platform/graphics/texmap/BitmapTextureGL.cpp
@@ -0,0 +1,347 @@
+/*
+ Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies)
+ Copyright (C) 2012 Igalia S.L.
+ Copyright (C) 2012 Adobe Systems Incorporated
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+ */
+
+#include "config.h"
+#include "BitmapTextureGL.h"
+
+#if USE(TEXTURE_MAPPER_GL)
+
+#include "Extensions3D.h"
+#include "FilterOperations.h"
+#include "GraphicsContext.h"
+#include "Image.h"
+#include "LengthFunctions.h"
+#include "NotImplemented.h"
+#include "TextureMapperShaderProgram.h"
+#include "Timer.h"
+#include <wtf/HashMap.h>
+#include <wtf/PassRefPtr.h>
+#include <wtf/RefCounted.h>
+
+#if USE(CAIRO)
+#include "CairoUtilities.h"
+#include "RefPtrCairo.h"
+#include <cairo.h>
+#include <wtf/text/CString.h>
+#endif
+
+#if OS(DARWIN)
+#define GL_UNSIGNED_INT_8_8_8_8_REV 0x8367
+#endif
+
+namespace WebCore {
+
+BitmapTextureGL* toBitmapTextureGL(BitmapTexture* texture)
+{
+ if (!texture || !texture->isBackedByOpenGL())
+ return 0;
+
+ return static_cast<BitmapTextureGL*>(texture);
+}
+
+BitmapTextureGL::BitmapTextureGL(RefPtr<GraphicsContext3D>&& context3D, const Flags flags)
+ : m_context3D(WTFMove(context3D))
+{
+ if (flags & FBOAttachment)
+ m_internalFormat = m_format = GraphicsContext3D::RGBA;
+ else {
+ // If GL_EXT_texture_format_BGRA8888 is supported in the OpenGLES
+ // internal and external formats need to be BGRA
+ m_internalFormat = GraphicsContext3D::RGBA;
+ m_format = GraphicsContext3D::BGRA;
+ if (m_context3D->isGLES2Compliant()) {
+ if (m_context3D->getExtensions().supports("GL_EXT_texture_format_BGRA8888"))
+ m_internalFormat = GraphicsContext3D::BGRA;
+ else
+ m_format = GraphicsContext3D::RGBA;
+ }
+ }
+}
+
+static void swizzleBGRAToRGBA(uint32_t* data, const IntRect& rect, int stride = 0)
+{
+ stride = stride ? stride : rect.width();
+ for (int y = rect.y(); y < rect.maxY(); ++y) {
+ uint32_t* p = data + y * stride;
+ for (int x = rect.x(); x < rect.maxX(); ++x)
+ p[x] = ((p[x] << 16) & 0xff0000) | ((p[x] >> 16) & 0xff) | (p[x] & 0xff00ff00);
+ }
+}
+
+static bool driverSupportsSubImage(GraphicsContext3D* context)
+{
+ if (context->isGLES2Compliant()) {
+ static bool supportsSubImage = context->getExtensions().supports("GL_EXT_unpack_subimage");
+ return supportsSubImage;
+ }
+
+ return true;
+}
+
+void BitmapTextureGL::didReset()
+{
+ if (!m_id)
+ m_id = m_context3D->createTexture();
+
+ m_shouldClear = true;
+ if (m_textureSize == contentSize())
+ return;
+
+ m_textureSize = contentSize();
+ m_context3D->bindTexture(GraphicsContext3D::TEXTURE_2D, m_id);
+ m_context3D->texParameteri(GraphicsContext3D::TEXTURE_2D, GraphicsContext3D::TEXTURE_MIN_FILTER, GraphicsContext3D::LINEAR);
+ m_context3D->texParameteri(GraphicsContext3D::TEXTURE_2D, GraphicsContext3D::TEXTURE_MAG_FILTER, GraphicsContext3D::LINEAR);
+ m_context3D->texParameteri(GraphicsContext3D::TEXTURE_2D, GraphicsContext3D::TEXTURE_WRAP_S, GraphicsContext3D::CLAMP_TO_EDGE);
+ m_context3D->texParameteri(GraphicsContext3D::TEXTURE_2D, GraphicsContext3D::TEXTURE_WRAP_T, GraphicsContext3D::CLAMP_TO_EDGE);
+
+ m_context3D->texImage2DDirect(GraphicsContext3D::TEXTURE_2D, 0, m_internalFormat, m_textureSize.width(), m_textureSize.height(), 0, m_format, m_type, 0);
+}
+
+void BitmapTextureGL::updateContentsNoSwizzle(const void* srcData, const IntRect& targetRect, const IntPoint& sourceOffset, int bytesPerLine, unsigned bytesPerPixel, Platform3DObject glFormat)
+{
+ m_context3D->bindTexture(GraphicsContext3D::TEXTURE_2D, m_id);
+ // For ES drivers that don't support sub-images.
+ if (driverSupportsSubImage(m_context3D.get())) {
+ // Use the OpenGL sub-image extension, now that we know it's available.
+ m_context3D->pixelStorei(GraphicsContext3D::UNPACK_ROW_LENGTH, bytesPerLine / bytesPerPixel);
+ m_context3D->pixelStorei(GraphicsContext3D::UNPACK_SKIP_ROWS, sourceOffset.y());
+ m_context3D->pixelStorei(GraphicsContext3D::UNPACK_SKIP_PIXELS, sourceOffset.x());
+ }
+
+ m_context3D->texSubImage2D(GraphicsContext3D::TEXTURE_2D, 0, targetRect.x(), targetRect.y(), targetRect.width(), targetRect.height(), glFormat, m_type, srcData);
+
+ // For ES drivers that don't support sub-images.
+ if (driverSupportsSubImage(m_context3D.get())) {
+ m_context3D->pixelStorei(GraphicsContext3D::UNPACK_ROW_LENGTH, 0);
+ m_context3D->pixelStorei(GraphicsContext3D::UNPACK_SKIP_ROWS, 0);
+ m_context3D->pixelStorei(GraphicsContext3D::UNPACK_SKIP_PIXELS, 0);
+ }
+}
+
+void BitmapTextureGL::updateContents(const void* srcData, const IntRect& targetRect, const IntPoint& sourceOffset, int bytesPerLine, UpdateContentsFlag updateContentsFlag)
+{
+ m_context3D->bindTexture(GraphicsContext3D::TEXTURE_2D, m_id);
+
+ const unsigned bytesPerPixel = 4;
+ char* data = reinterpret_cast<char*>(const_cast<void*>(srcData));
+ Vector<char> temporaryData;
+ IntPoint adjustedSourceOffset = sourceOffset;
+
+ // Texture upload requires subimage buffer if driver doesn't support subimage and we don't have full image upload.
+ bool requireSubImageBuffer = !driverSupportsSubImage(m_context3D.get())
+ && !(bytesPerLine == static_cast<int>(targetRect.width() * bytesPerPixel) && adjustedSourceOffset == IntPoint::zero());
+
+ // prepare temporaryData if necessary
+ if ((m_format == GraphicsContext3D::RGBA && updateContentsFlag == UpdateCannotModifyOriginalImageData) || requireSubImageBuffer) {
+ temporaryData.resize(targetRect.width() * targetRect.height() * bytesPerPixel);
+ data = temporaryData.data();
+ const char* bits = static_cast<const char*>(srcData);
+ const char* src = bits + sourceOffset.y() * bytesPerLine + sourceOffset.x() * bytesPerPixel;
+ char* dst = data;
+ const int targetBytesPerLine = targetRect.width() * bytesPerPixel;
+ for (int y = 0; y < targetRect.height(); ++y) {
+ memcpy(dst, src, targetBytesPerLine);
+ src += bytesPerLine;
+ dst += targetBytesPerLine;
+ }
+
+ bytesPerLine = targetBytesPerLine;
+ adjustedSourceOffset = IntPoint(0, 0);
+ }
+
+ if (m_format == GraphicsContext3D::RGBA)
+ swizzleBGRAToRGBA(reinterpret_cast_ptr<uint32_t*>(data), IntRect(adjustedSourceOffset, targetRect.size()), bytesPerLine / bytesPerPixel);
+
+ updateContentsNoSwizzle(data, targetRect, adjustedSourceOffset, bytesPerLine, bytesPerPixel, m_format);
+}
+
+void BitmapTextureGL::updateContents(Image* image, const IntRect& targetRect, const IntPoint& offset, UpdateContentsFlag updateContentsFlag)
+{
+ if (!image)
+ return;
+ NativeImagePtr frameImage = image->nativeImageForCurrentFrame();
+ if (!frameImage)
+ return;
+
+ int bytesPerLine;
+ const char* imageData;
+
+#if USE(CAIRO)
+ cairo_surface_t* surface = frameImage.get();
+ imageData = reinterpret_cast<const char*>(cairo_image_surface_get_data(surface));
+ bytesPerLine = cairo_image_surface_get_stride(surface);
+#endif
+
+ updateContents(imageData, targetRect, offset, bytesPerLine, updateContentsFlag);
+}
+
+static unsigned getPassesRequiredForFilter(FilterOperation::OperationType type)
+{
+ switch (type) {
+ case FilterOperation::GRAYSCALE:
+ case FilterOperation::SEPIA:
+ case FilterOperation::SATURATE:
+ case FilterOperation::HUE_ROTATE:
+ case FilterOperation::INVERT:
+ case FilterOperation::BRIGHTNESS:
+ case FilterOperation::CONTRAST:
+ case FilterOperation::OPACITY:
+ return 1;
+ case FilterOperation::BLUR:
+ case FilterOperation::DROP_SHADOW:
+ // We use two-passes (vertical+horizontal) for blur and drop-shadow.
+ return 2;
+ default:
+ return 0;
+ }
+}
+
+PassRefPtr<BitmapTexture> BitmapTextureGL::applyFilters(TextureMapper& textureMapper, const FilterOperations& filters)
+{
+ if (filters.isEmpty())
+ return this;
+
+ TextureMapperGL& texmapGL = static_cast<TextureMapperGL&>(textureMapper);
+ RefPtr<BitmapTexture> previousSurface = texmapGL.currentSurface();
+ RefPtr<BitmapTexture> resultSurface = this;
+ RefPtr<BitmapTexture> intermediateSurface;
+ RefPtr<BitmapTexture> spareSurface;
+
+ m_filterInfo = FilterInfo();
+
+ for (size_t i = 0; i < filters.size(); ++i) {
+ RefPtr<FilterOperation> filter = filters.operations()[i];
+ ASSERT(filter);
+
+ int numPasses = getPassesRequiredForFilter(filter->type());
+ for (int j = 0; j < numPasses; ++j) {
+ bool last = (i == filters.size() - 1) && (j == numPasses - 1);
+ if (!last) {
+ if (!intermediateSurface)
+ intermediateSurface = texmapGL.acquireTextureFromPool(contentSize(), BitmapTexture::SupportsAlpha | BitmapTexture::FBOAttachment);
+ texmapGL.bindSurface(intermediateSurface.get());
+ }
+
+ if (last) {
+ toBitmapTextureGL(resultSurface.get())->m_filterInfo = BitmapTextureGL::FilterInfo(filter, j, spareSurface);
+ break;
+ }
+
+ texmapGL.drawFiltered(*resultSurface.get(), spareSurface.get(), *filter, j);
+ if (!j && filter->type() == FilterOperation::DROP_SHADOW) {
+ spareSurface = resultSurface;
+ resultSurface = nullptr;
+ }
+ std::swap(resultSurface, intermediateSurface);
+ }
+ }
+
+ texmapGL.bindSurface(previousSurface.get());
+ return resultSurface;
+}
+
+void BitmapTextureGL::initializeStencil()
+{
+ if (m_rbo)
+ return;
+
+ m_rbo = m_context3D->createRenderbuffer();
+ m_context3D->bindRenderbuffer(GraphicsContext3D::RENDERBUFFER, m_rbo);
+ m_context3D->renderbufferStorage(GraphicsContext3D::RENDERBUFFER, GraphicsContext3D::STENCIL_INDEX8, m_textureSize.width(), m_textureSize.height());
+ m_context3D->bindRenderbuffer(GraphicsContext3D::RENDERBUFFER, 0);
+ m_context3D->framebufferRenderbuffer(GraphicsContext3D::FRAMEBUFFER, GraphicsContext3D::STENCIL_ATTACHMENT, GraphicsContext3D::RENDERBUFFER, m_rbo);
+ m_context3D->clearStencil(0);
+ m_context3D->clear(GraphicsContext3D::STENCIL_BUFFER_BIT);
+}
+
+void BitmapTextureGL::initializeDepthBuffer()
+{
+ if (m_depthBufferObject)
+ return;
+
+ m_depthBufferObject = m_context3D->createRenderbuffer();
+ m_context3D->bindRenderbuffer(GraphicsContext3D::RENDERBUFFER, m_depthBufferObject);
+ m_context3D->renderbufferStorage(GraphicsContext3D::RENDERBUFFER, GraphicsContext3D::DEPTH_COMPONENT16, m_textureSize.width(), m_textureSize.height());
+ m_context3D->bindRenderbuffer(GraphicsContext3D::RENDERBUFFER, 0);
+ m_context3D->framebufferRenderbuffer(GraphicsContext3D::FRAMEBUFFER, GraphicsContext3D::DEPTH_ATTACHMENT, GraphicsContext3D::RENDERBUFFER, m_depthBufferObject);
+}
+
+void BitmapTextureGL::clearIfNeeded()
+{
+ if (!m_shouldClear)
+ return;
+
+ m_clipStack.reset(IntRect(IntPoint::zero(), m_textureSize), ClipStack::YAxisMode::Default);
+ m_clipStack.applyIfNeeded(*m_context3D);
+ m_context3D->clearColor(0, 0, 0, 0);
+ m_context3D->clear(GraphicsContext3D::COLOR_BUFFER_BIT);
+ m_shouldClear = false;
+}
+
+void BitmapTextureGL::createFboIfNeeded()
+{
+ if (m_fbo)
+ return;
+
+ m_fbo = m_context3D->createFramebuffer();
+ m_context3D->bindFramebuffer(GraphicsContext3D::FRAMEBUFFER, m_fbo);
+ m_context3D->framebufferTexture2D(GraphicsContext3D::FRAMEBUFFER, GraphicsContext3D::COLOR_ATTACHMENT0, GraphicsContext3D::TEXTURE_2D, id(), 0);
+ m_shouldClear = true;
+}
+
+void BitmapTextureGL::bindAsSurface(GraphicsContext3D* context3D)
+{
+ context3D->bindTexture(GraphicsContext3D::TEXTURE_2D, 0);
+ createFboIfNeeded();
+ context3D->bindFramebuffer(GraphicsContext3D::FRAMEBUFFER, m_fbo);
+ context3D->viewport(0, 0, m_textureSize.width(), m_textureSize.height());
+ clearIfNeeded();
+ m_clipStack.apply(*m_context3D);
+}
+
+BitmapTextureGL::~BitmapTextureGL()
+{
+ if (m_id)
+ m_context3D->deleteTexture(m_id);
+
+ if (m_fbo)
+ m_context3D->deleteFramebuffer(m_fbo);
+
+ if (m_rbo)
+ m_context3D->deleteRenderbuffer(m_rbo);
+
+ if (m_depthBufferObject)
+ m_context3D->deleteRenderbuffer(m_depthBufferObject);
+}
+
+bool BitmapTextureGL::isValid() const
+{
+ return m_id;
+}
+
+IntSize BitmapTextureGL::size() const
+{
+ return m_textureSize;
+}
+
+}; // namespace WebCore
+
+#endif // USE(TEXTURE_MAPPER_GL)
diff --git a/Source/WebCore/platform/graphics/texmap/BitmapTextureGL.h b/Source/WebCore/platform/graphics/texmap/BitmapTextureGL.h
new file mode 100644
index 000000000..c4a80224a
--- /dev/null
+++ b/Source/WebCore/platform/graphics/texmap/BitmapTextureGL.h
@@ -0,0 +1,114 @@
+/*
+ Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies)
+ Copyright (C) 2014 Igalia S.L.
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+ */
+
+#ifndef BitmapTextureGL_h
+#define BitmapTextureGL_h
+
+#if USE(TEXTURE_MAPPER_GL)
+
+#include "BitmapTexture.h"
+#include "ClipStack.h"
+#include "FilterOperation.h"
+#include "GraphicsContext3D.h"
+#include "IntSize.h"
+#include "TextureMapperGL.h"
+
+namespace WebCore {
+
+class TextureMapper;
+class TextureMapperGL;
+class FilterOperation;
+
+class BitmapTextureGL : public BitmapTexture {
+public:
+ static Ref<BitmapTexture> create(Ref<GraphicsContext3D>&& context3D, const Flags flags = NoFlag)
+ {
+ return adoptRef(*new BitmapTextureGL(WTFMove(context3D), flags));
+ }
+
+ virtual ~BitmapTextureGL();
+
+ IntSize size() const override;
+ bool isValid() const override;
+ void didReset() override;
+ void bindAsSurface(GraphicsContext3D*);
+ void initializeStencil();
+ void initializeDepthBuffer();
+ virtual uint32_t id() const { return m_id; }
+ uint32_t textureTarget() const { return GraphicsContext3D::TEXTURE_2D; }
+ IntSize textureSize() const { return m_textureSize; }
+ void updateContents(Image*, const IntRect&, const IntPoint&, UpdateContentsFlag) override;
+ void updateContents(const void*, const IntRect& target, const IntPoint& sourceOffset, int bytesPerLine, UpdateContentsFlag) override;
+ void updateContentsNoSwizzle(const void*, const IntRect& target, const IntPoint& sourceOffset, int bytesPerLine, unsigned bytesPerPixel = 4, Platform3DObject glFormat = GraphicsContext3D::RGBA);
+ bool isBackedByOpenGL() const override { return true; }
+
+ PassRefPtr<BitmapTexture> applyFilters(TextureMapper&, const FilterOperations&) override;
+ struct FilterInfo {
+ RefPtr<FilterOperation> filter;
+ unsigned pass;
+ RefPtr<BitmapTexture> contentTexture;
+
+ FilterInfo(PassRefPtr<FilterOperation> f = 0, unsigned p = 0, PassRefPtr<BitmapTexture> t = 0)
+ : filter(f)
+ , pass(p)
+ , contentTexture(t)
+ { }
+ };
+ const FilterInfo* filterInfo() const { return &m_filterInfo; }
+ ClipStack& clipStack() { return m_clipStack; }
+
+ GC3Dint internalFormat() const { return m_internalFormat; }
+
+private:
+ BitmapTextureGL(RefPtr<GraphicsContext3D>&&, const Flags);
+
+ Platform3DObject m_id { 0 };
+ IntSize m_textureSize;
+ IntRect m_dirtyRect;
+ Platform3DObject m_fbo { 0 };
+ Platform3DObject m_rbo { 0 };
+ Platform3DObject m_depthBufferObject { 0 };
+ bool m_shouldClear { true };
+ ClipStack m_clipStack;
+ RefPtr<GraphicsContext3D> m_context3D;
+
+ void clearIfNeeded();
+ void createFboIfNeeded();
+
+ FilterInfo m_filterInfo;
+
+ GC3Dint m_internalFormat;
+ GC3Denum m_format;
+ GC3Denum m_type {
+#if OS(DARWIN)
+ GL_UNSIGNED_INT_8_8_8_8_REV
+#else
+ GraphicsContext3D::UNSIGNED_BYTE
+#endif
+ };
+};
+
+BitmapTextureGL* toBitmapTextureGL(BitmapTexture*);
+
+}
+
+#endif // USE(TEXTURE_MAPPER_GL)
+
+#endif // BitmapTextureGL_h
diff --git a/Source/WebCore/platform/graphics/texmap/BitmapTexturePool.cpp b/Source/WebCore/platform/graphics/texmap/BitmapTexturePool.cpp
new file mode 100644
index 000000000..00cc8ee36
--- /dev/null
+++ b/Source/WebCore/platform/graphics/texmap/BitmapTexturePool.cpp
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies)
+ * Copyright (C) 2015 Igalia S.L.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "BitmapTexturePool.h"
+
+#if USE(TEXTURE_MAPPER_GL)
+#include "BitmapTextureGL.h"
+#endif
+
+namespace WebCore {
+
+static const double s_releaseUnusedSecondsTolerance = 3;
+static const double s_releaseUnusedTexturesTimerInterval = 0.5;
+
+#if USE(TEXTURE_MAPPER_GL)
+BitmapTexturePool::BitmapTexturePool(RefPtr<GraphicsContext3D>&& context3D)
+ : m_context3D(WTFMove(context3D))
+ , m_releaseUnusedTexturesTimer(*this, &BitmapTexturePool::releaseUnusedTexturesTimerFired)
+{
+}
+#endif
+
+RefPtr<BitmapTexture> BitmapTexturePool::acquireTexture(const IntSize& size, const BitmapTexture::Flags flags)
+{
+ Vector<Entry>& list = flags & BitmapTexture::FBOAttachment ? m_attachmentTextures : m_textures;
+
+ Entry* selectedEntry = std::find_if(list.begin(), list.end(),
+ [&size](Entry& entry) { return entry.m_texture->refCount() == 1 && entry.m_texture->size() == size; });
+
+ if (selectedEntry == list.end()) {
+ list.append(Entry(createTexture(flags)));
+ selectedEntry = &list.last();
+ }
+
+ scheduleReleaseUnusedTextures();
+ selectedEntry->markIsInUse();
+ return selectedEntry->m_texture.copyRef();
+}
+
+void BitmapTexturePool::scheduleReleaseUnusedTextures()
+{
+ if (m_releaseUnusedTexturesTimer.isActive())
+ return;
+
+ m_releaseUnusedTexturesTimer.startOneShot(s_releaseUnusedTexturesTimerInterval);
+}
+
+void BitmapTexturePool::releaseUnusedTexturesTimerFired()
+{
+ // Delete entries, which have been unused in s_releaseUnusedSecondsTolerance.
+ double minUsedTime = monotonicallyIncreasingTime() - s_releaseUnusedSecondsTolerance;
+
+ if (!m_textures.isEmpty()) {
+ std::sort(m_textures.begin(), m_textures.end(),
+ [](const Entry& a, const Entry& b) { return a.m_lastUsedTime > b.m_lastUsedTime; });
+
+ for (size_t i = 0; i < m_textures.size(); ++i) {
+ if (m_textures[i].m_lastUsedTime < minUsedTime) {
+ m_textures.remove(i, m_textures.size() - i);
+ break;
+ }
+ }
+ }
+
+ if (!m_attachmentTextures.isEmpty()) {
+ std::sort(m_attachmentTextures.begin(), m_attachmentTextures.end(),
+ [](const Entry& a, const Entry& b) { return a.m_lastUsedTime > b.m_lastUsedTime; });
+
+ for (size_t i = 0; i < m_attachmentTextures.size(); ++i) {
+ if (m_attachmentTextures[i].m_lastUsedTime < minUsedTime) {
+ m_attachmentTextures.remove(i, m_attachmentTextures.size() - i);
+ break;
+ }
+ }
+ }
+
+ if (!m_textures.isEmpty() || !m_attachmentTextures.isEmpty())
+ scheduleReleaseUnusedTextures();
+}
+
+RefPtr<BitmapTexture> BitmapTexturePool::createTexture(const BitmapTexture::Flags flags)
+{
+#if USE(TEXTURE_MAPPER_GL)
+ return BitmapTextureGL::create(*m_context3D, flags);
+#else
+ return nullptr;
+#endif
+}
+
+} // namespace WebCore
diff --git a/Source/WebCore/platform/graphics/texmap/BitmapTexturePool.h b/Source/WebCore/platform/graphics/texmap/BitmapTexturePool.h
new file mode 100644
index 000000000..5c740fc10
--- /dev/null
+++ b/Source/WebCore/platform/graphics/texmap/BitmapTexturePool.h
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies)
+ * Copyright (C) 2014 Igalia S.L.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef BitmapTexturePool_h
+#define BitmapTexturePool_h
+
+#include "BitmapTexture.h"
+#include "Timer.h"
+#include <wtf/CurrentTime.h>
+
+#if USE(TEXTURE_MAPPER_GL)
+#include "GraphicsContext3D.h"
+#endif
+
+namespace WebCore {
+
+class GraphicsContext3D;
+class IntSize;
+
+class BitmapTexturePool {
+ WTF_MAKE_NONCOPYABLE(BitmapTexturePool);
+ WTF_MAKE_FAST_ALLOCATED;
+public:
+#if USE(TEXTURE_MAPPER_GL)
+ explicit BitmapTexturePool(RefPtr<GraphicsContext3D>&&);
+#endif
+
+ RefPtr<BitmapTexture> acquireTexture(const IntSize&, const BitmapTexture::Flags);
+
+private:
+ struct Entry {
+ explicit Entry(RefPtr<BitmapTexture>&& texture)
+ : m_texture(WTFMove(texture))
+ { }
+
+ void markIsInUse() { m_lastUsedTime = monotonicallyIncreasingTime(); }
+
+ RefPtr<BitmapTexture> m_texture;
+ double m_lastUsedTime { 0.0 };
+ };
+
+ void scheduleReleaseUnusedTextures();
+ void releaseUnusedTexturesTimerFired();
+ RefPtr<BitmapTexture> createTexture(const BitmapTexture::Flags);
+
+#if USE(TEXTURE_MAPPER_GL)
+ RefPtr<GraphicsContext3D> m_context3D;
+#endif
+
+ Vector<Entry> m_textures;
+ Vector<Entry> m_attachmentTextures;
+ Timer m_releaseUnusedTexturesTimer;
+};
+
+} // namespace WebCore
+
+#endif // BitmapTexturePool_h
diff --git a/Source/WebCore/platform/graphics/texmap/ClipStack.cpp b/Source/WebCore/platform/graphics/texmap/ClipStack.cpp
new file mode 100644
index 000000000..11a00bc8a
--- /dev/null
+++ b/Source/WebCore/platform/graphics/texmap/ClipStack.cpp
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies)
+ * Copyright (C) 2012 Adobe Systems Incorporated
+ * Copyright (C) 2012, 2016 Igalia S.L.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include "config.h"
+#include "ClipStack.h"
+
+#include "GraphicsContext3D.h"
+
+namespace WebCore {
+
+void ClipStack::push()
+{
+ clipStack.append(clipState);
+ clipStateDirty = true;
+}
+
+void ClipStack::pop()
+{
+ if (clipStack.isEmpty())
+ return;
+ clipState = clipStack.last();
+ clipStack.removeLast();
+ clipStateDirty = true;
+}
+
+void ClipStack::reset(const IntRect& rect, ClipStack::YAxisMode mode)
+{
+ clipStack.clear();
+ size = rect.size();
+ yAxisMode = mode;
+ clipState = State(rect);
+ clipStateDirty = true;
+}
+
+void ClipStack::intersect(const IntRect& rect)
+{
+ clipState.scissorBox.intersect(rect);
+ clipStateDirty = true;
+}
+
+void ClipStack::setStencilIndex(int stencilIndex)
+{
+ clipState.stencilIndex = stencilIndex;
+ clipStateDirty = true;
+}
+
+void ClipStack::apply(GraphicsContext3D& context)
+{
+ if (clipState.scissorBox.isEmpty())
+ return;
+
+ context.scissor(clipState.scissorBox.x(),
+ (yAxisMode == YAxisMode::Inverted) ? size.height() - clipState.scissorBox.maxY() : clipState.scissorBox.y(),
+ clipState.scissorBox.width(), clipState.scissorBox.height());
+ context.stencilOp(GraphicsContext3D::KEEP, GraphicsContext3D::KEEP, GraphicsContext3D::KEEP);
+ context.stencilFunc(GraphicsContext3D::EQUAL, clipState.stencilIndex - 1, clipState.stencilIndex - 1);
+ if (clipState.stencilIndex == 1)
+ context.disable(GraphicsContext3D::STENCIL_TEST);
+ else
+ context.enable(GraphicsContext3D::STENCIL_TEST);
+}
+
+void ClipStack::applyIfNeeded(GraphicsContext3D& context)
+{
+ if (!clipStateDirty)
+ return;
+
+ clipStateDirty = false;
+ apply(context);
+}
+
+} // namespace WebCore
diff --git a/Source/WebCore/platform/graphics/texmap/ClipStack.h b/Source/WebCore/platform/graphics/texmap/ClipStack.h
new file mode 100644
index 000000000..2fc40475b
--- /dev/null
+++ b/Source/WebCore/platform/graphics/texmap/ClipStack.h
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies)
+ * Copyright (C) 2015, 2016 Igalia S.L.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifndef ClipStack_h
+#define ClipStack_h
+
+#include "IntRect.h"
+#include "IntSize.h"
+#include <wtf/Vector.h>
+
+namespace WebCore {
+
+class GraphicsContext3D;
+
+class ClipStack {
+public:
+ struct State {
+ State(const IntRect& scissors = IntRect(), int stencil = 1)
+ : scissorBox(scissors)
+ , stencilIndex(stencil)
+ { }
+
+ IntRect scissorBox;
+ int stencilIndex;
+ };
+
+ // Y-axis should be inverted only when painting into the window.
+ enum class YAxisMode {
+ Default,
+ Inverted,
+ };
+
+ void push();
+ void pop();
+ State& current() { return clipState; }
+
+ void reset(const IntRect&, YAxisMode);
+ void intersect(const IntRect&);
+ void setStencilIndex(int);
+ int getStencilIndex() const { return clipState.stencilIndex; }
+
+ void apply(GraphicsContext3D&);
+ void applyIfNeeded(GraphicsContext3D&);
+
+ bool isCurrentScissorBoxEmpty() const { return clipState.scissorBox.isEmpty(); }
+
+private:
+ Vector<State> clipStack;
+ State clipState;
+ IntSize size;
+ bool clipStateDirty { false };
+ YAxisMode yAxisMode { YAxisMode::Default };
+};
+
+} // namespace WebCore
+
+#endif // ClipStack_h
diff --git a/Source/WebCore/platform/graphics/texmap/GraphicsLayerTextureMapper.cpp b/Source/WebCore/platform/graphics/texmap/GraphicsLayerTextureMapper.cpp
index 1b87ee626..78a0246f7 100644
--- a/Source/WebCore/platform/graphics/texmap/GraphicsLayerTextureMapper.cpp
+++ b/Source/WebCore/platform/graphics/texmap/GraphicsLayerTextureMapper.cpp
@@ -21,31 +21,25 @@
#include "GraphicsLayerTextureMapper.h"
#include "GraphicsContext.h"
-#include "GraphicsLayerAnimation.h"
#include "GraphicsLayerFactory.h"
#include "ImageBuffer.h"
+#include "TextureMapperAnimation.h"
#include <wtf/CurrentTime.h>
-#if USE(TEXTURE_MAPPER)
+#if !USE(COORDINATED_GRAPHICS)
namespace WebCore {
-TextureMapperLayer* toTextureMapperLayer(GraphicsLayer* layer)
-{
- return layer ? toGraphicsLayerTextureMapper(layer)->layer() : 0;
-}
-
-std::unique_ptr<GraphicsLayer> GraphicsLayer::create(GraphicsLayerFactory* factory, GraphicsLayerClient* client)
+std::unique_ptr<GraphicsLayer> GraphicsLayer::create(GraphicsLayerFactory* factory, GraphicsLayerClient& client, Type layerType)
{
if (!factory)
- return std::make_unique<GraphicsLayerTextureMapper>(client);
+ return std::make_unique<GraphicsLayerTextureMapper>(layerType, client);
- return factory->createGraphicsLayer(client);
+ return factory->createGraphicsLayer(layerType, client);
}
-GraphicsLayerTextureMapper::GraphicsLayerTextureMapper(GraphicsLayerClient* client)
- : GraphicsLayer(client)
- , m_layer(adoptPtr(new TextureMapperLayer()))
+GraphicsLayerTextureMapper::GraphicsLayerTextureMapper(Type layerType, GraphicsLayerClient& client)
+ : GraphicsLayer(layerType, client)
, m_compositedNativeImagePtr(0)
, m_changeMask(NoChanges)
, m_needsDisplay(false)
@@ -59,15 +53,11 @@ GraphicsLayerTextureMapper::GraphicsLayerTextureMapper(GraphicsLayerClient* clie
void GraphicsLayerTextureMapper::notifyChange(ChangeMask changeMask)
{
+ bool flushRequired = m_changeMask == NoChanges;
m_changeMask |= changeMask;
- if (!client())
- return;
- client()->notifyFlushRequired(this);
-}
-void GraphicsLayerTextureMapper::setName(const String& name)
-{
- GraphicsLayer::setName(name);
+ if (flushRequired)
+ client().notifyFlushRequired(this);
}
GraphicsLayerTextureMapper::~GraphicsLayerTextureMapper()
@@ -78,33 +68,23 @@ GraphicsLayerTextureMapper::~GraphicsLayerTextureMapper()
willBeDestroyed();
}
-void GraphicsLayerTextureMapper::willBeDestroyed()
-{
- GraphicsLayer::willBeDestroyed();
-}
-
-/* \reimp (GraphicsLayer.h): The current size might change, thus we need to update the whole display.
-*/
void GraphicsLayerTextureMapper::setNeedsDisplay()
{
if (!drawsContent())
return;
+ // The current size might change, thus we need to update the whole display.
m_needsDisplay = true;
notifyChange(DisplayChange);
addRepaintRect(FloatRect(FloatPoint(), m_size));
}
-/* \reimp (GraphicsLayer.h)
-*/
void GraphicsLayerTextureMapper::setContentsNeedsDisplay()
{
notifyChange(DisplayChange);
addRepaintRect(contentsRect());
}
-/* \reimp (GraphicsLayer.h)
-*/
void GraphicsLayerTextureMapper::setNeedsDisplayInRect(const FloatRect& rect, ShouldClipToLayer)
{
if (!drawsContent())
@@ -117,8 +97,6 @@ void GraphicsLayerTextureMapper::setNeedsDisplayInRect(const FloatRect& rect, Sh
addRepaintRect(rect);
}
-/* \reimp (GraphicsLayer.h)
-*/
bool GraphicsLayerTextureMapper::setChildren(const Vector<GraphicsLayer*>& children)
{
if (GraphicsLayer::setChildren(children)) {
@@ -128,40 +106,30 @@ bool GraphicsLayerTextureMapper::setChildren(const Vector<GraphicsLayer*>& child
return false;
}
-/* \reimp (GraphicsLayer.h)
-*/
void GraphicsLayerTextureMapper::addChild(GraphicsLayer* layer)
{
notifyChange(ChildrenChange);
GraphicsLayer::addChild(layer);
}
-/* \reimp (GraphicsLayer.h)
-*/
void GraphicsLayerTextureMapper::addChildAtIndex(GraphicsLayer* layer, int index)
{
GraphicsLayer::addChildAtIndex(layer, index);
notifyChange(ChildrenChange);
}
-/* \reimp (GraphicsLayer.h)
-*/
void GraphicsLayerTextureMapper::addChildAbove(GraphicsLayer* layer, GraphicsLayer* sibling)
{
GraphicsLayer::addChildAbove(layer, sibling);
notifyChange(ChildrenChange);
}
-/* \reimp (GraphicsLayer.h)
-*/
void GraphicsLayerTextureMapper::addChildBelow(GraphicsLayer* layer, GraphicsLayer* sibling)
{
GraphicsLayer::addChildBelow(layer, sibling);
notifyChange(ChildrenChange);
}
-/* \reimp (GraphicsLayer.h)
-*/
bool GraphicsLayerTextureMapper::replaceChild(GraphicsLayer* oldChild, GraphicsLayer* newChild)
{
if (GraphicsLayer::replaceChild(oldChild, newChild)) {
@@ -171,8 +139,6 @@ bool GraphicsLayerTextureMapper::replaceChild(GraphicsLayer* oldChild, GraphicsL
return false;
}
-/* \reimp (GraphicsLayer.h)
-*/
void GraphicsLayerTextureMapper::setMaskLayer(GraphicsLayer* value)
{
if (value == maskLayer())
@@ -187,8 +153,6 @@ void GraphicsLayerTextureMapper::setMaskLayer(GraphicsLayer* value)
}
-/* \reimp (GraphicsLayer.h)
-*/
void GraphicsLayerTextureMapper::setReplicatedByLayer(GraphicsLayer* value)
{
if (value == replicaLayer())
@@ -197,8 +161,6 @@ void GraphicsLayerTextureMapper::setReplicatedByLayer(GraphicsLayer* value)
notifyChange(ReplicaLayerChange);
}
-/* \reimp (GraphicsLayer.h)
-*/
void GraphicsLayerTextureMapper::setPosition(const FloatPoint& value)
{
if (value == position())
@@ -207,8 +169,6 @@ void GraphicsLayerTextureMapper::setPosition(const FloatPoint& value)
notifyChange(PositionChange);
}
-/* \reimp (GraphicsLayer.h)
-*/
void GraphicsLayerTextureMapper::setAnchorPoint(const FloatPoint3D& value)
{
if (value == anchorPoint())
@@ -217,8 +177,6 @@ void GraphicsLayerTextureMapper::setAnchorPoint(const FloatPoint3D& value)
notifyChange(AnchorPointChange);
}
-/* \reimp (GraphicsLayer.h)
-*/
void GraphicsLayerTextureMapper::setSize(const FloatSize& value)
{
if (value == size())
@@ -230,8 +188,6 @@ void GraphicsLayerTextureMapper::setSize(const FloatSize& value)
notifyChange(SizeChange);
}
-/* \reimp (GraphicsLayer.h)
-*/
void GraphicsLayerTextureMapper::setTransform(const TransformationMatrix& value)
{
if (value == transform())
@@ -241,8 +197,6 @@ void GraphicsLayerTextureMapper::setTransform(const TransformationMatrix& value)
notifyChange(TransformChange);
}
-/* \reimp (GraphicsLayer.h)
-*/
void GraphicsLayerTextureMapper::setChildrenTransform(const TransformationMatrix& value)
{
if (value == childrenTransform())
@@ -251,8 +205,6 @@ void GraphicsLayerTextureMapper::setChildrenTransform(const TransformationMatrix
notifyChange(ChildrenTransformChange);
}
-/* \reimp (GraphicsLayer.h)
-*/
void GraphicsLayerTextureMapper::setPreserves3D(bool value)
{
if (value == preserves3D())
@@ -261,8 +213,6 @@ void GraphicsLayerTextureMapper::setPreserves3D(bool value)
notifyChange(Preserves3DChange);
}
-/* \reimp (GraphicsLayer.h)
-*/
void GraphicsLayerTextureMapper::setMasksToBounds(bool value)
{
if (value == masksToBounds())
@@ -271,8 +221,6 @@ void GraphicsLayerTextureMapper::setMasksToBounds(bool value)
notifyChange(MasksToBoundsChange);
}
-/* \reimp (GraphicsLayer.h)
-*/
void GraphicsLayerTextureMapper::setDrawsContent(bool value)
{
if (value == drawsContent())
@@ -284,8 +232,6 @@ void GraphicsLayerTextureMapper::setDrawsContent(bool value)
setNeedsDisplay();
}
-/* \reimp (GraphicsLayer.h)
-*/
void GraphicsLayerTextureMapper::setContentsVisible(bool value)
{
if (value == contentsAreVisible())
@@ -296,8 +242,6 @@ void GraphicsLayerTextureMapper::setContentsVisible(bool value)
maskLayer()->setContentsVisible(value);
}
-/* \reimp (GraphicsLayer.h)
-*/
void GraphicsLayerTextureMapper::setContentsOpaque(bool value)
{
if (value == contentsOpaque())
@@ -306,8 +250,6 @@ void GraphicsLayerTextureMapper::setContentsOpaque(bool value)
GraphicsLayer::setContentsOpaque(value);
}
-/* \reimp (GraphicsLayer.h)
-*/
void GraphicsLayerTextureMapper::setBackfaceVisibility(bool value)
{
if (value == backfaceVisibility())
@@ -316,8 +258,6 @@ void GraphicsLayerTextureMapper::setBackfaceVisibility(bool value)
notifyChange(BackfaceVisibilityChange);
}
-/* \reimp (GraphicsLayer.h)
-*/
void GraphicsLayerTextureMapper::setOpacity(float value)
{
if (value == opacity())
@@ -326,9 +266,7 @@ void GraphicsLayerTextureMapper::setOpacity(float value)
notifyChange(OpacityChange);
}
-/* \reimp (GraphicsLayer.h)
-*/
-void GraphicsLayerTextureMapper::setContentsRect(const IntRect& value)
+void GraphicsLayerTextureMapper::setContentsRect(const FloatRect& value)
{
if (value == contentsRect())
return;
@@ -345,15 +283,12 @@ void GraphicsLayerTextureMapper::setContentsToSolidColor(const Color& color)
notifyChange(BackgroundColorChange);
}
-
-/* \reimp (GraphicsLayer.h)
-*/
void GraphicsLayerTextureMapper::setContentsToImage(Image* image)
{
if (image) {
// Make the decision about whether the image has changed.
// This code makes the assumption that pointer equality on a NativeImagePtr is a valid way to tell if the image is changed.
- // This assumption is true in Qt, GTK and EFL.
+ // This assumption is true for the GTK+ port.
NativeImagePtr newNativeImagePtr = image->nativeImageForCurrentFrame();
if (!newNativeImagePtr)
return;
@@ -365,28 +300,29 @@ void GraphicsLayerTextureMapper::setContentsToImage(Image* image)
if (!m_compositedImage)
m_compositedImage = TextureMapperTiledBackingStore::create();
m_compositedImage->setContentsToImage(image);
+ m_compositedImage->updateContentsScale(pageScaleFactor() * deviceScaleFactor());
} else {
- m_compositedNativeImagePtr = 0;
- m_compositedImage = 0;
+ m_compositedNativeImagePtr = nullptr;
+ m_compositedImage = nullptr;
}
- setContentsToMedia(m_compositedImage.get());
+ setContentsToPlatformLayer(m_compositedImage.get(), ContentsLayerForImage);
notifyChange(ContentChange);
GraphicsLayer::setContentsToImage(image);
}
-void GraphicsLayerTextureMapper::setContentsToMedia(TextureMapperPlatformLayer* media)
+void GraphicsLayerTextureMapper::setContentsToPlatformLayer(TextureMapperPlatformLayer* platformLayer, ContentsLayerPurpose purpose)
{
- if (media == m_contentsLayer)
+ if (platformLayer == m_contentsLayer)
return;
- GraphicsLayer::setContentsToMedia(media);
+ GraphicsLayer::setContentsToPlatformLayer(platformLayer, purpose);
notifyChange(ContentChange);
if (m_contentsLayer)
m_contentsLayer->setClient(0);
- m_contentsLayer = media;
+ m_contentsLayer = platformLayer;
if (m_contentsLayer)
m_contentsLayer->setClient(this);
@@ -428,26 +364,25 @@ void GraphicsLayerTextureMapper::setIsScrollable(bool isScrollable)
notifyChange(IsScrollableChange);
}
-/* \reimp (GraphicsLayer.h)
-*/
void GraphicsLayerTextureMapper::flushCompositingStateForThisLayerOnly()
{
prepareBackingStoreIfNeeded();
commitLayerChanges();
- m_layer->syncAnimations();
- updateBackingStoreIfNeeded();
+ m_layer.syncAnimations();
}
void GraphicsLayerTextureMapper::prepareBackingStoreIfNeeded()
{
- if (!shouldHaveBackingStore()) {
- m_backingStore.clear();
- m_changeMask |= BackingStoreChange;
- } else {
+ if (shouldHaveBackingStore()) {
if (!m_backingStore) {
m_backingStore = TextureMapperTiledBackingStore::create();
m_changeMask |= BackingStoreChange;
}
+ } else {
+ if (m_backingStore) {
+ m_backingStore = nullptr;
+ m_changeMask |= BackingStoreChange;
+ }
}
updateDebugBorderAndRepaintCount();
@@ -473,112 +408,98 @@ void GraphicsLayerTextureMapper::setDebugBorder(const Color& color, float width)
m_changeMask |= DebugVisualsChange;
}
-static void toTextureMapperLayerVector(const Vector<GraphicsLayer*>& layers, Vector<TextureMapperLayer*>& texmapLayers)
-{
- texmapLayers.reserveCapacity(layers.size());
- for (size_t i = 0; i < layers.size(); ++i)
- texmapLayers.append(toTextureMapperLayer(layers[i]));
-}
-
void GraphicsLayerTextureMapper::commitLayerChanges()
{
if (m_changeMask == NoChanges)
return;
- if (m_changeMask & ChildrenChange) {
- Vector<TextureMapperLayer*> textureMapperLayerChildren;
- toTextureMapperLayerVector(children(), textureMapperLayerChildren);
- m_layer->setChildren(textureMapperLayerChildren);
- }
+ if (m_changeMask & ChildrenChange)
+ m_layer.setChildren(children());
if (m_changeMask & MaskLayerChange)
- m_layer->setMaskLayer(toTextureMapperLayer(maskLayer()));
+ m_layer.setMaskLayer(&downcast<GraphicsLayerTextureMapper>(maskLayer())->layer());
if (m_changeMask & ReplicaLayerChange)
- m_layer->setReplicaLayer(toTextureMapperLayer(replicaLayer()));
+ m_layer.setReplicaLayer(&downcast<GraphicsLayerTextureMapper>(replicaLayer())->layer());
if (m_changeMask & PositionChange)
- m_layer->setPosition(position());
+ m_layer.setPosition(position());
if (m_changeMask & AnchorPointChange)
- m_layer->setAnchorPoint(anchorPoint());
+ m_layer.setAnchorPoint(anchorPoint());
if (m_changeMask & SizeChange)
- m_layer->setSize(size());
+ m_layer.setSize(size());
if (m_changeMask & TransformChange)
- m_layer->setTransform(transform());
+ m_layer.setTransform(transform());
if (m_changeMask & ChildrenTransformChange)
- m_layer->setChildrenTransform(childrenTransform());
+ m_layer.setChildrenTransform(childrenTransform());
if (m_changeMask & Preserves3DChange)
- m_layer->setPreserves3D(preserves3D());
+ m_layer.setPreserves3D(preserves3D());
if (m_changeMask & ContentsRectChange)
- m_layer->setContentsRect(contentsRect());
+ m_layer.setContentsRect(contentsRect());
if (m_changeMask & MasksToBoundsChange)
- m_layer->setMasksToBounds(masksToBounds());
+ m_layer.setMasksToBounds(masksToBounds());
if (m_changeMask & DrawsContentChange)
- m_layer->setDrawsContent(drawsContent());
+ m_layer.setDrawsContent(drawsContent());
if (m_changeMask & ContentsVisibleChange)
- m_layer->setContentsVisible(contentsAreVisible());
+ m_layer.setContentsVisible(contentsAreVisible());
if (m_changeMask & ContentsOpaqueChange)
- m_layer->setContentsOpaque(contentsOpaque());
+ m_layer.setContentsOpaque(contentsOpaque());
if (m_changeMask & BackfaceVisibilityChange)
- m_layer->setBackfaceVisibility(backfaceVisibility());
+ m_layer.setBackfaceVisibility(backfaceVisibility());
if (m_changeMask & OpacityChange)
- m_layer->setOpacity(opacity());
+ m_layer.setOpacity(opacity());
if (m_changeMask & BackgroundColorChange)
- m_layer->setSolidColor(solidColor());
+ m_layer.setSolidColor(m_solidColor);
-#if ENABLE(CSS_FILTERS)
if (m_changeMask & FilterChange)
- m_layer->setFilters(filters());
-#endif
+ m_layer.setFilters(filters());
if (m_changeMask & BackingStoreChange)
- m_layer->setBackingStore(m_backingStore);
+ m_layer.setBackingStore(m_backingStore);
if (m_changeMask & DebugVisualsChange)
- m_layer->setDebugVisuals(isShowingDebugBorder(), debugBorderColor(), debugBorderWidth(), isShowingRepaintCounter());
+ m_layer.setDebugVisuals(isShowingDebugBorder(), debugBorderColor(), debugBorderWidth(), isShowingRepaintCounter());
if (m_changeMask & RepaintCountChange)
- m_layer->setRepaintCount(repaintCount());
+ m_layer.setRepaintCount(repaintCount());
if (m_changeMask & ContentChange)
- m_layer->setContentsLayer(platformLayer());
+ m_layer.setContentsLayer(platformLayer());
if (m_changeMask & AnimationChange)
- m_layer->setAnimations(m_animations);
+ m_layer.setAnimations(m_animations);
if (m_changeMask & AnimationStarted)
- client()->notifyAnimationStarted(this, m_animationStartTime);
+ client().notifyAnimationStarted(this, "", m_animationStartTime);
if (m_changeMask & FixedToViewporChange)
- m_layer->setFixedToViewport(fixedToViewport());
+ m_layer.setFixedToViewport(fixedToViewport());
if (m_changeMask & IsScrollableChange)
- m_layer->setIsScrollable(isScrollable());
+ m_layer.setIsScrollable(isScrollable());
if (m_changeMask & CommittedScrollOffsetChange)
- m_layer->didCommitScrollOffset(m_committedScrollOffset);
+ m_layer.didCommitScrollOffset(m_committedScrollOffset);
m_changeMask = NoChanges;
}
-/* \reimp (GraphicsLayer.h)
-*/
void GraphicsLayerTextureMapper::flushCompositingState(const FloatRect& rect)
{
- if (!m_layer->textureMapper())
+ if (!m_layer.textureMapper())
return;
flushCompositingStateForThisLayerOnly();
@@ -587,13 +508,28 @@ void GraphicsLayerTextureMapper::flushCompositingState(const FloatRect& rect)
maskLayer()->flushCompositingState(rect);
if (replicaLayer())
replicaLayer()->flushCompositingState(rect);
- for (size_t i = 0; i < children().size(); ++i)
- children()[i]->flushCompositingState(rect);
+ for (auto* child : children())
+ child->flushCompositingState(rect);
+}
+
+void GraphicsLayerTextureMapper::updateBackingStoreIncludingSubLayers()
+{
+ if (!m_layer.textureMapper())
+ return;
+
+ updateBackingStoreIfNeeded();
+
+ if (maskLayer())
+ downcast<GraphicsLayerTextureMapper>(*maskLayer()).updateBackingStoreIfNeeded();
+ if (replicaLayer())
+ downcast<GraphicsLayerTextureMapper>(*replicaLayer()).updateBackingStoreIfNeeded();
+ for (auto* child : children())
+ downcast<GraphicsLayerTextureMapper>(*child).updateBackingStoreIncludingSubLayers();
}
void GraphicsLayerTextureMapper::updateBackingStoreIfNeeded()
{
- TextureMapper* textureMapper = m_layer->textureMapper();
+ TextureMapper* textureMapper = m_layer.textureMapper();
if (!textureMapper)
return;
@@ -610,8 +546,10 @@ void GraphicsLayerTextureMapper::updateBackingStoreIfNeeded()
return;
TextureMapperTiledBackingStore* backingStore = static_cast<TextureMapperTiledBackingStore*>(m_backingStore.get());
+ backingStore->updateContentsScale(pageScaleFactor() * deviceScaleFactor());
- backingStore->updateContents(textureMapper, this, m_size, dirtyRect, BitmapTexture::UpdateCanModifyOriginalImageData);
+ dirtyRect.scale(pageScaleFactor() * deviceScaleFactor());
+ backingStore->updateContents(*textureMapper, this, m_size, dirtyRect, BitmapTexture::UpdateCanModifyOriginalImageData);
m_needsDisplay = false;
m_needsDisplayRect = IntRect();
@@ -622,21 +560,44 @@ bool GraphicsLayerTextureMapper::shouldHaveBackingStore() const
return drawsContent() && contentsAreVisible() && !m_size.isEmpty();
}
-bool GraphicsLayerTextureMapper::addAnimation(const KeyframeValueList& valueList, const IntSize& boxSize, const Animation* anim, const String& keyframesName, double timeOffset)
+bool GraphicsLayerTextureMapper::filtersCanBeComposited(const FilterOperations& filters) const
+{
+ if (!filters.size())
+ return false;
+
+ for (const auto& filterOperation : filters.operations()) {
+ if (filterOperation->type() == FilterOperation::REFERENCE)
+ return false;
+ }
+
+ return true;
+}
+
+bool GraphicsLayerTextureMapper::addAnimation(const KeyframeValueList& valueList, const FloatSize& boxSize, const Animation* anim, const String& keyframesName, double timeOffset)
{
ASSERT(!keyframesName.isEmpty());
- if (!anim || anim->isEmptyOrZeroDuration() || valueList.size() < 2 || (valueList.property() != AnimatedPropertyWebkitTransform && valueList.property() != AnimatedPropertyOpacity))
+ if (!anim || anim->isEmptyOrZeroDuration() || valueList.size() < 2 || (valueList.property() != AnimatedPropertyTransform && valueList.property() != AnimatedPropertyOpacity))
return false;
+ if (valueList.property() == AnimatedPropertyFilter) {
+ int listIndex = validateFilterOperations(valueList);
+ if (listIndex < 0)
+ return false;
+
+ const auto& filters = static_cast<const FilterAnimationValue&>(valueList.at(listIndex)).value();
+ if (!filtersCanBeComposited(filters))
+ return false;
+ }
+
bool listsMatch = false;
bool hasBigRotation;
- if (valueList.property() == AnimatedPropertyWebkitTransform)
+ if (valueList.property() == AnimatedPropertyTransform)
listsMatch = validateTransformOperations(valueList, hasBigRotation) >= 0;
const double currentTime = monotonicallyIncreasingTime();
- m_animations.add(GraphicsLayerAnimation(keyframesName, valueList, boxSize, anim, currentTime - timeOffset, listsMatch));
+ m_animations.add(TextureMapperAnimation(keyframesName, valueList, boxSize, *anim, listsMatch, currentTime - timeOffset, 0, TextureMapperAnimation::AnimationState::Playing));
// m_animationStartTime is the time of the first real frame of animation, now or delayed by a negative offset.
if (timeOffset > 0)
m_animationStartTime = currentTime;
@@ -647,7 +608,7 @@ bool GraphicsLayerTextureMapper::addAnimation(const KeyframeValueList& valueList
return true;
}
-void GraphicsLayerTextureMapper::setAnimations(const GraphicsLayerAnimations& animations)
+void GraphicsLayerTextureMapper::setAnimations(const TextureMapperAnimations& animations)
{
m_animations = animations;
notifyChange(AnimationChange);
@@ -664,17 +625,26 @@ void GraphicsLayerTextureMapper::removeAnimation(const String& animationName)
m_animations.remove(animationName);
}
-#if ENABLE(CSS_FILTERS)
bool GraphicsLayerTextureMapper::setFilters(const FilterOperations& filters)
{
- TextureMapper* textureMapper = m_layer->textureMapper();
- // TextureMapperImageBuffer does not support CSS filters.
- if (!textureMapper || textureMapper->accelerationMode() == TextureMapper::SoftwareMode)
+ if (!m_layer.textureMapper())
return false;
- notifyChange(FilterChange);
- return GraphicsLayer::setFilters(filters);
+
+ bool canCompositeFilters = filtersCanBeComposited(filters);
+ if (GraphicsLayer::filters() == filters)
+ return canCompositeFilters;
+
+ if (canCompositeFilters) {
+ if (!GraphicsLayer::setFilters(filters))
+ return false;
+ notifyChange(FilterChange);
+ } else if (GraphicsLayer::filters().size()) {
+ clearFilters();
+ notifyChange(FilterChange);
+ }
+
+ return canCompositeFilters;
}
-#endif
void GraphicsLayerTextureMapper::setFixedToViewport(bool fixed)
{
diff --git a/Source/WebCore/platform/graphics/texmap/GraphicsLayerTextureMapper.h b/Source/WebCore/platform/graphics/texmap/GraphicsLayerTextureMapper.h
index e3340d346..42ed713d3 100644
--- a/Source/WebCore/platform/graphics/texmap/GraphicsLayerTextureMapper.h
+++ b/Source/WebCore/platform/graphics/texmap/GraphicsLayerTextureMapper.h
@@ -20,7 +20,7 @@
#ifndef GraphicsLayerTextureMapper_h
#define GraphicsLayerTextureMapper_h
-#if USE(TEXTURE_MAPPER)
+#if !USE(COORDINATED_GRAPHICS)
#include "GraphicsLayer.h"
#include "GraphicsLayerClient.h"
@@ -32,70 +32,68 @@
namespace WebCore {
-class GraphicsLayerTextureMapper : public GraphicsLayer, public TextureMapperPlatformLayer::Client {
+class GraphicsLayerTextureMapper final : public GraphicsLayer, TextureMapperPlatformLayer::Client {
public:
- explicit GraphicsLayerTextureMapper(GraphicsLayerClient*);
+ explicit GraphicsLayerTextureMapper(Type, GraphicsLayerClient&);
virtual ~GraphicsLayerTextureMapper();
- void setScrollClient(TextureMapperLayer::ScrollingClient* client) { m_layer->setScrollClient(client); }
- void setID(uint32_t id) { m_layer->setID(id); }
-
- // reimps from GraphicsLayer.h
- virtual void setNeedsDisplay();
- virtual void setContentsNeedsDisplay();
- virtual void setNeedsDisplayInRect(const FloatRect&, ShouldClipToLayer = ClipToLayer);
- virtual bool setChildren(const Vector<GraphicsLayer*>&);
- virtual void addChild(GraphicsLayer*);
- virtual void addChildAtIndex(GraphicsLayer*, int index);
- virtual void addChildAbove(GraphicsLayer* layer, GraphicsLayer* sibling);
- virtual void addChildBelow(GraphicsLayer* layer, GraphicsLayer* sibling);
- virtual bool replaceChild(GraphicsLayer* oldChild, GraphicsLayer* newChild);
- virtual void setMaskLayer(GraphicsLayer* layer);
- virtual void setPosition(const FloatPoint& p);
- virtual void setAnchorPoint(const FloatPoint3D& p);
- virtual void setSize(const FloatSize& size);
- virtual void setTransform(const TransformationMatrix& t);
- virtual void setChildrenTransform(const TransformationMatrix& t);
- virtual void setPreserves3D(bool b);
- virtual void setMasksToBounds(bool b);
- virtual void setDrawsContent(bool b);
- virtual void setContentsVisible(bool);
- virtual void setContentsOpaque(bool b);
- virtual void setBackfaceVisibility(bool b);
- virtual void setOpacity(float opacity);
- virtual void setContentsRect(const IntRect& r);
- virtual void setReplicatedByLayer(GraphicsLayer*);
- virtual void setContentsToImage(Image*);
- virtual void setContentsToSolidColor(const Color&);
- Color solidColor() const { return m_solidColor; }
- virtual void setContentsToMedia(PlatformLayer*);
- virtual void setContentsToCanvas(PlatformLayer* canvas) { setContentsToMedia(canvas); }
- virtual void setShowDebugBorder(bool) override;
- virtual void setDebugBorder(const Color&, float width) override;
- virtual void setShowRepaintCounter(bool) override;
- virtual void flushCompositingState(const FloatRect&);
- virtual void flushCompositingStateForThisLayerOnly();
- virtual void setName(const String& name);
- virtual bool hasContentsLayer() const { return m_contentsLayer; }
- virtual PlatformLayer* platformLayer() const { return m_contentsLayer; }
-
- inline int changeMask() const { return m_changeMask; }
-
- virtual bool addAnimation(const KeyframeValueList&, const IntSize&, const Animation*, const String&, double);
- virtual void pauseAnimation(const String&, double);
- virtual void removeAnimation(const String&);
- void setAnimations(const GraphicsLayerAnimations&);
-
- TextureMapperLayer* layer() const { return m_layer.get(); }
+ void setScrollClient(TextureMapperLayer::ScrollingClient* client) { m_layer.setScrollClient(client); }
+ void setID(uint32_t id) { m_layer.setID(id); }
+
+ // GraphicsLayer
+ bool setChildren(const Vector<GraphicsLayer*>&) override;
+ void addChild(GraphicsLayer*) override;
+ void addChildAtIndex(GraphicsLayer*, int index) override;
+ void addChildAbove(GraphicsLayer*, GraphicsLayer* sibling) override;
+ void addChildBelow(GraphicsLayer*, GraphicsLayer* sibling) override;
+ bool replaceChild(GraphicsLayer* oldChild, GraphicsLayer* newChild) override;
+
+ void setMaskLayer(GraphicsLayer*) override;
+ void setReplicatedByLayer(GraphicsLayer*) override;
+ void setPosition(const FloatPoint&) override;
+ void setAnchorPoint(const FloatPoint3D&) override;
+ void setSize(const FloatSize&) override;
+ void setTransform(const TransformationMatrix&) override;
+ void setChildrenTransform(const TransformationMatrix&) override;
+ void setPreserves3D(bool) override;
+ void setMasksToBounds(bool) override;
+ void setDrawsContent(bool) override;
+ void setContentsVisible(bool) override;
+ void setContentsOpaque(bool) override;
+ void setBackfaceVisibility(bool) override;
+ void setOpacity(float) override;
+ bool setFilters(const FilterOperations&) override;
+
+ void setNeedsDisplay() override;
+ void setNeedsDisplayInRect(const FloatRect&, ShouldClipToLayer = ClipToLayer) override;
+ void setContentsNeedsDisplay() override;
+ void setContentsRect(const FloatRect&) override;
+
+ bool addAnimation(const KeyframeValueList&, const FloatSize&, const Animation*, const String&, double) override;
+ void pauseAnimation(const String&, double) override;
+ void removeAnimation(const String&) override;
+
+ void setContentsToImage(Image*) override;
+ void setContentsToSolidColor(const Color&) override;
+ void setContentsToPlatformLayer(PlatformLayer*, ContentsLayerPurpose) override;
+ bool usesContentsLayer() const override { return m_contentsLayer; }
+ PlatformLayer* platformLayer() const override { return m_contentsLayer; }
+
+ void setShowDebugBorder(bool) override;
+ void setDebugBorder(const Color&, float width) override;
+ void setShowRepaintCounter(bool) override;
+
+ void flushCompositingState(const FloatRect&) override;
+ void flushCompositingStateForThisLayerOnly() override;
+
+ void updateBackingStoreIncludingSubLayers();
+
+ TextureMapperLayer& layer() { return m_layer; }
void didCommitScrollOffset(const IntSize&);
void setIsScrollable(bool);
bool isScrollable() const { return m_isScrollable; }
-#if ENABLE(CSS_FILTERS)
- virtual bool setFilters(const FilterOperations&);
-#endif
-
void setFixedToViewport(bool);
bool fixedToViewport() const { return m_fixedToViewport; }
@@ -103,8 +101,15 @@ public:
float debugBorderWidth() const { return m_debugBorderWidth; }
void setRepaintCount(int);
+ void setAnimations(const TextureMapperAnimations&);
+
private:
- virtual void willBeDestroyed();
+ // GraphicsLayer
+ bool isGraphicsLayerTextureMapper() const override { return true; }
+
+ // TextureMapperPlatformLayer::Client
+ void platformLayerWillBeDestroyed() override { setContentsToPlatformLayer(0, NoContentsLayer); }
+ void setPlatformLayerNeedsDisplay() override { setContentsNeedsDisplay(); }
void commitLayerChanges();
void updateDebugBorderAndRepaintCount();
@@ -112,8 +117,7 @@ private:
void prepareBackingStoreIfNeeded();
bool shouldHaveBackingStore() const;
- virtual void platformLayerWillBeDestroyed() override { setContentsToMedia(0); }
- virtual void setPlatformLayerNeedsDisplay() override { setContentsNeedsDisplay(); }
+ bool filtersCanBeComposited(const FilterOperations&) const;
// This set of flags help us defer which properties of the layer have been
// modified by the compositor, so we can know what to look for in the next flush.
@@ -160,7 +164,7 @@ private:
};
void notifyChange(ChangeMask);
- OwnPtr<TextureMapperLayer> m_layer;
+ TextureMapperLayer m_layer;
RefPtr<TextureMapperTiledBackingStore> m_compositedImage;
NativeImagePtr m_compositedNativeImagePtr;
RefPtr<TextureMapperBackingStore> m_backingStore;
@@ -175,21 +179,17 @@ private:
TextureMapperPlatformLayer* m_contentsLayer;
FloatRect m_needsDisplayRect;
- GraphicsLayerAnimations m_animations;
+ TextureMapperAnimations m_animations;
double m_animationStartTime;
IntSize m_committedScrollOffset;
bool m_isScrollable;
};
-inline static GraphicsLayerTextureMapper* toGraphicsLayerTextureMapper(GraphicsLayer* layer)
-{
- return static_cast<GraphicsLayerTextureMapper*>(layer);
-}
+} // namespace WebCore
-TextureMapperLayer* toTextureMapperLayer(GraphicsLayer*);
+SPECIALIZE_TYPE_TRAITS_GRAPHICSLAYER(WebCore::GraphicsLayerTextureMapper, isGraphicsLayerTextureMapper())
-}
#endif
#endif // GraphicsLayerTextureMapper_h
diff --git a/Source/WebCore/platform/graphics/texmap/TextureMapper.cpp b/Source/WebCore/platform/graphics/texmap/TextureMapper.cpp
index b863e79e5..973047f49 100644
--- a/Source/WebCore/platform/graphics/texmap/TextureMapper.cpp
+++ b/Source/WebCore/platform/graphics/texmap/TextureMapper.cpp
@@ -20,151 +20,28 @@
#include "config.h"
#include "TextureMapper.h"
+#include "BitmapTexturePool.h"
#include "FilterOperations.h"
#include "GraphicsLayer.h"
-#include "TextureMapperImageBuffer.h"
#include "Timer.h"
#include <wtf/CurrentTime.h>
-#if USE(ACCELERATED_COMPOSITING) && USE(TEXTURE_MAPPER)
-
namespace WebCore {
-struct BitmapTexturePoolEntry {
- explicit BitmapTexturePoolEntry(PassRefPtr<BitmapTexture> texture)
- : m_texture(texture)
- { }
- inline void markUsed() { m_timeLastUsed = monotonicallyIncreasingTime(); }
- static bool compareTimeLastUsed(const BitmapTexturePoolEntry& a, const BitmapTexturePoolEntry& b)
- {
- return a.m_timeLastUsed - b.m_timeLastUsed > 0;
- }
-
- RefPtr<BitmapTexture> m_texture;
- double m_timeLastUsed;
-};
-
-class BitmapTexturePool {
- WTF_MAKE_NONCOPYABLE(BitmapTexturePool);
- WTF_MAKE_FAST_ALLOCATED;
-public:
- BitmapTexturePool();
-
- PassRefPtr<BitmapTexture> acquireTexture(const IntSize&, TextureMapper*);
-
-private:
- void scheduleReleaseUnusedTextures();
- void releaseUnusedTexturesTimerFired(Timer<BitmapTexturePool>*);
-
- Vector<BitmapTexturePoolEntry> m_textures;
- Timer<BitmapTexturePool> m_releaseUnusedTexturesTimer;
-
- static const double s_releaseUnusedSecondsTolerance;
- static const double s_releaseUnusedTexturesTimerInterval;
-};
-
-const double BitmapTexturePool::s_releaseUnusedSecondsTolerance = 3;
-const double BitmapTexturePool::s_releaseUnusedTexturesTimerInterval = 0.5;
-
-BitmapTexturePool::BitmapTexturePool()
- : m_releaseUnusedTexturesTimer(this, &BitmapTexturePool::releaseUnusedTexturesTimerFired)
-{ }
+TextureMapper::TextureMapper() = default;
-void BitmapTexturePool::scheduleReleaseUnusedTextures()
-{
- if (m_releaseUnusedTexturesTimer.isActive())
- m_releaseUnusedTexturesTimer.stop();
-
- m_releaseUnusedTexturesTimer.startOneShot(s_releaseUnusedTexturesTimerInterval);
-}
-
-void BitmapTexturePool::releaseUnusedTexturesTimerFired(Timer<BitmapTexturePool>*)
-{
- if (m_textures.isEmpty())
- return;
-
- // Delete entries, which have been unused in s_releaseUnusedSecondsTolerance.
- std::sort(m_textures.begin(), m_textures.end(), BitmapTexturePoolEntry::compareTimeLastUsed);
-
- double minUsedTime = monotonicallyIncreasingTime() - s_releaseUnusedSecondsTolerance;
- for (size_t i = 0; i < m_textures.size(); ++i) {
- if (m_textures[i].m_timeLastUsed < minUsedTime) {
- m_textures.remove(i, m_textures.size() - i);
- break;
- }
- }
-}
-
-PassRefPtr<BitmapTexture> BitmapTexturePool::acquireTexture(const IntSize& size, TextureMapper* textureMapper)
-{
- BitmapTexturePoolEntry* selectedEntry = 0;
- for (size_t i = 0; i < m_textures.size(); ++i) {
- BitmapTexturePoolEntry* entry = &m_textures[i];
-
- // If the surface has only one reference (the one in m_textures), we can safely reuse it.
- if (entry->m_texture->refCount() > 1)
- continue;
-
- if (entry->m_texture->canReuseWith(size)) {
- selectedEntry = entry;
- break;
- }
- }
-
- if (!selectedEntry) {
- m_textures.append(BitmapTexturePoolEntry(textureMapper->createTexture()));
- selectedEntry = &m_textures.last();
- }
-
- scheduleReleaseUnusedTextures();
- selectedEntry->markUsed();
- return selectedEntry->m_texture;
-}
+TextureMapper::~TextureMapper() = default;
PassRefPtr<BitmapTexture> TextureMapper::acquireTextureFromPool(const IntSize& size, const BitmapTexture::Flags flags)
{
- RefPtr<BitmapTexture> selectedTexture = m_texturePool->acquireTexture(size, this);
+ RefPtr<BitmapTexture> selectedTexture = m_texturePool->acquireTexture(size, flags);
selectedTexture->reset(size, flags);
- return selectedTexture;
+ return selectedTexture.release();
}
-PassOwnPtr<TextureMapper> TextureMapper::create(AccelerationMode mode)
+std::unique_ptr<TextureMapper> TextureMapper::create()
{
- if (mode == SoftwareMode)
- return TextureMapperImageBuffer::create();
return platformCreateAccelerated();
}
-TextureMapper::TextureMapper(AccelerationMode accelerationMode)
- : m_context(0)
- , m_interpolationQuality(InterpolationDefault)
- , m_textDrawingMode(TextModeFill)
- , m_texturePool(adoptPtr(new BitmapTexturePool()))
- , m_accelerationMode(accelerationMode)
- , m_isMaskMode(false)
- , m_wrapMode(StretchWrap)
-{ }
-
-TextureMapper::~TextureMapper()
-{ }
-
-void BitmapTexture::updateContents(TextureMapper* textureMapper, GraphicsLayer* sourceLayer, const IntRect& targetRect, const IntPoint& offset, UpdateContentsFlag updateContentsFlag)
-{
- std::unique_ptr<ImageBuffer> imageBuffer = ImageBuffer::create(targetRect.size());
- GraphicsContext* context = imageBuffer->context();
- context->setImageInterpolationQuality(textureMapper->imageInterpolationQuality());
- context->setTextDrawingMode(textureMapper->textDrawingMode());
-
- IntRect sourceRect(targetRect);
- sourceRect.setLocation(offset);
- context->translate(-offset.x(), -offset.y());
- sourceLayer->paintGraphicsLayerContents(*context, sourceRect);
-
- RefPtr<Image> image = imageBuffer->copyImage(DontCopyBackingStore);
-
- updateContents(image.get(), targetRect, IntPoint(), updateContentsFlag);
-}
-
} // namespace
-
-#endif
diff --git a/Source/WebCore/platform/graphics/texmap/TextureMapper.h b/Source/WebCore/platform/graphics/texmap/TextureMapper.h
index 681b24ea1..7233d0dcc 100644
--- a/Source/WebCore/platform/graphics/texmap/TextureMapper.h
+++ b/Source/WebCore/platform/graphics/texmap/TextureMapper.h
@@ -20,13 +20,8 @@
#ifndef TextureMapper_h
#define TextureMapper_h
-#if USE(ACCELERATED_COMPOSITING) && USE(TEXTURE_MAPPER)
-
-#if (PLATFORM(GTK) || PLATFORM(EFL)) && USE(OPENGL_ES_2)
-#define TEXMAP_OPENGL_ES_2
-#endif
-
-#include "GraphicsContext.h"
+#include "BitmapTexture.h"
+#include "Color.h"
#include "IntRect.h"
#include "IntSize.h"
#include "TransformationMatrix.h"
@@ -43,68 +38,9 @@ class GraphicsLayer;
class TextureMapper;
class FilterOperations;
-// A 2D texture that can be the target of software or GL rendering.
-class BitmapTexture : public RefCounted<BitmapTexture> {
-public:
- enum Flag {
- NoFlag = 0,
- SupportsAlpha = 0x01
- };
-
- enum UpdateContentsFlag {
- UpdateCanModifyOriginalImageData,
- UpdateCannotModifyOriginalImageData
- };
-
- typedef unsigned Flags;
-
- BitmapTexture()
- : m_flags(0)
- {
- }
-
- virtual ~BitmapTexture() { }
- virtual bool isBackedByOpenGL() const { return false; }
-
- virtual IntSize size() const = 0;
- virtual void updateContents(Image*, const IntRect&, const IntPoint& offset, UpdateContentsFlag) = 0;
- virtual void updateContents(TextureMapper*, GraphicsLayer*, const IntRect& target, const IntPoint& offset, UpdateContentsFlag);
- virtual void updateContents(const void*, const IntRect& target, const IntPoint& offset, int bytesPerLine, UpdateContentsFlag) = 0;
- virtual bool isValid() const = 0;
- inline Flags flags() const { return m_flags; }
-
- virtual int bpp() const { return 32; }
- virtual bool canReuseWith(const IntSize& /* contentsSize */, Flags = 0) { return false; }
- void reset(const IntSize& size, Flags flags = 0)
- {
- m_flags = flags;
- m_contentSize = size;
- didReset();
- }
- virtual void didReset() { }
-
- inline IntSize contentSize() const { return m_contentSize; }
- inline int numberOfBytes() const { return size().width() * size().height() * bpp() >> 3; }
- inline bool isOpaque() const { return !(m_flags & SupportsAlpha); }
-
-#if ENABLE(CSS_FILTERS)
- virtual PassRefPtr<BitmapTexture> applyFilters(TextureMapper*, const FilterOperations&) { return this; }
-#endif
-
-protected:
- IntSize m_contentSize;
-
-private:
- Flags m_flags;
-};
-
-// A "context" class used to encapsulate accelerated texture mapping functions: i.e. drawing a texture
-// onto the screen or into another texture with a specified transform, opacity and mask.
class TextureMapper {
WTF_MAKE_FAST_ALLOCATED;
- friend class BitmapTexture;
public:
- enum AccelerationMode { SoftwareMode, OpenGLMode };
enum PaintFlag {
PaintingMirrored = 1 << 0,
};
@@ -116,7 +52,9 @@ public:
typedef unsigned PaintFlags;
- static PassOwnPtr<TextureMapper> create(AccelerationMode newMode = SoftwareMode);
+ static std::unique_ptr<TextureMapper> create();
+
+ explicit TextureMapper();
virtual ~TextureMapper();
enum ExposedEdges {
@@ -136,20 +74,11 @@ public:
// makes a surface the target for the following drawTexture calls.
virtual void bindSurface(BitmapTexture* surface) = 0;
- void setGraphicsContext(GraphicsContext* context) { m_context = context; }
- GraphicsContext* graphicsContext() { return m_context; }
virtual void beginClip(const TransformationMatrix&, const FloatRect&) = 0;
virtual void endClip() = 0;
virtual IntRect clipBounds() = 0;
virtual PassRefPtr<BitmapTexture> createTexture() = 0;
- void setImageInterpolationQuality(InterpolationQuality quality) { m_interpolationQuality = quality; }
- void setTextDrawingMode(TextDrawingModeFlags mode) { m_textDrawingMode = mode; }
-
- InterpolationQuality imageInterpolationQuality() const { return m_interpolationQuality; }
- TextDrawingModeFlags textDrawingMode() const { return m_textDrawingMode; }
- AccelerationMode accelerationMode() const { return m_accelerationMode; }
-
virtual void beginPainting(PaintFlags = 0) { }
virtual void endPainting() { }
@@ -163,9 +92,7 @@ public:
void setWrapMode(WrapMode m) { m_wrapMode = m; }
protected:
- explicit TextureMapper(AccelerationMode);
-
- GraphicsContext* m_context;
+ std::unique_ptr<BitmapTexturePool> m_texturePool;
bool isInMaskMode() const { return m_isMaskMode; }
WrapMode wrapMode() const { return m_wrapMode; }
@@ -173,24 +100,18 @@ protected:
private:
#if USE(TEXTURE_MAPPER_GL)
- static PassOwnPtr<TextureMapper> platformCreateAccelerated();
+ static std::unique_ptr<TextureMapper> platformCreateAccelerated();
#else
- static PassOwnPtr<TextureMapper> platformCreateAccelerated()
+ static std::unique_ptr<TextureMapper> platformCreateAccelerated()
{
- return PassOwnPtr<TextureMapper>();
+ return nullptr;
}
#endif
- InterpolationQuality m_interpolationQuality;
- TextDrawingModeFlags m_textDrawingMode;
- OwnPtr<BitmapTexturePool> m_texturePool;
- AccelerationMode m_accelerationMode;
- bool m_isMaskMode;
+ bool m_isMaskMode { false };
TransformationMatrix m_patternTransform;
- WrapMode m_wrapMode;
+ WrapMode m_wrapMode { StretchWrap };
};
}
#endif
-
-#endif
diff --git a/Source/WebCore/platform/graphics/GraphicsLayerAnimation.cpp b/Source/WebCore/platform/graphics/texmap/TextureMapperAnimation.cpp
index add686f66..9cc1df6ad 100644
--- a/Source/WebCore/platform/graphics/GraphicsLayerAnimation.cpp
+++ b/Source/WebCore/platform/graphics/texmap/TextureMapperAnimation.cpp
@@ -18,9 +18,7 @@
*/
#include "config.h"
-
-#if USE(ACCELERATED_COMPOSITING)
-#include "GraphicsLayerAnimation.h"
+#include "TextureMapperAnimation.h"
#include "LayoutSize.h"
#include "UnitBezier.h"
@@ -28,18 +26,14 @@
namespace WebCore {
-#if ENABLE(CSS_FILTERS)
-static inline PassRefPtr<FilterOperation> blendFunc(FilterOperation* fromOp, FilterOperation* toOp, double progress, const IntSize& size, bool blendToPassthrough = false)
+static RefPtr<FilterOperation> blendFunc(FilterOperation* fromOp, FilterOperation& toOp, double progress, const FloatSize& size, bool blendToPassthrough = false)
{
- ASSERT(toOp);
- if (toOp->blendingNeedsRendererSize())
- return toOp->blend(fromOp, progress, LayoutSize(size.width(), size.height()), blendToPassthrough);
-
- return toOp->blend(fromOp, progress, blendToPassthrough);
+ if (toOp.blendingNeedsRendererSize())
+ return toOp.blend(fromOp, progress, LayoutSize(size), blendToPassthrough);
+ return toOp.blend(fromOp, progress, blendToPassthrough);
}
-
-static FilterOperations applyFilterAnimation(const FilterOperations& from, const FilterOperations& to, double progress, const IntSize& boxSize)
+static FilterOperations applyFilterAnimation(const FilterOperations& from, const FilterOperations& to, double progress, const FloatSize& boxSize)
{
// First frame of an animation.
if (!progress)
@@ -58,9 +52,9 @@ static FilterOperations applyFilterAnimation(const FilterOperations& from, const
size_t toSize = to.operations().size();
size_t size = std::max(fromSize, toSize);
for (size_t i = 0; i < size; i++) {
- RefPtr<FilterOperation> fromOp = (i < fromSize) ? from.operations()[i].get() : 0;
- RefPtr<FilterOperation> toOp = (i < toSize) ? to.operations()[i].get() : 0;
- RefPtr<FilterOperation> blendedOp = toOp ? blendFunc(fromOp.get(), toOp.get(), progress, boxSize) : (fromOp ? blendFunc(0, fromOp.get(), progress, boxSize, true) : 0);
+ RefPtr<FilterOperation> fromOp = (i < fromSize) ? from.operations()[i].get() : nullptr;
+ RefPtr<FilterOperation> toOp = (i < toSize) ? to.operations()[i].get() : nullptr;
+ RefPtr<FilterOperation> blendedOp = toOp ? blendFunc(fromOp.get(), *toOp, progress, boxSize) : (fromOp ? blendFunc(nullptr, *fromOp, progress, boxSize, true) : nullptr);
if (blendedOp)
result.operations().append(blendedOp);
else {
@@ -74,15 +68,12 @@ static FilterOperations applyFilterAnimation(const FilterOperations& from, const
return result;
}
-#endif
static bool shouldReverseAnimationValue(Animation::AnimationDirection direction, int loopCount)
{
- if (((direction == Animation::AnimationDirectionAlternate) && (loopCount & 1))
- || ((direction == Animation::AnimationDirectionAlternateReverse) && !(loopCount & 1))
- || direction == Animation::AnimationDirectionReverse)
- return true;
- return false;
+ return (direction == Animation::AnimationDirectionAlternate && loopCount & 1)
+ || (direction == Animation::AnimationDirectionAlternateReverse && !(loopCount & 1))
+ || direction == Animation::AnimationDirectionReverse;
}
static double normalizedAnimationValue(double runningTime, double duration, Animation::AnimationDirection direction, double iterationCount)
@@ -137,25 +128,22 @@ static inline double solveStepsFunction(int numSteps, bool stepAtStart, double t
return floor(numSteps * t) / numSteps;
}
-static inline float applyTimingFunction(const TimingFunction* timingFunction, float progress, double duration)
+static inline float applyTimingFunction(const TimingFunction& timingFunction, float progress, double duration)
{
- if (!timingFunction)
- return progress;
-
- if (timingFunction->isCubicBezierTimingFunction()) {
- const CubicBezierTimingFunction* ctf = static_cast<const CubicBezierTimingFunction*>(timingFunction);
- return solveCubicBezierFunction(ctf->x1(), ctf->y1(), ctf->x2(), ctf->y2(), progress, duration);
+ if (timingFunction.isCubicBezierTimingFunction()) {
+ auto& ctf = static_cast<const CubicBezierTimingFunction&>(timingFunction);
+ return solveCubicBezierFunction(ctf.x1(), ctf.y1(), ctf.x2(), ctf.y2(), progress, duration);
}
- if (timingFunction->isStepsTimingFunction()) {
- const StepsTimingFunction* stf = static_cast<const StepsTimingFunction*>(timingFunction);
- return solveStepsFunction(stf->numberOfSteps(), stf->stepAtStart(), double(progress));
+ if (timingFunction.isStepsTimingFunction()) {
+ auto& stf = static_cast<const StepsTimingFunction&>(timingFunction);
+ return solveStepsFunction(stf.numberOfSteps(), stf.stepAtStart(), double(progress));
}
return progress;
}
-static TransformationMatrix applyTransformAnimation(const TransformOperations& from, const TransformOperations& to, double progress, const IntSize& boxSize, bool listsMatch)
+static TransformationMatrix applyTransformAnimation(const TransformOperations& from, const TransformOperations& to, double progress, const FloatSize& boxSize, bool listsMatch)
{
TransformationMatrix matrix;
@@ -183,16 +171,16 @@ static TransformationMatrix applyTransformAnimation(const TransformOperations& f
// Animation to "-webkit-transform: none".
if (!to.size()) {
TransformOperations blended(from);
- for (size_t i = 0; i < blended.operations().size(); ++i)
- blended.operations()[i]->blend(0, progress, true)->apply(matrix, boxSize);
+ for (auto& operation : blended.operations())
+ operation->blend(nullptr, progress, true)->apply(matrix, boxSize);
return matrix;
}
// Animation from "-webkit-transform: none".
if (!from.size()) {
TransformOperations blended(to);
- for (size_t i = 0; i < blended.operations().size(); ++i)
- blended.operations()[i]->blend(0, 1. - progress, true)->apply(matrix, boxSize);
+ for (auto& operation : blended.operations())
+ operation->blend(nullptr, 1 - progress, true)->apply(matrix, boxSize);
return matrix;
}
@@ -203,77 +191,44 @@ static TransformationMatrix applyTransformAnimation(const TransformOperations& f
return matrix;
}
-static const TimingFunction* timingFunctionForAnimationValue(const AnimationValue& animValue, const Animation* anim)
+static const TimingFunction& timingFunctionForAnimationValue(const AnimationValue& animationValue, const Animation& animation)
{
- if (animValue.timingFunction())
- return animValue.timingFunction();
- if (anim->timingFunction())
- return anim->timingFunction().get();
-
+ if (animationValue.timingFunction())
+ return *animationValue.timingFunction();
+ if (animation.timingFunction())
+ return *animation.timingFunction();
return CubicBezierTimingFunction::defaultTimingFunction();
}
-GraphicsLayerAnimation::GraphicsLayerAnimation(const String& name, const KeyframeValueList& keyframes, const IntSize& boxSize, const Animation* animation, double startTime, bool listsMatch)
- : m_keyframes(keyframes)
+TextureMapperAnimation::TextureMapperAnimation(const String& name, const KeyframeValueList& keyframes, const FloatSize& boxSize, const Animation& animation, bool listsMatch, double startTime, double pauseTime, AnimationState state)
+ : m_name(name.isSafeToSendToAnotherThread() ? name : name.isolatedCopy())
+ , m_keyframes(keyframes)
, m_boxSize(boxSize)
- , m_animation(Animation::create(*animation))
- , m_name(name)
+ , m_animation(Animation::create(animation))
, m_listsMatch(listsMatch)
, m_startTime(startTime)
- , m_pauseTime(0)
+ , m_pauseTime(pauseTime)
, m_totalRunningTime(0)
, m_lastRefreshedTime(m_startTime)
- , m_state(PlayingState)
-{
-}
-
-void GraphicsLayerAnimation::applyInternal(Client* client, const AnimationValue& from, const AnimationValue& to, float progress)
-{
- switch (m_keyframes.property()) {
- case AnimatedPropertyOpacity:
- client->setAnimatedOpacity(applyOpacityAnimation((static_cast<const FloatAnimationValue&>(from).value()), (static_cast<const FloatAnimationValue&>(to).value()), progress));
- return;
- case AnimatedPropertyWebkitTransform:
- client->setAnimatedTransform(applyTransformAnimation(static_cast<const TransformAnimationValue&>(from).value(), static_cast<const TransformAnimationValue&>(to).value(), progress, m_boxSize, m_listsMatch));
- return;
-#if ENABLE(CSS_FILTERS)
- case AnimatedPropertyWebkitFilter:
- client->setAnimatedFilters(applyFilterAnimation(static_cast<const FilterAnimationValue&>(from).value(), static_cast<const FilterAnimationValue&>(to).value(), progress, m_boxSize));
- return;
-#endif
- default:
- ASSERT_NOT_REACHED();
- }
-}
-
-bool GraphicsLayerAnimation::isActive() const
-{
- if (state() != StoppedState)
- return true;
-
- return m_animation->fillsForwards();
-}
-
-bool GraphicsLayerAnimations::hasActiveAnimationsOfType(AnimatedPropertyID type) const
+ , m_state(state)
{
- for (size_t i = 0; i < m_animations.size(); ++i) {
- if (m_animations[i].isActive() && m_animations[i].property() == type)
- return true;
- }
- return false;
}
-bool GraphicsLayerAnimations::hasRunningAnimations() const
+TextureMapperAnimation::TextureMapperAnimation(const TextureMapperAnimation& other)
+ : m_name(other.m_name.isSafeToSendToAnotherThread() ? other.m_name : other.m_name.isolatedCopy())
+ , m_keyframes(other.m_keyframes)
+ , m_boxSize(other.m_boxSize)
+ , m_animation(Animation::create(*other.m_animation))
+ , m_listsMatch(other.m_listsMatch)
+ , m_startTime(other.m_startTime)
+ , m_pauseTime(other.m_pauseTime)
+ , m_totalRunningTime(other.m_totalRunningTime)
+ , m_lastRefreshedTime(other.m_lastRefreshedTime)
+ , m_state(other.m_state)
{
- for (size_t i = 0; i < m_animations.size(); ++i) {
- if (m_animations[i].state() == GraphicsLayerAnimation::PlayingState)
- return true;
- }
-
- return false;
}
-void GraphicsLayerAnimation::apply(Client* client)
+void TextureMapperAnimation::apply(Client& client)
{
if (!isActive())
return;
@@ -282,7 +237,8 @@ void GraphicsLayerAnimation::apply(Client* client)
double normalizedValue = normalizedAnimationValue(totalRunningTime, m_animation->duration(), m_animation->direction(), m_animation->iterationCount());
if (m_animation->iterationCount() != Animation::IterationCountInfinite && totalRunningTime >= m_animation->duration() * m_animation->iterationCount()) {
- setState(StoppedState);
+ m_state = AnimationState::Stopped;
+ m_pauseTime = 0;
if (m_animation->fillsForwards())
normalizedValue = normalizedAnimationValueForFillsForwards(m_animation->iterationCount(), m_animation->direction());
}
@@ -297,7 +253,7 @@ void GraphicsLayerAnimation::apply(Client* client)
return;
}
if (m_keyframes.size() == 2) {
- const TimingFunction* timingFunction = timingFunctionForAnimationValue(m_keyframes.at(0), m_animation.get());
+ auto& timingFunction = timingFunctionForAnimationValue(m_keyframes.at(0), *m_animation);
normalizedValue = applyTimingFunction(timingFunction, normalizedValue, m_animation->duration());
applyInternal(client, m_keyframes.at(0), m_keyframes.at(1), normalizedValue);
return;
@@ -310,16 +266,30 @@ void GraphicsLayerAnimation::apply(Client* client)
continue;
normalizedValue = (normalizedValue - from.keyTime()) / (to.keyTime() - from.keyTime());
- const TimingFunction* timingFunction = timingFunctionForAnimationValue(from, m_animation.get());
+ auto& timingFunction = timingFunctionForAnimationValue(from, *m_animation);
normalizedValue = applyTimingFunction(timingFunction, normalizedValue, m_animation->duration());
applyInternal(client, from, to, normalizedValue);
break;
}
}
-double GraphicsLayerAnimation::computeTotalRunningTime()
+void TextureMapperAnimation::pause(double time)
{
- if (state() == PausedState)
+ m_state = AnimationState::Paused;
+ m_pauseTime = time;
+}
+
+void TextureMapperAnimation::resume()
+{
+ m_state = AnimationState::Playing;
+ m_pauseTime = 0;
+ m_totalRunningTime = m_pauseTime;
+ m_lastRefreshedTime = monotonicallyIncreasingTime();
+}
+
+double TextureMapperAnimation::computeTotalRunningTime()
+{
+ if (m_state == AnimationState::Paused)
return m_pauseTime;
double oldLastRefreshedTime = m_lastRefreshedTime;
@@ -328,78 +298,96 @@ double GraphicsLayerAnimation::computeTotalRunningTime()
return m_totalRunningTime;
}
-void GraphicsLayerAnimation::pause(double time)
+bool TextureMapperAnimation::isActive() const
{
- setState(PausedState);
- m_pauseTime = time;
+ return m_state != AnimationState::Stopped || m_animation->fillsForwards();
}
-void GraphicsLayerAnimation::resume()
+void TextureMapperAnimation::applyInternal(Client& client, const AnimationValue& from, const AnimationValue& to, float progress)
{
- setState(PlayingState);
- m_totalRunningTime = m_pauseTime;
- m_lastRefreshedTime = monotonicallyIncreasingTime();
+ switch (m_keyframes.property()) {
+ case AnimatedPropertyOpacity:
+ client.setAnimatedOpacity(applyOpacityAnimation((static_cast<const FloatAnimationValue&>(from).value()), (static_cast<const FloatAnimationValue&>(to).value()), progress));
+ return;
+ case AnimatedPropertyTransform:
+ client.setAnimatedTransform(applyTransformAnimation(static_cast<const TransformAnimationValue&>(from).value(), static_cast<const TransformAnimationValue&>(to).value(), progress, m_boxSize, m_listsMatch));
+ return;
+ case AnimatedPropertyFilter:
+ client.setAnimatedFilters(applyFilterAnimation(static_cast<const FilterAnimationValue&>(from).value(), static_cast<const FilterAnimationValue&>(to).value(), progress, m_boxSize));
+ return;
+ default:
+ ASSERT_NOT_REACHED();
+ }
}
-void GraphicsLayerAnimations::add(const GraphicsLayerAnimation& animation)
+void TextureMapperAnimations::add(const TextureMapperAnimation& animation)
{
// Remove the old state if we are resuming a paused animation.
- remove(animation.name(), animation.property());
+ remove(animation.name(), animation.keyframes().property());
m_animations.append(animation);
}
-void GraphicsLayerAnimations::pause(const String& name, double offset)
+void TextureMapperAnimations::remove(const String& name)
+{
+ m_animations.removeAllMatching([&name] (const TextureMapperAnimation& animation) {
+ return animation.name() == name;
+ });
+}
+
+void TextureMapperAnimations::remove(const String& name, AnimatedPropertyID property)
{
- for (size_t i = 0; i < m_animations.size(); ++i) {
- if (m_animations[i].name() == name)
- m_animations[i].pause(offset);
+ m_animations.removeAllMatching([&name, property] (const TextureMapperAnimation& animation) {
+ return animation.name() == name && animation.keyframes().property() == property;
+ });
+}
+
+void TextureMapperAnimations::pause(const String& name, double offset)
+{
+ for (auto& animation : m_animations) {
+ if (animation.name() == name)
+ animation.pause(offset);
}
}
-void GraphicsLayerAnimations::suspend(double offset)
+void TextureMapperAnimations::suspend(double offset)
{
- for (size_t i = 0; i < m_animations.size(); ++i)
- m_animations[i].pause(offset);
+ for (auto& animation : m_animations)
+ animation.pause(offset);
}
-void GraphicsLayerAnimations::resume()
+void TextureMapperAnimations::resume()
{
- for (size_t i = 0; i < m_animations.size(); ++i)
- m_animations[i].resume();
+ for (auto& animation : m_animations)
+ animation.resume();
}
-void GraphicsLayerAnimations::remove(const String& name)
+void TextureMapperAnimations::apply(TextureMapperAnimation::Client& client)
{
- for (int i = m_animations.size() - 1; i >= 0; --i) {
- if (m_animations[i].name() == name)
- m_animations.remove(i);
- }
+ for (auto& animation : m_animations)
+ animation.apply(client);
}
-void GraphicsLayerAnimations::remove(const String& name, AnimatedPropertyID property)
+bool TextureMapperAnimations::hasActiveAnimationsOfType(AnimatedPropertyID type) const
{
- for (int i = m_animations.size() - 1; i >= 0; --i) {
- if (m_animations[i].name() == name && m_animations[i].property() == property)
- m_animations.remove(i);
- }
+ return std::any_of(m_animations.begin(), m_animations.end(),
+ [&type](const TextureMapperAnimation& animation) { return animation.isActive() && animation.keyframes().property() == type; });
}
-void GraphicsLayerAnimations::apply(GraphicsLayerAnimation::Client* client)
+bool TextureMapperAnimations::hasRunningAnimations() const
{
- for (size_t i = 0; i < m_animations.size(); ++i)
- m_animations[i].apply(client);
+ return std::any_of(m_animations.begin(), m_animations.end(),
+ [](const TextureMapperAnimation& animation) { return animation.state() == TextureMapperAnimation::AnimationState::Playing; });
}
-GraphicsLayerAnimations GraphicsLayerAnimations::getActiveAnimations() const
+TextureMapperAnimations TextureMapperAnimations::getActiveAnimations() const
{
- GraphicsLayerAnimations active;
- for (size_t i = 0; i < m_animations.size(); ++i) {
- if (m_animations[i].isActive())
- active.add(m_animations[i]);
+ TextureMapperAnimations active;
+ for (auto& animation : m_animations) {
+ if (animation.isActive())
+ active.add(animation);
}
return active;
}
-}
-#endif
+} // namespace WebCore
diff --git a/Source/WebCore/platform/graphics/GraphicsLayerAnimation.h b/Source/WebCore/platform/graphics/texmap/TextureMapperAnimation.h
index 2d2f68573..6a447d76e 100644
--- a/Source/WebCore/platform/graphics/GraphicsLayerAnimation.h
+++ b/Source/WebCore/platform/graphics/texmap/TextureMapperAnimation.h
@@ -17,60 +17,54 @@
Boston, MA 02110-1301, USA.
*/
-#ifndef GraphicsLayerAnimation_h
-#define GraphicsLayerAnimation_h
-
-#if USE(ACCELERATED_COMPOSITING)
+#ifndef TextureMapperAnimation_h
+#define TextureMapperAnimation_h
#include "GraphicsLayer.h"
-#include "TransformationMatrix.h"
-#include <wtf/HashMap.h>
-#include <wtf/text/StringHash.h>
namespace WebCore {
-class GraphicsLayerAnimation {
+class TransformationMatrix;
+
+class TextureMapperAnimation {
public:
- enum AnimationState { PlayingState, PausedState, StoppedState };
+ enum class AnimationState { Playing, Paused, Stopped };
+
class Client {
public:
virtual void setAnimatedTransform(const TransformationMatrix&) = 0;
virtual void setAnimatedOpacity(float) = 0;
-#if ENABLE(CSS_FILTERS)
virtual void setAnimatedFilters(const FilterOperations&) = 0;
-#endif
};
- GraphicsLayerAnimation()
+ TextureMapperAnimation()
: m_keyframes(AnimatedPropertyInvalid)
{ }
- GraphicsLayerAnimation(const String&, const KeyframeValueList&, const IntSize&, const Animation*, double, bool);
- void apply(Client*);
+ TextureMapperAnimation(const String&, const KeyframeValueList&, const FloatSize&, const Animation&, bool, double, double, AnimationState);
+ TextureMapperAnimation(const TextureMapperAnimation&);
+
+ void apply(Client&);
void pause(double);
void resume();
- double computeTotalRunningTime();
- AnimationState state() const { return m_state; }
- void setState(AnimationState s, double pauseTime = 0)
- {
- m_state = s;
- m_pauseTime = pauseTime;
- }
- AnimatedPropertyID property() const { return m_keyframes.property(); }
bool isActive() const;
- String name() const { return m_name; }
- IntSize boxSize() const { return m_boxSize; }
- double startTime() const { return m_startTime; }
- double pauseTime() const { return m_pauseTime; }
- PassRefPtr<Animation> animation() const { return m_animation.get(); }
+
+ const String& name() const { return m_name; }
const KeyframeValueList& keyframes() const { return m_keyframes; }
+ const FloatSize& boxSize() const { return m_boxSize; }
+ const RefPtr<Animation> animation() const { return m_animation; }
bool listsMatch() const { return m_listsMatch; }
+ double startTime() const { return m_startTime; }
+ double pauseTime() const { return m_pauseTime; }
+ AnimationState state() const { return m_state; }
private:
- void applyInternal(Client*, const AnimationValue& from, const AnimationValue& to, float progress);
+ void applyInternal(Client&, const AnimationValue& from, const AnimationValue& to, float progress);
+ double computeTotalRunningTime();
+
+ String m_name;
KeyframeValueList m_keyframes;
- IntSize m_boxSize;
+ FloatSize m_boxSize;
RefPtr<Animation> m_animation;
- String m_name;
bool m_listsMatch;
double m_startTime;
double m_pauseTime;
@@ -79,32 +73,32 @@ private:
AnimationState m_state;
};
-class GraphicsLayerAnimations {
+class TextureMapperAnimations {
public:
- GraphicsLayerAnimations() { }
+ TextureMapperAnimations() = default;
- void add(const GraphicsLayerAnimation&);
+ void add(const TextureMapperAnimation&);
void remove(const String& name);
void remove(const String& name, AnimatedPropertyID);
void pause(const String&, double);
void suspend(double);
void resume();
- void apply(GraphicsLayerAnimation::Client*);
+
+ void apply(TextureMapperAnimation::Client&);
+
bool isEmpty() const { return m_animations.isEmpty(); }
size_t size() const { return m_animations.size(); }
- const Vector<GraphicsLayerAnimation>& animations() const { return m_animations; }
- Vector<GraphicsLayerAnimation>& animations() { return m_animations; }
+ const Vector<TextureMapperAnimation>& animations() const { return m_animations; }
+ Vector<TextureMapperAnimation>& animations() { return m_animations; }
bool hasRunningAnimations() const;
bool hasActiveAnimationsOfType(AnimatedPropertyID type) const;
-
- GraphicsLayerAnimations getActiveAnimations() const;
+ TextureMapperAnimations getActiveAnimations() const;
private:
- Vector<GraphicsLayerAnimation> m_animations;
+ Vector<TextureMapperAnimation> m_animations;
};
}
-#endif
-#endif // GraphicsLayerAnimation_h
+#endif // TextureMapperAnimation_h
diff --git a/Source/WebCore/platform/graphics/texmap/TextureMapperBackingStore.cpp b/Source/WebCore/platform/graphics/texmap/TextureMapperBackingStore.cpp
index e68bc0914..ebed19a89 100644
--- a/Source/WebCore/platform/graphics/texmap/TextureMapperBackingStore.cpp
+++ b/Source/WebCore/platform/graphics/texmap/TextureMapperBackingStore.cpp
@@ -19,18 +19,12 @@
#include "config.h"
-#if USE(ACCELERATED_COMPOSITING) && USE(TEXTURE_MAPPER)
#include "TextureMapperBackingStore.h"
#include "GraphicsLayer.h"
#include "ImageBuffer.h"
#include "TextureMapper.h"
-#if USE(GRAPHICS_SURFACE)
-#include "GraphicsSurface.h"
-#include "TextureMapperGL.h"
-#endif
-
namespace WebCore {
unsigned TextureMapperBackingStore::calculateExposedTileEdges(const FloatRect& totalRect, const FloatRect& tileRect)
@@ -48,4 +42,3 @@ unsigned TextureMapperBackingStore::calculateExposedTileEdges(const FloatRect& t
}
}
-#endif
diff --git a/Source/WebCore/platform/graphics/texmap/TextureMapperBackingStore.h b/Source/WebCore/platform/graphics/texmap/TextureMapperBackingStore.h
index 4bae45b4f..914604171 100644
--- a/Source/WebCore/platform/graphics/texmap/TextureMapperBackingStore.h
+++ b/Source/WebCore/platform/graphics/texmap/TextureMapperBackingStore.h
@@ -20,27 +20,21 @@
#ifndef TextureMapperBackingStore_h
#define TextureMapperBackingStore_h
-#if USE(ACCELERATED_COMPOSITING) && USE(TEXTURE_MAPPER)
-
#include "FloatRect.h"
#include "Image.h"
#include "TextureMapper.h"
#include "TextureMapperPlatformLayer.h"
#include <wtf/RefPtr.h>
-#if USE(GRAPHICS_SURFACE)
-#include "GraphicsSurface.h"
-#endif
-
namespace WebCore {
class GraphicsLayer;
class TextureMapperBackingStore : public TextureMapperPlatformLayer, public RefCounted<TextureMapperBackingStore> {
public:
- virtual PassRefPtr<BitmapTexture> texture() const = 0;
- virtual void paintToTextureMapper(TextureMapper*, const FloatRect&, const TransformationMatrix&, float) = 0;
- virtual void drawRepaintCounter(TextureMapper*, int /* repaintCount */, const Color&, const FloatRect&, const TransformationMatrix&) { }
+ virtual RefPtr<BitmapTexture> texture() const = 0;
+ void paintToTextureMapper(TextureMapper&, const FloatRect&, const TransformationMatrix&, float) override = 0;
+ virtual void drawRepaintCounter(TextureMapper&, int /* repaintCount */, const Color&, const FloatRect&, const TransformationMatrix&) { }
virtual ~TextureMapperBackingStore() { }
protected:
@@ -48,6 +42,5 @@ protected:
};
}
-#endif
#endif // TextureMapperBackingStore_h
diff --git a/Source/WebCore/platform/graphics/texmap/TextureMapperFPSCounter.cpp b/Source/WebCore/platform/graphics/texmap/TextureMapperFPSCounter.cpp
index d74477010..db6a7a0d3 100644
--- a/Source/WebCore/platform/graphics/texmap/TextureMapperFPSCounter.cpp
+++ b/Source/WebCore/platform/graphics/texmap/TextureMapperFPSCounter.cpp
@@ -21,8 +21,6 @@
#include "config.h"
-#if USE(ACCELERATED_COMPOSITING) && USE(TEXTURE_MAPPER)
-
#include "TextureMapperFPSCounter.h"
#include "TextureMapper.h"
@@ -46,7 +44,7 @@ TextureMapperFPSCounter::TextureMapperFPSCounter()
}
}
-void TextureMapperFPSCounter::updateFPSAndDisplay(TextureMapper* textureMapper, const FloatPoint& location, const TransformationMatrix& matrix)
+void TextureMapperFPSCounter::updateFPSAndDisplay(TextureMapper& textureMapper, const FloatPoint& location, const TransformationMatrix& matrix)
{
if (!m_isShowingFPS)
return;
@@ -59,9 +57,7 @@ void TextureMapperFPSCounter::updateFPSAndDisplay(TextureMapper* textureMapper,
m_fpsTimestamp += delta;
}
- textureMapper->drawNumber(m_lastFPS, Color::black, location, matrix);
+ textureMapper.drawNumber(m_lastFPS, Color::black, location, matrix);
}
} // namespace WebCore
-
-#endif // USE(ACCELERATED_COMPOSITING)
diff --git a/Source/WebCore/platform/graphics/texmap/TextureMapperFPSCounter.h b/Source/WebCore/platform/graphics/texmap/TextureMapperFPSCounter.h
index 006237383..8aa4a9425 100644
--- a/Source/WebCore/platform/graphics/texmap/TextureMapperFPSCounter.h
+++ b/Source/WebCore/platform/graphics/texmap/TextureMapperFPSCounter.h
@@ -22,7 +22,6 @@
#ifndef TextureMapperFPSCounter_h
#define TextureMapperFPSCounter_h
-#if USE(ACCELERATED_COMPOSITING) && USE(TEXTURE_MAPPER)
#include "FloatPoint.h"
#include "TransformationMatrix.h"
#include <wtf/Noncopyable.h>
@@ -35,7 +34,7 @@ class TextureMapperFPSCounter {
WTF_MAKE_FAST_ALLOCATED;
public:
TextureMapperFPSCounter();
- void updateFPSAndDisplay(TextureMapper*, const FloatPoint& = FloatPoint::zero(), const TransformationMatrix& = TransformationMatrix());
+ void updateFPSAndDisplay(TextureMapper&, const FloatPoint& = FloatPoint::zero(), const TransformationMatrix& = TransformationMatrix());
private:
bool m_isShowingFPS;
@@ -47,8 +46,4 @@ private:
} // namespace WebCore
-#endif // USE(ACCELERATED_COMPOSITING)
-
#endif // TextureMapperFPSCounter_h
-
-
diff --git a/Source/WebCore/platform/graphics/texmap/TextureMapperGC3DPlatformLayer.cpp b/Source/WebCore/platform/graphics/texmap/TextureMapperGC3DPlatformLayer.cpp
new file mode 100644
index 000000000..9a9f4fb06
--- /dev/null
+++ b/Source/WebCore/platform/graphics/texmap/TextureMapperGC3DPlatformLayer.cpp
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 2011, 2012, 2017 Igalia S.L.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA
+ */
+
+#include "config.h"
+#include "TextureMapperGC3DPlatformLayer.h"
+
+#if ENABLE(GRAPHICS_CONTEXT_3D) && USE(TEXTURE_MAPPER)
+
+#if USE(OPENGL_ES_2)
+#include <GLES2/gl2.h>
+#include <GLES2/gl2ext.h>
+#endif
+
+#include "BitmapTextureGL.h"
+#include "GLContext.h"
+#include "TextureMapperPlatformLayerBuffer.h"
+
+namespace WebCore {
+
+TextureMapperGC3DPlatformLayer::TextureMapperGC3DPlatformLayer(GraphicsContext3D& context, GraphicsContext3D::RenderStyle renderStyle)
+ : m_context(context)
+ , m_renderStyle(renderStyle)
+{
+ switch (renderStyle) {
+ case GraphicsContext3D::RenderOffscreen:
+ m_glContext = GLContext::createOffscreenContext(&PlatformDisplay::sharedDisplayForCompositing());
+ break;
+ case GraphicsContext3D::RenderToCurrentGLContext:
+ break;
+ case GraphicsContext3D::RenderDirectlyToHostWindow:
+ ASSERT_NOT_REACHED();
+ break;
+ }
+
+#if USE(COORDINATED_GRAPHICS_THREADED)
+ if (m_renderStyle == GraphicsContext3D::RenderOffscreen)
+ m_platformLayerProxy = adoptRef(new TextureMapperPlatformLayerProxy());
+#endif
+}
+
+TextureMapperGC3DPlatformLayer::~TextureMapperGC3DPlatformLayer()
+{
+#if !USE(COORDINATED_GRAPHICS_THREADED)
+ if (client())
+ client()->platformLayerWillBeDestroyed();
+#endif
+}
+
+bool TextureMapperGC3DPlatformLayer::makeContextCurrent()
+{
+ return m_glContext ? m_glContext->makeContextCurrent() : false;
+}
+
+PlatformGraphicsContext3D TextureMapperGC3DPlatformLayer::platformContext()
+{
+ return m_glContext ? m_glContext->platformContext() : GLContext::current()->platformContext();
+}
+
+#if USE(COORDINATED_GRAPHICS_THREADED)
+RefPtr<TextureMapperPlatformLayerProxy> TextureMapperGC3DPlatformLayer::proxy() const
+{
+ return m_platformLayerProxy.copyRef();
+}
+
+void TextureMapperGC3DPlatformLayer::swapBuffersIfNeeded()
+{
+ ASSERT(m_renderStyle == GraphicsContext3D::RenderOffscreen);
+ if (m_context.layerComposited())
+ return;
+
+ m_context.prepareTexture();
+ IntSize textureSize(m_context.m_currentWidth, m_context.m_currentHeight);
+ TextureMapperGL::Flags flags = TextureMapperGL::ShouldFlipTexture | (m_context.m_attrs.alpha ? TextureMapperGL::ShouldBlend : 0);
+
+ {
+ LockHolder holder(m_platformLayerProxy->lock());
+ m_platformLayerProxy->pushNextBuffer(std::make_unique<TextureMapperPlatformLayerBuffer>(m_context.m_compositorTexture, textureSize, flags));
+ }
+
+ m_context.markLayerComposited();
+}
+#else
+void TextureMapperGC3DPlatformLayer::paintToTextureMapper(TextureMapper& textureMapper, const FloatRect& targetRect, const TransformationMatrix& matrix, float opacity)
+{
+ if (!m_glContext)
+ return;
+
+ ASSERT(m_renderStyle == GraphicsContext3D::RenderOffscreen);
+
+ m_context.markLayerComposited();
+
+#if USE(TEXTURE_MAPPER_GL)
+ if (m_context.m_attrs.antialias && m_context.m_state.boundFBO == m_context.m_multisampleFBO) {
+ GLContext* previousActiveContext = GLContext::current();
+ if (previousActiveContext != m_glContext.get())
+ m_context.makeContextCurrent();
+
+ m_context.resolveMultisamplingIfNecessary();
+ ::glBindFramebuffer(GraphicsContext3D::FRAMEBUFFER, m_context.m_state.boundFBO);
+
+ if (previousActiveContext && previousActiveContext != m_glContext.get())
+ previousActiveContext->makeContextCurrent();
+ }
+
+ TextureMapperGL& texmapGL = static_cast<TextureMapperGL&>(textureMapper);
+ TextureMapperGL::Flags flags = TextureMapperGL::ShouldFlipTexture | (m_context.m_attrs.alpha ? TextureMapperGL::ShouldBlend : 0);
+ IntSize textureSize(m_context.m_currentWidth, m_context.m_currentHeight);
+ texmapGL.drawTexture(m_context.m_texture, flags, textureSize, targetRect, matrix, opacity);
+#endif // USE(TEXTURE_MAPPER_GL)
+}
+#endif // USE(COORDINATED_GRAPHICS_THREADED)
+
+} // namespace WebCore
+
+#endif // ENABLE(GRAPHICS_CONTEXT_3D) && USE(TEXTURE_MAPPER)
diff --git a/Source/WebCore/platform/graphics/texmap/TextureMapperGC3DPlatformLayer.h b/Source/WebCore/platform/graphics/texmap/TextureMapperGC3DPlatformLayer.h
new file mode 100644
index 000000000..dd3f74fce
--- /dev/null
+++ b/Source/WebCore/platform/graphics/texmap/TextureMapperGC3DPlatformLayer.h
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2011, 2012, 2017 Igalia S.L.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA
+ */
+
+#pragma once
+
+#if ENABLE(GRAPHICS_CONTEXT_3D) && USE(TEXTURE_MAPPER)
+
+#include "GraphicsContext3D.h"
+#include "PlatformLayer.h"
+#include "TextureMapperPlatformLayer.h"
+#include "TextureMapperPlatformLayerProxy.h"
+
+namespace WebCore {
+
+class BitmapTextureGL;
+class GLContext;
+class TextureMapperPlatformLayerProxy;
+
+class TextureMapperGC3DPlatformLayer : public PlatformLayer {
+public:
+ TextureMapperGC3DPlatformLayer(GraphicsContext3D&, GraphicsContext3D::RenderStyle);
+ virtual ~TextureMapperGC3DPlatformLayer();
+
+ bool makeContextCurrent();
+ PlatformGraphicsContext3D platformContext();
+ GraphicsContext3D::RenderStyle renderStyle() { return m_renderStyle; }
+
+#if USE(COORDINATED_GRAPHICS_THREADED)
+ RefPtr<TextureMapperPlatformLayerProxy> proxy() const override;
+ void swapBuffersIfNeeded() override;
+#else
+ virtual void paintToTextureMapper(TextureMapper&, const FloatRect& target, const TransformationMatrix&, float opacity);
+#endif
+
+private:
+ GraphicsContext3D& m_context;
+ std::unique_ptr<GLContext> m_glContext;
+ GraphicsContext3D::RenderStyle m_renderStyle;
+
+#if USE(COORDINATED_GRAPHICS_THREADED)
+ RefPtr<TextureMapperPlatformLayerProxy> m_platformLayerProxy;
+ RefPtr<BitmapTextureGL> m_compositorTexture;
+#endif
+};
+
+} // namespace WebCore
+
+#endif // ENABLE(GRAPHICS_CONTEXT_3D) && USE(TEXTURE_MAPPER)
diff --git a/Source/WebCore/platform/graphics/texmap/TextureMapperGL.cpp b/Source/WebCore/platform/graphics/texmap/TextureMapperGL.cpp
index 05b884bd5..7464d047e 100644
--- a/Source/WebCore/platform/graphics/texmap/TextureMapperGL.cpp
+++ b/Source/WebCore/platform/graphics/texmap/TextureMapperGL.cpp
@@ -22,6 +22,10 @@
#include "config.h"
#include "TextureMapperGL.h"
+#if USE(TEXTURE_MAPPER_GL)
+
+#include "BitmapTextureGL.h"
+#include "BitmapTexturePool.h"
#include "Extensions3D.h"
#include "FilterOperations.h"
#include "GraphicsContext.h"
@@ -31,9 +35,10 @@
#include "TextureMapperShaderProgram.h"
#include "Timer.h"
#include <wtf/HashMap.h>
+#include <wtf/NeverDestroyed.h>
#include <wtf/PassRefPtr.h>
#include <wtf/RefCounted.h>
-#include <wtf/TemporaryChange.h>
+#include <wtf/SetForScope.h>
#if USE(CAIRO)
#include "CairoUtilities.h"
@@ -42,191 +47,84 @@
#include <wtf/text/CString.h>
#endif
-#if !USE(TEXMAP_OPENGL_ES_2)
-// FIXME: Move to Extensions3D.h.
-#define GL_UNSIGNED_INT_8_8_8_8_REV 0x8367
-#define GL_UNPACK_ROW_LENGTH 0x0CF2
-#define GL_UNPACK_SKIP_PIXELS 0x0CF4
-#define GL_UNPACK_SKIP_ROWS 0x0CF3
-#endif
-
-#if USE(ACCELERATED_COMPOSITING) && USE(TEXTURE_MAPPER)
-
namespace WebCore {
-struct TextureMapperGLData {
+
+class TextureMapperGLData {
WTF_MAKE_FAST_ALLOCATED;
public:
- struct SharedGLData : public RefCounted<SharedGLData> {
+ explicit TextureMapperGLData(GraphicsContext3D&);
+ ~TextureMapperGLData();
- typedef HashMap<PlatformGraphicsContext3D, SharedGLData*> GLContextDataMap;
- static GLContextDataMap& glContextDataMap()
- {
- static GLContextDataMap map;
- return map;
- }
+ void initializeStencil();
+ Platform3DObject getStaticVBO(GC3Denum target, GC3Dsizeiptr, const void* data);
+ Ref<TextureMapperShaderProgram> getShaderProgram(TextureMapperShaderProgram::Options);
+
+ TransformationMatrix projectionMatrix;
+ TextureMapper::PaintFlags PaintFlags { 0 };
+ GC3Dint previousProgram { 0 };
+ GC3Dint targetFrameBuffer { 0 };
+ bool didModifyStencil { false };
+ GC3Dint previousScissorState { 0 };
+ GC3Dint previousDepthState { 0 };
+ GC3Dint viewport[4] { 0, };
+ GC3Dint previousScissor[4] { 0, };
+ RefPtr<BitmapTexture> currentSurface;
+ const BitmapTextureGL::FilterInfo* filterInfo { nullptr };
- static PassRefPtr<SharedGLData> currentSharedGLData(GraphicsContext3D* context)
+private:
+ class SharedGLData : public RefCounted<SharedGLData> {
+ public:
+ static Ref<SharedGLData> currentSharedGLData(GraphicsContext3D& context)
{
- GLContextDataMap::iterator it = glContextDataMap().find(context->platformGraphicsContext3D());
- if (it != glContextDataMap().end())
- return it->value;
+ auto it = contextDataMap().find(context.platformGraphicsContext3D());
+ if (it != contextDataMap().end())
+ return *it->value;
- return adoptRef(new SharedGLData(context));
+ Ref<SharedGLData> data = adoptRef(*new SharedGLData(context));
+ contextDataMap().add(context.platformGraphicsContext3D(), data.ptr());
+ return data;
}
- PassRefPtr<TextureMapperShaderProgram> getShaderProgram(TextureMapperShaderProgram::Options options)
+ ~SharedGLData()
{
- HashMap<TextureMapperShaderProgram::Options, RefPtr<TextureMapperShaderProgram> >::AddResult result = m_programs.add(options, nullptr);
- if (result.isNewEntry)
- result.iterator->value = TextureMapperShaderProgram::create(m_context, options);
-
- return result.iterator->value;
+ ASSERT(std::any_of(contextDataMap().begin(), contextDataMap().end(),
+ [this](auto& entry) { return entry.value == this; }));
+ contextDataMap().removeIf([this](auto& entry) { return entry.value == this; });
}
- HashMap<TextureMapperShaderProgram::Options, RefPtr<TextureMapperShaderProgram> > m_programs;
- RefPtr<GraphicsContext3D> m_context;
+ private:
+ friend class TextureMapperGLData;
- explicit SharedGLData(GraphicsContext3D* context)
- : m_context(context)
+ using GLContextDataMap = HashMap<PlatformGraphicsContext3D, SharedGLData*>;
+ static GLContextDataMap& contextDataMap()
{
- glContextDataMap().add(context->platformGraphicsContext3D(), this);
+ static NeverDestroyed<GLContextDataMap> map;
+ return map;
}
- ~SharedGLData()
+ explicit SharedGLData(GraphicsContext3D& context)
{
- GLContextDataMap::const_iterator end = glContextDataMap().end();
- GLContextDataMap::iterator it;
- for (it = glContextDataMap().begin(); it != end; ++it) {
- if (it->value == this)
- break;
- }
-
- ASSERT(it != end);
- glContextDataMap().remove(it);
+ contextDataMap().add(context.platformGraphicsContext3D(), this);
}
- };
-
- SharedGLData& sharedGLData() const
- {
- return *sharedData;
- }
- void initializeStencil();
-
- explicit TextureMapperGLData(GraphicsContext3D* context)
- : context(context)
- , PaintFlags(0)
- , previousProgram(0)
- , targetFrameBuffer(0)
- , didModifyStencil(false)
- , previousScissorState(0)
- , previousDepthState(0)
- , sharedData(TextureMapperGLData::SharedGLData::currentSharedGLData(this->context))
-#if ENABLE(CSS_FILTERS)
- , filterInfo(0)
-#endif
- { }
-
- ~TextureMapperGLData();
- Platform3DObject getStaticVBO(GC3Denum target, GC3Dsizeiptr, const void* data);
+ HashMap<TextureMapperShaderProgram::Options, RefPtr<TextureMapperShaderProgram>> m_programs;
+ };
- GraphicsContext3D* context;
- TransformationMatrix projectionMatrix;
- TextureMapper::PaintFlags PaintFlags;
- GC3Dint previousProgram;
- GC3Dint targetFrameBuffer;
- bool didModifyStencil;
- GC3Dint previousScissorState;
- GC3Dint previousDepthState;
- GC3Dint viewport[4];
- GC3Dint previousScissor[4];
- RefPtr<SharedGLData> sharedData;
- RefPtr<BitmapTexture> currentSurface;
- HashMap<const void*, Platform3DObject> vbos;
-#if ENABLE(CSS_FILTERS)
- const BitmapTextureGL::FilterInfo* filterInfo;
-#endif
+ GraphicsContext3D& m_context;
+ Ref<SharedGLData> m_sharedGLData;
+ HashMap<const void*, Platform3DObject> m_vbos;
};
-Platform3DObject TextureMapperGLData::getStaticVBO(GC3Denum target, GC3Dsizeiptr size, const void* data)
+TextureMapperGLData::TextureMapperGLData(GraphicsContext3D& context)
+ : m_context(context)
+ , m_sharedGLData(SharedGLData::currentSharedGLData(m_context))
{
- HashMap<const void*, Platform3DObject>::AddResult result = vbos.add(data, 0);
- if (result.isNewEntry) {
- Platform3DObject vbo = context->createBuffer();
- context->bindBuffer(target, vbo);
- context->bufferData(target, size, data, GraphicsContext3D::STATIC_DRAW);
- result.iterator->value = vbo;
- }
-
- return result.iterator->value;
}
TextureMapperGLData::~TextureMapperGLData()
{
- HashMap<const void*, Platform3DObject>::iterator end = vbos.end();
- for (HashMap<const void*, Platform3DObject>::iterator it = vbos.begin(); it != end; ++it)
- context->deleteBuffer(it->value);
-}
-
-void TextureMapperGL::ClipStack::reset(const IntRect& rect, TextureMapperGL::ClipStack::YAxisMode mode)
-{
- clipStack.clear();
- size = rect.size();
- yAxisMode = mode;
- clipState = TextureMapperGL::ClipState(rect);
- clipStateDirty = true;
-}
-
-void TextureMapperGL::ClipStack::intersect(const IntRect& rect)
-{
- clipState.scissorBox.intersect(rect);
- clipStateDirty = true;
-}
-
-void TextureMapperGL::ClipStack::setStencilIndex(int stencilIndex)
-{
- clipState.stencilIndex = stencilIndex;
- clipStateDirty = true;
-}
-
-void TextureMapperGL::ClipStack::push()
-{
- clipStack.append(clipState);
- clipStateDirty = true;
-}
-
-void TextureMapperGL::ClipStack::pop()
-{
- if (clipStack.isEmpty())
- return;
- clipState = clipStack.last();
- clipStack.removeLast();
- clipStateDirty = true;
-}
-
-void TextureMapperGL::ClipStack::apply(GraphicsContext3D* context)
-{
- if (clipState.scissorBox.isEmpty())
- return;
-
- context->scissor(clipState.scissorBox.x(),
- (yAxisMode == InvertedYAxis) ? size.height() - clipState.scissorBox.maxY() : clipState.scissorBox.y(),
- clipState.scissorBox.width(), clipState.scissorBox.height());
- context->stencilOp(GraphicsContext3D::KEEP, GraphicsContext3D::KEEP, GraphicsContext3D::KEEP);
- context->stencilFunc(GraphicsContext3D::EQUAL, clipState.stencilIndex - 1, clipState.stencilIndex - 1);
- if (clipState.stencilIndex == 1)
- context->disable(GraphicsContext3D::STENCIL_TEST);
- else
- context->enable(GraphicsContext3D::STENCIL_TEST);
-}
-
-void TextureMapperGL::ClipStack::applyIfNeeded(GraphicsContext3D* context)
-{
- if (!clipStateDirty)
- return;
-
- clipStateDirty = false;
- apply(context);
+ for (auto& entry : m_vbos)
+ m_context.deleteBuffer(entry.value);
}
void TextureMapperGLData::initializeStencil()
@@ -239,30 +137,45 @@ void TextureMapperGLData::initializeStencil()
if (didModifyStencil)
return;
- context->clearStencil(0);
- context->clear(GraphicsContext3D::STENCIL_BUFFER_BIT);
+ m_context.clearStencil(0);
+ m_context.clear(GraphicsContext3D::STENCIL_BUFFER_BIT);
didModifyStencil = true;
}
-BitmapTextureGL* toBitmapTextureGL(BitmapTexture* texture)
+Platform3DObject TextureMapperGLData::getStaticVBO(GC3Denum target, GC3Dsizeiptr size, const void* data)
{
- if (!texture || !texture->isBackedByOpenGL())
- return 0;
+ auto addResult = m_vbos.ensure(data,
+ [this, target, size, data] {
+ Platform3DObject vbo = m_context.createBuffer();
+ m_context.bindBuffer(target, vbo);
+ m_context.bufferData(target, size, data, GraphicsContext3D::STATIC_DRAW);
+ return vbo;
+ });
+ return addResult.iterator->value;
+}
- return static_cast<BitmapTextureGL*>(texture);
+Ref<TextureMapperShaderProgram> TextureMapperGLData::getShaderProgram(TextureMapperShaderProgram::Options options)
+{
+ auto addResult = m_sharedGLData->m_programs.ensure(options,
+ [this, options] { return TextureMapperShaderProgram::create(Ref<GraphicsContext3D>(m_context), options); });
+ return *addResult.iterator->value;
}
TextureMapperGL::TextureMapperGL()
- : TextureMapper(OpenGLMode)
- , m_enableEdgeDistanceAntialiasing(false)
+ : m_enableEdgeDistanceAntialiasing(false)
{
m_context3D = GraphicsContext3D::createForCurrentGLContext();
- m_data = new TextureMapperGLData(m_context3D.get());
+ ASSERT(m_context3D);
+
+ m_data = new TextureMapperGLData(*m_context3D);
+#if USE(TEXTURE_MAPPER_GL)
+ m_texturePool = std::make_unique<BitmapTexturePool>(m_context3D.copyRef());
+#endif
}
-TextureMapperGL::ClipStack& TextureMapperGL::clipStack()
+ClipStack& TextureMapperGL::clipStack()
{
- return data().currentSurface ? toBitmapTextureGL(data().currentSurface.get())->m_clipStack : m_clipStack;
+ return data().currentSurface ? toBitmapTextureGL(data().currentSurface.get())->clipStack() : m_clipStack;
}
void TextureMapperGL::beginPainting(PaintFlags flags)
@@ -276,7 +189,7 @@ void TextureMapperGL::beginPainting(PaintFlags flags)
m_context3D->depthMask(0);
m_context3D->getIntegerv(GraphicsContext3D::VIEWPORT, data().viewport);
m_context3D->getIntegerv(GraphicsContext3D::SCISSOR_BOX, data().previousScissor);
- m_clipStack.reset(IntRect(0, 0, data().viewport[2], data().viewport[3]), ClipStack::InvertedYAxis);
+ m_clipStack.reset(IntRect(0, 0, data().viewport[2], data().viewport[3]), flags & PaintingMirrored ? ClipStack::YAxisMode::Default : ClipStack::YAxisMode::Inverted);
m_context3D->getIntegerv(GraphicsContext3D::FRAMEBUFFER_BINDING, &data().targetFrameBuffer);
data().PaintFlags = flags;
bindSurface(0);
@@ -308,7 +221,7 @@ void TextureMapperGL::drawBorder(const Color& color, float width, const FloatRec
if (clipStack().isCurrentScissorBoxEmpty())
return;
- RefPtr<TextureMapperShaderProgram> program = data().sharedGLData().getShaderProgram(TextureMapperShaderProgram::SolidColor);
+ Ref<TextureMapperShaderProgram> program = data().getShaderProgram(TextureMapperShaderProgram::SolidColor);
m_context3D->useProgram(program->programID());
float r, g, b, a;
@@ -316,7 +229,7 @@ void TextureMapperGL::drawBorder(const Color& color, float width, const FloatRec
m_context3D->uniform4f(program->colorLocation(), r, g, b, a);
m_context3D->lineWidth(width);
- draw(targetRect, modelViewMatrix, program.get(), GraphicsContext3D::LINE_LOOP, color.hasAlpha() ? ShouldBlend : 0);
+ draw(targetRect, modelViewMatrix, program.get(), GraphicsContext3D::LINE_LOOP, !color.isOpaque() ? ShouldBlend : 0);
}
// FIXME: drawNumber() should save a number texture-atlas and re-use whenever possible.
@@ -333,9 +246,15 @@ void TextureMapperGL::drawNumber(int number, const Color& color, const FloatPoin
cairo_surface_t* surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, width, height);
cairo_t* cr = cairo_create(surface);
- float r, g, b, a;
- color.getRGBA(r, g, b, a);
- cairo_set_source_rgba(cr, b, g, r, a); // Since we won't swap R+B when uploading a texture, paint with the swapped R+B color.
+ // Since we won't swap R+B when uploading a texture, paint with the swapped R+B color.
+ if (color.isExtended())
+ cairo_set_source_rgba(cr, color.asExtended().blue(), color.asExtended().green(), color.asExtended().red(), color.asExtended().alpha());
+ else {
+ float r, g, b, a;
+ color.getRGBA(r, g, b, a);
+ cairo_set_source_rgba(cr, b, g, r, a);
+ }
+
cairo_rectangle(cr, 0, 0, width, height);
cairo_fill(cr);
@@ -367,8 +286,6 @@ void TextureMapperGL::drawNumber(int number, const Color& color, const FloatPoin
#endif
}
-#if ENABLE(CSS_FILTERS)
-
static TextureMapperShaderProgram::Options optionsForFilterType(FilterOperation::OperationType type, unsigned pass)
{
switch (type) {
@@ -399,27 +316,6 @@ static TextureMapperShaderProgram::Options optionsForFilterType(FilterOperation:
}
}
-static unsigned getPassesRequiredForFilter(FilterOperation::OperationType type)
-{
- switch (type) {
- case FilterOperation::GRAYSCALE:
- case FilterOperation::SEPIA:
- case FilterOperation::SATURATE:
- case FilterOperation::HUE_ROTATE:
- case FilterOperation::INVERT:
- case FilterOperation::BRIGHTNESS:
- case FilterOperation::CONTRAST:
- case FilterOperation::OPACITY:
- return 1;
- case FilterOperation::BLUR:
- case FilterOperation::DROP_SHADOW:
- // We use two-passes (vertical+horizontal) for blur and drop-shadow.
- return 2;
- default:
- return 0;
- }
-}
-
// Create a normal distribution of 21 values between -2 and 2.
static const unsigned GaussianKernelHalfWidth = 11;
static const float GaussianKernelStep = 0.2;
@@ -453,23 +349,23 @@ static float* gaussianKernel()
return kernel;
}
-static void prepareFilterProgram(TextureMapperShaderProgram* program, const FilterOperation& operation, unsigned pass, const IntSize& size, GC3Duint contentTexture)
+static void prepareFilterProgram(TextureMapperShaderProgram& program, const FilterOperation& operation, unsigned pass, const IntSize& size, GC3Duint contentTexture)
{
- RefPtr<GraphicsContext3D> context = program->context();
- context->useProgram(program->programID());
+ Ref<GraphicsContext3D> context = program.context();
+ context->useProgram(program.programID());
switch (operation.type()) {
case FilterOperation::GRAYSCALE:
case FilterOperation::SEPIA:
case FilterOperation::SATURATE:
case FilterOperation::HUE_ROTATE:
- context->uniform1f(program->filterAmountLocation(), static_cast<const BasicColorMatrixFilterOperation&>(operation).amount());
+ context->uniform1f(program.filterAmountLocation(), static_cast<const BasicColorMatrixFilterOperation&>(operation).amount());
break;
case FilterOperation::INVERT:
case FilterOperation::BRIGHTNESS:
case FilterOperation::CONTRAST:
case FilterOperation::OPACITY:
- context->uniform1f(program->filterAmountLocation(), static_cast<const BasicComponentTransferFilterOperation&>(operation).amount());
+ context->uniform1f(program.filterAmountLocation(), static_cast<const BasicComponentTransferFilterOperation&>(operation).amount());
break;
case FilterOperation::BLUR: {
const BlurFilterOperation& blur = static_cast<const BlurFilterOperation&>(operation);
@@ -481,29 +377,29 @@ static void prepareFilterProgram(TextureMapperShaderProgram* program, const Filt
else
radius.setWidth(floatValueForLength(blur.stdDeviation(), size.width()) / size.width());
- context->uniform2f(program->blurRadiusLocation(), radius.width(), radius.height());
- context->uniform1fv(program->gaussianKernelLocation(), GaussianKernelHalfWidth, gaussianKernel());
+ context->uniform2f(program.blurRadiusLocation(), radius.width(), radius.height());
+ context->uniform1fv(program.gaussianKernelLocation(), GaussianKernelHalfWidth, gaussianKernel());
break;
}
case FilterOperation::DROP_SHADOW: {
const DropShadowFilterOperation& shadow = static_cast<const DropShadowFilterOperation&>(operation);
- context->uniform1fv(program->gaussianKernelLocation(), GaussianKernelHalfWidth, gaussianKernel());
+ context->uniform1fv(program.gaussianKernelLocation(), GaussianKernelHalfWidth, gaussianKernel());
switch (pass) {
case 0:
// First pass: horizontal alpha blur.
- context->uniform2f(program->blurRadiusLocation(), shadow.stdDeviation() / float(size.width()), 0);
- context->uniform2f(program->shadowOffsetLocation(), float(shadow.location().x()) / float(size.width()), float(shadow.location().y()) / float(size.height()));
+ context->uniform2f(program.blurRadiusLocation(), shadow.stdDeviation() / float(size.width()), 0);
+ context->uniform2f(program.shadowOffsetLocation(), float(shadow.location().x()) / float(size.width()), float(shadow.location().y()) / float(size.height()));
break;
case 1:
// Second pass: we need the shadow color and the content texture for compositing.
float r, g, b, a;
Color(premultipliedARGBFromColor(shadow.color())).getRGBA(r, g, b, a);
- context->uniform4f(program->colorLocation(), r, g, b, a);
- context->uniform2f(program->blurRadiusLocation(), 0, shadow.stdDeviation() / float(size.height()));
- context->uniform2f(program->shadowOffsetLocation(), 0, 0);
+ context->uniform4f(program.colorLocation(), r, g, b, a);
+ context->uniform2f(program.blurRadiusLocation(), 0, shadow.stdDeviation() / float(size.height()));
+ context->uniform2f(program.shadowOffsetLocation(), 0, 0);
context->activeTexture(GraphicsContext3D::TEXTURE1);
context->bindTexture(GraphicsContext3D::TEXTURE_2D, contentTexture);
- context->uniform1i(program->contentTextureLocation(), 1);
+ context->uniform1i(program.contentTextureLocation(), 1);
break;
}
break;
@@ -512,7 +408,6 @@ static void prepareFilterProgram(TextureMapperShaderProgram* program, const Filt
break;
}
}
-#endif
void TextureMapperGL::drawTexture(const BitmapTexture& texture, const FloatRect& targetRect, const TransformationMatrix& matrix, float opacity, unsigned exposedEdges)
{
@@ -523,13 +418,21 @@ void TextureMapperGL::drawTexture(const BitmapTexture& texture, const FloatRect&
return;
const BitmapTextureGL& textureGL = static_cast<const BitmapTextureGL&>(texture);
-#if ENABLE(CSS_FILTERS)
- TemporaryChange<const BitmapTextureGL::FilterInfo*> filterInfo(data().filterInfo, textureGL.filterInfo());
-#endif
+ SetForScope<const BitmapTextureGL::FilterInfo*> filterInfo(data().filterInfo, textureGL.filterInfo());
drawTexture(textureGL.id(), textureGL.isOpaque() ? 0 : ShouldBlend, textureGL.size(), targetRect, matrix, opacity, exposedEdges);
}
+static bool driverSupportsNPOTTextures(GraphicsContext3D& context)
+{
+ if (context.isGLES2Compliant()) {
+ static bool supportsNPOTTextures = context.getExtensions().supports("GL_OES_texture_npot");
+ return supportsNPOTTextures;
+ }
+
+ return true;
+}
+
void TextureMapperGL::drawTexture(Platform3DObject texture, Flags flags, const IntSize& textureSize, const FloatRect& targetRect, const TransformationMatrix& modelViewMatrix, float opacity, unsigned exposedEdges)
{
bool useRect = flags & ShouldUseARBTextureRect;
@@ -546,8 +449,9 @@ void TextureMapperGL::drawTexture(Platform3DObject texture, Flags flags, const I
options |= TextureMapperShaderProgram::Antialiasing;
flags |= ShouldAntialias;
}
+ if (wrapMode() == RepeatWrap && !driverSupportsNPOTTextures(*m_context3D))
+ options |= TextureMapperShaderProgram::ManualRepeat;
-#if ENABLE(CSS_FILTERS)
RefPtr<FilterOperation> filter = data().filterInfo ? data().filterInfo->filter: 0;
GC3Duint filterContentTextureID = 0;
@@ -558,18 +462,14 @@ void TextureMapperGL::drawTexture(Platform3DObject texture, Flags flags, const I
if (filter->affectsOpacity())
flags |= ShouldBlend;
}
-#endif
if (useAntialiasing || opacity < 1)
flags |= ShouldBlend;
- RefPtr<TextureMapperShaderProgram> program;
- program = data().sharedGLData().getShaderProgram(options);
+ Ref<TextureMapperShaderProgram> program = data().getShaderProgram(options);
-#if ENABLE(CSS_FILTERS)
if (filter)
prepareFilterProgram(program.get(), *filter.get(), data().filterInfo->pass, textureSize, filterContentTextureID);
-#endif
drawTexturedQuadWithProgram(program.get(), texture, flags, textureSize, targetRect, modelViewMatrix, opacity);
}
@@ -583,7 +483,7 @@ void TextureMapperGL::drawSolidColor(const FloatRect& rect, const Transformation
flags |= ShouldBlend | ShouldAntialias;
}
- RefPtr<TextureMapperShaderProgram> program = data().sharedGLData().getShaderProgram(options);
+ Ref<TextureMapperShaderProgram> program = data().getShaderProgram(options);
m_context3D->useProgram(program->programID());
float r, g, b, a;
@@ -595,7 +495,7 @@ void TextureMapperGL::drawSolidColor(const FloatRect& rect, const Transformation
draw(rect, matrix, program.get(), GraphicsContext3D::TRIANGLE_FAN, flags);
}
-void TextureMapperGL::drawEdgeTriangles(TextureMapperShaderProgram* program)
+void TextureMapperGL::drawEdgeTriangles(TextureMapperShaderProgram& program)
{
const GC3Dfloat left = 0;
const GC3Dfloat top = 0;
@@ -620,29 +520,29 @@ void TextureMapperGL::drawEdgeTriangles(TextureMapperShaderProgram* program)
Platform3DObject vbo = data().getStaticVBO(GraphicsContext3D::ARRAY_BUFFER, sizeof(GC3Dfloat) * 48, unitRectSideTriangles);
m_context3D->bindBuffer(GraphicsContext3D::ARRAY_BUFFER, vbo);
- m_context3D->vertexAttribPointer(program->vertexLocation(), 4, GraphicsContext3D::FLOAT, false, 0, 0);
+ m_context3D->vertexAttribPointer(program.vertexLocation(), 4, GraphicsContext3D::FLOAT, false, 0, 0);
m_context3D->drawArrays(GraphicsContext3D::TRIANGLES, 0, 12);
m_context3D->bindBuffer(GraphicsContext3D::ARRAY_BUFFER, 0);
}
-void TextureMapperGL::drawUnitRect(TextureMapperShaderProgram* program, GC3Denum drawingMode)
+void TextureMapperGL::drawUnitRect(TextureMapperShaderProgram& program, GC3Denum drawingMode)
{
static const GC3Dfloat unitRect[] = { 0, 0, 1, 0, 1, 1, 0, 1 };
Platform3DObject vbo = data().getStaticVBO(GraphicsContext3D::ARRAY_BUFFER, sizeof(GC3Dfloat) * 8, unitRect);
m_context3D->bindBuffer(GraphicsContext3D::ARRAY_BUFFER, vbo);
- m_context3D->vertexAttribPointer(program->vertexLocation(), 2, GraphicsContext3D::FLOAT, false, 0, 0);
+ m_context3D->vertexAttribPointer(program.vertexLocation(), 2, GraphicsContext3D::FLOAT, false, 0, 0);
m_context3D->drawArrays(drawingMode, 0, 4);
m_context3D->bindBuffer(GraphicsContext3D::ARRAY_BUFFER, 0);
}
-void TextureMapperGL::draw(const FloatRect& rect, const TransformationMatrix& modelViewMatrix, TextureMapperShaderProgram* shaderProgram, GC3Denum drawingMode, Flags flags)
+void TextureMapperGL::draw(const FloatRect& rect, const TransformationMatrix& modelViewMatrix, TextureMapperShaderProgram& program, GC3Denum drawingMode, Flags flags)
{
- TransformationMatrix matrix =
- TransformationMatrix(modelViewMatrix).multiply(TransformationMatrix::rectToRect(FloatRect(0, 0, 1, 1), rect));
+ TransformationMatrix matrix(modelViewMatrix);
+ matrix.multiply(TransformationMatrix::rectToRect(FloatRect(0, 0, 1, 1), rect));
- m_context3D->enableVertexAttribArray(shaderProgram->vertexLocation());
- shaderProgram->setMatrix(shaderProgram->modelViewMatrixLocation(), matrix);
- shaderProgram->setMatrix(shaderProgram->projectionMatrixLocation(), data().projectionMatrix);
+ m_context3D->enableVertexAttribArray(program.vertexLocation());
+ program.setMatrix(program.modelViewMatrixLocation(), matrix);
+ program.setMatrix(program.projectionMatrixLocation(), data().projectionMatrix);
if (isInMaskMode()) {
m_context3D->blendFunc(GraphicsContext3D::ZERO, GraphicsContext3D::SRC_ALPHA);
@@ -656,28 +556,40 @@ void TextureMapperGL::draw(const FloatRect& rect, const TransformationMatrix& mo
}
if (flags & ShouldAntialias)
- drawEdgeTriangles(shaderProgram);
+ drawEdgeTriangles(program);
else
- drawUnitRect(shaderProgram, drawingMode);
+ drawUnitRect(program, drawingMode);
- m_context3D->disableVertexAttribArray(shaderProgram->vertexLocation());
+ m_context3D->disableVertexAttribArray(program.vertexLocation());
m_context3D->blendFunc(GraphicsContext3D::ONE, GraphicsContext3D::ONE_MINUS_SRC_ALPHA);
m_context3D->enable(GraphicsContext3D::BLEND);
}
-void TextureMapperGL::drawTexturedQuadWithProgram(TextureMapperShaderProgram* program, uint32_t texture, Flags flags, const IntSize& size, const FloatRect& rect, const TransformationMatrix& modelViewMatrix, float opacity)
+void TextureMapperGL::drawTexturedQuadWithProgram(TextureMapperShaderProgram& program, uint32_t texture, Flags flags, const IntSize& size, const FloatRect& rect, const TransformationMatrix& modelViewMatrix, float opacity)
{
- m_context3D->useProgram(program->programID());
+ m_context3D->useProgram(program.programID());
m_context3D->activeTexture(GraphicsContext3D::TEXTURE0);
GC3Denum target = flags & ShouldUseARBTextureRect ? GC3Denum(Extensions3D::TEXTURE_RECTANGLE_ARB) : GC3Denum(GraphicsContext3D::TEXTURE_2D);
m_context3D->bindTexture(target, texture);
- m_context3D->uniform1i(program->samplerLocation(), 0);
- if (wrapMode() == RepeatWrap) {
+ m_context3D->uniform1i(program.samplerLocation(), 0);
+ if (wrapMode() == RepeatWrap && driverSupportsNPOTTextures(*m_context3D)) {
m_context3D->texParameteri(GraphicsContext3D::TEXTURE_2D, GraphicsContext3D::TEXTURE_WRAP_S, GraphicsContext3D::REPEAT);
m_context3D->texParameteri(GraphicsContext3D::TEXTURE_2D, GraphicsContext3D::TEXTURE_WRAP_T, GraphicsContext3D::REPEAT);
}
TransformationMatrix patternTransform = this->patternTransform();
+ if (flags & ShouldRotateTexture90) {
+ patternTransform.rotate(-90);
+ patternTransform.translate(-1, 0);
+ }
+ if (flags & ShouldRotateTexture180) {
+ patternTransform.rotate(180);
+ patternTransform.translate(-1, -1);
+ }
+ if (flags & ShouldRotateTexture270) {
+ patternTransform.rotate(-270);
+ patternTransform.translate(0, -1);
+ }
if (flags & ShouldFlipTexture)
patternTransform.flipY();
if (flags & ShouldUseARBTextureRect)
@@ -685,8 +597,8 @@ void TextureMapperGL::drawTexturedQuadWithProgram(TextureMapperShaderProgram* pr
if (flags & ShouldFlipTexture)
patternTransform.translate(0, -1);
- program->setMatrix(program->textureSpaceMatrixLocation(), patternTransform);
- m_context3D->uniform1f(program->opacityLocation(), opacity);
+ program.setMatrix(program.textureSpaceMatrixLocation(), patternTransform);
+ m_context3D->uniform1f(program.opacityLocation(), opacity);
if (opacity < 1)
flags |= ShouldBlend;
@@ -696,225 +608,17 @@ void TextureMapperGL::drawTexturedQuadWithProgram(TextureMapperShaderProgram* pr
m_context3D->texParameteri(GraphicsContext3D::TEXTURE_2D, GraphicsContext3D::TEXTURE_WRAP_T, GraphicsContext3D::CLAMP_TO_EDGE);
}
-BitmapTextureGL::BitmapTextureGL(TextureMapperGL* textureMapper)
- : m_id(0)
- , m_fbo(0)
- , m_rbo(0)
- , m_depthBufferObject(0)
- , m_shouldClear(true)
- , m_context3D(textureMapper->graphicsContext3D())
-{
-}
-
-bool BitmapTextureGL::canReuseWith(const IntSize& contentsSize, Flags)
-{
- return contentsSize == m_textureSize;
-}
-
-#if OS(DARWIN)
-#define DEFAULT_TEXTURE_PIXEL_TRANSFER_TYPE GL_UNSIGNED_INT_8_8_8_8_REV
-#else
-#define DEFAULT_TEXTURE_PIXEL_TRANSFER_TYPE GraphicsContext3D::UNSIGNED_BYTE
-#endif
-
-static void swizzleBGRAToRGBA(uint32_t* data, const IntRect& rect, int stride = 0)
-{
- stride = stride ? stride : rect.width();
- for (int y = rect.y(); y < rect.maxY(); ++y) {
- uint32_t* p = data + y * stride;
- for (int x = rect.x(); x < rect.maxX(); ++x)
- p[x] = ((p[x] << 16) & 0xff0000) | ((p[x] >> 16) & 0xff) | (p[x] & 0xff00ff00);
- }
-}
-
-// If GL_EXT_texture_format_BGRA8888 is supported in the OpenGLES
-// internal and external formats need to be BGRA
-static bool driverSupportsExternalTextureBGRA(GraphicsContext3D* context)
-{
- if (context->isGLES2Compliant()) {
- static bool supportsExternalTextureBGRA = context->getExtensions()->supports("GL_EXT_texture_format_BGRA8888");
- return supportsExternalTextureBGRA;
- }
-
- return true;
-}
-
-static bool driverSupportsSubImage(GraphicsContext3D* context)
-{
- if (context->isGLES2Compliant()) {
- static bool supportsSubImage = context->getExtensions()->supports("GL_EXT_unpack_subimage");
- return supportsSubImage;
- }
-
- return true;
-}
-
-void BitmapTextureGL::didReset()
-{
- if (!m_id)
- m_id = m_context3D->createTexture();
-
- m_shouldClear = true;
- if (m_textureSize == contentSize())
- return;
-
-
- m_textureSize = contentSize();
- m_context3D->bindTexture(GraphicsContext3D::TEXTURE_2D, m_id);
- m_context3D->texParameteri(GraphicsContext3D::TEXTURE_2D, GraphicsContext3D::TEXTURE_MIN_FILTER, GraphicsContext3D::LINEAR);
- m_context3D->texParameteri(GraphicsContext3D::TEXTURE_2D, GraphicsContext3D::TEXTURE_MAG_FILTER, GraphicsContext3D::LINEAR);
- m_context3D->texParameteri(GraphicsContext3D::TEXTURE_2D, GraphicsContext3D::TEXTURE_WRAP_S, GraphicsContext3D::CLAMP_TO_EDGE);
- m_context3D->texParameteri(GraphicsContext3D::TEXTURE_2D, GraphicsContext3D::TEXTURE_WRAP_T, GraphicsContext3D::CLAMP_TO_EDGE);
-
- Platform3DObject internalFormat = GraphicsContext3D::RGBA;
- Platform3DObject externalFormat = GraphicsContext3D::BGRA;
- if (m_context3D->isGLES2Compliant()) {
- if (driverSupportsExternalTextureBGRA(m_context3D.get()))
- internalFormat = GraphicsContext3D::BGRA;
- else
- externalFormat = GraphicsContext3D::RGBA;
- }
-
- m_context3D->texImage2DDirect(GraphicsContext3D::TEXTURE_2D, 0, internalFormat, m_textureSize.width(), m_textureSize.height(), 0, externalFormat, DEFAULT_TEXTURE_PIXEL_TRANSFER_TYPE, 0);
-}
-
-void BitmapTextureGL::updateContentsNoSwizzle(const void* srcData, const IntRect& targetRect, const IntPoint& sourceOffset, int bytesPerLine, unsigned bytesPerPixel, Platform3DObject glFormat)
-{
- m_context3D->bindTexture(GraphicsContext3D::TEXTURE_2D, m_id);
- if (driverSupportsSubImage(m_context3D.get())) { // For ES drivers that don't support sub-images.
- // Use the OpenGL sub-image extension, now that we know it's available.
- m_context3D->pixelStorei(GL_UNPACK_ROW_LENGTH, bytesPerLine / bytesPerPixel);
- m_context3D->pixelStorei(GL_UNPACK_SKIP_ROWS, sourceOffset.y());
- m_context3D->pixelStorei(GL_UNPACK_SKIP_PIXELS, sourceOffset.x());
- }
-
- m_context3D->texSubImage2D(GraphicsContext3D::TEXTURE_2D, 0, targetRect.x(), targetRect.y(), targetRect.width(), targetRect.height(), glFormat, DEFAULT_TEXTURE_PIXEL_TRANSFER_TYPE, srcData);
-
- if (driverSupportsSubImage(m_context3D.get())) { // For ES drivers that don't support sub-images.
- m_context3D->pixelStorei(GL_UNPACK_ROW_LENGTH, 0);
- m_context3D->pixelStorei(GL_UNPACK_SKIP_ROWS, 0);
- m_context3D->pixelStorei(GL_UNPACK_SKIP_PIXELS, 0);
- }
-}
-
-void BitmapTextureGL::updateContents(const void* srcData, const IntRect& targetRect, const IntPoint& sourceOffset, int bytesPerLine, UpdateContentsFlag updateContentsFlag)
-{
- Platform3DObject glFormat = GraphicsContext3D::RGBA;
- m_context3D->bindTexture(GraphicsContext3D::TEXTURE_2D, m_id);
-
- const unsigned bytesPerPixel = 4;
- char* data = reinterpret_cast<char*>(const_cast<void*>(srcData));
- Vector<char> temporaryData;
- IntPoint adjustedSourceOffset = sourceOffset;
-
- // Texture upload requires subimage buffer if driver doesn't support subimage and we don't have full image upload.
- bool requireSubImageBuffer = !driverSupportsSubImage(m_context3D.get())
- && !(bytesPerLine == static_cast<int>(targetRect.width() * bytesPerPixel) && adjustedSourceOffset == IntPoint::zero());
-
- // prepare temporaryData if necessary
- if ((!driverSupportsExternalTextureBGRA(m_context3D.get()) && updateContentsFlag == UpdateCannotModifyOriginalImageData) || requireSubImageBuffer) {
- temporaryData.resize(targetRect.width() * targetRect.height() * bytesPerPixel);
- data = temporaryData.data();
- const char* bits = static_cast<const char*>(srcData);
- const char* src = bits + sourceOffset.y() * bytesPerLine + sourceOffset.x() * bytesPerPixel;
- char* dst = data;
- const int targetBytesPerLine = targetRect.width() * bytesPerPixel;
- for (int y = 0; y < targetRect.height(); ++y) {
- memcpy(dst, src, targetBytesPerLine);
- src += bytesPerLine;
- dst += targetBytesPerLine;
- }
-
- bytesPerLine = targetBytesPerLine;
- adjustedSourceOffset = IntPoint(0, 0);
- }
-
- if (driverSupportsExternalTextureBGRA(m_context3D.get()))
- glFormat = GraphicsContext3D::BGRA;
- else
- swizzleBGRAToRGBA(reinterpret_cast_ptr<uint32_t*>(data), IntRect(adjustedSourceOffset, targetRect.size()), bytesPerLine / bytesPerPixel);
-
- updateContentsNoSwizzle(data, targetRect, adjustedSourceOffset, bytesPerLine, bytesPerPixel, glFormat);
-}
-
-void BitmapTextureGL::updateContents(Image* image, const IntRect& targetRect, const IntPoint& offset, UpdateContentsFlag updateContentsFlag)
-{
- if (!image)
- return;
- NativeImagePtr frameImage = image->nativeImageForCurrentFrame();
- if (!frameImage)
- return;
-
- int bytesPerLine;
- const char* imageData;
-
-#if USE(CAIRO)
- cairo_surface_t* surface = frameImage.get();
- imageData = reinterpret_cast<const char*>(cairo_image_surface_get_data(surface));
- bytesPerLine = cairo_image_surface_get_stride(surface);
-#endif
-
- updateContents(imageData, targetRect, offset, bytesPerLine, updateContentsFlag);
-}
-
-#if ENABLE(CSS_FILTERS)
void TextureMapperGL::drawFiltered(const BitmapTexture& sampler, const BitmapTexture* contentTexture, const FilterOperation& filter, int pass)
{
// For standard filters, we always draw the whole texture without transformations.
TextureMapperShaderProgram::Options options = optionsForFilterType(filter.type(), pass);
- RefPtr<TextureMapperShaderProgram> program = data().sharedGLData().getShaderProgram(options);
- ASSERT(program);
+ Ref<TextureMapperShaderProgram> program = data().getShaderProgram(options);
prepareFilterProgram(program.get(), filter, pass, sampler.contentSize(), contentTexture ? static_cast<const BitmapTextureGL*>(contentTexture)->id() : 0);
FloatRect targetRect(IntPoint::zero(), sampler.contentSize());
drawTexturedQuadWithProgram(program.get(), static_cast<const BitmapTextureGL&>(sampler).id(), 0, IntSize(1, 1), targetRect, TransformationMatrix(), 1);
}
-PassRefPtr<BitmapTexture> BitmapTextureGL::applyFilters(TextureMapper* textureMapper, const FilterOperations& filters)
-{
- if (filters.isEmpty())
- return this;
-
- TextureMapperGL* texmapGL = static_cast<TextureMapperGL*>(textureMapper);
- RefPtr<BitmapTexture> previousSurface = texmapGL->data().currentSurface;
- RefPtr<BitmapTexture> resultSurface = this;
- RefPtr<BitmapTexture> intermediateSurface;
- RefPtr<BitmapTexture> spareSurface;
-
- m_filterInfo = FilterInfo();
-
- for (size_t i = 0; i < filters.size(); ++i) {
- RefPtr<FilterOperation> filter = filters.operations()[i];
- ASSERT(filter);
-
- int numPasses = getPassesRequiredForFilter(filter->type());
- for (int j = 0; j < numPasses; ++j) {
- bool last = (i == filters.size() - 1) && (j == numPasses - 1);
- if (!last) {
- if (!intermediateSurface)
- intermediateSurface = texmapGL->acquireTextureFromPool(contentSize());
- texmapGL->bindSurface(intermediateSurface.get());
- }
-
- if (last) {
- toBitmapTextureGL(resultSurface.get())->m_filterInfo = BitmapTextureGL::FilterInfo(filter, j, spareSurface);
- break;
- }
-
- texmapGL->drawFiltered(*resultSurface.get(), spareSurface.get(), *filter, j);
- if (!j && filter->type() == FilterOperation::DROP_SHADOW) {
- spareSurface = resultSurface;
- resultSurface.clear();
- }
- std::swap(resultSurface, intermediateSurface);
- }
- }
-
- texmapGL->bindSurface(previousSurface.get());
- return resultSurface;
-}
-#endif
-
static inline TransformationMatrix createProjectionMatrix(const IntSize& size, bool mirrored)
{
const float nearValue = 9999999;
@@ -926,95 +630,6 @@ static inline TransformationMatrix createProjectionMatrix(const IntSize& size, b
-1, mirrored ? -1 : 1, -(farValue + nearValue) / (farValue - nearValue), 1);
}
-void BitmapTextureGL::initializeStencil()
-{
- if (m_rbo)
- return;
-
- m_rbo = m_context3D->createRenderbuffer();
- m_context3D->bindRenderbuffer(GraphicsContext3D::RENDERBUFFER, m_rbo);
-#ifdef TEXMAP_OPENGL_ES_2
- m_context3D->renderbufferStorage(GraphicsContext3D::RENDERBUFFER, GraphicsContext3D::STENCIL_INDEX8, m_textureSize.width(), m_textureSize.height());
-#else
- m_context3D->renderbufferStorage(GraphicsContext3D::RENDERBUFFER, GraphicsContext3D::DEPTH_STENCIL, m_textureSize.width(), m_textureSize.height());
-#endif
- m_context3D->bindRenderbuffer(GraphicsContext3D::RENDERBUFFER, 0);
- m_context3D->framebufferRenderbuffer(GraphicsContext3D::FRAMEBUFFER, GraphicsContext3D::STENCIL_ATTACHMENT, GraphicsContext3D::RENDERBUFFER, m_rbo);
- m_context3D->clearStencil(0);
- m_context3D->clear(GraphicsContext3D::STENCIL_BUFFER_BIT);
-}
-
-void BitmapTextureGL::initializeDepthBuffer()
-{
- if (m_depthBufferObject)
- return;
-
- m_depthBufferObject = m_context3D->createRenderbuffer();
- m_context3D->bindRenderbuffer(GraphicsContext3D::RENDERBUFFER, m_depthBufferObject);
- m_context3D->renderbufferStorage(GraphicsContext3D::RENDERBUFFER, GraphicsContext3D::DEPTH_COMPONENT16, m_textureSize.width(), m_textureSize.height());
- m_context3D->bindRenderbuffer(GraphicsContext3D::RENDERBUFFER, 0);
- m_context3D->framebufferRenderbuffer(GraphicsContext3D::FRAMEBUFFER, GraphicsContext3D::DEPTH_ATTACHMENT, GraphicsContext3D::RENDERBUFFER, m_depthBufferObject);
-}
-
-void BitmapTextureGL::clearIfNeeded()
-{
- if (!m_shouldClear)
- return;
-
- m_clipStack.reset(IntRect(IntPoint::zero(), m_textureSize), TextureMapperGL::ClipStack::DefaultYAxis);
- m_clipStack.applyIfNeeded(m_context3D.get());
- m_context3D->clearColor(0, 0, 0, 0);
- m_context3D->clear(GraphicsContext3D::COLOR_BUFFER_BIT);
- m_shouldClear = false;
-}
-
-void BitmapTextureGL::createFboIfNeeded()
-{
- if (m_fbo)
- return;
-
- m_fbo = m_context3D->createFramebuffer();
- m_context3D->bindFramebuffer(GraphicsContext3D::FRAMEBUFFER, m_fbo);
- m_context3D->framebufferTexture2D(GraphicsContext3D::FRAMEBUFFER, GraphicsContext3D::COLOR_ATTACHMENT0, GraphicsContext3D::TEXTURE_2D, id(), 0);
- m_shouldClear = true;
-}
-
-void BitmapTextureGL::bind(TextureMapperGL* textureMapper)
-{
- m_context3D->bindTexture(GraphicsContext3D::TEXTURE_2D, 0);
- createFboIfNeeded();
- m_context3D->bindFramebuffer(GraphicsContext3D::FRAMEBUFFER, m_fbo);
- m_context3D->viewport(0, 0, m_textureSize.width(), m_textureSize.height());
- clearIfNeeded();
- textureMapper->data().projectionMatrix = createProjectionMatrix(m_textureSize, true /* mirrored */);
- m_clipStack.apply(m_context3D.get());
-}
-
-BitmapTextureGL::~BitmapTextureGL()
-{
- if (m_id)
- m_context3D->deleteTexture(m_id);
-
- if (m_fbo)
- m_context3D->deleteFramebuffer(m_fbo);
-
- if (m_rbo)
- m_context3D->deleteRenderbuffer(m_rbo);
-
- if (m_depthBufferObject)
- m_context3D->deleteRenderbuffer(m_depthBufferObject);
-}
-
-bool BitmapTextureGL::isValid() const
-{
- return m_id;
-}
-
-IntSize BitmapTextureGL::size() const
-{
- return m_textureSize;
-}
-
TextureMapperGL::~TextureMapperGL()
{
delete m_data;
@@ -1023,11 +638,11 @@ TextureMapperGL::~TextureMapperGL()
void TextureMapperGL::bindDefaultSurface()
{
m_context3D->bindFramebuffer(GraphicsContext3D::FRAMEBUFFER, data().targetFrameBuffer);
- IntSize viewportSize(data().viewport[2], data().viewport[3]);
- data().projectionMatrix = createProjectionMatrix(viewportSize, data().PaintFlags & PaintingMirrored);
- m_context3D->viewport(data().viewport[0], data().viewport[1], viewportSize.width(), viewportSize.height());
- m_clipStack.apply(m_context3D.get());
- data().currentSurface.clear();
+ auto& viewport = data().viewport;
+ data().projectionMatrix = createProjectionMatrix(IntSize(viewport[2], viewport[3]), data().PaintFlags & PaintingMirrored);
+ m_context3D->viewport(viewport[0], viewport[1], viewport[2], viewport[3]);
+ m_clipStack.apply(*m_context3D);
+ data().currentSurface = nullptr;
}
void TextureMapperGL::bindSurface(BitmapTexture *surface)
@@ -1037,10 +652,16 @@ void TextureMapperGL::bindSurface(BitmapTexture *surface)
return;
}
- static_cast<BitmapTextureGL*>(surface)->bind(this);
+ static_cast<BitmapTextureGL*>(surface)->bindAsSurface(m_context3D.get());
+ data().projectionMatrix = createProjectionMatrix(surface->size(), true /* mirrored */);
data().currentSurface = surface;
}
+BitmapTexture* TextureMapperGL::currentSurface()
+{
+ return data().currentSurface.get();
+}
+
bool TextureMapperGL::beginScissorClip(const TransformationMatrix& modelViewMatrix, const FloatRect& targetRect)
{
// 3D transforms are currently not supported in scissor clipping
@@ -1056,7 +677,7 @@ bool TextureMapperGL::beginScissorClip(const TransformationMatrix& modelViewMatr
return false;
clipStack().intersect(rect);
- clipStack().applyIfNeeded(m_context3D.get());
+ clipStack().applyIfNeeded(*m_context3D);
return true;
}
@@ -1068,15 +689,17 @@ void TextureMapperGL::beginClip(const TransformationMatrix& modelViewMatrix, con
data().initializeStencil();
- RefPtr<TextureMapperShaderProgram> program = data().sharedGLData().getShaderProgram(TextureMapperShaderProgram::SolidColor);
+ Ref<TextureMapperShaderProgram> program = data().getShaderProgram(TextureMapperShaderProgram::SolidColor);
m_context3D->useProgram(program->programID());
m_context3D->enableVertexAttribArray(program->vertexLocation());
const GC3Dfloat unitRect[] = {0, 0, 1, 0, 1, 1, 0, 1};
- m_context3D->vertexAttribPointer(program->vertexLocation(), 2, GraphicsContext3D::FLOAT, false, 0, GC3Dintptr(unitRect));
+ Platform3DObject vbo = data().getStaticVBO(GraphicsContext3D::ARRAY_BUFFER, sizeof(GC3Dfloat) * 8, unitRect);
+ m_context3D->bindBuffer(GraphicsContext3D::ARRAY_BUFFER, vbo);
+ m_context3D->vertexAttribPointer(program->vertexLocation(), 2, GraphicsContext3D::FLOAT, false, 0, 0);
- TransformationMatrix matrix = TransformationMatrix(modelViewMatrix)
- .multiply(TransformationMatrix::rectToRect(FloatRect(0, 0, 1, 1), targetRect));
+ TransformationMatrix matrix(modelViewMatrix);
+ matrix.multiply(TransformationMatrix::rectToRect(FloatRect(0, 0, 1, 1), targetRect));
static const TransformationMatrix fullProjectionMatrix = TransformationMatrix::rectToRect(FloatRect(0, 0, 1, 1), FloatRect(-1, -1, 2, 2));
@@ -1103,18 +726,19 @@ void TextureMapperGL::beginClip(const TransformationMatrix& modelViewMatrix, con
m_context3D->drawArrays(GraphicsContext3D::TRIANGLE_FAN, 0, 4);
// Clear the state.
+ m_context3D->bindBuffer(GraphicsContext3D::ARRAY_BUFFER, 0);
m_context3D->disableVertexAttribArray(program->vertexLocation());
m_context3D->stencilMask(0);
// Increase stencilIndex and apply stencil testing.
clipStack().setStencilIndex(stencilIndex * 2);
- clipStack().applyIfNeeded(m_context3D.get());
+ clipStack().applyIfNeeded(*m_context3D);
}
void TextureMapperGL::endClip()
{
clipStack().pop();
- clipStack().applyIfNeeded(m_context3D.get());
+ clipStack().applyIfNeeded(*m_context3D);
}
IntRect TextureMapperGL::clipBounds()
@@ -1124,14 +748,14 @@ IntRect TextureMapperGL::clipBounds()
PassRefPtr<BitmapTexture> TextureMapperGL::createTexture()
{
- BitmapTextureGL* texture = new BitmapTextureGL(this);
- return adoptRef(texture);
+ return BitmapTextureGL::create(*m_context3D);
}
-PassOwnPtr<TextureMapper> TextureMapper::platformCreateAccelerated()
+std::unique_ptr<TextureMapper> TextureMapper::platformCreateAccelerated()
{
- return TextureMapperGL::create();
+ return std::make_unique<TextureMapperGL>();
}
};
-#endif
+
+#endif // USE(TEXTURE_MAPPER_GL)
diff --git a/Source/WebCore/platform/graphics/texmap/TextureMapperGL.h b/Source/WebCore/platform/graphics/texmap/TextureMapperGL.h
index 081403f00..acc78e8c8 100644
--- a/Source/WebCore/platform/graphics/texmap/TextureMapperGL.h
+++ b/Source/WebCore/platform/graphics/texmap/TextureMapperGL.h
@@ -1,5 +1,6 @@
/*
Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies)
+ Copyright (C) 2015 Igalia S.L.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
@@ -20,8 +21,9 @@
#ifndef TextureMapperGL_h
#define TextureMapperGL_h
-#if USE(ACCELERATED_COMPOSITING) && USE(TEXTURE_MAPPER)
+#if USE(TEXTURE_MAPPER_GL)
+#include "ClipStack.h"
#include "FilterOperation.h"
#include "FloatQuad.h"
#include "GraphicsContext3D.h"
@@ -38,95 +40,49 @@ class FilterOperation;
// An OpenGL-ES2 implementation of TextureMapper.
class TextureMapperGL : public TextureMapper {
public:
- static PassOwnPtr<TextureMapperGL> create() { return adoptPtr(new TextureMapperGL); }
+ TextureMapperGL();
virtual ~TextureMapperGL();
enum Flag {
ShouldBlend = 0x01,
ShouldFlipTexture = 0x02,
ShouldUseARBTextureRect = 0x04,
- ShouldAntialias = 0x08
+ ShouldAntialias = 0x08,
+ ShouldRotateTexture90 = 0x10,
+ ShouldRotateTexture180 = 0x20,
+ ShouldRotateTexture270 = 0x40
};
typedef int Flags;
// TextureMapper implementation
- virtual void drawBorder(const Color&, float borderWidth, const FloatRect&, const TransformationMatrix&) override;
- virtual void drawNumber(int number, const Color&, const FloatPoint&, const TransformationMatrix&) override;
- virtual void drawTexture(const BitmapTexture&, const FloatRect&, const TransformationMatrix&, float opacity, unsigned exposedEdges) override;
+ void drawBorder(const Color&, float borderWidth, const FloatRect&, const TransformationMatrix&) override;
+ void drawNumber(int number, const Color&, const FloatPoint&, const TransformationMatrix&) override;
+ void drawTexture(const BitmapTexture&, const FloatRect&, const TransformationMatrix&, float opacity, unsigned exposedEdges) override;
virtual void drawTexture(Platform3DObject texture, Flags, const IntSize& textureSize, const FloatRect& targetRect, const TransformationMatrix& modelViewMatrix, float opacity, unsigned exposedEdges = AllEdges);
- virtual void drawSolidColor(const FloatRect&, const TransformationMatrix&, const Color&) override;
-
- virtual void bindSurface(BitmapTexture* surface) override;
- virtual void beginClip(const TransformationMatrix&, const FloatRect&) override;
- virtual void beginPainting(PaintFlags = 0) override;
- virtual void endPainting() override;
- virtual void endClip() override;
- virtual IntRect clipBounds() override;
- virtual IntSize maxTextureSize() const override { return IntSize(2000, 2000); }
- virtual PassRefPtr<BitmapTexture> createTexture() override;
+ void drawSolidColor(const FloatRect&, const TransformationMatrix&, const Color&) override;
+
+ void bindSurface(BitmapTexture* surface) override;
+ BitmapTexture* currentSurface();
+ void beginClip(const TransformationMatrix&, const FloatRect&) override;
+ void beginPainting(PaintFlags = 0) override;
+ void endPainting() override;
+ void endClip() override;
+ IntRect clipBounds() override;
+ IntSize maxTextureSize() const override { return IntSize(2000, 2000); }
+ PassRefPtr<BitmapTexture> createTexture() override;
inline GraphicsContext3D* graphicsContext3D() const { return m_context3D.get(); }
-#if ENABLE(CSS_FILTERS)
void drawFiltered(const BitmapTexture& sourceTexture, const BitmapTexture* contentTexture, const FilterOperation&, int pass);
-#endif
void setEnableEdgeDistanceAntialiasing(bool enabled) { m_enableEdgeDistanceAntialiasing = enabled; }
private:
- struct ClipState {
- IntRect scissorBox;
- int stencilIndex;
- ClipState(const IntRect& scissors = IntRect(), int stencil = 1)
- : scissorBox(scissors)
- , stencilIndex(stencil)
- { }
- };
-
- class ClipStack {
- public:
- ClipStack()
- : clipStateDirty(false)
- { }
-
- // Y-axis should be inverted only when painting into the window.
- enum YAxisMode {
- DefaultYAxis,
- InvertedYAxis
- };
-
- void push();
- void pop();
- void apply(GraphicsContext3D*);
- void applyIfNeeded(GraphicsContext3D*);
- inline ClipState& current() { return clipState; }
- void reset(const IntRect&, YAxisMode);
- void intersect(const IntRect&);
- void setStencilIndex(int);
- inline int getStencilIndex() const
- {
- return clipState.stencilIndex;
- }
- inline bool isCurrentScissorBoxEmpty() const
- {
- return clipState.scissorBox.isEmpty();
- }
-
- private:
- ClipState clipState;
- Vector<ClipState> clipStack;
- bool clipStateDirty;
- IntSize size;
- YAxisMode yAxisMode;
- };
-
- TextureMapperGL();
-
- void drawTexturedQuadWithProgram(TextureMapperShaderProgram*, uint32_t texture, Flags, const IntSize&, const FloatRect&, const TransformationMatrix& modelViewMatrix, float opacity);
- void draw(const FloatRect&, const TransformationMatrix& modelViewMatrix, TextureMapperShaderProgram*, GC3Denum drawingMode, Flags);
+ void drawTexturedQuadWithProgram(TextureMapperShaderProgram&, uint32_t texture, Flags, const IntSize&, const FloatRect&, const TransformationMatrix& modelViewMatrix, float opacity);
+ void draw(const FloatRect&, const TransformationMatrix& modelViewMatrix, TextureMapperShaderProgram&, GC3Denum drawingMode, Flags);
- void drawUnitRect(TextureMapperShaderProgram*, GC3Denum drawingMode);
- void drawEdgeTriangles(TextureMapperShaderProgram*);
+ void drawUnitRect(TextureMapperShaderProgram&, GC3Denum drawingMode);
+ void drawEdgeTriangles(TextureMapperShaderProgram&);
bool beginScissorClip(const TransformationMatrix&, const FloatRect&);
void bindDefaultSurface();
@@ -136,72 +92,10 @@ private:
TextureMapperGLData* m_data;
ClipStack m_clipStack;
bool m_enableEdgeDistanceAntialiasing;
-
- friend class BitmapTextureGL;
-};
-
-class BitmapTextureGL : public BitmapTexture {
-public:
- virtual IntSize size() const;
- virtual bool isValid() const;
- virtual bool canReuseWith(const IntSize& contentsSize, Flags = 0);
- virtual void didReset();
- void bind(TextureMapperGL*);
- void initializeStencil();
- void initializeDepthBuffer();
- ~BitmapTextureGL();
- virtual uint32_t id() const { return m_id; }
- uint32_t textureTarget() const { return GraphicsContext3D::TEXTURE_2D; }
- IntSize textureSize() const { return m_textureSize; }
- void updateContents(Image*, const IntRect&, const IntPoint&, UpdateContentsFlag);
- virtual void updateContents(const void*, const IntRect& target, const IntPoint& sourceOffset, int bytesPerLine, UpdateContentsFlag);
- virtual bool isBackedByOpenGL() const { return true; }
-
-#if ENABLE(CSS_FILTERS)
- virtual PassRefPtr<BitmapTexture> applyFilters(TextureMapper*, const FilterOperations&) override;
- struct FilterInfo {
- RefPtr<FilterOperation> filter;
- unsigned pass;
- RefPtr<BitmapTexture> contentTexture;
-
- FilterInfo(PassRefPtr<FilterOperation> f = 0, unsigned p = 0, PassRefPtr<BitmapTexture> t = 0)
- : filter(f)
- , pass(p)
- , contentTexture(t)
- { }
- };
- const FilterInfo* filterInfo() const { return &m_filterInfo; }
-#endif
-
-private:
- void updateContentsNoSwizzle(const void*, const IntRect& target, const IntPoint& sourceOffset, int bytesPerLine, unsigned bytesPerPixel = 4, Platform3DObject glFormat = GraphicsContext3D::RGBA);
-
- Platform3DObject m_id;
- IntSize m_textureSize;
- IntRect m_dirtyRect;
- Platform3DObject m_fbo;
- Platform3DObject m_rbo;
- Platform3DObject m_depthBufferObject;
- bool m_shouldClear;
- TextureMapperGL::ClipStack m_clipStack;
- RefPtr<GraphicsContext3D> m_context3D;
-
- explicit BitmapTextureGL(TextureMapperGL*);
- BitmapTextureGL();
-
- void clearIfNeeded();
- void createFboIfNeeded();
-
-#if ENABLE(CSS_FILTERS)
- FilterInfo m_filterInfo;
-#endif
-
- friend class TextureMapperGL;
};
-BitmapTextureGL* toBitmapTextureGL(BitmapTexture*);
+} // namespace WebCore
-}
-#endif
+#endif // USE(TEXTURE_MAPPER_GL)
-#endif
+#endif // TextureMapperGL_h
diff --git a/Source/WebCore/platform/graphics/texmap/TextureMapperImageBuffer.cpp b/Source/WebCore/platform/graphics/texmap/TextureMapperImageBuffer.cpp
deleted file mode 100644
index 41041f91a..000000000
--- a/Source/WebCore/platform/graphics/texmap/TextureMapperImageBuffer.cpp
+++ /dev/null
@@ -1,162 +0,0 @@
-/*
- Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies)
-
- This library is free software; you can redistribute it and/or
- modify it under the terms of the GNU Library General Public
- License as published by the Free Software Foundation; either
- version 2 of the License, or (at your option) any later version.
-
- This library is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Library General Public License for more details.
-
- You should have received a copy of the GNU Library General Public License
- along with this library; see the file COPYING.LIB. If not, write to
- the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
- Boston, MA 02110-1301, USA.
- */
-
-#include "config.h"
-#include "TextureMapperImageBuffer.h"
-
-#include "GraphicsLayer.h"
-#include "NotImplemented.h"
-
-#if USE(TEXTURE_MAPPER)
-namespace WebCore {
-
-static const int s_maximumAllowedImageBufferDimension = 4096;
-
-void BitmapTextureImageBuffer::updateContents(const void* data, const IntRect& targetRect, const IntPoint& sourceOffset, int bytesPerLine, UpdateContentsFlag)
-{
-#if PLATFORM(CAIRO)
- RefPtr<cairo_surface_t> surface = adoptRef(cairo_image_surface_create_for_data(static_cast<unsigned char*>(data()),
- CAIRO_FORMAT_ARGB32,
- targetRect.width(), targetRect.height(),
- bytesPerLine));
- m_image->context()->platformContext()->drawSurfaceToContext(surface.get(), targetRect,
- IntRect(sourceOffset, targetRect.size()), m_image->context());
-#else
- UNUSED_PARAM(data);
- UNUSED_PARAM(targetRect);
- UNUSED_PARAM(sourceOffset);
- UNUSED_PARAM(bytesPerLine);
-#endif
-}
-
-void BitmapTextureImageBuffer::updateContents(TextureMapper*, GraphicsLayer* sourceLayer, const IntRect& targetRect, const IntPoint& sourceOffset, UpdateContentsFlag)
-{
- GraphicsContext* context = m_image->context();
-
- context->clearRect(targetRect);
-
- IntRect sourceRect(targetRect);
- sourceRect.setLocation(sourceOffset);
- context->save();
- context->clip(targetRect);
- context->translate(targetRect.x() - sourceOffset.x(), targetRect.y() - sourceOffset.y());
- sourceLayer->paintGraphicsLayerContents(*context, sourceRect);
- context->restore();
-}
-
-void BitmapTextureImageBuffer::didReset()
-{
- m_image = ImageBuffer::create(contentSize());
-}
-
-void BitmapTextureImageBuffer::updateContents(Image* image, const IntRect& targetRect, const IntPoint& offset, UpdateContentsFlag)
-{
- m_image->context()->drawImage(image, ColorSpaceDeviceRGB, targetRect, IntRect(offset, targetRect.size()), CompositeCopy, ImageOrientationDescription());
-}
-
-IntSize TextureMapperImageBuffer::maxTextureSize() const
-{
- return IntSize(s_maximumAllowedImageBufferDimension, s_maximumAllowedImageBufferDimension);
-}
-
-void TextureMapperImageBuffer::beginClip(const TransformationMatrix& matrix, const FloatRect& rect)
-{
- GraphicsContext* context = currentContext();
- if (!context)
- return;
-#if ENABLE(3D_RENDERING)
- TransformationMatrix previousTransform = context->get3DTransform();
-#else
- AffineTransform previousTransform = context->getCTM();
-#endif
- context->save();
-
-#if ENABLE(3D_RENDERING)
- context->concat3DTransform(matrix);
-#else
- context->concatCTM(matrix.toAffineTransform());
-#endif
-
- context->clip(rect);
-
-#if ENABLE(3D_RENDERING)
- context->set3DTransform(previousTransform);
-#else
- context->setCTM(previousTransform);
-#endif
-}
-
-void TextureMapperImageBuffer::drawTexture(const BitmapTexture& texture, const FloatRect& targetRect, const TransformationMatrix& matrix, float opacity, unsigned /* exposedEdges */)
-{
- GraphicsContext* context = currentContext();
- if (!context)
- return;
-
- const BitmapTextureImageBuffer& textureImageBuffer = static_cast<const BitmapTextureImageBuffer&>(texture);
- ImageBuffer* image = textureImageBuffer.m_image.get();
- context->save();
- context->setCompositeOperation(isInMaskMode() ? CompositeDestinationIn : CompositeSourceOver);
- context->setAlpha(opacity);
-#if ENABLE(3D_RENDERING)
- context->concat3DTransform(matrix);
-#else
- context->concatCTM(matrix.toAffineTransform());
-#endif
- context->drawImageBuffer(image, ColorSpaceDeviceRGB, targetRect);
- context->restore();
-}
-
-void TextureMapperImageBuffer::drawSolidColor(const FloatRect& rect, const TransformationMatrix& matrix, const Color& color)
-{
- GraphicsContext* context = currentContext();
- if (!context)
- return;
-
- context->save();
- context->setCompositeOperation(isInMaskMode() ? CompositeDestinationIn : CompositeSourceOver);
-#if ENABLE(3D_RENDERING)
- context->concat3DTransform(matrix);
-#else
- context->concatCTM(matrix.toAffineTransform());
-#endif
-
- context->fillRect(rect, color, ColorSpaceDeviceRGB);
- context->restore();
-}
-
-void TextureMapperImageBuffer::drawBorder(const Color&, float /* borderWidth */, const FloatRect&, const TransformationMatrix&)
-{
- notImplemented();
-}
-
-void TextureMapperImageBuffer::drawNumber(int /* number */, const Color&, const FloatPoint&, const TransformationMatrix&)
-{
- notImplemented();
-}
-
-#if ENABLE(CSS_FILTERS)
-PassRefPtr<BitmapTexture> BitmapTextureImageBuffer::applyFilters(TextureMapper*, const FilterOperations&)
-{
- ASSERT_NOT_REACHED();
- return this;
-}
-#endif
-
-}
-#endif
diff --git a/Source/WebCore/platform/graphics/texmap/TextureMapperImageBuffer.h b/Source/WebCore/platform/graphics/texmap/TextureMapperImageBuffer.h
deleted file mode 100644
index f5e38b358..000000000
--- a/Source/WebCore/platform/graphics/texmap/TextureMapperImageBuffer.h
+++ /dev/null
@@ -1,82 +0,0 @@
-/*
- Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies)
-
- This library is free software; you can redistribute it and/or
- modify it under the terms of the GNU Library General Public
- License as published by the Free Software Foundation; either
- version 2 of the License, or (at your option) any later version.
-
- This library is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Library General Public License for more details.
-
- You should have received a copy of the GNU Library General Public License
- along with this library; see the file COPYING.LIB. If not, write to
- the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
- Boston, MA 02110-1301, USA.
- */
-
-#ifndef TextureMapperImageBuffer_h
-#define TextureMapperImageBuffer_h
-
-#include "ImageBuffer.h"
-#include "TextureMapper.h"
-
-#if USE(TEXTURE_MAPPER)
-namespace WebCore {
-
-class BitmapTextureImageBuffer : public BitmapTexture {
- friend class TextureMapperImageBuffer;
-public:
- static PassRefPtr<BitmapTexture> create() { return adoptRef(new BitmapTextureImageBuffer); }
- virtual IntSize size() const { return m_image->internalSize(); }
- virtual void didReset();
- virtual bool isValid() const { return m_image.get(); }
- inline GraphicsContext* graphicsContext() { return m_image ? m_image->context() : 0; }
- virtual void updateContents(Image*, const IntRect&, const IntPoint&, UpdateContentsFlag);
- virtual void updateContents(TextureMapper*, GraphicsLayer*, const IntRect& target, const IntPoint& offset, UpdateContentsFlag);
- virtual void updateContents(const void*, const IntRect& target, const IntPoint& sourceOffset, int bytesPerLine, UpdateContentsFlag);
-#if ENABLE(CSS_FILTERS)
- PassRefPtr<BitmapTexture> applyFilters(TextureMapper*, const FilterOperations&);
-#endif
-
-private:
- BitmapTextureImageBuffer() { }
- std::unique_ptr<ImageBuffer> m_image;
-};
-
-
-class TextureMapperImageBuffer : public TextureMapper {
- WTF_MAKE_FAST_ALLOCATED;
-public:
- static PassOwnPtr<TextureMapper> create() { return adoptPtr(new TextureMapperImageBuffer); }
-
- // TextureMapper implementation
- virtual void drawBorder(const Color&, float borderWidth, const FloatRect&, const TransformationMatrix&) override;
- virtual void drawNumber(int number, const Color&, const FloatPoint&, const TransformationMatrix&) override;
- virtual void drawTexture(const BitmapTexture&, const FloatRect& targetRect, const TransformationMatrix&, float opacity, unsigned exposedEdges) override;
- virtual void drawSolidColor(const FloatRect&, const TransformationMatrix&, const Color&) override;
- virtual void beginClip(const TransformationMatrix&, const FloatRect&) override;
- virtual void bindSurface(BitmapTexture* surface) override { m_currentSurface = surface;}
- virtual void endClip() override { graphicsContext()->restore(); }
- virtual IntRect clipBounds() override { return currentContext()->clipBounds(); }
- virtual IntSize maxTextureSize() const;
- virtual PassRefPtr<BitmapTexture> createTexture() override { return BitmapTextureImageBuffer::create(); }
-
- inline GraphicsContext* currentContext()
- {
- return m_currentSurface ? static_cast<BitmapTextureImageBuffer*>(m_currentSurface.get())->graphicsContext() : graphicsContext();
- }
-
-private:
- TextureMapperImageBuffer()
- : TextureMapper(SoftwareMode)
- { }
- RefPtr<BitmapTexture> m_currentSurface;
-};
-
-}
-#endif // USE(TEXTURE_MAPPER)
-
-#endif // TextureMapperImageBuffer_h
diff --git a/Source/WebCore/platform/graphics/texmap/TextureMapperLayer.cpp b/Source/WebCore/platform/graphics/texmap/TextureMapperLayer.cpp
index 7e7babbcd..d39cffcbb 100644
--- a/Source/WebCore/platform/graphics/texmap/TextureMapperLayer.cpp
+++ b/Source/WebCore/platform/graphics/texmap/TextureMapperLayer.cpp
@@ -21,35 +21,25 @@
#include "TextureMapperLayer.h"
#include "FloatQuad.h"
+#include "GraphicsLayerTextureMapper.h"
#include "Region.h"
#include <wtf/MathExtras.h>
-#if USE(ACCELERATED_COMPOSITING) && USE(TEXTURE_MAPPER)
-
namespace WebCore {
class TextureMapperPaintOptions {
public:
- RefPtr<BitmapTexture> surface;
- float opacity;
+ TextureMapperPaintOptions(TextureMapper& textureMapper)
+ : textureMapper(textureMapper)
+ { }
+
+ TextureMapper& textureMapper;
TransformationMatrix transform;
+ RefPtr<BitmapTexture> surface;
+ float opacity { 1 };
IntSize offset;
- TextureMapper* textureMapper;
- TextureMapperPaintOptions()
- : opacity(1)
- , textureMapper(0)
- { }
};
-const TextureMapperLayer* TextureMapperLayer::rootLayer() const
-{
- if (m_effectTarget)
- return m_effectTarget->rootLayer();
- if (m_parent)
- return m_parent->rootLayer();
- return this;
-}
-
void TextureMapperLayer::computeTransformsRecursive()
{
if (m_state.size.isEmpty() && m_state.masksToBounds)
@@ -72,9 +62,9 @@ void TextureMapperLayer::computeTransformsRecursive()
m_state.maskLayer->computeTransformsRecursive();
if (m_state.replicaLayer)
m_state.replicaLayer->computeTransformsRecursive();
- for (size_t i = 0; i < m_children.size(); ++i) {
- RELEASE_ASSERT(m_children[i]->m_parent == this);
- m_children[i]->computeTransformsRecursive();
+ for (auto* child : m_children) {
+ ASSERT(child->m_parent == this);
+ child->computeTransformsRecursive();
}
// Reorder children if needed on the way back up.
@@ -86,19 +76,19 @@ void TextureMapperLayer::paint()
{
computeTransformsRecursive();
- TextureMapperPaintOptions options;
- options.textureMapper = m_textureMapper;
- options.textureMapper->bindSurface(0);
+ ASSERT(m_textureMapper);
+ TextureMapperPaintOptions options(*m_textureMapper);
+ options.textureMapper.bindSurface(0);
+
paintRecursive(options);
}
static Color blendWithOpacity(const Color& color, float opacity)
{
- RGBA32 rgba = color.rgb();
- // See Color::getRGBA() to know how to extract alpha from color.
- float alpha = alphaChannel(rgba) / 255.;
- float effectiveAlpha = alpha * opacity;
- return Color(colorWithOverrideAlpha(rgba, effectiveAlpha));
+ if (color.isOpaque() && opacity == 1.)
+ return color;
+
+ return color.colorWithAlphaMultipliedBy(opacity);
}
void TextureMapperLayer::computePatternTransformIfNeeded()
@@ -108,8 +98,8 @@ void TextureMapperLayer::computePatternTransformIfNeeded()
m_patternTransformDirty = false;
m_patternTransform =
- TransformationMatrix::rectToRect(FloatRect(FloatPoint::zero(), m_state.contentsTileSize), m_state.contentsRect)
- .multiply(TransformationMatrix().translate(m_state.contentsTilePhase.x() / m_state.contentsRect.width(), m_state.contentsTilePhase.y() / m_state.contentsRect.height()));
+ TransformationMatrix::rectToRect(FloatRect(FloatPoint::zero(), m_state.contentsTileSize), FloatRect(FloatPoint::zero(), m_state.contentsRect.size()))
+ .multiply(TransformationMatrix().translate(m_state.contentsTilePhase.width() / m_state.contentsRect.width(), m_state.contentsTilePhase.height() / m_state.contentsRect.height()));
}
void TextureMapperLayer::paintSelf(const TextureMapperPaintOptions& options)
@@ -123,15 +113,15 @@ void TextureMapperLayer::paintSelf(const TextureMapperPaintOptions& options)
transform.multiply(options.transform);
transform.multiply(m_currentTransform.combined());
- if (m_state.solidColor.isValid() && !m_state.contentsRect.isEmpty() && m_state.solidColor.alpha()) {
- options.textureMapper->drawSolidColor(m_state.contentsRect, transform, blendWithOpacity(m_state.solidColor, options.opacity));
+ if (m_state.solidColor.isValid() && !m_state.contentsRect.isEmpty() && m_state.solidColor.isVisible()) {
+ options.textureMapper.drawSolidColor(m_state.contentsRect, transform, blendWithOpacity(m_state.solidColor, options.opacity));
if (m_state.showDebugBorders)
- options.textureMapper->drawBorder(m_state.debugBorderColor, m_state.debugBorderWidth, layerRect(), transform);
+ options.textureMapper.drawBorder(m_state.debugBorderColor, m_state.debugBorderWidth, layerRect(), transform);
return;
}
- options.textureMapper->setWrapMode(TextureMapper::StretchWrap);
- options.textureMapper->setPatternTransform(TransformationMatrix());
+ options.textureMapper.setWrapMode(TextureMapper::StretchWrap);
+ options.textureMapper.setPatternTransform(TransformationMatrix());
if (m_backingStore) {
FloatRect targetRect = layerRect();
@@ -149,8 +139,8 @@ void TextureMapperLayer::paintSelf(const TextureMapperPaintOptions& options)
if (!m_state.contentsTileSize.isEmpty()) {
computePatternTransformIfNeeded();
- options.textureMapper->setWrapMode(TextureMapper::RepeatWrap);
- options.textureMapper->setPatternTransform(m_patternTransform);
+ options.textureMapper.setWrapMode(TextureMapper::RepeatWrap);
+ options.textureMapper.setPatternTransform(m_patternTransform);
}
ASSERT(!layerRect().isEmpty());
@@ -159,16 +149,12 @@ void TextureMapperLayer::paintSelf(const TextureMapperPaintOptions& options)
m_contentsLayer->drawBorder(options.textureMapper, m_state.debugBorderColor, m_state.debugBorderWidth, m_state.contentsRect, transform);
}
-int TextureMapperLayer::compareGraphicsLayersZValue(const void* a, const void* b)
-{
- TextureMapperLayer* const* layerA = static_cast<TextureMapperLayer* const*>(a);
- TextureMapperLayer* const* layerB = static_cast<TextureMapperLayer* const*>(b);
- return int(((*layerA)->m_centerZ - (*layerB)->m_centerZ) * 1000);
-}
-
void TextureMapperLayer::sortByZOrder(Vector<TextureMapperLayer* >& array)
{
- qsort(array.data(), array.size(), sizeof(TextureMapperLayer*), compareGraphicsLayersZValue);
+ std::sort(array.begin(), array.end(),
+ [](TextureMapperLayer* a, TextureMapperLayer* b) {
+ return a->m_centerZ < b->m_centerZ;
+ });
}
void TextureMapperLayer::paintSelfAndChildren(const TextureMapperPaintOptions& options)
@@ -184,14 +170,14 @@ void TextureMapperLayer::paintSelfAndChildren(const TextureMapperPaintOptions& o
clipTransform.translate(options.offset.width(), options.offset.height());
clipTransform.multiply(options.transform);
clipTransform.multiply(m_currentTransform.combined());
- options.textureMapper->beginClip(clipTransform, layerRect());
+ options.textureMapper.beginClip(clipTransform, layerRect());
}
- for (size_t i = 0; i < m_children.size(); ++i)
- m_children[i]->paintRecursive(options);
+ for (auto* child : m_children)
+ child->paintRecursive(options);
if (shouldClip)
- options.textureMapper->endClip();
+ options.textureMapper.endClip();
}
bool TextureMapperLayer::shouldBlend() const
@@ -224,7 +210,7 @@ void TextureMapperLayer::paintSelfAndChildrenWithReplica(const TextureMapperPain
TextureMapperPaintOptions replicaOptions(options);
replicaOptions.transform
.multiply(m_state.replicaLayer->m_currentTransform.combined())
- .multiply(m_currentTransform.combined().inverse());
+ .multiply(m_currentTransform.combined().inverse().value_or(TransformationMatrix()));
paintSelfAndChildren(replicaOptions);
}
@@ -243,17 +229,15 @@ void TextureMapperLayer::setAnimatedOpacity(float opacity)
TransformationMatrix TextureMapperLayer::replicaTransform()
{
- return TransformationMatrix(m_state.replicaLayer->m_currentTransform.combined()).multiply(m_currentTransform.combined().inverse());
+ return TransformationMatrix(m_state.replicaLayer->m_currentTransform.combined()).multiply(m_currentTransform.combined().inverse().value_or(TransformationMatrix()));
}
-#if ENABLE(CSS_FILTERS)
void TextureMapperLayer::setAnimatedFilters(const FilterOperations& filters)
{
m_currentFilters = filters;
}
-#endif
-static void resolveOverlaps(Region newRegion, Region& overlapRegion, Region& nonOverlapRegion)
+static void resolveOverlaps(Region& newRegion, Region& overlapRegion, Region& nonOverlapRegion)
{
Region newOverlapRegion(newRegion);
newOverlapRegion.intersect(nonOverlapRegion);
@@ -271,10 +255,9 @@ void TextureMapperLayer::computeOverlapRegions(Region& overlapRegion, Region& no
FloatRect boundingRect;
if (m_backingStore || m_state.masksToBounds || m_state.maskLayer || hasFilters())
boundingRect = layerRect();
- else if (m_contentsLayer || m_state.solidColor.alpha())
+ else if (m_contentsLayer || m_state.solidColor.isVisible())
boundingRect = m_state.contentsRect;
-#if ENABLE(CSS_FILTERS)
if (m_currentFilters.hasOutsets()) {
FilterOutsets outsets = m_currentFilters.outsets();
IntRect unfilteredTargetRect(boundingRect);
@@ -282,7 +265,6 @@ void TextureMapperLayer::computeOverlapRegions(Region& overlapRegion, Region& no
boundingRect.expand(outsets.left() + outsets.right(), outsets.top() + outsets.bottom());
boundingRect.unite(unfilteredTargetRect);
}
-#endif
TransformationMatrix replicaMatrix;
if (m_state.replicaLayer) {
@@ -304,10 +286,8 @@ void TextureMapperLayer::computeOverlapRegions(Region& overlapRegion, Region& no
Region newNonOverlapRegion(enclosingIntRect(boundingRect));
if (!m_state.masksToBounds) {
- for (size_t i = 0; i < m_children.size(); ++i) {
- TextureMapperLayer* child = m_children[i];
+ for (auto* child : m_children)
child->computeOverlapRegions(newOverlapRegion, newNonOverlapRegion, ResolveSelfOverlapIfNeeded);
- }
}
if (m_state.replicaLayer) {
@@ -345,14 +325,13 @@ void TextureMapperLayer::paintUsingOverlapRegions(const TextureMapperPaintOption
nonOverlapRegion.translate(options.offset);
Vector<IntRect> rects = nonOverlapRegion.rects();
- for (size_t i = 0; i < rects.size(); ++i) {
- IntRect rect = rects[i];
- if (!rect.intersects(options.textureMapper->clipBounds()))
+ for (auto& rect : rects) {
+ if (!rect.intersects(options.textureMapper.clipBounds()))
continue;
- options.textureMapper->beginClip(TransformationMatrix(), rects[i]);
+ options.textureMapper.beginClip(TransformationMatrix(), rect);
paintSelfAndChildrenWithReplica(options);
- options.textureMapper->endClip();
+ options.textureMapper.endClip();
}
rects = overlapRegion.rects();
@@ -362,11 +341,10 @@ void TextureMapperLayer::paintUsingOverlapRegions(const TextureMapperPaintOption
rects.append(overlapRegion.bounds());
}
- IntSize maxTextureSize = options.textureMapper->maxTextureSize();
- IntRect adjustedClipBounds(options.textureMapper->clipBounds());
+ IntSize maxTextureSize = options.textureMapper.maxTextureSize();
+ IntRect adjustedClipBounds(options.textureMapper.clipBounds());
adjustedClipBounds.move(-options.offset);
- for (size_t i = 0; i < rects.size(); ++i) {
- IntRect rect = rects[i];
+ for (auto& rect : rects) {
for (int x = rect.x(); x < rect.maxX(); x += maxTextureSize.width()) {
for (int y = rect.y(); y < rect.maxY(); y += maxTextureSize.height()) {
IntRect tileRect(IntPoint(x, y), maxTextureSize);
@@ -382,34 +360,32 @@ void TextureMapperLayer::paintUsingOverlapRegions(const TextureMapperPaintOption
void TextureMapperLayer::applyMask(const TextureMapperPaintOptions& options)
{
- options.textureMapper->setMaskMode(true);
+ options.textureMapper.setMaskMode(true);
paintSelf(options);
- options.textureMapper->setMaskMode(false);
+ options.textureMapper.setMaskMode(false);
}
PassRefPtr<BitmapTexture> TextureMapperLayer::paintIntoSurface(const TextureMapperPaintOptions& options, const IntSize& size)
{
- RefPtr<BitmapTexture> surface = options.textureMapper->acquireTextureFromPool(size);
+ RefPtr<BitmapTexture> surface = options.textureMapper.acquireTextureFromPool(size, BitmapTexture::SupportsAlpha | BitmapTexture::FBOAttachment);
TextureMapperPaintOptions paintOptions(options);
paintOptions.surface = surface;
- options.textureMapper->bindSurface(surface.get());
+ options.textureMapper.bindSurface(surface.get());
paintSelfAndChildren(paintOptions);
if (m_state.maskLayer)
m_state.maskLayer->applyMask(options);
-#if ENABLE(CSS_FILTERS)
surface = surface->applyFilters(options.textureMapper, m_currentFilters);
-#endif
- options.textureMapper->bindSurface(surface.get());
- return surface;
+ options.textureMapper.bindSurface(surface.get());
+ return surface.release();
}
static void commitSurface(const TextureMapperPaintOptions& options, PassRefPtr<BitmapTexture> surface, const IntRect& rect, float opacity)
{
- options.textureMapper->bindSurface(options.surface.get());
+ options.textureMapper.bindSurface(options.surface.get());
TransformationMatrix targetTransform;
targetTransform.translate(options.offset.width(), options.offset.height());
targetTransform.multiply(options.transform);
- options.textureMapper->drawTexture(*surface.get(), rect, targetTransform, opacity);
+ options.textureMapper.drawTexture(*surface.get(), rect, targetTransform, opacity);
}
void TextureMapperLayer::paintWithIntermediateSurface(const TextureMapperPaintOptions& options, const IntRect& rect)
@@ -430,13 +406,13 @@ void TextureMapperLayer::paintWithIntermediateSurface(const TextureMapperPaintOp
if (replicaSurface && options.opacity == 1) {
commitSurface(options, replicaSurface, rect, 1);
- replicaSurface.clear();
+ replicaSurface = nullptr;
}
mainSurface = paintIntoSurface(paintOptions, rect.size());
if (replicaSurface) {
- options.textureMapper->bindSurface(replicaSurface.get());
- options.textureMapper->drawTexture(*mainSurface.get(), FloatRect(FloatPoint::zero(), rect.size()));
+ options.textureMapper.bindSurface(replicaSurface.get());
+ options.textureMapper.drawTexture(*mainSurface.get(), FloatRect(FloatPoint::zero(), rect.size()));
mainSurface = replicaSurface;
}
@@ -461,23 +437,33 @@ void TextureMapperLayer::paintRecursive(const TextureMapperPaintOptions& options
TextureMapperLayer::~TextureMapperLayer()
{
- for (int i = m_children.size() - 1; i >= 0; --i)
- m_children[i]->m_parent = 0;
+ for (auto* child : m_children)
+ child->m_parent = nullptr;
- if (m_parent)
- m_parent->m_children.remove(m_parent->m_children.find(this));
+ removeFromParent();
+
+ if (m_effectTarget) {
+ if (m_effectTarget->m_state.maskLayer == this)
+ m_effectTarget->m_state.maskLayer = nullptr;
+ if (m_effectTarget->m_state.replicaLayer == this)
+ m_effectTarget->m_state.replicaLayer = nullptr;
+ }
}
-TextureMapper* TextureMapperLayer::textureMapper() const
+#if !USE(COORDINATED_GRAPHICS)
+void TextureMapperLayer::setChildren(const Vector<GraphicsLayer*>& newChildren)
{
- return rootLayer()->m_textureMapper;
+ removeAllChildren();
+ for (auto* child : newChildren)
+ addChild(&downcast<GraphicsLayerTextureMapper>(child)->layer());
}
+#endif
void TextureMapperLayer::setChildren(const Vector<TextureMapperLayer*>& newChildren)
{
removeAllChildren();
- for (size_t i = 0; i < newChildren.size(); ++i)
- addChild(newChildren[i]);
+ for (auto* child : newChildren)
+ addChild(child);
}
void TextureMapperLayer::addChild(TextureMapperLayer* childLayer)
@@ -494,25 +480,19 @@ void TextureMapperLayer::addChild(TextureMapperLayer* childLayer)
void TextureMapperLayer::removeFromParent()
{
if (m_parent) {
- unsigned i;
- for (i = 0; i < m_parent->m_children.size(); i++) {
- if (this == m_parent->m_children[i]) {
- m_parent->m_children.remove(i);
- break;
- }
- }
-
- m_parent = 0;
+ size_t index = m_parent->m_children.find(this);
+ ASSERT(index != notFound);
+ m_parent->m_children.remove(index);
}
+
+ m_parent = nullptr;
}
void TextureMapperLayer::removeAllChildren()
{
- while (m_children.size()) {
- TextureMapperLayer* curLayer = m_children[0];
- ASSERT(curLayer->m_parent);
- curLayer->removeFromParent();
- }
+ auto oldChildren = WTFMove(m_children);
+ for (auto* child : oldChildren)
+ child->m_parent = nullptr;
}
void TextureMapperLayer::setMaskLayer(TextureMapperLayer* maskLayer)
@@ -565,7 +545,7 @@ void TextureMapperLayer::setChildrenTransform(const TransformationMatrix& childr
m_currentTransform.setChildrenTransform(childrenTransform);
}
-void TextureMapperLayer::setContentsRect(const IntRect& contentsRect)
+void TextureMapperLayer::setContentsRect(const FloatRect& contentsRect)
{
if (contentsRect == m_state.contentsRect)
return;
@@ -573,7 +553,7 @@ void TextureMapperLayer::setContentsRect(const IntRect& contentsRect)
m_patternTransformDirty = true;
}
-void TextureMapperLayer::setContentsTileSize(const IntSize& size)
+void TextureMapperLayer::setContentsTileSize(const FloatSize& size)
{
if (size == m_state.contentsTileSize)
return;
@@ -581,7 +561,7 @@ void TextureMapperLayer::setContentsTileSize(const IntSize& size)
m_patternTransformDirty = true;
}
-void TextureMapperLayer::setContentsTilePhase(const IntPoint& phase)
+void TextureMapperLayer::setContentsTilePhase(const FloatSize& phase)
{
if (phase == m_state.contentsTilePhase)
return;
@@ -624,12 +604,10 @@ void TextureMapperLayer::setSolidColor(const Color& color)
m_state.solidColor = color;
}
-#if ENABLE(CSS_FILTERS)
void TextureMapperLayer::setFilters(const FilterOperations& filters)
{
m_state.filters = filters;
}
-#endif
void TextureMapperLayer::setDebugVisuals(bool showDebugBorders, const Color& debugBorderColor, float debugBorderWidth, bool showRepaintCounter)
{
@@ -649,7 +627,7 @@ void TextureMapperLayer::setContentsLayer(TextureMapperPlatformLayer* platformLa
m_contentsLayer = platformLayer;
}
-void TextureMapperLayer::setAnimations(const GraphicsLayerAnimations& animations)
+void TextureMapperLayer::setAnimations(const TextureMapperAnimations& animations)
{
m_animations = animations;
}
@@ -669,33 +647,29 @@ bool TextureMapperLayer::descendantsOrSelfHaveRunningAnimations() const
if (m_animations.hasRunningAnimations())
return true;
- for (size_t i = 0; i < m_children.size(); ++i) {
- if (m_children[i]->descendantsOrSelfHaveRunningAnimations())
- return true;
- }
-
- return false;
+ return std::any_of(m_children.begin(), m_children.end(),
+ [](TextureMapperLayer* child) {
+ return child->descendantsOrSelfHaveRunningAnimations();
+ });
}
void TextureMapperLayer::applyAnimationsRecursively()
{
syncAnimations();
- for (size_t i = 0; i < m_children.size(); ++i)
- m_children[i]->applyAnimationsRecursively();
+ for (auto* child : m_children)
+ child->applyAnimationsRecursively();
}
void TextureMapperLayer::syncAnimations()
{
- m_animations.apply(this);
- if (!m_animations.hasActiveAnimationsOfType(AnimatedPropertyWebkitTransform))
+ m_animations.apply(*this);
+ if (!m_animations.hasActiveAnimationsOfType(AnimatedPropertyTransform))
m_currentTransform.setLocalTransform(m_state.transform);
if (!m_animations.hasActiveAnimationsOfType(AnimatedPropertyOpacity))
m_currentOpacity = m_state.opacity;
-#if ENABLE(CSS_FILTERS)
- if (!m_animations.hasActiveAnimationsOfType(AnimatedPropertyWebkitFilter))
+ if (!m_animations.hasActiveAnimationsOfType(AnimatedPropertyFilter))
m_currentFilters = m_state.filters;
-#endif
}
bool TextureMapperLayer::isAncestorFixedToViewport() const
@@ -756,7 +730,7 @@ TextureMapperLayer* TextureMapperLayer::findScrollableContentsLayerAt(const Floa
FloatSize TextureMapperLayer::mapScrollOffset(const FloatSize& offset)
{
double zeroX, zeroY, offsetX, offsetY;
- TransformationMatrix transform = m_currentTransform.combined().inverse();
+ TransformationMatrix transform = m_currentTransform.combined().inverse().value_or(TransformationMatrix());
transform.map(0, 0, zeroX, zeroY);
transform.map(offset.width(), offset.height(), offsetX, offsetY);
return FloatSize(offsetX - zeroX, offsetY - zeroY);
@@ -795,4 +769,3 @@ void TextureMapperLayer::didCommitScrollOffset(const IntSize& offset)
}
}
-#endif
diff --git a/Source/WebCore/platform/graphics/texmap/TextureMapperLayer.h b/Source/WebCore/platform/graphics/texmap/TextureMapperLayer.h
index 87ff3f4a7..3b1c16b0b 100644
--- a/Source/WebCore/platform/graphics/texmap/TextureMapperLayer.h
+++ b/Source/WebCore/platform/graphics/texmap/TextureMapperLayer.h
@@ -20,22 +20,21 @@
#ifndef TextureMapperLayer_h
#define TextureMapperLayer_h
-#if USE(ACCELERATED_COMPOSITING) && USE(TEXTURE_MAPPER)
-
#include "FilterOperations.h"
#include "FloatRect.h"
-#include "GraphicsLayerAnimation.h"
#include "GraphicsLayerTransform.h"
#include "TextureMapper.h"
+#include "TextureMapperAnimation.h"
#include "TextureMapperBackingStore.h"
namespace WebCore {
+class GraphicsLayer;
class Region;
class TextureMapperPaintOptions;
class TextureMapperPlatformLayer;
-class TextureMapperLayer : public GraphicsLayerAnimation::Client {
+class TextureMapperLayer : public TextureMapperAnimation::Client {
WTF_MAKE_NONCOPYABLE(TextureMapperLayer);
WTF_MAKE_FAST_ALLOCATED;
public:
@@ -74,9 +73,12 @@ public:
void setIsScrollable(bool isScrollable) { m_isScrollable = isScrollable; }
bool isScrollable() const { return m_isScrollable; }
- TextureMapper* textureMapper() const;
+ TextureMapper* textureMapper() const { return rootLayer().m_textureMapper; }
void setTextureMapper(TextureMapper* texmap) { m_textureMapper = texmap; }
+#if !USE(COORDINATED_GRAPHICS)
+ void setChildren(const Vector<GraphicsLayer*>&);
+#endif
void setChildren(const Vector<TextureMapperLayer*>&);
void setMaskLayer(TextureMapperLayer*);
void setReplicaLayer(TextureMapperLayer*);
@@ -86,7 +88,7 @@ public:
void setPreserves3D(bool);
void setTransform(const TransformationMatrix&);
void setChildrenTransform(const TransformationMatrix&);
- void setContentsRect(const IntRect&);
+ void setContentsRect(const FloatRect&);
void setMasksToBounds(bool);
void setDrawsContent(bool);
bool drawsContent() const { return m_state.drawsContent; }
@@ -99,26 +101,20 @@ public:
void setBackfaceVisibility(bool);
void setOpacity(float);
void setSolidColor(const Color&);
- void setContentsTileSize(const IntSize&);
- void setContentsTilePhase(const IntPoint&);
-#if ENABLE(CSS_FILTERS)
+ void setContentsTileSize(const FloatSize&);
+ void setContentsTilePhase(const FloatSize&);
void setFilters(const FilterOperations&);
-#endif
bool hasFilters() const
{
-#if ENABLE(CSS_FILTERS)
return !m_currentFilters.isEmpty();
-#else
- return false;
-#endif
}
void setDebugVisuals(bool showDebugBorders, const Color& debugBorderColor, float debugBorderWidth, bool showRepaintCounter);
bool isShowingRepaintCounter() const { return m_state.showRepaintCounter; }
void setRepaintCount(int);
void setContentsLayer(TextureMapperPlatformLayer*);
- void setAnimations(const GraphicsLayerAnimations&);
+ void setAnimations(const TextureMapperAnimations&);
void setFixedToViewport(bool);
bool fixedToViewport() const { return m_fixedToViewport; }
void setBackingStore(PassRefPtr<TextureMapperBackingStore>);
@@ -134,13 +130,19 @@ public:
void addChild(TextureMapperLayer*);
private:
- const TextureMapperLayer* rootLayer() const;
+ const TextureMapperLayer& rootLayer() const
+ {
+ if (m_effectTarget)
+ return m_effectTarget->rootLayer();
+ if (m_parent)
+ return m_parent->rootLayer();
+ return *this;
+ }
void computeTransformsRecursive();
- static int compareGraphicsLayersZValue(const void* a, const void* b);
static void sortByZOrder(Vector<TextureMapperLayer* >& array);
- PassRefPtr<BitmapTexture> texture() { return m_backingStore ? m_backingStore->texture() : 0; }
+ RefPtr<BitmapTexture> texture() { return m_backingStore ? m_backingStore->texture() : 0; }
FloatPoint adjustedPosition() const { return m_state.pos + m_scrollPositionDelta - m_userScrollOffset; }
bool isAncestorFixedToViewport() const;
TransformationMatrix replicaTransform();
@@ -163,12 +165,10 @@ private:
void applyMask(const TextureMapperPaintOptions&);
void computePatternTransformIfNeeded();
- // GraphicsLayerAnimation::Client
- virtual void setAnimatedTransform(const TransformationMatrix&) override;
- virtual void setAnimatedOpacity(float) override;
-#if ENABLE(CSS_FILTERS)
- virtual void setAnimatedFilters(const FilterOperations&) override;
-#endif
+ // TextureMapperAnimation::Client
+ void setAnimatedTransform(const TransformationMatrix&) override;
+ void setAnimatedOpacity(float) override;
+ void setAnimatedFilters(const FilterOperations&) override;
bool isVisible() const;
enum ContentsLayerCount {
@@ -191,9 +191,7 @@ private:
TextureMapperPlatformLayer* m_contentsLayer;
GraphicsLayerTransform m_currentTransform;
float m_currentOpacity;
-#if ENABLE(CSS_FILTERS)
FilterOperations m_currentFilters;
-#endif
float m_centerZ;
template<class HitTestCondition> TextureMapperLayer* hitTest(const FloatPoint&, HitTestCondition);
@@ -210,14 +208,12 @@ private:
TransformationMatrix childrenTransform;
float opacity;
FloatRect contentsRect;
- IntSize contentsTileSize;
- IntPoint contentsTilePhase;
+ FloatSize contentsTileSize;
+ FloatSize contentsTilePhase;
TextureMapperLayer* maskLayer;
TextureMapperLayer* replicaLayer;
Color solidColor;
-#if ENABLE(CSS_FILTERS)
FilterOperations filters;
-#endif
Color debugBorderColor;
float debugBorderWidth;
int repaintCount;
@@ -253,7 +249,7 @@ private:
State m_state;
TextureMapper* m_textureMapper;
- GraphicsLayerAnimations m_animations;
+ TextureMapperAnimations m_animations;
FloatSize m_scrollPositionDelta;
bool m_fixedToViewport;
uint32_t m_id;
@@ -266,6 +262,5 @@ private:
};
}
-#endif
#endif // TextureMapperLayer_h
diff --git a/Source/WebCore/platform/graphics/texmap/TextureMapperPlatformLayer.h b/Source/WebCore/platform/graphics/texmap/TextureMapperPlatformLayer.h
index 2482e0e94..ddacbfd0a 100644
--- a/Source/WebCore/platform/graphics/texmap/TextureMapperPlatformLayer.h
+++ b/Source/WebCore/platform/graphics/texmap/TextureMapperPlatformLayer.h
@@ -20,9 +20,7 @@
#ifndef TextureMapperPlatformLayer_h
#define TextureMapperPlatformLayer_h
-#if USE(GRAPHICS_SURFACE)
-#include "GraphicsSurface.h"
-#endif
+#if USE(TEXTURE_MAPPER)
#include "TextureMapper.h"
#include "TransformationMatrix.h"
@@ -39,22 +37,16 @@ public:
TextureMapperPlatformLayer() : m_client(0) { }
virtual ~TextureMapperPlatformLayer() { }
- virtual void paintToTextureMapper(TextureMapper*, const FloatRect&, const TransformationMatrix& modelViewMatrix = TransformationMatrix(), float opacity = 1.0) = 0;
+ virtual void paintToTextureMapper(TextureMapper&, const FloatRect&, const TransformationMatrix& modelViewMatrix = TransformationMatrix(), float opacity = 1.0) = 0;
virtual void swapBuffers() { }
- virtual void drawBorder(TextureMapper* textureMapper, const Color& color, float borderWidth, const FloatRect& targetRect, const TransformationMatrix& transform)
+ virtual void drawBorder(TextureMapper& textureMapper, const Color& color, float borderWidth, const FloatRect& targetRect, const TransformationMatrix& transform)
{
- textureMapper->drawBorder(color, borderWidth, targetRect, transform);
+ textureMapper.drawBorder(color, borderWidth, targetRect, transform);
}
void setClient(TextureMapperPlatformLayer::Client* client)
{
m_client = client;
}
-#if USE(GRAPHICS_SURFACE)
- virtual IntSize platformLayerSize() const { return IntSize(); }
- virtual uint32_t copyToGraphicsSurface() { return 0; }
- virtual GraphicsSurfaceToken graphicsSurfaceToken() const { return GraphicsSurfaceToken(); }
- virtual GraphicsSurface::Flags graphicsSurfaceFlags() const { return GraphicsSurface::SupportsTextureTarget | GraphicsSurface::SupportsSharing; }
-#endif
protected:
TextureMapperPlatformLayer::Client* client() { return m_client; }
@@ -65,4 +57,6 @@ private:
};
+#endif // USE(TEXTURE_MAPPER)
+
#endif // TextureMapperPlatformLayer_h
diff --git a/Source/WebCore/platform/graphics/texmap/TextureMapperPlatformLayerBuffer.cpp b/Source/WebCore/platform/graphics/texmap/TextureMapperPlatformLayerBuffer.cpp
new file mode 100644
index 000000000..88851fe4b
--- /dev/null
+++ b/Source/WebCore/platform/graphics/texmap/TextureMapperPlatformLayerBuffer.cpp
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2015 Igalia S.L.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "TextureMapperPlatformLayerBuffer.h"
+
+namespace WebCore {
+
+TextureMapperPlatformLayerBuffer::TextureMapperPlatformLayerBuffer(RefPtr<BitmapTexture>&& texture)
+ : m_texture(WTFMove(texture))
+ , m_textureID(0)
+ , m_extraFlags(0)
+ , m_hasManagedTexture(true)
+{
+}
+
+TextureMapperPlatformLayerBuffer::TextureMapperPlatformLayerBuffer(GLuint textureID, const IntSize& size, TextureMapperGL::Flags flags)
+ : m_textureID(textureID)
+ , m_size(size)
+ , m_extraFlags(flags)
+ , m_hasManagedTexture(false)
+{
+}
+
+bool TextureMapperPlatformLayerBuffer::canReuseWithoutReset(const IntSize& size, GC3Dint internalFormat)
+{
+ return m_texture && (m_texture->size() == size) && (static_cast<BitmapTextureGL*>(m_texture.get())->internalFormat() == internalFormat || internalFormat == GraphicsContext3D::DONT_CARE);
+}
+
+void TextureMapperPlatformLayerBuffer::paintToTextureMapper(TextureMapper& textureMapper, const FloatRect& targetRect, const TransformationMatrix& modelViewMatrix, float opacity)
+{
+ TextureMapperGL& texmapGL = static_cast<TextureMapperGL&>(textureMapper);
+
+ if (m_hasManagedTexture) {
+ ASSERT(m_texture);
+ BitmapTextureGL* textureGL = static_cast<BitmapTextureGL*>(m_texture.get());
+ texmapGL.drawTexture(textureGL->id(), m_extraFlags, textureGL->size(), targetRect, modelViewMatrix, opacity);
+ return;
+ }
+
+ ASSERT(m_textureID);
+ texmapGL.drawTexture(m_textureID, m_extraFlags, m_size, targetRect, modelViewMatrix, opacity);
+}
+
+} // namespace WebCore
diff --git a/Source/WebCore/platform/graphics/texmap/TextureMapperPlatformLayerBuffer.h b/Source/WebCore/platform/graphics/texmap/TextureMapperPlatformLayerBuffer.h
new file mode 100644
index 000000000..2afe5b308
--- /dev/null
+++ b/Source/WebCore/platform/graphics/texmap/TextureMapperPlatformLayerBuffer.h
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2015 Igalia S.L.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include "BitmapTextureGL.h"
+#include "GraphicsTypes3D.h"
+#include "TextureMapperPlatformLayer.h"
+#include <wtf/CurrentTime.h>
+
+#if USE(COORDINATED_GRAPHICS_THREADED)
+
+namespace WebCore {
+
+class TextureMapperPlatformLayerBuffer : public TextureMapperPlatformLayer {
+ WTF_MAKE_NONCOPYABLE(TextureMapperPlatformLayerBuffer);
+ WTF_MAKE_FAST_ALLOCATED();
+public:
+ TextureMapperPlatformLayerBuffer(RefPtr<BitmapTexture>&&);
+ TextureMapperPlatformLayerBuffer(GLuint textureID, const IntSize&, TextureMapperGL::Flags);
+
+ virtual ~TextureMapperPlatformLayerBuffer() = default;
+
+ void paintToTextureMapper(TextureMapper&, const FloatRect&, const TransformationMatrix& modelViewMatrix = TransformationMatrix(), float opacity = 1.0) final;
+
+ bool canReuseWithoutReset(const IntSize&, GC3Dint internalFormat);
+ BitmapTextureGL& textureGL() { return static_cast<BitmapTextureGL&>(*m_texture); }
+
+ inline void markUsed() { m_timeLastUsed = monotonicallyIncreasingTime(); }
+ double lastUsedTime() const { return m_timeLastUsed; }
+
+ class UnmanagedBufferDataHolder {
+ WTF_MAKE_NONCOPYABLE(UnmanagedBufferDataHolder);
+ WTF_MAKE_FAST_ALLOCATED();
+ public:
+ UnmanagedBufferDataHolder() = default;
+ virtual ~UnmanagedBufferDataHolder() = default;
+ };
+
+ bool hasManagedTexture() const { return m_hasManagedTexture; }
+ void setUnmanagedBufferDataHolder(std::unique_ptr<UnmanagedBufferDataHolder> holder) { m_unmanagedBufferDataHolder = WTFMove(holder); }
+ void setExtraFlags(TextureMapperGL::Flags flags) { m_extraFlags = flags; }
+
+private:
+
+ RefPtr<BitmapTexture> m_texture;
+ double m_timeLastUsed { 0 };
+
+ GLuint m_textureID;
+ IntSize m_size;
+ TextureMapperGL::Flags m_extraFlags;
+ bool m_hasManagedTexture;
+ std::unique_ptr<UnmanagedBufferDataHolder> m_unmanagedBufferDataHolder;
+};
+
+} // namespace WebCore
+
+#endif // COORDINATED_GRAPHICS_THREADED
diff --git a/Source/WebCore/platform/graphics/texmap/TextureMapperPlatformLayerProxy.cpp b/Source/WebCore/platform/graphics/texmap/TextureMapperPlatformLayerProxy.cpp
new file mode 100644
index 000000000..a3ea24754
--- /dev/null
+++ b/Source/WebCore/platform/graphics/texmap/TextureMapperPlatformLayerProxy.cpp
@@ -0,0 +1,197 @@
+/*
+ * Copyright (C) 2015 Igalia S.L.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "TextureMapperPlatformLayerProxy.h"
+
+#if USE(COORDINATED_GRAPHICS_THREADED)
+
+#include "BitmapTextureGL.h"
+#include "TextureMapperGL.h"
+#include "TextureMapperLayer.h"
+#include "TextureMapperPlatformLayerBuffer.h"
+
+const double s_releaseUnusedSecondsTolerance = 1;
+const double s_releaseUnusedBuffersTimerInterval = 0.5;
+
+namespace WebCore {
+
+TextureMapperPlatformLayerProxy::TextureMapperPlatformLayerProxy()
+ : m_compositor(nullptr)
+ , m_targetLayer(nullptr)
+ , m_releaseUnusedBuffersTimer(RunLoop::current(), this, &TextureMapperPlatformLayerProxy::releaseUnusedBuffersTimerFired)
+{
+}
+
+TextureMapperPlatformLayerProxy::~TextureMapperPlatformLayerProxy()
+{
+ LockHolder locker(m_lock);
+ if (m_targetLayer)
+ m_targetLayer->setContentsLayer(nullptr);
+}
+
+void TextureMapperPlatformLayerProxy::activateOnCompositingThread(Compositor* compositor, TextureMapperLayer* targetLayer)
+{
+#ifndef NDEBUG
+ m_compositorThreadID = m_compositorThreadID ? m_compositorThreadID : WTF::currentThread();
+#endif
+ ASSERT(m_compositorThreadID == WTF::currentThread());
+ ASSERT(compositor);
+ ASSERT(targetLayer);
+ LockHolder locker(m_lock);
+ m_compositor = compositor;
+ m_targetLayer = targetLayer;
+ if (m_targetLayer && m_currentBuffer)
+ m_targetLayer->setContentsLayer(m_currentBuffer.get());
+
+ m_compositorThreadUpdateTimer = std::make_unique<RunLoop::Timer<TextureMapperPlatformLayerProxy>>(RunLoop::current(), this, &TextureMapperPlatformLayerProxy::compositorThreadUpdateTimerFired);
+}
+
+void TextureMapperPlatformLayerProxy::invalidate()
+{
+ ASSERT(m_compositorThreadID == WTF::currentThread());
+ Function<void()> updateFunction;
+ {
+ LockHolder locker(m_lock);
+ m_compositor = nullptr;
+ m_targetLayer = nullptr;
+
+ m_currentBuffer = nullptr;
+ m_pendingBuffer = nullptr;
+ m_releaseUnusedBuffersTimer.stop();
+ m_usedBuffers.clear();
+
+ // Clear the timer and dispatch the update function manually now.
+ m_compositorThreadUpdateTimer = nullptr;
+ if (!m_compositorThreadUpdateFunction)
+ return;
+ updateFunction = WTFMove(m_compositorThreadUpdateFunction);
+ }
+
+ updateFunction();
+}
+
+bool TextureMapperPlatformLayerProxy::isActive()
+{
+ ASSERT(m_lock.isHeld());
+ return !!m_targetLayer && !!m_compositor;
+}
+
+void TextureMapperPlatformLayerProxy::pushNextBuffer(std::unique_ptr<TextureMapperPlatformLayerBuffer> newBuffer)
+{
+ ASSERT(m_lock.isHeld());
+ m_pendingBuffer = WTFMove(newBuffer);
+
+ if (m_compositor)
+ m_compositor->onNewBufferAvailable();
+}
+
+std::unique_ptr<TextureMapperPlatformLayerBuffer> TextureMapperPlatformLayerProxy::getAvailableBuffer(const IntSize& size, GC3Dint internalFormat)
+{
+ ASSERT(m_lock.isHeld());
+ std::unique_ptr<TextureMapperPlatformLayerBuffer> availableBuffer;
+
+ auto buffers = WTFMove(m_usedBuffers);
+ for (auto& buffer : buffers) {
+ if (!buffer)
+ continue;
+
+ if (!availableBuffer && buffer->canReuseWithoutReset(size, internalFormat)) {
+ availableBuffer = WTFMove(buffer);
+ availableBuffer->markUsed();
+ continue;
+ }
+ m_usedBuffers.append(WTFMove(buffer));
+ }
+
+ if (!m_usedBuffers.isEmpty())
+ scheduleReleaseUnusedBuffers();
+ return availableBuffer;
+}
+
+void TextureMapperPlatformLayerProxy::scheduleReleaseUnusedBuffers()
+{
+ if (!m_releaseUnusedBuffersTimer.isActive())
+ m_releaseUnusedBuffersTimer.startOneShot(s_releaseUnusedBuffersTimerInterval);
+}
+
+void TextureMapperPlatformLayerProxy::releaseUnusedBuffersTimerFired()
+{
+ LockHolder locker(m_lock);
+ if (m_usedBuffers.isEmpty())
+ return;
+
+ auto buffers = WTFMove(m_usedBuffers);
+ double minUsedTime = monotonicallyIncreasingTime() - s_releaseUnusedSecondsTolerance;
+
+ for (auto& buffer : buffers) {
+ if (buffer && buffer->lastUsedTime() >= minUsedTime)
+ m_usedBuffers.append(WTFMove(buffer));
+ }
+}
+
+void TextureMapperPlatformLayerProxy::swapBuffer()
+{
+ ASSERT(m_compositorThreadID == WTF::currentThread());
+ LockHolder locker(m_lock);
+ if (!m_targetLayer || !m_pendingBuffer)
+ return;
+
+ auto prevBuffer = WTFMove(m_currentBuffer);
+
+ m_currentBuffer = WTFMove(m_pendingBuffer);
+ m_targetLayer->setContentsLayer(m_currentBuffer.get());
+
+ if (prevBuffer && prevBuffer->hasManagedTexture())
+ m_usedBuffers.append(WTFMove(prevBuffer));
+}
+
+bool TextureMapperPlatformLayerProxy::scheduleUpdateOnCompositorThread(Function<void()>&& updateFunction)
+{
+ LockHolder locker(m_lock);
+ if (!m_compositorThreadUpdateTimer)
+ return false;
+
+ m_compositorThreadUpdateFunction = WTFMove(updateFunction);
+ m_compositorThreadUpdateTimer->startOneShot(0);
+ return true;
+}
+
+void TextureMapperPlatformLayerProxy::compositorThreadUpdateTimerFired()
+{
+ Function<void()> updateFunction;
+ {
+ LockHolder locker(m_lock);
+ if (!m_compositorThreadUpdateFunction)
+ return;
+ updateFunction = WTFMove(m_compositorThreadUpdateFunction);
+ }
+
+ updateFunction();
+}
+
+} // namespace WebCore
+
+#endif // USE(COORDINATED_GRAPHICS_THREADED)
diff --git a/Source/WebCore/platform/graphics/texmap/TextureMapperPlatformLayerProxy.h b/Source/WebCore/platform/graphics/texmap/TextureMapperPlatformLayerProxy.h
new file mode 100644
index 000000000..b710ab4ce
--- /dev/null
+++ b/Source/WebCore/platform/graphics/texmap/TextureMapperPlatformLayerProxy.h
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2015 Igalia S.L.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef TextureMapperPlatformLayerProxy_h
+#define TextureMapperPlatformLayerProxy_h
+
+#if USE(COORDINATED_GRAPHICS_THREADED)
+
+#include "GraphicsTypes3D.h"
+#include "TextureMapper.h"
+#include "TransformationMatrix.h"
+#include <wtf/Condition.h>
+#include <wtf/Function.h>
+#include <wtf/Lock.h>
+#include <wtf/RunLoop.h>
+#include <wtf/ThreadSafeRefCounted.h>
+#include <wtf/Vector.h>
+
+#ifndef NDEBUG
+#include <wtf/Threading.h>
+#endif
+
+namespace WebCore {
+
+class TextureMapperLayer;
+class TextureMapperPlatformLayerProxy;
+class TextureMapperPlatformLayerBuffer;
+
+class TextureMapperPlatformLayerProxyProvider {
+public:
+ virtual RefPtr<TextureMapperPlatformLayerProxy> proxy() const = 0;
+ virtual void swapBuffersIfNeeded() = 0;
+};
+
+class TextureMapperPlatformLayerProxy : public ThreadSafeRefCounted<TextureMapperPlatformLayerProxy> {
+ WTF_MAKE_FAST_ALLOCATED();
+public:
+ class Compositor {
+ public:
+ virtual void onNewBufferAvailable() = 0;
+ };
+
+ TextureMapperPlatformLayerProxy();
+ virtual ~TextureMapperPlatformLayerProxy();
+
+ // To avoid multiple lock/release situation to update a single frame,
+ // the implementation of TextureMapperPlatformLayerProxyProvider should
+ // aquire / release the lock explicitly to use below methods.
+ Lock& lock() { return m_lock; }
+ std::unique_ptr<TextureMapperPlatformLayerBuffer> getAvailableBuffer(const IntSize&, GC3Dint internalFormat);
+ void pushNextBuffer(std::unique_ptr<TextureMapperPlatformLayerBuffer>);
+ bool isActive();
+
+ void activateOnCompositingThread(Compositor*, TextureMapperLayer*);
+ void invalidate();
+
+ void swapBuffer();
+
+ bool scheduleUpdateOnCompositorThread(Function<void()>&&);
+
+private:
+ void scheduleReleaseUnusedBuffers();
+ void releaseUnusedBuffersTimerFired();
+
+ Compositor* m_compositor;
+ TextureMapperLayer* m_targetLayer;
+
+ std::unique_ptr<TextureMapperPlatformLayerBuffer> m_currentBuffer;
+ std::unique_ptr<TextureMapperPlatformLayerBuffer> m_pendingBuffer;
+
+ Lock m_lock;
+
+ Vector<std::unique_ptr<TextureMapperPlatformLayerBuffer>> m_usedBuffers;
+
+ RunLoop::Timer<TextureMapperPlatformLayerProxy> m_releaseUnusedBuffersTimer;
+#ifndef NDEBUG
+ ThreadIdentifier m_compositorThreadID { 0 };
+#endif
+
+ void compositorThreadUpdateTimerFired();
+ std::unique_ptr<RunLoop::Timer<TextureMapperPlatformLayerProxy>> m_compositorThreadUpdateTimer;
+ Function<void()> m_compositorThreadUpdateFunction;
+};
+
+} // namespace WebCore
+
+#endif // USE(COORDINATED_GRAPHICS_THREADED)
+
+#endif // TextureMapperPlatformLayerProxy_h
diff --git a/Source/WebCore/platform/graphics/texmap/TextureMapperShaderProgram.cpp b/Source/WebCore/platform/graphics/texmap/TextureMapperShaderProgram.cpp
index 5c0a76db6..b0c371b99 100644
--- a/Source/WebCore/platform/graphics/texmap/TextureMapperShaderProgram.cpp
+++ b/Source/WebCore/platform/graphics/texmap/TextureMapperShaderProgram.cpp
@@ -22,19 +22,12 @@
#include "config.h"
#include "TextureMapperShaderProgram.h"
-#if USE(ACCELERATED_COMPOSITING) && USE(TEXTURE_MAPPER)
-#include "LengthFunctions.h"
+#if USE(TEXTURE_MAPPER_GL)
+
#include "Logging.h"
#include "TextureMapperGL.h"
-
#include <wtf/text/StringBuilder.h>
-#if PLATFORM(WIN) || (PLATFORM(GTK) && OS(WINDOWS))
-#undef NO_ERROR
-#endif
-
-#define STRINGIFY(...) #__VA_ARGS__
-
namespace WebCore {
static inline bool compositingLogEnabled()
@@ -46,85 +39,21 @@ static inline bool compositingLogEnabled()
#endif
}
-TextureMapperShaderProgram::TextureMapperShaderProgram(PassRefPtr<GraphicsContext3D> context, const String& vertex, const String& fragment)
- : m_context(context)
-{
- m_vertexShader = m_context->createShader(GraphicsContext3D::VERTEX_SHADER);
- m_fragmentShader = m_context->createShader(GraphicsContext3D::FRAGMENT_SHADER);
- m_context->shaderSource(m_vertexShader, vertex);
- m_context->shaderSource(m_fragmentShader, fragment);
- m_id = m_context->createProgram();
- m_context->compileShader(m_vertexShader);
- m_context->compileShader(m_fragmentShader);
- m_context->attachShader(m_id, m_vertexShader);
- m_context->attachShader(m_id, m_fragmentShader);
- m_context->linkProgram(m_id);
-
- if (!compositingLogEnabled())
- return;
-
- if (m_context->getError() == GraphicsContext3D::NO_ERROR)
- return;
-
- String log = m_context->getShaderInfoLog(m_vertexShader);
- LOG(Compositing, "Vertex shader log: %s\n", log.utf8().data());
- log = m_context->getShaderInfoLog(m_fragmentShader);
- LOG(Compositing, "Fragment shader log: %s\n", log.utf8().data());
- log = m_context->getProgramInfoLog(m_id);
- LOG(Compositing, "Program log: %s\n", log.utf8().data());
-}
-
-void TextureMapperShaderProgram::setMatrix(GC3Duint location, const TransformationMatrix& matrix)
-{
- GC3Dfloat matrixAsFloats[] = {
- GC3Dfloat(matrix.m11()), GC3Dfloat(matrix.m12()), GC3Dfloat(matrix.m13()), GC3Dfloat(matrix.m14()),
- GC3Dfloat(matrix.m21()), GC3Dfloat(matrix.m22()), GC3Dfloat(matrix.m23()), GC3Dfloat(matrix.m24()),
- GC3Dfloat(matrix.m31()), GC3Dfloat(matrix.m32()), GC3Dfloat(matrix.m33()), GC3Dfloat(matrix.m34()),
- GC3Dfloat(matrix.m41()), GC3Dfloat(matrix.m42()), GC3Dfloat(matrix.m43()), GC3Dfloat(matrix.m44())
- };
-
- m_context->uniformMatrix4fv(location, 1, false, matrixAsFloats);
-}
-
-GC3Duint TextureMapperShaderProgram::getLocation(const AtomicString& name, VariableType type)
-{
- HashMap<AtomicString, GC3Duint>::iterator it = m_variables.find(name);
- if (it != m_variables.end())
- return it->value;
-
- GC3Duint location = 0;
- switch (type) {
- case UniformVariable:
- location = m_context->getUniformLocation(m_id, name);
- break;
- case AttribVariable:
- location = m_context->getAttribLocation(m_id, name);
- break;
- default:
- ASSERT_NOT_REACHED();
- break;
- }
-
- m_variables.add(name, location);
- return location;
-}
+#define STRINGIFY(...) #__VA_ARGS__
-TextureMapperShaderProgram::~TextureMapperShaderProgram()
-{
- Platform3DObject programID = m_id;
- if (!programID)
- return;
+#define GLSL_DIRECTIVE(...) "#"#__VA_ARGS__"\n"
- m_context->detachShader(programID, m_vertexShader);
- m_context->deleteShader(m_vertexShader);
- m_context->detachShader(programID, m_fragmentShader);
- m_context->deleteShader(m_fragmentShader);
- m_context->deleteProgram(programID);
-}
+#define TEXTURE_SPACE_MATRIX_PRECISION_DIRECTIVE \
+ GLSL_DIRECTIVE(ifdef GL_FRAGMENT_PRECISION_HIGH) \
+ GLSL_DIRECTIVE(define TextureSpaceMatrixPrecision highp) \
+ GLSL_DIRECTIVE(else) \
+ GLSL_DIRECTIVE(define TextureSpaceMatrixPrecision mediump) \
+ GLSL_DIRECTIVE(endif)
-#define GLSL_DIRECTIVE(...) "#"#__VA_ARGS__"\n"
static const char* vertexTemplate =
+ TEXTURE_SPACE_MATRIX_PRECISION_DIRECTIVE
STRINGIFY(
+ precision TextureSpaceMatrixPrecision float;
attribute vec4 a_vertex;
uniform mat4 u_modelViewMatrix;
uniform mat4 u_projectionMatrix;
@@ -211,7 +140,10 @@ static const char* fragmentTemplate =
RECT_TEXTURE_DIRECTIVE
ANTIALIASING_TEX_COORD_DIRECTIVE
BLUR_CONSTANTS
+ TEXTURE_SPACE_MATRIX_PRECISION_DIRECTIVE
STRINGIFY(
+ precision TextureSpaceMatrixPrecision float;
+ uniform mat4 u_textureSpaceMatrix;
precision mediump float;
uniform SamplerType s_sampler;
uniform sampler2D s_contentTexture;
@@ -224,10 +156,10 @@ static const char* fragmentTemplate =
uniform vec2 u_shadowOffset;
uniform vec4 u_color;
uniform float u_gaussianKernel[GAUSSIAN_KERNEL_HALF_WIDTH];
- uniform mat4 u_textureSpaceMatrix;
void noop(inout vec4 dummyParameter) { }
void noop(inout vec4 dummyParameter, vec2 texCoord) { }
+ void noop(inout vec2 dummyParameter) { }
float antialias() { return smoothstep(0., 1., v_antialias); }
@@ -239,6 +171,8 @@ static const char* fragmentTemplate =
vec2 vertexTransformTexCoord() { return v_transformedTexCoord; }
+ void applyManualRepeat(inout vec2 pos) { pos = fract(pos); }
+
void applyTexture(inout vec4 color, vec2 texCoord) { color = SamplerFunction(s_sampler, texCoord); }
void applyOpacity(inout vec4 color) { color *= u_opacity; }
void applyAntialiasing(inout vec4 color) { color *= antialias(); }
@@ -350,6 +284,7 @@ static const char* fragmentTemplate =
{
vec4 color = vec4(1., 1., 1., 1.);
vec2 texCoord = transformTexCoord();
+ applyManualRepeatIfNeeded(texCoord);
applyTextureIfNeeded(color, texCoord);
applySolidColorIfNeeded(color);
applyAntialiasingIfNeeded(color);
@@ -369,13 +304,13 @@ static const char* fragmentTemplate =
}
);
-PassRefPtr<TextureMapperShaderProgram> TextureMapperShaderProgram::create(PassRefPtr<GraphicsContext3D> context, TextureMapperShaderProgram::Options options)
+Ref<TextureMapperShaderProgram> TextureMapperShaderProgram::create(Ref<GraphicsContext3D>&& context, TextureMapperShaderProgram::Options options)
{
- StringBuilder shaderBuilder;
#define SET_APPLIER_FROM_OPTIONS(Applier) \
- shaderBuilder.append(\
+ optionsApplierBuilder.append(\
(options & TextureMapperShaderProgram::Applier) ? ENABLE_APPLIER(Applier) : DISABLE_APPLIER(Applier))
+ StringBuilder optionsApplierBuilder;
SET_APPLIER_FROM_OPTIONS(Texture);
SET_APPLIER_FROM_OPTIONS(Rect);
SET_APPLIER_FROM_OPTIONS(SolidColor);
@@ -392,16 +327,84 @@ PassRefPtr<TextureMapperShaderProgram> TextureMapperShaderProgram::create(PassRe
SET_APPLIER_FROM_OPTIONS(BlurFilter);
SET_APPLIER_FROM_OPTIONS(AlphaBlur);
SET_APPLIER_FROM_OPTIONS(ContentTexture);
- StringBuilder vertexBuilder;
- vertexBuilder.append(shaderBuilder.toString());
- vertexBuilder.append(vertexTemplate);
- shaderBuilder.append(fragmentTemplate);
+ SET_APPLIER_FROM_OPTIONS(ManualRepeat);
+
+ StringBuilder vertexShaderBuilder;
+ vertexShaderBuilder.append(optionsApplierBuilder.toString());
+ vertexShaderBuilder.append(vertexTemplate);
+
+ StringBuilder fragmentShaderBuilder;
+ fragmentShaderBuilder.append(optionsApplierBuilder.toString());
+ fragmentShaderBuilder.append(fragmentTemplate);
+
+ return adoptRef(*new TextureMapperShaderProgram(WTFMove(context), vertexShaderBuilder.toString(), fragmentShaderBuilder.toString()));
+}
+
+TextureMapperShaderProgram::TextureMapperShaderProgram(Ref<GraphicsContext3D>&& context, const String& vertex, const String& fragment)
+ : m_context(WTFMove(context))
+{
+ m_vertexShader = m_context->createShader(GraphicsContext3D::VERTEX_SHADER);
+ m_context->shaderSource(m_vertexShader, vertex);
+
+ m_fragmentShader = m_context->createShader(GraphicsContext3D::FRAGMENT_SHADER);
+ m_context->shaderSource(m_fragmentShader, fragment);
+
+ m_id = m_context->createProgram();
+ m_context->compileShader(m_vertexShader);
+ m_context->compileShader(m_fragmentShader);
+ m_context->attachShader(m_id, m_vertexShader);
+ m_context->attachShader(m_id, m_fragmentShader);
+ m_context->linkProgram(m_id);
+
+ if (!compositingLogEnabled())
+ return;
+
+ if (m_context->getError() == GraphicsContext3D::NO_ERROR)
+ return;
- String vertexSource = vertexBuilder.toString();
- String fragmentSource = shaderBuilder.toString();
+ String log = m_context->getShaderInfoLog(m_vertexShader);
+ LOG(Compositing, "Vertex shader log: %s\n", log.utf8().data());
+ log = m_context->getShaderInfoLog(m_fragmentShader);
+ LOG(Compositing, "Fragment shader log: %s\n", log.utf8().data());
+ log = m_context->getProgramInfoLog(m_id);
+ LOG(Compositing, "Program log: %s\n", log.utf8().data());
+}
+
+TextureMapperShaderProgram::~TextureMapperShaderProgram()
+{
+ if (!m_id)
+ return;
- return adoptRef(new TextureMapperShaderProgram(context, vertexSource, fragmentSource));
+ m_context->detachShader(m_id, m_vertexShader);
+ m_context->deleteShader(m_vertexShader);
+ m_context->detachShader(m_id, m_fragmentShader);
+ m_context->deleteShader(m_fragmentShader);
+ m_context->deleteProgram(m_id);
}
+void TextureMapperShaderProgram::setMatrix(GC3Duint location, const TransformationMatrix& matrix)
+{
+ TransformationMatrix::FloatMatrix4 floatMatrix;
+ matrix.toColumnMajorFloatArray(floatMatrix);
+ m_context->uniformMatrix4fv(location, 1, false, floatMatrix);
}
-#endif
+
+GC3Duint TextureMapperShaderProgram::getLocation(const AtomicString& name, VariableType type)
+{
+ auto addResult = m_variables.ensure(name,
+ [this, &name, type] {
+ switch (type) {
+ case UniformVariable:
+ return m_context->getUniformLocation(m_id, name);
+ case AttribVariable:
+ return m_context->getAttribLocation(m_id, name);
+ }
+ ASSERT_NOT_REACHED();
+ return 0;
+ });
+ return addResult.iterator->value;
+}
+
+} // namespace WebCore
+
+#endif // USE(TEXTURE_MAPPER_GL)
diff --git a/Source/WebCore/platform/graphics/texmap/TextureMapperShaderProgram.h b/Source/WebCore/platform/graphics/texmap/TextureMapperShaderProgram.h
index 3345fc9fe..541a42b27 100644
--- a/Source/WebCore/platform/graphics/texmap/TextureMapperShaderProgram.h
+++ b/Source/WebCore/platform/graphics/texmap/TextureMapperShaderProgram.h
@@ -21,16 +21,23 @@
#ifndef TextureMapperShaderProgram_h
#define TextureMapperShaderProgram_h
-#if USE(TEXTURE_MAPPER)
+#if USE(TEXTURE_MAPPER_GL)
+
#include "GraphicsContext3D.h"
#include "TransformationMatrix.h"
#include <wtf/HashMap.h>
-#include <wtf/PassRefPtr.h>
-#include <wtf/RefPtr.h>
+#include <wtf/NeverDestroyed.h>
+#include <wtf/Ref.h>
#include <wtf/text/AtomicStringHash.h>
namespace WebCore {
-#define TEXMAP_DECLARE_VARIABLE(Accessor, Name, Type) GC3Duint Accessor##Location() { static const AtomicString name(Name); return getLocation(name, Type); }
+
+#define TEXMAP_DECLARE_VARIABLE(Accessor, Name, Type) \
+ GC3Duint Accessor##Location() { \
+ static NeverDestroyed<const AtomicString> name(Name, AtomicString::ConstructFromLiteral); \
+ return getLocation(name.get(), Type); \
+ }
+
#define TEXMAP_DECLARE_UNIFORM(Accessor) TEXMAP_DECLARE_VARIABLE(Accessor, "u_"#Accessor, UniformVariable)
#define TEXMAP_DECLARE_ATTRIBUTE(Accessor) TEXMAP_DECLARE_VARIABLE(Accessor, "a_"#Accessor, AttribVariable)
#define TEXMAP_DECLARE_SAMPLER(Accessor) TEXMAP_DECLARE_VARIABLE(Accessor, "s_"#Accessor, UniformVariable)
@@ -53,15 +60,17 @@ public:
OpacityFilter = 1L << 13,
BlurFilter = 1L << 14,
AlphaBlur = 1L << 15,
- ContentTexture = 1L << 16
+ ContentTexture = 1L << 16,
+ ManualRepeat = 1L << 17
};
typedef unsigned Options;
- static PassRefPtr<TextureMapperShaderProgram> create(PassRefPtr<GraphicsContext3D>, Options);
+ static Ref<TextureMapperShaderProgram> create(Ref<GraphicsContext3D>&&, Options);
virtual ~TextureMapperShaderProgram();
+
Platform3DObject programID() const { return m_id; }
- GraphicsContext3D* context() { return m_context.get(); }
+ GraphicsContext3D& context() { return m_context; }
TEXMAP_DECLARE_ATTRIBUTE(vertex)
@@ -74,30 +83,29 @@ public:
TEXMAP_DECLARE_SAMPLER(sampler)
TEXMAP_DECLARE_SAMPLER(mask)
-#if ENABLE(CSS_FILTERS)
TEXMAP_DECLARE_UNIFORM(filterAmount)
TEXMAP_DECLARE_UNIFORM(gaussianKernel)
TEXMAP_DECLARE_UNIFORM(blurRadius)
TEXMAP_DECLARE_UNIFORM(shadowOffset)
TEXMAP_DECLARE_SAMPLER(contentTexture)
-#endif
void setMatrix(GC3Duint, const TransformationMatrix&);
private:
- TextureMapperShaderProgram(PassRefPtr<GraphicsContext3D>, const String& vertexShaderSource, const String& fragmentShaderSource);
+ TextureMapperShaderProgram(Ref<GraphicsContext3D>&&, const String& vertexShaderSource, const String& fragmentShaderSource);
+
Platform3DObject m_vertexShader;
Platform3DObject m_fragmentShader;
enum VariableType { UniformVariable, AttribVariable };
GC3Duint getLocation(const AtomicString&, VariableType);
- RefPtr<GraphicsContext3D> m_context;
+ Ref<GraphicsContext3D> m_context;
Platform3DObject m_id;
HashMap<AtomicString, GC3Duint> m_variables;
};
}
-#endif
+#endif // USE(TEXTURE_MAPPER_GL)
#endif // TextureMapperShaderProgram_h
diff --git a/Source/WebCore/platform/graphics/texmap/TextureMapperSurfaceBackingStore.cpp b/Source/WebCore/platform/graphics/texmap/TextureMapperSurfaceBackingStore.cpp
deleted file mode 100644
index 8d38776e9..000000000
--- a/Source/WebCore/platform/graphics/texmap/TextureMapperSurfaceBackingStore.cpp
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- Copyright (C) 2013 Nokia Corporation and/or its subsidiary(-ies)
-
- This library is free software; you can redistribute it and/or
- modify it under the terms of the GNU Library General Public
- License as published by the Free Software Foundation; either
- version 2 of the License, or (at your option) any later version.
-
- This library is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Library General Public License for more details.
-
- You should have received a copy of the GNU Library General Public License
- along with this library; see the file COPYING.LIB. If not, write to
- the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
- Boston, MA 02110-1301, USA.
- */
-
-#include "config.h"
-
-#if USE(ACCELERATED_COMPOSITING) && USE(GRAPHICS_SURFACE)
-#include "TextureMapperSurfaceBackingStore.h"
-
-#include "GraphicsSurface.h"
-
-namespace WebCore {
-
-void TextureMapperSurfaceBackingStore::setGraphicsSurface(PassRefPtr<GraphicsSurface> surface)
-{
- m_graphicsSurface = surface;
-}
-
-void TextureMapperSurfaceBackingStore::swapBuffersIfNeeded(uint32_t)
-{
- if (m_graphicsSurface)
- m_graphicsSurface->swapBuffers();
-}
-
-PassRefPtr<BitmapTexture> TextureMapperSurfaceBackingStore::texture() const
-{
- // FIXME: Instead of just returning an empty texture, we should wrap the texture contents into a BitmapTexture.
- RefPtr<BitmapTexture> emptyTexture;
- return emptyTexture;
-}
-
-void TextureMapperSurfaceBackingStore::paintToTextureMapper(TextureMapper* textureMapper, const FloatRect& targetRect, const TransformationMatrix& transform, float opacity)
-{
- if (m_graphicsSurface)
- m_graphicsSurface->paintToTextureMapper(textureMapper, targetRect, transform, opacity);
-}
-
-} // namespace WebCore
-#endif
diff --git a/Source/WebCore/platform/graphics/texmap/TextureMapperSurfaceBackingStore.h b/Source/WebCore/platform/graphics/texmap/TextureMapperSurfaceBackingStore.h
deleted file mode 100644
index 793c61dc3..000000000
--- a/Source/WebCore/platform/graphics/texmap/TextureMapperSurfaceBackingStore.h
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- Copyright (C) 2013 Nokia Corporation and/or its subsidiary(-ies)
-
- This library is free software; you can redistribute it and/or
- modify it under the terms of the GNU Library General Public
- License as published by the Free Software Foundation; either
- version 2 of the License, or (at your option) any later version.
-
- This library is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Library General Public License for more details.
-
- You should have received a copy of the GNU Library General Public License
- along with this library; see the file COPYING.LIB. If not, write to
- the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
- Boston, MA 02110-1301, USA.
- */
-
-#ifndef TextureMapperSurfaceBackingStore_h
-#define TextureMapperSurfaceBackingStore_h
-
-#if USE(ACCELERATED_COMPOSITING) && USE(GRAPHICS_SURFACE)
-
-#include "GraphicsSurface.h"
-#include "TextureMapperBackingStore.h"
-#include <wtf/RefPtr.h>
-
-namespace WebCore {
-
-class TextureMapper;
-class FloatRect;
-
-class TextureMapperSurfaceBackingStore : public TextureMapperBackingStore {
-public:
- static PassRefPtr<TextureMapperSurfaceBackingStore> create() { return adoptRef(new TextureMapperSurfaceBackingStore); }
- void setGraphicsSurface(PassRefPtr<GraphicsSurface>);
- void swapBuffersIfNeeded(uint32_t frontBuffer);
- virtual PassRefPtr<BitmapTexture> texture() const;
- virtual void paintToTextureMapper(TextureMapper*, const FloatRect&, const TransformationMatrix&, float);
- virtual ~TextureMapperSurfaceBackingStore() { }
-
-private:
- TextureMapperSurfaceBackingStore()
- : TextureMapperBackingStore()
- { }
-
- RefPtr<GraphicsSurface> m_graphicsSurface;
-};
-
-}
-
-#endif
-
-#endif
diff --git a/Source/WebCore/platform/graphics/texmap/TextureMapperTile.cpp b/Source/WebCore/platform/graphics/texmap/TextureMapperTile.cpp
index 2ea7fb6ed..39365673e 100644
--- a/Source/WebCore/platform/graphics/texmap/TextureMapperTile.cpp
+++ b/Source/WebCore/platform/graphics/texmap/TextureMapperTile.cpp
@@ -18,18 +18,16 @@
*/
#include "config.h"
-#if USE(ACCELERATED_COMPOSITING) && USE(TEXTURE_MAPPER)
#include "TextureMapperTile.h"
#include "Image.h"
#include "TextureMapper.h"
-#include <wtf/RefPtr.h>
namespace WebCore {
class GraphicsLayer;
-void TextureMapperTile::updateContents(TextureMapper* textureMapper, Image* image, const IntRect& dirtyRect, BitmapTexture::UpdateContentsFlag updateContentsFlag)
+void TextureMapperTile::updateContents(TextureMapper& textureMapper, Image* image, const IntRect& dirtyRect, BitmapTexture::UpdateContentsFlag updateContentsFlag)
{
IntRect targetRect = enclosingIntRect(m_rect);
targetRect.intersect(dirtyRect);
@@ -43,14 +41,14 @@ void TextureMapperTile::updateContents(TextureMapper* textureMapper, Image* imag
// Normalize targetRect to the texture's coordinates.
targetRect.move(-m_rect.x(), -m_rect.y());
if (!m_texture) {
- m_texture = textureMapper->createTexture();
+ m_texture = textureMapper.createTexture();
m_texture->reset(targetRect.size(), image->currentFrameKnownToBeOpaque() ? 0 : BitmapTexture::SupportsAlpha);
}
m_texture->updateContents(image, targetRect, sourceOffset, updateContentsFlag);
}
-void TextureMapperTile::updateContents(TextureMapper* textureMapper, GraphicsLayer* sourceLayer, const IntRect& dirtyRect, BitmapTexture::UpdateContentsFlag updateContentsFlag)
+void TextureMapperTile::updateContents(TextureMapper& textureMapper, GraphicsLayer* sourceLayer, const IntRect& dirtyRect, BitmapTexture::UpdateContentsFlag updateContentsFlag, float scale)
{
IntRect targetRect = enclosingIntRect(m_rect);
targetRect.intersect(dirtyRect);
@@ -62,18 +60,17 @@ void TextureMapperTile::updateContents(TextureMapper* textureMapper, GraphicsLay
targetRect.move(-m_rect.x(), -m_rect.y());
if (!m_texture) {
- m_texture = textureMapper->createTexture();
+ m_texture = textureMapper.createTexture();
m_texture->reset(targetRect.size(), BitmapTexture::SupportsAlpha);
}
- m_texture->updateContents(textureMapper, sourceLayer, targetRect, sourceOffset, updateContentsFlag);
+ m_texture->updateContents(textureMapper, sourceLayer, targetRect, sourceOffset, updateContentsFlag, scale);
}
-void TextureMapperTile::paint(TextureMapper* textureMapper, const TransformationMatrix& transform, float opacity, const unsigned exposedEdges)
+void TextureMapperTile::paint(TextureMapper& textureMapper, const TransformationMatrix& transform, float opacity, const unsigned exposedEdges)
{
if (texture().get())
- textureMapper->drawTexture(*texture().get(), rect(), transform, opacity, exposedEdges);
+ textureMapper.drawTexture(*texture().get(), rect(), transform, opacity, exposedEdges);
}
} // namespace WebCore
-#endif
diff --git a/Source/WebCore/platform/graphics/texmap/TextureMapperTile.h b/Source/WebCore/platform/graphics/texmap/TextureMapperTile.h
index ee6809f20..6aebd3139 100644
--- a/Source/WebCore/platform/graphics/texmap/TextureMapperTile.h
+++ b/Source/WebCore/platform/graphics/texmap/TextureMapperTile.h
@@ -20,8 +20,6 @@
#ifndef TextureMapperTile_h
#define TextureMapperTile_h
-#if USE(ACCELERATED_COMPOSITING) && USE(TEXTURE_MAPPER)
-
#include "FloatRect.h"
#include "Image.h"
#include "TextureMapper.h"
@@ -34,14 +32,14 @@ class GraphicsLayer;
class TextureMapperTile {
public:
- inline PassRefPtr<BitmapTexture> texture() const { return m_texture; }
+ inline RefPtr<BitmapTexture> texture() const { return m_texture; }
inline FloatRect rect() const { return m_rect; }
inline void setTexture(BitmapTexture* texture) { m_texture = texture; }
inline void setRect(const FloatRect& rect) { m_rect = rect; }
- void updateContents(TextureMapper*, Image*, const IntRect&, BitmapTexture::UpdateContentsFlag UpdateCanModifyOriginalImageData);
- void updateContents(TextureMapper*, GraphicsLayer*, const IntRect&, BitmapTexture::UpdateContentsFlag UpdateCanModifyOriginalImageData);
- virtual void paint(TextureMapper*, const TransformationMatrix&, float, const unsigned exposedEdges);
+ void updateContents(TextureMapper&, Image*, const IntRect&, BitmapTexture::UpdateContentsFlag UpdateCanModifyOriginalImageData);
+ void updateContents(TextureMapper&, GraphicsLayer*, const IntRect&, BitmapTexture::UpdateContentsFlag UpdateCanModifyOriginalImageData, float scale = 1);
+ virtual void paint(TextureMapper&, const TransformationMatrix&, float, const unsigned exposedEdges);
virtual ~TextureMapperTile() { }
explicit TextureMapperTile(const FloatRect& rect)
@@ -55,6 +53,5 @@ private:
};
}
-#endif
#endif
diff --git a/Source/WebCore/platform/graphics/texmap/TextureMapperTiledBackingStore.cpp b/Source/WebCore/platform/graphics/texmap/TextureMapperTiledBackingStore.cpp
index ef564023d..6833ac6b5 100644
--- a/Source/WebCore/platform/graphics/texmap/TextureMapperTiledBackingStore.cpp
+++ b/Source/WebCore/platform/graphics/texmap/TextureMapperTiledBackingStore.cpp
@@ -19,27 +19,26 @@
#include "config.h"
-#if USE(ACCELERATED_COMPOSITING) && USE(TEXTURE_MAPPER)
#include "TextureMapperTiledBackingStore.h"
#include "ImageBuffer.h"
+#include "ImageObserver.h"
#include "TextureMapper.h"
namespace WebCore {
class GraphicsLayer;
-TextureMapperTiledBackingStore::TextureMapperTiledBackingStore()
-{
-}
-
-void TextureMapperTiledBackingStore::updateContentsFromImageIfNeeded(TextureMapper* textureMapper)
+void TextureMapperTiledBackingStore::updateContentsFromImageIfNeeded(TextureMapper& textureMapper)
{
if (!m_image)
return;
- updateContents(textureMapper, m_image.get(), m_image->size(), m_image->rect(), BitmapTexture::UpdateCannotModifyOriginalImageData);
- m_image.clear();
+ updateContents(textureMapper, m_image.get(), m_image->size(), enclosingIntRect(m_image->rect()), BitmapTexture::UpdateCannotModifyOriginalImageData);
+
+ if (m_image->imageObserver())
+ m_image->imageObserver()->didDraw(m_image.get());
+ m_image = nullptr;
}
TransformationMatrix TextureMapperTiledBackingStore::adjustedTransformForRect(const FloatRect& targetRect)
@@ -47,34 +46,48 @@ TransformationMatrix TextureMapperTiledBackingStore::adjustedTransformForRect(co
return TransformationMatrix::rectToRect(rect(), targetRect);
}
-void TextureMapperTiledBackingStore::paintToTextureMapper(TextureMapper* textureMapper, const FloatRect& targetRect, const TransformationMatrix& transform, float opacity)
+void TextureMapperTiledBackingStore::paintToTextureMapper(TextureMapper& textureMapper, const FloatRect& targetRect, const TransformationMatrix& transform, float opacity)
{
updateContentsFromImageIfNeeded(textureMapper);
TransformationMatrix adjustedTransform = transform * adjustedTransformForRect(targetRect);
- for (size_t i = 0; i < m_tiles.size(); ++i)
- m_tiles[i].paint(textureMapper, adjustedTransform, opacity, calculateExposedTileEdges(rect(), m_tiles[i].rect()));
+ for (auto& tile : m_tiles)
+ tile.paint(textureMapper, adjustedTransform, opacity, calculateExposedTileEdges(rect(), tile.rect()));
}
-void TextureMapperTiledBackingStore::drawBorder(TextureMapper* textureMapper, const Color& borderColor, float borderWidth, const FloatRect& targetRect, const TransformationMatrix& transform)
+void TextureMapperTiledBackingStore::drawBorder(TextureMapper& textureMapper, const Color& borderColor, float borderWidth, const FloatRect& targetRect, const TransformationMatrix& transform)
{
TransformationMatrix adjustedTransform = transform * adjustedTransformForRect(targetRect);
- for (size_t i = 0; i < m_tiles.size(); ++i)
- textureMapper->drawBorder(borderColor, borderWidth, m_tiles[i].rect(), adjustedTransform);
+ for (auto& tile : m_tiles)
+ textureMapper.drawBorder(borderColor, borderWidth, tile.rect(), adjustedTransform);
}
-void TextureMapperTiledBackingStore::drawRepaintCounter(TextureMapper* textureMapper, int repaintCount, const Color& borderColor, const FloatRect& targetRect, const TransformationMatrix& transform)
+void TextureMapperTiledBackingStore::drawRepaintCounter(TextureMapper& textureMapper, int repaintCount, const Color& borderColor, const FloatRect& targetRect, const TransformationMatrix& transform)
{
TransformationMatrix adjustedTransform = transform * adjustedTransformForRect(targetRect);
- for (size_t i = 0; i < m_tiles.size(); ++i)
- textureMapper->drawNumber(repaintCount, borderColor, m_tiles[i].rect().location(), adjustedTransform);
+ for (auto& tile : m_tiles)
+ textureMapper.drawNumber(repaintCount, borderColor, tile.rect().location(), adjustedTransform);
+}
+
+void TextureMapperTiledBackingStore::updateContentsScale(float scale)
+{
+ if (m_contentsScale == scale)
+ return;
+
+ m_isScaleDirty = true;
+ m_contentsScale = scale;
}
void TextureMapperTiledBackingStore::createOrDestroyTilesIfNeeded(const FloatSize& size, const IntSize& tileSize, bool hasAlpha)
{
- if (size == m_size)
+ if (size == m_size && !m_isScaleDirty)
return;
m_size = size;
+ m_isScaleDirty = false;
+
+ FloatSize scaledSize(m_size);
+ if (!m_image)
+ scaledSize.scale(m_contentsScale);
Vector<FloatRect> tileRectsToAdd;
Vector<int> tileIndicesToRemove;
@@ -82,8 +95,8 @@ void TextureMapperTiledBackingStore::createOrDestroyTilesIfNeeded(const FloatSiz
// This method recycles tiles. We check which tiles we need to add, which to remove, and use as many
// removable tiles as replacement for new tiles when possible.
- for (float y = 0; y < m_size.height(); y += tileSize.height()) {
- for (float x = 0; x < m_size.width(); x += tileSize.width()) {
+ for (float y = 0; y < scaledSize.height(); y += tileSize.height()) {
+ for (float x = 0; x < scaledSize.width(); x += tileSize.width()) {
FloatRect tileRect(x, y, tileSize.width(), tileSize.height());
tileRect.intersect(rect());
tileRectsToAdd.append(tileRect);
@@ -112,51 +125,52 @@ void TextureMapperTiledBackingStore::createOrDestroyTilesIfNeeded(const FloatSiz
}
// Recycle removable tiles to be used for newly requested tiles.
- for (size_t i = 0; i < tileRectsToAdd.size(); ++i) {
+ for (auto& rect : tileRectsToAdd) {
if (!tileIndicesToRemove.isEmpty()) {
// We recycle an existing tile for usage with a new tile rect.
TextureMapperTile& tile = m_tiles[tileIndicesToRemove.last()];
tileIndicesToRemove.removeLast();
- tile.setRect(tileRectsToAdd[i]);
+ tile.setRect(rect);
if (tile.texture())
tile.texture()->reset(enclosingIntRect(tile.rect()).size(), hasAlpha ? BitmapTexture::SupportsAlpha : 0);
continue;
}
- m_tiles.append(TextureMapperTile(tileRectsToAdd[i]));
+ m_tiles.append(TextureMapperTile(rect));
}
// Remove unnecessary tiles, if they weren't recycled.
// We use a threshold to make sure we don't create/destroy tiles too eagerly.
- for (size_t i = 0; i < tileIndicesToRemove.size() && m_tiles.size() > TileEraseThreshold; ++i)
- m_tiles.remove(tileIndicesToRemove[i]);
+ for (auto& index : tileIndicesToRemove) {
+ if (m_tiles.size() <= TileEraseThreshold)
+ break;
+ m_tiles.remove(index);
+ }
}
-void TextureMapperTiledBackingStore::updateContents(TextureMapper* textureMapper, Image* image, const FloatSize& totalSize, const IntRect& dirtyRect, BitmapTexture::UpdateContentsFlag updateContentsFlag)
+void TextureMapperTiledBackingStore::updateContents(TextureMapper& textureMapper, Image* image, const FloatSize& totalSize, const IntRect& dirtyRect, BitmapTexture::UpdateContentsFlag updateContentsFlag)
{
- createOrDestroyTilesIfNeeded(totalSize, textureMapper->maxTextureSize(), !image->currentFrameKnownToBeOpaque());
- for (size_t i = 0; i < m_tiles.size(); ++i)
- m_tiles[i].updateContents(textureMapper, image, dirtyRect, updateContentsFlag);
+ createOrDestroyTilesIfNeeded(totalSize, textureMapper.maxTextureSize(), !image->currentFrameKnownToBeOpaque());
+ for (auto& tile : m_tiles)
+ tile.updateContents(textureMapper, image, dirtyRect, updateContentsFlag);
}
-void TextureMapperTiledBackingStore::updateContents(TextureMapper* textureMapper, GraphicsLayer* sourceLayer, const FloatSize& totalSize, const IntRect& dirtyRect, BitmapTexture::UpdateContentsFlag updateContentsFlag)
+void TextureMapperTiledBackingStore::updateContents(TextureMapper& textureMapper, GraphicsLayer* sourceLayer, const FloatSize& totalSize, const IntRect& dirtyRect, BitmapTexture::UpdateContentsFlag updateContentsFlag)
{
- createOrDestroyTilesIfNeeded(totalSize, textureMapper->maxTextureSize(), true);
- for (size_t i = 0; i < m_tiles.size(); ++i)
- m_tiles[i].updateContents(textureMapper, sourceLayer, dirtyRect, updateContentsFlag);
+ createOrDestroyTilesIfNeeded(totalSize, textureMapper.maxTextureSize(), true);
+ for (auto& tile : m_tiles)
+ tile.updateContents(textureMapper, sourceLayer, dirtyRect, updateContentsFlag, m_contentsScale);
}
-PassRefPtr<BitmapTexture> TextureMapperTiledBackingStore::texture() const
+RefPtr<BitmapTexture> TextureMapperTiledBackingStore::texture() const
{
- for (size_t i = 0; i < m_tiles.size(); ++i) {
- RefPtr<BitmapTexture> texture = m_tiles[i].texture();
- if (texture)
+ for (const auto& tile : m_tiles) {
+ if (auto texture = tile.texture())
return texture;
}
- return PassRefPtr<BitmapTexture>();
+ return nullptr;
}
} // namespace WebCore
-#endif
diff --git a/Source/WebCore/platform/graphics/texmap/TextureMapperTiledBackingStore.h b/Source/WebCore/platform/graphics/texmap/TextureMapperTiledBackingStore.h
index 38161c475..8514a4f73 100644
--- a/Source/WebCore/platform/graphics/texmap/TextureMapperTiledBackingStore.h
+++ b/Source/WebCore/platform/graphics/texmap/TextureMapperTiledBackingStore.h
@@ -20,8 +20,6 @@
#ifndef TextureMapperTiledBackingStore_h
#define TextureMapperTiledBackingStore_h
-#if USE(ACCELERATED_COMPOSITING) && USE(TEXTURE_MAPPER)
-
#include "FloatRect.h"
#include "Image.h"
#include "TextureMapperBackingStore.h"
@@ -37,28 +35,37 @@ public:
static PassRefPtr<TextureMapperTiledBackingStore> create() { return adoptRef(new TextureMapperTiledBackingStore); }
virtual ~TextureMapperTiledBackingStore() { }
- virtual PassRefPtr<BitmapTexture> texture() const override;
- virtual void paintToTextureMapper(TextureMapper*, const FloatRect&, const TransformationMatrix&, float) override;
- virtual void drawBorder(TextureMapper*, const Color&, float borderWidth, const FloatRect&, const TransformationMatrix&) override;
- virtual void drawRepaintCounter(TextureMapper*, int repaintCount, const Color&, const FloatRect&, const TransformationMatrix&) override;
- void updateContents(TextureMapper*, Image*, const FloatSize&, const IntRect&, BitmapTexture::UpdateContentsFlag);
- void updateContents(TextureMapper*, GraphicsLayer*, const FloatSize&, const IntRect&, BitmapTexture::UpdateContentsFlag);
+ RefPtr<BitmapTexture> texture() const override;
+ void paintToTextureMapper(TextureMapper&, const FloatRect&, const TransformationMatrix&, float) override;
+ void drawBorder(TextureMapper&, const Color&, float borderWidth, const FloatRect&, const TransformationMatrix&) override;
+ void drawRepaintCounter(TextureMapper&, int repaintCount, const Color&, const FloatRect&, const TransformationMatrix&) override;
+
+ void updateContentsScale(float);
+ void updateContents(TextureMapper&, Image*, const FloatSize&, const IntRect&, BitmapTexture::UpdateContentsFlag);
+ void updateContents(TextureMapper&, GraphicsLayer*, const FloatSize&, const IntRect&, BitmapTexture::UpdateContentsFlag);
void setContentsToImage(Image* image) { m_image = image; }
private:
- TextureMapperTiledBackingStore();
+ TextureMapperTiledBackingStore() { }
+
void createOrDestroyTilesIfNeeded(const FloatSize& backingStoreSize, const IntSize& tileSize, bool hasAlpha);
- void updateContentsFromImageIfNeeded(TextureMapper*);
+ void updateContentsFromImageIfNeeded(TextureMapper&);
TransformationMatrix adjustedTransformForRect(const FloatRect&);
- inline FloatRect rect() const { return FloatRect(FloatPoint::zero(), m_size); }
+ inline FloatRect rect() const
+ {
+ FloatRect rect(FloatPoint::zero(), m_size);
+ rect.scale(m_contentsScale);
+ return rect;
+ }
Vector<TextureMapperTile> m_tiles;
FloatSize m_size;
RefPtr<Image> m_image;
+ float m_contentsScale { 1 };
+ bool m_isScaleDirty { false };
};
} // namespace WebCore
-#endif
#endif
diff --git a/Source/WebCore/platform/graphics/texmap/coordinated/CoordinatedGraphicsLayer.cpp b/Source/WebCore/platform/graphics/texmap/coordinated/CoordinatedGraphicsLayer.cpp
new file mode 100644
index 000000000..8da58b2ce
--- /dev/null
+++ b/Source/WebCore/platform/graphics/texmap/coordinated/CoordinatedGraphicsLayer.cpp
@@ -0,0 +1,1182 @@
+/*
+ Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies)
+ Copyright (C) 2010 Apple Inc. All rights reserved.
+ Copyright (C) 2012 Company 100, Inc.
+ Copyright (C) 2012 Intel Corporation. All rights reserved.
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+ */
+
+#include "config.h"
+#include "CoordinatedGraphicsLayer.h"
+
+#if USE(COORDINATED_GRAPHICS)
+
+#include "FloatQuad.h"
+#include "GraphicsContext.h"
+#include "GraphicsLayer.h"
+#include "GraphicsLayerFactory.h"
+#include "ScrollableArea.h"
+#include <wtf/CurrentTime.h>
+#ifndef NDEBUG
+#include <wtf/SetForScope.h>
+#endif
+#include <wtf/text/CString.h>
+
+namespace WebCore {
+
+std::unique_ptr<GraphicsLayer> GraphicsLayer::create(GraphicsLayerFactory* factory, GraphicsLayerClient& client, Type layerType)
+{
+ if (!factory)
+ return std::make_unique<CoordinatedGraphicsLayer>(layerType, client);
+
+ return factory->createGraphicsLayer(layerType, client);
+}
+
+static CoordinatedLayerID toCoordinatedLayerID(GraphicsLayer* layer)
+{
+ return is<CoordinatedGraphicsLayer>(layer) ? downcast<CoordinatedGraphicsLayer>(*layer).id() : 0;
+}
+
+void CoordinatedGraphicsLayer::notifyFlushRequired()
+{
+ if (!m_coordinator)
+ return;
+
+ if (m_coordinator->isFlushingLayerChanges())
+ return;
+
+ client().notifyFlushRequired(this);
+}
+
+void CoordinatedGraphicsLayer::didChangeLayerState()
+{
+ m_shouldSyncLayerState = true;
+ notifyFlushRequired();
+}
+
+void CoordinatedGraphicsLayer::didChangeAnimations()
+{
+ m_shouldSyncAnimations = true;
+ notifyFlushRequired();
+}
+
+void CoordinatedGraphicsLayer::didChangeChildren()
+{
+ m_shouldSyncChildren = true;
+ notifyFlushRequired();
+}
+
+void CoordinatedGraphicsLayer::didChangeFilters()
+{
+ m_shouldSyncFilters = true;
+ notifyFlushRequired();
+}
+
+void CoordinatedGraphicsLayer::didChangeImageBacking()
+{
+ m_shouldSyncImageBacking = true;
+ notifyFlushRequired();
+}
+
+void CoordinatedGraphicsLayer::setShouldUpdateVisibleRect()
+{
+ m_shouldUpdateVisibleRect = true;
+ for (auto& child : children())
+ downcast<CoordinatedGraphicsLayer>(*child).setShouldUpdateVisibleRect();
+ if (replicaLayer())
+ downcast<CoordinatedGraphicsLayer>(*replicaLayer()).setShouldUpdateVisibleRect();
+}
+
+void CoordinatedGraphicsLayer::didChangeGeometry()
+{
+ didChangeLayerState();
+ setShouldUpdateVisibleRect();
+}
+
+CoordinatedGraphicsLayer::CoordinatedGraphicsLayer(Type layerType, GraphicsLayerClient& client)
+ : GraphicsLayer(layerType, client)
+#ifndef NDEBUG
+ , m_isPurging(false)
+#endif
+ , m_shouldUpdateVisibleRect(true)
+ , m_shouldSyncLayerState(true)
+ , m_shouldSyncChildren(true)
+ , m_shouldSyncFilters(true)
+ , m_shouldSyncImageBacking(true)
+ , m_shouldSyncAnimations(true)
+ , m_fixedToViewport(false)
+ , m_movingVisibleRect(false)
+ , m_pendingContentsScaleAdjustment(false)
+ , m_pendingVisibleRectAdjustment(false)
+#if USE(COORDINATED_GRAPHICS_THREADED)
+ , m_shouldSyncPlatformLayer(false)
+ , m_shouldUpdatePlatformLayer(false)
+#endif
+ , m_coordinator(0)
+ , m_compositedNativeImagePtr(0)
+ , m_platformLayer(0)
+ , m_animationStartedTimer(*this, &CoordinatedGraphicsLayer::animationStartedTimerFired)
+ , m_scrollableArea(0)
+{
+ static CoordinatedLayerID nextLayerID = 1;
+ m_id = nextLayerID++;
+}
+
+CoordinatedGraphicsLayer::~CoordinatedGraphicsLayer()
+{
+ if (m_coordinator) {
+ purgeBackingStores();
+ m_coordinator->detachLayer(this);
+ }
+ ASSERT(!m_coordinatedImageBacking);
+ ASSERT(!m_mainBackingStore);
+ willBeDestroyed();
+}
+
+bool CoordinatedGraphicsLayer::setChildren(const Vector<GraphicsLayer*>& children)
+{
+ bool ok = GraphicsLayer::setChildren(children);
+ if (!ok)
+ return false;
+ didChangeChildren();
+ return true;
+}
+
+void CoordinatedGraphicsLayer::addChild(GraphicsLayer* layer)
+{
+ GraphicsLayer::addChild(layer);
+ didChangeChildren();
+}
+
+void CoordinatedGraphicsLayer::addChildAtIndex(GraphicsLayer* layer, int index)
+{
+ GraphicsLayer::addChildAtIndex(layer, index);
+ didChangeChildren();
+}
+
+void CoordinatedGraphicsLayer::addChildAbove(GraphicsLayer* layer, GraphicsLayer* sibling)
+{
+ GraphicsLayer::addChildAbove(layer, sibling);
+ didChangeChildren();
+}
+
+void CoordinatedGraphicsLayer::addChildBelow(GraphicsLayer* layer, GraphicsLayer* sibling)
+{
+ GraphicsLayer::addChildBelow(layer, sibling);
+ didChangeChildren();
+}
+
+bool CoordinatedGraphicsLayer::replaceChild(GraphicsLayer* oldChild, GraphicsLayer* newChild)
+{
+ bool ok = GraphicsLayer::replaceChild(oldChild, newChild);
+ if (!ok)
+ return false;
+ didChangeChildren();
+ return true;
+}
+
+void CoordinatedGraphicsLayer::removeFromParent()
+{
+ if (CoordinatedGraphicsLayer* parentLayer = downcast<CoordinatedGraphicsLayer>(parent()))
+ parentLayer->didChangeChildren();
+ GraphicsLayer::removeFromParent();
+}
+
+void CoordinatedGraphicsLayer::setPosition(const FloatPoint& p)
+{
+ if (position() == p)
+ return;
+
+ GraphicsLayer::setPosition(p);
+ m_layerState.positionChanged = true;
+ didChangeGeometry();
+}
+
+void CoordinatedGraphicsLayer::setAnchorPoint(const FloatPoint3D& p)
+{
+ if (anchorPoint() == p)
+ return;
+
+ GraphicsLayer::setAnchorPoint(p);
+ m_layerState.anchorPointChanged = true;
+ didChangeGeometry();
+}
+
+void CoordinatedGraphicsLayer::setSize(const FloatSize& size)
+{
+ if (this->size() == size)
+ return;
+
+ GraphicsLayer::setSize(size);
+ m_layerState.sizeChanged = true;
+
+ if (maskLayer())
+ maskLayer()->setSize(size);
+ didChangeGeometry();
+}
+
+void CoordinatedGraphicsLayer::setTransform(const TransformationMatrix& t)
+{
+ if (transform() == t)
+ return;
+
+ GraphicsLayer::setTransform(t);
+ m_layerState.transformChanged = true;
+
+ didChangeGeometry();
+}
+
+void CoordinatedGraphicsLayer::setChildrenTransform(const TransformationMatrix& t)
+{
+ if (childrenTransform() == t)
+ return;
+
+ GraphicsLayer::setChildrenTransform(t);
+ m_layerState.childrenTransformChanged = true;
+
+ didChangeGeometry();
+}
+
+void CoordinatedGraphicsLayer::setPreserves3D(bool b)
+{
+ if (preserves3D() == b)
+ return;
+
+ GraphicsLayer::setPreserves3D(b);
+ m_layerState.preserves3D = b;
+ m_layerState.flagsChanged = true;
+
+ didChangeGeometry();
+}
+
+void CoordinatedGraphicsLayer::setMasksToBounds(bool b)
+{
+ if (masksToBounds() == b)
+ return;
+ GraphicsLayer::setMasksToBounds(b);
+ m_layerState.masksToBounds = b;
+ m_layerState.flagsChanged = true;
+
+ didChangeGeometry();
+}
+
+void CoordinatedGraphicsLayer::setDrawsContent(bool b)
+{
+ if (drawsContent() == b)
+ return;
+ GraphicsLayer::setDrawsContent(b);
+ m_layerState.drawsContent = b;
+ m_layerState.flagsChanged = true;
+
+ didChangeLayerState();
+}
+
+void CoordinatedGraphicsLayer::setContentsVisible(bool b)
+{
+ if (contentsAreVisible() == b)
+ return;
+ GraphicsLayer::setContentsVisible(b);
+ m_layerState.contentsVisible = b;
+ m_layerState.flagsChanged = true;
+
+ if (maskLayer())
+ maskLayer()->setContentsVisible(b);
+
+ didChangeLayerState();
+}
+
+void CoordinatedGraphicsLayer::setContentsOpaque(bool b)
+{
+ if (contentsOpaque() == b)
+ return;
+ if (m_mainBackingStore)
+ m_mainBackingStore->setSupportsAlpha(!b);
+ GraphicsLayer::setContentsOpaque(b);
+ m_layerState.contentsOpaque = b;
+ m_layerState.flagsChanged = true;
+
+ didChangeLayerState();
+}
+
+void CoordinatedGraphicsLayer::setBackfaceVisibility(bool b)
+{
+ if (backfaceVisibility() == b)
+ return;
+
+ GraphicsLayer::setBackfaceVisibility(b);
+ m_layerState.backfaceVisible = b;
+ m_layerState.flagsChanged = true;
+
+ didChangeLayerState();
+}
+
+void CoordinatedGraphicsLayer::setOpacity(float opacity)
+{
+ if (this->opacity() == opacity)
+ return;
+
+ GraphicsLayer::setOpacity(opacity);
+ m_layerState.opacity = opacity;
+ m_layerState.opacityChanged = true;
+
+ didChangeLayerState();
+}
+
+void CoordinatedGraphicsLayer::setContentsRect(const FloatRect& r)
+{
+ if (contentsRect() == r)
+ return;
+
+ GraphicsLayer::setContentsRect(r);
+ m_layerState.contentsRect = r;
+ m_layerState.contentsRectChanged = true;
+
+ didChangeLayerState();
+}
+
+void CoordinatedGraphicsLayer::setContentsTileSize(const FloatSize& s)
+{
+ if (contentsTileSize() == s)
+ return;
+
+ GraphicsLayer::setContentsTileSize(s);
+ m_layerState.contentsTileSize = s;
+ m_layerState.contentsTilingChanged = true;
+ didChangeLayerState();
+}
+
+void CoordinatedGraphicsLayer::setContentsTilePhase(const FloatSize& p)
+{
+ if (contentsTilePhase() == p)
+ return;
+
+ GraphicsLayer::setContentsTilePhase(p);
+ m_layerState.contentsTilePhase = p;
+ m_layerState.contentsTilingChanged = true;
+ didChangeLayerState();
+}
+
+bool GraphicsLayer::supportsContentsTiling()
+{
+ return true;
+}
+
+void CoordinatedGraphicsLayer::setContentsNeedsDisplay()
+{
+#if USE(COORDINATED_GRAPHICS_THREADED)
+ if (m_platformLayer)
+ m_shouldUpdatePlatformLayer = true;
+#endif
+
+ notifyFlushRequired();
+ addRepaintRect(contentsRect());
+}
+
+void CoordinatedGraphicsLayer::setContentsToPlatformLayer(PlatformLayer* platformLayer, ContentsLayerPurpose)
+{
+#if USE(COORDINATED_GRAPHICS_THREADED)
+ if (m_platformLayer != platformLayer)
+ m_shouldSyncPlatformLayer = true;
+
+ m_platformLayer = platformLayer;
+ notifyFlushRequired();
+#else
+ UNUSED_PARAM(platformLayer);
+#endif
+}
+
+bool CoordinatedGraphicsLayer::filtersCanBeComposited(const FilterOperations& filters) const
+{
+ if (!filters.size())
+ return false;
+
+ for (const auto& filterOperation : filters.operations()) {
+ if (filterOperation->type() == FilterOperation::REFERENCE)
+ return false;
+ }
+
+ return true;
+}
+
+bool CoordinatedGraphicsLayer::setFilters(const FilterOperations& newFilters)
+{
+ bool canCompositeFilters = filtersCanBeComposited(newFilters);
+ if (filters() == newFilters)
+ return canCompositeFilters;
+
+ if (canCompositeFilters) {
+ if (!GraphicsLayer::setFilters(newFilters))
+ return false;
+ didChangeFilters();
+ } else if (filters().size()) {
+ clearFilters();
+ didChangeFilters();
+ }
+
+ return canCompositeFilters;
+}
+
+void CoordinatedGraphicsLayer::setContentsToSolidColor(const Color& color)
+{
+ if (m_layerState.solidColor == color)
+ return;
+
+ m_layerState.solidColor = color;
+ m_layerState.solidColorChanged = true;
+
+ didChangeLayerState();
+}
+
+void CoordinatedGraphicsLayer::setShowDebugBorder(bool show)
+{
+ if (isShowingDebugBorder() == show)
+ return;
+
+ GraphicsLayer::setShowDebugBorder(show);
+ m_layerState.showDebugBorders = true;
+ m_layerState.flagsChanged = true;
+
+ didChangeLayerState();
+}
+
+void CoordinatedGraphicsLayer::setShowRepaintCounter(bool show)
+{
+ if (isShowingRepaintCounter() == show)
+ return;
+
+ GraphicsLayer::setShowRepaintCounter(show);
+ m_layerState.showRepaintCounter = true;
+ m_layerState.flagsChanged = true;
+
+ didChangeLayerState();
+}
+
+void CoordinatedGraphicsLayer::setContentsToImage(Image* image)
+{
+ NativeImagePtr nativeImagePtr = image ? image->nativeImageForCurrentFrame() : nullptr;
+ if (m_compositedImage == image && m_compositedNativeImagePtr == nativeImagePtr)
+ return;
+
+ m_compositedImage = image;
+ m_compositedNativeImagePtr = nativeImagePtr;
+
+ GraphicsLayer::setContentsToImage(image);
+ didChangeImageBacking();
+}
+
+void CoordinatedGraphicsLayer::setMaskLayer(GraphicsLayer* layer)
+{
+ if (layer == maskLayer())
+ return;
+
+ GraphicsLayer::setMaskLayer(layer);
+
+ if (!layer)
+ return;
+
+ layer->setSize(size());
+ layer->setContentsVisible(contentsAreVisible());
+ auto& coordinatedLayer = downcast<CoordinatedGraphicsLayer>(*layer);
+ coordinatedLayer.didChangeLayerState();
+
+ m_layerState.mask = coordinatedLayer.id();
+ m_layerState.maskChanged = true;
+
+ didChangeLayerState();
+}
+
+bool CoordinatedGraphicsLayer::shouldDirectlyCompositeImage(Image* image) const
+{
+ if (!image || !image->isBitmapImage())
+ return false;
+
+ enum { MaxDimenstionForDirectCompositing = 2000 };
+ if (image->width() > MaxDimenstionForDirectCompositing || image->height() > MaxDimenstionForDirectCompositing)
+ return false;
+
+ return true;
+}
+
+void CoordinatedGraphicsLayer::setReplicatedByLayer(GraphicsLayer* layer)
+{
+ if (layer == replicaLayer())
+ return;
+
+ GraphicsLayer::setReplicatedByLayer(layer);
+ m_layerState.replica = toCoordinatedLayerID(layer);
+ m_layerState.replicaChanged = true;
+ didChangeLayerState();
+}
+
+void CoordinatedGraphicsLayer::setNeedsDisplay()
+{
+ setNeedsDisplayInRect(FloatRect(FloatPoint(), size()));
+}
+
+void CoordinatedGraphicsLayer::setNeedsDisplayInRect(const FloatRect& rect, ShouldClipToLayer)
+{
+ if (m_mainBackingStore)
+ m_mainBackingStore->invalidate(IntRect(rect));
+
+ didChangeLayerState();
+
+ addRepaintRect(rect);
+}
+
+void CoordinatedGraphicsLayer::setScrollableArea(ScrollableArea* scrollableArea)
+{
+ bool oldScrollable = isScrollable();
+ m_scrollableArea = scrollableArea;
+ if (oldScrollable == isScrollable())
+ return;
+
+ m_layerState.isScrollable = isScrollable();
+ m_layerState.flagsChanged = true;
+ didChangeLayerState();
+}
+
+void CoordinatedGraphicsLayer::commitScrollOffset(const IntSize& offset)
+{
+ if (!isScrollable() || offset.isZero())
+ return;
+
+ m_scrollableArea->notifyScrollPositionChanged(m_scrollableArea->scrollPosition() + offset);
+ m_layerState.committedScrollOffset += offset;
+ m_layerState.committedScrollOffsetChanged = true;
+ didChangeLayerState();
+}
+
+void CoordinatedGraphicsLayer::setFixedToViewport(bool isFixed)
+{
+ if (m_fixedToViewport == isFixed)
+ return;
+
+ m_fixedToViewport = isFixed;
+ m_layerState.fixedToViewport = isFixed;
+ m_layerState.flagsChanged = true;
+
+ didChangeLayerState();
+}
+
+void CoordinatedGraphicsLayer::flushCompositingState(const FloatRect& rect)
+{
+ if (CoordinatedGraphicsLayer* mask = downcast<CoordinatedGraphicsLayer>(maskLayer()))
+ mask->flushCompositingStateForThisLayerOnly();
+
+ if (CoordinatedGraphicsLayer* replica = downcast<CoordinatedGraphicsLayer>(replicaLayer()))
+ replica->flushCompositingStateForThisLayerOnly();
+
+ flushCompositingStateForThisLayerOnly();
+
+ for (auto& child : children())
+ child->flushCompositingState(rect);
+}
+
+void CoordinatedGraphicsLayer::syncChildren()
+{
+ if (!m_shouldSyncChildren)
+ return;
+ m_shouldSyncChildren = false;
+ m_layerState.childrenChanged = true;
+ m_layerState.children.clear();
+ for (auto& child : children())
+ m_layerState.children.append(toCoordinatedLayerID(child));
+}
+
+void CoordinatedGraphicsLayer::syncFilters()
+{
+ if (!m_shouldSyncFilters)
+ return;
+ m_shouldSyncFilters = false;
+
+ m_layerState.filters = GraphicsLayer::filters();
+ m_layerState.filtersChanged = true;
+}
+
+void CoordinatedGraphicsLayer::syncImageBacking()
+{
+ if (!m_shouldSyncImageBacking)
+ return;
+ m_shouldSyncImageBacking = false;
+
+ if (m_compositedNativeImagePtr) {
+ ASSERT(!shouldHaveBackingStore());
+ ASSERT(m_compositedImage);
+
+ bool imageInstanceReplaced = m_coordinatedImageBacking && (m_coordinatedImageBacking->id() != CoordinatedImageBacking::getCoordinatedImageBackingID(m_compositedImage.get()));
+ if (imageInstanceReplaced)
+ releaseImageBackingIfNeeded();
+
+ if (!m_coordinatedImageBacking) {
+ m_coordinatedImageBacking = m_coordinator->createImageBackingIfNeeded(m_compositedImage.get());
+ m_coordinatedImageBacking->addHost(this);
+ m_layerState.imageID = m_coordinatedImageBacking->id();
+ }
+
+ m_coordinatedImageBacking->markDirty();
+ m_layerState.imageChanged = true;
+ } else
+ releaseImageBackingIfNeeded();
+}
+
+void CoordinatedGraphicsLayer::syncLayerState()
+{
+ if (!m_shouldSyncLayerState)
+ return;
+ m_shouldSyncLayerState = false;
+
+ m_layerState.childrenTransform = childrenTransform();
+ m_layerState.contentsRect = contentsRect();
+ m_layerState.mask = toCoordinatedLayerID(maskLayer());
+ m_layerState.opacity = opacity();
+ m_layerState.replica = toCoordinatedLayerID(replicaLayer());
+ m_layerState.transform = transform();
+
+ m_layerState.anchorPoint = m_adjustedAnchorPoint;
+ m_layerState.pos = m_adjustedPosition;
+ m_layerState.size = m_adjustedSize;
+
+ if (m_layerState.flagsChanged) {
+ m_layerState.contentsOpaque = contentsOpaque();
+ m_layerState.drawsContent = drawsContent();
+ m_layerState.contentsVisible = contentsAreVisible();
+ m_layerState.backfaceVisible = backfaceVisibility();
+ m_layerState.masksToBounds = masksToBounds();
+ m_layerState.preserves3D = preserves3D();
+ m_layerState.fixedToViewport = fixedToViewport();
+ m_layerState.showDebugBorders = isShowingDebugBorder();
+ m_layerState.showRepaintCounter = isShowingRepaintCounter();
+ m_layerState.isScrollable = isScrollable();
+ }
+
+ if (m_layerState.showDebugBorders)
+ updateDebugIndicators();
+}
+
+void CoordinatedGraphicsLayer::setDebugBorder(const Color& color, float width)
+{
+ ASSERT(m_layerState.showDebugBorders);
+ if (m_layerState.debugBorderColor != color) {
+ m_layerState.debugBorderColor = color;
+ m_layerState.debugBorderColorChanged = true;
+ }
+
+ if (m_layerState.debugBorderWidth != width) {
+ m_layerState.debugBorderWidth = width;
+ m_layerState.debugBorderWidthChanged = true;
+ }
+}
+
+void CoordinatedGraphicsLayer::syncAnimations()
+{
+ if (!m_shouldSyncAnimations)
+ return;
+
+ m_shouldSyncAnimations = false;
+ m_layerState.animations = m_animations.getActiveAnimations();
+ m_layerState.animationsChanged = true;
+}
+
+void CoordinatedGraphicsLayer::syncPlatformLayer()
+{
+#if USE(COORDINATED_GRAPHICS_THREADED)
+ if (!m_shouldSyncPlatformLayer)
+ return;
+
+ m_shouldSyncPlatformLayer = false;
+ m_layerState.platformLayerChanged = true;
+ if (m_platformLayer)
+ m_layerState.platformLayerProxy = m_platformLayer->proxy();
+#endif
+}
+
+void CoordinatedGraphicsLayer::updatePlatformLayer()
+{
+#if USE(COORDINATED_GRAPHICS_THREADED)
+ if (!m_shouldUpdatePlatformLayer)
+ return;
+
+ m_shouldUpdatePlatformLayer = false;
+ if (m_platformLayer)
+ m_platformLayer->swapBuffersIfNeeded();
+#endif
+}
+
+void CoordinatedGraphicsLayer::flushCompositingStateForThisLayerOnly()
+{
+ // When we have a transform animation, we need to update visible rect every frame to adjust the visible rect of a backing store.
+ bool hasActiveTransformAnimation = selfOrAncestorHasActiveTransformAnimation();
+ if (hasActiveTransformAnimation)
+ m_movingVisibleRect = true;
+
+ // Sets the values.
+ computePixelAlignment(m_adjustedPosition, m_adjustedSize, m_adjustedAnchorPoint, m_pixelAlignmentOffset);
+
+ syncImageBacking();
+ syncLayerState();
+ syncAnimations();
+ computeTransformedVisibleRect();
+ syncChildren();
+ syncFilters();
+ syncPlatformLayer();
+ updatePlatformLayer();
+
+ // Only unset m_movingVisibleRect after we have updated the visible rect after the animation stopped.
+ if (!hasActiveTransformAnimation)
+ m_movingVisibleRect = false;
+}
+
+void CoordinatedGraphicsLayer::syncPendingStateChangesIncludingSubLayers()
+{
+ if (m_layerState.hasPendingChanges()) {
+ m_coordinator->syncLayerState(m_id, m_layerState);
+ resetLayerState();
+ }
+
+ if (maskLayer())
+ downcast<CoordinatedGraphicsLayer>(*maskLayer()).syncPendingStateChangesIncludingSubLayers();
+
+ for (auto& child : children())
+ downcast<CoordinatedGraphicsLayer>(*child).syncPendingStateChangesIncludingSubLayers();
+}
+
+void CoordinatedGraphicsLayer::resetLayerState()
+{
+ m_layerState.changeMask = 0;
+ m_layerState.tilesToCreate.clear();
+ m_layerState.tilesToRemove.clear();
+ m_layerState.tilesToUpdate.clear();
+ m_layerState.committedScrollOffset = IntSize();
+}
+
+bool CoordinatedGraphicsLayer::imageBackingVisible()
+{
+ ASSERT(m_coordinatedImageBacking);
+ return transformedVisibleRect().intersects(IntRect(contentsRect()));
+}
+
+void CoordinatedGraphicsLayer::releaseImageBackingIfNeeded()
+{
+ if (!m_coordinatedImageBacking)
+ return;
+
+ ASSERT(m_coordinator);
+ m_coordinatedImageBacking->removeHost(this);
+ m_coordinatedImageBacking = nullptr;
+ m_layerState.imageID = InvalidCoordinatedImageBackingID;
+ m_layerState.imageChanged = true;
+}
+
+CoordinatedGraphicsLayer* CoordinatedGraphicsLayer::findFirstDescendantWithContentsRecursively()
+{
+ if (shouldHaveBackingStore())
+ return this;
+
+ for (auto& child : children()) {
+ if (CoordinatedGraphicsLayer* layer = downcast<CoordinatedGraphicsLayer>(*child).findFirstDescendantWithContentsRecursively())
+ return layer;
+ }
+
+ return nullptr;
+}
+
+void CoordinatedGraphicsLayer::setVisibleContentRectTrajectoryVector(const FloatPoint& trajectoryVector)
+{
+ if (!m_mainBackingStore)
+ return;
+
+ m_mainBackingStore->setTrajectoryVector(trajectoryVector);
+ setNeedsVisibleRectAdjustment();
+}
+
+void CoordinatedGraphicsLayer::deviceOrPageScaleFactorChanged()
+{
+ if (shouldHaveBackingStore())
+ m_pendingContentsScaleAdjustment = true;
+}
+
+float CoordinatedGraphicsLayer::effectiveContentsScale()
+{
+ return selfOrAncestorHaveNonAffineTransforms() ? 1 : deviceScaleFactor() * pageScaleFactor();
+}
+
+void CoordinatedGraphicsLayer::adjustContentsScale()
+{
+ ASSERT(shouldHaveBackingStore());
+ if (!m_mainBackingStore || m_mainBackingStore->contentsScale() == effectiveContentsScale())
+ return;
+
+ // Between creating the new backing store and painting the content,
+ // we do not want to drop the previous one as that might result in
+ // briefly seeing flickering as the old tiles may be dropped before
+ // something replaces them.
+ m_previousBackingStore = WTFMove(m_mainBackingStore);
+
+ // No reason to save the previous backing store for non-visible areas.
+ m_previousBackingStore->removeAllNonVisibleTiles(transformedVisibleRect(), IntRect(0, 0, size().width(), size().height()));
+}
+
+void CoordinatedGraphicsLayer::createBackingStore()
+{
+ m_mainBackingStore = std::make_unique<TiledBackingStore>(this, effectiveContentsScale());
+ m_mainBackingStore->setSupportsAlpha(!contentsOpaque());
+}
+
+void CoordinatedGraphicsLayer::tiledBackingStorePaint(GraphicsContext& context, const IntRect& rect)
+{
+ if (rect.isEmpty())
+ return;
+ paintGraphicsLayerContents(context, rect);
+}
+
+void CoordinatedGraphicsLayer::didUpdateTileBuffers()
+{
+ if (!isShowingRepaintCounter())
+ return;
+
+ m_layerState.repaintCount = incrementRepaintCount();
+ m_layerState.repaintCountChanged = true;
+}
+
+void CoordinatedGraphicsLayer::tiledBackingStoreHasPendingTileCreation()
+{
+ setNeedsVisibleRectAdjustment();
+ notifyFlushRequired();
+}
+
+static void clampToContentsRectIfRectIsInfinite(FloatRect& rect, const FloatSize& contentsSize)
+{
+ if (rect.width() >= LayoutUnit::nearlyMax() || rect.width() <= LayoutUnit::nearlyMin()) {
+ rect.setX(0);
+ rect.setWidth(contentsSize.width());
+ }
+
+ if (rect.height() >= LayoutUnit::nearlyMax() || rect.height() <= LayoutUnit::nearlyMin()) {
+ rect.setY(0);
+ rect.setHeight(contentsSize.height());
+ }
+}
+
+IntRect CoordinatedGraphicsLayer::transformedVisibleRect()
+{
+ // Non-invertible layers are not visible.
+ if (!m_layerTransform.combined().isInvertible())
+ return IntRect();
+
+ // Return a projection of the visible rect (surface coordinates) onto the layer's plane (layer coordinates).
+ // The resulting quad might be squewed and the visible rect is the bounding box of this quad,
+ // so it might spread further than the real visible area (and then even more amplified by the cover rect multiplier).
+ ASSERT(m_cachedInverseTransform == m_layerTransform.combined().inverse().value_or(TransformationMatrix()));
+ FloatRect rect = m_cachedInverseTransform.clampedBoundsOfProjectedQuad(FloatQuad(m_coordinator->visibleContentsRect()));
+ clampToContentsRectIfRectIsInfinite(rect, size());
+ return enclosingIntRect(rect);
+}
+
+bool CoordinatedGraphicsLayer::paintToSurface(const IntSize& size, uint32_t& atlas, IntPoint& offset, CoordinatedSurface::Client& client)
+{
+ ASSERT(m_coordinator);
+ ASSERT(m_coordinator->isFlushingLayerChanges());
+ return m_coordinator->paintToSurface(size, contentsOpaque() ? CoordinatedSurface::NoFlags : CoordinatedSurface::SupportsAlpha, atlas, offset, client);
+}
+
+void CoordinatedGraphicsLayer::createTile(uint32_t tileID, float scaleFactor)
+{
+ ASSERT(m_coordinator);
+ ASSERT(m_coordinator->isFlushingLayerChanges());
+
+ TileCreationInfo creationInfo;
+ creationInfo.tileID = tileID;
+ creationInfo.scale = scaleFactor;
+ m_layerState.tilesToCreate.append(creationInfo);
+}
+
+void CoordinatedGraphicsLayer::updateTile(uint32_t tileID, const SurfaceUpdateInfo& updateInfo, const IntRect& tileRect)
+{
+ ASSERT(m_coordinator);
+ ASSERT(m_coordinator->isFlushingLayerChanges());
+
+ TileUpdateInfo tileUpdateInfo;
+ tileUpdateInfo.tileID = tileID;
+ tileUpdateInfo.tileRect = tileRect;
+ tileUpdateInfo.updateInfo = updateInfo;
+ m_layerState.tilesToUpdate.append(tileUpdateInfo);
+}
+
+void CoordinatedGraphicsLayer::removeTile(uint32_t tileID)
+{
+ ASSERT(m_coordinator);
+ ASSERT(m_coordinator->isFlushingLayerChanges() || m_isPurging);
+ m_layerState.tilesToRemove.append(tileID);
+}
+
+void CoordinatedGraphicsLayer::updateContentBuffersIncludingSubLayers()
+{
+ if (CoordinatedGraphicsLayer* mask = downcast<CoordinatedGraphicsLayer>(maskLayer()))
+ mask->updateContentBuffers();
+
+ if (CoordinatedGraphicsLayer* replica = downcast<CoordinatedGraphicsLayer>(replicaLayer()))
+ replica->updateContentBuffers();
+
+ updateContentBuffers();
+
+ for (auto& child : children())
+ downcast<CoordinatedGraphicsLayer>(*child).updateContentBuffersIncludingSubLayers();
+}
+
+void CoordinatedGraphicsLayer::updateContentBuffers()
+{
+ if (!shouldHaveBackingStore()) {
+ m_mainBackingStore = nullptr;
+ m_previousBackingStore = nullptr;
+ return;
+ }
+
+ if (m_pendingContentsScaleAdjustment) {
+ adjustContentsScale();
+ m_pendingContentsScaleAdjustment = false;
+ }
+
+ // This is the only place we (re)create the main tiled backing store, once we
+ // have a remote client and we are ready to send our data to the UI process.
+ if (!m_mainBackingStore) {
+ createBackingStore();
+ m_pendingVisibleRectAdjustment = true;
+ }
+
+ if (m_pendingVisibleRectAdjustment) {
+ m_pendingVisibleRectAdjustment = false;
+ m_mainBackingStore->createTilesIfNeeded(transformedVisibleRect(), IntRect(0, 0, size().width(), size().height()));
+ }
+
+ m_mainBackingStore->updateTileBuffers();
+
+ // The previous backing store is kept around to avoid flickering between
+ // removing the existing tiles and painting the new ones. The first time
+ // the visibleRect is full painted we remove the previous backing store.
+ if (m_mainBackingStore->visibleAreaIsCovered())
+ m_previousBackingStore = nullptr;
+}
+
+void CoordinatedGraphicsLayer::purgeBackingStores()
+{
+#ifndef NDEBUG
+ SetForScope<bool> updateModeProtector(m_isPurging, true);
+#endif
+ m_mainBackingStore = nullptr;
+ m_previousBackingStore = nullptr;
+
+ releaseImageBackingIfNeeded();
+
+ didChangeLayerState();
+}
+
+void CoordinatedGraphicsLayer::setCoordinator(CoordinatedGraphicsLayerClient* coordinator)
+{
+ m_coordinator = coordinator;
+}
+
+void CoordinatedGraphicsLayer::setNeedsVisibleRectAdjustment()
+{
+ if (shouldHaveBackingStore())
+ m_pendingVisibleRectAdjustment = true;
+}
+
+static inline bool isIntegral(float value)
+{
+ return static_cast<int>(value) == value;
+}
+
+FloatPoint CoordinatedGraphicsLayer::computePositionRelativeToBase()
+{
+ FloatPoint offset;
+ for (const GraphicsLayer* currLayer = this; currLayer; currLayer = currLayer->parent())
+ offset += currLayer->position();
+
+ return offset;
+}
+
+void CoordinatedGraphicsLayer::computePixelAlignment(FloatPoint& position, FloatSize& size, FloatPoint3D& anchorPoint, FloatSize& alignmentOffset)
+{
+ if (isIntegral(effectiveContentsScale())) {
+ position = m_position;
+ size = m_size;
+ anchorPoint = m_anchorPoint;
+ alignmentOffset = FloatSize();
+ return;
+ }
+
+ FloatPoint positionRelativeToBase = computePositionRelativeToBase();
+
+ FloatRect baseRelativeBounds(positionRelativeToBase, m_size);
+ FloatRect scaledBounds = baseRelativeBounds;
+
+ // Scale by the effective scale factor to compute the screen-relative bounds.
+ scaledBounds.scale(effectiveContentsScale());
+
+ // Round to integer boundaries.
+ // NOTE: When using enclosingIntRect (as mac) it will have different sizes depending on position.
+ FloatRect alignedBounds = enclosingIntRect(scaledBounds);
+
+ // Convert back to layer coordinates.
+ alignedBounds.scale(1 / effectiveContentsScale());
+
+ // Convert back to layer coordinates.
+ alignmentOffset = baseRelativeBounds.location() - alignedBounds.location();
+
+ position = m_position - alignmentOffset;
+ size = alignedBounds.size();
+
+ // Now we have to compute a new anchor point which compensates for rounding.
+ float anchorPointX = m_anchorPoint.x();
+ float anchorPointY = m_anchorPoint.y();
+
+ if (alignedBounds.width())
+ anchorPointX = (baseRelativeBounds.width() * anchorPointX + alignmentOffset.width()) / alignedBounds.width();
+
+ if (alignedBounds.height())
+ anchorPointY = (baseRelativeBounds.height() * anchorPointY + alignmentOffset.height()) / alignedBounds.height();
+
+ anchorPoint = FloatPoint3D(anchorPointX, anchorPointY, m_anchorPoint.z() * effectiveContentsScale());
+}
+
+void CoordinatedGraphicsLayer::computeTransformedVisibleRect()
+{
+ if (!m_shouldUpdateVisibleRect && !m_movingVisibleRect)
+ return;
+
+ m_shouldUpdateVisibleRect = false;
+ TransformationMatrix currentTransform = transform();
+ if (m_movingVisibleRect)
+ client().getCurrentTransform(this, currentTransform);
+ m_layerTransform.setLocalTransform(currentTransform);
+
+ m_layerTransform.setAnchorPoint(m_adjustedAnchorPoint);
+ m_layerTransform.setPosition(m_adjustedPosition);
+ m_layerTransform.setSize(m_adjustedSize);
+
+ m_layerTransform.setFlattening(!preserves3D());
+ m_layerTransform.setChildrenTransform(childrenTransform());
+ m_layerTransform.combineTransforms(parent() ? downcast<CoordinatedGraphicsLayer>(*parent()).m_layerTransform.combinedForChildren() : TransformationMatrix());
+
+ m_cachedInverseTransform = m_layerTransform.combined().inverse().value_or(TransformationMatrix());
+
+ // The combined transform will be used in tiledBackingStoreVisibleRect.
+ setNeedsVisibleRectAdjustment();
+}
+
+bool CoordinatedGraphicsLayer::shouldHaveBackingStore() const
+{
+ return drawsContent() && contentsAreVisible() && !m_size.isEmpty();
+}
+
+bool CoordinatedGraphicsLayer::selfOrAncestorHasActiveTransformAnimation() const
+{
+ if (m_animations.hasActiveAnimationsOfType(AnimatedPropertyTransform))
+ return true;
+
+ if (!parent())
+ return false;
+
+ return downcast<CoordinatedGraphicsLayer>(*parent()).selfOrAncestorHasActiveTransformAnimation();
+}
+
+bool CoordinatedGraphicsLayer::selfOrAncestorHaveNonAffineTransforms()
+{
+ if (m_animations.hasActiveAnimationsOfType(AnimatedPropertyTransform))
+ return true;
+
+ if (!m_layerTransform.combined().isAffine())
+ return true;
+
+ if (!parent())
+ return false;
+
+ return downcast<CoordinatedGraphicsLayer>(*parent()).selfOrAncestorHaveNonAffineTransforms();
+}
+
+bool CoordinatedGraphicsLayer::addAnimation(const KeyframeValueList& valueList, const FloatSize& boxSize, const Animation* anim, const String& keyframesName, double delayAsNegativeTimeOffset)
+{
+ ASSERT(!keyframesName.isEmpty());
+
+ if (!anim || anim->isEmptyOrZeroDuration() || valueList.size() < 2 || (valueList.property() != AnimatedPropertyTransform && valueList.property() != AnimatedPropertyOpacity && valueList.property() != AnimatedPropertyFilter))
+ return false;
+
+ if (valueList.property() == AnimatedPropertyFilter) {
+ int listIndex = validateFilterOperations(valueList);
+ if (listIndex < 0)
+ return false;
+
+ const auto& filters = static_cast<const FilterAnimationValue&>(valueList.at(listIndex)).value();
+ if (!filtersCanBeComposited(filters))
+ return false;
+ }
+
+ bool listsMatch = false;
+ bool ignoredHasBigRotation;
+
+ if (valueList.property() == AnimatedPropertyTransform)
+ listsMatch = validateTransformOperations(valueList, ignoredHasBigRotation) >= 0;
+
+ m_lastAnimationStartTime = monotonicallyIncreasingTime() - delayAsNegativeTimeOffset;
+ m_animations.add(TextureMapperAnimation(keyframesName, valueList, boxSize, *anim, listsMatch, m_lastAnimationStartTime, 0, TextureMapperAnimation::AnimationState::Playing));
+ m_animationStartedTimer.startOneShot(0);
+ didChangeAnimations();
+ return true;
+}
+
+void CoordinatedGraphicsLayer::pauseAnimation(const String& animationName, double time)
+{
+ m_animations.pause(animationName, time);
+ didChangeAnimations();
+}
+
+void CoordinatedGraphicsLayer::removeAnimation(const String& animationName)
+{
+ m_animations.remove(animationName);
+ didChangeAnimations();
+}
+
+void CoordinatedGraphicsLayer::suspendAnimations(double time)
+{
+ m_animations.suspend(time);
+ didChangeAnimations();
+}
+
+void CoordinatedGraphicsLayer::resumeAnimations()
+{
+ m_animations.resume();
+ didChangeAnimations();
+}
+
+void CoordinatedGraphicsLayer::animationStartedTimerFired()
+{
+ client().notifyAnimationStarted(this, "", m_lastAnimationStartTime);
+}
+
+#if USE(COORDINATED_GRAPHICS_THREADED)
+void CoordinatedGraphicsLayer::platformLayerWillBeDestroyed()
+{
+}
+
+void CoordinatedGraphicsLayer::setPlatformLayerNeedsDisplay()
+{
+}
+#endif
+
+} // namespace WebCore
+
+#endif // USE(COORDINATED_GRAPHICS)
diff --git a/Source/WebCore/platform/graphics/texmap/coordinated/CoordinatedGraphicsLayer.h b/Source/WebCore/platform/graphics/texmap/coordinated/CoordinatedGraphicsLayer.h
new file mode 100644
index 000000000..bf25111db
--- /dev/null
+++ b/Source/WebCore/platform/graphics/texmap/coordinated/CoordinatedGraphicsLayer.h
@@ -0,0 +1,246 @@
+/*
+ Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies)
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+ */
+
+
+#ifndef CoordinatedGraphicsLayer_h
+#define CoordinatedGraphicsLayer_h
+
+#if USE(COORDINATED_GRAPHICS)
+
+#include "CoordinatedGraphicsState.h"
+#include "CoordinatedImageBacking.h"
+#include "FloatPoint3D.h"
+#include "GraphicsLayer.h"
+#include "GraphicsLayerTransform.h"
+#include "Image.h"
+#include "IntSize.h"
+#include "TextureMapperAnimation.h"
+#include "TextureMapperPlatformLayer.h"
+#include "TiledBackingStore.h"
+#include "TiledBackingStoreClient.h"
+#include "TransformationMatrix.h"
+#include <wtf/text/StringHash.h>
+
+namespace WebCore {
+class CoordinatedGraphicsLayer;
+class TextureMapperAnimations;
+class ScrollableArea;
+
+class CoordinatedGraphicsLayerClient {
+public:
+ virtual bool isFlushingLayerChanges() const = 0;
+ virtual FloatRect visibleContentsRect() const = 0;
+ virtual Ref<CoordinatedImageBacking> createImageBackingIfNeeded(Image*) = 0;
+ virtual void detachLayer(CoordinatedGraphicsLayer*) = 0;
+ virtual bool paintToSurface(const IntSize&, CoordinatedSurface::Flags, uint32_t& atlasID, IntPoint&, CoordinatedSurface::Client&) = 0;
+
+ virtual void syncLayerState(CoordinatedLayerID, CoordinatedGraphicsLayerState&) = 0;
+};
+
+class CoordinatedGraphicsLayer : public GraphicsLayer
+ , public TiledBackingStoreClient
+#if USE(COORDINATED_GRAPHICS_THREADED)
+ , public TextureMapperPlatformLayer::Client
+#endif
+ , public CoordinatedImageBacking::Host {
+public:
+ explicit CoordinatedGraphicsLayer(Type, GraphicsLayerClient&);
+ virtual ~CoordinatedGraphicsLayer();
+
+ PlatformLayerID primaryLayerID() const override { return id(); }
+
+ // Reimplementations from GraphicsLayer.h.
+ bool setChildren(const Vector<GraphicsLayer*>&) override;
+ void addChild(GraphicsLayer*) override;
+ void addChildAtIndex(GraphicsLayer*, int) override;
+ void addChildAbove(GraphicsLayer*, GraphicsLayer*) override;
+ void addChildBelow(GraphicsLayer*, GraphicsLayer*) override;
+ bool replaceChild(GraphicsLayer*, GraphicsLayer*) override;
+ void removeFromParent() override;
+ void setPosition(const FloatPoint&) override;
+ void setAnchorPoint(const FloatPoint3D&) override;
+ void setSize(const FloatSize&) override;
+ void setTransform(const TransformationMatrix&) override;
+ void setChildrenTransform(const TransformationMatrix&) override;
+ void setPreserves3D(bool) override;
+ void setMasksToBounds(bool) override;
+ void setDrawsContent(bool) override;
+ void setContentsVisible(bool) override;
+ void setContentsOpaque(bool) override;
+ void setBackfaceVisibility(bool) override;
+ void setOpacity(float) override;
+ void setContentsRect(const FloatRect&) override;
+ void setContentsTilePhase(const FloatSize&) override;
+ void setContentsTileSize(const FloatSize&) override;
+ void setContentsToImage(Image*) override;
+ void setContentsToSolidColor(const Color&) override;
+ void setShowDebugBorder(bool) override;
+ void setShowRepaintCounter(bool) override;
+ bool shouldDirectlyCompositeImage(Image*) const override;
+ void setContentsToPlatformLayer(PlatformLayer*, ContentsLayerPurpose) override;
+ void setMaskLayer(GraphicsLayer*) override;
+ void setReplicatedByLayer(GraphicsLayer*) override;
+ void setNeedsDisplay() override;
+ void setNeedsDisplayInRect(const FloatRect&, ShouldClipToLayer = ClipToLayer) override;
+ void setContentsNeedsDisplay() override;
+ void deviceOrPageScaleFactorChanged() override;
+ void flushCompositingState(const FloatRect&) override;
+ void flushCompositingStateForThisLayerOnly() override;
+ bool setFilters(const FilterOperations&) override;
+ bool addAnimation(const KeyframeValueList&, const FloatSize&, const Animation*, const String&, double) override;
+ void pauseAnimation(const String&, double) override;
+ void removeAnimation(const String&) override;
+ void suspendAnimations(double time) override;
+ void resumeAnimations() override;
+ bool usesContentsLayer() const override { return m_platformLayer || m_compositedImage; }
+
+ void syncPendingStateChangesIncludingSubLayers();
+ void updateContentBuffersIncludingSubLayers();
+
+ FloatPoint computePositionRelativeToBase();
+ void computePixelAlignment(FloatPoint& position, FloatSize&, FloatPoint3D& anchorPoint, FloatSize& alignmentOffset);
+
+ void setVisibleContentRectTrajectoryVector(const FloatPoint&);
+
+ void setScrollableArea(ScrollableArea*);
+ bool isScrollable() const { return !!m_scrollableArea; }
+ void commitScrollOffset(const IntSize&);
+
+ CoordinatedLayerID id() const { return m_id; }
+
+ void setFixedToViewport(bool isFixed);
+
+ IntRect coverRect() const { return m_mainBackingStore ? m_mainBackingStore->mapToContents(m_mainBackingStore->coverRect()) : IntRect(); }
+ IntRect transformedVisibleRect();
+
+ // TiledBackingStoreClient
+ void tiledBackingStorePaint(GraphicsContext&, const IntRect&) override;
+ void didUpdateTileBuffers() override;
+ void tiledBackingStoreHasPendingTileCreation() override;
+ void createTile(uint32_t tileID, float) override;
+ void updateTile(uint32_t tileID, const SurfaceUpdateInfo&, const IntRect&) override;
+ void removeTile(uint32_t tileID) override;
+ bool paintToSurface(const IntSize&, uint32_t& /* atlasID */, IntPoint&, CoordinatedSurface::Client&) override;
+
+ void setCoordinator(CoordinatedGraphicsLayerClient*);
+
+ void setNeedsVisibleRectAdjustment();
+ void purgeBackingStores();
+
+ CoordinatedGraphicsLayer* findFirstDescendantWithContentsRecursively();
+
+private:
+ bool isCoordinatedGraphicsLayer() const override { return true; }
+
+ void syncPlatformLayer();
+ void updatePlatformLayer();
+#if USE(COORDINATED_GRAPHICS_THREADED)
+ void platformLayerWillBeDestroyed() override;
+ void setPlatformLayerNeedsDisplay() override;
+#endif
+
+ void setDebugBorder(const Color&, float width) override;
+
+ bool fixedToViewport() const { return m_fixedToViewport; }
+
+ void didChangeLayerState();
+ void didChangeAnimations();
+ void didChangeGeometry();
+ void didChangeChildren();
+ void didChangeFilters();
+ void didChangeImageBacking();
+
+ void resetLayerState();
+ void syncLayerState();
+ void syncAnimations();
+ void syncChildren();
+ void syncFilters();
+ void syncImageBacking();
+ void computeTransformedVisibleRect();
+ void updateContentBuffers();
+
+ void createBackingStore();
+ void releaseImageBackingIfNeeded();
+
+ void notifyFlushRequired();
+
+ // CoordinatedImageBacking::Host
+ bool imageBackingVisible() override;
+ bool shouldHaveBackingStore() const;
+ bool selfOrAncestorHasActiveTransformAnimation() const;
+ bool selfOrAncestorHaveNonAffineTransforms();
+ void adjustContentsScale();
+
+ void setShouldUpdateVisibleRect();
+ float effectiveContentsScale();
+
+ void animationStartedTimerFired();
+
+ bool filtersCanBeComposited(const FilterOperations&) const;
+
+ CoordinatedLayerID m_id;
+ CoordinatedGraphicsLayerState m_layerState;
+ GraphicsLayerTransform m_layerTransform;
+ TransformationMatrix m_cachedInverseTransform;
+ FloatSize m_pixelAlignmentOffset;
+ FloatSize m_adjustedSize;
+ FloatPoint m_adjustedPosition;
+ FloatPoint3D m_adjustedAnchorPoint;
+
+#ifndef NDEBUG
+ bool m_isPurging;
+#endif
+ bool m_shouldUpdateVisibleRect: 1;
+ bool m_shouldSyncLayerState: 1;
+ bool m_shouldSyncChildren: 1;
+ bool m_shouldSyncFilters: 1;
+ bool m_shouldSyncImageBacking: 1;
+ bool m_shouldSyncAnimations: 1;
+ bool m_fixedToViewport : 1;
+ bool m_movingVisibleRect : 1;
+ bool m_pendingContentsScaleAdjustment : 1;
+ bool m_pendingVisibleRectAdjustment : 1;
+#if USE(COORDINATED_GRAPHICS_THREADED)
+ bool m_shouldSyncPlatformLayer : 1;
+ bool m_shouldUpdatePlatformLayer : 1;
+#endif
+
+ CoordinatedGraphicsLayerClient* m_coordinator;
+ std::unique_ptr<TiledBackingStore> m_mainBackingStore;
+ std::unique_ptr<TiledBackingStore> m_previousBackingStore;
+
+ RefPtr<Image> m_compositedImage;
+ NativeImagePtr m_compositedNativeImagePtr;
+ RefPtr<CoordinatedImageBacking> m_coordinatedImageBacking;
+
+ PlatformLayer* m_platformLayer;
+ Timer m_animationStartedTimer;
+ TextureMapperAnimations m_animations;
+ double m_lastAnimationStartTime { 0.0 };
+
+ ScrollableArea* m_scrollableArea;
+};
+
+} // namespace WebCore
+
+SPECIALIZE_TYPE_TRAITS_GRAPHICSLAYER(WebCore::CoordinatedGraphicsLayer, isCoordinatedGraphicsLayer())
+
+#endif // USE(COORDINATED_GRAPHICS)
+
+#endif // CoordinatedGraphicsLayer_h
diff --git a/Source/WebCore/platform/graphics/texmap/coordinated/CoordinatedGraphicsState.h b/Source/WebCore/platform/graphics/texmap/coordinated/CoordinatedGraphicsState.h
new file mode 100644
index 000000000..c810530b5
--- /dev/null
+++ b/Source/WebCore/platform/graphics/texmap/coordinated/CoordinatedGraphicsState.h
@@ -0,0 +1,195 @@
+/*
+ * Copyright (C) 2013 Nokia Corporation and/or its subsidiary(-ies)
+ * Copyright (C) 2013 Company 100, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef CoordinatedGraphicsState_h
+#define CoordinatedGraphicsState_h
+
+#if USE(COORDINATED_GRAPHICS)
+
+#include "Color.h"
+#include "FilterOperations.h"
+#include "FloatRect.h"
+#include "FloatSize.h"
+#include "IntRect.h"
+#include "IntSize.h"
+#include "SurfaceUpdateInfo.h"
+#include "TextureMapperAnimation.h"
+#include "TransformationMatrix.h"
+
+#if USE(COORDINATED_GRAPHICS_THREADED)
+#include "TextureMapperPlatformLayerProxy.h"
+#endif
+
+namespace WebCore {
+
+class CoordinatedSurface;
+
+typedef uint32_t CoordinatedLayerID;
+enum { InvalidCoordinatedLayerID = 0 };
+
+typedef uint64_t CoordinatedImageBackingID;
+enum { InvalidCoordinatedImageBackingID = 0 };
+
+struct TileUpdateInfo {
+ uint32_t tileID;
+ IntRect tileRect;
+ WebCore::SurfaceUpdateInfo updateInfo;
+};
+
+struct TileCreationInfo {
+ uint32_t tileID;
+ float scale;
+};
+
+struct CoordinatedGraphicsLayerState {
+ union {
+ struct {
+ bool positionChanged: 1;
+ bool anchorPointChanged: 1;
+ bool sizeChanged: 1;
+ bool transformChanged: 1;
+ bool childrenTransformChanged: 1;
+ bool contentsRectChanged: 1;
+ bool opacityChanged: 1;
+ bool solidColorChanged: 1;
+ bool debugBorderColorChanged: 1;
+ bool debugBorderWidthChanged: 1;
+ bool replicaChanged: 1;
+ bool maskChanged: 1;
+ bool imageChanged: 1;
+ bool flagsChanged: 1;
+ bool animationsChanged: 1;
+ bool filtersChanged: 1;
+ bool childrenChanged: 1;
+ bool repaintCountChanged : 1;
+ bool platformLayerChanged: 1;
+ bool platformLayerShouldSwapBuffers: 1;
+ bool isScrollableChanged: 1;
+ bool committedScrollOffsetChanged: 1;
+ bool contentsTilingChanged: 1;
+ };
+ unsigned changeMask;
+ };
+ union {
+ struct {
+ bool contentsOpaque : 1;
+ bool drawsContent : 1;
+ bool contentsVisible : 1;
+ bool backfaceVisible : 1;
+ bool masksToBounds : 1;
+ bool preserves3D : 1;
+ bool fixedToViewport : 1;
+ bool showDebugBorders : 1;
+ bool showRepaintCounter : 1;
+ bool isScrollable: 1;
+ };
+ unsigned flags;
+ };
+
+ CoordinatedGraphicsLayerState()
+ : changeMask(0)
+ , contentsOpaque(false)
+ , drawsContent(false)
+ , contentsVisible(true)
+ , backfaceVisible(true)
+ , masksToBounds(false)
+ , preserves3D(false)
+ , fixedToViewport(false)
+ , showDebugBorders(false)
+ , showRepaintCounter(false)
+ , isScrollable(false)
+ , opacity(0)
+ , debugBorderWidth(0)
+ , replica(InvalidCoordinatedLayerID)
+ , mask(InvalidCoordinatedLayerID)
+ , imageID(InvalidCoordinatedImageBackingID)
+ , repaintCount(0)
+#if USE(COORDINATED_GRAPHICS_THREADED)
+ , platformLayerProxy(0)
+#endif
+ {
+ }
+
+ FloatPoint pos;
+ FloatPoint3D anchorPoint;
+ FloatSize size;
+ TransformationMatrix transform;
+ TransformationMatrix childrenTransform;
+ FloatRect contentsRect;
+ FloatSize contentsTilePhase;
+ FloatSize contentsTileSize;
+ float opacity;
+ Color solidColor;
+ Color debugBorderColor;
+ float debugBorderWidth;
+ FilterOperations filters;
+ TextureMapperAnimations animations;
+ Vector<uint32_t> children;
+ Vector<TileCreationInfo> tilesToCreate;
+ Vector<uint32_t> tilesToRemove;
+ CoordinatedLayerID replica;
+ CoordinatedLayerID mask;
+ CoordinatedImageBackingID imageID;
+
+ unsigned repaintCount;
+ Vector<TileUpdateInfo> tilesToUpdate;
+
+#if USE(COORDINATED_GRAPHICS_THREADED)
+ RefPtr<TextureMapperPlatformLayerProxy> platformLayerProxy;
+#endif
+
+ IntSize committedScrollOffset;
+
+ bool hasPendingChanges() const
+ {
+ return changeMask || tilesToUpdate.size() || tilesToRemove.size() || tilesToCreate.size();
+ }
+};
+
+struct CoordinatedGraphicsState {
+ uint32_t rootCompositingLayer;
+ FloatPoint scrollPosition;
+ IntSize contentsSize;
+ IntRect coveredRect;
+
+ Vector<CoordinatedLayerID> layersToCreate;
+ Vector<std::pair<CoordinatedLayerID, CoordinatedGraphicsLayerState> > layersToUpdate;
+ Vector<CoordinatedLayerID> layersToRemove;
+
+ Vector<CoordinatedImageBackingID> imagesToCreate;
+ Vector<CoordinatedImageBackingID> imagesToRemove;
+ Vector<std::pair<CoordinatedImageBackingID, RefPtr<CoordinatedSurface> > > imagesToUpdate;
+ Vector<CoordinatedImageBackingID> imagesToClear;
+
+ Vector<std::pair<uint32_t /* atlasID */, RefPtr<CoordinatedSurface> > > updateAtlasesToCreate;
+ Vector<uint32_t /* atlasID */> updateAtlasesToRemove;
+};
+
+} // namespace WebCore
+
+#endif // USE(COORDINATED_GRAPHICS)
+
+#endif // CoordinatedGraphicsState_h
diff --git a/Source/WebCore/platform/graphics/texmap/coordinated/CoordinatedImageBacking.cpp b/Source/WebCore/platform/graphics/texmap/coordinated/CoordinatedImageBacking.cpp
new file mode 100644
index 000000000..3f5673e8f
--- /dev/null
+++ b/Source/WebCore/platform/graphics/texmap/coordinated/CoordinatedImageBacking.cpp
@@ -0,0 +1,182 @@
+/*
+ * Copyright (C) 2012 Company 100, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#if USE(COORDINATED_GRAPHICS)
+#include "CoordinatedImageBacking.h"
+
+#include "CoordinatedGraphicsState.h"
+#include "GraphicsContext.h"
+
+namespace WebCore {
+
+class ImageBackingSurfaceClient : public CoordinatedSurface::Client {
+public:
+ ImageBackingSurfaceClient(Image& image, const IntRect& rect)
+ : m_image(image)
+ , m_rect(rect)
+ {
+ }
+
+ void paintToSurfaceContext(GraphicsContext& context) override
+ {
+ context.drawImage(m_image, m_rect, m_rect);
+ }
+
+private:
+ Image& m_image;
+ IntRect m_rect;
+};
+
+CoordinatedImageBackingID CoordinatedImageBacking::getCoordinatedImageBackingID(Image* image)
+{
+ // CoordinatedImageBacking keeps a RefPtr<Image> member, so the same Image pointer can not refer two different instances until CoordinatedImageBacking releases the member.
+ return reinterpret_cast<CoordinatedImageBackingID>(image);
+}
+
+PassRefPtr<CoordinatedImageBacking> CoordinatedImageBacking::create(Client* client, PassRefPtr<Image> image)
+{
+ return adoptRef(new CoordinatedImageBacking(client, image));
+}
+
+CoordinatedImageBacking::CoordinatedImageBacking(Client* client, PassRefPtr<Image> image)
+ : m_client(client)
+ , m_image(image)
+ , m_id(getCoordinatedImageBackingID(m_image.get()))
+ , m_clearContentsTimer(*this, &CoordinatedImageBacking::clearContentsTimerFired)
+ , m_isDirty(false)
+ , m_isVisible(false)
+{
+ // FIXME: We would need to decode a small image directly into a GraphicsSurface.
+ // http://webkit.org/b/101426
+
+ m_client->createImageBacking(id());
+}
+
+CoordinatedImageBacking::~CoordinatedImageBacking()
+{
+}
+
+void CoordinatedImageBacking::addHost(Host* host)
+{
+ ASSERT(!m_hosts.contains(host));
+ m_hosts.append(host);
+}
+
+void CoordinatedImageBacking::removeHost(Host* host)
+{
+ size_t position = m_hosts.find(host);
+ ASSERT(position != notFound);
+ m_hosts.remove(position);
+
+ if (m_hosts.isEmpty())
+ m_client->removeImageBacking(id());
+}
+
+void CoordinatedImageBacking::markDirty()
+{
+ m_isDirty = true;
+}
+
+void CoordinatedImageBacking::update()
+{
+ releaseSurfaceIfNeeded();
+
+ bool changedToVisible;
+ updateVisibilityIfNeeded(changedToVisible);
+ if (!m_isVisible)
+ return;
+
+ if (!changedToVisible) {
+ if (!m_isDirty)
+ return;
+
+ if (m_nativeImagePtr == m_image->nativeImageForCurrentFrame()) {
+ m_isDirty = false;
+ return;
+ }
+ }
+
+ m_surface = CoordinatedSurface::create(IntSize(m_image->size()), !m_image->currentFrameKnownToBeOpaque() ? CoordinatedSurface::SupportsAlpha : CoordinatedSurface::NoFlags);
+ if (!m_surface) {
+ m_isDirty = false;
+ return;
+ }
+
+ IntRect rect(IntPoint::zero(), IntSize(m_image->size()));
+
+ ImageBackingSurfaceClient surfaceClient(*m_image, rect);
+ m_surface->paintToSurface(rect, surfaceClient);
+
+ m_nativeImagePtr = m_image->nativeImageForCurrentFrame();
+
+ m_client->updateImageBacking(id(), m_surface.copyRef());
+ m_isDirty = false;
+}
+
+void CoordinatedImageBacking::releaseSurfaceIfNeeded()
+{
+ // We must keep m_surface until UI Process reads m_surface.
+ // If m_surface exists, it was created in the previous update.
+ m_surface = nullptr;
+}
+
+static const double clearContentsTimerInterval = 3;
+
+void CoordinatedImageBacking::updateVisibilityIfNeeded(bool& changedToVisible)
+{
+ bool previousIsVisible = m_isVisible;
+
+ m_isVisible = false;
+ for (auto& host : m_hosts) {
+ if (host->imageBackingVisible()) {
+ m_isVisible = true;
+ break;
+ }
+ }
+
+ bool changedToInvisible = previousIsVisible && !m_isVisible;
+ if (changedToInvisible) {
+ ASSERT(!m_clearContentsTimer.isActive());
+ m_clearContentsTimer.startOneShot(clearContentsTimerInterval);
+ }
+
+ changedToVisible = !previousIsVisible && m_isVisible;
+
+ if (m_isVisible && m_clearContentsTimer.isActive()) {
+ m_clearContentsTimer.stop();
+ // We don't want to update the texture if we didn't remove the texture.
+ changedToVisible = false;
+ }
+}
+
+void CoordinatedImageBacking::clearContentsTimerFired()
+{
+ m_client->clearImageBackingContents(id());
+}
+
+} // namespace WebCore
+#endif
diff --git a/Source/WebCore/platform/graphics/texmap/coordinated/CoordinatedImageBacking.h b/Source/WebCore/platform/graphics/texmap/coordinated/CoordinatedImageBacking.h
new file mode 100644
index 000000000..b8603c68e
--- /dev/null
+++ b/Source/WebCore/platform/graphics/texmap/coordinated/CoordinatedImageBacking.h
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2012 Company 100, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+#ifndef CoordinatedImageBacking_h
+#define CoordinatedImageBacking_h
+
+#if USE(COORDINATED_GRAPHICS)
+#include "CoordinatedGraphicsState.h"
+#include "CoordinatedSurface.h"
+#include "Image.h"
+#include "Timer.h"
+#include <wtf/RefCounted.h>
+#include <wtf/Vector.h>
+
+namespace WebCore {
+
+class CoordinatedImageBacking : public RefCounted<CoordinatedImageBacking> {
+public:
+ class Client {
+ public:
+ virtual void createImageBacking(CoordinatedImageBackingID) = 0;
+ virtual void updateImageBacking(CoordinatedImageBackingID, RefPtr<CoordinatedSurface>&&) = 0;
+ virtual void clearImageBackingContents(CoordinatedImageBackingID) = 0;
+ virtual void removeImageBacking(CoordinatedImageBackingID) = 0;
+ };
+
+ class Host {
+ public:
+ virtual bool imageBackingVisible() = 0;
+ };
+
+ static PassRefPtr<CoordinatedImageBacking> create(Client*, PassRefPtr<Image>);
+ virtual ~CoordinatedImageBacking();
+
+ static CoordinatedImageBackingID getCoordinatedImageBackingID(Image*);
+ CoordinatedImageBackingID id() const { return m_id; }
+
+ void addHost(Host*);
+ void removeHost(Host*);
+
+ // When a new image is updated or an animated gif is progressed, CoordinatedGraphicsLayer calls markDirty().
+ void markDirty();
+
+ // Create, remove or update its backing.
+ void update();
+
+private:
+ CoordinatedImageBacking(Client*, PassRefPtr<Image>);
+
+ void releaseSurfaceIfNeeded();
+ void updateVisibilityIfNeeded(bool& changedToVisible);
+ void clearContentsTimerFired();
+
+ Client* m_client;
+ RefPtr<Image> m_image;
+ NativeImagePtr m_nativeImagePtr;
+ CoordinatedImageBackingID m_id;
+ Vector<Host*> m_hosts;
+
+ RefPtr<CoordinatedSurface> m_surface;
+
+ Timer m_clearContentsTimer;
+
+ bool m_isDirty;
+ bool m_isVisible;
+
+};
+
+} // namespace WebCore
+#endif // USE(COORDINATED_GRAPHICS)
+
+#endif // CoordinatedImageBacking_h
diff --git a/Source/WebCore/platform/graphics/TypesettingFeatures.h b/Source/WebCore/platform/graphics/texmap/coordinated/CoordinatedSurface.cpp
index aa46beb02..e0d815bd0 100644
--- a/Source/WebCore/platform/graphics/TypesettingFeatures.h
+++ b/Source/WebCore/platform/graphics/texmap/coordinated/CoordinatedSurface.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2010 Apple Inc. All rights reserved.
+ * Copyright (C) 2013 Company 100, Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -23,16 +23,32 @@
* THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef TypesettingFeatures_h
-#define TypesettingFeatures_h
+#include "config.h"
+#include "CoordinatedSurface.h"
+
+#if USE(COORDINATED_GRAPHICS)
namespace WebCore {
- enum TypesettingFeature {
- Kerning = 1 << 0,
- Ligatures = 1 << 1,
- };
- typedef unsigned TypesettingFeatures;
+CoordinatedSurface::Factory* CoordinatedSurface::s_factory = 0;
+
+void CoordinatedSurface::setFactory(CoordinatedSurface::Factory factory)
+{
+ s_factory = factory;
+}
+
+RefPtr<CoordinatedSurface> CoordinatedSurface::create(const IntSize& size, Flags flags)
+{
+ ASSERT(s_factory);
+ return s_factory(size, flags);
+}
+
+CoordinatedSurface::CoordinatedSurface(const IntSize& size, Flags flags)
+ : m_size(size)
+ , m_flags(flags)
+{
+}
+
} // namespace WebCore
-#endif // TypesettingFeatures_h
+#endif // USE(COORDINATED_GRAPHICS)
diff --git a/Source/WebCore/platform/graphics/texmap/coordinated/CoordinatedSurface.h b/Source/WebCore/platform/graphics/texmap/coordinated/CoordinatedSurface.h
new file mode 100644
index 000000000..ac9ae1712
--- /dev/null
+++ b/Source/WebCore/platform/graphics/texmap/coordinated/CoordinatedSurface.h
@@ -0,0 +1,76 @@
+/*
+ Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies)
+ Copyright (C) 2012 Company 100, Inc.
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+ */
+
+#ifndef CoordinatedSurface_h
+#define CoordinatedSurface_h
+
+#if USE(COORDINATED_GRAPHICS)
+#include "IntRect.h"
+#include <wtf/RefPtr.h>
+#include <wtf/ThreadSafeRefCounted.h>
+
+namespace WebCore {
+class BitmapTexture;
+class GraphicsContext;
+
+class CoordinatedSurface : public ThreadSafeRefCounted<CoordinatedSurface> {
+public:
+ enum Flag {
+ NoFlags = 0,
+ SupportsAlpha = 1 << 0,
+ };
+ typedef unsigned Flags;
+
+ class Client {
+ public:
+ virtual ~Client() { }
+ virtual void paintToSurfaceContext(GraphicsContext&) = 0;
+ };
+
+ typedef RefPtr<CoordinatedSurface> Factory(const IntSize&, Flags);
+ static void setFactory(Factory);
+ static RefPtr<CoordinatedSurface> create(const IntSize&, Flags);
+
+ virtual ~CoordinatedSurface() { }
+
+ bool supportsAlpha() const { return flags() & SupportsAlpha; }
+ IntSize size() const { return m_size; }
+
+ virtual void paintToSurface(const IntRect&, Client&) = 0;
+
+#if USE(TEXTURE_MAPPER)
+ virtual void copyToTexture(RefPtr<BitmapTexture>, const IntRect& target, const IntPoint& sourceOffset) = 0;
+#endif
+
+protected:
+ CoordinatedSurface(const IntSize&, Flags);
+ Flags flags() const { return m_flags; }
+
+ IntSize m_size;
+ Flags m_flags;
+
+private:
+ static CoordinatedSurface::Factory* s_factory;
+};
+
+} // namespace WebCore
+
+#endif // USE(COORDINATED_GRAPHICS)
+#endif // CoordinatedSurface_h
diff --git a/Source/WebCore/platform/graphics/texmap/coordinated/SurfaceUpdateInfo.h b/Source/WebCore/platform/graphics/texmap/coordinated/SurfaceUpdateInfo.h
new file mode 100644
index 000000000..0e749be93
--- /dev/null
+++ b/Source/WebCore/platform/graphics/texmap/coordinated/SurfaceUpdateInfo.h
@@ -0,0 +1,48 @@
+/*
+ Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies)
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+ */
+
+#ifndef SurfaceUpdateInfo_h
+#define SurfaceUpdateInfo_h
+
+#if USE(COORDINATED_GRAPHICS)
+
+#include "IntRect.h"
+
+namespace WebCore {
+
+class SurfaceUpdateInfo {
+
+public:
+ SurfaceUpdateInfo() { }
+
+ // The rect to be updated.
+ IntRect updateRect;
+
+ // The id of the update atlas including the shareable bitmap containing the updates.
+ uint32_t atlasID { 0 };
+
+ // The offset in the bitmap where the rendered contents are.
+ IntPoint surfaceOffset;
+};
+
+} // namespace WebCore
+
+#endif // USE(COORDINATED_GRAPHICS)
+
+#endif // SurfaceUpdateInfo_h
diff --git a/Source/WebCore/platform/graphics/texmap/coordinated/Tile.cpp b/Source/WebCore/platform/graphics/texmap/coordinated/Tile.cpp
new file mode 100644
index 000000000..f155d4433
--- /dev/null
+++ b/Source/WebCore/platform/graphics/texmap/coordinated/Tile.cpp
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies)
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "Tile.h"
+
+#if USE(COORDINATED_GRAPHICS)
+#include "GraphicsContext.h"
+#include "SurfaceUpdateInfo.h"
+#include "TiledBackingStore.h"
+#include "TiledBackingStoreClient.h"
+
+namespace WebCore {
+
+static const uint32_t InvalidTileID = 0;
+
+Tile::Tile(TiledBackingStore& tiledBackingStore, const Coordinate& tileCoordinate)
+ : m_tiledBackingStore(tiledBackingStore)
+ , m_coordinate(tileCoordinate)
+ , m_rect(tiledBackingStore.tileRectForCoordinate(tileCoordinate))
+ , m_ID(InvalidTileID)
+ , m_dirtyRect(m_rect)
+{
+}
+
+Tile::~Tile()
+{
+ if (m_ID != InvalidTileID)
+ m_tiledBackingStore.client()->removeTile(m_ID);
+}
+
+bool Tile::isDirty() const
+{
+ return !m_dirtyRect.isEmpty();
+}
+
+void Tile::invalidate(const IntRect& dirtyRect)
+{
+ IntRect tileDirtyRect = intersection(dirtyRect, m_rect);
+ if (tileDirtyRect.isEmpty())
+ return;
+
+ m_dirtyRect.unite(tileDirtyRect);
+}
+
+bool Tile::updateBackBuffer()
+{
+ if (!isDirty())
+ return false;
+
+ SurfaceUpdateInfo updateInfo;
+
+ if (!m_tiledBackingStore.client()->paintToSurface(m_dirtyRect.size(), updateInfo.atlasID, updateInfo.surfaceOffset, *this))
+ return false;
+
+ updateInfo.updateRect = m_dirtyRect;
+ updateInfo.updateRect.move(-m_rect.x(), -m_rect.y());
+
+ static uint32_t id = 1;
+ if (m_ID == InvalidTileID) {
+ m_ID = id++;
+ // We may get an invalid ID due to wrap-around on overflow.
+ if (m_ID == InvalidTileID)
+ m_ID = id++;
+ m_tiledBackingStore.client()->createTile(m_ID, m_tiledBackingStore.contentsScale());
+ }
+ m_tiledBackingStore.client()->updateTile(m_ID, updateInfo, m_rect);
+
+ m_dirtyRect = IntRect();
+
+ return true;
+}
+
+void Tile::paintToSurfaceContext(GraphicsContext& context)
+{
+ context.translate(-m_dirtyRect.x(), -m_dirtyRect.y());
+ context.scale(FloatSize(m_tiledBackingStore.contentsScale(), m_tiledBackingStore.contentsScale()));
+ m_tiledBackingStore.client()->tiledBackingStorePaint(context, m_tiledBackingStore.mapToContents(m_dirtyRect));
+}
+
+bool Tile::isReadyToPaint() const
+{
+ return m_ID != InvalidTileID;
+}
+
+void Tile::resize(const IntSize& newSize)
+{
+ m_rect = IntRect(m_rect.location(), newSize);
+ m_dirtyRect = m_rect;
+}
+
+} // namespace WebCore
+
+#endif // USE(COORDINATED_GRAPHICS)
diff --git a/Source/WebCore/platform/graphics/texmap/coordinated/Tile.h b/Source/WebCore/platform/graphics/texmap/coordinated/Tile.h
new file mode 100644
index 000000000..0b04d75aa
--- /dev/null
+++ b/Source/WebCore/platform/graphics/texmap/coordinated/Tile.h
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2010-2011 Nokia Corporation and/or its subsidiary(-ies)
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef Tile_h
+#define Tile_h
+
+#if USE(COORDINATED_GRAPHICS)
+#include "CoordinatedSurface.h"
+#include "IntPoint.h"
+#include "IntPointHash.h"
+#include "IntRect.h"
+#include <wtf/RefCounted.h>
+
+namespace WebCore {
+
+class GraphicsContext;
+class TiledBackingStore;
+
+class Tile : public CoordinatedSurface::Client {
+public:
+ typedef IntPoint Coordinate;
+
+ Tile(TiledBackingStore&, const Coordinate&);
+ ~Tile();
+
+ bool isDirty() const;
+ void invalidate(const IntRect&);
+ bool updateBackBuffer();
+ bool isReadyToPaint() const;
+
+ const Coordinate& coordinate() const { return m_coordinate; }
+ const IntRect& rect() const { return m_rect; }
+ void resize(const IntSize&);
+
+ void paintToSurfaceContext(GraphicsContext&) override;
+
+private:
+ TiledBackingStore& m_tiledBackingStore;
+ Coordinate m_coordinate;
+ IntRect m_rect;
+
+ uint32_t m_ID;
+ IntRect m_dirtyRect;
+};
+
+} // namespace WebCore
+
+#endif // USE(COORDINATED_GRAPHICS)
+
+#endif // Tile_h
diff --git a/Source/WebCore/platform/graphics/texmap/coordinated/TiledBackingStore.cpp b/Source/WebCore/platform/graphics/texmap/coordinated/TiledBackingStore.cpp
new file mode 100644
index 000000000..b6389165a
--- /dev/null
+++ b/Source/WebCore/platform/graphics/texmap/coordinated/TiledBackingStore.cpp
@@ -0,0 +1,425 @@
+/*
+ Copyright (C) 2010-2012 Nokia Corporation and/or its subsidiary(-ies)
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+ */
+
+#include "config.h"
+#include "TiledBackingStore.h"
+
+#if USE(COORDINATED_GRAPHICS)
+#include "GraphicsContext.h"
+#include "MemoryPressureHandler.h"
+#include "TiledBackingStoreClient.h"
+#include <wtf/CheckedArithmetic.h>
+
+namespace WebCore {
+
+static const int defaultTileDimension = 512;
+
+static IntPoint innerBottomRight(const IntRect& rect)
+{
+ // Actually, the rect does not contain rect.maxX(). Refer to IntRect::contain.
+ return IntPoint(rect.maxX() - 1, rect.maxY() - 1);
+}
+
+TiledBackingStore::TiledBackingStore(TiledBackingStoreClient* client, float contentsScale)
+ : m_client(client)
+ , m_tileSize(defaultTileDimension, defaultTileDimension)
+ , m_coverAreaMultiplier(2.0f)
+ , m_contentsScale(contentsScale)
+ , m_supportsAlpha(false)
+ , m_pendingTileCreation(false)
+{
+}
+
+TiledBackingStore::~TiledBackingStore()
+{
+}
+
+void TiledBackingStore::setTrajectoryVector(const FloatPoint& trajectoryVector)
+{
+ m_pendingTrajectoryVector = trajectoryVector;
+ m_pendingTrajectoryVector.normalize();
+}
+
+void TiledBackingStore::createTilesIfNeeded(const IntRect& unscaledVisibleRect, const IntRect& contentsRect)
+{
+ IntRect scaledContentsRect = mapFromContents(contentsRect);
+ IntRect visibleRect = mapFromContents(unscaledVisibleRect);
+ float coverAreaMultiplier = MemoryPressureHandler::singleton().isUnderMemoryPressure() ? 1.0f : 2.0f;
+
+ bool didChange = m_trajectoryVector != m_pendingTrajectoryVector || m_visibleRect != visibleRect || m_rect != scaledContentsRect || m_coverAreaMultiplier != coverAreaMultiplier;
+ if (didChange || m_pendingTileCreation)
+ createTiles(visibleRect, scaledContentsRect, coverAreaMultiplier);
+}
+
+void TiledBackingStore::invalidate(const IntRect& contentsDirtyRect)
+{
+ IntRect dirtyRect(mapFromContents(contentsDirtyRect));
+ IntRect keepRectFitToTileSize = tileRectForCoordinate(tileCoordinateForPoint(m_keepRect.location()));
+ keepRectFitToTileSize.unite(tileRectForCoordinate(tileCoordinateForPoint(innerBottomRight(m_keepRect))));
+
+ // Only iterate on the part of the rect that we know we might have tiles.
+ IntRect coveredDirtyRect = intersection(dirtyRect, keepRectFitToTileSize);
+ Tile::Coordinate topLeft = tileCoordinateForPoint(coveredDirtyRect.location());
+ Tile::Coordinate bottomRight = tileCoordinateForPoint(innerBottomRight(coveredDirtyRect));
+
+ for (int yCoordinate = topLeft.y(); yCoordinate <= bottomRight.y(); ++yCoordinate) {
+ for (int xCoordinate = topLeft.x(); xCoordinate <= bottomRight.x(); ++xCoordinate) {
+ Tile* currentTile = m_tiles.get(Tile::Coordinate(xCoordinate, yCoordinate));
+ if (!currentTile)
+ continue;
+ // Pass the full rect to each tile as coveredDirtyRect might not
+ // contain them completely and we don't want partial tile redraws.
+ currentTile->invalidate(dirtyRect);
+ }
+ }
+}
+
+void TiledBackingStore::updateTileBuffers()
+{
+ // FIXME: In single threaded case, tile back buffers could be updated asynchronously
+ // one by one and then swapped to front in one go. This would minimize the time spent
+ // blocking on tile updates.
+ bool updated = false;
+ for (auto& tile : m_tiles.values()) {
+ if (!tile->isDirty())
+ continue;
+
+ updated |= tile->updateBackBuffer();
+ }
+
+ if (updated)
+ m_client->didUpdateTileBuffers();
+}
+
+double TiledBackingStore::tileDistance(const IntRect& viewport, const Tile::Coordinate& tileCoordinate) const
+{
+ if (viewport.intersects(tileRectForCoordinate(tileCoordinate)))
+ return 0;
+
+ IntPoint viewCenter = viewport.location() + IntSize(viewport.width() / 2, viewport.height() / 2);
+ Tile::Coordinate centerCoordinate = tileCoordinateForPoint(viewCenter);
+
+ return std::max(abs(centerCoordinate.y() - tileCoordinate.y()), abs(centerCoordinate.x() - tileCoordinate.x()));
+}
+
+// Returns a ratio between 0.0f and 1.0f of the surface covered by rendered tiles.
+float TiledBackingStore::coverageRatio(const WebCore::IntRect& dirtyRect) const
+{
+ float rectArea = dirtyRect.width() * dirtyRect.height();
+ float coverArea = 0.0f;
+
+ Tile::Coordinate topLeft = tileCoordinateForPoint(dirtyRect.location());
+ Tile::Coordinate bottomRight = tileCoordinateForPoint(innerBottomRight(dirtyRect));
+
+ for (int yCoordinate = topLeft.y(); yCoordinate <= bottomRight.y(); ++yCoordinate) {
+ for (int xCoordinate = topLeft.x(); xCoordinate <= bottomRight.x(); ++xCoordinate) {
+ Tile::Coordinate currentCoordinate(xCoordinate, yCoordinate);
+ Tile* currentTile = m_tiles.get(currentCoordinate);
+ if (currentTile && currentTile->isReadyToPaint()) {
+ IntRect coverRect = intersection(dirtyRect, currentTile->rect());
+ coverArea += coverRect.width() * coverRect.height();
+ }
+ }
+ }
+ return coverArea / rectArea;
+}
+
+bool TiledBackingStore::visibleAreaIsCovered() const
+{
+ return coverageRatio(intersection(m_visibleRect, m_rect)) == 1.0f;
+}
+
+void TiledBackingStore::createTiles(const IntRect& visibleRect, const IntRect& scaledContentsRect, float coverAreaMultiplier)
+{
+ // Update our backing store geometry.
+ const IntRect previousRect = m_rect;
+ m_rect = scaledContentsRect;
+ m_trajectoryVector = m_pendingTrajectoryVector;
+ m_visibleRect = visibleRect;
+ m_coverAreaMultiplier = coverAreaMultiplier;
+
+ if (m_rect.isEmpty()) {
+ setCoverRect(IntRect());
+ setKeepRect(IntRect());
+ return;
+ }
+
+ /* We must compute cover and keep rects using the visibleRect, instead of the rect intersecting the visibleRect with m_rect,
+ * because TBS can be used as a backing store of GraphicsLayer and the visible rect usually does not intersect with m_rect.
+ * In the below case, the intersecting rect is an empty.
+ *
+ * +---------------+
+ * | |
+ * | m_rect |
+ * | +-------|-----------------------+
+ * | | HERE | cover or keep |
+ * +---------------+ rect |
+ * | +---------+ |
+ * | | visible | |
+ * | | rect | |
+ * | +---------+ |
+ * | |
+ * | |
+ * +-------------------------------+
+ *
+ * We must create or keep the tiles in the HERE region.
+ */
+
+ IntRect coverRect;
+ IntRect keepRect;
+ computeCoverAndKeepRect(m_visibleRect, coverRect, keepRect);
+
+ setCoverRect(coverRect);
+ setKeepRect(keepRect);
+
+ if (coverRect.isEmpty())
+ return;
+
+ // Resize tiles at the edge in case the contents size has changed, but only do so
+ // after having dropped tiles outside the keep rect.
+ bool didResizeTiles = false;
+ if (previousRect != m_rect)
+ didResizeTiles = resizeEdgeTiles();
+
+ // Search for the tile position closest to the viewport center that does not yet contain a tile.
+ // Which position is considered the closest depends on the tileDistance function.
+ double shortestDistance = std::numeric_limits<double>::infinity();
+ Vector<Tile::Coordinate> tilesToCreate;
+ unsigned requiredTileCount = 0;
+
+ // Cover areas (in tiles) with minimum distance from the visible rect. If the visible rect is
+ // not covered already it will be covered first in one go, due to the distance being 0 for tiles
+ // inside the visible rect.
+ Tile::Coordinate topLeft = tileCoordinateForPoint(coverRect.location());
+ Tile::Coordinate bottomRight = tileCoordinateForPoint(innerBottomRight(coverRect));
+ for (int yCoordinate = topLeft.y(); yCoordinate <= bottomRight.y(); ++yCoordinate) {
+ for (int xCoordinate = topLeft.x(); xCoordinate <= bottomRight.x(); ++xCoordinate) {
+ Tile::Coordinate currentCoordinate(xCoordinate, yCoordinate);
+ if (m_tiles.contains(currentCoordinate))
+ continue;
+ ++requiredTileCount;
+ double distance = tileDistance(m_visibleRect, currentCoordinate);
+ if (distance > shortestDistance)
+ continue;
+ if (distance < shortestDistance) {
+ tilesToCreate.clear();
+ shortestDistance = distance;
+ }
+ tilesToCreate.append(currentCoordinate);
+ }
+ }
+
+ // Now construct the tile(s) within the shortest distance.
+ unsigned tilesToCreateCount = tilesToCreate.size();
+ for (unsigned n = 0; n < tilesToCreateCount; ++n) {
+ Tile::Coordinate coordinate = tilesToCreate[n];
+ m_tiles.add(coordinate, std::make_unique<Tile>(*this, coordinate));
+ }
+ requiredTileCount -= tilesToCreateCount;
+
+ // Paint the content of the newly created tiles or resized tiles.
+ if (tilesToCreateCount || didResizeTiles)
+ updateTileBuffers();
+
+ // Re-call createTiles on a timer to cover the visible area with the newest shortest distance.
+ m_pendingTileCreation = requiredTileCount;
+ if (m_pendingTileCreation)
+ m_client->tiledBackingStoreHasPendingTileCreation();
+}
+
+void TiledBackingStore::adjustForContentsRect(IntRect& rect) const
+{
+ IntRect bounds = m_rect;
+ IntSize candidateSize = rect.size();
+
+ rect.intersect(bounds);
+
+ if (rect.size() == candidateSize)
+ return;
+
+ /*
+ * In the following case, there is no intersection of the contents rect and the cover rect.
+ * Thus the latter should not be inflated.
+ *
+ * +---------------+
+ * | m_rect |
+ * +---------------+
+ *
+ * +-------------------------------+
+ * | cover rect |
+ * | +---------+ |
+ * | | visible | |
+ * | | rect | |
+ * | +---------+ |
+ * +-------------------------------+
+ */
+ if (rect.isEmpty())
+ return;
+
+ // Try to create a cover rect of the same size as the candidate, but within content bounds.
+ int pixelsCovered = 0;
+ if (!WTF::safeMultiply(candidateSize.width(), candidateSize.height(), pixelsCovered))
+ pixelsCovered = std::numeric_limits<int>::max();
+
+ if (rect.width() < candidateSize.width())
+ rect.inflateY(((pixelsCovered / rect.width()) - rect.height()) / 2);
+ if (rect.height() < candidateSize.height())
+ rect.inflateX(((pixelsCovered / rect.height()) - rect.width()) / 2);
+
+ rect.intersect(bounds);
+}
+
+void TiledBackingStore::computeCoverAndKeepRect(const IntRect& visibleRect, IntRect& coverRect, IntRect& keepRect) const
+{
+ coverRect = visibleRect;
+ keepRect = visibleRect;
+
+ // If we cover more that the actual viewport we can be smart about which tiles we choose to render.
+ if (m_coverAreaMultiplier > 1) {
+ // The initial cover area covers equally in each direction, according to the coverAreaMultiplier.
+ coverRect.inflateX(visibleRect.width() * (m_coverAreaMultiplier - 1) / 2);
+ coverRect.inflateY(visibleRect.height() * (m_coverAreaMultiplier - 1) / 2);
+ keepRect = coverRect;
+
+ if (m_trajectoryVector != FloatPoint::zero()) {
+ // A null trajectory vector (no motion) means that tiles for the coverArea will be created.
+ // A non-null trajectory vector will shrink the covered rect to visibleRect plus its expansion from its
+ // center toward the cover area edges in the direction of the given vector.
+
+ // E.g. if visibleRect == (10,10)5x5 and coverAreaMultiplier == 3.0:
+ // a (0,0) trajectory vector will create tiles intersecting (5,5)15x15,
+ // a (1,0) trajectory vector will create tiles intersecting (10,10)10x5,
+ // and a (1,1) trajectory vector will create tiles intersecting (10,10)10x10.
+
+ // Multiply the vector by the distance to the edge of the cover area.
+ float trajectoryVectorMultiplier = (m_coverAreaMultiplier - 1) / 2;
+
+ // Unite the visible rect with a "ghost" of the visible rect moved in the direction of the trajectory vector.
+ coverRect = visibleRect;
+ coverRect.move(coverRect.width() * m_trajectoryVector.x() * trajectoryVectorMultiplier, coverRect.height() * m_trajectoryVector.y() * trajectoryVectorMultiplier);
+
+ coverRect.unite(visibleRect);
+ }
+ ASSERT(keepRect.contains(coverRect));
+ }
+
+ adjustForContentsRect(coverRect);
+
+ // The keep rect is an inflated version of the cover rect, inflated in tile dimensions.
+ keepRect.unite(coverRect);
+ keepRect.inflateX(m_tileSize.width() / 2);
+ keepRect.inflateY(m_tileSize.height() / 2);
+ keepRect.intersect(m_rect);
+
+ ASSERT(coverRect.isEmpty() || keepRect.contains(coverRect));
+}
+
+bool TiledBackingStore::resizeEdgeTiles()
+{
+ bool wasResized = false;
+ Vector<Tile::Coordinate> tilesToRemove;
+ for (auto& tile : m_tiles.values()) {
+ Tile::Coordinate tileCoordinate = tile->coordinate();
+ IntRect tileRect = tile->rect();
+ IntRect expectedTileRect = tileRectForCoordinate(tileCoordinate);
+ if (expectedTileRect.isEmpty())
+ tilesToRemove.append(tileCoordinate);
+ else if (expectedTileRect != tileRect) {
+ tile->resize(expectedTileRect.size());
+ wasResized = true;
+ }
+ }
+
+ for (auto& coordinateToRemove : tilesToRemove)
+ m_tiles.remove(coordinateToRemove);
+
+ return wasResized;
+}
+
+void TiledBackingStore::setKeepRect(const IntRect& keepRect)
+{
+ // Drop tiles outside the new keepRect.
+
+ FloatRect keepRectF = keepRect;
+
+ Vector<Tile::Coordinate> toRemove;
+ for (auto& tile : m_tiles.values()) {
+ Tile::Coordinate coordinate = tile->coordinate();
+ FloatRect tileRect = tile->rect();
+ if (!tileRect.intersects(keepRectF))
+ toRemove.append(coordinate);
+ }
+
+ for (auto& coordinateToRemove : toRemove)
+ m_tiles.remove(coordinateToRemove);
+
+ m_keepRect = keepRect;
+}
+
+void TiledBackingStore::removeAllNonVisibleTiles(const IntRect& unscaledVisibleRect, const IntRect& contentsRect)
+{
+ IntRect boundedVisibleRect = mapFromContents(intersection(unscaledVisibleRect, contentsRect));
+ setKeepRect(boundedVisibleRect);
+}
+
+IntRect TiledBackingStore::mapToContents(const IntRect& rect) const
+{
+ return enclosingIntRect(FloatRect(rect.x() / m_contentsScale,
+ rect.y() / m_contentsScale,
+ rect.width() / m_contentsScale,
+ rect.height() / m_contentsScale));
+}
+
+IntRect TiledBackingStore::mapFromContents(const IntRect& rect) const
+{
+ return enclosingIntRect(FloatRect(rect.x() * m_contentsScale,
+ rect.y() * m_contentsScale,
+ rect.width() * m_contentsScale,
+ rect.height() * m_contentsScale));
+}
+
+IntRect TiledBackingStore::tileRectForCoordinate(const Tile::Coordinate& coordinate) const
+{
+ IntRect rect(coordinate.x() * m_tileSize.width(),
+ coordinate.y() * m_tileSize.height(),
+ m_tileSize.width(),
+ m_tileSize.height());
+
+ rect.intersect(m_rect);
+ return rect;
+}
+
+Tile::Coordinate TiledBackingStore::tileCoordinateForPoint(const IntPoint& point) const
+{
+ int x = point.x() / m_tileSize.width();
+ int y = point.y() / m_tileSize.height();
+ return Tile::Coordinate(std::max(x, 0), std::max(y, 0));
+}
+
+void TiledBackingStore::setSupportsAlpha(bool a)
+{
+ if (a == m_supportsAlpha)
+ return;
+ m_supportsAlpha = a;
+ invalidate(m_rect);
+}
+
+}
+
+#endif
diff --git a/Source/WebCore/platform/graphics/texmap/coordinated/TiledBackingStore.h b/Source/WebCore/platform/graphics/texmap/coordinated/TiledBackingStore.h
new file mode 100644
index 000000000..c17f7644d
--- /dev/null
+++ b/Source/WebCore/platform/graphics/texmap/coordinated/TiledBackingStore.h
@@ -0,0 +1,109 @@
+/*
+ Copyright (C) 2010-2012 Nokia Corporation and/or its subsidiary(-ies)
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+ */
+
+#ifndef TiledBackingStore_h
+#define TiledBackingStore_h
+
+#if USE(COORDINATED_GRAPHICS)
+
+#include "FloatPoint.h"
+#include "IntPoint.h"
+#include "IntRect.h"
+#include "Tile.h"
+#include "Timer.h"
+#include <wtf/Assertions.h>
+#include <wtf/HashMap.h>
+
+namespace WebCore {
+
+class GraphicsContext;
+class TiledBackingStoreClient;
+
+class TiledBackingStore {
+ WTF_MAKE_NONCOPYABLE(TiledBackingStore); WTF_MAKE_FAST_ALLOCATED;
+public:
+ TiledBackingStore(TiledBackingStoreClient*, float contentsScale = 1.f);
+ ~TiledBackingStore();
+
+ TiledBackingStoreClient* client() { return m_client; }
+
+ void setTrajectoryVector(const FloatPoint&);
+ void createTilesIfNeeded(const IntRect& unscaledVisibleRect, const IntRect& contentsRect);
+
+ float contentsScale() { return m_contentsScale; }
+
+ void updateTileBuffers();
+
+ void invalidate(const IntRect& dirtyRect);
+
+ IntRect mapToContents(const IntRect&) const;
+ IntRect mapFromContents(const IntRect&) const;
+
+ IntRect tileRectForCoordinate(const Tile::Coordinate&) const;
+ Tile::Coordinate tileCoordinateForPoint(const IntPoint&) const;
+ double tileDistance(const IntRect& viewport, const Tile::Coordinate&) const;
+
+ IntRect coverRect() const { return m_coverRect; }
+ bool visibleAreaIsCovered() const;
+ void removeAllNonVisibleTiles(const IntRect& unscaledVisibleRect, const IntRect& contentsRect);
+
+ void setSupportsAlpha(bool);
+
+private:
+ void createTiles(const IntRect& visibleRect, const IntRect& scaledContentsRect, float coverAreaMultiplier);
+ void computeCoverAndKeepRect(const IntRect& visibleRect, IntRect& coverRect, IntRect& keepRect) const;
+
+ bool resizeEdgeTiles();
+ void setCoverRect(const IntRect& rect) { m_coverRect = rect; }
+ void setKeepRect(const IntRect&);
+
+ float coverageRatio(const IntRect&) const;
+ void adjustForContentsRect(IntRect&) const;
+
+ void paintCheckerPattern(GraphicsContext*, const IntRect&, const Tile::Coordinate&);
+
+private:
+ TiledBackingStoreClient* m_client;
+
+ typedef HashMap<Tile::Coordinate, std::unique_ptr<Tile>> TileMap;
+ TileMap m_tiles;
+
+ IntSize m_tileSize;
+ float m_coverAreaMultiplier;
+
+ FloatPoint m_trajectoryVector;
+ FloatPoint m_pendingTrajectoryVector;
+ IntRect m_visibleRect;
+
+ IntRect m_coverRect;
+ IntRect m_keepRect;
+ IntRect m_rect;
+
+ float m_contentsScale;
+
+ bool m_supportsAlpha;
+ bool m_pendingTileCreation;
+
+ friend class Tile;
+};
+
+}
+
+#endif
+#endif
diff --git a/Source/WebCore/platform/graphics/texmap/coordinated/TiledBackingStoreClient.h b/Source/WebCore/platform/graphics/texmap/coordinated/TiledBackingStoreClient.h
new file mode 100644
index 000000000..61936ff9c
--- /dev/null
+++ b/Source/WebCore/platform/graphics/texmap/coordinated/TiledBackingStoreClient.h
@@ -0,0 +1,49 @@
+/*
+ Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies)
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+ */
+
+#ifndef TiledBackingStoreClient_h
+#define TiledBackingStoreClient_h
+
+#include "CoordinatedSurface.h"
+
+namespace WebCore {
+
+#if USE(COORDINATED_GRAPHICS)
+
+class GraphicsContext;
+class SurfaceUpdateInfo;
+
+class TiledBackingStoreClient {
+public:
+ virtual ~TiledBackingStoreClient() { }
+ virtual void tiledBackingStorePaint(GraphicsContext&, const IntRect&) = 0;
+ virtual void didUpdateTileBuffers() = 0;
+ virtual void tiledBackingStoreHasPendingTileCreation() = 0;
+
+ virtual void createTile(uint32_t tileID, float) = 0;
+ virtual void updateTile(uint32_t tileID, const SurfaceUpdateInfo&, const IntRect&) = 0;
+ virtual void removeTile(uint32_t tileID) = 0;
+ virtual bool paintToSurface(const IntSize&, uint32_t& atlasID, IntPoint&, CoordinatedSurface::Client&) = 0;
+};
+
+#endif
+
+}
+
+#endif
diff --git a/Source/WebCore/platform/graphics/transforms/AffineTransform.cpp b/Source/WebCore/platform/graphics/transforms/AffineTransform.cpp
index 62a7639ba..8ebdd98f1 100644
--- a/Source/WebCore/platform/graphics/transforms/AffineTransform.cpp
+++ b/Source/WebCore/platform/graphics/transforms/AffineTransform.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2005, 2006 Apple Computer, Inc. All rights reserved.
+ * Copyright (C) 2005, 2006 Apple Inc. All rights reserved.
* 2010 Dirk Schulze <krit@webkit.org>
*
* Redistribution and use in source and binary forms, with or without
@@ -11,10 +11,10 @@
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
- * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
@@ -31,21 +31,34 @@
#include "FloatQuad.h"
#include "FloatRect.h"
#include "IntRect.h"
+#include "TextStream.h"
#include "TransformationMatrix.h"
#include <wtf/MathExtras.h>
namespace WebCore {
+#if COMPILER(MSVC)
AffineTransform::AffineTransform()
{
- setMatrix(1, 0, 0, 1, 0, 0);
+ m_transform = { 1, 0, 0, 1, 0, 0 };
+}
+
+AffineTransform::AffineTransform(double a, double b, double c, double d, double e, double f)
+{
+ m_transform = { a, b, c, d, e, f };
+}
+#else
+AffineTransform::AffineTransform()
+ : m_transform { { 1, 0, 0, 1, 0, 0 } }
+{
}
AffineTransform::AffineTransform(double a, double b, double c, double d, double e, double f)
+ : m_transform{ { a, b, c, d, e, f } }
{
- setMatrix(a, b, c, d, e, f);
}
+#endif
void AffineTransform::makeIdentity()
{
@@ -79,21 +92,23 @@ double AffineTransform::yScale() const
return sqrt(m_transform[2] * m_transform[2] + m_transform[3] * m_transform[3]);
}
-double AffineTransform::det() const
+static double det(const std::array<double, 6>& transform)
{
- return m_transform[0] * m_transform[3] - m_transform[1] * m_transform[2];
+ return transform[0] * transform[3] - transform[1] * transform[2];
}
bool AffineTransform::isInvertible() const
{
- return det() != 0.0;
+ double determinant = det(m_transform);
+
+ return std::isfinite(determinant) && determinant != 0;
}
-AffineTransform AffineTransform::inverse() const
+std::optional<AffineTransform> AffineTransform::inverse() const
{
- double determinant = det();
- if (determinant == 0.0)
- return AffineTransform();
+ double determinant = det(m_transform);
+ if (!std::isfinite(determinant) || determinant == 0)
+ return std::nullopt;
AffineTransform result;
if (isIdentityOrTranslation()) {
@@ -128,7 +143,7 @@ AffineTransform& AffineTransform::multiply(const AffineTransform& other)
trans.m_transform[4] = other.m_transform[4] * m_transform[0] + other.m_transform[5] * m_transform[2] + m_transform[4];
trans.m_transform[5] = other.m_transform[4] * m_transform[1] + other.m_transform[5] * m_transform[3] + m_transform[5];
- setMatrix(trans.m_transform);
+ *this = trans;
return *this;
}
@@ -158,6 +173,16 @@ AffineTransform& AffineTransform::scale(double sx, double sy)
return *this;
}
+AffineTransform& AffineTransform::scaleNonUniform(double sx, double sy)
+{
+ return scale(sx, sy);
+}
+
+AffineTransform& AffineTransform::scale(const FloatSize& s)
+{
+ return scale(s.width(), s.height());
+}
+
// *this = *this * translation
AffineTransform& AffineTransform::translate(double tx, double ty)
{
@@ -172,9 +197,9 @@ AffineTransform& AffineTransform::translate(double tx, double ty)
return *this;
}
-AffineTransform& AffineTransform::scaleNonUniform(double sx, double sy)
+AffineTransform& AffineTransform::translate(const FloatPoint& t)
{
- return scale(sx, sy);
+ return translate(t.x(), t.y());
}
AffineTransform& AffineTransform::rotateFromVector(double x, double y)
@@ -401,4 +426,20 @@ void AffineTransform::recompose(const DecomposedType& decomp)
this->scale(decomp.scaleX, decomp.scaleY);
}
+TextStream& operator<<(TextStream& ts, const AffineTransform& transform)
+{
+ if (transform.isIdentity())
+ ts << "identity";
+ else
+ ts << "{m=(("
+ << transform.a() << "," << transform.b()
+ << ")("
+ << transform.c() << "," << transform.d()
+ << ")) t=("
+ << transform.e() << "," << transform.f()
+ << ")}";
+
+ return ts;
+}
+
}
diff --git a/Source/WebCore/platform/graphics/transforms/AffineTransform.h b/Source/WebCore/platform/graphics/transforms/AffineTransform.h
index 0a5b45732..a0b1f7bd3 100644
--- a/Source/WebCore/platform/graphics/transforms/AffineTransform.h
+++ b/Source/WebCore/platform/graphics/transforms/AffineTransform.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2005, 2006 Apple Computer, Inc. All rights reserved.
+ * Copyright (C) 2005-2016 Apple Inc. All rights reserved.
* 2010 Dirk Schulze <krit@webkit.org>
*
* Redistribution and use in source and binary forms, with or without
@@ -11,10 +11,10 @@
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
- * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
@@ -27,8 +27,10 @@
#ifndef AffineTransform_h
#define AffineTransform_h
-#include <string.h> // for memcpy
+#include "PlatformExportMacros.h"
+#include <array>
#include <wtf/FastMalloc.h>
+#include <wtf/Optional.h>
#if USE(CG)
typedef struct CGAffineTransform CGAffineTransform;
@@ -36,6 +38,11 @@ typedef struct CGAffineTransform CGAffineTransform;
#include <cairo.h>
#endif
+#if PLATFORM(WIN)
+struct D2D_MATRIX_3X2_F;
+typedef D2D_MATRIX_3X2_F D2D1_MATRIX_3X2_F;
+#endif
+
namespace WebCore {
class FloatPoint;
@@ -45,18 +52,21 @@ class FloatSize;
class IntPoint;
class IntSize;
class IntRect;
+class TextStream;
class TransformationMatrix;
class AffineTransform {
WTF_MAKE_FAST_ALLOCATED;
public:
- typedef double Transform[6];
-
- AffineTransform();
- AffineTransform(double a, double b, double c, double d, double e, double f);
+ WEBCORE_EXPORT AffineTransform();
+ WEBCORE_EXPORT AffineTransform(double a, double b, double c, double d, double e, double f);
#if USE(CG)
- AffineTransform(const CGAffineTransform&);
+ WEBCORE_EXPORT AffineTransform(const CGAffineTransform&);
+#endif
+
+#if PLATFORM(WIN)
+ AffineTransform(const D2D1_MATRIX_3X2_F&);
#endif
void setMatrix(double a, double b, double c, double d, double e, double f);
@@ -64,22 +74,22 @@ public:
void map(double x, double y, double& x2, double& y2) const;
// Rounds the mapped point to the nearest integer value.
- IntPoint mapPoint(const IntPoint&) const;
+ WEBCORE_EXPORT IntPoint mapPoint(const IntPoint&) const;
- FloatPoint mapPoint(const FloatPoint&) const;
+ WEBCORE_EXPORT FloatPoint mapPoint(const FloatPoint&) const;
- IntSize mapSize(const IntSize&) const;
+ WEBCORE_EXPORT IntSize mapSize(const IntSize&) const;
- FloatSize mapSize(const FloatSize&) const;
+ WEBCORE_EXPORT FloatSize mapSize(const FloatSize&) const;
// Rounds the resulting mapped rectangle out. This is helpful for bounding
// box computations but may not be what is wanted in other contexts.
- IntRect mapRect(const IntRect&) const;
+ WEBCORE_EXPORT IntRect mapRect(const IntRect&) const;
- FloatRect mapRect(const FloatRect&) const;
- FloatQuad mapQuad(const FloatQuad&) const;
+ WEBCORE_EXPORT FloatRect mapRect(const FloatRect&) const;
+ WEBCORE_EXPORT FloatQuad mapQuad(const FloatQuad&) const;
- bool isIdentity() const;
+ WEBCORE_EXPORT bool isIdentity() const;
double a() const { return m_transform[0]; }
void setA(double a) { m_transform[0] = a; }
@@ -94,34 +104,35 @@ public:
double f() const { return m_transform[5]; }
void setF(double f) { m_transform[5] = f; }
- void makeIdentity();
+ WEBCORE_EXPORT void makeIdentity();
- AffineTransform& multiply(const AffineTransform& other);
- AffineTransform& scale(double);
+ WEBCORE_EXPORT AffineTransform& multiply(const AffineTransform& other);
+ WEBCORE_EXPORT AffineTransform& scale(double);
AffineTransform& scale(double sx, double sy);
- AffineTransform& scaleNonUniform(double sx, double sy);
- AffineTransform& rotate(double d);
+ WEBCORE_EXPORT AffineTransform& scaleNonUniform(double sx, double sy);
+ WEBCORE_EXPORT AffineTransform& scale(const FloatSize&);
+ WEBCORE_EXPORT AffineTransform& rotate(double);
AffineTransform& rotateFromVector(double x, double y);
- AffineTransform& translate(double tx, double ty);
- AffineTransform& shear(double sx, double sy);
- AffineTransform& flipX();
- AffineTransform& flipY();
- AffineTransform& skew(double angleX, double angleY);
+ WEBCORE_EXPORT AffineTransform& translate(double tx, double ty);
+ WEBCORE_EXPORT AffineTransform& translate(const FloatPoint&);
+ WEBCORE_EXPORT AffineTransform& shear(double sx, double sy);
+ WEBCORE_EXPORT AffineTransform& flipX();
+ WEBCORE_EXPORT AffineTransform& flipY();
+ WEBCORE_EXPORT AffineTransform& skew(double angleX, double angleY);
AffineTransform& skewX(double angle);
AffineTransform& skewY(double angle);
// These functions get the length of an axis-aligned unit vector
// once it has been mapped through the transform
- double xScale() const;
- double yScale() const;
+ WEBCORE_EXPORT double xScale() const;
+ WEBCORE_EXPORT double yScale() const;
- double det() const;
- bool isInvertible() const;
- AffineTransform inverse() const;
+ bool isInvertible() const; // If you call this this, you're probably doing it wrong.
+ WEBCORE_EXPORT std::optional<AffineTransform> inverse() const;
- void blend(const AffineTransform& from, double progress);
+ WEBCORE_EXPORT void blend(const AffineTransform& from, double progress);
- TransformationMatrix toTransformationMatrix() const;
+ WEBCORE_EXPORT TransformationMatrix toTransformationMatrix() const;
bool isIdentityOrTranslation() const
{
@@ -165,16 +176,20 @@ public:
}
#if USE(CG)
- operator CGAffineTransform() const;
+ WEBCORE_EXPORT operator CGAffineTransform() const;
#elif USE(CAIRO)
operator cairo_matrix_t() const;
#endif
+#if PLATFORM(WIN)
+ operator D2D1_MATRIX_3X2_F() const;
+#endif
+
static AffineTransform translation(double x, double y)
{
return AffineTransform(1, 0, 0, 1, x, y);
}
-
+
// decompose the matrix into its component parts
typedef struct {
double scaleX, scaleY;
@@ -187,16 +202,12 @@ public:
void recompose(const DecomposedType&);
private:
- void setMatrix(const Transform m)
- {
- if (m && m != m_transform)
- memcpy(m_transform, m, sizeof(Transform));
- }
-
- Transform m_transform;
+ std::array<double, 6> m_transform;
};
-AffineTransform makeMapBetweenRects(const FloatRect& source, const FloatRect& dest);
+WEBCORE_EXPORT AffineTransform makeMapBetweenRects(const FloatRect& source, const FloatRect& dest);
+
+WEBCORE_EXPORT TextStream& operator<<(TextStream&, const AffineTransform&);
}
diff --git a/Source/WebCore/platform/graphics/transforms/IdentityTransformOperation.h b/Source/WebCore/platform/graphics/transforms/IdentityTransformOperation.h
index c1f7f4512..eed4c847b 100644
--- a/Source/WebCore/platform/graphics/transforms/IdentityTransformOperation.h
+++ b/Source/WebCore/platform/graphics/transforms/IdentityTransformOperation.h
@@ -26,36 +26,44 @@
#define IdentityTransformOperation_h
#include "TransformOperation.h"
+#include <wtf/Ref.h>
namespace WebCore {
-class IdentityTransformOperation : public TransformOperation {
+class IdentityTransformOperation final : public TransformOperation {
public:
- static PassRefPtr<IdentityTransformOperation> create()
+ static Ref<IdentityTransformOperation> create()
{
- return adoptRef(new IdentityTransformOperation());
+ return adoptRef(*new IdentityTransformOperation());
}
-
+
+ Ref<TransformOperation> clone() const override
+ {
+ return create();
+ }
+
private:
- virtual bool isIdentity() const { return true; }
- virtual OperationType type() const { return IDENTITY; }
- virtual bool isSameType(const TransformOperation& o) const { return o.type() == IDENTITY; }
+ bool isIdentity() const override { return true; }
+ OperationType type() const override { return IDENTITY; }
+ bool isSameType(const TransformOperation& o) const override { return o.type() == IDENTITY; }
- virtual bool operator==(const TransformOperation& o) const
+ bool operator==(const TransformOperation& o) const override
{
return isSameType(o);
}
- virtual bool apply(TransformationMatrix&, const FloatSize&) const
+ bool apply(TransformationMatrix&, const FloatSize&) const override
{
return false;
}
- virtual PassRefPtr<TransformOperation> blend(const TransformOperation*, double, bool = false)
+ Ref<TransformOperation> blend(const TransformOperation*, double, bool = false) override
{
- return this;
+ return *this;
}
+ void dump(TextStream&) const final;
+
IdentityTransformOperation()
{
}
@@ -64,4 +72,6 @@ private:
} // namespace WebCore
+SPECIALIZE_TYPE_TRAITS_TRANSFORMOPERATION(WebCore::IdentityTransformOperation, type() == WebCore::TransformOperation::IDENTITY)
+
#endif // IdentityTransformOperation_h
diff --git a/Source/WebCore/platform/graphics/transforms/Matrix3DTransformOperation.cpp b/Source/WebCore/platform/graphics/transforms/Matrix3DTransformOperation.cpp
index 443816f9e..c710f3981 100644
--- a/Source/WebCore/platform/graphics/transforms/Matrix3DTransformOperation.cpp
+++ b/Source/WebCore/platform/graphics/transforms/Matrix3DTransformOperation.cpp
@@ -13,7 +13,7 @@
* THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
@@ -26,14 +26,26 @@
#include "config.h"
#include "Matrix3DTransformOperation.h"
+#include "TextStream.h"
#include <algorithm>
namespace WebCore {
-PassRefPtr<TransformOperation> Matrix3DTransformOperation::blend(const TransformOperation* from, double progress, bool blendToIdentity)
+bool Matrix3DTransformOperation::operator==(const TransformOperation& other) const
+{
+ return isSameType(other) && m_matrix == downcast<Matrix3DTransformOperation>(other).m_matrix;
+}
+
+static Ref<TransformOperation> createOperation(TransformationMatrix& to, TransformationMatrix& from, double progress)
+{
+ to.blend(from, progress);
+ return Matrix3DTransformOperation::create(to);
+}
+
+Ref<TransformOperation> Matrix3DTransformOperation::blend(const TransformOperation* from, double progress, bool blendToIdentity)
{
if (from && !from->isSameType(*this))
- return this;
+ return *this;
// Convert the TransformOperations into matrices
FloatSize size;
@@ -45,10 +57,13 @@ PassRefPtr<TransformOperation> Matrix3DTransformOperation::blend(const Transform
apply(toT, size);
if (blendToIdentity)
- std::swap(fromT, toT);
+ return createOperation(fromT, toT, progress);
+ return createOperation(toT, fromT, progress);
+}
- toT.blend(fromT, progress);
- return Matrix3DTransformOperation::create(toT);
+void Matrix3DTransformOperation::dump(TextStream& ts) const
+{
+ ts << type() << "(" << m_matrix << ")";
}
} // namespace WebCore
diff --git a/Source/WebCore/platform/graphics/transforms/Matrix3DTransformOperation.h b/Source/WebCore/platform/graphics/transforms/Matrix3DTransformOperation.h
index 889c2b732..a30b0aa59 100644
--- a/Source/WebCore/platform/graphics/transforms/Matrix3DTransformOperation.h
+++ b/Source/WebCore/platform/graphics/transforms/Matrix3DTransformOperation.h
@@ -13,7 +13,7 @@
* THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
@@ -27,40 +27,43 @@
#define Matrix3DTransformOperation_h
#include "TransformOperation.h"
+#include <wtf/Ref.h>
namespace WebCore {
-class Matrix3DTransformOperation : public TransformOperation {
+class Matrix3DTransformOperation final : public TransformOperation {
public:
- static PassRefPtr<Matrix3DTransformOperation> create(const TransformationMatrix& matrix)
+ static Ref<Matrix3DTransformOperation> create(const TransformationMatrix& matrix)
{
- return adoptRef(new Matrix3DTransformOperation(matrix));
+ return adoptRef(*new Matrix3DTransformOperation(matrix));
+ }
+
+ Ref<TransformOperation> clone() const override
+ {
+ return adoptRef(*new Matrix3DTransformOperation(m_matrix));
}
TransformationMatrix matrix() const {return m_matrix; }
private:
- virtual bool isIdentity() const { return m_matrix.isIdentity(); }
+ bool isIdentity() const override { return m_matrix.isIdentity(); }
+ bool isAffectedByTransformOrigin() const override { return !isIdentity(); }
- virtual OperationType type() const { return MATRIX_3D; }
- virtual bool isSameType(const TransformOperation& o) const { return o.type() == MATRIX_3D; }
+ OperationType type() const override { return MATRIX_3D; }
+ bool isSameType(const TransformOperation& o) const override { return o.type() == MATRIX_3D; }
- virtual bool operator==(const TransformOperation& o) const
- {
- if (!isSameType(o))
- return false;
- const Matrix3DTransformOperation* m = static_cast<const Matrix3DTransformOperation*>(&o);
- return m_matrix == m->m_matrix;
- }
+ bool operator==(const TransformOperation&) const override;
- virtual bool apply(TransformationMatrix& transform, const FloatSize&) const
+ bool apply(TransformationMatrix& transform, const FloatSize&) const override
{
- transform.multiply(TransformationMatrix(m_matrix));
+ transform.multiply(m_matrix);
return false;
}
- virtual PassRefPtr<TransformOperation> blend(const TransformOperation* from, double progress, bool blendToIdentity = false);
+ Ref<TransformOperation> blend(const TransformOperation* from, double progress, bool blendToIdentity = false) override;
+ void dump(TextStream&) const final;
+
Matrix3DTransformOperation(const TransformationMatrix& mat)
{
m_matrix = mat;
@@ -71,4 +74,6 @@ private:
} // namespace WebCore
+SPECIALIZE_TYPE_TRAITS_TRANSFORMOPERATION(WebCore::Matrix3DTransformOperation, type() == WebCore::TransformOperation::MATRIX_3D)
+
#endif // Matrix3DTransformOperation_h
diff --git a/Source/WebCore/platform/graphics/transforms/MatrixTransformOperation.cpp b/Source/WebCore/platform/graphics/transforms/MatrixTransformOperation.cpp
index a3658a91d..17860e4e1 100644
--- a/Source/WebCore/platform/graphics/transforms/MatrixTransformOperation.cpp
+++ b/Source/WebCore/platform/graphics/transforms/MatrixTransformOperation.cpp
@@ -22,29 +22,52 @@
#include "config.h"
#include "MatrixTransformOperation.h"
+#include "TextStream.h"
#include <algorithm>
namespace WebCore {
-PassRefPtr<TransformOperation> MatrixTransformOperation::blend(const TransformOperation* from, double progress, bool blendToIdentity)
+bool MatrixTransformOperation::operator==(const TransformOperation& other) const
+{
+ if (!isSameType(other))
+ return false;
+ const MatrixTransformOperation& m = downcast<MatrixTransformOperation>(other);
+ return m_a == m.m_a && m_b == m.m_b && m_c == m.m_c && m_d == m.m_d && m_e == m.m_e && m_f == m.m_f;
+}
+
+static Ref<TransformOperation> createOperation(TransformationMatrix& to, TransformationMatrix& from, double progress)
+{
+ to.blend(from, progress);
+ return MatrixTransformOperation::create(to);
+}
+
+Ref<TransformOperation> MatrixTransformOperation::blend(const TransformOperation* from, double progress, bool blendToIdentity)
{
if (from && !from->isSameType(*this))
- return this;
+ return *this;
// convert the TransformOperations into matrices
- FloatSize size;
TransformationMatrix fromT;
TransformationMatrix toT(m_a, m_b, m_c, m_d, m_e, m_f);
if (from) {
- const MatrixTransformOperation* m = static_cast<const MatrixTransformOperation*>(from);
- fromT.setMatrix(m->m_a, m->m_b, m->m_c, m->m_d, m->m_e, m->m_f);
+ const MatrixTransformOperation& m = downcast<MatrixTransformOperation>(*from);
+ fromT.setMatrix(m.m_a, m.m_b, m.m_c, m.m_d, m.m_e, m.m_f);
}
-
+
if (blendToIdentity)
- std::swap(fromT, toT);
+ return createOperation(fromT, toT, progress);
+ return createOperation(toT, fromT, progress);
+}
- toT.blend(fromT, progress);
- return MatrixTransformOperation::create(toT.a(), toT.b(), toT.c(), toT.d(), toT.e(), toT.f());
+void MatrixTransformOperation::dump(TextStream& ts) const
+{
+ ts << "("
+ << TextStream::FormatNumberRespectingIntegers(m_a) << ", "
+ << TextStream::FormatNumberRespectingIntegers(m_b) << ", "
+ << TextStream::FormatNumberRespectingIntegers(m_c) << ", "
+ << TextStream::FormatNumberRespectingIntegers(m_d) << ", "
+ << TextStream::FormatNumberRespectingIntegers(m_e) << ", "
+ << TextStream::FormatNumberRespectingIntegers(m_f) << ")";
}
} // namespace WebCore
diff --git a/Source/WebCore/platform/graphics/transforms/MatrixTransformOperation.h b/Source/WebCore/platform/graphics/transforms/MatrixTransformOperation.h
index 76c5dffcd..3e88a9954 100644
--- a/Source/WebCore/platform/graphics/transforms/MatrixTransformOperation.h
+++ b/Source/WebCore/platform/graphics/transforms/MatrixTransformOperation.h
@@ -27,47 +27,49 @@
#include "TransformOperation.h"
#include "TransformationMatrix.h"
+#include <wtf/Ref.h>
namespace WebCore {
-class MatrixTransformOperation : public TransformOperation {
+class MatrixTransformOperation final : public TransformOperation {
public:
- static PassRefPtr<MatrixTransformOperation> create(double a, double b, double c, double d, double e, double f)
+ static Ref<MatrixTransformOperation> create(double a, double b, double c, double d, double e, double f)
{
- return adoptRef(new MatrixTransformOperation(a, b, c, d, e, f));
+ return adoptRef(*new MatrixTransformOperation(a, b, c, d, e, f));
}
- static PassRefPtr<MatrixTransformOperation> create(const TransformationMatrix& t)
+ static Ref<MatrixTransformOperation> create(const TransformationMatrix& t)
{
- return adoptRef(new MatrixTransformOperation(t));
+ return adoptRef(*new MatrixTransformOperation(t));
+ }
+
+ Ref<TransformOperation> clone() const override
+ {
+ return adoptRef(*new MatrixTransformOperation(matrix()));
}
TransformationMatrix matrix() const { return TransformationMatrix(m_a, m_b, m_c, m_d, m_e, m_f); }
private:
- virtual bool isIdentity() const { return m_a == 1 && m_b == 0 && m_c == 0 && m_d == 1 && m_e == 0 && m_f == 0; }
+ bool isIdentity() const override { return m_a == 1 && m_b == 0 && m_c == 0 && m_d == 1 && m_e == 0 && m_f == 0; }
+ bool isAffectedByTransformOrigin() const override { return !isIdentity(); }
- virtual OperationType type() const { return MATRIX; }
- virtual bool isSameType(const TransformOperation& o) const { return o.type() == MATRIX; }
-
- virtual bool operator==(const TransformOperation& o) const
- {
- if (!isSameType(o))
- return false;
+ OperationType type() const override { return MATRIX; }
+ bool isSameType(const TransformOperation& o) const override { return o.type() == MATRIX; }
- const MatrixTransformOperation* m = static_cast<const MatrixTransformOperation*>(&o);
- return m_a == m->m_a && m_b == m->m_b && m_c == m->m_c && m_d == m->m_d && m_e == m->m_e && m_f == m->m_f;
- }
+ bool operator==(const TransformOperation&) const override;
- virtual bool apply(TransformationMatrix& transform, const FloatSize&) const
+ bool apply(TransformationMatrix& transform, const FloatSize&) const override
{
TransformationMatrix matrix(m_a, m_b, m_c, m_d, m_e, m_f);
transform.multiply(matrix);
return false;
}
- virtual PassRefPtr<TransformOperation> blend(const TransformOperation* from, double progress, bool blendToIdentity = false);
-
+ Ref<TransformOperation> blend(const TransformOperation* from, double progress, bool blendToIdentity = false) override;
+
+ void dump(TextStream&) const final;
+
MatrixTransformOperation(double a, double b, double c, double d, double e, double f)
: m_a(a)
, m_b(b)
@@ -98,4 +100,6 @@ private:
} // namespace WebCore
+SPECIALIZE_TYPE_TRAITS_TRANSFORMOPERATION(WebCore::MatrixTransformOperation, type() == WebCore::TransformOperation::MATRIX)
+
#endif // MatrixTransformOperation_h
diff --git a/Source/WebCore/platform/graphics/transforms/PerspectiveTransformOperation.cpp b/Source/WebCore/platform/graphics/transforms/PerspectiveTransformOperation.cpp
index a3ffb664c..a612aed6e 100644
--- a/Source/WebCore/platform/graphics/transforms/PerspectiveTransformOperation.cpp
+++ b/Source/WebCore/platform/graphics/transforms/PerspectiveTransformOperation.cpp
@@ -13,7 +13,7 @@
* THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
@@ -27,14 +27,22 @@
#include "PerspectiveTransformOperation.h"
#include "AnimationUtilities.h"
+#include "TextStream.h"
#include <wtf/MathExtras.h>
namespace WebCore {
-PassRefPtr<TransformOperation> PerspectiveTransformOperation::blend(const TransformOperation* from, double progress, bool blendToIdentity)
+bool PerspectiveTransformOperation::operator==(const TransformOperation& other) const
+{
+ if (!isSameType(other))
+ return false;
+ return m_p == downcast<PerspectiveTransformOperation>(other).m_p;
+}
+
+Ref<TransformOperation> PerspectiveTransformOperation::blend(const TransformOperation* from, double progress, bool blendToIdentity)
{
if (from && !from->isSameType(*this))
- return this;
+ return *this;
if (blendToIdentity) {
double p = floatValueForLength(m_p, 1);
@@ -42,7 +50,7 @@ PassRefPtr<TransformOperation> PerspectiveTransformOperation::blend(const Transf
return PerspectiveTransformOperation::create(Length(clampToPositiveInteger(p), Fixed));
}
- const PerspectiveTransformOperation* fromOp = static_cast<const PerspectiveTransformOperation*>(from);
+ const PerspectiveTransformOperation* fromOp = downcast<PerspectiveTransformOperation>(from);
Length fromP = fromOp ? fromOp->m_p : Length(m_p.type());
Length toP = m_p;
@@ -61,4 +69,9 @@ PassRefPtr<TransformOperation> PerspectiveTransformOperation::blend(const Transf
return PerspectiveTransformOperation::create(Length(0, Fixed));
}
+void PerspectiveTransformOperation::dump(TextStream& ts) const
+{
+ ts << type() << "(" << m_p << ")";
+}
+
} // namespace WebCore
diff --git a/Source/WebCore/platform/graphics/transforms/PerspectiveTransformOperation.h b/Source/WebCore/platform/graphics/transforms/PerspectiveTransformOperation.h
index 73aa680ad..d9b20a561 100644
--- a/Source/WebCore/platform/graphics/transforms/PerspectiveTransformOperation.h
+++ b/Source/WebCore/platform/graphics/transforms/PerspectiveTransformOperation.h
@@ -13,7 +13,7 @@
* THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
@@ -29,38 +29,42 @@
#include "Length.h"
#include "LengthFunctions.h"
#include "TransformOperation.h"
+#include <wtf/Ref.h>
namespace WebCore {
-class PerspectiveTransformOperation : public TransformOperation {
+class PerspectiveTransformOperation final : public TransformOperation {
public:
- static PassRefPtr<PerspectiveTransformOperation> create(const Length& p)
+ static Ref<PerspectiveTransformOperation> create(const Length& p)
{
- return adoptRef(new PerspectiveTransformOperation(p));
+ return adoptRef(*new PerspectiveTransformOperation(p));
+ }
+
+ Ref<TransformOperation> clone() const override
+ {
+ return adoptRef(*new PerspectiveTransformOperation(m_p));
}
Length perspective() const { return m_p; }
private:
- virtual bool isIdentity() const { return !floatValueForLength(m_p, 1); }
- virtual OperationType type() const { return PERSPECTIVE; }
- virtual bool isSameType(const TransformOperation& o) const { return o.type() == PERSPECTIVE; }
+ bool isIdentity() const override { return !floatValueForLength(m_p, 1); }
+ bool isAffectedByTransformOrigin() const override { return !isIdentity(); }
- virtual bool operator==(const TransformOperation& o) const
- {
- if (!isSameType(o))
- return false;
- const PerspectiveTransformOperation* p = static_cast<const PerspectiveTransformOperation*>(&o);
- return m_p == p->m_p;
- }
+ OperationType type() const override { return PERSPECTIVE; }
+ bool isSameType(const TransformOperation& o) const override { return o.type() == PERSPECTIVE; }
+
+ bool operator==(const TransformOperation&) const override;
- virtual bool apply(TransformationMatrix& transform, const FloatSize&) const
+ bool apply(TransformationMatrix& transform, const FloatSize&) const override
{
transform.applyPerspective(floatValueForLength(m_p, 1));
return false;
}
- virtual PassRefPtr<TransformOperation> blend(const TransformOperation* from, double progress, bool blendToIdentity = false);
+ Ref<TransformOperation> blend(const TransformOperation* from, double progress, bool blendToIdentity = false) override;
+
+ void dump(TextStream&) const final;
PerspectiveTransformOperation(const Length& p)
: m_p(p)
@@ -73,4 +77,6 @@ private:
} // namespace WebCore
+SPECIALIZE_TYPE_TRAITS_TRANSFORMOPERATION(WebCore::PerspectiveTransformOperation, type() == WebCore::TransformOperation::PERSPECTIVE)
+
#endif // PerspectiveTransformOperation_h
diff --git a/Source/WebCore/platform/graphics/transforms/RotateTransformOperation.cpp b/Source/WebCore/platform/graphics/transforms/RotateTransformOperation.cpp
index 32b4c6e1c..52c3dbcc5 100644
--- a/Source/WebCore/platform/graphics/transforms/RotateTransformOperation.cpp
+++ b/Source/WebCore/platform/graphics/transforms/RotateTransformOperation.cpp
@@ -23,20 +23,29 @@
#include "RotateTransformOperation.h"
#include "AnimationUtilities.h"
+#include "TextStream.h"
#include <algorithm>
#include <wtf/MathExtras.h>
namespace WebCore {
-PassRefPtr<TransformOperation> RotateTransformOperation::blend(const TransformOperation* from, double progress, bool blendToIdentity)
+bool RotateTransformOperation::operator==(const TransformOperation& other) const
+{
+ if (!isSameType(other))
+ return false;
+ const RotateTransformOperation& r = downcast<RotateTransformOperation>(other);
+ return m_x == r.m_x && m_y == r.m_y && m_z == r.m_z && m_angle == r.m_angle;
+}
+
+Ref<TransformOperation> RotateTransformOperation::blend(const TransformOperation* from, double progress, bool blendToIdentity)
{
if (from && !from->isSameType(*this))
- return this;
+ return *this;
if (blendToIdentity)
return RotateTransformOperation::create(m_x, m_y, m_z, m_angle - m_angle * progress, m_type);
- const RotateTransformOperation* fromOp = static_cast<const RotateTransformOperation*>(from);
+ const RotateTransformOperation* fromOp = downcast<RotateTransformOperation>(from);
// Optimize for single axis rotation
if (!fromOp || (fromOp->m_x == 0 && fromOp->m_y == 0 && fromOp->m_z == 1) ||
@@ -91,4 +100,9 @@ PassRefPtr<TransformOperation> RotateTransformOperation::blend(const TransformOp
return RotateTransformOperation::create(x, y, z, angle, ROTATE_3D);
}
+void RotateTransformOperation::dump(TextStream& ts) const
+{
+ ts << type() << "(" << TextStream::FormatNumberRespectingIntegers(m_x) << ", " << TextStream::FormatNumberRespectingIntegers(m_y) << ", " << TextStream::FormatNumberRespectingIntegers(m_z) << ", " << TextStream::FormatNumberRespectingIntegers(m_angle) << "deg)";
+}
+
} // namespace WebCore
diff --git a/Source/WebCore/platform/graphics/transforms/RotateTransformOperation.h b/Source/WebCore/platform/graphics/transforms/RotateTransformOperation.h
index f47a51e17..be8c6e2f3 100644
--- a/Source/WebCore/platform/graphics/transforms/RotateTransformOperation.h
+++ b/Source/WebCore/platform/graphics/transforms/RotateTransformOperation.h
@@ -26,19 +26,25 @@
#define RotateTransformOperation_h
#include "TransformOperation.h"
+#include <wtf/Ref.h>
namespace WebCore {
-class RotateTransformOperation : public TransformOperation {
+class RotateTransformOperation final : public TransformOperation {
public:
- static PassRefPtr<RotateTransformOperation> create(double angle, OperationType type)
+ static Ref<RotateTransformOperation> create(double angle, OperationType type)
{
- return adoptRef(new RotateTransformOperation(0, 0, 1, angle, type));
+ return adoptRef(*new RotateTransformOperation(0, 0, 1, angle, type));
}
- static PassRefPtr<RotateTransformOperation> create(double x, double y, double z, double angle, OperationType type)
+ static Ref<RotateTransformOperation> create(double x, double y, double z, double angle, OperationType type)
{
- return adoptRef(new RotateTransformOperation(x, y, z, angle, type));
+ return adoptRef(*new RotateTransformOperation(x, y, z, angle, type));
+ }
+
+ Ref<TransformOperation> clone() const override
+ {
+ return adoptRef(*new RotateTransformOperation(m_x, m_y, m_z, m_angle, m_type));
}
double x() const { return m_x; }
@@ -47,26 +53,23 @@ public:
double angle() const { return m_angle; }
private:
- virtual bool isIdentity() const { return m_angle == 0; }
+ bool isIdentity() const override { return m_angle == 0; }
+ bool isAffectedByTransformOrigin() const override { return !isIdentity(); }
- virtual OperationType type() const { return m_type; }
- virtual bool isSameType(const TransformOperation& o) const { return o.type() == m_type; }
+ OperationType type() const override { return m_type; }
+ bool isSameType(const TransformOperation& o) const override { return o.type() == m_type; }
- virtual bool operator==(const TransformOperation& o) const
- {
- if (!isSameType(o))
- return false;
- const RotateTransformOperation* r = static_cast<const RotateTransformOperation*>(&o);
- return m_x == r->m_x && m_y == r->m_y && m_z == r->m_z && m_angle == r->m_angle;
- }
+ bool operator==(const TransformOperation&) const override;
- virtual bool apply(TransformationMatrix& transform, const FloatSize& /*borderBoxSize*/) const
+ bool apply(TransformationMatrix& transform, const FloatSize& /*borderBoxSize*/) const override
{
transform.rotate3d(m_x, m_y, m_z, m_angle);
return false;
}
- virtual PassRefPtr<TransformOperation> blend(const TransformOperation* from, double progress, bool blendToIdentity = false);
+ Ref<TransformOperation> blend(const TransformOperation* from, double progress, bool blendToIdentity = false) override;
+
+ void dump(TextStream&) const final;
RotateTransformOperation(double x, double y, double z, double angle, OperationType type)
: m_x(x)
@@ -75,7 +78,7 @@ private:
, m_angle(angle)
, m_type(type)
{
- ASSERT(type == ROTATE_X || type == ROTATE_Y || type == ROTATE_Z || type == ROTATE_3D);
+ ASSERT(isRotateTransformOperationType());
}
double m_x;
@@ -87,4 +90,6 @@ private:
} // namespace WebCore
+SPECIALIZE_TYPE_TRAITS_TRANSFORMOPERATION(WebCore::RotateTransformOperation, isRotateTransformOperationType())
+
#endif // RotateTransformOperation_h
diff --git a/Source/WebCore/platform/graphics/transforms/ScaleTransformOperation.cpp b/Source/WebCore/platform/graphics/transforms/ScaleTransformOperation.cpp
index e8806f203..862eafe15 100644
--- a/Source/WebCore/platform/graphics/transforms/ScaleTransformOperation.cpp
+++ b/Source/WebCore/platform/graphics/transforms/ScaleTransformOperation.cpp
@@ -23,20 +23,29 @@
#include "ScaleTransformOperation.h"
#include "AnimationUtilities.h"
+#include "TextStream.h"
namespace WebCore {
-PassRefPtr<TransformOperation> ScaleTransformOperation::blend(const TransformOperation* from, double progress, bool blendToIdentity)
+bool ScaleTransformOperation::operator==(const TransformOperation& other) const
+{
+ if (!isSameType(other))
+ return false;
+ const ScaleTransformOperation& s = downcast<ScaleTransformOperation>(other);
+ return m_x == s.m_x && m_y == s.m_y && m_z == s.m_z;
+}
+
+Ref<TransformOperation> ScaleTransformOperation::blend(const TransformOperation* from, double progress, bool blendToIdentity)
{
if (from && !from->isSameType(*this))
- return this;
+ return *this;
if (blendToIdentity)
return ScaleTransformOperation::create(WebCore::blend(m_x, 1.0, progress),
WebCore::blend(m_y, 1.0, progress),
WebCore::blend(m_z, 1.0, progress), m_type);
- const ScaleTransformOperation* fromOp = static_cast<const ScaleTransformOperation*>(from);
+ const ScaleTransformOperation* fromOp = downcast<ScaleTransformOperation>(from);
double fromX = fromOp ? fromOp->m_x : 1.0;
double fromY = fromOp ? fromOp->m_y : 1.0;
double fromZ = fromOp ? fromOp->m_z : 1.0;
@@ -45,4 +54,9 @@ PassRefPtr<TransformOperation> ScaleTransformOperation::blend(const TransformOpe
WebCore::blend(fromZ, m_z, progress), m_type);
}
+void ScaleTransformOperation::dump(TextStream& ts) const
+{
+ ts << type() << "(" << TextStream::FormatNumberRespectingIntegers(m_x) << ", " << TextStream::FormatNumberRespectingIntegers(m_y) << ", " << TextStream::FormatNumberRespectingIntegers(m_z) << ")";
+}
+
} // namespace WebCore
diff --git a/Source/WebCore/platform/graphics/transforms/ScaleTransformOperation.h b/Source/WebCore/platform/graphics/transforms/ScaleTransformOperation.h
index 62beb23ce..ae8a55556 100644
--- a/Source/WebCore/platform/graphics/transforms/ScaleTransformOperation.h
+++ b/Source/WebCore/platform/graphics/transforms/ScaleTransformOperation.h
@@ -26,19 +26,25 @@
#define ScaleTransformOperation_h
#include "TransformOperation.h"
+#include <wtf/Ref.h>
namespace WebCore {
-class ScaleTransformOperation : public TransformOperation {
+class ScaleTransformOperation final : public TransformOperation {
public:
- static PassRefPtr<ScaleTransformOperation> create(double sx, double sy, OperationType type)
+ static Ref<ScaleTransformOperation> create(double sx, double sy, OperationType type)
{
- return adoptRef(new ScaleTransformOperation(sx, sy, 1, type));
+ return adoptRef(*new ScaleTransformOperation(sx, sy, 1, type));
}
- static PassRefPtr<ScaleTransformOperation> create(double sx, double sy, double sz, OperationType type)
+ static Ref<ScaleTransformOperation> create(double sx, double sy, double sz, OperationType type)
{
- return adoptRef(new ScaleTransformOperation(sx, sy, sz, type));
+ return adoptRef(*new ScaleTransformOperation(sx, sy, sz, type));
+ }
+
+ Ref<TransformOperation> clone() const override
+ {
+ return adoptRef(*new ScaleTransformOperation(m_x, m_y, m_z, m_type));
}
double x() const { return m_x; }
@@ -46,26 +52,23 @@ public:
double z() const { return m_z; }
private:
- virtual bool isIdentity() const { return m_x == 1 && m_y == 1 && m_z == 1; }
+ bool isIdentity() const override { return m_x == 1 && m_y == 1 && m_z == 1; }
+ bool isAffectedByTransformOrigin() const override { return !isIdentity(); }
- virtual OperationType type() const { return m_type; }
- virtual bool isSameType(const TransformOperation& o) const { return o.type() == m_type; }
+ OperationType type() const override { return m_type; }
+ bool isSameType(const TransformOperation& o) const override { return o.type() == m_type; }
- virtual bool operator==(const TransformOperation& o) const
- {
- if (!isSameType(o))
- return false;
- const ScaleTransformOperation* s = static_cast<const ScaleTransformOperation*>(&o);
- return m_x == s->m_x && m_y == s->m_y && m_z == s->m_z;
- }
+ bool operator==(const TransformOperation&) const override;
- virtual bool apply(TransformationMatrix& transform, const FloatSize&) const
+ bool apply(TransformationMatrix& transform, const FloatSize&) const override
{
transform.scale3d(m_x, m_y, m_z);
return false;
}
- virtual PassRefPtr<TransformOperation> blend(const TransformOperation* from, double progress, bool blendToIdentity = false);
+ Ref<TransformOperation> blend(const TransformOperation* from, double progress, bool blendToIdentity = false) override;
+
+ void dump(TextStream&) const final;
ScaleTransformOperation(double sx, double sy, double sz, OperationType type)
: m_x(sx)
@@ -73,7 +76,7 @@ private:
, m_z(sz)
, m_type(type)
{
- ASSERT(type == SCALE_X || type == SCALE_Y || type == SCALE_Z || type == SCALE || type == SCALE_3D);
+ ASSERT(isScaleTransformOperationType());
}
double m_x;
@@ -84,4 +87,6 @@ private:
} // namespace WebCore
+SPECIALIZE_TYPE_TRAITS_TRANSFORMOPERATION(WebCore::ScaleTransformOperation, isScaleTransformOperationType())
+
#endif // ScaleTransformOperation_h
diff --git a/Source/WebCore/platform/graphics/transforms/SkewTransformOperation.cpp b/Source/WebCore/platform/graphics/transforms/SkewTransformOperation.cpp
index d92d895ce..ec52112ef 100644
--- a/Source/WebCore/platform/graphics/transforms/SkewTransformOperation.cpp
+++ b/Source/WebCore/platform/graphics/transforms/SkewTransformOperation.cpp
@@ -23,21 +23,35 @@
#include "SkewTransformOperation.h"
#include "AnimationUtilities.h"
+#include "TextStream.h"
namespace WebCore {
-PassRefPtr<TransformOperation> SkewTransformOperation::blend(const TransformOperation* from, double progress, bool blendToIdentity)
+bool SkewTransformOperation::operator==(const TransformOperation& other) const
+{
+ if (!isSameType(other))
+ return false;
+ const SkewTransformOperation& s = downcast<SkewTransformOperation>(other);
+ return m_angleX == s.m_angleX && m_angleY == s.m_angleY;
+}
+
+Ref<TransformOperation> SkewTransformOperation::blend(const TransformOperation* from, double progress, bool blendToIdentity)
{
if (from && !from->isSameType(*this))
- return this;
+ return *this;
if (blendToIdentity)
return SkewTransformOperation::create(WebCore::blend(m_angleX, 0.0, progress), WebCore::blend(m_angleY, 0.0, progress), m_type);
- const SkewTransformOperation* fromOp = static_cast<const SkewTransformOperation*>(from);
+ const SkewTransformOperation* fromOp = downcast<SkewTransformOperation>(from);
double fromAngleX = fromOp ? fromOp->m_angleX : 0;
double fromAngleY = fromOp ? fromOp->m_angleY : 0;
return SkewTransformOperation::create(WebCore::blend(fromAngleX, m_angleX, progress), WebCore::blend(fromAngleY, m_angleY, progress), m_type);
}
+void SkewTransformOperation::dump(TextStream& ts) const
+{
+ ts << type() << "(" << TextStream::FormatNumberRespectingIntegers(m_angleX) << "deg, " << TextStream::FormatNumberRespectingIntegers(m_angleY) << "deg)";
+}
+
} // namespace WebCore
diff --git a/Source/WebCore/platform/graphics/transforms/SkewTransformOperation.h b/Source/WebCore/platform/graphics/transforms/SkewTransformOperation.h
index e9f0c819b..422d14b6a 100644
--- a/Source/WebCore/platform/graphics/transforms/SkewTransformOperation.h
+++ b/Source/WebCore/platform/graphics/transforms/SkewTransformOperation.h
@@ -26,45 +26,50 @@
#define SkewTransformOperation_h
#include "TransformOperation.h"
+#include <wtf/Ref.h>
namespace WebCore {
-class SkewTransformOperation : public TransformOperation {
+class SkewTransformOperation final : public TransformOperation {
public:
- static PassRefPtr<SkewTransformOperation> create(double angleX, double angleY, OperationType type)
+ static Ref<SkewTransformOperation> create(double angleX, double angleY, OperationType type)
{
- return adoptRef(new SkewTransformOperation(angleX, angleY, type));
+ return adoptRef(*new SkewTransformOperation(angleX, angleY, type));
+ }
+
+ Ref<TransformOperation> clone() const override
+ {
+ return adoptRef(*new SkewTransformOperation(m_angleX, m_angleY, m_type));
}
double angleX() const { return m_angleX; }
double angleY() const { return m_angleY; }
private:
- virtual bool isIdentity() const { return m_angleX == 0 && m_angleY == 0; }
- virtual OperationType type() const { return m_type; }
- virtual bool isSameType(const TransformOperation& o) const { return o.type() == m_type; }
+ bool isIdentity() const override { return m_angleX == 0 && m_angleY == 0; }
+ bool isAffectedByTransformOrigin() const override { return !isIdentity(); }
- virtual bool operator==(const TransformOperation& o) const
- {
- if (!isSameType(o))
- return false;
- const SkewTransformOperation* s = static_cast<const SkewTransformOperation*>(&o);
- return m_angleX == s->m_angleX && m_angleY == s->m_angleY;
- }
+ OperationType type() const override { return m_type; }
+ bool isSameType(const TransformOperation& o) const override { return o.type() == m_type; }
+
+ bool operator==(const TransformOperation&) const override;
- virtual bool apply(TransformationMatrix& transform, const FloatSize&) const
+ bool apply(TransformationMatrix& transform, const FloatSize&) const override
{
transform.skew(m_angleX, m_angleY);
return false;
}
- virtual PassRefPtr<TransformOperation> blend(const TransformOperation* from, double progress, bool blendToIdentity = false);
+ Ref<TransformOperation> blend(const TransformOperation* from, double progress, bool blendToIdentity = false) override;
+
+ void dump(TextStream&) const final;
SkewTransformOperation(double angleX, double angleY, OperationType type)
: m_angleX(angleX)
, m_angleY(angleY)
, m_type(type)
{
+ ASSERT(isSkewTransformOperationType());
}
double m_angleX;
@@ -74,4 +79,6 @@ private:
} // namespace WebCore
+SPECIALIZE_TYPE_TRAITS_TRANSFORMOPERATION(WebCore::SkewTransformOperation, isSkewTransformOperationType())
+
#endif // SkewTransformOperation_h
diff --git a/Source/WebCore/platform/graphics/transforms/TransformOperation.cpp b/Source/WebCore/platform/graphics/transforms/TransformOperation.cpp
new file mode 100644
index 000000000..2ec90c12a
--- /dev/null
+++ b/Source/WebCore/platform/graphics/transforms/TransformOperation.cpp
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2016 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "TransformOperation.h"
+
+#include "IdentityTransformOperation.h"
+#include "TextStream.h"
+
+namespace WebCore {
+
+void IdentityTransformOperation::dump(TextStream& ts) const
+{
+ ts << type();
+}
+
+TextStream& operator<<(TextStream& ts, TransformOperation::OperationType type)
+{
+ switch (type) {
+ case TransformOperation::SCALE_X: ts << "scaleX"; break;
+ case TransformOperation::SCALE_Y: ts << "scaleY"; break;
+ case TransformOperation::SCALE: ts << "scale"; break;
+ case TransformOperation::TRANSLATE_X: ts << "translateX"; break;
+ case TransformOperation::TRANSLATE_Y: ts << "translateY"; break;
+ case TransformOperation::TRANSLATE: ts << "translate"; break;
+ case TransformOperation::ROTATE: ts << "rotate"; break;
+ case TransformOperation::SKEW_X: ts << "skewX"; break;
+ case TransformOperation::SKEW_Y: ts << "skewY"; break;
+ case TransformOperation::SKEW: ts << "skew"; break;
+ case TransformOperation::MATRIX: ts << "matrix"; break;
+ case TransformOperation::SCALE_Z: ts << "scaleX"; break;
+ case TransformOperation::SCALE_3D: ts << "scale3d"; break;
+ case TransformOperation::TRANSLATE_Z: ts << "translateZ"; break;
+ case TransformOperation::TRANSLATE_3D: ts << "translate3d"; break;
+ case TransformOperation::ROTATE_X: ts << "rotateX"; break;
+ case TransformOperation::ROTATE_Y: ts << "rotateY"; break;
+ case TransformOperation::ROTATE_3D: ts << "rotate3d"; break;
+ case TransformOperation::MATRIX_3D: ts << "matrix3d"; break;
+ case TransformOperation::PERSPECTIVE: ts << "perspective"; break;
+ case TransformOperation::IDENTITY: ts << "identity"; break;
+ case TransformOperation::NONE: ts << "none"; break;
+ }
+
+ return ts;
+}
+
+TextStream& operator<<(TextStream& ts, const TransformOperation& operation)
+{
+ operation.dump(ts);
+ return ts;
+}
+
+} // namespace WebCore
diff --git a/Source/WebCore/platform/graphics/transforms/TransformOperation.h b/Source/WebCore/platform/graphics/transforms/TransformOperation.h
index f9f975fa4..4f6aa88da 100644
--- a/Source/WebCore/platform/graphics/transforms/TransformOperation.h
+++ b/Source/WebCore/platform/graphics/transforms/TransformOperation.h
@@ -27,8 +27,9 @@
#include "FloatSize.h"
#include "TransformationMatrix.h"
-#include <wtf/PassRefPtr.h>
+#include <wtf/Forward.h>
#include <wtf/RefCounted.h>
+#include <wtf/TypeCasts.h>
namespace WebCore {
@@ -53,6 +54,8 @@ public:
virtual ~TransformOperation() { }
+ virtual Ref<TransformOperation> clone() const = 0;
+
virtual bool operator==(const TransformOperation&) const = 0;
bool operator!=(const TransformOperation& o) const { return !(*this == o); }
@@ -61,10 +64,12 @@ public:
// Return true if the borderBoxSize was used in the computation, false otherwise.
virtual bool apply(TransformationMatrix&, const FloatSize& borderBoxSize) const = 0;
- virtual PassRefPtr<TransformOperation> blend(const TransformOperation* from, double progress, bool blendToIdentity = false) = 0;
+ virtual Ref<TransformOperation> blend(const TransformOperation* from, double progress, bool blendToIdentity = false) = 0;
virtual OperationType type() const = 0;
virtual bool isSameType(const TransformOperation&) const { return false; }
+
+ virtual bool isAffectedByTransformOrigin() const { return false; }
bool is3DOperation() const
{
@@ -79,8 +84,38 @@ public:
opType == MATRIX_3D ||
opType == PERSPECTIVE;
}
+
+ bool isRotateTransformOperationType() const
+ {
+ return type() == ROTATE_X || type() == ROTATE_Y || type() == ROTATE_Z || type() == ROTATE || type() == ROTATE_3D;
+ }
+
+ bool isScaleTransformOperationType() const
+ {
+ return type() == SCALE_X || type() == SCALE_Y || type() == SCALE_Z || type() == SCALE || type() == SCALE_3D;
+ }
+
+ bool isSkewTransformOperationType() const
+ {
+ return type() == SKEW_X || type() == SKEW_Y || type() == SKEW;
+ }
+
+ bool isTranslateTransformOperationType() const
+ {
+ return type() == TRANSLATE_X || type() == TRANSLATE_Y || type() == TRANSLATE_Z || type() == TRANSLATE || type() == TRANSLATE_3D;
+ }
+
+ virtual void dump(TextStream&) const = 0;
};
+TextStream& operator<<(TextStream&, TransformOperation::OperationType);
+TextStream& operator<<(TextStream&, const TransformOperation&);
+
} // namespace WebCore
+#define SPECIALIZE_TYPE_TRAITS_TRANSFORMOPERATION(ToValueTypeName, predicate) \
+SPECIALIZE_TYPE_TRAITS_BEGIN(ToValueTypeName) \
+ static bool isType(const WebCore::TransformOperation& operation) { return operation.predicate; } \
+SPECIALIZE_TYPE_TRAITS_END()
+
#endif // TransformOperation_h
diff --git a/Source/WebCore/platform/graphics/transforms/TransformOperations.cpp b/Source/WebCore/platform/graphics/transforms/TransformOperations.cpp
index a71de471a..9b156bd3d 100644
--- a/Source/WebCore/platform/graphics/transforms/TransformOperations.cpp
+++ b/Source/WebCore/platform/graphics/transforms/TransformOperations.cpp
@@ -24,6 +24,7 @@
#include "IdentityTransformOperation.h"
#include "Matrix3DTransformOperation.h"
+#include "TextStream.h"
#include <algorithm>
namespace WebCore {
@@ -63,6 +64,15 @@ bool TransformOperations::operationsMatch(const TransformOperations& other) cons
return true;
}
+bool TransformOperations::affectedByTransformOrigin() const
+{
+ for (const auto& operation : m_operations) {
+ if (operation->isAffectedByTransformOrigin())
+ return true;
+ }
+ return false;
+}
+
TransformOperations TransformOperations::blendByMatchingOperations(const TransformOperations& from, const double& progress) const
{
TransformOperations result;
@@ -71,9 +81,9 @@ TransformOperations TransformOperations::blendByMatchingOperations(const Transfo
unsigned toSize = operations().size();
unsigned size = std::max(fromSize, toSize);
for (unsigned i = 0; i < size; i++) {
- RefPtr<TransformOperation> fromOperation = (i < fromSize) ? from.operations()[i].get() : 0;
- RefPtr<TransformOperation> toOperation = (i < toSize) ? operations()[i].get() : 0;
- RefPtr<TransformOperation> blendedOperation = toOperation ? toOperation->blend(fromOperation.get(), progress) : (fromOperation ? fromOperation->blend(0, progress, true) : 0);
+ RefPtr<TransformOperation> fromOperation = (i < fromSize) ? from.operations()[i].get() : nullptr;
+ RefPtr<TransformOperation> toOperation = (i < toSize) ? operations()[i].get() : nullptr;
+ RefPtr<TransformOperation> blendedOperation = toOperation ? toOperation->blend(fromOperation.get(), progress) : (fromOperation ? RefPtr<TransformOperation>(fromOperation->blend(nullptr, progress, true)) : nullptr);
if (blendedOperation)
result.operations().append(blendedOperation);
else {
@@ -117,4 +127,11 @@ TransformOperations TransformOperations::blend(const TransformOperations& from,
return blendByUsingMatrixInterpolation(from, progress, size);
}
+TextStream& operator<<(TextStream& ts, const TransformOperations& ops)
+{
+ for (const auto& operation : ops.operations())
+ ts << *operation;
+ return ts;
+}
+
} // namespace WebCore
diff --git a/Source/WebCore/platform/graphics/transforms/TransformOperations.h b/Source/WebCore/platform/graphics/transforms/TransformOperations.h
index 96045d3f9..3e1d08e0e 100644
--- a/Source/WebCore/platform/graphics/transforms/TransformOperations.h
+++ b/Source/WebCore/platform/graphics/transforms/TransformOperations.h
@@ -65,6 +65,8 @@ public:
{
m_operations.clear();
}
+
+ bool affectedByTransformOrigin() const;
Vector<RefPtr<TransformOperation>>& operations() { return m_operations; }
const Vector<RefPtr<TransformOperation>>& operations() const { return m_operations; }
@@ -80,6 +82,8 @@ private:
Vector<RefPtr<TransformOperation>> m_operations;
};
+TextStream& operator<<(TextStream&, const TransformOperations&);
+
} // namespace WebCore
#endif // TransformOperations_h
diff --git a/Source/WebCore/platform/graphics/transforms/TransformState.cpp b/Source/WebCore/platform/graphics/transforms/TransformState.cpp
index 541b1c648..5ef0e5bf4 100644
--- a/Source/WebCore/platform/graphics/transforms/TransformState.cpp
+++ b/Source/WebCore/platform/graphics/transforms/TransformState.cpp
@@ -10,10 +10,10 @@
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
- * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
@@ -26,8 +26,6 @@
#include "config.h"
#include "TransformState.h"
-#include <wtf/PassOwnPtr.h>
-
namespace WebCore {
TransformState& TransformState::operator=(const TransformState& other)
@@ -37,15 +35,20 @@ TransformState& TransformState::operator=(const TransformState& other)
m_mapQuad = other.m_mapQuad;
if (m_mapPoint)
m_lastPlanarPoint = other.m_lastPlanarPoint;
- if (m_mapQuad)
+ if (m_mapQuad) {
m_lastPlanarQuad = other.m_lastPlanarQuad;
+ if (other.m_lastPlanarSecondaryQuad)
+ m_lastPlanarSecondaryQuad = std::make_unique<FloatQuad>(*other.m_lastPlanarSecondaryQuad);
+ else
+ m_lastPlanarSecondaryQuad = nullptr;
+ }
m_accumulatingTransform = other.m_accumulatingTransform;
m_direction = other.m_direction;
- m_accumulatedTransform.clear();
+ m_accumulatedTransform = nullptr;
if (other.m_accumulatedTransform)
- m_accumulatedTransform = adoptPtr(new TransformationMatrix(*other.m_accumulatedTransform));
+ m_accumulatedTransform = std::make_unique<TransformationMatrix>(*other.m_accumulatedTransform);
return *this;
}
@@ -63,8 +66,11 @@ void TransformState::translateMappedCoordinates(const LayoutSize& offset)
LayoutSize adjustedOffset = (m_direction == ApplyTransformDirection) ? offset : -offset;
if (m_mapPoint)
m_lastPlanarPoint.move(adjustedOffset);
- if (m_mapQuad)
+ if (m_mapQuad) {
m_lastPlanarQuad.move(adjustedOffset);
+ if (m_lastPlanarSecondaryQuad)
+ m_lastPlanarSecondaryQuad->move(adjustedOffset);
+ }
}
void TransformState::move(const LayoutSize& offset, TransformAccumulation accumulate)
@@ -121,12 +127,12 @@ void TransformState::applyTransform(const TransformationMatrix& transformFromCon
// If we have an accumulated transform from last time, multiply in this transform
if (m_accumulatedTransform) {
if (m_direction == ApplyTransformDirection)
- m_accumulatedTransform = adoptPtr(new TransformationMatrix(transformFromContainer * *m_accumulatedTransform));
+ m_accumulatedTransform = std::make_unique<TransformationMatrix>(transformFromContainer * *m_accumulatedTransform);
else
m_accumulatedTransform->multiply(transformFromContainer);
} else if (accumulate == AccumulateTransform) {
// Make one if we started to accumulate
- m_accumulatedTransform = adoptPtr(new TransformationMatrix(transformFromContainer));
+ m_accumulatedTransform = std::make_unique<TransformationMatrix>(transformFromContainer);
}
if (accumulate == FlattenTransform) {
@@ -164,7 +170,7 @@ FloatPoint TransformState::mappedPoint(bool* wasClamped) const
if (m_direction == ApplyTransformDirection)
return m_accumulatedTransform->mapPoint(point);
- return m_accumulatedTransform->inverse().projectPoint(point, wasClamped);
+ return m_accumulatedTransform->inverse().value_or(TransformationMatrix()).projectPoint(point, wasClamped);
}
FloatQuad TransformState::mappedQuad(bool* wasClamped) const
@@ -173,14 +179,46 @@ FloatQuad TransformState::mappedQuad(bool* wasClamped) const
*wasClamped = false;
FloatQuad quad = m_lastPlanarQuad;
- quad.move((m_direction == ApplyTransformDirection) ? m_accumulatedOffset : -m_accumulatedOffset);
+ mapQuad(quad, m_direction, wasClamped);
+ return quad;
+}
+
+std::optional<FloatQuad> TransformState::mappedSecondaryQuad(bool* wasClamped) const
+{
+ if (wasClamped)
+ *wasClamped = false;
+
+ if (!m_lastPlanarSecondaryQuad)
+ return std::optional<FloatQuad>();
+
+ FloatQuad quad = *m_lastPlanarSecondaryQuad;
+ mapQuad(quad, m_direction, wasClamped);
+ return quad;
+}
+
+void TransformState::setLastPlanarSecondaryQuad(const FloatQuad* quad)
+{
+ if (!quad) {
+ m_lastPlanarSecondaryQuad = nullptr;
+ return;
+ }
+
+ // Map the quad back through any transform or offset back into the last flattening coordinate space.
+ FloatQuad backMappedQuad(*quad);
+ mapQuad(backMappedQuad, inverseDirection());
+ m_lastPlanarSecondaryQuad = std::make_unique<FloatQuad>(backMappedQuad);
+}
+
+void TransformState::mapQuad(FloatQuad& quad, TransformDirection direction, bool* wasClamped) const
+{
+ quad.move((direction == ApplyTransformDirection) ? m_accumulatedOffset : -m_accumulatedOffset);
if (!m_accumulatedTransform)
- return quad;
+ return;
- if (m_direction == ApplyTransformDirection)
- return m_accumulatedTransform->mapQuad(quad);
+ if (direction == ApplyTransformDirection)
+ quad = m_accumulatedTransform->mapQuad(quad);
- return m_accumulatedTransform->inverse().projectQuad(quad, wasClamped);
+ quad = m_accumulatedTransform->inverse().value_or(TransformationMatrix()).projectQuad(quad, wasClamped);
}
void TransformState::flattenWithTransform(const TransformationMatrix& t, bool* wasClamped)
@@ -188,14 +226,21 @@ void TransformState::flattenWithTransform(const TransformationMatrix& t, bool* w
if (m_direction == ApplyTransformDirection) {
if (m_mapPoint)
m_lastPlanarPoint = t.mapPoint(m_lastPlanarPoint);
- if (m_mapQuad)
+ if (m_mapQuad) {
m_lastPlanarQuad = t.mapQuad(m_lastPlanarQuad);
+ if (m_lastPlanarSecondaryQuad)
+ *m_lastPlanarSecondaryQuad = t.mapQuad(*m_lastPlanarSecondaryQuad);
+ }
+
} else {
- TransformationMatrix inverseTransform = t.inverse();
+ TransformationMatrix inverseTransform = t.inverse().value_or(TransformationMatrix());
if (m_mapPoint)
m_lastPlanarPoint = inverseTransform.projectPoint(m_lastPlanarPoint);
- if (m_mapQuad)
+ if (m_mapQuad) {
m_lastPlanarQuad = inverseTransform.projectQuad(m_lastPlanarQuad, wasClamped);
+ if (m_lastPlanarSecondaryQuad)
+ *m_lastPlanarSecondaryQuad = inverseTransform.projectQuad(*m_lastPlanarSecondaryQuad, wasClamped);
+ }
}
// We could throw away m_accumulatedTransform if we wanted to here, but that
diff --git a/Source/WebCore/platform/graphics/transforms/TransformState.h b/Source/WebCore/platform/graphics/transforms/TransformState.h
index 874177b5d..cd955abf4 100644
--- a/Source/WebCore/platform/graphics/transforms/TransformState.h
+++ b/Source/WebCore/platform/graphics/transforms/TransformState.h
@@ -10,10 +10,10 @@
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
- * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
@@ -32,7 +32,7 @@
#include "IntSize.h"
#include "LayoutSize.h"
#include "TransformationMatrix.h"
-#include <wtf/OwnPtr.h>
+#include <wtf/Optional.h>
namespace WebCore {
@@ -75,30 +75,45 @@ public:
void setQuad(const FloatQuad& quad)
{
- // FIXME: this assumes that the quad being added is in the coordinate system of the current state.
- // This breaks if we're simultaneously mapping a point. https://bugs.webkit.org/show_bug.cgi?id=106680
- ASSERT(!m_mapPoint);
- m_accumulatedOffset = LayoutSize();
+ // We must be in a flattened state (no accumulated offset) when setting this quad.
+ ASSERT(m_accumulatedOffset == LayoutSize());
m_lastPlanarQuad = quad;
}
+ // FIXME: webkit.org/b/144226 use std::optional<FloatQuad>.
+ void setSecondaryQuad(const FloatQuad* quad)
+ {
+ // We must be in a flattened state (no accumulated offset) when setting this secondary quad.
+ ASSERT(m_accumulatedOffset == LayoutSize());
+ if (quad)
+ m_lastPlanarSecondaryQuad = std::make_unique<FloatQuad>(*quad);
+ else
+ m_lastPlanarSecondaryQuad = nullptr;
+ }
+
+ // FIXME: webkit.org/b/144226 use std::optional<FloatQuad>.
+ void setLastPlanarSecondaryQuad(const FloatQuad*);
+
void move(LayoutUnit x, LayoutUnit y, TransformAccumulation accumulate = FlattenTransform)
{
move(LayoutSize(x, y), accumulate);
}
void move(const LayoutSize&, TransformAccumulation = FlattenTransform);
- void applyTransform(const AffineTransform& transformFromContainer, TransformAccumulation = FlattenTransform, bool* wasClamped = 0);
- void applyTransform(const TransformationMatrix& transformFromContainer, TransformAccumulation = FlattenTransform, bool* wasClamped = 0);
- void flatten(bool* wasClamped = 0);
+ void applyTransform(const AffineTransform& transformFromContainer, TransformAccumulation = FlattenTransform, bool* wasClamped = nullptr);
+ void applyTransform(const TransformationMatrix& transformFromContainer, TransformAccumulation = FlattenTransform, bool* wasClamped = nullptr);
+ void flatten(bool* wasClamped = nullptr);
// Return the coords of the point or quad in the last flattened layer
FloatPoint lastPlanarPoint() const { return m_lastPlanarPoint; }
FloatQuad lastPlanarQuad() const { return m_lastPlanarQuad; }
+ FloatQuad* lastPlanarSecondaryQuad() const { return m_lastPlanarSecondaryQuad.get(); }
+ bool isMappingSecondaryQuad() const { return m_lastPlanarSecondaryQuad.get(); }
// Return the point or quad mapped through the current transform
- FloatPoint mappedPoint(bool* wasClamped = 0) const;
- FloatQuad mappedQuad(bool* wasClamped = 0) const;
+ FloatPoint mappedPoint(bool* wasClamped = nullptr) const;
+ FloatQuad mappedQuad(bool* wasClamped = nullptr) const;
+ std::optional<FloatQuad> mappedSecondaryQuad(bool* wasClamped = nullptr) const;
private:
void translateTransform(const LayoutSize&);
@@ -106,17 +121,29 @@ private:
void flattenWithTransform(const TransformationMatrix&, bool* wasClamped);
void applyAccumulatedOffset();
+ TransformDirection direction() const { return m_direction; }
+ TransformDirection inverseDirection() const;
+
+ void mapQuad(FloatQuad&, TransformDirection, bool* clamped = nullptr) const;
+
FloatPoint m_lastPlanarPoint;
FloatQuad m_lastPlanarQuad;
+ std::unique_ptr<FloatQuad> m_lastPlanarSecondaryQuad; // Optional second quad to map.
// We only allocate the transform if we need to
- OwnPtr<TransformationMatrix> m_accumulatedTransform;
+ std::unique_ptr<TransformationMatrix> m_accumulatedTransform;
LayoutSize m_accumulatedOffset;
bool m_accumulatingTransform;
- bool m_mapPoint, m_mapQuad;
+ bool m_mapPoint;
+ bool m_mapQuad;
TransformDirection m_direction;
};
+inline TransformState::TransformDirection TransformState::inverseDirection() const
+{
+ return m_direction == ApplyTransformDirection ? UnapplyInverseTransformDirection : ApplyTransformDirection;
+}
+
} // namespace WebCore
#endif // TransformState_h
diff --git a/Source/WebCore/platform/graphics/transforms/TransformationMatrix.cpp b/Source/WebCore/platform/graphics/transforms/TransformationMatrix.cpp
index c5066668d..2427b01c1 100644
--- a/Source/WebCore/platform/graphics/transforms/TransformationMatrix.cpp
+++ b/Source/WebCore/platform/graphics/transforms/TransformationMatrix.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2005, 2006, 2013 Apple Computer, Inc. All rights reserved.
+ * Copyright (C) 2005, 2006, 2013 Apple Inc. All rights reserved.
* Copyright (C) 2009 Torch Mobile, Inc.
*
* Redistribution and use in source and binary forms, with or without
@@ -11,10 +11,10 @@
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
- * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
@@ -32,7 +32,7 @@
#include "FloatQuad.h"
#include "IntRect.h"
#include "LayoutRect.h"
-
+#include "TextStream.h"
#include <wtf/Assertions.h>
#include <wtf/MathExtras.h>
@@ -1445,7 +1445,7 @@ bool TransformationMatrix::isInvertible() const
return true;
}
-TransformationMatrix TransformationMatrix::inverse() const
+std::optional<TransformationMatrix> TransformationMatrix::inverse() const
{
if (isIdentityOrTranslation()) {
// identity matrix
@@ -1460,9 +1460,10 @@ TransformationMatrix TransformationMatrix::inverse() const
}
TransformationMatrix invMat;
- bool inverted = WebCore::inverse(m_matrix, invMat.m_matrix);
- if (!inverted)
- return TransformationMatrix();
+ // FIXME: Use LU decomposition to apply the inverse instead of calculating the inverse explicitly.
+ // Calculating the inverse of a 4x4 matrix using cofactors is numerically unstable and unnecessary to apply the inverse transformation to a point.
+ if (!WebCore::inverse(m_matrix, invMat.m_matrix))
+ return std::nullopt;
return invMat;
}
@@ -1500,8 +1501,11 @@ void TransformationMatrix::blend2(const TransformationMatrix& from, double progr
{
Decomposed2Type fromDecomp;
Decomposed2Type toDecomp;
- from.decompose2(fromDecomp);
- decompose2(toDecomp);
+ if (!from.decompose2(fromDecomp) || !decompose2(toDecomp)) {
+ if (progress < 0.5)
+ *this = from;
+ return;
+ }
// If x-axis of one is flipped, and y-axis of the other, convert to an unflipped rotation.
if ((fromDecomp.scaleX < 0 && toDecomp.scaleY < 0) || (fromDecomp.scaleY < 0 && toDecomp.scaleX < 0)) {
@@ -1540,8 +1544,11 @@ void TransformationMatrix::blend4(const TransformationMatrix& from, double progr
{
Decomposed4Type fromDecomp;
Decomposed4Type toDecomp;
- from.decompose4(fromDecomp);
- decompose4(toDecomp);
+ if (!from.decompose4(fromDecomp) || !decompose4(toDecomp)) {
+ if (progress < 0.5)
+ *this = from;
+ return;
+ }
blendFloat(fromDecomp.scaleX, toDecomp.scaleX, progress);
blendFloat(fromDecomp.scaleY, toDecomp.scaleY, progress);
@@ -1579,6 +1586,9 @@ bool TransformationMatrix::decompose2(Decomposed2Type& decomp) const
memset(&decomp, 0, sizeof(decomp));
decomp.scaleX = 1;
decomp.scaleY = 1;
+ decomp.m11 = 1;
+ decomp.m22 = 1;
+ return true;
}
return WebCore::decompose2(m_matrix, decomp);
@@ -1592,6 +1602,7 @@ bool TransformationMatrix::decompose4(Decomposed4Type& decomp) const
decomp.scaleX = 1;
decomp.scaleY = 1;
decomp.scaleZ = 1;
+ return true;
}
return WebCore::decompose4(m_matrix, decomp);
@@ -1738,4 +1749,20 @@ bool TransformationMatrix::isBackFaceVisible() const
return zComponentOfTransformedNormal < 0;
}
+TextStream& operator<<(TextStream& ts, const TransformationMatrix& transform)
+{
+ ts << "\n";
+ ts.increaseIndent();
+ ts.writeIndent();
+ ts << "[" << transform.m11() << " " << transform.m12() << " " << transform.m13() << " " << transform.m14() << "]\n";
+ ts.writeIndent();
+ ts << "[" << transform.m21() << " " << transform.m22() << " " << transform.m23() << " " << transform.m24() << "]\n";
+ ts.writeIndent();
+ ts << "[" << transform.m31() << " " << transform.m32() << " " << transform.m33() << " " << transform.m34() << "]\n";
+ ts.writeIndent();
+ ts << "[" << transform.m41() << " " << transform.m42() << " " << transform.m43() << " " << transform.m44() << "]";
+ ts.decreaseIndent();
+ return ts;
+}
+
}
diff --git a/Source/WebCore/platform/graphics/transforms/TransformationMatrix.h b/Source/WebCore/platform/graphics/transforms/TransformationMatrix.h
index 011cd87b5..5db0c002a 100644
--- a/Source/WebCore/platform/graphics/transforms/TransformationMatrix.h
+++ b/Source/WebCore/platform/graphics/transforms/TransformationMatrix.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2005, 2006 Apple Computer, Inc. All rights reserved.
+ * Copyright (C) 2005-2016 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -10,10 +10,10 @@
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
- * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
@@ -31,6 +31,7 @@
#include "IntPoint.h"
#include <string.h> //for memcpy
#include <wtf/FastMalloc.h>
+#include <wtf/Optional.h>
#if USE(CA)
typedef struct CATransform3D CATransform3D;
@@ -49,6 +50,11 @@ typedef struct tagXFORM XFORM;
#endif
#endif
+#if PLATFORM(WIN)
+struct D2D_MATRIX_3X2_F;
+typedef D2D_MATRIX_3X2_F D2D1_MATRIX_3X2_F;
+#endif
+
namespace WebCore {
class AffineTransform;
@@ -56,6 +62,7 @@ class IntRect;
class LayoutRect;
class FloatRect;
class FloatQuad;
+class TextStream;
#if CPU(X86_64)
#define TRANSFORMATION_MATRIX_USE_X86_64_SSE2
@@ -65,7 +72,7 @@ class TransformationMatrix {
WTF_MAKE_FAST_ALLOCATED;
public:
-#if CPU(APPLE_ARMV7S) || defined(TRANSFORMATION_MATRIX_USE_X86_64_SSE2)
+#if (PLATFORM(IOS) && CPU(ARM_THUMB2)) || defined(TRANSFORMATION_MATRIX_USE_X86_64_SSE2)
#if COMPILER(MSVC)
__declspec(align(16)) typedef double Matrix4[4][4];
#else
@@ -76,7 +83,7 @@ public:
#endif
TransformationMatrix() { makeIdentity(); }
- TransformationMatrix(const AffineTransform& t);
+ WEBCORE_EXPORT TransformationMatrix(const AffineTransform&);
TransformationMatrix(const TransformationMatrix& t) { *this = t; }
TransformationMatrix(double a, double b, double c, double d, double e, double f) { setMatrix(a, b, c, d, e, f); }
TransformationMatrix(double m11, double m12, double m13, double m14,
@@ -126,15 +133,15 @@ public:
m_matrix[3][0] == 0 && m_matrix[3][1] == 0 && m_matrix[3][2] == 0 && m_matrix[3][3] == 1;
}
- // This form preserves the double math from input to output
+ // This form preserves the double math from input to output.
void map(double x, double y, double& x2, double& y2) const { multVecMatrix(x, y, x2, y2); }
- // Map a 3D point through the transform, returning a 3D point.
+ // Maps a 3D point through the transform, returning a 3D point.
FloatPoint3D mapPoint(const FloatPoint3D&) const;
- // Map a 2D point through the transform, returning a 2D point.
+ // Maps a 2D point through the transform, returning a 2D point.
// Note that this ignores the z component, effectively projecting the point into the z=0 plane.
- FloatPoint mapPoint(const FloatPoint&) const;
+ WEBCORE_EXPORT FloatPoint mapPoint(const FloatPoint&) const;
// Like the version above, except that it rounds the mapped point to the nearest integer value.
IntPoint mapPoint(const IntPoint& p) const
@@ -143,25 +150,23 @@ public:
}
// If the matrix has 3D components, the z component of the result is
- // dropped, effectively projecting the rect into the z=0 plane
- FloatRect mapRect(const FloatRect&) const;
+ // dropped, effectively projecting the rect into the z=0 plane.
+ WEBCORE_EXPORT FloatRect mapRect(const FloatRect&) const;
// Rounds the resulting mapped rectangle out. This is helpful for bounding
// box computations but may not be what is wanted in other contexts.
- IntRect mapRect(const IntRect&) const;
+ WEBCORE_EXPORT IntRect mapRect(const IntRect&) const;
LayoutRect mapRect(const LayoutRect&) const;
// If the matrix has 3D components, the z component of the result is
- // dropped, effectively projecting the quad into the z=0 plane
- FloatQuad mapQuad(const FloatQuad&) const;
-
- // Map a point on the z=0 plane into a point on
- // the plane with with the transform applied, by extending
- // a ray perpendicular to the source plane and computing
- // the local x,y position of the point where that ray intersects
- // with the destination plane.
+ // dropped, effectively projecting the quad into the z=0 plane.
+ WEBCORE_EXPORT FloatQuad mapQuad(const FloatQuad&) const;
+
+ // Maps a point on the z=0 plane into a point on the plane with with the transform applied, by
+ // extending a ray perpendicular to the source plane and computing the local x,y position of
+ // the point where that ray intersects with the destination plane.
FloatPoint projectPoint(const FloatPoint&, bool* clamped = 0) const;
- // Projects the four corners of the quad
+ // Projects the four corners of the quad.
FloatQuad projectQuad(const FloatQuad&, bool* clamped = 0) const;
// Projects the four corners of the quad and takes a bounding box,
// while sanitizing values created when the w component is negative.
@@ -219,61 +224,74 @@ public:
void setF(double f) { m_matrix[3][1] = f; }
// this = mat * this.
- TransformationMatrix& multiply(const TransformationMatrix&);
+ WEBCORE_EXPORT TransformationMatrix& multiply(const TransformationMatrix&);
- TransformationMatrix& scale(double);
- TransformationMatrix& scaleNonUniform(double sx, double sy);
+ WEBCORE_EXPORT TransformationMatrix& scale(double);
+ WEBCORE_EXPORT TransformationMatrix& scaleNonUniform(double sx, double sy);
TransformationMatrix& scale3d(double sx, double sy, double sz);
// Angle is in degrees.
TransformationMatrix& rotate(double d) { return rotate3d(0, 0, d); }
TransformationMatrix& rotateFromVector(double x, double y);
- TransformationMatrix& rotate3d(double rx, double ry, double rz);
+ WEBCORE_EXPORT TransformationMatrix& rotate3d(double rx, double ry, double rz);
- // The vector (x,y,z) is normalized if it's not already. A vector of
- // (0,0,0) uses a vector of (0,0,1).
+ // The vector (x,y,z) is normalized if it's not already. A vector of (0,0,0) uses a vector of (0,0,1).
TransformationMatrix& rotate3d(double x, double y, double z, double angle);
- TransformationMatrix& translate(double tx, double ty);
+ WEBCORE_EXPORT TransformationMatrix& translate(double tx, double ty);
TransformationMatrix& translate3d(double tx, double ty, double tz);
// translation added with a post-multiply
TransformationMatrix& translateRight(double tx, double ty);
TransformationMatrix& translateRight3d(double tx, double ty, double tz);
- TransformationMatrix& flipX();
- TransformationMatrix& flipY();
- TransformationMatrix& skew(double angleX, double angleY);
+ WEBCORE_EXPORT TransformationMatrix& flipX();
+ WEBCORE_EXPORT TransformationMatrix& flipY();
+ WEBCORE_EXPORT TransformationMatrix& skew(double angleX, double angleY);
TransformationMatrix& skewX(double angle) { return skew(angle, 0); }
TransformationMatrix& skewY(double angle) { return skew(0, angle); }
TransformationMatrix& applyPerspective(double p);
bool hasPerspective() const { return m_matrix[2][3] != 0.0f; }
- // returns a transformation that maps a rect to a rect
- static TransformationMatrix rectToRect(const FloatRect&, const FloatRect&);
+ // Returns a transformation that maps a rect to a rect.
+ WEBCORE_EXPORT static TransformationMatrix rectToRect(const FloatRect&, const FloatRect&);
- bool isInvertible() const;
+ bool isInvertible() const; // If you call this this, you're probably doing it wrong.
+ WEBCORE_EXPORT std::optional<TransformationMatrix> inverse() const;
- // This method returns the identity matrix if it is not invertible.
- // Use isInvertible() before calling this if you need to know.
- TransformationMatrix inverse() const;
-
- // decompose the matrix into its component parts
- typedef struct {
+ // Decompose the matrix into its component parts.
+ struct Decomposed2Type {
double scaleX, scaleY;
double translateX, translateY;
double angle;
double m11, m12, m21, m22;
- } Decomposed2Type;
-
- typedef struct {
+
+ bool operator==(const Decomposed2Type& other) const
+ {
+ return scaleX == other.scaleX && scaleY == other.scaleY
+ && translateX == other.translateX && translateY == other.translateY
+ && angle == other.angle
+ && m11 == other.m11 && m12 == other.m12 && m21 == other.m21 && m22 == other.m22;
+ }
+ };
+
+ struct Decomposed4Type {
double scaleX, scaleY, scaleZ;
double skewXY, skewXZ, skewYZ;
double quaternionX, quaternionY, quaternionZ, quaternionW;
double translateX, translateY, translateZ;
double perspectiveX, perspectiveY, perspectiveZ, perspectiveW;
- } Decomposed4Type;
+
+ bool operator==(const Decomposed4Type& other) const
+ {
+ return scaleX == other.scaleX && scaleY == other.scaleY && scaleZ == other.scaleZ
+ && skewXY == other.skewXY && skewXZ == other.skewXZ && skewYZ == other.skewYZ
+ && quaternionX == other.quaternionX && quaternionY == other.quaternionY && quaternionZ == other.quaternionZ && quaternionW == other.quaternionW
+ && translateX == other.translateX && translateY == other.translateY && translateZ == other.translateZ
+ && perspectiveX == other.perspectiveX && perspectiveY == other.perspectiveY && perspectiveZ == other.perspectiveZ && perspectiveW == other.perspectiveW;
+ }
+ };
bool decompose2(Decomposed2Type&) const;
void recompose2(const Decomposed2Type&);
@@ -281,9 +299,9 @@ public:
bool decompose4(Decomposed4Type&) const;
void recompose4(const Decomposed4Type&);
- void blend(const TransformationMatrix& from, double progress);
- void blend2(const TransformationMatrix& from, double progress);
- void blend4(const TransformationMatrix& from, double progress);
+ WEBCORE_EXPORT void blend(const TransformationMatrix& from, double progress);
+ WEBCORE_EXPORT void blend2(const TransformationMatrix& from, double progress);
+ WEBCORE_EXPORT void blend4(const TransformationMatrix& from, double progress);
bool isAffine() const
{
@@ -291,10 +309,10 @@ public:
m31() == 0 && m32() == 0 && m33() == 1 && m34() == 0 && m43() == 0 && m44() == 1);
}
- // Throw away the non-affine parts of the matrix (lossy!)
- void makeAffine();
+ // Throw away the non-affine parts of the matrix (lossy!).
+ WEBCORE_EXPORT void makeAffine();
- AffineTransform toAffineTransform() const;
+ WEBCORE_EXPORT AffineTransform toAffineTransform() const;
bool operator==(const TransformationMatrix& m2) const
{
@@ -333,12 +351,12 @@ public:
}
#if USE(CA)
- TransformationMatrix(const CATransform3D&);
- operator CATransform3D() const;
+ WEBCORE_EXPORT TransformationMatrix(const CATransform3D&);
+ WEBCORE_EXPORT operator CATransform3D() const;
#endif
#if USE(CG)
- TransformationMatrix(const CGAffineTransform&);
- operator CGAffineTransform() const;
+ WEBCORE_EXPORT TransformationMatrix(const CGAffineTransform&);
+ WEBCORE_EXPORT operator CGAffineTransform() const;
#elif USE(CAIRO)
operator cairo_matrix_t() const;
#endif
@@ -347,6 +365,11 @@ public:
operator XFORM() const;
#endif
+#if PLATFORM(WIN)
+ TransformationMatrix(const D2D1_MATRIX_3X2_F&);
+ operator D2D1_MATRIX_3X2_F() const;
+#endif
+
bool isIdentityOrTranslation() const
{
return m_matrix[0][0] == 1 && m_matrix[0][1] == 0 && m_matrix[0][2] == 0 && m_matrix[0][3] == 0
@@ -357,7 +380,7 @@ public:
bool isIntegerTranslation() const;
- // This method returns the matrix without 3D components.
+ // Returns the matrix without 3D components.
TransformationMatrix to2dTransform() const;
typedef float FloatMatrix4[16];
@@ -380,7 +403,6 @@ private:
return FloatPoint(static_cast<float>(resultX), static_cast<float>(resultY));
}
- // multiply passed 3D point by matrix
void multVecMatrix(double x, double y, double z, double& dstX, double& dstY, double& dstZ) const;
FloatPoint3D internalMapPoint(const FloatPoint3D& sourcePoint) const
{
@@ -400,6 +422,8 @@ private:
Matrix4 m_matrix;
};
+WEBCORE_EXPORT TextStream& operator<<(TextStream&, const TransformationMatrix&);
+
} // namespace WebCore
#endif // TransformationMatrix_h
diff --git a/Source/WebCore/platform/graphics/transforms/TranslateTransformOperation.cpp b/Source/WebCore/platform/graphics/transforms/TranslateTransformOperation.cpp
index f140c5081..02880849e 100644
--- a/Source/WebCore/platform/graphics/transforms/TranslateTransformOperation.cpp
+++ b/Source/WebCore/platform/graphics/transforms/TranslateTransformOperation.cpp
@@ -21,24 +21,39 @@
#include "config.h"
#include "TranslateTransformOperation.h"
+
#include "FloatConversion.h"
+#include "TextStream.h"
namespace WebCore {
-PassRefPtr<TransformOperation> TranslateTransformOperation::blend(const TransformOperation* from, double progress, bool blendToIdentity)
+bool TranslateTransformOperation::operator==(const TransformOperation& other) const
+{
+ if (!isSameType(other))
+ return false;
+ const TranslateTransformOperation& t = downcast<TranslateTransformOperation>(other);
+ return m_x == t.m_x && m_y == t.m_y && m_z == t.m_z;
+}
+
+Ref<TransformOperation> TranslateTransformOperation::blend(const TransformOperation* from, double progress, bool blendToIdentity)
{
if (from && !from->isSameType(*this))
- return this;
+ return *this;
Length zeroLength(0, Fixed);
if (blendToIdentity)
- return TranslateTransformOperation::create(zeroLength.blend(m_x, progress), zeroLength.blend(m_y, progress), zeroLength.blend(m_z, progress), m_type);
+ return TranslateTransformOperation::create(WebCore::blend(m_x, zeroLength, progress), WebCore::blend(m_y, zeroLength, progress), WebCore::blend(m_z, zeroLength, progress), m_type);
- const TranslateTransformOperation* fromOp = static_cast<const TranslateTransformOperation*>(from);
+ const TranslateTransformOperation* fromOp = downcast<TranslateTransformOperation>(from);
Length fromX = fromOp ? fromOp->m_x : zeroLength;
Length fromY = fromOp ? fromOp->m_y : zeroLength;
Length fromZ = fromOp ? fromOp->m_z : zeroLength;
- return TranslateTransformOperation::create(m_x.blend(fromX, progress), m_y.blend(fromY, progress), m_z.blend(fromZ, progress), m_type);
+ return TranslateTransformOperation::create(WebCore::blend(fromX, x(), progress), WebCore::blend(fromY, y(), progress), WebCore::blend(fromZ, z(), progress), m_type);
+}
+
+void TranslateTransformOperation::dump(TextStream& ts) const
+{
+ ts << type() << "(" << m_x << ", " << m_y << ", " << m_z << ")";
}
} // namespace WebCore
diff --git a/Source/WebCore/platform/graphics/transforms/TranslateTransformOperation.h b/Source/WebCore/platform/graphics/transforms/TranslateTransformOperation.h
index 63cf587b2..e9f687541 100644
--- a/Source/WebCore/platform/graphics/transforms/TranslateTransformOperation.h
+++ b/Source/WebCore/platform/graphics/transforms/TranslateTransformOperation.h
@@ -28,19 +28,25 @@
#include "Length.h"
#include "LengthFunctions.h"
#include "TransformOperation.h"
+#include <wtf/Ref.h>
namespace WebCore {
-class TranslateTransformOperation : public TransformOperation {
+class TranslateTransformOperation final : public TransformOperation {
public:
- static PassRefPtr<TranslateTransformOperation> create(const Length& tx, const Length& ty, OperationType type)
+ static Ref<TranslateTransformOperation> create(const Length& tx, const Length& ty, OperationType type)
{
- return adoptRef(new TranslateTransformOperation(tx, ty, Length(0, Fixed), type));
+ return adoptRef(*new TranslateTransformOperation(tx, ty, Length(0, Fixed), type));
}
- static PassRefPtr<TranslateTransformOperation> create(const Length& tx, const Length& ty, const Length& tz, OperationType type)
+ static Ref<TranslateTransformOperation> create(const Length& tx, const Length& ty, const Length& tz, OperationType type)
{
- return adoptRef(new TranslateTransformOperation(tx, ty, tz, type));
+ return adoptRef(*new TranslateTransformOperation(tx, ty, tz, type));
+ }
+
+ Ref<TransformOperation> clone() const override
+ {
+ return adoptRef(*new TranslateTransformOperation(m_x, m_y, m_z, m_type));
}
double x(const FloatSize& borderBoxSize) const { return floatValueForLength(m_x, borderBoxSize.width()); }
@@ -52,26 +58,22 @@ public:
Length z() const { return m_z; }
private:
- virtual bool isIdentity() const { return !floatValueForLength(m_x, 1) && !floatValueForLength(m_y, 1) && !floatValueForLength(m_z, 1); }
+ bool isIdentity() const override { return !floatValueForLength(m_x, 1) && !floatValueForLength(m_y, 1) && !floatValueForLength(m_z, 1); }
- virtual OperationType type() const { return m_type; }
- virtual bool isSameType(const TransformOperation& o) const { return o.type() == m_type; }
+ OperationType type() const override { return m_type; }
+ bool isSameType(const TransformOperation& o) const override { return o.type() == m_type; }
- virtual bool operator==(const TransformOperation& o) const
- {
- if (!isSameType(o))
- return false;
- const TranslateTransformOperation* t = static_cast<const TranslateTransformOperation*>(&o);
- return m_x == t->m_x && m_y == t->m_y && m_z == t->m_z;
- }
+ bool operator==(const TransformOperation&) const override;
- virtual bool apply(TransformationMatrix& transform, const FloatSize& borderBoxSize) const
+ bool apply(TransformationMatrix& transform, const FloatSize& borderBoxSize) const override
{
transform.translate3d(x(borderBoxSize), y(borderBoxSize), z(borderBoxSize));
- return m_x.type() == Percent || m_y.type() == Percent;
+ return m_x.isPercent() || m_y.isPercent();
}
- virtual PassRefPtr<TransformOperation> blend(const TransformOperation* from, double progress, bool blendToIdentity = false);
+ Ref<TransformOperation> blend(const TransformOperation* from, double progress, bool blendToIdentity = false) override;
+
+ void dump(TextStream&) const final;
TranslateTransformOperation(const Length& tx, const Length& ty, const Length& tz, OperationType type)
: m_x(tx)
@@ -79,7 +81,7 @@ private:
, m_z(tz)
, m_type(type)
{
- ASSERT(type == TRANSLATE_X || type == TRANSLATE_Y || type == TRANSLATE_Z || type == TRANSLATE || type == TRANSLATE_3D);
+ ASSERT(isTranslateTransformOperationType());
}
Length m_x;
@@ -90,4 +92,6 @@ private:
} // namespace WebCore
+SPECIALIZE_TYPE_TRAITS_TRANSFORMOPERATION(WebCore::TranslateTransformOperation, isTranslateTransformOperationType())
+
#endif // TranslateTransformOperation_h
diff --git a/Source/WebCore/platform/graphics/wayland/PlatformDisplayWayland.cpp b/Source/WebCore/platform/graphics/wayland/PlatformDisplayWayland.cpp
new file mode 100644
index 000000000..42291396f
--- /dev/null
+++ b/Source/WebCore/platform/graphics/wayland/PlatformDisplayWayland.cpp
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2014 Igalia S.L.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "PlatformDisplayWayland.h"
+
+#if PLATFORM(WAYLAND)
+
+#include "GLContextEGL.h"
+#include <cstring>
+// These includes need to be in this order because wayland-egl.h defines WL_EGL_PLATFORM
+// and egl.h checks that to decide whether it's Wayland platform.
+#include <wayland-egl.h>
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
+#include <wtf/Assertions.h>
+
+namespace WebCore {
+
+const struct wl_registry_listener PlatformDisplayWayland::s_registryListener = {
+ // globalCallback
+ [](void* data, struct wl_registry*, uint32_t name, const char* interface, uint32_t) {
+ static_cast<PlatformDisplayWayland*>(data)->registryGlobal(interface, name);
+ },
+ // globalRemoveCallback
+ [](void*, struct wl_registry*, uint32_t)
+ {
+ }
+};
+
+std::unique_ptr<PlatformDisplay> PlatformDisplayWayland::create()
+{
+ struct wl_display* display = wl_display_connect(getenv("DISPLAY"));
+ if (!display)
+ return nullptr;
+
+ return std::make_unique<PlatformDisplayWayland>(display, NativeDisplayOwned::Yes);
+}
+
+PlatformDisplayWayland::PlatformDisplayWayland(struct wl_display* display, NativeDisplayOwned displayOwned)
+ : PlatformDisplay(displayOwned)
+{
+ initialize(display);
+}
+
+PlatformDisplayWayland::~PlatformDisplayWayland()
+{
+ if (m_nativeDisplayOwned == NativeDisplayOwned::Yes)
+ wl_display_destroy(m_display);
+}
+
+void PlatformDisplayWayland::initialize(wl_display* display)
+{
+ m_display = display;
+ if (!m_display)
+ return;
+
+ m_registry.reset(wl_display_get_registry(m_display));
+ wl_registry_add_listener(m_registry.get(), &s_registryListener, this);
+ wl_display_roundtrip(m_display);
+
+#if USE(EGL)
+#if defined(EGL_KHR_platform_wayland)
+ const char* extensions = eglQueryString(nullptr, EGL_EXTENSIONS);
+ if (GLContext::isExtensionSupported(extensions, "EGL_KHR_platform_base")) {
+ if (auto* getPlatformDisplay = reinterpret_cast<PFNEGLGETPLATFORMDISPLAYEXTPROC>(eglGetProcAddress("eglGetPlatformDisplay")))
+ m_eglDisplay = getPlatformDisplay(EGL_PLATFORM_WAYLAND_KHR, m_display, nullptr);
+ } else if (GLContext::isExtensionSupported(extensions, "EGL_EXT_platform_base")) {
+ if (auto* getPlatformDisplay = reinterpret_cast<PFNEGLGETPLATFORMDISPLAYEXTPROC>(eglGetProcAddress("eglGetPlatformDisplayEXT")))
+ m_eglDisplay = getPlatformDisplay(EGL_PLATFORM_WAYLAND_KHR, m_display, nullptr);
+ } else
+#endif
+ m_eglDisplay = eglGetDisplay(m_display);
+
+ PlatformDisplay::initializeEGLDisplay();
+#endif
+}
+
+void PlatformDisplayWayland::registryGlobal(const char* interface, uint32_t name)
+{
+ if (!std::strcmp(interface, "wl_compositor"))
+ m_compositor.reset(static_cast<struct wl_compositor*>(wl_registry_bind(m_registry.get(), name, &wl_compositor_interface, 1)));
+}
+
+WlUniquePtr<struct wl_surface> PlatformDisplayWayland::createSurface() const
+{
+ if (!m_compositor)
+ return nullptr;
+
+ return WlUniquePtr<struct wl_surface>(wl_compositor_create_surface(m_compositor.get()));
+}
+
+} // namespace WebCore
+
+#endif // PLATFORM(WAYLAND)
diff --git a/Source/WebCore/platform/graphics/FontData.h b/Source/WebCore/platform/graphics/wayland/PlatformDisplayWayland.h
index 22154da9f..e17dce04a 100644
--- a/Source/WebCore/platform/graphics/FontData.h
+++ b/Source/WebCore/platform/graphics/wayland/PlatformDisplayWayland.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2008 Apple Inc. All rights reserved.
+ * Copyright (C) 2014 Igalia S.L.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -20,50 +20,50 @@
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef FontData_h
-#define FontData_h
+#ifndef PlatformDisplayWayland_h
+#define PlatformDisplayWayland_h
-#include <wtf/FastMalloc.h>
-#include <wtf/Forward.h>
-#include <wtf/Noncopyable.h>
-#include <wtf/PassRefPtr.h>
-#include <wtf/RefCounted.h>
-#include <wtf/unicode/Unicode.h>
+#if PLATFORM(WAYLAND)
-namespace WebCore {
+#include "PlatformDisplay.h"
+#include "WlUniquePtr.h"
+#include <wayland-client.h>
-class SimpleFontData;
+namespace WebCore {
-class FontData : public RefCounted<FontData> {
- WTF_MAKE_NONCOPYABLE(FontData); WTF_MAKE_FAST_ALLOCATED;
+class PlatformDisplayWayland : public PlatformDisplay {
public:
- FontData()
- : m_maxGlyphPageTreeLevel(0)
- {
- }
+ static std::unique_ptr<PlatformDisplay> create();
+ PlatformDisplayWayland(struct wl_display*, NativeDisplayOwned = NativeDisplayOwned::No);
+ virtual ~PlatformDisplayWayland();
- virtual ~FontData();
+ struct wl_display* native() const { return m_display; }
- virtual const SimpleFontData* fontDataForCharacter(UChar32) const = 0;
- virtual bool containsCharacters(const UChar*, int length) const = 0;
- virtual bool isCustomFont() const = 0;
- virtual bool isLoading() const = 0;
- virtual bool isSegmented() const = 0;
+ WlUniquePtr<struct wl_surface> createSurface() const;
- void setMaxGlyphPageTreeLevel(unsigned level) const { m_maxGlyphPageTreeLevel = level; }
- unsigned maxGlyphPageTreeLevel() const { return m_maxGlyphPageTreeLevel; }
+private:
+ static const struct wl_registry_listener s_registryListener;
-#ifndef NDEBUG
- virtual String description() const = 0;
-#endif
+ Type type() const override { return PlatformDisplay::Type::Wayland; }
-private:
- mutable unsigned m_maxGlyphPageTreeLevel;
+protected:
+ PlatformDisplayWayland() = default;
+ void initialize(struct wl_display*);
+
+ virtual void registryGlobal(const char* interface, uint32_t name);
+
+ struct wl_display* m_display;
+ WlUniquePtr<struct wl_registry> m_registry;
+ WlUniquePtr<struct wl_compositor> m_compositor;
};
} // namespace WebCore
-#endif // FontData_h
+SPECIALIZE_TYPE_TRAITS_PLATFORM_DISPLAY(PlatformDisplayWayland, Wayland)
+
+#endif // PLATFORM(WAYLAND)
+
+#endif // PlatformDisplayWayland_h
diff --git a/Source/WebCore/platform/graphics/FontRenderingMode.h b/Source/WebCore/platform/graphics/wayland/WlUniquePtr.h
index c1ce49705..03d2dab59 100644
--- a/Source/WebCore/platform/graphics/FontRenderingMode.h
+++ b/Source/WebCore/platform/graphics/wayland/WlUniquePtr.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2008 Apple Inc. All rights reserved.
+ * Copyright (C) 2016 Igalia S.L.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -20,18 +20,42 @@
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef FontRenderingMode_h
-#define FontRenderingMode_h
+#pragma once
+
+#if PLATFORM(WAYLAND)
+#include <wayland-client-protocol.h>
+#include <wayland-server.h>
namespace WebCore {
-// This setting is used to provide ways of switching between multiple rendering modes that may have different
-// metrics. It is used to switch between CG and GDI text on Windows.
-enum FontRenderingMode { NormalRenderingMode, AlternateRenderingMode };
+template<typename T>
+struct WlPtrDeleter {
+};
+
+template<typename T>
+using WlUniquePtr = std::unique_ptr<T, WlPtrDeleter<T>>;
+
+#define FOR_EACH_WAYLAND_DELETER(macro) \
+ macro(struct wl_display, wl_display_destroy) \
+ macro(struct wl_global, wl_global_destroy) \
+ macro(struct wl_surface, wl_surface_destroy) \
+ macro(struct wl_compositor, wl_compositor_destroy) \
+ macro(struct wl_registry, wl_registry_destroy) \
+ macro(struct wl_proxy, wl_proxy_destroy)
+
+#define DEFINE_WAYLAND_DELETER(typeName, deleterFunc) \
+ template<> struct WlPtrDeleter<typeName> \
+ { \
+ void operator() (typeName* ptr) const { deleterFunc(ptr); } \
+ };
+
+FOR_EACH_WAYLAND_DELETER(DEFINE_WAYLAND_DELETER)
+#undef FOR_EACH_WAYLAND_DELETER
+#undef DEFINE_WAYLAND_DELETER
} // namespace WebCore
-#endif // FontRenderingMode_h
+#endif // PLATFORM(WAYLAND)
diff --git a/Source/WebCore/platform/graphics/win/DIBPixelData.cpp b/Source/WebCore/platform/graphics/win/DIBPixelData.cpp
deleted file mode 100644
index e9a0f9b15..000000000
--- a/Source/WebCore/platform/graphics/win/DIBPixelData.cpp
+++ /dev/null
@@ -1,124 +0,0 @@
-/*
- * Copyright (C) 2011 Brent Fulgham <bfulgham@webkit.org>
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
- * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
- * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#include "config.h"
-#include "DIBPixelData.h"
-
-namespace WebCore {
-
-static const WORD bitmapType = 0x4d42; // BMP format
-static const WORD bitmapPixelsPerMeter = 2834; // 72 dpi
-
-DIBPixelData::DIBPixelData(HBITMAP bitmap)
-{
- initialize(bitmap);
-}
-
-void DIBPixelData::initialize(HBITMAP bitmap)
-{
- BITMAP bmpInfo;
- GetObject(bitmap, sizeof(bmpInfo), &bmpInfo);
-
- m_bitmapBuffer = reinterpret_cast<UInt8*>(bmpInfo.bmBits);
- m_bitmapBufferLength = bmpInfo.bmWidthBytes * bmpInfo.bmHeight;
- m_size = IntSize(bmpInfo.bmWidth, bmpInfo.bmHeight);
- m_bytesPerRow = bmpInfo.bmWidthBytes;
- m_bitsPerPixel = bmpInfo.bmBitsPixel;
-}
-
-#ifndef NDEBUG
-void DIBPixelData::writeToFile(LPCWSTR filePath)
-{
- HANDLE hFile = ::CreateFile(filePath, GENERIC_WRITE, 0, 0, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
- if (INVALID_HANDLE_VALUE == hFile)
- return;
-
- BITMAPFILEHEADER header;
- header.bfType = bitmapType;
- header.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);
- header.bfReserved1 = 0;
- header.bfReserved2 = 0;
- header.bfSize = sizeof(BITMAPFILEHEADER);
-
- BITMAPINFOHEADER info;
- info.biSize = sizeof(BITMAPINFOHEADER);
- info.biWidth = m_size.width();
- info.biHeight = m_size.height();
- info.biPlanes = 1;
- info.biBitCount = m_bitsPerPixel;
- info.biCompression = BI_RGB;
- info.biSizeImage = bufferLength();
- info.biXPelsPerMeter = bitmapPixelsPerMeter;
- info.biYPelsPerMeter = bitmapPixelsPerMeter;
- info.biClrUsed = 0;
- info.biClrImportant = 0;
-
- DWORD bytesWritten = 0;
- ::WriteFile(hFile, &header, sizeof(header), &bytesWritten, 0);
- ::WriteFile(hFile, &info, sizeof(info), &bytesWritten, 0);
- ::WriteFile(hFile, buffer(), bufferLength(), &bytesWritten, 0);
-
- ::CloseHandle(hFile);
-}
-#endif
-
-void DIBPixelData::setRGBABitmapAlpha(HDC hdc, const IntRect& dstRect, unsigned char level)
-{
- HBITMAP bitmap = static_cast<HBITMAP>(GetCurrentObject(hdc, OBJ_BITMAP));
- DIBPixelData pixelData(bitmap);
- ASSERT(pixelData.bitsPerPixel() == 32);
-
- IntRect drawRect(dstRect);
-#if !OS(WINCE)
- XFORM trans;
- GetWorldTransform(hdc, &trans);
- IntSize transformedPosition(trans.eDx, trans.eDy);
- drawRect.move(transformedPosition);
-#endif
-
- int pixelDataWidth = pixelData.size().width();
- int pixelDataHeight = pixelData.size().height();
- IntRect bitmapRect(0, 0, pixelDataWidth, pixelDataHeight);
- drawRect.intersect(bitmapRect);
- if (drawRect.isEmpty())
- return;
-
- RGBQUAD* bytes = reinterpret_cast<RGBQUAD*>(pixelData.buffer());
- bytes += drawRect.y() * pixelDataWidth;
-
- size_t width = drawRect.width();
- size_t height = drawRect.height();
- int x = drawRect.x();
- for (size_t i = 0; i < height; i++) {
- RGBQUAD* p = bytes + x;
- for (size_t j = 0; j < width; j++) {
- p->rgbReserved = level;
- p++;
- }
- bytes += pixelDataWidth;
- }
-}
-
-} // namespace WebCore
diff --git a/Source/WebCore/platform/graphics/win/GraphicsContextCairoWin.cpp b/Source/WebCore/platform/graphics/win/GraphicsContextCairoWin.cpp
deleted file mode 100644
index 3fb2ac033..000000000
--- a/Source/WebCore/platform/graphics/win/GraphicsContextCairoWin.cpp
+++ /dev/null
@@ -1,183 +0,0 @@
-/*
- * Copyright (C) 2008, 2013 Apple Inc. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
- * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
- * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#include "config.h"
-#include "GraphicsContext.h"
-
-#include "AffineTransform.h"
-#include "DIBPixelData.h"
-#include "Path.h"
-
-#include <cairo-win32.h>
-#include "GraphicsContextPlatformPrivateCairo.h"
-#include <wtf/win/GDIObject.h>
-
-using namespace std;
-
-namespace WebCore {
-
-#if PLATFORM(WIN)
-static cairo_t* createCairoContextWithHDC(HDC hdc, bool hasAlpha)
-{
- // Put the HDC In advanced mode so it will honor affine transforms.
- SetGraphicsMode(hdc, GM_ADVANCED);
-
- cairo_surface_t* surface = 0;
-
- HBITMAP bitmap = static_cast<HBITMAP>(GetCurrentObject(hdc, OBJ_BITMAP));
-
- BITMAP info;
- if (!GetObject(bitmap, sizeof(info), &info))
- surface = cairo_win32_surface_create(hdc);
- else {
- ASSERT(info.bmBitsPixel == 32);
-
- surface = cairo_image_surface_create_for_data((unsigned char*)info.bmBits,
- CAIRO_FORMAT_ARGB32,
- info.bmWidth,
- info.bmHeight,
- info.bmWidthBytes);
- }
-
- cairo_t* context = cairo_create(surface);
- cairo_surface_destroy(surface);
-
- return context;
-}
-
-GraphicsContext::GraphicsContext(HDC dc, bool hasAlpha)
- : m_updatingControlTints(false),
- m_transparencyCount(0)
-{
- platformInit(dc, hasAlpha);
-}
-
-void GraphicsContext::platformInit(HDC dc, bool hasAlpha)
-{
- cairo_t* cr = 0;
- if (dc)
- cr = createCairoContextWithHDC(dc, hasAlpha);
- else
- setPaintingDisabled(true);
-
- m_data = new GraphicsContextPlatformPrivateToplevel(new PlatformContextCairo(cr));
- m_data->m_hdc = dc;
- if (platformContext()->cr()) {
- // Make sure the context starts in sync with our state.
- setPlatformFillColor(fillColor(), fillColorSpace());
- setPlatformStrokeColor(strokeColor(), strokeColorSpace());
- }
- if (cr)
- cairo_destroy(cr);
-}
-#endif
-
-static void setRGBABitmapAlpha(unsigned char* bytes, size_t length, unsigned char level)
-{
- for (size_t i = 0; i < length; i += 4)
- bytes[i + 3] = level;
-}
-
-static void drawBitmapToContext(GraphicsContextPlatformPrivate* context, cairo_t* cr, const DIBPixelData& pixelData, const IntSize& translate)
-{
- // Need to make a cairo_surface_t out of the bitmap's pixel buffer and then draw
- // it into our context.
- cairo_surface_t* surface = cairo_image_surface_create_for_data(pixelData.buffer(),
- CAIRO_FORMAT_ARGB32,
- pixelData.size().width(),
- pixelData.size().height(),
- pixelData.bytesPerRow());
-
- // Flip the target surface so that when we set the srcImage as
- // the surface it will draw right-side-up.
- cairo_save(cr);
- cairo_translate(cr, static_cast<double>(translate.width()), static_cast<double>(translate.height()));
- cairo_scale(cr, 1, -1);
- cairo_set_source_surface(cr, surface, 0, 0);
-
- if (context->layers.size())
- cairo_paint_with_alpha(cr, context->layers.last());
- else
- cairo_paint(cr);
-
- // Delete all our junk.
- cairo_surface_destroy(surface);
- cairo_restore(cr);
-}
-
-void GraphicsContext::releaseWindowsContext(HDC hdc, const IntRect& dstRect, bool supportAlphaBlend, bool mayCreateBitmap)
-{
- bool createdBitmap = mayCreateBitmap && (!m_data->m_hdc || isInTransparencyLayer());
- if (!hdc || !createdBitmap) {
- m_data->restore();
- return;
- }
-
- if (dstRect.isEmpty())
- return;
-
- auto bitmap = adoptGDIObject(static_cast<HBITMAP>(::GetCurrentObject(hdc, OBJ_BITMAP)));
-
- DIBPixelData pixelData(bitmap.get());
- ASSERT(pixelData.bitsPerPixel() == 32);
-
- // If this context does not support alpha blending, then it may have
- // been drawn with GDI functions which always set the alpha channel
- // to zero. We need to manually set the bitmap to be fully opaque.
- unsigned char* bytes = reinterpret_cast<unsigned char*>(pixelData.buffer());
- if (!supportAlphaBlend)
- setRGBABitmapAlpha(bytes, pixelData.size().height() * pixelData.bytesPerRow(), 255);
-
- drawBitmapToContext(m_data, platformContext()->cr(), pixelData, IntSize(dstRect.x(), dstRect.height() + dstRect.y()));
-
- ::DeleteDC(hdc);
-}
-
-#if PLATFORM(WIN)
-void GraphicsContext::drawWindowsBitmap(WindowsBitmap* bitmap, const IntPoint& point)
-{
- drawBitmapToContext(m_data, platformContext()->cr(), bitmap->windowsDIB(), IntSize(point.x(), bitmap->size().height() + point.y()));
-}
-
-void GraphicsContextPlatformPrivate::syncContext(cairo_t* cr)
-{
- if (!cr)
- return;
-
- cairo_surface_t* surface = cairo_get_target(cr);
- m_hdc = cairo_win32_surface_get_dc(surface);
-
- SetGraphicsMode(m_hdc, GM_ADVANCED); // We need this call for themes to honor world transforms.
-}
-
-void GraphicsContextPlatformPrivate::flush()
-{
- cairo_surface_t* surface = cairo_win32_surface_create(m_hdc);
- cairo_surface_flush(surface);
- cairo_surface_destroy(surface);
-}
-#endif
-
-}
diff --git a/Source/WebCore/platform/graphics/win/GraphicsContextWin.cpp b/Source/WebCore/platform/graphics/win/GraphicsContextWin.cpp
deleted file mode 100644
index 44fa7f069..000000000
--- a/Source/WebCore/platform/graphics/win/GraphicsContextWin.cpp
+++ /dev/null
@@ -1,211 +0,0 @@
-/*
- * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2013 Apple Inc. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
- * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
- * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#include "config.h"
-#include "GraphicsContext.h"
-
-#if USE(CG)
-#include "GraphicsContextPlatformPrivateCG.h"
-#elif USE(CAIRO)
-#include "GraphicsContextPlatformPrivateCairo.h"
-#endif
-
-#include "AffineTransform.h"
-#include "BitmapInfo.h"
-#include "TransformationMatrix.h"
-#include "NotImplemented.h"
-#include "Path.h"
-#include <wtf/MathExtras.h>
-#include <wtf/win/GDIObject.h>
-
-using namespace std;
-
-namespace WebCore {
-
-static void fillWithClearColor(HBITMAP bitmap)
-{
- BITMAP bmpInfo;
- GetObject(bitmap, sizeof(bmpInfo), &bmpInfo);
- int bufferSize = bmpInfo.bmWidthBytes * bmpInfo.bmHeight;
- memset(bmpInfo.bmBits, 0, bufferSize);
-}
-
-#if PLATFORM(WIN)
-void GraphicsContext::setShouldIncludeChildWindows(bool include)
-{
- m_data->m_shouldIncludeChildWindows = include;
-}
-
-bool GraphicsContext::shouldIncludeChildWindows() const
-{
- return m_data->m_shouldIncludeChildWindows;
-}
-
-GraphicsContext::WindowsBitmap::WindowsBitmap(HDC hdc, const IntSize& size)
- : m_hdc(0)
-{
- BitmapInfo bitmapInfo = BitmapInfo::create(size);
-
- void* storage = 0;
- m_bitmap = CreateDIBSection(0, &bitmapInfo, DIB_RGB_COLORS, &storage, 0, 0);
- if (!m_bitmap)
- return;
-
- m_hdc = CreateCompatibleDC(hdc);
- SelectObject(m_hdc, m_bitmap);
-
- m_pixelData.initialize(m_bitmap);
-
- ASSERT(storage == m_pixelData.buffer());
-
- SetGraphicsMode(m_hdc, GM_ADVANCED);
-}
-
-GraphicsContext::WindowsBitmap::~WindowsBitmap()
-{
- if (!m_bitmap)
- return;
-
- DeleteDC(m_hdc);
- DeleteObject(m_bitmap);
-}
-
-PassOwnPtr<GraphicsContext::WindowsBitmap> GraphicsContext::createWindowsBitmap(const IntSize& size)
-{
- return adoptPtr(new WindowsBitmap(m_data->m_hdc, size));
-}
-#endif
-
-HDC GraphicsContext::getWindowsContext(const IntRect& dstRect, bool supportAlphaBlend, bool mayCreateBitmap)
-{
- // FIXME: Should a bitmap be created also when a shadow is set?
- if (mayCreateBitmap && (!m_data->m_hdc || isInTransparencyLayer())) {
- if (dstRect.isEmpty())
- return 0;
-
- // Create a bitmap DC in which to draw.
- BitmapInfo bitmapInfo = BitmapInfo::create(dstRect.size());
-
- void* pixels = 0;
- HBITMAP bitmap = ::CreateDIBSection(NULL, &bitmapInfo, DIB_RGB_COLORS, &pixels, 0, 0);
- if (!bitmap)
- return 0;
-
- auto bitmapDC = adoptGDIObject(::CreateCompatibleDC(m_data->m_hdc));
- ::SelectObject(bitmapDC.get(), bitmap);
-
- // Fill our buffer with clear if we're going to alpha blend.
- if (supportAlphaBlend)
- fillWithClearColor(bitmap);
-
- // Make sure we can do world transforms.
- ::SetGraphicsMode(bitmapDC.get(), GM_ADVANCED);
-
- // Apply a translation to our context so that the drawing done will be at (0,0) of the bitmap.
- XFORM xform = TransformationMatrix().translate(-dstRect.x(), -dstRect.y());
-
- ::SetWorldTransform(bitmapDC.get(), &xform);
-
- return bitmapDC.leak();
- }
-
- m_data->flush();
- m_data->save();
- return m_data->m_hdc;
-}
-
-#if PLATFORM(WIN)
-void GraphicsContextPlatformPrivate::save()
-{
- if (!m_hdc)
- return;
- SaveDC(m_hdc);
-}
-
-void GraphicsContextPlatformPrivate::restore()
-{
- if (!m_hdc)
- return;
- RestoreDC(m_hdc, -1);
-}
-
-void GraphicsContextPlatformPrivate::clip(const FloatRect& clipRect)
-{
- if (!m_hdc)
- return;
- IntersectClipRect(m_hdc, clipRect.x(), clipRect.y(), clipRect.maxX(), clipRect.maxY());
-}
-
-void GraphicsContextPlatformPrivate::clip(const Path&)
-{
- notImplemented();
-}
-
-void GraphicsContextPlatformPrivate::scale(const FloatSize& size)
-{
- if (!m_hdc)
- return;
-
- XFORM xform = TransformationMatrix().scaleNonUniform(size.width(), size.height());
- ModifyWorldTransform(m_hdc, &xform, MWT_LEFTMULTIPLY);
-}
-
-static const double deg2rad = 0.017453292519943295769; // pi/180
-
-void GraphicsContextPlatformPrivate::rotate(float degreesAngle)
-{
- XFORM xform = TransformationMatrix().rotate(degreesAngle);
- ModifyWorldTransform(m_hdc, &xform, MWT_LEFTMULTIPLY);
-}
-
-void GraphicsContextPlatformPrivate::translate(float x , float y)
-{
- if (!m_hdc)
- return;
-
- XFORM xform = TransformationMatrix().translate(x, y);
- ModifyWorldTransform(m_hdc, &xform, MWT_LEFTMULTIPLY);
-}
-
-void GraphicsContextPlatformPrivate::concatCTM(const AffineTransform& transform)
-{
- if (!m_hdc)
- return;
-
- XFORM xform = transform.toTransformationMatrix();
- ModifyWorldTransform(m_hdc, &xform, MWT_LEFTMULTIPLY);
-}
-
-void GraphicsContextPlatformPrivate::setCTM(const AffineTransform& transform)
-{
- if (!m_hdc)
- return;
-
- XFORM xform = transform.toTransformationMatrix();
- SetWorldTransform(m_hdc, &xform);
-}
-#endif
-
-}
diff --git a/Source/WebCore/platform/graphics/win/LocalWindowsContext.h b/Source/WebCore/platform/graphics/win/LocalWindowsContext.h
deleted file mode 100644
index 5951e4962..000000000
--- a/Source/WebCore/platform/graphics/win/LocalWindowsContext.h
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * Copyright (C) 2010 Apple Inc. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
- * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
- * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#ifndef LocalWindowsContext_h
-#define LocalWindowsContext_h
-
-#include "config.h"
-#include "GraphicsContext.h"
-
-namespace WebCore {
-
-class LocalWindowsContext {
- WTF_MAKE_NONCOPYABLE(LocalWindowsContext);
-public:
- LocalWindowsContext(GraphicsContext* graphicsContext, const IntRect& rect, bool supportAlphaBlend = true, bool mayCreateBitmap = true)
- : m_graphicsContext(graphicsContext)
- , m_rect(rect)
- , m_supportAlphaBlend(supportAlphaBlend)
- , m_mayCreateBitmap(mayCreateBitmap)
- {
- m_hdc = m_graphicsContext->getWindowsContext(m_rect, m_supportAlphaBlend, m_mayCreateBitmap);
- }
-
- ~LocalWindowsContext()
- {
- m_graphicsContext->releaseWindowsContext(m_hdc, m_rect, m_supportAlphaBlend, m_mayCreateBitmap);
- }
-
- HDC hdc() const { return m_hdc; }
-
-private:
- GraphicsContext* m_graphicsContext;
- HDC m_hdc;
- IntRect m_rect;
- bool m_supportAlphaBlend;
- bool m_mayCreateBitmap;
-};
-
-} // namespace WebCore
-#endif // LocalWindowsContext_h
diff --git a/Source/WebCore/platform/graphics/win/TransformationMatrixWin.cpp b/Source/WebCore/platform/graphics/win/TransformationMatrixWin.cpp
deleted file mode 100644
index 47806a271..000000000
--- a/Source/WebCore/platform/graphics/win/TransformationMatrixWin.cpp
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Copyright (C) 2009 Apple Inc. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
- * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
- * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#include "config.h"
-#include "TransformationMatrix.h"
-
-#include <windows.h>
-
-namespace WebCore {
-
-TransformationMatrix::operator XFORM() const
-{
- XFORM xform;
- xform.eM11 = a();
- xform.eM12 = b();
- xform.eM21 = c();
- xform.eM22 = d();
- xform.eDx = e();
- xform.eDy = f();
-
- return xform;
-}
-
-}
diff --git a/Source/WebCore/platform/graphics/x11/PlatformDisplayX11.cpp b/Source/WebCore/platform/graphics/x11/PlatformDisplayX11.cpp
new file mode 100644
index 000000000..5aefe6d51
--- /dev/null
+++ b/Source/WebCore/platform/graphics/x11/PlatformDisplayX11.cpp
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 2015 Igalia S.L
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "PlatformDisplayX11.h"
+
+#include "GLContext.h"
+
+#if PLATFORM(X11)
+#include <X11/Xlib.h>
+#include <X11/extensions/Xcomposite.h>
+#if PLATFORM(GTK)
+#include <X11/extensions/Xdamage.h>
+#endif
+
+#if USE(EGL)
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
+#endif
+
+namespace WebCore {
+
+std::unique_ptr<PlatformDisplay> PlatformDisplayX11::create()
+{
+ Display* display = XOpenDisplay(getenv("DISPLAY"));
+ if (!display)
+ return nullptr;
+
+ return std::make_unique<PlatformDisplayX11>(display, NativeDisplayOwned::Yes);
+}
+
+PlatformDisplayX11::PlatformDisplayX11(Display* display, NativeDisplayOwned displayOwned)
+ : PlatformDisplay(displayOwned)
+ , m_display(display)
+{
+}
+
+PlatformDisplayX11::~PlatformDisplayX11()
+{
+#if USE(EGL) || USE(GLX)
+ // Clear the sharing context before releasing the display.
+ m_sharingGLContext = nullptr;
+#endif
+ if (m_nativeDisplayOwned == NativeDisplayOwned::Yes)
+ XCloseDisplay(m_display);
+}
+
+#if USE(EGL)
+void PlatformDisplayX11::initializeEGLDisplay()
+{
+#if defined(EGL_KHR_platform_x11)
+ const char* extensions = eglQueryString(nullptr, EGL_EXTENSIONS);
+ if (GLContext::isExtensionSupported(extensions, "EGL_KHR_platform_base")) {
+ if (auto* getPlatformDisplay = reinterpret_cast<PFNEGLGETPLATFORMDISPLAYEXTPROC>(eglGetProcAddress("eglGetPlatformDisplay")))
+ m_eglDisplay = getPlatformDisplay(EGL_PLATFORM_X11_KHR, m_display, nullptr);
+ } else if (GLContext::isExtensionSupported(extensions, "EGL_EXT_platform_base")) {
+ if (auto* getPlatformDisplay = reinterpret_cast<PFNEGLGETPLATFORMDISPLAYEXTPROC>(eglGetProcAddress("eglGetPlatformDisplayEXT")))
+ m_eglDisplay = getPlatformDisplay(EGL_PLATFORM_X11_KHR, m_display, nullptr);
+ } else
+#endif
+ m_eglDisplay = eglGetDisplay(m_display);
+
+ PlatformDisplay::initializeEGLDisplay();
+}
+#endif // USE(EGL)
+
+bool PlatformDisplayX11::supportsXComposite() const
+{
+ if (!m_supportsXComposite) {
+ if (m_display) {
+ int eventBase, errorBase;
+ m_supportsXComposite = XCompositeQueryExtension(m_display, &eventBase, &errorBase);
+ } else
+ m_supportsXComposite = false;
+ }
+ return m_supportsXComposite.value();
+}
+
+bool PlatformDisplayX11::supportsXDamage(std::optional<int>& damageEventBase, std::optional<int>& damageErrorBase) const
+{
+ if (!m_supportsXDamage) {
+ m_supportsXDamage = false;
+#if PLATFORM(GTK)
+ if (m_display) {
+ int eventBase, errorBase;
+ m_supportsXDamage = XDamageQueryExtension(m_display, &eventBase, &errorBase);
+ if (m_supportsXDamage.value()) {
+ m_damageEventBase = eventBase;
+ m_damageErrorBase = errorBase;
+ }
+ }
+#endif
+ }
+
+ damageEventBase = m_damageEventBase;
+ damageErrorBase = m_damageErrorBase;
+ return m_supportsXDamage.value();
+}
+
+} // namespace WebCore
+
+#endif // PLATFORM(X11)
+
diff --git a/Source/WebCore/platform/graphics/x11/PlatformDisplayX11.h b/Source/WebCore/platform/graphics/x11/PlatformDisplayX11.h
new file mode 100644
index 000000000..311744576
--- /dev/null
+++ b/Source/WebCore/platform/graphics/x11/PlatformDisplayX11.h
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2015 Igalia S.L
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef PlatformDisplayX11_h
+#define PlatformDisplayX11_h
+
+#if PLATFORM(X11)
+
+#include "PlatformDisplay.h"
+#include <wtf/Optional.h>
+
+typedef struct _XDisplay Display;
+
+namespace WebCore {
+
+class PlatformDisplayX11 final : public PlatformDisplay {
+public:
+ static std::unique_ptr<PlatformDisplay> create();
+ PlatformDisplayX11(Display*, NativeDisplayOwned = NativeDisplayOwned::No);
+ virtual ~PlatformDisplayX11();
+
+ Display* native() const { return m_display; }
+ bool supportsXComposite() const;
+ bool supportsXDamage(std::optional<int>& damageEventBase, std::optional<int>& damageErrorBase) const;
+
+private:
+ Type type() const override { return PlatformDisplay::Type::X11; }
+
+#if USE(EGL)
+ void initializeEGLDisplay() override;
+#endif
+
+ Display* m_display { nullptr };
+ mutable std::optional<bool> m_supportsXComposite;
+ mutable std::optional<bool> m_supportsXDamage;
+ mutable std::optional<int> m_damageEventBase;
+ mutable std::optional<int> m_damageErrorBase;
+};
+
+} // namespace WebCore
+
+SPECIALIZE_TYPE_TRAITS_PLATFORM_DISPLAY(PlatformDisplayX11, X11)
+
+#endif // PLATFORM(X11)
+
+#endif // PlatformDisplayX11
diff --git a/Source/WebCore/platform/graphics/x11/XErrorTrapper.cpp b/Source/WebCore/platform/graphics/x11/XErrorTrapper.cpp
new file mode 100644
index 000000000..6fd4d49df
--- /dev/null
+++ b/Source/WebCore/platform/graphics/x11/XErrorTrapper.cpp
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2016 Igalia S.L
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "XErrorTrapper.h"
+
+#if PLATFORM(X11)
+#include <sys/types.h>
+#include <unistd.h>
+#include <wtf/HashMap.h>
+#include <wtf/NeverDestroyed.h>
+
+namespace WebCore {
+
+static HashMap<Display*, Vector<XErrorTrapper*>>& xErrorTrappersMap()
+{
+ static NeverDestroyed<HashMap<Display*, Vector<XErrorTrapper*>>> trappersMap;
+ return trappersMap;
+}
+
+XErrorTrapper::XErrorTrapper(Display* display, Policy policy, Vector<unsigned char>&& expectedErrors)
+ : m_display(display)
+ , m_policy(policy)
+ , m_expectedErrors(WTFMove(expectedErrors))
+{
+ xErrorTrappersMap().add(m_display, Vector<XErrorTrapper*>()).iterator->value.append(this);
+ m_previousErrorHandler = XSetErrorHandler([](Display* display, XErrorEvent* event) -> int {
+ auto iterator = xErrorTrappersMap().find(display);
+ if (iterator == xErrorTrappersMap().end())
+ return 0;
+
+ ASSERT(!iterator->value.isEmpty());
+ iterator->value.last()->errorEvent(event);
+ return 0;
+ });
+}
+
+XErrorTrapper::~XErrorTrapper()
+{
+ XSync(m_display, False);
+ auto iterator = xErrorTrappersMap().find(m_display);
+ ASSERT(iterator != xErrorTrappersMap().end());
+ auto* trapper = iterator->value.takeLast();
+ ASSERT_UNUSED(trapper, trapper == this);
+ if (iterator->value.isEmpty())
+ xErrorTrappersMap().remove(iterator);
+
+ XSetErrorHandler(m_previousErrorHandler);
+}
+
+unsigned char XErrorTrapper::errorCode() const
+{
+ XSync(m_display, False);
+ return m_errorCode;
+}
+
+void XErrorTrapper::errorEvent(XErrorEvent* event)
+{
+ m_errorCode = event->error_code;
+ if (m_policy == Policy::Ignore)
+ return;
+
+ if (m_expectedErrors.contains(m_errorCode))
+ return;
+
+ static const char errorFormatString[] = "The program with pid %d received an X Window System error.\n"
+ "The error was '%s'.\n"
+ " (Details: serial %ld error_code %d request_code %d minor_code %d)\n";
+ char errorMessage[64];
+ XGetErrorText(m_display, m_errorCode, errorMessage, 63);
+ WTFLogAlways(errorFormatString, getpid(), errorMessage, event->serial, event->error_code, event->request_code, event->minor_code);
+
+ if (m_policy == Policy::Crash)
+ CRASH();
+}
+
+} // namespace WebCore
+
+#endif // PLATFORM(X11)
diff --git a/Source/WebCore/platform/graphics/x11/XErrorTrapper.h b/Source/WebCore/platform/graphics/x11/XErrorTrapper.h
new file mode 100644
index 000000000..5d53b6c33
--- /dev/null
+++ b/Source/WebCore/platform/graphics/x11/XErrorTrapper.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2016 Igalia S.L
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#if PLATFORM(X11)
+#include <X11/Xlib.h>
+#include <wtf/Vector.h>
+
+namespace WebCore {
+
+class XErrorTrapper {
+public:
+ enum class Policy { Ignore, Warn, Crash };
+ XErrorTrapper(Display*, Policy = Policy::Ignore, Vector<unsigned char>&& expectedErrors = { });
+ ~XErrorTrapper();
+
+ unsigned char errorCode() const;
+
+private:
+ void errorEvent(XErrorEvent*);
+
+ Display* m_display { nullptr };
+ Policy m_policy { Policy::Ignore };
+ Vector<unsigned char> m_expectedErrors;
+ XErrorHandler m_previousErrorHandler { nullptr };
+ unsigned char m_errorCode { 0 };
+};
+
+} // namespace WebCore
+
+#endif // PLATFORM(X11)
diff --git a/Source/WebCore/platform/graphics/x11/XUniquePtr.h b/Source/WebCore/platform/graphics/x11/XUniquePtr.h
new file mode 100644
index 000000000..37a18c29d
--- /dev/null
+++ b/Source/WebCore/platform/graphics/x11/XUniquePtr.h
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2015 Igalia S.L
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef XUniquePtr_h
+#define XUniquePtr_h
+
+#if PLATFORM(X11)
+#include "PlatformDisplayX11.h"
+#include <X11/Xutil.h>
+
+#if USE(GLX)
+typedef struct __GLXcontextRec* GLXContext;
+extern "C" void glXDestroyContext(Display*, GLXContext);
+#endif
+
+namespace WebCore {
+
+template<typename T>
+struct XPtrDeleter {
+ void operator()(T* ptr) const
+ {
+ XFree(ptr);
+ }
+};
+
+template<typename T>
+using XUniquePtr = std::unique_ptr<T, XPtrDeleter<T>>;
+
+template<> struct XPtrDeleter<XImage> {
+ void operator() (XImage* ptr) const
+ {
+ XDestroyImage(ptr);
+ }
+};
+
+template<> struct XPtrDeleter<_XGC> {
+ void operator() (_XGC* ptr) const
+ {
+ XFreeGC(downcast<PlatformDisplayX11>(PlatformDisplay::sharedDisplay()).native(), ptr);
+ }
+};
+// Give a name to this to avoid having to use the internal struct name.
+using XUniqueGC = XUniquePtr<_XGC>;
+
+#if USE(GLX)
+template<> struct XPtrDeleter<__GLXcontextRec> {
+ void operator() (__GLXcontextRec* ptr)
+ {
+ glXDestroyContext(downcast<PlatformDisplayX11>(PlatformDisplay::sharedDisplay()).native(), ptr);
+ }
+};
+// Give a name to this to avoid having to use the internal struct name.
+using XUniqueGLXContext = XUniquePtr<__GLXcontextRec>;
+#endif
+
+} // namespace WebCore
+
+#endif // PLATFORM(X11)
+
+#endif // XUniquePtr_h
diff --git a/Source/WebCore/platform/graphics/x11/XUniqueResource.cpp b/Source/WebCore/platform/graphics/x11/XUniqueResource.cpp
new file mode 100644
index 000000000..f300ee4f5
--- /dev/null
+++ b/Source/WebCore/platform/graphics/x11/XUniqueResource.cpp
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2015 Igalia S.L
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "XUniqueResource.h"
+
+#if PLATFORM(X11)
+#include "PlatformDisplayX11.h"
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+
+#if PLATFORM(GTK)
+#include <X11/extensions/Xdamage.h>
+#endif
+
+#if USE(GLX)
+#include <GL/glx.h>
+#endif
+
+namespace WebCore {
+
+static inline Display* sharedDisplay()
+{
+ return downcast<PlatformDisplayX11>(PlatformDisplay::sharedDisplay()).native();
+}
+
+template<> void XUniqueResource<XResource::Colormap>::deleteXResource(unsigned long resource)
+{
+ if (resource)
+ XFreeColormap(sharedDisplay(), resource);
+}
+
+#if PLATFORM(GTK)
+template<> void XUniqueResource<XResource::Damage>::deleteXResource(unsigned long resource)
+{
+ if (resource)
+ XDamageDestroy(sharedDisplay(), resource);
+}
+#endif
+
+template<> void XUniqueResource<XResource::Pixmap>::deleteXResource(unsigned long resource)
+{
+ if (resource)
+ XFreePixmap(sharedDisplay(), resource);
+}
+
+template<> void XUniqueResource<XResource::Window>::deleteXResource(unsigned long resource)
+{
+ if (resource)
+ XDestroyWindow(sharedDisplay(), resource);
+}
+
+#if USE(GLX)
+template<> void XUniqueResource<XResource::GLXPbuffer>::deleteXResource(unsigned long resource)
+{
+ if (resource)
+ glXDestroyPbuffer(sharedDisplay(), resource);
+}
+
+template<> void XUniqueResource<XResource::GLXPixmap>::deleteXResource(unsigned long resource)
+{
+ if (resource)
+ glXDestroyGLXPixmap(sharedDisplay(), resource);
+}
+#endif // USE(GLX)
+
+} // namespace WebCore
+
+#endif // PLATFORM(X11)
diff --git a/Source/WebCore/platform/graphics/x11/XUniqueResource.h b/Source/WebCore/platform/graphics/x11/XUniqueResource.h
new file mode 100644
index 000000000..0da8b0c9c
--- /dev/null
+++ b/Source/WebCore/platform/graphics/x11/XUniqueResource.h
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2015 Igalia S.L
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef XUniqueResource_h
+#define XUniqueResource_h
+
+#if PLATFORM(X11)
+
+#if USE(GLX)
+typedef unsigned long GLXPbuffer;
+typedef unsigned long GLXPixmap;
+#endif
+
+namespace WebCore {
+
+enum class XResource {
+ Colormap,
+#if PLATFORM(GTK)
+ Damage,
+#endif
+ Pixmap,
+ Window,
+#if USE(GLX)
+ GLXPbuffer,
+ GLXPixmap,
+#endif
+};
+
+template <XResource T> class XUniqueResource {
+public:
+ XUniqueResource()
+ {
+ }
+
+ XUniqueResource(unsigned long resource)
+ : m_resource(resource)
+ {
+ }
+
+ XUniqueResource(XUniqueResource&& uniqueResource)
+ : m_resource(uniqueResource.release())
+ {
+ }
+
+ XUniqueResource& operator=(XUniqueResource&& uniqueResource)
+ {
+ reset(uniqueResource.release());
+ return *this;
+ }
+
+ ~XUniqueResource()
+ {
+ reset();
+ }
+
+ unsigned long get() const { return m_resource; }
+ unsigned long release() { return std::exchange(m_resource, 0); }
+
+ void reset(unsigned long resource = 0)
+ {
+ std::swap(m_resource, resource);
+ deleteXResource(resource);
+ }
+
+ explicit operator bool() const { return m_resource; }
+
+private:
+ static void deleteXResource(unsigned long resource);
+
+ unsigned long m_resource { 0 };
+};
+
+using XUniqueColormap = XUniqueResource<XResource::Colormap>;
+#if PLATFORM(GTK)
+using XUniqueDamage = XUniqueResource<XResource::Damage>;
+#endif
+using XUniquePixmap = XUniqueResource<XResource::Pixmap>;
+using XUniqueWindow = XUniqueResource<XResource::Window>;
+#if USE(GLX)
+using XUniqueGLXPbuffer = XUniqueResource<XResource::GLXPbuffer>;
+using XUniqueGLXPixmap = XUniqueResource<XResource::GLXPixmap>;
+#endif
+
+} // namespace WebCore
+
+#endif // PLATFORM(X11)
+
+#endif // XUniqueResource_h